feat: view api service page

This commit is contained in:
2025-05-31 17:58:40 +02:00
parent dfc5587608
commit 944c650ab3
2 changed files with 203 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import PersonalInfoPage from "./pages/PersonalInfo";
import ApiServicesPage from "./pages/ApiServices";
import AdminLayout from "./layout/AdminLayout";
import ApiServiceCreatePage from "./pages/ApiServices/Create";
import ViewApiServicePage from "./pages/ApiServices/View";
const router = createBrowserRouter([
{
@ -39,6 +40,10 @@ const router = createBrowserRouter([
children: [
{ index: true, element: <ApiServicesPage /> },
{ path: "create", element: <ApiServiceCreatePage /> },
{
path: "view/:serviceId",
element: <ViewApiServicePage />,
},
],
},
],

View File

@ -0,0 +1,198 @@
import Breadcrumbs from "@/components/ui/breadcrumbs";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useAdmin } from "@/store/admin";
import { useEffect, type FC } from "react";
import { Link, useParams } from "react-router";
const InfoCard = ({
title,
children,
}: {
title: string;
children: React.ReactNode;
}) => (
<div className="border dark:border-gray-800 border-gray-300 rounded mb-4">
<div className="p-4 border-b dark:border-gray-800 border-gray-300">
<h2 className="text-gray-800 dark:text-gray-200 font-semibold text-lg">
{title}
</h2>
</div>
<div className="p-4">{children}</div>
</div>
);
const ViewApiServicePage: FC = () => {
const { serviceId } = useParams();
const apiService = useAdmin((state) => state.viewApiService);
// const loading = useAdmin((state) => state.fetchingApiService);
const loadService = useAdmin((state) => state.fetchApiService);
useEffect(() => {
if (typeof serviceId === "string") loadService(serviceId);
}, [loadService, serviceId]);
if (!apiService) {
return (
<div className="p-4 flex items-center justify-center h-[60vh]">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-2 border-blue-500 border-t-transparent mx-auto mb-3" />
<p className="text-gray-600 dark:text-gray-400">
Loading API Service...
</p>
</div>
</div>
);
}
return (
<div className="dark:text-gray-200 text-gray-800 p-4">
<Breadcrumbs
items={[
{ href: "/admin", label: "Admin" },
{ href: "/admin/api-services", label: "API Services" },
{ label: "View API Service" },
]}
/>
<div className="p-4">
{/* 📋 Main Details */}
<InfoCard title="API Service Details">
<div className="flex flex-col gap-4 text-sm text-gray-700 dark:text-gray-300">
<div>
<span className="font-medium text-gray-900 dark:text-white">
Name:
</span>{" "}
{apiService.name}
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Description:
</span>{" "}
{apiService.description}
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Redirect URIs:
</span>
<ul className="list-disc list-inside ml-4 mt-1">
{apiService.redirect_uris.map((uri) => (
<li key={uri}>{uri}</li>
))}
</ul>
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Scopes:
</span>
<div className="flex flex-wrap gap-2 mt-1">
{apiService.scopes.map((scope) => (
<span
key={scope}
className="bg-blue-100 dark:bg-blue-800/30 text-blue-700 dark:text-blue-300 text-xs font-medium px-2 py-1 rounded"
>
{scope}
</span>
))}
</div>
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Grant Types:
</span>
<div className="flex flex-wrap gap-2 mt-1">
{apiService.grant_types.map((grant) => (
<span
key={grant}
className="bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-300 text-xs font-medium px-2 py-1 rounded"
>
{grant}
</span>
))}
</div>
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Created At:
</span>{" "}
{new Date(apiService.created_at).toLocaleString()}
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Updated At:
</span>{" "}
{new Date(apiService.updated_at).toLocaleString()}
</div>
<div>
<span className="font-medium text-gray-900 dark:text-white">
Status:
</span>{" "}
<span
className={`font-semibold px-2 py-1 rounded ${
apiService.is_active
? "bg-green-200 text-green-800 dark:bg-green-700/20 dark:text-green-300"
: "bg-red-200 text-red-800 dark:bg-red-700/20 dark:text-red-300"
}`}
>
{apiService.is_active ? "Active" : "Inactive"}
</span>
</div>
</div>
</InfoCard>
{/* 🔐 Credentials */}
<InfoCard title="Client Credentials">
<div className="flex flex-col gap-4">
<div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
<span className="font-medium text-gray-900 dark:text-white">
Client ID:
</span>
</p>
<Input value={apiService.client_id} disabled />
</div>
<div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
<span className="font-medium text-gray-900 dark:text-white">
Client Secret:
</span>
</p>
<Input value="***************" disabled />
</div>
<Button variant="outlined" onClick={() => {}}>
Regenerate Credentials
</Button>
</div>
</InfoCard>
{/* 🚀 Actions */}
<div className="flex flex-wrap gap-4 mt-6 justify-between items-center">
<Link to="/admin/api-services">
<Button variant="outlined">Back</Button>
</Link>
<div className="flex flex-row items-center gap-4">
<Button
variant="text"
className={
apiService.is_active
? "text-red-400 hover:text-red-500"
: "text-green-400 hover:text-green-500"
}
onClick={() => {}}
>
{apiService.is_active ? "Disable" : "Enable"}
</Button>
<Button variant="contained" disabled>
Edit
</Button>
</div>
</div>
</div>
</div>
);
};
export default ViewApiServicePage;