Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Zapiony/PUCE_UZDI_2026/llms.txt

Use this file to discover all available pages before exploring further.

The Dashboard (/app/dashboard) is the default landing view for all authenticated users. On mount it fires two parallel API calls — GET /api/v1/adolescente and GET /api/v1/expediente — and derives every counter, chart, and review alert from those live datasets. While the data is loading a full-page skeleton shimmer is displayed; once both requests resolve the page animates into its final layout. A dynamic greeting (Buenos días / Buenas tardes / Buenas noches) together with the current day and date sets the operational context at the top of the page.

Skeleton Loader

Before any data is available, DashboardSkeleton renders placeholder blocks styled with the .sk CSS class, which applies a continuous shimmer animation. The skeleton mirrors the exact layout of the real page — four stat-card placeholders, two chart placeholders, a table placeholder, and a card-grid placeholder — so the page does not jump when content arrives.
/* design-system.css — skeleton shimmer */
.sk {
  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 8px;
}
@keyframes shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
The skeleton is removed from the DOM (via v-if="loading") once both adolescenteService.getAll() and expedienteService.getAll() have settled — success or error.

Statistics Cards

Four clickable .stat cards are rendered in a .stat-grid (4-column CSS grid). Each card navigates to the relevant module when clicked.

Adolescentes registrados

Total adolescents returned by GET /api/v1/adolescente. Clicking navigates to /app/adolescentes.

Expedientes en proceso

Count of expedientes whose estado === 'En proceso'. Clicking navigates to /app/expedientes.

Con alerta (atraso / incomparecencia)

Count of expedientes with estado === 'Atraso' or estado === 'Incomparecencia'. When the count is greater than zero a delta label is shown. Clicking navigates to /app/expedientes.

Total expedientes

Total expedientes regardless of state. Clicking navigates to /app/expedientes.
Hover state is applied inline via mouseenter / mouseleave handlers that toggle a box-shadow:
// DashboardView.vue — stat card hover
@mouseenter="(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 16px rgba(0,0,0,0.10)'"
@mouseleave="(e.currentTarget as HTMLElement).style.boxShadow = ''"

Charts

Both charts sit side-by-side in a .grid-2 two-column layout.

Bar Chart — Expedientes por tipo de medida

DashBarChart receives an items prop of type { l: string; v: number }[] derived from the live expediente list. The view groups expedientes by medida name, sorts descending by count, and passes the top 5.
// DashboardView.vue — barData computed
const barData = computed(() => {
  const counts: Record<string, number> = {}
  for (const e of expedientes.value) {
    const name = e.medida || 'Sin medida'
    counts[name] = (counts[name] ?? 0) + 1
  }
  return Object.entries(counts)
    .map(([l, v]) => ({ l: l.length > 16 ? l.substring(0, 14) + '…' : l, v }))
    .sort((a, b) => b.v - a.v)
    .slice(0, 5)
})
Each bar is a CSS div whose height is proportional to (value / max) * 100% with a minimum of 6% to stay visible. Hovering a bar column dims all other bars to opacity: 0.4 and bolds the hovered value label. Bars alternate the .alt CSS class for a two-tone color scheme.

Donut Chart — Estado de medidas (SVG)

DashDonut renders a pure-SVG donut using stroke-dasharray / stroke-dashoffset path segments. Segments are driven by donutData:
// DashboardView.vue — donutData computed
const donutData = computed(() => [
  { l: 'En proceso',      v: expedientes.value.filter(e => e.estado === 'En proceso').length,      c: '#16a34a' },
  { l: 'Atraso',          v: expedientes.value.filter(e => e.estado === 'Atraso').length,          c: '#d97706' },
  { l: 'Incomparecencia', v: expedientes.value.filter(e => e.estado === 'Incomparecencia').length, c: '#dc2626' },
].filter(d => d.v > 0))
Clicking a segment highlights it and updates a central label showing the segment name and count. Segments with v === 0 are filtered out so the legend stays clean.
ColorEstadoMeaning
#16a34a (green)En procesoMeasure is running on schedule
#d97706 (amber)AtrasoAdolescent is behind schedule
#dc2626 (red)IncomparecenciaAdolescent failed to appear

Upcoming Reviews Table

RevisionListCard displays the five nearest upcoming review dates, sorted ascending by fecha_fin_medida. Only expedientes that have a fecha_fin_medida value appear in the list.
// DashboardView.vue — revisionData computed
const revisionData = computed(() =>
  [...expedientes.value]
    .filter(e => e._raw.fecha_fin_medida)
    .sort((a, b) =>
      new Date(a._raw.fecha_fin_medida!).getTime() -
      new Date(b._raw.fecha_fin_medida!).getTime()
    )
    .slice(0, 5)
    .map(e => ({ adol: e.adol, medida: e.medida, fin: e.fin, estado: e.estado, tono: e.tono }))
)
Each row is color-coded using the .badge + tone classes from the design system:
Badge toneEstadoVisual cue
.badge.greenEn procesoGreen pill
.badge.amberAtrasoAmber pill
.badge.redIncomparecenciaRed pill
Rows with an amber or red badge indicate cases that require immediate attention. Navigate to /app/expedientes and filter by Estado → Atraso or Incomparecencia to act on them.

Quick Access Cards

QuickAccessCard renders a 2×2 grid of shortcut cards for the four main modules:

Adolescentes

Register and manage adolescent profiles.

Expedientes

Open and track socio-educational case files.

Medidas

Manage the catalogue of socio-educational measure types.

Reportes

Generate and export statistical reports.

Global Search (Ctrl+K)

The global search overlay is part of AppShell.vue (the layout wrapper) and is available on every view, including the Dashboard. Press Ctrl+K (or +K on macOS) to open it.
  • Adolescentes and expedientes are loaded from the API the first time the overlay is opened.
  • Typing filters both lists in real time (client-side, no additional network requests).
  • Results display the full name and cédula (adolescentes), or the expediente code and adolescent name (expedientes).
  • Pressing Enter or clicking a result navigates directly to the relevant module.
  • Press Esc or click outside the overlay to close it.
The global search index is populated on first open. If records are added during the session, refresh the page to update the search index.

AppShell Sidebar Navigation

The sidebar is divided into navigation groups that are dynamically built based on the authenticated user’s role (tppr_id).
GroupItemsVisible to
GeneralInicio, Mi PerfilAll roles
GestiónAdolescentes, Expedientes, MedidasAll roles
AnálisisReportesCoordinador, Administrador
ConfiguraciónUsuarios, ParámetrosAdministrador only
The active route receives a blue-gradient background and a left accent line.

Role-Based Visibility (tppr_id)

Sidebar groups are filtered by the navGroups computed property in AppShell.vue. The router guard also redirects unauthorized direct URL access back to the Dashboard.
tppr_idRoleVisible groups
1TécnicoGeneral, Gestión
2CoordinadorGeneral, Gestión, Análisis
3AdministradorGeneral, Gestión, Análisis, Configuración

API Calls Made by the Dashboard

// DashboardView.vue — onMounted
onMounted(async () => {
  try {
    [adolescentes.value, expedientes.value] = await Promise.all([
      adolescenteService.getAll(),   // GET /api/v1/adolescente
      expedienteService.getAll(),    // GET /api/v1/expediente
    ])
  } catch {
    // on error: loading ends, charts/tables show empty state
  } finally {
    loading.value = false
  }
})
Both requests carry the JWT from localStorage via the Axios request interceptor defined in src/services/api.ts.

Build docs developers (and LLMs) love