Skip to content

Commit 05effcc

Browse files
Merge pull request #17 from foundersandcoders/feature/ap-25-individual-apprentice-attendance-view
Individual apprentice attendance view (AP-25)
2 parents 3773dea + 8143ecc commit 05effcc

File tree

24 files changed

+1723
-134
lines changed

24 files changed

+1723
-134
lines changed

.claude/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,26 @@ Custom automation for working with Jira tasks.
1212

1313
## Complete Workflow
1414

15-
### 1. Start a Task
15+
### 1. Create the Plan
1616

1717
```
1818
/plan AP-23
1919
```
2020

2121
This will:
22-
- Fetch AP-23 from Jira
22+
- Fetch AP-23 from Jira (if connection fails, you'll be asked to run `/mcp``atlassian``4. Reconnect`)
2323
- Move it to "In Progress"
2424
- Create branch: `feature/ap-23-{slugified-summary}`
2525
- Write `docs/plan.md` with checkbox tasks
2626
- Create `.claude/loop` (activates the iterator)
2727

28-
### 2. Automatic Loop
28+
**Claude stops here.** Review the plan if you want.
29+
30+
### 2. Start Implementation
31+
32+
Say `start` (or any prompt to begin working). This triggers the first task.
33+
34+
### 3. Automatic Loop
2935

3036
The hook (`.claude/hooks/plan-iterator.sh`) runs after each Claude response:
3137

@@ -45,13 +51,13 @@ For each task, Claude:
4551
4. Evaluates if `/update-report` is needed
4652
5. Hook triggers → continues to next task
4753

48-
### 3. Loop Ends
54+
### 4. Loop Ends
4955

5056
**Automatic:** When all tasks are `[x]`, `.claude/loop` is deleted and loop stops. Claude does NOT auto-start the next Jira ticket.
5157

5258
**Manual:** Run `/stop` or `rm .claude/loop`
5359

54-
### 4. Finish (manual steps)
60+
### 5. Finish (manual steps)
5561

5662
When the loop ends, you decide what to do next:
5763
- Create PR

.claude/hooks/plan-iterator.sh

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ COMPLETED=$(grep -c '^\s*- \[x\]' "$PLAN_FILE" 2>/dev/null | head -1 || echo "0"
3737
COMPLETED=${COMPLETED:-0}
3838

3939
# Stage all changes FIRST
40-
git add -A 2>/dev/null || true
40+
git add -A >/dev/null 2>&1 || true
4141

4242
# Then commit if there are staged changes
4343
if ! git diff --cached --quiet 2>/dev/null; then
4444
if [[ "$COMPLETED" -gt 0 ]]; then
4545
LAST_DONE=$(grep -E '^\s*- \[x\]' "$PLAN_FILE" | tail -1 | sed 's/.*\[x\] //')
46-
git commit -m "feat: $LAST_DONE" 2>/dev/null || true
46+
git commit -m "feat: $LAST_DONE" >/dev/null 2>&1 || true
4747
fi
4848
fi
4949

5050
# Find next task
5151
NEXT_TASK=$(grep -m1 '^\s*- \[ \]' "$PLAN_FILE" | sed 's/.*\[ \] //' | sed 's/"/\\"/g')
5252

53-
# Build the system message
54-
read -r -d '' MESSAGE << MSGEOF
53+
# Build the reason message for Claude
54+
read -r -d '' REASON << MSGEOF
5555
PLAN ITERATOR: Continue with the next task.
5656
5757
## Current Progress
@@ -76,8 +76,8 @@ $NEXT_TASK
7676
Run /stop or delete .claude/loop
7777
MSGEOF
7878

79-
# Escape message for JSON
80-
ESCAPED_MSG=$(echo "$MESSAGE" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read())[1:-1])')
79+
# Escape reason for JSON
80+
ESCAPED_REASON=$(echo "$REASON" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read())[1:-1])')
8181

82-
# Return JSON to continue
83-
echo "{\"continue\": true, \"systemMessage\": \"$ESCAPED_MSG\"}"
82+
# Return JSON with decision: block to prevent Claude from stopping
83+
echo "{\"decision\": \"block\", \"reason\": \"$ESCAPED_REASON\"}"

