{{ a.unitName }} is understaffed today — {{ a.present }}/{{ a.total }} attending present
View units →
{{ alert.message }} (and {{ systemAlerts.length - 1 }} more)
{{ new Date().toLocaleDateString('en-GB', { weekday: 'long' }) }}
{{ new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }) }}
Week {{ Math.ceil((new Date() - new Date(new Date().getFullYear(),0,1)) / 604800000) }} · Q{{ Math.ceil((new Date().getMonth()+1)/3) }} · {{ (() => { const ends = rotations.filter(r=>r.rotation_status==='active').map(r=>r.end_date).sort().slice(-1)[0]; if(!ends) return 'No active block'; const d=Math.round((new Date(ends+'T00:00:00')-new Date())/86400000); return d>0?'Rotation block ends '+d+'d':'Block ends today'; })() }}
{{ systemSummary.healthLabel }} · {{ systemSummary.healthScore }}%
{{ systemStats.totalStaff || 0 }}
Medical Staff
{{ systemStats.activeAttending || 0 }} attending {{ systemStats.activeResidents || 0 }} residents {{ systemStats.onLeaveStaff }} on leave
{{ systemStats.activeRotations || 0 }}
Active Rotations
{{ systemStats.endingThisWeek || 0 }} ending {{ systemStats.startingNextWeek || 0 }} starting
{{ todaysOnCallCount || 0 }}
On-call Today
{{ formatDrName(todaysOnCall[0].physicianName) }} {{ todaysOnCall[0].startTime }}–{{ todaysOnCall[0].endTime }}
No coverage today
{{ analyticsSummary?.researchLines || researchLines.length || 0 }}
Research Lines
{{ analyticsSummary?.clinicalTrials?.total || clinicalTrials.length || 0 }} studies {{ analyticsSummary?.innovationProjects?.total || innovationProjects.length || 0 }} projects
On-call · Next 3 Days
Loading…
Rotations · This Week
All clear — no changes this week
Rotation Units · Occupancy
No active units
{{ unit.unit_name }} {{ rotations.filter(r=>r.training_unit_id===unit.id&&r.rotation_status==='active').length }}/{{ unit.maximum_residents||0 }}
{{ residentGapWarnings.length }} resident{{ residentGapWarnings.length !== 1 ? 's' : '' }} unassigned — next 3 months
{{ nmAvI(w.name) }}
{{ w.name || '—' }} PGY-{{ w.year }}
{{ g }}
Clinical Staff
{{ deptPulseStats.active }}
Available
active staff
{{ deptPulseStats.onCallToday }}
On call today
{{ deptPulseStats.onCallToday === 0 ? '⚠ no coverage' : 'physicians' }}
{{ deptPulseStats.onLeave }}
On leave
right now
{{ deptPulseStats.rotationCount }}
Rotations
active
{{ staffFilters.residentCategory === 'external_resident' ? 'External Residents' : staffFilters.residentCategory === 'rotating_other_dept' ? 'Rotating Residents' : 'Internal Residents' }}  × {{ filteredMedicalStaff.length }} of {{ medicalStaff.length }}
No staff match this filter
Name Type Rotation Status Actions
{{ nmAvI(staff.full_name) }}
{{ staff.full_name }}
{{ getStaffNextEvent(staff.id).label }}
Affiliated · {{ staff.primary_dept_name || 'External' }} {{ staff.specialization }} {{ staff.resident_category === 'external_resident' ? 'External' : 'Internal' }}
{{ formatStaffTypeShortFn(staff.staff_type) }}
{{ getTrainingUnitName(getCurrentRotationForStaff(staff.id).training_unit_id) }} {{ formatClinicalDuration(getCurrentRotationForStaff(staff.id).start_date, getCurrentRotationForStaff(staff.id).end_date) }}
{{ formatEmploymentStatus(staff.employment_status) }}
No medical staff found
No results match your filters — try clearing them Add your first staff member to get started
On-call Schedule
Primary Call Backup Call Today Past Hover for details · Click to edit
{{ physician.name }} {{ (physician.shifts || []).length }} shift{{ (physician.shifts || []).length !== 1 ? 's' : '' }}
{{ shift.dayLabel }} {{ shift.dateLabel }} · {{ shift.areaName }}
{{ shift.shift_type === 'float_physician' ? 'Float Physician' : shift.shift_type === 'primary_call' ? 'Primary Call' : 'Backup Call' }}
{{ shift.coverage_area?.name || coverageAreas.find(a=>a.id===shift.coverage_area_id)?.name }}
{{ shift.start_time }} – {{ shift.end_time }}
{{ shift.coverage_notes }}
Backup: {{ shift.backupName }}
● On call today
Completed
Upcoming
No shifts assigned
No schedules found
No on-call shifts match your filters
Shift PeriodPhysician TypeStatus BackupActions
Shift Period Physician Type Status Backup Actions
{{ formatDate(schedule.duty_date) }} TODAY +1d
{{ (schedule.start_time||'').slice(0,5) }} {{ (schedule.end_time||'').slice(0,5) }}
{{ schedule.coverage_area?.name || coverageAreas.find(a=>a.id===schedule.coverage_area_id)?.name }}
{{ (getPhysicianName(schedule.primary_physician_id)||'??').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ getPhysicianName(schedule.primary_physician_id) }}
On duty now
{{ {'primary_call':'Primary','backup_call':'Backup','weekend_coverage':'Weekend','float_physician':'Float'}[schedule.shift_type] || schedule.shift_type }} {{ new Date(schedule.duty_date + 'T' + (schedule.start_time||'00:00')) > new Date() ? 'Upcoming' : isToday(schedule.duty_date) ? 'Live' : 'Done' }} {{ getPhysicianName(schedule.backup_physician_id) }}
No schedules found
No results match your filters No on-call shifts have been scheduled yet
{{ calloutKPIs.thisMonth }}
This month
Emergency call-outs
{{ calloutKPIs.night }}
Night calls
After 22:00
{{ calloutKPIs.weekend }}
Weekend calls
Sat / Sun
{{ calloutKPIs.ytd }}
Year to date
All call-outs
Calls by coverage area
Calls by reason
{{ (calloutReasonLabels[reason[0]]||reason[0]).slice(0,20) }}
{{ reason[1] }}
Physician duty load this period
Uneven duty distribution detected — some physicians are carrying significantly more load
{{ p.name }}
{{ p.callouts }} call-out{{ p.callouts!==1?'s':'' }}
{{ p.night }}N · {{ p.weekend }}W
+{{ p.deviation }}% {{ p.deviation }}%
Emergency call-out log {{ callouts.length }} records
No call-outs logged
Record unscheduled emergency call-outs to track duty load across the team
Physician Called at Type Duration Reason
{{ (c.staff?.full_name || c.staff_id || '??').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ c.staff?.full_name || '—' }}
{{ c.called_at ? new Date(c.called_at).toLocaleDateString('es-ES',{day:'2-digit',month:'short',year:'numeric'}) : '—' }}
{{ c.called_at ? new Date(c.called_at).toLocaleTimeString('es-ES',{hour:'2-digit',minute:'2-digit'}) : '' }}
{{ calloutTimeTypes[c.time_type] || c.time_type }} {{ Math.round((new Date(c.end_time) - new Date(c.called_at)) / 60000) }}m {{ calloutReasonLabels[c.reason_category] || c.reason_category }}
{{ c.notes }}
On-call summary per physician
No data yet
Log call-outs in the duty log tab to see the summary
Physician Scheduled on-call Call-outs Night Weekend Total vs avg
{{ (row.name||'??').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ row.name }}
{{ formatStaffType(row.staffType) }}
{{ row.scheduled }} {{ row.callouts }} {{ row.night }} {{ row.weekend }} {{ row.total }}
Uneven distribution detected — one or more physicians are significantly above the team average this period
{{ new Date(new Date().getFullYear(), new Date().getMonth() + oncallMonthOffset, 1) .toLocaleDateString('en-GB', { month: 'long', year: 'numeric' }) }}
{{ oncallMonthSummary }}
{{ area.name }}
Unassigned
{{ d }}
{{ day }}
{{ residentGapWarnings.length }} resident{{ residentGapWarnings.length > 1 ? 's' : '' }} unassigned in next 3 months Click a resident to assign a rotation
{{ (w.name||'??').split(' ').map(p=>p[0]||'').join('').slice(0,2).toUpperCase() }}
{{ w.name || '—' }} PGY-{{ w.year }}
{{ g }}
Resident Rotations
{{ filteredRotations.length }} of {{ rotations.length }}
Active Scheduled Completed Click orb for details · Hover to preview
{{ resident.full_name }} {{ formatTrainingYear(resident.training_year) || 'PGY-?' }} {{ getDepartmentName(resident.department_id) }}
{{ rot.unitName ? rot.unitName.split(' ').slice(0,2).join(' ') : formatClinicalDuration(rot.start_date, rot.end_date) }}
{{ rot.unitName }}
{{ formatDate(rot.start_date) }} → {{ formatDate(rot.end_date) }}
Completed
{{ resident.currentRotation.unitName ? resident.currentRotation.unitName.split(' ').slice(0,2).join(' ') : formatClinicalDuration(resident.currentRotation.start_date, resident.currentRotation.end_date) }}
{{ resident.currentRotation.unitName }}
{{ formatDate(resident.currentRotation.start_date) }} → {{ formatDate(resident.currentRotation.end_date) }}
{{ getDaysRemaining(resident.currentRotation.end_date) }}d remaining
● Active now
{{ rot.unitName ? rot.unitName.split(' ').slice(0,2).join(' ') : formatClinicalDuration(rot.start_date, rot.end_date) }}
{{ rot.unitName }}
{{ formatDate(rot.start_date) }} → {{ formatDate(rot.end_date) }}
Scheduled
No rotations assigned
No rotations found
No residents match your current filters, or no rotations have been assigned yet
{{ getHorizonRangeLabel() }}
{{ m.year }}
Resident
{{ m.label }}
No residents with rotations to display
ResidentUnit PeriodDays Left StatusActions
Resident Unit Period Days Left Status Supervisor Actions
{{ (getResidentName(rotation.resident_id)||'??').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ getResidentName(rotation.resident_id) }}
{{ rotation.resident.external_contact_name }}
{{ getTrainingUnitName(rotation.training_unit_id) }}
{{ rotation.training_unit.dept.name }}
{{ formatDateShort(rotation.start_date) }} {{ formatDateShort(rotation.end_date) }}
in {{ getDaysUntilStart(rotation.start_date) }} days {{ formatRotationStatus(rotation.rotation_status) }} {{ getSupervisorName(rotation.supervising_attending_id) }}
No rotations found
No results match your filters No resident rotations have been assigned yet
Training Units
{{ filteredTrainingUnits.filter(u => getUnitActiveRotationCount(u.id) < u.maximum_residents).length }}
Available
slots open now
{{ filteredTrainingUnits.filter(u => getUnitActiveRotationCount(u.id) > 0 && getUnitActiveRotationCount(u.id) < u.maximum_residents && getUnitActiveRotationCount(u.id) / u.maximum_residents > 0.5).length }}
Filling up
>50% occupied
{{ filteredTrainingUnits.filter(u => getUnitActiveRotationCount(u.id) >= u.maximum_residents).length }}
Full
no slots now
{{ filteredTrainingUnits.filter(u => !!getUnitOverlapWarning(u.id)).length }}
Overlap alert
scheduled conflict
{{ departments.find(d => d.id === trainingUnitFilters.department)?.name || 'Department' }} ×
{{ weeklyStaffingGrid.monday?.toLocaleDateString('es-ES',{day:'2-digit',month:'short'}) }} — {{ new Date(weeklyStaffingGrid.monday?.getTime() + 6*86400000).toLocaleDateString('es-ES',{day:'2-digit',month:'short',year:'numeric'}) }}
{{ day.label }}
Full team Under 50% No cover
Showing
Occupied Partial Available
{{ m.label }}
No clinical units found
Add your first clinical rotation unit to get started
{{ absenceKPIs.absentNow }}
Absent today
{{ absenceKPIs.absentName }} · day {{ absenceKPIs.absentDay }} No current absences
{{ absenceKPIs.upcoming }}
Upcoming planned
Next: {{ absenceKPIs.nextDate }} None scheduled
{{ absenceKPIs.thisMonth }}
This month
{{ absenceKPIs.coveredCount }} covered
{{ absenceKPIs.noCoverage }}
No coverage
Needs attention All covered
Leave & Coverage
Staff memberReason StatusFrom UntilDurationCoverage
Staff member Reason Status From Until Duration Coverage
{{ (getStaffName(absence.staff_member_id)||'??').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ getStaffName(absence.staff_member_id) }}
{{ absence.absence_reason ? absence.absence_reason.replace('_',' ') : '—' }} Absent nowPlannedReturnedCancelled{{ absence.current_status }} {{ formatDate(absence.start_date) }} {{ formatDate(absence.end_date) }} {{ calculateAbsenceDuration(absence.start_date, absence.end_date) }} days {{ getStaffName(absence.covering_staff_id) }} Covered No coverage
No absences found
No results match your filtersNo absence records have been logged yet
30-day coverage forecast {{ absCoverage30.filter(d=>d.pct < 0.8).length }} days below 80% Full coverage
{{ day.label }}
{{ day.available }}/{{ day.available + day.absent }} available
{{ absCalendarTitle }}
{{ absCalendarDays.filter(d=>!d.otherMonth).reduce((s,d)=>s+d.absences.length,0) }} absences {{ absCalendarDays.filter(d=>!d.otherMonth && d.risk==='high').length }} high-risk {{ absCalendarDays.filter(d=>!d.otherMonth && d.risk==='medium').length }} medium
{{ dow }}
{{ day.day }}
{{ day.day }}
{{ day.absences.length }}
+{{ day.absences.length - 3 }} more
{{ stat.icon }}
{{ stat.val }}
{{ stat.label }}
{{ getAbsHorizonLabel() }}
{{ m.label }} {{ m.year }}
Cover
{{ cov.available }}/{{ cov.total }}

