diff options
Diffstat (limited to 'frontend/src/lib/dialogs')
-rw-r--r-- | frontend/src/lib/dialogs/AddArtist.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/AddCharacter.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/AddCircle.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/AddNamespace.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/AddTag.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/AddWorld.svelte | 30 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/ConfirmDeletion.svelte | 51 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditArtist.svelte | 46 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditCharacter.svelte | 46 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditCircle.svelte | 46 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditNamespace.svelte | 46 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditTag.svelte | 44 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/EditWorld.svelte | 46 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/UpdateComics.svelte | 96 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/UpdateTags.svelte | 45 | ||||
-rw-r--r-- | frontend/src/lib/dialogs/components/UpdateModeSelector.svelte | 24 |
16 files changed, 670 insertions, 0 deletions
diff --git a/frontend/src/lib/dialogs/AddArtist.svelte b/frontend/src/lib/dialogs/AddArtist.svelte new file mode 100644 index 0000000..6ec93c5 --- /dev/null +++ b/frontend/src/lib/dialogs/AddArtist.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addArtist, type ArtistInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import ArtistForm from '$lib/forms/ArtistForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let artist = { name: '' }; + + function add(event: CustomEvent<ArtistInput>) { + addArtist(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add Artist</h2> + </svelte:fragment> + <ArtistForm bind:artist on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={artist.name.length > 0} /> + </div> + </ArtistForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/AddCharacter.svelte b/frontend/src/lib/dialogs/AddCharacter.svelte new file mode 100644 index 0000000..23fea08 --- /dev/null +++ b/frontend/src/lib/dialogs/AddCharacter.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addCharacter, type CharacterInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import CharacterForm from '$lib/forms/CharacterForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let character = { name: '' }; + + function add(event: CustomEvent<CharacterInput>) { + addCharacter(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add Character</h2> + </svelte:fragment> + <CharacterForm bind:character on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={character.name.length > 0} /> + </div> + </CharacterForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/AddCircle.svelte b/frontend/src/lib/dialogs/AddCircle.svelte new file mode 100644 index 0000000..f0ef014 --- /dev/null +++ b/frontend/src/lib/dialogs/AddCircle.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addCircle, type CircleInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import CircleForm from '$lib/forms/CircleForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let circle = { name: '' }; + + function add(event: CustomEvent<CircleInput>) { + addCircle(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add Circle</h2> + </svelte:fragment> + <CircleForm bind:circle on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={circle.name.length > 0} /> + </div> + </CircleForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/AddNamespace.svelte b/frontend/src/lib/dialogs/AddNamespace.svelte new file mode 100644 index 0000000..e81b22a --- /dev/null +++ b/frontend/src/lib/dialogs/AddNamespace.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addNamespace, type NamespaceInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import NamespaceForm from '$lib/forms/NamespaceForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let namespace = { name: '' }; + + function add(event: CustomEvent<NamespaceInput>) { + addNamespace(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add Namespace</h2> + </svelte:fragment> + <NamespaceForm bind:namespace on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={namespace.name.length > 0} /> + </div> + </NamespaceForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/AddTag.svelte b/frontend/src/lib/dialogs/AddTag.svelte new file mode 100644 index 0000000..00d3a03 --- /dev/null +++ b/frontend/src/lib/dialogs/AddTag.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addTag, type TagInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import TagForm from '$lib/forms/TagForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let tag = { name: '', namespaces: [] }; + + function add(event: CustomEvent<TagInput>) { + addTag(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add Tag</h2> + </svelte:fragment> + <TagForm bind:tag on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={tag.name.length > 0} /> + </div> + </TagForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/AddWorld.svelte b/frontend/src/lib/dialogs/AddWorld.svelte new file mode 100644 index 0000000..ceb946e --- /dev/null +++ b/frontend/src/lib/dialogs/AddWorld.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { addWorld, type WorldInput } from '$gql/Mutations'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import WorldForm from '$lib/forms/WorldForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + let world = { name: '' }; + + function add(event: CustomEvent<WorldInput>) { + addWorld(client, { input: event.detail }).then(closeModal).catch(toastFinally); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Add World</h2> + </svelte:fragment> + <WorldForm bind:world on:submit={add}> + <div class="flex justify-end gap-4"> + <SubmitButton active={world.name.length > 0} /> + </div> + </WorldForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/ConfirmDeletion.svelte b/frontend/src/lib/dialogs/ConfirmDeletion.svelte new file mode 100644 index 0000000..6b0cbf8 --- /dev/null +++ b/frontend/src/lib/dialogs/ConfirmDeletion.svelte @@ -0,0 +1,51 @@ +<script lang="ts"> + import { accelerator } from '$lib/Shortcuts'; + import Dialog from '$lib/components/Dialog.svelte'; + import { closeModal } from 'svelte-modals'; + + export let isOpen: boolean; + export let callback: () => void; + + export let names: string[]; + export let typename: string; + export let warning: string | undefined = undefined; + const multiple = names.length > 1; + const formattedTypename = multiple ? `${typename}s` : typename; + const formattedNames = multiple ? `${names.length} ${formattedTypename}` : names[0]; + + function confirm() { + callback(); + closeModal(); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Delete {formattedTypename}</h2> + </svelte:fragment> + <form on:submit|preventDefault={confirm}> + <div class="flex flex-col"> + <p class="mb-3"> + Are you sure you want to delete <span class="font-semibold">{formattedNames}</span>? + </p> + {#if multiple} + <ul class="mb-3 ml-8 list-disc"> + {#each names.slice(0, 10) as name} + <li>{name}</li> + {/each} + </ul> + {#if names.length - 10 > 0} + <p>... and {names.length - 10} more.</p> + {/if} + {/if} + {#if warning} + <p class="font-medium text-red-600">Warning: {warning}</p> + {/if} + </div> + + <div class="flex justify-end gap-4"> + <button type="submit" class="btn-rose" use:accelerator={'Enter'}>Delete</button> + <button type="button" on:click={closeModal} class="btn-slate">Cancel</button> + </div> + </form> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditArtist.svelte b/frontend/src/lib/dialogs/EditArtist.svelte new file mode 100644 index 0000000..dd08bc6 --- /dev/null +++ b/frontend/src/lib/dialogs/EditArtist.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { deleteArtists, updateArtists, type ArtistInput } from '$gql/Mutations'; + import { itemEquals } from '$gql/Utils'; + import { type Artist } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import ArtistForm from '$lib/forms/ArtistForm.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let artist: Artist; + const original = structuredClone(artist); + $: pending = !itemEquals(artist, original); + + function save(event: CustomEvent<ArtistInput>) { + updateArtists(client, { ids: artist.id, input: event.detail }) + .then(closeModal) + .catch(toastFinally); + } + + function deleteArtist() { + confirmDeletion('Artist', artist.name, () => { + deleteArtists(client, { ids: artist.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Artist</h2> + </svelte:fragment> + <ArtistForm bind:artist on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteArtist} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </ArtistForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditCharacter.svelte b/frontend/src/lib/dialogs/EditCharacter.svelte new file mode 100644 index 0000000..3b45e78 --- /dev/null +++ b/frontend/src/lib/dialogs/EditCharacter.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { deleteCharacters, updateCharacters, type CharacterInput } from '$gql/Mutations'; + import { itemEquals } from '$gql/Utils'; + import { type Character } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import CharacterForm from '$lib/forms/CharacterForm.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let character: Character; + const original = structuredClone(character); + $: pending = !itemEquals(original, character); + + function save(event: CustomEvent<CharacterInput>) { + updateCharacters(client, { ids: character.id, input: event.detail }) + .then(closeModal) + .catch(toastFinally); + } + + function deleteCharacter() { + confirmDeletion('Character', character.name, () => { + deleteCharacters(client, { ids: character.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Character</h2> + </svelte:fragment> + <CharacterForm bind:character on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteCharacter} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </CharacterForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditCircle.svelte b/frontend/src/lib/dialogs/EditCircle.svelte new file mode 100644 index 0000000..bdc1217 --- /dev/null +++ b/frontend/src/lib/dialogs/EditCircle.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { deleteCircles, updateCircles, type CircleInput } from '$gql/Mutations'; + import { itemEquals } from '$gql/Utils'; + import { type Circle } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import CircleForm from '$lib/forms/CircleForm.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let circle: Circle; + const original = structuredClone(circle); + $: pending = !itemEquals(original, circle); + + function save(event: CustomEvent<CircleInput>) { + updateCircles(client, { ids: circle.id, input: event.detail }) + .then(closeModal) + .catch(toastFinally); + } + + function deleteCircle() { + confirmDeletion('Circle', circle.name, () => { + deleteCircles(client, { ids: circle.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Circle</h2> + </svelte:fragment> + <CircleForm bind:circle on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteCircle} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </CircleForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditNamespace.svelte b/frontend/src/lib/dialogs/EditNamespace.svelte new file mode 100644 index 0000000..f398b21 --- /dev/null +++ b/frontend/src/lib/dialogs/EditNamespace.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { deleteNamespaces, updateNamespaces, type NamespaceInput } from '$gql/Mutations'; + import { itemEquals } from '$gql/Utils'; + import { type Namespace } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import NamespaceForm from '$lib/forms/NamespaceForm.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let namespace: Namespace; + const original = structuredClone(namespace); + $: pending = !itemEquals(original, namespace); + + function save(event: CustomEvent<NamespaceInput>) { + updateNamespaces(client, { ids: namespace.id, input: event.detail }) + .then(closeModal) + .catch(toastFinally); + } + + function deleteNamespace() { + confirmDeletion('Namespace', namespace.name, () => { + deleteNamespaces(client, { ids: namespace.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Namespace</h2> + </svelte:fragment> + <NamespaceForm bind:namespace on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteNamespace} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </NamespaceForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditTag.svelte b/frontend/src/lib/dialogs/EditTag.svelte new file mode 100644 index 0000000..d2d0013 --- /dev/null +++ b/frontend/src/lib/dialogs/EditTag.svelte @@ -0,0 +1,44 @@ +<script lang="ts"> + import { deleteTags, updateTags, type TagInput } from '$gql/Mutations'; + import { tagEquals } from '$gql/Utils'; + import { type FullTag } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import TagForm from '$lib/forms/TagForm.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let tag: FullTag; + const original = structuredClone(tag); + $: pending = !tagEquals(original, tag); + + function save(event: CustomEvent<TagInput>) { + updateTags(client, { ids: tag.id, input: event.detail }).then(closeModal).catch(toastFinally); + } + + function deleteTag() { + confirmDeletion('Tag', tag.name, () => { + deleteTags(client, { ids: tag.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Tag</h2> + </svelte:fragment> + <TagForm bind:tag on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteTag} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </TagForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/EditWorld.svelte b/frontend/src/lib/dialogs/EditWorld.svelte new file mode 100644 index 0000000..82afe6a --- /dev/null +++ b/frontend/src/lib/dialogs/EditWorld.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { type World } from '$gql/graphql'; + import { deleteWorlds, updateWorlds, type WorldInput } from '$gql/Mutations'; + import { itemEquals } from '$gql/Utils'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import Dialog from '$lib/components/Dialog.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import WorldForm from '$lib/forms/WorldForm.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + + const client = getContextClient(); + + export let isOpen: boolean; + + export let world: World; + const original = structuredClone(world); + $: pending = !itemEquals(original, world); + + function save(event: CustomEvent<WorldInput>) { + updateWorlds(client, { ids: world.id, input: event.detail }) + .then(closeModal) + .catch(toastFinally); + } + + function deleteWorld() { + confirmDeletion('World', world.name, () => { + deleteWorlds(client, { ids: world.id }).then(closeModal).catch(toastFinally); + }); + } +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit World</h2> + </svelte:fragment> + <WorldForm bind:world on:submit={save}> + <div class="flex gap-4"> + <DeleteButton on:click={deleteWorld} /> + <div class="grow" /> + <SubmitButton active={pending} /> + </div> + </WorldForm> +</Dialog> diff --git a/frontend/src/lib/dialogs/UpdateComics.svelte b/frontend/src/lib/dialogs/UpdateComics.svelte new file mode 100644 index 0000000..8de9622 --- /dev/null +++ b/frontend/src/lib/dialogs/UpdateComics.svelte @@ -0,0 +1,96 @@ +<script lang="ts"> + import { updateComics } from '$gql/Mutations'; + import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries'; + import { categories, censorships, directions, languages, layouts, ratings } from '$lib/Enums'; + import { toastFinally } from '$lib/Toasts'; + import { UpdateComicsControls } from '$lib/Update'; + import Dialog from '$lib/components/Dialog.svelte'; + import Labelled from '$lib/components/Labelled.svelte'; + import LabelledBlock from '$lib/components/LabelledBlock.svelte'; + import Select from '$lib/components/Select.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + import UpdateModeSelector from './components/UpdateModeSelector.svelte'; + + const client = getContextClient(); + + export let isOpen: boolean; + export let ids: number[]; + + $: tagsQuery = comicTagList(client); + $: artistsQuery = artistList(client); + $: charactersQuery = characterList(client); + $: circlesQuery = circleList(client); + $: worldsQuery = worldList(client); + + $: tags = $tagsQuery.data?.comicTags.edges; + $: artists = $artistsQuery.data?.artists.edges; + $: characters = $charactersQuery.data?.characters.edges; + $: circles = $circlesQuery.data?.circles.edges; + $: worlds = $worldsQuery.data?.worlds.edges; + + const controls = new UpdateComicsControls(); + + const update = () => { + updateComics(client, { + ids: ids, + input: controls.toInput() + }) + .then(closeModal) + .catch(toastFinally); + }; +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Comics</h2> + </svelte:fragment> + <form on:submit|preventDefault={update}> + <div class="grid-labels"> + <Labelled label="Category" let:id> + <Select clearable {id} options={categories} bind:value={controls.category.value} /> + </Labelled> + <Labelled label="Rating" let:id> + <Select clearable {id} options={ratings} bind:value={controls.rating.value} /> + </Labelled> + <Labelled label="Censorship" let:id> + <Select clearable {id} options={censorships} bind:value={controls.censorship.value} /> + </Labelled> + <Labelled label="Language" let:id> + <Select clearable {id} options={languages} bind:value={controls.language.value} /> + </Labelled> + <Labelled label="Direction" let:id> + <Select clearable {id} options={directions} bind:value={controls.direction.value} /> + </Labelled> + <Labelled label="Layout" let:id> + <Select clearable {id} options={layouts} bind:value={controls.layout.value} /> + </Labelled> + </div> + + <LabelledBlock label="Artists" let:id> + <Select multi {id} options={artists} bind:value={controls.artists.ids} /> + <UpdateModeSelector bind:mode={controls.artists.options.mode} slot="controls" /> + </LabelledBlock> + <LabelledBlock label="Circles" let:id> + <Select multi {id} options={circles} bind:value={controls.circles.ids} /> + <UpdateModeSelector bind:mode={controls.circles.options.mode} slot="controls" /> + </LabelledBlock> + <LabelledBlock label="Characters" let:id> + <Select multi {id} options={characters} bind:value={controls.characters.ids} /> + <UpdateModeSelector bind:mode={controls.characters.options.mode} slot="controls" /> + </LabelledBlock> + <LabelledBlock label="Worlds" let:id> + <Select multi {id} options={worlds} bind:value={controls.worlds.ids} /> + <UpdateModeSelector bind:mode={controls.worlds.options.mode} slot="controls" /> + </LabelledBlock> + <LabelledBlock label="Tags" let:id> + <Select multi {id} options={tags} bind:value={controls.tags.ids} /> + <UpdateModeSelector bind:mode={controls.tags.options.mode} slot="controls" /> + </LabelledBlock> + + <div class="flex justify-end gap-4"> + <SubmitButton active={controls.hasInput()} /> + </div> + </form> +</Dialog> diff --git a/frontend/src/lib/dialogs/UpdateTags.svelte b/frontend/src/lib/dialogs/UpdateTags.svelte new file mode 100644 index 0000000..f753c7f --- /dev/null +++ b/frontend/src/lib/dialogs/UpdateTags.svelte @@ -0,0 +1,45 @@ +<script lang="ts"> + import { updateTags } from '$gql/Mutations'; + import { namespaceList } from '$gql/Queries'; + import { toastFinally } from '$lib/Toasts'; + import { UpdateTagsControls } from '$lib/Update'; + import Dialog from '$lib/components/Dialog.svelte'; + import LabelledBlock from '$lib/components/LabelledBlock.svelte'; + import Select from '$lib/components/Select.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { getContextClient } from '@urql/svelte'; + import { closeModal } from 'svelte-modals'; + import UpdateModeSelector from './components/UpdateModeSelector.svelte'; + + const client = getContextClient(); + + $: namespaceQuery = namespaceList(client); + $: namespaces = $namespaceQuery.data?.namespaces.edges; + + export let isOpen: boolean; + export let ids: number[]; + + const controls = new UpdateTagsControls(); + + const update = () => { + updateTags(client, { ids: ids, input: controls.toInput() }) + .then(closeModal) + .catch(toastFinally); + }; +</script> + +<Dialog {isOpen}> + <svelte:fragment slot="header"> + <h2>Edit Tags</h2> + </svelte:fragment> + <form on:submit|preventDefault={update}> + <LabelledBlock label="Namespaces" let:id> + <Select multi {id} options={namespaces} bind:value={controls.namespaces.ids} /> + <UpdateModeSelector bind:mode={controls.namespaces.options.mode} slot="controls" /> + </LabelledBlock> + + <div class="flex justify-end gap-4"> + <SubmitButton active={controls.hasInput()} /> + </div> + </form> +</Dialog> diff --git a/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte new file mode 100644 index 0000000..e4b4479 --- /dev/null +++ b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import { UpdateMode } from '$gql/graphql'; + import { UpdateModeLabel } from '$lib/Enums'; + + export let mode: UpdateMode; + + function select(e: string) { + mode = e as UpdateMode; + } +</script> + +<div class="flex gap-1 pb-1 text-xs"> + {#each Object.entries(UpdateModeLabel) as [e, label]} + <button + type="button" + class:active={mode === e} + class:dangerous={mode !== UpdateMode.Add} + class="btn btn-xs hover:bg-slate-700 [&.active.dangerous]:bg-rose-800 [&.active]:bg-indigo-700" + on:click={() => select(e)} + > + {label} + </button> + {/each} +</div> |