Compare commits
No commits in common. "main" and "dev-landing-v2" have entirely different histories.
main
...
dev-landin
45
.drone.yml
45
.drone.yml
|
|
@ -1,45 +0,0 @@
|
||||||
kind: pipeline
|
|
||||||
type: ssh
|
|
||||||
name: mediahub-fe-build-deploy
|
|
||||||
|
|
||||||
server:
|
|
||||||
host:
|
|
||||||
from_secret: ssh_host
|
|
||||||
user:
|
|
||||||
from_secret: ssh_user
|
|
||||||
ssh_key:
|
|
||||||
from_secret: ssh_key
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: prepare repo
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- dev-sabda-v2
|
|
||||||
- main
|
|
||||||
- prod
|
|
||||||
commands:
|
|
||||||
- rm -rf /opt/build/mediahub-fe
|
|
||||||
- mkdir -p /opt/build
|
|
||||||
- cd /opt/build
|
|
||||||
- git clone http://38.47.180.165:3000/mediahub/mediahub-fe.git
|
|
||||||
|
|
||||||
- name: build image
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- dev-sabda-v2
|
|
||||||
- prod
|
|
||||||
commands:
|
|
||||||
- docker login 38.47.180.165:3000 -u administrator -p HarborDockerImageRep0
|
|
||||||
- cd /opt/build/mediahub-fe
|
|
||||||
- docker build -t 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH .
|
|
||||||
- docker push 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
|
||||||
|
|
||||||
- name: deploy
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- prod
|
|
||||||
commands:
|
|
||||||
- docker pull 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
|
||||||
- docker stop new-mediahub-fe || true
|
|
||||||
- docker rm new-mediahub-fe || true
|
|
||||||
- docker run -dt -p 4200:3000 --restart always --name new-mediahub-fe 38.47.180.165:3000/mediahub/mediahub-fe:$DRONE_BRANCH
|
|
||||||
4
.env
4
.env
|
|
@ -1,3 +1,3 @@
|
||||||
NEXT_PUBLIC_API=https://new.netidhub.com/api
|
NEXT_PUBLIC_API=https://netidhub.com/api
|
||||||
NEXT_PUBLIC=https://new.netidhub.com
|
NEXT_PUBLIC=https://netidhub.com
|
||||||
NEXT_PUBLIC_TINYMCE_API_KEY=bhteuja26yz5p0aubxry9b95hs33amgn65kjv5km0fd5iuev
|
NEXT_PUBLIC_TINYMCE_API_KEY=bhteuja26yz5p0aubxry9b95hs33amgn65kjv5km0fd5iuev
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
build-dev:
|
build-dev:
|
||||||
stage: build
|
stage: build
|
||||||
when: on_success
|
when: on_success
|
||||||
only:
|
only:
|
||||||
- main
|
- main
|
||||||
- dev-landing-v2
|
- dev-landing-v2
|
||||||
image:
|
image: docker:stable
|
||||||
name: docker:25.0.3-cli
|
|
||||||
services:
|
services:
|
||||||
- name: docker:25.0.3-dind
|
- name: docker:dind
|
||||||
command: ["--insecure-registry=38.47.185.86:8900"]
|
command: ["--insecure-registry=103.82.242.92:8900"]
|
||||||
script:
|
script:
|
||||||
- docker logout
|
- docker logout
|
||||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 38.47.185.86:8900
|
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900
|
||||||
- docker build -t 38.47.185.86:8900/mediahub/new-mediahub-fe:dev .
|
- docker build -t 103.82.242.92:8900/mediahub/new-mediahub-fe:dev .
|
||||||
- docker push 38.47.185.86:8900/mediahub/new-mediahub-fe:dev
|
- docker push 103.82.242.92:8900/mediahub/new-mediahub-fe:dev
|
||||||
|
|
||||||
auto-deploy:
|
auto-deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
|
@ -26,5 +25,7 @@ auto-deploy:
|
||||||
- main
|
- main
|
||||||
- dev-landing-v2
|
- dev-landing-v2
|
||||||
image: curlimages/curl:latest
|
image: curlimages/curl:latest
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
script:
|
script:
|
||||||
- curl --user admin:$JENKINS_PWD http://38.47.185.86:8080/job/auto-deploy-new-mediahub-fe/build?token=autodeploynewmediahub
|
- curl --user admin:$JENKINS_PWD http://38.47.180.165:8080/job/auto-deploy-new-mediahub-fe/build?token=autodeploynewmediahub
|
||||||
|
|
|
||||||
41
Dockerfile
41
Dockerfile
|
|
@ -25,49 +25,10 @@ RUN pnpm install
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build aplikasi
|
# Build aplikasi
|
||||||
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build
|
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm next build
|
||||||
|
|
||||||
# Expose port untuk server
|
# Expose port untuk server
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Perintah untuk menjalankan aplikasi
|
# Perintah untuk menjalankan aplikasi
|
||||||
CMD ["pnpm", "run", "start"]
|
CMD ["pnpm", "run", "start"]
|
||||||
|
|
||||||
|
|
||||||
# # Gunakan base image Node.js Alpine yang ringan
|
|
||||||
# FROM node:23.5.0-alpine
|
|
||||||
|
|
||||||
# # Atur environment
|
|
||||||
# ENV PORT=3000
|
|
||||||
# ENV NODE_ENV=production
|
|
||||||
# ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
||||||
|
|
||||||
# # Install dependencies global
|
|
||||||
# RUN npm install -g pnpm pm2
|
|
||||||
|
|
||||||
# # Set working directory
|
|
||||||
# WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
# # Salin file penting untuk caching dependencies
|
|
||||||
# COPY package.json pnpm-lock.yaml* ./
|
|
||||||
|
|
||||||
# # Salin vendor jika diperlukan (ckeditor misalnya)
|
|
||||||
# COPY vendor/ckeditor5 ./vendor/ckeditor5
|
|
||||||
|
|
||||||
# # Install dependencies
|
|
||||||
# RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# # Salin semua source code
|
|
||||||
# COPY . .
|
|
||||||
|
|
||||||
# # Salin ecosystem config
|
|
||||||
# COPY ecosystem.config.js ./
|
|
||||||
|
|
||||||
# # Build Next.js
|
|
||||||
# RUN pnpm run build
|
|
||||||
|
|
||||||
# # Expose port
|
|
||||||
# EXPOSE 3000
|
|
||||||
|
|
||||||
# # Jalankan Next.js dalam mode cluster
|
|
||||||
# CMD ["pm2-runtime", "start", "ecosystem.config.js"]
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { useRouter } from "next/navigation";
|
||||||
import { deleteUser } from "@/service/management-user/management-user";
|
import { deleteUser } from "@/service/management-user/management-user";
|
||||||
import { stringify } from "querystring";
|
import { stringify } from "querystring";
|
||||||
|
|
||||||
const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[] => [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
|
|
@ -30,13 +30,11 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
||||||
header: "Nama",
|
header: "Nama",
|
||||||
cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
|
cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "address",
|
accessorKey: "address",
|
||||||
header: "Wilayah",
|
header: "Wilayah",
|
||||||
cell: () => <span>MABES</span>,
|
cell: ({ row }) => <span>MABES</span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "userRolePlacements",
|
accessorKey: "userRolePlacements",
|
||||||
header: "Posisi",
|
header: "Posisi",
|
||||||
|
|
@ -54,7 +52,6 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
||||||
return <span>{posisi}</span>;
|
return <span>{posisi}</span>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "role.name",
|
accessorKey: "role.name",
|
||||||
header: "Bidang Keahlian",
|
header: "Bidang Keahlian",
|
||||||
|
|
@ -84,77 +81,72 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
const router = useRouter();
|
||||||
const doDelete = async (id: number) => {
|
const doDelete = async (id: number) => {
|
||||||
Swal.fire({
|
|
||||||
title: "Menghapus user...",
|
|
||||||
text: "Mohon tunggu",
|
|
||||||
allowOutsideClick: false,
|
|
||||||
didOpen: () => Swal.showLoading(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await deleteUser(id);
|
const response = await deleteUser(id);
|
||||||
|
|
||||||
Swal.close();
|
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
toast({
|
toast({
|
||||||
title: stringify(response?.message),
|
title: stringify(response?.message),
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
toast({
|
||||||
|
title: "Success delete",
|
||||||
|
});
|
||||||
|
|
||||||
toast({ title: "Berhasil menghapus user" });
|
router.push("?dataChange=true");
|
||||||
|
|
||||||
// ⬅️ INI YANG PENTING → REFRESH TABLE TANPA RELOAD
|
|
||||||
onRefresh();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (id: number) => {
|
const handleDelete = (id: number) => {
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Hapus user ini?",
|
title: "Apakah anda ingin menghapus data user?",
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: "#dc3545",
|
confirmButtonColor: "#dc3545",
|
||||||
confirmButtonText: "Iya",
|
confirmButtonText: "Iya",
|
||||||
cancelButtonText: "Tidak",
|
cancelButtonText: "Tidak",
|
||||||
}).then((res) => {
|
}).then((result) => {
|
||||||
if (res.isConfirmed) doDelete(id);
|
if (result.isConfirmed) {
|
||||||
|
doDelete(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button size="icon" variant="ghost">
|
<Button
|
||||||
<MoreVertical className="h-4 w-4" />
|
size="icon"
|
||||||
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
|
>
|
||||||
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link href={`/admin/add-experts/detail/${row?.original?.id}`}>
|
||||||
<Link href={`/admin/add-experts/detail/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
<Eye className="w-4 h-4 me-1.5" /> View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link href={`/admin/add-experts/update/${row.original.id}`}>
|
<Link href={`/admin/add-experts/update/${row?.original?.id}`}>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<SquarePen className="w-4 h-4 me-1.5" /> Edit
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDelete(row.original.userKeycloakId)}
|
onClick={() => handleDelete(row.original.userKeycloakId)}
|
||||||
className="text-red-600 cursor-pointer hover:bg-red-300"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
>
|
>
|
||||||
<Trash2 className="w-4 h-4 me-1.5" /> Delete
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
@ -162,4 +154,4 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default getColumns;
|
export default columns;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
|
ColumnDef,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
PaginationState,
|
PaginationState,
|
||||||
SortingState,
|
SortingState,
|
||||||
|
|
@ -14,6 +15,7 @@ import {
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -23,6 +25,7 @@ import {
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { UserIcon } from "lucide-react";
|
import { UserIcon } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -32,14 +35,43 @@ import {
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Input } from "@/components/ui/input";
|
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 { useRouter, useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
// import columns from "./column";
|
import columns from "./column";
|
||||||
import getColumns from "./column";
|
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { listDataMedia } from "@/service/broadcast/broadcast";
|
||||||
import { listEnableCategory } from "@/service/content/content";
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { close, loading } from "@/config/swal";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
import { listDataExperts } from "@/service/experts/experts";
|
import { listDataExperts } from "@/service/experts/experts";
|
||||||
|
|
||||||
|
const dummyData = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Prof. Dr. Ravi",
|
||||||
|
region: "Nasional",
|
||||||
|
skills: "Komunikasi",
|
||||||
|
experience: "Akademisi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Prof. Dr. Novan",
|
||||||
|
region: "DKI Jakarta",
|
||||||
|
skills: "Hukum",
|
||||||
|
experience: "Akademisi + Praktisi",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const AddExpertTable = () => {
|
const AddExpertTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
@ -65,8 +97,7 @@ const AddExpertTable = () => {
|
||||||
const [limit, setLimit] = React.useState(10);
|
const [limit, setLimit] = React.useState(10);
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
// columns,
|
columns,
|
||||||
columns: getColumns({ onRefresh: fetchData }),
|
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
onColumnFiltersChange: setColumnFilters,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
|
@ -170,7 +201,7 @@ const AddExpertTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between mb-10 items-center">
|
<div className="flex justify-between mb-10 items-center">
|
||||||
<p className="text-xl font-medium text-default-900">Tenaga Ahli</p>
|
<p className="text-xl font-medium text-default-900">Tenaga Ahli</p>
|
||||||
<Link href="/admin/add-experts/create">
|
<Link href="/admin/add-experts/create">
|
||||||
|
|
@ -252,11 +283,7 @@ const AddExpertTable = () => {
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
// colSpan={columns.length}
|
|
||||||
colSpan={table.getAllLeafColumns().length}
|
|
||||||
className="h-24 text-center"
|
|
||||||
>
|
|
||||||
No results.
|
No results.
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
||||||
|
|
@ -35,61 +35,32 @@ import {
|
||||||
import { error, loading } from "@/config/swal";
|
import { error, loading } from "@/config/swal";
|
||||||
import { Eye, EyeOff } from "lucide-react";
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
|
|
||||||
// const FormSchema = z.object({
|
|
||||||
// name: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// username: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// password: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// phoneNumber: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// email: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// skills: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// experiences: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// company: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
name: z.string({ required_error: "Required" }),
|
name: z.string({
|
||||||
username: z
|
required_error: "Required",
|
||||||
.string({ required_error: "Required" })
|
}),
|
||||||
.refine((val) => !/\s/.test(val), {
|
username: z.string({
|
||||||
message: "Username tidak boleh mengandung spasi",
|
required_error: "Required",
|
||||||
}),
|
}),
|
||||||
// .transform((val) => val.toLowerCase()),
|
password: z.string({
|
||||||
|
required_error: "Required",
|
||||||
password: z
|
}),
|
||||||
.string({ required_error: "Required" })
|
phoneNumber: z.string({
|
||||||
.min(8, "Minimal 8 karakter")
|
required_error: "Required",
|
||||||
.regex(/[A-Z]/, "Harus mengandung huruf besar (A-Z)")
|
}),
|
||||||
.regex(/[0-9]/, "Harus mengandung angka (0-9)")
|
email: z.string({
|
||||||
.regex(/[^A-Za-z0-9]/, "Harus mengandung karakter spesial (!@#$%^&*)"),
|
required_error: "Required",
|
||||||
|
}),
|
||||||
// confirmPassword: z.string({ required_error: "Required" }),
|
skills: z.string({
|
||||||
|
required_error: "Required",
|
||||||
phoneNumber: z.string({ required_error: "Required" }),
|
}),
|
||||||
email: z.string({ required_error: "Required" }),
|
experiences: z.string({
|
||||||
skills: z.string({ required_error: "Required" }),
|
required_error: "Required",
|
||||||
experiences: z.string({ required_error: "Required" }),
|
}),
|
||||||
company: z.string({ required_error: "Required" }),
|
company: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
// .refine((data) => data.password === data.confirmPassword, {
|
|
||||||
// path: ["confirmPassword"],
|
|
||||||
// message: "Konfirmasi password tidak sama",
|
|
||||||
// });
|
|
||||||
|
|
||||||
export type Placements = {
|
export type Placements = {
|
||||||
index: number;
|
index: number;
|
||||||
|
|
@ -103,7 +74,6 @@ export default function AddExpertForm() {
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
});
|
});
|
||||||
const [passwordStrength, setPasswordStrength] = useState("");
|
|
||||||
const [incrementId, setIncrementId] = useState(1);
|
const [incrementId, setIncrementId] = useState(1);
|
||||||
const [placementRows, setPlacementRows] = useState<Placements[]>([
|
const [placementRows, setPlacementRows] = useState<Placements[]>([
|
||||||
{ index: 0, roleId: "", userLevelId: 0 },
|
{ index: 0, roleId: "", userLevelId: 0 },
|
||||||
|
|
@ -165,7 +135,7 @@ export default function AddExpertForm() {
|
||||||
};
|
};
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
|
|
||||||
// check availability first
|
// check availability first
|
||||||
var placementArr: any[] = [];
|
var placementArr: any[] = [];
|
||||||
placementRows.forEach((row: any) => {
|
placementRows.forEach((row: any) => {
|
||||||
|
|
@ -291,19 +261,6 @@ export default function AddExpertForm() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeStrength = (password: string) => {
|
|
||||||
let score = 0;
|
|
||||||
if (password.length >= 8) score++;
|
|
||||||
if (/[A-Z]/.test(password)) score++;
|
|
||||||
if (/[0-9]/.test(password)) score++;
|
|
||||||
if (/[^A-Za-z0-9]/.test(password)) score++;
|
|
||||||
|
|
||||||
if (score <= 1) return "weak";
|
|
||||||
if (score === 2 || score === 3) return "medium";
|
|
||||||
if (score === 4) return "strong";
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
|
|
@ -311,7 +268,7 @@ export default function AddExpertForm() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-3 bg-white dark:bg-black rounded-sm p-4"
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
>
|
>
|
||||||
<p className="fonnt-semibold">Campaign</p>
|
<p className="fonnt-semibold">Campaign</p>
|
||||||
<FormField
|
<FormField
|
||||||
|
|
@ -331,39 +288,6 @@ export default function AddExpertForm() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
|
||||||
name="username"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Username (huruf kecil, tanpa spasi)</FormLabel>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value={field.value}
|
|
||||||
placeholder="masukkan username"
|
|
||||||
onChange={(e) => {
|
|
||||||
let value = e.target.value;
|
|
||||||
|
|
||||||
// Hapus spasi otomatis
|
|
||||||
value = value.replace(/\s+/g, "");
|
|
||||||
|
|
||||||
// Jadikan lowercase otomatis
|
|
||||||
value = value.toLowerCase();
|
|
||||||
|
|
||||||
field.onChange(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Info tambahan */}
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
Username otomatis menjadi huruf kecil tanpa spasi.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{/* <FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
|
@ -379,7 +303,7 @@ export default function AddExpertForm() {
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="phoneNumber"
|
name="phoneNumber"
|
||||||
|
|
@ -425,69 +349,6 @@ export default function AddExpertForm() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password</FormLabel>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
value={field.value}
|
|
||||||
placeholder="Masukkan Password"
|
|
||||||
onChange={(e) => {
|
|
||||||
field.onChange(e.target.value);
|
|
||||||
setPasswordStrength(computeStrength(e.target.value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-default-500"
|
|
||||||
>
|
|
||||||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<FormLabel className="text-gray-400 text-[12px]">
|
|
||||||
Password harus memiliki minimal 8 karakter, special karakter,
|
|
||||||
angka dan huruf kapital
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
{/* Strength meter */}
|
|
||||||
{field.value && (
|
|
||||||
<div className="mt-2">
|
|
||||||
<div
|
|
||||||
className={`h-2 rounded transition-all ${
|
|
||||||
passwordStrength === "weak"
|
|
||||||
? "bg-red-500 w-1/4"
|
|
||||||
: passwordStrength === "medium"
|
|
||||||
? "bg-yellow-500 w-2/4"
|
|
||||||
: "bg-green-500 w-full"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p
|
|
||||||
className={`text-xs mt-1 ${
|
|
||||||
passwordStrength === "weak"
|
|
||||||
? "text-red-500"
|
|
||||||
: passwordStrength === "medium"
|
|
||||||
? "text-yellow-600"
|
|
||||||
: "text-green-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{passwordStrength === "weak" && "Weak Password"}
|
|
||||||
{passwordStrength === "medium" && "Medium Password"}
|
|
||||||
{passwordStrength === "strong" && "Strong Password"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{/* <FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
|
@ -512,7 +373,7 @@ export default function AddExpertForm() {
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="skills"
|
name="skills"
|
||||||
|
|
|
||||||
|
|
@ -37,43 +37,32 @@ import { Eye, EyeOff } from "lucide-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
name: z.string().optional(),
|
name: z.string({
|
||||||
username: z.string().optional(),
|
required_error: "Required",
|
||||||
password: z.string().optional(),
|
}),
|
||||||
phoneNumber: z.string().optional(),
|
username: z.string({
|
||||||
email: z.string().optional(),
|
required_error: "Required",
|
||||||
skills: z.string().optional(),
|
}),
|
||||||
experiences: z.string().optional(),
|
password: z.string({
|
||||||
company: z.string().optional(),
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
phoneNumber: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
skills: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
experiences: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
company: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// const FormSchema = z.object({
|
|
||||||
// name: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// username: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// password: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// phoneNumber: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// email: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// skills: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// experiences: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// company: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
export type Placements = {
|
export type Placements = {
|
||||||
index: number;
|
index: number;
|
||||||
roleId?: string;
|
roleId?: string;
|
||||||
|
|
@ -107,10 +96,6 @@ interface Detail {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
userRolePlacements?: {
|
|
||||||
roleId: number;
|
|
||||||
userLevelId: number;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UpdateExpertForm() {
|
export default function UpdateExpertForm() {
|
||||||
|
|
@ -164,39 +149,6 @@ export default function UpdateExpertForm() {
|
||||||
initState();
|
initState();
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!detail) return;
|
|
||||||
|
|
||||||
// Isi semua form field
|
|
||||||
form.reset({
|
|
||||||
name: detail.fullname || "",
|
|
||||||
username: detail.username || "",
|
|
||||||
phoneNumber: detail.phoneNumber || "",
|
|
||||||
email: detail.email || "",
|
|
||||||
password: "",
|
|
||||||
skills: detail?.userProfilesAdditional?.userCompetency?.id
|
|
||||||
? String(detail.userProfilesAdditional.userCompetency.id)
|
|
||||||
: "",
|
|
||||||
experiences: detail?.userProfilesAdditional?.userExperienceId
|
|
||||||
? String(detail.userProfilesAdditional.userExperienceId)
|
|
||||||
: "",
|
|
||||||
company: detail?.userProfilesAdditional?.companyName || "",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 🔥 Masukkan posisi existing
|
|
||||||
if (detail.userRolePlacements && detail.userRolePlacements.length > 0) {
|
|
||||||
const mapped = detail.userRolePlacements.map(
|
|
||||||
(item: any, idx: number) => ({
|
|
||||||
index: idx,
|
|
||||||
roleId: String(item.roleId),
|
|
||||||
userLevelId: Number(item.userLevelId),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
setPlacementRows(mapped);
|
|
||||||
}
|
|
||||||
}, [detail]);
|
|
||||||
|
|
||||||
if (!detail) return <div>Loading...</div>;
|
if (!detail) return <div>Loading...</div>;
|
||||||
|
|
||||||
const togglePasswordType = () => {
|
const togglePasswordType = () => {
|
||||||
|
|
@ -237,35 +189,18 @@ export default function UpdateExpertForm() {
|
||||||
|
|
||||||
const dataReq = {
|
const dataReq = {
|
||||||
id: detail?.id,
|
id: detail?.id,
|
||||||
firstName: data.name || detail.fullname,
|
firstName: data.name,
|
||||||
username: data.username || detail.username,
|
username: data.username,
|
||||||
email: data.email || detail.email,
|
email: data.email,
|
||||||
password: data.password || undefined,
|
password: data.password,
|
||||||
address: "",
|
address: "",
|
||||||
roleId: "EXP-ID",
|
roleId: "EXP-ID",
|
||||||
phoneNumber: data.phoneNumber || detail.phoneNumber,
|
phoneNumber: data.phoneNumber,
|
||||||
userCompetencyId:
|
userCompetencyId: data.skills,
|
||||||
data.skills || detail.userProfilesAdditional?.userCompetency?.id,
|
userExperienceId: data.experiences,
|
||||||
userExperienceId:
|
companyName: data.company,
|
||||||
data.experiences || detail.userProfilesAdditional?.userExperienceId,
|
|
||||||
companyName: data.company || detail.userProfilesAdditional?.companyName,
|
|
||||||
isAdmin: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// const dataReq = {
|
|
||||||
// id: detail?.id,
|
|
||||||
// firstName: data.name,
|
|
||||||
// username: data.username,
|
|
||||||
// email: data.email,
|
|
||||||
// password: data.password,
|
|
||||||
// address: "",
|
|
||||||
// roleId: "EXP-ID",
|
|
||||||
// phoneNumber: data.phoneNumber,
|
|
||||||
// userCompetencyId: data.skills,
|
|
||||||
// userExperienceId: data.experiences,
|
|
||||||
// companyName: data.company,
|
|
||||||
// };
|
|
||||||
|
|
||||||
loading();
|
loading();
|
||||||
const res = await saveUserInternal(dataReq);
|
const res = await saveUserInternal(dataReq);
|
||||||
const resData = res?.data?.data;
|
const resData = res?.data?.data;
|
||||||
|
|
@ -387,15 +322,10 @@ export default function UpdateExpertForm() {
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Nama Lengkap</FormLabel>
|
<FormLabel>Nama Lengkap</FormLabel>
|
||||||
{/* <Input
|
<Input
|
||||||
defaultValue={detail?.fullname}
|
defaultValue={detail?.fullname}
|
||||||
placeholder="Masukkan Nama Lengkap"
|
placeholder="Masukkan Nama Lengkap"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
defaultValue={detail?.fullname}
|
|
||||||
placeholder="Masukkan Nama Lengkap"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
@ -403,24 +333,18 @@ export default function UpdateExpertForm() {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
disabled
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Username</FormLabel>
|
<FormLabel>Username</FormLabel>
|
||||||
{/* <Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
defaultValue={detail?.username}
|
defaultValue={detail?.username}
|
||||||
placeholder="Masukkan"
|
placeholder="Masukkan"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
defaultValue={detail?.username}
|
|
||||||
placeholder="Masukkan"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|
@ -431,17 +355,11 @@ export default function UpdateExpertForm() {
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>No. HP</FormLabel>
|
<FormLabel>No. HP</FormLabel>
|
||||||
{/* <Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={detail?.phoneNumber}
|
defaultValue={detail?.phoneNumber}
|
||||||
placeholder="Masukkan No.Hp"
|
placeholder="Masukkan No.Hp"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="number"
|
|
||||||
defaultValue={detail?.phoneNumber}
|
|
||||||
placeholder="Masukkan"
|
|
||||||
/>
|
/>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -453,46 +371,17 @@ export default function UpdateExpertForm() {
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Email</FormLabel>
|
<FormLabel>Email</FormLabel>
|
||||||
{/* <Input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
defaultValue={detail?.email}
|
defaultValue={detail?.email}
|
||||||
placeholder="Masukkan email"
|
placeholder="Masukkan email"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="email"
|
|
||||||
defaultValue={detail?.email}
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
/>
|
/>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{/* <FormField
|
<FormField
|
||||||
control={form.control}
|
|
||||||
name="password"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Password (Opsional)</FormLabel>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
placeholder="Kosongkan jika tidak ingin mengubah password"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2"
|
|
||||||
>
|
|
||||||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/> */}
|
|
||||||
{/* <FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
|
@ -517,7 +406,7 @@ export default function UpdateExpertForm() {
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="skills"
|
name="skills"
|
||||||
|
|
@ -592,21 +481,12 @@ export default function UpdateExpertForm() {
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Nama Institusi/Perusahaan</FormLabel>
|
<FormLabel>Nama Institusi/Perusahaan</FormLabel>
|
||||||
{/* <Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={detail?.userProfilesAdditional?.companyName || ""}
|
value={detail?.userProfilesAdditional?.companyName || ""}
|
||||||
placeholder="Nama Institusi/Perusahaan"
|
placeholder="Nama Institusi/Perusahaan"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/> */}
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
defaultValue={
|
|
||||||
detail?.userProfilesAdditional?.companyName || ""
|
|
||||||
}
|
|
||||||
placeholder="Nama Institusi/Perusahaan"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|
@ -617,7 +497,6 @@ export default function UpdateExpertForm() {
|
||||||
{placementRows?.map((row: any) => (
|
{placementRows?.map((row: any) => (
|
||||||
<div key={row.index} className="flex items-center gap-2 my-2">
|
<div key={row.index} className="flex items-center gap-2 my-2">
|
||||||
<Select
|
<Select
|
||||||
value={row.roleId}
|
|
||||||
onValueChange={(e) =>
|
onValueChange={(e) =>
|
||||||
handleSelectionChange(row.index, "roleId", e)
|
handleSelectionChange(row.index, "roleId", e)
|
||||||
}
|
}
|
||||||
|
|
@ -654,7 +533,6 @@ export default function UpdateExpertForm() {
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select> */}
|
</Select> */}
|
||||||
<Select
|
<Select
|
||||||
value={row.userLevelId}
|
|
||||||
onValueChange={(e) =>
|
onValueChange={(e) =>
|
||||||
handleSelectionChange(row.index, "userLevelId", e)
|
handleSelectionChange(row.index, "userLevelId", e)
|
||||||
}
|
}
|
||||||
|
|
@ -684,7 +562,7 @@ export default function UpdateExpertForm() {
|
||||||
type="button"
|
type="button"
|
||||||
size="md"
|
size="md"
|
||||||
onClick={handleAddRow}
|
onClick={handleAddRow}
|
||||||
disabled={placementRows.length >= 2}
|
disabled={placementRows.length >= 2} // optional: disable button if already 1 row added
|
||||||
>
|
>
|
||||||
Tambah
|
Tambah
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,8 @@ export default function ContentManagement() {
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Accordion id="polri" type="single" collapsible className="w-full">
|
<Accordion id="polri" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
KONTEN YANG DISIMPAN OLEH PENGGUNA POLRI INDONESIA
|
KONTEN YANG DISIMPAN OLEH PENGGUNA POLRI INDONESIA
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -161,8 +161,8 @@ export default function ContentManagement() {
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion id="2" type="single" collapsible className="w-full">
|
<Accordion id="2" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-2" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-2" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -189,8 +189,8 @@ export default function ContentManagement() {
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion id="3" type="single" collapsible className="w-full">
|
<Accordion id="3" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
KONTEN YANG DISIMPAN OLEH PENGGUNA JURNALIS INTERNASIONAL
|
KONTEN YANG DISIMPAN OLEH PENGGUNA JURNALIS INTERNASIONAL
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -217,8 +217,8 @@ export default function ContentManagement() {
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion id="4" type="single" collapsible className="w-full">
|
<Accordion id="4" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
PENAMBAHAN JUMLAH PENGGUNA POLRI INDONESIA
|
PENAMBAHAN JUMLAH PENGGUNA POLRI INDONESIA
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -245,8 +245,8 @@ export default function ContentManagement() {
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion id="5" type="single" collapsible className="w-full">
|
<Accordion id="5" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -273,8 +273,8 @@ export default function ContentManagement() {
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion id="6" type="single" collapsible className="w-full">
|
<Accordion id="6" type="single" collapsible className="w-full">
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INTERNASIONAL
|
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INTERNASIONAL
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,8 @@ export default function EmergencyIssue() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
ANALISA BERKAITAN DENGAN AKUN PELAPOR{" "}
|
ANALISA BERKAITAN DENGAN AKUN PELAPOR{" "}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,8 @@ export default function FeedbackCenter() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
TICKET PADA FEEDBACK CENTER{" "}
|
TICKET PADA FEEDBACK CENTER{" "}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,8 @@ export default function ContentManagement() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
PUBLISH JADWAL PRESS CONFERENCE TERBANYAK
|
PUBLISH JADWAL PRESS CONFERENCE TERBANYAK
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -147,8 +147,8 @@ export default function ContentManagement() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
JUMLAH PRODUKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
JUMLAH PRODUKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -180,8 +180,8 @@ export default function ContentManagement() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
TINGKAT INTERAKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
TINGKAT INTERAKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
@ -213,8 +213,8 @@ export default function ContentManagement() {
|
||||||
collapsible
|
collapsible
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
<AccordionItem value="item-1" className="bg-white w-full">
|
||||||
<AccordionTrigger className="bg-white dark:bg-black">
|
<AccordionTrigger className="bg-white">
|
||||||
AKTIFITAS MEDIA BERKAITAN DENGAN PERS RILIS
|
AKTIFITAS MEDIA BERKAITAN DENGAN PERS RILIS
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
|
|
|
||||||
|
|
@ -30,27 +30,34 @@ const columns: ColumnDef<any>[] = [
|
||||||
accessorKey: "accountName",
|
accessorKey: "accountName",
|
||||||
header: "Nama",
|
header: "Nama",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<span className="normal-case">{row.original.mediaBlastAccountName}</span>
|
<span className="normal-case">{row.getValue("accountName")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "accountType",
|
accessorKey: "accountType",
|
||||||
header: "Tipe Akun",
|
header: "Tipe Akun",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<span className="normal-case">{row.original.mediaBlastAccountType}</span>
|
<span className="normal-case">{row.getValue("accountType")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "accountCategory",
|
||||||
|
header: "Kategory",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="uppercase">{row.getValue("accountCategory")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "emailAddress",
|
accessorKey: "emailAddress",
|
||||||
header: "Email",
|
header: "Email",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<span className="normal-case">{row.original.mediaBlastAccountEmail}</span>
|
<span className="normal-case">{row.getValue("emailAddress")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "whatsappNumber",
|
accessorKey: "whatsappNumber",
|
||||||
header: "Whatsapp",
|
header: "Whatsapp",
|
||||||
cell: ({ row }) => <span>{row.original.mediaBlastAccountPhone}</span>,
|
cell: ({ row }) => <span>{row.getValue("whatsappNumber")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
|
@ -106,7 +113,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<Link
|
<Link
|
||||||
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.mediaBlastAccountId}`}
|
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.id}`}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
|
ColumnDef,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
PaginationState,
|
PaginationState,
|
||||||
SortingState,
|
SortingState,
|
||||||
|
|
@ -14,6 +15,7 @@ import {
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
|
@ -22,64 +24,25 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import {
|
import { UserIcon } from "lucide-react";
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
DialogFooter,
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
DialogHeader,
|
import columns from "./column";
|
||||||
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 {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Icon } from "@iconify/react";
|
import { close, loading } from "@/config/swal";
|
||||||
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,
|
|
||||||
getUserListAll,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
import { close, loading, error, success, successCallback } from "@/config/swal";
|
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
|
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||||
// 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 AccountListTable = () => {
|
||||||
const params = useParams();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const campaignId = params?.id as string;
|
|
||||||
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
|
@ -93,19 +56,10 @@ const AccountListTable = () => {
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [filtered, setFiltered] = React.useState<string[]>([]);
|
const [filtered, setFiltered] = React.useState<string[]>([]);
|
||||||
|
|
||||||
// --- state utk Dialog Pilih Akun ---
|
|
||||||
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
|
||||||
const [accountCategory, setAccountCategory] = React.useState<string>("");
|
|
||||||
const [selectedAccount, setSelectedAccount] = React.useState<any[]>([]);
|
|
||||||
const [selectedCategory, setSelectedCategory] = React.useState<string>("");
|
|
||||||
const [availableAccountsList, setAvailableAccountsList] =
|
|
||||||
React.useState<any[]>(availableAccounts);
|
|
||||||
const [usersList, setUsersList] = React.useState<any[]>([]);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -129,24 +83,24 @@ const AccountListTable = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get("page");
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) setPage(Number(pageFromUrl));
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [page, filtered]);
|
}, [page]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
loading();
|
loading();
|
||||||
const res = await getMediaBlastCampaignAccountList(
|
const res = await getMediaBlastAccountPage(
|
||||||
page - 1,
|
page - 1,
|
||||||
filtered ? filtered.join(",") : "",
|
filtered ? filtered.join(",") : ""
|
||||||
campaignId
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content || [];
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * 10 + index + 1;
|
item.no = (page - 1) * 10 + index + 1;
|
||||||
});
|
});
|
||||||
|
|
@ -155,349 +109,42 @@ const AccountListTable = () => {
|
||||||
setTotalData(data?.totalElements);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(data?.totalPages);
|
setTotalPage(data?.totalPages);
|
||||||
close();
|
close();
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
console.error("Error fetching tasks:", err);
|
console.error("Error fetching tasks:", error);
|
||||||
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 getUserListAll();
|
|
||||||
|
|
||||||
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) => {
|
const handleFilter = (id: string, checked: boolean) => {
|
||||||
let temp = [...filtered];
|
let temp = [...filtered];
|
||||||
if (checked) temp = [...temp, id];
|
if (checked) {
|
||||||
else temp = temp.filter((a) => a !== id);
|
temp = [...temp, id];
|
||||||
setFiltered(temp);
|
} else {
|
||||||
};
|
temp = temp.filter((a) => a !== id);
|
||||||
|
|
||||||
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;
|
setFiltered(temp);
|
||||||
|
console.log("sss", temp);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between mb-3 items-center">
|
<div className="flex justify-between mb-3 items-center">
|
||||||
<p className="text-xl font-medium text-default-900">Daftar Akun</p>
|
<p className="text-xl font-medium text-default-900">Daftar Akun</p>
|
||||||
<div className="flex flex-row gap-3">
|
<div className="flex flex-row gap-3">
|
||||||
{/* === Dialog Pilih Akun === */}
|
|
||||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button size="sm" className="text-sm">
|
|
||||||
<Icon icon="tdesign:user-add-filled" className="mr-2" />
|
|
||||||
Pilih Akun
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent
|
|
||||||
size="md"
|
|
||||||
className="max-w-xl max-h-[80vh] overflow-y-auto"
|
|
||||||
>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Pilih Akun Untuk Campaign Ini</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-4 my-3">
|
|
||||||
<RadioGroup
|
|
||||||
value={accountCategory}
|
|
||||||
onValueChange={(val) => {
|
|
||||||
setAccountCategory(val);
|
|
||||||
setSelectedAccount([]);
|
|
||||||
setSelectedCategory("");
|
|
||||||
if (val === "custom") {
|
|
||||||
fetchUsersList();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="flex space-x-6"
|
|
||||||
>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="all-account" id="all-account" />
|
|
||||||
<Label htmlFor="all-account">Semua Akun</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="kategori" id="kategori" />
|
|
||||||
<Label htmlFor="kategori">Kategori</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<RadioGroupItem value="custom" id="custom" />
|
|
||||||
<Label htmlFor="custom">Kustom</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
{/* Category Selection */}
|
|
||||||
{accountCategory === "kategori" && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Pilih Kategori:</Label>
|
|
||||||
<UISelect onValueChange={(val) => setSelectedCategory(val)}>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Pilih kategori" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="umum">Umum</SelectItem>
|
|
||||||
<SelectItem value="polri">Polri</SelectItem>
|
|
||||||
<SelectItem value="ksp">KSP</SelectItem>
|
|
||||||
<SelectItem value="jurnalis">Jurnalis</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</UISelect>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Custom Account Selection */}
|
|
||||||
{accountCategory === "custom" && (
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Label>Pilih User:</Label>
|
|
||||||
<ReactSelect
|
|
||||||
isMulti
|
|
||||||
options={usersList.map((user: any) => ({
|
|
||||||
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 && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>User Terpilih ({selectedAccount.length}):</Label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{selectedAccount.map((acc) => (
|
|
||||||
<Badge
|
|
||||||
key={acc.id}
|
|
||||||
className="flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{acc.fullname}
|
|
||||||
<X
|
|
||||||
className="h-3 w-3 cursor-pointer"
|
|
||||||
onClick={() => removeSelectedAccount(acc.id)}
|
|
||||||
/>
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* All Accounts Info */}
|
|
||||||
{accountCategory === "all-account" && (
|
|
||||||
<div className="p-3 bg-blue-50 rounded-md">
|
|
||||||
<p className="text-sm text-blue-700">
|
|
||||||
Semua akun akan ditambahkan ke campaign ini.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Category Accounts Info */}
|
|
||||||
{accountCategory === "kategori" && selectedCategory && (
|
|
||||||
<div className="p-3 bg-green-50 rounded-md">
|
|
||||||
<p className="text-sm text-green-700">
|
|
||||||
Semua akun dengan role "{selectedCategory.toUpperCase()}"
|
|
||||||
akan ditambahkan.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Custom Selection Info */}
|
|
||||||
{accountCategory === "custom" && (
|
|
||||||
<div className="p-3 bg-purple-50 rounded-md">
|
|
||||||
<p className="text-sm text-purple-700">
|
|
||||||
{selectedAccount.length} user terpilih akan ditambahkan ke
|
|
||||||
campaign ini.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
onClick={saveCampaignAccount}
|
|
||||||
disabled={
|
|
||||||
!accountCategory ||
|
|
||||||
(accountCategory === "custom" &&
|
|
||||||
selectedAccount.length < 1) ||
|
|
||||||
(accountCategory === "kategori" && !selectedCategory)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</Button>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button variant="outline" onClick={resetDialogState}>
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
</DialogClose>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* === Filter Akun === */}
|
|
||||||
<div className="flex flex-row justify-end">
|
|
||||||
{/* <div className="flex flex-row gap-4">
|
|
||||||
<Link href="/admin/broadcast/campaign-list/account-list/create">
|
<Link href="/admin/broadcast/campaign-list/account-list/create">
|
||||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
<Button color="primary" size="md" className="text-sm">
|
||||||
<span>
|
<Icon icon="tdesign:user-add-filled" />
|
||||||
<svg
|
Tambah Akun
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M17 13h-4v4h-2v-4H7v-2h4V7h2v4h4m2-8H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
Tambahkan Akun
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
{/* <Link href="/admin/broadcast/campaign-list/import">
|
||||||
<span>
|
<Button color="success" size="md" className="text-sm">
|
||||||
<svg
|
<UserIcon />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
Import Akun
|
||||||
width="24"
|
</Button>
|
||||||
height="24"
|
</Link> */}
|
||||||
viewBox="0 0 24 24"
|
</div>
|
||||||
>
|
</div>
|
||||||
<g fill="none">
|
<div className="flex justify-end">
|
||||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M12 2v6.5a1.5 1.5 0 0 0 1.5 1.5H20v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1h3.414l-1.121 1.121a1 1 0 1 0 1.414 1.415l2.829-2.829a1 1 0 0 0 0-1.414l-2.829-2.828a1 1 0 1 0-1.414 1.414L7.414 17H4V4a2 2 0 0 1 2-2zM4 17v2H3a1 1 0 1 1 0-2zM14 2.043a2 2 0 0 1 1 .543L19.414 7a2 2 0 0 1 .543 1H14z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
Import Akun
|
|
||||||
</Button>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button size="md" variant="outline">
|
<Button size="md" variant="outline">
|
||||||
|
|
@ -516,28 +163,65 @@ const AccountListTable = () => {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||||
{["polri", "jurnalis", "umum", "ksp"].map((cat) => (
|
<div className="flex items-center space-x-2">
|
||||||
<div key={cat} className="flex items-center space-x-2">
|
<Checkbox
|
||||||
<Checkbox
|
id="accepted"
|
||||||
id={cat}
|
checked={filtered.includes("polri")}
|
||||||
checked={filtered.includes(cat)}
|
onCheckedChange={(e) => handleFilter("polri", Boolean(e))}
|
||||||
onCheckedChange={(e) => handleFilter(cat, Boolean(e))}
|
/>
|
||||||
/>
|
<label
|
||||||
<label
|
htmlFor="accepted"
|
||||||
htmlFor={cat}
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
className="text-xs font-medium leading-none"
|
>
|
||||||
>
|
POLRI
|
||||||
{cat.toUpperCase()}
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
<div className="flex items-center space-x-2">
|
||||||
))}
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("jurnalis")}
|
||||||
|
onCheckedChange={(e) =>
|
||||||
|
handleFilter("jurnalis", Boolean(e))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
JURNALIS
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("umum")}
|
||||||
|
onCheckedChange={(e) => handleFilter("umum", Boolean(e))}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
UMUM
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id="accepted"
|
||||||
|
checked={filtered.includes("ksp")}
|
||||||
|
onCheckedChange={(e) => handleFilter("ksp", Boolean(e))}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="accepted"
|
||||||
|
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
KSP
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* === Table Data === */}
|
|
||||||
<Table className="overflow-hidden">
|
<Table className="overflow-hidden">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
@ -579,7 +263,6 @@ const AccountListTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<TablePagination
|
<TablePagination
|
||||||
table={table}
|
table={table}
|
||||||
totalData={totalData}
|
totalData={totalData}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
|
|
@ -13,80 +14,55 @@ import {
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { CalendarIcon } from "lucide-react";
|
||||||
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
|
import { getOnlyDate } from "@/utils/globals";
|
||||||
import {
|
import {
|
||||||
getMediaBlastCampaignPage,
|
|
||||||
saveMediaBlastAccount,
|
saveMediaBlastAccount,
|
||||||
|
saveMediaBlastCampaign,
|
||||||
} from "@/service/broadcast/broadcast";
|
} from "@/service/broadcast/broadcast";
|
||||||
import { error } from "@/config/swal";
|
import { error } from "@/config/swal";
|
||||||
import { useRouter } from "@/i18n/routing";
|
import { useRouter } from "@/i18n/routing";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { useEffect, useState } from "react";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
|
||||||
// ----------------------------
|
|
||||||
// ZOD SCHEMA (dinamis)
|
|
||||||
// ----------------------------
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
name: z.string({ required_error: "Required" }),
|
name: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
accountType: z
|
accountType: z
|
||||||
.array(z.string())
|
.array(z.string())
|
||||||
.min(1, "Pilih minimal satu tipe akun"),
|
.refine((value) => value.some((item) => item), {
|
||||||
|
message: "Required",
|
||||||
email: z.string().optional(),
|
}),
|
||||||
whatsapp: z.string().optional(),
|
accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
||||||
|
required_error: "Required",
|
||||||
campaignId: z.string({ required_error: "Required" }),
|
}),
|
||||||
}).refine(
|
email: z.string({
|
||||||
(data) => {
|
required_error: "Required",
|
||||||
if (data.accountType.includes("email") && !data.email) return false;
|
}),
|
||||||
return true;
|
whatsapp: z.string({
|
||||||
},
|
required_error: "Required",
|
||||||
{ message: "Email wajib diisi", path: ["email"] }
|
}),
|
||||||
).refine(
|
});
|
||||||
(data) => {
|
|
||||||
if (data.accountType.includes("wa") && !data.whatsapp) return false;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{ message: "Whatsapp wajib diisi", path: ["whatsapp"] }
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------
|
|
||||||
// COMPONENT
|
|
||||||
// ----------------------------
|
|
||||||
|
|
||||||
export default function CreateAccountForBroadcast() {
|
export default function CreateAccountForBroadcast() {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
defaultValues: {
|
defaultValues: { accountType: [] },
|
||||||
accountType: [],
|
|
||||||
email: "",
|
|
||||||
whatsapp: "",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedTypes = form.watch("accountType");
|
|
||||||
const [campaigns, setCampaigns] = useState<any[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchCampaignList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function fetchCampaignList() {
|
|
||||||
try {
|
|
||||||
const res = await getMediaBlastCampaignPage(0);
|
|
||||||
setCampaigns(res?.data?.data?.content ?? []);
|
|
||||||
} catch (e) {
|
|
||||||
console.log("Error fetch campaign:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
MySwal.fire({
|
MySwal.fire({
|
||||||
title: "Simpan Data",
|
title: "Simpan Data",
|
||||||
|
|
@ -109,8 +85,10 @@ export default function CreateAccountForBroadcast() {
|
||||||
icon: "success",
|
icon: "success",
|
||||||
confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then(() => {
|
}).then((result) => {
|
||||||
router.push("/admin/broadcast/campaign-list/account-list");
|
if (result.isConfirmed) {
|
||||||
|
router.push("/admin/broadcast/campaign-list/account-list");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,21 +96,20 @@ export default function CreateAccountForBroadcast() {
|
||||||
const reqData = {
|
const reqData = {
|
||||||
accountName: data.name,
|
accountName: data.name,
|
||||||
accountType: data.accountType.join(","),
|
accountType: data.accountType.join(","),
|
||||||
emailAddress: data.email ?? "",
|
accountCategory: data.accountCategory,
|
||||||
whatsappNumber: data.whatsapp ?? "",
|
emailAddress: data.email,
|
||||||
campaignId: data.campaignId,
|
whatsappNumber: data.whatsapp,
|
||||||
};
|
};
|
||||||
|
console.log("data", data);
|
||||||
console.log("REQ:", reqData);
|
|
||||||
|
|
||||||
const response = await saveMediaBlastAccount(reqData);
|
const response = await saveMediaBlastAccount(reqData);
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
error(response.message);
|
error(response.message);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
successSubmit();
|
successSubmit();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
|
|
@ -141,9 +118,7 @@ export default function CreateAccountForBroadcast() {
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-3 bg-white rounded-sm p-4"
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
>
|
>
|
||||||
<p className="font-semibold">Account</p>
|
<p className="fonnt-semibold">Account</p>
|
||||||
|
|
||||||
{/* NAMA */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|
@ -155,125 +130,172 @@ export default function CreateAccountForBroadcast() {
|
||||||
placeholder="Masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* CHECKBOX TIPE AKUN */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="accountType"
|
name="accountType"
|
||||||
render={({ field }) => (
|
render={() => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Tipe Akun</FormLabel>
|
<FormLabel>Tipe Akun</FormLabel>
|
||||||
<div className="flex flex-row gap-4">
|
<div className="flex flex-row gap-2">
|
||||||
{/* WA */}
|
{" "}
|
||||||
<div className="flex items-center gap-2">
|
<FormField
|
||||||
<Checkbox
|
key="wa"
|
||||||
checked={field.value.includes("wa")}
|
control={form.control}
|
||||||
onCheckedChange={(checked) =>
|
name="accountType"
|
||||||
checked
|
render={({ field }) => {
|
||||||
? field.onChange([...field.value, "wa"])
|
return (
|
||||||
: field.onChange(field.value.filter((v) => v !== "wa"))
|
<FormItem
|
||||||
}
|
key="wa"
|
||||||
/>
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
<label>Whatsapp</label>
|
>
|
||||||
</div>
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
{/* EMAIL */}
|
checked={field.value?.includes("wa")}
|
||||||
<div className="flex items-center gap-2">
|
onCheckedChange={(checked) => {
|
||||||
<Checkbox
|
return checked
|
||||||
checked={field.value.includes("email")}
|
? field.onChange([...field.value, "wa"])
|
||||||
onCheckedChange={(checked) =>
|
: field.onChange(
|
||||||
checked
|
field.value?.filter(
|
||||||
? field.onChange([...field.value, "email"])
|
(value) => value !== "wa"
|
||||||
: field.onChange(
|
)
|
||||||
field.value.filter((v) => v !== "email")
|
);
|
||||||
)
|
}}
|
||||||
}
|
/>
|
||||||
/>
|
</FormControl>
|
||||||
<label>Email</label>
|
<FormLabel className="font-normal">
|
||||||
</div>
|
Whatsapp
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
key="email"
|
||||||
|
control={form.control}
|
||||||
|
name="accountType"
|
||||||
|
render={({ field }) => {
|
||||||
|
return (
|
||||||
|
<FormItem
|
||||||
|
key="email"
|
||||||
|
className="flex flex-row items-start space-x-3 space-y-0"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes("email")}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
return checked
|
||||||
|
? field.onChange([...field.value, "email"])
|
||||||
|
: field.onChange(
|
||||||
|
field.value?.filter(
|
||||||
|
(value) => value !== "email"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">Email</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* FORM WHATSAPP */}
|
|
||||||
{selectedTypes.includes("wa") && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="whatsapp"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Whatsapp</FormLabel>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder="Masukkan nomor Whatsapp"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* FORM EMAIL */}
|
|
||||||
{selectedTypes.includes("email") && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="email"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Masukkan email"
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* CAMPAIGN */}
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="campaignId"
|
name="accountCategory"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem className="space-y-3">
|
||||||
<FormLabel>Campaign</FormLabel>
|
<FormLabel>Kategori</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<select
|
<RadioGroup
|
||||||
className="w-full border rounded-md p-2 text-sm"
|
onValueChange={field.onChange}
|
||||||
value={field.value}
|
defaultValue={field.value}
|
||||||
onChange={field.onChange}
|
className="flex flex-row gap-2"
|
||||||
>
|
>
|
||||||
<option value="" className="text-slate-400">
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
Pilih campaign
|
<FormControl>
|
||||||
</option>
|
<RadioGroupItem value="polri" />
|
||||||
|
</FormControl>
|
||||||
{campaigns.map((c: any) => (
|
<FormLabel className="font-normal">POLRI</FormLabel>
|
||||||
<option key={c.id} value={c.id}>
|
</FormItem>
|
||||||
{c.title || `Campaign ${c.id}`}
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
</option>
|
<FormControl>
|
||||||
))}
|
<RadioGroupItem value="jurnalis" />
|
||||||
</select>
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">JURNALIS</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="umum" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">UMUM</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroupItem value="ksp" />
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel className="font-normal">KSP</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan email"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* BUTTON */}
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="whatsapp"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Nama</FormLabel>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={field.value}
|
||||||
|
placeholder="Masukkan whatsapp"
|
||||||
|
onChange={field.onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="flex flex-row gap-2 mt-4 pt-4">
|
<div className="flex flex-row gap-2 mt-4 pt-4">
|
||||||
<Button type="button" variant="outline" color="destructive">
|
<Button
|
||||||
|
size="md"
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
color="destructive"
|
||||||
|
className="text-xs"
|
||||||
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" color="primary">
|
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -282,380 +304,3 @@ export default function CreateAccountForBroadcast() {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// "use client";
|
|
||||||
// import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
// import { z } from "zod";
|
|
||||||
// import { useForm } from "react-hook-form";
|
|
||||||
// import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
// import {
|
|
||||||
// Form,
|
|
||||||
// FormControl,
|
|
||||||
// FormField,
|
|
||||||
// FormItem,
|
|
||||||
// FormLabel,
|
|
||||||
// FormMessage,
|
|
||||||
// } from "@/components/ui/form";
|
|
||||||
// import withReactContent from "sweetalert2-react-content";
|
|
||||||
// import Swal from "sweetalert2";
|
|
||||||
// import { Input } from "@/components/ui/input";
|
|
||||||
// import { Button } from "@/components/ui/button";
|
|
||||||
// import {
|
|
||||||
// getMediaBlastCampaignPage,
|
|
||||||
// saveMediaBlastAccount,
|
|
||||||
// } from "@/service/broadcast/broadcast";
|
|
||||||
// import { error } from "@/config/swal";
|
|
||||||
// import { useRouter } from "@/i18n/routing";
|
|
||||||
// import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
// import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
||||||
// import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
// // const FormSchema = z.object({
|
|
||||||
// // name: z.string({
|
|
||||||
// // required_error: "Required",
|
|
||||||
// // }),
|
|
||||||
// // accountType: z
|
|
||||||
// // .array(z.string())
|
|
||||||
// // .refine((value) => value.some((item) => item), {
|
|
||||||
// // message: "Required",
|
|
||||||
// // }),
|
|
||||||
// // accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
|
||||||
// // required_error: "Required",
|
|
||||||
// // }),
|
|
||||||
// // email: z.string({
|
|
||||||
// // required_error: "Required",
|
|
||||||
// // }),
|
|
||||||
// // whatsapp: z.string({
|
|
||||||
// // required_error: "Required",
|
|
||||||
// // }),
|
|
||||||
// // campaignId: z.string({ required_error: "Required" }),
|
|
||||||
// // });
|
|
||||||
// const FormSchema = z
|
|
||||||
// .object({
|
|
||||||
// name: z.string().min(1, "Required"),
|
|
||||||
|
|
||||||
// accountType: z.array(z.string()).refine((value) => value.length > 0, {
|
|
||||||
// message: "Pilih minimal satu tipe akun",
|
|
||||||
// }),
|
|
||||||
|
|
||||||
// accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
|
|
||||||
// email: z.string().optional(),
|
|
||||||
// whatsapp: z.string().optional(),
|
|
||||||
|
|
||||||
// campaignId: z.string().min(1, "Required"),
|
|
||||||
// })
|
|
||||||
// .refine(
|
|
||||||
// (data) => {
|
|
||||||
// if (data.accountType.includes("email")) {
|
|
||||||
// return !!data.email && data.email.trim() !== "";
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// },
|
|
||||||
// { path: ["email"], message: "Email wajib diisi" }
|
|
||||||
// )
|
|
||||||
// .refine(
|
|
||||||
// (data) => {
|
|
||||||
// if (data.accountType.includes("wa")) {
|
|
||||||
// return !!data.whatsapp && data.whatsapp.trim() !== "";
|
|
||||||
// }
|
|
||||||
// return true;
|
|
||||||
// },
|
|
||||||
// { path: ["whatsapp"], message: "Whatsapp wajib diisi" }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// export default function CreateAccountForBroadcast() {
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
// const router = useRouter();
|
|
||||||
// const form = useForm<z.infer<typeof FormSchema>>({
|
|
||||||
// resolver: zodResolver(FormSchema),
|
|
||||||
// defaultValues: { accountType: [] },
|
|
||||||
// });
|
|
||||||
// const selectedTypes = form.watch("accountType");
|
|
||||||
// const [campaigns, setCampaigns] = useState<any[]>([]);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// fetchCampaignList();
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// async function fetchCampaignList() {
|
|
||||||
// try {
|
|
||||||
// const res = await getMediaBlastCampaignPage(0);
|
|
||||||
// setCampaigns(res?.data?.data?.content ?? []);
|
|
||||||
// } catch (e) {
|
|
||||||
// console.log("Error fetch campaign:", e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Simpan Data",
|
|
||||||
// text: "Apakah Anda yakin ingin menyimpan data ini?",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#d33",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "Simpan",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// save(data);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// function successSubmit() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// router.push("/admin/broadcast/campaign-list/account-list");
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const save = async (data: z.infer<typeof FormSchema>) => {
|
|
||||||
// const reqData = {
|
|
||||||
// accountName: data.name,
|
|
||||||
// accountType: data.accountType.join(","),
|
|
||||||
// accountCategory: data.accountCategory,
|
|
||||||
// emailAddress: data.email ?? "",
|
|
||||||
// whatsappNumber: data.whatsapp ?? "",
|
|
||||||
// campaignId: data.campaignId,
|
|
||||||
// };
|
|
||||||
// console.log("data", data);
|
|
||||||
|
|
||||||
// const response = await saveMediaBlastAccount(reqData);
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// successSubmit();
|
|
||||||
// };
|
|
||||||
// return (
|
|
||||||
// <div>
|
|
||||||
// <SiteBreadcrumb />
|
|
||||||
// <Form {...form}>
|
|
||||||
// <form
|
|
||||||
// onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
// className="space-y-3 bg-white rounded-sm p-4"
|
|
||||||
// >
|
|
||||||
// <p className="fonnt-semibold">Account</p>
|
|
||||||
// <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="name"
|
|
||||||
// render={({ field }) => (
|
|
||||||
// <FormItem>
|
|
||||||
// <FormLabel>Nama</FormLabel>
|
|
||||||
// <Input
|
|
||||||
// value={field.value}
|
|
||||||
// placeholder="Masukkan nama"
|
|
||||||
// onChange={field.onChange}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// />
|
|
||||||
// <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="accountType"
|
|
||||||
// render={() => (
|
|
||||||
// <FormItem>
|
|
||||||
// <FormLabel>Tipe Akun</FormLabel>
|
|
||||||
// <div className="flex flex-row gap-2">
|
|
||||||
// {" "}
|
|
||||||
// <FormField
|
|
||||||
// key="wa"
|
|
||||||
// control={form.control}
|
|
||||||
// name="accountType"
|
|
||||||
// render={({ field }) => {
|
|
||||||
// return (
|
|
||||||
// <FormItem
|
|
||||||
// key="wa"
|
|
||||||
// className="flex flex-row items-start space-x-3 space-y-0"
|
|
||||||
// >
|
|
||||||
// <FormControl>
|
|
||||||
// <Checkbox
|
|
||||||
// checked={field.value?.includes("wa")}
|
|
||||||
// onCheckedChange={(checked) => {
|
|
||||||
// return checked
|
|
||||||
// ? field.onChange([...field.value, "wa"])
|
|
||||||
// : field.onChange(
|
|
||||||
// field.value?.filter(
|
|
||||||
// (value) => value !== "wa"
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">
|
|
||||||
// Whatsapp
|
|
||||||
// </FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// );
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// <FormField
|
|
||||||
// key="email"
|
|
||||||
// control={form.control}
|
|
||||||
// name="accountType"
|
|
||||||
// render={({ field }) => {
|
|
||||||
// return (
|
|
||||||
// <FormItem
|
|
||||||
// key="email"
|
|
||||||
// className="flex flex-row items-start space-x-3 space-y-0"
|
|
||||||
// >
|
|
||||||
// <FormControl>
|
|
||||||
// <Checkbox
|
|
||||||
// checked={field.value?.includes("email")}
|
|
||||||
// onCheckedChange={(checked) => {
|
|
||||||
// return checked
|
|
||||||
// ? field.onChange([...field.value, "email"])
|
|
||||||
// : field.onChange(
|
|
||||||
// field.value?.filter(
|
|
||||||
// (value) => value !== "email"
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">Email</FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// );
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
// </div>
|
|
||||||
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// />
|
|
||||||
// {/* <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="accountCategory"
|
|
||||||
// render={({ field }) => (
|
|
||||||
// <FormItem className="space-y-3">
|
|
||||||
// <FormLabel>Kategori</FormLabel>
|
|
||||||
// <FormControl>
|
|
||||||
// <RadioGroup
|
|
||||||
// onValueChange={field.onChange}
|
|
||||||
// defaultValue={field.value}
|
|
||||||
// className="flex flex-row gap-2"
|
|
||||||
// >
|
|
||||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
// <FormControl>
|
|
||||||
// <RadioGroupItem value="polri" />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">POLRI</FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
// <FormControl>
|
|
||||||
// <RadioGroupItem value="jurnalis" />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">JURNALIS</FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
// <FormControl>
|
|
||||||
// <RadioGroupItem value="umum" />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">UMUM</FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// <FormItem className="flex items-center space-x-3 space-y-0">
|
|
||||||
// <FormControl>
|
|
||||||
// <RadioGroupItem value="ksp" />
|
|
||||||
// </FormControl>
|
|
||||||
// <FormLabel className="font-normal">KSP</FormLabel>
|
|
||||||
// </FormItem>
|
|
||||||
// </RadioGroup>
|
|
||||||
// </FormControl>
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// /> */}
|
|
||||||
// <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="email"
|
|
||||||
// render={({ field }) => (
|
|
||||||
// <FormItem>
|
|
||||||
// <FormLabel>Email</FormLabel>
|
|
||||||
// <Input
|
|
||||||
// type="email"
|
|
||||||
// value={field.value}
|
|
||||||
// placeholder="Masukkan email"
|
|
||||||
// onChange={field.onChange}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// />
|
|
||||||
// <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="whatsapp"
|
|
||||||
// render={({ field }) => (
|
|
||||||
// <FormItem>
|
|
||||||
// <FormLabel>Nama</FormLabel>
|
|
||||||
// <Input
|
|
||||||
// type="number"
|
|
||||||
// value={field.value}
|
|
||||||
// placeholder="Masukkan whatsapp"
|
|
||||||
// onChange={field.onChange}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// />
|
|
||||||
// <FormField
|
|
||||||
// control={form.control}
|
|
||||||
// name="campaignId"
|
|
||||||
// render={({ field }) => (
|
|
||||||
// <FormItem>
|
|
||||||
// <FormLabel>Campaign</FormLabel>
|
|
||||||
// <FormControl>
|
|
||||||
// <select
|
|
||||||
// className="w-full border rounded-md p-2"
|
|
||||||
// value={field.value}
|
|
||||||
// onChange={field.onChange}
|
|
||||||
// >
|
|
||||||
// <option value="" className="text-slate-400">
|
|
||||||
// Pilih campaign
|
|
||||||
// </option>
|
|
||||||
|
|
||||||
// {campaigns.map((c: any) => (
|
|
||||||
// <option key={c.id} value={c.id}>
|
|
||||||
// {c.title || `Campaign ${c.id}`}
|
|
||||||
// </option>
|
|
||||||
// ))}
|
|
||||||
// </select>
|
|
||||||
// </FormControl>
|
|
||||||
// <FormMessage />
|
|
||||||
// </FormItem>
|
|
||||||
// )}
|
|
||||||
// />
|
|
||||||
// <div className="flex flex-row gap-2 mt-4 pt-4">
|
|
||||||
// <Button
|
|
||||||
// size="md"
|
|
||||||
// type="button"
|
|
||||||
// variant="outline"
|
|
||||||
// color="destructive"
|
|
||||||
// className="text-xs"
|
|
||||||
// >
|
|
||||||
// Cancel
|
|
||||||
// </Button>
|
|
||||||
// <Button size="md" type="submit" color="primary" className="text-xs">
|
|
||||||
// Submit
|
|
||||||
// </Button>
|
|
||||||
// </div>
|
|
||||||
// </form>
|
|
||||||
// </Form>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -37,74 +37,46 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import {
|
|
||||||
getUserById,
|
|
||||||
saveUserInternal,
|
|
||||||
} from "@/service/management-user/management-user";
|
|
||||||
|
|
||||||
// const FormSchema = z.object({
|
|
||||||
// fullname: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// // accountType: z
|
|
||||||
// // .array(z.string())
|
|
||||||
// // .refine((value) => value.some((item) => item), {
|
|
||||||
// // message: "Required",
|
|
||||||
// // }),
|
|
||||||
// // accountCategory: z.enum(["polri", "jurnalis", "umum", "ksp"], {
|
|
||||||
// // required_error: "Required",
|
|
||||||
// // }),
|
|
||||||
// email: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// phoneNumber: z.string({
|
|
||||||
// required_error: "Required",
|
|
||||||
// }),
|
|
||||||
// });
|
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
fullname: z.string({ required_error: "Required" }),
|
name: z.string({
|
||||||
email: z.string({ required_error: "Required" }),
|
required_error: "Required",
|
||||||
phoneNumber: z.string({ required_error: "Required" }),
|
}),
|
||||||
username: z.string().optional(),
|
accountType: z
|
||||||
role: z.string().optional(),
|
.array(z.string())
|
||||||
level: z.string().optional(),
|
.refine((value) => value.some((item) => item), {
|
||||||
nrp: z.string().optional(),
|
message: "Required",
|
||||||
address: z.string().optional(),
|
}),
|
||||||
password: z.string().optional(),
|
accountCategory: z.enum(["polri", "jurnalis", "umumu", "ksp"], {
|
||||||
confirmPassword: z.string().optional(),
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
email: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
|
whatsapp: z.string({
|
||||||
|
required_error: "Required",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function EditAccountForBroadcast() {
|
export default function EditAccountForBroadcast() {
|
||||||
const id = useParams()?.id;
|
const id = useParams()?.id;
|
||||||
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
// defaultValues: { accountType: [] },
|
defaultValues: { accountType: [] },
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getDetailData() {
|
async function getDetailData() {
|
||||||
const response = await getUserById(String(id));
|
const response = await getMediaBlastAccount(String(id));
|
||||||
const details = response?.data?.data;
|
const details = response?.data?.data;
|
||||||
console.log("Response full:", response);
|
console.log("new", details);
|
||||||
form.setValue("fullname", details?.fullname);
|
form.setValue("name", details.accountName);
|
||||||
form.setValue("username", details?.username);
|
form.setValue("email", details?.emailAddress);
|
||||||
form.setValue("phoneNumber", details?.phoneNumber);
|
form.setValue("whatsapp", details?.whatsappNumber);
|
||||||
// form.setValue("nrp", details?.memberIdentity);
|
form.setValue("accountCategory", details?.accountCategory);
|
||||||
// form.setValue("address", details?.address);
|
form.setValue("accountType", details?.accountType.split(","));
|
||||||
form.setValue("email", details?.email);
|
|
||||||
form.setValue("role", details?.role?.code);
|
|
||||||
// form.setValue("level", String(details?.userLevelId));
|
|
||||||
// form.setValue("name", details?.accountName);
|
|
||||||
// form.setValue("fullname", details?.fullname);
|
|
||||||
// form.setValue("email", details?.email);
|
|
||||||
// form.setValue("phoneNumber", details?.phoneNumber);
|
|
||||||
// form.setValue("whatsapp", details?.whatsappNumber);
|
|
||||||
// form.setValue("accountCategory", details?.accountCategory);
|
|
||||||
// form.setValue("accountType", details?.accountType.split(","));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetailData();
|
getDetailData();
|
||||||
|
|
@ -134,32 +106,23 @@ export default function EditAccountForBroadcast() {
|
||||||
confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
router.push("/admin/broadcast/campaign-list");
|
router.push("/admin/broadcast/campaign-list/account-list");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const save = async (data: z.infer<typeof FormSchema>) => {
|
const save = async (data: z.infer<typeof FormSchema>) => {
|
||||||
const reqData = {
|
const reqData = {
|
||||||
id: Number(id),
|
id: String(id),
|
||||||
// accountName: data.fullname,
|
accountName: data.name,
|
||||||
// accountType: data.accountType.join(","),
|
accountType: data.accountType.join(","),
|
||||||
// accountCategory: data.accountCategory,
|
accountCategory: data.accountCategory,
|
||||||
// emailAddress: data.email,
|
emailAddress: data.email,
|
||||||
phoneNumber: data.phoneNumber,
|
whatsappNumber: data.whatsapp,
|
||||||
firstName: data.fullname,
|
|
||||||
username: data.username,
|
|
||||||
roleId: data.role,
|
|
||||||
// userLevelId: Number(data.level),
|
|
||||||
// memberIdentity: data.nrp,
|
|
||||||
// address: data.address,
|
|
||||||
email: data.email,
|
|
||||||
isDefault: false,
|
|
||||||
isAdmin: true,
|
|
||||||
};
|
};
|
||||||
console.log("data", data);
|
console.log("data", data);
|
||||||
|
|
||||||
const response = await saveUserInternal(reqData);
|
const response = await saveMediaBlastAccount(reqData);
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
error(response.message);
|
error(response.message);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -178,7 +141,7 @@ export default function EditAccountForBroadcast() {
|
||||||
<p className="fonnt-semibold">Account</p>
|
<p className="fonnt-semibold">Account</p>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="fullname"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Nama</FormLabel>
|
<FormLabel>Nama</FormLabel>
|
||||||
|
|
@ -192,7 +155,7 @@ export default function EditAccountForBroadcast() {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{/* <FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="accountType"
|
name="accountType"
|
||||||
render={() => (
|
render={() => (
|
||||||
|
|
@ -264,8 +227,8 @@ export default function EditAccountForBroadcast() {
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
{/* <FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="accountCategory"
|
name="accountCategory"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
|
|
@ -306,13 +269,13 @@ export default function EditAccountForBroadcast() {
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/> */}
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Email</FormLabel>
|
<FormLabel>Nama</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
type="email"
|
type="email"
|
||||||
value={field.value}
|
value={field.value}
|
||||||
|
|
@ -326,14 +289,14 @@ export default function EditAccountForBroadcast() {
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="phoneNumber"
|
name="whatsapp"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Nomor Whatsapp</FormLabel>
|
<FormLabel>Nama</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
value={field.value}
|
value={field.value}
|
||||||
placeholder="Masukkan nomor whatsapp"
|
placeholder="Masukkan whatsapp"
|
||||||
onChange={field.onChange}
|
onChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,8 @@ import {
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
import { close, error, loading, success } from "@/config/swal";
|
|
||||||
import { deleteMediaBlastCampaign } from "@/service/broadcast/broadcast";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -61,47 +58,10 @@ const columns: ColumnDef<any>[] = [
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
accessorKey: "action",
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
cell: ({ row, onDeleteSuccess }: any) => {
|
enableHiding: false,
|
||||||
const MySwal = withReactContent(Swal);
|
cell: ({ row }) => {
|
||||||
|
|
||||||
const handleDelete = (id: number) => {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Apakah anda ingin menghapus data?",
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonColor: "#dc3545",
|
|
||||||
confirmButtonText: "Iya",
|
|
||||||
cancelButtonText: "Tidak",
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
doDeleteAccount(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async function doDeleteAccount(id: number) {
|
|
||||||
loading();
|
|
||||||
const response = await deleteMediaBlastCampaign(id);
|
|
||||||
close();
|
|
||||||
|
|
||||||
if (response.error) {
|
|
||||||
error(response.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
console.log("Delete response:", response);
|
|
||||||
|
|
||||||
MySwal.fire({
|
|
||||||
icon: "success",
|
|
||||||
title: "Berhasil!",
|
|
||||||
text: "Data berhasil dihapus.",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
timer: 2000,
|
|
||||||
timerProgressBar: true,
|
|
||||||
});
|
|
||||||
// ✅ panggil callback dari parent
|
|
||||||
onDeleteSuccess?.(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -113,113 +73,28 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
<Link
|
||||||
>
|
href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
>
|
||||||
Detail
|
Detail
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
<Link
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
href={`//admin/broadcast/campaign-list/edit/${row.original.id}`}
|
<Link
|
||||||
>
|
href={`//admin/broadcast/campaign-list/edit/${row.original.id}`}
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
>
|
||||||
Edit
|
Edit
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
onClick={() => handleDelete(row.original.id)}
|
<a>Delete</a>
|
||||||
className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// accessorKey: "action",
|
|
||||||
// header: "Actions",
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row, onDeleteSuccess }: any) => {
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// const handleDelete = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Apakah anda ingin menghapus data?",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// confirmButtonColor: "#dc3545",
|
|
||||||
// confirmButtonText: "Iya",
|
|
||||||
// cancelButtonText: "Tidak",
|
|
||||||
// }).then((result: any) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// doDeleteAccount(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// async function doDeleteAccount(id: any) {
|
|
||||||
// loading();
|
|
||||||
// const response = await deleteMediaBlastCampaign(id);
|
|
||||||
// close();
|
|
||||||
|
|
||||||
// if (response.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// console.log("Delete response:", response);
|
|
||||||
|
|
||||||
// MySwal.fire({
|
|
||||||
// icon: "success",
|
|
||||||
// title: "Berhasil!",
|
|
||||||
// text: "Data berhasil dihapus.",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// timer: 2000,
|
|
||||||
// timerProgressBar: true,
|
|
||||||
// });
|
|
||||||
// // ✅ langsung hapus dari state
|
|
||||||
// onDeleteSuccess?.(id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link
|
|
||||||
// href={`/admin/broadcast/campaign-list/detail/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
|
||||||
// Detail
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <Link
|
|
||||||
// href={`//admin/broadcast/campaign-list/edit/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDelete(row.original.id)}
|
|
||||||
// className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer"
|
|
||||||
// >
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default columns;
|
export default columns;
|
||||||
|
|
|
||||||
|
|
@ -89,11 +89,6 @@ const CampaignListTable = () => {
|
||||||
|
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
|
||||||
function handleDeleteSuccess(id: number) {
|
|
||||||
setDataTable((prev) => prev.filter((item) => item.id !== id));
|
|
||||||
}
|
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -148,16 +143,16 @@ const CampaignListTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between mb-10 items-center">
|
<div className="flex justify-between mb-10 items-center">
|
||||||
<p className="text-xl font-medium text-default-900">Daftar Campaign</p>
|
<p className="text-xl font-medium text-default-900">Daftar Campaign</p>
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
{/* <Link href="/admin/broadcast/campaign-list/account-list">
|
<Link href="/admin/broadcast/campaign-list/account-list">
|
||||||
<Button color="primary" size="md" className="text-sm">
|
<Button color="primary" size="md" className="text-sm">
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
Daftar Akun
|
Daftar Akun
|
||||||
</Button>
|
</Button>
|
||||||
</Link> */}
|
</Link>
|
||||||
<Link href="/admin/broadcast/campaign-list/create">
|
<Link href="/admin/broadcast/campaign-list/create">
|
||||||
<Button color="primary" size="md" className="text-sm">
|
<Button color="primary" size="md" className="text-sm">
|
||||||
<NewCampaignIcon size={23} />
|
<NewCampaignIcon size={23} />
|
||||||
|
|
@ -194,14 +189,7 @@ const CampaignListTable = () => {
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell key={cell.id}>
|
<TableCell key={cell.id}>
|
||||||
{/* {flexRender(cell.column.columnDef.cell, cell.getContext())} */}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
{flexRender(cell.column.columnDef.cell, {
|
|
||||||
...cell.getContext(),
|
|
||||||
onDeleteSuccess: (id: number) => {
|
|
||||||
// setDataTable((prev) => prev.filter((item) => item.id !== id)
|
|
||||||
fetchData()
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ export default function CreateCampaign() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-3 bg-white dark:bg-black rounded-sm p-4"
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
>
|
>
|
||||||
<p className="fonnt-semibold">Campaign</p>
|
<p className="fonnt-semibold">Campaign</p>
|
||||||
<FormField
|
<FormField
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"use client";
|
'use client'
|
||||||
|
|
||||||
import "react-datepicker/dist/react-datepicker.css";
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
|
|
@ -33,9 +33,9 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import {
|
import {
|
||||||
getMediaBlastCampaignById,
|
getMediaBlastCampaignById,
|
||||||
getMediaBlastBroadcastList,
|
getMediaBlastBroadcastList
|
||||||
} from "@/service/broadcast/broadcast";
|
} from "@/service/broadcast/broadcast";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
|
|
@ -69,338 +69,319 @@ interface PageProps {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BroadcastCampaignDetail({
|
export default function BroadcastCampaignDetail({ params, searchParams }: PageProps) {
|
||||||
params,
|
const router = useRouter();
|
||||||
searchParams,
|
const pathname = usePathname();
|
||||||
}: PageProps) {
|
const { id, locale } = params;
|
||||||
const router = useRouter();
|
const [getData, setGetData] = useState<CampaignData[]>([]);
|
||||||
const pathname = usePathname();
|
const [totalPage, setTotalPage] = useState<number>(0);
|
||||||
const { id, locale } = params;
|
const [totalData, setTotalData] = useState<number>(0);
|
||||||
const [getData, setGetData] = useState<CampaignData[]>([]);
|
const [activeTab, setActiveTab] = useState<"sent" | "schedule" | "account-list">("sent");
|
||||||
const [totalPage, setTotalPage] = useState<number>(0);
|
const { page, size } = searchParams;
|
||||||
const [totalData, setTotalData] = useState<number>(0);
|
|
||||||
const [activeTab, setActiveTab] = useState<
|
|
||||||
"sent" | "schedule" | "account-list"
|
|
||||||
>("sent");
|
|
||||||
const { page, size } = searchParams;
|
|
||||||
const [calenderState, setCalenderState] = useState<boolean>(false);
|
|
||||||
const [typeFilter, setTypeFilter] = useState<string>("email");
|
|
||||||
const [dateRange, setDateRange] = useState<[Date, Date]>([
|
|
||||||
new Date(),
|
|
||||||
new Date(),
|
|
||||||
]);
|
|
||||||
const [startDate, endDate] = dateRange;
|
|
||||||
const [startDateString, setStartDateString] = useState<string | undefined>();
|
|
||||||
const [endDateString, setEndDateString] = useState<string | undefined>();
|
|
||||||
// Table state
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = useState({});
|
|
||||||
const [pagination, setPagination] = useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: parseInt(size || "10"),
|
|
||||||
});
|
|
||||||
const pages = page ? parseInt(page) - 1 : 0;
|
|
||||||
const currentPage = page ? parseInt(page) : 1;
|
|
||||||
const pageSize = parseInt(size || "10");
|
|
||||||
|
|
||||||
const isFHD = useMediaQuery({
|
const [calenderState, setCalenderState] = useState<boolean>(false);
|
||||||
minWidth: 1920,
|
const [typeFilter, setTypeFilter] = useState<string>("email");
|
||||||
});
|
const [dateRange, setDateRange] = useState<[Date, Date]>([new Date(), new Date()]);
|
||||||
|
const [startDate, endDate] = dateRange;
|
||||||
|
|
||||||
const setCurrentPage = (pageNumber: number) => {
|
const [startDateString, setStartDateString] = useState<string | undefined>();
|
||||||
const params = new URLSearchParams(searchParams);
|
const [endDateString, setEndDateString] = useState<string | undefined>();
|
||||||
params.set("page", pageNumber.toString());
|
|
||||||
router.push(`${pathname}?${params.toString()}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getListPaginationData() {
|
// Table state
|
||||||
loading();
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
console.log("Type : ", typeFilter);
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
console.log("Date : ", startDateString, endDateString);
|
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = useState({});
|
||||||
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: parseInt(size || "10"),
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
const pages = page ? parseInt(page) - 1 : 0;
|
||||||
const res = await getMediaBlastBroadcastList(
|
const currentPage = page ? parseInt(page) : 1;
|
||||||
pages,
|
const pageSize = parseInt(size || "10");
|
||||||
activeTab === "schedule",
|
|
||||||
startDateString || "",
|
|
||||||
endDateString || "",
|
|
||||||
typeFilter,
|
|
||||||
id
|
|
||||||
);
|
|
||||||
|
|
||||||
close();
|
const isFHD = useMediaQuery({
|
||||||
if (res?.data?.data) {
|
minWidth: 1920,
|
||||||
setupData(res.data.data);
|
});
|
||||||
}
|
|
||||||
} catch (error) {
|
const setCurrentPage = (pageNumber: number) => {
|
||||||
console.error("Error fetching data:", error);
|
const params = new URLSearchParams(searchParams);
|
||||||
close();
|
params.set('page', pageNumber.toString());
|
||||||
|
router.push(`${pathname}?${params.toString()}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getListPaginationData() {
|
||||||
|
loading();
|
||||||
|
console.log("Type : ", typeFilter);
|
||||||
|
console.log("Date : ", startDateString, endDateString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getMediaBlastBroadcastList(
|
||||||
|
pages,
|
||||||
|
activeTab === "schedule",
|
||||||
|
startDateString || "",
|
||||||
|
endDateString || "",
|
||||||
|
typeFilter,
|
||||||
|
id
|
||||||
|
);
|
||||||
|
|
||||||
|
close();
|
||||||
|
if (res?.data?.data) {
|
||||||
|
setupData(res.data.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getListPaginationData();
|
getListPaginationData();
|
||||||
}, [
|
}, [currentPage, pageSize, activeTab, endDateString, startDateString, typeFilter]);
|
||||||
currentPage,
|
|
||||||
pageSize,
|
|
||||||
activeTab,
|
|
||||||
endDateString,
|
|
||||||
startDateString,
|
|
||||||
typeFilter,
|
|
||||||
]);
|
|
||||||
|
|
||||||
function setupData(rawData: PaginatedResponse) {
|
function setupData(rawData: PaginatedResponse) {
|
||||||
console.log("raw", rawData);
|
console.log("raw", rawData);
|
||||||
if (rawData !== undefined) {
|
if (rawData !== undefined) {
|
||||||
const dataContent = rawData?.content;
|
const dataContent = rawData?.content;
|
||||||
const data: CampaignData[] = [];
|
const data: CampaignData[] = [];
|
||||||
|
|
||||||
dataContent.forEach((element, i) => {
|
dataContent.forEach((element, i) => {
|
||||||
element.no = (currentPage - 1) * pageSize + i + 1;
|
element.no = (currentPage - 1) * pageSize + i + 1;
|
||||||
data.push(element);
|
data.push(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
setGetData(data);
|
setGetData(data);
|
||||||
setTotalPage(rawData?.totalPages);
|
setTotalPage(rawData?.totalPages);
|
||||||
setTotalData(rawData?.totalElements);
|
setTotalData(rawData?.totalElements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const columns: ColumnDef<CampaignData>[] = [
|
const columns: ColumnDef<CampaignData>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: "No",
|
header: "No",
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "mediaBlastCampaign.title",
|
accessorKey: "mediaBlastCampaign.title",
|
||||||
header: "Campaign",
|
header: "Campaign",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Link
|
<Link href={`/${locale}/admin/broadcast/campaign-list/detail/${row.original.mediaBlastCampaignId}`} className="text-dark">
|
||||||
href={`/${locale}/admin/broadcast/campaign-list/detail/${row.original.mediaBlastCampaignId}`}
|
<span className="font-weight-bold">{row.original.mediaBlastCampaign?.title}</span>
|
||||||
className="text-dark"
|
</Link>
|
||||||
>
|
),
|
||||||
<span className="font-weight-bold">
|
},
|
||||||
{row.original.mediaBlastCampaign?.title}
|
{
|
||||||
</span>
|
accessorKey: "subject",
|
||||||
</Link>
|
header: "Judul",
|
||||||
),
|
cell: ({ row }) => (
|
||||||
},
|
<Link href={`/${locale}/admin/broadcast/content/detail/${row.original.id}`} className="text-dark">
|
||||||
{
|
<span className="font-weight-bold">{row.getValue("subject")}</span>
|
||||||
accessorKey: "subject",
|
</Link>
|
||||||
header: "Judul",
|
),
|
||||||
cell: ({ row }) => (
|
},
|
||||||
<Link
|
{
|
||||||
href={`/${locale}/admin/broadcast/content/detail/${row.original.id}`}
|
accessorKey: "type",
|
||||||
className="text-dark"
|
header: "Tipe",
|
||||||
>
|
cell: ({ row }) => (
|
||||||
<span className="font-weight-bold">{row.getValue("subject")}</span>
|
<div className="text-right text-black">
|
||||||
</Link>
|
{row.getValue("type")}
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "type",
|
|
||||||
header: "Tipe",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="text-right text-black">{row.getValue("type")}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "status",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="text-right text-black">{row.getValue("status")}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "sendDate",
|
|
||||||
header: "Tanggal & Waktu",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<div className="text-black">{row.getValue("sendDate")}</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: getData,
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function initState() {
|
|
||||||
if (startDate != null && endDate != null) {
|
|
||||||
setStartDateString(getOnlyDate(startDate));
|
|
||||||
setEndDateString(getOnlyDate(endDate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("date range", dateRange);
|
|
||||||
initState();
|
|
||||||
}, [calenderState, startDate, endDate]);
|
|
||||||
|
|
||||||
const handleTypeFilter = (type: string) => {
|
|
||||||
setTypeFilter(type);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-white dark:bg-black container-fluid rounded ">
|
|
||||||
<div className="mt-1 p-4">
|
|
||||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-4">
|
|
||||||
<Button
|
|
||||||
onClick={() => setActiveTab("sent")}
|
|
||||||
size="md"
|
|
||||||
className={`hover:text-white ${
|
|
||||||
activeTab === "sent"
|
|
||||||
? "bg-indigo-600 text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Sent
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => setActiveTab("schedule")}
|
|
||||||
size="md"
|
|
||||||
className={`hover:text-white ${
|
|
||||||
activeTab === "schedule"
|
|
||||||
? "bg-indigo-600 text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Schedule
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => setActiveTab("account-list")}
|
|
||||||
size="md"
|
|
||||||
className={`hover:text-white ${
|
|
||||||
activeTab === "account-list"
|
|
||||||
? "bg-indigo-600 text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
List Akun
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{activeTab === "account-list" ? (
|
|
||||||
<AccountListTable />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="broadcast-filter flex flex-column gap-3 mb-4">
|
|
||||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit h-fit">
|
|
||||||
<Button
|
|
||||||
onClick={() => handleTypeFilter("email")}
|
|
||||||
className={`hover:text-white ${
|
|
||||||
typeFilter === "email"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
Email Blast
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => handleTypeFilter("wa")}
|
|
||||||
className={`hover:text-white ${
|
|
||||||
typeFilter === "wa"
|
|
||||||
? "bg-black text-white "
|
|
||||||
: "bg-white text-black "
|
|
||||||
}`}
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
WhatsApp Blast
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="dashboard-date-picker">
|
|
||||||
<div className="mx-6 my-1">
|
|
||||||
<ReactDatePicker
|
|
||||||
selectsRange
|
|
||||||
startDate={startDate}
|
|
||||||
endDate={endDate}
|
|
||||||
onChange={(update) => {
|
|
||||||
setDateRange(update as [Date, Date]);
|
|
||||||
}}
|
|
||||||
placeholderText="Pilih Tanggal"
|
|
||||||
onCalendarClose={() => setCalenderState(!calenderState)}
|
|
||||||
className="form-control rounded-pill"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
</div>
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-right text-black">
|
||||||
|
{row.getValue("status")}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "sendDate",
|
||||||
|
header: "Tanggal & Waktu",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-black">
|
||||||
|
{row.getValue("sendDate")}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
<div className="w-full overflow-x-auto">
|
const table = useReactTable({
|
||||||
<Table className="overflow-hidden mt-3">
|
data: getData,
|
||||||
<TableHeader>
|
columns,
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
onSortingChange: setSorting,
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
onColumnFiltersChange: setColumnFilters,
|
||||||
{headerGroup.headers.map((header) => (
|
getCoreRowModel: getCoreRowModel(),
|
||||||
<TableHead key={header.id}>
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
{header.isPlaceholder
|
getSortedRowModel: getSortedRowModel(),
|
||||||
? null
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
: flexRender(
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
header.column.columnDef.header,
|
onRowSelectionChange: setRowSelection,
|
||||||
header.getContext()
|
onPaginationChange: setPagination,
|
||||||
)}
|
state: {
|
||||||
</TableHead>
|
sorting,
|
||||||
))}
|
columnFilters,
|
||||||
</TableRow>
|
columnVisibility,
|
||||||
))}
|
rowSelection,
|
||||||
</TableHeader>
|
pagination,
|
||||||
<TableBody>
|
},
|
||||||
{table.getRowModel().rows?.length ? (
|
});
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
useEffect(() => {
|
||||||
key={row.id}
|
function initState() {
|
||||||
data-state={row.getIsSelected() && "selected"}
|
if (startDate != null && endDate != null) {
|
||||||
className="h-[75px]"
|
setStartDateString(getOnlyDate(startDate));
|
||||||
>
|
setEndDateString(getOnlyDate(endDate));
|
||||||
{row.getVisibleCells().map((cell) => (
|
}
|
||||||
<TableCell key={cell.id}>
|
}
|
||||||
{flexRender(
|
console.log('date range', dateRange);
|
||||||
cell.column.columnDef.cell,
|
initState();
|
||||||
cell.getContext()
|
}, [calenderState, startDate, endDate]);
|
||||||
)}
|
|
||||||
</TableCell>
|
const handleTypeFilter = (type: string) => {
|
||||||
))}
|
setTypeFilter(type);
|
||||||
</TableRow>
|
};
|
||||||
))
|
|
||||||
) : (
|
return (
|
||||||
<TableRow>
|
<div className="bg-white container-fluid rounded rounded-xl">
|
||||||
<TableCell
|
<div className="mt-1 p-4">
|
||||||
colSpan={columns.length}
|
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-4">
|
||||||
className="h-24 text-center"
|
<Button
|
||||||
>
|
onClick={() => setActiveTab("sent")}
|
||||||
No results.
|
size="md"
|
||||||
</TableCell>
|
className={`hover:text-white ${
|
||||||
</TableRow>
|
activeTab === "sent"
|
||||||
)}
|
? "bg-indigo-600 text-white "
|
||||||
</TableBody>
|
: "bg-white text-black "
|
||||||
</Table>
|
}`}
|
||||||
<TablePagination
|
>
|
||||||
table={table}
|
Sent
|
||||||
totalData={totalData}
|
</Button>
|
||||||
totalPage={totalPage}
|
<Button
|
||||||
/>
|
onClick={() => setActiveTab("schedule")}
|
||||||
|
size="md"
|
||||||
|
className={`hover:text-white ${
|
||||||
|
activeTab === "schedule"
|
||||||
|
? "bg-indigo-600 text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Schedule
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setActiveTab("account-list")}
|
||||||
|
size="md"
|
||||||
|
className={`hover:text-white ${
|
||||||
|
activeTab === "account-list"
|
||||||
|
? "bg-indigo-600 text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
List Akun
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{activeTab === "account-list" ? (
|
||||||
|
<AccountListTable />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="broadcast-filter flex flex-column gap-3 mb-4">
|
||||||
|
<div className="flex flex-row gap-1 border-2 rounded-md w-fit h-fit">
|
||||||
|
<Button
|
||||||
|
onClick={() => handleTypeFilter("email")}
|
||||||
|
className={`hover:text-white ${
|
||||||
|
typeFilter === "email"
|
||||||
|
? "bg-black text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
|
size='sm'
|
||||||
|
>
|
||||||
|
Email Blast
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => handleTypeFilter("wa")}
|
||||||
|
className={`hover:text-white ${
|
||||||
|
typeFilter === "wa"
|
||||||
|
? "bg-black text-white "
|
||||||
|
: "bg-white text-black "
|
||||||
|
}`}
|
||||||
|
size='sm'
|
||||||
|
>
|
||||||
|
WhatsApp Blast
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="dashboard-date-picker">
|
||||||
|
<div className="mx-6 my-1">
|
||||||
|
<ReactDatePicker
|
||||||
|
selectsRange
|
||||||
|
startDate={startDate}
|
||||||
|
endDate={endDate}
|
||||||
|
onChange={(update) => {
|
||||||
|
setDateRange(update as [Date, Date]);
|
||||||
|
}}
|
||||||
|
placeholderText="Pilih Tanggal"
|
||||||
|
onCalendarClose={() => setCalenderState(!calenderState)}
|
||||||
|
className="form-control rounded-pill"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
totalData={totalData}
|
||||||
|
totalPage={totalPage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
);
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ export default function EditCampaign() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-3 bg-white dark:bg-black rounded-sm p-4"
|
className="space-y-3 bg-white rounded-sm p-4"
|
||||||
>
|
>
|
||||||
<p className="fonnt-semibold">Campaign</p>
|
<p className="fonnt-semibold">Campaign</p>
|
||||||
<FormField
|
<FormField
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import DetailContentBlast from "@/components/form/broadcast/content-blast--detail-form";
|
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
|
|
||||||
export default function DetailEmailBlast() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
<DetailContentBlast />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,7 @@ export default function CreateEmailBlast() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<ContentBlast />
|
<ContentBlast type="email" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -85,21 +85,23 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link href={`/contributor/content/image/detail/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link
|
||||||
|
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
Detail
|
Detail
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
<Link href={`/admin/broadcast/create/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||||
Email & Whatsapp Blast
|
Email Blast
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
{/* <Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||||
Whatsapp Blast
|
Whatsapp Blast
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link> */}
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
@ -204,7 +204,7 @@ const BroadcastEmailTable = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between ">
|
<div className="flex justify-between ">
|
||||||
<Link href="/admin/broadcast/campaign-list" className="mr-3">
|
<Link href="/admin/broadcast/campaign-list" className="mr-3">
|
||||||
<Button color="primary" size="md" className="text-sm">
|
<Button color="primary" size="md" className="text-sm">
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import BroadcastTable from "./create/component/table";
|
import BroadcastTable from "./email/component/table";
|
||||||
import { PlusIcon } from "lucide-react";
|
import { PlusIcon } from "lucide-react";
|
||||||
|
|
||||||
import EscalationTable from "../../shared/communication/escalation/components/escalation-table";
|
import EscalationTable from "../../shared/communication/escalation/components/escalation-table";
|
||||||
|
|
@ -8,7 +8,7 @@ import InternalTable from "../../shared/communication/internal/components/intern
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import BroadcastEmailTable from "./create/component/table";
|
import BroadcastEmailTable from "./email/component/table";
|
||||||
import BroadcastWhatsAppTable from "./whatsapp/component/table";
|
import BroadcastWhatsAppTable from "./whatsapp/component/table";
|
||||||
|
|
||||||
export default function AdminBroadcast() {
|
export default function AdminBroadcast() {
|
||||||
|
|
@ -16,7 +16,7 @@ export default function AdminBroadcast() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
{tab === "Email Blast" && <BroadcastEmailTable />}
|
{tab === "Email Blast" && <BroadcastEmailTable />}
|
||||||
{tab === "WhatsApp Blast" && <BroadcastWhatsAppTable />}
|
{tab === "WhatsApp Blast" && <BroadcastWhatsAppTable />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export default function CreateWABlast() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
{/* <ContentBlast /> */}
|
<ContentBlast type="wa" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,21 +80,23 @@ const columns: ColumnDef<any>[] = [
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link href={`/contributor/content/image/detail/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link
|
||||||
|
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
Detail
|
Detail
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||||
Email Blast
|
Email Blast
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none cursor-pointer">
|
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||||
Whatsapp Blast
|
Whatsapp Blast
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ export default function EditUserForm() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const passwordVal = form.watch("password");
|
||||||
|
const confPasswordVal = form.watch("confirmPassword");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initData();
|
initData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -353,8 +353,8 @@ export default function EditUserForm() {
|
||||||
<PasswordChecklist
|
<PasswordChecklist
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||||
minLength={8}
|
minLength={8}
|
||||||
value={passwordVal || ""}
|
value={passwordVal}
|
||||||
valueAgain={confPasswordVal || ""}
|
valueAgain={confPasswordVal}
|
||||||
onChange={(isValid) => {
|
onChange={(isValid) => {
|
||||||
form.setValue("isValidPassword", isValid);
|
form.setValue("isValidPassword", isValid);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { Check, ChevronsUpDown, Eye, EyeOff } from "lucide-react";
|
import { Check, ChevronsUpDown, Eye, EyeOff } from "lucide-react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
|
|
@ -289,17 +290,7 @@ export default function CreateUserForm() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.role == "OPT-ID") {
|
if (data.role == "OPT-ID") {
|
||||||
// req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
||||||
if (data.role == "OPT-ID") {
|
|
||||||
let snsValue = data?.sns ? data.sns.join(",") : "";
|
|
||||||
|
|
||||||
// ✅ Jika hanya 1 value → tambahkan koma agar backend tidak error
|
|
||||||
if (data?.sns && data.sns.length === 1) {
|
|
||||||
snsValue = snsValue + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
req.handledSocialMedia = snsValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.role == "KUR-ID") {
|
if (data.role == "KUR-ID") {
|
||||||
|
|
@ -352,7 +343,7 @@ export default function CreateUserForm() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-6 bg-white dark:bg-black p-10 w-full"
|
className="space-y-6 bg-white p-10 w-full"
|
||||||
>
|
>
|
||||||
<p className="text-xl">Data Pengelola Media Hub</p>
|
<p className="text-xl">Data Pengelola Media Hub</p>
|
||||||
<FormField
|
<FormField
|
||||||
|
|
@ -760,8 +751,8 @@ export default function CreateUserForm() {
|
||||||
<PasswordChecklist
|
<PasswordChecklist
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||||
minLength={8}
|
minLength={8}
|
||||||
value={passwordVal || ""}
|
value={passwordVal}
|
||||||
valueAgain={confPasswordVal || ""}
|
valueAgain={confPasswordVal}
|
||||||
onChange={(isValid) => {
|
onChange={(isValid) => {
|
||||||
form.setValue("isValidPassword", isValid);
|
form.setValue("isValidPassword", isValid);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,8 @@ export default function DetailUserForm() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const passwordVal = form.watch("password");
|
||||||
|
const confPasswordVal = form.watch("confirmPassword");
|
||||||
const selectedRole = form.watch("role");
|
const selectedRole = form.watch("role");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -221,6 +221,7 @@ export default function EditUserForm() {
|
||||||
form.setValue("level", String(res?.userLevelId));
|
form.setValue("level", String(res?.userLevelId));
|
||||||
} else {
|
} else {
|
||||||
initFetch();
|
initFetch();
|
||||||
|
console.log("sadad", res?.role?.code);
|
||||||
form.setValue("fullname", res?.fullname);
|
form.setValue("fullname", res?.fullname);
|
||||||
form.setValue("username", res?.username);
|
form.setValue("username", res?.username);
|
||||||
form.setValue("phoneNumber", res?.phoneNumber);
|
form.setValue("phoneNumber", res?.phoneNumber);
|
||||||
|
|
@ -755,8 +756,8 @@ export default function EditUserForm() {
|
||||||
<PasswordChecklist
|
<PasswordChecklist
|
||||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||||
minLength={8}
|
minLength={8}
|
||||||
value={passwordVal || ""}
|
value={passwordVal}
|
||||||
valueAgain={confPasswordVal || ""}
|
valueAgain={confPasswordVal}
|
||||||
onChange={(isValid) => {
|
onChange={(isValid) => {
|
||||||
form.setValue("isValidPassword", isValid);
|
form.setValue("isValidPassword", isValid);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default function ManagementUser() {
|
||||||
<ManagementUserVisualization />
|
<ManagementUserVisualization />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="flex flex-col gap-2 bg-white dark:bg-black rounded-lg p-3 mt-5">
|
<section className="flex flex-col gap-2 bg-white rounded-lg p-3 mt-5">
|
||||||
<div className="flex justify-between py-3">
|
<div className="flex justify-between py-3">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
Data User {isInternal ? "Internal" : "Eksternal"}
|
Data User {isInternal ? "Internal" : "Eksternal"}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ import {
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible";
|
||||||
import { validateMediaLink } from "@/service/media-tracking/media-tracking";
|
|
||||||
import toast from "react-hot-toast";
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -54,132 +52,12 @@ const columns: ColumnDef<any>[] = [
|
||||||
<span className="normal-case">{row.getValue("title")}</span>
|
<span className="normal-case">{row.getValue("title")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// accessorKey: "link",
|
|
||||||
// header: "Link Berita",
|
|
||||||
// cell: ({ row }) => (
|
|
||||||
// <span className="normal-case">{row.getValue("link")}</span>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
accessorKey: "link",
|
accessorKey: "link",
|
||||||
header: "Link Berita",
|
header: "Link Berita",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => (
|
||||||
const link = row.getValue<string>("link");
|
<span className="normal-case">{row.getValue("link")}</span>
|
||||||
|
),
|
||||||
if (!link) {
|
|
||||||
return <span className="text-muted-foreground">-</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={link}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-blue-600 underline hover:text-blue-800 break-all"
|
|
||||||
>
|
|
||||||
{link}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "validation",
|
|
||||||
header: "Validasi",
|
|
||||||
cell: ({ row, table }) => {
|
|
||||||
const original = row.original;
|
|
||||||
|
|
||||||
// const isValid = original.isValid;
|
|
||||||
const isRelevant = original.isRelevant;
|
|
||||||
const link = original.link;
|
|
||||||
|
|
||||||
const updateRow = (data: Partial<any>) => {
|
|
||||||
table.options.meta?.updateData(row.index, data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleValid = async () => {
|
|
||||||
try {
|
|
||||||
await validateMediaLink(original.id, true);
|
|
||||||
updateRow({
|
|
||||||
isRelevant: true,
|
|
||||||
});
|
|
||||||
table.options.meta?.refetchData?.();
|
|
||||||
} catch (err: any) {
|
|
||||||
toast.error(err.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInvalid = async () => {
|
|
||||||
try {
|
|
||||||
await validateMediaLink(original.id, false);
|
|
||||||
|
|
||||||
updateRow({
|
|
||||||
isRelevant: false,
|
|
||||||
});
|
|
||||||
table.options.meta?.refetchData?.();
|
|
||||||
} catch (err: any) {
|
|
||||||
toast.error(err.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!link) {
|
|
||||||
return <span className="text-muted-foreground">-</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRelevant === true) {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className="bg-green-600 hover:bg-green-700"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
Relevan
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={handleValid}
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M18.7 7.2c-.4-.4-1-.4-1.4 0l-7.5 7.5l-3.1-3.1c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4l3.8 3.8c.2.2.4.3.7.3s.5-.1.7-.3l8.2-8.2c.4-.4.4-1 0-1.4"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Relevan
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="outline" onClick={handleInvalid} className="flex text-center items-center justify-center">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M6.758 17.243L12.001 12m5.243-5.243L12 12m0 0L6.758 6.757M12.001 12l5.243 5.243"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Tidak Relevan
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,18 +116,6 @@ const NewsDetailTable = () => {
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
onRowSelectionChange: setRowSelection,
|
onRowSelectionChange: setRowSelection,
|
||||||
onPaginationChange: setPagination,
|
onPaginationChange: setPagination,
|
||||||
meta: {
|
|
||||||
updateData: (rowIndex: number, value: Partial<any>) => {
|
|
||||||
setDataTable((old) =>
|
|
||||||
old.map((row, index) =>
|
|
||||||
index === rowIndex ? { ...row, ...value } : row
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
refetchData: () => {
|
|
||||||
fetchData();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
state: {
|
state: {
|
||||||
sorting,
|
sorting,
|
||||||
columnFilters,
|
columnFilters,
|
||||||
|
|
@ -166,7 +154,7 @@ const NewsDetailTable = () => {
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
}, [page, showData, id]);
|
}, [page, showData]);
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
import { exportMediaTrackingToExcel } from "@/utils/export-media-tracking";
|
|
||||||
import { loading, close } from "@/config/swal";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
import { error } from "@/lib/swal";
|
|
||||||
import {
|
|
||||||
DownloadIcon,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -53,101 +45,19 @@ const columns: ColumnDef<any>[] = [
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "resultTotal",
|
accessorKey: "link",
|
||||||
header: () => <div className="text-center w-full">Total Artikel</div>,
|
header: "Jumlah Amplifikasi",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => <span>{row.getValue("link")}</span>,
|
||||||
const value = row.getValue("resultTotal") as number | string | null;
|
|
||||||
|
|
||||||
const finalValue =
|
|
||||||
value === null || value === undefined || value === ""
|
|
||||||
? 0
|
|
||||||
: Number(value);
|
|
||||||
|
|
||||||
return <div className="text-center w-full">{finalValue}</div>;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "amplification",
|
accessorKey: "status",
|
||||||
header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
|
header: "Status",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => <span>{row.getValue("status")}</span>,
|
||||||
const raw = row.getValue("amplification") as string | null;
|
|
||||||
|
|
||||||
let total = 0;
|
|
||||||
let 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 (
|
|
||||||
<div className="text-center w-full font-medium">
|
|
||||||
{total}
|
|
||||||
<span className="text-muted-foreground">/{invalidTotal}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// accessorKey: "status",
|
|
||||||
// header: "Status",
|
|
||||||
// cell: ({ row }) => <span>{row.getValue("status")}</span>,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// accessorKey: "isProcessing",
|
|
||||||
// header: () => <div className="text-center">Status</div>,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const raw = row.getValue("isProcessing");
|
|
||||||
// var status = "Sedang Diproses"
|
|
||||||
// if (Boolean(raw) == true) {
|
|
||||||
// status = "Selesai Diproses";
|
|
||||||
// }
|
|
||||||
// return <div className="text-center">{status}</div>;
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
accessorKey: "isProcessing",
|
accessorKey: "date",
|
||||||
header: () => <div className="text-center">Status</div>,
|
header: "Tanggal Penarikan",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => <span>{row.getValue("date")}</span>,
|
||||||
const raw = Boolean(row.getValue("isProcessing"));
|
|
||||||
const statusText = raw ? "Sedang Diproses" : "Sudah Selesai";
|
|
||||||
const colorClass = raw
|
|
||||||
? "bg-yellow-100 text-yellow-700 border border-yellow-300"
|
|
||||||
: "bg-green-100 text-green-700 border border-green-300";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="text-center">
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded text-xs font-medium inline-block ${colorClass}`}
|
|
||||||
>
|
|
||||||
{statusText}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "createdAt",
|
|
||||||
header: () => <div className="text-center">Tanggal Penarikan</div>,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const raw = row.getValue("createdAt");
|
|
||||||
if (!raw || typeof raw !== "string")
|
|
||||||
return <div className="text-center">-</div>;
|
|
||||||
|
|
||||||
const date = new Date(raw);
|
|
||||||
if (isNaN(date.getTime())) return <div className="text-center">-</div>;
|
|
||||||
|
|
||||||
const formatted = date.toLocaleDateString("id-ID", {
|
|
||||||
day: "2-digit",
|
|
||||||
month: "short",
|
|
||||||
year: "numeric",
|
|
||||||
});
|
|
||||||
|
|
||||||
return <div className="text-center">{formatted}</div>;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
|
|
@ -168,31 +78,13 @@ const columns: ColumnDef<any>[] = [
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
<Link href={`/admin/media-tracking/detail/${row.original.id}`}>
|
||||||
<DropdownMenuItem className="p-2 border-b cursor-pointer text-default-700 group focus:bg-default focus:text-primary-foreground items-center rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
View
|
View
|
||||||
{row.original.mediaUpload.fileType.secondaryName &&
|
{row.original.mediaUpload.fileType.secondaryName &&
|
||||||
row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
|
row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
<DropdownMenuItem
|
|
||||||
className="p-2 border-b cursor-pointer text-default-700 group rounded-none focus:bg-default focus:text-primary-foreground "
|
|
||||||
onClick={async () => {
|
|
||||||
try {
|
|
||||||
loading();
|
|
||||||
await exportMediaTrackingToExcel({
|
|
||||||
mediaTrackingId: row.original.id,
|
|
||||||
});
|
|
||||||
close();
|
|
||||||
} catch (e: any) {
|
|
||||||
close();
|
|
||||||
error(e.message || "Gagal export data");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DownloadIcon className="w-4 h-4 me-1.5" />
|
|
||||||
<p>Download</p>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ const ResultTable = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3 border ">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3 border ">
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row justify-end sm:items-center md:items-center lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row justify-end sm:items-center md:items-center lg:items-center">
|
||||||
<div className=" flex flex-row justify-end items-center gap-3">
|
<div className=" flex flex-row justify-end items-center gap-3">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ import { PaginationState } from "@tanstack/react-table";
|
||||||
import page from "../page";
|
import page from "../page";
|
||||||
import CustomPagination from "@/components/table/custom-pagination";
|
import CustomPagination from "@/components/table/custom-pagination";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
|
|
||||||
export default function TrackingBeritaCard() {
|
export default function TrackingBeritaCard() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
@ -32,7 +30,6 @@ export default function TrackingBeritaCard() {
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [totalPage, setTotalPage] = useState(1);
|
const [totalPage, setTotalPage] = useState(1);
|
||||||
const [showData, setShowData] = useState("6");
|
const [showData, setShowData] = useState("6");
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initFecth();
|
initFecth();
|
||||||
|
|
@ -40,7 +37,7 @@ export default function TrackingBeritaCard() {
|
||||||
|
|
||||||
const initFecth = async () => {
|
const initFecth = async () => {
|
||||||
loading();
|
loading();
|
||||||
const response = await listDataTracking(Number(showData), page - 1, search);
|
const response = await listDataTracking(showData, page - 1);
|
||||||
const data = response?.data?.data;
|
const data = response?.data?.data;
|
||||||
const newData = data?.content;
|
const newData = data?.content;
|
||||||
setTotalPage(data?.totalPages || 1);
|
setTotalPage(data?.totalPages || 1);
|
||||||
|
|
@ -56,25 +53,17 @@ export default function TrackingBeritaCard() {
|
||||||
setContent(response?.data?.data?.content || []);
|
setContent(response?.data?.data?.content || []);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setSearch(value);
|
setSearch(value);
|
||||||
|
|
||||||
const response = await listDataTracking(Number(showData), 0, value);
|
if (value.trim() === "") {
|
||||||
setContent(response?.data?.data?.content || []);
|
initFecth();
|
||||||
|
} else {
|
||||||
|
fecthAll(value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
// const value = e.target.value;
|
|
||||||
// setSearch(value);
|
|
||||||
|
|
||||||
// if (value.trim() === "") {
|
|
||||||
// initFecth();
|
|
||||||
// } else {
|
|
||||||
// fecthAll(value);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleSelect = (id: number) => {
|
const handleSelect = (id: number) => {
|
||||||
setSelectedItems((prev) =>
|
setSelectedItems((prev) =>
|
||||||
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
||||||
|
|
@ -83,143 +72,30 @@ export default function TrackingBeritaCard() {
|
||||||
|
|
||||||
const doSave = async () => {
|
const doSave = async () => {
|
||||||
if (selectedItems.length === 0) {
|
if (selectedItems.length === 0) {
|
||||||
MySwal.fire(
|
toast("Pilih minimal 1 berita untuk disimpan.");
|
||||||
"Peringatan",
|
|
||||||
"Pilih minimal 1 berita untuk disimpan.",
|
|
||||||
"warning"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading();
|
const promises = selectedItems.map((id) =>
|
||||||
|
mediaTrackingSave({
|
||||||
const promises = selectedItems.map(async (id) => {
|
|
||||||
const res = await mediaTrackingSave({
|
|
||||||
mediaUploadId: id,
|
mediaUploadId: id,
|
||||||
duration: 24,
|
duration: 24,
|
||||||
scrapingPeriod: 3,
|
scrapingPeriod: 3,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
// cek pesan API
|
|
||||||
if (!res?.data?.success) {
|
|
||||||
throw new Error(
|
|
||||||
res?.data?.message ||
|
|
||||||
"Limit media tracking per hari sudah tercapai. Maksimal 5 tracking per hari."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
close();
|
|
||||||
|
|
||||||
await MySwal.fire({
|
toast("Berhasil Menambahkan", {
|
||||||
icon: "success",
|
description: "",
|
||||||
title: "Berhasil!",
|
|
||||||
text: "Tracking berita berhasil ditambahkan.",
|
|
||||||
confirmButtonColor: "#2563eb",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setSelectedItems([]);
|
setSelectedItems([]);
|
||||||
initFecth();
|
initFecth();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
close();
|
error(err?.message || "Gagal menyimpan data.");
|
||||||
MySwal.fire({
|
|
||||||
icon: "error",
|
|
||||||
title: "Gagal!",
|
|
||||||
text: err?.message || "Terjadi kesalahan saat menyimpan data.",
|
|
||||||
confirmButtonColor: "#dc2626",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const doSave = async () => {
|
|
||||||
// if (selectedItems.length === 0) {
|
|
||||||
// toast("Pilih minimal 1 berita untuk disimpan.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const promises = selectedItems.map((id) =>
|
|
||||||
// mediaTrackingSave({
|
|
||||||
// mediaUploadId: id,
|
|
||||||
// duration: 24,
|
|
||||||
// scrapingPeriod: 3,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// await Promise.all(promises);
|
|
||||||
|
|
||||||
// toast("Berhasil Menambahkan", {
|
|
||||||
// description: "",
|
|
||||||
// });
|
|
||||||
// setSelectedItems([]);
|
|
||||||
// initFecth();
|
|
||||||
// } catch (err: any) {
|
|
||||||
// error(err?.message || "Gagal menyimpan data.");
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const doSave = async () => {
|
|
||||||
// if (selectedItems.length === 0) {
|
|
||||||
// MySwal.fire(
|
|
||||||
// "Peringatan",
|
|
||||||
// "Pilih minimal 1 berita untuk disimpan.",
|
|
||||||
// "warning"
|
|
||||||
// );
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// loading();
|
|
||||||
|
|
||||||
// const promises = selectedItems.map((id) =>
|
|
||||||
// mediaTrackingSave({
|
|
||||||
// mediaUploadId: id,
|
|
||||||
// duration: 24,
|
|
||||||
// scrapingPeriod: 3,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// await Promise.all(promises);
|
|
||||||
|
|
||||||
// close();
|
|
||||||
|
|
||||||
// await MySwal.fire({
|
|
||||||
// icon: "success",
|
|
||||||
// title: "Berhasil!",
|
|
||||||
// text: "Tracking berita berhasil ditambahkan.",
|
|
||||||
// confirmButtonColor: "#2563eb",
|
|
||||||
// });
|
|
||||||
|
|
||||||
// setSelectedItems([]);
|
|
||||||
// initFecth();
|
|
||||||
// } catch (err: any) {
|
|
||||||
// close();
|
|
||||||
// MySwal.fire({
|
|
||||||
// icon: "error",
|
|
||||||
// title: "Gagal!",
|
|
||||||
// text: err?.message || "Terjadi kesalahan saat menyimpan data.",
|
|
||||||
// confirmButtonColor: "#dc2626",
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
const slugify = (text: string) => {
|
|
||||||
return text
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/[^a-z0-9]+/g, "-")
|
|
||||||
.replace(/(^-|-$)+/g, "");
|
|
||||||
};
|
|
||||||
|
|
||||||
const goToDetail = (item: any) => {
|
|
||||||
const type = item.type || "image";
|
|
||||||
const slug = slugify(item.title || "");
|
|
||||||
const url = `/in/${type}/detail/${item.id}-${slug}`;
|
|
||||||
|
|
||||||
window.location.href = url;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
|
|
@ -265,7 +141,7 @@ export default function TrackingBeritaCard() {
|
||||||
<div className="text-sm text-blue-600 font-medium">
|
<div className="text-sm text-blue-600 font-medium">
|
||||||
{selectedItems.length} Item Terpilih{" "}
|
{selectedItems.length} Item Terpilih{" "}
|
||||||
<span className="text-black">
|
<span className="text-black">
|
||||||
/ Tracking Berita tersisa {5 - selectedItems.length}
|
/ Tracking Berita tersisa {29 - selectedItems.length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Button className="bg-blue-600 text-white" onClick={doSave}>
|
<Button className="bg-blue-600 text-white" onClick={doSave}>
|
||||||
|
|
@ -275,48 +151,6 @@ export default function TrackingBeritaCard() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
{content?.length > 0 &&
|
|
||||||
content.map((item: any) => (
|
|
||||||
<Card
|
|
||||||
key={item.id}
|
|
||||||
className="relative overflow-hidden shadow-sm border rounded-lg"
|
|
||||||
>
|
|
||||||
{/* KLIK GAMBAR = CHECKLIST */}
|
|
||||||
<div
|
|
||||||
className="cursor-pointer"
|
|
||||||
onClick={() => handleSelect(item.id)}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={item.thumbnailLink}
|
|
||||||
alt={item.title}
|
|
||||||
className="w-full h-[300px] object-cover"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* CHECKBOX */}
|
|
||||||
<div className="absolute top-2 left-2">
|
|
||||||
<div className="w-5 h-5 border-2 border-white bg-white rounded-sm flex items-center justify-center">
|
|
||||||
{selectedItems.includes(item.id) && (
|
|
||||||
<div className="w-3 h-3 bg-blue-600 rounded-sm" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* KLIK JUDUL = DETAIL */}
|
|
||||||
<p
|
|
||||||
className="p-2 text-sm font-medium hover:underline cursor-pointer"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
goToDetail(item);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</p>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
|
||||||
{content?.length > 1 &&
|
{content?.length > 1 &&
|
||||||
content.map((item: any) => (
|
content.map((item: any) => (
|
||||||
<Card
|
<Card
|
||||||
|
|
@ -341,7 +175,7 @@ export default function TrackingBeritaCard() {
|
||||||
</p>
|
</p>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div> */}
|
</div>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
{content && content?.length > 0 ? (
|
{content && content?.length > 0 ? (
|
||||||
<CustomPagination
|
<CustomPagination
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,7 @@ const ContentListBanner = () => {
|
||||||
checked={selectedItems.length === data.length}
|
checked={selectedItems.length === data.length}
|
||||||
onCheckedChange={handleSelectAll}
|
onCheckedChange={handleSelectAll}
|
||||||
/>
|
/>
|
||||||
<span className="text-black dark:text-white">Pilih Semua</span>
|
<span>Pilih Semua</span>
|
||||||
</div>
|
</div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<Button color="primary" onClick={() => handleBanner(selectedItems)}>
|
<Button color="primary" onClick={() => handleBanner(selectedItems)}>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export default function AdminBanner() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
{selectedTab === "content" ? "List Media" : " List Banner"}
|
{selectedTab === "content" ? "List Media" : " List Banner"}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
router.push("/admin/settings/category?dataChange=true");
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Menubar className="border-none dark:bg-black">
|
<Menubar className="border-none">
|
||||||
<MenubarMenu>
|
<MenubarMenu>
|
||||||
<MenubarTrigger>
|
<MenubarTrigger>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -125,16 +125,16 @@ export default function CreateCategoryModal() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const contentType = form.watch("contentType");
|
const contentType = form.watch("contentType");
|
||||||
const isAllContentChecked = contentType && listContent.every((item) =>
|
const isAllContentChecked = listContent.every((item) =>
|
||||||
contentType.includes(item.id)
|
contentType?.includes(item.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const users = form.watch("selectedUser");
|
const users = form.watch("selectedUser");
|
||||||
const isAllUserChecked = users && userList.every((item) => users.includes(item.id));
|
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
const target = form.watch("publishTo");
|
||||||
const isAllTargetChecked = target && publishToList.every((item) =>
|
const isAllTargetChecked = publishToList.every((item) =>
|
||||||
target.includes(item.id)
|
target?.includes(item.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
|
|
@ -253,7 +253,7 @@ export default function CreateCategoryModal() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-3 bg-white dark:bg-[#1f2937] rounded-sm"
|
className="space-y-3 bg-white rounded-sm"
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
|
||||||
|
|
@ -138,34 +138,35 @@ export default function EditCategoryModal(props: {
|
||||||
maxFiles: 1,
|
maxFiles: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// initFetch();
|
const initFetch = async () => {
|
||||||
// }, [id]);
|
const req = await getCategoryDetail(id);
|
||||||
|
const data = req?.data?.data;
|
||||||
|
console.log("dataC", data);
|
||||||
|
form.setValue("id", String(data?.id));
|
||||||
|
form.setValue("title", String(data?.name));
|
||||||
|
form.setValue("description", String(data?.description));
|
||||||
|
form.setValue("contentType", data?.mediaTypes?.split(","));
|
||||||
|
form.setValue(
|
||||||
|
"selectedUser",
|
||||||
|
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
||||||
|
);
|
||||||
|
form.setValue("publishTo", data?.publishedLocation?.split(","));
|
||||||
|
form.setValue("file", thumbnailLink);
|
||||||
|
|
||||||
const initFetch = async () => {
|
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
||||||
const req = await getCategoryDetail(id);
|
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
||||||
const data = req?.data?.data;
|
};
|
||||||
form.setValue("id", String(data?.id));
|
|
||||||
form.setValue("title", String(data?.name));
|
|
||||||
form.setValue("description", String(data?.description));
|
|
||||||
form.setValue("contentType", data?.mediaTypes?.split(",") || []);
|
|
||||||
form.setValue(
|
|
||||||
"selectedUser",
|
|
||||||
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
|
||||||
);
|
|
||||||
form.setValue("publishTo", data?.publishedLocation?.split(",") || []);
|
|
||||||
form.setValue("file", thumbnailLink);
|
|
||||||
|
|
||||||
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
initFetch();
|
||||||
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
}, [id]);
|
||||||
};
|
|
||||||
|
|
||||||
function removeAndReturn(inputString: string, toRemove: number[]) {
|
function removeAndReturn(inputString: string, toRemove: number[]) {
|
||||||
const numbers = inputString?.split(",").map(Number);
|
const numbers = inputString.split(",").map(Number);
|
||||||
|
|
||||||
const filteredNumbers = numbers?.filter((num) => !toRemove?.includes(num));
|
const filteredNumbers = numbers.filter((num) => !toRemove.includes(num));
|
||||||
|
|
||||||
return filteredNumbers?.map(String);
|
return filteredNumbers.map(String);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterString(inputString: string, type: string) {
|
function filterString(inputString: string, type: string) {
|
||||||
|
|
@ -182,16 +183,17 @@ export default function EditCategoryModal(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentType = form.watch("contentType");
|
const contentType = form.watch("contentType");
|
||||||
const isAllContentChecked =
|
const isAllContentChecked = listContent.every((item) =>
|
||||||
contentType && listContent.every((item) => contentType.includes(item.id));
|
contentType?.includes(item.id)
|
||||||
|
);
|
||||||
|
|
||||||
const users = form.watch("selectedUser");
|
const users = form.watch("selectedUser");
|
||||||
const isAllUserChecked =
|
const isAllUserChecked = userList.every((item) => users?.includes(item.id));
|
||||||
users && userList.every((item) => users.includes(item.id));
|
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
const target = form.watch("publishTo");
|
||||||
const isAllTargetChecked =
|
const isAllTargetChecked = publishToList.every((item) =>
|
||||||
target && publishToList.every((item) => target.includes(item.id));
|
target?.includes(item.id)
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getRoles();
|
getRoles();
|
||||||
|
|
@ -211,7 +213,6 @@ export default function EditCategoryModal(props: {
|
||||||
const uniqueNumbers = Array.from(new Set(numbers));
|
const uniqueNumbers = Array.from(new Set(numbers));
|
||||||
return uniqueNumbers.join(",");
|
return uniqueNumbers.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
const formMedia = new FormData();
|
const formMedia = new FormData();
|
||||||
|
|
||||||
|
|
@ -262,9 +263,7 @@ export default function EditCategoryModal(props: {
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<a
|
<a
|
||||||
onClick={() =>{
|
onClick={() => setIsOpen(true)}
|
||||||
initFetch()
|
|
||||||
setIsOpen(true); } }
|
|
||||||
className="hover:underline cursor-pointer"
|
className="hover:underline cursor-pointer"
|
||||||
>
|
>
|
||||||
{isDetail ? "Detail" : "Edit"}
|
{isDetail ? "Detail" : "Edit"}
|
||||||
|
|
|
||||||
|
|
@ -10,34 +10,15 @@ export default function StatusToogle(props: {
|
||||||
}) {
|
}) {
|
||||||
const { id, initValue } = props;
|
const { id, initValue } = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// const publishCategory = async (id: number, status: string) => {
|
|
||||||
// const response = await publishUnpublishCategory(id, status);
|
|
||||||
// console.log(response);
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// router.push("/admin/settings/category?dataChange=true");
|
|
||||||
// };
|
|
||||||
const publishCategory = async (id: number, status: string) => {
|
const publishCategory = async (id: number, status: string) => {
|
||||||
const response = await publishUnpublishCategory(id, status);
|
const response = await publishUnpublishCategory(id, status);
|
||||||
console.log("API Response:", response);
|
console.log(response);
|
||||||
|
|
||||||
// cek error interceptor
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
error(response.message || "Terjadi kesalahan");
|
error(response.message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cek flag success asli dari backend
|
|
||||||
if (response?.data?.success === false) {
|
|
||||||
error(response?.data?.message || "Terjadi kesalahan");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push("/admin/settings/category?dataChange=true");
|
router.push("/admin/settings/category?dataChange=true");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
id={String(id)}
|
id={String(id)}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,21 @@ import {
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import columns from "./column";
|
import columns from "./column";
|
||||||
|
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { NewCampaignIcon } from "@/components/icon";
|
||||||
import { getCategories } from "@/service/settings/settings";
|
import { getCategories } from "@/service/settings/settings";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import CreateCategoryModal from "./create";
|
import CreateCategoryModal from "./create";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import {
|
import {
|
||||||
|
|
@ -175,7 +186,7 @@ const AdminCategoryTable = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between mb-10 items-center">
|
<div className="flex justify-between mb-10 items-center">
|
||||||
<p className="text-xl font-medium text-default-900">{t("category", { defaultValue: "Category" })}</p>
|
<p className="text-xl font-medium text-default-900">{t("category", { defaultValue: "Category" })}</p>
|
||||||
<CreateCategoryModal />
|
<CreateCategoryModal />
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,11 @@ export function UnitMapping(props: {
|
||||||
|
|
||||||
const unitType = form.watch("items");
|
const unitType = form.watch("items");
|
||||||
|
|
||||||
const isAllUnitChecked = unitType && unitList.every((item) =>
|
const isAllUnitChecked = unitList.every((item) =>
|
||||||
unitType.includes(String(item.id))
|
unitType?.includes(String(item.id))
|
||||||
);
|
);
|
||||||
const isAllSatkerChecked = unitType && satkerList.every((item) =>
|
const isAllSatkerChecked = satkerList.every((item) =>
|
||||||
unitType.includes(String(item.id))
|
unitType?.includes(String(item.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
const setupUnit = (data: UnitType[]) => {
|
const setupUnit = (data: UnitType[]) => {
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@ export default function CreateFAQModal() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
const target = form.watch("publishTo");
|
||||||
const isAllTargetChecked = target && publishToList.every((item) =>
|
const isAllTargetChecked = publishToList.every((item) =>
|
||||||
target.includes(item.id)
|
target?.includes(item.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -78,7 +78,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
// loading();
|
// loading();
|
||||||
const data = {
|
const data = {
|
||||||
|
|
@ -132,7 +132,7 @@ const columns: ColumnDef<any>[] = [
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
{/* <DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
<Link href={`/admin/settings/iklan/detail/${row.original.id}`}>
|
<Link href={`/admin/settings/iklan/detail/${row.original.id}`}>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
|
@ -152,33 +152,6 @@ const columns: ColumnDef<any>[] = [
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent> */}
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<Link href={`/admin/settings/iklan/detail/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{levelNumber === "1" && (
|
|
||||||
<>
|
|
||||||
<Link href={`/admin/settings/iklan/update/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleDeleteAdvertisements(row.original.id)}
|
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import {
|
||||||
UploadIcon,
|
UploadIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -53,7 +53,7 @@ import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import columns from "./column";
|
import columns from "./column";
|
||||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||||
|
|
@ -69,7 +69,7 @@ import {
|
||||||
import { listEnableCategory } from "@/service/content/content";
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
import { TambahIklanModal } from "@/components/form/setting/form-add-iklan";
|
import { TambahIklanModal } from "@/components/form/setting/form-add-iklan";
|
||||||
|
|
||||||
const AdvertisementsList = () => {
|
const AdvertisementsList = () => {
|
||||||
|
|
@ -94,9 +94,6 @@ const AdvertisementsList = () => {
|
||||||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const roleId = getCookiesDecrypt("urie");
|
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
|
|
@ -153,7 +150,11 @@ const AdvertisementsList = () => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
loading();
|
loading();
|
||||||
const res = await listDataAdvertisements(page - 1, showData, "");
|
const res = await listDataAdvertisements(
|
||||||
|
page - 1,
|
||||||
|
showData,
|
||||||
|
"",
|
||||||
|
);
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.content;
|
const contentData = data?.content;
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
|
@ -206,27 +207,14 @@ const AdvertisementsList = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
{levelNumber === "1" && (
|
<div className="flex-none">
|
||||||
<div className="flex-none">
|
<Link href={"/admin/settings/iklan/create"}>
|
||||||
<Link href={"/admin/settings/iklan/create"}>
|
<Button color="primary" className="text-white" size="md">
|
||||||
<Button
|
<UploadIcon size={18} className="mr-2" />
|
||||||
disabled={dataTable.length == 4}
|
Tambah Iklan
|
||||||
color="primary"
|
</Button>
|
||||||
className="text-white"
|
</Link>
|
||||||
size="md"
|
</div>
|
||||||
>
|
|
||||||
<UploadIcon size={18} className="mr-2" />
|
|
||||||
Tambah Iklan
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
{dataTable.length == 4 && (
|
|
||||||
<p className="text-sm text-red-400 pt-1">
|
|
||||||
Jumlah Iklan Sudah Maksimal (4)
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* <TambahIklanModal /> */}
|
{/* <TambahIklanModal /> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between ">
|
<div className="flex justify-between ">
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,22 @@ import {
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
import { listEnableCategory } from "@/service/content/content";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { close, loading } from "@/config/swal";
|
import { close, loading } from "@/config/swal";
|
||||||
import { Link, useRouter } from "@/i18n/routing";
|
import { Link, useRouter } from "@/i18n/routing";
|
||||||
|
import { NewCampaignIcon } from "@/components/icon";
|
||||||
import { getCategories } from "@/service/settings/settings";
|
import { getCategories } from "@/service/settings/settings";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
|
|
@ -176,7 +188,7 @@ const AdminSettingTrackingTable = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex items-end justify-between">
|
<div className="flex items-end justify-between">
|
||||||
{/* <CreateSettingTracking /> */}
|
{/* <CreateSettingTracking /> */}
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ const SurveyListTable = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">Survey</div>
|
<div className="flex-1 text-xl font-medium text-default-900">Survey</div>
|
||||||
<div className="flex flex-row gap-2 items-center justify-between">
|
<div className="flex flex-row gap-2 items-center justify-between">
|
||||||
<div className="w-full md:w-[200px] lg:w-[300px] px-2">
|
<div className="w-full md:w-[200px] lg:w-[300px] px-2">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export default function AdminSurvey() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<SurveyListTable />
|
<SurveyListTable />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string[]>([]);
|
const [selectedCategory, setSelectedCategory] = useState<string[]>([]);
|
||||||
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
||||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
console.log("DUARR", roleId)
|
||||||
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
|
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
|
||||||
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
@ -303,7 +303,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const firstType = typeSplit[0] as EventType;
|
const firstType = typeSplit[0] as EventType;
|
||||||
|
|
||||||
const colors: Record<EventType, string> = {
|
const colors: Record<EventType, string> = {
|
||||||
"0": "bg-gray-500",
|
"0": "bg-black",
|
||||||
"1": "bg-yellow-500",
|
"1": "bg-yellow-500",
|
||||||
"2": "bg-blue-400",
|
"2": "bg-blue-400",
|
||||||
"3": "bg-slate-400",
|
"3": "bg-slate-400",
|
||||||
|
|
@ -408,16 +408,14 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
const hasMoreEvents = events.length > 3;
|
const hasMoreEvents = events.length > 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 bg-white dark:bg-black rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
<div className="flex-1 bg-white rounded-lg shadow-sm border border-gray-200 pb-3 mr-1">
|
||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
<h4 className="font-bold text-center">{label}</h4>
|
<h4 className="font-bold text-center">{label}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
{events.length === 0 ? (
|
{events.length === 0 ? (
|
||||||
<div className="mt-1 py-2 rounded-lg bg-white dark:bg-black border border-black dark:border-gray-500">
|
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
|
||||||
<p className="text-center">
|
<p className="text-center">{t("no-data-yet", { defaultValue: "No Data Yet" })}</p>
|
||||||
{t("no-data-yet", { defaultValue: "No Data Yet" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
@ -529,16 +527,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<CardHeader className="border-none mb-2 pt-5">
|
<CardHeader className="border-none mb-2 pt-5">
|
||||||
{/* {[3, 11, 2, 12].includes(roleId) && (
|
{[3, 11, 2, 12].includes(roleId) && (
|
||||||
<Button
|
|
||||||
onClick={handleDateClick}
|
|
||||||
className="dark:bg-background dark:text-foreground w-full"
|
|
||||||
>
|
|
||||||
<Plus className="w-4 h-4 me-1" />
|
|
||||||
{t("addEvent", { defaultValue: "Add Event" })}
|
|
||||||
</Button>
|
|
||||||
)} */}
|
|
||||||
{[3, 11, 2, 12].includes(roleId) && levelNumber !== 3 && (
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleDateClick}
|
onClick={handleDateClick}
|
||||||
className="dark:bg-background dark:text-foreground w-full"
|
className="dark:bg-background dark:text-foreground w-full"
|
||||||
|
|
@ -553,18 +542,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="dark:bg-background dark:text-foreground w-full">
|
<Button className="dark:bg-background dark:text-foreground w-full">
|
||||||
<Book size={15} className="w-4 h-4 mr-3" />
|
<Book size={15} className="w-4 h-4 mr-3" />
|
||||||
{t("bag-pa-monitoring-results", {
|
{t("bag-pa-monitoring-results", { defaultValue: "Bag Pa Monitoring Results" })}
|
||||||
defaultValue: "Bag Pa Monitoring Results",
|
|
||||||
})}
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>{t("monitoring-results", { defaultValue: "Monitoring Results" })}</DialogTitle>
|
||||||
{t("monitoring-results", {
|
|
||||||
defaultValue: "Monitoring Results",
|
|
||||||
})}
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{getModalContent()}
|
{getModalContent()}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
|
||||||
|
|
@ -108,10 +108,8 @@ const EventModal = ({
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
|
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||||
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
|
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||||
{}
|
|
||||||
);
|
|
||||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||||
const [isRecording, setIsRecording] = useState(false);
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
const [timer, setTimer] = useState<number>(120);
|
const [timer, setTimer] = useState<number>(120);
|
||||||
|
|
@ -154,17 +152,7 @@ const EventModal = ({
|
||||||
international: false,
|
international: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// State untuk melacak apakah perubahan berasal dari checkbox Jenis Agenda
|
const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
|
||||||
const [isUpdatingFromJenisAgenda, setIsUpdatingFromJenisAgenda] =
|
|
||||||
useState(false);
|
|
||||||
// State untuk melacak jenis perubahan spesifik
|
|
||||||
const [jenisAgendaChangeType, setJenisAgendaChangeType] =
|
|
||||||
useState<string>("");
|
|
||||||
|
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
const poldaState = Cookies.get("state");
|
|
||||||
const [agendaType, setAgendaType] = React.useState("");
|
|
||||||
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
||||||
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
||||||
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
||||||
|
|
@ -263,272 +251,15 @@ const EventModal = ({
|
||||||
fetchDetailData();
|
fetchDetailData();
|
||||||
}, [event, setValue]);
|
}, [event, setValue]);
|
||||||
|
|
||||||
// useEffect untuk sinkronisasi checkbox modal dengan Jenis Agenda
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
listDest.length > 0 &&
|
|
||||||
isUpdatingFromJenisAgenda &&
|
|
||||||
jenisAgendaChangeType
|
|
||||||
) {
|
|
||||||
syncModalWithJenisAgenda();
|
|
||||||
}
|
|
||||||
}, [isUpdatingFromJenisAgenda, jenisAgendaChangeType]);
|
|
||||||
|
|
||||||
// useEffect untuk update wilayahPublish ketika pilihan modal berubah
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isUpdatingFromJenisAgenda && listDest.length > 0) {
|
|
||||||
updateWilayahPublishFromModal();
|
|
||||||
}
|
|
||||||
}, [checkedLevels, isUpdatingFromJenisAgenda]);
|
|
||||||
|
|
||||||
// Fungsi untuk update wilayahPublish berdasarkan checkbox modal
|
|
||||||
const updateWilayahPublishFromModal = () => {
|
|
||||||
// Hanya update jika tidak sedang dalam proses update dari Jenis Agenda
|
|
||||||
if (!isUpdatingFromJenisAgenda && listDest.length > 0) {
|
|
||||||
// Hitung item yang dipilih berdasarkan checkedLevels
|
|
||||||
const checkedPoldaCount = listDest.filter(
|
|
||||||
(item: any) =>
|
|
||||||
item.levelNumber === 2 &&
|
|
||||||
item.name !== "SATKER POLRI" &&
|
|
||||||
checkedLevels.has(Number(item.id))
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const checkedPolresCount = listDest.reduce((total: number, item: any) => {
|
|
||||||
if (item.subDestination) {
|
|
||||||
return (
|
|
||||||
total +
|
|
||||||
item.subDestination.filter((sub: any) =>
|
|
||||||
checkedLevels.has(Number(sub.id))
|
|
||||||
).length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
const satkerItem: any = listDest.find(
|
|
||||||
(item: any) => item.name === "SATKER POLRI"
|
|
||||||
);
|
|
||||||
const checkedSatkerCount = satkerItem
|
|
||||||
? (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) +
|
|
||||||
(satkerItem.subDestination?.filter((sub: any) =>
|
|
||||||
checkedLevels.has(Number(sub.id))
|
|
||||||
).length || 0)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
// Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut
|
|
||||||
const hasSelectedPolda = checkedPoldaCount > 0;
|
|
||||||
const hasSelectedPolres = checkedPolresCount > 0;
|
|
||||||
const hasSelectedSatker = checkedSatkerCount > 0;
|
|
||||||
|
|
||||||
// Update arrays untuk backend
|
|
||||||
const newSelectedPolda = listDest
|
|
||||||
.filter(
|
|
||||||
(item: any) =>
|
|
||||||
item.levelNumber === 2 &&
|
|
||||||
item.name !== "SATKER POLRI" &&
|
|
||||||
checkedLevels.has(Number(item.id))
|
|
||||||
)
|
|
||||||
.map((item: any) => String(item.id));
|
|
||||||
|
|
||||||
const newSelectedPolres: string[] = [];
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((sub: any) => {
|
|
||||||
if (checkedLevels.has(Number(sub.id))) {
|
|
||||||
newSelectedPolres.push(String(sub.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const newSelectedSatker: string[] = [];
|
|
||||||
if (satkerItem) {
|
|
||||||
if (checkedLevels.has(Number(satkerItem.id))) {
|
|
||||||
newSelectedSatker.push(String(satkerItem.id));
|
|
||||||
}
|
|
||||||
if (satkerItem.subDestination) {
|
|
||||||
satkerItem.subDestination.forEach((sub: any) => {
|
|
||||||
if (checkedLevels.has(Number(sub.id))) {
|
|
||||||
newSelectedSatker.push(String(sub.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update state arrays
|
|
||||||
setSelectedPolda(newSelectedPolda);
|
|
||||||
setSelectedPolres(newSelectedPolres);
|
|
||||||
setSelectedSatker(newSelectedSatker);
|
|
||||||
|
|
||||||
// Update wilayahPublish berdasarkan yang dipilih di modal
|
|
||||||
setWilayahPublish((prev) => {
|
|
||||||
const newState = { ...prev };
|
|
||||||
|
|
||||||
// Update individual checkboxes
|
|
||||||
newState.polda = hasSelectedPolda;
|
|
||||||
newState.polres = hasSelectedPolres;
|
|
||||||
newState.satker = hasSelectedSatker;
|
|
||||||
|
|
||||||
// Update checkbox "semua" berdasarkan level user
|
|
||||||
if (levelNumber === 1) {
|
|
||||||
// Level 1: semua checkbox harus aktif (nasional, polda, polres, satker, international)
|
|
||||||
newState.semua =
|
|
||||||
newState.nasional &&
|
|
||||||
hasSelectedPolda &&
|
|
||||||
hasSelectedPolres &&
|
|
||||||
hasSelectedSatker &&
|
|
||||||
newState.international;
|
|
||||||
} else if (levelNumber === 2) {
|
|
||||||
// Level 2: hanya polres yang perlu aktif
|
|
||||||
newState.semua = hasSelectedPolres;
|
|
||||||
} else {
|
|
||||||
newState.semua = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newState;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update agendaType berdasarkan checkbox yang aktif
|
|
||||||
const selectedKeys = [];
|
|
||||||
if (hasSelectedPolda) selectedKeys.push(wilayahValueMap.polda);
|
|
||||||
if (hasSelectedPolres) selectedKeys.push(wilayahValueMap.polres);
|
|
||||||
if (hasSelectedSatker) selectedKeys.push(wilayahValueMap.satker);
|
|
||||||
|
|
||||||
setAgendaType(selectedKeys.join(","));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fungsi untuk sinkronisasi checkbox modal dengan Jenis Agenda
|
|
||||||
const syncModalWithJenisAgenda = () => {
|
|
||||||
// Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Jenis Agenda
|
|
||||||
if (isUpdatingFromJenisAgenda) {
|
|
||||||
const newCheckedLevels = new Set(checkedLevels);
|
|
||||||
|
|
||||||
// Handle checklist actions - menambahkan semua item ke modal
|
|
||||||
if (jenisAgendaChangeType === "polda_checked") {
|
|
||||||
// Checklist semua polda
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
|
||||||
newCheckedLevels.add(Number(item.id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (jenisAgendaChangeType === "polres_checked") {
|
|
||||||
// Checklist semua polres, tapi hanya yang poldanya sudah di-checklist
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (
|
|
||||||
item.levelNumber === 2 &&
|
|
||||||
item.name !== "SATKER POLRI" &&
|
|
||||||
newCheckedLevels.has(Number(item.id))
|
|
||||||
) {
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((polres: any) => {
|
|
||||||
newCheckedLevels.add(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (jenisAgendaChangeType === "satker_checked") {
|
|
||||||
// Checklist satker
|
|
||||||
const satkerItem: any = listDest.find(
|
|
||||||
(item: any) => item.name === "SATKER POLRI"
|
|
||||||
);
|
|
||||||
if (satkerItem) {
|
|
||||||
newCheckedLevels.add(Number(satkerItem.id));
|
|
||||||
if (satkerItem.subDestination) {
|
|
||||||
satkerItem.subDestination.forEach((sub: any) => {
|
|
||||||
newCheckedLevels.add(Number(sub.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Handle unchecklist actions - menghapus item dari modal
|
|
||||||
else if (jenisAgendaChangeType === "polres_unchecked") {
|
|
||||||
// Clear polres dari checkedLevels
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((polres: any) => {
|
|
||||||
newCheckedLevels.delete(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (jenisAgendaChangeType === "polda_unchecked") {
|
|
||||||
// Clear polda dan polres dari checkedLevels
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
|
||||||
newCheckedLevels.delete(Number(item.id));
|
|
||||||
// Juga clear polres dari polda ini
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((polres: any) => {
|
|
||||||
newCheckedLevels.delete(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setWilayahPublish((prev) => ({ ...prev, polres: false }));
|
|
||||||
} else if (jenisAgendaChangeType === "satker_unchecked") {
|
|
||||||
// Clear satker dari checkedLevels
|
|
||||||
const satkerItem: any = listDest.find(
|
|
||||||
(item: any) => item.name === "SATKER POLRI"
|
|
||||||
);
|
|
||||||
if (satkerItem) {
|
|
||||||
newCheckedLevels.delete(Number(satkerItem.id));
|
|
||||||
if (satkerItem.subDestination) {
|
|
||||||
satkerItem.subDestination.forEach((sub: any) => {
|
|
||||||
newCheckedLevels.delete(Number(sub.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCheckedLevels(newCheckedLevels);
|
|
||||||
|
|
||||||
// Reset flag setelah sinkronisasi selesai
|
|
||||||
setIsUpdatingFromJenisAgenda(false);
|
|
||||||
setJenisAgendaChangeType("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDatePickerOpen(false);
|
setIsDatePickerOpen(false);
|
||||||
}, [onClose]);
|
}, [onClose])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchPoldaPolres() {
|
|
||||||
try {
|
|
||||||
const response = await getUserLevelForAssignments();
|
|
||||||
setListDest(response?.data?.data.list);
|
|
||||||
const initialExpandedState = response?.data?.data.list.reduce(
|
|
||||||
(acc: any, polda: any) => {
|
|
||||||
acc[polda.id] = false;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
setExpandedPolda(initialExpandedState);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching Polda/Polres data:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchPoldaPolres();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleCheckboxChange = (levelId: number) => {
|
const handleCheckboxChange = (levelId: number) => {
|
||||||
setCheckedLevels((prev) => {
|
setCheckedLevels((prev) => {
|
||||||
const updatedLevels = new Set(prev);
|
const updatedLevels = new Set(prev);
|
||||||
const isCurrentlyChecked = updatedLevels.has(levelId);
|
if (updatedLevels.has(levelId)) {
|
||||||
|
|
||||||
if (isCurrentlyChecked) {
|
|
||||||
updatedLevels.delete(levelId);
|
updatedLevels.delete(levelId);
|
||||||
|
|
||||||
// Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya
|
|
||||||
const poldaItem = listDest.find(
|
|
||||||
(item: any) => Number(item.id) === levelId
|
|
||||||
) as any;
|
|
||||||
if (poldaItem && poldaItem.subDestination) {
|
|
||||||
poldaItem.subDestination.forEach((polres: any) => {
|
|
||||||
updatedLevels.delete(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
updatedLevels.add(levelId);
|
updatedLevels.add(levelId);
|
||||||
}
|
}
|
||||||
|
|
@ -546,15 +277,6 @@ const EventModal = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleWilayah = (key: string) => {
|
const toggleWilayah = (key: string) => {
|
||||||
// Set flag bahwa perubahan berasal dari checkbox Jenis Agenda
|
|
||||||
setIsUpdatingFromJenisAgenda(true);
|
|
||||||
setJenisAgendaChangeType(
|
|
||||||
key +
|
|
||||||
(wilayahPublish[key as keyof typeof wilayahPublish]
|
|
||||||
? "_unchecked"
|
|
||||||
: "_checked")
|
|
||||||
);
|
|
||||||
|
|
||||||
setWilayahPublish((prev: any) => {
|
setWilayahPublish((prev: any) => {
|
||||||
let newState = { ...prev };
|
let newState = { ...prev };
|
||||||
if (key === "semua") {
|
if (key === "semua") {
|
||||||
|
|
@ -570,106 +292,15 @@ const EventModal = ({
|
||||||
|
|
||||||
if (newChecked) {
|
if (newChecked) {
|
||||||
setAgendaType("0,1,2,3,4,5");
|
setAgendaType("0,1,2,3,4,5");
|
||||||
// Checklist semua item di modal ketika "semua" di-checklist
|
|
||||||
const allCheckedLevels = new Set<number>();
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
allCheckedLevels.add(Number(item.id));
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((sub: any) => {
|
|
||||||
allCheckedLevels.add(Number(sub.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setCheckedLevels(allCheckedLevels);
|
|
||||||
} else {
|
} else {
|
||||||
setAgendaType("");
|
setAgendaType("");
|
||||||
// Clear semua pilihan modal ketika "semua" di-unchecklist
|
|
||||||
setCheckedLevels(new Set());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validasi khusus untuk POLRES
|
|
||||||
if (key === "polres" && !prev[key]) {
|
|
||||||
// Cek apakah ada POLDA yang sudah dipilih di modal
|
|
||||||
const hasSelectedPolda = listDest.some(
|
|
||||||
(item: any) =>
|
|
||||||
item.levelNumber === 2 &&
|
|
||||||
item.name !== "SATKER POLRI" &&
|
|
||||||
checkedLevels.has(Number(item.id))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasSelectedPolda) {
|
|
||||||
// Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan
|
|
||||||
alert(
|
|
||||||
"Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."
|
|
||||||
);
|
|
||||||
// Reset flag karena perubahan dibatalkan
|
|
||||||
setIsUpdatingFromJenisAgenda(false);
|
|
||||||
setJenisAgendaChangeType("");
|
|
||||||
return prev; // Batalkan perubahan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newState[key] = !prev[key];
|
newState[key] = !prev[key];
|
||||||
|
newState.semua = false;
|
||||||
// Jika checkbox di-unchecklist, clear pilihan modal yang sesuai
|
|
||||||
if (prev[key]) {
|
|
||||||
const newCheckedLevels = new Set(checkedLevels);
|
|
||||||
if (key === "polda") {
|
|
||||||
// Clear polda dan polres
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
|
|
||||||
newCheckedLevels.delete(Number(item.id));
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((polres: any) => {
|
|
||||||
newCheckedLevels.delete(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (key === "polres") {
|
|
||||||
// Clear polres
|
|
||||||
listDest.forEach((item: any) => {
|
|
||||||
if (item.subDestination) {
|
|
||||||
item.subDestination.forEach((polres: any) => {
|
|
||||||
newCheckedLevels.delete(Number(polres.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (key === "satker") {
|
|
||||||
// Clear satker
|
|
||||||
const satkerItem: any = listDest.find(
|
|
||||||
(item: any) => item.name === "SATKER POLRI"
|
|
||||||
);
|
|
||||||
if (satkerItem) {
|
|
||||||
newCheckedLevels.delete(Number(satkerItem.id));
|
|
||||||
if (satkerItem.subDestination) {
|
|
||||||
satkerItem.subDestination.forEach((sub: any) => {
|
|
||||||
newCheckedLevels.delete(Number(sub.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setCheckedLevels(newCheckedLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update checkbox "semua" berdasarkan status semua checkbox lainnya
|
|
||||||
// Untuk level 1: semua, nasional, polda, polres, satker, international harus aktif
|
|
||||||
// Untuk level 2: semua, polres harus aktif
|
|
||||||
if (levelNumber === 1) {
|
|
||||||
newState.semua =
|
|
||||||
newState.nasional &&
|
|
||||||
newState.polda &&
|
|
||||||
newState.polres &&
|
|
||||||
newState.satker &&
|
|
||||||
newState.international;
|
|
||||||
} else if (levelNumber === 2) {
|
|
||||||
newState.semua = newState.polres;
|
|
||||||
} else {
|
|
||||||
newState.semua = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedKeys = Object.entries(newState)
|
const selectedKeys = Object.entries(newState)
|
||||||
.filter(([k, v]) => v && k !== "semua")
|
.filter(([k, v]) => v && k !== "semua")
|
||||||
|
|
@ -720,8 +351,8 @@ const EventModal = ({
|
||||||
id: detailData?.id,
|
id: detailData?.id,
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
agendaType: agendaTypeList.join(","),
|
agendaType: agendaTypeList.join(","),
|
||||||
assignedToLevel: assignedToLevelList.join(","),
|
assignedToLevel: assignedToLevelList.join(","),
|
||||||
startDate: date?.from ? format(date.from, "yyyy-MM-dd") : null,
|
startDate: date?.from ? format(date.from, "yyyy-MM-dd") : null,
|
||||||
endDate: date?.to ? format(date.to, "yyyy-MM-dd") : null,
|
endDate: date?.to ? format(date.to, "yyyy-MM-dd") : null,
|
||||||
};
|
};
|
||||||
|
|
@ -762,7 +393,13 @@ const EventModal = ({
|
||||||
setIsAudioUploadFinish(true);
|
setIsAudioUploadFinish(true);
|
||||||
}
|
}
|
||||||
audioFiles.map(async (item: FileWithPreview, index: number) => {
|
audioFiles.map(async (item: FileWithPreview, index: number) => {
|
||||||
await uploadResumableFile(index, String(id), item, "4", "0");
|
await uploadResumableFile(
|
||||||
|
index,
|
||||||
|
String(id),
|
||||||
|
item,
|
||||||
|
"4",
|
||||||
|
"0"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
if (publish) {
|
if (publish) {
|
||||||
setIsPublishing(true);
|
setIsPublishing(true);
|
||||||
|
|
@ -796,7 +433,7 @@ const EventModal = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Event data:", event);
|
console.log("Event data:", event);
|
||||||
console.log("Selected date:", selectedDate);
|
console.log("Selected date:", selectedDate);
|
||||||
|
|
||||||
if (selectedDate) {
|
if (selectedDate) {
|
||||||
setDate({
|
setDate({
|
||||||
from: selectedDate.date,
|
from: selectedDate.date,
|
||||||
|
|
@ -842,23 +479,23 @@ const EventModal = ({
|
||||||
const countdown = setInterval(() => {
|
const countdown = setInterval(() => {
|
||||||
setTimer((prevTimer) => {
|
setTimer((prevTimer) => {
|
||||||
if (prevTimer <= 1) {
|
if (prevTimer <= 1) {
|
||||||
clearInterval(countdown);
|
clearInterval(countdown);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return prevTimer - 1;
|
return prevTimer - 1;
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
handleStopRecording();
|
handleStopRecording();
|
||||||
}
|
}
|
||||||
}, 120000);
|
}, 120000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStopRecording = () => {
|
const handleStopRecording = () => {
|
||||||
setIsRecording(false);
|
setIsRecording(false);
|
||||||
setTimer(120);
|
setTimer(120);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addAudioElement = (blob: Blob) => {
|
const addAudioElement = (blob: Blob) => {
|
||||||
|
|
@ -894,6 +531,7 @@ const EventModal = ({
|
||||||
|
|
||||||
const resCsrf = await getCsrfToken();
|
const resCsrf = await getCsrfToken();
|
||||||
const csrfToken = resCsrf?.data?.token;
|
const csrfToken = resCsrf?.data?.token;
|
||||||
|
console.log("CSRF TOKEN : ", csrfToken);
|
||||||
const headers = {
|
const headers = {
|
||||||
"X-XSRF-TOKEN": csrfToken,
|
"X-XSRF-TOKEN": csrfToken,
|
||||||
};
|
};
|
||||||
|
|
@ -1104,10 +742,7 @@ const EventModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<Label htmlFor="date">Tanggal</Label>
|
<Label htmlFor="date">Tanggal</Label>
|
||||||
<Popover
|
<Popover open={isDatePickerOpen} onOpenChange={() => setIsDatePickerOpen(true)}>
|
||||||
open={isDatePickerOpen}
|
|
||||||
onOpenChange={() => setIsDatePickerOpen(true)}
|
|
||||||
>
|
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -1164,180 +799,88 @@ const EventModal = ({
|
||||||
Semua
|
Semua
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
{levelNumber === 1 && (
|
<Checkbox
|
||||||
<>
|
id="nasional"
|
||||||
<div>
|
checked={wilayahPublish.nasional}
|
||||||
<Checkbox
|
onCheckedChange={() => toggleWilayah("nasional")}
|
||||||
id="nasional"
|
/>
|
||||||
checked={wilayahPublish.nasional}
|
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||||
onCheckedChange={() => toggleWilayah("nasional")}
|
Nasional
|
||||||
/>
|
</label>
|
||||||
<label
|
</div>
|
||||||
htmlFor="nasional"
|
<div>
|
||||||
className="ml-2 text-sm mr-2"
|
<Checkbox
|
||||||
>
|
id="polda"
|
||||||
Nasional
|
checked={wilayahPublish.polda}
|
||||||
</label>
|
onCheckedChange={() => toggleWilayah("polda")}
|
||||||
</div>
|
/>
|
||||||
<div>
|
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
||||||
<Checkbox
|
Polda
|
||||||
id="polda"
|
</label>
|
||||||
checked={wilayahPublish.polda}
|
{wilayahPublish.polda && (
|
||||||
onCheckedChange={() => toggleWilayah("polda")}
|
<UnitMapping
|
||||||
/>
|
unit="Polda"
|
||||||
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
isDetail={isDetailMode}
|
||||||
Polda
|
initData={selectedPolda}
|
||||||
</label>
|
sendDataToParent={(data: any) =>
|
||||||
</div>
|
setSelectedPolda(data)
|
||||||
</>
|
}
|
||||||
)}
|
|
||||||
|
|
||||||
{(levelNumber === 1 || levelNumber === 2) && (
|
|
||||||
<div>
|
|
||||||
<Checkbox
|
|
||||||
id="polres"
|
|
||||||
checked={wilayahPublish.polres}
|
|
||||||
onCheckedChange={() => toggleWilayah("polres")}
|
|
||||||
/>
|
/>
|
||||||
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
)}
|
||||||
Polres
|
</div>
|
||||||
</label>
|
<div>
|
||||||
</div>
|
<Checkbox
|
||||||
)}
|
id="polres"
|
||||||
|
checked={wilayahPublish.polres}
|
||||||
{levelNumber === 1 && (
|
onCheckedChange={() => toggleWilayah("polres")}
|
||||||
<>
|
/>
|
||||||
<div>
|
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||||
<Checkbox
|
Polres
|
||||||
id="satker"
|
</label>
|
||||||
checked={wilayahPublish.satker}
|
{wilayahPublish.polres && (
|
||||||
onCheckedChange={() => toggleWilayah("satker")}
|
<UnitMapping
|
||||||
/>
|
unit="Polres"
|
||||||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
isDetail={isDetailMode}
|
||||||
Satker
|
initData={selectedPolres}
|
||||||
</label>
|
sendDataToParent={(data: any) =>
|
||||||
</div>
|
setSelectedPolres(data)
|
||||||
<div>
|
}
|
||||||
<Checkbox
|
/>
|
||||||
id="international"
|
)}
|
||||||
checked={wilayahPublish.international}
|
</div>
|
||||||
onCheckedChange={() =>
|
<div>
|
||||||
toggleWilayah("international")
|
<Checkbox
|
||||||
}
|
id="satker"
|
||||||
/>
|
checked={wilayahPublish.satker}
|
||||||
<label
|
onCheckedChange={() => toggleWilayah("satker")}
|
||||||
htmlFor="international"
|
/>
|
||||||
className="ml-2 text-sm mr-2"
|
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||||
>
|
Satker
|
||||||
Internasional
|
</label>
|
||||||
</label>
|
{wilayahPublish.satker && (
|
||||||
</div>
|
<UnitMapping
|
||||||
</>
|
unit="Satker"
|
||||||
)}
|
isDetail={isDetailMode}
|
||||||
|
initData={selectedSatker}
|
||||||
<div className="pl-1">
|
sendDataToParent={(data: any) =>
|
||||||
<Dialog>
|
setSelectedSatker(data)
|
||||||
<DialogTrigger asChild>
|
}
|
||||||
<Button variant="soft" size="sm" color="primary">
|
/>
|
||||||
[Kustom]
|
)}
|
||||||
</Button>
|
</div>
|
||||||
</DialogTrigger>
|
<div>
|
||||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
<Checkbox
|
||||||
<DialogHeader>
|
id="international"
|
||||||
<DialogTitle>
|
checked={wilayahPublish.international}
|
||||||
Daftar Wilayah Polda dan Polres
|
onCheckedChange={() => toggleWilayah("international")}
|
||||||
</DialogTitle>
|
/>
|
||||||
</DialogHeader>
|
<label
|
||||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
htmlFor="international"
|
||||||
{listDest?.map((polda: any) => (
|
className="ml-2 text-sm mr-2"
|
||||||
<div key={polda.id} className="border p-2">
|
>
|
||||||
<Label className="flex items-center">
|
Internasional
|
||||||
<Checkbox
|
</label>
|
||||||
checked={checkedLevels.has(
|
|
||||||
Number(polda.id)
|
|
||||||
)}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handleCheckboxChange(Number(polda.id))
|
|
||||||
}
|
|
||||||
className="mr-3"
|
|
||||||
/>
|
|
||||||
{polda.name}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
toggleExpand(polda.id);
|
|
||||||
}}
|
|
||||||
className="ml-2 focus:outline-none"
|
|
||||||
>
|
|
||||||
{expandedPolda[polda.id] ? (
|
|
||||||
<ChevronUp size={16} />
|
|
||||||
) : (
|
|
||||||
<ChevronDown size={16} />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</Label>
|
|
||||||
{expandedPolda[polda.id] && (
|
|
||||||
<div className="ml-6 mt-2">
|
|
||||||
<Label className="block">
|
|
||||||
<Checkbox
|
|
||||||
checked={polda?.subDestination?.every(
|
|
||||||
(polres: any) =>
|
|
||||||
checkedLevels.has(Number(polres.id))
|
|
||||||
)}
|
|
||||||
onCheckedChange={(isChecked) => {
|
|
||||||
const updatedLevels = new Set(
|
|
||||||
checkedLevels
|
|
||||||
);
|
|
||||||
polda?.subDestination?.forEach(
|
|
||||||
(polres: any) => {
|
|
||||||
if (isChecked) {
|
|
||||||
updatedLevels.add(
|
|
||||||
Number(polres.id)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
updatedLevels.delete(
|
|
||||||
Number(polres.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setCheckedLevels(updatedLevels);
|
|
||||||
}}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
Pilih Semua
|
|
||||||
</Label>
|
|
||||||
{polda?.subDestination?.map(
|
|
||||||
(polres: any) => (
|
|
||||||
<Label
|
|
||||||
key={polres.id}
|
|
||||||
className="block mt-1"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={checkedLevels.has(
|
|
||||||
Number(polres.id)
|
|
||||||
)}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
handleCheckboxChange(
|
|
||||||
Number(polres.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
{polres.name}
|
|
||||||
</Label>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1371,7 +914,8 @@ const EventModal = ({
|
||||||
<Label>Video</Label>
|
<Label>Video</Label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"video/*": [],
|
"mp4/*": [],
|
||||||
|
"mov/*": [],
|
||||||
}}
|
}}
|
||||||
maxSize={100}
|
maxSize={100}
|
||||||
label="Upload file dengan format .mp4 atau .mov."
|
label="Upload file dengan format .mp4 atau .mov."
|
||||||
|
|
@ -1383,7 +927,7 @@ const EventModal = ({
|
||||||
className="object-fill h-full w-full rounded-md"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={file.url}
|
src={file.url}
|
||||||
controls
|
controls
|
||||||
title={`Video ${file.id}`}
|
title={`Video ${file.id}`} // Mengganti alt dengan title
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
|
|
@ -1462,9 +1006,10 @@ const EventModal = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label>Teks</Label>
|
<Label>Teks</Label>
|
||||||
|
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"application/pdf": [],
|
"pdf/*": [],
|
||||||
}}
|
}}
|
||||||
maxSize={100}
|
maxSize={100}
|
||||||
label="Upload file dengan format .pdf."
|
label="Upload file dengan format .pdf."
|
||||||
|
|
@ -1520,7 +1065,8 @@ const EventModal = ({
|
||||||
/>
|
/>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
accept={{
|
accept={{
|
||||||
"audio/*": [],
|
"mp3/*": [],
|
||||||
|
"wav/*": [],
|
||||||
}}
|
}}
|
||||||
maxSize={100}
|
maxSize={100}
|
||||||
label="Upload file dengan format .mp3 atau .wav."
|
label="Upload file dengan format .mp3 atau .wav."
|
||||||
|
|
@ -1657,7 +1203,7 @@ const EventModal = ({
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
style={
|
style={
|
||||||
roleId > 12
|
roleId > 12
|
||||||
? {
|
? {
|
||||||
display: "none",
|
display: "none",
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,66 +65,38 @@ export function UnitMapping(props: {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
const response = await getUserLevelForAssignments();
|
const response = await getUserLevelForAssignments();
|
||||||
const list: UnitType[] = response?.data?.data?.list ?? [];
|
setupUnit(response?.data?.data.list);
|
||||||
setupUnit(list);
|
console.log("list", response?.data?.data.list);
|
||||||
// console.log("list", list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initState();
|
initState();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Selalu punya array
|
const unitType = form.watch("items");
|
||||||
const unitType = form.watch("items") ?? [];
|
|
||||||
|
|
||||||
// ---- FIX: case-insensitive + flatMap semua node ----
|
const isAllUnitChecked = unitList.every((item) =>
|
||||||
const setupUnit = (data: UnitType[] = []) => {
|
unitType?.includes(String(item.id))
|
||||||
const norm = (v?: string) => (v ?? "").toUpperCase();
|
);
|
||||||
|
const isAllSatkerChecked = satkerList.every((item) =>
|
||||||
|
unitType?.includes(String(item.id))
|
||||||
|
);
|
||||||
|
const isAllPolresChecked = polresList.every((item) =>
|
||||||
|
unitType?.includes(String(item.id))
|
||||||
|
);
|
||||||
|
|
||||||
const poldaNodes = data.filter((a) => norm(a.name).includes("POLDA"));
|
const setupUnit = (data: UnitType[]) => {
|
||||||
const satkerNodes = data.filter((a) => norm(a.name).includes("SATKER"));
|
const temp = data.filter((a) => a.name.includes("POLDA"));
|
||||||
|
const temp2 = data.filter((a) => a.name.includes("SATKER"));
|
||||||
// POLDA level (UnitType)
|
const temp3 = temp.flatMap((item) => item.subDestination || []);
|
||||||
setUnitList(poldaNodes);
|
setUnitList(temp);
|
||||||
|
setSatkerList(temp2[0]?.subDestination || []);
|
||||||
// SATKER adalah gabungan semua subDestination dari node SATKER
|
setPolresList(temp3);
|
||||||
const allSatker = satkerNodes.flatMap((n) => n.subDestination ?? []) ?? [];
|
|
||||||
setSatkerList(allSatker);
|
|
||||||
|
|
||||||
// POLRES adalah gabungan semua subDestination dari node POLDA
|
|
||||||
const allPolres = poldaNodes.flatMap((n) => n.subDestination ?? []);
|
|
||||||
setPolresList(allPolres);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- FIX: jangan true saat list kosong ----
|
|
||||||
const isAllUnitChecked =
|
|
||||||
unitList.length > 0 &&
|
|
||||||
unitList.every((item) => unitType.includes(String(item.id)));
|
|
||||||
|
|
||||||
const isAllSatkerChecked =
|
|
||||||
satkerList.length > 0 &&
|
|
||||||
satkerList.every((item) => unitType.includes(String(item.id)));
|
|
||||||
|
|
||||||
const isAllPolresChecked =
|
|
||||||
polresList.length > 0 &&
|
|
||||||
polresList.every((item) => unitType.includes(String(item.id)));
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sendDataToParent(form.getValues("items"));
|
sendDataToParent(form.getValues("items"));
|
||||||
}, [unitType]);
|
}, [unitType]);
|
||||||
|
|
||||||
// helper untuk ambil id-ids per kategori aktif
|
|
||||||
const currentIds = () => {
|
|
||||||
if (unit === "Polda") return unitList.map((i) => String(i.id));
|
|
||||||
if (unit === "Satker") return satkerList.map((i) => String(i.id));
|
|
||||||
return polresList.map((i) => String(i.id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const allCheckedByUnit =
|
|
||||||
unit === "Polda"
|
|
||||||
? isAllUnitChecked
|
|
||||||
: unit === "Satker"
|
|
||||||
? isAllSatkerChecked
|
|
||||||
: isAllPolresChecked;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
|
|
@ -144,48 +116,41 @@ export function UnitMapping(props: {
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`all-${unit}`}
|
id={`all-${unit}`}
|
||||||
checked={allCheckedByUnit}
|
checked={
|
||||||
|
unit === "Polda"
|
||||||
|
? isAllUnitChecked
|
||||||
|
: unit === "Satker"
|
||||||
|
? isAllSatkerChecked
|
||||||
|
: isAllPolresChecked
|
||||||
|
}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
const ids = currentIds();
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
// gabungkan supaya kategori lain tidak hilang
|
form.setValue(
|
||||||
const merged = Array.from(new Set([...unitType, ...ids]));
|
"items",
|
||||||
form.setValue("items", merged, {
|
unit === "Polda"
|
||||||
shouldDirty: true,
|
? unitList.map((item) => String(item.id))
|
||||||
shouldTouch: true,
|
: unit === "Satker"
|
||||||
shouldValidate: true,
|
? satkerList.map((item) => String(item.id))
|
||||||
});
|
: polresList.map((item) => String(item.id))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// hapus hanya id kategori aktif
|
form.setValue("items", []);
|
||||||
const filtered = unitType.filter((v) => !ids.includes(v));
|
|
||||||
form.setValue("items", filtered, {
|
|
||||||
shouldDirty: true,
|
|
||||||
shouldTouch: true,
|
|
||||||
shouldValidate: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label
|
|
||||||
htmlFor={`all-${unit}`}
|
<label htmlFor="all" className="text-sm text-black uppercase">
|
||||||
className="text-sm text-black uppercase"
|
|
||||||
>
|
|
||||||
SEMUA {unit}
|
SEMUA {unit}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="items"
|
name="items"
|
||||||
render={() => (
|
render={() => (
|
||||||
<FormItem
|
<FormItem
|
||||||
className={`grid ${
|
className={`grid grid-cols-${
|
||||||
unit === "Polda"
|
unit === "Polda" ? "2" : unit === "Satker" ? "3" : "4"
|
||||||
? "grid-cols-2"
|
}`}
|
||||||
: unit === "Satker"
|
|
||||||
? "grid-cols-3"
|
|
||||||
: "grid-cols-4"
|
|
||||||
} gap-2`}
|
|
||||||
>
|
>
|
||||||
{(unit === "Polda"
|
{(unit === "Polda"
|
||||||
? unitList
|
? unitList
|
||||||
|
|
@ -197,46 +162,33 @@ export function UnitMapping(props: {
|
||||||
key={item.id}
|
key={item.id}
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="items"
|
name="items"
|
||||||
render={({ field }) => (
|
render={({ field }) => {
|
||||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
return (
|
||||||
<FormControl>
|
<FormItem
|
||||||
<Checkbox
|
key={String(item.id)}
|
||||||
checked={field.value?.includes(String(item.id))}
|
className="flex flex-row items-center space-x-3 space-y-0"
|
||||||
onCheckedChange={(checked) => {
|
>
|
||||||
if (checked) {
|
<FormControl>
|
||||||
form.setValue(
|
<Checkbox
|
||||||
"items",
|
checked={field.value?.includes(String(item.id))}
|
||||||
Array.from(
|
onCheckedChange={(checked) => {
|
||||||
new Set([
|
return checked
|
||||||
...(field.value ?? []),
|
? field.onChange([
|
||||||
|
...field.value,
|
||||||
String(item.id),
|
String(item.id),
|
||||||
])
|
])
|
||||||
),
|
: field.onChange(
|
||||||
{
|
field.value?.filter(
|
||||||
shouldDirty: true,
|
(value) => value !== String(item.id)
|
||||||
shouldTouch: true,
|
)
|
||||||
shouldValidate: true,
|
);
|
||||||
}
|
}}
|
||||||
);
|
/>
|
||||||
} else {
|
</FormControl>
|
||||||
form.setValue(
|
<p className="text-sm text-black">{item.name}</p>
|
||||||
"items",
|
</FormItem>
|
||||||
(field.value ?? []).filter(
|
);
|
||||||
(v: string) => v !== String(item.id)
|
}}
|
||||||
),
|
|
||||||
{
|
|
||||||
shouldDirty: true,
|
|
||||||
shouldTouch: true,
|
|
||||||
shouldValidate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<p className="text-sm text-black">{item.name}</p>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import { deleteMedia } from "@/service/content/content";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "@/i18n/routing";
|
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table");
|
||||||
|
|
@ -182,11 +181,14 @@ const useTableColumns = () => {
|
||||||
header: t("action", { defaultValue: "Action" }),
|
header: t("action", { defaultValue: "Action" }),
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const router = useRouter();
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
const data = { id };
|
// loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await deleteMedia(data);
|
const response = await deleteMedia(data);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
|
|
@ -228,21 +230,16 @@ const useTableColumns = () => {
|
||||||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (userLevelId !== undefined && roleId !== undefined) {
|
if (userLevelId !== undefined && roleId !== undefined) {
|
||||||
setIsMabesApprover(
|
setIsMabesApprover(
|
||||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
const canEdit =
|
|
||||||
Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
isMabesApprover ||
|
|
||||||
roleId === 14;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -263,8 +260,16 @@ const useTableColumns = () => {
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
{/* <Link
|
||||||
{canEdit && (
|
href={`/contributor/content/audio/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link> */}
|
||||||
|
{(Number(row.original.uploadedById) === Number(userId) ||
|
||||||
|
isMabesApprover) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/content/audio/update/${row.original.id}`}
|
href={`/contributor/content/audio/update/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -274,7 +279,6 @@ const useTableColumns = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteMedia(row.original.id)}
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
|
@ -282,6 +286,15 @@ const useTableColumns = () => {
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Hapus
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)} */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -132,36 +132,29 @@ const useTableColumns = () => {
|
||||||
accessorKey: "statusName",
|
accessorKey: "statusName",
|
||||||
header: "Status",
|
header: "Status",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const statusId = Number(row.original?.statusId);
|
const statusColors: Record<string, string> = {
|
||||||
const reviewedAtLevel = row.original?.reviewedAtLevel || "";
|
diterima: "bg-green-100 text-green-600",
|
||||||
const creatorGroupLevelId = Number(row.original?.creatorGroupLevelId);
|
"menunggu review": "bg-orange-100 text-orange-600",
|
||||||
const needApprovalFromLevel = Number(
|
|
||||||
row.original?.needApprovalFromLevel
|
|
||||||
);
|
|
||||||
|
|
||||||
const userHasReviewed = reviewedAtLevel.includes(`:${userLevelId}:`);
|
|
||||||
const isCreator = creatorGroupLevelId === Number(userLevelId);
|
|
||||||
|
|
||||||
const isWaitingForReview =
|
|
||||||
statusId === 2 && !userHasReviewed && !isCreator;
|
|
||||||
|
|
||||||
const isApprovalNeeded =
|
|
||||||
statusId === 1 && needApprovalFromLevel === Number(userLevelId);
|
|
||||||
|
|
||||||
const label =
|
|
||||||
isWaitingForReview || isApprovalNeeded
|
|
||||||
? "Menunggu Review"
|
|
||||||
: statusId === 2
|
|
||||||
? "Diterima"
|
|
||||||
: row.original?.statusName;
|
|
||||||
|
|
||||||
const colors: Record<string, string> = {
|
|
||||||
"Menunggu Review": "bg-orange-100 text-orange-600",
|
|
||||||
Diterima: "bg-green-100 text-green-600",
|
|
||||||
default: "bg-red-200 text-red-600",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusStyles = colors[label] || colors.default;
|
const colors = [
|
||||||
|
"bg-orange-100 text-orange-600",
|
||||||
|
"bg-orange-100 text-orange-600",
|
||||||
|
"bg-green-100 text-green-600",
|
||||||
|
"bg-blue-100 text-blue-600",
|
||||||
|
"bg-red-200 text-red-600",
|
||||||
|
];
|
||||||
|
|
||||||
|
const status =
|
||||||
|
Number(row.original?.statusId) == 2 &&
|
||||||
|
row.original?.reviewedAtLevel !== null &&
|
||||||
|
!row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) &&
|
||||||
|
Number(row.original?.creatorGroupLevelId) != Number(userLevelId)
|
||||||
|
? "1"
|
||||||
|
: row.original?.statusId;
|
||||||
|
const statusStyles =
|
||||||
|
colors[Number(status)] || "bg-red-200 text-red-600";
|
||||||
|
// const statusStyles = statusColors[status] || "bg-red-200 text-red-600";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
|
|
@ -170,7 +163,18 @@ const useTableColumns = () => {
|
||||||
statusStyles
|
statusStyles
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{label}
|
{(Number(row.original?.statusId) == 2 &&
|
||||||
|
!row.original?.reviewedAtLevel !== null &&
|
||||||
|
!row.original?.reviewedAtLevel?.includes(
|
||||||
|
`:${Number(userLevelId)}:`
|
||||||
|
) &&
|
||||||
|
Number(row.original?.creatorGroupLevelId) !=
|
||||||
|
Number(userLevelId)) ||
|
||||||
|
(Number(row.original?.statusId) == 1 &&
|
||||||
|
Number(row.original?.needApprovalFromLevel) ==
|
||||||
|
Number(userLevelId))
|
||||||
|
? "Menunggu Review"
|
||||||
|
: row.original?.statusName}{" "}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -186,7 +190,11 @@ const useTableColumns = () => {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
const data = { id };
|
// loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await deleteMedia(data);
|
const response = await deleteMedia(data);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
|
|
@ -228,21 +236,16 @@ const useTableColumns = () => {
|
||||||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (userLevelId !== undefined && roleId !== undefined) {
|
if (userLevelId !== undefined && roleId !== undefined) {
|
||||||
setIsMabesApprover(
|
setIsMabesApprover(
|
||||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
const canEdit =
|
|
||||||
Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
isMabesApprover ||
|
|
||||||
roleId === 14;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -263,8 +266,16 @@ const useTableColumns = () => {
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
{/* <Link
|
||||||
{canEdit && (
|
href={`/contributor/content/image/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link> */}
|
||||||
|
{(Number(row.original.uploadedById) === Number(userId) ||
|
||||||
|
isMabesApprover) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/content/image/update/${row.original.id}`}
|
href={`/contributor/content/image/update/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -274,7 +285,6 @@ const useTableColumns = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteMedia(row.original.id)}
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
|
@ -282,138 +292,20 @@ const useTableColumns = () => {
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Hapus
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)} */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// accessorKey: "action",
|
|
||||||
// header: t("action", { defaultValue: "Action" }),
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const router = useRouter();
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// async function doDelete(id: any) {
|
|
||||||
// // loading();
|
|
||||||
// const data = {
|
|
||||||
// id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const response = await deleteMedia(data);
|
|
||||||
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// success();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function success() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// window.location.reload();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const handleDeleteMedia = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Hapus Data",
|
|
||||||
// text: "",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#3085d6",
|
|
||||||
// confirmButtonColor: "#d33",
|
|
||||||
// confirmButtonText: "Hapus",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// doDelete(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
|
||||||
// const userId = getCookiesDecrypt("uie");
|
|
||||||
// const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
// const roleId = getCookiesDecrypt("urie");
|
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// if (userLevelId !== undefined && roleId !== undefined) {
|
|
||||||
// setIsMabesApprover(
|
|
||||||
// Number(userLevelId) == 216 && Number(roleId) == 3
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }, [userLevelId, roleId]);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/image/detail/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <Eye className="w-4 h-4 me-1.5" />
|
|
||||||
// View
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// {/* <Link
|
|
||||||
// href={`/contributor/content/image/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link> */}
|
|
||||||
// {(Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
// isMabesApprover) && (
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/image/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// )}
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// {/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Hapus
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// )} */}
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return columns;
|
return columns;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,8 +8,8 @@ const ImageCreatePage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4 ">
|
<div className="space-y-4">
|
||||||
<FormImage />
|
<FormImage />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import {
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -20,35 +19,11 @@ import { Link } from "@/components/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
|
||||||
id: "select",
|
|
||||||
header: ({ table }) => (
|
|
||||||
<Checkbox
|
|
||||||
checked={
|
|
||||||
table.getIsAllPageRowsSelected() ||
|
|
||||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
|
||||||
}
|
|
||||||
onCheckedChange={(val) => table.toggleAllPageRowsSelected(!!val)}
|
|
||||||
aria-label="Pilih semua pada halaman ini"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<Checkbox
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
onCheckedChange={(val) => row.toggleSelected(!!val)}
|
|
||||||
aria-label="Pilih baris"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
header: t("no", { defaultValue: "No" }),
|
header: t("no", { defaultValue: "No" }),
|
||||||
|
|
@ -81,6 +56,7 @@ const useTableColumns = () => {
|
||||||
<span className="whitespace-nowrap">{row.getValue("contentTag")}</span>
|
<span className="whitespace-nowrap">{row.getValue("contentTag")}</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "contentType",
|
accessorKey: "contentType",
|
||||||
header: t("type-content", { defaultValue: "Type Content" }),
|
header: t("type-content", { defaultValue: "Type Content" }),
|
||||||
|
|
@ -97,6 +73,7 @@ const useTableColumns = () => {
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "isPublish",
|
accessorKey: "isPublish",
|
||||||
header: "Status",
|
header: "Status",
|
||||||
|
|
@ -118,6 +95,7 @@ const useTableColumns = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "contentCreatedDate",
|
accessorKey: "contentCreatedDate",
|
||||||
header: t("upload-date", { defaultValue: "Upload Date" }),
|
header: t("upload-date", { defaultValue: "Upload Date" }),
|
||||||
|
|
@ -126,6 +104,7 @@ const useTableColumns = () => {
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
const formattedDate =
|
const formattedDate =
|
||||||
createdAt && !isNaN(new Date(createdAt).getTime())
|
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||||
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||||
|
|
@ -139,24 +118,17 @@ const useTableColumns = () => {
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const isDisabled = row.original.isPublish;
|
const isDisabled = row.original.isPublish; // Check the isPublish value
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent) => {
|
|
||||||
if (e.ctrlKey) {
|
|
||||||
console.log("Ctrl + Click detected");
|
|
||||||
}
|
|
||||||
// Paksa buka menu meskipun ctrl ditekan
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild disabled={isDisabled}>
|
<DropdownMenuTrigger asChild disabled={isDisabled}>
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={handleClick}
|
className={`bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent ${
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
isDisabled ? "cursor-not-allowed opacity-50" : ""
|
||||||
|
}`}
|
||||||
|
disabled={isDisabled} // Disable button if isPublish is true
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open menu</span>
|
<span className="sr-only">Open menu</span>
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
|
|
@ -167,10 +139,10 @@ const useTableColumns = () => {
|
||||||
href={`/contributor/content/spit/convert/${row.original.contentId}`}
|
href={`/contributor/content/spit/convert/${row.original.contentId}`}
|
||||||
>
|
>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className={`cursor-pointer p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none ${
|
className={`p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none ${
|
||||||
isDisabled ? "cursor-not-allowed opacity-50" : ""
|
isDisabled ? "cursor-not-allowed opacity-50" : ""
|
||||||
}`}
|
}`}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled} // Disable dropdown item if isPublish is true
|
||||||
>
|
>
|
||||||
<MoveDownRight className="w-4 h-4 me-1.5" />
|
<MoveDownRight className="w-4 h-4 me-1.5" />
|
||||||
Pindah Ke Mediahub
|
Pindah Ke Mediahub
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,7 @@ import {
|
||||||
import { title } from "process";
|
import { title } from "process";
|
||||||
import { getCookiesDecrypt } from "@/lib/utils";
|
import { getCookiesDecrypt } from "@/lib/utils";
|
||||||
import TablePagination from "@/components/table/table-pagination";
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
import {
|
import { listEnableCategory, listSPIT } from "@/service/content/content";
|
||||||
deleteBulkSPIT,
|
|
||||||
listEnableCategory,
|
|
||||||
listSPIT,
|
|
||||||
} from "@/service/content/content";
|
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
|
@ -55,12 +51,8 @@ import {
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { format, parseISO } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import useTableColumns from "./columns";
|
import useTableColumns from "./columns";
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import { close, error, loading } from "@/config/swal";
|
|
||||||
import toast from "react-hot-toast";
|
|
||||||
|
|
||||||
// export type CompanyData = {
|
// export type CompanyData = {
|
||||||
// no: number;
|
// no: number;
|
||||||
|
|
@ -128,30 +120,6 @@ const TableSPIT = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
async function onSubmitDelete(ids: string[]) {
|
|
||||||
if (ids.length === 0) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
loading();
|
|
||||||
const response = await deleteBulkSPIT(ids);
|
|
||||||
|
|
||||||
if (response?.error) {
|
|
||||||
error(response?.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("Konten berhasil dihapus");
|
|
||||||
table.resetRowSelection();
|
|
||||||
await fetchData();
|
|
||||||
} catch (err) {
|
|
||||||
toast.error("Terjadi kesalahan saat menghapus");
|
|
||||||
} finally {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const pageFromUrl = searchParams?.get("page");
|
const pageFromUrl = searchParams?.get("page");
|
||||||
if (pageFromUrl) {
|
if (pageFromUrl) {
|
||||||
|
|
@ -159,27 +127,22 @@ const TableSPIT = () => {
|
||||||
}
|
}
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
// React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// fetchData();
|
fetchData();
|
||||||
// }, [showData, page, search, statusFilter, dateFilter]);
|
}, [showData, page, search, statusFilter, dateFilter]);
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// fetchData();
|
|
||||||
// getCategories();
|
|
||||||
// }, [
|
|
||||||
// // categoryFilter,
|
|
||||||
// statusFilter,
|
|
||||||
// page,
|
|
||||||
// showData,
|
|
||||||
// search,
|
|
||||||
// // startDate,
|
|
||||||
// // endDate,
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
getCategories();
|
getCategories();
|
||||||
}, [statusFilter, page, showData, search, dateFilter]);
|
}, [
|
||||||
|
// categoryFilter,
|
||||||
|
statusFilter,
|
||||||
|
page,
|
||||||
|
showData,
|
||||||
|
search,
|
||||||
|
// startDate,
|
||||||
|
// endDate,
|
||||||
|
]);
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const category = await listEnableCategory("4");
|
const category = await listEnableCategory("4");
|
||||||
|
|
@ -187,13 +150,6 @@ const TableSPIT = () => {
|
||||||
setCategories(resCategory || []);
|
setCategories(resCategory || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panggil fetchData otomatis ketika dateFilter berubah
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (dateFilter) {
|
|
||||||
fetchData();
|
|
||||||
}
|
|
||||||
}, [dateFilter]);
|
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
let isPublish;
|
let isPublish;
|
||||||
if (statusFilter.length === 0) {
|
if (statusFilter.length === 0) {
|
||||||
|
|
@ -205,7 +161,7 @@ const TableSPIT = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedStartDate = dateFilter
|
const formattedStartDate = dateFilter
|
||||||
? format(new Date(dateFilter + "T00:00:00"), "yyyy-MM-dd")
|
? format(new Date(dateFilter), "yyyy-MM-dd")
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -223,6 +179,8 @@ const TableSPIT = () => {
|
||||||
item.no = (page - 1) * Number(showData) + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
setSpitTable(contentData);
|
setSpitTable(contentData);
|
||||||
setTotalData(data?.totalElements);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(data?.totalPages);
|
setTotalPage(data?.totalPages);
|
||||||
|
|
@ -247,15 +205,15 @@ const TableSPIT = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex justify-between items-center px-5">
|
<div className="flex justify-between items-center px-5">
|
||||||
<div className="w-[150px] md:w-[250px] lg:w-[300px]">
|
<div className="w-[150px] md:w-[250px] lg:w-[250px]">
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
<Search className=" h-6 w-6 dark:text-white" />
|
<Search className=" h-4 w-4 dark:text-white" />
|
||||||
</InputGroupText>
|
</InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Judul..."
|
placeholder="Search Judul..."
|
||||||
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white text-[16px]"
|
className="bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
/>
|
/>
|
||||||
|
|
@ -312,7 +270,7 @@ const TableSPIT = () => {
|
||||||
<div className="flex flex-row justify-between my-1 mx-1">
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
<p>Filter</p>
|
<p>Filter</p>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="mx-2 my-1">
|
<div className="mx-2 my-1">
|
||||||
<Label>{t("date", { defaultValue: "Date" })}</Label>
|
<Label>{t("date", { defaultValue: "Date" })}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
|
|
@ -320,21 +278,7 @@ const TableSPIT = () => {
|
||||||
onChange={(e) => setDateFilter(e.target.value)}
|
onChange={(e) => setDateFilter(e.target.value)}
|
||||||
className="max-w-sm"
|
className="max-w-sm"
|
||||||
/>
|
/>
|
||||||
</div> */}
|
|
||||||
<div className="mx-2 my-1">
|
|
||||||
<Label>{t("date", { defaultValue: "Tanggal" })}</Label>
|
|
||||||
<Input
|
|
||||||
type="date"
|
|
||||||
value={dateFilter}
|
|
||||||
onChange={(e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setDateFilter(value);
|
|
||||||
fetchData(); // langsung panggil data baru
|
|
||||||
}}
|
|
||||||
className="max-w-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Label className="ml-2 mt-2">Status</Label>
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
<div className="flex items-center px-4 py-1">
|
<div className="flex items-center px-4 py-1">
|
||||||
<input
|
<input
|
||||||
|
|
@ -394,37 +338,6 @@ const TableSPIT = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{table.getSelectedRowModel().rows.length > 0 && (
|
|
||||||
<div className="mt-4 px-5">
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
onClick={async () => {
|
|
||||||
const ids = table
|
|
||||||
.getSelectedRowModel()
|
|
||||||
.rows.map((row) => row.original.contentId);
|
|
||||||
|
|
||||||
const confirm = await MySwal.fire({
|
|
||||||
title: "Yakin ingin menghapus?",
|
|
||||||
text: `Anda akan menghapus ${ids.length} item.`,
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: "Ya, hapus",
|
|
||||||
cancelButtonText: "Batal",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (confirm.isConfirmed) {
|
|
||||||
onSubmitDelete(ids);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="bg-red-600 text-white"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 mr-2" />
|
|
||||||
Hapus yang dipilih ({table.getSelectedRowModel().rows.length})
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import { deleteMedia } from "@/service/content/content";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "@/i18n/routing";
|
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table");
|
||||||
|
|
@ -177,18 +176,20 @@ const useTableColumns = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
header: t("action", { defaultValue: "Action" }),
|
header: t("action", { defaultValue: "Action" }),
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const router = useRouter();
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
const data = { id };
|
// loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await deleteMedia(data);
|
const response = await deleteMedia(data);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
|
|
@ -230,21 +231,16 @@ const useTableColumns = () => {
|
||||||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (userLevelId !== undefined && roleId !== undefined) {
|
if (userLevelId !== undefined && roleId !== undefined) {
|
||||||
setIsMabesApprover(
|
setIsMabesApprover(
|
||||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
const canEdit =
|
|
||||||
Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
isMabesApprover ||
|
|
||||||
roleId === 14;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -265,8 +261,8 @@ const useTableColumns = () => {
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
{(Number(row.original.uploadedById) === Number(userId) ||
|
||||||
{canEdit && (
|
isMabesApprover) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/content/teks/update/${row.original.id}`}
|
href={`/contributor/content/teks/update/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -276,7 +272,14 @@ const useTableColumns = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
{/* <Link
|
||||||
|
href={`/contributor/content/teks/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link> */}
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteMedia(row.original.id)}
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
|
@ -284,136 +287,20 @@ const useTableColumns = () => {
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Hapus
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)} */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// accessorKey: "action",
|
|
||||||
// header: t("action", { defaultValue: "Action" }),
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// async function doDelete(id: any) {
|
|
||||||
// // loading();
|
|
||||||
// const data = {
|
|
||||||
// id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const response = await deleteMedia(data);
|
|
||||||
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// success();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function success() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// window.location.reload();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const handleDeleteMedia = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Hapus Data",
|
|
||||||
// text: "",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#3085d6",
|
|
||||||
// confirmButtonColor: "#d33",
|
|
||||||
// confirmButtonText: "Hapus",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// doDelete(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
|
||||||
// const userId = getCookiesDecrypt("uie");
|
|
||||||
// const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
// const roleId = getCookiesDecrypt("urie");
|
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// if (userLevelId !== undefined && roleId !== undefined) {
|
|
||||||
// setIsMabesApprover(
|
|
||||||
// Number(userLevelId) == 216 && Number(roleId) == 3
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }, [userLevelId, roleId]);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/teks/detail/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <Eye className="w-4 h-4 me-1.5" />
|
|
||||||
// View
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// {(Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
// isMabesApprover) && (
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/teks/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// )}
|
|
||||||
// {/* <Link
|
|
||||||
// href={`/contributor/content/teks/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link> */}
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// {/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Hapus
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// )} */}
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
return columns;
|
return columns;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,7 +18,6 @@ import Swal from "sweetalert2";
|
||||||
import withReactContent from "sweetalert2-react-content";
|
import withReactContent from "sweetalert2-react-content";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "@/i18n/routing";
|
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table");
|
||||||
|
|
@ -177,18 +176,20 @@ const useTableColumns = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
header: t("action", { defaultValue: "Action" }),
|
header: t("action", { defaultValue: "Action" }),
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const router = useRouter();
|
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
const data = { id };
|
// loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await deleteMedia(data);
|
const response = await deleteMedia(data);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
|
|
@ -230,21 +231,16 @@ const useTableColumns = () => {
|
||||||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||||
const userId = getCookiesDecrypt("uie");
|
const userId = getCookiesDecrypt("uie");
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (userLevelId !== undefined && roleId !== undefined) {
|
if (userLevelId !== undefined && roleId !== undefined) {
|
||||||
setIsMabesApprover(
|
setIsMabesApprover(
|
||||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [userLevelId, roleId]);
|
}, [userLevelId, roleId]);
|
||||||
|
|
||||||
const canEdit =
|
|
||||||
Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
isMabesApprover ||
|
|
||||||
roleId === 14;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -265,8 +261,16 @@ const useTableColumns = () => {
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
{/* <Link
|
||||||
{canEdit && (
|
href={`/contributor/content/video/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link> */}
|
||||||
|
{(Number(row.original.uploadedById) === Number(userId) ||
|
||||||
|
isMabesApprover) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/content/video/update/${row.original.id}`}
|
href={`/contributor/content/video/update/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -276,7 +280,6 @@ const useTableColumns = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteMedia(row.original.id)}
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
|
@ -284,136 +287,20 @@ const useTableColumns = () => {
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDeleteMedia(row.original.id)}
|
||||||
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Hapus
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)} */}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// accessorKey: "action",
|
|
||||||
// header: t("action", { defaultValue: "Action" }),
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// async function doDelete(id: any) {
|
|
||||||
// // loading();
|
|
||||||
// const data = {
|
|
||||||
// id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const response = await deleteMedia(data);
|
|
||||||
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// success();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function success() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// window.location.reload();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const handleDeleteMedia = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Hapus Data",
|
|
||||||
// text: "",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#3085d6",
|
|
||||||
// confirmButtonColor: "#d33",
|
|
||||||
// confirmButtonText: "Hapus",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// doDelete(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
|
||||||
// const userId = getCookiesDecrypt("uie");
|
|
||||||
// const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
// const roleId = getCookiesDecrypt("urie");
|
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// if (userLevelId !== undefined && roleId !== undefined) {
|
|
||||||
// setIsMabesApprover(
|
|
||||||
// Number(userLevelId) == 216 && Number(roleId) == 3
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }, [userLevelId, roleId]);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/video/detail/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <Eye className="w-4 h-4 me-1.5" />
|
|
||||||
// View
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// {/* <Link
|
|
||||||
// href={`/contributor/content/video/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link> */}
|
|
||||||
// {(Number(row.original.uploadedById) === Number(userId) ||
|
|
||||||
// isMabesApprover) && (
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/content/video/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// )}
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// {/* {(row.original.uploadedById === userId || isMabesApprover) && (
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteMedia(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Hapus
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// )} */}
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return columns;
|
return columns;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,7 +26,6 @@ import { deleteBlog } from "@/service/blog/blog";
|
||||||
import { error, loading, close } from "@/lib/swal";
|
import { error, loading, close } from "@/lib/swal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { downloadReport } from "@/service/report/report";
|
|
||||||
|
|
||||||
const useTableColumns = ({
|
const useTableColumns = ({
|
||||||
handlePreview,
|
handlePreview,
|
||||||
|
|
@ -128,10 +127,14 @@ const useTableColumns = ({
|
||||||
const handleDownload = async (id: string) => {
|
const handleDownload = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
loading();
|
loading();
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://netidhub.com/api/media/report/download?id=${id}`,
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const response = await downloadReport(id);
|
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
console.log(response?.data);
|
|
||||||
const url = window.URL.createObjectURL(new Blob([response?.data], { type : "application/pdf"}));
|
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute("download", `report-${id}.pdf`);
|
link.setAttribute("download", `report-${id}.pdf`);
|
||||||
|
|
@ -149,34 +152,6 @@ const useTableColumns = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleDownload = async (id: string) => {
|
|
||||||
// try {
|
|
||||||
// loading();
|
|
||||||
// const response = await axios.get(
|
|
||||||
// `https://new.netidhub.com/api/media/report/download?id=${id}`,
|
|
||||||
// {
|
|
||||||
// responseType: "blob",
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const url = window.URL.createObjectURL(new Blob([response.data]));
|
|
||||||
// const link = document.createElement("a");
|
|
||||||
// link.href = url;
|
|
||||||
// link.setAttribute("download", `report-${id}.pdf`);
|
|
||||||
// document.body.appendChild(link);
|
|
||||||
// link.click();
|
|
||||||
// link.remove();
|
|
||||||
// close();
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Download failed", error);
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Gagal",
|
|
||||||
// text: "Terjadi kesalahan saat mengunduh file.",
|
|
||||||
// icon: "error",
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ const ReportTable = () => {
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const [showData, setShowData] = React.useState("10");
|
const [showData, setShowData] = React.useState("50");
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
|
|
@ -106,68 +106,6 @@ const ReportTable = () => {
|
||||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||||
const [openPreview, setOpenPreview] = React.useState(false);
|
const [openPreview, setOpenPreview] = React.useState(false);
|
||||||
const [previewData, setPreviewData] = React.useState<any>(null);
|
const [previewData, setPreviewData] = React.useState<any>(null);
|
||||||
const [openDateDialog, setOpenDateDialog] = React.useState(false);
|
|
||||||
const [reportDate, setReportDate] = React.useState("");
|
|
||||||
const [startDate, setStartDate] = React.useState("");
|
|
||||||
const [endDate, setEndDate] = React.useState("");
|
|
||||||
|
|
||||||
// handleGenerateReport ubah sedikit
|
|
||||||
const handleGenerateReport = async () => {
|
|
||||||
if (!startDate || !endDate) {
|
|
||||||
MySwal.fire(
|
|
||||||
"Warning",
|
|
||||||
"Silakan pilih tanggal awal dan tanggal akhir terlebih dahulu",
|
|
||||||
"warning"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new Date(startDate) > new Date(endDate)) {
|
|
||||||
MySwal.fire(
|
|
||||||
"Warning",
|
|
||||||
"Tanggal awal tidak boleh lebih besar dari tanggal akhir",
|
|
||||||
"warning"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = `Report ${format(
|
|
||||||
new Date(startDate),
|
|
||||||
"dd-MM-yyyy"
|
|
||||||
)} - ${format(new Date(endDate), "dd-MM-yyyy")}`;
|
|
||||||
const requestData = {
|
|
||||||
title,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await saveReport(requestData);
|
|
||||||
if (response?.error) {
|
|
||||||
MySwal.fire(
|
|
||||||
"Error",
|
|
||||||
response?.message || "Gagal menyimpan laporan",
|
|
||||||
"error"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Sukses",
|
|
||||||
text: "Laporan berhasil dibuat.",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "OK",
|
|
||||||
}).then(() => {
|
|
||||||
fetchData();
|
|
||||||
setStartDate("");
|
|
||||||
setEndDate("");
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Generate report error:", error);
|
|
||||||
MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePreview = (id: string) => {
|
const handlePreview = (id: string) => {
|
||||||
const url = `https://new.netidhub.com/api/media/report/view?id=${id}`;
|
const url = `https://new.netidhub.com/api/media/report/view?id=${id}`;
|
||||||
|
|
@ -246,6 +184,8 @@ const ReportTable = () => {
|
||||||
? prev.filter((id: any) => id !== categoryId)
|
? prev.filter((id: any) => id !== categoryId)
|
||||||
: [...prev, categoryId]
|
: [...prev, categoryId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Perbarui filter kategori
|
||||||
setCategoryFilter((prev) => {
|
setCategoryFilter((prev) => {
|
||||||
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
||||||
|
|
||||||
|
|
@ -266,51 +206,50 @@ const ReportTable = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value); // Perbarui state search
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value);
|
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handleGenerateReport = async () => {
|
const handleGenerateReport = async () => {
|
||||||
// const today = new Date();
|
const today = new Date();
|
||||||
// const formattedDate = format(today, "dd-MM-yyyy");
|
const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025
|
||||||
// const title = `Report ${formattedDate}`;
|
const title = `Report ${formattedDate}`;
|
||||||
|
|
||||||
// const requestData = {
|
const requestData = {
|
||||||
// title,
|
title,
|
||||||
// date: reportDate,
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
// const response = await saveReport(requestData);
|
const response = await saveReport(requestData);
|
||||||
|
|
||||||
// if (response?.error) {
|
if (response?.error) {
|
||||||
// MySwal.fire(
|
MySwal.fire(
|
||||||
// "Error",
|
"Error",
|
||||||
// response?.message || "Gagal menyimpan laporan",
|
response?.message || "Gagal menyimpan laporan",
|
||||||
// "error"
|
"error"
|
||||||
// );
|
);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// MySwal.fire({
|
MySwal.fire({
|
||||||
// title: "Sukses",
|
title: "Sukses",
|
||||||
// text: "Laporan berhasil dibuat.",
|
text: "Laporan berhasil dibuat.",
|
||||||
// icon: "success",
|
icon: "success",
|
||||||
// confirmButtonColor: "#3085d6",
|
confirmButtonColor: "#3085d6",
|
||||||
// confirmButtonText: "OK",
|
confirmButtonText: "OK",
|
||||||
// }).then(() => {
|
}).then(() => {
|
||||||
// fetchData();
|
fetchData(); // Refresh tabel setelah generate
|
||||||
// });
|
});
|
||||||
// } catch (error) {
|
} catch (error) {
|
||||||
// console.error("Generate report error:", error);
|
console.error("Generate report error:", error);
|
||||||
// MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog open={openPreview} onOpenChange={setOpenPreview}>
|
<Dialog open={openPreview} onOpenChange={setOpenPreview}>
|
||||||
<DialogContent className="min-w-max h-[700px] p-0 overflow-hidden">
|
<DialogContent className="min-w-max h-[500px] p-0 overflow-hidden">
|
||||||
<DialogHeader className="p-4 border-b">
|
<DialogHeader className="p-4 border-b">
|
||||||
<DialogTitle>Preview Laporan</DialogTitle>
|
<DialogTitle>Preview Laporan</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
@ -330,19 +269,10 @@ const ReportTable = () => {
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
{t("table", { defaultValue: "Table" })}{" "}
|
{t("table", { defaultValue: "Table" })} {t("report", { defaultValue: "Report" })}
|
||||||
{t("report", { defaultValue: "Report" })}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
{/* <Button fullWidth color="primary" onClick={handleGenerateReport}>
|
<Button fullWidth color="primary" onClick={handleGenerateReport}>
|
||||||
<Plus size={18} className=" me-1.5" />
|
|
||||||
{t("generate-report", { defaultValue: "Generate Report" })}
|
|
||||||
</Button> */}
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
color="primary"
|
|
||||||
onClick={() => setOpenDateDialog(true)}
|
|
||||||
>
|
|
||||||
<Plus size={18} className=" me-1.5" />
|
<Plus size={18} className=" me-1.5" />
|
||||||
{t("generate-report", { defaultValue: "Generate Report" })}
|
{t("generate-report", { defaultValue: "Generate Report" })}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -408,13 +338,13 @@ const ReportTable = () => {
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="end"
|
align="end"
|
||||||
className="w-64 h-fit overflow-y-auto"
|
className="w-64 h-[200px] overflow-y-auto"
|
||||||
>
|
>
|
||||||
<div className="flex flex-row justify-between my-1 mx-1">
|
<div className="flex flex-row justify-between my-1 mx-1">
|
||||||
<p>Filter</p>
|
<p>Filter</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-2 my-1">
|
<div className="mx-2 my-1">
|
||||||
<Label>Tanggal Generate</Label>
|
<Label>{t("date", { defaultValue: "Date" })}</Label>
|
||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
value={dateFilter}
|
value={dateFilter}
|
||||||
|
|
@ -431,8 +361,8 @@ const ReportTable = () => {
|
||||||
className="max-w-sm"
|
className="max-w-sm"
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div> */}
|
||||||
{/* <Label className="ml-2 mt-2">Status</Label> */}
|
<Label className="ml-2 mt-2">Status</Label>
|
||||||
{/* <div className="flex items-center px-4 py-1">
|
<div className="flex items-center px-4 py-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="status-1"
|
id="status-1"
|
||||||
|
|
@ -443,8 +373,8 @@ const ReportTable = () => {
|
||||||
<label htmlFor="status-1" className="text-sm">
|
<label htmlFor="status-1" className="text-sm">
|
||||||
{t("wait-review", { defaultValue: "Wait Review" })}
|
{t("wait-review", { defaultValue: "Wait Review" })}
|
||||||
</label>
|
</label>
|
||||||
</div> */}
|
</div>
|
||||||
{/* <div className="flex items-center px-4 py-1">
|
<div className="flex items-center px-4 py-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="status-2"
|
id="status-2"
|
||||||
|
|
@ -455,7 +385,7 @@ const ReportTable = () => {
|
||||||
<label htmlFor="status-2" className="text-sm">
|
<label htmlFor="status-2" className="text-sm">
|
||||||
{t("acc", { defaultValue: "Acc" })}
|
{t("acc", { defaultValue: "Acc" })}
|
||||||
</label>
|
</label>
|
||||||
</div> */}
|
</div>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -542,49 +472,6 @@ const ReportTable = () => {
|
||||||
totalPage={totalPage}
|
totalPage={totalPage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Dialog open={openDateDialog} onOpenChange={setOpenDateDialog}>
|
|
||||||
<DialogContent className="sm:max-w-md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Pilih Rentang Tanggal Laporan</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="startDate">Tanggal Awal</Label>
|
|
||||||
<Input
|
|
||||||
id="startDate"
|
|
||||||
type="date"
|
|
||||||
value={startDate}
|
|
||||||
onChange={(e) => setStartDate(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="endDate">Tanggal Akhir</Label>
|
|
||||||
<Input
|
|
||||||
id="endDate"
|
|
||||||
type="date"
|
|
||||||
value={endDate}
|
|
||||||
onChange={(e) => setEndDate(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-end gap-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setOpenDateDialog(false)}
|
|
||||||
>
|
|
||||||
Batal
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setOpenDateDialog(false);
|
|
||||||
handleGenerateReport();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Generate
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -17,10 +17,9 @@ import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { error } from "@/lib/swal";
|
import { error } from "@/lib/swal";
|
||||||
import { deleteCalendar } from "@/service/schedule/schedule";
|
import { deleteCalendar } from "@/service/schedule/schedule";
|
||||||
import { loading, success } from "@/config/swal";
|
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -101,11 +100,15 @@ const useTableColumns = () => {
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
|
||||||
const userId = Number(getCookiesDecrypt("uie"));
|
|
||||||
|
|
||||||
async function doDelete(id: any) {
|
async function doDelete(id: any) {
|
||||||
|
// loading();
|
||||||
|
const data = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await deleteCalendar(id);
|
const response = await deleteCalendar(id);
|
||||||
|
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
error(response.message);
|
error(response.message);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -153,134 +156,34 @@ const useTableColumns = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link
|
||||||
<Link
|
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
||||||
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
>
|
||||||
>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
Detail
|
||||||
Detail
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
{row.original.createdById === userId && (
|
|
||||||
<Link
|
|
||||||
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
|
||||||
>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{row.original.createdById === userId && (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => handleDeleteCalendars(row.original.id)}
|
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleDeleteCalendars(row.original.id)}
|
||||||
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
|
>
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// accessorKey: "action",
|
|
||||||
// header: t("action", { defaultValue: "Action" }),
|
|
||||||
// enableHiding: false,
|
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// async function doDelete(id: any) {
|
|
||||||
// // loading();
|
|
||||||
// const data = {
|
|
||||||
// id,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const response = await deleteCalendar(id);
|
|
||||||
|
|
||||||
// if (response?.error) {
|
|
||||||
// error(response.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// success();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function success() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// window.location.reload();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const handleDeleteCalendars = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Hapus Data",
|
|
||||||
// text: "",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#3085d6",
|
|
||||||
// confirmButtonColor: "#d33",
|
|
||||||
// confirmButtonText: "Hapus",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// doDelete(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <Eye className="w-4 h-4 me-1.5" />
|
|
||||||
// Detail
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <Link
|
|
||||||
// href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
|
||||||
// >
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => handleDeleteCalendars(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return columns;
|
return columns;
|
||||||
|
|
|
||||||
|
|
@ -226,12 +226,12 @@ const LiveReportTable = () => {
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<div className="border dark:bg-transparent border-black dark:border dark:border-white rounded-md w-full md:w-fit">
|
<div className="border border-black rounded-md w-full md:w-fit">
|
||||||
<Select
|
<Select
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
onValueChange={(value) => setSelectedType(value)}
|
onValueChange={(value) => setSelectedType(value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full md:w-[150px] text-black dark:text-white">
|
<SelectTrigger className="w-full md:w-[150px] text-black">
|
||||||
<SelectValue placeholder="Tipe" />
|
<SelectValue placeholder="Tipe" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,8 @@ import withReactContent from "sweetalert2-react-content";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const useTableColumns = (
|
const useTableColumns = () => {
|
||||||
activeTab: "ta" | "daily" | "special" | "mabes-koor",
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
) => {
|
|
||||||
const t = useTranslations("Table");
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
|
|
@ -52,12 +50,6 @@ const useTableColumns = (
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
accessorKey: "uniqueCode",
|
|
||||||
header: t("code", { defaultValue: "Code" }),
|
|
||||||
cell: ({ row }) => <span>{row.getValue("uniqueCode")}</span>,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
accessorKey: "createdAt",
|
accessorKey: "createdAt",
|
||||||
header: t("upload-date", { defaultValue: "Upload Date" }),
|
header: t("upload-date", { defaultValue: "Upload Date" }),
|
||||||
|
|
@ -115,31 +107,6 @@ const useTableColumns = (
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||||
|
|
||||||
// ❗ jika tab = "special"
|
|
||||||
if (activeTab === "special") {
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
>
|
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
|
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
|
||||||
<Link href={`/contributor/task/detail/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
|
||||||
View
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteProcess(id: any) {
|
async function deleteProcess(id: any) {
|
||||||
loading();
|
loading();
|
||||||
const resDelete = await deleteTaskTa(id);
|
const resDelete = await deleteTaskTa(id);
|
||||||
|
|
@ -192,11 +159,7 @@ const useTableColumns = (
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="p-0" align="end">
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
{/* {(roleId == 11 || roleId == 12 || roleId == 19) && ( */}
|
{(roleId == 11 || roleId == 12 || roleId == 19) && (
|
||||||
{(roleId == 11 ||
|
|
||||||
roleId == 12 ||
|
|
||||||
roleId == 19 ||
|
|
||||||
roleId == 3) && (
|
|
||||||
<Link href={`/contributor/task-ta/detail/${row.original.id}`}>
|
<Link href={`/contributor/task-ta/detail/${row.original.id}`}>
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
|
@ -204,16 +167,15 @@ const useTableColumns = (
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{roleId == 11 ||
|
{roleId == 11 && (
|
||||||
(roleId == 3 && (
|
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
|
||||||
<Link href={`/contributor/task-ta/update/${row.original.id}`}>
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
Edit
|
||||||
Edit
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
</Link>
|
||||||
</Link>
|
)}
|
||||||
))}
|
{(roleId == 11 || roleId == 12 || roleId == 19) && (
|
||||||
{(roleId == 12 || roleId == 19) && (
|
|
||||||
<Link
|
<Link
|
||||||
href={`/contributor/task-ta/upload-task/${row.original.id}`}
|
href={`/contributor/task-ta/upload-task/${row.original.id}`}
|
||||||
>
|
>
|
||||||
|
|
@ -223,16 +185,15 @@ const useTableColumns = (
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{roleId == 11 ||
|
{roleId == 11 && (
|
||||||
(roleId == 3 && (
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
onClick={() => TaskDelete(row.original.id)}
|
||||||
onClick={() => TaskDelete(row.original.id)}
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
>
|
||||||
>
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
Delete
|
||||||
Delete
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
)}
|
||||||
))}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -35,19 +35,10 @@ const TaskTaPage = () => {
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
{t("tabel", { defaultValue: "Tabel" })}{" "}
|
{t("tabel", { defaultValue: "Tabel" })} {t("task-ta", { defaultValue: "Task Ta" })}
|
||||||
{t("task-ta", { defaultValue: "Task Ta" })}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
{/* {roleId !== 12 && (
|
{roleId !== 12 && (
|
||||||
<Link href={"/contributor/task-ta/create"}>
|
|
||||||
<Button color="primary" className="text-white">
|
|
||||||
<UploadIcon size={18} className="mr-2" />
|
|
||||||
{t("create-task", { defaultValue: "Create Task" })}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)} */}
|
|
||||||
{roleId !== 12 && roleId !== 19 && (
|
|
||||||
<Link href={"/contributor/task-ta/create"}>
|
<Link href={"/contributor/task-ta/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon size={18} className="mr-2" />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -23,7 +23,7 @@ import Swal from "sweetalert2";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const useTableColumns = () => {
|
const useTableColumns = () => {
|
||||||
const t = useTranslations("Table");
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "no",
|
accessorKey: "no",
|
||||||
|
|
@ -124,97 +124,9 @@ const useTableColumns = () => {
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
header: t("action", { defaultValue: "Action" }),
|
header: t("action", { defaultValue: "Action" }),
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
// cell: ({ row }) => {
|
|
||||||
// const router = useRouter();
|
|
||||||
// const MySwal = withReactContent(Swal);
|
|
||||||
|
|
||||||
// async function deleteProcess(id: any) {
|
|
||||||
// loading();
|
|
||||||
// const resDelete = await deleteTask(id);
|
|
||||||
|
|
||||||
// if (resDelete?.error) {
|
|
||||||
// error(resDelete.message);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// success();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function success() {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Sukses",
|
|
||||||
// icon: "success",
|
|
||||||
// confirmButtonColor: "#3085d6",
|
|
||||||
// confirmButtonText: "OK",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// window.location.reload();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const TaskDelete = (id: any) => {
|
|
||||||
// MySwal.fire({
|
|
||||||
// title: "Hapus Data",
|
|
||||||
// text: "",
|
|
||||||
// icon: "warning",
|
|
||||||
// showCancelButton: true,
|
|
||||||
// cancelButtonColor: "#3085d6",
|
|
||||||
// confirmButtonColor: "#d33",
|
|
||||||
// confirmButtonText: "Hapus",
|
|
||||||
// }).then((result) => {
|
|
||||||
// if (result.isConfirmed) {
|
|
||||||
// deleteProcess(id);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button
|
|
||||||
// size="icon"
|
|
||||||
// className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
|
||||||
// >
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreVertical className="h-4 w-4 text-default-800" />
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent className="p-0" align="end">
|
|
||||||
// <Link href={`/contributor/task/detail/${row.original.id}`}>
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <Eye className="w-4 h-4 me-1.5" />
|
|
||||||
// View
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <Link href={`/contributor/task/update/${row.original.id}`}>
|
|
||||||
// <DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
// <SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
// Edit
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </Link>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={() => TaskDelete(row.original.id)}
|
|
||||||
// className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
// >
|
|
||||||
// <Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
// Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const MySwal = withReactContent(Swal);
|
const MySwal = withReactContent(Swal);
|
||||||
const roleId = getCookiesDecrypt("urie");
|
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
|
||||||
const contentLevelNumber =
|
|
||||||
row.original.createdBy?.userLevel?.levelNumber;
|
|
||||||
const isSameLevelOrLower =
|
|
||||||
levelNumber &&
|
|
||||||
contentLevelNumber &&
|
|
||||||
+levelNumber <= +contentLevelNumber;
|
|
||||||
|
|
||||||
async function deleteProcess(id: any) {
|
async function deleteProcess(id: any) {
|
||||||
loading();
|
loading();
|
||||||
|
|
@ -256,8 +168,6 @@ const useTableColumns = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDone = row.original.isDone;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -276,41 +186,19 @@ const useTableColumns = () => {
|
||||||
View
|
View
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link href={`/contributor/task/update/${row.original.id}`}>
|
||||||
{/* {!isDone && (
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
<>
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
<Link href={`/contributor/task/update/${row.original.id}`}>
|
Edit
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
</DropdownMenuItem>
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
</Link>
|
||||||
Edit
|
<DropdownMenuItem
|
||||||
</DropdownMenuItem>
|
onClick={() => TaskDelete(row.original.id)}
|
||||||
</Link>
|
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
||||||
<DropdownMenuItem
|
>
|
||||||
onClick={() => TaskDelete(row.original.id)}
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
Delete
|
||||||
>
|
</DropdownMenuItem>
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)} */}
|
|
||||||
{!isDone && isSameLevelOrLower && (
|
|
||||||
<>
|
|
||||||
<Link href={`/contributor/task/update/${row.original.id}`}>
|
|
||||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => TaskDelete(row.original.id)}
|
|
||||||
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ const TaskTable = () => {
|
||||||
className={` ${
|
className={` ${
|
||||||
isSpecificAttention
|
isSpecificAttention
|
||||||
? "bg-default-900 text-white dark:text-black"
|
? "bg-default-900 text-white dark:text-black"
|
||||||
: "dark:text-default-700 border-2 dark:border dark:border-gray-500"
|
: "dark:text-default-700 border-2"
|
||||||
}
|
}
|
||||||
px-[18px] py-1 transition duration-100 rounded`}
|
px-[18px] py-1 transition duration-100 rounded`}
|
||||||
>
|
>
|
||||||
|
|
@ -218,7 +218,7 @@ const TaskTable = () => {
|
||||||
${
|
${
|
||||||
!isSpecificAttention
|
!isSpecificAttention
|
||||||
? "bg-default-900 text-white dark:text-black"
|
? "bg-default-900 text-white dark:text-black"
|
||||||
: " dark:text-default-700 border-2 dark:border dark:border-gray-500"
|
: " dark:text-default-700 border-2"
|
||||||
}
|
}
|
||||||
px-[18px] py-1 transition duration-100 rounded
|
px-[18px] py-1 transition duration-100 rounded
|
||||||
`}
|
`}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@ import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { UploadIcon } from "lucide-react";
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
import {
|
import { checkAuthorization, checkLoginSession } from "@/lib/utils";
|
||||||
checkAuthorization,
|
|
||||||
checkLoginSession,
|
|
||||||
getCookiesDecrypt,
|
|
||||||
} from "@/lib/utils";
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
|
@ -17,13 +13,12 @@ const TaskPage = () => {
|
||||||
const t = useTranslations("AnalyticsDashboard");
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function initState() {
|
function initState() {
|
||||||
checkAuthorization("admin");
|
checkAuthorization("admin"); // Specify the page, e.g., "admin" or another value
|
||||||
checkLoginSession();
|
checkLoginSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
initState();
|
initState();
|
||||||
}, []);
|
}, []);
|
||||||
const levelNumber = getCookiesDecrypt("ulne");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -34,28 +29,16 @@ const TaskPage = () => {
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
<div className="flex flex-col sm:flex-row lg:flex-row lg:items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
{t("tabel", { defaultValue: "Tabel" })}{" "}
|
{t("tabel", { defaultValue: "Tabel" })} {t("task", { defaultValue: "Task" })}
|
||||||
{t("task", { defaultValue: "Task" })}
|
|
||||||
</div>
|
</div>
|
||||||
{Number(levelNumber) !== 3 && (
|
<div className="flex-none">
|
||||||
<div className="flex-none">
|
|
||||||
<Link href="/contributor/task/create">
|
|
||||||
<Button color="primary" className="text-white">
|
|
||||||
<UploadIcon size={18} className="mr-2" />
|
|
||||||
{t("create-task", { defaultValue: "Create Task" })}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* <div className="flex-none">
|
|
||||||
<Link href={"/contributor/task/create"}>
|
<Link href={"/contributor/task/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon size={18} className="mr-2" />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-task", { defaultValue: "Create Task" })}
|
{t("create-task", { defaultValue: "Create Task" })}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export default function UserFeedback() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="flex flex-col gap-2 bg-white dark:bg-black p-4">
|
<div className="flex flex-col gap-2 bg-white p-4">
|
||||||
<p className="text-lg">Hasil Feedback</p>
|
<p className="text-lg">Hasil Feedback</p>
|
||||||
<div className="grid grid-cols-2 gap-5">
|
<div className="grid grid-cols-2 gap-5">
|
||||||
{listData?.map(
|
{listData?.map(
|
||||||
|
|
@ -43,7 +43,7 @@ export default function UserFeedback() {
|
||||||
list?.avgScore !== "NaN" && (
|
list?.avgScore !== "NaN" && (
|
||||||
<div
|
<div
|
||||||
key={list?.id}
|
key={list?.id}
|
||||||
className="flex flex-col gap-2 bg-gray-100 dark:bg-blue-950 rounded-md p-5"
|
className="flex flex-col gap-2 bg-gray-100 rounded-md p-5"
|
||||||
>
|
>
|
||||||
<div className="flex flex-row gap-3 items-center">
|
<div className="flex flex-row gap-3 items-center">
|
||||||
<p className="text-3xl">{parseInt(list?.avgScore)}</p>
|
<p className="text-3xl">{parseInt(list?.avgScore)}</p>
|
||||||
|
|
|
||||||
|
|
@ -281,11 +281,11 @@ export default function DetailDaily() {
|
||||||
|
|
||||||
const output = form.watch("output");
|
const output = form.watch("output");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
|
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
|
@ -301,7 +301,7 @@ export default function DetailDaily() {
|
||||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked ? [...(output ?? []), id] : (output ?? []).filter((value) => value !== id)
|
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -318,11 +318,11 @@ export default function DetailDaily() {
|
||||||
|
|
||||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
form.setValue("unit", [...(unit ?? []), id]);
|
form.setValue("unit", [...unit, id]);
|
||||||
} else {
|
} else {
|
||||||
if (id == "2") {
|
if (id == "2") {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (const element of unit ?? []) {
|
for (const element of unit) {
|
||||||
if (element == "1") {
|
if (element == "1") {
|
||||||
temp.push("1");
|
temp.push("1");
|
||||||
}
|
}
|
||||||
|
|
@ -331,7 +331,7 @@ export default function DetailDaily() {
|
||||||
} else {
|
} else {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
(unit ?? []).filter((value) => value !== id)
|
unit.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -542,14 +542,14 @@ export default function DetailDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -564,7 +564,7 @@ export default function DetailDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -574,7 +574,7 @@ export default function DetailDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -592,7 +592,7 @@ export default function DetailDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -603,7 +603,7 @@ export default function DetailDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
|
||||||
|
|
@ -294,11 +294,11 @@ export default function EditDaily() {
|
||||||
|
|
||||||
const output = form.watch("output");
|
const output = form.watch("output");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
|
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
|
@ -314,7 +314,7 @@ export default function EditDaily() {
|
||||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked ? [...(output ?? []), id] : (output ?? []).filter((value) => value !== id)
|
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -331,11 +331,11 @@ export default function EditDaily() {
|
||||||
|
|
||||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
form.setValue("unit", [...(unit ?? []), id]);
|
form.setValue("unit", [...unit, id]);
|
||||||
} else {
|
} else {
|
||||||
if (id == "2") {
|
if (id == "2") {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (const element of unit ?? []) {
|
for (const element of unit) {
|
||||||
if (element == "1") {
|
if (element == "1") {
|
||||||
temp.push("1");
|
temp.push("1");
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +344,7 @@ export default function EditDaily() {
|
||||||
} else {
|
} else {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
(unit ?? []).filter((value) => value !== id)
|
unit.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -506,7 +506,7 @@ export default function EditDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={unit?.includes(item.id)}
|
checked={unit?.includes(item.id)}
|
||||||
disabled={
|
disabled={
|
||||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
item.id === "3" && !unit.includes("2")
|
||||||
}
|
}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleUnitCheckedChange(item.id, checked)
|
handleUnitCheckedChange(item.id, checked)
|
||||||
|
|
@ -550,14 +550,14 @@ export default function EditDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -572,7 +572,7 @@ export default function EditDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -582,7 +582,7 @@ export default function EditDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -600,7 +600,7 @@ export default function EditDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -611,7 +611,7 @@ export default function EditDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
|
||||||
|
|
@ -242,11 +242,11 @@ export default function CreateDaily() {
|
||||||
|
|
||||||
const output = form.watch("output");
|
const output = form.watch("output");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
|
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
|
@ -262,7 +262,7 @@ export default function CreateDaily() {
|
||||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked ? [...(output ?? []), id] : (output ?? []).filter((value) => value !== id)
|
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -279,11 +279,11 @@ export default function CreateDaily() {
|
||||||
|
|
||||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
form.setValue("unit", [...(unit ?? []), id]);
|
form.setValue("unit", [...unit, id]);
|
||||||
} else {
|
} else {
|
||||||
if (id == "2") {
|
if (id == "2") {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (const element of unit ?? []) {
|
for (const element of unit) {
|
||||||
if (element == "1") {
|
if (element == "1") {
|
||||||
temp.push("1");
|
temp.push("1");
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +292,7 @@ export default function CreateDaily() {
|
||||||
} else {
|
} else {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
(unit ?? []).filter((value) => value !== id)
|
unit.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -472,7 +472,7 @@ export default function CreateDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={unit?.includes(item.id)}
|
checked={unit?.includes(item.id)}
|
||||||
disabled={
|
disabled={
|
||||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
item.id === "3" && !unit.includes("2")
|
||||||
}
|
}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleUnitCheckedChange(item.id, checked)
|
handleUnitCheckedChange(item.id, checked)
|
||||||
|
|
@ -516,14 +516,14 @@ export default function CreateDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -538,7 +538,7 @@ export default function CreateDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +548,7 @@ export default function CreateDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -566,7 +566,7 @@ export default function CreateDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -577,7 +577,7 @@ export default function CreateDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
|
||||||
|
|
@ -281,11 +281,11 @@ export default function DetailDaily() {
|
||||||
|
|
||||||
const output = form.watch("output");
|
const output = form.watch("output");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
|
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
|
@ -301,7 +301,7 @@ export default function DetailDaily() {
|
||||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked ? [...(output ?? []), id] : (output ?? []).filter((value) => value !== id)
|
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -318,11 +318,11 @@ export default function DetailDaily() {
|
||||||
|
|
||||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
form.setValue("unit", [...(unit ?? []), id]);
|
form.setValue("unit", [...unit, id]);
|
||||||
} else {
|
} else {
|
||||||
if (id == "2") {
|
if (id == "2") {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (const element of unit ?? []) {
|
for (const element of unit) {
|
||||||
if (element == "1") {
|
if (element == "1") {
|
||||||
temp.push("1");
|
temp.push("1");
|
||||||
}
|
}
|
||||||
|
|
@ -331,7 +331,7 @@ export default function DetailDaily() {
|
||||||
} else {
|
} else {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
(unit ?? []).filter((value) => value !== id)
|
unit.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -542,14 +542,14 @@ export default function DetailDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -564,7 +564,7 @@ export default function DetailDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -574,7 +574,7 @@ export default function DetailDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -592,7 +592,7 @@ export default function DetailDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -603,7 +603,7 @@ export default function DetailDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
|
||||||
|
|
@ -294,11 +294,11 @@ export default function EditDaily() {
|
||||||
|
|
||||||
const output = form.watch("output");
|
const output = form.watch("output");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
|
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
|
@ -314,7 +314,7 @@ export default function EditDaily() {
|
||||||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked ? [...(output ?? []), id] : (output ?? []).filter((value) => value !== id)
|
checked ? [...output, id] : output.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -331,11 +331,11 @@ export default function EditDaily() {
|
||||||
|
|
||||||
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
const handleUnitCheckedChange = (id: string, checked: boolean | string) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
form.setValue("unit", [...(unit ?? []), id]);
|
form.setValue("unit", [...unit, id]);
|
||||||
} else {
|
} else {
|
||||||
if (id == "2") {
|
if (id == "2") {
|
||||||
const temp = [];
|
const temp = [];
|
||||||
for (const element of unit ?? []) {
|
for (const element of unit) {
|
||||||
if (element == "1") {
|
if (element == "1") {
|
||||||
temp.push("1");
|
temp.push("1");
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +344,7 @@ export default function EditDaily() {
|
||||||
} else {
|
} else {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
(unit ?? []).filter((value) => value !== id)
|
unit.filter((value) => value !== id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -506,7 +506,7 @@ export default function EditDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={unit?.includes(item.id)}
|
checked={unit?.includes(item.id)}
|
||||||
disabled={
|
disabled={
|
||||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
item.id === "3" && !unit.includes("2")
|
||||||
}
|
}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleUnitCheckedChange(item.id, checked)
|
handleUnitCheckedChange(item.id, checked)
|
||||||
|
|
@ -550,14 +550,14 @@ export default function EditDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -572,7 +572,7 @@ export default function EditDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -582,7 +582,7 @@ export default function EditDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -600,7 +600,7 @@ export default function EditDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -611,7 +611,7 @@ export default function EditDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
|
||||||
|
|
@ -264,9 +264,9 @@ export default function CreateDaily() {
|
||||||
const media = form.watch("media");
|
const media = form.watch("media");
|
||||||
const unit = form.watch("unit");
|
const unit = form.watch("unit");
|
||||||
|
|
||||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||||
const isAllMediaChecked = media && medias.every((item) => media.includes(item.id));
|
const isAllMediaChecked = medias.every((item) => media?.includes(item.id));
|
||||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initState() {
|
async function initState() {
|
||||||
|
|
@ -398,8 +398,10 @@ export default function CreateDaily() {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"output",
|
"output",
|
||||||
checked
|
checked
|
||||||
? [...(output ?? []), item.id]
|
? [...output, item.id]
|
||||||
: (output ?? []).filter((value) => value !== item.id)
|
: output.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -462,14 +464,16 @@ export default function CreateDaily() {
|
||||||
checked={unit?.includes(item.id)}
|
checked={unit?.includes(item.id)}
|
||||||
disabled={
|
disabled={
|
||||||
item.id === "polres" &&
|
item.id === "polres" &&
|
||||||
!(unit?.includes("polda") ?? false)
|
!unit.includes("polda")
|
||||||
}
|
}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"unit",
|
"unit",
|
||||||
checked
|
checked
|
||||||
? [...(unit ?? []), item.id]
|
? [...unit, item.id]
|
||||||
: (unit ?? []).filter((value) => value !== item.id)
|
: unit.filter(
|
||||||
|
(value) => value !== item.id
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -511,14 +515,14 @@ export default function CreateDaily() {
|
||||||
id={list.id}
|
id={list.id}
|
||||||
name={`all${list.id}`}
|
name={`all${list.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("2") ?? false
|
unit.includes("2")
|
||||||
? true
|
? true
|
||||||
: !!selected[list.id]
|
: !!selected[list.id]
|
||||||
}
|
}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleParentChange(list.id)
|
handleParentChange(list.id)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("2") ?? false}
|
disabled={unit.includes("2")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={list.name}
|
htmlFor={list.name}
|
||||||
|
|
@ -533,7 +537,7 @@ export default function CreateDaily() {
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selectAll[list.id]
|
: !!selectAll[list.id]
|
||||||
}
|
}
|
||||||
|
|
@ -543,7 +547,7 @@ export default function CreateDaily() {
|
||||||
Boolean(e)
|
Boolean(e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="all-polres"
|
htmlFor="all-polres"
|
||||||
|
|
@ -561,7 +565,7 @@ export default function CreateDaily() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${list.id}${subDes.id}`}
|
id={`${list.id}${subDes.id}`}
|
||||||
checked={
|
checked={
|
||||||
unit?.includes("3") ?? false
|
unit.includes("3")
|
||||||
? true
|
? true
|
||||||
: !!selected[
|
: !!selected[
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
|
|
@ -572,7 +576,7 @@ export default function CreateDaily() {
|
||||||
`${list.id}${subDes.id}`
|
`${list.id}${subDes.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={unit?.includes("3") ?? false}
|
disabled={unit.includes("3")}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`${list.id}${subDes.id}`}
|
htmlFor={`${list.id}${subDes.id}`}
|
||||||
|
|
@ -644,8 +648,8 @@ export default function CreateDaily() {
|
||||||
form.setValue(
|
form.setValue(
|
||||||
"media",
|
"media",
|
||||||
checked
|
checked
|
||||||
? [...(media ?? []), item.id]
|
? [...media, item.id]
|
||||||
: (media ?? []).filter(
|
: media.filter(
|
||||||
(value) => value !== item.id
|
(value) => value !== item.id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import ReportTable from "../contributor/report/components/report-table";
|
||||||
const DashboardPage = () => {
|
const DashboardPage = () => {
|
||||||
const t = useTranslations("AnalyticsDashboard");
|
const t = useTranslations("AnalyticsDashboard");
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
|
||||||
|
|
||||||
return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? (
|
return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -55,23 +54,18 @@ const DashboardPage = () => {
|
||||||
>
|
>
|
||||||
{t("schedule", { defaultValue: "Schedule" })}
|
{t("schedule", { defaultValue: "Schedule" })}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
{levelNumber !== 3 && (
|
<TabsTrigger
|
||||||
<>
|
value="indeks"
|
||||||
<TabsTrigger
|
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||||
value="indeks"
|
>
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
{t("indeks", { defaultValue: "Indeks" })}
|
||||||
>
|
</TabsTrigger>
|
||||||
{t("indeks", { defaultValue: "Indeks" })}
|
<TabsTrigger
|
||||||
</TabsTrigger>
|
value="report"
|
||||||
|
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||||
<TabsTrigger
|
>
|
||||||
value="report"
|
{t("report", { defaultValue: "Report" })}
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
</TabsTrigger>
|
||||||
>
|
|
||||||
{t("report", { defaultValue: "Report" })}
|
|
||||||
</TabsTrigger>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Card>
|
</Card>
|
||||||
<TabsContent value="routine-task">
|
<TabsContent value="routine-task">
|
||||||
|
|
@ -81,24 +75,18 @@ const DashboardPage = () => {
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
<StatisticsBlock
|
<StatisticsBlock
|
||||||
title={t("Hasil_unggah_disetujui_hari_ini", {
|
title={t("Hasil_unggah_disetujui_hari_ini", { defaultValue: "Hasil Unggah Disetujui Hari Ini" })}
|
||||||
defaultValue: "Hasil Unggah Disetujui Hari Ini",
|
|
||||||
})}
|
|
||||||
total="3,564"
|
total="3,564"
|
||||||
className="bg-info/10 border-none shadow-none"
|
className="bg-info/10 border-none shadow-none"
|
||||||
/>
|
/>
|
||||||
<StatisticsBlock
|
<StatisticsBlock
|
||||||
title={t("Hasil_unggah_direvisi_hari_ini", {
|
title={t("Hasil_unggah_direvisi_hari_ini", { defaultValue: "Hasil Unggah Direvisi Hari Ini" })}
|
||||||
defaultValue: "Hasil Unggah Direvisi Hari Ini",
|
|
||||||
})}
|
|
||||||
total="564"
|
total="564"
|
||||||
className="bg-warning/10 border-none shadow-none"
|
className="bg-warning/10 border-none shadow-none"
|
||||||
chartColor="#FB8F65"
|
chartColor="#FB8F65"
|
||||||
/>
|
/>
|
||||||
<StatisticsBlock
|
<StatisticsBlock
|
||||||
title={t("Hasil_unggah_ditolak_hari_ini", {
|
title={t("Hasil_unggah_ditolak_hari_ini", { defaultValue: "Hasil Unggah Ditolak Hari Ini" })}
|
||||||
defaultValue: "Hasil Unggah Ditolak Hari Ini",
|
|
||||||
})}
|
|
||||||
total="+5.0%"
|
total="+5.0%"
|
||||||
className="bg-primary/10 border-none shadow-none"
|
className="bg-primary/10 border-none shadow-none"
|
||||||
chartColor="#2563eb"
|
chartColor="#2563eb"
|
||||||
|
|
@ -113,9 +101,7 @@ const DashboardPage = () => {
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center">
|
<CardHeader className="flex flex-row items-center">
|
||||||
<CardTitle className="flex-1 text-lg">
|
<CardTitle className="flex-1 text-lg">
|
||||||
{t("Total-Content-Production", {
|
{t("Total-Content-Production", { defaultValue: "Total Content Production" })}
|
||||||
defaultValue: "Total Content Production",
|
|
||||||
})}
|
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<DashboardDropdown />
|
<DashboardDropdown />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
@ -127,9 +113,7 @@ const DashboardPage = () => {
|
||||||
<div className="lg:col-span-8 col-span-12">
|
<div className="lg:col-span-8 col-span-12">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center">
|
<CardHeader className="flex flex-row items-center">
|
||||||
<CardTitle className="flex-1">
|
<CardTitle className="flex-1">{t("tabel", { defaultValue: "Tabel" })}</CardTitle>
|
||||||
{t("tabel", { defaultValue: "Tabel" })}
|
|
||||||
</CardTitle>
|
|
||||||
{/* <DashboardDropdown /> */}
|
{/* <DashboardDropdown /> */}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
|
|
@ -148,26 +132,14 @@ const DashboardPage = () => {
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Tabel Penugasan
|
Tabel Penugasan
|
||||||
</div>
|
</div>
|
||||||
{Number(levelNumber) !== 3 && (
|
<div>
|
||||||
<div className="flex-none">
|
|
||||||
<Link href="/contributor/task/create">
|
|
||||||
<Button color="primary" className="text-white">
|
|
||||||
<UploadIcon size={18} className="mr-2" />
|
|
||||||
{t("create-task", {
|
|
||||||
defaultValue: "Create Task",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* <div>
|
|
||||||
<Link href={"/contributor/task/create"}>
|
<Link href={"/contributor/task/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon />
|
<UploadIcon />
|
||||||
Buat Penugasan
|
Buat Penugasan
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<CardContent className="p-0 mt-3">
|
<CardContent className="p-0 mt-3">
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Eye,
|
Eye,
|
||||||
|
|
@ -62,7 +61,6 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
|
|
||||||
type StatusFilter = string[];
|
type StatusFilter = string[];
|
||||||
|
|
||||||
|
|
@ -89,8 +87,7 @@ const ContentTable = () => {
|
||||||
const userLevelId = getCookiesDecrypt("ulie");
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
const [categories, setCategories] = React.useState<string[]>();
|
const [categories, setCategories] = React.useState<string[]>();
|
||||||
const [categoryFilter, setCategoryFilter] = React.useState<string[]>([]);
|
const [categoryFilter, setCategoryFilter] = React.useState<string[]>([]);
|
||||||
// const [statusFilter, setStatusFilter] = React.useState<StatusFilter>([]);
|
const [statusFilter, setStatusFilter] = React.useState<StatusFilter>([]);
|
||||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
|
||||||
const [startDateString, setStartDateString] = React.useState<string>("");
|
const [startDateString, setStartDateString] = React.useState<string>("");
|
||||||
const [endDateString, setEndDateString] = React.useState<string>("");
|
const [endDateString, setEndDateString] = React.useState<string>("");
|
||||||
const [filterByCreator, setFilterByCreator] = React.useState<string>("");
|
const [filterByCreator, setFilterByCreator] = React.useState<string>("");
|
||||||
|
|
@ -171,14 +168,6 @@ const ContentTable = () => {
|
||||||
table.getColumn("judul")?.setFilterValue(e.target.value);
|
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleStatusCheckboxChange(value: any) {
|
|
||||||
setStatusFilter((prev: any) =>
|
|
||||||
prev.includes(value)
|
|
||||||
? prev.filter((status: any) => status !== value)
|
|
||||||
: [...prev, value]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between md:flex-row md:items-center md:justify-between lg:flex-row items-start lg:justify-between lg:items-center px-5">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between md:flex-row md:items-center md:justify-between lg:flex-row items-start lg:justify-between lg:items-center px-5">
|
||||||
|
|
@ -196,88 +185,43 @@ const ContentTable = () => {
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row gap-3">
|
<div className=" items-center gap-3 w-full md:w-[200px]">
|
||||||
<div className=" items-center gap-3 w-full md:w-[152px] border border-black rounded-lg">
|
{/* <Select
|
||||||
<Select
|
onValueChange={(value) => {
|
||||||
onValueChange={(value) => {
|
setStatusFilter([value]);
|
||||||
setFileTypeFilter([value]);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<SelectTrigger className="w-[180px]">
|
||||||
<SelectTrigger className="w-full lg:w-[150px]">
|
<SelectValue placeholder="Select a Filter Status" />
|
||||||
<SelectValue placeholder={t("selectFilter")} />
|
</SelectTrigger>
|
||||||
</SelectTrigger>
|
<SelectContent>
|
||||||
<SelectContent>
|
<SelectGroup>
|
||||||
<SelectGroup>
|
<SelectLabel>Status</SelectLabel>
|
||||||
<SelectLabel>Filter</SelectLabel>
|
<SelectItem value="1">Menunggu Review</SelectItem>
|
||||||
<SelectItem value="1">{t("image")}</SelectItem>
|
<SelectItem value="2">Diterima</SelectItem>
|
||||||
<SelectItem value="2">{t("audio-visual")}</SelectItem>
|
<SelectItem value="3">Minta Update</SelectItem>
|
||||||
<SelectItem value="3">{t("text")}</SelectItem>
|
<SelectItem value="4">Ditolak</SelectItem>
|
||||||
<SelectItem value="4">{t("audio")}</SelectItem>
|
</SelectGroup>
|
||||||
</SelectGroup>
|
</SelectContent>
|
||||||
</SelectContent>
|
</Select> */}
|
||||||
</Select>
|
<Select
|
||||||
</div>
|
onValueChange={(value) => {
|
||||||
<DropdownMenu>
|
setFileTypeFilter([value]);
|
||||||
<DropdownMenuTrigger asChild>
|
}}
|
||||||
<Button variant="outline" className="ml-auto" size="md">
|
>
|
||||||
Filter <ChevronDown />
|
<SelectTrigger className="w-full lg:w-[180px]">
|
||||||
</Button>
|
<SelectValue placeholder={t("selectFilter")} />
|
||||||
</DropdownMenuTrigger>
|
</SelectTrigger>
|
||||||
<DropdownMenuContent
|
<SelectContent>
|
||||||
align="end"
|
<SelectGroup>
|
||||||
className="w-64 h-[150px] overflow-y-auto"
|
<SelectLabel>Filter</SelectLabel>
|
||||||
>
|
<SelectItem value="1">{t("image")}</SelectItem>
|
||||||
<Label className="ml-2 mt-2">Status</Label>
|
<SelectItem value="2">{t("audio-visual")}</SelectItem>
|
||||||
<div className="flex items-center px-4 py-1">
|
<SelectItem value="3">{t("text")}</SelectItem>
|
||||||
<input
|
<SelectItem value="4">Audio</SelectItem>
|
||||||
type="checkbox"
|
</SelectGroup>
|
||||||
id="status-2"
|
</SelectContent>
|
||||||
className="mr-2"
|
</Select>
|
||||||
checked={statusFilter.includes(1)}
|
|
||||||
onChange={() => handleStatusCheckboxChange(1)}
|
|
||||||
/>
|
|
||||||
<label htmlFor="status-2" className="text-sm">
|
|
||||||
Menunggu Review
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center px-4 py-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="status-2"
|
|
||||||
className="mr-2"
|
|
||||||
checked={statusFilter.includes(2)}
|
|
||||||
onChange={() => handleStatusCheckboxChange(2)}
|
|
||||||
/>
|
|
||||||
<label htmlFor="status-2" className="text-sm">
|
|
||||||
Diterima
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center px-4 py-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="status-3"
|
|
||||||
className="mr-2"
|
|
||||||
checked={statusFilter.includes(3)}
|
|
||||||
onChange={() => handleStatusCheckboxChange(3)}
|
|
||||||
/>
|
|
||||||
<label htmlFor="status-3" className="text-sm">
|
|
||||||
Minta Update
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center px-4 py-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="status-4"
|
|
||||||
className="mr-2"
|
|
||||||
checked={statusFilter.includes(4)}
|
|
||||||
onChange={() => handleStatusCheckboxChange(4)}
|
|
||||||
/>
|
|
||||||
<label htmlFor="status-4" className="text-sm">
|
|
||||||
Ditolak
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
|
@ -8,42 +10,13 @@ import {
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { Link } from "@/components/navigation";
|
import { Link } from "@/components/navigation";
|
||||||
import { deleteTicketInternal } from "@/service/communication/communication";
|
|
||||||
import withReactContent from "sweetalert2-react-content";
|
|
||||||
import Swal from "sweetalert2";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
const MySwal = withReactContent(Swal);
|
const useTableColumns = () => {
|
||||||
|
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||||
const useTableColumns = (onDeleteSuccess?: () => void) => {
|
|
||||||
const t = useTranslations("Table");
|
|
||||||
|
|
||||||
const handleDelete = async (id: any) => {
|
|
||||||
MySwal.fire({
|
|
||||||
title: "Delete Data?",
|
|
||||||
text: "Apakah Anda yakin ingin menghapus data ini?",
|
|
||||||
icon: "warning",
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonColor: "#d33",
|
|
||||||
confirmButtonColor: "#3085d6",
|
|
||||||
confirmButtonText: "Ya, hapus",
|
|
||||||
}).then(async (result) => {
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
try {
|
|
||||||
await deleteTicketInternal(id);
|
|
||||||
|
|
||||||
MySwal.fire("Sukses", "Data berhasil dihapus!", "success");
|
|
||||||
|
|
||||||
if (onDeleteSuccess) onDeleteSuccess();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
MySwal.fire("Gagal", "Terjadi kesalahan saat menghapus", "error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: ColumnDef<any>[] = [
|
const columns: ColumnDef<any>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -94,46 +67,46 @@ const useTableColumns = (onDeleteSuccess?: () => void) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
header: t("action"),
|
accessorKey: "action",
|
||||||
cell: ({ row }) => (
|
header: t("action", { defaultValue: "Action" }),
|
||||||
<DropdownMenu>
|
enableHiding: false,
|
||||||
<DropdownMenuTrigger asChild>
|
cell: ({ row }) => {
|
||||||
<Button size="icon" className="bg-transparent hover:bg-transparent">
|
return (
|
||||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
<DropdownMenu>
|
||||||
</Button>
|
<DropdownMenuTrigger asChild>
|
||||||
</DropdownMenuTrigger>
|
<Button
|
||||||
<DropdownMenuContent align="end" className="p-0">
|
size="icon"
|
||||||
{/* View */}
|
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
|
||||||
<Link
|
>
|
||||||
href={`/shared/communication/internal/detail/${row.original.id}`}
|
<span className="sr-only">Open menu</span>
|
||||||
>
|
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||||
<DropdownMenuItem className="p-2 border-b cursor-pointer hover:bg-slate-100">
|
</Button>
|
||||||
<Eye className="w-4 h-4 me-1.5" />
|
</DropdownMenuTrigger>
|
||||||
View
|
<DropdownMenuContent className="p-0" align="end">
|
||||||
|
<Link
|
||||||
|
href={`/shared/communication/internal/detail/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<Eye className="w-4 h-4 me-1.5" />
|
||||||
|
View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href={`/shared/communication/internal/update/${row.original.id}`}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||||
|
<SquarePen className="w-4 h-4 me-1.5" />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</Link>
|
||||||
|
<DropdownMenuItem className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none">
|
||||||
|
<Trash2 className="w-4 h-4 me-1.5" />
|
||||||
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
{/* Edit */}
|
);
|
||||||
<Link
|
},
|
||||||
href={`/shared/communication/internal/update/${row.original.id}`}
|
|
||||||
>
|
|
||||||
<DropdownMenuItem className="p-2 border-b cursor-pointer hover:bg-slate-100">
|
|
||||||
<SquarePen className="w-4 h-4 me-1.5" />
|
|
||||||
Edit
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Delete */}
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="p-2 text-destructive bg-destructive/30 cursor-pointer"
|
|
||||||
onClick={() => handleDelete(row.original.id)}
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4 me-1.5" />
|
|
||||||
Delete
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ import useTableColumns from "./columns";
|
||||||
const InternalTable = () => {
|
const InternalTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
|
@ -78,6 +79,7 @@ const InternalTable = () => {
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
const [showData, setShowData] = React.useState("10");
|
const [showData, setShowData] = React.useState("10");
|
||||||
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
|
|
@ -85,7 +87,11 @@ const InternalTable = () => {
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
const columns = useTableColumns(() => fetchData());
|
const userId = getCookiesDecrypt("uie");
|
||||||
|
const userLevelId = getCookiesDecrypt("ulie");
|
||||||
|
|
||||||
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
const columns = useTableColumns();
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: dataTable,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const CommunicationPage = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="w-full overflow-x-auto bg-white dark:bg-black p-4 rounded-sm space-y-3">
|
<div className="w-full overflow-x-auto bg-white p-4 rounded-sm space-y-3">
|
||||||
<div className="flex justify-between py-3">
|
<div className="flex justify-between py-3">
|
||||||
<p className="text-lg">{tab}</p>
|
<p className="text-lg">{tab}</p>
|
||||||
{tab === "Pertanyaan Internal" && (
|
{tab === "Pertanyaan Internal" && (
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ import { useTranslations } from "next-intl";
|
||||||
const ContestPage = () => {
|
const ContestPage = () => {
|
||||||
const [userLevelId, setUserLevelId] = useState<any>(null);
|
const [userLevelId, setUserLevelId] = useState<any>(null);
|
||||||
const t = useTranslations("Contest");
|
const t = useTranslations("Contest");
|
||||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUserLevelId(Number(getCookiesDecrypt("ulie")));
|
setUserLevelId(Number(getCookiesDecrypt("ulie")));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -27,36 +25,18 @@ const ContestPage = () => {
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
{t("table", { defaultValue: "Table" })}{" "}
|
{t("table", { defaultValue: "Table" })} {t("contest", { defaultValue: "Contest" })}
|
||||||
{t("contest", { defaultValue: "Contest" })}
|
|
||||||
</div>
|
</div>
|
||||||
{userLevelId !== 776 &&
|
{userLevelId !== 776 && userLevelId !== null && (
|
||||||
userLevelId !== null &&
|
|
||||||
levelNumber !== 3 && (
|
|
||||||
<div className="flex-none">
|
|
||||||
<Link href={"/shared/contest/create"}>
|
|
||||||
<Button color="primary" className="text-white">
|
|
||||||
<UploadIcon size={18} className="mr-2" />
|
|
||||||
{t("create-contest", {
|
|
||||||
defaultValue: "Create Contest",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* {userLevelId !== 776 && userLevelId !== null && (
|
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
<Link href={"/shared/contest/create"}>
|
<Link href={"/shared/contest/create"}>
|
||||||
<Button color="primary" className="text-white">
|
<Button color="primary" className="text-white">
|
||||||
<UploadIcon size={18} className="mr-2" />
|
<UploadIcon size={18} className="mr-2" />
|
||||||
{t("create-contest", {
|
{t("create-contest", { defaultValue: "Create Contest" })}
|
||||||
defaultValue: "Create Contest",
|
|
||||||
})}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)} */}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const VideoSliderPage = () => {
|
||||||
});
|
});
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const request = {
|
const request = {
|
||||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||||
contentTypeId: "2",
|
contentTypeId: "2",
|
||||||
};
|
};
|
||||||
const response = await getListContent(request);
|
const response = await getListContent(request);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const AudioSliderPage = () => {
|
||||||
});
|
});
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const request = {
|
const request = {
|
||||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||||
contentTypeId: "4",
|
contentTypeId: "4",
|
||||||
};
|
};
|
||||||
const response = await getListContent(request);
|
const response = await getListContent(request);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const ImageSliderPage = () => {
|
||||||
});
|
});
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const request = {
|
const request = {
|
||||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||||
contentTypeId: "3",
|
contentTypeId: "3",
|
||||||
};
|
};
|
||||||
const response = await getListContent(request);
|
const response = await getListContent(request);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const TeksSliderPage = () => {
|
||||||
});
|
});
|
||||||
const initFetch = async () => {
|
const initFetch = async () => {
|
||||||
const request = {
|
const request = {
|
||||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||||
contentTypeId: "1",
|
contentTypeId: "1",
|
||||||
};
|
};
|
||||||
const response = await getListContent(request);
|
const response = await getListContent(request);
|
||||||
|
|
|
||||||
|
|
@ -473,7 +473,7 @@ export default function DetailImage() {
|
||||||
className="object-fill h-full w-full rounded-md"
|
className="object-fill h-full w-full rounded-md"
|
||||||
src={data.url}
|
src={data.url}
|
||||||
controls
|
controls
|
||||||
title={`Video ${data.id}`}
|
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||||
/>
|
/>
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,6 @@ const QuestionsTable = (props: {
|
||||||
const [page, setPage] = React.useState(1);
|
const [page, setPage] = React.useState(1);
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = React.useState<string>("");
|
||||||
const [activeTab, setActiveTab] = React.useState<"publik" | "rutin">(
|
|
||||||
"publik"
|
|
||||||
);
|
|
||||||
const [reportType, setReportType] = React.useState<"general" | "routine">(
|
|
||||||
"general"
|
|
||||||
);
|
|
||||||
|
|
||||||
const roleId = getCookiesDecrypt("urie");
|
const roleId = getCookiesDecrypt("urie");
|
||||||
|
|
||||||
|
|
@ -142,21 +136,13 @@ const QuestionsTable = (props: {
|
||||||
}
|
}
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// fetchData();
|
|
||||||
// setPagination({
|
|
||||||
// pageIndex: 0,
|
|
||||||
// pageSize: Number(showData),
|
|
||||||
// });
|
|
||||||
// }, [page, showData]);
|
|
||||||
// ✅ update useEffect, tambahin reportType supaya reload saat tab diganti
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
setPagination({
|
setPagination({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: Number(showData),
|
pageSize: Number(showData),
|
||||||
});
|
});
|
||||||
}, [page, showData, reportType]);
|
}, [page, showData]);
|
||||||
|
|
||||||
let typingTimer: any;
|
let typingTimer: any;
|
||||||
const doneTypingInterval = 1500;
|
const doneTypingInterval = 1500;
|
||||||
|
|
@ -174,52 +160,6 @@ const QuestionsTable = (props: {
|
||||||
fetchData();
|
fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function fetchData() {
|
|
||||||
// const typeNow =
|
|
||||||
// title === "comment"
|
|
||||||
// ? "1"
|
|
||||||
// : title === "facebook"
|
|
||||||
// ? "2"
|
|
||||||
// : title === "instagram"
|
|
||||||
// ? "3"
|
|
||||||
// : title === "x"
|
|
||||||
// ? "4"
|
|
||||||
// : title === "youtube"
|
|
||||||
// ? "5"
|
|
||||||
// : title === "emergency"
|
|
||||||
// ? "6"
|
|
||||||
// : title === "email"
|
|
||||||
// ? "7"
|
|
||||||
// : title === "inbox"
|
|
||||||
// ? "8"
|
|
||||||
// : title === "whatsapp"
|
|
||||||
// ? "9"
|
|
||||||
// : title === "tiktok"
|
|
||||||
// ? "10"
|
|
||||||
// : "";
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const res = await getQuestionPagination(
|
|
||||||
// search,
|
|
||||||
// page - 1,
|
|
||||||
// typeNow,
|
|
||||||
// showData
|
|
||||||
// );
|
|
||||||
// const data = res?.data?.data;
|
|
||||||
// const contentData = data?.page?.content;
|
|
||||||
// console.log("contentDatassss : ", data);
|
|
||||||
|
|
||||||
// contentData.forEach((item: any, index: number) => {
|
|
||||||
// item.no = (page - 1) * Number(showData) + index + 1;
|
|
||||||
// });
|
|
||||||
// setDataTable(contentData);
|
|
||||||
// props.statisticData(data?.statistic);
|
|
||||||
// setTotalData(data?.page?.totalElements);
|
|
||||||
// setTotalPage(data?.page?.totalPages);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error fetching tasks:", error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const typeNow =
|
const typeNow =
|
||||||
title === "comment"
|
title === "comment"
|
||||||
|
|
@ -245,15 +185,12 @@ const QuestionsTable = (props: {
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ⬇️ tambahan param khusus untuk emergency
|
|
||||||
const res = await getQuestionPagination(
|
const res = await getQuestionPagination(
|
||||||
search,
|
search,
|
||||||
page - 1,
|
page - 1,
|
||||||
typeNow,
|
typeNow,
|
||||||
showData,
|
showData
|
||||||
title === "emergency" ? reportType : undefined
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = res?.data?.data;
|
const data = res?.data?.data;
|
||||||
const contentData = data?.page?.content;
|
const contentData = data?.page?.content;
|
||||||
console.log("contentDatassss : ", data);
|
console.log("contentDatassss : ", data);
|
||||||
|
|
@ -261,7 +198,6 @@ const QuestionsTable = (props: {
|
||||||
contentData.forEach((item: any, index: number) => {
|
contentData.forEach((item: any, index: number) => {
|
||||||
item.no = (page - 1) * Number(showData) + index + 1;
|
item.no = (page - 1) * Number(showData) + index + 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataTable(contentData);
|
setDataTable(contentData);
|
||||||
props.statisticData(data?.statistic);
|
props.statisticData(data?.statistic);
|
||||||
setTotalData(data?.page?.totalElements);
|
setTotalData(data?.page?.totalElements);
|
||||||
|
|
@ -331,23 +267,6 @@ const QuestionsTable = (props: {
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
{title === "emergency" && (
|
|
||||||
<div className="flex gap-2 mt-4">
|
|
||||||
<Button
|
|
||||||
variant={reportType === "general" ? "default" : "outline"}
|
|
||||||
onClick={() => setReportType("general")}
|
|
||||||
>
|
|
||||||
Laporan Publik
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={reportType === "routine" ? "default" : "outline"}
|
|
||||||
onClick={() => setReportType("routine")}
|
|
||||||
>
|
|
||||||
Laporan Rutin
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Table className="overflow-hidden mt-3">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ export default function CreateSpvFAQModal() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const target = form.watch("publishTo");
|
const target = form.watch("publishTo");
|
||||||
const isAllTargetChecked = target && publishToList.every((item) =>
|
const isAllTargetChecked = publishToList.every((item) =>
|
||||||
target.includes(item.id)
|
target?.includes(item.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
export const metadata = {
|
|
||||||
title: "Ticketing",
|
|
||||||
};
|
|
||||||
|
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
return <>{children}</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Layout;
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
|
||||||
import TicketingTable from "../components/table";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
|
|
||||||
const TicketingPage = async () => {
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<SiteBreadcrumb />
|
|
||||||
|
|
||||||
<section
|
|
||||||
id="table"
|
|
||||||
className="flex flex-col gap-2 bg-white dark:bg-black rounded-lg p-3 mt-5"
|
|
||||||
>
|
|
||||||
{/* <div className="flex justify-between py-3">
|
|
||||||
<p className="text-lg">Semua Ticket : 0</p>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<TicketingTable />
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TicketingPage;
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue