From 0ad1acee094d9758a4289b7becaacdb94d24e30d Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Fri, 9 Jan 2026 18:35:20 +0700 Subject: [PATCH 01/10] fix: button relevant in media tracking and table --- .../detail/component/column.tsx | 49 ++++++++++++++--- .../media-tracking/detail/component/table.tsx | 5 +- .../results/component/column.tsx | 38 ++++++------- .../agenda-setting/event-modal.tsx | 1 - service/media-tracking/media-tracking.ts | 53 +++++++++++-------- src/types/react-table.d.ts | 1 + 6 files changed, 98 insertions(+), 49 deletions(-) diff --git a/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx b/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx index 866579b5..a7094d30 100644 --- a/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/detail/component/column.tsx @@ -89,7 +89,8 @@ const columns: ColumnDef[] = [ cell: ({ row, table }) => { const original = row.original; - const isValid = original.isValid; + // const isValid = original.isValid; + const isRelevant = original.isRelevant; const link = original.link; const updateRow = (data: Partial) => { @@ -99,10 +100,10 @@ const columns: ColumnDef[] = [ const handleValid = async () => { try { await validateMediaLink(original.id, true); - updateRow({ - isValid: true, + isRelevant: true, }); + table.options.meta?.refetchData?.(); } catch (err: any) { toast.error(err.message); } @@ -113,8 +114,9 @@ const columns: ColumnDef[] = [ await validateMediaLink(original.id, false); updateRow({ - isValid: false, + isRelevant: false, }); + table.options.meta?.refetchData?.(); } catch (err: any) { toast.error(err.message); } @@ -124,24 +126,55 @@ const columns: ColumnDef[] = [ return -; } - if (isValid === true) { + if (isRelevant === true) { return ( ); } return (
- -
diff --git a/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx b/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx index fa0ae7c0..670d9269 100644 --- a/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/detail/component/table.tsx @@ -124,6 +124,9 @@ const NewsDetailTable = () => { ) ); }, + refetchData: () => { + fetchData(); + }, }, state: { sorting, @@ -163,7 +166,7 @@ const NewsDetailTable = () => { pageIndex: 0, pageSize: Number(showData), }); - }, [page, showData]); + }, [page, showData, id]); async function fetchData() { try { diff --git a/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx b/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx index a5aa31cb..bcee9b65 100644 --- a/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx +++ b/app/[locale]/(protected)/admin/media-tracking/results/component/column.tsx @@ -52,32 +52,34 @@ const columns: ColumnDef[] = [ header: "Judul", cell: ({ row }) => {row.getValue("title")}, }, - // { - // accessorKey: "resultTotal", - // header: () =>
Jumlah Amplifikasi
, - // cell: ({ row }) => { - // const value = row.getValue("resultTotal") as number | string | null; + { + accessorKey: "resultTotal", + header: () =>
Total Artikel
, + cell: ({ row }) => { + const value = row.getValue("resultTotal") as number | string | null; - // const finalValue = - // value === null || value === undefined || value === "" - // ? 0 - // : Number(value); + const finalValue = + value === null || value === undefined || value === "" + ? 0 + : Number(value); - // return
{finalValue}
; - // }, - // }, + return
{finalValue}
; + }, + }, { accessorKey: "amplification", header: () =>
Jumlah Amplifikasi
, cell: ({ row }) => { - const totalRaw = row.getValue("amplification") as number | string | null; + const raw = row.getValue("amplification") as string | null; - const total = - totalRaw === null || totalRaw === undefined || totalRaw === "" - ? 0 - : Number(totalRaw); + let total = 0; + let invalidTotal = 0; - const invalidTotal = 0; + if (raw && typeof raw === "string") { + const parts = raw.split("/").map((v) => v.trim()); + total = Number(parts[0]) || 0; + invalidTotal = Number(parts[1]) || 0; + } return (
diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 7382f25c..93e5c60d 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -894,7 +894,6 @@ const EventModal = ({ const resCsrf = await getCsrfToken(); const csrfToken = resCsrf?.data?.token; - console.log("CSRF TOKEN : ", csrfToken); const headers = { "X-XSRF-TOKEN": csrfToken, }; diff --git a/service/media-tracking/media-tracking.ts b/service/media-tracking/media-tracking.ts index e3874a3b..0841c6b0 100644 --- a/service/media-tracking/media-tracking.ts +++ b/service/media-tracking/media-tracking.ts @@ -2,6 +2,7 @@ import api from "@/src/lib/api"; import { httpGetInterceptor, httpPostInterceptor, + httpPutInterceptor, } from "../http-config/http-interceptor-service"; export async function getMediaTrackingMonitoring(page: number, size: number) { @@ -63,31 +64,41 @@ export async function listDataTracking( ); } - export async function listDataAllNonPagination(search: string) { return await httpGetInterceptor( `media/public/list?enablePage=0&sort=desc&title=${search || ""}` ); } -export const validateMediaLink = async ( - resultId: number, - isRelevant: boolean -) => { - try { - const res = await api.put( - "/media/tracking/monitoring/results/relevant", - { - resultId, - isRelevant, - } - ); +export async function validateMediaLink(resultId: number, isRelevant: boolean) { + const url = "media/tracking/monitoring/results/relevant"; - return res.data; - } catch (error: any) { - throw new Error( - error?.response?.data?.messages?.[0] || - "Gagal memperbarui status relevansi" - ); - } -}; \ No newline at end of file + const payload = { + resultId, + isRelevant, + }; + + return httpPutInterceptor(url, payload); +} + +// export const validateMediaLink = async ( +// resultId: number, +// isRelevant: boolean +// ) => { +// try { +// const res = await api.put( +// "/media/tracking/monitoring/results/relevant", +// { +// resultId, +// isRelevant, +// } +// ); + +// return res.data; +// } catch (error: any) { +// throw new Error( +// error?.response?.data?.messages?.[0] || +// "Gagal memperbarui status relevansi" +// ); +// } +// }; diff --git a/src/types/react-table.d.ts b/src/types/react-table.d.ts index 682a93a0..5e8e7154 100644 --- a/src/types/react-table.d.ts +++ b/src/types/react-table.d.ts @@ -4,5 +4,6 @@ import "@tanstack/react-table"; declare module "@tanstack/react-table" { interface TableMeta { updateData: (rowIndex: number, value: Partial) => void; + refetchData?: () => void; } } From f1b59ee537eb5dfc62222f4f2f77ff55ba945f7b Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Fri, 9 Jan 2026 18:47:26 +0700 Subject: [PATCH 02/10] fix: add xlsx and file-saver dependency --- package.json | 2 + pnpm-lock.yaml | 118 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 94678a27..3c2a656e 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "dayjs": "^1.11.11", "embla-carousel-autoplay": "^8.1.3", "embla-carousel-react": "^8.1.3", + "file-saver": "^2.0.5", "framer-motion": "^11.15.0", "geojson": "^0.5.0", "html-react-parser": "^5.2.0", @@ -146,6 +147,7 @@ "uuid": "^13.0.0", "vaul": "^0.9.1", "wavesurfer.js": "^7.8.16", + "xlsx": "^0.18.5", "yup": "^1.6.1", "zod": "^3.23.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dc44bd5..128be34a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@ckeditor/ckeditor5-react': specifier: ^6.2.0 - version: 6.3.0(@ckeditor/ckeditor5-core@41.3.1)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@41.3.1)(@ckeditor/ckeditor5-utils@41.3.1)(@ckeditor/ckeditor5-watchdog@41.3.1)(react@18.3.1) + version: 6.3.0(@ckeditor/ckeditor5-core@47.1.0)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@47.1.0)(@ckeditor/ckeditor5-utils@47.1.0)(@ckeditor/ckeditor5-watchdog@47.1.0)(react@18.3.1) '@dnd-kit/core': specifier: ^6.1.0 version: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -224,6 +224,9 @@ importers: embla-carousel-react: specifier: ^8.1.3 version: 8.6.0(react@18.3.1) + file-saver: + specifier: ^2.0.5 + version: 2.0.5 framer-motion: specifier: ^11.15.0 version: 11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -395,6 +398,9 @@ importers: wavesurfer.js: specifier: ^7.8.16 version: 7.11.0 + xlsx: + specifier: ^0.18.5 + version: 0.18.5 yup: specifier: ^1.6.1 version: 1.7.1 @@ -3033,6 +3039,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -3310,6 +3320,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + chalk@2.3.0: resolution: {integrity: sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==} engines: {node: '>=4'} @@ -3421,6 +3435,10 @@ packages: code-block-writer@12.0.0: resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + collect-v8-coverage@1.0.3: resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} @@ -3542,6 +3560,11 @@ packages: typescript: optional: true + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -4262,6 +4285,9 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + file-selector@2.1.2: resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==} engines: {node: '>= 12'} @@ -4331,6 +4357,10 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + framer-motion@11.18.2: resolution: {integrity: sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==} peerDependencies: @@ -6887,6 +6917,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -7554,10 +7588,18 @@ packages: engines: {node: '>= 8'} hasBin: true + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -7601,6 +7643,11 @@ packages: utf-8-validate: optional: true + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -7940,8 +7987,6 @@ snapshots: '@ckeditor/ckeditor5-core': 47.1.0 '@ckeditor/ckeditor5-upload': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-alignment@41.3.1': dependencies: @@ -8007,8 +8052,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-bookmark@47.1.0': dependencies: @@ -8076,8 +8119,6 @@ snapshots: '@ckeditor/ckeditor5-core': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-code-block@41.3.1': dependencies: @@ -8144,8 +8185,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-decoupled@47.1.0': dependencies: @@ -8155,8 +8194,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-inline@47.1.0': dependencies: @@ -8166,8 +8203,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-editor-multi-root@47.1.0': dependencies: @@ -8467,8 +8502,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 '@ckeditor/ckeditor5-widget': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-mention@47.1.0': dependencies: @@ -8527,13 +8560,13 @@ snapshots: '@ckeditor/ckeditor5-engine': 47.1.0 ckeditor5: 47.1.0 - '@ckeditor/ckeditor5-react@6.3.0(@ckeditor/ckeditor5-core@41.3.1)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@41.3.1)(@ckeditor/ckeditor5-utils@41.3.1)(@ckeditor/ckeditor5-watchdog@41.3.1)(react@18.3.1)': + '@ckeditor/ckeditor5-react@6.3.0(@ckeditor/ckeditor5-core@47.1.0)(@ckeditor/ckeditor5-editor-multi-root@47.1.0)(@ckeditor/ckeditor5-engine@47.1.0)(@ckeditor/ckeditor5-utils@47.1.0)(@ckeditor/ckeditor5-watchdog@47.1.0)(react@18.3.1)': dependencies: - '@ckeditor/ckeditor5-core': 41.3.1 + '@ckeditor/ckeditor5-core': 47.1.0 '@ckeditor/ckeditor5-editor-multi-root': 47.1.0 - '@ckeditor/ckeditor5-engine': 41.3.1 - '@ckeditor/ckeditor5-utils': 41.3.1 - '@ckeditor/ckeditor5-watchdog': 41.3.1 + '@ckeditor/ckeditor5-engine': 47.1.0 + '@ckeditor/ckeditor5-utils': 47.1.0 + '@ckeditor/ckeditor5-watchdog': 47.1.0 prop-types: 15.8.1 react: 18.3.1 @@ -8544,8 +8577,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-restricted-editing@47.1.0': dependencies: @@ -8593,8 +8624,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 47.1.0 '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-special-characters@47.1.0': dependencies: @@ -8761,8 +8790,6 @@ snapshots: '@ckeditor/ckeditor5-utils': 47.1.0 ckeditor5: 47.1.0 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@csstools/color-helpers@5.1.0': {} @@ -10900,6 +10927,8 @@ snapshots: acorn@8.15.0: {} + adler-32@1.3.1: {} + agent-base@7.1.4: {} ajv@6.12.6: @@ -11240,6 +11269,11 @@ snapshots: ccount@2.0.1: {} + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + chalk@2.3.0: dependencies: ansi-styles: 3.2.1 @@ -11443,6 +11477,8 @@ snapshots: code-block-writer@12.0.0: {} + codepage@1.15.0: {} + collect-v8-coverage@1.0.3: {} color-convert@1.9.3: @@ -11551,6 +11587,8 @@ snapshots: optionalDependencies: typescript: 5.9.3 + crc-32@1.2.2: {} + cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -12094,7 +12132,7 @@ snapshots: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -12124,7 +12162,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -12139,7 +12177,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -12460,6 +12498,8 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-saver@2.0.5: {} + file-selector@2.1.2: dependencies: tslib: 2.8.1 @@ -12530,6 +12570,8 @@ snapshots: forwarded@0.2.0: {} + frac@1.1.2: {} + framer-motion@11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: motion-dom: 11.18.1 @@ -16050,6 +16092,10 @@ snapshots: sprintf-js@1.0.3: {} + ssf@0.11.2: + dependencies: + frac: 1.1.2 + stable-hash@0.0.5: {} stack-utils@2.0.6: @@ -16867,8 +16913,12 @@ snapshots: dependencies: isexe: 2.0.0 + wmf@1.0.2: {} + word-wrap@1.2.5: {} + word@0.3.0: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -16898,6 +16948,16 @@ snapshots: ws@8.18.3: {} + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} From 5614b5ada51bd88808866cc070104270aa2a6ad2 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Fri, 9 Jan 2026 20:08:13 +0700 Subject: [PATCH 03/10] fixing --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8daffc34..150aeaee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,20 +18,20 @@ COPY package.json ./ COPY vendor/ckeditor5 ./vendor/ckeditor5 # Install dependencies -RUN pnpm install -# RUN pnpm install --frozen-lockfile +RUN npm install +# RUN npm install --frozen-lockfile # Menyalin source code aplikasi COPY . . # Build aplikasi -RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build +RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build # Expose port untuk server EXPOSE 3000 # Perintah untuk menjalankan aplikasi -CMD ["pnpm", "run", "start"] +CMD ["npm", "run", "start"] # # Gunakan base image Node.js Alpine yang ringan @@ -55,7 +55,7 @@ CMD ["pnpm", "run", "start"] # COPY vendor/ckeditor5 ./vendor/ckeditor5 # # Install dependencies -# RUN pnpm install --frozen-lockfile +# RUN npm install --frozen-lockfile # # Salin semua source code # COPY . . From ad5d048e78ce4306047a17752b800d63f68b6b6a Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Fri, 9 Jan 2026 20:27:20 +0700 Subject: [PATCH 04/10] fixing --- types/file-saver.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 types/file-saver.d.ts diff --git a/types/file-saver.d.ts b/types/file-saver.d.ts new file mode 100644 index 00000000..45edd694 --- /dev/null +++ b/types/file-saver.d.ts @@ -0,0 +1,7 @@ +declare module "file-saver" { + export function saveAs( + data: Blob | File | string, + filename?: string, + options?: any + ): void; +} From ec2d7eb203aa64bbe92887a09d0f07a7cd9ad679 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Fri, 9 Jan 2026 20:32:39 +0700 Subject: [PATCH 05/10] fixing --- types/file-saver.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 types/file-saver.d.ts diff --git a/types/file-saver.d.ts b/types/file-saver.d.ts new file mode 100644 index 00000000..45edd694 --- /dev/null +++ b/types/file-saver.d.ts @@ -0,0 +1,7 @@ +declare module "file-saver" { + export function saveAs( + data: Blob | File | string, + filename?: string, + options?: any + ): void; +} From 7c721dcc9696b87b5dafdc0f020441c1085adf01 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Mon, 12 Jan 2026 09:09:52 +0700 Subject: [PATCH 06/10] fix: form create task-ta --- components/form/task-ta/task-ta-form.tsx | 230 ++++++++++++++--------- lib/menus.ts | 7 + 2 files changed, 143 insertions(+), 94 deletions(-) diff --git a/components/form/task-ta/task-ta-form.tsx b/components/form/task-ta/task-ta-form.tsx index 2c0c217a..b9840320 100644 --- a/components/form/task-ta/task-ta-form.tsx +++ b/components/form/task-ta/task-ta-form.tsx @@ -43,7 +43,7 @@ import { getCsrfToken } from "@/service/auth"; import { loading } from "@/lib/swal"; import { useTranslations } from "next-intl"; import dynamic from "next/dynamic"; -import { cn } from "@/lib/utils"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; import { Popover, PopoverContent, @@ -181,6 +181,32 @@ export default function FormTaskTa() { mode: "all", }); + const [profile, setProfile] = useState(null); + const userLevelId = Number(getCookiesDecrypt("ulie")); + const roleId = Number(getCookiesDecrypt("urie")); + const userId = Number(getCookiesDecrypt("uie")); + + const MABES_LEVEL_ID = 216; + const APPROVER_ROLE_ID = 3; + const isMabes = userLevelId === MABES_LEVEL_ID; + const isApprover = roleId === APPROVER_ROLE_ID; + + const isMabesApprover = + userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID; + + + useEffect(() => { + async function fetchUserLevel() { + try { + const res = await getUserLevelForAssignments(); + setProfile(res?.data?.data); + } catch (e) { + console.error("Failed fetch user level", e); + } + } + fetchUserLevel(); + }, []); + useEffect(() => { getDataAdditional(); }, []); @@ -360,12 +386,20 @@ export default function FormTaskTa() { const requestData = { ...data, assignedToUsers: handleExpertChange(), + // assignedToUsers: isMabesApprover ? "464" : handleExpertChange(), assignmentType: taskType, assignmentTypeId: type, expertCompetencies: Array.from(selectedCompetencies).join(","), attachmentUrl: cleanedLinks, }; + console.log("FINAL ASSIGNED TO:", { + isMabesApprover, + assignedToUsers: isMabesApprover + ? String(roleId) + : handleExpertChange(), + }); + const response = await createTaskTa(requestData); const id = String(response?.data?.data.id); @@ -718,101 +752,109 @@ export default function FormTaskTa() {
-
- -
- {userCompetencies?.map((item: any) => ( -
- handleCompetencyChange(item.id)} - /> - -
- ))} -
-
-
- -
- - - - - - - Daftar Tenaga Ahli - -
- {listExpert?.map((expert: any) => ( -
- -
- ))} + {!isMabesApprover && ( +
+ +
+ {userCompetencies?.map((item: any) => ( +
+ handleCompetencyChange(item.id)} + /> +
- -
-
- {checkedLevels.size > 0 && ( -
- -
- {Array.from(checkedLevels).map((expertId) => { - const expert = listExpert?.find( - (exp: any) => exp.id === expertId - ); - return expert ? ( -
- {expert.fullname} - -
- ) : null; - })} -
+ ))}
- )} -
+ + )} + {!isMabesApprover && ( +
+ +
+ + + + + + + Daftar Tenaga Ahli + +
+ {listExpert?.map((expert: any) => ( +
+ +
+ ))} +
+
+
+
+ {checkedLevels.size > 0 && ( +
+ +
+ {Array.from(checkedLevels).map((expertId) => { + const expert = listExpert?.find( + (exp: any) => exp.id === expertId + ); + return expert ? ( +
+ {expert.fullname} + +
+ ) : null; + })} +
+
+ )} +
+ )}
Date: Tue, 13 Jan 2026 02:20:57 +0700 Subject: [PATCH 07/10] feat: rollback using pnpm --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 150aeaee..8daffc34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,20 +18,20 @@ COPY package.json ./ COPY vendor/ckeditor5 ./vendor/ckeditor5 # Install dependencies -RUN npm install -# RUN npm install --frozen-lockfile +RUN pnpm install +# RUN pnpm install --frozen-lockfile # Menyalin source code aplikasi COPY . . # Build aplikasi -RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build +RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build # Expose port untuk server EXPOSE 3000 # Perintah untuk menjalankan aplikasi -CMD ["npm", "run", "start"] +CMD ["pnpm", "run", "start"] # # Gunakan base image Node.js Alpine yang ringan @@ -55,7 +55,7 @@ CMD ["npm", "run", "start"] # COPY vendor/ckeditor5 ./vendor/ckeditor5 # # Install dependencies -# RUN npm install --frozen-lockfile +# RUN pnpm install --frozen-lockfile # # Salin semua source code # COPY . . From ce7c343808ee75b9bd89574a619a82e1fe81afa2 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Tue, 13 Jan 2026 09:52:29 +0700 Subject: [PATCH 08/10] fix: taskTA from mabes to koorkurator --- .../task-ta/components/columns.tsx | 2 +- .../task-ta/components/task-ta-table.tsx | 981 +++++++++++++----- components/form/task-ta/task-ta-form.tsx | 259 +++-- 3 files changed, 925 insertions(+), 317 deletions(-) diff --git a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx index 7141b761..8f8f9072 100644 --- a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx @@ -22,7 +22,7 @@ import withReactContent from "sweetalert2-react-content"; import Swal from "sweetalert2"; import { useTranslations } from "next-intl"; -const useTableColumns = (activeTab: "ta" | "daily" | "special") => { +const useTableColumns = (activeTab: "ta" | "daily" | "special" |"mabes-koor") => { const t = useTranslations("Table"); const columns: ColumnDef[] = [ { diff --git a/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx b/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx index 7c91b7cf..7964e26a 100644 --- a/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { - ColumnDef, ColumnFiltersState, PaginationState, SortingState, @@ -14,8 +13,8 @@ import { getSortedRowModel, useReactTable, } from "@tanstack/react-table"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/ui/button"; import { Table, TableBody, @@ -24,46 +23,47 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { - ChevronDown, - ChevronLeft, - ChevronRight, - Eye, - MoreVertical, - Search, - SquarePen, - Trash2, - TrendingDown, - TrendingUp, -} from "lucide-react"; -import { cn } from "@/lib/utils"; import { DropdownMenu, DropdownMenuContent, - DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { InputGroup, InputGroupText } from "@/components/ui/input-group"; -import { paginationBlog } from "@/service/blog/blog"; -import { ticketingPagination } from "@/service/ticketing/ticketing"; -import { Badge } from "@/components/ui/badge"; -import { useRouter, useSearchParams } from "next/navigation"; -import TablePagination from "@/components/table/table-pagination"; -import columns from "./columns"; -import { listTask, listTaskMabesForTa, listTaskTa } from "@/service/task"; import { Label } from "@/components/ui/label"; -import { format } from "date-fns"; -import { useTranslations } from "next-intl"; -import useTableColumns from "./columns"; +import TablePagination from "@/components/table/table-pagination"; -const TaskTaTable = () => { +import { Search, ChevronDown } from "lucide-react"; +import { format } from "date-fns"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useTranslations } from "next-intl"; + +import { getCookiesDecrypt } from "@/lib/utils"; +import { listTask, listTaskTa } from "@/service/task"; + +import useTableColumns from "./columns"; // kamu sudah punya + +type ActiveTab = "ta" | "daily" | "special" | "mabes-koor"; + +const MABES_LEVEL_ID = 216; +const APPROVER_ROLE_ID = 3; + +export default function TaskTaTable() { const router = useRouter(); const searchParams = useSearchParams(); const t = useTranslations("AnalyticsDashboard"); + + // ✅ user identity from cookies + const userLevelId = Number(getCookiesDecrypt("ulie")); + const roleId = Number(getCookiesDecrypt("urie")); + const userId = Number(getCookiesDecrypt("uie")); + + const isMabesApprover = + userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID; + + // table states const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); @@ -73,24 +73,27 @@ const TaskTaTable = () => { const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); + const [showData, setShowData] = React.useState("10"); const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: Number(showData), }); - const [activeTab, setActiveTab] = React.useState<"ta" | "daily" | "special">( - "ta" + + const [activeTab, setActiveTab] = React.useState( + isMabesApprover ? "mabes-koor" : "ta" ); const [statusFilter, setStatusFilter] = React.useState([]); const [dateFilter, setDateFilter] = React.useState(""); - const [endDate, setEndDate] = React.useState(""); const [filterByCode, setFilterByCode] = React.useState(""); + const [search, setSearch] = React.useState(""); + const [page, setPage] = React.useState(1); const [totalPage, setTotalPage] = React.useState(1); - const [limit, setLimit] = React.useState(10); - const [isSpecificAttention, setIsSpecificAttention] = React.useState(true); - const [search, setSearch] = React.useState(""); + + // ✅ columns based on tab const columns = useTableColumns(activeTab); + const table = useReactTable({ data: dataTable, columns, @@ -114,17 +117,15 @@ const TaskTaTable = () => { React.useEffect(() => { const pageFromUrl = searchParams?.get("page"); - if (pageFromUrl) { - setPage(Number(pageFromUrl)); - } + if (pageFromUrl) setPage(Number(pageFromUrl)); }, [searchParams]); React.useEffect(() => { fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ page, showData, - isSpecificAttention, search, dateFilter, filterByCode, @@ -138,7 +139,7 @@ const TaskTaTable = () => { : ""; try { - let res; + let res: any; if (activeTab === "ta") { res = await listTaskTa( @@ -150,7 +151,9 @@ const TaskTaTable = () => { "atensi-khusus", statusFilter ); - } else if (activeTab === "daily") { + } + + if (activeTab === "daily") { res = await listTaskTa( page - 1, search, @@ -160,7 +163,9 @@ const TaskTaTable = () => { "tugas-harian", statusFilter ); - } else if (activeTab === "special") { + } + + if (activeTab === "special") { res = await listTask( page - 1, search, @@ -172,19 +177,73 @@ const TaskTaTable = () => { ); } + // ✅ TAB BARU: khusus Mabes Approver + if (activeTab === "mabes-koor") { + // kalau bukan Mabes Approver, jangan fetch + if (!isMabesApprover) { + setDataTable([]); + setTotalData(0); + setTotalPage(0); + return; + } + + // NOTE: backend endpoint harus return tasks mabes -> koorkurator + res = await listTaskTa( + page - 1, + search, + showData, + filterByCode, + formattedStartDate, + "atensi-khusus", + statusFilter + ); + } + let contentData = res?.data?.data?.content || []; - // ⛔ Jika upload belum selesai → sembunyikan task baru + // ⛔ blok task baru kalau upload belum selesai (fitur kamu) const isUploadingTA = localStorage.getItem("TA_UPLOAD_IN_PROGRESS") === "true"; - if (isUploadingTA) { const now = new Date(); - // Filter task yg dibuat < 5 menit terakhir (belum selesai upload) contentData = contentData.filter((item: any) => { const created = new Date(item.createdAt); const diff = now.getTime() - created.getTime(); - return diff > 5 * 60 * 1000; // lebih dari 5 menit + return diff > 5 * 60 * 1000; + }); + } + + // ✅ OPTIONAL SAFETY FILTER (kalau backend belum 100% benar) + // sesuaikan field names sesuai response kamu + // if (activeTab === "mabes-koor") { + // contentData = contentData.filter((item: any) => { + // const createdByLevelName = + // item?.createdByLevelName || item?.createdByLevel?.name; + // const assignedToLevelName = + // item?.assignedToLevelName || item?.assignedToLevel?.name; + + // // minimal filter: + // return ( + // String(createdByLevelName || "") + // .toUpperCase() + // .includes("MABES") && + // String(assignedToLevelName || "") + // .toUpperCase() + // .includes("KOOR") + // ); + // }); + // } + if (activeTab === "mabes-koor") { + contentData = contentData.filter((item: any) => { + const createdByLevel = item?.createdBy?.userLevel?.name || ""; + + // KOOR KURATOR = user id 464 (sesuai create kamu) + const assignedUsers = String(item?.assignedToUsers || ""); + + return ( + /MABES/i.test(createdByLevel) && + assignedUsers.split(",").includes("464") + ); }); } @@ -193,153 +252,50 @@ const TaskTaTable = () => { }); setDataTable(contentData); - setTotalData(res?.data?.data?.totalElements); - setTotalPage(res?.data?.data?.totalPages); + setTotalData(res?.data?.data?.totalElements || 0); + setTotalPage(res?.data?.data?.totalPages || 0); } catch (error) { console.error("Error fetching tasks:", error); + setDataTable([]); + setTotalData(0); + setTotalPage(0); } } - // async function fetchData() { - // const formattedStartDate = dateFilter - // ? format(new Date(dateFilter), "yyyy-MM-dd") - // : ""; - - // try { - // let res; - - // if (activeTab === "ta") { - // res = await listTaskTa( - // page - 1, - // search, - // showData, - // filterByCode, - // formattedStartDate, - // "atensi-khusus", - // statusFilter - // ); - // } else if (activeTab === "daily") { - // res = await listTaskTa( - // page - 1, - // search, - // showData, - // filterByCode, - // formattedStartDate, - // "tugas-harian", - // statusFilter - // ); - // } else if (activeTab === "special") { - // res = await listTask( - // page - 1, - // search, - // showData, - // filterByCode, - // formattedStartDate, - // "atensi-khusus", - // statusFilter - // ); - // } - - // const data = res?.data?.data; - // const contentData = data?.content || []; - - // contentData.forEach((item: any, index: number) => { - // item.no = (page - 1) * Number(showData) + index + 1; - // }); - - // setDataTable(contentData); - // setTotalData(data?.totalElements); - // setTotalPage(data?.totalPages); - // } catch (error) { - // console.error("Error fetching tasks:", error); - // } - // } - - // async function fetchData() { - // const formattedStartDate = dateFilter - // ? format(new Date(dateFilter), "yyyy-MM-dd") - // : ""; - // try { - // const res = isSpecificAttention - // ? await listTaskTa( - // page - 1, - // search, - // showData, - // filterByCode, - // formattedStartDate, - // "atensi-khusus", - // statusFilter - // ) - // : await listTask( - // page - 1, - // search, - // showData, - // filterByCode, - // formattedStartDate, - // "atensi-khusus", - // statusFilter - // ); - - // const data = res?.data?.data; - // const contentData = data?.content; - - // // let contentDataFilter = res?.data?.data?.content || []; - - // // Filter berdasarkan status - // // contentDataFilter = contentDataFilter.filter((item: any) => { - // // const isSelesai = statusFilter.includes(1) ? item.isDone : true; - // // const isAktif = statusFilter.includes(2) ? item.isActive : true; - // // return isSelesai && isAktif; - // // }); - - // contentData.forEach((item: any, index: number) => { - // item.no = (page - 1) * Number(showData) + index + 1; - // }); - - // console.log("contentData : ", contentData); - - // setDataTable(contentData); - // setTotalData(data?.totalElements); - // setTotalPage(data?.totalPages); - // } catch (error) { - // console.error("Error fetching tasks:", error); - // } - // } - const handleSearch = (e: React.ChangeEvent) => { - setFilterByCode(e.target.value); - setSearch(e.target.value); - table.getColumn("judul")?.setFilterValue(e.target.value); + const value = e.target.value; + setFilterByCode(value); + setSearch(value); + table.getColumn("judul")?.setFilterValue(value); }; function handleStatusCheckboxChange(value: number) { setStatusFilter((prev) => - prev.includes(value) - ? prev.filter((status) => status !== value) - : [...prev, value] + prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value] ); } - // const handleSearchFilterByCode = (e: React.ChangeEvent) => { - // const value = e.target.value; - // console.log("code :", value); - // setFilterByCode(value); - // fetchData(); - // }; - return (
-
-
-
-
+
@@ -424,14 +344,14 @@ const TaskTaTable = () => {
-
+
@@ -461,6 +381,7 @@ const TaskTaTable = () => {
+ + {

Filter

+
{ className="max-w-sm" />
- {/*
- - -
*/} +
{ {t("done", { defaultValue: "Done" })}
+
{
- {/*
- ) => - table.getColumn("status")?.setFilterValue(event.target.value) - } - className="max-w-sm " - /> -
*/}
+ {table.getHeaderGroups().map((headerGroup) => ( @@ -555,6 +460,7 @@ const TaskTaTable = () => { ))} + {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( @@ -579,6 +485,7 @@ const TaskTaTable = () => { )}
+ { />
); -}; +} -export default TaskTaTable; +// "use client"; + +// import * as React from "react"; +// import { +// ColumnDef, +// ColumnFiltersState, +// PaginationState, +// SortingState, +// VisibilityState, +// flexRender, +// getCoreRowModel, +// getFilteredRowModel, +// getPaginationRowModel, +// getSortedRowModel, +// useReactTable, +// } from "@tanstack/react-table"; +// import { Button } from "@/components/ui/button"; + +// import { +// Table, +// TableBody, +// TableCell, +// TableHead, +// TableHeader, +// TableRow, +// } from "@/components/ui/table"; +// import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +// import { +// ChevronDown, +// ChevronLeft, +// ChevronRight, +// Eye, +// MoreVertical, +// Search, +// SquarePen, +// Trash2, +// TrendingDown, +// TrendingUp, +// } from "lucide-react"; +// import { cn } from "@/lib/utils"; +// import { +// DropdownMenu, +// DropdownMenuContent, +// DropdownMenuItem, +// DropdownMenuRadioGroup, +// DropdownMenuRadioItem, +// DropdownMenuTrigger, +// } from "@/components/ui/dropdown-menu"; +// import { Input } from "@/components/ui/input"; +// import { InputGroup, InputGroupText } from "@/components/ui/input-group"; +// import { paginationBlog } from "@/service/blog/blog"; +// import { ticketingPagination } from "@/service/ticketing/ticketing"; +// import { Badge } from "@/components/ui/badge"; +// import { useRouter, useSearchParams } from "next/navigation"; +// import TablePagination from "@/components/table/table-pagination"; +// import columns from "./columns"; +// import { listTask, listTaskMabesForTa, listTaskTa } from "@/service/task"; +// import { Label } from "@/components/ui/label"; +// import { format } from "date-fns"; +// import { useTranslations } from "next-intl"; +// import useTableColumns from "./columns"; + +// const TaskTaTable = () => { +// const router = useRouter(); +// const searchParams = useSearchParams(); +// const t = useTranslations("AnalyticsDashboard"); +// const [dataTable, setDataTable] = React.useState([]); +// const [totalData, setTotalData] = React.useState(1); +// const [sorting, setSorting] = React.useState([]); +// const [columnFilters, setColumnFilters] = React.useState( +// [] +// ); +// const [columnVisibility, setColumnVisibility] = +// React.useState({}); +// const [rowSelection, setRowSelection] = React.useState({}); +// const [showData, setShowData] = React.useState("10"); +// const [pagination, setPagination] = React.useState({ +// pageIndex: 0, +// pageSize: Number(showData), +// }); +// const [activeTab, setActiveTab] = React.useState<"ta" | "daily" | "special">( +// "ta" +// ); +// const [statusFilter, setStatusFilter] = React.useState([]); +// const [dateFilter, setDateFilter] = React.useState(""); +// const [endDate, setEndDate] = React.useState(""); +// const [filterByCode, setFilterByCode] = React.useState(""); +// const [page, setPage] = React.useState(1); +// const [totalPage, setTotalPage] = React.useState(1); +// const [limit, setLimit] = React.useState(10); +// const [isSpecificAttention, setIsSpecificAttention] = React.useState(true); +// const [search, setSearch] = React.useState(""); +// const columns = useTableColumns(activeTab); +// const table = useReactTable({ +// data: dataTable, +// columns, +// onSortingChange: setSorting, +// onColumnFiltersChange: setColumnFilters, +// getCoreRowModel: getCoreRowModel(), +// getPaginationRowModel: getPaginationRowModel(), +// getSortedRowModel: getSortedRowModel(), +// getFilteredRowModel: getFilteredRowModel(), +// onColumnVisibilityChange: setColumnVisibility, +// onRowSelectionChange: setRowSelection, +// onPaginationChange: setPagination, +// state: { +// sorting, +// columnFilters, +// columnVisibility, +// rowSelection, +// pagination, +// }, +// }); + +// React.useEffect(() => { +// const pageFromUrl = searchParams?.get("page"); +// if (pageFromUrl) { +// setPage(Number(pageFromUrl)); +// } +// }, [searchParams]); + +// React.useEffect(() => { +// fetchData(); +// }, [ +// page, +// showData, +// isSpecificAttention, +// search, +// dateFilter, +// filterByCode, +// statusFilter, +// activeTab, +// ]); + +// async function fetchData() { +// const formattedStartDate = dateFilter +// ? format(new Date(dateFilter), "yyyy-MM-dd") +// : ""; + +// try { +// let res; + +// if (activeTab === "ta") { +// res = await listTaskTa( +// page - 1, +// search, +// showData, +// filterByCode, +// formattedStartDate, +// "atensi-khusus", +// statusFilter +// ); +// } else if (activeTab === "daily") { +// res = await listTaskTa( +// page - 1, +// search, +// showData, +// filterByCode, +// formattedStartDate, +// "tugas-harian", +// statusFilter +// ); +// } else if (activeTab === "special") { +// res = await listTask( +// page - 1, +// search, +// showData, +// filterByCode, +// formattedStartDate, +// "atensi-khusus", +// statusFilter +// ); +// } + +// let contentData = res?.data?.data?.content || []; + +// // ⛔ Jika upload belum selesai → sembunyikan task baru +// const isUploadingTA = +// localStorage.getItem("TA_UPLOAD_IN_PROGRESS") === "true"; + +// if (isUploadingTA) { +// const now = new Date(); +// // Filter task yg dibuat < 5 menit terakhir (belum selesai upload) +// contentData = contentData.filter((item: any) => { +// const created = new Date(item.createdAt); +// const diff = now.getTime() - created.getTime(); +// return diff > 5 * 60 * 1000; // lebih dari 5 menit +// }); +// } + +// contentData.forEach((item: any, index: number) => { +// item.no = (page - 1) * Number(showData) + index + 1; +// }); + +// setDataTable(contentData); +// setTotalData(res?.data?.data?.totalElements); +// setTotalPage(res?.data?.data?.totalPages); +// } catch (error) { +// console.error("Error fetching tasks:", error); +// } +// } + +// // async function fetchData() { +// // const formattedStartDate = dateFilter +// // ? format(new Date(dateFilter), "yyyy-MM-dd") +// // : ""; + +// // try { +// // let res; + +// // if (activeTab === "ta") { +// // res = await listTaskTa( +// // page - 1, +// // search, +// // showData, +// // filterByCode, +// // formattedStartDate, +// // "atensi-khusus", +// // statusFilter +// // ); +// // } else if (activeTab === "daily") { +// // res = await listTaskTa( +// // page - 1, +// // search, +// // showData, +// // filterByCode, +// // formattedStartDate, +// // "tugas-harian", +// // statusFilter +// // ); +// // } else if (activeTab === "special") { +// // res = await listTask( +// // page - 1, +// // search, +// // showData, +// // filterByCode, +// // formattedStartDate, +// // "atensi-khusus", +// // statusFilter +// // ); +// // } + +// // const data = res?.data?.data; +// // const contentData = data?.content || []; + +// // contentData.forEach((item: any, index: number) => { +// // item.no = (page - 1) * Number(showData) + index + 1; +// // }); + +// // setDataTable(contentData); +// // setTotalData(data?.totalElements); +// // setTotalPage(data?.totalPages); +// // } catch (error) { +// // console.error("Error fetching tasks:", error); +// // } +// // } + +// // async function fetchData() { +// // const formattedStartDate = dateFilter +// // ? format(new Date(dateFilter), "yyyy-MM-dd") +// // : ""; +// // try { +// // const res = isSpecificAttention +// // ? await listTaskTa( +// // page - 1, +// // search, +// // showData, +// // filterByCode, +// // formattedStartDate, +// // "atensi-khusus", +// // statusFilter +// // ) +// // : await listTask( +// // page - 1, +// // search, +// // showData, +// // filterByCode, +// // formattedStartDate, +// // "atensi-khusus", +// // statusFilter +// // ); + +// // const data = res?.data?.data; +// // const contentData = data?.content; + +// // // let contentDataFilter = res?.data?.data?.content || []; + +// // // Filter berdasarkan status +// // // contentDataFilter = contentDataFilter.filter((item: any) => { +// // // const isSelesai = statusFilter.includes(1) ? item.isDone : true; +// // // const isAktif = statusFilter.includes(2) ? item.isActive : true; +// // // return isSelesai && isAktif; +// // // }); + +// // contentData.forEach((item: any, index: number) => { +// // item.no = (page - 1) * Number(showData) + index + 1; +// // }); + +// // console.log("contentData : ", contentData); + +// // setDataTable(contentData); +// // setTotalData(data?.totalElements); +// // setTotalPage(data?.totalPages); +// // } catch (error) { +// // console.error("Error fetching tasks:", error); +// // } +// // } + +// const handleSearch = (e: React.ChangeEvent) => { +// setFilterByCode(e.target.value); +// setSearch(e.target.value); +// table.getColumn("judul")?.setFilterValue(e.target.value); +// }; + +// function handleStatusCheckboxChange(value: number) { +// setStatusFilter((prev) => +// prev.includes(value) +// ? prev.filter((status) => status !== value) +// : [...prev, value] +// ); +// } + +// // const handleSearchFilterByCode = (e: React.ChangeEvent) => { +// // const value = e.target.value; +// // console.log("code :", value); +// // setFilterByCode(value); +// // fetchData(); +// // }; + +// return ( +//
+//
+//
+//
+//
+// +//
+//
+//
+//
+//
+//
+// +// +// +// +// +// +//
+ +//
+//
+//
+// +// +// +// +// +// +// +// 1 - 10 Data +// +// +// 1 - 50 Data +// +// +// 1 - 100 Data +// +// +// 1 - 250 Data +// +// +// +// +//
+// +// +// +// +// +//
+//

Filter

+//
+//
+// +// setDateFilter(e.target.value)} +// className="max-w-sm" +// /> +//
+// {/*
+// +// +//
*/} +// +//
+// handleStatusCheckboxChange(1)} +// /> +// +//
+//
+// handleStatusCheckboxChange(2)} +// /> +// +//
+//
+//
+//
+//
+// {/*
+// ) => +// table.getColumn("status")?.setFilterValue(event.target.value) +// } +// className="max-w-sm " +// /> +//
*/} +//
+// +// +// {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 TaskTaTable; diff --git a/components/form/task-ta/task-ta-form.tsx b/components/form/task-ta/task-ta-form.tsx index b9840320..89a879a4 100644 --- a/components/form/task-ta/task-ta-form.tsx +++ b/components/form/task-ta/task-ta-form.tsx @@ -186,14 +186,15 @@ export default function FormTaskTa() { const roleId = Number(getCookiesDecrypt("urie")); const userId = Number(getCookiesDecrypt("uie")); - const MABES_LEVEL_ID = 216; - const APPROVER_ROLE_ID = 3; + const MABES_LEVEL_ID = 216; // userLevelId Mabes Polri + const APPROVER_ROLE_ID = 3; // roleId Approver const isMabes = userLevelId === MABES_LEVEL_ID; const isApprover = roleId === APPROVER_ROLE_ID; const isMabesApprover = userLevelId === MABES_LEVEL_ID && roleId === APPROVER_ROLE_ID; + const shouldHideExpert = isMabes && isApprover; useEffect(() => { async function fetchUserLevel() { @@ -378,63 +379,126 @@ export default function FormTaskTa() { // // }); // }; + // const save = async (data: TaskSchema) => { + // const cleanedLinks = links + // .map((link) => link.trim()) + // .filter((link) => link.startsWith("http")); + + // const requestData = { + // ...data, + // // assignedToUsers: handleExpertChange(), + // assignedToUsers: isMabesApprover ? "464" : handleExpertChange(), + // assignmentType: taskType, + // assignmentTypeId: type, + // expertCompetencies: Array.from(selectedCompetencies).join(","), + // attachmentUrl: cleanedLinks, + // }; + + // console.log("FINAL ASSIGNED TO:", { + // isMabesApprover, + // assignedToUsers: isMabesApprover + // ? String(roleId) + // : handleExpertChange(), + // }); + + // const response = await createTaskTa(requestData); + // const id = String(response?.data?.data.id); + + // // Set block table TA + // localStorage.setItem("TA_UPLOAD_IN_PROGRESS", "true"); + + // loading(); // SHOW SWAL LOADING + + // // Kumpulkan semua upload + // const allUploads: Promise[] = []; + + // imageFiles.forEach((item, idx) => + // allUploads.push(uploadResumableFile(idx, id, item, "1", "0")) + // ); + + // videoFiles.forEach((item, idx) => + // allUploads.push(uploadResumableFile(idx, id, item, "2", "0")) + // ); + + // textFiles.forEach((item, idx) => + // allUploads.push(uploadResumableFile(idx, id, item, "3", "0")) + // ); + + // audioFiles.forEach((item, idx) => + // allUploads.push(uploadResumableFile(idx, id, item, "4", "0")) + // ); + + // // Tunggu upload selesai + // await Promise.all(allUploads); + + // // Hapus flag + // localStorage.removeItem("TA_UPLOAD_IN_PROGRESS"); + + // // Close loading + redirect + // successSubmit("/in/contributor/task-ta"); + // }; + const save = async (data: TaskSchema) => { - const cleanedLinks = links - .map((link) => link.trim()) - .filter((link) => link.startsWith("http")); + try { + loading(); - const requestData = { - ...data, - assignedToUsers: handleExpertChange(), - // assignedToUsers: isMabesApprover ? "464" : handleExpertChange(), - assignmentType: taskType, - assignmentTypeId: type, - expertCompetencies: Array.from(selectedCompetencies).join(","), - attachmentUrl: cleanedLinks, - }; + const cleanedLinks = links + .map((link) => link.trim()) + .filter((link) => link.startsWith("http")); - console.log("FINAL ASSIGNED TO:", { - isMabesApprover, - assignedToUsers: isMabesApprover - ? String(roleId) - : handleExpertChange(), - }); + const requestData = { + ...data, + // assignedToUsers: isMabesApprover ? "464" : handleExpertChange(), + assignedToUsers: isMabesApprover + ? ["464", "8258"] + : handleExpertChange(), + assignmentType: taskType, + assignmentTypeId: type, + expertCompetencies: Array.from(selectedCompetencies).join(","), + attachmentUrl: cleanedLinks, + }; - const response = await createTaskTa(requestData); - const id = String(response?.data?.data.id); + const response = await createTaskTa(requestData); - // Set block table TA - localStorage.setItem("TA_UPLOAD_IN_PROGRESS", "true"); + if (!response?.data?.data?.id) { + throw new Error("Gagal membuat task"); + } - loading(); // SHOW SWAL LOADING + const assignmentId = String(response.data.data.id); - // Kumpulkan semua upload - const allUploads: Promise[] = []; + const uploads: Promise[] = []; - imageFiles.forEach((item, idx) => - allUploads.push(uploadResumableFile(idx, id, item, "1", "0")) - ); + imageFiles.forEach((file, i) => + uploads.push(uploadResumableFile(i, assignmentId, file, "1", "0")) + ); - videoFiles.forEach((item, idx) => - allUploads.push(uploadResumableFile(idx, id, item, "2", "0")) - ); + videoFiles.forEach((file, i) => + uploads.push(uploadResumableFile(i, assignmentId, file, "2", "0")) + ); - textFiles.forEach((item, idx) => - allUploads.push(uploadResumableFile(idx, id, item, "3", "0")) - ); + textFiles.forEach((file, i) => + uploads.push(uploadResumableFile(i, assignmentId, file, "3", "0")) + ); - audioFiles.forEach((item, idx) => - allUploads.push(uploadResumableFile(idx, id, item, "4", "0")) - ); + audioFiles.forEach((file, i) => + uploads.push(uploadResumableFile(i, assignmentId, file, "4", "0")) + ); - // Tunggu upload selesai - await Promise.all(allUploads); + await Promise.all(uploads); - // Hapus flag - localStorage.removeItem("TA_UPLOAD_IN_PROGRESS"); + successSubmit("/in/contributor/task-ta"); + } catch (err: any) { + console.error("SUBMIT ERROR:", err); - // Close loading + redirect - successSubmit("/in/contributor/task-ta"); + Swal.fire({ + icon: "error", + title: "Gagal", + text: + err?.response?.data?.message || + err?.message || + "Terjadi kesalahan, data tidak tersimpan", + }); + } }; const onSubmit = (data: TaskSchema) => { @@ -577,46 +641,91 @@ export default function FormTaskTa() { // upload.start(); // } + // function uploadResumableFile( + // idx: number, + // id: string, + // file: any, + // fileTypeId: string, + // duration: string + // ) { + // return new Promise(async (resolve, reject) => { + // const resCsrf = await getCsrfToken(); + // const csrfToken = resCsrf?.data?.token; + + // const upload = new Upload(file, { + // endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`, + // headers: { "X-XSRF-TOKEN": csrfToken }, + // retryDelays: [0, 3000, 6000, 12000], + // chunkSize: 20000, + // metadata: { + // assignmentId: id, + // filename: file.name, + // contentType: file.type, + // fileTypeId, + // duration, + // }, + + // onBeforeRequest(req) { + // req.getUnderlyingObject().withCredentials = true; + // }, + + // onError(err) { + // console.error("Upload error:", err); + // reject(err); + // }, + + // onSuccess() { + // console.log("Upload selesai:", file.name); + // resolve(true); + // }, + // }); + + // upload.start(); + // }); + // } + function uploadResumableFile( idx: number, id: string, - file: any, + file: File, fileTypeId: string, duration: string ) { return new Promise(async (resolve, reject) => { - const resCsrf = await getCsrfToken(); - const csrfToken = resCsrf?.data?.token; + try { + const resCsrf = await getCsrfToken(); + const csrfToken = resCsrf?.data?.token; - const upload = new Upload(file, { - endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`, - headers: { "X-XSRF-TOKEN": csrfToken }, - retryDelays: [0, 3000, 6000, 12000], - chunkSize: 20000, - metadata: { - assignmentId: id, - filename: file.name, - contentType: file.type, - fileTypeId, - duration, - }, + const upload = new Upload(file, { + endpoint: `${process.env.NEXT_PUBLIC_API}/assignment-expert/file/upload`, + headers: { "X-XSRF-TOKEN": csrfToken }, + retryDelays: [0, 3000, 6000], + chunkSize: 20000, + metadata: { + assignmentId: id, + filename: file.name, + contentType: file.type, + fileTypeId, + duration, + }, - onBeforeRequest(req) { - req.getUnderlyingObject().withCredentials = true; - }, + onBeforeRequest(req) { + req.getUnderlyingObject().withCredentials = true; + }, - onError(err) { - console.error("Upload error:", err); - reject(err); - }, + onError(error) { + reject(error); + }, - onSuccess() { - console.log("Upload selesai:", file.name); - resolve(true); - }, - }); + onSuccess() { + resolve(true); + }, + }); - upload.start(); + upload.start(); + } catch (err) { + reject(err); + } }); } @@ -988,7 +1097,9 @@ export default function FormTaskTa() { handleLinkChange(index, e.target.value) From c99e1a5c7dd0ebbb50e6004f6be6aa643844b17c Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Wed, 21 Jan 2026 10:06:40 +0700 Subject: [PATCH 09/10] fix: penugasan mabes=>koorkurator --- .../contributor/task-ta/components/task-ta-table.tsx | 12 +++++++++++- components/form/task-ta/task-ta-form.tsx | 4 +--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx b/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx index 7964e26a..590eba20 100644 --- a/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/components/task-ta-table.tsx @@ -237,7 +237,6 @@ export default function TaskTaTable() { contentData = contentData.filter((item: any) => { const createdByLevel = item?.createdBy?.userLevel?.name || ""; - // KOOR KURATOR = user id 464 (sesuai create kamu) const assignedUsers = String(item?.assignedToUsers || ""); return ( @@ -307,6 +306,17 @@ export default function TaskTaTable() { Atensi Khusus Mabes + + + + + + +
+ )} + + {/* {Number(detail?.needApprovalFrom) == Number(userLevelId) && Number(userLevelNumber) < 2 ? ( Number(detail?.uploadedById) == Number(userId) ? ( "" @@ -527,13 +615,15 @@ export default function FormDetailLiveReport() { ) ) : ( "" - )} + )} */} - {t("leave-comment", { defaultValue: "Leave Comment" })} + + {t("leave-comment", { defaultValue: "Leave Comment" })} +

@@ -544,15 +634,15 @@ export default function FormDetailLiveReport() { status === "2" ? "text-primary" : status === "3" - ? "text-warning" - : "text-destructive" + ? "text-warning" + : "text-destructive" } > {status === "2" ? "Disetujui" : status === "3" - ? "Revisi" - : "Ditolak"} + ? "Revisi" + : "Ditolak"}

{status === "2" && (