Skip to content

Commit 709a5ba

Browse files
committed
Nav for Dynamic Endpoints WIP
1 parent 5c45338 commit 709a5ba

4 files changed

Lines changed: 145 additions & 2 deletions

File tree

src/lib/config/navigation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,12 +318,12 @@ function buildDynamicEndpointsItems(): NavigationItem[] {
318318
const items: NavigationItem[] = [
319319
{
320320
href: "/dynamic-endpoints/system",
321-
label: "System Dynamic Endpoints",
321+
label: "System",
322322
iconComponent: Plug,
323323
},
324324
{
325325
href: "/dynamic-endpoints/bank",
326-
label: "Bank Dynamic Endpoints",
326+
label: "Bank",
327327
iconComponent: Building2,
328328
},
329329
];

src/routes/(protected)/consumers/[consumer_id]/edit/+page.server.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { SessionOAuthHelper } from "$lib/oauth/sessionHelper";
77

88
const logger = createLogger("EditConsumerServer");
99

10+
interface CallCounter {
11+
calls_made: number;
12+
reset_in_seconds: number;
13+
}
14+
1015
interface Consumer {
1116
consumer_id: string;
1217
key?: string;
@@ -21,6 +26,14 @@ interface Consumer {
2126
created: string;
2227
logo_url?: string;
2328
certificate_pem?: string;
29+
call_counters?: {
30+
per_second?: CallCounter;
31+
per_minute?: CallCounter;
32+
per_hour?: CallCounter;
33+
per_day?: CallCounter;
34+
per_week?: CallCounter;
35+
per_month?: CallCounter;
36+
};
2437
created_by_user?: {
2538
user_id: string;
2639
email: string;

src/routes/(protected)/consumers/[consumer_id]/edit/+page.svelte

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,78 @@
412412
</div>
413413
</div>
414414

415+
<!-- Call Counters Section -->
416+
<div class="mb-8 rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">
417+
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
418+
API Call Counters
419+
</h2>
420+
<p class="mb-4 text-sm text-gray-500 dark:text-gray-400">
421+
Current API usage for this consumer (from Redis).
422+
</p>
423+
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-4">
424+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
425+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Second</div>
426+
{#if consumer.call_counters?.per_second && typeof consumer.call_counters.per_second.calls_made === 'number'}
427+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_second.calls_made}</div>
428+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_second.reset_in_seconds}s</div>
429+
{:else}
430+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
431+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
432+
{/if}
433+
</div>
434+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
435+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Minute</div>
436+
{#if consumer.call_counters?.per_minute && typeof consumer.call_counters.per_minute.calls_made === 'number'}
437+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_minute.calls_made}</div>
438+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_minute.reset_in_seconds}s</div>
439+
{:else}
440+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
441+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
442+
{/if}
443+
</div>
444+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
445+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Hour</div>
446+
{#if consumer.call_counters?.per_hour && typeof consumer.call_counters.per_hour.calls_made === 'number'}
447+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_hour.calls_made}</div>
448+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_hour.reset_in_seconds}s</div>
449+
{:else}
450+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
451+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
452+
{/if}
453+
</div>
454+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
455+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Day</div>
456+
{#if consumer.call_counters?.per_day && typeof consumer.call_counters.per_day.calls_made === 'number'}
457+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_day.calls_made}</div>
458+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_day.reset_in_seconds}s</div>
459+
{:else}
460+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
461+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
462+
{/if}
463+
</div>
464+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
465+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Week</div>
466+
{#if consumer.call_counters?.per_week && typeof consumer.call_counters.per_week.calls_made === 'number'}
467+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_week.calls_made}</div>
468+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_week.reset_in_seconds}s</div>
469+
{:else}
470+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
471+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
472+
{/if}
473+
</div>
474+
<div class="rounded-lg bg-gray-50 p-3 dark:bg-gray-900/50">
475+
<div class="text-xs font-medium text-gray-500 dark:text-gray-400">Per Month</div>
476+
{#if consumer.call_counters?.per_month && typeof consumer.call_counters.per_month.calls_made === 'number'}
477+
<div class="mt-1 text-lg font-semibold text-gray-900 dark:text-gray-100">{consumer.call_counters.per_month.calls_made}</div>
478+
<div class="text-xs text-gray-500 dark:text-gray-400">resets in {consumer.call_counters.per_month.reset_in_seconds}s</div>
479+
{:else}
480+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">Err</div>
481+
<div class="text-xs text-gray-500 dark:text-gray-400">unavailable</div>
482+
{/if}
483+
</div>
484+
</div>
485+
</div>
486+
415487
<!-- Scopes Section -->
416488
<div class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">
417489
<div class="flex items-center justify-between mb-4">

src/routes/+layout.svelte

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
rbacItems,
1111
accountAccessItems,
1212
dynamicEntitiesItems,
13+
dynamicEndpointsItems,
1314
abacItems,
1415
} from "$lib/config/navigation";
1516
import Toast from "$lib/components/Toast.svelte";
@@ -71,6 +72,7 @@
7172
let isRbacExpanded = $state(false);
7273
let isAccountAccessExpanded = $state(false);
7374
let isDynamicEntitiesExpanded = $state(false);
75+
let isDynamicEndpointsExpanded = $state(false);
7476
let isAbacExpanded = $state(false);
7577
let displayMode: "dark" | "light" = $state("dark");
7678
let systemDynamicEntities = $state<any[]>([]);
@@ -177,6 +179,10 @@
177179
page.url.pathname === "/dynamic-entities" ||
178180
page.url.pathname.startsWith("/dynamic-entities/"),
179181
);
182+
let isDynamicEndpointsActive = $derived(
183+
page.url.pathname === "/dynamic-endpoints" ||
184+
page.url.pathname.startsWith("/dynamic-endpoints/"),
185+
);
180186
let isAbacActive = $derived(
181187
page.url.pathname === "/abac" || page.url.pathname.startsWith("/abac/"),
182188
);
@@ -208,6 +214,12 @@
208214
if (isAccountAccessActive) {
209215
isAccountAccessExpanded = true;
210216
}
217+
if (isDynamicEntitiesActive) {
218+
isDynamicEntitiesExpanded = true;
219+
}
220+
if (isDynamicEndpointsActive) {
221+
isDynamicEndpointsExpanded = true;
222+
}
211223
if (isAbacActive) {
212224
isAbacExpanded = true;
213225
}
@@ -255,6 +267,10 @@
255267
isDynamicEntitiesExpanded = !isDynamicEntitiesExpanded;
256268
}
257269
270+
function toggleDynamicEndpoints() {
271+
isDynamicEndpointsExpanded = !isDynamicEndpointsExpanded;
272+
}
273+
258274
function toggleAbac() {
259275
isAbacExpanded = !isAbacExpanded;
260276
}
@@ -737,6 +753,48 @@
737753
</Navigation.Menu>
738754
{/if}
739755
</Navigation.Group>
756+
757+
<!-- Dynamic Endpoints Group -->
758+
<Navigation.Group>
759+
<button
760+
type="button"
761+
class="btn w-full justify-start gap-3 px-2 hover:preset-tonal"
762+
class:preset-filled-primary-50-950={isDynamicEndpointsActive}
763+
class:border={isDynamicEndpointsActive}
764+
class:border-solid-secondary-500={isDynamicEndpointsActive}
765+
onclick={toggleDynamicEndpoints}
766+
>
767+
<Plug class="size-5" />
768+
<span>Dynamic Endpoints</span>
769+
{#if isDynamicEndpointsExpanded}
770+
<ChevronDown class="h-4 w-4" />
771+
{:else}
772+
<ChevronRight class="h-4 w-4" />
773+
{/if}
774+
</button>
775+
776+
{#if isDynamicEndpointsExpanded}
777+
<Navigation.Menu class="mt-1 ml-4 flex flex-col gap-1 px-2">
778+
{#each dynamicEndpointsItems as subItem}
779+
{@const Icon = subItem.iconComponent}
780+
<a
781+
href={subItem.href}
782+
class="btn w-full justify-start gap-3 px-2 pl-6 text-sm hover:preset-tonal"
783+
class:preset-filled-secondary-50-950={page.url.pathname ===
784+
subItem.href}
785+
class:border-l-2={page.url.pathname === subItem.href}
786+
class:border-primary-500={page.url.pathname ===
787+
subItem.href}
788+
title={subItem.label}
789+
aria-label={subItem.label}
790+
>
791+
<Icon class="size-4" />
792+
<span>{subItem.label}</span>
793+
</a>
794+
{/each}
795+
</Navigation.Menu>
796+
{/if}
797+
</Navigation.Group>
740798
{/if}
741799
</Navigation.Content>
742800

0 commit comments

Comments
 (0)