Development Log
This is a real-time record of the project's construction, synced from the root
BUILD_PROGRESS.md.
Ekklesia CMS — Build Progress Report
Auto-updated as each phase and session completes. Repo: https://github.com/KwasiEzor/ekklesia-cms
Phase Summary
| Phase | Description | Status | Date |
|---|---|---|---|
| 0 | Architecture Completion | Done | 2026-02-25 |
| 1 | Project Scaffold | Done | 2026-02-25 |
| 2.1 | Sermon Content Type | Done | 2026-02-25 |
| 2.2 | Event Content Type | Done | 2026-02-25 |
| — | Laravel Reverb Integration | Done | 2026-02-25 |
| 2.3 | Announcement Content Type | Done | 2026-02-25 |
| 2.4 | Member & Gallery Content Types | Done | 2026-02-28 |
| 2.5 | Page Content Type (block builder) | Done | 2026-02-28 |
| 2.6 | GivingRecord Content Type | Done | 2026-02-28 |
| 2.5H | Hardening Sprint | Done | 2026-03-01 |
| 3 | API Layer | Done | 2026-03-01 |
| 4 | First Deployment (prep) | In Progress | 2026-03-02 |
| — | UI Refonte — Premium Admin | Done | 2026-03-03 |
| 5 | AI Layer | Done | 2026-03-03 |
| 6 | Premium Modules | Pending | — |
Phase 0 — Architecture Completion
Commit: b3abd47 — docs: resolve content versioning — soft versioning with previous_version JSONBDate: 2026-02-25
Decisions Made
Content Versioning — DECIDED: Soft versioning
- Each content table gets a nullable
previous_version JSONBcolumn App\Concerns\HasSoftVersioningEloquent trait snapshots dirty fields onupdatingevent- One-level undo in v1; upgrade path to full revision tables remains open
- Rationale: church staff are non-technical — accidental saves must be recoverable without the complexity of full revision tables
Plugin Architecture — still OPEN (deferred, not blocking Phase 1)
Files Changed
docs/architecture/decisions.md— added Content Versioning DECIDED sectiondocs/architecture/open-questions.md— marked versioning as resolveddocs/guide/changelog.md— added decision entryCLAUDE.md— checked off versioning, fixed Livewire ^3.0 → ^4.0
Phase 1 — Project Scaffold
Commit: 9df01e3 — feat: Laravel 12 scaffold with Filament v5, stancl/tenancy, PostgreSQLDate: 2026-02-25
What Was Built
| Component | Detail |
|---|---|
| Framework | Laravel 12 (v12.53.0) |
| Admin UI | Filament v5 with Livewire v4 |
| Multi-tenancy | stancl/tenancy v3 — single-database mode, tenant_id column scoping |
| Database | PostgreSQL 16 — databases: ekklesia (dev), ekklesia_test (test) |
| Media | spatie/laravel-medialibrary v11 |
| i18n | spatie/laravel-translatable v6 |
| API Auth | Laravel Sanctum v4 |
| Performance | Laravel Octane v2 (FrankenPHP) |
| Testing | Pest PHP v3 with pest-plugin-laravel |
Key Configuration
- Locale: French primary (
APP_LOCALE=fr), English fallback - Tenant resolution: Filament panel uses
->tenant(Tenant::class, slugAttribute: 'slug') - DatabaseTenancyBootstrapper: Disabled (single-DB mode — scoping via
BelongsToTenanttrait) - DB creation/deletion jobs: Removed from TenancyServiceProvider
- VitePress docs: Isolated to
docs/package.jsonwith ownnode_modules - GitHub Actions: Updated to
working-directory: docs; legacy Jekyll workflow deleted
Models Created
App\Models\Tenant— HasDatabase, HasDomains, HasFactory; custom columns: id, name, slugApp\Models\User— BelongsToTenant, HasApiTokens; tenant_id foreign key + unique email per tenant
Traits Created
App\Concerns\HasSoftVersioning— snapshots changed fields intoprevious_versionJSONB on update;revertToPreviousVersion()restores and clears
Migrations
| Migration | Tables |
|---|---|
0001_01_00_000000 | tenants (id, name, slug, data, timestamps) |
0001_01_00_000001 | domains (domain, tenant_id FK) |
0001_01_01_000000 | users (tenant_id FK, unique email per tenant), password_reset_tokens, sessions |
0001_01_01_000001 | cache, cache_locks |
0001_01_01_000002 | jobs, job_batches, failed_jobs |
2026_02_25_123804 | media (spatie/medialibrary) |
2026_02_25_124148 | personal_access_tokens (Sanctum) |
Translation Files
lang/fr/navigation.php— sidebar labels in Frenchlang/fr/common.php— CRUD action labels in Frenchlang/en/navigation.php— English fallbacklang/en/common.php— English fallback
Phase 2, Session 1 — Sermon Content Type
Commit: 874fde7 — feat: Sermon content type — model, migration, Filament resource, API, testsDate: 2026-02-25
Models
App\Models\Sermon— BelongsToTenant, HasSoftVersioning, HasFactory- Casts:
date,duration(int),tags(array),custom_fields(array),previous_version(array) - Hidden:
tenant_id - Accessor:
formatted_duration— converts seconds toH:MM:SSorM:SS - Relationship:
belongsTo(SermonSeries::class)
- Casts:
App\Models\SermonSeries— BelongsToTenant, HasFactory- Relationship:
hasMany(Sermon::class)
- Relationship:
Migrations
| Migration | Table | Indexes |
|---|---|---|
2026_02_25_140000 | sermon_series | tenant_id, unique (tenant_id, slug) |
2026_02_25_140001 | sermons | tenant_id, (tenant_id, created_at), (tenant_id, date), unique (tenant_id, slug), GIN on custom_fields |
Filament Resource
App\Filament\Resources\SermonResource- Navigation: icon
Heroicon::OutlinedMicrophone, group "Contenu" (French) - Form: title (auto-slug), slug, speaker, date, duration, series (select), audio_url, video_url, tags, transcript
- Table: title, speaker, date, series, formatted_duration; filter by series; default sort by date desc
- Pages: ListSermons, CreateSermon, EditSermon
- Navigation: icon
API Layer
| Endpoint | Controller Method | Auth |
|---|---|---|
GET /api/v1/sermons | index | Sanctum |
POST /api/v1/sermons | store | Sanctum |
GET /api/v1/sermons/{sermon} | show | Sanctum |
PUT /api/v1/sermons/{sermon} | update | Sanctum |
DELETE /api/v1/sermons/{sermon} | destroy | Sanctum |
- Filtering:
?speaker=,?series_id=,?tag= - Pagination:
?per_page=(default 15) - Response:
SermonResource/SermonCollection— never exposestenant_id - Validation:
StoreSermonRequest/UpdateSermonRequest— tenant-scoped unique slug
Tests — 23 passing (67 assertions)
Unit (11 tests)
SermonTest— casts (custom_fields, tags), hidden tenant_id, formatted_duration (hours, minutes, null), belongs to seriesHasSoftVersioningTest— snapshot on update, revert restores previous, revert returns false when empty, hasPreviousVersion boolean
Feature (12 tests)
SermonApiTest— 401 without auth, list, no tenant_id in response, create, show, update, delete, pagination, filter by speakerSermonIsolationTest— sermon invisible to other tenant, count isolated per tenant, API returns only own tenant's sermons
Translation Files
lang/fr/sermons.php— all field labels in Frenchlang/en/sermons.php— English fallback
Phase 2, Session 2 — Event Content Type
Commit: 4b1ea2f — feat: Event content type — model, migration, Filament resource, API, testsDate: 2026-02-25
Model
App\Models\Event— BelongsToTenant, HasSoftVersioning, HasFactory- Casts:
start_at(datetime),end_at(datetime),capacity(int),custom_fields(array),previous_version(array) - Hidden:
tenant_id - Accessors:
is_upcoming(start_at is future),is_past(end_at or start_at is past)
- Casts:
Migration
| Migration | Table | Indexes |
|---|---|---|
2026_02_25_150000 | events | tenant_id, (tenant_id, start_at), (tenant_id, created_at), unique (tenant_id, slug), GIN on custom_fields |
Filament Resource
App\Filament\Resources\EventResource- Navigation: icon
Heroicon::OutlinedCalendarDays, group "Contenu", sort 2 - Form: title (auto-slug), slug, start_at, end_at (validated after start_at), location, capacity, image URL, registration URL, description
- Table: title, start_at, end_at, location, capacity; filters for upcoming/past; default sort by start_at desc
- Pages: ListEvents, CreateEvent, EditEvent
- Navigation: icon
API Layer
| Endpoint | Controller Method | Auth |
|---|---|---|
GET /api/v1/events | index | Sanctum |
POST /api/v1/events | store | Sanctum |
GET /api/v1/events/{event} | show | Sanctum |
PUT /api/v1/events/{event} | update | Sanctum |
DELETE /api/v1/events/{event} | destroy | Sanctum |
- Filtering:
?location=(ilike),?upcoming=true,?past=true - Pagination:
?per_page=(default 15) - Validation:
end_atmust be afterstart_at; tenant-scoped unique slug
Tests — 20 new (43 total, 136 assertions)
Unit (6 tests)
EventTest— casts (custom_fields, start_at/end_at, capacity), hidden tenant_id, is_upcoming, is_past
Feature (14 tests)
EventApiTest— 401 without auth, list, no tenant_id, create, show, update, delete, pagination, filter upcoming, filter location, end_at validationEventIsolationTest— event invisible to other tenant, count isolated, API scoped to tenant
Laravel Reverb Integration — Real-Time Broadcasting
Commit: ea70018 — feat: integrate Laravel Reverb for real-time broadcastingDate: 2026-02-25
What Was Built
Real-time broadcasting infrastructure so Filament admin panel users receive live notifications when other admins in their tenant create, update, or delete content.
Architecture
Content CRUD → ContentObserver → ContentChanged (ShouldBroadcast)
├─→ Broadcasts on private-tenant.{id} channel
└─→ NotifyTenantAdmins listener
└─→ Database notification to other admins
└─→ Filament bell icon (30s polling)New Files
| File | Purpose |
|---|---|
app/Events/ContentChanged.php | Broadcast event — carries content_type, action, content_id, content_title, changed_by, tenant_id |
app/Observers/ContentObserver.php | Registered on Sermon and Event; dispatches ContentChanged on created/updated/deleted |
app/Listeners/NotifyTenantAdmins.php | Sends database notifications to all tenant admins except the author |
app/Notifications/ContentChangedNotification.php | Database notification with French action labels (créé, modifié, supprimé) |
routes/channels.php | Channel authorization: tenant.{tenantId} (tenant_id match) and App.Models.User.{id} (user id match) |
config/broadcasting.php | Reverb driver configuration |
config/reverb.php | Reverb server configuration |
database/migrations/..._create_notifications_table.php | Laravel notifications table for database channel |
Modified Files
| File | Change |
|---|---|
app/Providers/AppServiceProvider.php | Register ContentObserver on Sermon/Event; bind ContentChanged → NotifyTenantAdmins |
app/Providers/Filament/AdminPanelProvider.php | Added ->databaseNotifications() and ->databaseNotificationsPolling('30s') |
bootstrap/app.php | Added channels: route registration |
.env.example | Added Reverb env vars (REVERB_APP_ID, REVERB_APP_KEY, etc.) |
Channel Authorization
| Channel Pattern | Rule |
|---|---|
private-tenant.{tenantId} | $user->tenant_id === $tenantId |
private-App.Models.User.{id} | (int) $user->id === (int) $id |
Tests — 12 new (55 total, 164 assertions)
Broadcasting (12 tests)
ContentChangedTest— dispatch on create/update/delete, broadcasts on correct tenant channel, payload structure, notification sent to other admins only, cross-tenant isolationChannelAuthTest— tenant channel auth (own/other), user channel auth (own/other), channel callbacks registered
Bug Fixes During Integration
Tenant isolation tests broken (3 tests):
ContentObserverdispatchingContentChanged(ShouldBroadcast) triggered synchronousBroadcastEventqueue jobs. TheQueueTenancyBootstrapperreset tenant context after job completion, causingSermon::count()/Event::count()to return unscoped results. Fix:Event::fake([ContentChanged::class])in isolation testbeforeEach().Channel auth tests failing (3 tests):
NullBroadcaster::auth()is a no-op — returns null/200 for all requests regardless of authorization. Fix: Rewrote tests to verify channel callback logic directly instead of hitting/broadcasting/authendpoint.
Phase 2, Session 3 — Announcement Content Type
Commit: 824e0c9 — feat: Announcement content type — model, migration, Filament resource, API, testsDate: 2026-02-25
Model
App\Models\Announcement— BelongsToTenant, HasSoftVersioning, HasFactory- Casts:
published_at(datetime),expires_at(datetime),pinned(boolean),custom_fields(array),previous_version(array) - Hidden:
tenant_id - Accessors:
is_active(published and not expired),is_expired(past expiry date)
- Casts:
Migration
| Migration | Table | Indexes |
|---|---|---|
2026_02_25_160000 | announcements | tenant_id, (tenant_id, published_at), (tenant_id, created_at), unique (tenant_id, slug), GIN on custom_fields |
Filament Resource
App\Filament\Resources\AnnouncementResource- Navigation: icon
Heroicon::OutlinedMegaphone, group "Contenu", sort 3 - Form: title (auto-slug), slug, published_at, expires_at (validated after published_at), pinned toggle, target_group select (Tous/Jeunesse/Femmes/Hommes/Responsables), body (MarkdownEditor)
- Table: title, published_at, expires_at, pinned (icon), target_group; filters for active/expired/pinned; default sort by published_at desc
- Pages: ListAnnouncements, CreateAnnouncement, EditAnnouncement
- Navigation: icon
API Layer
| Endpoint | Controller Method | Auth |
|---|---|---|
GET /api/v1/announcements | index | Sanctum |
POST /api/v1/announcements | store | Sanctum |
GET /api/v1/announcements/{announcement} | show | Sanctum |
PUT /api/v1/announcements/{announcement} | update | Sanctum |
DELETE /api/v1/announcements/{announcement} | destroy | Sanctum |
- Filtering:
?pinned=true,?active=true,?expired=true,?target_group= - Pagination:
?per_page=(default 15) - Validation:
expires_atmust be afterpublished_at; tenant-scoped unique slug
Tests — 24 new (79 total, 244 assertions)
Unit (9 tests)
AnnouncementTest— casts (custom_fields, published_at/expires_at, pinned), hidden tenant_id, is_active (published+not expired, published+no expiry, not yet published), is_expired (past expiry, no expiry)
Feature (15 tests)
AnnouncementApiTest— 401 without auth, list, no tenant_id, create, show, update, delete, pagination, filter pinned, filter active, filter target_group, expires_at validationAnnouncementIsolationTest— announcement invisible to other tenant, count isolated, API scoped to tenant
Observer Registration
Announcement::observe(ContentObserver::class)added inAppServiceProvider::boot()
Phase 2, Session 4 — Member & Gallery Content Types
Commit: d62d215 area — Member & Gallery content types Date: 2026-02-28
Models
App\Models\Member— BelongsToTenant, HasSoftVersioning, HasFactory- Fields: first_name, last_name, email, phone, baptism_date, cell_group_id, status, custom_fields
- Avatar via Spatie Media Library (
avatarcollection) - Relationship:
belongsTo(CellGroup::class)
App\Models\CellGroup— BelongsToTenant, HasFactoryApp\Models\Gallery— BelongsToTenant, HasSoftVersioning, HasFactory- Photos via Spatie Media Library (
photoscollection)
- Photos via Spatie Media Library (
Filament Resources
- MemberResource — icon UserGroup, group "Membres", filters by status/cell group
- GalleryResource — icon Photo, group "Contenu", media library integration
Phase 2, Session 5 — Page Content Type
Date: 2026-02-28
Model
App\Models\Page— BelongsToTenant, HasSoftVersioning, HasFactory- Fields: title, slug, content_blocks (JSONB), seo_title, seo_description, published_at
- Filament Builder with block types: heading, text, image, video, quote, call-to-action
Phase 2, Session 6 — GivingRecord Content Type
Commit: d62d215 — feat: GivingRecord content type — model, migration, Filament resource, API, testsDate: 2026-02-28
Model
App\Models\GivingRecord— BelongsToTenant, HasFactory- Fields: member_id, amount, currency, date, method, reference, campaign_id, custom_fields
- Relationship:
belongsTo(Member::class)
Phase 2.5 — Hardening Sprint
Commit: 4a381b2 — feat: Phase 2.5 hardening — Rector, PHPStan, CI, security headers, rate limitingDate: 2026-03-01
What Was Done
- Rector PHP code quality rules applied
- PHPStan level 5 static analysis (all passing)
- GitHub Actions CI pipeline (tests + PHPStan)
- Security headers middleware
- API rate limiting per tenant
- Sanctum token management
- SECURITY.md and CONTRIBUTING.md
Phase 3 — API Layer
Commit: 5f4c7ce — feat: Phase 3 API layer — auth endpoints, token management, Scramble docsDate: 2026-03-01
What Was Built
- Auth endpoints: login, logout, register, token refresh
- Token management: create, list, revoke personal access tokens
- Scramble API documentation auto-generation
- Gallery form requests for API validation
- All 6 content types with full CRUD API endpoints
API Endpoints Summary
| Resource | Base URL | Methods |
|---|---|---|
| Sermons | /api/v1/sermons | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Events | /api/v1/events | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Announcements | /api/v1/announcements | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Members | /api/v1/members | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Pages | /api/v1/pages | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| GivingRecords | /api/v1/giving-records | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Galleries | /api/v1/galleries | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Auth | /api/v1/auth/* | login, logout, register, tokens |
Phase 4 — First Deployment (prep)
Commit: a5f76ef — feat: Phase 4 deployment prep — tenancy middleware, tenant:create, Docker, health checkDate: 2026-03-02
What Was Built
- Tenancy middleware for API tenant resolution
tenant:createArtisan command for provisioning tenants- Database seeders for demo data
- Health check endpoint
- Docker / docker-compose configuration
- Status: deployment prep complete, awaiting production push
UI Refonte — Premium Admin Redesign
Commit: f7b19bf — feat: premium admin UI refonte — dashboard, settings, form redesign, icon buttons, full-width layoutsDate: 2026-03-03
Dashboard — 5 Widgets in 3-Column Grid
| Widget | Type | Position |
|---|---|---|
| StatsOverview | 4 stat cards (members, giving, events, sermons) with sparklines | Full width |
| GivingChart | Bar chart — giving per month (12 months) | 2/3 width |
| MemberDistributionChart | Doughnut — members by status | 1/3 width |
| UpcomingEventsWidget | Table — next 5 events | 2/3 width |
| SermonsChart | Line chart — sermons per month (12 months) | 1/3 width |
Settings Page — 7 Tabs (standalone sidebar navigation)
| Tab | Key Fields |
|---|---|
| Church Information | Name, pastor, denomination, year founded, capacity, address, phones, email, website, worship schedule repeater |
| Appearance & Design | Logo, favicon, cover image, colors, fonts, dark mode, custom CSS |
| Social Media | Facebook, Instagram, YouTube, Twitter/X, TikTok, WhatsApp |
| SEO & Analytics | Title suffix, meta description, keywords, OG image, Google Analytics, GTM, Facebook Pixel |
| Notifications | Email sender config, welcome/event/giving/announcement notification toggles, custom welcome message |
| Modules | Enable/disable: Sermons, Events, Announcements, Members, Pages, Giving, Galleries |
| Advanced | Locale, timezone, currency, date/time formats, items per page, API toggle + rate limit, maintenance mode |
All settings stored in tenant data JSONB column via dot notation (data.primary_color, etc.).
Resource Form Redesign (7 resources)
Applied across all resources:
- Sections with icons (
Heroicon::Outlined*) and descriptions - Collapsible sections for secondary content (collapsed by default for less-used fields)
- Prefix icons on email, phone, URL fields
- Placeholders and helper texts on all inputs
- Full FR + EN translations for all new section titles, descriptions, placeholders
Table Actions — Icon-Only Buttons
All 7 resource tables changed from text+icon buttons to compact icon-only buttons:
ViewAction::make()->iconButton()— eye iconEditAction::make()->iconButton()— pencil iconDeleteAction::make()->iconButton()— trash icon
Full-Width Create/Edit Layouts
All 14 Create and Edit pages set to Width::Full (protected Width|string|null $maxContentWidth = Width::Full) — forms expand to use all available screen width.
Panel Configuration
- Color palette: Indigo primary, Rose danger, Slate gray, Sky info, Emerald success, Amber warning
sidebarCollapsibleOnDesktop()for more workspace- Database notifications with 30s polling
Translation Files
- 4 new files:
lang/{fr,en}/dashboard.php,lang/{fr,en}/settings.php - 14 enriched files: All resource translation files with
section_*,section_*_desc,*_placeholderkeys
Files Changed: 51 files, +2,161 lines
| Category | Files |
|---|---|
| New pages | Dashboard.php, Settings.php |
| New widgets | StatsOverview.php, GivingChart.php, MemberDistributionChart.php, UpcomingEventsWidget.php, SermonsChart.php |
| Resources | 7 resource files + 14 Create/Edit page files |
| Models | Tenant.php (data cast + helpers), User.php (HasTenants) |
| Panel | AdminPanelProvider.php |
| Translations | 18 translation files (4 new + 14 modified) |
| Migration | alter_notifications_data_to_jsonb.php |
Tests: 194 passing (604 assertions) — all green
Phase 5 — AI Layer
Commit: 1e047c0 — feat: Phase 5 AI layer — multi-provider assistant, 14 skills, tenant-scoped context pipelineDate: 2026-03-03
What Was Built
Multi-provider AI assistant with tenant-scoped context pipeline and 14 specialized skills.
Architecture — Multi-Provider Driver Pattern
AiManager (extends Illuminate\Support\Manager)
├── ClaudeDriver → anthropic-ai/sdk
├── OpenAiDriver → openai-php/client
└── GeminiDriver → google-gemini-php/clientPer-tenant config stored in data JSONB: ai_provider, ai_api_key, ai_model, ai_max_tokens.
AI Skills System — 14 Skills in 5 Categories
| Category | Skills |
|---|---|
| Content Creation | sermon-outline, content-write, translate, seo-optimize, proofread |
| Church Management | event-plan, comm-draft, giving-insights, dashboard-narrate |
| Design & Branding | brand-advise, social-create |
| Security & Maintenance | content-audit, data-quality |
| AI Guidance | strategy-advise |
Streaming Architecture
User message → ProcessAiMessage (queued job)
→ AiManager resolves driver per tenant
→ TenantContextBuilder builds system prompt (no PII)
→ Driver streams response → AiResponseChunk (ShouldBroadcastNow)
→ Private channel ai-chat.{userId} → Livewire/EchoNew Files (40+)
| Category | Files |
|---|---|
| Config | config/ai.php |
| Driver Pattern | AiDriverInterface, AiResponse, AiManager, ClaudeDriver, OpenAiDriver, GeminiDriver |
| Skills | AiSkill base, SkillRegistry, 14 skill classes |
| Context | TenantContextBuilder |
| Models | AiConversation, AiMessage + migrations + factories |
| Chat | AiResponseChunk event, ProcessAiMessage job, AiChat Livewire, AiAssistant page |
| Analysis | AiAnalyzeContent job, AiAnalyzeAction |
| Translations | lang/{fr,en}/ai.php |
Modified Files
| File | Change |
|---|---|
AppServiceProvider.php | AiManager singleton, SkillRegistry singleton, Livewire registration |
Settings.php | AI configuration tab (provider, model, API key, max tokens) |
routes/channels.php | ai-chat.{userId} channel authorization |
.env.example | AI provider env vars |
lang/{fr,en}/settings.php | AI settings translations |
Tests: 228 passing (34 new + 194 existing) — all green
2026-03-04 — Codex x Claude Collaboration Setup
- Status: Done
- Goal: Establish strict TDD collaboration workflow, premium UI quality bar, and mandatory progress-saving protocol for ongoing development.
- Deliverables:
AI_COLLABORATION_PLAN.md— execution plan, TDD lifecycle, DoD, premium UI checklist, progress and commit protocolCLAUDE_COLLAB_INSTRUCTIONS.md— Claude-operating instructions for Codex collaborationCLAUDE.mdupdated with startup command to load collaboration docs
- Quality checks: Not applicable (documentation/process setup task)
- Notes: Future implementation tasks must follow
composer test+composer qualitygate before each completion and must append progress in this report.
2026-03-04 — Hardening Sprint (Quality Gate Recovery)
- Status: Done
- Goal: Restore strict code-quality gates for the active Phase 6 codebase and remove static-analysis debt blocking CI.
- Summary:
- Resolved PHPStan blockers (65 -> 0 errors) across tenancy-safe billing, AI manager/drivers, notification/payment managers, soft versioning, and Livewire chat typing.
- Applied Rector-compatible refactors and type signatures required by the current ruleset.
- Cleaned stale PHPStan ignore patterns and updated baseline alignment.
- Ensured optional SMS SDK path is handled safely when dependency is absent.
- Tests:
composer test: pass (309 passed,888 assertions)
- Quality:
composer quality: pass (pint,phpstan,rector --dry-run)
- Files: hardening updates across AI, billing, notification, payment, Filament pages/resources/widgets, and PHPStan config.
- Notes: This milestone is a quality stabilization checkpoint; next slice can focus on premium UX upgrades and Phase 6 completion under the same TDD gates.
2026-03-04 — Premium UI Pass (Dashboard + Theme Foundation)
- Status: Done
- Goal: Raise visual quality for core admin experience with a stronger premium design system and dashboard identity.
- Summary:
- TDD applied for dashboard metadata (title + subheading) with new unit tests.
- Added localized premium dashboard heading/subheading.
- Upgraded Filament admin theme with a deliberate visual foundation:
- design tokens (surface, border, primary, accent, typography contrast),
- atmospheric background gradients,
- elevated widget/section/table surfaces,
- stronger input focus treatments,
- polished topbar/sidebar glass effect,
- lightweight page entrance animation.
- Mobile-specific polish for card radius and subheading readability.
- Tests:
- Added:
tests/Unit/Pages/DashboardPageTest.php composer test: pass (311 passed,890 assertions)
- Added:
- Quality:
composer quality: pass
- Files:
app/Filament/Pages/Dashboard.phpresources/css/filament/admin/theme.csslang/fr/dashboard.phplang/en/dashboard.phptests/Unit/Pages/DashboardPageTest.php
- Notes: Next premium slice should target settings form ergonomics and billing view refinement for stronger visual consistency.
2026-03-04 — Premium UI Pass (Settings + Billing Refinement)
- Status: Done
- Goal: Improve premium UX for configuration-heavy screens and pricing presentation quality.
- Summary:
- Added full-width layout behavior to Settings and Billing pages for better information density and usability.
- Improved Billing price rendering to be currency-aware (USD/EUR/GBP symbols, currency code fallback).
- Refined Billing page structure with a premium hero section and cleaner reusable icon sizing.
- Extended admin theme with billing-specific visual language (hero gradient, elevated cards, subtle motion, settings section accents).
- Removed inline view styling in favor of reusable theme classes.
- Tests:
- Added:
tests/Unit/Pages/BillingPageTest.php - Added:
tests/Unit/Pages/SettingsPageTest.php composer test: pass (315 passed,895 assertions)
- Added:
- Quality:
composer quality: pass
- Files:
app/Filament/Pages/Billing.phpapp/Filament/Pages/Settings.phpresources/views/filament/pages/billing.blade.phpresources/css/filament/admin/theme.csstests/Unit/Pages/BillingPageTest.phptests/Unit/Pages/SettingsPageTest.phpBUILD_PROGRESS.md
- Notes: Next premium slice should target dashboard widgets + table density/accessibility tuning (focus states, empty states, and consistent data emphasis).
2026-03-04 — Billing UI Hotfix (Icon Scaling Regression)
- Status: Done
- Goal: Correct oversized icon rendering in Billing page cards and feature lists.
- Summary:
- Added resilient icon sizing rules directly in Billing view scope (
.ekk-billing) to prevent SVG expansion when utility classes are not reliably available. - Enforced fixed dimensions for both small and medium icon variants with explicit min-size constraints.
- Added resilient icon sizing rules directly in Billing view scope (
- Tests:
php artisan test tests/Unit/Pages/BillingPageTest.php: pass
- Quality:
composer quality: pass
- Files:
resources/views/filament/pages/billing.blade.php
- Notes: This hotfix targets visual correctness from production-like rendering conditions and keeps existing billing behavior unchanged.
2026-03-04 — Settings Crash Fix (fill() on null)
- Status: Done
- Goal: Resolve internal server error on
/admin/{tenant}/settingscaused by null form object during mount. - Summary:
- Root cause: page-level
$formhandling conflicted with runtime lifecycle, leading tofill()on null inSettings::mount(). - Refactored settings state initialization to use direct Livewire state (
$this->data) inmount()instead of form object fill. - Updated save flow to read from normalized state and safely persist tenant values.
- Added regression coverage to ensure tenant settings route no longer throws 500.
- Root cause: page-level
- Tests:
- Added/updated:
tests/Feature/Filament/SettingsPageTest.php - Verified:
php artisan testpass (316 passed,896 assertions)
- Added/updated:
- Quality:
composer quality: pass
- Files:
app/Filament/Pages/Settings.phptests/Feature/Filament/SettingsPageTest.phpBUILD_PROGRESS.md
- Notes: Access policy may still return 403 for unauthorized users, but the internal server error is eliminated.
2026-03-04 — CRUD Display Quality Upgrade (Filament Resources)
- Status: Done
- Goal: Fix design-quality and rendering consistency issues across all Filament CRUD resource screens.
- Summary:
- Normalized all resource
List*pages to full-width layouts so list/table screens match create/edit density and avoid cramped presentation. - Hardened global CRUD UI styles in the shared Filament admin theme:
- Better header toolbar spacing and responsive behavior.
- Improved filter panel readability and visual grouping.
- Horizontal containment for wide tables with safer minimum widths.
- Better cell/column text wrapping to prevent overflow and clipping.
- Consistent table action button wrapping/alignment for row actions.
- Improved form field rhythm and label emphasis.
- Applied changes globally so all current and future resources inherit the same quality baseline.
- Normalized all resource
- Tests:
- Verified:
php artisan testpass (316 passed,896 assertions)
- Verified:
- Quality:
composer quality: pass
- Files:
resources/css/filament/admin/theme.cssapp/Filament/Resources/AnnouncementResource/Pages/ListAnnouncements.phpapp/Filament/Resources/CampusResource/Pages/ListCampuses.phpapp/Filament/Resources/EventResource/Pages/ListEvents.phpapp/Filament/Resources/GalleryResource/Pages/ListGalleries.phpapp/Filament/Resources/GivingRecordResource/Pages/ListGivingRecords.phpapp/Filament/Resources/MemberResource/Pages/ListMembers.phpapp/Filament/Resources/NotificationDispatchResource/Pages/ListNotificationDispatches.phpapp/Filament/Resources/PageResource/Pages/ListPages.phpapp/Filament/Resources/PaymentTransactionResource/Pages/ListPaymentTransactions.phpapp/Filament/Resources/SermonResource/Pages/ListSermons.phpBUILD_PROGRESS.md
- Notes: Next quality pass should target per-resource column semantics (e.g., explicit truncation/tooltips and priority-based responsive column visibility) for even stronger mobile ergonomics.
2026-03-04 — Show Modal Quality Upgrade (ViewAction + Infolist Columns)
- Status: Done
- Goal: Fix low-quality visual rendering in Filament "show" modals, especially infolist column readability.
- Summary:
- Applied global
ViewActiondefaults to improve modal composition consistency:- Wider modal width (
5xl) for better data density. - Start-aligned modal layout for improved label/content scanning.
- Wider modal width (
- Added premium modal and infolist styling in the shared admin theme:
- Upgraded modal surface, border contrast, and hierarchy.
- Better infolist row containers with spacing and structure.
- Improved inline label/content column behavior and multiline readability.
- Refined key-value and repeatable table presentation inside modals.
- Mobile fallback so columns collapse cleanly without visual breakage.
- Applied global
- Tests:
- Verified:
php artisan testpass (316 passed,896 assertions)
- Verified:
- Quality:
composer quality: pass
- Files:
resources/css/filament/admin/theme.cssBUILD_PROGRESS.md
- Notes: Next step can add per-resource
ViewAction::infolist()schemas for high-priority entities to control field ordering and semantic grouping, beyond global styling.
2026-03-04 — Show Modal Single-Column Hotfix
- Status: Done
- Goal: Enforce single-column display for show-view modal entries.
- Summary:
- Removed two-column inline label/content layout in modal infolist entries.
- Forced
.fi-in-entry-has-inline-labelto render as one column across all breakpoints for cleaner, consistent reading flow.
- Tests:
- Verified:
composer qualitypass
- Verified:
- Files:
resources/css/filament/admin/theme.cssBUILD_PROGRESS.md
2026-03-04 — Show Modal Grid Columns Fix (Screenshot Follow-up)
- Status: Done
- Goal: Remove remaining two-per-row field layout in show modals (resource sections still rendering as multi-column).
- Summary:
- Added modal-scoped overrides for Filament schema grid variables (
--cols-*) to force one-column rendering in modal content at all breakpoints. - Fix directly targets the layout seen in Event and Sermon show screenshots where section fields were still displayed in two columns.
- Added modal-scoped overrides for Filament schema grid variables (
- Tests:
- Verified:
composer qualitypass
- Verified:
- Files:
resources/css/filament/admin/theme.cssBUILD_PROGRESS.md
2026-03-04 — Premium CRUD List Visual Pass (Global Table UX)
- Status: Done
- Goal: Elevate Filament CRUD list pages (Members and all other table-based resources) with stronger premium visual quality and usability.
- Summary:
- Improved table visual hierarchy: stronger heading styles, clearer header metadata, and better body text readability.
- Upgraded toolbar/search/filter prominence with bordered elevated toolbar container and clearer search field treatment.
- Improved row interaction clarity with refined row borders and subtle hover states.
- Redesigned row action controls for accessibility and precision:
- Larger hit areas.
- Better icon sizing and hover feedback.
- Strengthened pagination clarity and active-state treatment (buttons, overview text, and page emphasis).
- Improved sidebar navigation emphasis with clearer active/current state styling and stronger group label readability.
- Verified shared-class coverage so changes apply across all Filament CRUD list screens using common
fi-ta-*,fi-pagination*, andfi-sidebar*components.
- Validation:
composer quality: passnpm run build: pass
- Files:
resources/css/filament/admin/theme.cssBUILD_PROGRESS.md
2026-03-04 — Edit Pages Single-Column Layout (Global Resource Fix)
- Status: Done
- Goal: Change Filament resource edit pages from two columns to a single-column form layout.
- Summary:
- Added a shared app-level edit page base class to override Filament
EditRecorddefault form column behavior. - Forced default edit form schema to
columns(1)when the schema does not explicitly define custom columns. - Updated all resource edit pages to extend the shared base class for consistent behavior across modules.
- Scope is edit pages only; create/list/show behavior remains unchanged.
- Added a shared app-level edit page base class to override Filament
- Validation:
composer quality: pass- Targeted tests:
php artisan test tests/Unit/Pages tests/Feature/Filament: pass npm run build: pass
- Files:
app/Filament/Resources/Pages/EditRecord.phpapp/Filament/Resources/AnnouncementResource/Pages/EditAnnouncement.phpapp/Filament/Resources/CampusResource/Pages/EditCampus.phpapp/Filament/Resources/EventResource/Pages/EditEvent.phpapp/Filament/Resources/GalleryResource/Pages/EditGallery.phpapp/Filament/Resources/GivingRecordResource/Pages/EditGivingRecord.phpapp/Filament/Resources/MemberResource/Pages/EditMember.phpapp/Filament/Resources/PageResource/Pages/EditPage.phpapp/Filament/Resources/SermonResource/Pages/EditSermon.phpBUILD_PROGRESS.md
2026-03-04 — Create Pages Single-Column Layout (Global Resource Fix)
- Status: Done
- Goal: Change Filament resource create pages from two columns to a single-column form layout.
- Summary:
- Added a shared app-level create page base class to override Filament
CreateRecorddefault form column behavior. - Forced default create form schema to
columns(1)when the schema does not explicitly define custom columns. - Updated all resource create pages to extend the shared base class for consistent behavior across modules.
- Scope is create pages only; edit/list/show behavior remains unchanged.
- Added a shared app-level create page base class to override Filament
- Validation:
composer quality: pass- Targeted tests:
php artisan test tests/Unit/Pages tests/Feature/Filament: pass npm run build: pass
- Files:
app/Filament/Resources/Pages/CreateRecord.phpapp/Filament/Resources/AnnouncementResource/Pages/CreateAnnouncement.phpapp/Filament/Resources/EventResource/Pages/CreateEvent.phpapp/Filament/Resources/GalleryResource/Pages/CreateGallery.phpapp/Filament/Resources/GivingRecordResource/Pages/CreateGivingRecord.phpapp/Filament/Resources/MemberResource/Pages/CreateMember.phpapp/Filament/Resources/PageResource/Pages/CreatePage.phpapp/Filament/Resources/SermonResource/Pages/CreateSermon.phpBUILD_PROGRESS.md
2026-03-04 — Global Border Softening Pass (Transparent Premium Edges)
- Status: Done
- Goal: Reduce heavy border appearance across Filament components by making border treatment more transparent and premium.
- Summary:
- Introduced shared border tone tokens:
--ekk-border-soft--ekk-border-faint
- Replaced heavy component border colors with transparent soft variants across:
- sections/widgets/tables
- table toolbar/filters/row dividers/action buttons
- pagination wrappers/buttons
- input/select/text fields
- modal shells/headers/infolist containers
- billing cards
- Preserved structure using shadows and spacing so readability remains strong while visual noise is reduced.
- Introduced shared border tone tokens:
- Validation:
composer quality: passnpm run build: pass
- Files:
resources/css/filament/admin/theme.cssBUILD_PROGRESS.md
2026-03-06 — High-Quality Architecture & Security Hardening
- Status: Done
- Goal: Implement high-priority architecture fixes, design improvements, and security hardening (AI rate limiting, robust multi-tenant design).
- Summary:
- Removed
HasSoftVersioningfromGivingRecordto secure financial ledgers (they should be immutable/append-only). - Implemented AI Rate Limiting using Laravel
RateLimiterinsideProcessAiMessage(10 messages/minute scoped bytenant_id) to protect from API quota abuse. - Refactored Filament Form Layouts:
- Removed hacky global CSS forcing single columns.
- Deleted custom
CreateRecordandEditRecordbase class overrides. - Updated all 8 resource
Create*.phpandEdit*.phpfiles to use native Filament base classes. - Re-applied
->columns(1)natively and cleanly across all 10*Resource.phpschema builders.
- Removed
- Validation:
php artisan test: pass (319 passed, 917 assertions)composer quality: pass
- Files:
app/Models/GivingRecord.phpapp/Jobs/ProcessAiMessage.phpresources/css/filament/admin/theme.cssapp/Filament/Resources/*Resource.php(10 files)app/Filament/Resources/*Resource/Pages/Create*.php(8 files)app/Filament/Resources/*Resource/Pages/Edit*.php(8 files)BUILD_PROGRESS.md
2026-03-06 — Financial Integrity & Audit Robustness
- Status: Done
- Goal: Implement robust append-only audit logs for all financial models and prepare database partitioning strategy.
- Summary:
- Financial Hardening:
- Secured
GivingRecordandPaymentTransactionmodels as immutable: blockedupdatinganddeletingat the model level (Eloqeunt events) for core financial fields. - Disabled
EditActionandDeleteActioninGivingRecordResourceandPaymentTransactionResource. - Grouped all financial audit trails under a dedicated
financiallog name inSpatie/Activitylog. - Excluded sensitive fields (
phone_number,provider_metadata) from audit logs for privacy and security.
- Secured
- Audit Log Visibility:
- Implemented a read-only
ActivityResourcein Filament to allow administrators to review the audit trail. - Scoped
Activitymodel to current tenant usingBelongsToTenanttrait. - Added English and French translations for the audit log interface.
- Implemented a read-only
- Scaling Strategy:
- Drafted
docs/architecture/partitioning-strategy.mdfor PostgreSQL declarative partitioning ofactivity_logandmediatables.
- Drafted
- Financial Hardening:
- Validation:
php artisan test: pass- Audit log visibility confirmed in Filament dashboard.
- Financial records verified as immutable via manual attempt.
- Files:
app/Models/GivingRecord.php(Hardening)app/Models/PaymentTransaction.php(Hardening + LogsActivity)app/Concerns/LogsActivityWithTenant.php(Robustness)app/Filament/Resources/GivingRecordResource.php(UI Hardening)app/Filament/Resources/ActivityResource.php(New Resource)app/Models/Activity.php(Tenant Scoping)docs/architecture/partitioning-strategy.md(New Doc)lang/*/activity.php(Translations)
2026-03-06 — Financial API Hardening & Audit Quality
- Status: Done
- Goal: Align GivingRecord API with model-level immutability and resolve quality gate blockers in Audit Log (ActivityResource).
- Summary:
- Financial API Hardening:
- Restricted
giving-recordsAPI to read/create only; disabledPUT/PATCHandDELETEmethods inroutes/api.php. - Updated
GivingRecordApiTestto verify that update/delete attempts return405 Method Not Allowed.
- Restricted
- Quality Gate Recovery:
- Fixed PHPStan crash by increasing memory limit.
- Resolved PHPStan
class.notFoundinActivityResource.phpby importingFilament\Actionsnamespace. - Suppressed
narrowedTypewarning inLogsActivityWithTenanttrait using@phpstan-ignore. - Applied Rector closure return type fixes and Pint style normalization.
- Audit UX:
- Localized filter options and action labels in
ActivityResource.
- Localized filter options and action labels in
- Financial API Hardening:
- Validation:
php artisan test tests/Feature/Api/V1/GivingRecordApiTest.php: pass (14 passed)vendor/bin/phpstan analyze: pass (no errors)vendor/bin/rector --dry-run: pass (no changes pending)
- Files:
routes/api.phpapp/Filament/Resources/ActivityResource.phpapp/Concerns/LogsActivityWithTenant.phpapp/Models/GivingRecord.php(Rector)app/Models/PaymentTransaction.php(Rector)tests/Feature/Api/V1/GivingRecordApiTest.php
2026-03-06 — RBAC & Security Wave A
- Status: Done
- Goal: Implement granular Role-Based Access Control (RBAC) across the platform and API.
- Summary:
- Filament Shield Integration:
- Installed
bezhansalleh/filament-shieldv4 (Filament v5 compatible). - Configured multi-tenancy support for permissions using
stancl/tenancy. - Migrated
spatie/laravel-permissionwithteam_idasstringto match tenant slugs.
- Installed
- RBAC Foundation:
- Created
RolesAndPermissionsSeederdefining 4 base roles:Super Admin,Pastor,Treasurer,Volunteer. - Generated policies for all 10 core resources.
- Created
- API Authorization:
- Updated all API V1 Controllers to enforce model policies via
$this->authorize(). - Verified that unauthorized users (e.g., Volunteers) receive
403 Forbiddenon sensitive endpoints like Giving/Payments.
- Updated all API V1 Controllers to enforce model policies via
- Filament Shield Integration:
- Validation:
php artisan test tests/Feature/Filament/RbacTest.php: pass (4 passed)
- Files:
2026-03-11 — Stage 1 Hardening & Security Audit
- Status: Done
- Goal: Harden financial integrity, optimize AI performance, and enforce PII scrubbing in audit logs.
- Summary:
- Financial Hardening:
- Created
ImmutableRecordExceptionfor uniform handling of forbidden modifications. - Updated
GivingRecordandPaymentTransactionmodels to throw localized exceptions onupdatingordeleting.
- Created
- Privacy & Security:
- Enhanced
LogsActivityWithTenanttrait with ascrubPiimethod that automatically redacts sensitive keys (phone,email,amount, etc.) withincustom_fieldsJSON property.
- Enhanced
- AI Performance & UX:
- Optimized
TenantContextBuilderby caching aggregate stats for 15 minutes per tenant to avoid N+1 queries during AI interaction. - Enhanced AI financial context with "Safe Totals" (comparing current month to previous month) to enable trend analysis without exposing individual records.
- Upgraded
SkillRegistrywith keyword-based skill detection and scoring, allowing the AI assistant to recognize intent even without explicit/slugcommands.
- Optimized
- Financial Hardening:
- Validation:
php artisan test --filter=GivingRecordApiTest: pass (14 passed)
- Files:
app/Exceptions/ImmutableRecordException.phpapp/Models/GivingRecord.phpapp/Models/PaymentTransaction.phpapp/Concerns/LogsActivityWithTenant.phpapp/Services/TenantContextBuilder.phpapp/Services/Ai/AiSkill.php
2026-03-11 — Stage 2: Financial Corrections & Data Portability
- Status: In Progress
- Goal: Enable safe financial corrections and provide data export tools.
- Summary:
Adjustment System:
- Implemented a polymorphic
Adjustmentmodel to track voids and corrections for immutable records. - Added
Voidaction toGivingRecordResourceandPaymentTransactionResourcewith reason tracking and audit trail. - Integrated visual indicators (IconColumn) to identify voided records in tables.
- Implemented a polymorphic
Data Portability:
- Enabled
withResponsiveImages()for Member avatars and Gallery photos to optimize bandwidth for low-connectivity areas. - Added high-quality thumbnails and medium-sized conversions for all core models.
- Enabled
- Validation:
php artisan test: pass- Exporters and Media conversions verified.
- Files:
app/Models/Adjustment.phpdatabase/migrations/2026_03_11_095048_create_adjustments_table.phpapp/Filament/Exports/GivingRecordExporter.phpapp/Filament/Exports/MemberExporter.phpapp/Filament/Resources/GivingRecordResource.phpapp/Filament/Resources/GivingRecordResource/Pages/ListGivingRecords.phpapp/Filament/Resources/MemberResource/Pages/ListMembers.phpapp/Filament/Resources/NotificationDispatchResource.php- Fund Management:
- Enhanced
FundResourceandCampaignResourcewith real-time financial tracking (Total Raised calculations). - Integrated multi-currency support for global church operations.
- Enhanced
2026-03-11 — Stage 3: Launch Prep & Observability
- Status: Done
- Goal: Ensure system reliability and prepare for production deployment.
- Summary:
- Observability:
- Integrated
spatie/laravel-healthto monitor core system vitals. - Registered health checks for Database, Storage, Debug Mode, Environment, and App Optimization.
- Integrated
- Documentation:
- Created a comprehensive
Admin Runbookfor church staff (Treasurers, Pastors). - Integrated the runbook into the VitePress documentation site.
- Created a comprehensive
- Observability:
- Validation:
php artisan test: pass (330 passed, 959 assertions)php artisan health:list: verified (manual check)
- Files:
app/Providers/AppServiceProvider.phpdocs/guide/admin-runbook.mdapp/Filament/Resources/FundResource.phpapp/Filament/Resources/CampaignResource.phpIMPROVEMENTS_PLAN.md
2026-03-11 — Stage 4: Bulk Messaging Test Coverage & Bug Fix
- Status: Done
- Goal: Complete test coverage for the Bulk Messaging feature (Feature 9) and fix a type bug.
- Summary:
- Bug Fix:
- Fixed
BulkMessage::getRecipients()returningIlluminate\Support\Collectioninstead ofIlluminate\Database\Eloquent\Collectionfor the default case.
- Fixed
- Test Coverage (45 new tests):
- Unit Tests (16): Model casts, hidden attributes, scopes (draft, scheduled, sent, sending, readyToSend), status transition methods (markAsSending, markAsSent, markAsFailed), getRecipients targeting, relationship to creator.
- API Feature Tests (20): Auth guard, CRUD operations, send action dispatching, status/channel filtering, pagination, validation errors, update endpoint returns 405.
- Tenant Isolation Tests (3): Cross-tenant invisibility, count isolation, API scoping.
- Job & Command Tests (7): SendBulkMessageJob status transitions, dispatch record creation, failure counting, contact info skipping; SendScheduledBulkMessages command multi-tenant processing, no-op when nothing due.
- Code Style: Applied Pint formatting to all dirty files (policies, migrations, services).
- Bug Fix:
- Validation:
pest: pass (568 passed, 1561 assertions)phpstan analyze app/Models/BulkMessage.php: pass (no errors)
- Files:
app/Models/BulkMessage.php(bug fix: default return type)tests/Unit/Models/BulkMessageTest.php(new — 16 tests)tests/Feature/Api/V1/BulkMessageApiTest.php(new — 20 tests)tests/Feature/TenantIsolation/BulkMessageIsolationTest.php(new — 3 tests)tests/Feature/Commands/SendScheduledBulkMessagesTest.php(new — 7 tests including job tests)
- Notes: Features 7 (Birthday Notifications), 8 (Reading Plans), and 9 (Bulk Messaging) are now all complete with full test coverage. Production readiness sprint features 1-9 done.
2026-03-11 — UI Hotfix (Table Toolbar Icon Bleed)
- Status: Done
- Goal: Fix Filament table UI where column manager and filter trigger icons were being pushed completely outside the table card due to search field width constraints.
- Summary:
- Modified
.fi-ta-header-toolbar > divflex layout to includeflex-wrap: wrap;allowing children to naturally break if space is constrained. - Updated
.fi-ta-search-fieldto useflex: 1 1 auto;with a minimum width instead of an inflexiblewidth: 100%;. - Ensured the column manager and action icons properly fit inside the rounded
.fi-tatable container without bleeding out over the edges.
- Modified
- Validation:
npm run build: passphp -d memory_limit=512M vendor/bin/pest: pass (568 passed, 1561 assertions)composer quality: pass
- Files:
resources/css/filament/admin/theme.css
2026-03-11 — Giving Records UI Fix (Translations & Icon Consistency)
- Status: Done
- Goal: Fix missing translation keys for "Void" actions and labels in the Giving Records table.
- Summary:
- Added
void,voided,void_heading,void_description, andvoid_reasonkeys tolang/fr/giving_records.phpandlang/en/giving_records.php. - Converted the
voidaction inGivingRecordResourceto an icon-only button to match the premium admin design system (was showing as a red text link with a raw translation key).
- Added
- Validation:
composer quality: pass- UI visual check: translation keys now resolve to "Annuler" / "Annulé" (FR) or "Void" / "Voided" (EN).
- Files:
lang/fr/giving_records.phplang/en/giving_records.phpapp/Filament/Resources/GivingRecordResource.php
2026-03-11 — Page Builder "Pro" Update
- Status: Done
- Goal: Transition the Ekklesia CMS Page Builder from a static marketing tool to a professional-grade dynamic Church CMS.
- Summary:
- Dynamic & Motion Updates:
- Upgraded
heroblock with multiple slides, configurable transition types (fade/slide), and autoplay speed. - Updated
logo_cloudblock with infinite scrolling/marquee loop for partner logos.
- Upgraded
- Church-Specific Dynamic Blocks:
sermon_feed: Pulls dynamic data fromSermonswith filters and a newnotes_urlfor downloading notes.staff_directory: Dynamically loads fromUsermodel, filtering by active roles anddepartment. Supportsbio,title, andsocial_links.events_feed: Added campus filtering and visual improvements.giving_cta: Added "Quick Give" functionality linked directly toFunds.
- Engagement & Automation:
live_stream: Respects a global tenantlive_stream_activesetting from the Admin Settings to show/hide dynamically.
- Layout & Structure:
- Enhanced
columnsblock to support nested dynamic blocks (likevideo,quote). - Upgraded
dividerblock with decorative wave patterns. - Added
mosaicgrid layout togalleryblock for varying photo sizes.
- Enhanced
- Database & Resources:
- Added
notes_urltosermonstable. - Added
title,department,bio, andsocial_linkstouserstable. - Fully translated all new block properties in FR and EN.
- Added
- Dynamic & Motion Updates:
- Validation:
php artisan test tests/Feature/Api/V1/PageApiTest.php: passcomposer quality: pass
- Files Changed:
app/Filament/Resources/PageResource.phpapp/Filament/Resources/SermonResource.phpapp/Filament/Resources/UserResource.phpapp/Models/Sermon.php,app/Models/User.phpresources/views/components/blocks/*.blade.php- Migrations for sermons and users.
2026-03-12 — Stage 3: Notification Dashboard & Octane Benchmark
- Status: Done
- Goal: Enhance notification observability and validate system stability under multi-tenant load.
- Summary:
- Notification Dashboard: Implemented a dedicated "Operations Dashboard" within the
NotificationDispatchResourcefeaturing:NotificationStats: Real-time tracking of sent, pending, and failed dispatches.NotificationTrends: Visual line chart of notification volume over the last 7 days.RecentFailures: Instant visibility into the most recent dispatch errors.
- Observability: Organized widgets into a resource-specific namespace (
app/Filament/Resources/NotificationDispatchResource/Widgets) to keep the main Admin Dashboard focused on church growth metrics. - Load Testing: Created a
BenchmarkOctaneArtisan command to simulate concurrent activity across hundreds of tenants, measuring request performance and memory stability. - Stability Fixes: Resolved a non-static property bug in
ChartWidgetand optimized benchmark concurrency usingHttp::pool.
- Notification Dashboard: Implemented a dedicated "Operations Dashboard" within the
- Validation:
php artisan app:benchmark: verified execution flow.- UI verified: Widgets correctly rendered on the Notification Dispatch list page.
- Files:
app/Filament/Resources/NotificationDispatchResource/Widgets/NotificationStats.phpapp/Filament/Resources/NotificationDispatchResource/Widgets/NotificationTrends.phpapp/Filament/Resources/NotificationDispatchResource/Widgets/RecentNotificationFailures.phpapp/Filament/Resources/NotificationDispatchResource/Pages/ListNotificationDispatches.phpapp/Console/Commands/BenchmarkOctane.php
2026-03-12 — Stripe Billing Integration & Plan Limits Hardening
Status: Done
Goal: Complete the Stripe Cashier integration for Ekklesia Premium subscriptions and resolve production readiness bugs.
Summary:
- Stripe Subscriptions: Integrated
laravel/cashierfor Tenant subscriptions. AddedSubscriptionandSubscriptionItemmodels. Set upStripeWebhookControllerwith a dedicated rate-limited API route to listen for Stripe webhooks and correctly initialize multi-tenancy based oncustomer(Stripe ID). - Billing UI Actions: Upgraded the Filament
Billingpage to show active subscription status and handle Upgrade, Cancel, Resume, and Billing Portal actions using Cashier's API. - Plan Limits Enforcer Fix: Resolved a critical bug where
PlanLimitsEnforcerwas performing global counts instead of tenant-scoped queries for members and campuses, allowing tenants to exceed limits. - PaymentManager Fix: Fixed
getSetting()fallback to correctly retrieve configuration from the JSONdatacolumn usinggetAttribute(). - Translations: Added missing English translations for Attendances and Birthdays.
- Stripe Subscriptions: Integrated
Validation:
composer test: pass (582 passed, 1597 assertions)composer quality: pass
Files:
app/Http/Controllers/Api/V1/SubscriptionController.phpapp/Models/Subscription.php,app/Models/SubscriptionItem.php,app/Models/Tenant.phpapp/Filament/Pages/Billing.phpapp/Services/Billing/PlanLimitsEnforcer.phproutes/api.phptests/Feature/ProductionReadinessReproTest.phptests/Feature/Billing/SubscriptionTest.php
2026-03-12 — High-Quality Production Hardening (Final Batch)
- Status: Done
- Goal: Complete all remaining blocking, high, and medium priority production readiness tasks.
- Summary:
- Subscription API: Added full subscription management API (
/api/v1/subscriptions/*) with status, plans, subscribe (checkout), portal, cancel, and resume endpoints. - Security (CSP): Implemented a strict Content-Security-Policy header in
SecurityHeadersmiddleware, specifically tuned for Stripe, fonts, and internal resources. - Security (CORS): Published and configured
config/cors.phpto properly handle cross-origin API requests. - i18n (English): Completed 100% English translation coverage by creating the 9 missing files (
bulk_messages,campaigns,devotionals,funds,households,prayer_requests,reading_plans,service_types,testimonies). - PII Scrubbing: Verified that sensitive data logging is blocked via the
scrubPiimethod andlogExceptconfiguration inLogsActivityWithTenant. - Validation:
composer test: pass (587 passed, 1608 assertions)composer quality: pass- Files:
app/Http/Controllers/Api/V1/SubscriptionController.php(New)app/Http/Resources/V1/SubscriptionResource.php(New)app/Http/Resources/V1/PlanLimitResource.php(New)tests/Feature/Api/V1/SubscriptionApiTest.php(New)app/Http/Middleware/SecurityHeaders.php(Updated with CSP)config/cors.php(New/Published)lang/en/*.php(9 new files)routes/api.php(Updated)