Refactor slot editing to use searchable selections
Instead of having to type in exactly the name of events or shifts and then hope you remembered it right, replace these interactions with the custom select component that gives a complete list of the available choices and allows quickly searching for the right one.
This commit is contained in:
parent
da65103e05
commit
b0d5cdf791
4 changed files with 125 additions and 215 deletions
|
@ -7,7 +7,6 @@
|
|||
<th>end</th>
|
||||
<th>duration</th>
|
||||
<th>shift</th>
|
||||
<th>s</th>
|
||||
<th>role</th>
|
||||
<th>assigned</th>
|
||||
<th v-if="edit"></th>
|
||||
|
@ -35,24 +34,16 @@
|
|||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
v-model="newShiftName"
|
||||
>
|
||||
<SelectSingleEntity
|
||||
:entities="schedule.shifts"
|
||||
v-model="newShiftId"
|
||||
/>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<select
|
||||
<SelectSingleEntity
|
||||
:entities="schedule.roles"
|
||||
v-model="newShiftRoleId"
|
||||
>
|
||||
<option
|
||||
v-for="role in schedule.roles.values()"
|
||||
:key="role.id"
|
||||
:value="role.id"
|
||||
:disabled="role.deleted"
|
||||
:selected="role.id === newShiftRoleId"
|
||||
>{{ role.name }}</option>
|
||||
</select>
|
||||
/>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
|
@ -90,34 +81,28 @@
|
|||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
v-model="ss.name"
|
||||
>
|
||||
<SelectSingleEntity
|
||||
:entities="schedule.shifts"
|
||||
:modelValue="ss.slot.shiftId"
|
||||
@update:modelValue="ss.slot.setShiftId($event)"
|
||||
/>
|
||||
</td>
|
||||
<td>{{ status(ss) }}</td>
|
||||
<td>
|
||||
<select
|
||||
<SelectSingleEntity
|
||||
v-if="ss.shift"
|
||||
:entities="schedule.roles"
|
||||
v-model="ss.shift.roleId"
|
||||
>
|
||||
<option
|
||||
v-for="role in schedule.roles.values()"
|
||||
:key="role.id"
|
||||
:value="role.id"
|
||||
:disabled="role.deleted"
|
||||
:selected="role.id === ss.shift.roleId"
|
||||
>{{ role.name }}</option>
|
||||
</select>
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<AssignedCrew
|
||||
:edit="true"
|
||||
<SelectMultiEntity
|
||||
:entities="usersStore.users"
|
||||
v-model="ss.slot.assigned"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
:disabled="ss.deleted"
|
||||
:disabled="ss.slot.deleted"
|
||||
type="button"
|
||||
@click="ss.slot.deleted = true"
|
||||
>Remove</button>
|
||||
|
@ -149,12 +134,18 @@
|
|||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
v-model="newShiftName"
|
||||
>
|
||||
<SelectSingleEntity
|
||||
:entities="schedule.shifts"
|
||||
v-model="newShiftId"
|
||||
/>
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<td>
|
||||
<SelectSingleEntity
|
||||
:entities="schedule.roles"
|
||||
v-model="newShiftRoleId"
|
||||
/>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<button
|
||||
type="button"
|
||||
@click="newShiftSlot()"
|
||||
|
@ -180,10 +171,11 @@
|
|||
<td>{{ ss.start.toFormat("yyyy-LL-dd HH:mm") }}</td>
|
||||
<td>{{ ss.end.toFormat("HH:mm") }}</td>
|
||||
<td>{{ ss.end.diff(ss.start).toFormat('hh:mm') }}</td>
|
||||
<td>{{ ss.name }}</td>
|
||||
<td>{{ status(ss) }}</td>
|
||||
<td>{{ ss.roleId }}</td>
|
||||
<td><AssignedCrew :modelValue="ss.assigned" :edit="false" /></td>
|
||||
<td>{{ ss.shift?.name }}</td>
|
||||
<td>{{ ss.shift?.roleId }}</td>
|
||||
<td>
|
||||
<AssignedCrew :modelValue="ss.slot.assigned" :edit="false" />
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
|
@ -206,12 +198,8 @@ const props = defineProps<{
|
|||
interface ShiftSlot {
|
||||
type: "slot",
|
||||
id: Id,
|
||||
deleted: boolean,
|
||||
shift: ClientScheduleShift,
|
||||
shift?: ClientScheduleShift,
|
||||
slot: ClientScheduleShiftSlot,
|
||||
name: string,
|
||||
roleId: Id,
|
||||
assigned: Set<Id>,
|
||||
start: DateTime,
|
||||
end: DateTime,
|
||||
}
|
||||
|
@ -221,24 +209,13 @@ interface Gap {
|
|||
id?: undefined,
|
||||
shift?: undefined,
|
||||
slot?: undefined,
|
||||
name?: undefined,
|
||||
role?: undefined,
|
||||
start: DateTime,
|
||||
end: DateTime,
|
||||
}
|
||||
|
||||
function status(shiftSlot: ShiftSlot) {
|
||||
if (
|
||||
!shiftSlot.shift
|
||||
|| shiftSlot.shift.name !== shiftSlot.name
|
||||
) {
|
||||
const shift = [...schedule.value.shifts.values()].find(shift => !shift.deleted && shift.name === shiftSlot.name);
|
||||
return shift ? "L" : "N";
|
||||
}
|
||||
return shiftSlot.shift.slots.size === 1 ? "" : shiftSlot.shift.slots.size;
|
||||
}
|
||||
|
||||
const accountStore = useAccountStore();
|
||||
const usersStore = useUsersStore();
|
||||
await usersStore.fetch();
|
||||
const schedule = await useSchedule();
|
||||
|
||||
const oneDayMs = 24 * 60 * 60 * 1000;
|
||||
|
@ -286,7 +263,7 @@ function durationFromTime(time: string) {
|
|||
}
|
||||
return duration;
|
||||
}
|
||||
const newShiftName = ref("");
|
||||
const newShiftId = ref<Id>();
|
||||
|
||||
function editShiftSlot(
|
||||
shiftSlot: ShiftSlot,
|
||||
|
@ -298,20 +275,18 @@ function editShiftSlot(
|
|||
) {
|
||||
if (edits.start) {
|
||||
const start = DateTime.fromISO(edits.start, { zone: accountStore.activeTimezone, locale: accountStore.activeLocale });
|
||||
shiftSlot.start = start;
|
||||
shiftSlot.end = start.plus(shiftSlot.slot.end.diff(shiftSlot.slot.start));
|
||||
shiftSlot.slot.start = start;
|
||||
shiftSlot.slot.end = start.plus(shiftSlot.slot.end.diff(shiftSlot.slot.start));
|
||||
}
|
||||
if (edits.end !== undefined) {
|
||||
shiftSlot.end = endFromTime(shiftSlot.start, edits.end);
|
||||
shiftSlot.slot.end = endFromTime(shiftSlot.slot.start, edits.end);
|
||||
}
|
||||
if (edits.duration !== undefined) {
|
||||
shiftSlot.end = shiftSlot.start.plus(durationFromTime(edits.duration));
|
||||
shiftSlot.slot.end = shiftSlot.slot.start.plus(durationFromTime(edits.duration));
|
||||
}
|
||||
}
|
||||
function newShiftSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
||||
const name = newShiftName.value;
|
||||
const nameId = toId(name);
|
||||
const shift = [...schedule.value.shifts.values()].find(shift => toId(shift.name) === nameId);
|
||||
const shift = schedule.value.shifts.get(newShiftId.value!);
|
||||
if (!shift) {
|
||||
alert("Invalid shift");
|
||||
return;
|
||||
|
@ -352,7 +327,7 @@ function newShiftSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
|||
);
|
||||
schedule.value.shiftSlots.set(slot.id, slot);
|
||||
shift.slotIds.add(slot.id);
|
||||
newShiftName.value = "";
|
||||
newShiftId.value = undefined;
|
||||
}
|
||||
|
||||
const oneHourMs = 60 * 60 * 1000;
|
||||
|
@ -367,25 +342,20 @@ function gapFormat(gap: Gap) {
|
|||
|
||||
const shiftSlots = computed(() => {
|
||||
const data: (ShiftSlot | Gap)[] = [];
|
||||
for (const shift of schedule.value.shifts.values()) {
|
||||
if (props.roleId !== undefined && shift.roleId !== props.roleId)
|
||||
for (const slot of schedule.value.shiftSlots.values()) {
|
||||
const shift = schedule.value.shifts.get(slot.shiftId!);
|
||||
if (shift && props.roleId !== undefined && shift.roleId !== props.roleId)
|
||||
continue;
|
||||
for (const slot of shift.slots.values()) {
|
||||
if (props.shiftSlotFilter && !props.shiftSlotFilter(slot))
|
||||
continue;
|
||||
data.push({
|
||||
type: "slot",
|
||||
id: slot.id,
|
||||
deleted: slot.deleted || shift.deleted,
|
||||
shift,
|
||||
slot,
|
||||
name: shift.name,
|
||||
roleId: shift.roleId,
|
||||
assigned: slot.assigned,
|
||||
start: slot.start,
|
||||
end: slot.end,
|
||||
});
|
||||
}
|
||||
if (props.shiftSlotFilter && !props.shiftSlotFilter(slot))
|
||||
continue;
|
||||
data.push({
|
||||
type: "slot",
|
||||
id: slot.id,
|
||||
shift,
|
||||
slot,
|
||||
start: slot.start,
|
||||
end: slot.end,
|
||||
});
|
||||
}
|
||||
const byTime = (a: DateTime, b: DateTime) => a.toMillis() - b.toMillis();
|
||||
data.sort((a, b) => byTime(a.start, b.start) || byTime(a.end, b.end));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue