fix:login page, table:image,audio,audio-visual,teks

This commit is contained in:
Anang Yusman 2024-12-07 01:37:45 +07:00
parent 8edaedc9d5
commit 60edcc288f
20 changed files with 1773 additions and 358 deletions

View File

@ -2,7 +2,9 @@
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
@ -12,7 +14,6 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { columns } from "./columns";
import { Input } from "@/components/ui/input";
import {
@ -26,8 +27,177 @@ import {
import { data } from "./data";
import TablePagination from "./table-pagination";
import { Button } from "@/components/ui/button";
import { format } from "date-fns";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Trash2,
} from "lucide-react";
import { title } from "process";
import { getCookiesDecrypt } from "@/lib/utils";
import { listDataImage, listDataVideo } from "@/service/content/content";
export type CompanyData = {
no: number;
title: string;
categoryName: string;
createdAt: string;
creatorGroup: string;
publishedOn: string;
isPublish: any;
isPublishOnPolda: any;
isDone: string;
};
export const columns: ColumnDef<CompanyData>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
},
{
accessorKey: "title",
header: "Judul",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
),
},
{
accessorKey: "categoryName",
header: "Kategori",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "creatorGroup",
header: "Sumber ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorGroup")}</span>
),
},
{
accessorKey: "publishedOn",
header: "Penempatan File",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "isDone",
header: "Status",
cell: ({ row }) => {
const isDone = row.getValue<boolean>("isDone");
return (
<div>
<Button
size="sm"
color="success"
variant="outline"
className={` btn btn-sm ${
isDone ? "btn-outline-success" : "btn-outline-primary"
} pill-btn ml-1`}
>
{isDone ? "Selesai" : "Aktif"}
</Button>
</div>
);
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
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">
<a href="/en/task/detail/[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>
</a>
<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>
);
},
},
];
const TableVideo = () => {
const [videoTable, setVideoTable] = React.useState<CompanyData[]>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
@ -35,9 +205,29 @@ const TableVideo = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, // Halaman pertama
pageSize: 10, // Jumlah baris per halaman
});
const [page, setPage] = React.useState(1); // Halaman aktif
const [totalPage, setTotalPage] = React.useState(1); // Total halaman
const [limit, setLimit] = React.useState(6); // Jumlah baris per halaman
const [search, setSearch] = React.useState(title);
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
data,
data: videoTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -55,6 +245,48 @@ const TableVideo = () => {
},
});
React.useEffect(() => {
initState();
}, [page, limit]);
async function initState() {
try {
const isForSelf = Number(roleId) == 4;
const res = await listDataVideo(
limit,
page,
isForSelf,
!isForSelf,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1")
? "1,2"
: statusFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1") ? userLevelId : "",
filterByCreator,
filterBySource,
startDateString,
endDateString
);
const data = res.data.data.content.map((item: any, index: number) => ({
no: (page - 1) * limit + index + 1,
title: item.title,
categoryName: item.categoryName,
creatorGroup: item.creatorGroup,
assignmentType: item.assignmentType?.name || "-",
createdAt: item.createdAt,
isDone: item.isDone,
publishedOn: item.publishedOn,
isPublish: item.isPublish,
isPublishOnPolda: item.isPublishOnPolda,
}));
setVideoTable(data);
setTotalPage(res.data.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full">
<div className="flex items-center py-4 px-5">
@ -72,22 +304,20 @@ const TableVideo = () => {
</div>
</div>
<Table>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
@ -97,6 +327,7 @@ const TableVideo = () => {
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
@ -114,7 +345,41 @@ const TableVideo = () => {
)}
</TableBody>
</Table>
<TablePagination table={table} />
<div className="flex items-center justify-center py-4 gap-2 flex-none">
<Button
variant="outline"
size="icon"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
className="w-8 h-8"
>
<ChevronLeft className="w-4 h-4" />
</Button>
{table.getPageOptions().map((page, pageIndex) => (
<Button
key={`basic-data-table-${pageIndex}`}
onClick={() => table.setPageIndex(pageIndex)}
size="icon"
className="w-8 h-8"
variant={
table.getState().pagination.pageIndex === pageIndex
? "default"
: "outline"
}
>
{page + 1}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
className="w-8 h-8"
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
</div>
);
};

View File

@ -2,7 +2,9 @@
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
@ -12,7 +14,6 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { columns } from "./columns";
import { Input } from "@/components/ui/input";
import {
@ -26,8 +27,181 @@ import {
import { data } from "./data";
import TablePagination from "./table-pagination";
import { Button } from "@/components/ui/button";
import { format } from "date-fns";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Trash2,
} from "lucide-react";
import { title } from "process";
import { getCookiesDecrypt } from "@/lib/utils";
import {
listDataAudio,
listDataImage,
listDataVideo,
} from "@/service/content/content";
export type CompanyData = {
no: number;
title: string;
categoryName: string;
createdAt: string;
creatorGroup: string;
publishedOn: string;
isPublish: any;
isPublishOnPolda: any;
isDone: string;
};
export const columns: ColumnDef<CompanyData>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
},
{
accessorKey: "title",
header: "Judul",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
),
},
{
accessorKey: "categoryName",
header: "Kategori",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "creatorGroup",
header: "Sumber ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorGroup")}</span>
),
},
{
accessorKey: "publishedOn",
header: "Penempatan File",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "isDone",
header: "Status",
cell: ({ row }) => {
const isDone = row.getValue<boolean>("isDone");
return (
<div>
<Button
size="sm"
color="success"
variant="outline"
className={` btn btn-sm ${
isDone ? "btn-outline-success" : "btn-outline-primary"
} pill-btn ml-1`}
>
{isDone ? "Selesai" : "Aktif"}
</Button>
</div>
);
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
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">
<a href="/en/task/detail/[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>
</a>
<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>
);
},
},
];
const TableAudio = () => {
const [videoTable, setVideoTable] = React.useState<CompanyData[]>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
@ -35,9 +209,29 @@ const TableAudio = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, // Halaman pertama
pageSize: 10, // Jumlah baris per halaman
});
const [page, setPage] = React.useState(1); // Halaman aktif
const [totalPage, setTotalPage] = React.useState(1); // Total halaman
const [limit, setLimit] = React.useState(6); // Jumlah baris per halaman
const [search, setSearch] = React.useState(title);
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
data,
data: videoTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -55,6 +249,48 @@ const TableAudio = () => {
},
});
React.useEffect(() => {
initState();
}, [page, limit]);
async function initState() {
try {
const isForSelf = Number(roleId) == 4;
const res = await listDataAudio(
limit,
page,
isForSelf,
!isForSelf,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1")
? "1,2"
: statusFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1") ? userLevelId : "",
filterByCreator,
filterBySource,
startDateString,
endDateString
);
const data = res.data.data.content.map((item: any, index: number) => ({
no: (page - 1) * limit + index + 1,
title: item.title,
categoryName: item.categoryName,
creatorGroup: item.creatorGroup,
assignmentType: item.assignmentType?.name || "-",
createdAt: item.createdAt,
isDone: item.isDone,
publishedOn: item.publishedOn,
isPublish: item.isPublish,
isPublishOnPolda: item.isPublishOnPolda,
}));
setVideoTable(data);
setTotalPage(res.data.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full">
<div className="flex items-center py-4 px-5">
@ -72,22 +308,20 @@ const TableAudio = () => {
</div>
</div>
<Table>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
@ -97,6 +331,7 @@ const TableAudio = () => {
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
@ -114,7 +349,41 @@ const TableAudio = () => {
)}
</TableBody>
</Table>
<TablePagination table={table} />
<div className="flex items-center justify-center py-4 gap-2 flex-none">
<Button
variant="outline"
size="icon"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
className="w-8 h-8"
>
<ChevronLeft className="w-4 h-4" />
</Button>
{table.getPageOptions().map((page, pageIndex) => (
<Button
key={`basic-data-table-${pageIndex}`}
onClick={() => table.setPageIndex(pageIndex)}
size="icon"
className="w-8 h-8"
variant={
table.getState().pagination.pageIndex === pageIndex
? "default"
: "outline"
}
>
{page + 1}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
className="w-8 h-8"
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
</div>
);
};

View File

@ -45,7 +45,8 @@ import {
import { title } from "process";
import { getCookiesDecrypt } from "@/lib/utils";
import { listDataImage } from "@/service/content/content-image";
import { listDataImage } from "@/service/content/content";
import page from "../page";
export type CompanyData = {
no: number;
@ -197,7 +198,6 @@ export const columns: ColumnDef<CompanyData>[] = [
];
const TableImage = () => {
const [imageTable, setImageTable] = React.useState<CompanyData[]>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
@ -205,14 +205,14 @@ const TableImage = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [imageTable, setImageTable] = React.useState<CompanyData[]>([]);
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, // Halaman pertama
pageSize: 10, // Jumlah baris per halaman
});
const [page, setPage] = React.useState(1); // Halaman aktif
const [totalPage, setTotalPage] = React.useState(1); // Total halaman
const [limit, setLimit] = React.useState(10); // Jumlah baris per halaman
const [search, setSearch] = React.useState(title);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [limit, setLimit] = React.useState<number>(10); // Jumlah baris per halaman
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
@ -247,14 +247,14 @@ const TableImage = () => {
React.useEffect(() => {
initState();
}, [page, limit]);
}, [limit]);
async function initState() {
try {
const isForSelf = Number(roleId) == 4;
const isForSelf = Number(roleId) === 4;
const res = await listDataImage(
limit,
page,
pagination.pageSize, // Ambil nilai dari pagination.pageSize
pagination.pageIndex + 1, // API sering menggunakan page 1-based
isForSelf,
!isForSelf,
categoryFilter?.sort().join(","),
@ -267,12 +267,24 @@ const TableImage = () => {
startDateString,
endDateString
);
const data = res.data.data.content.map((item: any, index: number) => ({
no: (page - 1) * limit + index + 1,
setupData(res.data?.data);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
function setupData(rawData: {
content: any[];
totalPages: number;
totalElements: number;
}) {
if (rawData?.content) {
const data: CompanyData[] = rawData.content.map((item, index) => ({
no: pagination.pageIndex * pagination.pageSize + index + 1,
title: item.title,
categoryName: item.categoryName,
creatorGroup: item.creatorGroup,
assignmentType: item.assignmentType?.name || "-",
createdAt: item.createdAt,
isDone: item.isDone,
publishedOn: item.publishedOn,
@ -281,9 +293,8 @@ const TableImage = () => {
}));
setImageTable(data);
setTotalPage(res.data.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
setTotalPage(rawData.totalPages);
console.log(data, "dataImage");
}
}

View File

@ -2,7 +2,9 @@
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
@ -12,7 +14,6 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { columns } from "./columns";
import { Input } from "@/components/ui/input";
import {
@ -26,8 +27,183 @@ import {
import { data } from "./data";
import TablePagination from "./table-pagination";
import { Button } from "@/components/ui/button";
import { format } from "date-fns";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Trash2,
} from "lucide-react";
import { title } from "process";
import { getCookiesDecrypt } from "@/lib/utils";
import {
listDataAudio,
listDataImage,
listDataVideo,
listSPIT,
} from "@/service/content/content";
import { pages } from "next/dist/build/templates/app-page";
export type CompanyData = {
no: number;
title: string;
categoryName: string;
createdAt: string;
creatorGroup: string;
publishedOn: string;
isPublish: any;
isPublishOnPolda: any;
isDone: string;
};
export const columns: ColumnDef<CompanyData>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
},
{
accessorKey: "title",
header: "Judul",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
),
},
{
accessorKey: "categoryName",
header: "Kategori",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "creatorGroup",
header: "Sumber ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorGroup")}</span>
),
},
{
accessorKey: "publishedOn",
header: "Penempatan File",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "isDone",
header: "Status",
cell: ({ row }) => {
const isDone = row.getValue<boolean>("isDone");
return (
<div>
<Button
size="sm"
color="success"
variant="outline"
className={` btn btn-sm ${
isDone ? "btn-outline-success" : "btn-outline-primary"
} pill-btn ml-1`}
>
{isDone ? "Selesai" : "Aktif"}
</Button>
</div>
);
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
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">
<a href="/en/task/detail/[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>
</a>
<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>
);
},
},
];
const TableSPIT = () => {
const [spitTable, setSpitTable] = React.useState<CompanyData[]>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
@ -35,9 +211,29 @@ const TableSPIT = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, // Halaman pertama
pageSize: 10, // Jumlah baris per halaman
});
const [page, setPage] = React.useState(1); // Halaman aktif
const [totalPage, setTotalPage] = React.useState(1); // Total halaman
const [limit, setLimit] = React.useState(6); // Jumlah baris per halaman
const [search, setSearch] = React.useState(title);
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([1, 2]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
data,
data: spitTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -55,6 +251,47 @@ const TableSPIT = () => {
},
});
React.useEffect(() => {
initState();
}, [page, limit]);
async function initState() {
try {
const isForSelf = Number(roleId) == 4;
let isPublish;
if (statusFilter.length > 1) {
isPublish = "";
} else if (statusFilter.length === 1) {
if (statusFilter.includes(1)) {
isPublish = false;
} else {
isPublish = true;
}
} else {
isPublish = undefined;
}
const res = await listSPIT(pages, limit, search, isPublish);
const data = res.data.data.content.map((item: any, index: number) => ({
no: (page - 1) * limit + index + 1,
title: item.title,
categoryName: item.categoryName,
creatorGroup: item.creatorGroup,
assignmentType: item.assignmentType?.name || "-",
createdAt: item.createdAt,
isDone: item.isDone,
publishedOn: item.publishedOn,
isPublish: item.isPublish,
isPublishOnPolda: item.isPublishOnPolda,
}));
setSpitTable(data);
setTotalPage(res.data.totalPages);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
return (
<div className="w-full">
<div className="flex items-center py-4 px-5">
@ -72,22 +309,20 @@ const TableSPIT = () => {
</div>
</div>
<Table>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
@ -97,6 +332,7 @@ const TableSPIT = () => {
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
@ -114,7 +350,41 @@ const TableSPIT = () => {
)}
</TableBody>
</Table>
<TablePagination table={table} />
<div className="flex items-center justify-center py-4 gap-2 flex-none">
<Button
variant="outline"
size="icon"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
className="w-8 h-8"
>
<ChevronLeft className="w-4 h-4" />
</Button>
{table.getPageOptions().map((page, pageIndex) => (
<Button
key={`basic-data-table-${pageIndex}`}
onClick={() => table.setPageIndex(pageIndex)}
size="icon"
className="w-8 h-8"
variant={
table.getState().pagination.pageIndex === pageIndex
? "default"
: "outline"
}
>
{page + 1}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
className="w-8 h-8"
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
</div>
);
};

View File

@ -2,7 +2,9 @@
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
PaginationState,
SortingState,
VisibilityState,
flexRender,
@ -12,7 +14,6 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { columns } from "./columns";
import { Input } from "@/components/ui/input";
import {
@ -26,6 +27,175 @@ import {
import { data } from "./data";
import TablePagination from "./table-pagination";
import { Button } from "@/components/ui/button";
import { format } from "date-fns";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
ChevronLeft,
ChevronRight,
Eye,
MoreVertical,
Trash2,
} from "lucide-react";
import { title } from "process";
import { getCookiesDecrypt } from "@/lib/utils";
import { listDataImage, listDataTeks } from "@/service/content/content";
import page from "../page";
export type CompanyData = {
no: number;
title: string;
categoryName: string;
createdAt: string;
creatorGroup: string;
publishedOn: string;
isPublish: any;
isPublishOnPolda: any;
isDone: string;
};
export const columns: ColumnDef<CompanyData>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("no")}
</h4>
</div>
</div>
),
},
{
accessorKey: "title",
header: "Judul",
cell: ({ row }) => (
<div className="flex items-center gap-5">
<div className="flex-1 text-start">
<h4 className="text-sm font-medium text-default-600 whitespace-nowrap mb-1">
{row.getValue("title")}
</h4>
</div>
</div>
),
},
{
accessorKey: "categoryName",
header: "Kategori",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("categoryName")}</span>
),
},
{
accessorKey: "createdAt",
header: "Tanggal Unggah",
cell: ({ row }) => {
const createdAt = row.getValue("createdAt") as
| string
| number
| undefined;
const formattedDate =
createdAt && !isNaN(new Date(createdAt).getTime())
? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss")
: "-";
return <span className="whitespace-nowrap">{formattedDate}</span>;
},
},
{
accessorKey: "creatorGroup",
header: "Sumber ",
cell: ({ row }) => (
<span className="whitespace-nowrap">{row.getValue("creatorGroup")}</span>
),
},
{
accessorKey: "publishedOn",
header: "Penempatan File",
cell: ({ row }) => {
const isPublish = row.original.isPublish;
const isPublishOnPolda = row.original.isPublishOnPolda;
let displayText = "-";
if (isPublish && !isPublishOnPolda) {
displayText = "Mabes";
} else if (isPublish && isPublishOnPolda) {
displayText = "Mabes & Polda";
} else if (!isPublish && isPublishOnPolda) {
displayText = "Polda";
}
return (
<div className="text-center whitespace-nowrap" title={displayText}>
{displayText}
</div>
);
},
},
{
accessorKey: "isDone",
header: "Status",
cell: ({ row }) => {
const isDone = row.getValue<boolean>("isDone");
return (
<div>
<Button
size="sm"
color="success"
variant="outline"
className={` btn btn-sm ${
isDone ? "btn-outline-success" : "btn-outline-primary"
} pill-btn ml-1`}
>
{isDone ? "Selesai" : "Aktif"}
</Button>
</div>
);
},
},
{
id: "actions",
accessorKey: "action",
header: "Actions",
enableHiding: false,
cell: ({ row }) => {
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">
<a href="/en/task/detail/[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>
</a>
<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>
);
},
},
];
const TableTeks = () => {
const [sorting, setSorting] = React.useState<SortingState>([]);
@ -35,9 +205,29 @@ const TableTeks = () => {
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [imageTable, setImageTable] = React.useState<CompanyData[]>([]);
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0, // Halaman pertama
pageSize: 10, // Jumlah baris per halaman
});
const [totalPage, setTotalPage] = React.useState<number>(1);
const [limit, setLimit] = React.useState<number>(10); // Jumlah baris per halaman
const [search, setSearch] = React.useState<string>("");
const userId = getCookiesDecrypt("uie");
const userLevelId = getCookiesDecrypt("ulie");
const [categories, setCategories] = React.useState();
const [categoryFilter, setCategoryFilter] = React.useState([]);
const [statusFilter, setStatusFilter] = React.useState([]);
const [startDateString, setStartDateString] = React.useState("");
const [endDateString, setEndDateString] = React.useState("");
const [filterByCreator, setFilterByCreator] = React.useState("");
const [filterBySource, setFilterBySource] = React.useState("");
const roleId = getCookiesDecrypt("urie");
const table = useReactTable({
data,
data: imageTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -55,6 +245,59 @@ const TableTeks = () => {
},
});
React.useEffect(() => {
initState();
}, [limit]);
async function initState() {
try {
const isForSelf = Number(roleId) === 4;
const res = await listDataTeks(
pagination.pageSize, // Ambil nilai dari pagination.pageSize
pagination.pageIndex + 1, // API sering menggunakan page 1-based
isForSelf,
!isForSelf,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1")
? "1,2"
: statusFilter?.sort().join(","),
statusFilter?.sort().join(",").includes("1") ? userLevelId : "",
filterByCreator,
filterBySource,
startDateString,
endDateString
);
setupData(res.data?.data);
} catch (error) {
console.error("Error fetching tasks:", error);
}
}
function setupData(rawData: {
content: any[];
totalPages: number;
totalElements: number;
}) {
if (rawData?.content) {
const data: CompanyData[] = rawData.content.map((item, index) => ({
no: pagination.pageIndex * pagination.pageSize + index + 1,
title: item.title,
categoryName: item.categoryName,
creatorGroup: item.creatorGroup,
createdAt: item.createdAt,
isDone: item.isDone,
publishedOn: item.publishedOn,
isPublish: item.isPublish,
isPublishOnPolda: item.isPublishOnPolda,
}));
setImageTable(data);
setTotalPage(rawData.totalPages);
console.log(data, "dataImage");
}
}
return (
<div className="w-full">
<div className="flex items-center py-4 px-5">
@ -72,22 +315,20 @@ const TableTeks = () => {
</div>
</div>
<Table>
<Table className="overflow-hidden">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
<TableRow key={headerGroup.id} className="bg-default-200">
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
@ -97,6 +338,7 @@ const TableTeks = () => {
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className="h-[75px]"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
@ -114,7 +356,41 @@ const TableTeks = () => {
)}
</TableBody>
</Table>
<TablePagination table={table} />
<div className="flex items-center justify-center py-4 gap-2 flex-none">
<Button
variant="outline"
size="icon"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
className="w-8 h-8"
>
<ChevronLeft className="w-4 h-4" />
</Button>
{table.getPageOptions().map((page, pageIndex) => (
<Button
key={`basic-data-table-${pageIndex}`}
onClick={() => table.setPageIndex(pageIndex)}
size="icon"
className="w-8 h-8"
variant={
table.getState().pagination.pageIndex === pageIndex
? "default"
: "outline"
}
>
{page + 1}
</Button>
))}
<Button
variant="outline"
size="icon"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
className="w-8 h-8"
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
</div>
);
};

View File

@ -1,11 +1,23 @@
"use client";
import { Card, CardContent } from "@/components/ui/card";
import TaskTable from "./table-task/task-table";
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 } from "@/lib/utils";
import React, { useEffect } from "react";
const TaskPage = () => {
useEffect(() => {
function initState() {
checkAuthorization("admin"); // Specify the page, e.g., "admin" or another value
checkLoginSession();
}
initState();
}, []);
const TaskPage = async () => {
return (
<div>
<SiteBreadcrumb />

View File

@ -51,6 +51,13 @@ import { title } from "process";
import search from "../../app/chat/components/search";
import { format } from "date-fns";
import { listTask } from "@/service/task";
import { getCookiesDecrypt } from "@/lib/utils";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
} from "@/components/ui/select";
export type CompanyData = {
no: number;
@ -202,9 +209,13 @@ const TaskTable = () => {
});
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [limit, setLimit] = React.useState(100);
const [limit, setLimit] = React.useState(10);
const [search, setSearch] = React.useState(title);
const userId = getCookiesDecrypt("uie");
const userLevelNumber = getCookiesDecrypt("ulne");
const userRoleId = getCookiesDecrypt("urie");
const table = useReactTable({
data: taskTable,
columns,
@ -261,6 +272,18 @@ const TaskTable = () => {
/>
</InputGroup>
</div>
<div className="col-sm-2 col-xs-4 my-1">
{/* <Select onValueChange={handleLimit} name="limit">
<SelectContent>
<SelectGroup>
<SelectItem value="10">1-10 Data</SelectItem>
<SelectItem value="20">1-20 Data</SelectItem>
<SelectItem value="25">1-25 Data</SelectItem>
<SelectItem value="50">1-50 Data</SelectItem>
</SelectGroup>
</SelectContent>
</Select> */}
</div>
<div className="flex-none">
<Input
placeholder="Filter Status..."

View File

@ -1,5 +1,4 @@
"use client";
import SearchSection from "@/components/landing-page/SearchSection";
import NewContent from "@/components/landing-page/new-content";
import PopularContent from "@/components/landing-page/popular-content";

View File

@ -20,10 +20,6 @@ import {
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import JoditEditor from "jodit-react";
import { type } from "os";
import loading from "@/app/[locale]/(protected)/app/projects/loading";
import { request } from "http";
import { error } from "@/lib/utils";
import { createTask } from "@/service/task";
const taskSchema = z.object({

View File

@ -1,5 +1,10 @@
import React from "react";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { FiFile, FiImage, FiMusic, FiYoutube } from "react-icons/fi";
const SearchSection = () => {
@ -8,10 +13,15 @@ const SearchSection = () => {
<div className="max-w-screen-lg mx-auto text-center">
{/* Heading */}
<h1 className="text-2xl md:text-3xl font-bold text-gray-800 dark:text-white">
<span className="text-[#bb3523] dark:text-white">Eksplorasi</span> dan <span className="text-[#bb3523] dark:text-white">Download</span> Liputan Resmi Kami
<span className="text-[#bb3523] dark:text-white">Eksplorasiii</span>{" "}
dan <span className="text-[#bb3523] dark:text-white">Download</span>{" "}
Liputan Resmi Kami
</h1>
<div className="w-full h-1 bg-[#bb3523] mx-auto mt-2"></div>
<p className="text-sm md:text-base text-gray-500 dark:text-gray-100 mt-4">Liputan resmi yang bersumber dari kegiatan Polri di Mabes dan Polda seluruh Indonesia</p>
<p className="text-sm md:text-base text-gray-500 dark:text-gray-100 mt-4">
Liputan resmi yang bersumber dari kegiatan Polri di Mabes dan Polda
seluruh Indonesia
</p>
{/* Search Form */}
<div className="mt-6 flex flex-col md:flex-row justify-center gap-4">
@ -20,19 +30,39 @@ const SearchSection = () => {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<a className="text-black dark:text-white flex flex-row justify-center items-center ml-5 cursor-pointer">
<svg className="mx-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
className="mx-2"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20 7.5H5C4.6023 7.5004 4.221 7.65856 3.93978 7.93978C3.65856 8.221 3.5004 8.6023 3.5 9V19.5C3.5004 19.8977 3.65856 20.279 3.93978 20.5602C4.221 20.8414 4.6023 20.9996 5 21H20C20.3977 20.9996 20.779 20.8414 21.0602 20.5602C21.3414 20.279 21.4996 19.8977 21.5 19.5V9C21.4996 8.6023 21.3414 8.221 21.0602 7.93978C20.779 7.65856 20.3977 7.5004 20 7.5ZM10.25 17.25V11.25L15.5 14.25L10.25 17.25ZM5 4.5H20V6H5V4.5ZM6.5 1.5H18.5V3H6.5V1.5Z"
fill="currentColor"
/>
</svg>
Konten
<svg className="flex items-center justify-center" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="currentColor" fill-rule="evenodd" d="m6 7l6 6l6-6l2 2l-8 8l-8-8z" />
<svg
className="flex items-center justify-center"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
fill-rule="evenodd"
d="m6 7l6 6l6-6l2 2l-8 8l-8-8z"
/>
</svg>
</a>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="p-0 rounded-md overflow-hidden">
<DropdownMenuContent
align="end"
className="p-0 rounded-md overflow-hidden"
>
<DropdownMenuItem className="flex items-center gap-1.5 p-2 border-b text-default-600 group focus:bg-default focus:text-primary-foreground rounded-none group">
<span className="text-default-700c flex flex-row justify-center items-center group-hover:text-primary-foreground">
<FiYoutube className="mr-2" />
@ -64,18 +94,29 @@ const SearchSection = () => {
{/* Search Input */}
<div className="flex items-center flex-1 border border-gray-300 rounded-lg overflow-hidden">
<span className="material-icons text-black dark:text-white px-4">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m19.6 21l-6.3-6.3q-.75.6-1.725.95T9.5 16q-2.725 0-4.612-1.888T3 9.5t1.888-4.612T9.5 3t4.613 1.888T16 9.5q0 1.1-.35 2.075T14.7 13.3l6.3 6.3zM9.5 14q1.875 0 3.188-1.312T14 9.5t-1.312-3.187T9.5 5T6.313 6.313T5 9.5t1.313 3.188T9.5 14"
/>
</svg>
</span>
<input type="text" placeholder="Pencarian" className="w-full py-2 px-2 text-sm text-gray-700 dark:text-gray-100 focus:outline-none" />
<input
type="text"
placeholder="Pencarian"
className="w-full py-2 px-2 text-sm text-gray-700 dark:text-gray-100 focus:outline-none"
/>
</div>
{/* Button */}
<button className="px-6 py-2 bg-[#bb3523] text-white rounded-lg hover:bg-red-700">Cari Liputan &gt;</button>
<button className="px-6 py-2 bg-[#bb3523] text-white rounded-lg hover:bg-red-700">
Cari Liputan &gt;
</button>
</div>
</div>
</section>

View File

@ -4,65 +4,184 @@ import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Link } from "@/i18n/routing";
import Cookies from "js-cookie";
import { Icon } from "@/components/ui/icon";
import { useForm, SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { cn } from "@/lib/utils";
import { cn, setCookiesEncrypt } from "@/lib/utils";
import { Loader2 } from "lucide-react";
import { loginUser } from "@/action/auth-action";
import { getProfile, setLogin } from "@/service/auth";
import { toast } from "sonner";
import { useRouter } from "@/components/navigation";
import { Link, useRouter } from "@/components/navigation";
import { warning } from "@/lib/swal";
// Schema validasi menggunakan zod
const schema = z.object({
email: z.string().email({ message: "Your email is invalid." }),
password: z.string().min(4),
username: z.string().min(1, { message: "Judul diperlukan" }),
password: z
.string()
.min(4, { message: "Password must be at least 4 characters." }),
});
// Tipe untuk form values
type LoginFormValues = {
username: string;
password: string;
};
const LoginForm = () => {
const [isPending, startTransition] = React.useTransition();
const router = useRouter();
const [passwordType, setPasswordType] = React.useState("password");
const togglePasswordType = () => {
if (passwordType === "text") {
setPasswordType("password");
} else if (passwordType === "password") {
setPasswordType("text");
}
setPasswordType((prevType) =>
prevType === "password" ? "text" : "password"
);
};
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
} = useForm<LoginFormValues>({
resolver: zodResolver(schema),
mode: "all",
defaultValues: {
email: "dashcode@codeshaper.net",
password: "password",
},
});
const [isVisible, setIsVisible] = React.useState(false);
const toggleVisibility = () => setIsVisible(!isVisible);
const onSubmit = (data: z.infer<typeof schema>) => {
// Fungsi submit form
const onSubmit: SubmitHandler<LoginFormValues> = async (data) => {
startTransition(async () => {
try {
const response = await loginUser(data);
const response = await setLogin({
...data,
grant_type: "password",
client_id: "mediahub-app",
});
if (!!response.error) {
toast("Event has been created", {
description: "Sunday, December 03, 2023 at 9:00 AM",
});
if (response.error) {
toast.error("Username / Password Tidak Sesuai");
} else {
router.push("/dashboard");
toast.success("Successfully logged in");
const { access_token } = response.data;
const { refresh_token } = response.data;
const dateTime = new Date();
const newTime = dateTime.getTime() + 10 * 60 * 1000;
Cookies.set("access_token", access_token, {
expires: 1,
});
Cookies.set("refresh_token", refresh_token, {
expires: 1,
});
Cookies.set("time_refresh", new Date(newTime).toISOString(), {
expires: 1,
});
Cookies.set("is_first_login", String(true), {
secure: true,
sameSite: "strict",
});
const profile = await getProfile(access_token);
console.log("PROFILE : ", profile?.data?.data);
if (
profile?.data?.data?.isInternational == true ||
profile?.data?.data?.isActive == false ||
profile?.data?.data?.isDelete == true
) {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
warning(
"Akun Anda tidak dapat digunakan untuk masuk ke MediaHub Polri",
"/auth/login"
);
} else {
Cookies.set("home_path", profile.data?.data?.homePath, {
expires: 1,
});
Cookies.set(
"profile_picture",
profile.data?.data?.profilePictureUrl,
{
expires: 1,
}
);
Cookies.set("state", profile.data?.data?.userLevel?.name, {
expires: 1,
});
setCookiesEncrypt("uie", profile.data.data?.id, {
expires: 1,
});
setCookiesEncrypt("urie", profile.data.data?.roleId, {
expires: 1,
});
setCookiesEncrypt("urne", profile.data.data?.role?.name, {
expires: 1,
});
setCookiesEncrypt("ulie", profile.data.data?.userLevel?.id, {
expires: 1,
});
setCookiesEncrypt(
"ulplie",
profile.data.data?.userLevel?.parentLevelId,
{
expires: 1,
}
);
setCookiesEncrypt(
"ulne",
profile.data.data?.userLevel?.levelNumber,
{
expires: 1,
}
);
setCookiesEncrypt("ufne", profile.data.data?.fullname, {
expires: 1,
});
setCookiesEncrypt("ulnae", profile.data.data?.userLevel?.name, {
expires: 1,
});
setCookiesEncrypt("uinse", profile.data.data?.instituteId, {
expires: 1,
});
if (
Number(profile.data.data?.roleId) == 2 ||
Number(profile.data.data?.roleId) == 3 ||
Number(profile.data.data?.roleId) == 4 ||
Number(profile.data.data?.roleId) == 9 ||
Number(profile.data.data?.roleId) == 10 ||
Number(profile.data.data?.roleId) == 11 ||
Number(profile.data.data?.roleId) == 12
) {
if (
profile.data.data?.userLevel?.id == 761 ||
profile.data.data?.userLevel?.parentLevelId == 761
) {
window.location.href = "/admin/welcome";
// router.push('/admin/dashboard');
Cookies.set("status", "login", {
expires: 1,
});
} else {
window.location.href = "/en/dashboard";
// router.push('/admin/dashboard');
Cookies.set("status", "login", {
expires: 1,
});
}
} else {
window.location.href = "/";
Cookies.set("status", "login", {
expires: 1,
});
}
}
}
} catch (err: any) {
toast.error(err.message);
toast.error(err.message || "An unexpected error occurred.");
}
});
};
@ -70,41 +189,39 @@ const LoginForm = () => {
return (
<form onSubmit={handleSubmit(onSubmit)} className="mt-5 2xl:mt-7 space-y-4">
<div className="space-y-2">
<Label htmlFor="email" className=" font-medium text-default-600">
Email{" "}
<Label htmlFor="username" className="font-medium text-default-600">
Username
</Label>
<Input
size="lg"
disabled={isPending}
{...register("email")}
type="email"
id="email"
{...register("username")}
id="username"
type="username"
className={cn("", {
"border-destructive ": errors.email,
"border-destructive": errors.username,
})}
/>
{errors.username?.message && (
<div className="text-destructive mt-2 text-sm">
{errors.username.message}
</div>
)}
</div>
{errors.email && (
<div className=" text-destructive mt-2 text-sm">
{errors.email.message}
</div>
)}
<div className="mt-3.5 space-y-2">
<Label htmlFor="password" className="mb-2 font-medium text-default-600">
Password{" "}
Password
</Label>
<div className="relative">
<Input
size="lg"
disabled={isPending}
{...register("password")}
type={passwordType}
id="password"
className="peer "
placeholder=" "
type={passwordType}
className="peer"
/>
<div
className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer"
onClick={togglePasswordType}
@ -119,12 +236,12 @@ const LoginForm = () => {
)}
</div>
</div>
{errors.password?.message && (
<div className="text-destructive mt-2 text-sm">
{errors.password.message}
</div>
)}
</div>
{errors.password && (
<div className=" text-destructive mt-2 text-sm">
{errors.password.message}
</div>
)}
<div className="flex justify-between">
<div className="flex gap-2 items-center">
@ -145,4 +262,5 @@ const LoginForm = () => {
</form>
);
};
export default LoginForm;

View File

@ -123,7 +123,6 @@ export async function getAPIInterceptor(url: any) {
data: null,
};
}
// Fungsi postAPIInterceptor
export async function postAPIInterceptor(url: string, data: any) {
const response = await axiosInterceptor

92
lib/swal.ts Normal file
View File

@ -0,0 +1,92 @@
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
const MySwal = withReactContent(Swal);
const Toast = MySwal.mixin({
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.addEventListener("mouseenter", Swal.stopTimer);
toast.addEventListener("mouseleave", Swal.resumeTimer);
},
});
export function loading(msg?: any) {
let timerInterval: any;
MySwal.fire({
title: msg || "Loading...",
allowOutsideClick: false,
timerProgressBar: true,
didOpen: () => {
MySwal.showLoading();
timerInterval = setInterval(() => {}, 100);
},
willClose: () => {
clearInterval(timerInterval);
},
});
}
export function error(msg?: any) {
MySwal.fire({
icon: "error",
title: "Failed...",
text: msg || "Unknown Error",
});
}
export function successRouter(redirect: string, router?: any) {
MySwal.fire({
title: "Success!",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "Ok",
allowOutsideClick: false,
}).then((result) => {
if (result.isConfirmed) {
router.push(redirect);
}
});
}
export function success(title: string) {
MySwal.fire({
title: title || "Success!",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
return true;
}
});
}
export function close() {
MySwal.close();
}
export function warning(text: string, redirect: string, router?: any) {
MySwal.fire({
title: text,
icon: "warning",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push(redirect);
}
});
}
export function successToast(title: string, text: string) {
Toast.fire({
icon: "error",
title: title,
text: text,
});
}

View File

@ -36,6 +36,47 @@ export const hexToRGB = (hex: any, alpha?: number): any => {
}
};
export function getCookiesDecrypt(param: any) {
const cookiesEncrypt = Cookies.get(param);
try {
if (cookiesEncrypt != undefined) {
const output = CryptoJS.AES.decrypt(
cookiesEncrypt.toString(),
`${param}_EncryptKey@mediahub`
).toString(CryptoJS.enc.Utf8);
if (output.startsWith('"')) {
return output.slice(1, -1);
}
return output;
}
} catch (e) {
console.log("Error", cookiesEncrypt);
}
}
export function successToast(title: string, text: string) {
Toast.fire({
icon: "error",
title: title,
text: text,
});
}
export function setCookiesEncrypt(
param: string,
data: any,
options?: Cookies.CookieAttributes
) {
// Enkripsi data
const cookiesEncrypt = CryptoJS.AES.encrypt(
JSON.stringify(data),
`${param}_EncryptKey@mediahub`
).toString(); // Tambahkan .toString() di sini
// Simpan data terenkripsi di cookie
Cookies.set(param, cookiesEncrypt, options);
}
export function checkAuthorization(page: any) {
const roleId = getCookiesDecrypt("urie");
const levelNumber = getCookiesDecrypt("ulne");
@ -64,44 +105,3 @@ export function checkLoginSession() {
};
// doCheckSession(data);
}
export function getCookiesDecrypt(param: any) {
const cookiesEncrypt = Cookies.get(param);
try {
if (cookiesEncrypt != undefined) {
const output = CryptoJS.AES.decrypt(
cookiesEncrypt.toString(),
`${param}_EncryptKey@mediahub`
).toString(CryptoJS.enc.Utf8);
if (output.startsWith('"')) {
return output.slice(1, -1);
}
return output;
}
} catch (e) {
console.log("Error", cookiesEncrypt);
}
}
export function successToast(title: string, text: string) {
Toast.fire({
icon: "error",
title: title,
text: text,
});
}
export function setCookiesEncrypt(param: any, data: any) {
// Enkripsi data
const cookiesEncrypt = CryptoJS.AES.encrypt(
JSON.stringify(data),
`${param}_EncryptKey@mediahub`
).toString(); // Tambahkan .toString() di sini
// Simpan data terenkripsi di cookie
Cookies.set(param, cookiesEncrypt, { expires: 1 });
}
export function error(msg: any) {
MySwal.fire("Gagal", msg, "error");
}

View File

@ -3,92 +3,30 @@ import Cookies from "js-cookie";
import { getAPI, postAPI, postAPIWithJson } from "../config/api";
import { getAPIDummy } from "./http-config/axiosCustom";
export async function setLogin(data) {
const url = "signin";
return postAPI({ url, data });
import {
httpGetInterceptorWithToken,
httpPostInterceptor,
} from "./http-config/http-interceptor-service";
export async function setLogin(data: any) {
const pathUrl = "signin";
return postAPI(pathUrl, data);
}
export async function getProfile(token) {
export async function getProfile(token: any) {
const url = "users/info";
return getAPI({ url, token });
return getAPI(url, token);
}
export async function saveSession(data) {
const url = "users/save-session";
return postAPIWithJson({ url, data });
}
// export async function setLogin(data: any) {
// const pathUrl = `signin`;
// const headers = {
// "content-type": "application/json",
// };
// return await httpPost(pathUrl, headers, data);
// }
export async function checkSession(data) {
const url = "users/check-session";
return postAPIWithJson({ url, data });
}
export async function listProvince() {
const url = "public/users/provinces";
return getAPI({ url });
}
export async function listCity(id) {
const url = `public/users/cities?provId=${id}`;
return getAPI({ url });
}
export async function listDistricts(id) {
const url = `public/users/districts?cityId=${id}`;
return getAPI({ url });
}
export async function listInstitusi(roleId) {
const url = `public/users/institutes?categoryRoleId=${roleId}`;
return getAPI({ url });
}
export async function listRole() {
const url = "public/users/roles";
return getAPI({ url });
}
export async function refreshToken() {
const url = "signin";
const data = {
grant_type: "refresh_token",
client_id: "mediahub-app",
refresh_token: Cookies.get("refresh_token"),
};
return postAPI({ url, data });
}
export async function postRegistration(data) {
const url = "public/users/save";
return postAPIWithJson({ url, data });
}
export async function saveInstitutes(data) {
const url = "public/users/save-institutes";
return postAPIWithJson({ url, data });
}
export async function forgotPassword(username) {
const url = `forgot-password?username=${username}`;
return postAPIWithJson({ url });
}
export async function getDataByNIK(reqid, nik) {
const url = `http://spitpolri.com/api/back_end/get_ktp?reqid=${reqid}&nik=${nik}`;
return getAPIDummy({ url });
}
export async function getDataByNRP(reqid, nrp) {
const url = `http://spitpolri.com/api/back_end/get_nrp?reqid=${reqid}&nrp=${nrp}`;
return getAPIDummy({ url });
}
export async function getDataJournalist(cert) {
const url = `public/users/search-journalist?cert=${cert}`;
return getAPI({ url });
}
export async function getDataPersonil(nrp) {
const url = `public/users/search-personil?nrp=${nrp}`;
return getAPI({ url });
export async function userInfo(token: any) {
const pathUrl = `users/info`;
return await httpGetInterceptorWithToken(pathUrl, token);
}

View File

@ -1,19 +0,0 @@
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
export async function listDataImage(
page: any,
limit: any,
isForSelf: any,
isApproval: any,
categoryFilter: any,
statusFilter: any,
needApprovalFromLevel: any,
creator: any,
source: any,
startDate: any,
endDate: any
) {
return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}`
);
}

View File

@ -0,0 +1,85 @@
import { getAPIInterceptor } from "@/config/api";
import { httpGetInterceptor } from "../http-config/http-interceptor-service";
export async function listDataImage(
page: any,
limit: any,
isForSelf: any,
isApproval: any,
categoryFilter: any,
statusFilter: any,
needApprovalFromLevel: any,
creator: any,
source: any,
startDate: any,
endDate: any
) {
return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}`
);
}
export async function listDataVideo(
page: any,
limit: any,
isForSelf: any,
isApproval: any,
categoryFilter: any,
statusFilter: any,
needApprovalFromLevel: any,
creator: any,
source: any,
startDate: any,
endDate: any
) {
return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=2&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}`
);
}
export async function listDataTeks(
page: any,
limit: any,
isForSelf: any,
isApproval: any,
categoryFilter: any,
statusFilter: any,
needApprovalFromLevel: any,
creator: any,
source: any,
startDate: any,
endDate: any
) {
return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=3&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}`
);
}
export async function listDataAudio(
page: any,
limit: any,
isForSelf: any,
isApproval: any,
categoryFilter: any,
statusFilter: any,
needApprovalFromLevel: any,
creator: any,
source: any,
startDate: any,
endDate: any
) {
return await httpGetInterceptor(
`media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=4&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}`
);
}
export async function listSPIT(
page: any,
limit: any,
title = "",
isPublish: any
) {
return await httpGetInterceptor(
`media/spit/pagination?enablePage=1&page=${page}&size=${limit}&sort=desc&sortBy=contentTitleId&title=${title}&isPublish=${isPublish}`
);
}

View File

@ -40,6 +40,8 @@ axiosInterceptorInstance.interceptors.response.use(
originalRequest._retry = true;
const data = {
refreshToken: refreshToken,
grant_type: "refresh_token",
client_id: "mediahub-app",
};
const res = await login(data);
if (res.data?.data?.access_token) {

View File

@ -1,70 +1,100 @@
import axiosInstance from "@/config/axiosInstance";
import axiosBaseInstance from "./axios-base-instance";
import axios from "axios";
import Cookies from "js-cookie";
import qs from "qs";
export async function httpPost(pathUrl: any, headers: any, data?: any) {
const response = await axiosBaseInstance
.post(pathUrl, data, { headers })
.catch(function (error: any) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
// export async function httpPost(pathUrl: any, headers: any, data?: any) {
// const response = await axiosBaseInstance
// .post(pathUrl, data, { headers })
// .catch(function (error: any) {
// console.log(error);
// return error.response;
// });
// console.log("Response base svc : ", response);
// if (response?.status == 200 || response?.status == 201) {
// return {
// error: false,
// message: "success",
// data: response?.data,
// };
// } else {
// return {
// error: true,
// message: response?.data?.message || response?.data || null,
// data: null,
// };
// }
// }
const baseURL = "https://netidhub.com/api/";
const tokenAuth = Cookies.get("access_token")
? Cookies.get("access_token")
: null;
export async function postAPI(url: any, data: any) {
const headers = {
Authorization: `Bearer ${tokenAuth}`,
};
const response = await axiosInstance
.post(url, qs.stringify(data), { headers })
.catch((error) => error.response);
if (response?.status > 300) {
return {
error: true,
message: response?.data.error_description,
data: null,
};
}
return {
error: false,
message: "success",
data: response?.data,
};
}
export async function httpGet(pathUrl: any, headers: any) {
const response = await axiosBaseInstance
.get(pathUrl, { headers })
.catch(function (error: any) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
const response = await axiosBaseInstance
.get(pathUrl, { headers })
.catch(function (error: any) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
export async function httpPut(pathUrl: any, headers: any, data?: any) {
const response = await axiosBaseInstance
.put(pathUrl, data, { headers })
.catch(function (error: any) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}
const response = await axiosBaseInstance
.put(pathUrl, data, { headers })
.catch(function (error: any) {
console.log(error);
return error.response;
});
console.log("Response base svc : ", response);
if (response?.status == 200 || response?.status == 201) {
return {
error: false,
message: "success",
data: response?.data,
};
} else {
return {
error: true,
message: response?.data?.message || response?.data || null,
data: null,
};
}
}

View File

@ -1,3 +1,4 @@
import { title } from "process";
import {
deleteAPIInterceptor,
getAPIInterceptor,
@ -8,8 +9,15 @@ import {
httpPostInterceptor,
} from "./http-config/http-interceptor-service";
export async function listTask(size: number, page: number) {
return await httpGetInterceptor(`assignment/list?size=${size}&page=${page}`);
// export async function listTask(size: number, page: number) {
// return await httpGetInterceptor(
// `assignment/list?enablePage=1&title=${title}&size=${limit}&page=${page}`
// );
// }
export async function listTask(page: any, limit: any) {
const url = `assignment/list?size=${limit}&page=${page}`;
return getAPIInterceptor(url);
}
export async function createTask(data: any) {