Add headless CMS for official site content management
Integrate article and page management into the Laravel admin dashboard
to serve as a headless CMS for the Next.js frontend (usher-site).
Backend:
- 7 migrations: article_categories, article_tags, articles, pivots, attachments, pages
- 5 models with relationships: Article, ArticleCategory, ArticleTag, ArticleAttachment, Page
- 4 admin controllers: articles (with publish/archive/pin), categories, tags, pages
- Admin views with EasyMDE markdown editor, multi-select categories/tags
- Navigation section "官網管理" in admin sidebar
API (v1):
- GET /api/v1/articles (filtered by type, category, tag, search; paginated)
- GET /api/v1/articles/{slug} (with related articles)
- GET /api/v1/categories
- GET /api/v1/pages/{slug} (with children)
- GET /api/v1/homepage (aggregated homepage data)
- Attachment download endpoint
- CORS configured for usher.org.tw, vercel.app, localhost:3000
Content migration:
- ImportHugoContent command: imports Hugo markdown files as articles/pages
- Successfully imported 27 articles, 17 categories, 11 tags, 9 pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
98
CLAUDE.md
98
CLAUDE.md
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
Taiwan NPO (Non-Profit Organization) management platform built with Laravel 11. Features member lifecycle management, multi-tier financial approval workflows, issue tracking, and document management with version control.
|
||||
Taiwan NPO (Non-Profit Organization) management platform built with Laravel 10 (PHP 8.1+). Features member lifecycle management, multi-tier financial approval workflows, issue tracking, and document management. UI is in Traditional Chinese. Currency is TWD (NT$).
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -28,21 +28,40 @@ php artisan db:seed --class=FinancialWorkflowTestDataSeeder # Finance test data
|
||||
./vendor/bin/phpstan analyse # Static analysis
|
||||
```
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Backend**: Laravel 10, PHP 8.1+, Spatie Laravel Permission
|
||||
- **Frontend**: Blade templates, Tailwind CSS 3.1 (`darkMode: 'class'`), Alpine.js 3.4, Vite 5
|
||||
- **PDF/Excel**: barryvdh/laravel-dompdf, maatwebsite/excel
|
||||
- **QR Codes**: simplesoftwareio/simple-qrcode
|
||||
- **DB**: SQLite (dev), MySQL (production)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Route Structure
|
||||
|
||||
Routes split across `routes/web.php` and `routes/auth.php`. Admin routes use group pattern:
|
||||
```php
|
||||
Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(...)
|
||||
```
|
||||
- **Public**: `/register/member`, `/documents`
|
||||
- **Member** (auth only): dashboard, payment submission
|
||||
- **Admin** (auth + admin): `/admin/*` with `admin.{module}.{action}` named routes
|
||||
|
||||
The `admin` middleware (`EnsureUserIsAdmin`) allows access if user has `admin` role OR any permissions at all.
|
||||
|
||||
### Multi-Tier Approval Workflows
|
||||
|
||||
The system uses tiered approval based on amount thresholds (configurable):
|
||||
Tiered approval based on amount thresholds (configurable in `config/accounting.php`):
|
||||
- **Small (<5,000)**: Secretary approval only
|
||||
- **Medium (5,000-50,000)**: Secretary → Chair
|
||||
- **Large (>50,000)**: Secretary → Chair → Board
|
||||
|
||||
Finance documents follow a 3-stage lifecycle:
|
||||
1. **Approval Stage**: Multi-tier approval based on amount
|
||||
2. **Disbursement Stage**: Dual confirmation (requester + cashier)
|
||||
3. **Recording Stage**: Accountant records to ledger
|
||||
Finance documents follow a 3-stage lifecycle with separate status fields:
|
||||
1. **Approval Stage** (`status`): Multi-tier approval based on amount
|
||||
2. **Disbursement Stage** (`disbursement_status`): Dual confirmation (requester + cashier)
|
||||
3. **Recording Stage** (`recording_status`): Accountant records to ledger
|
||||
|
||||
Key model methods for workflow state:
|
||||
```php
|
||||
$doc->isApprovalComplete() // All required approvals obtained
|
||||
$doc->isDisbursementComplete() // Both parties confirmed
|
||||
@@ -50,40 +69,61 @@ $doc->isRecordingComplete() // Ledger entry created
|
||||
$doc->isFullyProcessed() // All 3 stages complete
|
||||
```
|
||||
|
||||
### RBAC Structure
|
||||
### Shared Traits
|
||||
|
||||
Uses Spatie Laravel Permission. Core financial roles:
|
||||
- `finance_requester`: Submit finance documents
|
||||
- `finance_cashier`: Tier 1 approval, payment execution
|
||||
- `finance_accountant`: Tier 2 approval, create payment orders
|
||||
- `finance_chair`: Tier 3 approval
|
||||
- `finance_board_member`: Large amount approval
|
||||
|
||||
Permission checks: `$user->can('permission-name')` or `@can('permission-name')` in Blade.
|
||||
- **`HasApprovalWorkflow`**: Multi-tier approval with self-approval prevention (`isSelfApproval()`)
|
||||
- **`HasAccountingEntries`**: Double-entry bookkeeping, auto-generation, balance validation
|
||||
|
||||
### Service Layer
|
||||
|
||||
Complex business logic lives in `app/Services/`:
|
||||
- `MembershipFeeCalculator`: Calculates fees with disability discount support
|
||||
Complex business logic in `app/Services/`:
|
||||
- `MembershipFeeCalculator`: Fees with disability discount support
|
||||
- `SettingsService`: System-wide settings with caching
|
||||
- `FinanceDocumentApprovalService`: Multi-tier approval orchestration
|
||||
- `PaymentVerificationService`: Payment workflow coordination
|
||||
|
||||
Global helper: `settings('key')` returns cached setting values (defined in `app/helpers.php`, autoloaded).
|
||||
|
||||
### RBAC Structure
|
||||
|
||||
Uses Spatie Laravel Permission. Core financial roles:
|
||||
- `finance_requester`, `finance_cashier`, `finance_accountant`, `finance_chair`, `finance_board_member`
|
||||
|
||||
Permission checks: `$user->can('permission-name')` or `@can('permission-name')` in Blade.
|
||||
|
||||
### Data Security Patterns
|
||||
|
||||
- **National ID**: AES-256 encrypted (`national_id_encrypted`), SHA256 hashed for search (`national_id_hash`)
|
||||
- **File uploads**: Private disk storage, served via authenticated controller methods
|
||||
- **Audit logging**: All significant actions logged to `audit_logs` table
|
||||
- **National ID**: AES-256 encrypted (`national_id_encrypted`), SHA256 hashed for search (`national_id_hash`), virtual accessor `national_id` decrypts on read
|
||||
- **File uploads**: Private disk storage, served via authenticated controller
|
||||
- **Audit logging**: `AuditLogger::log($action, $auditable, $metadata)` — static class, call in controllers
|
||||
|
||||
### Member Lifecycle
|
||||
|
||||
States: `pending` → `active` → `expired` / `suspended`
|
||||
|
||||
Key model methods:
|
||||
Identity types: `patient`, `parent`, `social`, `other` (病友/家長分類). Members can have `guardian_member_id` for parent-child relationships.
|
||||
|
||||
```php
|
||||
$member->hasPaidMembership() // Active with future expiry
|
||||
$member->canSubmitPayment() // Pending with no pending payment
|
||||
$member->getNextFeeType() // entrance_fee or annual_fee
|
||||
```
|
||||
|
||||
### Conventions
|
||||
|
||||
- **Status values**: Defined as class constants (e.g., `FinanceDocument::STATUS_PENDING`), not enums or magic strings
|
||||
- **Controller validation**: Uses Form Request classes (`StoreMemberRequest`, etc.)
|
||||
- **DB transactions**: Used in controllers for multi-step operations
|
||||
- **UI text**: Hardcoded Traditional Chinese strings (no `__()` translation layer)
|
||||
- **Dark mode**: Supported throughout — use `dark:` Tailwind prefix for styles
|
||||
|
||||
### Custom Artisan Commands
|
||||
|
||||
- `assign:role` — Manual role assignment
|
||||
- `import:members` / `import:accounting-data` / `import:documents` — Bulk imports
|
||||
- `send:membership-expiry-reminders` — Automated email notifications
|
||||
- `archive:expired-documents` — Document lifecycle cleanup
|
||||
|
||||
### Testing Patterns
|
||||
|
||||
Tests use `RefreshDatabase` trait. Setup commonly includes:
|
||||
@@ -104,8 +144,12 @@ Test accounts (password: `password`):
|
||||
|
||||
## Key Files
|
||||
|
||||
- `routes/web.php` - All web routes (admin routes under `/admin` prefix)
|
||||
- `app/Models/FinanceDocument.php` - Core financial workflow logic with status constants
|
||||
- `app/Models/Member.php` - Member lifecycle with encrypted field handling
|
||||
- `database/seeders/` - RoleSeeder, ChartOfAccountSeeder, TestDataSeeder
|
||||
- `docs/SYSTEM_SPECIFICATION.md` - Complete system specification
|
||||
- `routes/web.php` — All web routes (admin routes under `/admin` prefix)
|
||||
- `config/accounting.php` — Account codes, amount tier thresholds, currency settings
|
||||
- `app/Models/FinanceDocument.php` — Core financial workflow logic with 27+ status constants
|
||||
- `app/Models/Member.php` — Member lifecycle with encrypted field handling
|
||||
- `app/Traits/HasApprovalWorkflow.php` — Shared multi-tier approval behavior
|
||||
- `app/Traits/HasAccountingEntries.php` — Double-entry bookkeeping behavior
|
||||
- `app/helpers.php` — Global `settings()` helper (autoloaded via composer)
|
||||
- `database/seeders/` — RoleSeeder, ChartOfAccountSeeder, TestDataSeeder
|
||||
- `docs/SYSTEM_SPECIFICATION.md` — Complete system specification
|
||||
|
||||
Reference in New Issue
Block a user