Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 62 user management components admin dashboard #66

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_EDITOR=code

APP_ENV=DEVELOPMENT
NEXT_PUBLIC_BACKEND_URL_BASE="http://localhost:8000/"
NEXT_PUBLIC_BACKEND_URL="http://localhost:8000/api"
68 changes: 56 additions & 12 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 49 additions & 7 deletions client/src/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
import React from "react";
import React, { useEffect, useState } from "react";

import Navbar from "@/components/navbar";
import { WaitingLoader } from "@/components/ui/loading";
import { useAuth } from "@/context/auth-provider";
import { useFetchData } from "@/hooks/use-fetch-data";
import { User } from "@/types/user";

import Sidebar from "./sidebar";

interface LayoutProps {
children: React.ReactNode;
}

/**
* Layout component that wraps the application with a Navbar or Sidebar based on user authentication status.
*
* @param {LayoutProps} props - The component props.
* @param {React.ReactNode} props.children - The child components to render within the layout.
*
*/
export default function Layout({ children }: LayoutProps) {
return (
<div>
<Navbar />
<main>{children}</main>
</div>
);
const [isAuthChecked, setIsAuthChecked] = useState(false);
const { userId } = useAuth();
const isLoggedIn = Boolean(userId);

// wait for auth to be checked before rendering
useEffect(() => {
setIsAuthChecked(true);
}, []);

const {
data: user,
error,
isLoading,
} = useFetchData<User>({
queryKey: ["user", userId],
endpoint: "/users/me",
staleTime: 5 * 60 * 1000,
enabled: Boolean(userId),
});

if (!isAuthChecked) return null;

if (!isLoggedIn) {
return (
<div>
<Navbar />
<main>{children}</main>
</div>
);
}

if (isLoading) return <WaitingLoader />;
if (!user) return <div>{error?.message || "Failed to load user data."}</div>;

return <Sidebar role={user.role}>{children}</Sidebar>;
}
2 changes: 1 addition & 1 deletion client/src/components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";

import { Button } from "@/components/ui/button";
import MobileNav from "@/components/ui/mobilenav";
import LoginModal from "@/components/ui/user/login-modal";
import { LoginModal } from "@/components/ui/Users/login-modal";

import logo from "../../public/wajo_white.svg";
import styles from "../styles/modules/navbar.module.css";
Expand Down
32 changes: 0 additions & 32 deletions client/src/components/sidebar-layout.tsx

This file was deleted.

78 changes: 78 additions & 0 deletions client/src/components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { useRouter } from "next/router";
import React from "react";

import AppSidebar from "@/components/ui/app-sidebar";
import { Separator } from "@/components/ui/separator";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { Role } from "@/types/user";

import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from "./ui/breadcrumb";

interface LayoutProps {
children: React.ReactNode;
role: Role;
}

/**
* Sidebar layout component that renders a sidebar with breadcrumb navigation.
* Displays different content based on the user's role.
*
* @param {LayoutProps} props - The component props.
* @param {React.ReactNode} props.children - The content to render inside the sidebar layout.
* @param {Role} props.role - The role of the user, used to control sidebar behavior.
*/
export default function Sidebar({ children, role }: LayoutProps) {
const router = useRouter();
const pathSegments = router.pathname.split("/").filter(Boolean);

const breadcrumbItems = pathSegments.map((segment, index) => {
const formattedSegment = segment
.replace(/_/g, " ") // replace underscores with spaces
.replace(/\b\w/g, (char) => char.toUpperCase()); // capitalize first letter
return {
title: formattedSegment,
url: `/${pathSegments.slice(0, index + 1).join("/")}`,
};
});

return (
<div>
<SidebarProvider>
<AppSidebar className="z-50" Role={role} />
<SidebarInset>
<header className="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-2 border-b bg-background px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-5" />
<Breadcrumb className="ml-4">
<BreadcrumbList className="text-xl">
{breadcrumbItems.map((item, index) => (
<React.Fragment key={item.url}>
<BreadcrumbItem>
<BreadcrumbLink href={item.url}>
{item.title}
</BreadcrumbLink>
</BreadcrumbItem>
{index < breadcrumbItems.length - 1 && (
<BreadcrumbSeparator className="[&>svg]:h-5 [&>svg]:w-5" />
)}
</React.Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
</header>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from "react";

import { WaitingLoader } from "@/components/ui/loading";
import { Search, SearchInput, SearchSelect } from "@/components/ui/search";
import {
Table,
Expand All @@ -17,10 +18,8 @@ import type { Leaderboard } from "@/types/leaderboard";
* It fetches leaderboard data from an API and allows users to filter the list based on
* leaderboard name and status. The component also handles loading and error states for
* data fetching.
*
* @returns {JSX.Element} The rendered LeaderboardList component.
*/
const LeaderboardList = () => {
export function LeaderboardList() {
const [filters, setFilters] = useState({
search: "",
status: "All",
Expand All @@ -36,7 +35,7 @@ const LeaderboardList = () => {
endpoint: "leaderboard/list",
});

if (isLeaderboardLoading) return <div>Loading...</div>;
if (isLeaderboardLoading) return <WaitingLoader />;

if (isLeaderboardError) {
console.error("Error fetching leaderboards:", leaderboardError);
Expand Down Expand Up @@ -165,6 +164,4 @@ const LeaderboardList = () => {
</Table>
</div>
);
};

export default LeaderboardList;
}
9 changes: 4 additions & 5 deletions client/src/components/ui/Question/data-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useEffect, useState } from "react";

import { Button } from "@/components/ui/button";
import { Pagination } from "@/components/ui/pagination";
import {
Table,
TableBody,
Expand All @@ -9,19 +11,16 @@ import {
TableRow,
} from "@/components/ui/table";

import { Button } from "../button";
import { Pagination } from "../pagination";

/**
* The Datagrid component is a flexible, paginated data table with sorting and navigation features.
*
* @param {DatagridProps} props - Props including datacontext (data array), onDataChange (callback for data update), and ChangePage (external control for current page).
* @param {DatagridProps<Question>} props - Props including datacontext (data array), onDataChange (callback for data update), and ChangePage (external control for current page).
*/
export function Datagrid({
datacontext,
onDataChange,
changePage,
}: DatagridProps) {
}: DatagridProps<Question>) {
// State to track sorting direction
const [isAscending, setIsAscending] = useState(true);
// State for the current page number
Expand Down
Loading
Loading