Add lessons module, rate cycles, EC2 deploy scripts, and help content

- Lessons module: lesson types, instructors, schedule slots, enrollments,
  sessions (list + week grid view), lesson plans, grading scales, templates
- Rate cycles: replace monthly_rate with billing_interval + billing_unit on
  enrollments; add weekly/monthly/quarterly rate presets to lesson types and
  schedule slots with auto-fill on enrollment form
- Member detail page: tabbed layout for details, identity documents, enrollments
- Sessions week view: custom 7-column grid replacing react-big-calendar
- Music store seed: instructors, lesson types, slots, enrollments, sessions,
  grading scale, lesson plan template
- Scrollbar styling: themed to match sidebar/app palette
- deploy/: EC2 setup and redeploy scripts, nginx config, systemd service
- Help: add Lessons category (overview, types, instructors, slots, enrollments,
  sessions, plans/grading); collapsible sidebar with independent scroll;
  remove POS/accounting references from docs
This commit is contained in:
Ryan Moon
2026-03-30 18:52:57 -05:00
parent 7680a73d88
commit 5ad27bc196
47 changed files with 6303 additions and 139 deletions

View File

@@ -17,6 +17,7 @@ import { accounts, members } from './accounts.js'
// --- Enums ---
export const lessonFormatEnum = pgEnum('lesson_format', ['private', 'group'])
export const billingUnitEnum = pgEnum('billing_unit', ['day', 'week', 'month', 'quarter', 'year'])
// --- Tables ---
@@ -37,7 +38,9 @@ export const lessonTypes = pgTable('lesson_type', {
instrument: varchar('instrument', { length: 100 }),
durationMinutes: integer('duration_minutes').notNull(),
lessonFormat: lessonFormatEnum('lesson_format').notNull().default('private'),
baseRateMonthly: varchar('base_rate_monthly', { length: 20 }),
rateWeekly: numeric('rate_weekly', { precision: 10, scale: 2 }),
rateMonthly: numeric('rate_monthly', { precision: 10, scale: 2 }),
rateQuarterly: numeric('rate_quarterly', { precision: 10, scale: 2 }),
isActive: boolean('is_active').notNull().default(true),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
@@ -55,6 +58,9 @@ export const scheduleSlots = pgTable('schedule_slot', {
startTime: time('start_time').notNull(),
room: varchar('room', { length: 100 }),
maxStudents: integer('max_students').notNull().default(1),
rateWeekly: numeric('rate_weekly', { precision: 10, scale: 2 }),
rateMonthly: numeric('rate_monthly', { precision: 10, scale: 2 }),
rateQuarterly: numeric('rate_quarterly', { precision: 10, scale: 2 }),
isActive: boolean('is_active').notNull().default(true),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
@@ -84,7 +90,9 @@ export const enrollments = pgTable('enrollment', {
status: enrollmentStatusEnum('status').notNull().default('active'),
startDate: date('start_date').notNull(),
endDate: date('end_date'),
monthlyRate: numeric('monthly_rate', { precision: 10, scale: 2 }),
rate: numeric('rate', { precision: 10, scale: 2 }),
billingInterval: integer('billing_interval'),
billingUnit: billingUnitEnum('billing_unit'),
makeupCredits: integer('makeup_credits').notNull().default(0),
notes: text('notes'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),