Files
go-no-go/docs/technical-requirements.md

19 KiB

Technical Requirements: Go No Go

1. Overview

Baker Tilly International "Go/No Go" checklist application. Users complete scored questionnaires across categories to decide whether to pursue business opportunities. Each questionnaire session covers one category, with questions organized into groups. Scored questions produce a cumulative score that determines a Go / No Go / Consult Leadership result.

Category note: The original plan specifies 6 categories (Audit, Outsource, Solution, Digital Solutions, Legal, Tax). The question source documents contain 5 files. "Solution" may be separate from "Outsource Solutions" or merged -- seed all 6 per the plan and reconcile during question seeding.

2. Tech Stack

  • Backend: Laravel 12
  • Frontend: Vue 3 with Inertia.js v2
  • Admin Panel: Laravel Nova 5 at /cp
  • Database: MySQL (go-no-go)
  • Auth (frontend): Laravel Socialite with Azure AD SSO
  • Auth (Nova): Built-in Nova authentication
  • Excel Export: maatwebsite/laravel-nova-excel

3. Two Separate Interfaces

The application has two distinct interfaces:

  • Frontend (questionnaire): Custom Vue 3 + Inertia.js v2 application at /. All questionnaire flow, session management, and user-facing pages. Custom routes, controllers, and Vue page components.
  • Admin panel (Nova): Laravel Nova 5 at /cp. Administrators log in here to manage questions and view sessions/answers. No questionnaire logic — strictly data management.

These are fully separate. The frontend has its own routes, controllers, and Vue pages. Nova handles administration only.

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: Built-in Nova authentication at /cp/login. Already configured and working. Only administrators access Nova.

4. Frontend Architecture (Vue + Inertia.js v2)

The entire user-facing frontend is built with Vue 3 and Inertia.js v2. No Blade templates for user pages.

Inertia Features and Their Application

Feature Usage Why
useForm helper All questionnaire form submissions Provides form state, error handling, processing indicators, dirty tracking. Each questionnaire step uses useForm to submit answers.
Form component Login, basic info entry Declarative form handling for simpler forms that don't need granular control.
Link component Navigation between steps, dashboard, session history SPA navigation. Use only/except props for partial reloads.
Shared data Via HandleInertiaRequests middleware Authenticated user, current session progress, flash messages, scoring thresholds -- available on every page without explicit passing.
Persistent layouts Main app layout Layout persists across questionnaire steps, keeping sidebar/progress indicator state without remounting.
Deferred props Inertia::defer() for session history, dashboard statistics Load questionnaire data immediately; defer non-critical analytics data.
WhenVisible Long questionnaire pages displayed as single scroll Load question group data only when user scrolls to that section.
Progress indicator Built-in NProgress bar Visual feedback during page transitions between questionnaire steps.
Head component Dynamic page titles Per-step titles like "Step 3: Risk Assessment - Audit".
Partial reloads Saving individual answers When saving a single answer, only reload answers/score data, not the entire question set.
History encryption EncryptHistory middleware Questionnaire data may contain sensitive client information -- encrypt browser history state.
Prefetching Next questionnaire step Prefetch on hover/focus for instant navigation to the next step.
Polling Optional/future Not needed for core flow. Could poll for session lock status if multi-user editing is added later.
Scroll management Save inline vs. navigate Preserve scroll position when saving answers inline; scroll to top when navigating to a new question group.

5. Database Tables

categories

The 6 fixed assessment categories. Seeded, not user-managed.

Column Type Constraints
id bigint unsigned PK, auto-increment
name varchar(255) required, unique
sort_order unsigned integer required, default 0
created_at timestamp nullable
updated_at timestamp nullable

Seed data (in order):

  1. Audit
  2. Outsource
  3. Solution
  4. Digital Solutions
  5. Legal
  6. Tax

question_groups

Groups of related questions, scoped to a single category. Each group becomes one step in the questionnaire flow.

Column Type Constraints
id bigint unsigned PK, auto-increment
category_id bigint unsigned FK to categories.id, required
name varchar(255) required
sort_order unsigned integer required, default 0
description text nullable
scoring_instructions text nullable, for display text like "If you answer yes, you will score 1 point..."
created_at timestamp nullable
updated_at timestamp nullable

Groups per category (from question docs):

  • Audit (7 groups): Opportunity Details, Client Background and History, Financial Information, Regulatory Compliance, Risk Assessment, Resource Allocation, Reporting Requirements
  • Outsource Solutions (6 groups): Opportunity Details, Client Background and History, Regulatory Compliance, Risk Assessment, Resource Allocation
  • Digital Solutions (7 groups): Opportunity Details, Client Background and History, Regulatory Compliance, Risk Assessment, Resource Allocation, Technology & Innovation Fit
  • Legal (8 groups): Opportunity Details, Client Background and History, Financial Information, Regulatory Compliance, Risk Assessment, Resource Allocation, Stakeholder Engagement, Fee Quote
  • Tax (7 groups): Opportunity Details, Client Background and History, Financial Information, Regulatory Compliance, Risk Assessment, Resource Allocation, Stakeholder Engagement

