+
{children}
diff --git a/dashboard/src/app/(inside)/relation/page.tsx b/dashboard/src/app/(inside)/relation/page.tsx
new file mode 100644
index 0000000..e6754a9
--- /dev/null
+++ b/dashboard/src/app/(inside)/relation/page.tsx
@@ -0,0 +1,12 @@
+export default async function ListRelationPage() {
+ return (
+
+
+
Relations
+
+ Part of milestone v0.4.0
+
+
+
+ );
+}
diff --git a/dashboard/src/app/service-worker.ts b/dashboard/src/app/service-worker.ts
index 0538ba3..b9741f4 100644
--- a/dashboard/src/app/service-worker.ts
+++ b/dashboard/src/app/service-worker.ts
@@ -1,18 +1,24 @@
-import type { PrecacheEntry } from '@serwist/precaching';
-import { installSerwist } from '@serwist/sw';
import { defaultCache } from '@serwist/next/worker';
+import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist';
+import { Serwist } from 'serwist';
-declare const self: ServiceWorkerGlobalScope & {
- // Change this attribute's name to your `injectionPoint`.
- // `injectionPoint` is an InjectManifest option.
- // See https://serwist.pages.dev/docs/build/inject-manifest/configuring
- __SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
-};
+declare const self: ServiceWorkerGlobalScope;
-installSerwist({
+declare global {
+ interface WorkerGlobalScope extends SerwistGlobalConfig {
+ // Change this attribute's name to your `injectionPoint`.
+ // `injectionPoint` is an InjectManifest option.
+ // See https://serwist.pages.dev/docs/build/configuring
+ __SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
+ }
+}
+
+const serwist = new Serwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
});
+
+serwist.addEventListeners();
diff --git a/dashboard/src/components/app-sidebar.tsx b/dashboard/src/components/app-sidebar.tsx
index 7039c7f..ff21f4d 100644
--- a/dashboard/src/components/app-sidebar.tsx
+++ b/dashboard/src/components/app-sidebar.tsx
@@ -5,14 +5,13 @@ import {
SidebarContent,
SidebarFooter,
SidebarGroup,
- SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
+ useSidebar,
} from '@/components/ui/sidebar';
-import { AppWindow, Home, LogOut, Moon, Sun, Users } from 'lucide-react';
-import React from 'react';
+import { AppWindow, ChartLine, FileLock2, Home, LogOut, LucideIcon, Moon, Sun, Users } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
@@ -21,50 +20,126 @@ import {
} from '@/components/ui/dropdown-menu';
import { useTheme } from 'next-themes';
import { LogoutLink } from '@/ory';
+import React from 'react';
import Link from 'next/link';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
+
+interface SidebarGroup {
+ type: 'group';
+ label: string;
+ items: SidebarItem[];
+}
+
+interface SidebarItem {
+ type: 'item';
+ label: string;
+ path: string;
+ icon: LucideIcon;
+}
+
+type SidebarContent = SidebarItem | SidebarGroup
+
+function renderSidebarMenuItem(item: SidebarItem, key: number, collapsed: boolean) {
+ return (
+
+
+
+
+
+
+ {item.label}
+
+
+
+
+
+ {item.label}
+
+
+ );
+}
export function AppSidebar({ ...props }: React.ComponentProps
) {
const { setTheme } = useTheme();
- const items = [
+ const { state } = useSidebar();
+
+ const items: SidebarContent[] = [
{
- title: 'Home',
- url: '/',
- icon: Home,
+ label: 'Application',
+ type: 'group',
+ items: [
+ {
+ type: 'item',
+ label: 'Home',
+ path: '/',
+ icon: Home,
+ },
+ {
+ type: 'item',
+ label: 'Analytics',
+ path: '/analytics',
+ icon: ChartLine,
+ },
+ ],
},
{
- title: 'Users',
- url: '/user',
- icon: Users,
+ label: 'Ory Kratos',
+ type: 'group',
+ items: [
+ {
+ type: 'item',
+ label: 'Users',
+ path: '/user',
+ icon: Users,
+ },
+ ],
},
{
- title: 'Applications',
- url: '/application',
- icon: AppWindow,
+ label: 'Ory Hydra',
+ type: 'group',
+ items: [
+ {
+ type: 'item',
+ label: 'Clients',
+ path: '/client',
+ icon: AppWindow,
+ },
+ ],
+ },
+ {
+ label: 'Ory Keto',
+ type: 'group',
+ items: [
+ {
+ type: 'item',
+ label: 'Relations',
+ path: '/relation',
+ icon: FileLock2,
+ },
+ ],
},
];
return (
-
- Application
-
-
- {items.map((item) => (
-
-
-
-
- {item.title}
-
-
-
- ))}
-
-
-
+
+ {items.map((item, index) => {
+ switch (item.type) {
+ case 'item':
+ return renderSidebarMenuItem(item, index, state === 'collapsed');
+ case 'group':
+ return (
+
+ {item.label}
+ {item.items.map((subItem, subIndex) => renderSidebarMenuItem(subItem, subIndex, state === 'collapsed'))}
+
+ );
+ }
+ })}
+
diff --git a/docker/ory-dev/hydra-create-client.sh b/docker/ory-dev/hydra-create-client.sh
index 8b6b038..181920e 100644
--- a/docker/ory-dev/hydra-create-client.sh
+++ b/docker/ory-dev/hydra-create-client.sh
@@ -2,15 +2,21 @@
# Ory Hydra CLI and writes the client id and
# client secret to the command line.
-read -r -p "Did you modify the script according to your needs? (y/N)? " answer
-if [ answer != "y" && anser != "Y" ]; then
- exit 0
+# Check if the number of arguments is correct
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 "
+ exit 1
fi
+name=$1
+owner=$2
+
# it is likely you will have to set different redirect-uris
# depending on the application you are trying to connect.
code_client=$(docker compose exec ory-hydra \
hydra create client \
+ --name "$name" \
+ --owner "$owner" \
--endpoint http://localhost:4445 \
--grant-type authorization_code,refresh_token \
--response-type code,id_token \