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>
6.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
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
# Development
php artisan serve && npm run dev # Start both servers
# Testing
php artisan test # Run all tests
php artisan test --filter=ClassName # Run specific test class
php artisan test --filter=test_method_name # Run specific test method
php artisan dusk # Run browser tests
# Database
php artisan migrate:fresh --seed # Reset with all seeders
php artisan db:seed --class=TestDataSeeder # Seed test data only
php artisan db:seed --class=FinancialWorkflowTestDataSeeder # Finance test data
# Code Quality
./vendor/bin/pint # Fix code style (PSR-12)
./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:
Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(...)
- Public:
/register/member,/documents - Member (auth only): dashboard, payment submission
- Admin (auth + admin):
/admin/*withadmin.{module}.{action}named routes
The admin middleware (EnsureUserIsAdmin) allows access if user has admin role OR any permissions at all.
Multi-Tier Approval Workflows
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 with separate status fields:
- Approval Stage (
status): Multi-tier approval based on amount - Disbursement Stage (
disbursement_status): Dual confirmation (requester + cashier) - Recording Stage (
recording_status): Accountant records to ledger
$doc->isApprovalComplete() // All required approvals obtained
$doc->isDisbursementComplete() // Both parties confirmed
$doc->isRecordingComplete() // Ledger entry created
$doc->isFullyProcessed() // All 3 stages complete
Shared Traits
HasApprovalWorkflow: Multi-tier approval with self-approval prevention (isSelfApproval())HasAccountingEntries: Double-entry bookkeeping, auto-generation, balance validation
Service Layer
Complex business logic in app/Services/:
MembershipFeeCalculator: Fees with disability discount supportSettingsService: System-wide settings with cachingFinanceDocumentApprovalService: Multi-tier approval orchestrationPaymentVerificationService: 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), virtual accessornational_iddecrypts 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
Identity types: patient, parent, social, other (病友/家長分類). Members can have guardian_member_id for parent-child relationships.
$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 assignmentimport:members/import:accounting-data/import:documents— Bulk importssend:membership-expiry-reminders— Automated email notificationsarchive:expired-documents— Document lifecycle cleanup
Testing Patterns
Tests use RefreshDatabase trait. Setup commonly includes:
protected function setUp(): void
{
parent::setUp();
$this->artisan('db:seed', ['--class' => 'RoleSeeder']);
}
Test accounts (password: password):
admin@test.com- Full accessrequester@test.com- Submit documentscashier@test.com- Tier 1 approvalaccountant@test.com- Tier 2 approvalchair@test.com- Tier 3 approval
Key Files
routes/web.php— All web routes (admin routes under/adminprefix)config/accounting.php— Account codes, amount tier thresholds, currency settingsapp/Models/FinanceDocument.php— Core financial workflow logic with 27+ status constantsapp/Models/Member.php— Member lifecycle with encrypted field handlingapp/Traits/HasApprovalWorkflow.php— Shared multi-tier approval behaviorapp/Traits/HasAccountingEntries.php— Double-entry bookkeeping behaviorapp/helpers.php— Globalsettings()helper (autoloaded via composer)database/seeders/— RoleSeeder, ChartOfAccountSeeder, TestDataSeederdocs/SYSTEM_SPECIFICATION.md— Complete system specification