149 lines
4.4 KiB
Svelte
149 lines
4.4 KiB
Svelte
<script lang="ts">
|
|
import { onMount, onDestroy } from 'svelte';
|
|
import { page } from '$app/stores';
|
|
import { goto } from '$app/navigation';
|
|
import { get } from 'svelte/store';
|
|
import { addNotification } from '$lib/core/stores/noti.js';
|
|
import { departmentStore } from '$lib/core/stores/departments.js';
|
|
import { referenceFromPage } from '$lib/core/stores/recipeStore.js';
|
|
import {
|
|
sheetCatalogs,
|
|
sheetCatalogsLoading,
|
|
type Catalog
|
|
} from '$lib/core/stores/sheetStore.js';
|
|
import { waitForOpenSocket } from '$lib/core/stores/websocketStore.js';
|
|
import { requestCatalogs } from '$lib/core/services/sheetService.js';
|
|
|
|
import Button from '$lib/components/ui/button/button.svelte';
|
|
import * as Table from '$lib/components/ui/table/index.js';
|
|
import Badge from '$lib/components/ui/badge/badge.svelte';
|
|
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
|
|
import { RefreshCw } from '@lucide/svelte/icons';
|
|
|
|
// Get country from route params or department store
|
|
let selectedCountry = $state<string>($page.params.country || get(departmentStore) || '');
|
|
let catalogs = $derived($sheetCatalogs);
|
|
let loading = $derived($sheetCatalogsLoading);
|
|
let error = $state<string | null>(null);
|
|
|
|
onMount(() => {
|
|
referenceFromPage.set('sheet');
|
|
|
|
if (selectedCountry) {
|
|
void loadCatalogs();
|
|
}
|
|
});
|
|
|
|
async function loadCatalogs() {
|
|
if (!selectedCountry) return;
|
|
|
|
error = null;
|
|
sheetCatalogsLoading.set(true);
|
|
sheetCatalogs.set([]);
|
|
|
|
const socket = await waitForOpenSocket();
|
|
if (!socket) {
|
|
error = 'WebSocket is still connecting. Please try again.';
|
|
sheetCatalogsLoading.set(false);
|
|
addNotification('ERR:WebSocket not connected');
|
|
return;
|
|
}
|
|
|
|
const sent = requestCatalogs(selectedCountry);
|
|
if (!sent) {
|
|
error = 'WebSocket not connected. Please try again.';
|
|
sheetCatalogsLoading.set(false);
|
|
addNotification('ERR:WebSocket not connected');
|
|
}
|
|
}
|
|
|
|
function handleEditCatalog(catalog: Catalog) {
|
|
if (catalog.status === 'locked') {
|
|
addNotification(`WARN:Catalog is locked by ${catalog.locked_by}`);
|
|
return;
|
|
}
|
|
goto(`/sheet/edit/${selectedCountry}/${catalog.catalog}`);
|
|
}
|
|
</script>
|
|
|
|
<div class="mx-8 flex">
|
|
<div class="w-full">
|
|
<!-- Header -->
|
|
<div class="mb-4 flex items-center justify-between">
|
|
<div>
|
|
<h1 class="m-8 text-4xl font-bold">Sheet Overview</h1>
|
|
<p class="mx-8 my-0 text-muted-foreground">
|
|
Catalogs for {selectedCountry.toUpperCase()}
|
|
</p>
|
|
</div>
|
|
<div class="mr-8">
|
|
<Button variant="outline" onclick={loadCatalogs} disabled={loading}>
|
|
<RefreshCw class="mr-2 h-4 w-4" />
|
|
Refresh
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content Area -->
|
|
<div class="mx-8">
|
|
{#if loading}
|
|
<div class="flex h-64 items-center justify-center">
|
|
<Spinner class="h-12 w-12" />
|
|
<p class="ml-4 text-muted-foreground">
|
|
Loading catalogs for {selectedCountry}...
|
|
</p>
|
|
</div>
|
|
{:else if error}
|
|
<div class="rounded-md border border-red-200 bg-red-50 p-4">
|
|
<p class="text-sm text-red-600">{error}</p>
|
|
<Button variant="outline" size="sm" class="mt-2" onclick={loadCatalogs}>
|
|
<RefreshCw class="mr-2 h-4 w-4" />
|
|
Retry
|
|
</Button>
|
|
</div>
|
|
{:else if catalogs.length === 0}
|
|
<div class="flex h-64 items-center justify-center text-muted-foreground">
|
|
<p>No catalogs found for {selectedCountry}</p>
|
|
</div>
|
|
{:else}
|
|
<Table.Root>
|
|
<Table.Header>
|
|
<Table.Row>
|
|
<Table.Head>Catalog Name</Table.Head>
|
|
<Table.Head>Status</Table.Head>
|
|
<Table.Head>Locked By</Table.Head>
|
|
<Table.Head>Actions</Table.Head>
|
|
</Table.Row>
|
|
</Table.Header>
|
|
<Table.Body>
|
|
{#each catalogs as catalog}
|
|
<Table.Row>
|
|
<Table.Cell class="font-medium">{catalog.catalog}</Table.Cell>
|
|
<Table.Cell>
|
|
<Badge
|
|
variant={catalog.status === 'free' ? 'default' : 'secondary'}
|
|
>
|
|
{catalog.status.toUpperCase()}
|
|
</Badge>
|
|
</Table.Cell>
|
|
<Table.Cell class="text-muted-foreground">
|
|
{catalog.locked_by || '-'}
|
|
</Table.Cell>
|
|
<Table.Cell>
|
|
<Button
|
|
size="sm"
|
|
variant={catalog.status === 'free' ? 'default' : 'outline'}
|
|
onclick={() => handleEditCatalog(catalog)}
|
|
disabled={catalog.status === 'locked'}
|
|
>
|
|
{catalog.status === 'free' ? 'Edit' : 'Locked'}
|
|
</Button>
|
|
</Table.Cell>
|
|
</Table.Row>
|
|
{/each}
|
|
</Table.Body>
|
|
</Table.Root>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|