# CLAUDE.md - Vuexy Theme Reference

This is the **Vuexy Vue 3 + Laravel Admin Dashboard Template** (TypeScript edition). It serves as the UI reference for the EZSCALE billing application at `../website/`. Do NOT modify files in this directory — use it as a read-only reference for components, patterns, layouts, and styling conventions.

## Purpose

When building UI for the EZSCALE site (`../website/`), consult this theme for:
- Component patterns and TypeScript conventions
- Vuetify component usage and defaults
- Layout structure (vertical nav, navbar, footer)
- Dashboard widget composition
- Data table patterns with server-side pagination
- Form validation patterns
- Navigation item structure
- Color system and theming
- Icon usage (Tabler icons)

## Tech Stack

- **Vue 3.5** with Composition API (`<script setup lang="ts">`)
- **TypeScript 5.9** (strict mode)
- **Vuetify 3.10** — Material Design component library
- **Vite 7** with laravel-vite-plugin
- **Pinia 3** — State management
- **Vue Router 4** — File-based routing via unplugin-vue-router
- **Vue-i18n 11** — Internationalization (en, fr, ar with RTL)
- **CASL** — Access control (action + subject permissions)
- **ApexCharts + Chart.js** — Charting
- **Tabler Icons** via @iconify/vue
- **SCSS/Sass** — Styling with Vuetify variable overrides
- **VueUse** — Composition utilities
- **MSW (Mock Service Worker)** — API mocking for development

## Directory Structure