No absences in this period

{{ absences.length === 0 ? 'No absence records have been created yet.' : 'All absences in this window are resolved. Navigate forward or switch to Planning mode.' }}

No staff found

{{ cfg.label }}
{{ absTimelinePlanning ? 'Click empty row to add absence' : 'Switch to Planning to schedule leave' }}
On-call assigned Absence (no cover) Absence (covered) On-call gap
Next 14 days
On-call coverage
Absences
{{ new Date(day.date+'T12:00:00').toLocaleDateString('en-GB',{weekday:'short'}) }}
{{ new Date(day.date+'T12:00:00').getDate() }}
{{ formatDrName(getPhysicianName(area.primary.primary_physician_id)) }} {{ area.name }}
{{ area.name }}: unassigned
No shifts scheduled
No data for next 14 days
Schedule on-call shifts to see the coverage overview
Situación operativa
Post today's numbers · {{ new Date().toLocaleDateString('es-ES', { weekday:'long', day:'numeric', month:'long' }) }}
Interconsultas
{{ metricVal('interconsultas_total') }}
{{ metricSub('interconsultas_total') }}
Urgencias neumología
{{ metricVal('er_patients_waiting') }}
{{ metricSub('er_patients_waiting') }} min espera
Camas libres
{{ metricVal('beds_free_total') }}
{{ metricSub('beds_free_total') }} UCI
Altas pendientes
{{ metricVal('discharges_pending_total') }}
{{ metricSub('discharges_pending_total') }} sin informe
Broncoscopias urg.
{{ metricVal('bronchoscopies_urgent') }}
Suite libre {{ metricSub('bronchoscopies_urgent') }}
Rotaciones activas
{{ rotations?.filter(r => r.rotation_status === 'active').length || 0 }}
{{ rotations?.filter(r => r.rotation_status === 'active' && getDaysRemaining(r.end_date) <= 7 && getDaysRemaining(r.end_date) >= 0).length || 0 }} terminan pronto

