diff --git a/web/src/App.tsx b/web/src/App.tsx index b896697..785d155 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -7,14 +7,42 @@ import RegisterPage from "./pages/Register"; import AuthorizePage from "./pages/Authorize"; import AuthenticatePage from "./pages/Authenticate"; import AuthLayout from "./layout/AuthLayout"; +import DashboardLayout from "./layout/DashboardLayout"; +import PersonalInfoPage from "./pages/PersonalInfo"; +import ApiServicesPage from "./pages/ApiServices"; +import AdminLayout from "./layout/AdminLayout"; const router = createBrowserRouter([ { path: "/", element: , children: [ - { index: true, element: }, - { path: "authorize", element: }, + { + path: "/", + element: , + children: [ + { + index: true, + element: , + }, + { + path: "personal-info", + element: , + }, + { + path: "admin", + element: , + children: [{ path: "api-services", element: }], + }, + ], + }, + ], + }, + { + path: "/auth", + element: , + children: [ + { index: true, element: }, { path: "login", element: }, { path: "register", element: }, { path: "authenticate", element: }, diff --git a/web/src/components/Home/Sidebar/index.tsx b/web/src/components/Home/Sidebar/index.tsx index 0d79fdc..30f4aae 100644 --- a/web/src/components/Home/Sidebar/index.tsx +++ b/web/src/components/Home/Sidebar/index.tsx @@ -1,26 +1,22 @@ import type { FC } from "react"; import { useBarItems } from "../tabs"; +import { Link } from "react-router"; -export interface ISidebarProps { - activeTab: string; - onChangeTab: (tab: string) => void; -} - -const Sidebar: FC = ({ activeTab, onChangeTab }) => { - const barItems = useBarItems(); +const Sidebar: FC = () => { + const [barItems, isActive] = useBarItems(); return (
{barItems.map((item) => ( -
onChangeTab(item.tab)} - className={`dark:text-gray-200 transition-colors text-sm cursor-pointer p-4 rounded-lg flex flex-row items-center gap-3${ - item.tab === activeTab ? " bg-gray-200 dark:bg-gray-900" : "" - }`} - > - {item.icon} {item.title} -
+ +
+ {item.icon} {item.title} +
+ ))}
); diff --git a/web/src/components/Home/Tabs/Home.tsx b/web/src/components/Home/Tabs/Home.tsx deleted file mode 100644 index 437abad..0000000 --- a/web/src/components/Home/Tabs/Home.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Button } from "@/components/ui/button"; -import Avatar from "@/feature/Avatar"; -import { useAuth } from "@/store/auth"; -import { type FC } from "react"; - -const Home: FC = () => { - const profile = useAuth((state) => state.profile); - const signOut = useAuth((state) => state.signOut); - - return ( -
-
- -
-

- Welcome, {profile?.full_name} -

-

- Manage your info, private and security to make Home Guard work better - for you. -

- - -
- ); -}; - -export default Home; diff --git a/web/src/components/Home/TopBar/index.tsx b/web/src/components/Home/TopBar/index.tsx index faee526..65df678 100644 --- a/web/src/components/Home/TopBar/index.tsx +++ b/web/src/components/Home/TopBar/index.tsx @@ -1,28 +1,24 @@ import { type FC } from "react"; import { useBarItems } from "../tabs"; +import { Link } from "react-router"; -export interface ITopBarProps { - activeTab: string; - onChangeTab: (tab: string) => void; -} - -const TopBar: FC = ({ activeTab, onChangeTab }) => { - const barItems = useBarItems(); +const TopBar: FC = () => { + const [barItems, isActive] = useBarItems(); return (
{barItems.map((item) => ( -
onChangeTab(item.tab)} - className={`flex-shrink-0 transition-all border-b-4 px-4 py-2 min-w-[120px] sm:min-w-0 sm:flex-1 flex items-center justify-center cursor-pointer select-none whitespace-nowrap text-sm font-medium ${ - item.tab === activeTab - ? " border-b-4 border-b-blue-500 text-blue-500" - : " border-b-transparent text-gray-500" - }`} - > - {item.title} -
+ +
+ {item.title} +
+ ))}
); diff --git a/web/src/components/Home/tabs.tsx b/web/src/components/Home/tabs.tsx index cab17e4..24bb489 100644 --- a/web/src/components/Home/tabs.tsx +++ b/web/src/components/Home/tabs.tsx @@ -1,37 +1,61 @@ import { useAuth } from "@/store/auth"; import { Blocks, Home, Settings2, User } from "lucide-react"; +import { useCallback, type ReactNode } from "react"; +import { useLocation } from "react-router"; -export const useBarItems = () => { +export interface BarItem { + icon: ReactNode; + title: string; + tab: string; + pathname: string; +} + +export const useBarItems = (): [BarItem[], (item: BarItem) => boolean] => { const profile = useAuth((state) => state.profile); + const location = useLocation(); + + const isActive = useCallback( + (item: BarItem) => { + return location.pathname === item.pathname; + }, + [location.pathname], + ); if (!profile) { - return []; + return [[], isActive]; } return [ - { - icon: , - title: "Home", - tab: "home", - }, - { - icon: , - title: "Personal Info", - tab: "personal-info", - }, - { - icon: , - title: "Data & Personalization", - tab: "data-personalization", - }, - ...(profile.isAdmin - ? [ - { - icon: , - title: "API Services", - tab: "api-services", - }, - ] - : []), + [ + { + icon: , + title: "Home", + tab: "home", + pathname: "/", + }, + { + icon: , + title: "Personal Info", + tab: "personal-info", + pathname: "/personal-info", + }, + { + icon: , + title: "Data & Personalization", + tab: "data-personalization", + pathname: "/data-personalize", + }, + ...(profile.isAdmin + ? [ + { + icon: , + title: "API Services", + tab: "api-services", + pathname: "/admin/api-services", + }, + ] + : []), + ], + isActive, ]; }; diff --git a/web/src/feature/AccountList/index.tsx b/web/src/feature/AccountList/index.tsx index d245ffb..6a35610 100644 --- a/web/src/feature/AccountList/index.tsx +++ b/web/src/feature/AccountList/index.tsx @@ -41,7 +41,7 @@ const AccountList: FC = () => { ))} - +
diff --git a/web/src/layout/AdminLayout.tsx b/web/src/layout/AdminLayout.tsx new file mode 100644 index 0000000..ba3af1b --- /dev/null +++ b/web/src/layout/AdminLayout.tsx @@ -0,0 +1,42 @@ +import { useAuth } from "@/store/auth"; +import type { FC } from "react"; +import { Navigate, Outlet } from "react-router"; + +const AdminLayout: FC = () => { + const profile = useAuth((state) => state.profile); + + if (!profile) { +
+
+ + Loading... +
+

+ Loading... +

+
; + } + + if (!profile?.isAdmin) { + return ; + } + + return ; +}; + +export default AdminLayout; diff --git a/web/src/layout/AuthLayout.tsx b/web/src/layout/AuthLayout.tsx index af3f3b8..812f766 100644 --- a/web/src/layout/AuthLayout.tsx +++ b/web/src/layout/AuthLayout.tsx @@ -45,8 +45,7 @@ const AuthLayout = () => { const navigate = useNavigate(); const isAuthPage = useMemo(() => { - const allowedPaths = ["/login", "/register", "/authenticate"]; - return allowedPaths.some((p) => location.pathname.startsWith(p)); + return location.pathname.startsWith("/auth"); }, [location.pathname]); const loading = useMemo(() => { @@ -116,7 +115,7 @@ const AuthLayout = () => { if (signInRequired && !isAuthPage) { return ( ); diff --git a/web/src/layout/DashboardLayout.tsx b/web/src/layout/DashboardLayout.tsx new file mode 100644 index 0000000..407af96 --- /dev/null +++ b/web/src/layout/DashboardLayout.tsx @@ -0,0 +1,41 @@ +import Sidebar from "@/components/Home/Sidebar"; +import TopBar from "@/components/Home/TopBar"; +import { Card, CardContent } from "@/components/ui/card"; +import { type FC } from "react"; +import { Outlet } from "react-router"; + +const DashboardLayout: FC = () => { + return ( +
+ +
+
+ icon + +
+

+ Home Guard +

+
+
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ ); +}; + +export default DashboardLayout; diff --git a/web/src/components/Home/Tabs/ApiServices.tsx b/web/src/pages/ApiServices/index.tsx similarity index 98% rename from web/src/components/Home/Tabs/ApiServices.tsx rename to web/src/pages/ApiServices/index.tsx index 2df600e..6260e55 100644 --- a/web/src/components/Home/Tabs/ApiServices.tsx +++ b/web/src/pages/ApiServices/index.tsx @@ -36,7 +36,7 @@ const services = [ }, ]; -const ApiServices: FC = () => { +const ApiServicesPage: FC = () => { return (
@@ -101,4 +101,4 @@ const ApiServices: FC = () => { ); }; -export default ApiServices; +export default ApiServicesPage; diff --git a/web/src/pages/Index/index.tsx b/web/src/pages/Index/index.tsx index c90d9e7..9517c4c 100644 --- a/web/src/pages/Index/index.tsx +++ b/web/src/pages/Index/index.tsx @@ -1,51 +1,31 @@ -import { useState, type FC } from "react"; +import { Button } from "@/components/ui/button"; +import Avatar from "@/feature/Avatar"; +import { useAuth } from "@/store/auth"; +import { type FC } from "react"; // import overlay from "@/assets/overlay.jpg"; // import darkOverlay from "@/assets/dark-overlay.jpg"; -import { Card, CardContent } from "@/components/ui/card"; -import Sidebar from "@/components/Home/Sidebar"; -import TopBar from "@/components/Home/TopBar"; -import Home from "@/components/Home/Tabs/Home"; -import PersonalInfo from "@/components/Home/Tabs/PersonalInfo"; -import ApiServices from "@/components/Home/Tabs/ApiServices"; - const IndexPage: FC = () => { - const [tab, setTab] = useState("home"); + const profile = useAuth((state) => state.profile); + const signOut = useAuth((state) => state.signOut); return ( -
- -
-
- icon +
+
+ +
+

+ Welcome, {profile?.full_name} +

+

+ Manage your info, private and security to make Home Guard work better + for you. +

-
-

- Home Guard -

-
-
- - {/* */} - -
- setTab(tab)} /> -
-
- setTab(tab)} /> - {tab === "home" && } - {/* {tab === "personal-info" && } */} -
-
- {tab === "personal-info" && } - {tab === "api-services" && } -
-
-
-
-
- +
); }; diff --git a/web/src/pages/Login/index.tsx b/web/src/pages/Login/index.tsx index 2123397..d29596d 100644 --- a/web/src/pages/Login/index.tsx +++ b/web/src/pages/Login/index.tsx @@ -161,7 +161,7 @@ export default function LoginPage() {
Don't have an account?{" "} diff --git a/web/src/components/Home/Tabs/PersonalInfo.tsx b/web/src/pages/PersonalInfo/index.tsx similarity index 95% rename from web/src/components/Home/Tabs/PersonalInfo.tsx rename to web/src/pages/PersonalInfo/index.tsx index 82456d2..c33b191 100644 --- a/web/src/components/Home/Tabs/PersonalInfo.tsx +++ b/web/src/pages/PersonalInfo/index.tsx @@ -1,9 +1,9 @@ import Avatar from "@/feature/Avatar"; import { useAuth } from "@/store/auth"; import { ChevronRight } from "lucide-react"; -import { type FC } from "react"; +import type { FC } from "react"; -const PersonalInfo: FC = () => { +const PersonalInfoPage: FC = () => { const profile = useAuth((state) => state.profile); return ( @@ -65,4 +65,4 @@ const PersonalInfo: FC = () => { ); }; -export default PersonalInfo; +export default PersonalInfoPage; diff --git a/web/src/pages/Register/index.tsx b/web/src/pages/Register/index.tsx index 67a81d5..3f32852 100644 --- a/web/src/pages/Register/index.tsx +++ b/web/src/pages/Register/index.tsx @@ -239,7 +239,7 @@ export default function RegisterPage() {
Already have an account?{" "}