mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-24 21:17:31 +00:00
refactor: enforce eslint rules
This commit is contained in:
@@ -28,11 +28,13 @@ export default [
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/']
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/', 'src/lib/paraglide/', 'src/paraglide/']
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off'
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
// Pocket ID does not support deployments under a SvelteKit base path
|
||||
'svelte/no-navigation-without-resolve': 'off'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
+66
-65
@@ -1,67 +1,68 @@
|
||||
{
|
||||
"name": "pocket-id-frontend",
|
||||
"version": "2.11.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "vite dev --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 3000",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
|
||||
"@opentelemetry/resources": "^2.8.0",
|
||||
"@opentelemetry/sdk-trace-web": "^2.8.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.41.1",
|
||||
"@simplewebauthn/browser": "^13.3.0",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"axios": "^1.16.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.2.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"runed": "^0.37.1",
|
||||
"sveltekit-superforms": "^2.30.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "^2.18.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.2.6",
|
||||
"@inlang/plugin-message-format": "^4.4.0",
|
||||
"@internationalized/date": "^3.12.1",
|
||||
"@lucide/svelte": "^1.16.0",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.60.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
||||
"@types/node": "^25.9.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"bits-ui": "^2.18.1",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.17.1",
|
||||
"formsnap": "^2.0.1",
|
||||
"globals": "^17.6.0",
|
||||
"mode-watcher": "^1.1.0",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-svelte": "^3.5.2",
|
||||
"prettier-plugin-tailwindcss": "^0.8.0",
|
||||
"shadcn-svelte": "^1.3.0",
|
||||
"svelte": "^5.55.8",
|
||||
"svelte-check": "^4.4.8",
|
||||
"svelte-sonner": "^1.1.1",
|
||||
"tailwind-variants": "^3.2.2",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"tslib": "^2.8.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.4",
|
||||
"vite": "^8.0.16",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
"name": "pocket-id-frontend",
|
||||
"version": "2.11.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "vite dev --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 3000",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
|
||||
"@opentelemetry/resources": "^2.8.0",
|
||||
"@opentelemetry/sdk-trace-web": "^2.8.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.41.1",
|
||||
"@simplewebauthn/browser": "^13.3.0",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"axios": "^1.16.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.2.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"runed": "^0.37.1",
|
||||
"sveltekit-superforms": "^2.30.1",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@inlang/paraglide-js": "^2.18.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.2.6",
|
||||
"@inlang/plugin-message-format": "^4.4.0",
|
||||
"@internationalized/date": "^3.12.1",
|
||||
"@lucide/svelte": "^1.16.0",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.60.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
||||
"@types/node": "^25.9.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"bits-ui": "^2.18.1",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.17.1",
|
||||
"formsnap": "^2.0.1",
|
||||
"globals": "^17.6.0",
|
||||
"mode-watcher": "^1.1.0",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-svelte": "^3.5.2",
|
||||
"prettier-plugin-tailwindcss": "^0.8.0",
|
||||
"shadcn-svelte": "^1.3.0",
|
||||
"svelte": "^5.55.8",
|
||||
"svelte-check": "^4.4.8",
|
||||
"svelte-sonner": "^1.1.1",
|
||||
"tailwind-variants": "^3.2.2",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"tslib": "^2.8.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.4",
|
||||
"vite": "^8.0.16",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,10 @@
|
||||
|
||||
$effect(() => {
|
||||
if (filters) {
|
||||
filters.userID;
|
||||
filters.event;
|
||||
filters.location;
|
||||
filters.clientName;
|
||||
void filters.userID;
|
||||
void filters.event;
|
||||
void filters.location;
|
||||
void filters.clientName;
|
||||
untrack(() => tableRef?.refresh());
|
||||
}
|
||||
});
|
||||
@@ -94,7 +94,7 @@
|
||||
if (!filters) return undefined;
|
||||
return Object.fromEntries(
|
||||
Object.entries(filters)
|
||||
.filter(([_, value]) => value !== undefined && value !== null && value !== '')
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([key, value]) => [key, [value]])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
let containerNode: HTMLElement | null = $state(null);
|
||||
|
||||
$effect(() => {
|
||||
page.route;
|
||||
void page.route;
|
||||
applyAnimationDelays();
|
||||
});
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
avoidCollisions={false}
|
||||
strategy="absolute"
|
||||
>
|
||||
{#each filteredSuggestions as suggestion, index}
|
||||
{#each filteredSuggestions as suggestion, index (suggestion)}
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
|
||||
@@ -39,14 +39,14 @@
|
||||
<div {...restProps}>
|
||||
<FormInput>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
{#each customClaims as _, i}
|
||||
{#each customClaims as customClaim, i (customClaim)}
|
||||
<div class="flex gap-x-2">
|
||||
<AutoCompleteInput
|
||||
placeholder={m.key()}
|
||||
suggestions={filteredSuggestions}
|
||||
bind:value={customClaims[i].key}
|
||||
bind:value={customClaim.key}
|
||||
/>
|
||||
<Input placeholder={m.value()} bind:value={customClaims[i].value} />
|
||||
<Input placeholder={m.value()} bind:value={customClaim.value} />
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="outline">
|
||||
{#each items.filter((item) => selectedItems.includes(item.value)) as item}
|
||||
{#each items.filter((item) => selectedItems.includes(item.value)) as item (item.value)}
|
||||
<Badge variant="secondary">
|
||||
{item.label}
|
||||
</Badge>
|
||||
@@ -43,7 +43,7 @@
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="start" class="w-[var(--bits-dropdown-menu-anchor-width)]">
|
||||
{#each items as item}
|
||||
{#each items as item (item.value)}
|
||||
<DropdownMenu.CheckboxItem
|
||||
checked={selectedItems.includes(item.value)}
|
||||
onCheckedChange={() => handleItemSelect(item.value)}
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div class="flex flex-wrap items-center gap-1">
|
||||
{#if selectedItems.length > 0}
|
||||
{#each selectedLabels as label}
|
||||
{#each selectedLabels as label, i (i)}
|
||||
<Badge variant="secondary">{label}</Badge>
|
||||
{/each}
|
||||
{:else}
|
||||
@@ -118,7 +118,7 @@
|
||||
{/if}
|
||||
</Command.Empty>
|
||||
<Command.Group class="max-h-60 overflow-y-auto">
|
||||
{#each filteredItems as item}
|
||||
{#each filteredItems as item (item.value)}
|
||||
<Command.Item
|
||||
aria-checked={selectedItems.includes(item.value)}
|
||||
value={item.value}
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
{/if}
|
||||
</Command.Empty>
|
||||
<Command.Group>
|
||||
{#each filteredItems as item}
|
||||
{#each filteredItems as item (item.value)}
|
||||
<Command.Item
|
||||
value={item.value}
|
||||
onSelect={() => {
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
{#if typeof parsedContent === 'string'}
|
||||
{parsedContent}
|
||||
{:else}
|
||||
{#each parsedContent as part}
|
||||
{#each parsedContent as part, i (i)}
|
||||
{#if part.type === 'text'}
|
||||
{part.content}
|
||||
{:else if part.type === 'bold'}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
let error = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
props.src;
|
||||
void props.src;
|
||||
error = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
{selectedExpiration}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each Object.keys(availableExpirations) as key}
|
||||
{#each Object.keys(availableExpirations) as key (key)}
|
||||
<Select.Item value={key}>{key}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
description={m.view_the_groups_you_are_a_member_of()}
|
||||
/>
|
||||
{/if}
|
||||
{#each customScopes as scope}
|
||||
{#each customScopes as scope (scope)}
|
||||
<ScopeItem
|
||||
icon={LucideKeyRound}
|
||||
name={infoByKey.get(scope)?.name ?? scope}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
aria-label={m.settings()}
|
||||
data-sveltekit-keepfocus
|
||||
>
|
||||
{#each items as item, i}
|
||||
{#each items as item, i (groupId(item, i))}
|
||||
{#if item.children?.length}
|
||||
{@const id = groupId(item, i)}
|
||||
<div class="group">
|
||||
@@ -110,7 +110,7 @@
|
||||
class="border-border/50 ml-2 border-l pl-2"
|
||||
transition:slide|local={{ duration: 120 }}
|
||||
>
|
||||
{#each item.children as child, j}
|
||||
{#each item.children as child, j (child.href ?? child.label)}
|
||||
<li>
|
||||
<a
|
||||
href={child.href}
|
||||
|
||||
@@ -5,17 +5,14 @@
|
||||
import type { UserSignUp } from '$lib/types/user.type';
|
||||
import { preventDefault } from '$lib/utils/event-util';
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { tryCatch } from '$lib/utils/try-catch-util';
|
||||
import { emptyToUndefined, usernameSchema } from '$lib/utils/zod-util';
|
||||
import { get } from 'svelte/store';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
let {
|
||||
callback,
|
||||
isLoading
|
||||
callback
|
||||
}: {
|
||||
callback: (user: UserSignUp) => Promise<boolean>;
|
||||
isLoading: boolean;
|
||||
} = $props();
|
||||
|
||||
const initialData: UserSignUp = {
|
||||
@@ -35,18 +32,11 @@
|
||||
|
||||
const { inputs, ...form } = createForm<FormSchema>(formSchema, initialData);
|
||||
|
||||
let userData: UserSignUp | null = $state(null);
|
||||
|
||||
async function onSubmit() {
|
||||
const data = form.validate();
|
||||
if (!data) return;
|
||||
|
||||
isLoading = true;
|
||||
const result = await tryCatch(callback(data));
|
||||
if (result.data) {
|
||||
userData = data;
|
||||
isLoading = false;
|
||||
}
|
||||
await callback(data);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
}
|
||||
];
|
||||
|
||||
const actions: CreateAdvancedTableActions<SignupToken> = (_) => [
|
||||
const actions: CreateAdvancedTableActions<SignupToken> = () => [
|
||||
{
|
||||
label: m.copy(),
|
||||
icon: Copy,
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
{getExpirationLabel($inputs.ttl.value)}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each availableExpirations as expiration}
|
||||
{#each availableExpirations as expiration (expiration.value)}
|
||||
<Select.Item value={expiration.value.toString()}>
|
||||
{expiration.label}
|
||||
</Select.Item>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" generics="TData, TValue">
|
||||
<script lang="ts">
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Command from '$lib/components/ui/command/index.js';
|
||||
@@ -15,7 +15,7 @@
|
||||
options,
|
||||
selectedValues = new Set<string | boolean>(),
|
||||
showCheckboxes = true,
|
||||
onChanged = (selected: Set<string | boolean>) => {}
|
||||
onChanged = () => {}
|
||||
}: {
|
||||
title: string;
|
||||
options: {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#each filterableColumns as col}
|
||||
{#each filterableColumns as col (col.column)}
|
||||
<AdvancedTableFilter
|
||||
title={col.name}
|
||||
options={col.options}
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
{:else}
|
||||
{#if !items}
|
||||
<div>
|
||||
{#each Array((tablePreferences.current.length || 10) + 1) as _}
|
||||
{#each Array.from({ length: (tablePreferences.current.length || 10) + 1 }, (_, i) => i) as i (i)}
|
||||
<div>
|
||||
<Skeleton class="mt-3 h-[45px] w-full rounded-lg" />
|
||||
</div>
|
||||
@@ -222,7 +222,7 @@
|
||||
</Table.Head>
|
||||
{/if}
|
||||
|
||||
{#each visibleColumns as column}
|
||||
{#each visibleColumns as column (column.key ?? column.column ?? column.label)}
|
||||
<Table.Head
|
||||
class={cn(column.sortable && 'p-0')}
|
||||
aria-sort={column.sortable
|
||||
@@ -266,7 +266,7 @@
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each items.data as item}
|
||||
{#each items.data as item (item.id)}
|
||||
<Table.Row
|
||||
class={{
|
||||
'bg-muted/20': selectedIds?.includes(item.id),
|
||||
@@ -283,7 +283,7 @@
|
||||
/>
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
{#each visibleColumns as column}
|
||||
{#each visibleColumns as column (column.key ?? column.column ?? column.label)}
|
||||
<Table.Cell>
|
||||
{#if column.value}
|
||||
{column.value(item)}
|
||||
@@ -306,7 +306,7 @@
|
||||
<span class="sr-only">{m.toggle_menu()}</span>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
{#each actions(item).filter((a) => !a.hidden) as action}
|
||||
{#each actions(item).filter((a) => !a.hidden) as action (action.label)}
|
||||
<DropdownMenu.Item
|
||||
onclick={() => action.onClick(item)}
|
||||
disabled={action.disabled}
|
||||
@@ -342,7 +342,7 @@
|
||||
{items?.pagination.itemsPerPage}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each availablePageSizes as size}
|
||||
{#each availablePageSizes as size (size)}
|
||||
<Select.Item value={size.toString()}>{size}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
function onTabChange(newValue: string) {
|
||||
if (useHash && page.url.hash !== newValue) {
|
||||
history.replaceState(history.state, "",location.pathname + location.search + `#${newValue}`);
|
||||
history.replaceState(history.state, '', location.pathname + location.search + `#${newValue}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -108,7 +108,9 @@ function parseValue(value: string) {
|
||||
return parsed;
|
||||
}
|
||||
value = String(parsed);
|
||||
} catch {}
|
||||
} catch {
|
||||
// Keep the original string when the value is not valid JSON
|
||||
}
|
||||
|
||||
// Handle rest of the types
|
||||
if (value === 'true') {
|
||||
|
||||
@@ -5,9 +5,7 @@ export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
|
||||
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
||||
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
|
||||
|
||||
@@ -237,13 +237,13 @@
|
||||
>
|
||||
{#snippet children({ cells })}
|
||||
<InputOTP.Group>
|
||||
{#each cells.slice(0, 4) as cell}
|
||||
{#each cells.slice(0, 4) as cell (cell)}
|
||||
<InputOTP.Slot {cell} />
|
||||
{/each}
|
||||
</InputOTP.Group>
|
||||
<InputOTP.Separator />
|
||||
<InputOTP.Group>
|
||||
{#each cells.slice(4) as cell}
|
||||
{#each cells.slice(4) as cell (cell)}
|
||||
<InputOTP.Slot {cell} />
|
||||
{/each}
|
||||
</InputOTP.Group>
|
||||
|
||||
@@ -2,15 +2,9 @@
|
||||
import SignInWrapper from '$lib/components/login-wrapper.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import OidcService from '$lib/services/oidc-service';
|
||||
import WebAuthnService from '$lib/services/webauthn-service';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import ClientProviderImages from '../../authorize/components/client-provider-images.svelte';
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
const webauthnService = new WebAuthnService();
|
||||
const oidcService = new OidcService();
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
let { error } = data;
|
||||
</script>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
{m.if_you_do_not_have_access_to_your_passkey_you_can_sign_in_using_one_of_the_following_methods()}
|
||||
</p>
|
||||
<Item.Group class="mt-5 gap-3">
|
||||
{#each methods as method}
|
||||
{#each methods as method (method.href)}
|
||||
<Item.Root variant="outline" class="gap-5">
|
||||
{#snippet child({ props })}
|
||||
<a href={method.href + page.url.search} {...props}>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
try {
|
||||
goto(data.redirect);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
error = m.invalid_redirect_url();
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -94,7 +94,7 @@
|
||||
<InputOTP.Root maxlength={6} bind:value={code} autofocus>
|
||||
{#snippet children({ cells })}
|
||||
<InputOTP.Group>
|
||||
{#each cells as cell}
|
||||
{#each cells as cell (cell)}
|
||||
<InputOTP.Slot {cell} />
|
||||
{/each}
|
||||
</InputOTP.Group>
|
||||
|
||||
@@ -36,7 +36,10 @@
|
||||
{ href: '/settings/admin/oidc-clients', label: m.oidc_clients() },
|
||||
{ href: '/settings/admin/apis', label: m.apis() },
|
||||
{ href: '/settings/admin/api-keys', label: m.api_keys() },
|
||||
{ href: '/settings/admin/application-configuration', label: m.application_configuration() }
|
||||
{
|
||||
href: '/settings/admin/application-configuration',
|
||||
label: m.application_configuration()
|
||||
}
|
||||
];
|
||||
|
||||
if (user?.isAdmin || $userStore?.isAdmin) {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const load: LayoutLoad = async () => {
|
||||
const currentVersion = versionService.getCurrentVersion();
|
||||
|
||||
let newestVersion = null;
|
||||
let isUpToDate = true;
|
||||
let isUpToDate: boolean;
|
||||
try {
|
||||
newestVersion = await versionService.getNewestVersion();
|
||||
// If newestVersion is empty, it means the check is disabled or failed.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import type { Passkey } from '$lib/types/passkey.type';
|
||||
import type { AccountUpdate, UserCreate } from '$lib/types/user.type';
|
||||
import type { AccountUpdate } from '$lib/types/user.type';
|
||||
import { axiosErrorToast, getWebauthnErrorMessage } from '$lib/utils/error-util';
|
||||
import {
|
||||
KeyRound,
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
{locales[currentLocale]}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-100">
|
||||
{#each Object.entries(locales) as [value, label]}
|
||||
{#each Object.entries(locales) as [value, label] (value)}
|
||||
<Select.Item {value}>{label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</script>
|
||||
|
||||
<Item.Group class="mt-3">
|
||||
{#each passkeys as passkey}
|
||||
{#each passkeys as passkey (passkey.id)}
|
||||
<GlassRowItem
|
||||
label={passkey.name}
|
||||
description={m.added_on() + ' ' + new Date(passkey.createdAt).toLocaleDateString()}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { preventDefault } from '$lib/utils/event-util';
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { emptyToUndefined } from '$lib/utils/zod-util';
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
let {
|
||||
@@ -17,7 +18,7 @@
|
||||
let isLoading = $state(false);
|
||||
|
||||
// Set default expiration to 30 days from now
|
||||
const defaultExpiry = new Date();
|
||||
const defaultExpiry = new SvelteDate();
|
||||
defaultExpiry.setDate(defaultExpiry.getDate() + 30);
|
||||
defaultExpiry.setHours(0, 0, 0, 0);
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-y-3">
|
||||
{#each permissions as _, i}
|
||||
{#each permissions as permission, i (permission)}
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<Input
|
||||
class="font-mono sm:w-1/3"
|
||||
placeholder={m.api_permission_key()}
|
||||
bind:value={permissions[i].key}
|
||||
bind:value={permission.key}
|
||||
/>
|
||||
<Input class="sm:w-1/4" placeholder={m.name()} bind:value={permissions[i].name} />
|
||||
<Input placeholder={m.description()} bind:value={permissions[i].description} />
|
||||
<Input class="sm:w-1/4" placeholder={m.name()} bind:value={permission.name} />
|
||||
<Input placeholder={m.description()} bind:value={permission.description} />
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{ label: m.api_permissions(), key: 'permissions', value: (item) => item.permissions.length }
|
||||
];
|
||||
|
||||
const actions: CreateAdvancedTableActions<Api> = (api) => [
|
||||
const actions: CreateAdvancedTableActions<Api> = () => [
|
||||
{
|
||||
label: m.edit(),
|
||||
primary: true,
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#each accentColors as accent}
|
||||
{#each accentColors as accent (accent.color)}
|
||||
{@render colorOption(accent.label, accent.color, selectedColor === accent.color)}
|
||||
{/each}
|
||||
{#if isCustomColor || isPreviousColorCustom}
|
||||
|
||||
+4
-6
@@ -95,17 +95,15 @@
|
||||
await callback(data);
|
||||
|
||||
// Update the app config to don't display the unsaved changes warning
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// @ts-ignore
|
||||
appConfig[key] = value;
|
||||
});
|
||||
Object.assign(appConfig, data);
|
||||
|
||||
toast.success(m.email_configuration_updated_successfully());
|
||||
return true;
|
||||
}
|
||||
async function onTestEmail() {
|
||||
// @ts-ignore
|
||||
const hasChanges = Object.keys($inputs).some((key) => $inputs[key].value !== appConfig[key]);
|
||||
const hasChanges = Object.entries($inputs).some(
|
||||
([key, input]) => input.value !== appConfig[key as keyof AllAppConfig]
|
||||
);
|
||||
|
||||
if (hasChanges) {
|
||||
openConfirmDialog({
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@
|
||||
?.label ?? $inputs.homePageUrl.value}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each homePageUrlOptions as option}
|
||||
{#each homePageUrlOptions as option (option.value)}
|
||||
<Select.Item value={option.value}>
|
||||
{option.label}
|
||||
</Select.Item>
|
||||
|
||||
@@ -294,7 +294,7 @@
|
||||
{/if}
|
||||
{#if showAllDetails}
|
||||
<div transition:slide>
|
||||
{#each Object.entries(setupDetails) as [key, value]}
|
||||
{#each Object.entries(setupDetails) as [key, value] (key)}
|
||||
<div class="mb-2 flex flex-col sm:flex-row sm:items-center">
|
||||
<Field.Label class="w-52">{key}</Field.Label>
|
||||
<CopyToClipboard {value}>
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each apis as api}
|
||||
{#each apis as api (api.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<div class="flex flex-col gap-1">
|
||||
|
||||
+3
-4
@@ -66,10 +66,9 @@
|
||||
}
|
||||
|
||||
async function onSync() {
|
||||
const hasChanges = Object.keys($inputs).some(
|
||||
// @ts-ignore
|
||||
(key) => $inputs[key].value !== (existingProvider as any)[key]
|
||||
);
|
||||
const hasChanges =
|
||||
$inputs.endpoint.value !== existingProvider?.endpoint ||
|
||||
($inputs.token?.value ?? '') !== (existingProvider?.token ?? '');
|
||||
|
||||
if (hasChanges) {
|
||||
openConfirmDialog({
|
||||
|
||||
@@ -5,19 +5,17 @@
|
||||
import * as Field from '$lib/components/ui/field';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import type { OidcClient, OidcClientFederatedIdentity } from '$lib/types/oidc.type';
|
||||
import type { OidcClientFederatedIdentity } from '$lib/types/oidc.type';
|
||||
import { LucideMinus, LucidePlus } from '@lucide/svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
let {
|
||||
client,
|
||||
federatedIdentities = $bindable([]),
|
||||
errors,
|
||||
...restProps
|
||||
}: HTMLAttributes<HTMLDivElement> & {
|
||||
client?: OidcClient;
|
||||
federatedIdentities: OidcClientFederatedIdentity[];
|
||||
errors?: z.core.$ZodIssue[];
|
||||
|
||||
@@ -66,7 +64,7 @@
|
||||
docsLink="https://pocket-id.org/docs/guides/oidc-client-authentication"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
{#each federatedIdentities as identity, i}
|
||||
{#each federatedIdentities as identity, i (identity)}
|
||||
<div class="space-y-3 rounded-lg border p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<Field.Label>Identity {i + 1}</Field.Label>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div {...restProps}>
|
||||
<FormInput {label} {description}>
|
||||
<div class="flex flex-col gap-y-2">
|
||||
{#each callbackURLs as _, i}
|
||||
{#each callbackURLs.keys() as i (i)}
|
||||
<div class="flex gap-x-2">
|
||||
<Input
|
||||
aria-invalid={!!error}
|
||||
|
||||
@@ -154,7 +154,9 @@
|
||||
darkLogo = input;
|
||||
darkLogoDataURL = URL.createObjectURL(input);
|
||||
}
|
||||
logoUrlInput && (logoUrlInput.value = '');
|
||||
if (logoUrlInput) {
|
||||
logoUrlInput.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +164,15 @@
|
||||
if (light) {
|
||||
logo = null;
|
||||
logoDataURL = null;
|
||||
$inputs.logoUrl && ($inputs.logoUrl.value = '');
|
||||
if ($inputs.logoUrl) {
|
||||
$inputs.logoUrl.value = '';
|
||||
}
|
||||
} else {
|
||||
darkLogo = null;
|
||||
darkLogoDataURL = null;
|
||||
$inputs.darkLogoUrl && ($inputs.darkLogoUrl.value = '');
|
||||
if ($inputs.darkLogoUrl) {
|
||||
$inputs.darkLogoUrl.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +323,6 @@
|
||||
/>
|
||||
{/if}
|
||||
<FederatedIdentitiesInput
|
||||
client={existingClient}
|
||||
bind:federatedIdentities={$inputs.credentials.value.federatedIdentities}
|
||||
errors={getFederatedIdentityErrors($errors)}
|
||||
/>
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
}
|
||||
];
|
||||
|
||||
const actions: CreateAdvancedTableActions<OidcClientWithAllowedUserGroupsCount> = (_) => [
|
||||
const actions: CreateAdvancedTableActions<OidcClientWithAllowedUserGroupsCount> = () => [
|
||||
{
|
||||
label: m.edit(),
|
||||
primary: true,
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{#each Object.entries(data || {}) as [key, value]}
|
||||
{#each Object.entries(data || {}) as [key, value] (key)}
|
||||
<div class="grid grid-cols-1 items-start gap-4 border-b pb-3 md:grid-cols-[200px_1fr]">
|
||||
<Field.Label class="pt-1">{key}</Field.Label>
|
||||
<div class="min-w-0">
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function onNameInput(_: Event) {
|
||||
function onNameInput() {
|
||||
hasManualNameEdit = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</script>
|
||||
|
||||
<Item.Group class="mt-3">
|
||||
{#each [...passkeys].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) as passkey}
|
||||
{#each [...passkeys].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) as passkey (passkey.id)}
|
||||
<PasskeyRow
|
||||
label={passkey.name}
|
||||
description={m.added_on() + ' ' + new Date(passkey.createdAt).toLocaleDateString()}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
class="grid gap-3"
|
||||
style="grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));"
|
||||
>
|
||||
{#each clients.data as client}
|
||||
{#each clients.data as client (client.id)}
|
||||
<AuthorizedOidcClientCard {client} onRevoke={revokeAuthorizedClient} />
|
||||
{/each}
|
||||
<!-- Gap fix if two elements are present-->
|
||||
|
||||
@@ -18,22 +18,17 @@
|
||||
let { data } = $props();
|
||||
const userService = new UserService();
|
||||
|
||||
let isLoading = $state(false);
|
||||
let error: string | undefined = $state();
|
||||
|
||||
async function handleSignup(userData: UserSignUp) {
|
||||
isLoading = true;
|
||||
|
||||
const result = await tryCatch(userService.signup({ ...userData, token: data.token }));
|
||||
|
||||
if (result.error) {
|
||||
error = getAxiosErrorMessage(result.error);
|
||||
isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
await userStore.setUser(result.data);
|
||||
isLoading = false;
|
||||
|
||||
goto('/signup/add-passkey');
|
||||
return true;
|
||||
@@ -75,7 +70,7 @@
|
||||
</p>
|
||||
{/if}
|
||||
{#if $appConfigStore.allowUserSignups === 'open' || data.token}
|
||||
<SignupForm callback={handleSignup} {isLoading} />
|
||||
<SignupForm callback={handleSignup} />
|
||||
<div class="mt-10 flex w-full items-center justify-between gap-2">
|
||||
<a class="text-muted-foreground mt-5 flex text-sm" href="/login"
|
||||
><LucideChevronLeft class="size-5" /> {m.back()}</a
|
||||
|
||||
@@ -13,25 +13,19 @@
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoginLogoErrorSuccessIndicator from '../../login/components/login-logo-error-success-indicator.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
const userService = new UserService();
|
||||
|
||||
let isLoading = $state(false);
|
||||
let error: string | undefined = $state();
|
||||
|
||||
async function handleSignup(userData: UserSignUp) {
|
||||
isLoading = true;
|
||||
|
||||
const result = await tryCatch(userService.signupInitialUser(userData));
|
||||
|
||||
if (result.error) {
|
||||
error = getAxiosErrorMessage(result.error);
|
||||
isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
await userStore.setUser(result.data);
|
||||
isLoading = false;
|
||||
|
||||
goto('/signup/add-passkey');
|
||||
return true;
|
||||
@@ -61,7 +55,7 @@
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<SignupForm callback={handleSignup} {isLoading} />
|
||||
<SignupForm callback={handleSignup} />
|
||||
<div class="mt-10 flex w-full justify-end">
|
||||
<Button type="submit" form="sign-up-form" onclick={() => (error = undefined)}
|
||||
>{m.signup()}</Button
|
||||
|
||||
Generated
+16
@@ -111,6 +111,9 @@ importers:
|
||||
specifier: ^4.4.3
|
||||
version: 4.4.3
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1(eslint@10.7.0(jiti@2.7.0))
|
||||
'@inlang/paraglide-js':
|
||||
specifier: ^2.18.0
|
||||
version: 2.22.0(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))
|
||||
@@ -483,6 +486,15 @@ packages:
|
||||
resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
|
||||
'@eslint/js@10.0.1':
|
||||
resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
peerDependencies:
|
||||
eslint: ^10.0.0
|
||||
peerDependenciesMeta:
|
||||
eslint:
|
||||
optional: true
|
||||
|
||||
'@eslint/object-schema@3.0.5':
|
||||
resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
@@ -3366,6 +3378,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.15
|
||||
|
||||
'@eslint/js@10.0.1(eslint@10.7.0(jiti@2.7.0))':
|
||||
optionalDependencies:
|
||||
eslint: 10.7.0(jiti@2.7.0)
|
||||
|
||||
'@eslint/object-schema@3.0.5': {}
|
||||
|
||||
'@eslint/plugin-kit@0.7.2':
|
||||
|
||||
Reference in New Issue
Block a user