All categories also have an "Additional Comments" section handled as the additional_comments field on the session, not as a separate question group.

questions

Individual questions within a group. Each question is self-contained with boolean flags controlling which answer options are available and how details are handled.

Column Type Constraints
id bigint unsigned PK, auto-increment
question_group_id bigint unsigned FK to question_groups.id, required
text text required
has_yes boolean required, default false
has_no boolean required, default false
has_na boolean required, default false
details varchar(50) nullable, enum: req_on_no, req_on_yes, optional, required
sort_order unsigned integer required, default 0
is_scored boolean required, default false
created_at timestamp nullable
updated_at timestamp nullable

Question Configuration

Each question is configured through boolean flags and a details mode. No cross-question dependencies exist — every question is self-contained.

Six configuration patterns cover all question types:

Pattern has_yes has_no has_na details Example
Pure open text false false false required "What sort of audit opportunity is it?"
Scored, no details true true true null "Does the sector come with a reputation we are comfortable with?"
Scored + details on no true true true req_on_no "There have been no significant changes in the client's business?"
Scored + details on yes true true true req_on_yes "Are there any previous audit reports or findings that need to be considered?"
Scored + details always true true true required "Have you completed a conflict check?"
Scored + details optional true true true optional "Has the client provided financial statements?"
  • When has_yes, has_no, and has_na are all false → the question renders as open text only (textarea)
  • When any are true → radio buttons render for the enabled options
  • The details column controls when a details textarea appears and whether it is required

sessions

One entry per user per questionnaire run. A session covers one category (not all categories).

Column Type Constraints
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
basic_info json stores: client_name, client_contact, lead_firm_name, lead_firm_contact
additional_comments text nullable, free text from final section
completed_at timestamp nullable
created_at timestamp nullable
updated_at timestamp nullable

Basic Info JSON Structure

Every category starts with these fields, stored in basic_info:

{
  "client_name": "Acme Corp",
  "client_contact": "John Doe",
  "lead_firm_name": "Baker Tilly NL",
  "lead_firm_contact": "Jane Smith"
}

answers

Stores all user responses. One answer per question per session.

Column Type Constraints
id bigint unsigned PK, auto-increment
session_id bigint unsigned FK to sessions.id, required
question_id bigint unsigned FK to questions.id, required
value varchar(255) nullable, stores "yes" / "no" / "not_applicable" for scored questions
text_value text nullable, stores free text for open text questions and details fields
created_at timestamp nullable
updated_at timestamp nullable

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.

Column Type Constraints
id bigint unsigned PK, auto-increment
user_id bigint unsigned FK to users.id, nullable (for unauthenticated events like login attempts)
session_id bigint unsigned FK to sessions.id, nullable
category_id bigint unsigned FK to categories.id, nullable
action varchar(100) required
metadata json nullable, additional context per action
created_at timestamp nullable

The logs table is append-only — rows are never updated or deleted.

Logged Actions

Action When Metadata
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}
answer_saved User saves an answer {question_id, question_group_id, value}
step_viewed User navigates to a question group {question_group_id}

6. Frontend Routes & Controllers

All questionnaire routes are custom (defined in routes/web.php), handled by custom controllers, and render Vue page components via Inertia.

Method Route Controller Vue Page Description
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 /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

Nova is served separately at /cp with its own authentication. No custom routes needed for Nova.

7. Questionnaire Flow

  1. User logs in via Azure AD SSO
  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. 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. 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 /

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
  • Not Applicable = excluded from scoring (does not count toward total)
  • Score calculated server-side and returned to frontend via Inertia props

Thresholds (same across all categories)

Score Result Color Meaning
10+ points GO Green Pursue the opportunity
5-9 points Consult Leadership Yellow Speak to SL or SSL leadership
1-4 points NO GO Red Do not pursue

9. Nova Resources & Policies

All Nova resources have an associated Laravel Policy that controls authorization.

Resource Model Policy Menu Item Notes
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.

Policy Summary

Model View Create Update Delete
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

Nova Field Behavior (Global Rule)

Applies to all Nova resources without exception:

  • Filterable: Every field that can be filtered, must be filterable.
  • Sortable: Every field that can be sorted, must be sortable.
  • Copyable: Every field that can be copied, must be copyable.

10. Nova Excel Export

  • Package: maatwebsite/laravel-nova-excel
  • Every Nova resource index gets a DownloadExcel action
  • Exports all visible columns to .xlsx
  • Added to every resource's actions() method