mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Initial
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { Tabs, TabsList, TabsTrigger, } from "@/components/custom/original-tabs";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function AdminSideTabs() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const path = location.pathname;
|
||||
|
||||
const tabItems = [
|
||||
{ value: "tab-1", label: "Dashboard", paths: ["/admin"] },
|
||||
{ value: "tab-2", label: "User Management", paths: ["users"] },
|
||||
{ value: "tab-3", label: "Content Management", paths: ["content", "courses"] },
|
||||
{ value: "tab-5", label: "Site Content", paths: ["contents"] },
|
||||
]
|
||||
|
||||
const getTabValue = () => {
|
||||
// ← exact match for /admin only
|
||||
const isDashboard = path === '/admin'
|
||||
if (isDashboard) return "tab-1"
|
||||
|
||||
const found = tabItems.slice(1).find(item =>
|
||||
item.paths.some(p => p && path.includes(p))
|
||||
)
|
||||
return found ? found.value : ""
|
||||
}
|
||||
|
||||
const [value, setValue] = useState(getTabValue());
|
||||
|
||||
const handleTabChange = (val) => {
|
||||
setValue(val)
|
||||
const tab = tabItems.find(t => t.value === val)
|
||||
const primaryPath = tab?.paths?.[0]
|
||||
|
||||
// ← no adminId, just /admin/users etc.
|
||||
const targetPath = primaryPath && primaryPath !== '/admin'
|
||||
? `/admin/${primaryPath}`
|
||||
: `/admin`
|
||||
|
||||
navigate(targetPath)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setValue(getTabValue());
|
||||
}, [path]);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Tabs value={value} onValueChange={handleTabChange}>
|
||||
<ScrollArea className="max-w-full overflow-x-auto border-b w-full">
|
||||
<TabsList
|
||||
className={cn(
|
||||
"bg-background rounded-none justify-start mx-2 my-1 flex gap-1"
|
||||
)}
|
||||
>
|
||||
{tabItems.map((tab) => (
|
||||
<TabsTrigger
|
||||
key={tab.value}
|
||||
value={tab.value}
|
||||
className="data-[state=active]:bg-muted data-[state=active]:shadow-none hover:bg-muted text-muted-foreground data-[state=active]:text-foreground"
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: AdminAppLayout.jsx
|
||||
* Type of Program: Layout Route
|
||||
* Description: A Layout designated for Admin End-User.
|
||||
* Module: Admin
|
||||
* Author: lash0000
|
||||
* Date Created: November. 28, 2025
|
||||
***********************************************************************************************************************************************************************
|
||||
* Change History:
|
||||
* DATE AUTHOR LOG NUMBER DESCRIPTION
|
||||
* Oct. 10, 2025 lash0000 001 Initial creation - STAR Phase 1 Project
|
||||
***********************************************************************************************************************************************************************/
|
||||
import { Outlet, useNavigate } from "react-router-dom"
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
|
||||
import { TooltipProvider } from "@/components/ui/tooltip"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Toaster } from "sonner"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
import AdminSideTabs from "../components/AdminSideTabs"
|
||||
import UserMenu from "@/components/generic/UserMenu"
|
||||
import { ROLE_CONFIG } from "@/data/profile.data"
|
||||
|
||||
const AdminLayout = () => {
|
||||
const { user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const role = ROLE_CONFIG[user?.acc_type] ?? { label: user?.acc_type, variant: 'outline' }
|
||||
|
||||
async function handleLogout() {
|
||||
// setSignOutOpen(true);
|
||||
// setSignOutLoading(true);
|
||||
|
||||
// try {
|
||||
// await logout();
|
||||
// navigate("/", { replace: true });
|
||||
// } finally {
|
||||
// setSignOutLoading(false);
|
||||
// setSignOutOpen(false);
|
||||
// }
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="philproperties-admin">
|
||||
<TooltipProvider>
|
||||
<div className={cn('')} >
|
||||
<div className="w-full flex items-center justify-between xs:px-4 lg:px-5 py-3">
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="xs:hidden sm:block w-40 cursor-pointer" onClick={() => navigate(`/admin/${id}`)}>
|
||||
<img src="/philpro-white.png" alt="" className="object-cover" />
|
||||
</div>
|
||||
<div>
|
||||
<svg
|
||||
data-testid="geist-icon"
|
||||
height="16"
|
||||
width="16"
|
||||
viewBox="0 0 16 16"
|
||||
strokeLinejoin="round"
|
||||
className="xs:hidden sm:block fill-slate-300"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.01526 15.3939L4.3107 14.7046L10.3107 0.704556L10.6061 0.0151978L11.9849 0.606077L11.6894 1.29544L5.68942 15.2954L5.39398 15.9848L4.01526 15.3939Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="name" className="lg:-ml-1 font-medium text-sm flex items-center gap-2">
|
||||
<Badge variant={role.variant} className="xs:hidden md:block capitalize">
|
||||
{role.label}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<UserMenu />
|
||||
</div>
|
||||
</div>
|
||||
<div id="side-tabs">
|
||||
<AdminSideTabs />
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-body">
|
||||
<Outlet />
|
||||
<Toaster position="bottom-right" richColors />
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminLayout
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const Admin = () => {
|
||||
return (
|
||||
<div>
|
||||
Admin Page
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Admin
|
||||
@@ -0,0 +1,31 @@
|
||||
import ProtectedRoute from '../../../routes/ProtectedRoute'
|
||||
import AdminLayout from '../layouts/AdminLayout'
|
||||
import Admin from '../pages/Admin'
|
||||
import ProfilePage from '@/components/generic/Profile'
|
||||
|
||||
export const AdminRoutes = {
|
||||
element: <ProtectedRoute allowedRoles={['admin']} />,
|
||||
children: [
|
||||
{
|
||||
path: '/admin',
|
||||
element: <AdminLayout />, // ← shared sidebar/header for all admin pages
|
||||
children: [
|
||||
// Admin Management
|
||||
{ index: true, element: <Admin /> }, // /admin
|
||||
{ path: 'my-profile', element: <ProfilePage /> },
|
||||
// Users Management
|
||||
// {
|
||||
// path: 'users',
|
||||
// element: <UsersLayout />, // ← shared header/nav for user pages
|
||||
// children: [
|
||||
// { index: true, element: <Users /> }, // /admin/users
|
||||
// { path: 'add', element: <UsersAdd /> }, // /admin/users/add
|
||||
// { path: 'edit/:id', element: <UsersEdit /> }, // /admin/users/edit/123
|
||||
// ]
|
||||
// },
|
||||
|
||||
// Add here
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user