diff --git a/.changeset/overview-preview-connections.md b/.changeset/overview-preview-connections.md new file mode 100644 index 00000000..844ebc3a --- /dev/null +++ b/.changeset/overview-preview-connections.md @@ -0,0 +1,5 @@ +--- +"@luzzle/web": patch +--- + +Connect Published and Preview overviews and offer fresh saved-source previews when a preview fails or expires. diff --git a/apps/web/src/lib/pieces/viewer/OverviewActions.svelte b/apps/web/src/lib/pieces/viewer/OverviewActions.svelte new file mode 100644 index 00000000..63c401e8 --- /dev/null +++ b/apps/web/src/lib/pieces/viewer/OverviewActions.svelte @@ -0,0 +1,76 @@ + + +{#if preview} +
+ +
+{/if} +{#if hasPublishedVersion} + View published +{/if} + + diff --git a/apps/web/src/lib/pieces/viewer/PieceOverview.svelte b/apps/web/src/lib/pieces/viewer/PieceOverview.svelte index f8a16095..054daeec 100644 --- a/apps/web/src/lib/pieces/viewer/PieceOverview.svelte +++ b/apps/web/src/lib/pieces/viewer/PieceOverview.svelte @@ -67,7 +67,7 @@ padding: var(--space-2) var(--space-3); border-top: 1px solid var(--color-outline-variant); text-align: left; - vertical-align: top; + vertical-align: middle; overflow-wrap: anywhere; } .overview :global(.overview-facts th) { @@ -112,7 +112,7 @@ grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); border-top: 1px solid var(--color-outline-variant); padding: 0; - align-items: start; + align-items: center; } .overview :global(.overview-facts th), .overview :global(.overview-facts td) { diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/+layout.server.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/+layout.server.ts index 5ce43cc5..64e1471d 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/+layout.server.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/+layout.server.ts @@ -1,6 +1,9 @@ import type { LayoutServerLoad } from './$types' +import { db } from '#lib/server/database/index.js' +import { getWebPiece } from '#lib/server/pieces.js' -export const load: LayoutServerLoad = ({ locals }) => { +export const load: LayoutServerLoad = async ({ locals, params }) => { locals.inlineConfiguredStyles() - return {} + const published = await getWebPiece(db, params.path) + return { hasPublishedVersion: !!published } } diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte index 7e00d98f..791dfeff 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+layout.svelte @@ -146,6 +146,7 @@ undefined}> import type { ProgressPhase } from '#lib/components/progress.js' import { formatDuration, previewDuration } from './duration.js' + import OverviewActions from '#lib/pieces/viewer/OverviewActions.svelte' interface Props { file: string type?: string status: string + hasPublishedVersion?: boolean streamDisconnected?: boolean createdAt: string | null finishedAt: string | null @@ -16,6 +18,7 @@ file, type, status, + hasPublishedVersion = false, streamDisconnected = false, createdAt, finishedAt, @@ -84,9 +87,7 @@ {#if !streamDisconnected && (status === 'enqueued' || status === 'waiting')}
Waiting for worker…
{:else if !streamDisconnected && status === 'expired'} -
- Assets are purged after 2 days. Re-run the preview from the editor. -
+
Assets are purged after 2 days.
{/if} @@ -131,6 +132,18 @@ {/if} + {#if hasPublishedVersion} + + Publication + + + {/if} + {#if status === 'failed' || status === 'expired'} + + Preview + + + {/if} diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/PreviewFacts.test.ts b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/PreviewFacts.test.ts index 2418828e..653af745 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/PreviewFacts.test.ts +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/PreviewFacts.test.ts @@ -1,10 +1,15 @@ -import { readFileSync } from 'node:fs' -import { expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' import { render } from 'svelte/server' import type { ComponentProps } from 'svelte' import type { ProgressPhase } from '#lib/components/progress.js' import PreviewFacts from './PreviewFacts.svelte' +vi.mock('$app/forms', () => ({ enhance: vi.fn() })) +vi.mock('$app/paths', () => ({ + resolve: (route: string, params: { path: string }) => + route.replace('/(auth)', '').replace('[...path]', () => params.path) +})) + function facts(overrides: Partial> = {}) { return render(PreviewFacts, { props: { @@ -45,52 +50,6 @@ function field(html: string, name: string) { return rows(html).find(([label]) => label === name)?.[1] } -test('fixed UI copy is literal markup rather than JavaScript label maps', () => { - const source = readFileSync(new URL('./PreviewFacts.svelte', import.meta.url), 'utf8') - const markup = source - .replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1>/g, '') - .replace(/\{[^}]*\}/g, '') - .replace(/\s+/g, ' ') - - expect(source).not.toMatch(/\b(?:statusLabels|durationLabels)\b|\bnew\s+Map\s*\(/) - for (const label of [ - 'Preview', - 'File', - 'Type', - 'State', - 'Duration', - 'Created', - 'Finished', - 'Unknown', - 'Queued', - 'Building', - 'Completed', - 'Failed', - 'Expired', - 'Refresh to reconnect', - 'Waiting for worker…', - 'Assets are purged after 2 days. Re-run the preview from the editor.', - 'elapsed', - 'total time', - 'stopped at', - 'UTC' - ]) - expect(markup).toContain(label) -}) - -test('facts have no state icon imports, markup, classification, motion, or local badge styles', () => { - const source = readFileSync(new URL('./PreviewFacts.svelte', import.meta.url), 'utf8') - const style = source.match(/