feat: location.pathname based bar navigation + admin layout + separate

auth routes
This commit is contained in:
2025-05-29 23:42:17 +02:00
parent 78e84567c7
commit db2cb36f54
14 changed files with 219 additions and 143 deletions

View File

@ -0,0 +1,104 @@
import type { FC } from "react";
import { Link } from "react-router";
const services = [
{
id: "1",
name: "User Service",
clientId: "user-svc-001",
isActive: true,
createdAt: "2024-09-15T10:20:30Z",
updatedAt: "2025-01-10T12:00:00Z",
},
{
id: "2",
name: "Billing Service",
clientId: "billing-svc-009",
isActive: false,
createdAt: "2024-10-01T08:45:10Z",
updatedAt: "2025-03-22T14:30:00Z",
},
{
id: "3",
name: "Analytics Service",
clientId: "analytics-svc-777",
isActive: true,
createdAt: "2024-11-25T16:00:00Z",
updatedAt: "2025-02-05T10:15:45Z",
},
{
id: "4",
name: "Email Service",
clientId: "email-svc-333",
isActive: false,
createdAt: "2023-07-10T13:00:00Z",
updatedAt: "2024-12-31T09:25:00Z",
},
];
const ApiServicesPage: FC = () => {
return (
<div className="overflow-x-auto rounded shadow-md dark:shadow-gray-800">
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-800">
<tr>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-200">
Name
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-200">
Client ID
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-200">
Is Active
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-200">
Created At
</th>
<th className="px-6 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-200">
Updated At
</th>
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
{services.map((service) => (
<tr
key={service.id}
className="hover:bg-gray-50 dark:hover:bg-gray-800"
>
<td className="px-6 py-4 text-sm font-medium text-blue-600">
<Link
to={`/services/${service.id}`}
className="hover:underline hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
>
{service.name}
</Link>
</td>
<td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">
{service.clientId}
</td>
<td className="px-6 py-4 text-sm">
<span
className={`inline-block px-2 py-1 text-xs rounded-full font-semibold ${
service.isActive
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300"
: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"
}`}
>
{service.isActive ? "Yes" : "No"}
</span>
</td>
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{new Date(service.createdAt).toLocaleString()}
</td>
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{new Date(service.updatedAt).toLocaleString()}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default ApiServicesPage;

View File

@ -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<string>("home");
const profile = useAuth((state) => state.profile);
const signOut = useAuth((state) => state.signOut);
return (
<div className="relative z-10 flex items-center justify-center min-h-screen">
<Card className="overflow-y-auto min-h-screen w-full min-w-full shadow-lg bg-white/85 dark:bg-black/85 backdrop-blur-md sm:rounded-none">
<div className="flex flex-col items-center sm:pt-0 relative">
<div className="flex flex-row items-center absolute left-4 top-4">
<img src="/icon.png" alt="icon" className="w-6 h-6" />
<div className="flex flex-col items-center gap-2 p-7">
<div className="w-24 h-24 sm:w-36 sm:h-36 overflow-hidden rounded-full flex items-center justify-center bg-gray-300">
<Avatar iconSize={64} />
</div>
<h1 className="dark:text-gray-200 text-gray-800 text-2xl select-none">
Welcome, {profile?.full_name}
</h1>
<p className="text-gray-600 dark:text-gray-500 select-none text-center text-sm/normal sm:text-lg">
Manage your info, private and security to make Home Guard work better
for you.
</p>
<div className="ml-2">
<p className="text-sm text-gray-600 text-left dark:text-gray-500">
Home Guard
</p>
</div>
</div>
{/* <LogIn className="w-8 h-8 text-gray-700 mb-4" /> */}
<CardContent className="w-full space-y-4 flex-1" spacing={false}>
<div className="flex flex-row">
<Sidebar activeTab={tab} onChangeTab={(tab) => setTab(tab)} />
<div className="sm:p-4 max-w-full flex-1">
<div className="flex flex-col w-full items-center gap-2">
<TopBar activeTab={tab} onChangeTab={(tab) => setTab(tab)} />
{tab === "home" && <Home />}
{/* {tab === "personal-info" && <PersonalInfo />} */}
</div>
<div className="p-4">
{tab === "personal-info" && <PersonalInfo />}
{tab === "api-services" && <ApiServices />}
</div>
</div>
</div>
</CardContent>
</div>
</Card>
<Button className="mt-10" onClick={signOut}>
Sign Out
</Button>
</div>
);
};

View File

@ -161,7 +161,7 @@ export default function LoginPage() {
<div className="text-sm text-center text-gray-600">
Don't have an account?{" "}
<Link
to="/register"
to="/auth/register"
state={location.state}
className="text-blue-600 hover:underline"
>

View File

@ -0,0 +1,68 @@
import Avatar from "@/feature/Avatar";
import { useAuth } from "@/store/auth";
import { ChevronRight } from "lucide-react";
import type { FC } from "react";
const PersonalInfoPage: FC = () => {
const profile = useAuth((state) => state.profile);
return (
<>
<h1 className="dark:text-gray-200 text-gray-800 text-2xl">
Your profile info in Home services
</h1>
<p className="text-gray-500 text-sm mt-2 sm:text-lg">
Personal info and options to manage it. You can make some of this info,
like your contact details, visible to others so they can reach you
easily. You can also see a summary of your profiles.
</p>
<div className="border dark:border-gray-800 border-gray-300 p-4 rounded mt-4">
<h3 className="dark:text-gray-300 text-gray-800">Basic info</h3>
<p className="text-gray-500 text-sm mt-2 mb-4">
Some info may be visible to other services and tools using Home Guard.{" "}
<a href="#" className="text-blue-500">
Learn more
</a>
</p>
{/* Profile Picture */}
<div className="flex flex-row items-center justify-between px-2 p-4 border-b gap-2 dark:border-b-gray-800 border-b-gray-100">
<div className="flex flex-col items-start gap-2">
<p className="text-sm dark:text-gray-400 font-medium text-gray-600">
Profile picture
</p>
<p className="text-sm dark:text-gray-500 text-gray-600">
Add a profile picture to personalize your account
</p>
</div>
<div>
<div className="w-16 h-16 overflow-hidden rounded-full dark:bg-gray-400 bg-gray-700">
<Avatar iconSize={12} />
</div>
</div>
</div>
{/* Name */}
<div className="flex flex-row items-center justify-between px-2 p-4 border-b dark:border-b-gray-800 border-b-gray-100">
<div className="flex flex-col items-start gap-2">
<p className="text-sm dark:text-gray-400 font-medium text-gray-600">
Name
</p>
<p className="text dark:text-gray-200 text-gray-800">
{profile?.full_name}
</p>
</div>
<div>
<div className="text-gray-500">
<ChevronRight size={26} />
</div>
</div>
</div>
</div>
</>
);
};
export default PersonalInfoPage;

View File

@ -239,7 +239,7 @@ export default function RegisterPage() {
<div className="text-sm text-center text-gray-600">
Already have an account?{" "}
<Link
to="/login"
to="/auth/login"
state={location.state}
className="text-blue-600 hover:underline"
>