visual continunity
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/** Trash endpoints — ported from trashModel.js + views/trash. */
|
||||
import { apiFetch } from '$lib/api/client';
|
||||
import { getCsrfHeaders } from '$lib/api/csrf';
|
||||
import { t } from '$lib/i18n/index.svelte';
|
||||
import { fetchResourcePage, type ResourcePage, type ResourcePageOpts } from './resources';
|
||||
import type { TrashResourceItem } from '$lib/api/types';
|
||||
|
||||
@@ -8,6 +9,81 @@ export function fetchTrashPage(opts?: ResourcePageOpts): Promise<ResourcePage<Tr
|
||||
return fetchResourcePage<TrashResourceItem>('/api/trash/resources', 'deletion_date', opts);
|
||||
}
|
||||
|
||||
/** Days from now until `value` (negative when already past). */
|
||||
function daysUntil(value: number | string | Date | null | undefined): number | null {
|
||||
if (value === null || value === undefined) return null;
|
||||
let date: Date;
|
||||
if (value instanceof Date) date = value;
|
||||
else if (typeof value === 'number') date = new Date(value < 1e12 ? value * 1000 : value);
|
||||
else date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return null;
|
||||
return Math.floor((date.getTime() - Date.now()) / 86_400_000);
|
||||
}
|
||||
|
||||
export type ExpiryTier = 'never' | 'normal' | 'caution' | 'soon' | 'urgent' | 'expired';
|
||||
|
||||
export interface ExpiryChip {
|
||||
tier: ExpiryTier;
|
||||
icon: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tiered "remaining lifetime" chip for a trash deletion date — ported from
|
||||
* `formatExpiryChip` in static/js/core/formatters.js. `null` means "Never".
|
||||
*/
|
||||
export function expiryChip(value: number | string | null | undefined): ExpiryChip {
|
||||
if (value === null || value === undefined) {
|
||||
return { tier: 'never', icon: 'infinity', label: t('expiryChip.never', 'Never expires') };
|
||||
}
|
||||
const days = daysUntil(value);
|
||||
if (days === null) {
|
||||
return { tier: 'normal', icon: 'calendar', label: String(value) };
|
||||
}
|
||||
if (days < 0)
|
||||
return {
|
||||
tier: 'expired',
|
||||
icon: 'exclamation-triangle',
|
||||
label: t('expiryChip.expired', 'Expired')
|
||||
};
|
||||
if (days === 0)
|
||||
return { tier: 'urgent', icon: 'clock', label: t('expiryChip.today', 'Expires today') };
|
||||
if (days === 1)
|
||||
return { tier: 'urgent', icon: 'clock', label: t('expiryChip.tomorrow', 'Expires tomorrow') };
|
||||
if (days <= 7)
|
||||
return {
|
||||
tier: 'soon',
|
||||
icon: 'calendar',
|
||||
label: t('expiryChip.inDays', { count: days }, 'Expires in {{count}} days')
|
||||
};
|
||||
if (days <= 30)
|
||||
return {
|
||||
tier: 'caution',
|
||||
icon: 'calendar',
|
||||
label: t('expiryChip.inDays', { count: days }, 'Expires in {{count}} days')
|
||||
};
|
||||
return {
|
||||
tier: 'normal',
|
||||
icon: 'calendar',
|
||||
label: t('expiryChip.onDate', { count: days }, 'Expires in {{count}} days')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Coarse "remaining days" bucket label for the trash group-by swimlanes —
|
||||
* ported from `normalizeExpiryBucket`.
|
||||
*/
|
||||
export function remainingDaysBucket(value: number | string | null | undefined): string {
|
||||
const days = daysUntil(value);
|
||||
if (days === null) return t('expiryBucket.noExpiry', 'No expiration');
|
||||
if (days < 0) return t('expiryBucket.expired', 'Expired');
|
||||
if (days === 0) return t('expiryBucket.today', 'Today');
|
||||
if (days === 1) return t('expiryBucket.tomorrow', 'Tomorrow');
|
||||
if (days <= 7) return t('expiryBucket.week', 'In less than 7 days');
|
||||
if (days <= 30) return t('expiryBucket.month', 'In less than 30 days');
|
||||
return t('expiryBucket.later', 'Later');
|
||||
}
|
||||
|
||||
export async function restoreTrashItem(trashId: string): Promise<void> {
|
||||
const res = await apiFetch(`/api/trash/${trashId}/restore`, {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user