Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Attendance tracking using a junction table pattern (one record per apprentice pe
| Status | `fldew45fDGpgl1aRr` | singleSelect | Attendance status |
| Date Time (from Event) | `fldokfSk68MhJGlm6` | multipleLookupValues | Event date/time lookup |
| FAC Cohort (from Event) | `fldkc9zLJe7NZVAz1` | multipleLookupValues | Cohort lookup from Event |
| External Name | `fldIhZnMxfjh9ps78` | singleLineText | Name for non-registered attendees |
| External Email | `fldHREfpkx1bGv3K3` | email | Email for non-registered attendees |

---

Expand Down Expand Up @@ -221,6 +223,8 @@ Scheduled events/sessions for attendance tracking.
| Survey | `fld9XBHnCWBtZiZah` | url | Optional survey form URL |
| Attendance | `fldcPf53fVfStFZsa` | multipleRecordLinks | Linked attendance records |
| Name - Date | `fld7POykodV0LGsg1` | formula | Display name with date |
| Public | `fldatQzdAo8evWlNc` | checkbox | Visible on public check-in page |
| Check-in Code | `fldKMWSFmYONkvYMK` | number | 4-digit code for external attendees |

---

Expand Down
37 changes: 37 additions & 0 deletions scripts/schema-2025-12-29-13-05-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Airtable Schema

## Learners / Attendace - Apprentice Pulse

Table ID: `tblkDbhJcuT9TTwFc`

| Field | ID | Type |
|-------|-----|------|
| Id | `fldGdpuw6SoHkQbOs` | autoNumber |
| Apprentice | `fldOyo3hlj9Ht0rfZ` | multipleRecordLinks |
| Cohort | `fldn53kWDE8GHg2Yy` | multipleLookupValues |
| Checkin Time | `fldvXHPmoLlEA8EuN` | dateTime |
| Status | `fldew45fDGpgl1aRr` | singleSelect |
| Event | `fldiHd75LYtopwyN9` | multipleRecordLinks |
| Date Time (from Event) | `fldokfSk68MhJGlm6` | multipleLookupValues |
| FAC Cohort (from FAC Cohort) (from Event) | `fldE783vnY3SLjmh7` | multipleLookupValues |
| FAC Cohort (from Event) | `fldkc9zLJe7NZVAz1` | multipleLookupValues |
| External Name | `fldIhZnMxfjh9ps78` | singleLineText |
| External Email | `fldHREfpkx1bGv3K3` | email |

## Learners / Events - Apprentice Pulse

Table ID: `tblkbskw4fuTq0E9p`

| Field | ID | Type |
|-------|-----|------|
| Name | `fldMCZijN6TJeUdFR` | singleLineText |
| FAC Cohort | `fldcXDEDkeHvWTnxE` | multipleRecordLinks |
| FAC Cohort (from FAC Cohort) | `fldsLUl1MrhhsVBe7` | multipleLookupValues |
| Date Time | `fld8AkM3EanzZa5QX` | dateTime |
| Survey | `fld9XBHnCWBtZiZah` | url |
| Select | `fldo7fwAsFhkA1icC` | singleSelect |
| Attendace - Apprentice Pulse | `fldcPf53fVfStFZsa` | multipleRecordLinks |
| Name - Date | `fld7POykodV0LGsg1` | formula |
| Public | `fldatQzdAo8evWlNc` | checkbox |
| Number | `fldKMWSFmYONkvYMK` | number |

4 changes: 4 additions & 0 deletions src/lib/airtable/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export const EVENT_FIELDS = {
SURVEY: 'fld9XBHnCWBtZiZah', // url (optional survey form)
ATTENDANCE: 'fldcPf53fVfStFZsa', // multipleRecordLinks to Attendance (reverse link)
NAME_DATE: 'fld7POykodV0LGsg1', // formula (display name)
PUBLIC: 'fldatQzdAo8evWlNc', // checkbox (visible to all on check-in page)
CHECK_IN_CODE: 'fldKMWSFmYONkvYMK', // number (4-digit code for external attendees)
} as const;

// Fields - Attendance
Expand All @@ -48,4 +50,6 @@ export const ATTENDANCE_FIELDS = {
EVENT: 'fldiHd75LYtopwyN9', // multipleRecordLinks to Events
CHECKIN_TIME: 'fldvXHPmoLlEA8EuN', // dateTime
STATUS: 'fldew45fDGpgl1aRr', // singleSelect (Present/Absent/Late)
EXTERNAL_NAME: 'fldIhZnMxfjh9ps78', // singleLineText (for non-registered attendees)
EXTERNAL_EMAIL: 'fldHREfpkx1bGv3K3', // email (for non-registered attendees)
} as const;
6 changes: 6 additions & 0 deletions src/lib/airtable/events.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ describe('events', () => {
cohortId: 'recCohort1',
eventType: 'Regular class',
surveyUrl: 'https://survey.example.com',
isPublic: false,
checkInCode: undefined,
});
});
});
Expand Down Expand Up @@ -103,6 +105,8 @@ describe('events', () => {
cohortId: 'recCohort2',
eventType: 'Workshop',
surveyUrl: undefined,
isPublic: false,
checkInCode: undefined,
});
});

