Skip to content

Last updated:

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:

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.

ComponentPurpose
UiButtonButtons with variant, size, loading state, icons
UiInputText inputs with label, error, icon support
UiTextAreaMulti-line text input
UiModalTeleported modal dialog with header/body/footer
UiTableData table with column config and row slots
UiPaginationPage navigation with v-model
UiCardContent card wrapper
UiAlertAlert messages (success, error, warning, info)
UiBadgeStatus badges with color-coded status map
UiAvatarUser avatar display
UiCheckboxCheckbox input
UiDropdown / UiDropdownCardDropdown menus
UiLabelForm labels
UiSearchInputSearch input with icon
UiSearchSelectSearchable select with async fetch
UiAutocompleteAutocomplete input
UiFilterButtonFilter toggle button
UiSearchFiltersFilter panel
UiIconButtonIcon-only button
UiTabs / UiStatusTabsTab navigation
UiStatCardDashboard stat card with icon and badge
UiSkeletonLoaderLoading placeholder with shimmer
UiPinInputPIN code input
UiPageHeaderPage title with subtitle and action slots
UiSnackBarToast notifications
UiConfirmDialogConfirmation dialog (template ref API)
UiConfirmDialogProviderGlobal confirmation dialog (composable API)
UiDialogProviderDynamic 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.vue

Domain components follow consistent naming conventions:

PatternPurposeExample
[Entity]Form.vueCreate form for an entityInsuranceForm.vue
Update[Entity]Form.vueEdit form, receives data propUpdateInsuranceForm.vue
[Entity]Table.vueTable display for entity listPatientTable.vue
[Entity]TableRow.vueIndividual row renderingPatientTableRow.vue
[Entity]Filters.vueFilter UI for list pagesPatientFilters.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.

vue
<!-- 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

  1. Always use <script setup lang="ts"> -- Every component uses the Composition API with TypeScript.
  2. Define props with interface Props -- Use TypeScript interfaces, not runtime prop definitions.
  3. Use withDefaults for default values -- Pair defineProps<Props>() with withDefaults().
  4. Emit typed events -- Use defineEmits<{ eventName: [payload] }>().
  5. Tailwind for all styling -- No CSS frameworks beyond Tailwind. Utility classes directly in templates.
  6. Icons from Heroicons -- Use @heroicons/vue/24/outline for all icon needs.

CPR - Clinical Patient Records