add assignment completion ui
This commit is contained in:
parent
0d9e2fa71b
commit
9f53245987
3 changed files with 335 additions and 22 deletions
|
|
@ -17,7 +17,7 @@ export const scheduleSummarySchema = z.object({
|
|||
assignmentCount: z.number().int().nonnegative(),
|
||||
})
|
||||
|
||||
const scheduleAssignmentSchema = z.object({
|
||||
const assignmentIdentitySchema = z.object({
|
||||
id: z.number().int().positive(),
|
||||
element: z.object({
|
||||
name: z.string().min(1),
|
||||
|
|
@ -26,7 +26,11 @@ const scheduleAssignmentSchema = z.object({
|
|||
}),
|
||||
})
|
||||
|
||||
export const assignmentForDateSchema = scheduleAssignmentSchema.extend({
|
||||
const scheduleAssignmentSchema = assignmentIdentitySchema.extend({
|
||||
completedAt: z.string().datetime({ offset: true }).nullable(),
|
||||
})
|
||||
|
||||
export const assignmentForDateSchema = assignmentIdentitySchema.extend({
|
||||
schedule: z.object({
|
||||
id: z.number().int().positive(),
|
||||
set: z.object({
|
||||
|
|
@ -57,6 +61,13 @@ const assignmentsForDateResponseSchema = z.object({
|
|||
assignments: z.array(assignmentForDateSchema),
|
||||
})
|
||||
|
||||
const assignmentCompletionResponseSchema = z.object({
|
||||
assignment: z.object({
|
||||
id: z.number().int().positive(),
|
||||
completedAt: z.string().datetime({ offset: true }).nullable(),
|
||||
}),
|
||||
})
|
||||
|
||||
const errorResponseSchema = z.object({
|
||||
error: z.string().min(1),
|
||||
})
|
||||
|
|
@ -75,6 +86,7 @@ const LIST_ERROR = "We couldn't load your schedules."
|
|||
const DETAIL_ERROR = "We couldn't load this schedule."
|
||||
const CREATE_ERROR = "We couldn't create this schedule."
|
||||
const ASSIGNMENTS_ERROR = "We couldn't load today's assignments."
|
||||
const COMPLETION_ERROR = "We couldn't update this assignment."
|
||||
|
||||
export const useSchedulesStore = defineStore('schedules', () => {
|
||||
const schedules = ref<ScheduleSummary[]>([])
|
||||
|
|
@ -89,6 +101,8 @@ export const useSchedulesStore = defineStore('schedules', () => {
|
|||
const assignmentsForDate = ref<AssignmentForDate[]>([])
|
||||
const assignmentsLoading = ref(false)
|
||||
const assignmentsError = ref<string | null>(null)
|
||||
const assignmentCompletionPendingIds = ref<number[]>([])
|
||||
const assignmentCompletionErrors = ref<Record<number, string>>({})
|
||||
let activeDetailRequestId = 0
|
||||
let assignmentsRequestId = 0
|
||||
|
||||
|
|
@ -274,6 +288,84 @@ export const useSchedulesStore = defineStore('schedules', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function setAssignmentCompleted(
|
||||
assignmentId: number,
|
||||
completed: boolean,
|
||||
): Promise<boolean> {
|
||||
if (assignmentCompletionPendingIds.value.includes(assignmentId)) {
|
||||
return false
|
||||
}
|
||||
|
||||
assignmentCompletionPendingIds.value = [...assignmentCompletionPendingIds.value, assignmentId]
|
||||
const remainingErrors = { ...assignmentCompletionErrors.value }
|
||||
delete remainingErrors[assignmentId]
|
||||
assignmentCompletionErrors.value = remainingErrors
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/assignments/${assignmentId}`, {
|
||||
method: 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ completed }),
|
||||
})
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error('assignment completion request failed')
|
||||
}
|
||||
|
||||
const responseBody: unknown = await response.json()
|
||||
const assignment = assignmentCompletionResponseSchema.parse(responseBody).assignment
|
||||
const completionDoesNotMatchRequest = completed
|
||||
? assignment.completedAt === null
|
||||
: assignment.completedAt !== null
|
||||
if (assignment.id !== assignmentId || completionDoesNotMatchRequest) {
|
||||
throw new Error('assignment completion response did not match request')
|
||||
}
|
||||
|
||||
if (activeSchedule.value !== null) {
|
||||
for (const day of activeSchedule.value.days) {
|
||||
const activeAssignment = day.assignments.find(
|
||||
(candidate) => candidate.id === assignmentId,
|
||||
)
|
||||
if (activeAssignment !== undefined) {
|
||||
activeAssignment.completedAt = assignment.completedAt
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assignment.completedAt !== null) {
|
||||
assignmentsForDate.value = assignmentsForDate.value.filter(
|
||||
(candidate) => candidate.id !== assignmentId,
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
} catch {
|
||||
assignmentCompletionErrors.value = {
|
||||
...assignmentCompletionErrors.value,
|
||||
[assignmentId]: COMPLETION_ERROR,
|
||||
}
|
||||
|
||||
return false
|
||||
} finally {
|
||||
assignmentCompletionPendingIds.value = assignmentCompletionPendingIds.value.filter(
|
||||
(pendingId) => pendingId !== assignmentId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function isAssignmentCompletionPending(assignmentId: number): boolean {
|
||||
return assignmentCompletionPendingIds.value.includes(assignmentId)
|
||||
}
|
||||
|
||||
function assignmentCompletionError(assignmentId: number): string | null {
|
||||
return assignmentCompletionErrors.value[assignmentId] ?? null
|
||||
}
|
||||
|
||||
return {
|
||||
schedules,
|
||||
listLoading,
|
||||
|
|
@ -287,9 +379,14 @@ export const useSchedulesStore = defineStore('schedules', () => {
|
|||
assignmentsForDate,
|
||||
assignmentsLoading,
|
||||
assignmentsError,
|
||||
assignmentCompletionPendingIds,
|
||||
assignmentCompletionErrors,
|
||||
fetchSchedules,
|
||||
fetchSchedule,
|
||||
createSchedule,
|
||||
fetchAssignmentsForDate,
|
||||
setAssignmentCompleted,
|
||||
isAssignmentCompletionPending,
|
||||
assignmentCompletionError,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue