mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-29 18:56:36 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import ApplicationConfigurationService from '$lib/services/application-configuration-service';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
const applicationConfigurationService = new ApplicationConfigurationService(
|
||||
cookies.get('access_token')
|
||||
);
|
||||
const applicationConfiguration = await applicationConfigurationService.list();
|
||||
return { applicationConfiguration };
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import ApplicationConfigurationService from '$lib/services/application-configuration-service';
|
||||
import applicationConfigurationStore from '$lib/stores/application-configuration-store';
|
||||
import type { AllApplicationConfiguration } from '$lib/types/application-configuration';
|
||||
import { axiosErrorToast } from '$lib/utils/error-util';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import ApplicationConfigurationForm from './application-configuration-form.svelte';
|
||||
import UpdateApplicationImages from './update-application-images.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
let applicationConfiguration = $state(data.applicationConfiguration);
|
||||
|
||||
const applicationConfigurationService = new ApplicationConfigurationService();
|
||||
|
||||
async function updateConfiguration(configuration: AllApplicationConfiguration) {
|
||||
await applicationConfigurationService
|
||||
.update(configuration)
|
||||
.then(() => toast.success('Application configuration updated successfully'))
|
||||
.catch(axiosErrorToast);
|
||||
await applicationConfigurationStore.reload();
|
||||
}
|
||||
|
||||
async function updateImages(
|
||||
logo: File | null,
|
||||
backgroundImage: File | null,
|
||||
favicon: File | null
|
||||
) {
|
||||
const faviconPromise = favicon
|
||||
? applicationConfigurationService.updateFavicon(favicon)
|
||||
: Promise.resolve();
|
||||
const logoPromise = logo ? applicationConfigurationService.updateLogo(logo) : Promise.resolve();
|
||||
const backgroundImagePromise = backgroundImage
|
||||
? applicationConfigurationService.updateBackgroundImage(backgroundImage)
|
||||
: Promise.resolve();
|
||||
|
||||
await Promise.all([logoPromise, backgroundImagePromise, faviconPromise])
|
||||
.then(() => toast.success('Images updated successfully'))
|
||||
.catch(axiosErrorToast);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Application Configuration</title>
|
||||
</svelte:head>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>General</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<ApplicationConfigurationForm {applicationConfiguration} callback={updateConfiguration} />
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Images</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<UpdateApplicationImages callback={updateImages} />
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import FormInput from '$lib/components/form-input.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import type { AllApplicationConfiguration } from '$lib/types/application-configuration';
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { z } from 'zod';
|
||||
|
||||
let {
|
||||
callback,
|
||||
applicationConfiguration
|
||||
}: {
|
||||
applicationConfiguration: AllApplicationConfiguration;
|
||||
callback: (user: AllApplicationConfiguration) => Promise<void>;
|
||||
} = $props();
|
||||
|
||||
let isLoading = $state(false);
|
||||
|
||||
const updatedApplicationConfiguration: AllApplicationConfiguration = {
|
||||
appName: applicationConfiguration.appName
|
||||
};
|
||||
|
||||
const formSchema = z.object({
|
||||
appName: z.string().min(2).max(30)
|
||||
});
|
||||
type FormSchema = typeof formSchema;
|
||||
|
||||
const { inputs, ...form } = createForm<FormSchema>(formSchema, updatedApplicationConfiguration);
|
||||
async function onSubmit() {
|
||||
const data = form.validate();
|
||||
if (!data) return;
|
||||
isLoading = true;
|
||||
await callback(data);
|
||||
isLoading = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<form onsubmit={onSubmit}>
|
||||
<div class="flex gap-3">
|
||||
<div class="w-full">
|
||||
<FormInput label="Application Name" bind:input={$inputs.appName} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 flex justify-end">
|
||||
<Button {isLoading} type="submit">Save</Button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import FileInput from '$lib/components/file-input.svelte';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
let {
|
||||
id,
|
||||
imageClass,
|
||||
label,
|
||||
image = $bindable<File | null>(null),
|
||||
imageURL,
|
||||
accept = 'image/png, image/jpeg, image/svg+xml',
|
||||
...restProps
|
||||
}: HTMLAttributes<HTMLDivElement> & {
|
||||
id: string;
|
||||
imageClass: string;
|
||||
label: string;
|
||||
image: File | null;
|
||||
imageURL: string;
|
||||
accept?: string;
|
||||
} = $props();
|
||||
|
||||
let imageDataURL = $state(imageURL);
|
||||
|
||||
function onImageChange(e: Event) {
|
||||
const file = (e.target as HTMLInputElement).files?.[0] || null;
|
||||
if (!file) return;
|
||||
|
||||
image = file;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
imageDataURL = event.target?.result as string;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div {...restProps}>
|
||||
<Label for={id}>{label}</Label>
|
||||
<FileInput {id} variant="secondary" {accept} onchange={onImageChange}>
|
||||
<div class="bg-muted group relative flex items-center rounded">
|
||||
<img
|
||||
class={cn(
|
||||
'h-full w-full rounded object-cover p-3 transition-opacity duration-200 group-hover:opacity-10',
|
||||
imageClass
|
||||
)}
|
||||
src={imageDataURL}
|
||||
alt={label}
|
||||
/>
|
||||
<span
|
||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transform font-medium opacity-0 transition-opacity group-hover:opacity-100"
|
||||
>
|
||||
Update
|
||||
</span>
|
||||
</div>
|
||||
</FileInput>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import ApplicationImage from './application-image.svelte';
|
||||
|
||||
let {
|
||||
callback
|
||||
}: {
|
||||
callback: (logo: File | null, backgroundImage: File | null, favicon: File | null) => void;
|
||||
} = $props();
|
||||
|
||||
let logo = $state<File | null>(null);
|
||||
let backgroundImage = $state<File | null>(null);
|
||||
let favicon = $state<File | null>(null);
|
||||
</script>
|
||||
|
||||
<div class="application-images-grid">
|
||||
<ApplicationImage
|
||||
id="favicon"
|
||||
imageClass="h-14 w-14 p-2"
|
||||
label="Favicon"
|
||||
bind:image={favicon}
|
||||
imageURL="/api/application-configuration/favicon"
|
||||
accept="image/x-icon"
|
||||
/>
|
||||
<ApplicationImage
|
||||
id="logo"
|
||||
imageClass="h-32 w-32"
|
||||
label="Logo"
|
||||
bind:image={logo}
|
||||
imageURL="/api/application-configuration/logo"
|
||||
/>
|
||||
<ApplicationImage
|
||||
id="background-image"
|
||||
class="basis-full lg:basis-auto"
|
||||
imageClass="h-[350px] max-w-[500px]"
|
||||
label="Background Image"
|
||||
bind:image={backgroundImage}
|
||||
imageURL="/api/application-configuration/background-image"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<Button class="mt-5" onclick={() => callback(logo, backgroundImage, favicon)}>Save</Button>
|
||||
</div>
|
||||
Reference in New Issue
Block a user