diff --git a/CLAUDE.md b/CLAUDE.md index 4278f55..50f6b20 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,33 +3,59 @@ ## Project EZSCALE Site — Laravel 12 application replacing WHMCS for VPS/Dedicated Server hosting management (billing, subscriptions, provisioning, customer management, SSO). +## Phase Tracking (MANDATORY) +All work MUST be tracked against GitHub issues and `TASKS.md`: + +1. **Before starting any phase work:** Check the relevant GitHub issue (see `gh issue list`) and `TASKS.md` for current status. +2. **While working:** Update the GitHub issue with progress comments as significant milestones are completed (e.g., `gh issue comment --body "Completed X"`). +3. **After completing work:** Update `TASKS.md` to check off completed items and close/update the corresponding GitHub issue. +4. **New tasks discovered during work:** Create sub-issues or add items to `TASKS.md` immediately. +5. **Commit messages:** Reference the GitHub issue number (e.g., `Fix checkout validation (#3)`). + +GitHub issues: https://github.com/EZSCALE/accounting/issues + ## Laravel App Location -The Laravel application is in **`website/`**. All artisan, composer, and npm commands run from there. This is currently a **base Laravel 12 install** — no additional packages or custom code added yet. +The Laravel application is in **`website/`**. All artisan, composer, and npm commands run from there. ``` -website/ # Laravel 12 base install -├── app/ # Models, Http (Controllers), Providers -├── bootstrap/app.php # Middleware, exceptions, routing (Laravel 12 style) -├── config/ # Standard Laravel config files -├── database/migrations/ # Default user, cache, jobs migrations only -├── resources/ # Views, JS, CSS -├── routes/ # web.php, console.php -├── tests/ # Pest 4 test suite -├── composer.json # PHP 8.2+, Laravel 12, Pest 4, Pint -├── package.json # Vite 7, Tailwind CSS 4, Axios +website/ +├── app/ +│ ├── Models/ # 14 Eloquent models +│ ├── Http/Controllers/ # Account/ and Admin/ controllers +│ ├── Services/Billing/ # BillingServiceInterface, Stripe, PayPal, Dunning +│ ├── Events/ # PaymentSucceeded/Failed, SubscriptionCreated/Cancelled +│ ├── Listeners/ # HandlePaymentSucceeded/Failed +│ └── Providers/ # AppServiceProvider, FortifyServiceProvider +├── bootstrap/app.php # Middleware, exceptions, routing (Laravel 12 style) +├── config/ # App, auth, fortify, passport, cashier, paypal, permission +├── database/ +│ ├── migrations/ # 30 migrations (15 custom + defaults + packages) +│ ├── factories/ # 7 factories +│ └── seeders/ # Roles, plans, admin user +├── resources/ +│ ├── js/ # Vue 3 + Inertia pages (TO BE MIGRATED TO TypeScript) +│ │ ├── Layouts/ # AppLayout, AuthLayout, AdminLayout +│ │ ├── Pages/ # Auth/, Billing/, Plans/, Subscriptions/, Admin/ +│ │ └── Components/ # Card, Button, NavLink, FlashMessages +│ ├── css/app.css # Tailwind CSS 4 +│ └── views/app.blade.php # Inertia root template +├── routes/ # web.php, account.php, admin.php, marketing.php, webhooks.php, api.php +├── tests/ # 53 Pest tests (Phase 1 + Phase 2) +├── composer.json +├── package.json └── vite.config.js ``` ## Tech Stack -- **Framework:** Laravel 12 (PHP 8.2+), Laravel 12 slim structure (no Kernel files) -- **Frontend:** Tailwind CSS 4 + Vite 7 (Vue 3 + Inertia.js to be added) -- **UI Theme:** Vuexy VueJS + Laravel Admin Dashboard Template (to be integrated) +- **Framework:** Laravel 12 (PHP 8.3), Laravel 12 slim structure (no Kernel files) +- **Frontend:** Vue 3 + Inertia.js v2 + TypeScript (REQUIRED) + Tailwind CSS 4 + Vite 7 +- **UI Theme:** Vuexy Vue + Laravel Admin Dashboard (reference at `../vuexy-theme-vue-laravel-full-example-typescript/`) - **Testing:** Pest 4 + PHPUnit 12 - **Formatting:** Laravel Pint -- **Payments:** Laravel Cashier Stripe + srmklive/laravel-paypal (to be installed) +- **Payments:** Laravel Cashier (Stripe) + srmklive/paypal (PayPal) - **Database:** MySQL 8.x, Redis for cache/queue/sessions -- **Auth:** Laravel Fortify + Passport (to be installed) -- **Roles:** spatie/laravel-permission (to be installed) +- **Auth:** Laravel Fortify (login, register, 2FA, password reset, email verify) + Passport (OAuth2/SSO) +- **Roles:** spatie/laravel-permission (admin, customer) ## Commands ```bash @@ -40,10 +66,85 @@ php artisan test --compact # Run Pest tests php artisan migrate # Run migrations npm run dev # Vite dev server only npm run build # Production build -vendor/bin/pint --dirty # Format changed files +vendor/bin/pint --dirty --format agent # Format changed files +``` + +## TypeScript Requirement (MANDATORY) +**All frontend code MUST use TypeScript.** This applies to all Vue components, composables, utilities, and type definitions. + +### Rules +- All `.vue` files must use ` +``` + +### Migration Note +Existing `.vue` files in `resources/js/` were written in plain JavaScript during Phase 1-2. They must be migrated to TypeScript as they are touched. When working on a page, convert it to TypeScript as part of the work. + +## Vuexy Theme Reference +The Vuexy theme is at `../vuexy-theme-vue-laravel-full-example-typescript/`. Use it as a **reference** for UI patterns, component design, and styling conventions. + +### Key Vuexy Patterns to Follow +- **Component structure:** ` + + + + +``` + +### Stat Card (Horizontal) +```vue + +``` + +### Stat Card (Vertical with Chart) +```vue + +``` + +### Dashboard Page Layout +```vue + +``` + +### Data Table (Server-Side Pagination) +```vue + + + +``` + +### Form with Validation +```vue + + + +``` + +### Status Resolver Pattern +```typescript +const resolveStatusVariant = (status: string): { color: string; icon: string } => { + const map: Record = { + 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( + 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 + +``` + +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=` + +## 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: `` + +## 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` |