No briefing posted yet today

{{ bc.content || bc.title }}
{{ bc.created_by_name || '—' }} · {{ formatTimeAgo(bc.created_at) }} · expires {{ formatDate(bc.publish_end_date) }}
{{ (currentUser?.full_name || 'U').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
What does the team need to know?
Broadcast Protocol Note Kudos
{{ (item.created_by_name || item.author || '?').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }}
{{ item.created_by_name || item.author || 'Staff' }} {{ broadcastTypeConfig[getAnnouncementType(item)]?.label }} {{ formatTimeAgo(item.created_at) }}
{{ item.title }}
{{ item.content || item.title }}

Nothing posted yet. Be first to update the department.

Settings
Staff types, degrees, services, coverage areas & system configuration
Staff Types
{{ st.display_name.slice(0,2).toUpperCase() }}
{{ st.display_name }}
{{ st.type_key }}
R
No staff types — click Add Staff Type to create one.
Academic Degrees
No academic degrees defined
Add degree titles such as MD, PhD, MIR
{{ deg.abbreviation || '??' }}
{{ deg.name }}
inactive
Rotation Services
{{ svc.name }}
{{ svc.institution_name }}{{ svc.institution_name && svc.city ? ' · ' : '' }}{{ svc.city }}
{{ svc.service_type==='external_institution' ? 'External' : 'Internal' }}
{{ svc.contact_name || svc.contact_email }}
{{ svc.contact_name }}
{{ svc.contact_phone }}
Add a service
No rotation services defined yet.
General Settings
These settings apply system-wide. Changes take effect immediately.
Audit Logging
Record all user actions to the audit log
Email Notifications
Send email alerts for key events
Absence Notifications
Alert coordinators when absences are recorded
Maintenance Mode
Blocks all non-admin API access
Coverage Areas

Define the coverage areas for on-call assignments (e.g. ICU, Hospitalization, General). Each shift is assigned to one area, allowing multiple primaries per day — one per area.

{{ area.name }}
{{ area.code }} Weekends ✓ Coverage req.
No coverage areas defined
Add areas like ICU, Hospitalization, General to enable per-area on-call scheduling
Migration needed: Run the coverage_areas SQL in Supabase before using this feature.
{{ researchLines.filter(l => l.active !== false).length }}
Active lines
of {{ researchLines.length }} total
{{ clinicalTrials.filter(t => ['Activo','Reclutando'].includes(t.status)).length }}
Active studies
{{ clinicalTrials.filter(t => t.status === 'Reclutando').length }} recruiting
{{ innovationProjects.length }}
Projects
in pipeline
{{ clinicalTrials.reduce((s,t) => s + (t.actual_enrollment || 0), 0) || 0 }}
Participants
No research lines found
{{ researchLineFilters.search ? 'Try a different search' : 'Add your first research line to get started' }}
L{{ line.line_number }}
{{ line.research_line_name || line.name }}
{{ line.coordinator_name ? line.coordinator_name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() : '?' }}
{{ line.coordinator_name ? line.coordinator_name.split(' ').slice(-1)[0] : 'No coord.' }} {{ line.stats.activeTrials }} active
{{ line.stats?.totalStudies || 0 }}
Studies
{{ line.stats?.activeTrials || 0 }}
Active
{{ line.stats?.totalProjects || 0 }}
Projects
{{ kw }} +{{ line.keywords.length - 4 }}
L{{ selectedLine.line_number }}
{{ selectedLine.research_line_name || selectedLine.name }}
{{ selectedLine.coordinator_name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() }}
{{ selectedLine.coordinator_name || 'No coordinator assigned' }} {{ selectedLine.stats.activeTrials }} active
{{ selectedLine.description }}
{{ kw }}
No studies in this line yet
{{ (trial.phase || '').replace('Phase ','') }}
{{ trial.title }}
{{ trial.protocol_id }} {{ trial.status }} {{ trial.study_type }} {{ trial.sponsor_name ? trial.sponsor_name.split(' ').slice(0,2).join(' ') : trial.sponsor_type }} CEIm ✓ CEIm pendiente Protocolo ✓ Financiado Busca financ. Multicentro {{ d }} +{{ trial.target_diseases.length - 2 }} ⚠ incompleto
{{ trial.actual_enrollment || 0 }}/{{ trial.enrollment_target }}
{{ Math.round(((trial.actual_enrollment||0)/trial.enrollment_target)*100) }}%
No projects in this line yet
{{ proj.title }}
{{ proj.current_stage || proj.development_stage }} {{ proj.category }} Financiado Busca financ. Partner ✓ Busca partner TRL {{ proj.trl_level }} {{ proj.ip_status }} Scope ✓ {{ d }}
About this line
{{ selectedLine?.description || 'No description available.' }}
Keywords
{{ kw }}
{{ (selectedStudy.phase || '').replace('Phase ','') }}
{{ selectedStudy.title }}
{{ selectedStudy.status }} {{ selectedStudy.protocol_id }} PI: {{ selectedStudy.pi.full_name }}
Record incomplete — {{ getStudyCompleteness(selectedStudy).total - getStudyCompleteness(selectedStudy).score }} fields missing
{{ m }}
Study details
Status
{{ selectedStudy.status }}
Phase
{{ selectedStudy.phase || '—' }}
Study type
{{ selectedStudy.study_type || '—' }}
Population
{{ selectedStudy.population_type ? { adult: 'Adult', paediatric: 'Paediatric', mixed: 'Mixed', not_applicable: 'N/A' }[selectedStudy.population_type] : '—' }}
Sponsor
{{ selectedStudy.sponsor_name || '—' }}
Sponsor type
{{ selectedStudy.sponsor_type || '—' }}
Protocol finalized
{{ selectedStudy.protocol_finalized ? 'Yes ✓' : 'Pending' }}
Ethics (CEIm)
{{ { approved: 'Approved ✓', pending: 'Pending', exempt: 'Exempt', not_required: 'Not required' }[selectedStudy.ethics_status] }}Not set
Multicentre
{{ selectedStudy.is_multicentre ? `Yes (${selectedStudy.participating_centres || '?'} centres)` : 'No' }}
Funding
{{ { funded: 'Funded', seeking: 'Seeking funding', not_applicable: 'Not applicable', completed: 'Completed' }[selectedStudy.funding_status || 'not_applicable'] }}
Scope note
{{ selectedStudy.scope_note || '—' }}
Target diseases
{{ d }}
No diseases tagged — add now
Timeline
Start date
{{ selectedStudy.start_date || '—' }}
Estimated end
{{ selectedStudy.estimated_end_date || selectedStudy.end_date || '—' }}
Actual end
{{ selectedStudy.actual_end_date }}
Milestones {{ (selectedStudy.milestones||[]).filter(m=>m.done).length }}/{{ (selectedStudy.milestones||[]).length }} done
No milestones yet — add via Edit
{{ ms.label }}
{{ ms.date }}
✓ Done
Description
{{ selectedStudy.description }}
Study team {{ ((selectedStudy.co_investigators||[]).length + (selectedStudy.sub_investigators||[]).length + (selectedStudy.external_team||[]).length) + 1 }} members
{{ selectedStudy.pi?.full_name ? selectedStudy.pi.full_name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() : 'PI' }}
{{ selectedStudy.pi?.full_name || 'PI assigned' }}
Principal Investigator
PI
{{ selectedStudy.dm.full_name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() }}
{{ selectedStudy.dm.full_name }}
Data Manager
DM
{{ medicalStaff.find(s=>s.id===staffId)?.full_name?.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() || '?' }}
{{ medicalStaff.find(s=>s.id===staffId)?.full_name }}
{{ selectedStudy.team_roles?.[staffId] || 'Co-investigator' }}
CHUAC
{{ ext.name ? ext.name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() : (ext.institution||'?').slice(0,2).toUpperCase() }}
{{ ext.name || ext.institution }}
{{ ext.role || 'Collaborator' }} · {{ ext.institution }}
External
No team members assigned — assign via Edit
Enrollment
{{ selectedStudy.actual_enrollment || 0 }} / {{ selectedStudy.enrollment_target }}
Target not set
{{ Math.round(((selectedStudy.actual_enrollment||0)/selectedStudy.enrollment_target)*100) }}% of target
Actions
{{ selectedProject.current_stage || selectedProject.development_stage }} {{ selectedProject.category }}
{{ selectedProject.title }}
Record incomplete — {{ getProjectCompleteness(selectedProject).total - getProjectCompleteness(selectedProject).score }} fields missing
{{ m }}
Project details
Stage
{{ selectedProject.current_stage || selectedProject.development_stage }}
TRL level
{{ selectedProject.trl_level ? 'TRL ' + selectedProject.trl_level : '—' }}
Scope finalized
{{ selectedProject.scope_finalized ? 'Yes ✓' : 'Pending' }}
Funding
{{ { funded:'Funded', seeking:'Seeking funding', not_applicable:'Not applicable', completed:'Completed' }[selectedProject.funding_status || 'not_applicable'] }}
Partner
{{ selectedProject.partner_found ? (selectedProject.partner_name || 'Partner found ✓') : 'No partner yet' }}
IP status
{{ selectedProject.ip_status || '—' }}
Regulatory pathway
{{ { none:'None', ce_mdr:'CE MDR', samd:'SaMD', aemps:'AEMPS', fda:'FDA', other:'Other' }[selectedProject.regulatory_pathway || 'none'] }}
Population
{{ { adult:'Adult', paediatric:'Paediatric', mixed:'Mixed', not_applicable:'N/A' }[selectedProject.population_type || 'adult'] }}
Target diseases
{{ d }}
No diseases tagged — add now
Description
{{ selectedProject.description }}
Partner needs
{{ n }}
Project team
{{ selectedProject.lead.full_name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() }}
{{ selectedProject.lead.full_name }}
Lead Investigator
Lead
{{ medicalStaff.find(s=>s.id===staffId)?.full_name?.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() || '?' }}
{{ medicalStaff.find(s=>s.id===staffId)?.full_name }}
{{ selectedProject.team_roles?.[staffId] || 'Co-investigator' }}
CHUAC
{{ ext.name ? ext.name.split(' ').slice(-2).map(w=>w[0]).join('').toUpperCase() : (ext.institution||'?').slice(0,2).toUpperCase() }}
{{ ext.name || ext.institution }}
{{ ext.role || 'Partner' }} · {{ ext.institution }}
External
No team members assigned — assign via Edit
Actions
All Studies
Showing {{ filteredTrialsAll.length }} of {{ clinicalTrials.length }}
{{ (trial.phase || '').replace('Phase ','') }}
{{ trial.title }}
{{ trial.protocol_id }} {{ trial.status }} L{{ trial.research_lines?.line_number }} · {{ trial.research_lines?.name?.split(' ').slice(0,3).join(' ') }}… CEIm ✓ Protocolo ✓ {{ d }}
{{ trial.actual_enrollment || 0 }}/{{ trial.enrollment_target }}
All Projects
{{ (innovationProjects||[]).filter(p => { const s = p.current_stage || ({'En Desarrollo':'Prototipo','Fase Piloto':'Piloto','Validación Clínica':'Validación'})[p.development_stage]; return s === stage.key; }).length }}
{{ stage.label }}
No projects found
{{ proj.title }}
{{ proj.current_stage || proj.development_stage }} {{ proj.category }} Funded Seeking funding Partner ✓ TRL {{ proj.trl_level }} L{{ proj.research_lines?.line_number }}
Analytics
Portfolio overview
{{ kpi.val }}
{{ kpi.label }}
{{ kpi.sub }}
Studies by status
{{ s.label }} {{ clinicalTrials.filter(t=>t.status===s.label).length }}
Studies by phase
{{ p.replace('Phase ','') }}
{{ p }} {{ clinicalTrials.filter(t=>t.phase===p).length }}
Projects by stage
{{ stage.label }} {{ (innovationProjects||[]).filter(p => { const s = p.current_stage || ({'En Desarrollo':'Prototipo','Fase Piloto':'Piloto','Validación Clínica':'Validación'})[p.development_stage]; return s === stage.key; }).length }}
Data completeness — studies
{{ check.label }}
{{ check.count }}/{{ clinicalTrials.length }}
No posts found
{{ newsFilters.search || newsFilters.type || newsFilters.scope ? 'Try clearing your filters' : 'Create your first post to share updates with the team' }}
Homepage
{{ post.post_type === 'update' ? 'Update' : post.post_type === 'article' ? 'Article' : post.post_type === 'highlight' ? 'Highlight' : 'Publication' }}
{{ post.title }}
{{ post.journal_name }}
{{ post.authors_text }}
DOI {{ post.doi }} {{ post.body }} No publication details added yet
{{ post.body }}
{{ (newsAuthorName(post.author_id)||'?').split(' ').filter(Boolean).slice(0,2).map(n=>n[0]).join('').toUpperCase() }} {{ newsAuthorName(post.author_id) }} {{ newsLineName(post.research_line_id) }} {{ formatNewsDate(post.published_at || post.created_at) }} {{ post.word_count }}w Expires {{ formatDate(post.expires_at) }}
{{ post.status === 'draft' ? 'Draft' : 'Archived' }}
{{ post.is_featured ? 'Remove from homepage' : 'Feature on homepage' }}
Archive
Delete
Loading posts…
Syncing…