Components Overview
CPR uses a three-tier component architecture built on Vue 3's Composition API with <script setup lang="ts">. All components are auto-imported by Nuxt, so you never need manual import statements for components.
Auto-Import Configuration
Components live under app/components/ and are registered with pathPrefix: false in nuxt.config.ts:
// nuxt.config.ts
components: [
{
path: '~/components',
pathPrefix: false,
},
],This means a component at app/components/ui/UiButton.vue is available as <UiButton> anywhere in the app -- no import needed. Nested directories do not add prefixes, so app/components/configuration/forms/InsuranceForms/InsuranceForm.vue is simply <InsuranceForm>.
The Three Tiers
Tier 1: UI Components (app/components/ui/)
Generic, reusable building blocks with no business logic. These handle presentation only and accept all configuration through props.
| Component | Purpose |
|---|---|
UiButton | Buttons with variant, size, loading state, icons |
UiInput | Text inputs with label, error, icon support |
UiTextArea | Multi-line text input |
UiModal | Teleported modal dialog with header/body/footer |
UiTable | Data table with column config and row slots |
UiPagination | Page navigation with v-model |
UiCard | Content card wrapper |
UiAlert | Alert messages (success, error, warning, info) |
UiBadge | Status badges with color-coded status map |
UiAvatar | User avatar display |
UiCheckbox | Checkbox input |
UiDropdown / UiDropdownCard | Dropdown menus |
UiLabel | Form labels |
UiSearchInput | Search input with icon |
UiSearchSelect | Searchable select with async fetch |
UiAutocomplete | Autocomplete input |
UiFilterButton | Filter toggle button |
UiSearchFilters | Filter panel |
UiIconButton | Icon-only button |
UiTabs / UiStatusTabs | Tab navigation |
UiStatCard | Dashboard stat card with icon and badge |
UiSkeletonLoader | Loading placeholder with shimmer |
UiPinInput | PIN code input |
UiPageHeader | Page title with subtitle and action slots |
UiSnackBar | Toast notifications |
UiConfirmDialog | Confirmation dialog (template ref API) |
UiConfirmDialogProvider | Global confirmation dialog (composable API) |
UiDialogProvider | Dynamic dialog rendering for form modals |
See Base Components for the full API reference.
Tier 2: Domain Components (app/components/[module]/)
Business-specific components that compose UI components and encode domain logic. They are organized by module:
app/components/
configuration/
forms/
InsuranceForms/
InsuranceForm.vue # Create form
UpdateInsuranceForm.vue # Edit form
MedicineForms/
MedicineForm.vue
UpdateMedicineForm.vue
patient/
PatientTable.vue
PatientTableRow.vue
PatientFilters.vue
pharmacy/
forms/
items/
PharmacyItemForm.vue
UpdatePharmacyItemForm.vue
queue/
TransferPatientForm.vue
surgery/
SurgeryForm.vueDomain components follow consistent naming conventions:
| Pattern | Purpose | Example |
|---|---|---|
[Entity]Form.vue | Create form for an entity | InsuranceForm.vue |
Update[Entity]Form.vue | Edit form, receives data prop | UpdateInsuranceForm.vue |
[Entity]Table.vue | Table display for entity list | PatientTable.vue |
[Entity]TableRow.vue | Individual row rendering | PatientTableRow.vue |
[Entity]Filters.vue | Filter UI for list pages | PatientFilters.vue |
Tier 3: Page Components (app/pages/)
Top-level pages that orchestrate domain components, manage data fetching, and wire up routing. Pages are file-based routes handled by Nuxt.
<!-- app/pages/configuration/insurances/index.vue -->
<template>
<div class="px-6 lg:px-8 pb-8">
<UiPageHeader title="Insurance Providers">
<template #actions>
<UiButton @click="handleAddInsuranceClick">
Add Insurance
</UiButton>
</template>
</UiPageHeader>
<InsuranceFilters v-model:search="filters.search" />
<InsuranceTable :data="insurances" @edit="handleUpdateInsuranceClick" />
<UiPagination v-model:current-page="currentPage" :per-page="15" :total="total" />
</div>
</template>Utility Components
A few components fall outside the three-tier model and serve cross-cutting purposes:
BranchSelector.vue-- Clinic branch switcher, rendered in the default layout header.PhilippineAddressForm.vue-- Reusable Philippine address fields (region, province, city, barangay) used across patient and supplier forms.ReportLayout.vue-- Print-friendly report wrapper.
Component Conventions
- Always use
<script setup lang="ts">-- Every component uses the Composition API with TypeScript. - Define props with
interface Props-- Use TypeScript interfaces, not runtime prop definitions. - Use
withDefaultsfor default values -- PairdefineProps<Props>()withwithDefaults(). - Emit typed events -- Use
defineEmits<{ eventName: [payload] }>(). - Tailwind for all styling -- No CSS frameworks beyond Tailwind. Utility classes directly in templates.
- Icons from Heroicons -- Use
@heroicons/vue/24/outlinefor all icon needs.
