diff --git a/app/[locale]/(protected)/admin/add-experts/component/table.tsx b/app/[locale]/(protected)/admin/add-experts/component/table.tsx index d3f08916..57be718c 100644 --- a/app/[locale]/(protected)/admin/add-experts/component/table.tsx +++ b/app/[locale]/(protected)/admin/add-experts/component/table.tsx @@ -201,7 +201,7 @@ const AddExpertTable = () => { } return ( -
+

Tenaga Ahli

diff --git a/app/[locale]/(protected)/admin/add-experts/create/page.tsx b/app/[locale]/(protected)/admin/add-experts/create/page.tsx index 3645c52a..f512cba9 100644 --- a/app/[locale]/(protected)/admin/add-experts/create/page.tsx +++ b/app/[locale]/(protected)/admin/add-experts/create/page.tsx @@ -268,7 +268,7 @@ export default function AddExpertForm() {

Campaign

- - + + KONTEN YANG DISIMPAN OLEH PENGGUNA POLRI INDONESIA @@ -161,8 +161,8 @@ export default function ContentManagement() { - - + + PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA @@ -189,8 +189,8 @@ export default function ContentManagement() { - - + + KONTEN YANG DISIMPAN OLEH PENGGUNA JURNALIS INTERNASIONAL @@ -217,8 +217,8 @@ export default function ContentManagement() { - - + + PENAMBAHAN JUMLAH PENGGUNA POLRI INDONESIA @@ -245,8 +245,8 @@ export default function ContentManagement() { - - + + PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA @@ -273,8 +273,8 @@ export default function ContentManagement() { - - + + PENAMBAHAN JUMLAH PENGGUNA JURNALIS INTERNASIONAL diff --git a/app/[locale]/(protected)/admin/analysis/emergency-issue/page.tsx b/app/[locale]/(protected)/admin/analysis/emergency-issue/page.tsx index 9fd67f21..80a383d0 100644 --- a/app/[locale]/(protected)/admin/analysis/emergency-issue/page.tsx +++ b/app/[locale]/(protected)/admin/analysis/emergency-issue/page.tsx @@ -88,8 +88,8 @@ export default function EmergencyIssue() { collapsible className="w-full" > - - + + ANALISA BERKAITAN DENGAN AKUN PELAPOR{" "} diff --git a/app/[locale]/(protected)/admin/analysis/feedback-center/page.tsx b/app/[locale]/(protected)/admin/analysis/feedback-center/page.tsx index 1e0b0fcc..64210506 100644 --- a/app/[locale]/(protected)/admin/analysis/feedback-center/page.tsx +++ b/app/[locale]/(protected)/admin/analysis/feedback-center/page.tsx @@ -89,8 +89,8 @@ export default function FeedbackCenter() { collapsible className="w-full" > - - + + TICKET PADA FEEDBACK CENTER{" "} diff --git a/app/[locale]/(protected)/admin/analysis/schedule/page.tsx b/app/[locale]/(protected)/admin/analysis/schedule/page.tsx index 342c2298..9049a3b8 100644 --- a/app/[locale]/(protected)/admin/analysis/schedule/page.tsx +++ b/app/[locale]/(protected)/admin/analysis/schedule/page.tsx @@ -114,8 +114,8 @@ export default function ContentManagement() { collapsible className="w-full" > - - + + PUBLISH JADWAL PRESS CONFERENCE TERBANYAK @@ -147,8 +147,8 @@ export default function ContentManagement() { collapsible className="w-full" > - - + + JUMLAH PRODUKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE @@ -180,8 +180,8 @@ export default function ContentManagement() { collapsible className="w-full" > - - + + TINGKAT INTERAKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE @@ -213,8 +213,8 @@ export default function ContentManagement() { collapsible className="w-full" > - - + + AKTIFITAS MEDIA BERKAITAN DENGAN PERS RILIS diff --git a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx index e21af944..f8dcfd54 100644 --- a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx +++ b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/column.tsx @@ -30,34 +30,27 @@ const columns: ColumnDef[] = [ accessorKey: "accountName", header: "Nama", cell: ({ row }) => ( - {row.getValue("accountName")} + {row.original.mediaBlastAccountName} ), }, { accessorKey: "accountType", header: "Tipe Akun", cell: ({ row }) => ( - {row.getValue("accountType")} - ), - }, - { - accessorKey: "accountCategory", - header: "Kategory", - cell: ({ row }) => ( - {row.getValue("accountCategory")} + {row.original.mediaBlastAccountType} ), }, { accessorKey: "emailAddress", header: "Email", cell: ({ row }) => ( - {row.getValue("emailAddress")} + {row.original.mediaBlastAccountEmail} ), }, { accessorKey: "whatsappNumber", header: "Whatsapp", - cell: ({ row }) => {row.getValue("whatsappNumber")}, + cell: ({ row }) => {row.original.mediaBlastAccountPhone}, }, { id: "actions", diff --git a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx index 1cea4616..434d6061 100644 --- a/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx +++ b/app/[locale]/(protected)/admin/broadcast/campaign-list/account-list/component/table.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { - ColumnDef, ColumnFiltersState, PaginationState, SortingState, @@ -15,7 +14,6 @@ import { useReactTable, } from "@tanstack/react-table"; import { Button } from "@/components/ui/button"; - import { Table, TableBody, @@ -24,25 +22,60 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { UserIcon } from "lucide-react"; - -import { useRouter, useSearchParams } from "next/navigation"; -import TablePagination from "@/components/table/table-pagination"; -import columns from "./column"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogClose, +} from "@/components/ui/dialog"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Label } from "@/components/ui/label"; +import { + Select as UISelect, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast"; import { Checkbox } from "@/components/ui/checkbox"; -import { close, loading } from "@/config/swal"; -import { Link } from "@/i18n/routing"; -import { Icon } from "@iconify/react/dist/iconify.js"; +import { Icon } from "@iconify/react"; +import { useParams, useSearchParams } from "next/navigation"; +import { Badge } from "@/components/ui/badge"; +import { X } from "lucide-react"; +import ReactSelect from "react-select"; + +import columns from "./column"; +import TablePagination from "@/components/table/table-pagination"; +import { + getMediaBlastCampaignAccountList, + deleteMediaBlastCampaignAccount, + saveMediaBlastCampaignAccountBulk, +} from "@/service/broadcast/broadcast"; +import { AdministrationUserList } from "@/service/management-user/management-user"; +import { close, loading, error, success, successCallback } from "@/config/swal"; + +// Mock data for available accounts - replace with actual API call +const availableAccounts = [ + { id: "1", accountName: "Account 1", category: "polri" }, + { id: "2", accountName: "Account 2", category: "jurnalis" }, + { id: "3", accountName: "Account 3", category: "umum" }, + { id: "4", accountName: "Account 4", category: "ksp" }, + { id: "5", accountName: "Account 5", category: "polri" }, +]; const AccountListTable = () => { - const router = useRouter(); + const params = useParams(); const searchParams = useSearchParams(); + const campaignId = params?.id as string; + const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); @@ -56,10 +89,18 @@ const AccountListTable = () => { pageIndex: 0, pageSize: 10, }); - const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); const [filtered, setFiltered] = React.useState([]); + + // --- state utk Dialog Pilih Akun --- + const [isDialogOpen, setIsDialogOpen] = React.useState(false); + const [accountCategory, setAccountCategory] = React.useState(""); + const [selectedAccount, setSelectedAccount] = React.useState([]); + const [selectedCategory, setSelectedCategory] = React.useState(""); + const [availableAccountsList, setAvailableAccountsList] = React.useState(availableAccounts); + const [usersList, setUsersList] = React.useState([]); + const table = useReactTable({ data: dataTable, columns, @@ -83,24 +124,24 @@ const AccountListTable = () => { React.useEffect(() => { const pageFromUrl = searchParams?.get("page"); - if (pageFromUrl) { - setPage(Number(pageFromUrl)); - } + if (pageFromUrl) setPage(Number(pageFromUrl)); }, [searchParams]); React.useEffect(() => { fetchData(); - }, [page]); + }, [page, filtered]); async function fetchData() { try { loading(); - const res = await getMediaBlastAccountPage( + const res = await getMediaBlastCampaignAccountList( page - 1, - filtered ? filtered.join(",") : "" + filtered ? filtered.join(",") : "", + campaignId ); + const data = res?.data?.data; - const contentData = data?.content; + const contentData = data?.content || []; contentData.forEach((item: any, index: number) => { item.no = (page - 1) * 10 + index + 1; }); @@ -109,41 +150,304 @@ const AccountListTable = () => { setTotalData(data?.totalElements); setTotalPage(data?.totalPages); close(); - } catch (error) { - console.error("Error fetching tasks:", error); + } catch (err) { + console.error("Error fetching tasks:", err); + close(); } } + // --- API helpers --- + async function doDeleteAccount(id: string) { + loading(); + const response = await deleteMediaBlastCampaignAccount(id); + close(); + if (response?.error) { + error(response.message); + return; + } + fetchData(); + } + + async function saveCampaignAccount() { + try { + loading(); + + if (accountCategory === "all-account") { + // Handle all accounts - send only campaignId and category "all" + const request = { + mediaBlastCampaignId: campaignId, + mediaBlastAccountCategory: "all", + }; + const response = await saveMediaBlastCampaignAccountBulk(request); + if (response?.error) { + error(response.message); + return; + } + } else if (accountCategory === "kategori" && selectedCategory) { + // Handle category selection - send campaignId and role-based category + let roleId = ""; + switch (selectedCategory) { + case "umum": + roleId = "5"; + break; + case "jurnalis": + roleId = "6"; + break; + case "polri": + roleId = "7"; + break; + case "ksp": + roleId = "8"; + break; + default: + roleId = "5"; + } + + const request = { + mediaBlastCampaignId: campaignId, + mediaBlastAccountCategory: `role-${roleId}`, + }; + const response = await saveMediaBlastCampaignAccountBulk(request); + if (response?.error) { + error(response.message); + return; + } + } else if (accountCategory === "custom") { + // Handle custom selection - send campaignId and selected user IDs + const request = { + mediaBlastCampaignId: campaignId, + mediaBlastAccountIds: selectedAccount.map(acc => acc.id), + }; + const response = await saveMediaBlastCampaignAccountBulk(request); + if (response?.error) { + error(response.message); + return; + } + } + + close(); + successCallback("Akun berhasil ditambahkan ke campaign!"); + resetDialogState(); + fetchData(); + } catch (err) { + close(); + error("Terjadi kesalahan saat menyimpan akun"); + } + } + + const resetDialogState = () => { + setAccountCategory(""); + setSelectedAccount([]); + setSelectedCategory(""); + setUsersList([]); + setIsDialogOpen(false); + }; + + const fetchUsersList = async () => { + try { + loading(); + const response = await AdministrationUserList( + "1", // levelId + 0, // page + "", // name + "100", // size + "1", // featureId + "" // role + ); + + if (response?.data?.data?.content) { + setUsersList(response.data.data.content); + } + close(); + } catch (err) { + close(); + error("Terjadi kesalahan saat mengambil daftar user"); + } + }; + const handleFilter = (id: string, checked: boolean) => { let temp = [...filtered]; - if (checked) { - temp = [...temp, id]; - } else { - temp = temp.filter((a) => a !== id); - } + if (checked) temp = [...temp, id]; + else temp = temp.filter((a) => a !== id); setFiltered(temp); - // console.log("sss", temp); + }; + + + + const removeSelectedAccount = (accountId: string) => { + setSelectedAccount(selectedAccount.filter(acc => acc.id !== accountId)); + }; + + const getFilteredAccounts = () => { + if (accountCategory === "kategori" && selectedCategory) { + return availableAccountsList.filter(acc => acc.category === selectedCategory); + } + return availableAccountsList; }; return ( -
+

Daftar Akun

- - - - {/* - - */} + {/* === Dialog Pilih Akun === */} + + + + + + + Pilih Akun Untuk Campaign Ini + + +
+ { + setAccountCategory(val); + setSelectedAccount([]); + setSelectedCategory(""); + if (val === "custom") { + fetchUsersList(); + } + }} + className="flex space-x-6" + > +
+ + +
+
+ + +
+
+ + +
+
+ + {/* Category Selection */} + {accountCategory === "kategori" && ( +
+ + setSelectedCategory(val)}> + + + + + Umum + Polri + KSP + Jurnalis + + +
+ )} + + {/* Custom Account Selection */} + {accountCategory === "custom" && ( +
+ + ({ + value: user.id, + label: `${user.fullname} (${user.role?.name})`, + user: user + }))} + value={selectedAccount.map((acc: any) => ({ + value: acc.id, + label: `${acc.fullname} (${acc.role?.name})`, + user: acc + }))} + onChange={(selectedOptions: any) => { + const selectedUsers = selectedOptions ? selectedOptions.map((option: any) => option.user) : []; + setSelectedAccount(selectedUsers); + }} + placeholder="Cari dan pilih user..." + noOptionsMessage={() => "Tidak ada user ditemukan"} + loadingMessage={() => "Memuat..."} + isSearchable={true} + isClearable={true} + className="react-select" + classNamePrefix="select" + /> + + {/* Selected Accounts Display */} + {selectedAccount.length > 0 && ( +
+ +
+ {selectedAccount.map((acc) => ( + + {acc.fullname} + removeSelectedAccount(acc.id)} + /> + + ))} +
+
+ )} +
+ )} + + {/* All Accounts Info */} + {accountCategory === "all-account" && ( +
+

+ Semua akun akan ditambahkan ke campaign ini. +

+
+ )} + + {/* Category Accounts Info */} + {accountCategory === "kategori" && selectedCategory && ( +
+

+ Semua akun dengan role "{selectedCategory.toUpperCase()}" akan ditambahkan. +

+
+ )} + + {/* Custom Selection Info */} + {accountCategory === "custom" && ( +
+

+ {selectedAccount.length} user terpilih akan ditambahkan ke campaign ini. +

+
+ )} +
+ + + + + + + +
+
+ + {/* === Filter Akun === */}
@@ -163,65 +467,28 @@ const AccountListTable = () => {
-
- handleFilter("polri", Boolean(e))} - /> - -
-
- - handleFilter("jurnalis", Boolean(e)) - } - /> - -
-
- handleFilter("umum", Boolean(e))} - /> - -
-
- handleFilter("ksp", Boolean(e))} - /> - -
+ {["polri", "jurnalis", "umum", "ksp"].map((cat) => ( +
+ handleFilter(cat, Boolean(e))} + /> + +
+ ))}
+ + {/* === Table Data === */} {table.getHeaderGroups().map((headerGroup) => ( @@ -263,6 +530,7 @@ const AccountListTable = () => { )}
+ { } return ( -
+

Daftar Campaign

diff --git a/app/[locale]/(protected)/admin/broadcast/campaign-list/create/page.tsx b/app/[locale]/(protected)/admin/broadcast/campaign-list/create/page.tsx index 210c8241..a3c4f2f5 100644 --- a/app/[locale]/(protected)/admin/broadcast/campaign-list/create/page.tsx +++ b/app/[locale]/(protected)/admin/broadcast/campaign-list/create/page.tsx @@ -101,7 +101,7 @@ export default function CreateCampaign() {

Campaign

("sent"); const { page, size } = searchParams; - const [calenderState, setCalenderState] = useState(false); const [typeFilter, setTypeFilter] = useState("email"); const [dateRange, setDateRange] = useState<[Date, Date]>([ @@ -91,10 +90,8 @@ export default function BroadcastCampaignDetail({ new Date(), ]); const [startDate, endDate] = dateRange; - const [startDateString, setStartDateString] = useState(); const [endDateString, setEndDateString] = useState(); - // Table state const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -104,7 +101,6 @@ export default function BroadcastCampaignDetail({ pageIndex: 0, pageSize: parseInt(size || "10"), }); - const pages = page ? parseInt(page) - 1 : 0; const currentPage = page ? parseInt(page) : 1; const pageSize = parseInt(size || "10"); @@ -264,7 +260,7 @@ export default function BroadcastCampaignDetail({ }; return ( -
+
- - - - - 1 - 10 Data - - - 1 - 20 Data - - - 1 - 25 Data - - - 1 - 50 Data - - - - +
+ {/* Header */} +
+
+

+ Semua Ticket: {totalElements} +

- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ))} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - - - No results. - - - )} - -
- - - ); -}; -export default TicketingTable; +
+ {/* Sidebar */} + + + {/* Middle: Issue List */} +
+ {/* top controls: select all, search, sort */} +
+
+ +
+
+ setSearch(e.target.value)} + className="pl-10" + /> +
+ +
+
+
+ +
+ + + + + + + setSortOrder(value as "latest" | "oldest") + } + > + + Latest + + + Oldest + + + + +
+ +
+ + + + + +
+ Show +
+ { + setPageSize(Number(v)); + setPage(1); + }} + > + + 10 + + + 20 + + + 25 + + + 50 + + +
+
+
+
+
+ + {/* list */} +
+
    + {issues.length === 0 ? ( +
  • + No issues +
  • + ) : ( + issues.map((it) => { + const key = String(it.id); + const src = sourceToIconLabel(it.source); + return ( +
  • { + setSelectedIssue(it); + setSelectedTicketId(String(it.id)); // ✅ ini yang bikin detail muncul + }} + className={cn( + "flex items-start gap-3 px-4 py-3 border-b cursor-pointer hover:bg-gray-50", + selectedIssue?.id === it.id ? "bg-gray-100" : "" + )} + > +
    + { + e.stopPropagation(); + toggleSelectOne(it.id); + }} + onClick={(e) => e.stopPropagation()} + className="h-4 w-4 mt-1" + /> +
    + +
    +
    +
    + + {src.short} + +
    +
    + {(it.title ?? "") + .split(" ") + .slice(0, 25) + .join(" ") + + ((it.title ?? "").split(" ").length > 25 + ? "..." + : "")} +
    + +
    + {src.label} + + {it.timeAgo ?? "—"} +
    +
    +
    + +
    + New Issue +
    + {it.timeAgo} +
    +
    +
    +
    +
  • + ); + }) + )} +
+
+ + {/* pagination simple */} +
+
+ {`1-${pageSize} of ${totalElements}`} +
+
+ +
+ {page} / {totalPages} +
+ +
+
+
+ + {/* Right: Chat Panel */} +
+ {selectedTicketId ? ( + + ) : ( +
+ Pilih ticket dari sebelah kiri +
+ )} +
+ {/*
+
+
+
+ {selectedIssue ? ( +
+
+ {(selectedIssue?.title ?? "") + .split(" ") + .slice(0, 25) + .join(" ") + + ((selectedIssue?.title ?? "").split(" ").length > 25 + ? "..." + : "")} +
+ +
+ • {selectedIssue.source} +
+
+ ) : ( +
Chat
+ )} +
+
+
+ +
+ {!selectedIssue ? ( +
+
+
+ + + +
+
+ Pilih issue untuk melihat detail +
+
+
+ ) : ( +
+ {chatMessages.map((m) => ( +
+
+ {m.text} +
+
+ {m.from === "agent" ? "Anda" : "User"} + + {m.time} +
+
+ ))} +
+ )} +
+ +
+
+
+ + + + {errors.message && ( +

{errors.message.message}

+ )}
- - +
); diff --git a/app/[locale]/globals.css b/app/[locale]/globals.css index 688a4f26..25750c7f 100644 --- a/app/[locale]/globals.css +++ b/app/[locale]/globals.css @@ -575,10 +575,108 @@ html[dir="rtl"] .react-select .select__loading-indicator { background: #9ca3af; } +/* CKEditor Styling */ .ck-editor__editable_inline { min-height: 200px; } +/* Main CKEditor content area styling */ +.ck.ck-editor__editable { + padding: 1.5em 2em !important; + min-height: 400px; + max-height: 600px; + line-height: 1.6; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #cbd5e1 #f1f5f9; +} + +/* CKEditor content styling */ +.ck.ck-editor__editable .ck-content { + padding: 0; +} + +/* CKEditor scrollbar styling */ +.ck.ck-editor__editable::-webkit-scrollbar { + width: 8px; +} + +.ck.ck-editor__editable::-webkit-scrollbar-track { + background: #f1f5f9; + border-radius: 4px; +} + +.ck.ck-editor__editable::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 4px; +} + +.ck.ck-editor__editable::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* CKEditor editable area focus state */ +.ck.ck-editor__editable.ck-focused { + border-color: #1a9aef; + box-shadow: 0 0 0 2px rgba(26, 154, 239, 0.2); +} + +/* CKEditor toolbar styling */ +.ck.ck-toolbar { + border-radius: 4px 4px 0 0; +} + +/* CKEditor editable border styling */ +.ck.ck-editor__editable { + border-radius: 0 0 4px 4px; + border: 1px solid #d1d5db; +} + +/* CKEditor content typography */ +.ck.ck-editor__editable p { + margin: 0.5em 0; +} + +/* View Editor specific styling (read-only mode) */ +.ckeditor-view-wrapper .ck.ck-editor__editable { + background-color: #f8fafc !important; + color: #4b5563 !important; + cursor: default !important; + border: 1px solid #d1d5db !important; + border-radius: 6px !important; +} + +.ckeditor-view-wrapper .ck.ck-editor__editable.ck-focused { + border-color: #d1d5db !important; + box-shadow: none !important; +} + +.ckeditor-view-wrapper .ck.ck-toolbar { + display: none !important; +} + +.ck.ck-editor__editable h1, +.ck.ck-editor__editable h2, +.ck.ck-editor__editable h3, +.ck.ck-editor__editable h4, +.ck.ck-editor__editable h5, +.ck.ck-editor__editable h6 { + margin: 1em 0 0.5em 0; +} + +.ck.ck-editor__editable ul, +.ck.ck-editor__editable ol { + margin: 0.5em 0; + padding-left: 2em; +} + +.ck.ck-editor__editable blockquote { + margin: 1em 0; + padding: 0.5em 1em; + border-left: 4px solid #d1d5db; + background-color: #f9fafb; +} + /* Hide FullCalendar grid elements */ .fc-view-harness:has(.hide-calendar-grid) { display: none; diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 67ff5946..3204db83 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import "./theme.css"; +import "../../style/ckeditor.css"; import { ThemeProvider } from "@/providers/theme-provider"; import MountedProvider from "@/providers/mounted.provider"; import { Toaster } from "@/components/ui/toaster"; diff --git a/components/editor/custom-editor.js b/components/editor/custom-editor.js index b248cf54..51204d80 100644 --- a/components/editor/custom-editor.js +++ b/components/editor/custom-editor.js @@ -5,79 +5,166 @@ import { CKEditor } from "@ckeditor/ckeditor5-react"; import Editor from "ckeditor5-custom-build"; function CustomEditor(props) { - const editorRef = useRef(null); - const [isEditorReady, setIsEditorReady] = useState(false); - const [currentContent, setCurrentContent] = useState(props.initialData || ""); - - // Handle editor initialization - const handleInit = useCallback((evt, editor) => { - editorRef.current = editor; - setIsEditorReady(true); - - // Set initial content immediately when editor is ready - if (currentContent) { - editor.setContent(currentContent); - } - - // Simple onChange handler - editor.on('change', () => { - const content = editor.getContent(); - setCurrentContent(content); - if (props.onChange) { - props.onChange(content); - } - }); - }, [currentContent, props.onChange]); - - // Watch for changes in initialData prop - useEffect(() => { - if (props.initialData !== currentContent) { - setCurrentContent(props.initialData || ""); - - // Update editor content if editor is ready - if (editorRef.current && isEditorReady) { - editorRef.current.setContent(props.initialData || ""); - } - } - }, [props.initialData, currentContent, isEditorReady]); - - // Handle initial data when editor becomes ready - useEffect(() => { - if (isEditorReady && currentContent && editorRef.current) { - editorRef.current.setContent(currentContent); - } - }, [isEditorReady, currentContent]); + const maxHeight = props.maxHeight || 600; return ( - { - const data = editor.getData(); - // console.log({ event, editor, data }); - props.onChange(data); - }} - config={{ - toolbar: [ - "heading", - "fontsize", - "bold", - "italic", - "link", - "numberedList", - "bulletedList", - "undo", - "redo", - "alignment", - "outdent", - "indent", - "blockQuote", - "insertTable", - "codeBlock", - "sourceEditing", - ], - }} - /> +
+ { + const data = editor.getData(); + console.log({ event, editor, data }); + props.onChange(data); + }} + config={{ + toolbar: [ + "heading", + "fontsize", + "bold", + "italic", + "link", + "numberedList", + "bulletedList", + "undo", + "redo", + "alignment", + "outdent", + "indent", + "blockQuote", + "insertTable", + "codeBlock", + "sourceEditing", + ], + content_style: ` + body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-size: 14px; + line-height: 1.6; + color: #111 !important; + background: #fff !important; + margin: 0; + padding: 1rem; + } + p { + margin: 0.5em 0; + } + h1, h2, h3, h4, h5, h6 { + margin: 1em 0 0.5em 0; + color: inherit !important; + } + ul, ol { + margin: 0.5em 0; + padding-left: 2em; + } + blockquote { + margin: 1em 0; + padding: 0.5em 1em; + border-left: 4px solid #d1d5db; + background-color: #f9fafb; + color: inherit !important; + } + `, + height: props.height || 400, + removePlugins: ["Title"], + mobile: { + theme: "silver", + }, + }} + /> + +
); } diff --git a/components/editor/view-editor.js b/components/editor/view-editor.js index c2529003..4b0cdff3 100644 --- a/components/editor/view-editor.js +++ b/components/editor/view-editor.js @@ -3,17 +3,261 @@ import { CKEditor } from "@ckeditor/ckeditor5-react"; import Editor from "ckeditor5-custom-build"; function ViewEditor(props) { + const maxHeight = props.maxHeight || 600; // Default max height 600px + return ( - +
+ + +
); } export default ViewEditor; + +// import React from "react"; +// import { CKEditor } from "@ckeditor/ckeditor5-react"; +// import Editor from "ckeditor5-custom-build"; + +// function ViewEditor(props) { +// const maxHeight = props.maxHeight || 600; + +// return ( +//
+// +// +//
+// ); +// } + +// export default ViewEditor; diff --git a/components/form/blog/blog-form.tsx b/components/form/blog/blog-form.tsx index f81a7e16..2ee3b01d 100644 --- a/components/form/blog/blog-form.tsx +++ b/components/form/blog/blog-form.tsx @@ -32,6 +32,7 @@ import { postBlog, uploadThumbnailBlog } from "@/service/blog/blog"; import dynamic from "next/dynamic"; import { error } from "console"; import { loading } from "@/lib/swal"; +import { getCookiesDecrypt } from "@/lib/utils"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -92,6 +93,7 @@ export default function FormBlog() { const [thumbnail, setThumbnail] = useState(null); const [preview, setPreview] = useState(null); const inputRef = useRef(null); + const roleName = getCookiesDecrypt("urne"); const [unitSelection, setUnitSelection] = useState({ allUnit: false, @@ -180,6 +182,7 @@ export default function FormBlog() { const save = async (data: TaskSchema) => { loading(); + console.log("roleName", roleName); const finalTags = tags.join(", "); const requestData = { ...data, @@ -190,6 +193,7 @@ export default function FormBlog() { metadata: data.meta, tags: finalTags, isDraft, + isInternational: roleName?.includes("INT") ? true : false, }; const response = await postBlog(requestData); diff --git a/components/form/broadcast/content-blast-form.tsx b/components/form/broadcast/content-blast-form.tsx index 0806dd5a..5f5af17b 100644 --- a/components/form/broadcast/content-blast-form.tsx +++ b/components/form/broadcast/content-blast-form.tsx @@ -290,7 +290,7 @@ export default function ContentBlast(props: { type: string }) { - Email Terkirim + Terkirim !!
{ - setIsSubmitting(true); + setIsSubmitting(true); MySwal.fire({ title: "Menyimpan...", @@ -192,7 +192,7 @@ export default function FormCollaboration() { // console.log("Form Data Submitted:", requestData); // console.log("response", response); - Swal.close(); + Swal.close(); MySwal.fire({ title: "Sukses", @@ -255,6 +255,78 @@ export default function FormCollaboration() { Priority* @@ -268,7 +340,7 @@ export default function FormCollaboration() { }), }} /> -
+
*/}
@@ -283,8 +355,82 @@ export default function FormCollaboration() { onChange={handleChange} formatOptionLabel={formatOptionLabel} isMulti={false} + styles={{ + control: (base, state) => ({ + ...base, + backgroundColor: + document.documentElement.classList.contains("dark") + ? "#1f2937" // bg-gray-800 + : "#ffffff", // bg-white + color: document.documentElement.classList.contains("dark") + ? "#f9fafb" // text-gray-100 + : "#111827", // text-gray-900 + borderColor: state.isFocused + ? "#2563eb" + : base.borderColor, + boxShadow: state.isFocused + ? "0 0 0 1px #2563eb" + : base.boxShadow, + "&:hover": { + borderColor: "#2563eb", + }, + }), + menu: (base) => ({ + ...base, + backgroundColor: + document.documentElement.classList.contains("dark") + ? "#1f2937" + : "#ffffff", + color: document.documentElement.classList.contains("dark") + ? "#f9fafb" + : "#111827", + }), + option: (base, state) => ({ + ...base, + backgroundColor: state.isSelected + ? "#2563eb" + : state.isFocused + ? "#2563eb33" + : document.documentElement.classList.contains("dark") + ? "#1f2937" + : "#ffffff", + color: state.isSelected + ? "#ffffff" + : document.documentElement.classList.contains("dark") + ? "#f9fafb" + : "#111827", + cursor: "pointer", + }), + singleValue: (base) => ({ + ...base, + color: document.documentElement.classList.contains("dark") + ? "#f9fafb" + : "#111827", + }), + placeholder: (base) => ({ + ...base, + color: document.documentElement.classList.contains("dark") + ? "#9ca3af" + : "#6b7280", + }), + }} />
+ + {/*
+ +