summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/scraper/ComicScrapeForm.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/scraper/ComicScrapeForm.svelte')
-rw-r--r--frontend/src/lib/scraper/ComicScrapeForm.svelte138
1 files changed, 138 insertions, 0 deletions
diff --git a/frontend/src/lib/scraper/ComicScrapeForm.svelte b/frontend/src/lib/scraper/ComicScrapeForm.svelte
new file mode 100644
index 0000000..30ad89b
--- /dev/null
+++ b/frontend/src/lib/scraper/ComicScrapeForm.svelte
@@ -0,0 +1,138 @@
+<script lang="ts">
+ import { upsertComics } from '$gql/Mutations';
+ import { comicScrapersQuery, scrapeComic } from '$gql/Queries';
+ import { isError } from '$gql/Utils';
+ import { OnMissing, type FullComicFragment } from '$gql/graphql';
+ import { ScrapedComicSelector, getScraperContext } from '$lib/Scraper';
+ import { toastError, toastFinally } from '$lib/Toasts';
+ import Select from '$lib/components/Select.svelte';
+ import Spinner from '$lib/components/Spinner.svelte';
+ import { getContextClient } from '@urql/svelte';
+ import SelectorGroup from './components/SelectorGroup.svelte';
+ import SelectorItem from './components/SelectorItem.svelte';
+
+ let client = getContextClient();
+ const context = getScraperContext();
+
+ export let comic: FullComicFragment;
+ let createMissing = false;
+ let loading = false;
+
+ $: scrapersResult = comicScrapersQuery(client, { id: comic.id });
+ $: scrapers = $scrapersResult.data?.comicScrapers;
+
+ function scrape() {
+ loading = true;
+ scrapeComic(client, { id: comic.id, scraper: $context.scraper })
+ .then((result) => {
+ if (result.error) {
+ toastError(result.error.message);
+ return;
+ }
+
+ if (result.data) {
+ if (isError(result.data.scrapeComic)) {
+ toastError(result.data.scrapeComic.message);
+ return;
+ }
+
+ if (result.data.scrapeComic.__typename === 'ScrapeComicResult') {
+ $context.selector = new ScrapedComicSelector(result.data.scrapeComic.data, comic);
+ $context.warnings = result.data.scrapeComic.warnings;
+ }
+ }
+ })
+ .catch(toastFinally)
+ .finally(() => (loading = false));
+ }
+
+ function updateFromScrape(createMissing: boolean) {
+ if (!$context.selector) return;
+
+ upsertComics(client, {
+ ids: comic.id,
+ input: $context.selector.toInput(createMissing ? OnMissing.Create : OnMissing.Ignore)
+ })
+ .then(() => {
+ $context.selector = undefined;
+ $context.warnings = [];
+ })
+ .catch(toastFinally);
+ }
+</script>
+
+<div class="flex flex-col gap-4 text-sm">
+ {#if scrapers && scrapers.length === 0}
+ <h2 class="text-base">No scrapers available.</h2>
+ {:else}
+ <form on:submit|preventDefault={scrape}>
+ <div class="grid grid-cols-6 gap-2">
+ <div class="col-span-5">
+ <Select
+ id="scrapers"
+ options={scrapers}
+ placeholder={'Select scraper...'}
+ bind:value={$context.scraper}
+ />
+ </div>
+ <button type="submit" disabled={!$context.scraper} class="btn-blue">Scrape</button>
+ </div>
+ </form>
+ {/if}
+
+ {#if loading}
+ <Spinner />
+ {:else if $context.selector}
+ {#if $context.warnings.length > 0}
+ <div class="flex flex-col gap-2">
+ <h2 class="flex gap-1 border-b border-slate-700 text-base font-medium">Warnings</h2>
+ <ul class="ml-2 list-inside list-disc">
+ {#each $context.warnings as warning}
+ <li>{warning}</li>
+ {/each}
+ </ul>
+ </div>
+ {/if}
+ {#if !$context.selector.hasData()}
+ <h2 class="text-base">No data to merge.</h2>
+ {:else}
+ <div class="flex flex-col gap-2">
+ <h2 class="border-b border-slate-700 text-base font-medium">Results</h2>
+ <form on:submit|preventDefault={() => updateFromScrape(createMissing)}>
+ <div class="grid grid-cols-6 gap-4 pb-2">
+ <SelectorItem title="Title" selector={$context.selector.title} />
+ <SelectorItem title="Original Title" selector={$context.selector.originalTitle} />
+ <SelectorItem title="URL" selector={$context.selector.url} />
+ <SelectorItem title="Date" selector={$context.selector.date} --span="2" />
+ <SelectorItem title="Category" selector={$context.selector.category} --span="2" />
+ <SelectorItem title="Language" selector={$context.selector.language} --span="2" />
+ <SelectorItem title="Rating" selector={$context.selector.rating} --span="2" />
+ <SelectorItem title="Censorship" selector={$context.selector.censorship} --span="2" />
+ <SelectorItem title="Direction" selector={$context.selector.direction} --span="2" />
+ <SelectorItem title="Layout" selector={$context.selector.layout} --span="2" />
+ <SelectorGroup title="Artists" selectors={$context.selector.artists} />
+ <SelectorGroup title="Circles" selectors={$context.selector.circles} />
+ <SelectorGroup title="Characters" selectors={$context.selector.characters} />
+ <SelectorGroup title="Worlds" selectors={$context.selector.worlds} />
+ <SelectorGroup title="Tags" selectors={$context.selector.tags} />
+ </div>
+ <div class="flex flex-col gap-2">
+ <h2 class="border-b border-slate-700 text-base font-medium">Options</h2>
+ <div class="flex items-center gap-1">
+ <input
+ class="h-4 w-4"
+ type="checkbox"
+ id="create-missing"
+ bind:checked={createMissing}
+ />
+ <label class="shrink-0" for="create-missing">Create missing items</label>
+ </div>
+ </div>
+ <div class="flex gap-4">
+ <button type="submit" class="btn-blue">Merge</button>
+ </div>
+ </form>
+ </div>
+ {/if}
+ {/if}
+</div>