```
resources/ts/                    # All TypeScript source code
├── main.ts                      # Entry point (creates app, registers plugins)
├── App.vue                      # Root component (VApp wrapper, theme CSS vars)
│
├── @core/                       # Core theme utilities (DO NOT MODIFY)
│   ├── initCore.ts              # Theme initialization (sync loader theme, skin changes, RTL)
│   ├── enums.ts                 # Skins (Default, Bordered), Theme (light, dark, system)
│   ├── types.ts                 # ExplicitThemeConfig, Options, SortItem, CustomInputContent
│   ├── stores/config.ts         # ConfigStore (theme, skin, isVerticalNavSemiDark)
│   ├── composable/
│   │   ├── useSkins.ts          # Skin CSS class injection, layout attributes
│   │   ├── useCookie.ts         # Reactive cookie read/write
│   │   └── createUrl.ts         # Query string builder for API calls
│   ├── components/
│   │   └── cards/
│   │       ├── CardStatisticsHorizontal.vue  # Metric + icon (horizontal)
│   │       └── CardStatisticsVertical.vue    # Metric + icon + chart (vertical)
│   ├── utils/
│   │   ├── plugins.ts           # Auto-registers all plugins from plugins/ directory
│   │   ├── validators.ts        # Form validators (required, email, password, url, between)
│   │   ├── helpers.ts           # isEmpty, isNullOrUndefined, isEmptyArray, isObject, isToday
│   │   └── colorConverter.ts    # hexToRgb, rgbaToHex
│   └── libs/                    # Chart.js and ApexCharts wrapper configs
│
├── @layouts/                    # Layout system (DO NOT MODIFY)
│   ├── config.ts                # Default layout configuration
│   ├── enums.ts                 # ContentWidth, NavbarType, FooterType, AppContentLayoutNav
│   ├── types.ts                 # NavLink, NavGroup, NavSectionTitle, VerticalNavItems
│   ├── symbols.ts               # Vue injection symbols
│   ├── utils.ts                 # Layout utility functions
│   ├── stores/config.ts         # LayoutConfigStore (navbarType, isVerticalNavCollapsed, etc.)
│   └── components/
│       ├── VerticalNavLayout.vue             # Main layout wrapper
│       ├── DefaultLayoutWithVerticalNav.vue  # Vertical nav + navbar + content + footer
│       ├── DefaultLayoutWithHorizontalNav.vue
│       ├── VerticalNav.vue                   # Sidebar navigation
│       ├── VerticalNavLink.vue               # Single nav link item
│       ├── VerticalNavGroup.vue              # Collapsible nav group
│       ├── VerticalNavSectionTitle.vue       # Section heading/divider
│       ├── HorizontalNav.vue                # Top bar navigation
│       ├── HorizontalNavLink.vue
│       ├── HorizontalNavGroup.vue
│       └── HorizontalNavPopper.vue          # Dropdown for horizontal nav
│
├── layouts/                     # Page layout templates
│   ├── default.vue              # Main layout (navbar + sidebar + content + footer)
│   ├── blank.vue                # Blank layout (auth pages, no nav)
│   └── components/
│       ├── DefaultLayoutWithVerticalNav.vue  # Composes navbar items (search, theme switcher, notifications, user profile)
│       ├── Footer.vue
│       ├── NavBarNotifications.vue
│       ├── NavSearchBar.vue
│       ├── NavbarShortcuts.vue
│       ├── NavbarThemeSwitcher.vue
│       └── UserProfile.vue
│
├── pages/                       # File-based routing (auto-generates routes)
│   ├── login.vue                # → /login (uses blank layout)
│   ├── register.vue             # → /register (uses blank layout)
│   ├── dashboards/
│   │   ├── analytics.vue        # → /dashboards/analytics
│   │   ├── crm.vue              # → /dashboards/crm
│   │   └── ecommerce.vue        # → /dashboards/ecommerce
│   ├── apps/
│   │   ├── invoice/
│   │   │   ├── list/index.vue   # → /apps/invoice/list (data table + filters)
│   │   │   ├── add/index.vue    # → /apps/invoice/add (form)
│   │   │   ├── edit/[id].vue    # → /apps/invoice/edit/:id
│   │   │   └── preview/[id].vue # → /apps/invoice/preview/:id
│   │   ├── email/index.vue
│   │   └── chat/index.vue
│   └── front-pages/             # Marketing pages
│
├── views/                       # Reusable view components (used by pages)
│   └── dashboards/
│       ├── analytics/           # Widget components for analytics dashboard
│       └── ecommerce/           # Widget components for ecommerce dashboard
│
├── plugins/                     # Vue plugins (loaded alphabetically)
│   ├── 1.router/
│   │   ├── index.ts             # Vue Router setup with file-based routing
│   │   ├── guards.ts            # Auth guards, CASL permission checks
│   │   └── additional-routes.ts
│   ├── 2.pinia.ts               # Pinia store initialization
│   ├── vuetify/
│   │   ├── index.ts             # Vuetify plugin setup
│   │   ├── theme.ts             # Light/dark theme color definitions
│   │   ├── defaults.ts          # Component defaults (variants, colors, densities)
│   │   └── icons.ts             # Icon aliases (Tabler icons)
│   ├── i18n/
│   │   ├── index.ts             # Vue-i18n setup
│   │   └── locales/             # en.json, fr.json, ar.json
│   ├── casl/index.ts            # CASL access control plugin
│   ├── fake-api/                # MSW mock API handlers
│   │   ├── index.ts
│   │   └── handlers/            # Mock endpoints by domain (invoice, auth, etc.)
│   ├── iconify/index.ts         # Iconify icon registration
│   └── layouts.ts               # Layout plugin setup
│
├── navigation/                  # Sidebar/navbar item definitions
│   ├── vertical/
│   │   ├── index.ts             # Exports all vertical nav items
│   │   ├── dashboard.ts         # Dashboard nav section
│   │   └── apps-and-pages.ts    # Apps & pages nav section
│   └── horizontal/
│       └── index.ts
│
├── composables/
│   └── useApi.ts                # HTTP client (createFetch + Bearer auth + JSON parsing)
│
└── utils/                       # App-level utilities

resources/styles/                # SCSS stylesheets
├── @core/
│   ├── base/                    # Global styles, Vuetify component overrides, skins
│   └── template/               # Template-specific styles, page styles
├── variables/
│   └── _vuetify.scss            # Vuetify SCSS variable overrides
└── styles.scss                  # Main stylesheet

themeConfig.ts                   # Global theme config (title, logo, layout, navbar, footer, i18n)
vite.config.ts                   # Vite build config with 10 plugins
tsconfig.json                    # Strict TypeScript with path aliases
```

