Commit 6aeb82d
authored
* 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.
1 parent 1472a63 commit 6aeb82d
54 files changed
Lines changed: 7808 additions & 275 deletions
File tree
- .github/workflows
- ai_model
- codebenders-dashboard
- app
- actions
- api
- analyze
- courses
- dfwi
- explain-pairing
- gateway-funnel
- sequences
- dashboard
- kpis
- readiness
- retention-risk
- risk-alerts
- query-history/export
- query-summary
- students
- [guid]
- auth/callback
- courses
- login
- query
- students
- [guid]
- components
- lib
- supabase
- docs
- demo
- screenshots
- plans
- migrations
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
109 | 110 | | |
110 | 111 | | |
111 | 112 | | |
| |||
158 | 159 | | |
159 | 160 | | |
160 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
161 | 168 | | |
162 | 169 | | |
163 | 170 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
176 | | - | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
177 | 187 | | |
178 | 188 | | |
179 | 189 | | |
| |||
646 | 656 | | |
647 | 657 | | |
648 | 658 | | |
| 659 | + | |
649 | 660 | | |
650 | 661 | | |
651 | 662 | | |
| |||
734 | 745 | | |
735 | 746 | | |
736 | 747 | | |
737 | | - | |
| 748 | + | |
738 | 749 | | |
739 | 750 | | |
740 | 751 | | |
| |||
845 | 856 | | |
846 | 857 | | |
847 | 858 | | |
848 | | - | |
| 859 | + | |
849 | 860 | | |
850 | 861 | | |
851 | 862 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
0 commit comments