fix(webui): show import refresh timestamp
This commit is contained in:
parent
7853d0cfb8
commit
3fdc815794
3 changed files with 43 additions and 8 deletions
|
|
@ -63,6 +63,11 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.importPageRefreshBtnRefreshing {
|
||||||
|
opacity: 0.72;
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
|
|
||||||
.importPageStagingBar {
|
.importPageStagingBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -88,6 +93,11 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.importStagingRefreshAt {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
/* Tab Bar */
|
/* Tab Bar */
|
||||||
.importPageTabBar {
|
.importPageTabBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { Link, Outlet } from '@tanstack/react-router';
|
import { Link, Outlet } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
import { Show } from '@/components/primitives';
|
||||||
import { useReactPageShell } from '@/platform/shell/route-controllers';
|
import { useReactPageShell } from '@/platform/shell/route-controllers';
|
||||||
|
|
||||||
import type { ImportQueueEntry } from '../-import.types';
|
import type { ImportQueueEntry } from '../-import.types';
|
||||||
import styles from './import-page.module.css';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getQueueProgressPercent,
|
getQueueProgressPercent,
|
||||||
|
|
@ -11,12 +11,16 @@ import {
|
||||||
getStagingStatsText,
|
getStagingStatsText,
|
||||||
} from '../-import.helpers';
|
} from '../-import.helpers';
|
||||||
import { useImportQueueWorkflow } from '../-import.store';
|
import { useImportQueueWorkflow } from '../-import.store';
|
||||||
|
import styles from './import-page.module.css';
|
||||||
import { fallbackImage, RefreshIcon, useImportStaging } from './import-shared';
|
import { fallbackImage, RefreshIcon, useImportStaging } from './import-shared';
|
||||||
|
|
||||||
export function ImportPage() {
|
export function ImportPage() {
|
||||||
useReactPageShell('import');
|
useReactPageShell('import');
|
||||||
|
|
||||||
const { refreshStaging, stagingFiles, stagingPath, stagingQuery } = useImportStaging();
|
const { refreshStaging, stagingFiles, stagingPath, stagingQuery } = useImportStaging();
|
||||||
|
const isRefreshing = stagingQuery.isRefetching;
|
||||||
|
const lastRefreshedAt =
|
||||||
|
stagingQuery.dataUpdatedAt > 0 ? formatShortTime(stagingQuery.dataUpdatedAt) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="import-page" data-testid="import-page">
|
<div id="import-page" data-testid="import-page">
|
||||||
|
|
@ -26,6 +30,8 @@ export function ImportPage() {
|
||||||
fileCountText={getStagingStatsText(stagingFiles)}
|
fileCountText={getStagingStatsText(stagingFiles)}
|
||||||
loading={stagingQuery.isLoading}
|
loading={stagingQuery.isLoading}
|
||||||
stagingPath={stagingPath}
|
stagingPath={stagingPath}
|
||||||
|
refreshing={isRefreshing}
|
||||||
|
lastRefreshedAt={lastRefreshedAt}
|
||||||
onRefresh={refreshStaging}
|
onRefresh={refreshStaging}
|
||||||
/>
|
/>
|
||||||
<ImportProcessingQueue />
|
<ImportProcessingQueue />
|
||||||
|
|
@ -38,17 +44,29 @@ export function ImportPage() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatShortTime(timestamp: number) {
|
||||||
|
return new Date(timestamp).toLocaleTimeString([], {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function ImportHeader({
|
function ImportHeader({
|
||||||
error,
|
error,
|
||||||
fileCountText,
|
fileCountText,
|
||||||
loading,
|
loading,
|
||||||
stagingPath,
|
stagingPath,
|
||||||
|
refreshing,
|
||||||
|
lastRefreshedAt,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
}: {
|
}: {
|
||||||
error: unknown;
|
error: unknown;
|
||||||
fileCountText: string;
|
fileCountText: string;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
stagingPath: string;
|
stagingPath: string;
|
||||||
|
refreshing: boolean;
|
||||||
|
lastRefreshedAt: string | null;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -60,18 +78,27 @@ function ImportHeader({
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={styles.importPageRefreshBtn}
|
className={`${styles.importPageRefreshBtn} ${
|
||||||
|
refreshing ? styles.importPageRefreshBtnRefreshing : ''
|
||||||
|
}`}
|
||||||
title="Re-scan import folder"
|
title="Re-scan import folder"
|
||||||
|
aria-busy={refreshing}
|
||||||
|
disabled={refreshing}
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
>
|
>
|
||||||
<RefreshIcon />
|
<RefreshIcon />
|
||||||
Refresh
|
{refreshing ? 'Refreshing...' : 'Refresh'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.importPageStagingBar} id="import-staging-bar">
|
<div className={styles.importPageStagingBar} id="import-staging-bar">
|
||||||
<span className={styles.importStagingPath} id="import-page-staging-path">
|
<span className={styles.importStagingPath} id="import-page-staging-path">
|
||||||
{error ? 'Import folder: error' : `Import: ${stagingPath}`}
|
{error ? 'Import folder: error' : `Import: ${stagingPath}`}
|
||||||
</span>
|
</span>
|
||||||
|
<Show when={lastRefreshedAt != null}>
|
||||||
|
<span className={styles.importStagingRefreshAt}>
|
||||||
|
{lastRefreshedAt ? `Last refreshed: ${lastRefreshedAt}` : null}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
<span className={styles.importStagingStats} id="import-page-staging-stats">
|
<span className={styles.importStagingStats} id="import-page-staging-stats">
|
||||||
{loading ? 'loading...' : fileCountText}
|
{loading ? 'loading...' : fileCountText}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -129,9 +156,7 @@ function ImportQueueItem({ entry }: { entry: ImportQueueEntry }) {
|
||||||
onError={fallbackImage}
|
onError={fallbackImage}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={`${styles.importPageQueueArt} ${styles.importPageQueueArtEmpty}`}>
|
<div className={`${styles.importPageQueueArt} ${styles.importPageQueueArtEmpty}`}>A</div>
|
||||||
A
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div className={styles.importPageQueueInfo}>
|
<div className={styles.importPageQueueInfo}>
|
||||||
<div className={styles.importPageQueueName}>{entry.label}</div>
|
<div className={styles.importPageQueueName}>{entry.label}</div>
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ export function useImportStaging() {
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refreshStaging: () => {
|
refreshStaging: async () => {
|
||||||
clearFinishedJobs();
|
clearFinishedJobs();
|
||||||
void invalidateImportStagingQueries(queryClient);
|
await invalidateImportStagingQueries(queryClient);
|
||||||
},
|
},
|
||||||
stagingFiles: stagingQuery.data?.files ?? [],
|
stagingFiles: stagingQuery.data?.files ?? [],
|
||||||
stagingPath: stagingQuery.data?.staging_path || 'Not configured',
|
stagingPath: stagingQuery.data?.staging_path || 'Not configured',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue