Skip to content

Commit 9b5bce5

Browse files
committed
feat: implement comprehensive meeting attendance tracking system
- Add database schema for meetings, meeting events, and attendance - New enums: meeting_status, meeting_event_type, attendance_event_type, scan_method - New tables: meeting, meeting_event, attendance with proper relations - Indexes on meeting_id and user_id for performance - Implement server-side logic - Share token generation for meetings - Secure redirect validation for auth flows - Meeting state management (upcoming/ongoing/recess/finished) - Add admin UI for meetings management - List/create meetings page with status badges - Meeting detail page with event timeline and status transitions - Attendees page with filtering, search, and CSV export - QR code scanner page for check-in/check-out using qr-scanner library - Manual check-in/check-out functionality - Features - Automatic toggle between CHECK_IN/CHECK_OUT based on current status - Meeting recess support with optional bulk check-out - Real-time attendance tracking with timestamps - Export attendance data to CSV - Comprehensive E2E tests for all workflows - Add translations (fi/en) for meetings UI - Add navigation item for Meetings in admin sidebar This is phase 2 of the QR code system - focuses on admin tools for meeting attendance. Phase 1 (member QR verification) already merged. Phase 3 (public attendance sharing) will come later.
1 parent 3581245 commit 9b5bce5

30 files changed

Lines changed: 3849 additions & 4 deletions

drizzle/0011_gray_warlock.sql

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
CREATE TYPE "public"."attendance_event_type" AS ENUM('CHECK_IN', 'CHECK_OUT');--> statement-breakpoint
2+
CREATE TYPE "public"."meeting_event_type" AS ENUM('START', 'RECESS_START', 'RECESS_END', 'FINISH');--> statement-breakpoint
3+
CREATE TYPE "public"."meeting_status" AS ENUM('upcoming', 'ongoing', 'recess', 'finished');--> statement-breakpoint
4+
CREATE TYPE "public"."scan_method" AS ENUM('qr_scan', 'manual');--> statement-breakpoint
5+
CREATE TABLE "attendance" (
6+
"id" text PRIMARY KEY NOT NULL,
7+
"meeting_id" text NOT NULL,
8+
"user_id" text NOT NULL,
9+
"event_type" "attendance_event_type" NOT NULL,
10+
"scan_method" "scan_method" NOT NULL,
11+
"scanned_by" text NOT NULL,
12+
"timestamp" timestamp with time zone DEFAULT now() NOT NULL
13+
);
14+
--> statement-breakpoint
15+
CREATE TABLE "meeting" (
16+
"id" text PRIMARY KEY NOT NULL,
17+
"name" text NOT NULL,
18+
"description" text,
19+
"status" "meeting_status" DEFAULT 'upcoming' NOT NULL,
20+
"started_at" timestamp with time zone,
21+
"finished_at" timestamp with time zone,
22+
"share_token" text,
23+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
24+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
25+
CONSTRAINT "meeting_shareToken_unique" UNIQUE("share_token")
26+
);
27+
--> statement-breakpoint
28+
CREATE TABLE "meeting_event" (
29+
"id" text PRIMARY KEY NOT NULL,
30+
"meeting_id" text NOT NULL,
31+
"event_type" "meeting_event_type" NOT NULL,
32+
"notes" text,
33+
"timestamp" timestamp with time zone DEFAULT now() NOT NULL
34+
);
35+
--> statement-breakpoint
36+
ALTER TABLE "attendance" ADD CONSTRAINT "attendance_meeting_id_meeting_id_fk" FOREIGN KEY ("meeting_id") REFERENCES "public"."meeting"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
37+
ALTER TABLE "attendance" ADD CONSTRAINT "attendance_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
38+
ALTER TABLE "attendance" ADD CONSTRAINT "attendance_scanned_by_user_id_fk" FOREIGN KEY ("scanned_by") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
39+
ALTER TABLE "meeting_event" ADD CONSTRAINT "meeting_event_meeting_id_meeting_id_fk" FOREIGN KEY ("meeting_id") REFERENCES "public"."meeting"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
40+
CREATE INDEX "idx_attendance_meeting_id" ON "attendance" USING btree ("meeting_id");--> statement-breakpoint
41+
CREATE INDEX "idx_attendance_user_id" ON "attendance" USING btree ("user_id");--> statement-breakpoint
42+
CREATE INDEX "idx_meeting_event_meeting_id" ON "meeting_event" USING btree ("meeting_id");

0 commit comments

Comments
 (0)