@damourlabs/euro-clinical-trials
Version:
A Nuxt 3 application for visualizing and managing clinical trials data in Europe, built with modern web technologies.
289 lines (242 loc) • 8.58 kB
Markdown
The `SiteComponent.vue` has been completely refactored to work with the new database schema structure. This document outlines the changes made and provides usage examples.
- **Old**: `site.id` → **New**: `site.uuid`
- **Old**: `site.trialId` → **New**: `site.trialUuid`
The component now handles user references as UUIDs instead of nested objects:
- **Old**: `site.principalInvestigator.name`
- **New**: `principalInvestigator?.name` (passed as separate prop)
- **Data Completeness**: Now handles string values (decimal) instead of numbers
- **Enum Values**: Updated to match new schema (e.g., `OnTime` instead of `onTime`)
### 2. New Props Structure
The component now accepts the following props:
```typescript
interface Props {
site: Site | undefined
certifications?: SiteCertification[] // NEW: Separate certifications data
documents?: Document[] // NEW: Separate documents data
principalInvestigator?: User // NEW: Resolved user object
contactPerson?: User // NEW: Resolved user object
studyCoordinator?: User // NEW: Resolved user object
users?: User[] // NEW: Array for document uploader lookup
}
```
- Now shows detailed certification information including issue and expiry dates
- Proper handling of the separate `SiteCertification` table
- Enhanced document display with proper metadata
- Added "View Document" functionality
- Proper handling of the separate `Document` table
- Graceful handling of missing user data
- Fallback displays for unassigned roles
- `Active`, `Inactive`, `Pending`, `Closed`
- `OnTime`, `Delayed`, `NotSubmitted`
- `Hospital`, `Clinic`, `University`, `ResearchCenter`
- `Protocol`, `InformedConsent`, `RegulatoryApproval`, `Other`
```vue
<template>
<SiteComponent
:site="site"
:certifications="siteCertifications"
:documents="siteDocuments"
:principal-investigator="principalInvestigator"
:contact-person="contactPerson"
:study-coordinator="studyCoordinator"
:users="allUsers"
/>
</template>
<script setup>
// Fetch site data with related information
const { data: siteData } = await $fetch(`/api/sites/${siteId}`, {
query: {
include: ['certifications', 'documents', 'users']
}
})
// Extract related data
const site = siteData.site
const siteCertifications = siteData.certifications
const siteDocuments = siteData.documents
const allUsers = siteData.users
// Find specific users
const principalInvestigator = allUsers.find(u => u.uuid === site.principalInvestigatorUuid)
const contactPerson = allUsers.find(u => u.uuid === site.contactPersonUuid)
const studyCoordinator = site.studyCoordinatorUuid
? allUsers.find(u => u.uuid === site.studyCoordinatorUuid)
: undefined
</script>
```
```vue
<!-- pages/sites/[uuid].vue -->
<template>
<div class="container mx-auto py-6">
<SiteComponent
v-if="!pending && siteData"
:site="siteData.site"
:certifications="siteData.certifications"
:documents="siteData.documents"
:principal-investigator="siteData.principalInvestigator"
:contact-person="siteData.contactPerson"
:study-coordinator="siteData.studyCoordinator"
:users="siteData.users"
/>
<div v-else-if="pending" class="text-center py-8">
<p>Loading site information...</p>
</div>
<div v-else class="text-center py-8">
<p>Site not found</p>
</div>
</div>
</template>
<script setup>
const route = useRoute()
const siteUuid = route.params.uuid
const { data: siteData, pending } = await useFetch(`/api/sites/${siteUuid}`, {
transform: (data: any) => ({
site: data.site,
certifications: data.certifications || [],
documents: data.documents || [],
principalInvestigator: data.users?.find((u: any) => u.uuid === data.site.principalInvestigatorUuid),
contactPerson: data.users?.find((u: any) => u.uuid === data.site.contactPersonUuid),
studyCoordinator: data.site.studyCoordinatorUuid
? data.users?.find((u: any) => u.uuid === data.site.studyCoordinatorUuid)
: undefined,
users: data.users || []
})
})
</script>
```
Your API endpoints should return data in this structure:
```typescript
// GET /api/sites/[uuid]
interface SiteDetailResponse {
site: Site
certifications: SiteCertification[]
documents: Document[]
users: User[] // All related users for this site
}
```
Example API response:
```json
{
"site": {
"uuid": "site-uuid-here",
"trialUuid": "trial-uuid-here",
"name": "Memorial Hospital Clinical Site",
"address": "123 Medical Drive, Health City, HC 12345",
"contactPersonUuid": "user-uuid-1",
"principalInvestigatorUuid": "user-uuid-2",
"studyCoordinatorUuid": "user-uuid-3",
"facilityType": "Hospital",
"status": "Active",
"patientsEnrolled": 45,
"targetEnrollment": 100,
"dataSubmissionStatus": "OnTime",
"activationDate": "2024-01-15",
"dataCompleteness": "85.50",
"lastMonitoringVisit": "2024-07-01",
"nextScheduledVisit": "2024-08-15",
"protocolDeviations": 2,
"adverseEventsReported": 1
},
"certifications": [
{
"uuid": "cert-uuid-1",
"siteUuid": "site-uuid-here",
"certificationName": "GCP Certification",
"issuedDate": "2024-01-01",
"expiryDate": "2025-01-01"
}
],
"documents": [
{
"uuid": "doc-uuid-1",
"title": "Site Activation Protocol",
"url": "https://storage.example.com/documents/site-activation.pdf",
"documentType": "Protocol",
"description": "Site activation protocol and procedures",
"uploadDate": "2024-01-10",
"uploadedBy": "user-uuid-4",
"siteUuid": "site-uuid-here"
}
],
"users": [
{
"uuid": "user-uuid-1",
"name": "Dr. Sarah Johnson",
"email": "sarah.johnson@memorial.com",
"institution": "Memorial Hospital",
"role": "Investigator"
},
{
"uuid": "user-uuid-2",
"name": "Dr. Michael Chen",
"email": "michael.chen@memorial.com",
"institution": "Memorial Hospital",
"role": "Investigator"
}
]
}
```
1. **Update API calls** to fetch related data (certifications, documents, users)
2. **Update prop passing** to use new structure
3. **Test user role assignments** to ensure proper display
4. **Verify document viewing** functionality works with your storage system
### Database Query Considerations
When fetching site data, ensure you're joining the related tables:
```sql
-- Example query structure
SELECT
s.*,
sc.*, -- site certifications
d.*, -- documents
u.* -- users
FROM sites s
LEFT JOIN site_certifications sc ON s.uuid = sc.site_uuid
LEFT JOIN documents d ON s.uuid = d.site_uuid
LEFT JOIN users u ON (
s.contact_person_uuid = u.uuid OR
s.principal_investigator_uuid = u.uuid OR
s.study_coordinator_uuid = u.uuid OR
d.uploaded_by = u.uuid
)
WHERE s.uuid = $1;
```
1. **Better Separation of Concerns**: Data fetching is separated from presentation
2. **Improved Performance**: Related data can be fetched efficiently
3. **Enhanced User Experience**: Better handling of missing data and loading states
4. **Future-Proof**: Easily extensible for additional related data
5. **Type Safety**: Full TypeScript support with proper type definitions
1. **Missing User Data**: Ensure all user UUIDs in the site object have corresponding user records
2. **Document Viewing Errors**: Verify document URLs are accessible and properly formatted
3. **Date Formatting Issues**: Ensure date strings are in proper ISO format
4. **Certification Display**: Check that certification data includes all required fields
```typescript
// Add debug logging to track data flow
console.log('Site data:', site)
console.log('Principal Investigator:', principalInvestigator)
console.log('Certifications:', certifications)
console.log('Documents:', documents)
```