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.
181 lines
3.9 KiB
Vue
181 lines
3.9 KiB
Vue
<template>
|
|
<div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>id</th>
|
|
<th>name</th>
|
|
<th>role</th>
|
|
<th>s</th>
|
|
<th>description</th>
|
|
<th v-if="edit"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template v-if="edit">
|
|
<tr
|
|
v-for="shift in schedule.shifts.values()"
|
|
:key="shift.id"
|
|
:class="{ removed: shift.deleted }"
|
|
>
|
|
<td>{{ shift.id }}</td>
|
|
<td>
|
|
<input
|
|
type="text"
|
|
v-model="shift.name"
|
|
>
|
|
</td>
|
|
<td>
|
|
<select
|
|
v-model="shift.roleId"
|
|
>
|
|
<option
|
|
v-for="role in schedule.roles.values()"
|
|
:key="role.id"
|
|
:value="role.id"
|
|
:disabled="shift.deleted"
|
|
:selected="shift.roleId === role.id"
|
|
>{{ role.name }}</option>
|
|
</select>
|
|
</td>
|
|
<td>{{ shift.slots.size ? shift.slots.size : "" }}</td>
|
|
<td>
|
|
<input
|
|
type="text"
|
|
v-model="shift.description"
|
|
>
|
|
</td>
|
|
<td>
|
|
<button
|
|
type="button"
|
|
:disabled="shift.deleted"
|
|
@click="shift.deleted = true"
|
|
>Delete</button>
|
|
<button
|
|
v-if="shift.isModified()"
|
|
type="button"
|
|
@click="schedule.shifts.discardId(shift.id)"
|
|
>Revert</button>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ schedule.nextClientId }}</td>
|
|
<td>
|
|
<input
|
|
type="text"
|
|
v-model="newShiftName"
|
|
>
|
|
</td>
|
|
<td>
|
|
<select 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>
|
|
<input
|
|
type="text"
|
|
v-model="newShiftDescription"
|
|
>
|
|
</td>
|
|
<td>
|
|
<button
|
|
v-if="shiftExists(newShiftName)"
|
|
disabled
|
|
>Shift already exists</button>
|
|
<button
|
|
v-else
|
|
type="button"
|
|
@click="newShift"
|
|
>Add Shift</button>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
<template v-else>
|
|
<tr
|
|
v-for="shift in schedule.shifts.values()"
|
|
:key="shift.id"
|
|
>
|
|
<td>{{ shift.id }}</td>
|
|
<td>{{ shift.name }}</td>
|
|
<td>{{ shift.roleId }}</td>
|
|
<td>{{ shift.slots.size ? shift.slots.size : "" }}</td>
|
|
<td>{{ shift.description }}</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { Info } from '~/shared/utils/luxon';
|
|
import { toId } from '~/shared/utils/functions';
|
|
|
|
const props = defineProps<{
|
|
edit?: boolean,
|
|
roleId?: number,
|
|
}>();
|
|
|
|
const schedule = await useSchedule();
|
|
const accountStore = useAccountStore();
|
|
|
|
const newShiftName = ref("");
|
|
const newShiftRoleId = ref(props.roleId);
|
|
watch(() => props.roleId, () => {
|
|
newShiftRoleId.value = props.roleId;
|
|
});
|
|
const newShiftDescription = ref("");
|
|
function shiftExists(name: string) {
|
|
name = toId(name);
|
|
return (
|
|
[...schedule.value.shifts.values()].some(s => !s.deleted && toId(s.name) === name)
|
|
);
|
|
}
|
|
function newShift() {
|
|
if (shiftExists(newShiftName.value)) {
|
|
alert(`Shift ${newShiftName.value} already exists`);
|
|
return;
|
|
}
|
|
if (newShiftRoleId.value === undefined) {
|
|
alert(`Invalid role`);
|
|
return;
|
|
}
|
|
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
|
const locale = accountStore.activeLocale;
|
|
const shift = ClientScheduleShift.create(
|
|
schedule.value,
|
|
schedule.value.nextClientId--,
|
|
newShiftRoleId.value,
|
|
newShiftName.value,
|
|
newShiftDescription.value,
|
|
new Set(),
|
|
{ zone, locale },
|
|
);
|
|
schedule.value.shifts.add(shift);
|
|
newShiftName.value = "";
|
|
newShiftDescription.value = "";
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
table {
|
|
border-spacing: 0;
|
|
}
|
|
table th {
|
|
text-align: left;
|
|
border-bottom: 1px solid var(--foreground);
|
|
}
|
|
table :is(th, td) + :is(th, td) {
|
|
padding-inline-start: 0.4em;
|
|
}
|
|
.removed :is(td, input) {
|
|
text-decoration: line-through;
|
|
}
|
|
</style>
|