.mcp.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"mcpServers": {
3+
"atlassian": {
4+
"type": "sse",
5+
"url": "https://mcp.atlassian.com/v1/sse"
6+
},
7+
"github": {
8+
"type": "stdio",
9+
"command": "npx",
10+
"args": ["-y", "@modelcontextprotocol/server-github"],
11+
"env": {
12+
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
13+
}
14+
},
15+
"postman": {
16+
"type": "stdio",
17+
"command": "npx",
18+
"args": ["@postman/postman-mcp-server@latest"],
19+
"env": {
20+
"POSTMAN_API_KEY": "${POSTMAN_API_KEY}"
21+
}
22+
}
23+
}
24+
}

docs/plan.md

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
1-
# AP-27 Attendance service - aggregate queries
1+
# AP-25 Individual apprentice attendance view
22

3-
> Extend attendance service with aggregate query functions for dashboard metrics. Functions needed: getApprenticeAttendanceStats, getCohortAttendanceStats, getAttendanceSummary. Returns: total events, attended count, attendance rate, late count, excused count, trend data.
3+
> Create a view to track individual apprentice attendance history and rates. List apprentices with metrics, per-apprentice attendance rate, history of events attended/missed, filter by cohort, sort options, and visual indicator for low attendance.
44
55
## Tasks
66

7-
- [x] Define TypeScript types for attendance stats (AttendanceStats, ApprenticeAttendanceStats, CohortAttendanceStats, AttendanceSummary)
8-
- [x] Add getAllAttendance() helper to fetch all attendance records
9-
- [x] Add getAllEvents() helper to fetch all events (needed for total event counts)
10-
- [x] Implement getApprenticeAttendanceStats(apprenticeId) - individual apprentice stats
11-
- [x] Implement getCohortAttendanceStats(cohortId) - cohort aggregate stats
12-
- [x] Implement getAttendanceSummary() - overall summary for dashboard card
13-
- [x] Add trend calculation helper (compare last 4 weeks vs previous 4 weeks)
14-
- [x] Write tests for getApprenticeAttendanceStats
15-
- [x] Write tests for getCohortAttendanceStats
16-
- [x] Write tests for getAttendanceSummary
7+
- [x] Create route and page structure for `/admin/attendance/apprentices`
8+
- [x] Add server load function to fetch all apprentices with their attendance stats
9+
- [x] Create ApprenticeAttendanceCard component to display individual metrics
10+
- [x] Implement attendance history list showing events attended/missed per apprentice
11+
- [x] Add cohort filter dropdown
12+
- [x] Add sort functionality (by name, by attendance rate)
13+
- [x] Add visual indicator for low attendance (below 80%)
14+
- [x] Add click-through to detailed apprentice view
15+
- [x] Write tests for the attendance apprentices page
1716

1817
## Notes
1918

20-
- Existing attendance service is in `src/lib/airtable/attendance.ts`
21-
- Existing types in `src/lib/types/attendance.ts`
22-
- Cohort data available via `listCohorts()` and `getApprenticesByCohortId()` in airtable.ts
23-
- Events have cohortIds array (multi-cohort support)
24-
- Status values: Present, Absent, Late, Excused
25-
- Client-side aggregation approach (no Airtable schema changes)
26-
- Attendance rate = (Present + Late) / Total events for cohort
19+
- Use existing `getApprenticeAttendanceStats` from attendance service (AP-27)
20+
- Attendance rate = (attended / total events) * 100
21+
- Low attendance threshold: 80%
22+
- Filter by cohort uses existing cohort data

docs/scratchpad.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1+
Attendance is not showing names? (check Airtable)
12

23

3-
td elements reduce padding
44

55

6+
Not coming status
67

7-
Checkin page show who I am
8-
When checked in disable button or show that I already logged in. Now allows multiple
8+
Login page pretty
99

1010

11+
Staff - Apprentice pulse, now has the student email. Use this for checkin as a student, not as an external
12+
1113

1214
Show mii plaza
1315

1416
Integration with LUMA
1517

1618

1719

18-
security for not checkin if not close to date.
19-
2020

2121

2222
On readme how give permissions

package-lock.json

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)