rayzenai / project-management-app
PMOPM — project & task tracking workspace (Laravel + Inertia + Svelte) with a JSON API for companion clients and external systems.
Package info
github.com/rayzenai/project-management-app
Type:project
pkg:composer/rayzenai/project-management-app
Requires
- php: ^8.5
- inertiajs/inertia-laravel: ^3.0
- laravel/framework: ^13.17
- laravel/sanctum: ^4.3
- laravel/tinker: ^3.0
- laravel/wayfinder: ^0.1.14
- rayzenai/project-management: ^1.1
Requires (Dev)
- fakerphp/faker: ^1.24
- larastan/larastan: ^3.9
- laravel/pail: ^1.2.5
- laravel/pao: ^1.0.6
- laravel/pint: ^1.27
- laravel/sail: ^1.53
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.9.3
- pestphp/pest: ^4.7
- pestphp/pest-plugin-drift: ^4.1
- pestphp/pest-plugin-laravel: ^4.1
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Project & task tracking workspace for PMOPM (Office of the Prime Minister and Council of Ministers) — projects → tasks → subtasks, assignments, notes, contacts, teams, members, activity log, notifications, and the 100-point plan tracker — with a JSON API for the companion mobile client and for any other system that needs to read or manage tasks.
This is a standalone application. Everything (models, services, controllers,
authorization, Svelte UI, migrations) lives in this repo; it no longer depends on
the retired rayzenai/project-management Composer package.
Tech stack
- Backend: Laravel 13, PHP 8.5, Sanctum (API token auth)
- Frontend: Inertia.js + Svelte 5, Tailwind CSS v4, Vite
- Testing: Pest (
tests/Feature/Workspace/**covers the domain,tests/Feature/Workspace/Api/**the JSON API) - Quality: Laravel Pint, PHPStan/Larastan (level 7), ESLint + Prettier +
svelte-check
Requirements
- PHP 8.5
- Composer 2
- Node 22 + npm
- PostgreSQL — task search uses a
pg_trgmmigration; the migration no-ops on other drivers (the test suite runs on SQLite) but you lose fuzzy search - Redis — cache, sessions, and the queue
- Docker (optional) — only to run the deployment image locally via
./dock.sh
Quick start
git clone git@github.com:rayzenai/project-management-app.git pmopm
cd pmopm
composer setup
composer setup runs composer install, copies .env.example to .env,
generates the app key, runs migrations, and installs + builds the frontend.
Manual equivalent:
composer install cp .env.example .env php artisan key:generate createdb pmopm # then set DB_* / REDIS_* in .env php artisan migrate php artisan db:seed # creates the PM_SUPER_ADMINS logins npm install npm run build # or: npm run dev
Serve with php artisan serve, or composer dev (server + queue listener +
Vite, concurrently).
Configuration
| Variable | Purpose |
|---|---|
PM_SUPER_ADMINS |
Comma-separated emails granted the manage-workspace gate (full super-admin access) |
PM_SUPER_ADMIN_PASSWORD |
Default password used by WorkspaceSuperadminSeeder |
PM_TRASH_TTL_DAYS |
Days a soft-deleted record survives before workspace:prune-trashed removes it (default 30) |
PM_REMINDERS_RUN_AT |
Time of day workspace:send-deadline-reminders runs (default 08:00) |
Config files worth knowing:
config/project-management.php— task status workflow, super-admins, trash TTL, reminder cadence.config/government.php— the 100-point plan metadata (categories, deadline types, oath date) that drives the plan tracker and task category labels.config/themes.php— the system / light / dark token sets for web and mobile appearance settings.
Using the app
/redirects to/workspace.- Web (Inertia) —
/workspace/*, session auth, branded login at/workspace/login. - JSON API —
/api/v1/*, Sanctum bearer auth. Full reference indocs/api.md.
Letting another system manage tasks
Mint a token for a service account and call the JSON API with it — no login flow needed:
php artisan workspace:api-token bot@example.com --name=crm-sync [--expires-days=90] [--revoke]
See docs/api.md → Integrating another system.
Console commands
| Command | Purpose |
|---|---|
workspace:api-token |
Issue / revoke an API token for an external system |
workspace:prune-trashed [--pretend] |
Force-delete soft-deleted rows past the trash TTL (scheduled daily) |
workspace:send-deadline-reminders [--pretend] |
Heads-up / due-today / overdue notifications (scheduled daily at PM_REMINDERS_RUN_AT) |
digest:send-weekly [--pretend] |
Weekly project digest email to subscribers |
Architecture
Request flow, mirrored across the web and API surfaces:
Route → Controller (thin) → FormRequest::authorize() → Service (returns ServiceResult) → JsonResource
Read CLAUDE.md for the full guide (authorization model, domain model, theming
layer, conventions).
Testing & quality
composer test # config:clear + pint check + phpstan + Pest php artisan test # Pest only composer lint # Pint (fixes) composer types:check # PHPStan npm run lint # ESLint (fixes) npm run format # Prettier (fixes) npm run types:check # svelte-check composer ci:check # everything CI runs
CI
GitHub Actions run on every push/PR:
.github/workflows/lint.yml— Pint + frontend lint/format.github/workflows/tests.yml— Pest, PHPStan, and the asset build
Docker & deployment
Dockerfile is the deployment artifact, built by Dokploy straight from a git
clone. Three stages: Composer vendor → asset build (PHP + Node 22, because the
wayfinder Vite plugin shells out to php artisan wayfinder:generate during
vite build) → serversideup/php:8.5-fpm-nginx serving on port 8080.
AUTORUN_ENABLED=true makes the container run migrate --force, storage:link,
and optimize at boot.
Run that exact image locally against your own Postgres and Redis:
./dock.sh build # build the image and start it on http://localhost:8101 ./dock.sh logs # follow container logs ./dock.sh artisan migrate:status ./dock.sh shell ./dock.sh down
Deployed as three Dokploy applications off this one image (./deploy.sh
triggers them):
| Application | Command | Notes |
|---|---|---|
pmopm-api |
(image default) | The only app with a domain; runs migrations via AUTORUN |
pmopm-scheduler |
php artisan schedule:work |
workspace:prune-trashed daily + workspace:send-deadline-reminders at PM_REMINDERS_RUN_AT |
pmopm-queue |
php artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 |
Both workers need AUTORUN_ENABLED=false (so they don't race the web app's
migrations) and a disabled Swarm healthcheck (they don't run nginx). See
~/rayzen/DOKPLOY.md §4 for the full playbook.