feat: update all features on kurator, edit layout, etc

This commit is contained in:
hanif salafi 2024-12-11 01:04:03 +07:00
parent 71bebd3a86
commit 227f33932b
51 changed files with 2268 additions and 2771 deletions

3
.gitignore vendored
View File

@ -35,3 +35,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
#idea intellij
/.idea/

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -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;

View File

@ -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;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,30 +1,35 @@
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 { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
import { Plus } from "lucide-react";
const BlogPage = async () => {
const FaqPage = async () => {
return (
<div>
<SiteBreadcrumb />
<div className="space-y-4">
<Card className="py-4 px-3">
<div className="flex flex-row justify-between items-center">
<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">
Table Indeks
FAQ Data
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<div className="flex-none">
<Button
fullWidth
size="md"
>
<Plus className="w-6 h-6 me-1.5"/>
New FAQ
</Button>
</div>
</div>
</Card>
<Card>
</CardTitle>
</CardHeader>
<CardContent className="p-0">
<BlogTable />
<FaqTable />
</CardContent>
</Card>
</div>
@ -32,4 +37,4 @@ const BlogPage = async () => {
);
};
export default BlogPage;
export default FaqPage;

View File

@ -1,7 +1,9 @@
export const metadata = {
title: "Blog",
};
import { Metadata } from "next";
export const metadata: Metadata = {
title: "Dashboard Media Hub",
description: "Dashboard Media Hub.",
};
const Layout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

View File

@ -1,35 +1,122 @@
import SiteBreadcrumb from "@/components/site-breadcrumb";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { UploadIcon } from "lucide-react";
import BlogTable from "./table/blog-table";
'use client'
import {
Accordion,
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 (
<div>
<SiteBreadcrumb />
<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">
Table Indeks
</div>
<div>
<Button color="primary" className="text-white">
<UploadIcon />
Tambah Indeks
<SiteBreadcrumb/>
<Tabs defaultValue={`category-0`}>
<div className="grid grid-cols-12 gap-6">
<Card className="lg:col-span-3 md:col-span-5 col-span-12 h-max">
<CardContent className=" p-6">
<TabsList className="md:flex-col gap-2 flex-wrap md:items-start justify-start">
<Button
fullWidth
size="lg"
className="dark:bg-background dark:ring-background dark:text-foreground"
>
<Plus className="w-6 h-6 me-1.5"/>
Add Category
</Button>
</div>
</div>
</Card>
<Card>
<CardContent className="p-0">
<BlogTable />
{categories?.map((category: any, index: number) => (
<TabsTrigger
key={index}
value={`category-${index}`}
onClick={() => {
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>
</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>
}
</TabsContent>
))}
</div>
</div>
</Tabs>
</div>
);
};
export default BlogPage;
export default KnowledgeBase;

View File

@ -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;

View File

@ -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",
},
];

View File

@ -15,10 +15,10 @@ const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<LayoutProvider>
<ThemeCustomize />
{/* <DashCodeHeader /> */}
<DashCodeHeader />
<DashCodeSidebar />
<LayoutContentProvider>{children}</LayoutContentProvider>
{/* <DashCodeFooter /> */}
<DashCodeFooter />
</LayoutProvider>
);
};

View File

@ -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;

View File

