Initial commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<script lang="ts">
|
||||
import { uploadBookFiles } from '$lib/api';
|
||||
import {
|
||||
displaySize,
|
||||
FileDropZone,
|
||||
type FileDropZoneProps
|
||||
} from '$lib/components/ui/file-drop-zone';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Field from '$lib/components/ui/field/index.js';
|
||||
|
||||
import { Switch } from '$lib/components/ui/switch/index';
|
||||
import { X } from '@lucide/svelte';
|
||||
|
||||
import type { Book } from '$lib/schema';
|
||||
import { tick } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
let { book }: { book: Book } = $props();
|
||||
|
||||
let files = $derived(uploadBookFiles.fields.files.value() ?? []);
|
||||
|
||||
let autoUploadOnDrop = $state(true);
|
||||
let formEl = $state<HTMLFormElement>();
|
||||
|
||||
const onUpload: FileDropZoneProps['onUpload'] = async (uploadedFiles) => {
|
||||
uploadBookFiles.fields.files.set([...Array.from(files), ...uploadedFiles]);
|
||||
if (autoUploadOnDrop && files.length > 0) {
|
||||
await tick();
|
||||
formEl?.requestSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
const onFileRejected: FileDropZoneProps['onFileRejected'] = async ({ reason, file }) => {
|
||||
toast.error(`${file.name} failed to upload!`, { description: reason });
|
||||
};
|
||||
</script>
|
||||
|
||||
<form
|
||||
{...uploadBookFiles.enhance(async ({ submit, form }) => {
|
||||
try {
|
||||
await submit();
|
||||
|
||||
// Check if there are any validation issues
|
||||
const issues = uploadBookFiles.fields.allIssues();
|
||||
if (issues && issues.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the files field
|
||||
uploadBookFiles.fields.files.set([]);
|
||||
toast.success('Files successfully added!');
|
||||
} catch (error) {
|
||||
console.error('Failed to upload files: ', error);
|
||||
toast.error('Failed to upload files');
|
||||
}
|
||||
})}
|
||||
bind:this={formEl}
|
||||
enctype="multipart/form-data"
|
||||
class="flex w-full flex-col gap-2 p-4"
|
||||
>
|
||||
<input {...uploadBookFiles.fields.book_id.as('hidden', book.id)} />
|
||||
|
||||
<FileDropZone
|
||||
{onUpload}
|
||||
{onFileRejected}
|
||||
accept=".pdf,.epub,.mobi,application/pdf,application/epub+zip,application/x-mobipocket-ebook"
|
||||
sublabel="Only PDF, EPUB, and MOBI files supported"
|
||||
/>
|
||||
<input class="hidden" {...uploadBookFiles.fields.files.as('file multiple')} />
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each files as file, idx}
|
||||
<div class="flex place-items-center justify-between gap-2">
|
||||
<div class="flex flex-col">
|
||||
<span>{file.name}</span>
|
||||
<span class="text-xs text-muted-foreground">{displaySize(file.size)}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onclick={() => {
|
||||
uploadBookFiles.fields.files.set([
|
||||
...Array.from(files).slice(0, idx),
|
||||
...Array.from(files).slice(idx + 1)
|
||||
]);
|
||||
}}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<Switch bind:checked={autoUploadOnDrop} />
|
||||
<Field.Label for="auto-upload-on-drop">Auto upload on file drop</Field.Label>
|
||||
<Button type="submit" class="ml-auto w-fit">Upload</Button>
|
||||
</div>
|
||||
</form>
|
||||
Reference in New Issue
Block a user