## Color System

### Primary Colors
| Color     | Light       | Dark        | Usage                    |
|-----------|-------------|-------------|--------------------------|
| primary   | `#7367F0`   | `#7367F0`   | Buttons, links, accents  |
| secondary | `#808390`   | `#808390`   | Secondary actions        |
| success   | `#28C76F`   | `#28C76F`   | Success states, paid     |
| info      | `#00BAD1`   | `#00BAD1`   | Info badges              |
| warning   | `#FF9F43`   | `#FF9F43`   | Warnings, pending        |
| error     | `#FF4C51`   | `#FF4C51`   | Errors, overdue          |

### Background Colors
| Surface       | Light       | Dark        |
|---------------|-------------|-------------|
| background    | `#F8F7FA`   | `#25293C`   |
| surface       | `#FFFFFF`   | `#2F3349`   |
| on-background | `#2F2B3D`   | `#E1DEF5`   |
| on-surface    | `#2F2B3D`   | `#E1DEF5`   |

### Skins
- **Default** — Standard background colors
- **Bordered** — Flat backgrounds with visible borders on cards

## Vuetify Component Defaults

All components have pre-configured defaults (see `plugins/vuetify/defaults.ts`):

```
VBtn        → color: 'primary'
VTextField  → variant: 'outlined', density: 'comfortable', color: 'primary', hideDetails: 'auto'
VSelect     → variant: 'outlined', density: 'comfortable', color: 'primary', hideDetails: 'auto'
VCheckbox   → color: 'primary', density: 'comfortable', hideDetails: 'auto'
VChip       → label: true (square corners)
VAvatar     → variant: 'flat'
VPagination → density: 'comfortable', variant: 'tonal'
```

## Component Patterns

### Vue Component Structure
```vue
<script lang="ts" setup>
// 1. Imports
import { computed, ref, watch } from 'vue'

// 2. Type definitions
interface Props {
  title: string
  color?: string
  items: Item[]
}

interface Item {
  id: number
  name: string
  status: 'active' | 'inactive'
}

// 3. Props & Emits
const props = withDefaults(defineProps<Props>(), {
  color: 'primary',
})

const emit = defineEmits<{
  'update:modelValue': [value: string]
  'submit': []
}>()

// 4. State
const isLoading = ref(false)
const searchQuery = ref('')

// 5. Computed
const filteredItems = computed(() =>
  props.items.filter(item => item.name.includes(searchQuery.value))
)

// 6. Methods
const handleSubmit = async () => {
  isLoading.value = true
  try {
    await $api('/endpoint', { method: 'POST' })
  } finally {
    isLoading.value = false
  }
}

// 7. Watchers
watch(() => props.title, (newVal) => { /* react */ })

// 8. Lifecycle
onMounted(() => { /* init */ })
</script>

<template>
  <!-- Template -->
</template>

<style lang="scss" scoped>
/* Component styles */
</style>
```

### Stat Card (Horizontal)
```vue
<CardStatisticsHorizontal
  title="Total Revenue"
  stats="$42,389"
  icon="tabler-currency-dollar"
  color="success"
/>
```

### Stat Card (Vertical with Chart)
```vue
<CardStatisticsVertical
  title="Sales"
  stats="$4,679"
  icon="tabler-chart-bar"
  color="primary"
  :height="80"
  :series="[{ data: [40, 20, 65, 50, 30, 60] }]"
  :chart-options="chartOptions"
/>
```