@ -43,122 +43,20 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { data } from "./data";
import { Input } from "@/components/ui/input";
import { InputGroup, InputGroupText } from "@/components/ui/input-group";
import { paginationBlog } from "@/service/blog/blog";
import { ticketingPagination } from "@/service/ticketing/ticketing";
import { Badge } from "@/components/ui/badge";
import TablePagination from "../../blog/table/table-pagination";
import { useRouter, useSearchParams } from "next/navigation";
export 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>
);
},
},
];
import TablePagination from "@/components/table/table-pagination";
import columns from "./columns";
const TicketingTable = () => {
const router = useRouter();
const searchParams = useSearchParams();
const [ticketData, setTicketData] = React.useState<any[]>([]);
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>(
@ -176,7 +74,7 @@ const TicketingTable = () => {
const [limit, setLimit] = React.useState(10);
const table = useReactTable({
data: ticketData,
data: dataTable,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
@ -218,7 +116,7 @@ const TicketingTable = () => {
console.log("contentData : ", contentData);
setTicketData(contentData);
setDataTable(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
} catch (error) {
@ -228,7 +126,7 @@ const TicketingTable = () => {
return (
<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>
<InputGroup merged>
<InputGroupText className="bg-transparent dark:border-secondary dark:group-focus-within:border-secondary">
@ -254,7 +152,7 @@ const TicketingTable = () => {
/>
</div>
</div>
<Table className="overflow-hidden">
<Table className="overflow-hidden mt-3">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="bg-default-200">
@ -295,7 +193,7 @@ const TicketingTable = () => {
)}
</TableBody>
</Table>
<TablePagination table={table} totalData={totalData} totalPage={totalPage} visiblePageCount={5} />
<TablePagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
);
};

View File

@ -1,22 +1,33 @@
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 { UploadIcon } from "lucide-react";
import TicketingTable from "./table/ticketing-table";
import TicketingTable from "./components/table";
const TicketingPage = async () => {
return (
<div>
<SiteBreadcrumb />
<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>
<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">
<TicketingTable />
</CardContent>

View File

@ -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",
},
];

View File

@ -27,7 +27,7 @@ const DashCodeHeader = async () => {
<Cart />
<Messages />
<Notifications />
{/* <ProfileInfo /> */}
<ProfileInfo />
<SheetMenu />
</div>
</HeaderContent>

View File

@ -27,7 +27,7 @@ const SiteBreadcrumb = ({ children }: { children?: ReactNode }) => {
<BreadcrumbItem
>
<Link href="/dashboard/analytics">
<Link href="/dashboard">
<Icon icon="heroicons:home" className=" h-5 w-5" />
</Link>
@ -43,9 +43,9 @@ const SiteBreadcrumb = ({ children }: { children?: ReactNode }) => {
<React.Fragment key={index}>
<BreadcrumbItem className=' capitalize' >
{isLast ? (
itemLink
itemLink?.replaceAll("-", " ")
) : (
<Link href={href} >{itemLink}</Link>
<Link href={href} >{itemLink?.replaceAll("-", "")}</Link>
)}
</BreadcrumbItem>
{locations.length !== index + 1 && <BreadcrumbSeparator />}

View File

@ -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;

View File

@ -24,14 +24,19 @@ TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> & { onClick?: () => void }
>(({ className, onClick, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
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",
className
)}
onClick={(e) => {
if (onClick) {
onClick()
}
}}
{...props}
/>
))

View File

@ -1445,43 +1445,43 @@ export function getMenuList(pathname: string, t: any): Group[] {
menus: [
{
id: "communication",
href: "/communication",
href: "/communications",
label: t("communication"),
active: pathname.includes("/communication"),
active: pathname.includes("/communications"),
icon: "icon-park-outline:communication",
submenus: [
{
href: "/communication/questions",
href: "/communications/questions",
label: t("questions"),
active: pathname.includes("/communication/questions"),
active: pathname.includes("/communications/questions"),
icon: "solar:inbox-line-outline",
children: [],
},
{
href: "/communication/internal",
href: "/communications/internal",
label: t("internal"),
active: pathname.includes("/communication/internal"),
active: pathname.includes("/communications/internal"),
icon: "ri:chat-private-line",
children: [],
},
{
href: "/communication/forward",
href: "/communications/forward",
label: t("forward"),
active: pathname.includes("/communication/forward"),
active: pathname.includes("/communications/forward"),
icon: "ri:share-forward-2-fill",
children: [],
},
{
href: "/communication/collaboration",
href: "/communications/collaboration",
label: t("collaboration"),
active: pathname.includes("/communication/collaboration"),
active: pathname.includes("/communications/collaboration"),
icon: "clarity:employee-group-line",
children: [],
},
{
href: "/communication/account-report",
href: "/communications/account-report",
label: t("account-report"),
active: pathname.includes("/communication/account-report"),
active: pathname.includes("/communications/account-report"),
icon: "uiw:user-delete",
children: [],
},

8
service/master/faq.ts Normal file
View File

@ -0,0 +1,8 @@
import {
httpGetInterceptor,
} from "../http-config/http-interceptor-service";
export async function getFaqList() {
const url = `master/faq/list`;
return httpGetInterceptor(url);
}

View File

@ -1,15 +1,13 @@
import {
httpGetInterceptor,
httpPostInterceptor,
} from "../http-config/http-interceptor-service";
export async function listContest(size: number, page: number) {
return await httpGetInterceptor(
`contest/pagination?enablePage=1&size=${size}&page=${page}`
);
export async function getKnowledgeBaseCategoryList() {
const url = `knowledge-base/category/list`;
return httpGetInterceptor(url);
}
export async function createTask(data: any) {
const url = "assignment";
return httpPostInterceptor(url, data);
export async function getKnowledgeBaseList(id: number) {
const url = `knowledge-base?categoryId=${id}`;
return httpGetInterceptor(url);
}