Skip to content

feat: NQL interface redesign — nav link, sidebar history, LLM Summarize (#88)#89

Merged
William-Hill merged 8 commits intorebranding/bishop-statefrom
feat/88-nql-redesign
Feb 23, 2026
Merged

feat: NQL interface redesign — nav link, sidebar history, LLM Summarize (#88)#89
William-Hill merged 8 commits intorebranding/bishop-statefrom
feat/88-nql-redesign

Conversation

@William-Hill
Copy link
Collaborator

Summary

  • Add Query to the global nav header so advisors can reach the query interface from any page
  • Redesign the /query page as a two-column analytics workbench: fixed 260px left sidebar for scrollable query history (with Export + Clear actions) and a mobile overlay drawer triggered by a header toggle
  • Add opt-in Summarize button next to query results that calls a new POST /api/query-summary endpoint, generating a 2–3 sentence plain-English narrative from the result data via gpt-4o-mini

Changes

File Change
components/nav-header.tsx Added { href: "/query", label: "Query" } to NAV_LINKS
app/api/query-summary/route.ts New POST endpoint — RBAC-guarded, caps data at 50 rows, maxOutputTokens: 200
lib/roles.ts Added RBAC entry for /api/query-summary (admin, advisor, ir, faculty)
components/query-history-panel.tsx Stripped Card wrapper; sidebar-native flex layout with pinned header/footer
app/query/page.tsx Two-column layout, mobile drawer, Summarize button + summary card, summary resets on new query

Test Plan

  • Query link appears in global nav and navigates to /query
  • Unauthorized roles (e.g. leadership) are redirected by middleware, not by nav filtering
  • On desktop (≥ 768px): query history sidebar is visible on the left, scrollable
  • On mobile (< 768px): sidebar is hidden; tap the toggle opens the history drawer with backdrop; tapping backdrop closes it
  • Run a query → Summarize button appears; click it → spinner → summary paragraph renders below results; button hides
  • Run a second query → summary clears before new results load
  • Export link downloads query-audit-log.csv; Clear removes all history entries
  • POST /api/query-summary returns 403 for unauthenticated requests and 400 for missing prompt/data
  • TypeScript: npx tsc --noEmit exits 0
  • Lint: npm run lint exits 0 errors (5 pre-existing warnings)

Closes #88

@William-Hill William-Hill merged commit 17b4a30 into rebranding/bishop-state Feb 23, 2026
2 checks passed
William-Hill added a commit that referenced this pull request Feb 24, 2026
* feat: add cohort and enrollment intensity filters to student roster (#81) (#82)

* docs: add demo script with 6-minute talk track and screenshot guide

* docs: move DEMO.md into docs/ directory

* docs: move demo script and screenshots into docs/demo/ subdirectory

* chore: untrack large presentation files, add *.pptx and docs PDFs to .gitignore

* feat: student roster page with drill-down, filtering, sorting, and CSV export (#65)

* feat: student roster with info popovers; fix gateway models using 'Y' not 'C' for completion target

* fix: credential model — add sought-credential fallback and class_weight=balanced; add sorting for enrollment and credential type columns

* fix: update credential type popover to reflect sought-credential fallback logic

* feat: dashboard filtering by cohort, enrollment type, and credential type (#66) (#72)

- Add filter bar above KPI tiles with Cohort, Enrollment Type, and Credential Type
  dropdowns (shadcn Select) and a Clear button with filtered-student count
- All 4 dashboard API routes now accept cohort, enrollmentType, credentialType
  query params and apply parameterized WHERE clauses
- Risk alerts and retention-risk routes use a CTE so percentage denominators
  are relative to the filtered set (not the full table)
- Readiness route conditionally JOINs student_level_with_predictions when
  enrollment or credential filters are active; existing institution/cohort/level
  params unchanged
- All fetch calls on the page are re-triggered when filter state changes

* feat: audit log export endpoint with CSV download button (#67) (#73)

- Add GET /api/query-history/export that reads logs/query-history.jsonl and
  streams a CSV with headers: timestamp, institution, prompt, vizType, rowCount
- Accepts optional ?from=ISO_DATE&to=ISO_DATE query params for date-range
  filtering; returns 404 with clear message if the log file does not exist yet
- Sets Content-Disposition: attachment; filename="query-audit-log.csv"
- Add "Export" button with download icon in the QueryHistoryPanel header that
  triggers a direct browser download via <a href download>

* feat: student detail view with personalized recommendations (#77) (#79)

- Add GET /api/students/[guid] joining student_level_with_predictions +
  llm_recommendations; returns 404 for unknown GUIDs
- Add /students/[guid] page with:
    - Student header: GUID, cohort, enrollment, credential, at-risk + readiness badges
    - FERPA disclaimer (de-identified GUID only, no PII stored)
    - Six prediction score cards (retention, readiness, gateway math/English,
      GPA risk, time-to-credential) color-coded green/yellow/red
    - AI Readiness Assessment card: rationale, risk factors (orange dot list),
      and recommended actions (checkbox-style checklist)
    - Graceful fallback when no assessment has been generated yet
    - Back button uses router.back() to preserve roster filter state
- Student roster rows are now fully clickable (onClick → router.push)
  with the GUID cell retaining its Link for ctrl/cmd+click support

* feat: Supabase Auth + role-based access control (FR6, #75) (#80)

* feat: Supabase Auth + role-based access control (FR6, #75)

Auth layer
- Install @supabase/supabase-js + @supabase/ssr
- lib/supabase/client.ts  — browser client (createBrowserClient)
- lib/supabase/server.ts  — server client (createServerClient + cookies)
- lib/supabase/middleware-client.ts — session refresh helper for middleware

Roles
- lib/roles.ts — Role type, ROUTE_PERMISSIONS map, canAccess() helper,
  ROLE_LABELS and ROLE_COLORS per role
- Five roles: admin | advisor | ir | faculty | leadership
  /students/**            → admin, advisor, ir
  /query                  → admin, advisor, ir, faculty
  /api/students/**        → admin, advisor, ir
  /api/query-history/export → admin, ir
  / and /methodology      → all roles (public within auth)

Middleware
- middleware.ts — unauthenticated → redirect /login; role resolved from
  user_roles table; canAccess() enforced; role + user-id + email forwarded
  as request headers (x-user-role, x-user-id, x-user-email) for API routes

Login page
- app/login/page.tsx — email/password form using createBrowserClient
- app/auth/callback/route.ts — PKCE code exchange handler

Navigation
- components/nav-header.tsx — sticky top bar: role badge, email, sign-out
- app/layout.tsx — server component reads session + role, renders NavHeader
  when authenticated

API guards
- /api/students: 403 for faculty + leadership
- /api/students/[guid]: 403 for faculty + leadership
- /api/query-history/export: 403 for non-admin/ir

Database & seed
- migrations/001_user_roles.sql — user_roles table + RLS policy
- scripts/seed-demo-users.ts — creates 5 demo users via service role key
  (admin/advisor/ir/faculty/leadership @bscc.edu, pw: BishopState2025!)

* fix: seed script accepts NEXT_PUBLIC_ env var names; install tsx dev dep

* feat: add cohort and enrollment intensity filters to student roster (#81)

* fix: use correct DB enrollment intensity values (Full-Time/Part-Time with hyphens)

* chore: add GitHub Actions CI/CD workflows (#83) (#84)

* fix: drop npm lockfile cache since package-lock.json is gitignored

npm ci requires a lockfile; switch to npm install in ci-dashboard and
security-audit workflows to avoid cache resolution failures.

* feat: course sequencing insights and DFWI analysis (#85) (#87)

* feat: add course_enrollments migration and data ingestion script (#85)

* fix: transaction safety and validation in course enrollment ingestion (#85)

* feat: add course DFWI, gateway funnel, and sequence API routes (#85)

* fix: sequence join granularity, gateway funnel clarity, DFWI result cap (#85)

* feat: add /courses page with DFWI table, gateway funnel, and co-enrollment pairs (#85)

* fix: percentage display and component cleanup in /courses page (#85)

* fix: gateway type label values (M/E) and add RBAC to gateway-funnel route (#85)

* feat: sortable column headers, info popovers for DFWI/pass rate, pairings table sort (#85)

* feat: tabbed courses page with AI-powered co-enrollment explainability

- Redesign /courses page with 3 tabs: DFWI Rates, Gateway Funnel, Co-enrollment Insights
- Add POST /api/courses/explain-pairing route: queries per-pair stats (individual
  DFWI/pass rates, breakdown by delivery method and instructor type) then calls
  gpt-4o-mini to generate an advisor-friendly narrative
- Co-enrollment Insights tab shows sortable pairings table with per-row Explain
  button that fetches and renders stats chips + LLM analysis inline
- Tab state is client-side (no Radix Tabs dependency needed)

* ci: trigger re-run after workflow fix

* fix: update ESLint for Next.js 16 (next lint removed)

- Replace next lint with direct eslint . in package.json lint script
- Rewrite eslint.config.mjs to use eslint-config-next flat config exports
  directly instead of deprecated FlatCompat bridge
- Add eslint and eslint-config-next as devDependencies
- Suppress pre-existing rule violations (no-explicit-any, no-unescaped-entities,
  set-state-in-effect) to avoid CI failures on legacy code

* feat: NQL interface redesign — nav link, sidebar history, LLM Summarize (#88) (#89)

* docs: NQL interface redesign design doc (#88)

* docs: NQL redesign implementation plan (#88)

* feat: add Query link to global nav header (#88)

* feat: add POST /api/query-summary LLM result narration (#88)

* fix: harden query-summary route input validation (#88)

* refactor: adapt QueryHistoryPanel for sidebar layout (#88)

* fix: restore institution label, fix text size, restore truncation threshold (#88)

* feat: sidebar layout + LLM Summarize button on query page (#88)

* fix: NQL rate formatting and course_enrollments routing (#90)

- Add isRateColumn helper and formatCellValue to render 0–1 probabilities
  as percentages in table cells, chart axes, tooltips, and KPI display
- Fix KPI suffix to only append % when value is actually in 0–1 range
- Add course_enrollments table to LLM prompt with schema, routing rules,
  DFWI/pass rate SQL patterns, FERPA guardrails, and worked example
- Add SchemaEntry interface; remove as-any cast on courseColumns
- SQL patterns return 0–1 scale; display layer handles multiplication

* fix: prevent LLM from adding institution_id filter to course_enrollments

course_enrollments has no institution_id column; the INSTITUTION context
in the prompt was causing the LLM to borrow the Institution_ID filter
from the student table and apply it to course queries, producing:
  Error: column "institution_id" does not exist

* feat: add viz type tab strip for user override of LLM chart choice

Renders Table/Bar/Line/Pie/KPI buttons above query results. Defaults to
the LLM's chosen vizType and resets on each new query, so users can
switch views without re-running the query.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant