Add editing of roles for shifts

This commit is contained in:
Hornwitser 2025-03-15 15:10:42 +01:00
parent 27d853d102
commit accc1690ff
2 changed files with 219 additions and 0 deletions

217
components/RolesTable.vue Normal file
View file

@ -0,0 +1,217 @@
<template>
<div>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
<th v-if="edit"></th>
</tr>
</thead>
<tbody>
<template v-if="edit">
<tr
v-for="role in roles"
:key="role.id"
:class="{ removed: removed.has(role.id) }"
>
<td>{{ role.id }}</td>
<td>
<input
type="text"
:value="role.name"
@input="editRole(role, { name: ($event as any).target.value })"
>
</td>
<td>
<input
type="text"
:value="role.description"
@input="editRole(role, { description: ($event as any).target.value })"
>
</td>
<td>
<button
type="button"
:disabled="removed.has(role.id)"
@click="delRole(role.id)"
>Delete</button>
<button
v-if="changes.some(c => c.data.id === role.id)"
type="button"
@click="revertRole(role.id)"
>Revert</button>
</td>
</tr>
<tr>
<td>{{ toId(newRoleName) }}</td>
<td>
<input
type="text"
v-model="newRoleName"
>
</td>
<td>
<input
type="text"
v-model="newRoleDescription"
>
</td>
<td>
<button
v-if="roleExists(newRoleName)"
disabled
>Role already exists</button>
<button
v-else
type="button"
@click="newRole"
>Add Role</button>
</td>
</tr>
</template>
<template v-else>
<tr
v-for="role in roles"
:key="role.id"
>
<td>{{ role.id }}</td>
<td>{{ role.name }}</td>
<td>{{ role.description }}</td>
</tr>
</template>
</tbody>
</table>
<p v-if="changes.length">
Changes are not saved yet.
<button
type="button"
@click="saveRoles"
>Save Changes</button>
</p>
<details>
<summary>Debug</summary>
<ol>
<li v-for="change in changes">
{{ JSON.stringify(change) }}
</li>
</ol>
</details>
</div>
</template>
<script lang="ts" setup>
import type { ChangeRecord, Role } from '~/shared/types/schedule';
import { applyChangeArray } from '~/shared/utils/changes';
import { toId } from '~/shared/utils/functions';
defineProps<{
edit?: boolean,
}>();
const schedule = await useSchedule();
const changes = ref<ChangeRecord<Role>[]>([]);
const removed = computed(() => new Set(changes.value.filter(c => c.op === "del").map(c => c.data.id)));
function replaceChange(
change: ChangeRecord<Role>,
changes: ChangeRecord<Role>[],
) {
const index = changes.findIndex(item => (
item.op === change.op && item.data.id === change.data.id
));
const copy = [...changes];
if (index !== -1)
copy.splice(index, 1, change);
else
copy.push(change);
return copy;
}
function revertChange(id: string, changes: ChangeRecord<Role>[]) {
return changes.filter(change => change.data.id !== id);
}
const newRoleName = ref("");
const newRoleDescription = ref("");
function editRole(
role: Role,
edits: { name?: string, description?: string }
) {
const copy = { ...role };
if (edits.name !== undefined) {
copy.name = edits.name;
}
if (edits.description !== undefined) {
copy.description = edits.description || undefined;
}
const change = { op: "set" as const, data: copy };
changes.value = replaceChange(change, changes.value);
}
function delRole(id: string) {
const change = { op: "del" as const, data: { id } };
changes.value = replaceChange(change, changes.value);
}
function revertRole(id: string) {
changes.value = revertChange(id, changes.value);
}
function roleExists(name: string) {
const id = toId(name);
return (
schedule.value.roles?.some(e => e.id === id)
|| changes.value.some(c => c.data.id === id)
);
}
function newRole() {
if (roleExists(newRoleName.value)) {
alert(`Role ${newRoleName.value} already exists`);
return;
}
const change = {
op: "set" as const,
data: {
id: toId(newRoleName.value),
name: newRoleName.value,
description: newRoleDescription.value || undefined,
slots: [],
},
};
changes.value = replaceChange(change, changes.value);
newRoleName.value = "";
newRoleDescription.value = "";
}
async function saveRoles() {
try {
await $fetch("/api/schedule", {
method: "PATCH",
body: { roles: changes.value },
});
changes.value = [];
} catch (err: any) {
console.error(err);
alert(err?.data?.message ?? err.message);
}
}
const roles = computed(() => {
const data = [...schedule.value.roles ?? []];
applyChangeArray(changes.value.filter(change => change.op === "set"), data);
return data;
});
</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>