Busflow Docs

Internal documentation portal

Skip to content

Shared UI Components

The foundational UI wrappers for the Busflow Workspace. These components act as a bridge between the Quasar layout engine and our Tailwind design system, enforcing the UX guidelines defined in docs/ux-design-decisions.md.

Exported Components

You can import all components directly from the public API:

ts
import { DataTable, DrawerForm, DetailPopup, TextField } from 'src/shared/ui';

1. DataTable

A heavily styled wrapper around Quasar's QTable. It enforces our Tailwind aesthetics, handles pagination intelligently (no default crash-inducing sort columns), and implements a standardized empty state.

Usage

vue
<template>
  <DataTable
    title="Fleet Management"
    :columns="columns"
    :rows="vehicles"
    :loading="isLoading"
    empty-headline="Let's build your fleet"
    empty-subtext="Add your vehicles to start dispatching."
  >
    <!-- Add buttons to the top right -->
    <template #top-actions>
      <q-btn color="primary" label="+ Add Vehicle" @click="openDrawer" />
    </template>

    <!-- Custom column rendering (uses Quasar's body-cell-[name] syntax) -->
    <template #body-cell-status="props">
      <q-td :props="props">
        <q-chip :color="props.row.status === 'ACTIVE' ? 'positive' : 'grey'">
          {{ props.row.status }}
        </q-chip>
      </q-td>
    </template>
  </DataTable>
</template>

2. DrawerForm

A standardized right-aligned slide-over QDrawer. It solves the "scrolling form" problem by hard-pinning the header and footer to the top and bottom of the viewport, wrapping the entire content in a native <form> element.

Usage

vue
<template>
  <DrawerForm 
    v-model="isOpen" 
    title="Add Vehicle" 
    submitLabel="Save Vehicle"
    :loading="isSubmitting"
    @submit="handleSave"
  >
    <!-- The body of the form goes here. It scrolls automatically. -->
    <TextField name="license_plate" label="License Plate" />
    <TextField name="capacity" label="Capacity" type="number" />
  </DrawerForm>
</template>

3. DetailPopup

A centered modal QDialog using a Tailwind card layout. Used for medium-complexity views (like clicking on a crew member to see their qualifications).

Usage

vue
<template>
  <DetailPopup v-model="isOpen" title="Vehicle Details">
    <template #header-badge>
      <q-chip color="positive" size="sm">ACTIVE</q-chip>
    </template>

    <!-- Body Content -->
    <div class="space-y-4">
      <div class="grid grid-cols-2 gap-4">
        <div>
          <p class="text-sm text-muted-foreground">License Plate</p>
          <p class="font-medium">{{ vehicle.license_plate }}</p>
        </div>
      </div>
    </div>
  </DetailPopup>
</template>

4. Validation Fields (VeeValidate + Valibot)

TextField and SelectField are thin wrappers around Quasar's q-input and q-select. They automatically bind to VeeValidate form state based on the name prop, pulling in validation errors and displaying them natively inline.

Note: To use these, the parent component must be wrapped in VeeValidate's <Form> or use useForm() with a Valibot schema.

Usage

vue
<script setup>
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/valibot';
import * as v from 'valibot';
import { TextField, SelectField, DrawerForm } from 'src/shared/ui';

// 1. Define Valibot Schema
const schema = v.object({
  model: v.pipe(v.string(), v.minLength(1, "Model is required")),
  class: v.pipe(v.string(), v.minLength(1, "Class is required"))
});

// 2. Setup VeeValidate
const { handleSubmit } = useForm({
  validationSchema: toTypedSchema(schema)
});

const onSubmit = handleSubmit((values) => {
  console.log(values);
});
</script>

<template>
  <DrawerForm v-model="isOpen" title="Add Vehicle" @submit="onSubmit">
    <!-- Notice we don't need v-model, we just bind the schema name! -->
    <TextField name="model" label="Vehicle Model" placeholder="e.g. Mercedes" />
    
    <SelectField 
      name="class" 
      label="Vehicle Class" 
      :options="['COACH', 'MINIBUS']" 
    />
  </DrawerForm>
</template>

Internal documentation — Busflow