### Dashboard Page Layout
```vue
<template>
  <VRow class="match-height">
    <!-- Full width chart -->
    <VCol cols="12" md="6">
      <WidgetChart />
    </VCol>

    <!-- Two metric cards side by side -->
    <VCol cols="12" md="3" sm="6">
      <CardStatisticsHorizontal title="Revenue" stats="$42k" icon="tabler-chart-bar" color="success" />
    </VCol>
    <VCol cols="12" md="3" sm="6">
      <CardStatisticsHorizontal title="Customers" stats="1,234" icon="tabler-users" color="primary" />
    </VCol>

    <!-- Data table -->
    <VCol cols="12">
      <RecentOrdersTable />
    </VCol>
  </VRow>
</template>
```

### Data Table (Server-Side Pagination)
```vue
<script lang="ts" setup>
const searchQuery = ref('')
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref<string>()
const orderBy = ref<string>()

const headers = [
  { title: '#', key: 'id' },
  { title: 'Client', key: 'client' },
  { title: 'Status', key: 'status', sortable: false },
  { title: 'Total', key: 'total' },
  { title: 'Actions', key: 'actions', sortable: false },
]

const { data } = await useApi<any>(createUrl('/invoices', {
  query: { q: searchQuery, itemsPerPage, page, sortBy, orderBy },
}))

const items = computed(() => data.value?.items ?? [])
const totalItems = computed(() => data.value?.total ?? 0)

const updateOptions = (options: any) => {
  sortBy.value = options.sortBy[0]?.key
  orderBy.value = options.sortBy[0]?.order
}
</script>

<template>
  <VCard>
    <VCardText class="d-flex justify-space-between align-center flex-wrap gap-4">
      <AppTextField v-model="searchQuery" placeholder="Search..." style="inline-size: 250px;" />
    </VCardText>

    <VDataTableServer
      v-model:items-per-page="itemsPerPage"
      v-model:page="page"
      :items="items"
      :items-length="totalItems"
      :headers="headers"
      class="text-no-wrap"
      @update:options="updateOptions"
    >
      <template #item.status="{ item }">
        <VChip :color="resolveStatusColor(item.status)" size="small">
          {{ item.status }}
        </VChip>
      </template>

      <template #item.actions="{ item }">
        <IconBtn size="small">
          <VIcon icon="tabler-eye" />
        </IconBtn>
        <IconBtn size="small">
          <VIcon icon="tabler-pencil" />
        </IconBtn>
        <IconBtn size="small" color="error">
          <VIcon icon="tabler-trash" />
        </IconBtn>
      </template>
    </VDataTableServer>
  </VCard>
</template>
```

### Form with Validation
```vue
<script lang="ts" setup>
import { VForm } from 'vuetify/components/VForm'
import { emailValidator, requiredValidator } from '@validators'

const refForm = ref<VForm>()
const formData = ref({
  email: '',
  password: '',
  name: '',
})

const onSubmit = () => {
  refForm.value?.validate().then(({ valid }) => {
    if (valid) {
      // Submit form
    }
  })
}
</script>

<template>
  <VForm ref="refForm" @submit.prevent="onSubmit">
    <VRow>
      <VCol cols="12" md="6">
        <AppTextField
          v-model="formData.name"
          label="Name"
          :rules="[requiredValidator]"
        />
      </VCol>
      <VCol cols="12" md="6">
        <AppTextField
          v-model="formData.email"
          label="Email"
          type="email"
          :rules="[requiredValidator, emailValidator]"
        />
      </VCol>
      <VCol cols="12">
        <VBtn type="submit" color="primary">
          Submit
        </VBtn>
      </VCol>
    </VRow>
  </VForm>
</template>
```

