diff --git a/Dockerfile b/Dockerfile index 1207f778..47a9efe2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,10 @@ # ============================== # Stage 1: Builder # ============================== -FROM node:20-bookworm-slim AS builder +FROM oven/bun:1-debian AS builder WORKDIR /app -# Install system deps RUN apt-get update && apt-get install -y --no-install-recommends \ libc6 \ git \ @@ -13,39 +12,33 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ && rm -rf /var/lib/apt/lists/* -# Copy dependency files first (for better caching) -COPY package.json package-lock.json* bun.lockb* ./ +COPY package.json bun.lockb* ./ ENV SHARP_IGNORE_GLOBAL_LIBVIPS=1 ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_OPTIONS="--max-old-space-size=4096" -# 🔥 Skip postinstall scripts (fix onnxruntime error) -RUN npm install --legacy-peer-deps --ignore-scripts +RUN bun install --frozen-lockfile -# Copy full source COPY . . -# Use .env.example as build env -# (Pastikan file ini ada di project) RUN cp .env.example .env || true -# Generate Prisma Client ENV PRISMA_CLI_BINARY_TARGETS=debian-openssl-3.0.x -RUN npx prisma generate +RUN bunx prisma generate -# Build Next.js -RUN npm run build +RUN bun run build # ============================== # Stage 2: Runner (Production) # ============================== -FROM node:20-bookworm-slim AS runner +FROM oven/bun:1-debian AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 +ENV PRISMA_CLI_BINARY_TARGETS=debian-openssl-3.0.x RUN apt-get update && apt-get install -y --no-install-recommends \ openssl \ @@ -55,10 +48,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ RUN groupadd --system --gid 1001 nodejs \ && useradd --system --uid 1001 --gid nodejs nextjs -# Copy standalone output -COPY --from=builder /app/.next/standalone ./ -COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/src ./src +COPY --from=builder /app/next.config.js ./next.config.js +COPY --from=builder /app/tsconfig.json ./tsconfig.json RUN chown -R nextjs:nodejs /app @@ -69,4 +66,4 @@ EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" -CMD ["node", "server.js"] \ No newline at end of file +CMD ["bun", "start"] \ No newline at end of file diff --git a/src/app/event/[id]/confirmation/route.ts b/src/app/(event-confirmation)/event/[id]/confirmation/route.ts similarity index 100% rename from src/app/event/[id]/confirmation/route.ts rename to src/app/(event-confirmation)/event/[id]/confirmation/route.ts diff --git a/src/app/zCoba/home/page.tsx b/src/app/zCoba/home/page.tsx deleted file mode 100644 index 437474b1..00000000 --- a/src/app/zCoba/home/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import ClientLayout from "./v2_coba_tamplate"; -import ViewV2 from "./v2_view"; - -export default async function Page() { - return ( - <> - - - ); -} diff --git a/src/app/zCoba/home/test_children.tsx b/src/app/zCoba/home/test_children.tsx deleted file mode 100644 index e3a96278..00000000 --- a/src/app/zCoba/home/test_children.tsx +++ /dev/null @@ -1,127 +0,0 @@ -"use client"; - -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { - listMenuHomeBody, - menuHomeJob, -} from "@/app_modules/home/component/list_menu_home"; -import { - ActionIcon, - Box, - Group, - Image, - Paper, - SimpleGrid, - Stack, - Text -} from "@mantine/core"; -import { IconUserSearch } from "@tabler/icons-react"; - -export function Test_Children() { - return ( - <> - - logo - - {Array.from(new Array(2)).map((e, i) => ( - - - {listMenuHomeBody.map((e, i) => ( - {}} - > - - - {e.icon} - - - {e.name} - - - - ))} - - - {/* Job View */} - - {}}> - - - {menuHomeJob.icon} - - - {menuHomeJob.name} - - - - {Array.from({ length: 2 }).map((e, i) => ( - - - - - - - - nama {i} - - - judulnya {i} - - - - - ))} - - - - - ))} - - - ); -} diff --git a/src/app/zCoba/home/test_footer.tsx b/src/app/zCoba/home/test_footer.tsx deleted file mode 100644 index baa2368a..00000000 --- a/src/app/zCoba/home/test_footer.tsx +++ /dev/null @@ -1,75 +0,0 @@ -"use client" - -import { MainColor } from "@/app_modules/_global/color"; -import { listMenuHomeFooter } from "@/app_modules/home"; -import { - ActionIcon, - Box, - Center, - SimpleGrid, - Stack, - Text, -} from "@mantine/core"; -import { IconUserCircle } from "@tabler/icons-react"; -import { useRouter } from "next/navigation"; - -export default function Test_FooterHome() { - const router = useRouter(); - - return ( - - - {listMenuHomeFooter.map((e) => ( -
- { - console.log("test") - }} - > - - {e.icon} - - - {e.name} - - -
- ))} - -
- - - console.log("test") - } - > - - - - Profile - - -
-
-
- ); -} diff --git a/src/app/zCoba/home/test_header.tsx b/src/app/zCoba/home/test_header.tsx deleted file mode 100644 index df65017a..00000000 --- a/src/app/zCoba/home/test_header.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; - -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { - Header, - Group, - ActionIcon, - Text, - Title, - Box, - Loader, -} from "@mantine/core"; -import { IconArrowLeft, IconChevronLeft } from "@tabler/icons-react"; -import { useRouter } from "next/navigation"; -import React, { useState } from "react"; - - -export default function Test_LayoutHeaderTamplate({ - title, - posotion, - // left button - hideButtonLeft, - iconLeft, - routerLeft, - customButtonLeft, - // right button - iconRight, - routerRight, - customButtonRight, - backgroundColor, -}: { - title: string; - posotion?: any; - // left button - hideButtonLeft?: boolean; - iconLeft?: any; - routerLeft?: any; - customButtonLeft?: React.ReactNode; - // right button - iconRight?: any; - routerRight?: any; - customButtonRight?: React.ReactNode; - backgroundColor?: string; -}) { - const router = useRouter(); - const [isLoading, setIsLoading] = useState(false); - const [isRightLoading, setRightLoading] = useState(false); - - return ( - <> - - - {hideButtonLeft ? ( - - ) : customButtonLeft ? ( - customButtonLeft - ) : ( - { - setIsLoading(true); - routerLeft === undefined - ? router.back() - : router.push(routerLeft, { scroll: false }); - }} - > - {/* PAKE LOADING SAAT KLIK BACK */} - {/* {isLoading ? ( - - ) : iconLeft ? ( - iconLeft - ) : ( - - )} */} - - - {/* GA PAKE LOADING SAAT KLIK BACK */} - {iconLeft ? ( - iconLeft - ) : ( - - )} - - )} - - - {title} - - - {customButtonRight ? ( - customButtonRight - ) : iconRight === undefined ? ( - - ) : routerRight === undefined ? ( - {iconRight} - ) : ( - { - setRightLoading(true); - router.push(routerRight); - }} - > - {isRightLoading ? ( - - ) : ( - iconRight - )} - - )} - - - - ); -} diff --git a/src/app/zCoba/home/test_tamplate.tsx b/src/app/zCoba/home/test_tamplate.tsx deleted file mode 100644 index ff55f50a..00000000 --- a/src/app/zCoba/home/test_tamplate.tsx +++ /dev/null @@ -1,127 +0,0 @@ -"use client"; - -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { - BackgroundImage, - Box, - Container, - rem, - ScrollArea, -} from "@mantine/core"; - -export function Test_Tamplate({ - children, - header, - footer, -}: { - children: React.ReactNode; - header: React.ReactNode; - footer?: React.ReactNode; -}) { - return ( - <> - - - {/* */} - - - {children} - - - {/* */} - - - - ); -} - -export function TestHeader({ header }: { header: React.ReactNode }) { - return ( - <> - - {header} - - - ); -} - -export function TestChildren({ - children, - footer, -}: { - children: React.ReactNode; - footer: React.ReactNode; -}) { - return ( - <> - - {children} - - - ); -} - -function TestFooter({ footer }: { footer: React.ReactNode }) { - return ( - <> - {footer ? ( - - - {footer} - - - ) : ( - "" - )} - - ); -} diff --git a/src/app/zCoba/home/v2_children.tsx b/src/app/zCoba/home/v2_children.tsx deleted file mode 100644 index 7942ba69..00000000 --- a/src/app/zCoba/home/v2_children.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; - -import { Box, ScrollArea } from "@mantine/core"; - -export function V2_Children({ - children, - height, -}: { - children: React.ReactNode; - height?: number; -}) { - return ( - <> - - {children} - {/* - */} - - - ); -} diff --git a/src/app/zCoba/home/v2_coba_tamplate.tsx b/src/app/zCoba/home/v2_coba_tamplate.tsx deleted file mode 100644 index 58fec604..00000000 --- a/src/app/zCoba/home/v2_coba_tamplate.tsx +++ /dev/null @@ -1,159 +0,0 @@ -"use client"; - -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { - ActionIcon, - BackgroundImage, // Import BackgroundImage dari Mantine - Box, - Button, - Container, - Group, - Text, - Title, -} from "@mantine/core"; -import { useDisclosure, useShallowEffect } from "@mantine/hooks"; -import { createStyles } from "@mantine/styles"; -import { IconBell, IconSearch } from "@tabler/icons-react"; -import { ReactNode, useEffect, useState } from "react"; - -// Styling langsung didefinisikan di dalam komponen -const useStyles = createStyles((theme) => ({ - pageContainer: { - display: "flex", - flexDirection: "column", - minHeight: "100dvh", // dynamic viewport height untuk mobile - width: "100%", - maxWidth: "500px", // Batasi lebar maksimum - margin: "0 auto", // Pusatkan layout - boxShadow: "0 0 10px rgba(0, 0, 0, 0.1)", // Tambahkan shadow untuk efek mobile-like - backgroundColor: MainColor.darkblue, // Warna latar belakang fallback - - [`@media (max-width: 768px)`]: { - maxWidth: "100%", // Pada layar mobile, gunakan lebar penuh - boxShadow: "none", // Hilangkan shadow pada mobile - }, - }, - - header: { - position: "sticky", - top: 0, - width: "100%", - maxWidth: "500px", // Batasi lebar header sesuai container - margin: "0 auto", // Pusatkan header - backgroundColor: MainColor.darkblue, - boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)", - zIndex: 1000, // Pastikan z-index tinggi - transition: "all 0.3s ease", - color: MainColor.yellow, - }, - - scrolled: { - boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)", - }, - - headerContainer: { - height: "8vh", - display: "flex", - alignItems: "center", - padding: "0 16px", // Padding untuk mobile view - - [`@media (max-width: 768px)`]: { - height: "8vh", - }, - borderBottom: `1px solid ${AccentColor.blue}`, - borderBottomLeftRadius: "10px", - borderBottomRightRadius: "10px", - }, - - content: { - flex: 1, - width: "100%", - overflowY: "auto", // Izinkan scrolling pada konten - paddingBottom: "15vh", // Sesuaikan dengan tinggi footer - }, - - footer: { - width: "100%", - backgroundColor: MainColor.darkblue, - borderTop: `1px solid ${AccentColor.blue}`, - height: "10vh", // Tinggi footer - display: "flex", - alignItems: "center", - justifyContent: "center", - position: "fixed", - bottom: 0, - left: "50%", // Pusatkan footer - transform: "translateX(-50%)", // Pusatkan footer - maxWidth: "500px", // Batasi lebar footer - color: MainColor.white, - borderTopLeftRadius: "10px", - borderTopRightRadius: "10px", - }, -})); - -interface ClientLayoutProps { - children: ReactNode; -} - -export default function ClientLayout({ children }: ClientLayoutProps) { - const [scrolled, setScrolled] = useState(false); - const { classes, cx } = useStyles(); - - // Effect untuk mendeteksi scroll - useEffect(() => { - function handleScroll() { - if (window.scrollY > 10) { - setScrolled(true); - } else { - setScrolled(false); - } - } - - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - }, []); - - return ( - - {/* Header - tetap di atas */} - - - - - - - Home Test - - - - - - - - {/* Konten utama - bisa di-scroll */} - - {children} - - - {/* Footer - tetap di bawah */} - - - - © 2025 Nama Perusahaan - - - - - - - - - ); -} diff --git a/src/app/zCoba/home/v2_header.tsx b/src/app/zCoba/home/v2_header.tsx deleted file mode 100644 index 38a72361..00000000 --- a/src/app/zCoba/home/v2_header.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client"; - -import { MainColor } from "@/app_modules/_global/color"; -import { ActionIcon, Box, Group, Title } from "@mantine/core"; -import { IconBell, IconChevronLeft } from "@tabler/icons-react"; - -export function V2_Header() { - return ( - <> - - - {}} - > - - - - - Test Tamplate - - - {}}> - - - - - - ); -} diff --git a/src/app/zCoba/home/v2_home_view.tsx b/src/app/zCoba/home/v2_home_view.tsx deleted file mode 100644 index 90e0fc9a..00000000 --- a/src/app/zCoba/home/v2_home_view.tsx +++ /dev/null @@ -1,127 +0,0 @@ -"use client"; - -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { - listMenuHomeBody, - menuHomeJob, -} from "@/app_modules/home/component/list_menu_home"; -import { - Box, - Stack, - SimpleGrid, - Paper, - ActionIcon, - Group, - Image, - Text, -} from "@mantine/core"; -import { IconUserSearch } from "@tabler/icons-react"; - -export function V2_HomeView() { - return ( - <> - - logo - - {Array.from(new Array(2)).map((e, i) => ( - - - {listMenuHomeBody.map((e, i) => ( - {}} - > - - - {e.icon} - - - {e.name} - - - - ))} - - - {/* Job View */} - - {}}> - - - {menuHomeJob.icon} - - - {menuHomeJob.name} - - - - {Array.from({ length: 2 }).map((e, i) => ( - - - - - - - - nama {i} - - - judulnya {i} - - - - - ))} - - - - - ))} - - - ); -} diff --git a/src/app/zCoba/home/v2_view.tsx b/src/app/zCoba/home/v2_view.tsx deleted file mode 100644 index 34b8933f..00000000 --- a/src/app/zCoba/home/v2_view.tsx +++ /dev/null @@ -1,117 +0,0 @@ -// app/page.tsx -"use client"; - -import { - Badge, - Box, - Button, - Card, - Group, - Image, - Paper, - Stack, - Text, - Title, -} from "@mantine/core"; -import ClientLayout from "./v2_coba_tamplate"; - -export default function ViewV2() { - return ( - - - - Selamat Datang - - - - Aplikasi dengan layout yang dioptimalkan untuk tampilan mobile - - - - {[...Array(5)].map((_, index) => ( - - - {`Produk - - - - Produk {index + 1} - - Baru - - - - - Deskripsi produk yang singkat dan padat untuk tampilan mobile. - Fokus pada informasi penting saja. - - - - - ))} - - - - {[...Array(5)].map((_, index) => ( - - Test - - ))} - - - {[...Array(5)].map((_, index) => ( -
- Test -
- ))} - - - - Promo Spesial - - - Dapatkan diskon 20% untuk pembelian pertama - - - -
-
- ); -} diff --git a/src/app/zCoba/page.tsx b/src/app/zCoba/page.tsx deleted file mode 100644 index 8c65e79c..00000000 --- a/src/app/zCoba/page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import { MainColor } from "@/app_modules/_global/color"; -import { - Avatar, - Button, - Center, - FileButton, - Paper, - Stack, -} from "@mantine/core"; -import { IconCamera } from "@tabler/icons-react"; -import { useState } from "react"; -import { DIRECTORY_ID } from "../../lib"; - -export default function Page() { - const [data, setData] = useState({ - name: "bagas", - hobi: [ - { - id: "1", - name: "mancing", - }, - { - id: "2", - name: "game", - }, - ], - }); - - return ( - <> - -
{JSON.stringify(data, null, 2)}
- - -
- - ); -} diff --git a/src/app/zCoba/pdf/_view.tsx b/src/app/zCoba/pdf/_view.tsx deleted file mode 100644 index fc7378ef..00000000 --- a/src/app/zCoba/pdf/_view.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { Button } from "@mantine/core"; - -interface DownloadButtonProps { - fileUrl: string; - fileName: string; -} - -export default function Coba() { - const fileUrl = - "https://wibu-storage.wibudev.com/api/pdf-to-image?url=https://wibu-storage.wibudev.com/api/files/cm7liew81000t3y8ax1v6yo02"; - const fileName = "example.pdf"; // Nama file yang akan diunduh - - return ( -
-

Download File Example

- -
- ); -} - -export function DownloadButton({ fileUrl, fileName }: DownloadButtonProps) { - const handleDownloadFromAPI = async () => { - try { - const response = await fetch("https://wibu-storage.wibudev.com/api/files/cm7liew81000t3y8ax1v6yo02") - const blob = await response.blob(); // Konversi respons ke Blob - const url = window.URL.createObjectURL(blob); // Buat URL untuk Blob - const link = document.createElement("a"); - link.href = url; - link.download = "generated-file.pdf"; // Nama file yang akan diunduh - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - window.URL.revokeObjectURL(url); // Bersihkan URL - } catch (error) { - console.error("Error downloading file:", error); - } - }; - - return ( - - ); -} diff --git a/src/app/zCoba/pdf/page.tsx b/src/app/zCoba/pdf/page.tsx deleted file mode 100644 index a645fa5d..00000000 --- a/src/app/zCoba/pdf/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Coba from "./_view"; - - - -async function Page() { - return ( - <> - - - ); -} - -export default Page; diff --git a/src/app/zCoba/scroll/_comp/ui_scroll.tsx b/src/app/zCoba/scroll/_comp/ui_scroll.tsx deleted file mode 100644 index 4d0651f7..00000000 --- a/src/app/zCoba/scroll/_comp/ui_scroll.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import React, { useState, useEffect } from "react"; - -// Tipe untuk data item (sesuaikan sesuai API kamu) -interface Item { - id: number; - name: string; -} - -// Props komponen -interface InfiniteScrollProps { - fetchFunction: (page: number) => Promise; - renderItem: (item: T) => React.ReactNode; - itemsPerPage?: number; - threshold?: number; // Jarak dari bawah halaman untuk memicu load -} - -const InfiniteScroll = ({ - fetchFunction, - renderItem, - itemsPerPage = 10, - threshold = 50, -}: InfiniteScrollProps) => { - const [items, setItems] = useState([]); - const [hasMore, setHasMore] = useState(true); - const [page, setPage] = useState(1); - - // Load data awal - useEffect(() => { - const loadInitialData = async () => { - const data = await fetchFunction(page); - if (data.length === 0) setHasMore(false); - setItems(data); - }; - - loadInitialData(); - }, [fetchFunction, page]); - - // Handle scroll event - useEffect(() => { - const handleScroll = () => { - const isBottom = - window.innerHeight + window.scrollY >= - document.body.offsetHeight - threshold; - - if (isBottom && hasMore) { - loadMoreItems(); - } - }; - - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - }, [hasMore, threshold]); - - const loadMoreItems = async () => { - const nextPage = page + 1; - const newItems = await fetchFunction(nextPage); - - if (newItems.length === 0) { - setHasMore(false); - } - - setItems((prev) => [...prev, ...newItems]); - setPage(nextPage); - }; - - return ( -
-
    - {items.map((item, index) => ( -
  • {renderItem(item)}
  • - ))} -
- {!hasMore &&

🎉 Semua data telah dimuat.

} -
- ); -}; - -export default InfiniteScroll; diff --git a/src/app/zCoba/scroll/page.tsx b/src/app/zCoba/scroll/page.tsx deleted file mode 100644 index 8adcf1d7..00000000 --- a/src/app/zCoba/scroll/page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client" - -import React, { useState } from "react"; -import InfiniteScroll from "./_comp/ui_scroll"; -import { apiGetMessageByRoomId } from "@/app_modules/colab/_lib/api_collaboration"; -import { ChatMessage } from "@/app/dev/(user)/colab/_comp/interface"; - -// Definisikan tipe data -interface User { - id: number; - name: string; - email: string; -} - - - -// Komponen App -function App() { - const [data, setData] = useState([]); - // Simulasi API call - const fetchUsers = async (page: number): Promise => { - const response = await apiGetMessageByRoomId({ - id: "cmb5x31dt0001tl7y7vj26pfy", - }); - setData(response.data); - return response.data; - }; - - return ( -
-

Infinite Scroll with TypeScript

- - fetchFunction={fetchUsers} - itemsPerPage={10} - threshold={100} - renderItem={(item) => ( -
- {item.User?.Profile?.name} - {item.message} -
- )} - /> -
- ); -} - -export default App; diff --git a/src/app/zCoba/skeleton/page.tsx b/src/app/zCoba/skeleton/page.tsx deleted file mode 100644 index df2e7133..00000000 --- a/src/app/zCoba/skeleton/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -"use client"; - -import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -import { - UIGlobal_LayoutHeaderTamplate, - UIGlobal_LayoutTamplate, -} from "@/app_modules/_global/ui"; -import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; -import { Button, Center, Grid, Group, Skeleton, Stack } from "@mantine/core"; -import Link from "next/link"; - -export default function Voting_ComponentSkeletonViewPuh() { - return ( - <> - } - > - - -
- -
- - -
- - {/* - - - - - - - - - - - - - - - - - */} - - {/* - {Array.from({ length: 4 }).map((_, i) => ( - - ))} - - - */} -
- - ); -} diff --git a/src/app/zCoba/test2/page.tsx b/src/app/zCoba/test2/page.tsx deleted file mode 100644 index d010f19e..00000000 --- a/src/app/zCoba/test2/page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client"; -import { gs_realtimeData, IRealtimeData } from "@/lib/global_state"; -import { Button, Stack } from "@mantine/core"; -import { useShallowEffect } from "@mantine/hooks"; -import { useAtom } from "jotai"; -import { WibuRealtime } from "wibu-pkg"; -import { v4 } from "uuid"; - -export default function Page() { - const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); - - useShallowEffect(() => { - console.log( - dataRealtime?.userId == "user2" - ? console.log("") - : console.log(dataRealtime) - ); - }, [dataRealtime]); - - async function onSend() { - const newData: IRealtimeData = { - appId: v4(), - status: "Publish", - userId: "user2", - pesan: "apa kabar", - title: "coba", - kategoriApp: "INVESTASI", - }; - - WibuRealtime.setData({ - type: "notification", - pushNotificationTo: "ADMIN", - }); - } - - return ( - - - - ); -} diff --git a/src/app/zCoba/text_editor/page.tsx b/src/app/zCoba/text_editor/page.tsx deleted file mode 100644 index 76fc18ac..00000000 --- a/src/app/zCoba/text_editor/page.tsx +++ /dev/null @@ -1,140 +0,0 @@ -"use client"; - -import { useEditor, EditorContent } from "@tiptap/react"; -import StarterKit from "@tiptap/starter-kit"; -import Image from "@tiptap/extension-image"; -import { useState } from "react"; -import { - Box, - Button, - Group, - Image as MantineImage, - Stack, - Text, -} from "@mantine/core"; -import Underline from "@tiptap/extension-underline"; -import { MainColor } from "@/app_modules/_global/color"; - -const listStiker = [ - { - id: 2, - name: "stiker2", - url: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQN9AKmsBY4yqdn3GueJJEVPJbfmf853gDL4cN8uc9eqsCTiJ1fzhcpywzVP68NCJEA5NQ&usqp=CAU", - }, - { - id: 3, - name: "stiker3", - url: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS2lkV3ZiQ8m-OELSui2JGVy80vnh1cyRUV7NrgFNluPVVs2HUAyCHwCMAKGe2s5jk2sn8&usqp=CAU", - }, - { - id: 4, - name: "stiker4", - url: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQHy9ZdsPc6dHgVTl5yIGpRJ-KtpTIsXA2_kbfO1Oc-pv_f7CNKGxhO56RjKujE3xCyb9k&usqp=CAU", - }, -]; - -export default function RichTextWithStickers() { - const [chat, setChat] = useState([]); - - const editor = useEditor({ - extensions: [ - StarterKit, // Sudah include Bold, Italic, dll - Underline, // Tambahan untuk underline - Image, - ], - content: "", - }); - - const insertSticker = (url: string) => { - editor?.chain().focus().setImage({ src: url }).run(); - }; - - return ( - - Tiptap Editor dengan Stiker Inline - - - - - - - - - - - - - - {listStiker.map((item) => ( - insertSticker(item.url)} - style={{ - border: "none", - background: "transparent", - cursor: "pointer", - }} - > - - - ))} - - - {/* - {chat.map((item, index) => ( - - ))} - */} - - ); -} diff --git a/src/app/zCoba/text_editor2/page.tsx b/src/app/zCoba/text_editor2/page.tsx deleted file mode 100644 index 580423c3..00000000 --- a/src/app/zCoba/text_editor2/page.tsx +++ /dev/null @@ -1,210 +0,0 @@ -"use client"; -import React, { useState, useEffect } from "react"; -import { - Box, - Button, - Group, - Image, - Paper, - ScrollArea, - SimpleGrid, - Stack, - Text, - Tooltip, - Modal, -} from "@mantine/core"; -import { useDisclosure } from "@mantine/hooks"; -import dynamic from "next/dynamic"; -import { MainColor } from "@/app_modules/_global/color"; -import { listStiker } from "@/app_modules/_global/lib/stiker"; - -// Dynamic import ReactQuill dengan SSR disabled -const ReactQuill = dynamic( - async () => { - const { default: RQ } = await import("react-quill"); - // Tidak perlu import CSS dengan import statement - return function comp({ forwardedRef, ...props }: any) { - return ; - }; - }, - { ssr: false, loading: () =>

Loading Editor...

} -); - - -type ChatItem = { - content: string; // HTML content including text and stickers -}; - -export default function Page() { - const [editorContent, setEditorContent] = useState(""); - const [chat, setChat] = useState([]); - const [opened, { open, close }] = useDisclosure(false); - const quillRef = React.useRef(null); - const [quillLoaded, setQuillLoaded] = useState(false); - - // Load CSS on client-side only - useEffect(() => { - // Add Quill CSS via tag - const link = document.createElement("link"); - link.href = "https://cdn.quilljs.com/1.3.6/quill.snow.css"; - link.rel = "stylesheet"; - document.head.appendChild(link); - - // Add custom style for stickers inside Quill editor - const style = document.createElement("style"); - style.textContent = ` - .ql-editor img { - max-width: 100px !important; - max-height: 100px !important; - } - .chat-content img { - max-width: 70px !important; - max-height: 70px !important; - } - `; - document.head.appendChild(style); - - setQuillLoaded(true); - - return () => { - // Clean up when component unmounts - document.head.removeChild(link); - document.head.removeChild(style); - }; - }, []); - - // Custom toolbar options for ReactQuill - const modules = { - toolbar: [ - [{ header: [1, 2, false] }], - ["bold", "italic", "underline", "strike", "blockquote"], - [{ list: "ordered" }, { list: "bullet" }], - ["link", "image"], - ["clean"], - ], - }; - - const formats = [ - "header", - "bold", - "italic", - "underline", - "strike", - "blockquote", - "list", - "bullet", - "link", - "image", - ]; - - const insertSticker = (stickerUrl: string) => { - if (!quillRef.current) return; - - const quill = quillRef.current.getEditor(); - const range = quill.getSelection(true); - - // Custom image insertion with size - // Use custom blot or HTML string with size attributes - const stickerHtml = `sticker`; - - // Insert HTML at cursor position - quill.clipboard.dangerouslyPasteHTML(range.index, stickerHtml); - - // Move cursor after inserted sticker - quill.setSelection(range.index + 1, 0); - - // Focus back on editor - quill.focus(); - - // Close sticker modal - close(); - }; - - // Function to send message - const sendMessage = () => { - if (editorContent.trim() !== "") { - setChat((prev) => [...prev, { content: editorContent }]); - setEditorContent(""); // Clear after sending - } - }; - - return ( - - - - - - {chat.map((item, index) => ( - -
- - ))} - - - - - - Chat Preview Data: - - -
-              {JSON.stringify(chat, null, 2)}
-            
-
-
- - - - - {quillLoaded && ( - - )} - - - - - - - - - - {/* Sticker Modal */} - - - {listStiker.map((item) => ( - - - {item.name} insertSticker(item.url)} - /> - - - ))} - - - - ); -} diff --git a/src/app/zCoba/upload/page.tsx b/src/app/zCoba/upload/page.tsx deleted file mode 100644 index 34ee4314..00000000 --- a/src/app/zCoba/upload/page.tsx +++ /dev/null @@ -1,133 +0,0 @@ -"use client"; - -import { MainColor } from "@/app_modules/_global/color"; -import { ComponentGlobal_BoxUploadImage } from "@/app_modules/_global/component"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; -import { - UIGlobal_LayoutHeaderTamplate, - UIGlobal_LayoutTamplate, -} from "@/app_modules/_global/ui"; -import { clientLogger } from "@/util/clientLogger"; -import { - AspectRatio, - Button, - Center, - FileButton, - Image, - Stack, -} from "@mantine/core"; -import { IconImageInPicture, IconUpload } from "@tabler/icons-react"; -import { useState } from "react"; - -export default function Page() { - return ( - <> - } - > - - - - ); -} - -function Upload() { - const [file, setFile] = useState(null); - const [image, setImage] = useState(null); - const [isLoading, setLoading] = useState(false); - - async function onUpload() { - if (!file) return alert("File Kosong"); - try { - setLoading(true); - const formData = new FormData(); - formData.append("file", file as File); - - const uploadPhoto = await funGlobal_UploadToStorage({ - file: file, - dirId: "cm5ohsepe002bq4nlxeejhg7q", - }); - - if (uploadPhoto.success) { - setLoading(false); - alert("berhasil upload"); - console.log("uploadPhoto", uploadPhoto); - } else { - setLoading(false); - console.log("gagal upload", uploadPhoto); - } - } catch (error) { - console.error("Error upload img:", error); - } - } - - return ( - <> - - - {image ? ( - - Avatar - - ) : ( -
- -
- )} -
- -
- { - try { - const buffer = URL.createObjectURL( - new Blob([new Uint8Array(await files.arrayBuffer())]) - ); - - // if (files.size > MAX_SIZE) { - // ComponentGlobal_NotifikasiPeringatan( - // PemberitahuanMaksimalFile - // ); - // return; - // } else { - - // } - - console.log("ini buffer", buffer); - - setFile(files); - setImage(buffer); - } catch (error) { - clientLogger.error("Upload error:", error); - } - }} - accept="image/png,image/jpeg" - > - {(props) => ( - - )} - -
- - -
- - ); -} diff --git a/src/app/zz-makuro/LoadDataContoh.tsx b/src/app/zz-makuro/LoadDataContoh.tsx deleted file mode 100644 index adf8a237..00000000 --- a/src/app/zz-makuro/LoadDataContoh.tsx +++ /dev/null @@ -1,30 +0,0 @@ -"use client"; -import { Stack } from "@mantine/core"; -import useSwr from "swr"; - -const fether = (url: string) => - fetch("https://jsonplaceholder.typicode.com" + url, { - cache: "force-cache", - next: { - revalidate: 60, - }, - }).then((res) => res.json()); - -export default function LoadDataContoh() { - const { data, isLoading, error, mutate, isValidating } = useSwr( - "/posts/1", - fether, - { - revalidateOnFocus: false, - revalidateOnReconnect: false, - refreshInterval: 1000, - } - ); - return ( - - {isLoading &&
Loading...
} - LoadDataContoh - {JSON.stringify(data, null, 2)} -
- ); -} diff --git a/src/app/zz-makuro/get-data-example.ts b/src/app/zz-makuro/get-data-example.ts deleted file mode 100644 index d52d9586..00000000 --- a/src/app/zz-makuro/get-data-example.ts +++ /dev/null @@ -1,9 +0,0 @@ -async function getDataExample() { - const res = await fetch("https://jsonplaceholder.typicode.com/posts", { - next: { - revalidate: 60, - }, - }); - - return res.json(); -} diff --git a/src/app/zz-makuro/page.tsx b/src/app/zz-makuro/page.tsx deleted file mode 100644 index a1df80ba..00000000 --- a/src/app/zz-makuro/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Suspense } from "react"; -import LoadDataContoh from "./LoadDataContoh"; - -const listMenu = [ - { - name: "Dashboard", - url: "/dashboard", - icon: "dashboard", - }, - { - name: "Event", - url: "/event", - icon: "event", - }, - { - name: "Donasi", - url: "/donasi", - icon: "donasi", - }, -]; - -const fether = async (url: string) => - fetch("https://jsonplaceholder.typicode.com" + url, { - next: { - revalidate: 2, - }, - }).then(async (res) => { - const data = await res.json(); - // console.log(data); - return data; - }); - -export default async function Page() { - const data = await fether("/posts/1"); - - return ( -
- {listMenu.map((item) => { - return ( - - ); - })} - {/* */} - Loading...
}> - {JSON.stringify(data, null, 2)} - -
- ); -}