diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index ba4f4f1..f14931e 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -20,7 +20,11 @@
"Bash(npm run build:*)",
"Bash(ls:*)",
"Bash(xargs:*)",
- "mcp__playwright__browser_take_screenshot"
+ "mcp__playwright__browser_take_screenshot",
+ "Write",
+ "Bash",
+ "mcp__playwright__browser_console_messages",
+ "mcp__playwright__browser_navigate_back"
]
}
}
diff --git a/.playwright-mcp/step-01-homepage.png b/.playwright-mcp/step-01-homepage.png
new file mode 100644
index 0000000..c0d2dde
Binary files /dev/null and b/.playwright-mcp/step-01-homepage.png differ
diff --git a/.playwright-mcp/step-03-login-jonathan.png b/.playwright-mcp/step-03-login-jonathan.png
new file mode 100644
index 0000000..c0d2dde
Binary files /dev/null and b/.playwright-mcp/step-03-login-jonathan.png differ
diff --git a/.playwright-mcp/step-03-nova-login.png b/.playwright-mcp/step-03-nova-login.png
new file mode 100644
index 0000000..1d14c75
Binary files /dev/null and b/.playwright-mcp/step-03-nova-login.png differ
diff --git a/.playwright-mcp/step-04-layout.png b/.playwright-mcp/step-04-layout.png
new file mode 100644
index 0000000..c348767
Binary files /dev/null and b/.playwright-mcp/step-04-layout.png differ
diff --git a/.playwright-mcp/step-05-landing.png b/.playwright-mcp/step-05-landing.png
new file mode 100644
index 0000000..d2455ce
Binary files /dev/null and b/.playwright-mcp/step-05-landing.png differ
diff --git a/.playwright-mcp/step-05-screening-result.png b/.playwright-mcp/step-05-screening-result.png
new file mode 100644
index 0000000..06f4050
Binary files /dev/null and b/.playwright-mcp/step-05-screening-result.png differ
diff --git a/.playwright-mcp/step-05-screening.png b/.playwright-mcp/step-05-screening.png
new file mode 100644
index 0000000..0c95453
Binary files /dev/null and b/.playwright-mcp/step-05-screening.png differ
diff --git a/.playwright-mcp/step-05-session-result.png b/.playwright-mcp/step-05-session-result.png
new file mode 100644
index 0000000..a6222b8
Binary files /dev/null and b/.playwright-mcp/step-05-session-result.png differ
diff --git a/.playwright-mcp/step-05-session-show.png b/.playwright-mcp/step-05-session-show.png
new file mode 100644
index 0000000..d01748e
Binary files /dev/null and b/.playwright-mcp/step-05-session-show.png differ
diff --git a/app/Http/Controllers/LandingController.php b/app/Http/Controllers/LandingController.php
new file mode 100644
index 0000000..b28a135
--- /dev/null
+++ b/app/Http/Controllers/LandingController.php
@@ -0,0 +1,19 @@
+ auth()->id(),
+ ]);
+
+ return redirect()->route('screening.show', $screening);
+ }
+
+ /**
+ * Display the screening questionnaire.
+ */
+ public function show(Screening $screening): Response
+ {
+ return Inertia::render('Screening/Show', [
+ 'screening' => $screening,
+ ]);
+ }
+
+ /**
+ * Save screening answers and redirect to result.
+ */
+ public function update(Request $request, Screening $screening): RedirectResponse
+ {
+ return redirect()->route('screening.result', $screening);
+ }
+
+ /**
+ * Display the screening result with available categories.
+ */
+ public function result(Screening $screening): Response
+ {
+ return Inertia::render('Screening/Result', [
+ 'screening' => $screening,
+ 'categories' => Category::orderBy('sort_order')->get(['id', 'name']),
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/SessionController.php b/app/Http/Controllers/SessionController.php
new file mode 100644
index 0000000..37d5191
--- /dev/null
+++ b/app/Http/Controllers/SessionController.php
@@ -0,0 +1,59 @@
+ auth()->id(),
+ 'category_id' => $request->input('category_id'),
+ 'screening_id' => $request->input('screening_id'),
+ 'status' => 'in_progress',
+ ]);
+
+ return redirect()->route('sessions.show', $session);
+ }
+
+ /**
+ * Display the session questionnaire with category.
+ */
+ public function show(Session $session): Response
+ {
+ $session->load('category');
+
+ return Inertia::render('Session/Show', [
+ 'session' => $session,
+ ]);
+ }
+
+ /**
+ * Save session answers and redirect to result.
+ */
+ public function update(Request $request, Session $session): RedirectResponse
+ {
+ return redirect()->route('sessions.result', $session);
+ }
+
+ /**
+ * Display the final session result.
+ */
+ public function result(Session $session): Response
+ {
+ return Inertia::render('Session/Result', [
+ 'session' => $session,
+ ]);
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 3844a23..021bca9 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -16,6 +16,10 @@
$middleware->web(append: [
\App\Http\Middleware\HandleInertiaRequests::class,
]);
+
+ $middleware->alias([
+ 'encrypt.history' => \Inertia\Middleware\EncryptHistory::class,
+ ]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md
index 846d38e..349cefb 100644
--- a/docs/implementation-plan.md
+++ b/docs/implementation-plan.md
@@ -10,7 +10,7 @@ # Implementation Plan: Go No Go
## Step 1: Install Dependencies and Configure Build
-[ ] **Install and configure the frontend toolchain and auth packages.**
+[x] **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`).
@@ -36,7 +36,7 @@ ## Step 1: Install Dependencies and Configure Build
## Step 2: Database -- Migrations and Models
-[ ] **Create all database tables and Eloquent models.**
+[x] **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.
@@ -70,7 +70,7 @@ ## Step 2: Database -- Migrations and Models
## Step 3: Authentication
-[ ] **Set up Socialite for frontend SSO.**
+[x] **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).
@@ -94,7 +94,7 @@ ## Step 3: Authentication
## Step 4: Frontend Layout and Shared Components
-[ ] **Build the persistent layout and all shared Vue components.**
+[x] **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.
@@ -121,7 +121,7 @@ ## Step 4: Frontend Layout and Shared Components
## 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.**
+[x] **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.
@@ -280,25 +280,25 @@ ## 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.
+Create the `QuestionCard` component that renders questions based on their field configuration (see the 6 patterns in `docs/technical-requirements.md` section 5). Render all questions on a single scrollable page within `Session/Show` (not paginated per group). In phase 2, questions will be visually grouped by their question group with group headers and scoring instructions. 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 at the bottom of the page.
**Creates:**
- `resources/js/Components/QuestionCard.vue`
-- Updated `resources/js/Pages/Session/Show.vue` -- step navigation, question group rendering
+- Updated `resources/js/Pages/Session/Show.vue` -- single-page question 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.
+**Validates:** All questions for the category render on a single scrollable page. Each question renders the correct UI pattern (radio buttons, text-only, details visibility). Answers save without page reload. Refreshing the page retains saved answers. All 6 question patterns render correctly. Additional Comments textarea appears at the bottom.
**Browser Verification (Playwright MCP):**
1. `browser_navigate` to `http://go-no-go.test/login-jonathan`
2. Complete pre-screening (5+ Yes), select a category known to have diverse question types (e.g., Audit), fill basic info
-*Test question group navigation:*
-3. `browser_snapshot` -- confirm first question group renders with questions and Next button
-4. `browser_take_screenshot` -- save as `step-09-question-group-1.png`
+*Test single-page question rendering:*
+3. `browser_snapshot` -- confirm all questions render on a single page
+4. `browser_take_screenshot` -- save as `step-09-questions-full-page.png`
-*Test all 6 question patterns (navigate through groups to find each):*
+*Test all 6 question patterns (scroll through to find each):*
5. For each question pattern encountered:
- `browser_snapshot` -- verify correct UI elements (radio buttons for Yes/No/NA, details textarea visibility)
- `browser_take_screenshot` -- save as `step-09-pattern-{N}.png` for each distinct pattern
@@ -308,17 +308,16 @@ ## Step 9: Questionnaire Flow -- Question Rendering and Answer Saving
- `browser_take_screenshot` -- save as `step-09-conditional-details.png`
*Test answer saving:*
-7. `browser_fill_form` -- answer several questions in the current group
-8. `browser_click` the Next button to go to the next group
-9. `browser_click` the Previous button to return
-10. `browser_snapshot` -- confirm previously saved answers are still selected
-11. `browser_take_screenshot` -- save as `step-09-answers-retained.png`
+7. `browser_fill_form` -- answer several questions on the page
+8. Refresh the page
+9. `browser_snapshot` -- confirm previously saved answers are still selected
+10. `browser_take_screenshot` -- save as `step-09-answers-retained.png`
-*Test step navigation:*
-12. `browser_click` Next through all question groups to reach the Additional Comments step
-13. `browser_snapshot` -- confirm Additional Comments textarea renders
-14. `browser_take_screenshot` -- save as `step-09-additional-comments.png`
-15. `browser_console_messages` with level `error` -- verify no errors throughout navigation
+*Test Additional Comments section:*
+11. Scroll to the bottom of the page
+12. `browser_snapshot` -- confirm Additional Comments textarea renders at the bottom
+13. `browser_take_screenshot` -- save as `step-09-additional-comments.png`
+14. `browser_console_messages` with level `error` -- verify no errors
---
diff --git a/docs/technical-requirements.md b/docs/technical-requirements.md
index 7f7ba61..d3c19d3 100644
--- a/docs/technical-requirements.md
+++ b/docs/technical-requirements.md
@@ -278,14 +278,14 @@ ## 7. Questionnaire Flow
- 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)
+8. All questions are displayed on a single page (not paginated per question or per group). In phase 2, questions will be visually grouped by their question group.
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`)
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
+12. Additional comments free text section at the bottom of the page
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 `/`
diff --git a/resources/js/Components/AppButton.vue b/resources/js/Components/AppButton.vue
new file mode 100644
index 0000000..1e8d7e0
--- /dev/null
+++ b/resources/js/Components/AppButton.vue
@@ -0,0 +1,109 @@
+
+
+
+
+ {{ title }}
+
+
Baker Tilly International Questionnaire Application
+Baker Tilly International Questionnaire Application
+Your screening result: Passed
+10 Yes/No questions will appear here
+Your result will appear here
+Client and lead firm information will appear here
+Category questions will appear here
+