diff --git a/apps/frontend/src/components/AppHeader.tsx b/apps/frontend/src/components/AppHeader.tsx index 3b351e7..cab58ab 100644 --- a/apps/frontend/src/components/AppHeader.tsx +++ b/apps/frontend/src/components/AppHeader.tsx @@ -30,7 +30,7 @@ interface User { // image: "https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-5.png", // }; -export default function AppHeader({toggle}: Props) { +export default function AppHeader({ toggle }: Props) { const [userMenuOpened, setUserMenuOpened] = useState(false); const { user }: { user: User | null } = useAuth(); @@ -40,7 +40,7 @@ export default function AppHeader({toggle}: Props) { // )); return ( -
+
+ - - Logout - + + Logout +
diff --git a/apps/frontend/src/components/AppNavbar.tsx b/apps/frontend/src/components/AppNavbar.tsx index c4bd3ce..5e9af2d 100644 --- a/apps/frontend/src/components/AppNavbar.tsx +++ b/apps/frontend/src/components/AppNavbar.tsx @@ -15,7 +15,7 @@ import AppHeader from "./AppHeader"; * * @returns A React element representing the application's navigation bar. */ -export default function AppNavbar(){ +export default function AppNavbar() { // const {user} = useAuth(); const { pathname } = useLocation(); @@ -28,68 +28,60 @@ export default function AppNavbar(){ const { data } = useQuery({ queryKey: ["sidebarData"], queryFn: async () => { - const res = await client.dashboard.getSidebarItems.$get(); - if (res.ok) { - const data = await res.json(); - return data; - } - console.error("Error:", res.status, res.statusText); - throw new Error("Error fetching sidebar data"); + const res = await client.dashboard.getSidebarItems.$get(); + if (res.ok) { + const data = await res.json(); + return data; + } + console.error("Error:", res.status, res.statusText); + throw new Error("Error fetching sidebar data"); }, }); useEffect(() => { const handleResize = () => { - if (window.innerWidth < 768) { // Ganti 768 dengan breakpoint mobile Anda - setSidebarOpen(false); - } else { - setSidebarOpen(true); - } + if (window.innerWidth < 768) { // Ganti 768 dengan breakpoint mobile Anda + setSidebarOpen(false); + } else { + setSidebarOpen(true); + } }; - + window.addEventListener('resize', handleResize); handleResize(); // Initial check - + return () => window.removeEventListener('resize', handleResize); - }, []); - + }, []); + const handleMenuItemClick = () => { if (window.innerWidth < 768) { - setSidebarOpen(false); + setSidebarOpen(false); } - }; + }; return ( <> -
- {/* Header */} - +
+ {/* Header */} + - {/* Sidebar */} -
- - {data?.map((menu, i) => ( - - ))} - -
- - {/* Overlay to close sidebar on mobile */} - {isSidebarOpen && ( + {/* Sidebar */}
- )} -
+ className={`fixed lg:relative w-64 bg-white top-16 left-0 h-full z-40 px-3 py-4 transition-transform border-x + ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'}`} + > + + {data?.map((menu, i) => ( + + ))} + +
+
); } diff --git a/apps/frontend/src/components/DashboardTable.tsx b/apps/frontend/src/components/DashboardTable.tsx index 5301c59..988435c 100644 --- a/apps/frontend/src/components/DashboardTable.tsx +++ b/apps/frontend/src/components/DashboardTable.tsx @@ -15,15 +15,15 @@ interface Props { export default function DashboardTable({ table }: Props) { return ( - - - +
+
+ {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ({ table }: Props) { ))} - + {table.getRowModel().rows.length > 0 ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( ({ table }: Props) { )}
-
+ ); } diff --git a/apps/frontend/src/components/PageTemplate.tsx b/apps/frontend/src/components/PageTemplate.tsx index c26a673..e07be85 100644 --- a/apps/frontend/src/components/PageTemplate.tsx +++ b/apps/frontend/src/components/PageTemplate.tsx @@ -26,7 +26,7 @@ import { PaginationLink, PaginationNext, PaginationPrevious, - } from "@/shadcn/components/ui/pagination" +} from "@/shadcn/components/ui/pagination" import { Select, SelectContent, @@ -82,7 +82,7 @@ const createCreateButton = ( const navigate = useNavigate(); return ( + type="button" + className="flex bg-white w-full text-black border items-center justify-between hover:bg-muted-foreground hover:text-white" + onClick={() => navigate({ to: `${window.location.pathname}`, search: { create: true } })} + > + + {property} + + + ); } else { return property; @@ -122,64 +122,62 @@ const CustomPagination = ({ totalPages, onPageChange, hasNextPage, - }: { +}: { currentPage: number; totalPages: number; onPageChange: (page: number) => void; hasNextPage: boolean; - }) => { +}) => { return ( - - - - onPageChange(currentPage - 1)} - className={`${ - currentPage === 1 - ? 'bg-white text-muted-foreground cursor-not-allowed' - : 'bg-white text-black hover:bg-muted hover:text-black' - }`} - aria-disabled={currentPage === 1} - > - Previous - - - - {Array.from({ length: totalPages }, (_, index) => index + 1).map((page) => ( - - onPageChange(page)} - className={page === currentPage ? 'border text-black' : ''} - > - {page} - - - ))} - - {totalPages > 1 && currentPage < totalPages && ( - - - - )} - - - onPageChange(currentPage + 1)} - className={`${ - !hasNextPage || currentPage === totalPages - ? 'bg-white text-muted-foreground cursor-not-allowed' - : 'bg-white text-black hover:bg-muted hover:text-black' - }`} - aria-disabled={!hasNextPage || currentPage === totalPages} - > - Next - - - - + + + + onPageChange(currentPage - 1)} + className={`${currentPage === 1 + ? 'bg-white text-muted-foreground cursor-not-allowed' + : 'bg-white text-black hover:bg-muted hover:text-black' + }`} + aria-disabled={currentPage === 1} + > + Previous + + + + {Array.from({ length: totalPages }, (_, index) => index + 1).map((page) => ( + + onPageChange(page)} + className={page === currentPage ? 'border text-black' : ''} + > + {page} + + + ))} + + {totalPages > 1 && currentPage < totalPages && ( + + + + )} + + + onPageChange(currentPage + 1)} + className={`${!hasNextPage || currentPage === totalPages + ? 'bg-white text-muted-foreground cursor-not-allowed' + : 'bg-white text-black hover:bg-muted hover:text-black' + }`} + aria-disabled={!hasNextPage || currentPage === totalPages} + > + Next + + + + ); - }; +}; /** * PageTemplate component for displaying a paginated table with search and filter functionality. @@ -204,10 +202,10 @@ export default function PageTemplate< const query = useQuery({ ...(typeof props.queryOptions === "function" ? props.queryOptions( - filterOptions.page, - filterOptions.limit, - filterOptions.q - ) + filterOptions.page, + filterOptions.limit, + filterOptions.q + ) : props.queryOptions), placeholderData: keepPreviousData, }); @@ -218,8 +216,8 @@ export default function PageTemplate< getCoreRowModel: getCoreRowModel(), defaultColumn: { cell: (props) => ( - - {props.getValue() as ReactNode} + + {props.getValue() as ReactNode} ), }, @@ -245,45 +243,41 @@ export default function PageTemplate< */ const handlePageChange = (page: number) => { if (page >= 1 && page <= (query.data?._metadata.totalPages ?? 1)) { - setFilterOptions((prev) => ({ - page: page - 1, // Adjust for zero-based index - limit: prev.limit, - q: prev.q, - })); + setFilterOptions((prev) => ({ + page: page - 1, // Adjust for zero-based index + limit: prev.limit, + q: prev.q, + })); } - }; - + }; + // Default values when query.data is undefined const totalPages = query.data?._metadata.totalPages ?? 1; const hasNextPage = query.data - ? filterOptions.page < totalPages - 1 - : false; + ? filterOptions.page < totalPages - 1 + : false; return (

{props.title}

- {/* Top Section */} - - {/* Table Functionality */}
{/* Search and Create Button */}
- - - handleSearchQueryChange(e.target.value) - } - placeholder="Search..." - /> - + + + handleSearchQueryChange(e.target.value) + } + placeholder="Search..." + /> +
{createCreateButton(props.createButton)} @@ -300,27 +294,27 @@ export default function PageTemplate< -
+
{ + setNavbarOpen(!openNavbar); + }; return isAuthenticated ? (
@@ -51,9 +54,7 @@ function DashboardLayout() { {/* Main Content */} -
+