### Status Resolver Pattern
```typescript
const resolveStatusVariant = (status: string): { color: string; icon: string } => {
  const map: Record<string, { color: string; icon: string }> = {
    active: { color: 'success', icon: 'tabler-check' },
    pending: { color: 'warning', icon: 'tabler-clock' },
    suspended: { color: 'error', icon: 'tabler-ban' },
    canceled: { color: 'secondary', icon: 'tabler-x' },
  }
  return map[status] ?? { color: 'secondary', icon: 'tabler-circle' }
}
```

## Navigation Item Types

```typescript
// Single clickable link
interface NavLink {
  title: string
  to?: string | RouteLocationRaw   // Route name or object
  href?: string                     // External URL
  icon?: { icon: string }           // Tabler icon
  badgeContent?: string             // Badge text
  badgeClass?: string               // Badge CSS class (e.g., 'bg-error')
  target?: '_blank' | '_self'
  action?: string                   // CASL action
  subject?: string                  // CASL subject
  disable?: boolean
}

// Collapsible group with children
interface NavGroup {
  title: string
  icon?: { icon: string }
  children: (NavLink | NavGroup)[]
  badgeContent?: string
  badgeClass?: string
  action?: string
  subject?: string
  disable?: boolean
}

// Section heading/divider
interface NavSectionTitle {
  heading: string
  action?: string
  subject?: string
}
```

### Navigation Example
```typescript
// navigation/vertical/dashboard.ts
export default [
  {
    title: 'Dashboards',
    icon: { icon: 'tabler-smart-home' },
    children: [
      { title: 'Analytics', to: 'dashboards-analytics' },
      { title: 'CRM', to: 'dashboards-crm' },
    ],
    badgeContent: '2',
    badgeClass: 'bg-error',
  },
]

// navigation/vertical/apps.ts
export default [
  { heading: 'Apps & Pages' },   // Section title
  {
    title: 'Invoice',
    icon: { icon: 'tabler-file-invoice' },
    children: [
      { title: 'List', to: 'apps-invoice-list' },
      { title: 'Add', to: 'apps-invoice-add' },
    ],
  },
  {
    title: 'Users',
    icon: { icon: 'tabler-users' },
    to: 'apps-users-list',
    action: 'read',              // CASL permission
    subject: 'User',
  },
]
```

## API Integration

### useApi Composable
```typescript
// Reactive fetch with Bearer token from cookie
const { data, execute: refetch } = await useApi<ResponseType>(
  createUrl('/endpoint', {
    query: { q: searchQuery, page, itemsPerPage, status: selectedStatus },
  })
)

// Non-reactive one-off request
const response = await $api('/endpoint', {
  method: 'POST',
  body: { key: 'value' },
  onResponseError({ response }) {
    errors.value = response._data.errors
  },
})

// Delete with refetch
const deleteItem = async (id: number) => {
  await $api(`/items/${id}`, { method: 'DELETE' })
  refetch()
}
```

## Validators

Available at `@validators` (auto-imported):

| Validator              | Usage                                     |
|------------------------|-------------------------------------------|
| `requiredValidator`    | Field is not empty                        |
| `emailValidator`       | Valid email format                         |
| `passwordValidator`    | Min 8 chars, upper, lower, digit, special |
| `confirmedValidator`   | Matches another field value               |
| `betweenValidator`     | Number within min/max range               |
| `urlValidator`         | Valid URL format                           |
| `integerValidator`     | Integer only                              |
| `regexValidator`       | Matches regex pattern                     |
| `alphaValidator`       | Alpha characters only                     |
| `lengthValidator`      | Exact string length                       |

## Layout Selection

Pages use `definePage()` macro for meta configuration:

```vue
<script lang="ts" setup>
// Use blank layout (no sidebar/navbar) — for auth pages
definePage({
  meta: {
    layout: 'blank',
    unauthenticatedOnly: true,
  },
})
</script>
```

Default layout (with sidebar + navbar) is used when no layout is specified.

## Route Guards

Authentication flow (`plugins/1.router/guards.ts`):

