Refactor ClientSchedule to mutable types
Use a single mutable location, event, slot, etc, for each unique resource that keeps track of the local editable client copy and the server copy of the data contained in it. This makes it much simpler to update these data structures as I can take advantage of the v-model bindings in Vue.js and work with the system instead of against it.
This commit is contained in:
parent
d48fb035b4
commit
e3ff872b5c
16 changed files with 1213 additions and 1125 deletions
|
@ -91,41 +91,39 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="es.name"
|
||||
@input="editEvent(es, { name: ($event as any).target.value })"
|
||||
v-model="es.event.name"
|
||||
>
|
||||
</td>
|
||||
<td>{{ status(es) }}</td>
|
||||
<td>
|
||||
<select
|
||||
:value="es.location.id"
|
||||
:value="es.locationId"
|
||||
@change="editEventSlot(es, { locationId: parseInt(($event as any).target.value) })"
|
||||
>
|
||||
<option
|
||||
v-for="location in schedule.locations.values()"
|
||||
:key="location.id"
|
||||
:value="location.id"
|
||||
:selected="location.id === es.location.id"
|
||||
:selected="location.id === es.locationId"
|
||||
>{{ location.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<AssignedCrew
|
||||
:edit="true"
|
||||
:modelValue="es.assigned"
|
||||
@update:modelValue="editEventSlot(es, { assigned: $event })"
|
||||
v-model="es.slot.assigned"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
:disabled="es.deleted"
|
||||
type="button"
|
||||
@click="editEventSlot(es, { deleted: true })"
|
||||
@click="es.slot.deleted = true"
|
||||
>Remove</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedEventSlot(es.id)"
|
||||
v-if="es.slot.isModified()"
|
||||
type="button"
|
||||
@click="revertEventSlot(es.id)"
|
||||
@click="es.event.discardSlot(es.slot.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</template>
|
||||
|
@ -185,7 +183,7 @@
|
|||
<td>{{ es.end.diff(es.start).toFormat('hh:mm') }}</td>
|
||||
<td>{{ es.name }}</td>
|
||||
<td>{{ status(es) }}</td>
|
||||
<td>{{ es.location.id }}</td>
|
||||
<td>{{ es.locationId }}</td>
|
||||
<td><AssignedCrew :modelValue="es.assigned" :edit="false" /></td>
|
||||
</template>
|
||||
</tr>
|
||||
|
@ -213,7 +211,7 @@ interface EventSlot {
|
|||
event: ClientScheduleEvent,
|
||||
slot: ClientScheduleEventSlot,
|
||||
name: string,
|
||||
location: ClientScheduleLocation,
|
||||
locationId: Id,
|
||||
assigned: Set<Id>,
|
||||
start: DateTime,
|
||||
end: DateTime,
|
||||
|
@ -290,48 +288,30 @@ function durationFromTime(time: string) {
|
|||
return duration;
|
||||
}
|
||||
const newEventName = ref("");
|
||||
function editEvent(
|
||||
eventSlot: EventSlot,
|
||||
edits: Parameters<ClientSchedule["editEvent"]>[1],
|
||||
) {
|
||||
schedule.value.editEvent(eventSlot.event, edits);
|
||||
}
|
||||
|
||||
function editEventSlot(
|
||||
eventSlot: EventSlot,
|
||||
edits: {
|
||||
deleted?: boolean,
|
||||
start?: string,
|
||||
end?: string,
|
||||
duration?: string,
|
||||
locationId?: Id,
|
||||
assigned?: Set<Id>,
|
||||
}
|
||||
) {
|
||||
const computedEdits: Parameters<ClientSchedule["editEventSlot"]>[1] = {
|
||||
deleted: edits.deleted,
|
||||
assigned: edits.assigned,
|
||||
};
|
||||
if (edits.start) {
|
||||
const start = DateTime.fromISO(edits.start, { zone: accountStore.activeTimezone, locale: accountStore.activeLocale });
|
||||
computedEdits.start = start;
|
||||
computedEdits.end = start.plus(eventSlot.end.diff(eventSlot.start));
|
||||
eventSlot.slot.start = start;
|
||||
eventSlot.slot.end = start.plus(eventSlot.end.diff(eventSlot.start));
|
||||
}
|
||||
if (edits.end !== undefined) {
|
||||
computedEdits.end = endFromTime(eventSlot.start, edits.end);
|
||||
eventSlot.slot.end = endFromTime(eventSlot.start, edits.end);
|
||||
}
|
||||
if (edits.duration !== undefined) {
|
||||
computedEdits.end = eventSlot.start.plus(durationFromTime(edits.duration));
|
||||
eventSlot.slot.end = eventSlot.start.plus(durationFromTime(edits.duration));
|
||||
}
|
||||
if (edits.locationId !== undefined) {
|
||||
const location = schedule.value.locations.get(edits.locationId);
|
||||
if (location)
|
||||
computedEdits.locations = [location];
|
||||
eventSlot.slot.locationIds = new Set([edits.locationId]);
|
||||
}
|
||||
schedule.value.editEventSlot(eventSlot.slot, computedEdits);
|
||||
}
|
||||
function revertEventSlot(id: Id) {
|
||||
schedule.value.restoreEventSlot(id);
|
||||
}
|
||||
function newEventSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
||||
const name = newEventName.value;
|
||||
|
@ -341,8 +321,7 @@ function newEventSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
|||
alert("Invalid event");
|
||||
return;
|
||||
}
|
||||
const location = schedule.value.locations.get(newEventLocation.value!);
|
||||
if (!location) {
|
||||
if (newEventLocation.value === undefined) {
|
||||
alert("Invalid location");
|
||||
return;
|
||||
}
|
||||
|
@ -367,18 +346,19 @@ function newEventSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
|||
alert("Invalid start and/or end time");
|
||||
return;
|
||||
}
|
||||
const slot = new ClientScheduleEventSlot(
|
||||
const slot = ClientScheduleEventSlot.create(
|
||||
schedule.value,
|
||||
schedule.value.nextClientId--,
|
||||
false,
|
||||
event.id,
|
||||
start,
|
||||
end,
|
||||
[location],
|
||||
new Set([newEventLocation.value]),
|
||||
new Set(),
|
||||
0,
|
||||
);
|
||||
schedule.value.eventSlots.set(slot.id, slot);
|
||||
event.slotIds.add(slot.id);
|
||||
newEventName.value = "";
|
||||
schedule.value.setEventSlot(slot);
|
||||
}
|
||||
|
||||
const oneHourMs = 60 * 60 * 1000;
|
||||
|
@ -399,8 +379,8 @@ const eventSlots = computed(() => {
|
|||
for (const slot of event.slots.values()) {
|
||||
if (props.eventSlotFilter && !props.eventSlotFilter(slot))
|
||||
continue;
|
||||
for (const location of slot.locations) {
|
||||
if (props.locationId !== undefined && location.id !== props.locationId)
|
||||
for (const locationId of slot.locationIds) {
|
||||
if (props.locationId !== undefined && locationId !== props.locationId)
|
||||
continue;
|
||||
data.push({
|
||||
type: "slot",
|
||||
|
@ -409,7 +389,7 @@ const eventSlots = computed(() => {
|
|||
event,
|
||||
slot,
|
||||
name: event.name,
|
||||
location,
|
||||
locationId,
|
||||
assigned: slot.assigned ?? [],
|
||||
start: slot.start,
|
||||
end: slot.end,
|
||||
|
|
|
@ -22,17 +22,15 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="event.name"
|
||||
:disabled="!canEdit(event)"
|
||||
@input="editEvent(event, { name: ($event as any).target.value })"
|
||||
v-model="event.name"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:disabled="!canEdit(event)"
|
||||
:value="event.description"
|
||||
@input="editEvent(event, { description: ($event as any).target.value })"
|
||||
v-model="event.description"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -41,7 +39,7 @@
|
|||
:disabled="!accountStore.canEditPublic"
|
||||
:value="!event.crew"
|
||||
:checked="!event.crew"
|
||||
@change="editEvent(event, { crew: !($event as any).target.value })"
|
||||
@change="event.crew = !($event.target as HTMLInputElement).value"
|
||||
>
|
||||
</td>
|
||||
<td>{{ event.slots.size ? event.slots.size : "" }}</td>
|
||||
|
@ -49,12 +47,12 @@
|
|||
<button
|
||||
type="button"
|
||||
:disabled="!canEdit(event) || event.deleted"
|
||||
@click="editEvent(event, { deleted: true })"
|
||||
@click="event.deleted = true"
|
||||
>Delete</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedEvent(event.id)"
|
||||
v-if="event.isModified()"
|
||||
type="button"
|
||||
@click="revertEvent(event.id)"
|
||||
@click="schedule.events.discardId(event.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -111,8 +109,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Id } from '~/shared/types/common';
|
||||
import { DateTime } from '~/shared/utils/luxon';
|
||||
import { Info } from '~/shared/utils/luxon';
|
||||
import { toId } from '~/shared/utils/functions';
|
||||
|
||||
defineProps<{
|
||||
|
@ -129,15 +126,6 @@ function canEdit(event: ClientScheduleEvent) {
|
|||
const newEventName = ref("");
|
||||
const newEventDescription = ref("");
|
||||
const newEventPublic = ref(false);
|
||||
function editEvent(
|
||||
event: ClientScheduleEvent,
|
||||
edits: Parameters<ClientSchedule["editEvent"]>[1],
|
||||
) {
|
||||
schedule.value.editEvent(event, edits);
|
||||
}
|
||||
function revertEvent(id: Id) {
|
||||
schedule.value.restoreEvent(id);
|
||||
}
|
||||
function eventExists(name: string) {
|
||||
name = toId(name);
|
||||
return (
|
||||
|
@ -149,19 +137,21 @@ function newEvent() {
|
|||
alert(`Event ${newEventName.value} already exists`);
|
||||
return;
|
||||
}
|
||||
const event = new ClientScheduleEvent(
|
||||
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
||||
const locale = accountStore.activeLocale;
|
||||
const event = ClientScheduleEvent.create(
|
||||
schedule.value,
|
||||
schedule.value.nextClientId--,
|
||||
DateTime.now(),
|
||||
false,
|
||||
newEventName.value,
|
||||
!newEventPublic.value,
|
||||
"",
|
||||
false,
|
||||
newEventDescription.value,
|
||||
0,
|
||||
new Map(),
|
||||
new Set(),
|
||||
{ zone, locale },
|
||||
);
|
||||
schedule.value.setEvent(event);
|
||||
schedule.value.events.add(event);
|
||||
newEventName.value = "";
|
||||
newEventDescription.value = "";
|
||||
newEventPublic.value = false;
|
||||
|
|
|
@ -20,27 +20,25 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="location.name"
|
||||
@input="editLocation(location, { name: ($event as any).target.value })"
|
||||
v-model="location.name"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="location.description"
|
||||
@input="editLocation(location, { description: ($event as any).target.value })"
|
||||
v-model="location.description"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
:disabled="location.deleted"
|
||||
type="button"
|
||||
@click="editLocation(location, { deleted: true })"
|
||||
@click="location.deleted = true"
|
||||
>Remove</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedLocation(location.id)"
|
||||
v-if="location.isModified()"
|
||||
type="button"
|
||||
@click="revertLocation(location.id)"
|
||||
@click="schedule.locations.discardId(location.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</template>
|
||||
|
@ -79,40 +77,28 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DateTime } from '~/shared/utils/luxon';
|
||||
import type { Id } from '~/shared/types/common';
|
||||
import { Info } from '~/shared/utils/luxon';
|
||||
|
||||
defineProps<{
|
||||
edit?: boolean
|
||||
}>();
|
||||
|
||||
const schedule = await useSchedule();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
const newLocationName = ref("");
|
||||
const newLocationDescription = ref("");
|
||||
|
||||
function editLocation(
|
||||
location: ClientScheduleLocation,
|
||||
edits: Parameters<ClientSchedule["editLocation"]>[1],
|
||||
) {
|
||||
try {
|
||||
schedule.value.editLocation(location, edits);
|
||||
} catch (err: any) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
function revertLocation(id: Id) {
|
||||
schedule.value.restoreLocation(id);
|
||||
}
|
||||
function newLocation() {
|
||||
const location = new ClientScheduleLocation(
|
||||
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
||||
const locale = accountStore.activeLocale;
|
||||
const location = ClientScheduleLocation.create(
|
||||
schedule.value.nextClientId--,
|
||||
DateTime.now(),
|
||||
false,
|
||||
newLocationName.value,
|
||||
newLocationDescription.value,
|
||||
{ zone, locale },
|
||||
);
|
||||
schedule.value.setLocation(location);
|
||||
schedule.value.locations.add(location);
|
||||
newLocationName.value = "";
|
||||
newLocationDescription.value = "";
|
||||
}
|
||||
|
|
|
@ -20,27 +20,25 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="role.name"
|
||||
@input="editRole(role, { name: ($event as any).target.value })"
|
||||
v-model="role.name"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="role.description"
|
||||
@input="editRole(role, { description: ($event as any).target.value })"
|
||||
v-model="role.description"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
:disabled="role.deleted"
|
||||
@click="editRole(role, { deleted: true })"
|
||||
@click="role.deleted = true"
|
||||
>Delete</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedRole(role.id)"
|
||||
v-if="role.isModified()"
|
||||
type="button"
|
||||
@click="revertRole(role.id)"
|
||||
@click="schedule.roles.discardId(role.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -87,36 +85,19 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DateTime } from '~/shared/utils/luxon';
|
||||
import { Info } from '~/shared/utils/luxon';
|
||||
import { toId } from '~/shared/utils/functions';
|
||||
import type { Id } from '~/shared/types/common';
|
||||
|
||||
defineProps<{
|
||||
edit?: boolean,
|
||||
}>();
|
||||
|
||||
const schedule = await useSchedule();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
const newRoleName = ref("");
|
||||
const newRoleDescription = ref("");
|
||||
|
||||
function editRole(
|
||||
role: ClientScheduleRole,
|
||||
edits: { deleted?: boolean, name?: string, description?: string }
|
||||
) {
|
||||
const copy = role.clone();
|
||||
if (edits.deleted !== undefined) copy.deleted = edits.deleted;
|
||||
if (edits.name !== undefined) copy.name = edits.name;
|
||||
if (edits.description !== undefined) copy.description = edits.description;
|
||||
try {
|
||||
schedule.value.setRole(copy);
|
||||
} catch (err: any) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
function revertRole(id: Id) {
|
||||
schedule.value.restoreRole(id);
|
||||
}
|
||||
function roleExists(name: string) {
|
||||
name = toId(name);
|
||||
return (
|
||||
|
@ -128,14 +109,15 @@ function newRole() {
|
|||
alert(`Role ${newRoleName.value} already exists`);
|
||||
return;
|
||||
}
|
||||
const role = new ClientScheduleRole(
|
||||
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
||||
const locale = accountStore.activeLocale;
|
||||
const role = ClientScheduleRole.create(
|
||||
schedule.value.nextClientId--,
|
||||
DateTime.now(),
|
||||
false,
|
||||
newRoleName.value,
|
||||
newRoleDescription.value,
|
||||
{ zone, locale },
|
||||
);
|
||||
schedule.value.setRole(role);
|
||||
schedule.value.roles.add(role);
|
||||
newRoleName.value = "";
|
||||
newRoleDescription.value = "";
|
||||
}
|
||||
|
|
|
@ -92,42 +92,39 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="ss.name"
|
||||
@input="editShift(ss, { name: ($event as any).target.value })"
|
||||
v-model="ss.name"
|
||||
>
|
||||
</td>
|
||||
<td>{{ status(ss) }}</td>
|
||||
<td>
|
||||
<select
|
||||
:value="ss.role.id"
|
||||
@change="editShift(ss, { role: schedule.roles.get(parseInt(($event as any).target.value)) })"
|
||||
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.role.id"
|
||||
:selected="role.id === ss.shift.roleId"
|
||||
>{{ role.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<AssignedCrew
|
||||
:edit="true"
|
||||
:modelValue="ss.assigned"
|
||||
@update:modelValue="editShiftSlot(ss, { assigned: $event })"
|
||||
v-model="ss.slot.assigned"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
:disabled="ss.deleted"
|
||||
type="button"
|
||||
@click="editShiftSlot(ss, { deleted: true })"
|
||||
@click="ss.slot.deleted = true"
|
||||
>Remove</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedShiftSlot(ss.slot.id)"
|
||||
v-if="ss.slot.isModified()"
|
||||
type="button"
|
||||
@click="revertShiftSlot(ss.id)"
|
||||
@click="ss.shift.discardSlot(ss.slot.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</template>
|
||||
|
@ -185,7 +182,7 @@
|
|||
<td>{{ ss.end.diff(ss.start).toFormat('hh:mm') }}</td>
|
||||
<td>{{ ss.name }}</td>
|
||||
<td>{{ status(ss) }}</td>
|
||||
<td>{{ ss.role.id }}</td>
|
||||
<td>{{ ss.roleId }}</td>
|
||||
<td><AssignedCrew :modelValue="ss.assigned" :edit="false" /></td>
|
||||
</template>
|
||||
</tr>
|
||||
|
@ -213,7 +210,7 @@ interface ShiftSlot {
|
|||
shift: ClientScheduleShift,
|
||||
slot: ClientScheduleShiftSlot,
|
||||
name: string,
|
||||
role: ClientScheduleRole,
|
||||
roleId: Id,
|
||||
assigned: Set<Id>,
|
||||
start: DateTime,
|
||||
end: DateTime,
|
||||
|
@ -290,42 +287,26 @@ function durationFromTime(time: string) {
|
|||
return duration;
|
||||
}
|
||||
const newShiftName = ref("");
|
||||
function editShift(
|
||||
shiftSlot: ShiftSlot,
|
||||
edits: Parameters<ClientSchedule["editShift"]>[1],
|
||||
) {
|
||||
schedule.value.editShift(shiftSlot.shift, edits);
|
||||
}
|
||||
|
||||
function editShiftSlot(
|
||||
shiftSlot: ShiftSlot,
|
||||
edits: {
|
||||
deleted?: boolean,
|
||||
start?: string,
|
||||
end?: string,
|
||||
duration?: string,
|
||||
assigned?: Set<Id>,
|
||||
}
|
||||
) {
|
||||
const computedEdits: Parameters<ClientSchedule["editShiftSlot"]>[1] = {
|
||||
deleted: edits.deleted,
|
||||
assigned: edits.assigned,
|
||||
};
|
||||
if (edits.start) {
|
||||
const start = DateTime.fromISO(edits.start, { zone: accountStore.activeTimezone, locale: accountStore.activeLocale });
|
||||
computedEdits.start = start;
|
||||
computedEdits.end = start.plus(shiftSlot.slot.end.diff(shiftSlot.slot.start));
|
||||
shiftSlot.start = start;
|
||||
shiftSlot.end = start.plus(shiftSlot.slot.end.diff(shiftSlot.slot.start));
|
||||
}
|
||||
if (edits.end !== undefined) {
|
||||
computedEdits.end = endFromTime(shiftSlot.start, edits.end);
|
||||
shiftSlot.end = endFromTime(shiftSlot.start, edits.end);
|
||||
}
|
||||
if (edits.duration !== undefined) {
|
||||
computedEdits.end = shiftSlot.start.plus(durationFromTime(edits.duration));
|
||||
shiftSlot.end = shiftSlot.start.plus(durationFromTime(edits.duration));
|
||||
}
|
||||
schedule.value.editShiftSlot(shiftSlot.slot, computedEdits);
|
||||
}
|
||||
function revertShiftSlot(id: Id) {
|
||||
schedule.value.restoreShiftSlot(id);
|
||||
}
|
||||
function newShiftSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
||||
const name = newShiftName.value;
|
||||
|
@ -361,15 +342,16 @@ function newShiftSlot(options: { start?: DateTime, end?: DateTime } = {}) {
|
|||
alert("Invalid start and/or end time");
|
||||
return;
|
||||
}
|
||||
const slot = new ClientScheduleShiftSlot(
|
||||
const slot = ClientScheduleShiftSlot.create(
|
||||
schedule.value,
|
||||
schedule.value.nextClientId--,
|
||||
false,
|
||||
shift.id,
|
||||
start,
|
||||
end,
|
||||
new Set(),
|
||||
);
|
||||
schedule.value.setShiftSlot(slot);
|
||||
schedule.value.shiftSlots.set(slot.id, slot);
|
||||
shift.slotIds.add(slot.id);
|
||||
newShiftName.value = "";
|
||||
}
|
||||
|
||||
|
@ -386,7 +368,7 @@ function gapFormat(gap: Gap) {
|
|||
const shiftSlots = computed(() => {
|
||||
const data: (ShiftSlot | Gap)[] = [];
|
||||
for (const shift of schedule.value.shifts.values()) {
|
||||
if (props.roleId !== undefined && shift.role.id !== props.roleId)
|
||||
if (props.roleId !== undefined && shift.roleId !== props.roleId)
|
||||
continue;
|
||||
for (const slot of shift.slots.values()) {
|
||||
if (props.shiftSlotFilter && !props.shiftSlotFilter(slot))
|
||||
|
@ -398,7 +380,7 @@ const shiftSlots = computed(() => {
|
|||
shift,
|
||||
slot,
|
||||
name: shift.name,
|
||||
role: shift.role,
|
||||
roleId: shift.roleId,
|
||||
assigned: slot.assigned,
|
||||
start: slot.start,
|
||||
end: slot.end,
|
||||
|
|
|
@ -22,21 +22,19 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="shift.name"
|
||||
@input="editShift(shift, { name: ($event as any).target.value })"
|
||||
v-model="shift.name"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
:value="shift.role.id"
|
||||
@change="editShift(shift, { role: schedule.roles.get(parseInt(($event as any).target.value, 10)) })"
|
||||
v-model="shift.roleId"
|
||||
>
|
||||
<option
|
||||
v-for="role in schedule.roles.values()"
|
||||
:key="role.id"
|
||||
:value="role.id"
|
||||
:disabled="shift.deleted"
|
||||
:selected="shift.role.id === role.id"
|
||||
:selected="shift.roleId === role.id"
|
||||
>{{ role.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
|
@ -44,20 +42,19 @@
|
|||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:value="shift.description"
|
||||
@input="editShift(shift, { description: ($event as any).target.value })"
|
||||
v-model="shift.description"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
:disabled="shift.deleted"
|
||||
@click="editShift(shift, { deleted: true })"
|
||||
@click="shift.deleted = true"
|
||||
>Delete</button>
|
||||
<button
|
||||
v-if="schedule.isModifiedShift(shift.id)"
|
||||
v-if="shift.isModified()"
|
||||
type="button"
|
||||
@click="revertShift(shift.id)"
|
||||
@click="schedule.shifts.discardId(shift.id)"
|
||||
>Revert</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -107,7 +104,7 @@
|
|||
>
|
||||
<td>{{ shift.id }}</td>
|
||||
<td>{{ shift.name }}</td>
|
||||
<td>{{ shift.role.id }}</td>
|
||||
<td>{{ shift.roleId }}</td>
|
||||
<td>{{ shift.slots.size ? shift.slots.size : "" }}</td>
|
||||
<td>{{ shift.description }}</td>
|
||||
</tr>
|
||||
|
@ -118,8 +115,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DateTime } from '~/shared/utils/luxon';
|
||||
import type { Id } from '~/shared/types/common';
|
||||
import { Info } from '~/shared/utils/luxon';
|
||||
import { toId } from '~/shared/utils/functions';
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -128,6 +124,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const schedule = await useSchedule();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
const newShiftName = ref("");
|
||||
const newShiftRoleId = ref(props.roleId);
|
||||
|
@ -135,15 +132,6 @@ watch(() => props.roleId, () => {
|
|||
newShiftRoleId.value = props.roleId;
|
||||
});
|
||||
const newShiftDescription = ref("");
|
||||
function editShift(
|
||||
shift: ClientScheduleShift,
|
||||
edits: Parameters<ClientSchedule["editShift"]>[1],
|
||||
) {
|
||||
schedule.value.editShift(shift, edits);
|
||||
}
|
||||
function revertShift(id: Id) {
|
||||
schedule.value.restoreShift(id);
|
||||
}
|
||||
function shiftExists(name: string) {
|
||||
name = toId(name);
|
||||
return (
|
||||
|
@ -155,21 +143,22 @@ function newShift() {
|
|||
alert(`Shift ${newShiftName.value} already exists`);
|
||||
return;
|
||||
}
|
||||
const role = schedule.value.roles.get(newShiftRoleId.value!);
|
||||
if (!role) {
|
||||
if (newShiftRoleId.value === undefined) {
|
||||
alert(`Invalid role`);
|
||||
return;
|
||||
}
|
||||
const shift = new ClientScheduleShift(
|
||||
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
||||
const locale = accountStore.activeLocale;
|
||||
const shift = ClientScheduleShift.create(
|
||||
schedule.value,
|
||||
schedule.value.nextClientId--,
|
||||
DateTime.now(),
|
||||
false,
|
||||
role,
|
||||
newShiftRoleId.value,
|
||||
newShiftName.value,
|
||||
newShiftDescription.value,
|
||||
new Map(),
|
||||
new Set(),
|
||||
{ zone, locale },
|
||||
);
|
||||
schedule.value.setShift(shift);
|
||||
schedule.value.shifts.add(shift);
|
||||
newShiftName.value = "";
|
||||
newShiftDescription.value = "";
|
||||
}
|
||||
|
|
|
@ -191,8 +191,8 @@ function* edgesFromShifts(
|
|||
if (slot.start > slot.end) {
|
||||
throw new Error(`Slot ${slot.id} ends before it starts.`);
|
||||
}
|
||||
yield { type: "start", source: "shift", roleId: shift.role.id, slot };
|
||||
yield { type: "end", source: "shift", roleId: shift.role.id, slot };
|
||||
yield { type: "start", source: "shift", roleId: shift.roleId, slot };
|
||||
yield { type: "end", source: "shift", roleId: shift.roleId, slot };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,8 +214,8 @@ function junctionsFromEdges(edges: Iterable<Edge>) {
|
|||
|
||||
function* spansFromJunctions(
|
||||
junctions: Iterable<Junction>,
|
||||
locations: Map<Id, ClientScheduleLocation>,
|
||||
roles: Map<Id, ClientScheduleRole>,
|
||||
locations: ClientMap<ClientScheduleLocation>,
|
||||
roles: ClientMap<ClientScheduleRole>,
|
||||
): Generator<Span> {
|
||||
const activeLocations = new Map(
|
||||
[...locations.keys()].map(id => [id, new Set<ClientScheduleEventSlot>()])
|
||||
|
@ -227,8 +227,8 @@ function* spansFromJunctions(
|
|||
for (const edge of start.edges) {
|
||||
if (edge.type === "start") {
|
||||
if (edge.source === "event") {
|
||||
for (const location of edge.slot.locations) {
|
||||
activeLocations.get(location.id)?.add(edge.slot)
|
||||
for (const locationId of edge.slot.locationIds) {
|
||||
activeLocations.get(locationId)?.add(edge.slot)
|
||||
}
|
||||
} else if (edge.source === "shift") {
|
||||
activeRoles.get(edge.roleId)?.add(edge.slot)
|
||||
|
@ -252,8 +252,8 @@ function* spansFromJunctions(
|
|||
for (const edge of end.edges) {
|
||||
if (edge.type === "end") {
|
||||
if (edge.source === "event") {
|
||||
for (const location of edge.slot.locations) {
|
||||
activeLocations.get(location.id)?.delete(edge.slot)
|
||||
for (const locationId of edge.slot.locationIds) {
|
||||
activeLocations.get(locationId)?.delete(edge.slot)
|
||||
}
|
||||
} else if (edge.source === "shift") {
|
||||
activeRoles.get(edge.roleId)?.delete(edge.slot);
|
||||
|
@ -367,10 +367,10 @@ function padStretch(stretch: Stretch, timezone: string): Stretch {
|
|||
|
||||
function tableElementsFromStretches(
|
||||
stretches: Iterable<Stretch>,
|
||||
events: Map<Id, ClientScheduleEvent>,
|
||||
locations: Map<Id, ClientScheduleLocation>,
|
||||
shifts: Map<Id, ClientScheduleShift>,
|
||||
roles: Map<Id, ClientScheduleRole>,
|
||||
events: ClientMap<ClientScheduleEvent>,
|
||||
locations: ClientMap<ClientScheduleLocation>,
|
||||
shifts: ClientMap<ClientScheduleShift>,
|
||||
roles: ClientMap<ClientScheduleRole>,
|
||||
timezone: string,
|
||||
) {
|
||||
type Col = { minutes?: number };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue