From 02dfc1568c4b1a1a0626cf17ae153330070cac7e Mon Sep 17 00:00:00 2001 From: Jonathan van Rij Date: Tue, 3 Feb 2026 09:08:05 +0100 Subject: [PATCH] this commit adds more documentation to the application --- CLAUDE.md | 4 +- docs/implementation-plan.md | 272 +++++++++++++++++++++++++++++++++ docs/index.md | 6 + docs/technical-requirements.md | 82 ++++++++-- docs/theming-templating-vue.md | 141 +++++++++++++++++ 5 files changed, 488 insertions(+), 17 deletions(-) create mode 100644 docs/implementation-plan.md diff --git a/CLAUDE.md b/CLAUDE.md index a35c494..adebb6d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,12 +2,12 @@ # CLAUDE.md – Go No Go ## Project Overview -Laravel 12 application with Laravel Nova 5 administration panel. The admin panel is served at `/cp`. Frontend users authenticate via Azure AD SSO (Laravel Socialite). Nova administrators use Fortify with 2FA and email verification. +Laravel 12 application with Laravel Nova 5 administration panel. The admin panel is served at `/cp`. Frontend users authenticate via Azure AD SSO (Laravel Socialite). Nova uses its built-in authentication. - **Framework:** Laravel 12 - **Admin Panel:** Laravel Nova 5 at `/cp` - **Auth (frontend):** Laravel Socialite with Azure AD SSO -- **Auth (Nova):** Fortify with 2FA + email verification +- **Auth (Nova):** Built-in Nova authentication - **Database:** MySQL (`go-no-go`) - **Local URL:** http://go-no-go.test (Laravel Herd) diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md new file mode 100644 index 0000000..d1884f6 --- /dev/null +++ b/docs/implementation-plan.md @@ -0,0 +1,272 @@ +# Implementation Plan: Go No Go + +Bare Laravel 12 + Nova 5 install to production-ready questionnaire application. + +**Current state:** Laravel framework and Nova installed (Nova auth already working). No Inertia, Vue, Socialite, or application code. + +**Reference docs:** `docs/technical-requirements.md`, `docs/theming-templating-vue.md`, `docs/questions-*.md` + +--- + +## Step 1: Install Dependencies and Configure Build + +[ ] **Install and configure the frontend toolchain and auth packages.** + +Install Vue 3, Inertia.js v2 (server-side adapter + client-side `@inertiajs/vue3`), Tailwind CSS 4 with design tokens from `docs/theming-templating-vue.md`, Heroicons, and Laravel Socialite. Configure `vite.config.js` for Vue + Inertia. Configure `tailwind.config.js` with the project color tokens (primary `#d1ec51`, secondary `#00b7b3`, surface `#2b303a`). Register the `HandleInertiaRequests` middleware. Create the Inertia root Blade template (`app.blade.php`). + +**Creates:** +- `vite.config.js` -- Vue plugin + Inertia page resolver +- `tailwind.config.js` -- custom color tokens +- `resources/views/app.blade.php` -- Inertia root template with `@vite` and `@inertiaHead` +- `resources/js/app.js` -- Vue + Inertia app bootstrap +- `app/Http/Middleware/HandleInertiaRequests.php` +- `composer.json` updates: `inertiajs/inertia-laravel`, `laravel/socialite` +- `package.json` updates: `vue`, `@inertiajs/vue3`, `@vitejs/plugin-vue`, `@heroicons/vue`, `tailwindcss` + +**Validates:** `npm run build` completes without errors. Visiting any route renders the Inertia root template without a blank page or console errors. + +--- + +## Step 2: Database -- Migrations and Models + +[ ] **Create all database tables and Eloquent models.** + +Create migrations for: `categories`, `question_groups`, `questions`, `screenings`, `screening_answers`, `sessions`, `answers`, `logs`. Create corresponding Eloquent models with relationships, `$fillable`, `$casts`, and any unique constraints (e.g., `answers` has a unique composite on `session_id + question_id`, `screening_answers` has a unique composite on `screening_id + question_number`). The `logs` table has no `updated_at` column (append-only). The `sessions` table has a nullable `screening_id` FK linking back to the screening that authorized it. See `docs/technical-requirements.md` section 5 for exact column definitions. + +**Creates:** +- `database/migrations/*_create_categories_table.php` +- `database/migrations/*_create_question_groups_table.php` +- `database/migrations/*_create_questions_table.php` +- `database/migrations/*_create_screenings_table.php` +- `database/migrations/*_create_screening_answers_table.php` +- `database/migrations/*_create_sessions_table.php` +- `database/migrations/*_create_answers_table.php` +- `database/migrations/*_create_logs_table.php` +- `app/Models/Category.php` +- `app/Models/QuestionGroup.php` +- `app/Models/Question.php` +- `app/Models/Screening.php` +- `app/Models/ScreeningAnswer.php` +- `app/Models/Session.php` +- `app/Models/Answer.php` +- `app/Models/Log.php` + +**Validates:** `herd php artisan migrate:fresh` runs cleanly. All models load without errors. `herd php artisan app:schema-generate` produces `database/schema.md` matching the spec. + +--- + +## Step 3: Authentication + +[ ] **Set up Socialite for frontend SSO.** + +Nova already handles its own admin authentication at `/cp` (built-in). Configure Socialite with the Azure AD driver for frontend SSO. Create `SocialiteController` with `redirect`, `callback`, and `logout` methods. The callback creates or matches users by email. Add the `/login-jonathan` dev auto-login route (local/testing environments only). + +**Creates:** +- `app/Http/Controllers/Auth/SocialiteController.php` +- `config/services.php` Azure AD configuration +- Route registrations in `routes/web.php` for `/login`, `/auth/callback`, `/logout`, `/login-jonathan` + +**Validates:** Visiting `/login` redirects to Azure AD (or errors with missing credentials, confirming the redirect is wired). Nova login at `/cp` continues to work as before. `/login-jonathan` logs in and redirects to `/` in local environment. + +--- + +## Step 4: Frontend Layout and Shared Components + +[ ] **Build the persistent layout and all shared Vue components.** + +Create `AppLayout.vue` as a persistent Inertia layout with `PageHeader` containing the Piccadilly logo and page title. Create shared components: `AppLogo`, `PageHeader`, `AppButton` (with variant/size/href/disabled/loading props per `docs/theming-templating-vue.md`), `ScoreIndicator` (color-coded by threshold). Wire up `HandleInertiaRequests` shared data (authenticated user, flash messages). Add the `EncryptHistory` middleware for sensitive session data. + +**Creates:** +- `resources/js/Layouts/AppLayout.vue` +- `resources/js/Components/AppLogo.vue` +- `resources/js/Components/PageHeader.vue` +- `resources/js/Components/AppButton.vue` +- `resources/js/Components/ScoreIndicator.vue` +- Shared data configuration in `HandleInertiaRequests.php` +- `EncryptHistory` middleware registration + +**Validates:** A test page using `AppLayout` renders the header with logo and title. `AppButton` renders all three variants. Shared data (`$page.props.auth.user`) is accessible in any page component. + +--- + +## Step 5: Page Stubs and Click-Through Flow + +[ ] **Create stub pages and controllers so the full two-stage navigation flow is clickable end to end.** + +Create stub Vue page components for the entire flow: `Pages/Landing.vue` (intro text + Continue button), `Pages/Screening/Show.vue` (placeholder for pre-screening questions), `Pages/Screening/Result.vue` (placeholder pass/fail + category picker), `Pages/Session/Show.vue` (placeholder for basic info + questionnaire), `Pages/Session/Result.vue` (placeholder final result + Again button). Create stub controllers: `LandingController@index`, `ScreeningController` with `store`, `show`, `update`, `result` methods, `SessionController` with `store`, `show`, `update`, `result` methods. Wire up all routes in `web.php` matching the route table in `docs/technical-requirements.md` section 6. + +This step is intentionally minimal -- no real data, no real forms. The goal is a complete clickable flow: Landing → Screening → Screening Result (with hardcoded category list) → Session/Show → Session/Result → back to Landing. + +**Creates:** +- `resources/js/Pages/Landing.vue` +- `resources/js/Pages/Screening/Show.vue` +- `resources/js/Pages/Screening/Result.vue` +- `resources/js/Pages/Session/Show.vue` +- `resources/js/Pages/Session/Result.vue` +- `app/Http/Controllers/LandingController.php` +- `app/Http/Controllers/ScreeningController.php` +- `app/Http/Controllers/SessionController.php` +- All frontend route definitions in `routes/web.php` + +**Validates:** Log in via `/login-jonathan`. See the landing page with Continue button. Click Continue to reach pre-screening. Click through to screening result. Select a category to start a session. Navigate through to the final result page. Click "Again" to return to landing. All page transitions work without errors. Browser back/forward works. + +--- + +## Step 6: Seeders -- Categories, Question Groups, and Questions + +[ ] **Seed all reference data: pre-screening questions, 6 categories, their question groups, and all questions with correct field configuration.** + +Create `DatabaseSeeder` (or dedicated seeders) for: the 10 pre-screening Yes/No questions (stored in config or a seeder — these are not in the `questions` table, they are handled by the screening flow), the 6 categories (Audit, Outsource, Solution, Digital Solutions, Legal, Tax) with correct `sort_order`, all question groups per category with names, descriptions, and `scoring_instructions` where applicable, and all questions with the correct `has_yes`, `has_no`, `has_na`, `details`, `is_scored`, and `sort_order` values. Source question data from `docs/questions-audit.md`, `docs/questions-outsource-solutions.md`, `docs/questions-digital-solutions.md`, `docs/questions-legal.md`, `docs/questions-tax.md`. + +**Creates:** +- `database/seeders/DatabaseSeeder.php` +- `database/seeders/CategorySeeder.php` (or inline) +- `database/seeders/QuestionSeeder.php` (or per-category seeders) +- Pre-screening question definitions (config file or seeder) + +**Validates:** `herd php artisan migrate:fresh --seed` runs cleanly. Verify category count is 6. Verify each category has the expected number of question groups. Spot-check several questions to confirm field configuration matches the source docs. Verify 10 pre-screening questions are available. + +--- + +## Step 7: Landing Page and Pre-Screening Flow + +[ ] **Build the real landing page and pre-screening questionnaire.** + +Build the Landing page: describes the application purpose (what Go/No Go is, what the user will do), with a "Continue" button that creates a screening and redirects to the pre-screening questions. Build the Screening/Show page: render the 10 Yes/No pre-screening questions using `useForm`. Save screening answers via `PUT /screening/{screening}`. On submit, calculate the screening score (Yes=1 point each), determine pass/fail (>=5 = pass), and redirect to the screening result page. Build Screening/Result: if failed (<5 points), show No Go result with "Again" button back to `/`. If passed, show the category picker (list of 6 categories) with "Start" buttons. Selecting a category creates a session linked to this screening and redirects to Session/Show. + +**Creates:** +- Updated `resources/js/Pages/Landing.vue` -- application description + Continue button +- Updated `resources/js/Pages/Screening/Show.vue` -- 10 Yes/No questions with useForm +- Updated `resources/js/Pages/Screening/Result.vue` -- pass/fail display + category picker on pass +- Updated `app/Http/Controllers/LandingController.php` +- Updated `app/Http/Controllers/ScreeningController.php` -- full store/show/update/result logic +- Screening scoring logic + +**Validates:** Landing page renders with description and Continue button. Clicking Continue creates a screening and shows 10 questions. Answering all No (score 0) → No Go result. Answering 5+ Yes → pass, category picker shown. Selecting a category creates a session and navigates to Session/Show. + +--- + +## Step 8: Basic Info Form + +[ ] **Build the basic info step as the first section of Session/Show.** + +Add the basic info form fields to the Session/Show page: `client_name`, `client_contact`, `lead_firm_name`, `lead_firm_contact`. Use Inertia `useForm` to save data to the session's `basic_info` JSON column via `PUT /sessions/{session}`. All four fields are required before the user can proceed to questions. Display validation errors inline. + +**Creates:** +- Updated `resources/js/Pages/Session/Show.vue` -- basic info form section +- Updated `app/Http/Controllers/SessionController.php` -- handle basic info save in `update` method +- Form validation rules for basic info fields + +**Validates:** Starting a new session shows the basic info form. Submitting with empty fields shows validation errors. Filling all fields and saving persists to the `basic_info` JSON column. Refreshing the page retains the saved values. + +--- + +## Step 9: Questionnaire Flow -- Question Rendering and Answer Saving + +[ ] **Build the full questionnaire UI with all 6 question patterns and answer persistence.** + +Create the `QuestionCard` component that renders questions based on their field configuration (see the 6 patterns in `docs/technical-requirements.md` section 5). Render question groups as steps within `Session/Show`. Each step shows its questions, scoring instructions (if present), and a "Next" / "Previous" navigation. Save answers via `PUT /sessions/{session}` using Inertia `useForm` with partial reloads (only reload answers/score, not the full question set). Handle `details` textarea visibility: show when `details` is `required` or `optional`; show conditionally for `req_on_yes` / `req_on_no` based on the selected value. Include the Additional Comments textarea as the final step. + +**Creates:** +- `resources/js/Components/QuestionCard.vue` +- Updated `resources/js/Pages/Session/Show.vue` -- step navigation, question group rendering +- Updated `app/Http/Controllers/SessionController.php` -- answer saving logic in `update` +- Answer validation rules + +**Validates:** Navigate through all question groups for a category. Each question renders the correct UI pattern (radio buttons, text-only, details visibility). Answers save without page reload. Navigating away and back retains saved answers. All 6 question patterns render correctly. + +--- + +## Step 10: Scoring and Result + +[ ] **Implement server-side scoring and the result page.** + +Calculate the score server-side from `is_scored` answers: Yes=1, No=0, NA=excluded. Return the running score and current result threshold via Inertia props during the questionnaire. Build the `ScoreIndicator` into the questionnaire flow with live color updates (green 10+, yellow 5-9, red 1-4). Build the real `Session/Result` page showing the final GO / NO GO / Consult Leadership result with color coding and an "Again" button that returns to `/`. On session submission, calculate final score and persist `score`, `result`, `status=completed`, and `completed_at` to the session. + +**Creates:** +- Scoring logic (service class or model method) +- Updated `app/Http/Controllers/SessionController.php` -- scoring in `update` (completion) and `result` +- Updated `resources/js/Pages/Session/Show.vue` -- ScoreIndicator integration +- Updated `resources/js/Pages/Session/Result.vue` -- final result display +- Updated `resources/js/Components/ScoreIndicator.vue` -- live threshold colors + +**Validates:** Complete a session with known answers and verify the score matches manual calculation. ScoreIndicator changes color as answers are saved. Result page displays the correct GO/NO GO/Consult result. Session record in the database has correct `score`, `result`, and `completed_at`. + +--- + +## Step 11: Activity Logging + +[ ] **Add append-only activity logging for analytics.** + +Create a logging service (or helper) that writes to the `logs` table. Integrate log writes into all relevant actions: `login`, `logout`, `screening_started`, `screening_completed`, `session_started`, `session_completed`, `session_abandoned`, `answer_saved`, `step_viewed`. Each log includes `user_id`, `session_id`, `category_id`, `action`, and `metadata` JSON as defined in `docs/technical-requirements.md` section 5 (logs table). The Log model should have no `updated_at` and should prevent updates/deletes. + +**Creates:** +- `app/Services/ActivityLogger.php` (or similar) +- Log write calls in `SocialiteController`, `SessionController`, and relevant middleware/listeners +- Updated `app/Models/Log.php` -- guard against updates/deletes + +**Validates:** Perform a full user flow (login, start session, answer questions, view steps, complete session, logout). Query the `logs` table and verify all expected actions are recorded with correct metadata. + +--- + +## Step 12: Nova Resources and Policies + +[ ] **Create all Nova resources, policies, and Excel export actions.** + +Create Nova resources: `CategoryResource`, `QuestionGroupResource`, `QuestionResource`, `ScreeningResource`, `SessionResource`, `AnswerResource`, `LogResource`. Create corresponding policies enforcing the permission matrix from `docs/technical-requirements.md` section 9 (most are read-only; only Question.text is editable). Apply field behaviors: all fields filterable, sortable, and copyable where applicable. Set menu visibility (only Question, Screening, Session, Log appear in sidebar). Install `maatwebsite/laravel-nova-excel` and add `DownloadExcel` action to every resource. + +**Creates:** +- `app/Nova/CategoryResource.php` +- `app/Nova/QuestionGroupResource.php` +- `app/Nova/QuestionResource.php` +- `app/Nova/ScreeningResource.php` +- `app/Nova/SessionResource.php` +- `app/Nova/AnswerResource.php` +- `app/Nova/LogResource.php` +- `app/Policies/CategoryPolicy.php` +- `app/Policies/QuestionGroupPolicy.php` +- `app/Policies/QuestionPolicy.php` +- `app/Policies/ScreeningPolicy.php` +- `app/Policies/SessionPolicy.php` +- `app/Policies/AnswerPolicy.php` +- `app/Policies/LogPolicy.php` +- Excel export action on all resources + +**Validates:** Log in to Nova at `/cp`. QuestionResource, SessionResource, and LogResource appear in the sidebar. CategoryResource, QuestionGroupResource, and AnswerResource do not appear in the sidebar but are accessible via relational fields. Only Question.text is editable. Export to Excel works on each resource index. Attempting create/update/delete on read-only resources is denied. + +--- + +## Step 13: Testing + +[ ] **Write automated tests for critical paths.** + +Write PHPUnit feature tests for: pre-screening scoring (pass/fail at 5-point threshold), category questionnaire scoring (known inputs produce expected scores), session lifecycle (create, answer, complete, abandon), answer saving and validation, policy authorization (CRUD permissions per resource), Socialite auth flow (mock Azure AD callback). Write Cypress E2E tests for: full two-stage flow (landing → screening → category selection → questionnaire → result → again), pre-screening fail path (score <5 → No Go), scoring display updates during questionnaire, result page shows correct outcome. + +**Creates:** +- `tests/Feature/ScreeningScoringTest.php` +- `tests/Feature/ScoringTest.php` +- `tests/Feature/SessionLifecycleTest.php` +- `tests/Feature/AnswerTest.php` +- `tests/Feature/PolicyTest.php` +- `tests/Feature/AuthTest.php` +- `tests/cypress/e2e/questionnaire-flow.cy.js` +- `tests/cypress/e2e/scoring-display.cy.js` +- `tests/cypress/e2e/result-page.cy.js` + +**Validates:** `herd php artisan test` passes all PHPUnit tests. `npx cypress run` passes all E2E tests. No regressions in existing functionality. + +--- + +## Step 14: Production Readiness + +[ ] **Final hardening, configuration, and review.** + +Update `.env.example` with all required environment variables (Azure AD credentials, database, app URL, Nova license key). Create custom error pages (404, 500) using the app design system. Review eager loading across all controllers to prevent N+1 queries. Verify CSRF protection on all POST/PUT routes. Confirm `EncryptHistory` middleware is active on session routes. Run a full smoke test of the complete flow: SSO login, landing page, pre-screening (pass and fail paths), category selection, basic info, complete all questions, view result, click Again, check Nova admin, verify logs, export Excel. + +**Creates:** +- Updated `.env.example` +- `resources/js/Pages/Error.vue` (or 404/500 specific pages) +- Query optimization passes on controllers +- Security checklist verification + +**Validates:** Fresh clone + `composer install` + `npm install` + `herd php artisan migrate:fresh --seed` + `npm run build` produces a working application. Full user flow works end to end. Nova admin flow works. No console errors, no N+1 queries, no missing environment variables. diff --git a/docs/index.md b/docs/index.md index 4403bda..f7bc69e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,6 +8,12 @@ ### Root Level - `docs/index.md` - This file; master index of all project documentation - `docs/technical-requirements.md` - Technical requirements for the questionnaire system +- `docs/flow.md` - Mermaid flowchart of the two-stage Go/No Go process (pre-screening → category questionnaire) +- `docs/implementation-plan.md` - Step-by-step implementation plan from bare install to production-ready (14 steps with status tracking) + +### Frontend + +- `docs/theming-templating-vue.md` - Design tokens, Tailwind config, layout, shared Vue components, icon and scoring color standards ### Agents diff --git a/docs/technical-requirements.md b/docs/technical-requirements.md index 05e3828..7f7ba61 100644 --- a/docs/technical-requirements.md +++ b/docs/technical-requirements.md @@ -13,7 +13,7 @@ ## 2. Tech Stack - **Admin Panel:** Laravel Nova 5 at `/cp` - **Database:** MySQL (`go-no-go`) - **Auth (frontend):** Laravel Socialite with Azure AD SSO -- **Auth (Nova):** Laravel Fortify (email/password + 2FA) +- **Auth (Nova):** Built-in Nova authentication - **Excel Export:** `maatwebsite/laravel-nova-excel` ## 3. Two Separate Interfaces @@ -30,7 +30,7 @@ ### Authentication Each interface has its own authentication method: - **Frontend:** Azure AD Single Sign-On via Laravel Socialite. Users visit `/login`, get redirected to Azure AD, and are returned to `/auth/callback`. Socialite handles the OAuth flow. Users are created or matched by email on first login. No password stored for frontend users. -- **Nova:** Standard email/password login via Laravel Fortify with 2FA and email verification at `/cp/login`. Only administrators access Nova. +- **Nova:** Built-in Nova authentication at `/cp/login`. Already configured and working. Only administrators access Nova. ## 4. Frontend Architecture (Vue + Inertia.js v2) @@ -149,6 +149,7 @@ ### sessions | id | bigint unsigned | PK, auto-increment | | user_id | bigint unsigned | FK to users.id, required | | category_id | bigint unsigned | FK to categories.id, required | +| screening_id | bigint unsigned | FK to screenings.id, nullable | | status | varchar(50) | required, enum: `in_progress`, `completed`, `abandoned` | | score | integer | nullable, calculated from scored answers | | result | varchar(50) | nullable, enum: `go`, `no_go`, `consult_leadership` | @@ -187,6 +188,34 @@ ### answers **Unique constraint:** `(session_id, question_id)` -- one answer per question per session. +### screenings + +Pre-screening sessions. One per attempt. Tracks the 10 Yes/No pre-screening questions before category selection. + +| Column | Type | Constraints | +|--------|------|-------------| +| id | bigint unsigned | PK, auto-increment | +| user_id | bigint unsigned | FK to users.id, required | +| score | integer | nullable, calculated from screening answers | +| passed | boolean | nullable, true if score >= 5 | +| created_at | timestamp | nullable | +| updated_at | timestamp | nullable | + +### screening_answers + +Stores answers to the 10 pre-screening questions. + +| Column | Type | Constraints | +|--------|------|-------------| +| id | bigint unsigned | PK, auto-increment | +| screening_id | bigint unsigned | FK to screenings.id, required | +| question_number | unsigned integer | required, 1-10 | +| value | varchar(10) | required, `yes` or `no` | +| created_at | timestamp | nullable | +| updated_at | timestamp | nullable | + +**Unique constraint:** `(screening_id, question_number)` -- one answer per question per screening. + ### logs Comprehensive activity log for usage analytics. Every meaningful user action is recorded to enable statistics on: who is using the application, which categories are used most, usage frequency, whether certain questions are always answered the same way, and whether specific firms or groups are more active than others. @@ -209,6 +238,8 @@ #### Logged Actions |--------|------|----------| | `login` | User completes SSO | `{email, firm_name}` | | `logout` | User logs out | — | +| `screening_started` | New screening created | — | +| `screening_completed` | Screening submitted | `{score, passed}` | | `session_started` | New session created | `{category_id}` | | `session_completed` | Session submitted | `{category_id, score, result}` | | `session_abandoned` | Session marked abandoned | `{category_id}` | @@ -221,12 +252,16 @@ ## 6. Frontend Routes & Controllers | Method | Route | Controller | Vue Page | Description | |--------|-------|------------|----------|-------------| -| GET | `/` | DashboardController@index | Dashboard | Start page. Start new session or view past sessions. | +| GET | `/` | LandingController@index | Landing | Landing page. Describes the application. Has a "Continue" button. | | GET | `/login` | SocialiteController@redirect | — | Redirects to Azure AD for SSO | | GET | `/auth/callback` | SocialiteController@callback | — | Azure AD callback, creates/finds user, logs in | | POST | `/logout` | SocialiteController@logout | — | Logout | -| POST | `/sessions` | SessionController@store | — | Create a new session for a selected category | -| GET | `/sessions/{session}` | SessionController@show | Session/Show | Basic info form + questionnaire flow | +| POST | `/screening` | ScreeningController@store | — | Create a new screening session | +| GET | `/screening/{screening}` | ScreeningController@show | Screening/Show | Pre-screening Yes/No questions (10 questions) | +| PUT | `/screening/{screening}` | ScreeningController@update | — | Save screening answers | +| GET | `/screening/{screening}/result` | ScreeningController@result | Screening/Result | Pre-screening result (pass/fail). If pass, shows category picker. | +| POST | `/sessions` | SessionController@store | — | Create a new session for a selected category (from screening result page) | +| GET | `/sessions/{session}` | SessionController@show | Session/Show | Basic info form + category questionnaire flow | | PUT | `/sessions/{session}` | SessionController@update | — | Save answers, update basic info, submit session | | GET | `/sessions/{session}/result` | SessionController@result | Session/Result | View final result after submission | @@ -235,22 +270,37 @@ ## 6. Frontend Routes & Controllers ## 7. Questionnaire Flow 1. User logs in via Azure AD SSO -2. `/` is the start page — shows: start new session, view past sessions -3. User selects a category to start a new session -4. Basic Information form (client name, client contact, lead firm name, lead firm contact) -5. Step through question groups in order (each group is a page/step) -6. Each question renders based on its field configuration: +2. `/` is the landing page — describes the application purpose, has a "Continue" button +3. User clicks Continue → creates a screening session, redirected to pre-screening questions +4. Pre-screening: 10 Yes/No scored questions (1 point per Yes) +5. Pre-screening result: + - Score < 5 points → No Go result page (red). "Again" button returns to `/`. + - Score >= 5 points → pass. Page shows category picker to continue. +6. User selects a category → creates a session, redirected to Session/Show +7. Basic Information form (client name, client contact, lead firm name, lead firm contact) +8. Step through question groups in order (each group is a page/step) +9. Each question renders based on its field configuration: - If `has_yes`/`has_no`/`has_na` are all false → render as open text (textarea only) - If any are true → render radio buttons for the enabled options - If `details` is set → render a details textarea with the appropriate requirement behavior (`required`, `optional`, `req_on_yes`, `req_on_no`) -7. Running score displayed (for scored questions only) -8. Color-coded result indicator updates live (green/yellow/red) -9. Final step: Additional comments free text -10. Submit and view result (GO / NO GO / Consult Leadership) -11. Session saved with score and result +10. Running score displayed (for scored questions only) +11. Color-coded result indicator updates live (green/yellow/red) +12. Final step: Additional comments free text +13. Submit and view result (GO / NO GO / Consult Leadership) +14. Session saved with score and result +15. All result pages have an "Again" button that returns to `/` ## 8. Scoring Logic +### Pre-Screening Threshold + +The pre-screening uses a simple pass/fail: +- 10 Yes/No questions, each Yes = 1 point +- Score >= 5 → pass (proceed to category selection) +- Score < 5 → fail (No Go, return to start) + +### Category Questionnaire Scoring + - Only questions with `is_scored = true` are scored - **Yes** = 1 point - **No** = 0 points @@ -274,6 +324,7 @@ ## 9. Nova Resources & Policies | CategoryResource | Category | CategoryPolicy | No | Lookup data only — no dedicated resource page. Exists as a Nova resource class so it can be referenced in relational fields (BelongsTo, HasMany). No CRUD operations exposed. | | QuestionGroupResource | QuestionGroup | QuestionGroupPolicy | No | Read-only. Filterable by category. No create, update, or delete. Displayed inline via HasMany on other resources only. | | QuestionResource | Question | QuestionPolicy | Yes | Only the `text` field is editable by administrators. All other fields (has_yes, has_no, has_na, details, is_scored, sort_order) are read-only after seeding. No create or delete. | +| ScreeningResource | Screening | ScreeningPolicy | Yes | Read-only. Filterable by user and result. View pre-screening scores. No create, update, or delete from Nova. | | SessionResource | Session | SessionPolicy | Yes | Filterable by user, category, and status. View scores and results. No create, update, or delete from Nova. | | AnswerResource | Answer | AnswerPolicy | No | Strictly read-only — no create, update, or delete by anyone (administrators included). Viewed within session context only. | | LogResource | Log | LogPolicy | Yes | Read-only. Filterable by user, category, action, and date range. For usage analytics and statistics. No create, update, or delete. | @@ -285,6 +336,7 @@ ### Policy Summary | Category | Yes | No | No | No | | QuestionGroup | Yes | No | No | No | | Question | Yes | No | Yes (text only) | No | +| Screening | Yes | No | No | No | | Session | Yes | No | No | No | | Answer | Yes | No | No | No | | Log | Yes | No | No | No | diff --git a/docs/theming-templating-vue.md b/docs/theming-templating-vue.md index e69de29..48c8592 100644 --- a/docs/theming-templating-vue.md +++ b/docs/theming-templating-vue.md @@ -0,0 +1,141 @@ +# Theming, Templating & Vue Component Standards + +Frontend design system for the Go No Go questionnaire application. Built with Vue 3, Inertia.js v2, and Tailwind CSS. + +## Design Tokens + +### Color Palette + +| Token | Hex | RGB | Tailwind Class | Usage | +|-------------|-----------|------------------|-----------------|---------------------------------------------| +| Primary | `#d1ec51` | 209, 236, 81 | `bg-primary`, `text-primary` | Buttons (default), accents, highlights | +| Secondary | `#00b7b3` | 0, 183, 179 | `bg-secondary`, `text-secondary` | Button hover states, secondary accents | +| Background | `#2b303a` | 43, 48, 58 | `bg-surface` | Page background, card backgrounds | +| Text | `#ffffff` | 255, 255, 255 | `text-white` | Primary body text on dark background | +| Text Muted | `#9ca3af` | 156, 163, 175 | `text-gray-400` | Secondary/muted text, labels, placeholders | + +Dark background means all primary text is white. Use `text-gray-400` for de-emphasized text. + +## Tailwind Configuration + +Register design tokens in `tailwind.config.js` so they are available as utility classes: + +```js +// tailwind.config.js +export default { + theme: { + extend: { + colors: { + primary: '#d1ec51', + secondary: '#00b7b3', + surface: '#2b303a', + }, + }, + }, +}; +``` + +This enables `bg-primary`, `text-secondary`, `bg-surface`, `border-primary`, etc. + +## Layout + +`resources/js/Layouts/AppLayout.vue` is the persistent layout wrapping all authenticated pages. + +Structure: +- **Header bar:** `PageHeader` component -- Piccadilly logo (top-left) with the current page title to its right +- **Main content area:** below the header, renders the page slot + +Applied via Inertia persistent layouts on each page component: + +```js +import AppLayout from '@/Layouts/AppLayout.vue'; + +defineOptions({ layout: AppLayout }); +``` + +Pages: Dashboard (`/`), Session/Show (questionnaire flow), Session/Result (final result). + +## Components + +All shared components live in `resources/js/Components/`. + +### AppButton + +Central button component. All buttons in the app use this -- no one-off button styling. + +| Prop | Type | Default | Description | +|------------|-----------|-------------|----------------------------------------------------------| +| `variant` | `String` | `'primary'` | `'primary'` / `'danger'` / `'ghost'` | +| `size` | `String` | `'md'` | `'sm'` / `'md'` / `'lg'` | +| `href` | `String` | `undefined` | When set, renders as Inertia `Link` instead of `button` | +| `disabled` | `Boolean` | `false` | Disables interaction and applies muted styling | +| `loading` | `Boolean` | `false` | Shows spinner and disables interaction | + +Default state: `bg-primary` background, dark text (`text-surface` or `text-gray-900`). +Hover state: `bg-secondary` background. +Danger variant: red background/text for destructive actions. +Ghost variant: transparent background, text-only. + +### AppLogo + +Renders the Piccadilly logo (SVG or image). No props beyond optional size overrides. Used inside `PageHeader`. + +### PageHeader + +Contains `AppLogo` (left) and the page title (right of logo). Used inside `AppLayout`. Accepts a `title` prop (String). + +### ScoreIndicator + +Displays the running score during a session with color coding based on thresholds. See [Scoring Colors](#scoring-colors) for the color rules. + +### QuestionCard + +Renders a single question in the questionnaire flow. Adapts its UI based on the question's field configuration: +- `has_yes` -- show Yes button +- `has_no` -- show No button +- `has_na` -- show N/A button +- `details` -- show a text input for additional notes + +## Icons + +Heroicons is the only icon library. No other icon packages. + +```bash +npm install @heroicons/vue +``` + +Default: **outline** variant (24x24): + +```vue + + + +``` + +Small/inline contexts: **solid** variant (20x20): + +```vue + + + +``` + +## Scoring Colors + +Result-specific colors, separate from brand colors. Used only in `ScoreIndicator` and the result page. + +| Result | Color | Tailwind Class | +|---------------------|--------------|-------------------------| +| GO | Green | `text-green-500` / `bg-green-500` | +| Consult Leadership | Yellow/Amber | `text-amber-500` / `bg-amber-500` | +| NO GO | Red | `text-red-500` / `bg-red-500` | + +These are standard Tailwind palette colors, not custom tokens.