Skip to content

Shared UI Components

All shared UI components live in frontend/src/shared/ui/. They are generic, reusable, and have no knowledge of domain models (vendors, invoices, etc.).

Usage

Import directly from the shared layer:

typescript
import AppButton from '@/shared/ui/AppButton.vue'
import AppModal from '@/shared/ui/AppModal.vue'

AppButton

A styled button with variant, size, and loading state support.

vue
<AppButton variant="primary" :loading="isSaving" @click="save">
  Save Vendor
</AppButton>

<AppButton variant="danger" size="sm" @click="confirmDelete">
  Delete
</AppButton>
PropTypeDefaultDescription
variant'primary' | 'secondary' | 'danger' | 'ghost''primary'Visual style
size'sm' | 'md' | 'lg''md'Button size
loadingbooleanfalseShows spinner, disables click
disabledbooleanfalseDisables the button

AppTextInput

Standard text input with label and validation error display.

vue
<AppTextInput
  v-model="form.name"
  label="Vendor Name"
  placeholder="Enter vendor name"
  :error="errors.name"
  required
/>

AppSelectInput

Dropdown select with typed options.

vue
<AppSelectInput
  v-model="form.vendorType"
  label="Vendor Type"
  :options="vendorTypeOptions"
  :error="errors.vendorType"
/>

AppCombobox

Searchable dropdown / autocomplete — useful for large option lists like vendors.

vue
<AppCombobox
  v-model="selectedVendorId"
  label="Vendor"
  :options="vendorOptions"
  placeholder="Search vendors..."
/>

AppDataTable

A full-featured data table with sortable columns, pagination controls, and empty/loading states.

vue
<AppDataTable
  :columns="columns"
  :rows="vendors"
  :loading="isLoading"
  :total-count="totalCount"
  :page="page"
  :page-size="pageSize"
  @sort="onSort"
  @page-change="onPageChange"
/>

AppModal

Accessible dialog with slot-based header, body, and footer.

vue
<AppModal :open="showModal" title="Create Vendor" @close="showModal = false">
  <template #body>
    <VendorForm @saved="onSaved" />
  </template>
</AppModal>

AppDeleteModal

Specialised modal for soft-delete confirmation with an optional reason field.

vue
<AppDeleteModal
  :open="showDeleteModal"
  entity-name="Vendor"
  :entity-label="vendor.name"
  @confirm="deleteVendor"
  @cancel="showDeleteModal = false"
/>

AppPermissionGate

Conditionally renders its default slot based on the user's JWT permissions. Renders nothing (not a 403 error) if the permission is absent.

vue
<AppPermissionGate collection="Vendor" action="Create">
  <AppButton @click="openCreate">New Vendor</AppButton>
</AppPermissionGate>
PropTypeDescription
collectionstringPermission collection (e.g. 'Vendor')
actionstringPermission action (e.g. 'Create')

AppStatusBadge

Colour-coded pill badge for enum status values like InvoiceStatus.

vue
<AppStatusBadge :status="invoice.status" />
<!-- Renders: FullyPaid (green), Overdue (red), PartiallyPaid (amber), Canceled (grey) -->

AppKpiCard

Dashboard KPI card with a title, value, and optional icon.

vue
<AppKpiCard title="Total Due" :value="summary.totalDue" icon="💰" format="currency" />

AppToast

Global toast notification system. Controlled via the useToast() composable:

typescript
const toast = useToast()
toast.success('Vendor saved successfully')
toast.error('Failed to delete invoice')

AppWizard

Multi-step wizard with step indicators — used by the Onboarding flow.

vue
<AppWizard :steps="steps" :current-step="currentStep">
  <template #step-1><PharmacySetupStep /></template>
  <template #step-2><AdminUserStep /></template>
</AppWizard>

AppTabList

Horizontal tab navigation for switching between views within a page.

vue
<AppTabList :tabs="['Overview', 'Invoices', 'Transactions']" v-model="activeTab" />

AppPasswordInput

Password field with show/hide toggle.

vue
<AppPasswordInput v-model="form.password" label="Password" :error="errors.password" />

AppSplash

Full-screen loading/splash screen — shown while the app initialises or checks onboarding status.

MedRegia — Pharmaceutical Vendor Management Platform