feat: update all features on kurator, edit layout, etc
This commit is contained in:
parent
71bebd3a86
commit
227f33932b
|
|
@ -35,3 +35,6 @@ yarn-error.log*
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
#idea intellij
|
||||||
|
/.idea/
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "question",
|
||||||
|
header: "Question",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("question")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "answer",
|
||||||
|
header: "Answer",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("answer")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
PaginationState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
MoreVertical,
|
||||||
|
Search,
|
||||||
|
SquarePen,
|
||||||
|
Trash2,
|
||||||
|
TrendingDown,
|
||||||
|
TrendingUp,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
|
import { getFaqList } from "@/service/master/faq";
|
||||||
|
import columns from "./column";
|
||||||
|
|
||||||
|
const FaqTable = () => {
|
||||||
|
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>([]);
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({});
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({});
|
||||||
|
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
const [page, setPage] = React.useState(1);
|
||||||
|
const [totalPage, setTotalPage] = React.useState(1);
|
||||||
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: dataTable,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
onPaginationChange: setPagination,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
pagination,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
setPage(Number(pageFromUrl));
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [page, limit]);
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
const res = await getFaqList();
|
||||||
|
const contentData = res.data?.data;
|
||||||
|
contentData.forEach((item: any, index: number) => {
|
||||||
|
item.no = (page - 1) * limit + index + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
|
setDataTable(contentData);
|
||||||
|
setTotalData(contentData?.totalElements || contentData?.length);
|
||||||
|
setTotalPage(contentData?.totalPages || 1);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tasks:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<div className="flex justify-between items-center px-6">
|
||||||
|
<div>
|
||||||
|
<InputGroup merged>
|
||||||
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter Status..."
|
||||||
|
value={
|
||||||
|
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
table.getColumn("status")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm "
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table className="overflow-hidden mt-3">
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
className="h-[75px]"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FaqTable;
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import FaqTable from "./components/table";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { Plus } from "lucide-react";
|
||||||
import BlogTable from "./table/blog-table";
|
|
||||||
|
|
||||||
const BlogPage = async () => {
|
const FaqPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
<Card>
|
||||||
<div className="flex flex-row justify-between items-center">
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
Table Indeks
|
FAQ Data
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-none">
|
||||||
<Button color="primary" className="text-white">
|
<Button
|
||||||
<UploadIcon />
|
fullWidth
|
||||||
Tambah Indeks
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New FAQ
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</CardTitle>
|
||||||
<Card>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<BlogTable />
|
<FaqTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -32,4 +37,4 @@ const BlogPage = async () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default FaqPage;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const metadata = {
|
import { Metadata } from "next";
|
||||||
title: "Blog",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Dashboard Media Hub",
|
||||||
|
description: "Dashboard Media Hub.",
|
||||||
|
};
|
||||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,122 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
'use client'
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import {
|
||||||
import { UploadIcon } from "lucide-react";
|
Accordion,
|
||||||
import BlogTable from "./table/blog-table";
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
|
import {Card, CardContent} from "@/components/ui/card";
|
||||||
|
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
|
||||||
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
|
import {getKnowledgeBaseCategoryList, getKnowledgeBaseList} from "@/service/master/knowledge-base";
|
||||||
|
import React from "react";
|
||||||
|
import {Plus} from "lucide-react";
|
||||||
|
import {Button} from "@/components/ui/button";
|
||||||
|
|
||||||
|
const KnowledgeBase = () => {
|
||||||
|
const [categories, setCategories] = React.useState<any>([]);
|
||||||
|
const [questions, setQuestions] = React.useState<any>([]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchCategoryList();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function fetchCategoryList() {
|
||||||
|
const response = await getKnowledgeBaseCategoryList();
|
||||||
|
const data = response.data?.data;
|
||||||
|
if (data) {
|
||||||
|
setCategories(data);
|
||||||
|
fetchQuestions(data[0]?.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchQuestions = async (id: number) => {
|
||||||
|
const response = await getKnowledgeBaseList(id);
|
||||||
|
const data = response.data?.data;
|
||||||
|
if (data) {
|
||||||
|
setQuestions(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const BlogPage = async () => {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb/>
|
<SiteBreadcrumb/>
|
||||||
<div className="space-y-4">
|
<Tabs defaultValue={`category-0`}>
|
||||||
<Card className="py-4 px-3">
|
<div className="grid grid-cols-12 gap-6">
|
||||||
<div className="flex flex-row justify-between items-center">
|
<Card className="lg:col-span-3 md:col-span-5 col-span-12 h-max">
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
<CardContent className=" p-6">
|
||||||
Table Indeks
|
<TabsList className="md:flex-col gap-2 flex-wrap md:items-start justify-start">
|
||||||
</div>
|
<Button
|
||||||
<div>
|
fullWidth
|
||||||
<Button color="primary" className="text-white">
|
size="lg"
|
||||||
<UploadIcon />
|
className="dark:bg-background dark:ring-background dark:text-foreground"
|
||||||
Tambah Indeks
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
Add Category
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
{categories?.map((category: any, index: number) => (
|
||||||
</div>
|
<TabsTrigger
|
||||||
</Card>
|
key={index}
|
||||||
<Card>
|
value={`category-${index}`}
|
||||||
<CardContent className="p-0">
|
onClick={() => {
|
||||||
<BlogTable />
|
fetchQuestions(category?.id);
|
||||||
|
}}
|
||||||
|
className="data-[state=active]:bg-secondary data-[state=active]:text-default rounded-md px-6 py-3 w-full justify-start"
|
||||||
|
>
|
||||||
|
{category?.name}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
<div className="lg:col-span-9 md:col-span-7 col-span-12 mt-4 lg:mt-0">
|
||||||
|
{categories?.map((cateogry: any, index: number) => (
|
||||||
|
<TabsContent key={index} value={`category-${index}`} className="mt-0">
|
||||||
|
<Accordion type="single" collapsible className="w-full">
|
||||||
|
{questions?.map((question: any) => (
|
||||||
|
<AccordionItem
|
||||||
|
key={question.id}
|
||||||
|
className="dark:bg-secondary bg-white"
|
||||||
|
value={question.id}
|
||||||
|
>
|
||||||
|
<AccordionTrigger
|
||||||
|
className="dark:bg-secondary bg-white data-[state=open]:bg-default-200 data-[state=active]:text-default">
|
||||||
|
{question.question}
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="dark:bg-secondary bg-white">
|
||||||
|
{question.answer}
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Accordion>
|
||||||
|
{questions?.length > 0 &&
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
size="md"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
Add Question
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BlogPage;
|
export default KnowledgeBase;
|
||||||
|
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
ColumnFiltersState,
|
|
||||||
PaginationState,
|
|
||||||
SortingState,
|
|
||||||
VisibilityState,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
getFilteredRowModel,
|
|
||||||
getPaginationRowModel,
|
|
||||||
getSortedRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
Eye,
|
|
||||||
MoreVertical,
|
|
||||||
Search,
|
|
||||||
SquarePen,
|
|
||||||
Trash2,
|
|
||||||
TrendingDown,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
|
|
||||||
export type CompanyData = {
|
|
||||||
no: number;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
createdAt: string;
|
|
||||||
tags: string;
|
|
||||||
statusName: string;
|
|
||||||
};
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
|
||||||
|
|
||||||
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 }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "tags",
|
|
||||||
header: "Tag ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("tags")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "statusName",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return (
|
|
||||||
<span className="whitespace-nowrap text-blue-600">
|
|
||||||
{row.getValue("statusName")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 BlogTable = () => {
|
|
||||||
const [blogTable, setBlogTable] = React.useState<CompanyData[]>([]);
|
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
React.useState<VisibilityState>({});
|
|
||||||
const [rowSelection, setRowSelection] = React.useState({});
|
|
||||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
|
||||||
pageIndex: 0,
|
|
||||||
pageSize: 10,
|
|
||||||
});
|
|
||||||
const [page, setPage] = React.useState(1);
|
|
||||||
const [totalPage, setTotalPage] = React.useState(1);
|
|
||||||
const [limit, setLimit] = React.useState(10);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
|
||||||
data: blogTable,
|
|
||||||
columns,
|
|
||||||
onSortingChange: setSorting,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
|
||||||
getSortedRowModel: getSortedRowModel(),
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onColumnVisibilityChange: setColumnVisibility,
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onPaginationChange: setPagination,
|
|
||||||
state: {
|
|
||||||
sorting,
|
|
||||||
columnFilters,
|
|
||||||
columnVisibility,
|
|
||||||
rowSelection,
|
|
||||||
pagination,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
initState();
|
|
||||||
}, [page, limit]);
|
|
||||||
|
|
||||||
async function initState() {
|
|
||||||
try {
|
|
||||||
const res = await paginationBlog(limit, page);
|
|
||||||
const data = res.data.data.content.map((item: any, index: number) => ({
|
|
||||||
no: (page - 1) * limit + index + 1,
|
|
||||||
title: item.title,
|
|
||||||
categoryName: item.categoryName,
|
|
||||||
tags: item.tags,
|
|
||||||
assignmentType: item.assignmentType?.name || "-",
|
|
||||||
createdAt: item.createdAt,
|
|
||||||
statusName: item.statusName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setBlogTable(data);
|
|
||||||
setTotalPage(res.data.totalPages);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching tasks:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full overflow-x-auto">
|
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
|
||||||
<div>
|
|
||||||
<InputGroup merged>
|
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
<div className="flex-none">
|
|
||||||
<Input
|
|
||||||
placeholder="Filter Status..."
|
|
||||||
value={
|
|
||||||
(table.getColumn("status")?.getFilterValue() as string) ?? ""
|
|
||||||
}
|
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
table.getColumn("status")?.setFilterValue(event.target.value)
|
|
||||||
}
|
|
||||||
className="max-w-sm "
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table className="overflow-hidden">
|
|
||||||
<TableHeader>
|
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
|
||||||
{headerGroup.headers.map((header) => (
|
|
||||||
<TableHead key={header.id}>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</TableHead>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
|
||||||
className="h-[75px]"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell key={cell.id}>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
||||||
No results.
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
<div className="flex items-center justify-end py-4 px-10">
|
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BlogTable;
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -15,10 +15,10 @@ const layout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<LayoutProvider>
|
<LayoutProvider>
|
||||||
<ThemeCustomize />
|
<ThemeCustomize />
|
||||||
{/* <DashCodeHeader /> */}
|
<DashCodeHeader />
|
||||||
<DashCodeSidebar />
|
<DashCodeSidebar />
|
||||||
<LayoutContentProvider>{children}</LayoutContentProvider>
|
<LayoutContentProvider>{children}</LayoutContentProvider>
|
||||||
{/* <DashCodeFooter /> */}
|
<DashCodeFooter />
|
||||||
</LayoutProvider>
|
</LayoutProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
|
||||||
|
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,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuItem,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
const columns: ColumnDef<any>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "no",
|
||||||
|
header: "No",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "referenceNumber",
|
||||||
|
header: "Ticket Number",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("referenceNumber")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "title",
|
||||||
|
header: "Title",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "commentFromUserName",
|
||||||
|
header: "Sender",
|
||||||
|
cell: ({ row }) => <span>{row.getValue("commentFromUserName")}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "type",
|
||||||
|
header: "Channel",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const type = row.getValue("type") as { name: string };
|
||||||
|
return <span>{type?.name}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdBy",
|
||||||
|
header: "Operator",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdBy = row.getValue("createdBy") as { fullname: string };
|
||||||
|
return <span>{createdBy?.fullname}</span>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "createdAt",
|
||||||
|
header: "Tanggal Unggah ",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const statusColors: Record<string, string> = {
|
||||||
|
open: "bg-primary/20 text-primary",
|
||||||
|
close: "bg-success/20 text-success",
|
||||||
|
};
|
||||||
|
const status = row.getValue("status") as { id: number, name: string };;
|
||||||
|
const statusName = status?.name?.toLocaleLowerCase();
|
||||||
|
console.log(statusName);
|
||||||
|
const statusStyles = statusColors[statusName] || "default";
|
||||||
|
if (statusName) {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
className={cn("rounded-full px-5",statusStyles)}
|
||||||
|
>{statusName} </Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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">
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
|
|
@ -43,122 +43,20 @@ import {
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
import { data } from "./data";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
|
||||||
import { paginationBlog } from "@/service/blog/blog";
|
import { paginationBlog } from "@/service/blog/blog";
|
||||||
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
import { ticketingPagination } from "@/service/ticketing/ticketing";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import TablePagination from "../../blog/table/table-pagination";
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import TablePagination from "@/components/table/table-pagination";
|
||||||
export const columns: ColumnDef<any>[] = [
|
import columns from "./columns";
|
||||||
{
|
|
||||||
accessorKey: "no",
|
|
||||||
header: "No",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("no")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "referenceNumber",
|
|
||||||
header: "Ticket Number",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("referenceNumber")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "title",
|
|
||||||
header: "Title",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("title")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "commentFromUserName",
|
|
||||||
header: "Sender",
|
|
||||||
cell: ({ row }) => <span>{row.getValue("commentFromUserName")}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "type",
|
|
||||||
header: "Channel",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const type = row.getValue("type") as { name: string };
|
|
||||||
return <span>{type?.name}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "createdBy",
|
|
||||||
header: "Operator",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const createdBy = row.getValue("createdBy") as { fullname: string };
|
|
||||||
return <span>{createdBy?.fullname}</span>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "createdAt",
|
|
||||||
header: "Tanggal Unggah ",
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<span className="whitespace-nowrap">{row.getValue("createdAt")}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "status",
|
|
||||||
header: "Status",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const statusColors: Record<string, string> = {
|
|
||||||
open: "bg-primary/20 text-primary",
|
|
||||||
close: "bg-success/20 text-success",
|
|
||||||
};
|
|
||||||
const status = row.getValue("status") as { id: number, name: string };;
|
|
||||||
const statusName = status?.name?.toLocaleLowerCase();
|
|
||||||
console.log(statusName);
|
|
||||||
const statusStyles = statusColors[statusName] || "default";
|
|
||||||
if (statusName) {
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
className={cn("rounded-full px-5",statusStyles)}
|
|
||||||
>{statusName} </Badge>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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">
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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 TicketingTable = () => {
|
const TicketingTable = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [ticketData, setTicketData] = React.useState<any[]>([]);
|
|
||||||
|
const [dataTable, setDataTable] = React.useState<any[]>([]);
|
||||||
const [totalData, setTotalData] = React.useState<number>(1);
|
const [totalData, setTotalData] = React.useState<number>(1);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
|
@ -176,7 +74,7 @@ const TicketingTable = () => {
|
||||||
const [limit, setLimit] = React.useState(10);
|
const [limit, setLimit] = React.useState(10);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: ticketData,
|
data: dataTable,
|
||||||
columns,
|
columns,
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
onColumnFiltersChange: setColumnFilters,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
|
@ -218,7 +116,7 @@ const TicketingTable = () => {
|
||||||
|
|
||||||
console.log("contentData : ", contentData);
|
console.log("contentData : ", contentData);
|
||||||
|
|
||||||
setTicketData(contentData);
|
setDataTable(contentData);
|
||||||
setTotalData(data?.totalElements);
|
setTotalData(data?.totalElements);
|
||||||
setTotalPage(data?.totalPages);
|
setTotalPage(data?.totalPages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -228,7 +126,7 @@ const TicketingTable = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full overflow-x-auto">
|
<div className="w-full overflow-x-auto">
|
||||||
<div className="flex justify-between items-center py-4 px-5">
|
<div className="flex justify-between items-center px-5">
|
||||||
<div>
|
<div>
|
||||||
<InputGroup merged>
|
<InputGroup merged>
|
||||||
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
|
||||||
|
|
@ -254,7 +152,7 @@ const TicketingTable = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Table className="overflow-hidden">
|
<Table className="overflow-hidden mt-3">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow key={headerGroup.id} className="bg-default-200">
|
<TableRow key={headerGroup.id} className="bg-default-200">
|
||||||
|
|
@ -295,7 +193,7 @@ const TicketingTable = () => {
|
||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
|
<TablePagination table={table} totalData={totalData} totalPage={totalPage} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -1,22 +1,33 @@
|
||||||
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
import SiteBreadcrumb from "@/components/site-breadcrumb";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { UploadIcon } from "lucide-react";
|
import { UploadIcon } from "lucide-react";
|
||||||
import TicketingTable from "./table/ticketing-table";
|
import TicketingTable from "./components/table";
|
||||||
|
|
||||||
const TicketingPage = async () => {
|
const TicketingPage = async () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SiteBreadcrumb />
|
<SiteBreadcrumb />
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Card className="py-4 px-3">
|
|
||||||
<div className="flex flex-row justify-between items-center">
|
|
||||||
<div className="flex-1 text-xl font-medium text-default-900">
|
|
||||||
Ticketing Table
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
<Card>
|
<Card>
|
||||||
|
<CardHeader className="border-b border-solid border-default-200 mb-6">
|
||||||
|
<CardTitle>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex-1 text-xl font-medium text-default-900">
|
||||||
|
Ticket Data
|
||||||
|
</div>
|
||||||
|
<div className="flex-none">
|
||||||
|
{/* <Button
|
||||||
|
fullWidth
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<Plus className="w-6 h-6 me-1.5"/>
|
||||||
|
New Ticket
|
||||||
|
</Button> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<TicketingTable />
|
<TicketingTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
export const data = [
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
status: "Terkirim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Ops Mantap Praja & Pilkada 2024",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Seputar Prestasi",
|
|
||||||
status: "Terkirim",
|
|
||||||
category: "Giat Pimpinan",
|
|
||||||
date: "15/10/2024 9:11",
|
|
||||||
tag: "percobaan",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -27,7 +27,7 @@ const DashCodeHeader = async () => {
|
||||||
<Cart />
|
<Cart />
|
||||||
<Messages />
|
<Messages />
|
||||||
<Notifications />
|
<Notifications />
|
||||||
{/* <ProfileInfo /> */}
|
<ProfileInfo />
|
||||||
<SheetMenu />
|
<SheetMenu />
|
||||||
</div>
|
</div>
|
||||||
</HeaderContent>
|
</HeaderContent>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const SiteBreadcrumb = ({ children }: { children?: ReactNode }) => {
|
||||||
|
|
||||||
<BreadcrumbItem
|
<BreadcrumbItem
|
||||||
>
|
>
|
||||||
<Link href="/dashboard/analytics">
|
<Link href="/dashboard">
|
||||||
<Icon icon="heroicons:home" className=" h-5 w-5" />
|
<Icon icon="heroicons:home" className=" h-5 w-5" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
@ -43,9 +43,9 @@ const SiteBreadcrumb = ({ children }: { children?: ReactNode }) => {
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
<BreadcrumbItem className=' capitalize' >
|
<BreadcrumbItem className=' capitalize' >
|
||||||
{isLast ? (
|
{isLast ? (
|
||||||
itemLink
|
itemLink?.replaceAll("-", " ")
|
||||||
) : (
|
) : (
|
||||||
<Link href={href} >{itemLink}</Link>
|
<Link href={href} >{itemLink?.replaceAll("-", "")}</Link>
|
||||||
)}
|
)}
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
{locations.length !== index + 1 && <BreadcrumbSeparator />}
|
{locations.length !== index + 1 && <BreadcrumbSeparator />}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useMediaQuery } from '@/hooks/use-media-query';
|
||||||
|
import { Table } from '@tanstack/react-table';
|
||||||
|
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
|
||||||
|
import { useSearchParams, useRouter } from 'next/navigation';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
interface DataTablePaginationProps {
|
||||||
|
table: Table<any>;
|
||||||
|
totalPage: number; // Total jumlah halaman
|
||||||
|
totalData: number; // Total jumlah data
|
||||||
|
visiblePageCount?: number; // Jumlah halaman yang ditampilkan (default 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
const TablePagination = ({
|
||||||
|
table,
|
||||||
|
totalPage,
|
||||||
|
totalData,
|
||||||
|
visiblePageCount = 5,
|
||||||
|
}: DataTablePaginationProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const [currentPageIndex, setCurrentPageIndex] = useState<number>(1);
|
||||||
|
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery("(min-width: 768px)");
|
||||||
|
if (!isMobile) {
|
||||||
|
visiblePageCount = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const pageFromUrl = searchParams?.get('page');
|
||||||
|
if (pageFromUrl) {
|
||||||
|
const pageIndex = Math.min(Math.max(1, Number(pageFromUrl)), totalPage);
|
||||||
|
setCurrentPageIndex(pageIndex);
|
||||||
|
table.setPageIndex(pageIndex - 1); // Sinkronisasi tabel dengan URL
|
||||||
|
}
|
||||||
|
}, [searchParams, totalPage, table]);
|
||||||
|
|
||||||
|
const handlePageChange = (pageIndex: number) => {
|
||||||
|
const clampedPageIndex = Math.min(Math.max(1, pageIndex), totalPage);
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
searchParams.set('page', clampedPageIndex.toString());
|
||||||
|
|
||||||
|
router.push(`${window.location.pathname}?${searchParams.toString()}`);
|
||||||
|
setCurrentPageIndex(clampedPageIndex);
|
||||||
|
table.setPageIndex(clampedPageIndex - 1); // Perbarui tabel dengan index berbasis 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const generatePageNumbers = () => {
|
||||||
|
const halfVisible = Math.floor(visiblePageCount / 2);
|
||||||
|
let startPage = Math.max(1, currentPageIndex - halfVisible);
|
||||||
|
let endPage = Math.min(totalPage, startPage + visiblePageCount - 1);
|
||||||
|
|
||||||
|
if (endPage - startPage + 1 < visiblePageCount) {
|
||||||
|
startPage = Math.max(1, endPage - visiblePageCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col md:flex-row items-center justify-between py-4 px-10 space-y-4 md:space-y-0">
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground text-center md:text-left">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of {totalData} row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handlePageChange(1)}
|
||||||
|
disabled={currentPageIndex === 1}
|
||||||
|
className="w-8 h-8"
|
||||||
|
>
|
||||||
|
<ChevronsLeft className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handlePageChange(currentPageIndex-1)}
|
||||||
|
disabled={currentPageIndex === 1}
|
||||||
|
className="w-8 h-8"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
{generatePageNumbers().map((pageIndex) => (
|
||||||
|
<Button
|
||||||
|
key={pageIndex}
|
||||||
|
onClick={() => handlePageChange(pageIndex)}
|
||||||
|
size="icon"
|
||||||
|
className="w-8 h-8"
|
||||||
|
variant={currentPageIndex === pageIndex ? 'default' : 'outline'}
|
||||||
|
>
|
||||||
|
{pageIndex}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handlePageChange(currentPageIndex+1)}
|
||||||
|
disabled={currentPageIndex === totalPage}
|
||||||
|
className="w-8 h-8"
|
||||||
|
>
|
||||||
|
<ChevronRight className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handlePageChange(totalPage)}
|
||||||
|
disabled={currentPageIndex === totalPage}
|
||||||
|
className="w-8 h-8"
|
||||||
|
>
|
||||||
|
<ChevronsRight className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TablePagination;
|
||||||
|
|
@ -24,14 +24,19 @@ TabsList.displayName = TabsPrimitive.List.displayName
|
||||||
|
|
||||||
const TabsTrigger = React.forwardRef<
|
const TabsTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & { onClick?: () => void }
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, onClick, ...props }, ref) => (
|
||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center justify-center whitespace-nowrap text-default-500 px-1 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-primary data-[state=active]:shadow-sm",
|
"flex items-center justify-center whitespace-nowrap text-default-500 px-1 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-primary data-[state=active]:shadow-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
24
lib/menus.ts
24
lib/menus.ts
|
|
@ -1445,43 +1445,43 @@ export function getMenuList(pathname: string, t: any): Group[] {
|
||||||
menus: [
|
menus: [
|
||||||
{
|
{
|
||||||
id: "communication",
|
id: "communication",
|
||||||
href: "/communication",
|
href: "/communications",
|
||||||
label: t("communication"),
|
label: t("communication"),
|
||||||
active: pathname.includes("/communication"),
|
active: pathname.includes("/communications"),
|
||||||
icon: "icon-park-outline:communication",
|
icon: "icon-park-outline:communication",
|
||||||
submenus: [
|
submenus: [
|
||||||
{
|
{
|
||||||
href: "/communication/questions",
|
href: "/communications/questions",
|
||||||
label: t("questions"),
|
label: t("questions"),
|
||||||
active: pathname.includes("/communication/questions"),
|
active: pathname.includes("/communications/questions"),
|
||||||
icon: "solar:inbox-line-outline",
|
icon: "solar:inbox-line-outline",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/communication/internal",
|
href: "/communications/internal",
|
||||||
label: t("internal"),
|
label: t("internal"),
|
||||||
active: pathname.includes("/communication/internal"),
|
active: pathname.includes("/communications/internal"),
|
||||||
icon: "ri:chat-private-line",
|
icon: "ri:chat-private-line",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/communication/forward",
|
href: "/communications/forward",
|
||||||
label: t("forward"),
|
label: t("forward"),
|
||||||
active: pathname.includes("/communication/forward"),
|
active: pathname.includes("/communications/forward"),
|
||||||
icon: "ri:share-forward-2-fill",
|
icon: "ri:share-forward-2-fill",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/communication/collaboration",
|
href: "/communications/collaboration",
|
||||||
label: t("collaboration"),
|
label: t("collaboration"),
|
||||||
active: pathname.includes("/communication/collaboration"),
|
active: pathname.includes("/communications/collaboration"),
|
||||||
icon: "clarity:employee-group-line",
|
icon: "clarity:employee-group-line",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "/communication/account-report",
|
href: "/communications/account-report",
|
||||||
label: t("account-report"),
|
label: t("account-report"),
|
||||||
active: pathname.includes("/communication/account-report"),
|
active: pathname.includes("/communications/account-report"),
|
||||||
icon: "uiw:user-delete",
|
icon: "uiw:user-delete",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import {
|
||||||
|
httpGetInterceptor,
|
||||||
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
|
export async function getFaqList() {
|
||||||
|
const url = `master/faq/list`;
|
||||||
|
return httpGetInterceptor(url);
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import {
|
import {
|
||||||
httpGetInterceptor,
|
httpGetInterceptor,
|
||||||
httpPostInterceptor,
|
|
||||||
} from "../http-config/http-interceptor-service";
|
} from "../http-config/http-interceptor-service";
|
||||||
|
|
||||||
export async function listContest(size: number, page: number) {
|
export async function getKnowledgeBaseCategoryList() {
|
||||||
return await httpGetInterceptor(
|
const url = `knowledge-base/category/list`;
|
||||||
`contest/pagination?enablePage=1&size=${size}&page=${page}`
|
return httpGetInterceptor(url);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createTask(data: any) {
|
export async function getKnowledgeBaseList(id: number) {
|
||||||
const url = "assignment";
|
const url = `knowledge-base?categoryId=${id}`;
|
||||||
return httpPostInterceptor(url, data);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue