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">
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
import AuthenticatedHeader from '@/components/AuthenticatedHeader.vue'
|
import AuthenticatedHeader from '@/components/AuthenticatedHeader.vue'
|
||||||
import { useSchedulesStore } from '@/stores/schedules'
|
import { type AssignmentForDate, useSchedulesStore } from '@/stores/schedules'
|
||||||
import { useSetsStore } from '@/stores/sets'
|
import { useSetsStore } from '@/stores/sets'
|
||||||
|
|
||||||
|
type TodayAssignmentQueue = {
|
||||||
|
assignment: AssignmentForDate
|
||||||
|
dueCount: number
|
||||||
|
}
|
||||||
|
|
||||||
const setsStore = useSetsStore()
|
const setsStore = useSetsStore()
|
||||||
const schedulesStore = useSchedulesStore()
|
const schedulesStore = useSchedulesStore()
|
||||||
const { sets, loading, error } = storeToRefs(setsStore)
|
const { sets, loading, error } = storeToRefs(setsStore)
|
||||||
const { schedules, listLoading, listError } = storeToRefs(schedulesStore)
|
const { schedules, listLoading, listError } = storeToRefs(schedulesStore)
|
||||||
const { assignmentsForDate, assignmentsLoading, assignmentsError } = storeToRefs(schedulesStore)
|
const { assignmentsForDate, assignmentsLoading, assignmentsError } = storeToRefs(schedulesStore)
|
||||||
const todayDate = ref(browserDate(new Date()))
|
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 () => {
|
onMounted(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|
@ -83,36 +106,54 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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.
|
Nothing is due today.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul v-else class="today-assignment-list" aria-label="Today's assignments">
|
<ul v-else class="today-assignment-list" aria-label="Today's assignments">
|
||||||
<li
|
<li
|
||||||
v-for="assignment in assignmentsForDate"
|
v-for="queue in todayAssignmentQueues"
|
||||||
:key="assignment.id"
|
:key="queue.assignment.schedule.id"
|
||||||
:data-today-assignment="assignment.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">
|
<article class="today-assignment">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
class="today-assignment-link"
|
class="today-assignment-link"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'schedule-detail',
|
name: 'schedule-detail',
|
||||||
params: { scheduleId: assignment.schedule.id },
|
params: { scheduleId: queue.assignment.schedule.id },
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="today-assignment__heading">
|
<div class="today-assignment__heading">
|
||||||
<p>{{ assignment.schedule.set.name }}</p>
|
<p>{{ queue.assignment.schedule.set.name }}</p>
|
||||||
<span class="today-assignment__kind">{{ assignment.element.kind }}</span>
|
<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>
|
</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">
|
<p class="today-assignment__path">
|
||||||
{{ assignment.element.path.join(' / ') }}
|
{{ queue.assignment.element.path.join(' / ') }}
|
||||||
</p>
|
</p>
|
||||||
<span class="today-assignment__action">
|
<span class="today-assignment__action">
|
||||||
Open schedule
|
Open schedule
|
||||||
|
|
@ -121,21 +162,21 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<div class="today-assignment__completion">
|
<div class="today-assignment__completion">
|
||||||
<p
|
<p
|
||||||
v-if="schedulesStore.assignmentCompletionError(assignment.id) !== null"
|
v-if="schedulesStore.assignmentCompletionError(queue.assignment.id) !== null"
|
||||||
class="assignment-completion-error"
|
class="assignment-completion-error"
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
{{ schedulesStore.assignmentCompletionError(assignment.id) }}
|
{{ schedulesStore.assignmentCompletionError(queue.assignment.id) }}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="assignment-completion-button"
|
class="assignment-completion-button"
|
||||||
:aria-label="`Mark ${assignment.element.path.join(' / ')} complete`"
|
:aria-label="`Mark ${queue.assignment.element.path.join(' / ')} complete`"
|
||||||
:disabled="schedulesStore.isAssignmentCompletionPending(assignment.id)"
|
:disabled="schedulesStore.isAssignmentCompletionPending(queue.assignment.id)"
|
||||||
@click="completeAssignment(assignment.id)"
|
@click="completeAssignment(queue.assignment.id)"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
schedulesStore.isAssignmentCompletionPending(assignment.id)
|
schedulesStore.isAssignmentCompletionPending(queue.assignment.id)
|
||||||
? 'Completing...'
|
? 'Completing...'
|
||||||
: 'Mark complete'
|
: 'Mark complete'
|
||||||
}}
|
}}
|
||||||
|
|
@ -355,6 +396,40 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
||||||
list-style: none;
|
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 {
|
.today-assignment-link {
|
||||||
display: block;
|
display: block;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
@ -362,8 +437,11 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
.today-assignment {
|
.today-assignment {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 1.4rem;
|
padding: 1.4rem;
|
||||||
border: 1px solid rgb(24 48 41 / 12%);
|
border: 1px solid rgb(24 48 41 / 12%);
|
||||||
|
|
@ -404,19 +482,35 @@ async function completeAssignment(assignmentId: number): Promise<void> {
|
||||||
text-transform: none;
|
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;
|
display: inline-flex;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
margin-top: 0.85rem;
|
|
||||||
padding: 0.3rem 0.55rem;
|
padding: 0.3rem 0.55rem;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
color: #923f31;
|
|
||||||
background: #f7e5dc;
|
|
||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: 0.025em;
|
letter-spacing: 0.025em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.today-assignment__overdue {
|
||||||
|
color: #923f31;
|
||||||
|
background: #f7e5dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today-assignment__queue-count {
|
||||||
|
color: #285c4e;
|
||||||
|
background: #e2ebe4;
|
||||||
|
}
|
||||||
|
|
||||||
.today-assignment__path {
|
.today-assignment__path {
|
||||||
margin: 1.75rem 0 0;
|
margin: 1.75rem 0 0;
|
||||||
color: #183029;
|
color: #183029;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue