queue today assignments by schedule
This commit is contained in:
parent
ba6e4908da
commit
fea6c3f7a8
1 changed files with 120 additions and 26 deletions
|
|
@ -1,17 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
|
||||
import AuthenticatedHeader from '@/components/AuthenticatedHeader.vue'
|
||||
import { useSchedulesStore } from '@/stores/schedules'
|
||||
import { type AssignmentForDate, useSchedulesStore } from '@/stores/schedules'
|
||||
import { useSetsStore } from '@/stores/sets'
|
||||
|
||||
type TodayAssignmentQueue = {
|
||||
assignment: AssignmentForDate
|
||||
dueCount: number
|
||||
}
|
||||
|
||||
const setsStore = useSetsStore()
|
||||
const schedulesStore = useSchedulesStore()
|
||||
const { sets, loading, error } = storeToRefs(setsStore)
|
||||
const { schedules, listLoading, listError } = storeToRefs(schedulesStore)
|
||||
const { assignmentsForDate, assignmentsLoading, assignmentsError } = storeToRefs(schedulesStore)
|
||||
const todayDate = ref(browserDate(new Date()))
|
||||
const todayAssignmentQueues = computed<TodayAssignmentQueue[]>(() => {
|
||||
const queuesBySchedule = new Map<number, TodayAssignmentQueue>()
|
||||
|
||||
for (const assignment of assignmentsForDate.value) {
|
||||
const queue = queuesBySchedule.get(assignment.schedule.id)
|
||||
if (queue !== undefined) {
|
||||
queue.dueCount += 1
|
||||
continue
|
||||
}
|
||||
|
||||
queuesBySchedule.set(assignment.schedule.id, {
|
||||
assignment,
|
||||
dueCount: 1,
|
||||
})
|
||||
}
|
||||
|
||||
return Array.from(queuesBySchedule.values())
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
|
|
@ -83,36 +106,54 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<p v-else-if="assignmentsForDate.length === 0" class="today-state" role="status">
|
||||
<p v-else-if="todayAssignmentQueues.length === 0" class="today-state" role="status">
|
||||
Nothing is due today.
|
||||
</p>
|
||||
|
||||
<ul v-else class="today-assignment-list" aria-label="Today's assignments">
|
||||
<li
|
||||
v-for="assignment in assignmentsForDate"
|
||||
:key="assignment.id"
|
||||
:data-today-assignment="assignment.id"
|
||||
v-for="queue in todayAssignmentQueues"
|
||||
:key="queue.assignment.schedule.id"
|
||||
class="today-assignment-stack"
|
||||
:class="{ 'today-assignment-stack--queued': queue.dueCount > 1 }"
|
||||
:data-due-count="queue.dueCount"
|
||||
:data-today-assignment="queue.assignment.id"
|
||||
:data-today-schedule="queue.assignment.schedule.id"
|
||||
>
|
||||
<article class="today-assignment">
|
||||
<RouterLink
|
||||
class="today-assignment-link"
|
||||
:to="{
|
||||
name: 'schedule-detail',
|
||||
params: { scheduleId: assignment.schedule.id },
|
||||
params: { scheduleId: queue.assignment.schedule.id },
|
||||
}"
|
||||
>
|
||||
<div class="today-assignment__heading">
|
||||
<p>{{ assignment.schedule.set.name }}</p>
|
||||
<span class="today-assignment__kind">{{ assignment.element.kind }}</span>
|
||||
<p>{{ queue.assignment.schedule.set.name }}</p>
|
||||
<span class="today-assignment__kind">
|
||||
{{ queue.assignment.element.kind }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOverdue(queue.assignment.scheduledDate) || queue.dueCount > 1"
|
||||
class="today-assignment__status"
|
||||
>
|
||||
<time
|
||||
v-if="isOverdue(queue.assignment.scheduledDate)"
|
||||
class="today-assignment__overdue"
|
||||
:datetime="queue.assignment.scheduledDate"
|
||||
v-text="overdueLabel(queue.assignment.scheduledDate)"
|
||||
></time>
|
||||
<span
|
||||
v-if="queue.dueCount > 1"
|
||||
class="today-assignment__queue-count"
|
||||
:aria-label="`${queue.dueCount} assignments from this schedule are due now. Complete this assignment first.`"
|
||||
>
|
||||
{{ queue.dueCount }} due now
|
||||
</span>
|
||||
</div>
|
||||
<time
|
||||
v-if="isOverdue(assignment.scheduledDate)"
|
||||
class="today-assignment__overdue"
|
||||
:datetime="assignment.scheduledDate"
|
||||
v-text="overdueLabel(assignment.scheduledDate)"
|
||||
></time>
|
||||
<p class="today-assignment__path">
|
||||
{{ assignment.element.path.join(' / ') }}
|
||||
{{ queue.assignment.element.path.join(' / ') }}
|
||||
</p>
|
||||
<span class="today-assignment__action">
|
||||
Open schedule
|
||||
|
|
@ -121,21 +162,21 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
|||
</RouterLink>
|
||||
<div class="today-assignment__completion">
|
||||
<p
|
||||
v-if="schedulesStore.assignmentCompletionError(assignment.id) !== null"
|
||||
v-if="schedulesStore.assignmentCompletionError(queue.assignment.id) !== null"
|
||||
class="assignment-completion-error"
|
||||
role="alert"
|
||||
>
|
||||
{{ schedulesStore.assignmentCompletionError(assignment.id) }}
|
||||
{{ schedulesStore.assignmentCompletionError(queue.assignment.id) }}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="assignment-completion-button"
|
||||
:aria-label="`Mark ${assignment.element.path.join(' / ')} complete`"
|
||||
:disabled="schedulesStore.isAssignmentCompletionPending(assignment.id)"
|
||||
@click="completeAssignment(assignment.id)"
|
||||
:aria-label="`Mark ${queue.assignment.element.path.join(' / ')} complete`"
|
||||
:disabled="schedulesStore.isAssignmentCompletionPending(queue.assignment.id)"
|
||||
@click="completeAssignment(queue.assignment.id)"
|
||||
>
|
||||
{{
|
||||
schedulesStore.isAssignmentCompletionPending(assignment.id)
|
||||
schedulesStore.isAssignmentCompletionPending(queue.assignment.id)
|
||||
? 'Completing...'
|
||||
: 'Mark complete'
|
||||
}}
|
||||
|
|
@ -355,6 +396,40 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
|||
list-style: none;
|
||||
}
|
||||
|
||||
.today-assignment-stack {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.today-assignment-stack--queued {
|
||||
padding: 0 0.55rem 0.55rem 0;
|
||||
}
|
||||
|
||||
.today-assignment-stack--queued::before,
|
||||
.today-assignment-stack--queued::after {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.today-assignment-stack--queued::before {
|
||||
z-index: 1;
|
||||
inset: 0.28rem 0.28rem 0.28rem 0.28rem;
|
||||
border: 1px solid rgb(24 48 41 / 10%);
|
||||
border-radius: 1rem;
|
||||
background: #eee9dc;
|
||||
transform: translate(0.28rem, 0.28rem);
|
||||
}
|
||||
|
||||
.today-assignment-stack--queued::after {
|
||||
z-index: 0;
|
||||
inset: 0.55rem 0 0 0.55rem;
|
||||
border: 1px solid rgb(24 48 41 / 8%);
|
||||
border-radius: 1rem;
|
||||
background: #e5dfd1;
|
||||
}
|
||||
|
||||
.today-assignment-link {
|
||||
display: block;
|
||||
color: inherit;
|
||||
|
|
@ -362,8 +437,11 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
|||
}
|
||||
|
||||
.today-assignment {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 1.4rem;
|
||||
border: 1px solid rgb(24 48 41 / 12%);
|
||||
|
|
@ -404,19 +482,35 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
|||
text-transform: none;
|
||||
}
|
||||
|
||||
.today-assignment__overdue {
|
||||
.today-assignment__status {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.today-assignment__overdue,
|
||||
.today-assignment__queue-count {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
margin-top: 0.85rem;
|
||||
padding: 0.3rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
color: #923f31;
|
||||
background: #f7e5dc;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.today-assignment__overdue {
|
||||
color: #923f31;
|
||||
background: #f7e5dc;
|
||||
}
|
||||
|
||||
.today-assignment__queue-count {
|
||||
color: #285c4e;
|
||||
background: #e2ebe4;
|
||||
}
|
||||
|
||||
.today-assignment__path {
|
||||
margin: 1.75rem 0 0;
|
||||
color: #183029;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue