- Move api.server.ts, errors.ts, types.ts, utils.ts, relativeTime.ts to lib/shared/ - Move person relationship components to lib/person/relationship/ - Move Stammbaum components to lib/person/genealogy/ - Move HelpPopover to lib/shared/primitives/ - Update all import paths across routes, specs, and lib files - Update vi.mock() paths in server-project test files - Remove now-empty legacy directories (components/, hooks/, server/, etc.) - Update vite.config.ts coverage include paths for new structure - Update frontend/CLAUDE.md to reflect domain-based lib/ layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import { createApiClient } from '$lib/shared/api.server';
|
|
import type { components, operations } from '$lib/generated/api';
|
|
|
|
type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO'];
|
|
type NotificationDTO = components['schemas']['NotificationDTO'];
|
|
type AuditKind = NonNullable<operations['getActivity']['parameters']['query']>['kinds'] extends
|
|
| (infer K)[]
|
|
| undefined
|
|
? K
|
|
: never;
|
|
|
|
export type FilterValue = 'alle' | 'fuer-dich' | 'hochgeladen' | 'transkription' | 'kommentare';
|
|
|
|
const VALID_FILTERS: FilterValue[] = [
|
|
'alle',
|
|
'fuer-dich',
|
|
'hochgeladen',
|
|
'transkription',
|
|
'kommentare'
|
|
];
|
|
|
|
// fuer-dich stays client-side: youMentioned || youParticipated cannot be expressed as a kinds filter
|
|
const KINDS_FOR_FILTER: Partial<Record<FilterValue, AuditKind[]>> = {
|
|
hochgeladen: ['FILE_UPLOADED'],
|
|
transkription: ['TEXT_SAVED', 'BLOCK_REVIEWED', 'ANNOTATION_CREATED'],
|
|
kommentare: ['COMMENT_ADDED', 'MENTION_CREATED']
|
|
};
|
|
|
|
function parseFilter(raw: string | null): FilterValue {
|
|
if (raw && (VALID_FILTERS as string[]).includes(raw)) return raw as FilterValue;
|
|
return 'alle';
|
|
}
|
|
|
|
export async function load({ fetch, url }) {
|
|
const api = createApiClient(fetch);
|
|
const filter = parseFilter(url.searchParams.get('filter'));
|
|
const limit = Math.min(Number(url.searchParams.get('limit')) || 40, 40);
|
|
const kinds = KINDS_FOR_FILTER[filter];
|
|
|
|
const [activityResult, unreadResult] = await Promise.allSettled([
|
|
api.GET('/api/dashboard/activity', { params: { query: { limit, ...(kinds && { kinds }) } } }),
|
|
api.GET('/api/notifications', {
|
|
params: { query: { read: false, page: 0, size: 20 } }
|
|
})
|
|
]);
|
|
|
|
let activityFeed: ActivityFeedItemDTO[] = [];
|
|
let unreadNotifications: NotificationDTO[] = [];
|
|
let loadError: string | null = null;
|
|
|
|
if (activityResult.status === 'fulfilled' && activityResult.value.response.ok) {
|
|
activityFeed = (activityResult.value.data as ActivityFeedItemDTO[]) ?? [];
|
|
} else if (activityResult.status === 'fulfilled') {
|
|
loadError = 'activity';
|
|
}
|
|
|
|
if (unreadResult.status === 'fulfilled' && unreadResult.value.response.ok) {
|
|
unreadNotifications = unreadResult.value.data?.content ?? [];
|
|
}
|
|
|
|
return {
|
|
filter,
|
|
activityFeed,
|
|
unreadNotifications,
|
|
loadError
|
|
};
|
|
}
|