UNPKG

@damourlabs/euro-clinical-trials

Version:

A Nuxt 3 application for visualizing and managing clinical trials data in Europe, built with modern web technologies.

222 lines (200 loc) 8 kB
import { h } from 'vue' import type { ColumnDef } from '@tanstack/vue-table' import type { Trial, AuditLog, User } from '~/server/database/schema'; import { UuidField, StatusBadge, FieldDate, GenericCompactCard } from '~/components/common/field' import { getHeaderClasses, getCellClasses, createResponsiveColumn } from '~/utils/table-helpers' async function fetchUser(_uuid: string) { const user = useUsersStore().getById(_uuid) if (user === undefined) { throw createError(`User with UUID ${_uuid} not found`) } return user } async function fetchTrial(_uuid: string) { const trial = useTrialsStore().getById(_uuid) if (trial === undefined) { throw createError(`Trial with UUID ${_uuid} not found`) } return trial } export const AUDIT_COLUMNS: ColumnDef<AuditLog>[] = [ createResponsiveColumn({ accessorKey: 'uuid', header: () => h('div', { class: getHeaderClasses('right') }, 'Audit ID'), cell: ({ row }) => { const audit = row.original return h('div', { class: getCellClasses('right') }, h(GenericCompactCard<AuditLog>, { entityUuid: audit.uuid, entityType: 'audit', fetchFunction: () => Promise.resolve(audit), titleField: 'action', subtitleField: 'entityType', statusField: undefined, colorScheme: 'orange' }) ) }, }), createResponsiveColumn({ accessorKey: 'action', header: () => h('div', { class: getHeaderClasses('center') }, 'Action'), cell: ({ row }) => { const action = row.getValue('action') as string return h('div', { class: getCellClasses('center') }, h(StatusBadge, { status: action }) ) }, }), createResponsiveColumn({ accessorKey: 'entityType', header: () => h('div', { class: getHeaderClasses('center') }, 'Entity Type'), cell: ({ row }) => { const entityType = row.getValue('entityType') as string return h('div', { class: getCellClasses('center') }, h(StatusBadge, { status: entityType }) ) }, }), createResponsiveColumn({ accessorKey: 'entityUuid', header: () => h('div', { class: getHeaderClasses('center') }, 'Entity'), cell: ({ row }) => { const entityType = row.getValue('entityType') as string const entityUuid = row.getValue('entityUuid') as string const detailUrl = getEntityDetailUrl(entityType, entityUuid) return h('div', { class: getCellClasses('center') }, h(UuidField, { uuid: entityUuid, shortDisplay: true, detailUrl: detailUrl }) ) }, }), createResponsiveColumn({ accessorKey: 'userUuid', header: () => h('div', { class: getHeaderClasses('center') }, 'User'), cell: ({ row }) => { const userUuid = row.getValue('userUuid') as string | null if (!userUuid) { return h('div', { class: getCellClasses('center') + ' text-gray-400' }, 'System') } return h('div', { class: getCellClasses('center') }, h(GenericCompactCard<User>, { entityUuid: userUuid, entityType: 'user', fetchFunction: fetchUser, titleField: 'name', subtitleField: 'email', statusField: 'role', colorScheme: 'purple' }) ) }, }), createResponsiveColumn({ accessorKey: 'trialUuid', header: () => h('div', { class: getHeaderClasses('center') }, 'Trial'), cell: ({ row }) => { const trialUuid = row.getValue('trialUuid') as string return h('div', { class: getCellClasses('center') }, h(GenericCompactCard<Trial>, { entityUuid: trialUuid, entityType: 'trial', fetchFunction: fetchTrial, titleField: 'title', subtitleField: 'description', statusField: 'status', colorScheme: 'blue' }) ) }, }), createResponsiveColumn({ accessorKey: 'timestamp', header: () => h('div', { class: getHeaderClasses('center') }, 'Timestamp'), cell: ({ row }) => { const timestamp = row.getValue('timestamp') as string return h('div', { class: getCellClasses('center') }, h(FieldDate, { date: timestamp, showTime: true }) ) }, }), createResponsiveColumn({ accessorKey: 'ipAddress', header: () => h('div', { class: getHeaderClasses('center') }, 'IP Address'), cell: ({ row }) => { const ipAddress = row.getValue('ipAddress') as string | null if (!ipAddress) { return h('div', { class: getCellClasses('center') + ' text-gray-400' }, 'Unknown') } return h('div', { class: getCellClasses('center') + ' font-mono text-sm' }, ipAddress) }, }), createResponsiveColumn({ accessorKey: 'userAgent', header: () => h('div', { class: getHeaderClasses('center') }, 'User Agent'), cell: ({ row }) => { const userAgent = row.getValue('userAgent') as string | null if (!userAgent) { return h('div', { class: getCellClasses('center') + ' text-gray-400' }, 'Unknown') } const truncated = userAgent.length > 40 ? userAgent.substring(0, 40) + '...' : userAgent return h('div', { class: getCellClasses('center') + ' font-mono text-xs', title: userAgent }, truncated) }, }), createResponsiveColumn({ accessorKey: 'sessionId', header: () => h('div', { class: getHeaderClasses('center') }, 'Session'), cell: ({ row }) => { const sessionId = row.getValue('sessionId') as string | null if (!sessionId) { return h('div', { class: getCellClasses('center') + ' text-gray-400' }, 'No session') } return h('div', { class: getCellClasses('center') }, h(UuidField, { uuid: sessionId, shortDisplay: true }) ) }, }), createResponsiveColumn({ accessorKey: 'changes', header: () => h('div', { class: getHeaderClasses('center') }, 'Changes'), cell: ({ row }) => { const changes = row.getValue('changes') as unknown[] | null if (!changes || changes.length === 0) { return h('div', { class: getCellClasses('center') + ' text-gray-400' }, 'No changes') } const changeCount = Array.isArray(changes) ? changes.length : 0 return h('div', { class: getCellClasses('center') }, h(StatusBadge, { status: `${changeCount} field${changeCount !== 1 ? 's' : ''}`, variant: 'info' }) ) }, }) ] // Helper function to generate entity detail URLs function getEntityDetailUrl(entityType: string, entityUuid: string): string { const typeUrlMap: Record<string, string> = { 'Trial': '/trials', 'Patient': '/patients', 'User': '/users', 'Site': '/sites', 'Document': '/documents', 'AdverseEvent': '/adverse-events', 'Protocol': '/protocols', 'GdprConsent': '/gdpr-consents' } const basePath = typeUrlMap[entityType] || '/unknown' return `${basePath}/${entityUuid}` }