1. **Public routes** (`meta.public: true`) — No auth check
2. **Unauthenticated-only** (`meta.unauthenticatedOnly: true`) — Redirect to `/` if logged in
3. **CASL permission check** — If `canNavigate(to)` fails:
   - Logged in → redirect to `/not-authorized`
   - Not logged in → redirect to `/login?to=<path>`

## Auto-Imports (No Explicit Import Needed)

### From Vue
`ref`, `computed`, `watch`, `watchEffect`, `onMounted`, `onUnmounted`, `h`, `nextTick`, `defineProps`, `defineEmits`, `withDefaults`, `defineAsyncComponent`

### From Vue Router
`useRouter()`, `useRoute()`

### From VueUse
`useWindowSize()`, `useElementHover()`, `useMediaQuery()`, `useToggle()`, `useStorage()`, `usePreferredColorScheme()`, `useWindowScroll()`

### From Pinia
`defineStore()`, `storeToRefs()`

### From Vue-i18n
`useI18n()`

### From Theme
`useSkins()`, `useCookie()`, `createUrl()`, validators, helpers

## Icons

Uses **Tabler Icons** via Iconify. Common icons:

| Icon Name                | Usage              |
|--------------------------|--------------------|
| `tabler-smart-home`      | Dashboard          |
| `tabler-users`           | Users/customers    |
| `tabler-file-invoice`    | Invoices           |
| `tabler-shopping-cart`   | Orders/commerce    |
| `tabler-chart-bar`       | Analytics/charts   |
| `tabler-currency-dollar` | Revenue/billing    |
| `tabler-server`          | Servers/services   |
| `tabler-shield-check`    | Security           |
| `tabler-settings`        | Settings           |
| `tabler-bell`            | Notifications      |
| `tabler-check`           | Success/confirmed  |
| `tabler-x`               | Close/cancel       |
| `tabler-pencil`          | Edit               |
| `tabler-trash`           | Delete             |
| `tabler-eye`             | View/preview       |
| `tabler-plus`            | Add/create         |
| `tabler-chevron-down`    | Dropdown           |
| `tabler-chevron-right`   | Navigate           |
| `tabler-menu-2`          | Mobile menu toggle |
| `tabler-circle`          | Default nav icon   |

Usage: `<VIcon icon="tabler-smart-home" size="26" />`

## TypeScript Path Aliases

```
@/*          → resources/ts/*
@themeConfig → themeConfig.ts
@layouts/*   → resources/ts/@layouts/*
@core/*      → resources/ts/@core/*
@images/*    → resources/images/*
@styles/*    → resources/styles/*
@validators  → resources/ts/@core/utils/validators
```

## Key Files to Reference

| Need                          | Look at                                              |
|-------------------------------|------------------------------------------------------|
| Dashboard layout              | `pages/dashboards/analytics.vue`                     |
| Data table with filters       | `pages/apps/invoice/list/index.vue`                  |
| Form with validation          | `pages/apps/invoice/add/index.vue`                   |
| Auth page (login)             | `pages/login.vue`                                    |
| Stat cards                    | `@core/components/cards/CardStatistics*.vue`          |
| Sidebar navigation            | `layouts/components/DefaultLayoutWithVerticalNav.vue` |
| Nav item definitions          | `navigation/vertical/dashboard.ts`                   |
| Theme colors                  | `plugins/vuetify/theme.ts`                           |
| Component defaults            | `plugins/vuetify/defaults.ts`                        |
| API fetching                  | `composables/useApi.ts`                              |
| Form validators               | `@core/utils/validators.ts`                          |
| Type definitions              | `@core/types.ts`, `views/apps/invoice/types.ts`      |
| Route guards                  | `plugins/1.router/guards.ts`                         |
| Theme config                  | `themeConfig.ts`                                     |
| Blank layout (auth)           | `layouts/blank.vue`                                  |
| Main layout                   | `layouts/default.vue`                                |