Expand Down Expand Up @@ -133,6 +137,8 @@ describe('events', () => {
expect(event).toEqual({
id: 'recNew123',
...input,
isPublic: false,
checkInCode: undefined,
});
expect(mockTable.create).toHaveBeenCalled();
});
Expand Down
25 changes: 20 additions & 5 deletions src/lib/airtable/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export function createEventsClient(apiKey: string, baseId: string) {
cohortId: cohortLookup?.[0] ?? '',
eventType: record.get(EVENT_FIELDS.EVENT_TYPE) as EventType,
surveyUrl: record.get(EVENT_FIELDS.SURVEY) as string | undefined,
isPublic: (record.get(EVENT_FIELDS.PUBLIC) as boolean) ?? false,
checkInCode: record.get(EVENT_FIELDS.CHECK_IN_CODE) as number | undefined,
};
});
}
Expand All @@ -62,6 +64,8 @@ export function createEventsClient(apiKey: string, baseId: string) {
cohortId: cohortLookup?.[0] ?? '',
eventType: record.get(EVENT_FIELDS.EVENT_TYPE) as EventType,
surveyUrl: record.get(EVENT_FIELDS.SURVEY) as string | undefined,
isPublic: (record.get(EVENT_FIELDS.PUBLIC) as boolean) ?? false,
checkInCode: record.get(EVENT_FIELDS.CHECK_IN_CODE) as number | undefined,
};
}
catch {
Expand All @@ -73,21 +77,28 @@ export function createEventsClient(apiKey: string, baseId: string) {
* Create a new event
*/
async function createEvent(data: CreateEventInput): Promise<Event> {
const record = await eventsTable.create({
const fields: Airtable.FieldSet = {
[EVENT_FIELDS.NAME]: data.name,
[EVENT_FIELDS.DATE_TIME]: data.dateTime,
[EVENT_FIELDS.COHORT]: [data.cohortId],
[EVENT_FIELDS.EVENT_TYPE]: data.eventType,
[EVENT_FIELDS.SURVEY]: data.surveyUrl,
});
};

if (data.cohortId) fields[EVENT_FIELDS.COHORT] = [data.cohortId];
if (data.surveyUrl) fields[EVENT_FIELDS.SURVEY] = data.surveyUrl;
if (data.isPublic !== undefined) fields[EVENT_FIELDS.PUBLIC] = data.isPublic;
if (data.checkInCode !== undefined) fields[EVENT_FIELDS.CHECK_IN_CODE] = data.checkInCode;

const record = await eventsTable.create(fields);

return {
id: record.id,
name: data.name,
dateTime: data.dateTime,
cohortId: data.cohortId,
cohortId: data.cohortId ?? '',
eventType: data.eventType,
surveyUrl: data.surveyUrl,
isPublic: data.isPublic ?? false,
checkInCode: data.checkInCode,
};
}

Expand All @@ -102,6 +113,8 @@ export function createEventsClient(apiKey: string, baseId: string) {
if (data.cohortId !== undefined) fields[EVENT_FIELDS.COHORT] = [data.cohortId];
if (data.eventType !== undefined) fields[EVENT_FIELDS.EVENT_TYPE] = data.eventType;
if (data.surveyUrl !== undefined) fields[EVENT_FIELDS.SURVEY] = data.surveyUrl;
if (data.isPublic !== undefined) fields[EVENT_FIELDS.PUBLIC] = data.isPublic;
if (data.checkInCode !== undefined) fields[EVENT_FIELDS.CHECK_IN_CODE] = data.checkInCode;

const record = await eventsTable.update(id, fields);
const cohortLookup = record.get(EVENT_FIELDS.COHORT) as string[] | undefined;
Expand All @@ -113,6 +126,8 @@ export function createEventsClient(apiKey: string, baseId: string) {
cohortId: cohortLookup?.[0] ?? '',
eventType: record.get(EVENT_FIELDS.EVENT_TYPE) as EventType,
surveyUrl: record.get(EVENT_FIELDS.SURVEY) as string | undefined,
isPublic: (record.get(EVENT_FIELDS.PUBLIC) as boolean) ?? false,
checkInCode: record.get(EVENT_FIELDS.CHECK_IN_CODE) as number | undefined,
};
}

Expand Down
8 changes: 7 additions & 1 deletion src/lib/types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ export interface Event {
cohortName?: string;
eventType: EventType;
surveyUrl?: string;
isPublic: boolean;
checkInCode?: number;
}

export interface CreateEventInput {
name: string;
dateTime: string;
cohortId: string;
cohortId?: string;
eventType: EventType;
surveyUrl?: string;
isPublic?: boolean;
checkInCode?: number;
}

export interface UpdateEventInput {
Expand All @@ -24,6 +28,8 @@ export interface UpdateEventInput {
cohortId?: string;
eventType?: EventType;
surveyUrl?: string;
isPublic?: boolean;
checkInCode?: number;
}

export interface EventFilters {
Expand Down
6 changes: 4 additions & 2 deletions src/routes/admin/events/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
function handleCohortFilter(event: Event) {
const select = event.target as HTMLSelectElement;
const cohortId = select.value;
const baseUrl = resolve('/admin/events');
if (cohortId) {
goto(resolve(`/admin/events?cohort=${cohortId}`));
// eslint-disable-next-line svelte/no-navigation-without-resolve -- query params require string concat
goto(baseUrl + '?cohort=' + cohortId);
}
else {
goto(resolve('/admin/events'));
goto(baseUrl);
}
}
</script>
Expand Down