Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0d544a0
Add rush application portal and CMS
invividl Aug 15, 2026
010710f
Scope rush CMS to a cycle and enforce apply essay limits
invividl Aug 15, 2026
44aad3a
Add admin allowlist management on the dashboard
invividl Aug 15, 2026
919e54d
Add brother portal with dummy sections and brothers admin list
invividl Aug 16, 2026
ce2056c
Fix interview search type error
invividl Aug 16, 2026
9a2e61c
Add admin apply preview and edit application text
invividl Aug 16, 2026
bef6786
Merge remote-tracking branch 'origin/develop' into feat/app-portal
invividl Aug 23, 2026
ffe15a0
Add S3 debug upload test, admin apply access, and file upload validation
invividl Aug 23, 2026
34f40e2
Wire apply file uploads to S3 with presigned PUT flow
invividl Aug 23, 2026
eb91259
Allow editing submitted applications until deadline without autosave
invividl Aug 23, 2026
06bdaa7
Add S3 cycle paths, file downloads, and clearer save UX
invividl Aug 23, 2026
ef622db
Updated toast messages for validation vs errors.
invividl Aug 23, 2026
11981d2
Add application reads schema and reviewer access gating
invividl Aug 23, 2026
044961c
Add reviewer reads portal with rubric guidance and submit flow
invividl Aug 23, 2026
e80e8f2
Add admin apps hub, rubric CMS, and new-cycle seeding
invividl Aug 23, 2026
4d5bcc3
Improve admin UX, uniqname inputs, and apply account bar
invividl Aug 24, 2026
179b66d
Add dark brother portal chrome for login and /portal
invividl Aug 24, 2026
db5e6ac
Add dark mode for application reads list and review
invividl Aug 24, 2026
3bdbee8
Add dark admin chrome and apply account bar polish
invividl Aug 25, 2026
9eeee8f
Improve admin apps delete and CSV export
invividl Aug 25, 2026
02e6baa
Shorten application read locks to 20 minutes
invividl Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ GOOGLE_CLIENT_SECRET=

# Your @umich.edu. Local only.
LOCAL_ADMIN_EMAILS=you@umich.edu

# AWS S3 (dev bucket for local + Vercel Preview; prod bucket later)
# Bucket CORS must allow PUT/GET from your dev origin (e.g. http://localhost:3000).
AWS_REGION=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_BUCKET=
149 changes: 149 additions & 0 deletions docs/database-diagram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Database diagram (DBML)

Paste into [dbdiagram.io](https://dbdiagram.io).

```dbml
// Public Postgres schema for the rush application portal.
// auth.users is Supabase Auth. applications.user_id stores that UUID with no FK.

Table admins {
email varchar [primary key, note: 'Extra web allowlist; e-board is also admin via assignments later']
created_at timestamptz [not null, default: `now()`]
}

Table brothers {
id uuid [pk]
first_name varchar
last_name varchar
umich_email varchar [note: 'nullable; unique when set; /portal login']
contact_email varchar
linkedin_url varchar
photo_filename varchar [note: 'Dummy filename until S3']
status varchar [not null, default: 'active', note: 'active | alumni']
pledge_class varchar
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]
}

Table rush_cycles {
id uuid [primary key, default: `gen_random_uuid()`]
name varchar [not null]
opens_at timestamptz [not null]
closes_at timestamptz [not null]
intro_markdown text [note: 'Welcome copy on /apply']
closed_markdown text [note: 'Shown on /apply after close']
public_blurb text [note: 'Copy on /rush']
interest_form_url varchar
youtube_url varchar
calendar_url varchar
hear_about_options varchar[] [not null, default: `{}`]
is_active boolean [not null, default: false, note: 'At most one true (partial unique index)']
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]

Note: 'One live cycle on the site. Owns /rush, schedule, and application questions.'
}

Table rush_events {
id uuid [primary key, default: `gen_random_uuid()`]
cycle_id uuid [not null]
title varchar [not null]
datetime varchar [not null]
location varchar [not null]
description text
button_label varchar
button_url varchar
order_index integer [not null, default: 0]
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]

indexes {
(cycle_id, order_index) [name: 'rush_events_cycle_id_idx']
}
}

Table cycle_questions {
id uuid [primary key, default: `gen_random_uuid()`]
cycle_id uuid [not null]
prompt text [not null]
help_text text
max_words integer [not null]
sort_order integer [not null, default: 0]
required boolean [not null, default: true]

indexes {
(cycle_id, sort_order) [name: 'cycle_questions_cycle_id_idx']
}
}

Table applications {
id uuid [primary key, default: `gen_random_uuid()`]
cycle_id uuid [not null]
user_id uuid [not null, note: 'auth.users.id; no FK']
email varchar [not null]
status varchar [not null, default: 'draft', note: 'draft | submitted']
submitted_at timestamptz
first_name varchar
last_name varchar
preferred_name varchar
pronouns varchar
phone varchar
majors varchar
minors varchar
graduation_year integer
gpa numeric
semesters_remaining integer
other_professional_fraternity boolean
campus_activities text
hear_about varchar[]
hear_about_other varchar
anything_else text
rush_feedback text
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`]

indexes {
(cycle_id, user_id) [unique, name: 'applications_cycle_user_unique']
user_id [name: 'applications_user_id_idx']
(cycle_id, status) [name: 'applications_cycle_status_idx']
}
}

Table application_answers {
id uuid [primary key, default: `gen_random_uuid()`]
application_id uuid [not null]
question_id uuid [not null]
body text

indexes {
(application_id, question_id) [unique, name: 'application_answers_app_question_unique']
}
}

Table application_files {
id uuid [primary key, default: `gen_random_uuid()`]
application_id uuid [not null]
slot varchar [not null, note: 'photo | transcript | resume | resume_anonymized | life_app_screenshot']
s3_key varchar [not null]
mime_type varchar
size_bytes integer
original_filename varchar
created_at timestamptz [not null, default: `now()`]

indexes {
(application_id, slot) [unique, name: 'application_files_app_slot_unique']
}
}

Table auth_users [note: 'Supabase Auth; not a public table we migrate'] {
id uuid [primary key]
email varchar
}

Ref: rush_events.cycle_id > rush_cycles.id [delete: cascade]
Ref: cycle_questions.cycle_id > rush_cycles.id [delete: cascade]
Ref: applications.cycle_id > rush_cycles.id [delete: restrict]
Ref: application_answers.application_id > applications.id [delete: cascade]
Ref: application_answers.question_id > cycle_questions.id [delete: restrict]
Ref: application_files.application_id > applications.id [delete: cascade]
```
Loading