Compare commits
5 Commits
9bc39c3abf
...
v1.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 327c03c098 | |||
|
|
5e48f5737f | ||
| ece85cff7c | |||
|
|
4264051d11 | ||
|
|
7ccc3ade9e |
@@ -5,6 +5,7 @@ using FictionArchive.Service.NovelService.Models.Novels;
|
|||||||
using FictionArchive.Service.NovelService.Models.SourceAdapters;
|
using FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||||
using FictionArchive.Service.NovelService.Services;
|
using FictionArchive.Service.NovelService.Services;
|
||||||
using FictionArchive.Service.NovelService.Services.SourceAdapters;
|
using FictionArchive.Service.NovelService.Services.SourceAdapters;
|
||||||
|
using FictionArchive.Service.Shared.Constants;
|
||||||
using FictionArchive.Service.Shared.Contracts.Events;
|
using FictionArchive.Service.Shared.Contracts.Events;
|
||||||
using HotChocolate.Authorization;
|
using HotChocolate.Authorization;
|
||||||
using HotChocolate.Types;
|
using HotChocolate.Types;
|
||||||
@@ -21,19 +22,8 @@ public class Mutation
|
|||||||
return await service.QueueNovelImport(novelUrl);
|
return await service.QueueNovelImport(novelUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
public async Task<ChapterPullRequested> FetchChapterContents(
|
|
||||||
Guid importId,
|
|
||||||
uint novelId,
|
|
||||||
uint volumeId,
|
|
||||||
uint chapterOrder,
|
|
||||||
NovelUpdateService service)
|
|
||||||
{
|
|
||||||
return await service.QueueChapterPull(importId, novelId, volumeId, chapterOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Error<KeyNotFoundException>]
|
[Error<KeyNotFoundException>]
|
||||||
[Authorize]
|
[Authorize(Roles = [AuthorizationConstants.Roles.Admin])]
|
||||||
public async Task<bool> DeleteNovel(uint novelId, NovelUpdateService service)
|
public async Task<bool> DeleteNovel(uint novelId, NovelUpdateService service)
|
||||||
{
|
{
|
||||||
await service.DeleteNovel(novelId);
|
await service.DeleteNovel(novelId);
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
using FictionArchive.Service.ReportingService.Models;
|
|
||||||
using FictionArchive.Service.ReportingService.Services;
|
|
||||||
using HotChocolate.Authorization;
|
|
||||||
using HotChocolate.Data;
|
|
||||||
|
|
||||||
namespace FictionArchive.Service.ReportingService.GraphQL;
|
|
||||||
|
|
||||||
[QueryType]
|
|
||||||
public static class JobQueries
|
|
||||||
{
|
|
||||||
[UseProjection]
|
|
||||||
[Authorize]
|
|
||||||
[UseFirstOrDefault]
|
|
||||||
public static IQueryable<Job> GetJobById(
|
|
||||||
Guid jobId,
|
|
||||||
ReportingDbContext db)
|
|
||||||
=> db.Jobs.Where(j => j.Id == jobId);
|
|
||||||
|
|
||||||
[UsePaging]
|
|
||||||
[UseProjection]
|
|
||||||
[UseFiltering]
|
|
||||||
[UseSorting]
|
|
||||||
[Authorize]
|
|
||||||
public static IQueryable<Job> GetJobs(ReportingDbContext db)
|
|
||||||
=> db.Jobs;
|
|
||||||
}
|
|
||||||
71
FictionArchive.Service.ReportingService/GraphQL/Query.cs
Normal file
71
FictionArchive.Service.ReportingService/GraphQL/Query.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using FictionArchive.Service.ReportingService.Models.DTOs;
|
||||||
|
using FictionArchive.Service.ReportingService.Services;
|
||||||
|
using HotChocolate.Authorization;
|
||||||
|
using HotChocolate.Data;
|
||||||
|
|
||||||
|
namespace FictionArchive.Service.ReportingService.GraphQL;
|
||||||
|
|
||||||
|
public class Query
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
//[UseProjection]
|
||||||
|
[UseFirstOrDefault]
|
||||||
|
public IQueryable<JobDto> GetJobById(
|
||||||
|
Guid jobId,
|
||||||
|
ReportingDbContext db)
|
||||||
|
=> db.Jobs.Where(j => j.Id == jobId).Select(j => new JobDto
|
||||||
|
{
|
||||||
|
Id = j.Id,
|
||||||
|
CreatedTime = j.CreatedTime,
|
||||||
|
LastUpdatedTime = j.LastUpdatedTime,
|
||||||
|
ParentJobId = j.ParentJobId,
|
||||||
|
JobType = j.JobType,
|
||||||
|
DisplayName = j.DisplayName,
|
||||||
|
Status = j.Status,
|
||||||
|
ErrorMessage = j.ErrorMessage,
|
||||||
|
Metadata = j.Metadata,
|
||||||
|
ChildJobs = j.ChildJobs.Select(c => new JobDto
|
||||||
|
{
|
||||||
|
Id = c.Id,
|
||||||
|
CreatedTime = c.CreatedTime,
|
||||||
|
LastUpdatedTime = c.LastUpdatedTime,
|
||||||
|
ParentJobId = c.ParentJobId,
|
||||||
|
JobType = c.JobType,
|
||||||
|
DisplayName = c.DisplayName,
|
||||||
|
Status = c.Status,
|
||||||
|
ErrorMessage = c.ErrorMessage,
|
||||||
|
Metadata = c.Metadata
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[UsePaging]
|
||||||
|
//[UseProjection]
|
||||||
|
[UseFiltering]
|
||||||
|
[UseSorting]
|
||||||
|
public IQueryable<JobDto> GetJobs(ReportingDbContext db)
|
||||||
|
=> db.Jobs.Select(j => new JobDto
|
||||||
|
{
|
||||||
|
Id = j.Id,
|
||||||
|
CreatedTime = j.CreatedTime,
|
||||||
|
LastUpdatedTime = j.LastUpdatedTime,
|
||||||
|
ParentJobId = j.ParentJobId,
|
||||||
|
JobType = j.JobType,
|
||||||
|
DisplayName = j.DisplayName,
|
||||||
|
Status = j.Status,
|
||||||
|
ErrorMessage = j.ErrorMessage,
|
||||||
|
Metadata = j.Metadata,
|
||||||
|
ChildJobs = j.ChildJobs.Select(c => new JobDto
|
||||||
|
{
|
||||||
|
Id = c.Id,
|
||||||
|
CreatedTime = c.CreatedTime,
|
||||||
|
LastUpdatedTime = c.LastUpdatedTime,
|
||||||
|
ParentJobId = c.ParentJobId,
|
||||||
|
JobType = c.JobType,
|
||||||
|
DisplayName = c.DisplayName,
|
||||||
|
Status = c.Status,
|
||||||
|
ErrorMessage = c.ErrorMessage,
|
||||||
|
Metadata = c.Metadata
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using FictionArchive.Common.Enums;
|
||||||
|
using HotChocolate.Data;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
namespace FictionArchive.Service.ReportingService.Models.DTOs;
|
||||||
|
|
||||||
|
public class JobDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; init; }
|
||||||
|
public Instant CreatedTime { get; init; }
|
||||||
|
public Instant LastUpdatedTime { get; init; }
|
||||||
|
public Guid? ParentJobId { get; init; }
|
||||||
|
public required string JobType { get; init; }
|
||||||
|
public required string DisplayName { get; init; }
|
||||||
|
public JobStatus Status { get; init; }
|
||||||
|
public string? ErrorMessage { get; init; }
|
||||||
|
public Dictionary<string, string>? Metadata { get; init; }
|
||||||
|
public IEnumerable<JobDto>? ChildJobs { get; init; }
|
||||||
|
}
|
||||||
@@ -36,9 +36,8 @@ public class Program
|
|||||||
#region GraphQL
|
#region GraphQL
|
||||||
|
|
||||||
builder.Services.AddGraphQLServer()
|
builder.Services.AddGraphQLServer()
|
||||||
.AddQueryConventions()
|
|
||||||
.AddTypeExtension(typeof(JobQueries))
|
|
||||||
.ApplySaneDefaults()
|
.ApplySaneDefaults()
|
||||||
|
.AddQueryType<Query>()
|
||||||
.AddAuthorization();
|
.AddAuthorization();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "graphql",
|
||||||
"applicationUrl": "https://localhost:7310;http://localhost:5140",
|
"applicationUrl": "https://localhost:7310;http://localhost:5140",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"subgraph": "Reporting",
|
"subgraph": "Reporting",
|
||||||
"http": {
|
"http": {
|
||||||
"baseAddress": "http://localhost:5140/graphql"
|
"baseAddress": "https://localhost:7310/graphql"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace FictionArchive.Service.SchedulerService.GraphQL;
|
|||||||
|
|
||||||
public class Query
|
public class Query
|
||||||
{
|
{
|
||||||
public async Task<IEnumerable<SchedulerJob>> GetJobs(JobManagerService jobManager)
|
public async Task<IEnumerable<SchedulerJob>> GetScheduledJobs(JobManagerService jobManager)
|
||||||
{
|
{
|
||||||
return await jobManager.GetScheduledJobs();
|
return await jobManager.GetScheduledJobs();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { writable, derived } from 'svelte/store';
|
import { writable, derived, get } from 'svelte/store';
|
||||||
import type { User } from 'oidc-client-ts';
|
import type { User } from 'oidc-client-ts';
|
||||||
import { userManager, isOidcConfigured } from './oidcConfig';
|
import { userManager, isOidcConfigured } from './oidcConfig';
|
||||||
|
|
||||||
@@ -8,6 +8,15 @@ export const isLoading = writable(true);
|
|||||||
export const isAuthenticated = derived(user, ($user) => $user !== null);
|
export const isAuthenticated = derived(user, ($user) => $user !== null);
|
||||||
export const isConfigured = isOidcConfigured;
|
export const isConfigured = isOidcConfigured;
|
||||||
|
|
||||||
|
export const userGroups = derived(user, ($user) => {
|
||||||
|
const groups = $user?.profile?.groups;
|
||||||
|
return Array.isArray(groups) ? (groups as string[]) : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
export function hasGroup(groupName: string): boolean {
|
||||||
|
return get(userGroups).includes(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
// Cookie management
|
// Cookie management
|
||||||
function setCookieFromUser(u: User) {
|
function setCookieFromUser(u: User) {
|
||||||
if (!u?.access_token) return;
|
if (!u?.access_token) return;
|
||||||
|
|||||||
217
fictionarchive-web-astro/src/lib/components/JobFilters.svelte
Normal file
217
fictionarchive-web-astro/src/lib/components/JobFilters.svelte
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select } from 'bits-ui';
|
||||||
|
import Search from '@lucide/svelte/icons/search';
|
||||||
|
import X from '@lucide/svelte/icons/x';
|
||||||
|
import ChevronDown from '@lucide/svelte/icons/chevron-down';
|
||||||
|
import Check from '@lucide/svelte/icons/check';
|
||||||
|
import { Input } from '$lib/components/ui/input';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
|
import type { JobStatus } from '$lib/graphql/__generated__/graphql';
|
||||||
|
import {
|
||||||
|
type JobFilters as JobFiltersType,
|
||||||
|
JOB_STATUS_OPTIONS,
|
||||||
|
hasActiveJobFilters,
|
||||||
|
EMPTY_JOB_FILTERS,
|
||||||
|
} from '$lib/utils/jobFilterParams';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
filters: JobFiltersType;
|
||||||
|
onFilterChange: (filters: JobFiltersType) => void;
|
||||||
|
availableJobTypes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let { filters, onFilterChange, availableJobTypes }: Props = $props();
|
||||||
|
|
||||||
|
let searchInput = $state(filters.search);
|
||||||
|
let searchTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const selectedStatusLabels = $derived(
|
||||||
|
filters.statuses
|
||||||
|
.map((s) => JOB_STATUS_OPTIONS.find((o) => o.value === s)?.label ?? s)
|
||||||
|
.join(', ')
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleSearchInput(value: string) {
|
||||||
|
searchInput = value;
|
||||||
|
if (searchTimeout) clearTimeout(searchTimeout);
|
||||||
|
searchTimeout = setTimeout(() => {
|
||||||
|
onFilterChange({ ...filters, search: value });
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStatusChange(selected: string[]) {
|
||||||
|
onFilterChange({ ...filters, statuses: selected as JobStatus[] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleJobTypeChange(value: string) {
|
||||||
|
onFilterChange({ ...filters, jobType: value === '__all__' ? '' : value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFilters() {
|
||||||
|
searchInput = '';
|
||||||
|
onFilterChange({ ...EMPTY_JOB_FILTERS });
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (filters.search !== searchInput && !searchTimeout) {
|
||||||
|
searchInput = filters.search;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
|
<!-- Search Input -->
|
||||||
|
<div class="relative min-w-[200px] flex-1">
|
||||||
|
<Search class="text-muted-foreground absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search jobs..."
|
||||||
|
value={searchInput}
|
||||||
|
oninput={(e) => handleSearchInput(e.currentTarget.value)}
|
||||||
|
class="pl-9"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Status Filter -->
|
||||||
|
<Select.Root
|
||||||
|
type="multiple"
|
||||||
|
value={filters.statuses}
|
||||||
|
onValueChange={(v) => handleStatusChange(v as string[])}
|
||||||
|
>
|
||||||
|
<Select.Trigger
|
||||||
|
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-9 min-w-[140px] items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<span class="truncate text-left">
|
||||||
|
{filters.statuses.length > 0 ? selectedStatusLabels : 'Status'}
|
||||||
|
</span>
|
||||||
|
<ChevronDown class="h-4 w-4 opacity-50" />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content
|
||||||
|
class="bg-popover text-popover-foreground z-50 max-h-60 min-w-[140px] overflow-auto rounded-md border p-1 shadow-md"
|
||||||
|
>
|
||||||
|
{#each JOB_STATUS_OPTIONS as option (option.value)}
|
||||||
|
<Select.Item
|
||||||
|
value={option.value}
|
||||||
|
class="hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
||||||
|
>
|
||||||
|
{#snippet children({ selected })}
|
||||||
|
<div
|
||||||
|
class="border-primary flex h-4 w-4 items-center justify-center rounded-sm border {selected
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: ''}"
|
||||||
|
>
|
||||||
|
{#if selected}
|
||||||
|
<Check class="h-3 w-3" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<span>{option.label}</span>
|
||||||
|
{/snippet}
|
||||||
|
</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
|
||||||
|
<!-- Job Type Filter -->
|
||||||
|
{#if availableJobTypes.length > 0}
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
value={filters.jobType || '__all__'}
|
||||||
|
onValueChange={(v) => v && handleJobTypeChange(v)}
|
||||||
|
>
|
||||||
|
<Select.Trigger
|
||||||
|
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-9 min-w-[140px] items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm shadow-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
>
|
||||||
|
<span class="truncate text-left">
|
||||||
|
{filters.jobType || 'Job Type'}
|
||||||
|
</span>
|
||||||
|
<ChevronDown class="h-4 w-4 opacity-50" />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content
|
||||||
|
class="bg-popover text-popover-foreground z-50 max-h-60 min-w-[140px] overflow-auto rounded-md border p-1 shadow-md"
|
||||||
|
>
|
||||||
|
<Select.Item
|
||||||
|
value="__all__"
|
||||||
|
class="hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none"
|
||||||
|
>
|
||||||
|
{#snippet children({ selected })}
|
||||||
|
<div class="flex h-4 w-4 items-center justify-center">
|
||||||
|
{#if selected}
|
||||||
|
<Check class="h-3 w-3" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<span>All Types</span>
|
||||||
|
{/snippet}
|
||||||
|
</Select.Item>
|
||||||
|
{#each availableJobTypes as jobType (jobType)}
|
||||||
|
<Select.Item
|
||||||
|
value={jobType}
|
||||||
|
class="hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none"
|
||||||
|
>
|
||||||
|
{#snippet children({ selected })}
|
||||||
|
<div class="flex h-4 w-4 items-center justify-center">
|
||||||
|
{#if selected}
|
||||||
|
<Check class="h-3 w-3" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<span>{jobType}</span>
|
||||||
|
{/snippet}
|
||||||
|
</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Clear Filters Button -->
|
||||||
|
{#if hasActiveJobFilters(filters)}
|
||||||
|
<Button variant="outline" size="sm" onclick={clearFilters} class="gap-1">
|
||||||
|
<X class="h-4 w-4" />
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Active Filter Badges -->
|
||||||
|
{#if hasActiveJobFilters(filters)}
|
||||||
|
<div class="mt-3 flex flex-wrap gap-2">
|
||||||
|
{#if filters.search}
|
||||||
|
<Badge variant="secondary" class="gap-1">
|
||||||
|
Search: {filters.search}
|
||||||
|
<button
|
||||||
|
onclick={() => {
|
||||||
|
searchInput = '';
|
||||||
|
onFilterChange({ ...filters, search: '' });
|
||||||
|
}}
|
||||||
|
class="hover:bg-secondary-foreground/20 ml-1 rounded-full p-0.5"
|
||||||
|
>
|
||||||
|
<X class="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
</Badge>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each filters.statuses as status (status)}
|
||||||
|
<Badge variant="secondary" class="gap-1">
|
||||||
|
{JOB_STATUS_OPTIONS.find((o) => o.value === status)?.label ?? status}
|
||||||
|
<button
|
||||||
|
onclick={() =>
|
||||||
|
onFilterChange({ ...filters, statuses: filters.statuses.filter((s) => s !== status) })}
|
||||||
|
class="hover:bg-secondary-foreground/20 ml-1 rounded-full p-0.5"
|
||||||
|
>
|
||||||
|
<X class="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
</Badge>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if filters.jobType}
|
||||||
|
<Badge variant="secondary" class="gap-1">
|
||||||
|
Type: {filters.jobType}
|
||||||
|
<button
|
||||||
|
onclick={() => onFilterChange({ ...filters, jobType: '' })}
|
||||||
|
class="hover:bg-secondary-foreground/20 ml-1 rounded-full p-0.5"
|
||||||
|
>
|
||||||
|
<X class="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
</Badge>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
168
fictionarchive-web-astro/src/lib/components/JobRow.svelte
Normal file
168
fictionarchive-web-astro/src/lib/components/JobRow.svelte
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { formatDistanceToNow, format } from 'date-fns';
|
||||||
|
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||||
|
import JobStatusBadge from './JobStatusBadge.svelte';
|
||||||
|
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||||
|
import { cn } from '$lib/utils';
|
||||||
|
|
||||||
|
interface MetadataEntry {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChildJob {
|
||||||
|
id: string;
|
||||||
|
jobType: string;
|
||||||
|
displayName: string;
|
||||||
|
status: string;
|
||||||
|
errorMessage: string | null;
|
||||||
|
metadata: MetadataEntry[] | null;
|
||||||
|
createdTime: string;
|
||||||
|
lastUpdatedTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface JobNode {
|
||||||
|
id: string;
|
||||||
|
parentJobId: string | null;
|
||||||
|
jobType: string;
|
||||||
|
displayName: string;
|
||||||
|
status: string;
|
||||||
|
errorMessage: string | null;
|
||||||
|
metadata: MetadataEntry[] | null;
|
||||||
|
createdTime: string;
|
||||||
|
lastUpdatedTime: string;
|
||||||
|
childJobs: ChildJob[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
job: JobNode;
|
||||||
|
expanded: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
columnCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { job, expanded, onToggle, columnCount }: Props = $props();
|
||||||
|
|
||||||
|
const children = $derived(job.childJobs ?? []);
|
||||||
|
const hasChildren = $derived(children.length > 0);
|
||||||
|
const metadata = $derived(job.metadata ?? []);
|
||||||
|
|
||||||
|
function formatTime(iso: string): string {
|
||||||
|
try {
|
||||||
|
return formatDistanceToNow(new Date(iso), { addSuffix: true });
|
||||||
|
} catch {
|
||||||
|
return iso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTimeFull(iso: string): string {
|
||||||
|
try {
|
||||||
|
return format(new Date(iso), 'yyyy-MM-dd HH:mm:ss');
|
||||||
|
} catch {
|
||||||
|
return iso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Main row -->
|
||||||
|
<tr class={cn("border-b hover:bg-muted/50 transition-colors", expanded && "bg-muted/30")}>
|
||||||
|
<td class="w-10 px-3 py-3">
|
||||||
|
{#if hasChildren}
|
||||||
|
<button onclick={onToggle} class={cn("hover:bg-accent rounded p-1 transition-transform", expanded && "rotate-90")}>
|
||||||
|
<ChevronRight class="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
<td class="px-3 py-3 font-medium">{job.displayName}</td>
|
||||||
|
<td class="px-3 py-3 text-muted-foreground text-sm">{job.jobType}</td>
|
||||||
|
<td class="px-3 py-3"><JobStatusBadge status={job.status} /></td>
|
||||||
|
<td class="px-3 py-3 text-sm text-muted-foreground">
|
||||||
|
<Tooltip.Provider>
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger class="cursor-default">{formatTime(job.createdTime)}</Tooltip.Trigger>
|
||||||
|
<Tooltip.Content>{formatTimeFull(job.createdTime)}</Tooltip.Content>
|
||||||
|
</Tooltip.Root>
|
||||||
|
</Tooltip.Provider>
|
||||||
|
</td>
|
||||||
|
<td class="px-3 py-3 text-sm text-muted-foreground">
|
||||||
|
{#if hasChildren}
|
||||||
|
{children.length} sub-job{children.length !== 1 ? 's' : ''}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Expanded detail -->
|
||||||
|
{#if expanded}
|
||||||
|
<tr class="border-b bg-muted/20">
|
||||||
|
<td colspan={columnCount} class="px-6 py-4">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<!-- Job details -->
|
||||||
|
<div class="grid grid-cols-2 gap-x-8 gap-y-2 text-sm max-w-lg">
|
||||||
|
<span class="text-muted-foreground">Last Updated</span>
|
||||||
|
<span>{formatTimeFull(job.lastUpdatedTime)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if job.errorMessage}
|
||||||
|
<div class="text-sm">
|
||||||
|
<span class="text-muted-foreground font-medium">Error: </span>
|
||||||
|
<span class="text-destructive">{job.errorMessage}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Metadata -->
|
||||||
|
{#if metadata.length > 0}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-muted-foreground mb-2">Metadata</h4>
|
||||||
|
<div class="grid grid-cols-2 gap-x-8 gap-y-1 text-sm max-w-lg">
|
||||||
|
{#each metadata as entry (entry.key)}
|
||||||
|
<span class="text-muted-foreground">{entry.key}</span>
|
||||||
|
<span class="break-all">{entry.value}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Sub-jobs table -->
|
||||||
|
{#if hasChildren}
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-medium text-muted-foreground mb-2">Sub-jobs</h4>
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b text-left text-muted-foreground">
|
||||||
|
<th class="px-3 py-2 font-medium">Name</th>
|
||||||
|
<th class="px-3 py-2 font-medium">Type</th>
|
||||||
|
<th class="px-3 py-2 font-medium">Status</th>
|
||||||
|
<th class="px-3 py-2 font-medium">Created</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each children as child (child.id)}
|
||||||
|
<tr class="border-b last:border-0">
|
||||||
|
<td class="px-3 py-2">{child.displayName}</td>
|
||||||
|
<td class="px-3 py-2 text-muted-foreground">{child.jobType}</td>
|
||||||
|
<td class="px-3 py-2"><JobStatusBadge status={child.status} /></td>
|
||||||
|
<td class="px-3 py-2 text-muted-foreground">
|
||||||
|
<Tooltip.Provider>
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger class="cursor-default">{formatTime(child.createdTime)}</Tooltip.Trigger>
|
||||||
|
<Tooltip.Content>{formatTimeFull(child.createdTime)}</Tooltip.Content>
|
||||||
|
</Tooltip.Root>
|
||||||
|
</Tooltip.Provider>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{#if child.errorMessage}
|
||||||
|
<tr class="border-b last:border-0">
|
||||||
|
<td colspan="4" class="px-3 pb-2">
|
||||||
|
<span class="text-destructive text-xs">{child.errorMessage}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { status }: Props = $props();
|
||||||
|
|
||||||
|
const statusConfig: Record<string, { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline'; class: string }> = {
|
||||||
|
COMPLETED: { label: 'Completed', variant: 'default', class: 'bg-green-600 hover:bg-green-600/90' },
|
||||||
|
IN_PROGRESS: { label: 'In Progress', variant: 'default', class: 'bg-yellow-500 hover:bg-yellow-500/90' },
|
||||||
|
PENDING: { label: 'Pending', variant: 'secondary', class: '' },
|
||||||
|
FAILED: { label: 'Failed', variant: 'destructive', class: '' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = $derived(statusConfig[status] ?? { label: status, variant: 'outline' as const, class: '' });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Badge variant={config.variant} class={config.class}>{config.label}</Badge>
|
||||||
175
fictionarchive-web-astro/src/lib/components/JobsTab.svelte
Normal file
175
fictionarchive-web-astro/src/lib/components/JobsTab.svelte
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { client } from '$lib/graphql/client';
|
||||||
|
import { JobsDocument, type JobsQuery } from '$lib/graphql/__generated__/graphql';
|
||||||
|
import JobsTable from './JobsTable.svelte';
|
||||||
|
import JobFilters from './JobFilters.svelte';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||||
|
import {
|
||||||
|
type JobFilters as JobFiltersType,
|
||||||
|
parseJobFiltersFromURL,
|
||||||
|
syncJobFiltersToURL,
|
||||||
|
jobFiltersToGraphQLWhere,
|
||||||
|
jobSortToGraphQLOrder,
|
||||||
|
hasActiveJobFilters,
|
||||||
|
EMPTY_JOB_FILTERS,
|
||||||
|
} from '$lib/utils/jobFilterParams';
|
||||||
|
|
||||||
|
const PAGE_SIZE = 20;
|
||||||
|
|
||||||
|
type JobsConnection = NonNullable<JobsQuery['jobs']>;
|
||||||
|
type JobEdge = NonNullable<JobsConnection['edges']>[number];
|
||||||
|
type PageInfo = JobsConnection['pageInfo'];
|
||||||
|
|
||||||
|
let edges: JobEdge[] = $state([]);
|
||||||
|
let pageInfo: PageInfo | null = $state(null);
|
||||||
|
let fetching = $state(false);
|
||||||
|
let error: string | null = $state(null);
|
||||||
|
let initialLoad = $state(true);
|
||||||
|
let filters: JobFiltersType = $state({ ...EMPTY_JOB_FILTERS });
|
||||||
|
|
||||||
|
// Pagination: stack of "after" cursors used to reach each page
|
||||||
|
// cursorStack[0] = the "after" cursor used to fetch page 2, etc.
|
||||||
|
let cursorStack: (string | null)[] = $state([]);
|
||||||
|
let currentPage = $derived(cursorStack.length + 1);
|
||||||
|
|
||||||
|
const jobs = $derived((edges ?? []).map((edge) => edge.node).filter(Boolean));
|
||||||
|
|
||||||
|
// Extract unique job types from loaded data for the filter dropdown
|
||||||
|
const availableJobTypes = $derived(
|
||||||
|
[...new Set(jobs.map((job) => job.jobType))].sort()
|
||||||
|
);
|
||||||
|
|
||||||
|
async function fetchJobs(after: string | null = null) {
|
||||||
|
fetching = true;
|
||||||
|
error = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const where = jobFiltersToGraphQLWhere(filters);
|
||||||
|
const order = jobSortToGraphQLOrder(filters.sort);
|
||||||
|
const result = await client
|
||||||
|
.query(JobsDocument, { first: PAGE_SIZE, after, where, order }, { requestPolicy: 'network-only' })
|
||||||
|
.toPromise();
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
error = result.error.message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.data?.jobs) {
|
||||||
|
edges = result.data.jobs.edges ?? [];
|
||||||
|
pageInfo = result.data.jobs.pageInfo;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error = e instanceof Error ? e.message : 'Unknown error';
|
||||||
|
} finally {
|
||||||
|
fetching = false;
|
||||||
|
initialLoad = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPage() {
|
||||||
|
if (pageInfo?.endCursor && pageInfo.hasNextPage) {
|
||||||
|
cursorStack = [...cursorStack, pageInfo.endCursor];
|
||||||
|
fetchJobs(pageInfo.endCursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevPage() {
|
||||||
|
if (cursorStack.length > 0) {
|
||||||
|
const newStack = [...cursorStack];
|
||||||
|
newStack.pop(); // remove current page's cursor
|
||||||
|
cursorStack = newStack;
|
||||||
|
// Fetch with the previous page's cursor (or null for page 1)
|
||||||
|
const after = newStack.length > 0 ? newStack[newStack.length - 1] : null;
|
||||||
|
fetchJobs(after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFilterChange(newFilters: JobFiltersType) {
|
||||||
|
filters = newFilters;
|
||||||
|
edges = [];
|
||||||
|
pageInfo = null;
|
||||||
|
cursorStack = [];
|
||||||
|
syncJobFiltersToURL(filters);
|
||||||
|
fetchJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
filters = parseJobFiltersFromURL();
|
||||||
|
fetchJobs();
|
||||||
|
|
||||||
|
const handlePopState = () => {
|
||||||
|
filters = parseJobFiltersFromURL();
|
||||||
|
edges = [];
|
||||||
|
pageInfo = null;
|
||||||
|
cursorStack = [];
|
||||||
|
fetchJobs();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('popstate', handlePopState);
|
||||||
|
return () => window.removeEventListener('popstate', handlePopState);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Card class="shadow-md shadow-primary/10">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Filters</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<JobFilters {filters} onFilterChange={handleFilterChange} availableJobTypes={availableJobTypes} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{#if fetching && initialLoad}
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<div class="flex items-center justify-center py-8">
|
||||||
|
<div
|
||||||
|
class="border-primary h-10 w-10 animate-spin rounded-full border-2 border-t-transparent"
|
||||||
|
aria-label="Loading jobs"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<Card class="border-destructive/40 bg-destructive/5">
|
||||||
|
<CardContent>
|
||||||
|
<p class="text-destructive py-4 text-sm">Could not load jobs: {error}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if !initialLoad && !error && jobs.length === 0}
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<p class="text-muted-foreground py-4 text-sm">
|
||||||
|
{#if hasActiveJobFilters(filters)}
|
||||||
|
No jobs match your filters. Try adjusting your search criteria.
|
||||||
|
{:else}
|
||||||
|
No jobs found.
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if jobs.length > 0}
|
||||||
|
<JobsTable {jobs} />
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="flex items-center justify-center gap-4">
|
||||||
|
<Button variant="outline" size="sm" disabled={currentPage === 1 || fetching} onclick={prevPage}>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<span class="text-sm text-muted-foreground">Page {currentPage}</span>
|
||||||
|
<Button variant="outline" size="sm" disabled={!pageInfo?.hasNextPage || fetching} onclick={nextPage}>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
45
fictionarchive-web-astro/src/lib/components/JobsTable.svelte
Normal file
45
fictionarchive-web-astro/src/lib/components/JobsTable.svelte
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { JobsQuery } from '$lib/graphql/__generated__/graphql';
|
||||||
|
import JobRow from './JobRow.svelte';
|
||||||
|
|
||||||
|
type JobNode = NonNullable<NonNullable<NonNullable<JobsQuery['jobs']>['edges']>[number]['node']>;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
jobs: JobNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let { jobs }: Props = $props();
|
||||||
|
|
||||||
|
let expandedJobId: string | null = $state(null);
|
||||||
|
|
||||||
|
const COLUMN_COUNT = 6;
|
||||||
|
|
||||||
|
function toggleRow(jobId: string) {
|
||||||
|
expandedJobId = expandedJobId === jobId ? null : jobId;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="overflow-x-auto rounded-md border">
|
||||||
|
<table class="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b bg-muted/50">
|
||||||
|
<th class="w-10 px-3 py-3"></th>
|
||||||
|
<th class="px-3 py-3 text-sm font-medium text-muted-foreground">Name</th>
|
||||||
|
<th class="px-3 py-3 text-sm font-medium text-muted-foreground">Type</th>
|
||||||
|
<th class="px-3 py-3 text-sm font-medium text-muted-foreground">Status</th>
|
||||||
|
<th class="px-3 py-3 text-sm font-medium text-muted-foreground">Created</th>
|
||||||
|
<th class="px-3 py-3 text-sm font-medium text-muted-foreground">Sub-jobs</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each jobs as job (job.id)}
|
||||||
|
<JobRow
|
||||||
|
{job}
|
||||||
|
expanded={expandedJobId === job.id}
|
||||||
|
onToggle={() => toggleRow(job.id)}
|
||||||
|
columnCount={COLUMN_COUNT}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -29,7 +29,11 @@
|
|||||||
<NavigationMenu.Item>
|
<NavigationMenu.Item>
|
||||||
<NavigationMenu.Link href="/reading-lists" active={isActive('/reading-lists')}>Reading Lists</NavigationMenu.Link>
|
<NavigationMenu.Link href="/reading-lists" active={isActive('/reading-lists')}>Reading Lists</NavigationMenu.Link>
|
||||||
</NavigationMenu.Item>
|
</NavigationMenu.Item>
|
||||||
|
<NavigationMenu.Item>
|
||||||
|
<NavigationMenu.Link href="/status" active={isActive('/status')}>Status</NavigationMenu.Link>
|
||||||
|
</NavigationMenu.Item>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</NavigationMenu.List>
|
</NavigationMenu.List>
|
||||||
</NavigationMenu.Root>
|
</NavigationMenu.Root>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { client } from '$lib/graphql/client';
|
import { client } from '$lib/graphql/client';
|
||||||
import { NovelDocument, ImportNovelDocument, DeleteNovelDocument, GetBookmarksDocument } from '$lib/graphql/__generated__/graphql';
|
import { NovelDocument, ImportNovelDocument, DeleteNovelDocument, GetBookmarksDocument } from '$lib/graphql/__generated__/graphql';
|
||||||
import { isAuthenticated } from '$lib/auth/authStore';
|
import { isAuthenticated, hasGroup } from '$lib/auth/authStore';
|
||||||
|
import { Groups } from '$lib/constants/groups';
|
||||||
import { Card, CardContent, CardHeader } from '$lib/components/ui/card';
|
import { Card, CardContent, CardHeader } from '$lib/components/ui/card';
|
||||||
import { Badge } from '$lib/components/ui/badge';
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
@@ -483,6 +484,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
|
{#if hasGroup(Groups.Admin)}
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -492,6 +494,7 @@
|
|||||||
<Trash2 class="h-3 w-3" />
|
<Trash2 class="h-3 w-3" />
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
|
{/if}
|
||||||
<AddToReadingListButton novelId={novel.id} />
|
<AddToReadingListButton novelId={novel.id} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if refreshSuccess}
|
{#if refreshSuccess}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Tabs from '$lib/components/ui/tabs';
|
||||||
|
import JobsTab from './JobsTab.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-3xl font-bold">Status</h1>
|
||||||
|
<p class="text-muted-foreground">Monitor jobs and system activity</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tabs.Root value="jobs">
|
||||||
|
<Tabs.List class="mb-6">
|
||||||
|
<Tabs.Trigger value="jobs">Jobs</Tabs.Trigger>
|
||||||
|
</Tabs.List>
|
||||||
|
|
||||||
|
<Tabs.Content value="jobs">
|
||||||
|
<JobsTab />
|
||||||
|
</Tabs.Content>
|
||||||
|
</Tabs.Root>
|
||||||
|
</div>
|
||||||
3
fictionarchive-web-astro/src/lib/constants/groups.ts
Normal file
3
fictionarchive-web-astro/src/lib/constants/groups.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const Groups = {
|
||||||
|
Admin: 'admin'
|
||||||
|
} as const;
|
||||||
@@ -257,6 +257,45 @@ export type InvitedUserDto = {
|
|||||||
username: Scalars['String']['output'];
|
username: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type JobDto = {
|
||||||
|
childJobs: Maybe<Array<JobDto>>;
|
||||||
|
createdTime: Scalars['Instant']['output'];
|
||||||
|
displayName: Scalars['String']['output'];
|
||||||
|
errorMessage: Maybe<Scalars['String']['output']>;
|
||||||
|
id: Scalars['UUID']['output'];
|
||||||
|
jobType: Scalars['String']['output'];
|
||||||
|
lastUpdatedTime: Scalars['Instant']['output'];
|
||||||
|
metadata: Maybe<Array<KeyValuePairOfStringAndString>>;
|
||||||
|
parentJobId: Maybe<Scalars['UUID']['output']>;
|
||||||
|
status: JobStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type JobDtoFilterInput = {
|
||||||
|
and?: InputMaybe<Array<JobDtoFilterInput>>;
|
||||||
|
childJobs?: InputMaybe<ListFilterInputTypeOfJobDtoFilterInput>;
|
||||||
|
createdTime?: InputMaybe<InstantFilterInput>;
|
||||||
|
displayName?: InputMaybe<StringOperationFilterInput>;
|
||||||
|
errorMessage?: InputMaybe<StringOperationFilterInput>;
|
||||||
|
id?: InputMaybe<UuidOperationFilterInput>;
|
||||||
|
jobType?: InputMaybe<StringOperationFilterInput>;
|
||||||
|
lastUpdatedTime?: InputMaybe<InstantFilterInput>;
|
||||||
|
metadata?: InputMaybe<ListFilterInputTypeOfKeyValuePairOfStringAndStringFilterInput>;
|
||||||
|
or?: InputMaybe<Array<JobDtoFilterInput>>;
|
||||||
|
parentJobId?: InputMaybe<UuidOperationFilterInput>;
|
||||||
|
status?: InputMaybe<JobStatusOperationFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type JobDtoSortInput = {
|
||||||
|
createdTime?: InputMaybe<SortEnumType>;
|
||||||
|
displayName?: InputMaybe<SortEnumType>;
|
||||||
|
errorMessage?: InputMaybe<SortEnumType>;
|
||||||
|
id?: InputMaybe<SortEnumType>;
|
||||||
|
jobType?: InputMaybe<SortEnumType>;
|
||||||
|
lastUpdatedTime?: InputMaybe<SortEnumType>;
|
||||||
|
parentJobId?: InputMaybe<SortEnumType>;
|
||||||
|
status?: InputMaybe<SortEnumType>;
|
||||||
|
};
|
||||||
|
|
||||||
export type JobKey = {
|
export type JobKey = {
|
||||||
group: Scalars['String']['output'];
|
group: Scalars['String']['output'];
|
||||||
name: Scalars['String']['output'];
|
name: Scalars['String']['output'];
|
||||||
@@ -266,6 +305,39 @@ export type JobPersistenceError = Error & {
|
|||||||
message: Scalars['String']['output'];
|
message: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const JobStatus = {
|
||||||
|
Completed: 'COMPLETED',
|
||||||
|
Failed: 'FAILED',
|
||||||
|
InProgress: 'IN_PROGRESS',
|
||||||
|
Pending: 'PENDING'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type JobStatus = typeof JobStatus[keyof typeof JobStatus];
|
||||||
|
export type JobStatusOperationFilterInput = {
|
||||||
|
eq?: InputMaybe<JobStatus>;
|
||||||
|
in?: InputMaybe<Array<JobStatus>>;
|
||||||
|
neq?: InputMaybe<JobStatus>;
|
||||||
|
nin?: InputMaybe<Array<JobStatus>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A connection to a list of items. */
|
||||||
|
export type JobsConnection = {
|
||||||
|
/** A list of edges. */
|
||||||
|
edges: Maybe<Array<JobsEdge>>;
|
||||||
|
/** A flattened list of the nodes. */
|
||||||
|
nodes: Maybe<Array<JobDto>>;
|
||||||
|
/** Information to aid in pagination. */
|
||||||
|
pageInfo: PageInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** An edge in a connection. */
|
||||||
|
export type JobsEdge = {
|
||||||
|
/** A cursor for use in pagination. */
|
||||||
|
cursor: Scalars['String']['output'];
|
||||||
|
/** The item at the end of the edge. */
|
||||||
|
node: JobDto;
|
||||||
|
};
|
||||||
|
|
||||||
export type KeyNotFoundError = Error & {
|
export type KeyNotFoundError = Error & {
|
||||||
message: Scalars['String']['output'];
|
message: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
@@ -275,6 +347,13 @@ export type KeyValuePairOfStringAndString = {
|
|||||||
value: Scalars['String']['output'];
|
value: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type KeyValuePairOfStringAndStringFilterInput = {
|
||||||
|
and?: InputMaybe<Array<KeyValuePairOfStringAndStringFilterInput>>;
|
||||||
|
key?: InputMaybe<StringOperationFilterInput>;
|
||||||
|
or?: InputMaybe<Array<KeyValuePairOfStringAndStringFilterInput>>;
|
||||||
|
value?: InputMaybe<StringOperationFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
export const Language = {
|
export const Language = {
|
||||||
Ch: 'CH',
|
Ch: 'CH',
|
||||||
En: 'EN',
|
En: 'EN',
|
||||||
@@ -304,6 +383,20 @@ export type ListFilterInputTypeOfImageDtoFilterInput = {
|
|||||||
some?: InputMaybe<ImageDtoFilterInput>;
|
some?: InputMaybe<ImageDtoFilterInput>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ListFilterInputTypeOfJobDtoFilterInput = {
|
||||||
|
all?: InputMaybe<JobDtoFilterInput>;
|
||||||
|
any?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
none?: InputMaybe<JobDtoFilterInput>;
|
||||||
|
some?: InputMaybe<JobDtoFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ListFilterInputTypeOfKeyValuePairOfStringAndStringFilterInput = {
|
||||||
|
all?: InputMaybe<KeyValuePairOfStringAndStringFilterInput>;
|
||||||
|
any?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
none?: InputMaybe<KeyValuePairOfStringAndStringFilterInput>;
|
||||||
|
some?: InputMaybe<KeyValuePairOfStringAndStringFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
export type ListFilterInputTypeOfNovelTagDtoFilterInput = {
|
export type ListFilterInputTypeOfNovelTagDtoFilterInput = {
|
||||||
all?: InputMaybe<NovelTagDtoFilterInput>;
|
all?: InputMaybe<NovelTagDtoFilterInput>;
|
||||||
any?: InputMaybe<Scalars['Boolean']['input']>;
|
any?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
@@ -576,10 +669,12 @@ export type Query = {
|
|||||||
bookmarks: Array<BookmarkDto>;
|
bookmarks: Array<BookmarkDto>;
|
||||||
chapter: Maybe<ChapterReaderDto>;
|
chapter: Maybe<ChapterReaderDto>;
|
||||||
currentUser: Maybe<UserDto>;
|
currentUser: Maybe<UserDto>;
|
||||||
jobs: Array<SchedulerJob>;
|
jobById: Maybe<JobDto>;
|
||||||
|
jobs: Maybe<JobsConnection>;
|
||||||
novels: Maybe<NovelsConnection>;
|
novels: Maybe<NovelsConnection>;
|
||||||
readingList: Maybe<ReadingListDto>;
|
readingList: Maybe<ReadingListDto>;
|
||||||
readingLists: Array<ReadingListDto>;
|
readingLists: Array<ReadingListDto>;
|
||||||
|
scheduledJobs: Array<SchedulerJob>;
|
||||||
translationEngines: Array<TranslationEngineDescriptor>;
|
translationEngines: Array<TranslationEngineDescriptor>;
|
||||||
translationRequests: Maybe<TranslationRequestsConnection>;
|
translationRequests: Maybe<TranslationRequestsConnection>;
|
||||||
};
|
};
|
||||||
@@ -598,6 +693,21 @@ export type QueryChapterArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QueryJobByIdArgs = {
|
||||||
|
jobId: Scalars['UUID']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QueryJobsArgs = {
|
||||||
|
after?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
before?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
first?: InputMaybe<Scalars['Int']['input']>;
|
||||||
|
last?: InputMaybe<Scalars['Int']['input']>;
|
||||||
|
order?: InputMaybe<Array<JobDtoSortInput>>;
|
||||||
|
where?: InputMaybe<JobDtoFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QueryNovelsArgs = {
|
export type QueryNovelsArgs = {
|
||||||
after?: InputMaybe<Scalars['String']['input']>;
|
after?: InputMaybe<Scalars['String']['input']>;
|
||||||
before?: InputMaybe<Scalars['String']['input']>;
|
before?: InputMaybe<Scalars['String']['input']>;
|
||||||
@@ -1076,6 +1186,16 @@ export type GetChapterQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type GetChapterQuery = { chapter: { id: any, order: any, name: string, body: string, url: string | null, revision: any, createdTime: any, lastUpdatedTime: any, novelId: any, novelName: string, volumeId: any, volumeName: string, volumeOrder: number, totalChaptersInVolume: number, prevChapterVolumeOrder: number | null, prevChapterOrder: any | null, nextChapterVolumeOrder: number | null, nextChapterOrder: any | null, images: Array<{ id: any, newPath: string | null }> } | null };
|
export type GetChapterQuery = { chapter: { id: any, order: any, name: string, body: string, url: string | null, revision: any, createdTime: any, lastUpdatedTime: any, novelId: any, novelName: string, volumeId: any, volumeName: string, volumeOrder: number, totalChaptersInVolume: number, prevChapterVolumeOrder: number | null, prevChapterOrder: any | null, nextChapterVolumeOrder: number | null, nextChapterOrder: any | null, images: Array<{ id: any, newPath: string | null }> } | null };
|
||||||
|
|
||||||
|
export type JobsQueryVariables = Exact<{
|
||||||
|
first?: InputMaybe<Scalars['Int']['input']>;
|
||||||
|
after?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
where?: InputMaybe<JobDtoFilterInput>;
|
||||||
|
order?: InputMaybe<Array<JobDtoSortInput> | JobDtoSortInput>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type JobsQuery = { jobs: { edges: Array<{ cursor: string, node: { id: any, parentJobId: any | null, jobType: string, displayName: string, status: JobStatus, errorMessage: string | null, createdTime: any, lastUpdatedTime: any, metadata: Array<{ key: string, value: string }> | null, childJobs: Array<{ id: any, jobType: string, displayName: string, status: JobStatus, errorMessage: string | null, createdTime: any, lastUpdatedTime: any, metadata: Array<{ key: string, value: string }> | null }> | null } }> | null, pageInfo: { hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null, endCursor: string | null } } | null };
|
||||||
|
|
||||||
export type NovelQueryVariables = Exact<{
|
export type NovelQueryVariables = Exact<{
|
||||||
id: Scalars['UnsignedInt']['input'];
|
id: Scalars['UnsignedInt']['input'];
|
||||||
}>;
|
}>;
|
||||||
@@ -1129,6 +1249,7 @@ export const UpdateReadingListDocument = {"kind":"Document","definitions":[{"kin
|
|||||||
export const UpsertBookmarkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertBookmark"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpsertBookmarkInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsertBookmark"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarkPayload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"bookmark"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UpsertBookmarkMutation, UpsertBookmarkMutationVariables>;
|
export const UpsertBookmarkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertBookmark"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpsertBookmarkInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsertBookmark"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarkPayload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"bookmark"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UpsertBookmarkMutation, UpsertBookmarkMutationVariables>;
|
||||||
export const GetBookmarksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetBookmarks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}}]} as unknown as DocumentNode<GetBookmarksQuery, GetBookmarksQueryVariables>;
|
export const GetBookmarksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetBookmarks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}}]} as unknown as DocumentNode<GetBookmarksQuery, GetBookmarksQueryVariables>;
|
||||||
export const GetChapterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChapter"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"volumeOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}}},{"kind":"Argument","name":{"kind":"Name","value":"chapterOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"revision"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"novelName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeId"}},{"kind":"Field","name":{"kind":"Name","value":"volumeName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"totalChaptersInVolume"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterOrder"}}]}}]}}]} as unknown as DocumentNode<GetChapterQuery, GetChapterQueryVariables>;
|
export const GetChapterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChapter"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"volumeOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}}},{"kind":"Argument","name":{"kind":"Name","value":"chapterOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"revision"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"novelName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeId"}},{"kind":"Field","name":{"kind":"Name","value":"volumeName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"totalChaptersInVolume"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterOrder"}}]}}]}}]} as unknown as DocumentNode<GetChapterQuery, GetChapterQueryVariables>;
|
||||||
|
export const JobsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Jobs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JobDtoFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"order"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JobDtoSortInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jobs"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}},{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"Variable","name":{"kind":"Name","value":"order"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parentJobId"}},{"kind":"Field","name":{"kind":"Name","value":"jobType"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"metadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"childJobs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"jobType"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"errorMessage"}},{"kind":"Field","name":{"kind":"Name","value":"metadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode<JobsQuery, JobsQueryVariables>;
|
||||||
export const NovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rawLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"statusOverride"}},{"kind":"Field","name":{"kind":"Name","value":"externalId"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"externalUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"source"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<NovelQuery, NovelQueryVariables>;
|
export const NovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rawLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"statusOverride"}},{"kind":"Field","name":{"kind":"Name","value":"externalId"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"externalUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"source"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<NovelQuery, NovelQueryVariables>;
|
||||||
export const NovelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novels"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"order"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoSortInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}},{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"Variable","name":{"kind":"Name","value":"order"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode<NovelsQuery, NovelsQueryVariables>;
|
export const NovelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novels"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"order"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoSortInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}},{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"Variable","name":{"kind":"Name","value":"order"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode<NovelsQuery, NovelsQueryVariables>;
|
||||||
export const GetReadingListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReadingList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"readingList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"itemCount"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"addedTime"}}]}}]}}]}}]} as unknown as DocumentNode<GetReadingListQuery, GetReadingListQueryVariables>;
|
export const GetReadingListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReadingList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"readingList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"itemCount"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"addedTime"}}]}}]}}]}}]} as unknown as DocumentNode<GetReadingListQuery, GetReadingListQueryVariables>;
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
query Jobs($first: Int, $after: String, $where: JobDtoFilterInput, $order: [JobDtoSortInput!]) {
|
||||||
|
jobs(first: $first, after: $after, where: $where, order: $order) {
|
||||||
|
edges {
|
||||||
|
cursor
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
parentJobId
|
||||||
|
jobType
|
||||||
|
displayName
|
||||||
|
status
|
||||||
|
errorMessage
|
||||||
|
metadata {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
createdTime
|
||||||
|
lastUpdatedTime
|
||||||
|
childJobs {
|
||||||
|
id
|
||||||
|
jobType
|
||||||
|
displayName
|
||||||
|
status
|
||||||
|
errorMessage
|
||||||
|
metadata {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
createdTime
|
||||||
|
lastUpdatedTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
fictionarchive-web-astro/src/lib/utils/jobFilterParams.ts
Normal file
131
fictionarchive-web-astro/src/lib/utils/jobFilterParams.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import type { JobDtoFilterInput, JobDtoSortInput, JobStatus, SortEnumType } from '$lib/graphql/__generated__/graphql';
|
||||||
|
|
||||||
|
export type JobSortField = 'createdTime' | 'lastUpdatedTime' | 'status';
|
||||||
|
export type JobSortDirection = SortEnumType;
|
||||||
|
|
||||||
|
export interface JobSort {
|
||||||
|
field: JobSortField;
|
||||||
|
direction: JobSortDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JobFilters {
|
||||||
|
search: string;
|
||||||
|
statuses: JobStatus[];
|
||||||
|
jobType: string;
|
||||||
|
sort: JobSort;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JOB_STATUS_OPTIONS: { value: JobStatus; label: string }[] = [
|
||||||
|
{ value: 'PENDING', label: 'Pending' },
|
||||||
|
{ value: 'IN_PROGRESS', label: 'In Progress' },
|
||||||
|
{ value: 'COMPLETED', label: 'Completed' },
|
||||||
|
{ value: 'FAILED', label: 'Failed' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DEFAULT_JOB_SORT: JobSort = {
|
||||||
|
field: 'createdTime',
|
||||||
|
direction: 'DESC',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EMPTY_JOB_FILTERS: JobFilters = {
|
||||||
|
search: '',
|
||||||
|
statuses: [],
|
||||||
|
jobType: '',
|
||||||
|
sort: DEFAULT_JOB_SORT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VALID_STATUSES: string[] = JOB_STATUS_OPTIONS.map((o) => o.value);
|
||||||
|
const VALID_SORT_FIELDS: JobSortField[] = ['createdTime', 'lastUpdatedTime', 'status'];
|
||||||
|
const VALID_SORT_DIRECTIONS: JobSortDirection[] = ['ASC', 'DESC'];
|
||||||
|
|
||||||
|
export function parseJobFiltersFromURL(searchParams?: URLSearchParams): JobFilters {
|
||||||
|
const params = searchParams ?? new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
const search = params.get('search') ?? '';
|
||||||
|
const jobType = params.get('jobType') ?? '';
|
||||||
|
|
||||||
|
const statusParam = params.get('status') ?? '';
|
||||||
|
const statuses = statusParam
|
||||||
|
.split(',')
|
||||||
|
.filter((s) => s && VALID_STATUSES.includes(s)) as JobStatus[];
|
||||||
|
|
||||||
|
const sortField = params.get('sortBy') as JobSortField | null;
|
||||||
|
const sortDir = params.get('sortDir') as JobSortDirection | null;
|
||||||
|
const sort: JobSort = {
|
||||||
|
field: sortField && VALID_SORT_FIELDS.includes(sortField) ? sortField : DEFAULT_JOB_SORT.field,
|
||||||
|
direction: sortDir && VALID_SORT_DIRECTIONS.includes(sortDir) ? sortDir : DEFAULT_JOB_SORT.direction,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { search, statuses, jobType, sort };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jobFiltersToURLParams(filters: JobFilters): string {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
if (filters.search.trim()) {
|
||||||
|
params.set('search', filters.search.trim());
|
||||||
|
}
|
||||||
|
if (filters.statuses.length > 0) {
|
||||||
|
params.set('status', filters.statuses.join(','));
|
||||||
|
}
|
||||||
|
if (filters.jobType.trim()) {
|
||||||
|
params.set('jobType', filters.jobType.trim());
|
||||||
|
}
|
||||||
|
if (filters.sort.field !== DEFAULT_JOB_SORT.field || filters.sort.direction !== DEFAULT_JOB_SORT.direction) {
|
||||||
|
params.set('sortBy', filters.sort.field);
|
||||||
|
params.set('sortDir', filters.sort.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function syncJobFiltersToURL(filters: JobFilters): void {
|
||||||
|
const params = jobFiltersToURLParams(filters);
|
||||||
|
const newUrl = params ? `${window.location.pathname}?${params}` : window.location.pathname;
|
||||||
|
window.history.replaceState({}, '', newUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jobFiltersToGraphQLWhere(filters: JobFilters): JobDtoFilterInput | null {
|
||||||
|
const conditions: JobDtoFilterInput[] = [];
|
||||||
|
|
||||||
|
if (filters.search.trim()) {
|
||||||
|
conditions.push({
|
||||||
|
displayName: { contains: filters.search.trim() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.statuses.length > 0) {
|
||||||
|
conditions.push({
|
||||||
|
status: { in: filters.statuses },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.jobType.trim()) {
|
||||||
|
conditions.push({
|
||||||
|
jobType: { eq: filters.jobType.trim() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always filter to top-level jobs only (no parent)
|
||||||
|
conditions.push({
|
||||||
|
parentJobId: { eq: null },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (conditions.length === 1) {
|
||||||
|
return conditions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { and: conditions };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jobSortToGraphQLOrder(sort: JobSort): JobDtoSortInput[] {
|
||||||
|
return [{ [sort.field]: sort.direction }];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasActiveJobFilters(filters: JobFilters): boolean {
|
||||||
|
return (
|
||||||
|
filters.search.trim().length > 0 ||
|
||||||
|
filters.statuses.length > 0 ||
|
||||||
|
filters.jobType.trim().length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
8
fictionarchive-web-astro/src/pages/status/index.astro
Normal file
8
fictionarchive-web-astro/src/pages/status/index.astro
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
import AppLayout from '../../layouts/AppLayout.astro';
|
||||||
|
import StatusPage from '../../lib/components/StatusPage.svelte';
|
||||||
|
---
|
||||||
|
|
||||||
|
<AppLayout title="Status - FictionArchive">
|
||||||
|
<StatusPage client:load />
|
||||||
|
</AppLayout>
|
||||||
Reference in New Issue
Block a user