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=https://new.netidhub.com
|
||||
NEXT_PUBLIC_API=https://netidhub.com/api
|
||||
NEXT_PUBLIC=https://netidhub.com
|
||||
NEXT_PUBLIC_TINYMCE_API_KEY=bhteuja26yz5p0aubxry9b95hs33amgn65kjv5km0fd5iuev
|
||||
|
|
@ -8,16 +8,15 @@ build-dev:
|
|||
only:
|
||||
- main
|
||||
- dev-landing-v2
|
||||
image:
|
||||
name: docker:25.0.3-cli
|
||||
image: docker:stable
|
||||
services:
|
||||
- name: docker:25.0.3-dind
|
||||
command: ["--insecure-registry=38.47.185.86:8900"]
|
||||
- name: docker:dind
|
||||
command: ["--insecure-registry=103.82.242.92:8900"]
|
||||
script:
|
||||
- docker logout
|
||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 38.47.185.86:8900
|
||||
- docker build -t 38.47.185.86:8900/mediahub/new-mediahub-fe:dev .
|
||||
- docker push 38.47.185.86:8900/mediahub/new-mediahub-fe:dev
|
||||
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900
|
||||
- docker build -t 103.82.242.92:8900/mediahub/new-mediahub-fe:dev .
|
||||
- docker push 103.82.242.92:8900/mediahub/new-mediahub-fe:dev
|
||||
|
||||
auto-deploy:
|
||||
stage: deploy
|
||||
|
|
@ -26,5 +25,7 @@ auto-deploy:
|
|||
- main
|
||||
- dev-landing-v2
|
||||
image: curlimages/curl:latest
|
||||
services:
|
||||
- docker:dind
|
||||
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 . .
|
||||
|
||||
# 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 3000
|
||||
|
||||
# Perintah untuk menjalankan aplikasi
|
||||
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 { stringify } from "querystring";
|
||||
|
||||
const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[] => [
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
header: "No",
|
||||
|
|
@ -30,13 +30,11 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
|||
header: "Nama",
|
||||
cell: ({ row }) => <span>{row.getValue("fullname")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "address",
|
||||
header: "Wilayah",
|
||||
cell: () => <span>MABES</span>,
|
||||
cell: ({ row }) => <span>MABES</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "userRolePlacements",
|
||||
header: "Posisi",
|
||||
|
|
@ -54,7 +52,6 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
|||
return <span>{posisi}</span>;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "role.name",
|
||||
header: "Bidang Keahlian",
|
||||
|
|
@ -84,77 +81,72 @@ const getColumns = ({ onRefresh }: { onRefresh: () => void }): ColumnDef<any>[]
|
|||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const { toast } = useToast();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
const router = useRouter();
|
||||
const doDelete = async (id: number) => {
|
||||
Swal.fire({
|
||||
title: "Menghapus user...",
|
||||
text: "Mohon tunggu",
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading(),
|
||||
});
|
||||
|
||||
const response = await deleteUser(id);
|
||||
|
||||
Swal.close();
|
||||
|
||||
if (response?.error) {
|
||||
toast({
|
||||
title: stringify(response?.message),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: "Success delete",
|
||||
});
|
||||
|
||||
toast({ title: "Berhasil menghapus user" });
|
||||
|
||||
// ⬅️ INI YANG PENTING → REFRESH TABLE TANPA RELOAD
|
||||
onRefresh();
|
||||
router.push("?dataChange=true");
|
||||
};
|
||||
|
||||
const handleDelete = (id: number) => {
|
||||
MySwal.fire({
|
||||
title: "Hapus user ini?",
|
||||
title: "Apakah anda ingin menghapus data user?",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#dc3545",
|
||||
confirmButtonText: "Iya",
|
||||
cancelButtonText: "Tidak",
|
||||
}).then((res) => {
|
||||
if (res.isConfirmed) doDelete(id);
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
doDelete(id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="icon" variant="ghost">
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
<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 align="end">
|
||||
|
||||
<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 cursor-pointer">
|
||||
<Eye className="w-4 h-4 me-1.5" /> View
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<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">
|
||||
<Eye className="w-4 h-4 me-1.5" />
|
||||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
<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">
|
||||
<SquarePen className="w-4 h-4 me-1.5" /> Edit
|
||||
<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">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
<DropdownMenuItem
|
||||
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>
|
||||
|
||||
</DropdownMenuContent>
|
||||
</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 {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
|
|
@ -14,6 +15,7 @@ import {
|
|||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -23,6 +25,7 @@ import {
|
|||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -32,14 +35,43 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
import { paginationBlog } from "@/service/blog/blog";
|
||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
// import columns from "./column";
|
||||
import getColumns from "./column";
|
||||
import columns 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 { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
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 router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -65,8 +97,7 @@ const AddExpertTable = () => {
|
|||
const [limit, setLimit] = React.useState(10);
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
// columns,
|
||||
columns: getColumns({ onRefresh: fetchData }),
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
|
|
@ -170,7 +201,7 @@ const AddExpertTable = () => {
|
|||
}
|
||||
|
||||
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">
|
||||
<p className="text-xl font-medium text-default-900">Tenaga Ahli</p>
|
||||
<Link href="/admin/add-experts/create">
|
||||
|
|
@ -252,11 +283,7 @@ const AddExpertTable = () => {
|
|||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
// colSpan={columns.length}
|
||||
colSpan={table.getAllLeafColumns().length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -35,61 +35,32 @@ import {
|
|||
import { error, loading } from "@/config/swal";
|
||||
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({
|
||||
name: z.string({ required_error: "Required" }),
|
||||
username: z
|
||||
.string({ required_error: "Required" })
|
||||
.refine((val) => !/\s/.test(val), {
|
||||
message: "Username tidak boleh mengandung spasi",
|
||||
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",
|
||||
}),
|
||||
// .transform((val) => val.toLowerCase()),
|
||||
|
||||
password: z
|
||||
.string({ required_error: "Required" })
|
||||
.min(8, "Minimal 8 karakter")
|
||||
.regex(/[A-Z]/, "Harus mengandung huruf besar (A-Z)")
|
||||
.regex(/[0-9]/, "Harus mengandung angka (0-9)")
|
||||
.regex(/[^A-Za-z0-9]/, "Harus mengandung karakter spesial (!@#$%^&*)"),
|
||||
|
||||
// confirmPassword: 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" }),
|
||||
});
|
||||
// .refine((data) => data.password === data.confirmPassword, {
|
||||
// path: ["confirmPassword"],
|
||||
// message: "Konfirmasi password tidak sama",
|
||||
// });
|
||||
|
||||
export type Placements = {
|
||||
index: number;
|
||||
|
|
@ -103,7 +74,6 @@ export default function AddExpertForm() {
|
|||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
const [passwordStrength, setPasswordStrength] = useState("");
|
||||
const [incrementId, setIncrementId] = useState(1);
|
||||
const [placementRows, setPlacementRows] = useState<Placements[]>([
|
||||
{ index: 0, roleId: "", userLevelId: 0 },
|
||||
|
|
@ -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 (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
|
|
@ -311,7 +268,7 @@ export default function AddExpertForm() {
|
|||
<Form {...form}>
|
||||
<form
|
||||
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>
|
||||
<FormField
|
||||
|
|
@ -331,39 +288,6 @@ export default function AddExpertForm() {
|
|||
)}
|
||||
/>
|
||||
<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}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
|
|
@ -379,7 +303,7 @@ export default function AddExpertForm() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phoneNumber"
|
||||
|
|
@ -425,69 +349,6 @@ export default function AddExpertForm() {
|
|||
)}
|
||||
/>
|
||||
<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}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
|
|
@ -512,7 +373,7 @@ export default function AddExpertForm() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="skills"
|
||||
|
|
|
|||
|
|
@ -37,43 +37,32 @@ import { Eye, EyeOff } from "lucide-react";
|
|||
import { useParams } from "next/navigation";
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
phoneNumber: z.string().optional(),
|
||||
email: z.string().optional(),
|
||||
skills: z.string().optional(),
|
||||
experiences: z.string().optional(),
|
||||
company: z.string().optional(),
|
||||
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({
|
||||
// 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 = {
|
||||
index: number;
|
||||
roleId?: string;
|
||||
|
|
@ -107,10 +96,6 @@ interface Detail {
|
|||
createdAt: string;
|
||||
};
|
||||
};
|
||||
userRolePlacements?: {
|
||||
roleId: number;
|
||||
userLevelId: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export default function UpdateExpertForm() {
|
||||
|
|
@ -164,39 +149,6 @@ export default function UpdateExpertForm() {
|
|||
initState();
|
||||
}, [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>;
|
||||
|
||||
const togglePasswordType = () => {
|
||||
|
|
@ -237,35 +189,18 @@ export default function UpdateExpertForm() {
|
|||
|
||||
const dataReq = {
|
||||
id: detail?.id,
|
||||
firstName: data.name || detail.fullname,
|
||||
username: data.username || detail.username,
|
||||
email: data.email || detail.email,
|
||||
password: data.password || undefined,
|
||||
firstName: data.name,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
address: "",
|
||||
roleId: "EXP-ID",
|
||||
phoneNumber: data.phoneNumber || detail.phoneNumber,
|
||||
userCompetencyId:
|
||||
data.skills || detail.userProfilesAdditional?.userCompetency?.id,
|
||||
userExperienceId:
|
||||
data.experiences || detail.userProfilesAdditional?.userExperienceId,
|
||||
companyName: data.company || detail.userProfilesAdditional?.companyName,
|
||||
isAdmin: true,
|
||||
phoneNumber: data.phoneNumber,
|
||||
userCompetencyId: data.skills,
|
||||
userExperienceId: data.experiences,
|
||||
companyName: data.company,
|
||||
};
|
||||
|
||||
// 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();
|
||||
const res = await saveUserInternal(dataReq);
|
||||
const resData = res?.data?.data;
|
||||
|
|
@ -387,15 +322,10 @@ export default function UpdateExpertForm() {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Lengkap</FormLabel>
|
||||
{/* <Input
|
||||
<Input
|
||||
defaultValue={detail?.fullname}
|
||||
placeholder="Masukkan Nama Lengkap"
|
||||
onChange={field.onChange}
|
||||
/> */}
|
||||
<Input
|
||||
{...field}
|
||||
defaultValue={detail?.fullname}
|
||||
placeholder="Masukkan Nama Lengkap"
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
|
|
@ -403,24 +333,18 @@ export default function UpdateExpertForm() {
|
|||
)}
|
||||
/>
|
||||
<FormField
|
||||
disabled
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Username</FormLabel>
|
||||
{/* <Input
|
||||
<Input
|
||||
type="text"
|
||||
defaultValue={detail?.username}
|
||||
placeholder="Masukkan"
|
||||
onChange={field.onChange}
|
||||
/> */}
|
||||
<Input
|
||||
{...field}
|
||||
type="text"
|
||||
defaultValue={detail?.username}
|
||||
placeholder="Masukkan"
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
@ -431,17 +355,11 @@ export default function UpdateExpertForm() {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>No. HP</FormLabel>
|
||||
{/* <Input
|
||||
<Input
|
||||
type="number"
|
||||
defaultValue={detail?.phoneNumber}
|
||||
placeholder="Masukkan No.Hp"
|
||||
onChange={field.onChange}
|
||||
/> */}
|
||||
<Input
|
||||
{...field}
|
||||
type="number"
|
||||
defaultValue={detail?.phoneNumber}
|
||||
placeholder="Masukkan"
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
@ -453,46 +371,17 @@ export default function UpdateExpertForm() {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
{/* <Input
|
||||
<Input
|
||||
type="email"
|
||||
defaultValue={detail?.email}
|
||||
placeholder="Masukkan email"
|
||||
onChange={field.onChange}
|
||||
/> */}
|
||||
<Input
|
||||
{...field}
|
||||
type="email"
|
||||
defaultValue={detail?.email}
|
||||
placeholder="Masukkan email"
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* <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
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
|
|
@ -517,7 +406,7 @@ export default function UpdateExpertForm() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="skills"
|
||||
|
|
@ -592,21 +481,12 @@ export default function UpdateExpertForm() {
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama Institusi/Perusahaan</FormLabel>
|
||||
{/* <Input
|
||||
<Input
|
||||
type="text"
|
||||
value={detail?.userProfilesAdditional?.companyName || ""}
|
||||
placeholder="Nama Institusi/Perusahaan"
|
||||
onChange={field.onChange}
|
||||
/> */}
|
||||
<Input
|
||||
{...field}
|
||||
type="text"
|
||||
defaultValue={
|
||||
detail?.userProfilesAdditional?.companyName || ""
|
||||
}
|
||||
placeholder="Nama Institusi/Perusahaan"
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
@ -617,7 +497,6 @@ export default function UpdateExpertForm() {
|
|||
{placementRows?.map((row: any) => (
|
||||
<div key={row.index} className="flex items-center gap-2 my-2">
|
||||
<Select
|
||||
value={row.roleId}
|
||||
onValueChange={(e) =>
|
||||
handleSelectionChange(row.index, "roleId", e)
|
||||
}
|
||||
|
|
@ -654,7 +533,6 @@ export default function UpdateExpertForm() {
|
|||
</SelectContent>
|
||||
</Select> */}
|
||||
<Select
|
||||
value={row.userLevelId}
|
||||
onValueChange={(e) =>
|
||||
handleSelectionChange(row.index, "userLevelId", e)
|
||||
}
|
||||
|
|
@ -684,7 +562,7 @@ export default function UpdateExpertForm() {
|
|||
type="button"
|
||||
size="md"
|
||||
onClick={handleAddRow}
|
||||
disabled={placementRows.length >= 2}
|
||||
disabled={placementRows.length >= 2} // optional: disable button if already 1 row added
|
||||
>
|
||||
Tambah
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -133,8 +133,8 @@ export default function ContentManagement() {
|
|||
<SiteBreadcrumb />
|
||||
<div className="flex flex-col gap-3">
|
||||
<Accordion id="polri" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
KONTEN YANG DISIMPAN OLEH PENGGUNA POLRI INDONESIA
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -161,8 +161,8 @@ export default function ContentManagement() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
<Accordion id="2" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-2" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-2" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -189,8 +189,8 @@ export default function ContentManagement() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
<Accordion id="3" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
KONTEN YANG DISIMPAN OLEH PENGGUNA JURNALIS INTERNASIONAL
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -217,8 +217,8 @@ export default function ContentManagement() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
<Accordion id="4" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
PENAMBAHAN JUMLAH PENGGUNA POLRI INDONESIA
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -245,8 +245,8 @@ export default function ContentManagement() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
<Accordion id="5" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INDONESIA
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -273,8 +273,8 @@ export default function ContentManagement() {
|
|||
</AccordionItem>
|
||||
</Accordion>
|
||||
<Accordion id="6" type="single" collapsible className="w-full">
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
PENAMBAHAN JUMLAH PENGGUNA JURNALIS INTERNASIONAL
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ export default function EmergencyIssue() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
ANALISA BERKAITAN DENGAN AKUN PELAPOR{" "}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ export default function FeedbackCenter() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
TICKET PADA FEEDBACK CENTER{" "}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
|
|||
|
|
@ -114,8 +114,8 @@ export default function ContentManagement() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
PUBLISH JADWAL PRESS CONFERENCE TERBANYAK
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -147,8 +147,8 @@ export default function ContentManagement() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
JUMLAH PRODUKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -180,8 +180,8 @@ export default function ContentManagement() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
TINGKAT INTERAKSI KONTEN UNTUK KATEGORI PRESS CONFERENCE
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
@ -213,8 +213,8 @@ export default function ContentManagement() {
|
|||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value="item-1" className="bg-white dark:bg-black w-full">
|
||||
<AccordionTrigger className="bg-white dark:bg-black">
|
||||
<AccordionItem value="item-1" className="bg-white w-full">
|
||||
<AccordionTrigger className="bg-white">
|
||||
AKTIFITAS MEDIA BERKAITAN DENGAN PERS RILIS
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
|
|
|
|||
|
|
@ -30,27 +30,34 @@ const columns: ColumnDef<any>[] = [
|
|||
accessorKey: "accountName",
|
||||
header: "Nama",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.original.mediaBlastAccountName}</span>
|
||||
<span className="normal-case">{row.getValue("accountName")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "accountType",
|
||||
header: "Tipe Akun",
|
||||
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",
|
||||
header: "Email",
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.original.mediaBlastAccountEmail}</span>
|
||||
<span className="normal-case">{row.getValue("emailAddress")}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "whatsappNumber",
|
||||
header: "Whatsapp",
|
||||
cell: ({ row }) => <span>{row.original.mediaBlastAccountPhone}</span>,
|
||||
cell: ({ row }) => <span>{row.getValue("whatsappNumber")}</span>,
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
|
|
@ -106,7 +113,7 @@ const columns: ColumnDef<any>[] = [
|
|||
<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">
|
||||
<Link
|
||||
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.mediaBlastAccountId}`}
|
||||
href={`/admin/broadcast/campaign-list/account-list/edit/${row.original.id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import * as React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
PaginationState,
|
||||
SortingState,
|
||||
|
|
@ -14,6 +15,7 @@ import {
|
|||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
|
@ -22,64 +24,25 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
} from "@/components/ui/dialog";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select as UISelect,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { UserIcon } from "lucide-react";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { getMediaBlastAccountPage } from "@/service/broadcast/broadcast";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { X } from "lucide-react";
|
||||
import ReactSelect from "react-select";
|
||||
|
||||
import columns from "./column";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import {
|
||||
getMediaBlastCampaignAccountList,
|
||||
deleteMediaBlastCampaignAccount,
|
||||
saveMediaBlastCampaignAccountBulk,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import {
|
||||
AdministrationUserList,
|
||||
getUserListAll,
|
||||
} from "@/service/management-user/management-user";
|
||||
import { close, loading, error, success, successCallback } from "@/config/swal";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link } from "@/i18n/routing";
|
||||
|
||||
// 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" },
|
||||
];
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
|
||||
const AccountListTable = () => {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const campaignId = params?.id as string;
|
||||
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
|
|
@ -93,19 +56,10 @@ const AccountListTable = () => {
|
|||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
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({
|
||||
data: dataTable,
|
||||
columns,
|
||||
|
|
@ -129,24 +83,24 @@ const AccountListTable = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) setPage(Number(pageFromUrl));
|
||||
if (pageFromUrl) {
|
||||
setPage(Number(pageFromUrl));
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [page, filtered]);
|
||||
}, [page]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await getMediaBlastCampaignAccountList(
|
||||
const res = await getMediaBlastAccountPage(
|
||||
page - 1,
|
||||
filtered ? filtered.join(",") : "",
|
||||
campaignId
|
||||
filtered ? filtered.join(",") : ""
|
||||
);
|
||||
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content || [];
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * 10 + index + 1;
|
||||
});
|
||||
|
|
@ -155,349 +109,42 @@ const AccountListTable = () => {
|
|||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
close();
|
||||
} catch (err) {
|
||||
console.error("Error fetching tasks:", err);
|
||||
close();
|
||||
} catch (error) {
|
||||
console.error("Error fetching tasks:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 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) => {
|
||||
let temp = [...filtered];
|
||||
if (checked) temp = [...temp, id];
|
||||
else temp = temp.filter((a) => a !== id);
|
||||
setFiltered(temp);
|
||||
};
|
||||
|
||||
const removeSelectedAccount = (accountId: string) => {
|
||||
setSelectedAccount(selectedAccount.filter((acc) => acc.id !== accountId));
|
||||
};
|
||||
|
||||
const getFilteredAccounts = () => {
|
||||
if (accountCategory === "kategori" && selectedCategory) {
|
||||
return availableAccountsList.filter(
|
||||
(acc) => acc.category === selectedCategory
|
||||
);
|
||||
if (checked) {
|
||||
temp = [...temp, id];
|
||||
} else {
|
||||
temp = temp.filter((a) => a !== id);
|
||||
}
|
||||
return availableAccountsList;
|
||||
setFiltered(temp);
|
||||
console.log("sss", temp);
|
||||
};
|
||||
|
||||
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">
|
||||
<p className="text-xl font-medium text-default-900">Daftar Akun</p>
|
||||
<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">
|
||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
||||
<span>
|
||||
<svg
|
||||
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 color="primary" size="md" className="text-sm">
|
||||
<Icon icon="tdesign:user-add-filled" />
|
||||
Tambah Akun
|
||||
</Button>
|
||||
</Link>
|
||||
<Button variant="default" className="bg-[#3f37c9] gap-2">
|
||||
<span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none">
|
||||
<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>
|
||||
{/* <Link href="/admin/broadcast/campaign-list/import">
|
||||
<Button color="success" size="md" className="text-sm">
|
||||
<UserIcon />
|
||||
Import Akun
|
||||
</Button>
|
||||
</div> */}
|
||||
|
||||
</Link> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button size="md" variant="outline">
|
||||
|
|
@ -516,28 +163,65 @@ const AccountListTable = () => {
|
|||
</a>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 overflow-auto max-h-[300px] text-xs custom-scrollbar-table">
|
||||
{["polri", "jurnalis", "umum", "ksp"].map((cat) => (
|
||||
<div key={cat} className="flex items-center space-x-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={cat}
|
||||
checked={filtered.includes(cat)}
|
||||
onCheckedChange={(e) => handleFilter(cat, Boolean(e))}
|
||||
id="accepted"
|
||||
checked={filtered.includes("polri")}
|
||||
onCheckedChange={(e) => handleFilter("polri", Boolean(e))}
|
||||
/>
|
||||
<label
|
||||
htmlFor={cat}
|
||||
className="text-xs font-medium leading-none"
|
||||
htmlFor="accepted"
|
||||
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{cat.toUpperCase()}
|
||||
POLRI
|
||||
</label>
|
||||
</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>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{/* === Table Data === */}
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
|
@ -579,7 +263,6 @@ const AccountListTable = () => {
|
|||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<TablePagination
|
||||
table={table}
|
||||
totalData={totalData}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
|
|
@ -13,80 +14,55 @@ import {
|
|||
} from "@/components/ui/form";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Input } from "@/components/ui/input";
|
||||
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 {
|
||||
getMediaBlastCampaignPage,
|
||||
saveMediaBlastAccount,
|
||||
saveMediaBlastCampaign,
|
||||
} from "@/service/broadcast/broadcast";
|
||||
import { error } from "@/config/swal";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// ----------------------------
|
||||
// ZOD SCHEMA (dinamis)
|
||||
// ----------------------------
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z.string({ required_error: "Required" }),
|
||||
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
accountType: z
|
||||
.array(z.string())
|
||||
.min(1, "Pilih minimal satu tipe akun"),
|
||||
|
||||
email: z.string().optional(),
|
||||
whatsapp: z.string().optional(),
|
||||
|
||||
campaignId: z.string({ required_error: "Required" }),
|
||||
}).refine(
|
||||
(data) => {
|
||||
if (data.accountType.includes("email") && !data.email) return false;
|
||||
return true;
|
||||
},
|
||||
{ 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
|
||||
// ----------------------------
|
||||
.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",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function CreateAccountForBroadcast() {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
accountType: [],
|
||||
email: "",
|
||||
whatsapp: "",
|
||||
},
|
||||
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",
|
||||
|
|
@ -109,8 +85,10 @@ export default function CreateAccountForBroadcast() {
|
|||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
router.push("/admin/broadcast/campaign-list/account-list");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -118,21 +96,20 @@ export default function CreateAccountForBroadcast() {
|
|||
const reqData = {
|
||||
accountName: data.name,
|
||||
accountType: data.accountType.join(","),
|
||||
emailAddress: data.email ?? "",
|
||||
whatsappNumber: data.whatsapp ?? "",
|
||||
campaignId: data.campaignId,
|
||||
accountCategory: data.accountCategory,
|
||||
emailAddress: data.email,
|
||||
whatsappNumber: data.whatsapp,
|
||||
};
|
||||
|
||||
console.log("REQ:", reqData);
|
||||
console.log("data", data);
|
||||
|
||||
const response = await saveMediaBlastAccount(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
successSubmit();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
|
|
@ -141,9 +118,7 @@ export default function CreateAccountForBroadcast() {
|
|||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white rounded-sm p-4"
|
||||
>
|
||||
<p className="font-semibold">Account</p>
|
||||
|
||||
{/* NAMA */}
|
||||
<p className="fonnt-semibold">Account</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
@ -155,125 +130,172 @@ export default function CreateAccountForBroadcast() {
|
|||
placeholder="Masukkan nama"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* CHECKBOX TIPE AKUN */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={({ field }) => (
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipe Akun</FormLabel>
|
||||
<div className="flex flex-row gap-4">
|
||||
{/* WA */}
|
||||
<div className="flex items-center gap-2">
|
||||
<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) =>
|
||||
checked
|
||||
checked={field.value?.includes("wa")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "wa"])
|
||||
: field.onChange(field.value.filter((v) => v !== "wa"))
|
||||
}
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== "wa"
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label>Whatsapp</label>
|
||||
</div>
|
||||
|
||||
{/* EMAIL */}
|
||||
<div className="flex items-center gap-2">
|
||||
</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) =>
|
||||
checked
|
||||
checked={field.value?.includes("email")}
|
||||
onCheckedChange={(checked) => {
|
||||
return checked
|
||||
? field.onChange([...field.value, "email"])
|
||||
: field.onChange(
|
||||
field.value.filter((v) => v !== "email")
|
||||
field.value?.filter(
|
||||
(value) => value !== "email"
|
||||
)
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">Email</FormLabel>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label>Email</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* FORM WHATSAPP */}
|
||||
{selectedTypes.includes("wa") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="whatsapp"
|
||||
name="accountCategory"
|
||||
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
|
||||
control={form.control}
|
||||
name="campaignId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Campaign</FormLabel>
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>Kategori</FormLabel>
|
||||
<FormControl>
|
||||
<select
|
||||
className="w-full border rounded-md p-2 text-sm"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
<RadioGroup
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
className="flex flex-row gap-2"
|
||||
>
|
||||
<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>
|
||||
<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>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">
|
||||
<Button type="button" variant="outline" color="destructive">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="outline"
|
||||
color="destructive"
|
||||
className="text-xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" color="primary">
|
||||
<Button size="md" type="submit" color="primary" className="text-xs">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -282,380 +304,3 @@ export default function CreateAccountForBroadcast() {
|
|||
</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 { useParams } from "next/navigation";
|
||||
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({
|
||||
fullname: z.string({ required_error: "Required" }),
|
||||
email: z.string({ required_error: "Required" }),
|
||||
phoneNumber: z.string({ required_error: "Required" }),
|
||||
username: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
level: z.string().optional(),
|
||||
nrp: z.string().optional(),
|
||||
address: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
confirmPassword: z.string().optional(),
|
||||
name: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
accountType: z
|
||||
.array(z.string())
|
||||
.refine((value) => value.some((item) => item), {
|
||||
message: "Required",
|
||||
}),
|
||||
accountCategory: z.enum(["polri", "jurnalis", "umumu", "ksp"], {
|
||||
required_error: "Required",
|
||||
}),
|
||||
email: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
whatsapp: z.string({
|
||||
required_error: "Required",
|
||||
}),
|
||||
});
|
||||
|
||||
export default function EditAccountForBroadcast() {
|
||||
const id = useParams()?.id;
|
||||
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
// defaultValues: { accountType: [] },
|
||||
defaultValues: { accountType: [] },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function getDetailData() {
|
||||
const response = await getUserById(String(id));
|
||||
const response = await getMediaBlastAccount(String(id));
|
||||
const details = response?.data?.data;
|
||||
console.log("Response full:", response);
|
||||
form.setValue("fullname", details?.fullname);
|
||||
form.setValue("username", details?.username);
|
||||
form.setValue("phoneNumber", details?.phoneNumber);
|
||||
// form.setValue("nrp", details?.memberIdentity);
|
||||
// form.setValue("address", details?.address);
|
||||
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(","));
|
||||
console.log("new", details);
|
||||
form.setValue("name", details.accountName);
|
||||
form.setValue("email", details?.emailAddress);
|
||||
form.setValue("whatsapp", details?.whatsappNumber);
|
||||
form.setValue("accountCategory", details?.accountCategory);
|
||||
form.setValue("accountType", details?.accountType.split(","));
|
||||
}
|
||||
|
||||
getDetailData();
|
||||
|
|
@ -134,32 +106,23 @@ export default function EditAccountForBroadcast() {
|
|||
confirmButtonText: "OK",
|
||||
}).then((result) => {
|
||||
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 reqData = {
|
||||
id: Number(id),
|
||||
// accountName: data.fullname,
|
||||
// accountType: data.accountType.join(","),
|
||||
// accountCategory: data.accountCategory,
|
||||
// emailAddress: data.email,
|
||||
phoneNumber: data.phoneNumber,
|
||||
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,
|
||||
id: String(id),
|
||||
accountName: data.name,
|
||||
accountType: data.accountType.join(","),
|
||||
accountCategory: data.accountCategory,
|
||||
emailAddress: data.email,
|
||||
whatsappNumber: data.whatsapp,
|
||||
};
|
||||
console.log("data", data);
|
||||
|
||||
const response = await saveUserInternal(reqData);
|
||||
const response = await saveMediaBlastAccount(reqData);
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
|
|
@ -178,7 +141,7 @@ export default function EditAccountForBroadcast() {
|
|||
<p className="fonnt-semibold">Account</p>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="fullname"
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
|
|
@ -192,7 +155,7 @@ export default function EditAccountForBroadcast() {
|
|||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* <FormField
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountType"
|
||||
render={() => (
|
||||
|
|
@ -264,8 +227,8 @@ export default function EditAccountForBroadcast() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
{/* <FormField
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="accountCategory"
|
||||
render={({ field }) => (
|
||||
|
|
@ -306,13 +269,13 @@ export default function EditAccountForBroadcast() {
|
|||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/> */}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="email"
|
||||
value={field.value}
|
||||
|
|
@ -326,14 +289,14 @@ export default function EditAccountForBroadcast() {
|
|||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phoneNumber"
|
||||
name="whatsapp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nomor Whatsapp</FormLabel>
|
||||
<FormLabel>Nama</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={field.value}
|
||||
placeholder="Masukkan nomor whatsapp"
|
||||
placeholder="Masukkan whatsapp"
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,8 @@ import {
|
|||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
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>[] = [
|
||||
{
|
||||
|
|
@ -61,47 +58,10 @@ const columns: ColumnDef<any>[] = [
|
|||
|
||||
{
|
||||
id: "actions",
|
||||
accessorKey: "action",
|
||||
header: "Actions",
|
||||
cell: ({ row, onDeleteSuccess }: any) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -113,113 +73,28 @@ const columns: ColumnDef<any>[] = [
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<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">
|
||||
<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>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<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>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<a>Delete</a>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</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;
|
||||
|
|
|
|||
|
|
@ -89,11 +89,6 @@ const CampaignListTable = () => {
|
|||
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
|
||||
function handleDeleteSuccess(id: number) {
|
||||
setDataTable((prev) => prev.filter((item) => item.id !== id));
|
||||
}
|
||||
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
|
|
@ -148,16 +143,16 @@ const CampaignListTable = () => {
|
|||
}
|
||||
|
||||
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">
|
||||
<p className="text-xl font-medium text-default-900">Daftar Campaign</p>
|
||||
<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">
|
||||
<UserIcon />
|
||||
Daftar Akun
|
||||
</Button>
|
||||
</Link> */}
|
||||
</Link>
|
||||
<Link href="/admin/broadcast/campaign-list/create">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
<NewCampaignIcon size={23} />
|
||||
|
|
@ -194,14 +189,7 @@ const CampaignListTable = () => {
|
|||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{/* {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()
|
||||
},
|
||||
})}
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export default function CreateCampaign() {
|
|||
<Form {...form}>
|
||||
<form
|
||||
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>
|
||||
<FormField
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"use client";
|
||||
'use client'
|
||||
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
|
@ -35,7 +35,7 @@ import { Button } from "@/components/ui/button";
|
|||
import TablePagination from "@/components/table/table-pagination";
|
||||
import {
|
||||
getMediaBlastCampaignById,
|
||||
getMediaBlastBroadcastList,
|
||||
getMediaBlastBroadcastList
|
||||
} from "@/service/broadcast/broadcast";
|
||||
|
||||
// Types
|
||||
|
|
@ -69,29 +69,24 @@ interface PageProps {
|
|||
};
|
||||
}
|
||||
|
||||
export default function BroadcastCampaignDetail({
|
||||
params,
|
||||
searchParams,
|
||||
}: PageProps) {
|
||||
export default function BroadcastCampaignDetail({ params, searchParams }: PageProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { id, locale } = params;
|
||||
const [getData, setGetData] = useState<CampaignData[]>([]);
|
||||
const [totalPage, setTotalPage] = useState<number>(0);
|
||||
const [totalData, setTotalData] = useState<number>(0);
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
"sent" | "schedule" | "account-list"
|
||||
>("sent");
|
||||
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 [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>([]);
|
||||
|
|
@ -101,6 +96,7 @@ export default function BroadcastCampaignDetail({
|
|||
pageIndex: 0,
|
||||
pageSize: parseInt(size || "10"),
|
||||
});
|
||||
|
||||
const pages = page ? parseInt(page) - 1 : 0;
|
||||
const currentPage = page ? parseInt(page) : 1;
|
||||
const pageSize = parseInt(size || "10");
|
||||
|
|
@ -111,7 +107,7 @@ export default function BroadcastCampaignDetail({
|
|||
|
||||
const setCurrentPage = (pageNumber: number) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("page", pageNumber.toString());
|
||||
params.set('page', pageNumber.toString());
|
||||
router.push(`${pathname}?${params.toString()}`);
|
||||
};
|
||||
|
||||
|
|
@ -142,14 +138,7 @@ export default function BroadcastCampaignDetail({
|
|||
|
||||
useEffect(() => {
|
||||
getListPaginationData();
|
||||
}, [
|
||||
currentPage,
|
||||
pageSize,
|
||||
activeTab,
|
||||
endDateString,
|
||||
startDateString,
|
||||
typeFilter,
|
||||
]);
|
||||
}, [currentPage, pageSize, activeTab, endDateString, startDateString, typeFilter]);
|
||||
|
||||
function setupData(rawData: PaginatedResponse) {
|
||||
console.log("raw", rawData);
|
||||
|
|
@ -178,13 +167,8 @@ export default function BroadcastCampaignDetail({
|
|||
accessorKey: "mediaBlastCampaign.title",
|
||||
header: "Campaign",
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
href={`/${locale}/admin/broadcast/campaign-list/detail/${row.original.mediaBlastCampaignId}`}
|
||||
className="text-dark"
|
||||
>
|
||||
<span className="font-weight-bold">
|
||||
{row.original.mediaBlastCampaign?.title}
|
||||
</span>
|
||||
<Link href={`/${locale}/admin/broadcast/campaign-list/detail/${row.original.mediaBlastCampaignId}`} className="text-dark">
|
||||
<span className="font-weight-bold">{row.original.mediaBlastCampaign?.title}</span>
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
|
|
@ -192,10 +176,7 @@ export default function BroadcastCampaignDetail({
|
|||
accessorKey: "subject",
|
||||
header: "Judul",
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
href={`/${locale}/admin/broadcast/content/detail/${row.original.id}`}
|
||||
className="text-dark"
|
||||
>
|
||||
<Link href={`/${locale}/admin/broadcast/content/detail/${row.original.id}`} className="text-dark">
|
||||
<span className="font-weight-bold">{row.getValue("subject")}</span>
|
||||
</Link>
|
||||
),
|
||||
|
|
@ -204,23 +185,29 @@ export default function BroadcastCampaignDetail({
|
|||
accessorKey: "type",
|
||||
header: "Tipe",
|
||||
cell: ({ row }) => (
|
||||
<div className="text-right text-black">{row.getValue("type")}</div>
|
||||
),
|
||||
<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>
|
||||
),
|
||||
<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="text-black">
|
||||
{row.getValue("sendDate")}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
@ -251,7 +238,7 @@ export default function BroadcastCampaignDetail({
|
|||
setEndDateString(getOnlyDate(endDate));
|
||||
}
|
||||
}
|
||||
console.log("date range", dateRange);
|
||||
console.log('date range', dateRange);
|
||||
initState();
|
||||
}, [calenderState, startDate, endDate]);
|
||||
|
||||
|
|
@ -260,7 +247,7 @@ export default function BroadcastCampaignDetail({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-black container-fluid rounded ">
|
||||
<div className="bg-white container-fluid rounded rounded-xl">
|
||||
<div className="mt-1 p-4">
|
||||
<div className="flex flex-row gap-1 border-2 rounded-md w-fit mb-4">
|
||||
<Button
|
||||
|
|
@ -311,7 +298,7 @@ export default function BroadcastCampaignDetail({
|
|||
? "bg-black text-white "
|
||||
: "bg-white text-black "
|
||||
}`}
|
||||
size="sm"
|
||||
size='sm'
|
||||
>
|
||||
Email Blast
|
||||
</Button>
|
||||
|
|
@ -322,7 +309,7 @@ export default function BroadcastCampaignDetail({
|
|||
? "bg-black text-white "
|
||||
: "bg-white text-black "
|
||||
}`}
|
||||
size="sm"
|
||||
size='sm'
|
||||
>
|
||||
WhatsApp Blast
|
||||
</Button>
|
||||
|
|
@ -372,20 +359,14 @@ export default function BroadcastCampaignDetail({
|
|||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ export default function EditCampaign() {
|
|||
<Form {...form}>
|
||||
<form
|
||||
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>
|
||||
<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 (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<ContentBlast />
|
||||
<ContentBlast type="email" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -85,21 +85,23 @@ const columns: ColumnDef<any>[] = [
|
|||
</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 cursor-pointer">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||
>
|
||||
Detail
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<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 cursor-pointer">
|
||||
Email & Whatsapp Blast
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link href={`/admin/broadcast/email/${row.original.id}`}>
|
||||
Email Blast
|
||||
</Link>
|
||||
{/* <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 cursor-pointer">
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link href={`/admin/broadcast/whatsapp/${row.original.id}`}>
|
||||
Whatsapp Blast
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</Link> */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
@ -204,7 +204,7 @@ const BroadcastEmailTable = () => {
|
|||
};
|
||||
|
||||
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 ">
|
||||
<Link href="/admin/broadcast/campaign-list" className="mr-3">
|
||||
<Button color="primary" size="md" className="text-sm">
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import BroadcastTable from "./create/component/table";
|
||||
import BroadcastTable from "./email/component/table";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
|
||||
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 { Link } from "@/i18n/routing";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import BroadcastEmailTable from "./create/component/table";
|
||||
import BroadcastEmailTable from "./email/component/table";
|
||||
import BroadcastWhatsAppTable from "./whatsapp/component/table";
|
||||
|
||||
export default function AdminBroadcast() {
|
||||
|
|
@ -16,7 +16,7 @@ export default function AdminBroadcast() {
|
|||
return (
|
||||
<div>
|
||||
<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 === "WhatsApp Blast" && <BroadcastWhatsAppTable />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default function CreateWABlast() {
|
|||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
{/* <ContentBlast /> */}
|
||||
<ContentBlast type="wa" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,21 +80,23 @@ const columns: ColumnDef<any>[] = [
|
|||
</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 cursor-pointer">
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<Link
|
||||
href={`/contributor/content/image/detail/${row.original.id}`}
|
||||
>
|
||||
Detail
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<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 cursor-pointer">
|
||||
Email Blast
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="p-2 border-b text-default-700 group focus:bg-default focus:text-primary-foreground rounded-none">
|
||||
<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 cursor-pointer">
|
||||
Whatsapp Blast
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ export default function EditUserForm() {
|
|||
},
|
||||
});
|
||||
|
||||
const passwordVal = form.watch("password");
|
||||
const confPasswordVal = form.watch("confirmPassword");
|
||||
|
||||
useEffect(() => {
|
||||
initData();
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -353,8 +353,8 @@ export default function EditUserForm() {
|
|||
<PasswordChecklist
|
||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||
minLength={8}
|
||||
value={passwordVal || ""}
|
||||
valueAgain={confPasswordVal || ""}
|
||||
value={passwordVal}
|
||||
valueAgain={confPasswordVal}
|
||||
onChange={(isValid) => {
|
||||
form.setValue("isValidPassword", isValid);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||
import { Check, ChevronsUpDown, Eye, EyeOff } from "lucide-react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
|
|
@ -289,17 +290,7 @@ export default function CreateUserForm() {
|
|||
};
|
||||
|
||||
if (data.role == "OPT-ID") {
|
||||
// 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;
|
||||
}
|
||||
req.handledSocialMedia = data?.sns ? data.sns.join(",") : "";
|
||||
}
|
||||
|
||||
if (data.role == "KUR-ID") {
|
||||
|
|
@ -352,7 +343,7 @@ export default function CreateUserForm() {
|
|||
<Form {...form}>
|
||||
<form
|
||||
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>
|
||||
<FormField
|
||||
|
|
@ -760,8 +751,8 @@ export default function CreateUserForm() {
|
|||
<PasswordChecklist
|
||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||
minLength={8}
|
||||
value={passwordVal || ""}
|
||||
valueAgain={confPasswordVal || ""}
|
||||
value={passwordVal}
|
||||
valueAgain={confPasswordVal}
|
||||
onChange={(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");
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ export default function EditUserForm() {
|
|||
form.setValue("level", String(res?.userLevelId));
|
||||
} else {
|
||||
initFetch();
|
||||
console.log("sadad", res?.role?.code);
|
||||
form.setValue("fullname", res?.fullname);
|
||||
form.setValue("username", res?.username);
|
||||
form.setValue("phoneNumber", res?.phoneNumber);
|
||||
|
|
@ -755,8 +756,8 @@ export default function EditUserForm() {
|
|||
<PasswordChecklist
|
||||
rules={["minLength", "specialChar", "number", "capital", "match"]}
|
||||
minLength={8}
|
||||
value={passwordVal || ""}
|
||||
valueAgain={confPasswordVal || ""}
|
||||
value={passwordVal}
|
||||
valueAgain={confPasswordVal}
|
||||
onChange={(isValid) => {
|
||||
form.setValue("isValidPassword", isValid);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default function ManagementUser() {
|
|||
<ManagementUserVisualization />
|
||||
</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">
|
||||
<p className="text-lg">
|
||||
Data User {isInternal ? "Internal" : "Eksternal"}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import {
|
|||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
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>[] = [
|
||||
{
|
||||
|
|
@ -54,132 +52,12 @@ const columns: ColumnDef<any>[] = [
|
|||
<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",
|
||||
header: "Link Berita",
|
||||
cell: ({ row }) => {
|
||||
const link = row.getValue<string>("link");
|
||||
|
||||
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>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<span className="normal-case">{row.getValue("link")}</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -116,18 +116,6 @@ const NewsDetailTable = () => {
|
|||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onPaginationChange: setPagination,
|
||||
meta: {
|
||||
updateData: (rowIndex: number, value: Partial<any>) => {
|
||||
setDataTable((old) =>
|
||||
old.map((row, index) =>
|
||||
index === rowIndex ? { ...row, ...value } : row
|
||||
)
|
||||
);
|
||||
},
|
||||
refetchData: () => {
|
||||
fetchData();
|
||||
},
|
||||
},
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
|
|
@ -166,7 +154,7 @@ const NewsDetailTable = () => {
|
|||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
}, [page, showData, id]);
|
||||
}, [page, showData]);
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { exportMediaTrackingToExcel } from "@/utils/export-media-tracking";
|
||||
import { loading, close } from "@/config/swal";
|
||||
import { error } from "@/lib/swal";
|
||||
import {
|
||||
DownloadIcon,
|
||||
Eye,
|
||||
MoreVertical,
|
||||
SquarePen,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -53,101 +45,19 @@ const columns: ColumnDef<any>[] = [
|
|||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "resultTotal",
|
||||
header: () => <div className="text-center w-full">Total Artikel</div>,
|
||||
cell: ({ row }) => {
|
||||
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: "link",
|
||||
header: "Jumlah Amplifikasi",
|
||||
cell: ({ row }) => <span>{row.getValue("link")}</span>,
|
||||
},
|
||||
{
|
||||
accessorKey: "amplification",
|
||||
header: () => <div className="text-center w-full">Jumlah Amplifikasi</div>,
|
||||
cell: ({ row }) => {
|
||||
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: "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",
|
||||
header: () => <div className="text-center">Status</div>,
|
||||
cell: ({ row }) => {
|
||||
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>;
|
||||
},
|
||||
accessorKey: "date",
|
||||
header: "Tanggal Penarikan",
|
||||
cell: ({ row }) => <span>{row.getValue("date")}</span>,
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
|
|
@ -168,31 +78,13 @@ const columns: ColumnDef<any>[] = [
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<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" />
|
||||
View
|
||||
View
|
||||
{row.original.mediaUpload.fileType.secondaryName &&
|
||||
row.original.mediaUpload.fileType.secondaryName.toLowerCase()}
|
||||
</DropdownMenuItem>
|
||||
</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>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ const ResultTable = () => {
|
|||
};
|
||||
|
||||
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-row justify-end items-center gap-3">
|
||||
<div className="flex items-center">
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ import { PaginationState } from "@tanstack/react-table";
|
|||
import page from "../page";
|
||||
import CustomPagination from "@/components/table/custom-pagination";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
export default function TrackingBeritaCard() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
|
@ -32,7 +30,6 @@ export default function TrackingBeritaCard() {
|
|||
const [page, setPage] = useState(1);
|
||||
const [totalPage, setTotalPage] = useState(1);
|
||||
const [showData, setShowData] = useState("6");
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
useEffect(() => {
|
||||
initFecth();
|
||||
|
|
@ -40,7 +37,7 @@ export default function TrackingBeritaCard() {
|
|||
|
||||
const initFecth = async () => {
|
||||
loading();
|
||||
const response = await listDataTracking(Number(showData), page - 1, search);
|
||||
const response = await listDataTracking(showData, page - 1);
|
||||
const data = response?.data?.data;
|
||||
const newData = data?.content;
|
||||
setTotalPage(data?.totalPages || 1);
|
||||
|
|
@ -56,25 +53,17 @@ export default function TrackingBeritaCard() {
|
|||
setContent(response?.data?.data?.content || []);
|
||||
};
|
||||
|
||||
const handleInputChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setSearch(value);
|
||||
|
||||
const response = await listDataTracking(Number(showData), 0, value);
|
||||
setContent(response?.data?.data?.content || []);
|
||||
if (value.trim() === "") {
|
||||
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) => {
|
||||
setSelectedItems((prev) =>
|
||||
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
||||
|
|
@ -83,143 +72,30 @@ export default function TrackingBeritaCard() {
|
|||
|
||||
const doSave = async () => {
|
||||
if (selectedItems.length === 0) {
|
||||
MySwal.fire(
|
||||
"Peringatan",
|
||||
"Pilih minimal 1 berita untuk disimpan.",
|
||||
"warning"
|
||||
);
|
||||
toast("Pilih minimal 1 berita untuk disimpan.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loading();
|
||||
|
||||
const promises = selectedItems.map(async (id) => {
|
||||
const res = await mediaTrackingSave({
|
||||
const promises = selectedItems.map((id) =>
|
||||
mediaTrackingSave({
|
||||
mediaUploadId: id,
|
||||
duration: 24,
|
||||
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);
|
||||
close();
|
||||
|
||||
await MySwal.fire({
|
||||
icon: "success",
|
||||
title: "Berhasil!",
|
||||
text: "Tracking berita berhasil ditambahkan.",
|
||||
confirmButtonColor: "#2563eb",
|
||||
toast("Berhasil Menambahkan", {
|
||||
description: "",
|
||||
});
|
||||
|
||||
setSelectedItems([]);
|
||||
initFecth();
|
||||
} catch (err: any) {
|
||||
close();
|
||||
MySwal.fire({
|
||||
icon: "error",
|
||||
title: "Gagal!",
|
||||
text: err?.message || "Terjadi kesalahan saat menyimpan data.",
|
||||
confirmButtonColor: "#dc2626",
|
||||
});
|
||||
error(err?.message || "Gagal menyimpan data.");
|
||||
}
|
||||
};
|
||||
|
||||
// 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 (
|
||||
<Card>
|
||||
<div className="p-4 space-y-4">
|
||||
|
|
@ -265,7 +141,7 @@ export default function TrackingBeritaCard() {
|
|||
<div className="text-sm text-blue-600 font-medium">
|
||||
{selectedItems.length} Item Terpilih{" "}
|
||||
<span className="text-black">
|
||||
/ Tracking Berita tersisa {5 - selectedItems.length}
|
||||
/ Tracking Berita tersisa {29 - selectedItems.length}
|
||||
</span>
|
||||
</div>
|
||||
<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">
|
||||
{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.map((item: any) => (
|
||||
<Card
|
||||
|
|
@ -341,7 +175,7 @@ export default function TrackingBeritaCard() {
|
|||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="mt-3">
|
||||
{content && content?.length > 0 ? (
|
||||
<CustomPagination
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ const ContentListBanner = () => {
|
|||
checked={selectedItems.length === data.length}
|
||||
onCheckedChange={handleSelectAll}
|
||||
/>
|
||||
<span className="text-black dark:text-white">Pilih Semua</span>
|
||||
<span>Pilih Semua</span>
|
||||
</div>
|
||||
{selectedItems.length > 0 && (
|
||||
<Button color="primary" onClick={() => handleBanner(selectedItems)}>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function AdminBanner() {
|
|||
return (
|
||||
<div>
|
||||
<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">
|
||||
{selectedTab === "content" ? "List Media" : " List Banner"}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ const columns: ColumnDef<any>[] = [
|
|||
router.push("/admin/settings/category?dataChange=true");
|
||||
};
|
||||
return (
|
||||
<Menubar className="border-none dark:bg-black">
|
||||
<Menubar className="border-none">
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -125,16 +125,16 @@ export default function CreateCategoryModal() {
|
|||
});
|
||||
|
||||
const contentType = form.watch("contentType");
|
||||
const isAllContentChecked = contentType && listContent.every((item) =>
|
||||
contentType.includes(item.id)
|
||||
const isAllContentChecked = listContent.every((item) =>
|
||||
contentType?.includes(item.id)
|
||||
);
|
||||
|
||||
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 isAllTargetChecked = target && publishToList.every((item) =>
|
||||
target.includes(item.id)
|
||||
const isAllTargetChecked = publishToList.every((item) =>
|
||||
target?.includes(item.id)
|
||||
);
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
|
|
@ -253,7 +253,7 @@ export default function CreateCategoryModal() {
|
|||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-3 bg-white dark:bg-[#1f2937] rounded-sm"
|
||||
className="space-y-3 bg-white rounded-sm"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
|
|
|||
|
|
@ -138,34 +138,35 @@ export default function EditCategoryModal(props: {
|
|||
maxFiles: 1,
|
||||
});
|
||||
|
||||
// useEffect(() => {
|
||||
// initFetch();
|
||||
// }, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
const initFetch = async () => {
|
||||
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("contentType", data?.mediaTypes?.split(","));
|
||||
form.setValue(
|
||||
"selectedUser",
|
||||
removeAndReturn(data?.publishedFor, [2, 3, 4])
|
||||
);
|
||||
form.setValue("publishTo", data?.publishedLocation?.split(",") || []);
|
||||
form.setValue("publishTo", data?.publishedLocation?.split(","));
|
||||
form.setValue("file", thumbnailLink);
|
||||
|
||||
setUnitData(filterString(data?.publishedLocationLevel, "under"));
|
||||
setSatkerData(filterString(data?.publishedLocationLevel, "above"));
|
||||
};
|
||||
|
||||
initFetch();
|
||||
}, [id]);
|
||||
|
||||
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) {
|
||||
|
|
@ -182,16 +183,17 @@ export default function EditCategoryModal(props: {
|
|||
}
|
||||
|
||||
const contentType = form.watch("contentType");
|
||||
const isAllContentChecked =
|
||||
contentType && listContent.every((item) => contentType.includes(item.id));
|
||||
const isAllContentChecked = listContent.every((item) =>
|
||||
contentType?.includes(item.id)
|
||||
);
|
||||
|
||||
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 isAllTargetChecked =
|
||||
target && publishToList.every((item) => target.includes(item.id));
|
||||
const isAllTargetChecked = publishToList.every((item) =>
|
||||
target?.includes(item.id)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getRoles();
|
||||
|
|
@ -211,7 +213,6 @@ export default function EditCategoryModal(props: {
|
|||
const uniqueNumbers = Array.from(new Set(numbers));
|
||||
return uniqueNumbers.join(",");
|
||||
}
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
const formMedia = new FormData();
|
||||
|
||||
|
|
@ -262,9 +263,7 @@ export default function EditCategoryModal(props: {
|
|||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger>
|
||||
<a
|
||||
onClick={() =>{
|
||||
initFetch()
|
||||
setIsOpen(true); } }
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="hover:underline cursor-pointer"
|
||||
>
|
||||
{isDetail ? "Detail" : "Edit"}
|
||||
|
|
|
|||
|
|
@ -10,34 +10,15 @@ export default function StatusToogle(props: {
|
|||
}) {
|
||||
const { id, initValue } = props;
|
||||
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 response = await publishUnpublishCategory(id, status);
|
||||
console.log("API Response:", response);
|
||||
|
||||
// cek error interceptor
|
||||
console.log(response);
|
||||
if (response?.error) {
|
||||
error(response.message || "Terjadi kesalahan");
|
||||
error(response.message);
|
||||
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");
|
||||
};
|
||||
|
||||
return (
|
||||
<Switch
|
||||
id={String(id)}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,21 @@ import {
|
|||
import { useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { NewCampaignIcon } from "@/components/icon";
|
||||
import { getCategories } from "@/service/settings/settings";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import CreateCategoryModal from "./create";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
|
|
@ -175,7 +186,7 @@ const AdminCategoryTable = () => {
|
|||
};
|
||||
|
||||
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">
|
||||
<p className="text-xl font-medium text-default-900">{t("category", { defaultValue: "Category" })}</p>
|
||||
<CreateCategoryModal />
|
||||
|
|
|
|||
|
|
@ -68,11 +68,11 @@ export function UnitMapping(props: {
|
|||
|
||||
const unitType = form.watch("items");
|
||||
|
||||
const isAllUnitChecked = unitType && unitList.every((item) =>
|
||||
unitType.includes(String(item.id))
|
||||
const isAllUnitChecked = unitList.every((item) =>
|
||||
unitType?.includes(String(item.id))
|
||||
);
|
||||
const isAllSatkerChecked = unitType && satkerList.every((item) =>
|
||||
unitType.includes(String(item.id))
|
||||
const isAllSatkerChecked = satkerList.every((item) =>
|
||||
unitType?.includes(String(item.id))
|
||||
);
|
||||
|
||||
const setupUnit = (data: UnitType[]) => {
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ export default function CreateFAQModal() {
|
|||
});
|
||||
|
||||
const target = form.watch("publishTo");
|
||||
const isAllTargetChecked = target && publishToList.every((item) =>
|
||||
target.includes(item.id)
|
||||
const isAllTargetChecked = publishToList.every((item) =>
|
||||
target?.includes(item.id)
|
||||
);
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -78,7 +78,7 @@ const columns: ColumnDef<any>[] = [
|
|||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
|
||||
async function doDelete(id: any) {
|
||||
// loading();
|
||||
const data = {
|
||||
|
|
@ -132,27 +132,6 @@ const columns: ColumnDef<any>[] = [
|
|||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
{/* <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>
|
||||
<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 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">
|
||||
|
|
@ -160,16 +139,12 @@ const columns: ColumnDef<any>[] = [
|
|||
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"
|
||||
|
|
@ -177,8 +152,6 @@ const columns: ColumnDef<any>[] = [
|
|||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import {
|
|||
UploadIcon,
|
||||
UserIcon,
|
||||
} from "lucide-react";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -53,7 +53,7 @@ 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 { useSearchParams } from "next/navigation";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting";
|
||||
|
|
@ -69,7 +69,7 @@ import {
|
|||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
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";
|
||||
|
||||
const AdvertisementsList = () => {
|
||||
|
|
@ -94,9 +94,6 @@ const AdvertisementsList = () => {
|
|||
const [statusFilter, setStatusFilter] = React.useState<number[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const table = useReactTable({
|
||||
data: dataTable,
|
||||
columns,
|
||||
|
|
@ -153,7 +150,11 @@ const AdvertisementsList = () => {
|
|||
async function fetchData() {
|
||||
try {
|
||||
loading();
|
||||
const res = await listDataAdvertisements(page - 1, showData, "");
|
||||
const res = await listDataAdvertisements(
|
||||
page - 1,
|
||||
showData,
|
||||
"",
|
||||
);
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.content;
|
||||
contentData.forEach((item: any, index: number) => {
|
||||
|
|
@ -206,27 +207,14 @@ const AdvertisementsList = () => {
|
|||
return (
|
||||
<>
|
||||
<div>
|
||||
{levelNumber === "1" && (
|
||||
<div className="flex-none">
|
||||
<Link href={"/admin/settings/iklan/create"}>
|
||||
<Button
|
||||
disabled={dataTable.length == 4}
|
||||
color="primary"
|
||||
className="text-white"
|
||||
size="md"
|
||||
>
|
||||
<Button color="primary" className="text-white" size="md">
|
||||
<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 /> */}
|
||||
</div>
|
||||
<div className="flex justify-between ">
|
||||
|
|
|
|||
|
|
@ -27,10 +27,22 @@ import {
|
|||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import columns from "./column";
|
||||
|
||||
import { listEnableCategory } from "@/service/content/content";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { close, loading } from "@/config/swal";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { NewCampaignIcon } from "@/components/icon";
|
||||
import { getCategories } from "@/service/settings/settings";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -176,7 +188,7 @@ const AdminSettingTrackingTable = () => {
|
|||
};
|
||||
|
||||
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">
|
||||
{/* <CreateSettingTracking /> */}
|
||||
<div className="flex-none">
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ const SurveyListTable = () => {
|
|||
};
|
||||
|
||||
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 flex-row gap-2 items-center justify-between">
|
||||
<div className="w-full md:w-[200px] lg:w-[300px] px-2">
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const [selectedCategory, setSelectedCategory] = useState<string[]>([]);
|
||||
const [selectedEventDate, setSelectedEventDate] = useState<Date | null>(null);
|
||||
const roleId = Number(getCookiesDecrypt("urie")) || 0;
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne")) || 0;
|
||||
console.log("DUARR", roleId)
|
||||
const userLevelId = Number(getCookiesDecrypt("ulie")) || 0;
|
||||
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
|
@ -303,7 +303,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const firstType = typeSplit[0] as EventType;
|
||||
|
||||
const colors: Record<EventType, string> = {
|
||||
"0": "bg-gray-500",
|
||||
"0": "bg-black",
|
||||
"1": "bg-yellow-500",
|
||||
"2": "bg-blue-400",
|
||||
"3": "bg-slate-400",
|
||||
|
|
@ -408,16 +408,14 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
const hasMoreEvents = events.length > 3;
|
||||
|
||||
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">
|
||||
<h4 className="font-bold text-center">{label}</h4>
|
||||
</div>
|
||||
<div className="px-2">
|
||||
{events.length === 0 ? (
|
||||
<div className="mt-1 py-2 rounded-lg bg-white dark:bg-black border border-black dark:border-gray-500">
|
||||
<p className="text-center">
|
||||
{t("no-data-yet", { defaultValue: "No Data Yet" })}
|
||||
</p>
|
||||
<div className="mt-1 py-2 rounded-lg bg-white border border-black">
|
||||
<p className="text-center">{t("no-data-yet", { defaultValue: "No Data Yet" })}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -529,16 +527,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
<Card className="col-span-12 lg:col-span-4 2xl:col-span-3 pb-5">
|
||||
<CardContent className="p-0">
|
||||
<CardHeader className="border-none mb-2 pt-5">
|
||||
{/* {[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 && (
|
||||
{[3, 11, 2, 12].includes(roleId) && (
|
||||
<Button
|
||||
onClick={handleDateClick}
|
||||
className="dark:bg-background dark:text-foreground w-full"
|
||||
|
|
@ -553,18 +542,12 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
|
|||
<DialogTrigger asChild>
|
||||
<Button className="dark:bg-background dark:text-foreground w-full">
|
||||
<Book size={15} className="w-4 h-4 mr-3" />
|
||||
{t("bag-pa-monitoring-results", {
|
||||
defaultValue: "Bag Pa Monitoring Results",
|
||||
})}
|
||||
{t("bag-pa-monitoring-results", { defaultValue: "Bag Pa Monitoring Results" })}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px] overflow-y-auto max-h-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t("monitoring-results", {
|
||||
defaultValue: "Monitoring Results",
|
||||
})}
|
||||
</DialogTitle>
|
||||
<DialogTitle>{t("monitoring-results", { defaultValue: "Monitoring Results" })}</DialogTitle>
|
||||
</DialogHeader>
|
||||
{getModalContent()}
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -108,10 +108,8 @@ const EventModal = ({
|
|||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
|
||||
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
|
||||
{}
|
||||
);
|
||||
const [checkedLevels, setCheckedLevels] = useState(new Set());
|
||||
const [expandedPolda, setExpandedPolda] = useState([{}]);
|
||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [timer, setTimer] = useState<number>(120);
|
||||
|
|
@ -154,17 +152,7 @@ const EventModal = ({
|
|||
international: false,
|
||||
});
|
||||
|
||||
// State untuk melacak apakah perubahan berasal dari checkbox Jenis Agenda
|
||||
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 [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType
|
||||
const [selectedPolda, setSelectedPolda] = useState<string[]>([]);
|
||||
const [selectedPolres, setSelectedPolres] = useState<string[]>([]);
|
||||
const [selectedSatker, setSelectedSatker] = useState<string[]>([]);
|
||||
|
|
@ -263,272 +251,15 @@ const EventModal = ({
|
|||
fetchDetailData();
|
||||
}, [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(() => {
|
||||
setIsDatePickerOpen(false);
|
||||
}, [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();
|
||||
}, []);
|
||||
}, [onClose])
|
||||
|
||||
const handleCheckboxChange = (levelId: number) => {
|
||||
setCheckedLevels((prev) => {
|
||||
const updatedLevels = new Set(prev);
|
||||
const isCurrentlyChecked = updatedLevels.has(levelId);
|
||||
|
||||
if (isCurrentlyChecked) {
|
||||
if (updatedLevels.has(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 {
|
||||
updatedLevels.add(levelId);
|
||||
}
|
||||
|
|
@ -546,15 +277,6 @@ const EventModal = ({
|
|||
};
|
||||
|
||||
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) => {
|
||||
let newState = { ...prev };
|
||||
if (key === "semua") {
|
||||
|
|
@ -570,106 +292,15 @@ const EventModal = ({
|
|||
|
||||
if (newChecked) {
|
||||
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 {
|
||||
setAgendaType("");
|
||||
// Clear semua pilihan modal ketika "semua" di-unchecklist
|
||||
setCheckedLevels(new Set());
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
// 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)
|
||||
.filter(([k, v]) => v && k !== "semua")
|
||||
|
|
@ -762,7 +393,13 @@ const EventModal = ({
|
|||
setIsAudioUploadFinish(true);
|
||||
}
|
||||
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) {
|
||||
setIsPublishing(true);
|
||||
|
|
@ -894,6 +531,7 @@ const EventModal = ({
|
|||
|
||||
const resCsrf = await getCsrfToken();
|
||||
const csrfToken = resCsrf?.data?.token;
|
||||
console.log("CSRF TOKEN : ", csrfToken);
|
||||
const headers = {
|
||||
"X-XSRF-TOKEN": csrfToken,
|
||||
};
|
||||
|
|
@ -1104,10 +742,7 @@ const EventModal = ({
|
|||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="date">Tanggal</Label>
|
||||
<Popover
|
||||
open={isDatePickerOpen}
|
||||
onOpenChange={() => setIsDatePickerOpen(true)}
|
||||
>
|
||||
<Popover open={isDatePickerOpen} onOpenChange={() => setIsDatePickerOpen(true)}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -1164,19 +799,13 @@ const EventModal = ({
|
|||
Semua
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{levelNumber === 1 && (
|
||||
<>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="nasional"
|
||||
checked={wilayahPublish.nasional}
|
||||
onCheckedChange={() => toggleWilayah("nasional")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="nasional"
|
||||
className="ml-2 text-sm mr-2"
|
||||
>
|
||||
<label htmlFor="nasional" className="ml-2 text-sm mr-2">
|
||||
Nasional
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -1189,11 +818,17 @@ const EventModal = ({
|
|||
<label htmlFor="polda" className="mx-2 text-sm mr-2">
|
||||
Polda
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
{wilayahPublish.polda && (
|
||||
<UnitMapping
|
||||
unit="Polda"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolda}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolda(data)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{(levelNumber === 1 || levelNumber === 2) && (
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="polres"
|
||||
|
|
@ -1203,11 +838,17 @@ const EventModal = ({
|
|||
<label htmlFor="polres" className="ml-2 text-sm mr-2">
|
||||
Polres
|
||||
</label>
|
||||
</div>
|
||||
{wilayahPublish.polres && (
|
||||
<UnitMapping
|
||||
unit="Polres"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedPolres}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedPolres(data)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{levelNumber === 1 && (
|
||||
<>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="satker"
|
||||
|
|
@ -1217,14 +858,22 @@ const EventModal = ({
|
|||
<label htmlFor="satker" className="mx-2 text-sm mr-2">
|
||||
Satker
|
||||
</label>
|
||||
{wilayahPublish.satker && (
|
||||
<UnitMapping
|
||||
unit="Satker"
|
||||
isDetail={isDetailMode}
|
||||
initData={selectedSatker}
|
||||
sendDataToParent={(data: any) =>
|
||||
setSelectedSatker(data)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
id="international"
|
||||
checked={wilayahPublish.international}
|
||||
onCheckedChange={() =>
|
||||
toggleWilayah("international")
|
||||
}
|
||||
onCheckedChange={() => toggleWilayah("international")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="international"
|
||||
|
|
@ -1233,112 +882,6 @@ const EventModal = ({
|
|||
Internasional
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="pl-1">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="soft" size="sm" color="primary">
|
||||
[Kustom]
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px] md:max-w-[500px] lg:max-w-[1500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Daftar Wilayah Polda dan Polres
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid grid-cols-2 gap-2 max-h-[400px] overflow-y-auto">
|
||||
{listDest?.map((polda: any) => (
|
||||
<div key={polda.id} className="border p-2">
|
||||
<Label className="flex items-center">
|
||||
<Checkbox
|
||||
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>
|
||||
|
||||
|
|
@ -1371,7 +914,8 @@ const EventModal = ({
|
|||
<Label>Video</Label>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"video/*": [],
|
||||
"mp4/*": [],
|
||||
"mov/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp4 atau .mov."
|
||||
|
|
@ -1383,7 +927,7 @@ const EventModal = ({
|
|||
className="object-fill h-full w-full rounded-md"
|
||||
src={file.url}
|
||||
controls
|
||||
title={`Video ${file.id}`}
|
||||
title={`Video ${file.id}`} // Mengganti alt dengan title
|
||||
/>
|
||||
<div
|
||||
key={index}
|
||||
|
|
@ -1462,9 +1006,10 @@ const EventModal = ({
|
|||
</div>
|
||||
<div>
|
||||
<Label>Teks</Label>
|
||||
|
||||
<FileUploader
|
||||
accept={{
|
||||
"application/pdf": [],
|
||||
"pdf/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .pdf."
|
||||
|
|
@ -1520,7 +1065,8 @@ const EventModal = ({
|
|||
/>
|
||||
<FileUploader
|
||||
accept={{
|
||||
"audio/*": [],
|
||||
"mp3/*": [],
|
||||
"wav/*": [],
|
||||
}}
|
||||
maxSize={100}
|
||||
label="Upload file dengan format .mp3 atau .wav."
|
||||
|
|
|
|||
|
|
@ -65,66 +65,38 @@ export function UnitMapping(props: {
|
|||
useEffect(() => {
|
||||
async function initState() {
|
||||
const response = await getUserLevelForAssignments();
|
||||
const list: UnitType[] = response?.data?.data?.list ?? [];
|
||||
setupUnit(list);
|
||||
// console.log("list", list);
|
||||
setupUnit(response?.data?.data.list);
|
||||
console.log("list", response?.data?.data.list);
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
// Selalu punya array
|
||||
const unitType = form.watch("items") ?? [];
|
||||
const unitType = form.watch("items");
|
||||
|
||||
// ---- FIX: case-insensitive + flatMap semua node ----
|
||||
const setupUnit = (data: UnitType[] = []) => {
|
||||
const norm = (v?: string) => (v ?? "").toUpperCase();
|
||||
const isAllUnitChecked = unitList.every((item) =>
|
||||
unitType?.includes(String(item.id))
|
||||
);
|
||||
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 satkerNodes = data.filter((a) => norm(a.name).includes("SATKER"));
|
||||
|
||||
// POLDA level (UnitType)
|
||||
setUnitList(poldaNodes);
|
||||
|
||||
// SATKER adalah gabungan semua subDestination dari node SATKER
|
||||
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);
|
||||
const setupUnit = (data: UnitType[]) => {
|
||||
const temp = data.filter((a) => a.name.includes("POLDA"));
|
||||
const temp2 = data.filter((a) => a.name.includes("SATKER"));
|
||||
const temp3 = temp.flatMap((item) => item.subDestination || []);
|
||||
setUnitList(temp);
|
||||
setSatkerList(temp2[0]?.subDestination || []);
|
||||
setPolresList(temp3);
|
||||
};
|
||||
|
||||
// ---- 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(() => {
|
||||
sendDataToParent(form.getValues("items"));
|
||||
}, [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 (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
|
|
@ -144,48 +116,41 @@ export function UnitMapping(props: {
|
|||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id={`all-${unit}`}
|
||||
checked={allCheckedByUnit}
|
||||
checked={
|
||||
unit === "Polda"
|
||||
? isAllUnitChecked
|
||||
: unit === "Satker"
|
||||
? isAllSatkerChecked
|
||||
: isAllPolresChecked
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
const ids = currentIds();
|
||||
if (checked) {
|
||||
// gabungkan supaya kategori lain tidak hilang
|
||||
const merged = Array.from(new Set([...unitType, ...ids]));
|
||||
form.setValue("items", merged, {
|
||||
shouldDirty: true,
|
||||
shouldTouch: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
form.setValue(
|
||||
"items",
|
||||
unit === "Polda"
|
||||
? unitList.map((item) => String(item.id))
|
||||
: unit === "Satker"
|
||||
? satkerList.map((item) => String(item.id))
|
||||
: polresList.map((item) => String(item.id))
|
||||
);
|
||||
} else {
|
||||
// hapus hanya id kategori aktif
|
||||
const filtered = unitType.filter((v) => !ids.includes(v));
|
||||
form.setValue("items", filtered, {
|
||||
shouldDirty: true,
|
||||
shouldTouch: true,
|
||||
shouldValidate: true,
|
||||
});
|
||||
form.setValue("items", []);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`all-${unit}`}
|
||||
className="text-sm text-black uppercase"
|
||||
>
|
||||
|
||||
<label htmlFor="all" className="text-sm text-black uppercase">
|
||||
SEMUA {unit}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="items"
|
||||
render={() => (
|
||||
<FormItem
|
||||
className={`grid ${
|
||||
unit === "Polda"
|
||||
? "grid-cols-2"
|
||||
: unit === "Satker"
|
||||
? "grid-cols-3"
|
||||
: "grid-cols-4"
|
||||
} gap-2`}
|
||||
className={`grid grid-cols-${
|
||||
unit === "Polda" ? "2" : unit === "Satker" ? "3" : "4"
|
||||
}`}
|
||||
>
|
||||
{(unit === "Polda"
|
||||
? unitList
|
||||
|
|
@ -197,46 +162,33 @@ export function UnitMapping(props: {
|
|||
key={item.id}
|
||||
control={form.control}
|
||||
name="items"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem
|
||||
key={String(item.id)}
|
||||
className="flex flex-row items-center space-x-3 space-y-0"
|
||||
>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value?.includes(String(item.id))}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
form.setValue(
|
||||
"items",
|
||||
Array.from(
|
||||
new Set([
|
||||
...(field.value ?? []),
|
||||
return checked
|
||||
? field.onChange([
|
||||
...field.value,
|
||||
String(item.id),
|
||||
])
|
||||
),
|
||||
{
|
||||
shouldDirty: true,
|
||||
shouldTouch: true,
|
||||
shouldValidate: true,
|
||||
}
|
||||
: field.onChange(
|
||||
field.value?.filter(
|
||||
(value) => value !== String(item.id)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
form.setValue(
|
||||
"items",
|
||||
(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 />
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import { deleteMedia } from "@/service/content/content";
|
|||
import { error } from "@/lib/swal";
|
||||
import Swal from "sweetalert2";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
|
|
@ -182,11 +181,14 @@ const useTableColumns = () => {
|
|||
header: t("action", { defaultValue: "Action" }),
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const router = useRouter();
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
async function doDelete(id: any) {
|
||||
const data = { id };
|
||||
// loading();
|
||||
const data = {
|
||||
id,
|
||||
};
|
||||
|
||||
const response = await deleteMedia(data);
|
||||
|
||||
if (response?.error) {
|
||||
|
|
@ -228,21 +230,16 @@ const useTableColumns = () => {
|
|||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (userLevelId !== undefined && roleId !== undefined) {
|
||||
setIsMabesApprover(
|
||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
||||
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||
);
|
||||
}
|
||||
}, [userLevelId, roleId]);
|
||||
|
||||
const canEdit =
|
||||
Number(row.original.uploadedById) === Number(userId) ||
|
||||
isMabesApprover ||
|
||||
roleId === 14;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -263,8 +260,16 @@ const useTableColumns = () => {
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
{canEdit && (
|
||||
{/* <Link
|
||||
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
|
||||
href={`/contributor/content/audio/update/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -274,7 +279,6 @@ const useTableColumns = () => {
|
|||
</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"
|
||||
|
|
@ -282,6 +286,15 @@ const useTableColumns = () => {
|
|||
<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>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -132,36 +132,29 @@ const useTableColumns = () => {
|
|||
accessorKey: "statusName",
|
||||
header: "Status",
|
||||
cell: ({ row }) => {
|
||||
const statusId = Number(row.original?.statusId);
|
||||
const reviewedAtLevel = row.original?.reviewedAtLevel || "";
|
||||
const creatorGroupLevelId = Number(row.original?.creatorGroupLevelId);
|
||||
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 statusColors: Record<string, string> = {
|
||||
diterima: "bg-green-100 text-green-600",
|
||||
"menunggu review": "bg-orange-100 text-orange-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 (
|
||||
<Badge
|
||||
|
|
@ -170,7 +163,18 @@ const useTableColumns = () => {
|
|||
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>
|
||||
);
|
||||
},
|
||||
|
|
@ -186,7 +190,11 @@ const useTableColumns = () => {
|
|||
const MySwal = withReactContent(Swal);
|
||||
|
||||
async function doDelete(id: any) {
|
||||
const data = { id };
|
||||
// loading();
|
||||
const data = {
|
||||
id,
|
||||
};
|
||||
|
||||
const response = await deleteMedia(data);
|
||||
|
||||
if (response?.error) {
|
||||
|
|
@ -228,21 +236,16 @@ const useTableColumns = () => {
|
|||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (userLevelId !== undefined && roleId !== undefined) {
|
||||
setIsMabesApprover(
|
||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
||||
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||
);
|
||||
}
|
||||
}, [userLevelId, roleId]);
|
||||
|
||||
const canEdit =
|
||||
Number(row.original.uploadedById) === Number(userId) ||
|
||||
isMabesApprover ||
|
||||
roleId === 14;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -263,8 +266,16 @@ const useTableColumns = () => {
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
{canEdit && (
|
||||
{/* <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}`}
|
||||
>
|
||||
|
|
@ -274,7 +285,6 @@ const useTableColumns = () => {
|
|||
</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"
|
||||
|
|
@ -282,138 +292,20 @@ const useTableColumns = () => {
|
|||
<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>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
// {
|
||||
// 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;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,7 +8,7 @@ const ImageCreatePage = async () => {
|
|||
return (
|
||||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4 ">
|
||||
<div className="space-y-4">
|
||||
<FormImage />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -20,35 +19,11 @@ import { Link } from "@/components/navigation";
|
|||
import { useTranslations } from "next-intl";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { useState } from "react";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
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",
|
||||
header: t("no", { defaultValue: "No" }),
|
||||
|
|
@ -81,6 +56,7 @@ const useTableColumns = () => {
|
|||
<span className="whitespace-nowrap">{row.getValue("contentTag")}</span>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "contentType",
|
||||
header: t("type-content", { defaultValue: "Type Content" }),
|
||||
|
|
@ -97,6 +73,7 @@ const useTableColumns = () => {
|
|||
</span>
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "isPublish",
|
||||
header: "Status",
|
||||
|
|
@ -118,6 +95,7 @@ const useTableColumns = () => {
|
|||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "contentCreatedDate",
|
||||
header: t("upload-date", { defaultValue: "Upload Date" }),
|
||||
|
|
@ -126,6 +104,7 @@ const useTableColumns = () => {
|
|||
| string
|
||||
| number
|
||||
| undefined;
|
||||
|
||||
const formattedDate =
|
||||
createdAt && !isNaN(new Date(createdAt).getTime())
|
||||
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
|
||||
|
|
@ -139,24 +118,17 @@ const useTableColumns = () => {
|
|||
header: "Actions",
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const isDisabled = row.original.isPublish;
|
||||
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);
|
||||
};
|
||||
const isDisabled = row.original.isPublish; // Check the isPublish value
|
||||
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild disabled={isDisabled}>
|
||||
<Button
|
||||
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>
|
||||
<MoreVertical className="h-4 w-4 text-default-800" />
|
||||
|
|
@ -167,10 +139,10 @@ const useTableColumns = () => {
|
|||
href={`/contributor/content/spit/convert/${row.original.contentId}`}
|
||||
>
|
||||
<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" : ""
|
||||
}`}
|
||||
disabled={isDisabled}
|
||||
disabled={isDisabled} // Disable dropdown item if isPublish is true
|
||||
>
|
||||
<MoveDownRight className="w-4 h-4 me-1.5" />
|
||||
Pindah Ke Mediahub
|
||||
|
|
|
|||
|
|
@ -36,11 +36,7 @@ import {
|
|||
import { title } from "process";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import TablePagination from "@/components/table/table-pagination";
|
||||
import {
|
||||
deleteBulkSPIT,
|
||||
listEnableCategory,
|
||||
listSPIT,
|
||||
} from "@/service/content/content";
|
||||
import { listEnableCategory, listSPIT } from "@/service/content/content";
|
||||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||
|
|
@ -55,12 +51,8 @@ import {
|
|||
} from "@/components/ui/dropdown-menu";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { format, parseISO } from "date-fns";
|
||||
import { format } from "date-fns";
|
||||
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 = {
|
||||
// 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(() => {
|
||||
const pageFromUrl = searchParams?.get("page");
|
||||
if (pageFromUrl) {
|
||||
|
|
@ -159,27 +127,22 @@ const TableSPIT = () => {
|
|||
}
|
||||
}, [searchParams]);
|
||||
|
||||
// React.useEffect(() => {
|
||||
// fetchData();
|
||||
// }, [showData, page, search, statusFilter, dateFilter]);
|
||||
|
||||
// React.useEffect(() => {
|
||||
// fetchData();
|
||||
// getCategories();
|
||||
// }, [
|
||||
// // categoryFilter,
|
||||
// statusFilter,
|
||||
// page,
|
||||
// showData,
|
||||
// search,
|
||||
// // startDate,
|
||||
// // endDate,
|
||||
// ]);
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, [showData, page, search, statusFilter, dateFilter]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
getCategories();
|
||||
}, [statusFilter, page, showData, search, dateFilter]);
|
||||
}, [
|
||||
// categoryFilter,
|
||||
statusFilter,
|
||||
page,
|
||||
showData,
|
||||
search,
|
||||
// startDate,
|
||||
// endDate,
|
||||
]);
|
||||
|
||||
async function getCategories() {
|
||||
const category = await listEnableCategory("4");
|
||||
|
|
@ -187,13 +150,6 @@ const TableSPIT = () => {
|
|||
setCategories(resCategory || []);
|
||||
}
|
||||
|
||||
// Panggil fetchData otomatis ketika dateFilter berubah
|
||||
React.useEffect(() => {
|
||||
if (dateFilter) {
|
||||
fetchData();
|
||||
}
|
||||
}, [dateFilter]);
|
||||
|
||||
async function fetchData() {
|
||||
let isPublish;
|
||||
if (statusFilter.length === 0) {
|
||||
|
|
@ -205,7 +161,7 @@ const TableSPIT = () => {
|
|||
}
|
||||
|
||||
const formattedStartDate = dateFilter
|
||||
? format(new Date(dateFilter + "T00:00:00"), "yyyy-MM-dd")
|
||||
? format(new Date(dateFilter), "yyyy-MM-dd")
|
||||
: "";
|
||||
|
||||
try {
|
||||
|
|
@ -223,6 +179,8 @@ const TableSPIT = () => {
|
|||
item.no = (page - 1) * Number(showData) + index + 1;
|
||||
});
|
||||
|
||||
console.log("contentData : ", contentData);
|
||||
|
||||
setSpitTable(contentData);
|
||||
setTotalData(data?.totalElements);
|
||||
setTotalPage(data?.totalPages);
|
||||
|
|
@ -247,15 +205,15 @@ const TableSPIT = () => {
|
|||
return (
|
||||
<div className="w-full">
|
||||
<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>
|
||||
<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>
|
||||
<Input
|
||||
type="text"
|
||||
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}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
|
|
@ -312,7 +270,7 @@ const TableSPIT = () => {
|
|||
<div className="flex flex-row justify-between my-1 mx-1">
|
||||
<p>Filter</p>
|
||||
</div>
|
||||
{/* <div className="mx-2 my-1">
|
||||
<div className="mx-2 my-1">
|
||||
<Label>{t("date", { defaultValue: "Date" })}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
|
|
@ -320,21 +278,7 @@ const TableSPIT = () => {
|
|||
onChange={(e) => setDateFilter(e.target.value)}
|
||||
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>
|
||||
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
|
|
@ -394,37 +338,6 @@ const TableSPIT = () => {
|
|||
</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">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import { deleteMedia } from "@/service/content/content";
|
|||
import withReactContent from "sweetalert2-react-content";
|
||||
import Swal from "sweetalert2";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
|
|
@ -177,18 +176,20 @@ const useTableColumns = () => {
|
|||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
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) {
|
||||
const data = { id };
|
||||
// loading();
|
||||
const data = {
|
||||
id,
|
||||
};
|
||||
|
||||
const response = await deleteMedia(data);
|
||||
|
||||
if (response?.error) {
|
||||
|
|
@ -230,21 +231,16 @@ const useTableColumns = () => {
|
|||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (userLevelId !== undefined && roleId !== undefined) {
|
||||
setIsMabesApprover(
|
||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
||||
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||
);
|
||||
}
|
||||
}, [userLevelId, roleId]);
|
||||
|
||||
const canEdit =
|
||||
Number(row.original.uploadedById) === Number(userId) ||
|
||||
isMabesApprover ||
|
||||
roleId === 14;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -265,8 +261,8 @@ const useTableColumns = () => {
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
{canEdit && (
|
||||
{(Number(row.original.uploadedById) === Number(userId) ||
|
||||
isMabesApprover) && (
|
||||
<Link
|
||||
href={`/contributor/content/teks/update/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -276,7 +272,14 @@ const useTableColumns = () => {
|
|||
</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"
|
||||
|
|
@ -284,136 +287,20 @@ const useTableColumns = () => {
|
|||
<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>
|
||||
);
|
||||
},
|
||||
},
|
||||
// {
|
||||
// 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;
|
||||
};
|
||||
|
|
|
|||
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 { error } from "@/lib/swal";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
|
|
@ -177,18 +176,20 @@ const useTableColumns = () => {
|
|||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
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) {
|
||||
const data = { id };
|
||||
// loading();
|
||||
const data = {
|
||||
id,
|
||||
};
|
||||
|
||||
const response = await deleteMedia(data);
|
||||
|
||||
if (response?.error) {
|
||||
|
|
@ -230,21 +231,16 @@ const useTableColumns = () => {
|
|||
const [isMabesApprover, setIsMabesApprover] = React.useState(false);
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (userLevelId !== undefined && roleId !== undefined) {
|
||||
setIsMabesApprover(
|
||||
Number(userLevelId) === 216 && Number(roleId) === 3
|
||||
Number(userLevelId) == 216 && Number(roleId) == 3
|
||||
);
|
||||
}
|
||||
}, [userLevelId, roleId]);
|
||||
|
||||
const canEdit =
|
||||
Number(row.original.uploadedById) === Number(userId) ||
|
||||
isMabesApprover ||
|
||||
roleId === 14;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -265,8 +261,16 @@ const useTableColumns = () => {
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
{canEdit && (
|
||||
{/* <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}`}
|
||||
>
|
||||
|
|
@ -276,7 +280,6 @@ const useTableColumns = () => {
|
|||
</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"
|
||||
|
|
@ -284,136 +287,20 @@ const useTableColumns = () => {
|
|||
<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>
|
||||
);
|
||||
},
|
||||
},
|
||||
// {
|
||||
// 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;
|
||||
|
|
|
|||
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 { useTranslations } from "next-intl";
|
||||
import axios from "axios";
|
||||
import { downloadReport } from "@/service/report/report";
|
||||
|
||||
const useTableColumns = ({
|
||||
handlePreview,
|
||||
|
|
@ -128,10 +127,14 @@ const useTableColumns = ({
|
|||
const handleDownload = async (id: string) => {
|
||||
try {
|
||||
loading();
|
||||
const response = await axios.get(
|
||||
`https://netidhub.com/api/media/report/download?id=${id}`,
|
||||
{
|
||||
responseType: "blob",
|
||||
}
|
||||
);
|
||||
|
||||
const response = await downloadReport(id);
|
||||
console.log(response?.data);
|
||||
const url = window.URL.createObjectURL(new Blob([response?.data], { type : "application/pdf"}));
|
||||
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
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 (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ const ReportTable = () => {
|
|||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
const [showData, setShowData] = React.useState("50");
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
|
|
@ -106,68 +106,6 @@ const ReportTable = () => {
|
|||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||
const [openPreview, setOpenPreview] = React.useState(false);
|
||||
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 url = `https://new.netidhub.com/api/media/report/view?id=${id}`;
|
||||
|
|
@ -246,6 +184,8 @@ const ReportTable = () => {
|
|||
? prev.filter((id: any) => id !== categoryId)
|
||||
: [...prev, categoryId]
|
||||
);
|
||||
|
||||
// Perbarui filter kategori
|
||||
setCategoryFilter((prev) => {
|
||||
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
||||
|
||||
|
|
@ -266,51 +206,50 @@ const ReportTable = () => {
|
|||
}
|
||||
|
||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value);
|
||||
table.getColumn("judul")?.setFilterValue(e.target.value);
|
||||
setSearch(e.target.value); // Perbarui state search
|
||||
table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel
|
||||
};
|
||||
|
||||
// const handleGenerateReport = async () => {
|
||||
// const today = new Date();
|
||||
// const formattedDate = format(today, "dd-MM-yyyy");
|
||||
// const title = `Report ${formattedDate}`;
|
||||
const handleGenerateReport = async () => {
|
||||
const today = new Date();
|
||||
const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025
|
||||
const title = `Report ${formattedDate}`;
|
||||
|
||||
// const requestData = {
|
||||
// title,
|
||||
// date: reportDate,
|
||||
// };
|
||||
const requestData = {
|
||||
title,
|
||||
};
|
||||
|
||||
// try {
|
||||
// const response = await saveReport(requestData);
|
||||
try {
|
||||
const response = await saveReport(requestData);
|
||||
|
||||
// if (response?.error) {
|
||||
// MySwal.fire(
|
||||
// "Error",
|
||||
// response?.message || "Gagal menyimpan laporan",
|
||||
// "error"
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
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();
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Generate report error:", error);
|
||||
// MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
||||
// }
|
||||
// };
|
||||
MySwal.fire({
|
||||
title: "Sukses",
|
||||
text: "Laporan berhasil dibuat.",
|
||||
icon: "success",
|
||||
confirmButtonColor: "#3085d6",
|
||||
confirmButtonText: "OK",
|
||||
}).then(() => {
|
||||
fetchData(); // Refresh tabel setelah generate
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Generate report error:", error);
|
||||
MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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">
|
||||
<DialogTitle>Preview Laporan</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
|
@ -330,19 +269,10 @@ const ReportTable = () => {
|
|||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
{t("table", { defaultValue: "Table" })}{" "}
|
||||
{t("report", { defaultValue: "Report" })}
|
||||
{t("table", { defaultValue: "Table" })} {t("report", { defaultValue: "Report" })}
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
{/* <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)}
|
||||
>
|
||||
<Button fullWidth color="primary" onClick={handleGenerateReport}>
|
||||
<Plus size={18} className=" me-1.5" />
|
||||
{t("generate-report", { defaultValue: "Generate Report" })}
|
||||
</Button>
|
||||
|
|
@ -408,13 +338,13 @@ const ReportTable = () => {
|
|||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
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">
|
||||
<p>Filter</p>
|
||||
</div>
|
||||
<div className="mx-2 my-1">
|
||||
<Label>Tanggal Generate</Label>
|
||||
<Label>{t("date", { defaultValue: "Date" })}</Label>
|
||||
<Input
|
||||
type="date"
|
||||
value={dateFilter}
|
||||
|
|
@ -431,8 +361,8 @@ const ReportTable = () => {
|
|||
className="max-w-sm"
|
||||
/>
|
||||
</div> */}
|
||||
{/* <Label className="ml-2 mt-2">Status</Label> */}
|
||||
{/* <div className="flex items-center px-4 py-1">
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-1"
|
||||
|
|
@ -443,8 +373,8 @@ const ReportTable = () => {
|
|||
<label htmlFor="status-1" className="text-sm">
|
||||
{t("wait-review", { defaultValue: "Wait Review" })}
|
||||
</label>
|
||||
</div> */}
|
||||
{/* <div className="flex items-center px-4 py-1">
|
||||
</div>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-2"
|
||||
|
|
@ -455,7 +385,7 @@ const ReportTable = () => {
|
|||
<label htmlFor="status-2" className="text-sm">
|
||||
{t("acc", { defaultValue: "Acc" })}
|
||||
</label>
|
||||
</div> */}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
|
@ -542,49 +472,6 @@ const ReportTable = () => {
|
|||
totalPage={totalPage}
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -17,10 +17,9 @@ import withReactContent from "sweetalert2-react-content";
|
|||
import Swal from "sweetalert2";
|
||||
import { error } from "@/lib/swal";
|
||||
import { deleteCalendar } from "@/service/schedule/schedule";
|
||||
import { loading, success } from "@/config/swal";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -101,11 +100,15 @@ const useTableColumns = () => {
|
|||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const MySwal = withReactContent(Swal);
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
const userId = Number(getCookiesDecrypt("uie"));
|
||||
|
||||
async function doDelete(id: any) {
|
||||
// loading();
|
||||
const data = {
|
||||
id,
|
||||
};
|
||||
|
||||
const response = await deleteCalendar(id);
|
||||
|
||||
if (response?.error) {
|
||||
error(response.message);
|
||||
return false;
|
||||
|
|
@ -153,7 +156,6 @@ const useTableColumns = () => {
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
|
||||
<Link
|
||||
href={`/contributor/schedule/calendar-polri/detail/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -162,9 +164,6 @@ const useTableColumns = () => {
|
|||
Detail
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
|
||||
{row.original.createdById === userId && (
|
||||
<Link
|
||||
href={`/contributor/schedule/calendar-polri/update/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -173,9 +172,6 @@ const useTableColumns = () => {
|
|||
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"
|
||||
|
|
@ -183,104 +179,11 @@ const useTableColumns = () => {
|
|||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</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;
|
||||
|
|
|
|||
|
|
@ -226,12 +226,12 @@ const LiveReportTable = () => {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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
|
||||
value={selectedType}
|
||||
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" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
|
|
|||
|
|
@ -22,10 +22,8 @@ import withReactContent from "sweetalert2-react-content";
|
|||
import Swal from "sweetalert2";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const useTableColumns = (
|
||||
activeTab: "ta" | "daily" | "special" | "mabes-koor",
|
||||
) => {
|
||||
const t = useTranslations("Table");
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -52,12 +50,6 @@ const useTableColumns = (
|
|||
),
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "uniqueCode",
|
||||
header: t("code", { defaultValue: "Code" }),
|
||||
cell: ({ row }) => <span>{row.getValue("uniqueCode")}</span>,
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: t("upload-date", { defaultValue: "Upload Date" }),
|
||||
|
|
@ -115,31 +107,6 @@ const useTableColumns = (
|
|||
const MySwal = withReactContent(Swal);
|
||||
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) {
|
||||
loading();
|
||||
const resDelete = await deleteTaskTa(id);
|
||||
|
|
@ -192,11 +159,7 @@ const useTableColumns = (
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
{/* {(roleId == 11 || roleId == 12 || roleId == 19) && ( */}
|
||||
{(roleId == 11 ||
|
||||
roleId == 12 ||
|
||||
roleId == 19 ||
|
||||
roleId == 3) && (
|
||||
{(roleId == 11 || roleId == 12 || roleId == 19) && (
|
||||
<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">
|
||||
<Eye className="w-4 h-4 me-1.5" />
|
||||
|
|
@ -204,16 +167,15 @@ const useTableColumns = (
|
|||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
{roleId == 11 ||
|
||||
(roleId == 3 && (
|
||||
{roleId == 11 && (
|
||||
<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">
|
||||
<SquarePen className="w-4 h-4 me-1.5" />
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
))}
|
||||
{(roleId == 12 || roleId == 19) && (
|
||||
)}
|
||||
{(roleId == 11 || roleId == 12 || roleId == 19) && (
|
||||
<Link
|
||||
href={`/contributor/task-ta/upload-task/${row.original.id}`}
|
||||
>
|
||||
|
|
@ -223,8 +185,7 @@ const useTableColumns = (
|
|||
</DropdownMenuItem>
|
||||
</Link>
|
||||
)}
|
||||
{roleId == 11 ||
|
||||
(roleId == 3 && (
|
||||
{roleId == 11 && (
|
||||
<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"
|
||||
|
|
@ -232,7 +193,7 @@ const useTableColumns = (
|
|||
<Trash2 className="w-4 h-4 me-1.5" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -35,19 +35,10 @@ const TaskTaPage = () => {
|
|||
<CardTitle>
|
||||
<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">
|
||||
{t("tabel", { defaultValue: "Tabel" })}{" "}
|
||||
{t("task-ta", { defaultValue: "Task Ta" })}
|
||||
{t("tabel", { defaultValue: "Tabel" })} {t("task-ta", { defaultValue: "Task Ta" })}
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
{/* {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 && (
|
||||
{roleId !== 12 && (
|
||||
<Link href={"/contributor/task-ta/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn, getCookiesDecrypt } from "@/lib/utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -23,7 +23,7 @@ import Swal from "sweetalert2";
|
|||
import { useTranslations } from "next-intl";
|
||||
|
||||
const useTableColumns = () => {
|
||||
const t = useTranslations("Table");
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
accessorKey: "no",
|
||||
|
|
@ -124,97 +124,9 @@ const useTableColumns = () => {
|
|||
accessorKey: "action",
|
||||
header: t("action", { defaultValue: "Action" }),
|
||||
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 }) => {
|
||||
const router = useRouter();
|
||||
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) {
|
||||
loading();
|
||||
|
|
@ -256,8 +168,6 @@ const useTableColumns = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const isDone = row.original.isDone;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -276,9 +186,6 @@ const useTableColumns = () => {
|
|||
View
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
|
||||
{/* {!isDone && (
|
||||
<>
|
||||
<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" />
|
||||
|
|
@ -292,25 +199,6 @@ const useTableColumns = () => {
|
|||
<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>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ const TaskTable = () => {
|
|||
className={` ${
|
||||
isSpecificAttention
|
||||
? "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`}
|
||||
>
|
||||
|
|
@ -218,7 +218,7 @@ const TaskTable = () => {
|
|||
${
|
||||
!isSpecificAttention
|
||||
? "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
|
||||
`}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { UploadIcon } from "lucide-react";
|
||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||
import { Link } from "@/components/navigation";
|
||||
import {
|
||||
checkAuthorization,
|
||||
checkLoginSession,
|
||||
getCookiesDecrypt,
|
||||
} from "@/lib/utils";
|
||||
import { checkAuthorization, checkLoginSession } from "@/lib/utils";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
|
|
@ -17,13 +13,12 @@ const TaskPage = () => {
|
|||
const t = useTranslations("AnalyticsDashboard");
|
||||
useEffect(() => {
|
||||
function initState() {
|
||||
checkAuthorization("admin");
|
||||
checkAuthorization("admin"); // Specify the page, e.g., "admin" or another value
|
||||
checkLoginSession();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
const levelNumber = getCookiesDecrypt("ulne");
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -34,28 +29,16 @@ const TaskPage = () => {
|
|||
<CardTitle>
|
||||
<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">
|
||||
{t("tabel", { defaultValue: "Tabel" })}{" "}
|
||||
{t("task", { defaultValue: "Task" })}
|
||||
{t("tabel", { defaultValue: "Tabel" })} {t("task", { defaultValue: "Task" })}
|
||||
</div>
|
||||
{Number(levelNumber) !== 3 && (
|
||||
<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"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon size={18} className="mr-2" />
|
||||
{t("create-task", { defaultValue: "Create Task" })}
|
||||
</Button>
|
||||
</Link>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default function UserFeedback() {
|
|||
return (
|
||||
<div>
|
||||
<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>
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
{listData?.map(
|
||||
|
|
@ -43,7 +43,7 @@ export default function UserFeedback() {
|
|||
list?.avgScore !== "NaN" && (
|
||||
<div
|
||||
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">
|
||||
<p className="text-3xl">{parseInt(list?.avgScore)}</p>
|
||||
|
|
|
|||
|
|
@ -281,11 +281,11 @@ export default function DetailDaily() {
|
|||
|
||||
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 isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
|
|
@ -301,7 +301,7 @@ export default function DetailDaily() {
|
|||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"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) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...(unit ?? []), id]);
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit ?? []) {
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
|
|
@ -331,7 +331,7 @@ export default function DetailDaily() {
|
|||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
(unit ?? []).filter((value) => value !== id)
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -542,14 +542,14 @@ export default function DetailDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -564,7 +564,7 @@ export default function DetailDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ export default function DetailDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -592,7 +592,7 @@ export default function DetailDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -603,7 +603,7 @@ export default function DetailDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
|
|||
|
|
@ -294,11 +294,11 @@ export default function EditDaily() {
|
|||
|
||||
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 isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
|
|
@ -314,7 +314,7 @@ export default function EditDaily() {
|
|||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"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) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...(unit ?? []), id]);
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit ?? []) {
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ export default function EditDaily() {
|
|||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
(unit ?? []).filter((value) => value !== id)
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -506,7 +506,7 @@ export default function EditDaily() {
|
|||
<Checkbox
|
||||
checked={unit?.includes(item.id)}
|
||||
disabled={
|
||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
||||
item.id === "3" && !unit.includes("2")
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
handleUnitCheckedChange(item.id, checked)
|
||||
|
|
@ -550,14 +550,14 @@ export default function EditDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -572,7 +572,7 @@ export default function EditDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -582,7 +582,7 @@ export default function EditDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -600,7 +600,7 @@ export default function EditDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -611,7 +611,7 @@ export default function EditDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
|
|||
|
|
@ -242,11 +242,11 @@ export default function CreateDaily() {
|
|||
|
||||
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 isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
|
|
@ -262,7 +262,7 @@ export default function CreateDaily() {
|
|||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"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) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...(unit ?? []), id]);
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit ?? []) {
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
|
|
@ -292,7 +292,7 @@ export default function CreateDaily() {
|
|||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
(unit ?? []).filter((value) => value !== id)
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -472,7 +472,7 @@ export default function CreateDaily() {
|
|||
<Checkbox
|
||||
checked={unit?.includes(item.id)}
|
||||
disabled={
|
||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
||||
item.id === "3" && !unit.includes("2")
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
handleUnitCheckedChange(item.id, checked)
|
||||
|
|
@ -516,14 +516,14 @@ export default function CreateDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -538,7 +538,7 @@ export default function CreateDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -548,7 +548,7 @@ export default function CreateDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -566,7 +566,7 @@ export default function CreateDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -577,7 +577,7 @@ export default function CreateDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
|
|||
|
|
@ -281,11 +281,11 @@ export default function DetailDaily() {
|
|||
|
||||
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 isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
|
|
@ -301,7 +301,7 @@ export default function DetailDaily() {
|
|||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"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) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...(unit ?? []), id]);
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit ?? []) {
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
|
|
@ -331,7 +331,7 @@ export default function DetailDaily() {
|
|||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
(unit ?? []).filter((value) => value !== id)
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -542,14 +542,14 @@ export default function DetailDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -564,7 +564,7 @@ export default function DetailDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ export default function DetailDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -592,7 +592,7 @@ export default function DetailDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -603,7 +603,7 @@ export default function DetailDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
|
|||
|
|
@ -294,11 +294,11 @@ export default function EditDaily() {
|
|||
|
||||
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 isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
const handleAllCheckedChange = (checked: boolean | string) => {
|
||||
if (checked) {
|
||||
|
|
@ -314,7 +314,7 @@ export default function EditDaily() {
|
|||
const handleItemCheckedChange = (id: string, checked: boolean | string) => {
|
||||
form.setValue(
|
||||
"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) => {
|
||||
if (checked) {
|
||||
form.setValue("unit", [...(unit ?? []), id]);
|
||||
form.setValue("unit", [...unit, id]);
|
||||
} else {
|
||||
if (id == "2") {
|
||||
const temp = [];
|
||||
for (const element of unit ?? []) {
|
||||
for (const element of unit) {
|
||||
if (element == "1") {
|
||||
temp.push("1");
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ export default function EditDaily() {
|
|||
} else {
|
||||
form.setValue(
|
||||
"unit",
|
||||
(unit ?? []).filter((value) => value !== id)
|
||||
unit.filter((value) => value !== id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -506,7 +506,7 @@ export default function EditDaily() {
|
|||
<Checkbox
|
||||
checked={unit?.includes(item.id)}
|
||||
disabled={
|
||||
item.id === "3" && !(unit?.includes("2") ?? false)
|
||||
item.id === "3" && !unit.includes("2")
|
||||
}
|
||||
onCheckedChange={(checked) =>
|
||||
handleUnitCheckedChange(item.id, checked)
|
||||
|
|
@ -550,14 +550,14 @@ export default function EditDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -572,7 +572,7 @@ export default function EditDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -582,7 +582,7 @@ export default function EditDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -600,7 +600,7 @@ export default function EditDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -611,7 +611,7 @@ export default function EditDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
|
|||
|
|
@ -264,9 +264,9 @@ export default function CreateDaily() {
|
|||
const media = form.watch("media");
|
||||
const unit = form.watch("unit");
|
||||
|
||||
const isAllChecked = output && items.every((item) => output.includes(item.id));
|
||||
const isAllMediaChecked = media && medias.every((item) => media.includes(item.id));
|
||||
const isAllUnitChecked = unit && units.every((item) => unit.includes(item.id));
|
||||
const isAllChecked = items.every((item) => output?.includes(item.id));
|
||||
const isAllMediaChecked = medias.every((item) => media?.includes(item.id));
|
||||
const isAllUnitChecked = units.every((item) => unit?.includes(item.id));
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
|
|
@ -398,8 +398,10 @@ export default function CreateDaily() {
|
|||
form.setValue(
|
||||
"output",
|
||||
checked
|
||||
? [...(output ?? []), item.id]
|
||||
: (output ?? []).filter((value) => value !== item.id)
|
||||
? [...output, item.id]
|
||||
: output.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -462,14 +464,16 @@ export default function CreateDaily() {
|
|||
checked={unit?.includes(item.id)}
|
||||
disabled={
|
||||
item.id === "polres" &&
|
||||
!(unit?.includes("polda") ?? false)
|
||||
!unit.includes("polda")
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue(
|
||||
"unit",
|
||||
checked
|
||||
? [...(unit ?? []), item.id]
|
||||
: (unit ?? []).filter((value) => value !== item.id)
|
||||
? [...unit, item.id]
|
||||
: unit.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -511,14 +515,14 @@ export default function CreateDaily() {
|
|||
id={list.id}
|
||||
name={`all${list.id}`}
|
||||
checked={
|
||||
unit?.includes("2") ?? false
|
||||
unit.includes("2")
|
||||
? true
|
||||
: !!selected[list.id]
|
||||
}
|
||||
onCheckedChange={() =>
|
||||
handleParentChange(list.id)
|
||||
}
|
||||
disabled={unit?.includes("2") ?? false}
|
||||
disabled={unit.includes("2")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={list.name}
|
||||
|
|
@ -533,7 +537,7 @@ export default function CreateDaily() {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selectAll[list.id]
|
||||
}
|
||||
|
|
@ -543,7 +547,7 @@ export default function CreateDaily() {
|
|||
Boolean(e)
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor="all-polres"
|
||||
|
|
@ -561,7 +565,7 @@ export default function CreateDaily() {
|
|||
<Checkbox
|
||||
id={`${list.id}${subDes.id}`}
|
||||
checked={
|
||||
unit?.includes("3") ?? false
|
||||
unit.includes("3")
|
||||
? true
|
||||
: !!selected[
|
||||
`${list.id}${subDes.id}`
|
||||
|
|
@ -572,7 +576,7 @@ export default function CreateDaily() {
|
|||
`${list.id}${subDes.id}`
|
||||
)
|
||||
}
|
||||
disabled={unit?.includes("3") ?? false}
|
||||
disabled={unit.includes("3")}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`${list.id}${subDes.id}`}
|
||||
|
|
@ -644,8 +648,8 @@ export default function CreateDaily() {
|
|||
form.setValue(
|
||||
"media",
|
||||
checked
|
||||
? [...(media ?? []), item.id]
|
||||
: (media ?? []).filter(
|
||||
? [...media, item.id]
|
||||
: media.filter(
|
||||
(value) => value !== item.id
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import ReportTable from "../contributor/report/components/report-table";
|
|||
const DashboardPage = () => {
|
||||
const t = useTranslations("AnalyticsDashboard");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
|
||||
return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? (
|
||||
<div>
|
||||
|
|
@ -55,23 +54,18 @@ const DashboardPage = () => {
|
|||
>
|
||||
{t("schedule", { defaultValue: "Schedule" })}
|
||||
</TabsTrigger>
|
||||
{levelNumber !== 3 && (
|
||||
<>
|
||||
<TabsTrigger
|
||||
value="indeks"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("indeks", { defaultValue: "Indeks" })}
|
||||
</TabsTrigger>
|
||||
|
||||
<TabsTrigger
|
||||
value="report"
|
||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground rounded-md px-6"
|
||||
>
|
||||
{t("report", { defaultValue: "Report" })}
|
||||
</TabsTrigger>
|
||||
</>
|
||||
)}
|
||||
</TabsList>
|
||||
</Card>
|
||||
<TabsContent value="routine-task">
|
||||
|
|
@ -81,24 +75,18 @@ const DashboardPage = () => {
|
|||
<CardContent className="p-4">
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_disetujui_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Disetujui Hari Ini",
|
||||
})}
|
||||
title={t("Hasil_unggah_disetujui_hari_ini", { defaultValue: "Hasil Unggah Disetujui Hari Ini" })}
|
||||
total="3,564"
|
||||
className="bg-info/10 border-none shadow-none"
|
||||
/>
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_direvisi_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Direvisi Hari Ini",
|
||||
})}
|
||||
title={t("Hasil_unggah_direvisi_hari_ini", { defaultValue: "Hasil Unggah Direvisi Hari Ini" })}
|
||||
total="564"
|
||||
className="bg-warning/10 border-none shadow-none"
|
||||
chartColor="#FB8F65"
|
||||
/>
|
||||
<StatisticsBlock
|
||||
title={t("Hasil_unggah_ditolak_hari_ini", {
|
||||
defaultValue: "Hasil Unggah Ditolak Hari Ini",
|
||||
})}
|
||||
title={t("Hasil_unggah_ditolak_hari_ini", { defaultValue: "Hasil Unggah Ditolak Hari Ini" })}
|
||||
total="+5.0%"
|
||||
className="bg-primary/10 border-none shadow-none"
|
||||
chartColor="#2563eb"
|
||||
|
|
@ -113,9 +101,7 @@ const DashboardPage = () => {
|
|||
<Card>
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
<CardTitle className="flex-1 text-lg">
|
||||
{t("Total-Content-Production", {
|
||||
defaultValue: "Total Content Production",
|
||||
})}
|
||||
{t("Total-Content-Production", { defaultValue: "Total Content Production" })}
|
||||
</CardTitle>
|
||||
<DashboardDropdown />
|
||||
</CardHeader>
|
||||
|
|
@ -127,9 +113,7 @@ const DashboardPage = () => {
|
|||
<div className="lg:col-span-8 col-span-12">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center">
|
||||
<CardTitle className="flex-1">
|
||||
{t("tabel", { defaultValue: "Tabel" })}
|
||||
</CardTitle>
|
||||
<CardTitle className="flex-1">{t("tabel", { defaultValue: "Tabel" })}</CardTitle>
|
||||
{/* <DashboardDropdown /> */}
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
|
|
@ -148,26 +132,14 @@ const DashboardPage = () => {
|
|||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
Tabel Penugasan
|
||||
</div>
|
||||
{Number(levelNumber) !== 3 && (
|
||||
<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>
|
||||
<div>
|
||||
<Link href={"/contributor/task/create"}>
|
||||
<Button color="primary" className="text-white">
|
||||
<UploadIcon />
|
||||
Buat Penugasan
|
||||
</Button>
|
||||
</Link>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<CardContent className="p-0 mt-3">
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import {
|
|||
} from "@/components/ui/table";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Eye,
|
||||
|
|
@ -62,7 +61,6 @@ import {
|
|||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
type StatusFilter = string[];
|
||||
|
||||
|
|
@ -89,8 +87,7 @@ const ContentTable = () => {
|
|||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const [categories, setCategories] = React.useState<string[]>();
|
||||
const [categoryFilter, setCategoryFilter] = React.useState<string[]>([]);
|
||||
// const [statusFilter, setStatusFilter] = React.useState<StatusFilter>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<any[]>([]);
|
||||
const [statusFilter, setStatusFilter] = React.useState<StatusFilter>([]);
|
||||
const [startDateString, setStartDateString] = React.useState<string>("");
|
||||
const [endDateString, setEndDateString] = React.useState<string>("");
|
||||
const [filterByCreator, setFilterByCreator] = React.useState<string>("");
|
||||
|
|
@ -171,14 +168,6 @@ const ContentTable = () => {
|
|||
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 (
|
||||
<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">
|
||||
|
|
@ -196,14 +185,31 @@ const ContentTable = () => {
|
|||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className="flex flex-row gap-3">
|
||||
<div className=" items-center gap-3 w-full md:w-[152px] border border-black rounded-lg">
|
||||
<div className=" items-center gap-3 w-full md:w-[200px]">
|
||||
{/* <Select
|
||||
onValueChange={(value) => {
|
||||
setStatusFilter([value]);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select a Filter Status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Status</SelectLabel>
|
||||
<SelectItem value="1">Menunggu Review</SelectItem>
|
||||
<SelectItem value="2">Diterima</SelectItem>
|
||||
<SelectItem value="3">Minta Update</SelectItem>
|
||||
<SelectItem value="4">Ditolak</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select> */}
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
setFileTypeFilter([value]);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-full lg:w-[150px]">
|
||||
<SelectTrigger className="w-full lg:w-[180px]">
|
||||
<SelectValue placeholder={t("selectFilter")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
|
@ -212,73 +218,11 @@ const ContentTable = () => {
|
|||
<SelectItem value="1">{t("image")}</SelectItem>
|
||||
<SelectItem value="2">{t("audio-visual")}</SelectItem>
|
||||
<SelectItem value="3">{t("text")}</SelectItem>
|
||||
<SelectItem value="4">{t("audio")}</SelectItem>
|
||||
<SelectItem value="4">Audio</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto" size="md">
|
||||
Filter <ChevronDown />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-64 h-[150px] overflow-y-auto"
|
||||
>
|
||||
<Label className="ml-2 mt-2">Status</Label>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="status-2"
|
||||
className="mr-2"
|
||||
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>
|
||||
<Table className="overflow-hidden mt-3">
|
||||
<TableHeader>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import * as React from "react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -8,42 +10,13 @@ import {
|
|||
DropdownMenuItem,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { format } from "date-fns";
|
||||
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";
|
||||
|
||||
const MySwal = withReactContent(Swal);
|
||||
|
||||
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 useTableColumns = () => {
|
||||
const t = useTranslations("Table"); // Panggil di dalam hook
|
||||
|
||||
const columns: ColumnDef<any>[] = [
|
||||
{
|
||||
|
|
@ -94,46 +67,46 @@ const useTableColumns = (onDeleteSuccess?: () => void) => {
|
|||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: t("action"),
|
||||
cell: ({ row }) => (
|
||||
accessorKey: "action",
|
||||
header: t("action", { defaultValue: "Action" }),
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size="icon" className="bg-transparent hover:bg-transparent">
|
||||
<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 align="end" className="p-0">
|
||||
{/* View */}
|
||||
<DropdownMenuContent className="p-0" align="end">
|
||||
<Link
|
||||
href={`/shared/communication/internal/detail/${row.original.id}`}
|
||||
>
|
||||
<DropdownMenuItem className="p-2 border-b cursor-pointer hover:bg-slate-100">
|
||||
<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>
|
||||
|
||||
{/* Edit */}
|
||||
<Link
|
||||
href={`/shared/communication/internal/update/${row.original.id}`}
|
||||
>
|
||||
<DropdownMenuItem className="p-2 border-b cursor-pointer hover:bg-slate-100">
|
||||
<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>
|
||||
|
||||
{/* Delete */}
|
||||
<DropdownMenuItem
|
||||
className="p-2 text-destructive bg-destructive/30 cursor-pointer"
|
||||
onClick={() => handleDelete(row.original.id)}
|
||||
>
|
||||
<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>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import useTableColumns from "./columns";
|
|||
const InternalTable = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||
const [totalData, setTotalData] = React.useState<number>(1);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
|
|
@ -78,6 +79,7 @@ const InternalTable = () => {
|
|||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [showData, setShowData] = React.useState("10");
|
||||
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
|
|
@ -85,7 +87,11 @@ const InternalTable = () => {
|
|||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
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({
|
||||
data: dataTable,
|
||||
columns,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const CommunicationPage = () => {
|
|||
return (
|
||||
<div>
|
||||
<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">
|
||||
<p className="text-lg">{tab}</p>
|
||||
{tab === "Pertanyaan Internal" && (
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ import { useTranslations } from "next-intl";
|
|||
const ContestPage = () => {
|
||||
const [userLevelId, setUserLevelId] = useState<any>(null);
|
||||
const t = useTranslations("Contest");
|
||||
const levelNumber = Number(getCookiesDecrypt("ulne"));
|
||||
|
||||
useEffect(() => {
|
||||
setUserLevelId(Number(getCookiesDecrypt("ulie")));
|
||||
}, []);
|
||||
|
|
@ -27,36 +25,18 @@ const ContestPage = () => {
|
|||
<CardTitle>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1 text-xl font-medium text-default-900">
|
||||
{t("table", { defaultValue: "Table" })}{" "}
|
||||
{t("contest", { defaultValue: "Contest" })}
|
||||
{t("table", { defaultValue: "Table" })} {t("contest", { defaultValue: "Contest" })}
|
||||
</div>
|
||||
{userLevelId !== 776 &&
|
||||
userLevelId !== null &&
|
||||
levelNumber !== 3 && (
|
||||
{userLevelId !== 776 && userLevelId !== null && (
|
||||
<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",
|
||||
})}
|
||||
{t("create-contest", { defaultValue: "Create Contest" })}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* {userLevelId !== 776 && userLevelId !== null && (
|
||||
<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>
|
||||
)} */}
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const VideoSliderPage = () => {
|
|||
});
|
||||
const initFetch = async () => {
|
||||
const request = {
|
||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
||||
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||
contentTypeId: "2",
|
||||
};
|
||||
const response = await getListContent(request);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const AudioSliderPage = () => {
|
|||
});
|
||||
const initFetch = async () => {
|
||||
const request = {
|
||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
||||
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||
contentTypeId: "4",
|
||||
};
|
||||
const response = await getListContent(request);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const ImageSliderPage = () => {
|
|||
});
|
||||
const initFetch = async () => {
|
||||
const request = {
|
||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
||||
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||
contentTypeId: "3",
|
||||
};
|
||||
const response = await getListContent(request);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const TeksSliderPage = () => {
|
|||
});
|
||||
const initFetch = async () => {
|
||||
const request = {
|
||||
sortBy: type == "popular" ? "favorite" : "createdAt",
|
||||
sortBy: type == "popular" ? "clickCount" : "createdAt",
|
||||
contentTypeId: "1",
|
||||
};
|
||||
const response = await getListContent(request);
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ export default function DetailImage() {
|
|||
className="object-fill h-full w-full rounded-md"
|
||||
src={data.url}
|
||||
controls
|
||||
title={`Video ${data.id}`}
|
||||
title={`Video ${data.id}`} // Mengganti alt dengan title
|
||||
/>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -98,12 +98,6 @@ const QuestionsTable = (props: {
|
|||
const [page, setPage] = React.useState(1);
|
||||
const [totalPage, setTotalPage] = React.useState(1);
|
||||
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");
|
||||
|
||||
|
|
@ -142,21 +136,13 @@ const QuestionsTable = (props: {
|
|||
}
|
||||
}, [searchParams]);
|
||||
|
||||
// React.useEffect(() => {
|
||||
// fetchData();
|
||||
// setPagination({
|
||||
// pageIndex: 0,
|
||||
// pageSize: Number(showData),
|
||||
// });
|
||||
// }, [page, showData]);
|
||||
// ✅ update useEffect, tambahin reportType supaya reload saat tab diganti
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
setPagination({
|
||||
pageIndex: 0,
|
||||
pageSize: Number(showData),
|
||||
});
|
||||
}, [page, showData, reportType]);
|
||||
}, [page, showData]);
|
||||
|
||||
let typingTimer: any;
|
||||
const doneTypingInterval = 1500;
|
||||
|
|
@ -174,52 +160,6 @@ const QuestionsTable = (props: {
|
|||
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() {
|
||||
const typeNow =
|
||||
title === "comment"
|
||||
|
|
@ -245,15 +185,12 @@ const QuestionsTable = (props: {
|
|||
: "";
|
||||
|
||||
try {
|
||||
// ⬇️ tambahan param khusus untuk emergency
|
||||
const res = await getQuestionPagination(
|
||||
search,
|
||||
page - 1,
|
||||
typeNow,
|
||||
showData,
|
||||
title === "emergency" ? reportType : undefined
|
||||
showData
|
||||
);
|
||||
|
||||
const data = res?.data?.data;
|
||||
const contentData = data?.page?.content;
|
||||
console.log("contentDatassss : ", data);
|
||||
|
|
@ -261,7 +198,6 @@ const QuestionsTable = (props: {
|
|||
contentData.forEach((item: any, index: number) => {
|
||||
item.no = (page - 1) * Number(showData) + index + 1;
|
||||
});
|
||||
|
||||
setDataTable(contentData);
|
||||
props.statisticData(data?.statistic);
|
||||
setTotalData(data?.page?.totalElements);
|
||||
|
|
@ -331,23 +267,6 @@ const QuestionsTable = (props: {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</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">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ export default function CreateSpvFAQModal() {
|
|||
});
|
||||
|
||||
const target = form.watch("publishTo");
|
||||
const isAllTargetChecked = target && publishToList.every((item) =>
|
||||
target.includes(item.id)
|
||||
const isAllTargetChecked = publishToList.every((item) =>
|
||||
target?.includes(item.id)
|
||||
);
|
||||
|
||||
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
|
|
@ -10,7 +10,7 @@ const TicketingDetailPage = async () => {
|
|||
<div>
|
||||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
{/* <FormDetailTicketing id={selectedTicketId} /> */}
|
||||
<FormDetailTicketing />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue