Add editable LocationsTable

This commit is contained in:
Hornwitser 2025-03-11 14:12:33 +01:00
parent 5255ed698e
commit c940f785c5
3 changed files with 188 additions and 3 deletions

View file

@ -53,7 +53,7 @@ a {
}
}
button {
button, input, textarea {
font-family: inherit;
font-size: inherit;
}

View file

@ -0,0 +1,181 @@
<template>
<figure>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
<th v-if="edit"></th>
</tr>
</thead>
<tbody>
<tr
v-for="location in locations"
:key="location.id"
:class="{ removed: removed.has(location.id) }"
>
<template v-if='edit'>
<td>{{ location.id }}</td>
<td>
<input
type="text"
:value="location.name"
@input="setLocation({ ...location, name: ($event as any).target.value })"
>
</td>
<td>
<input
type="text"
:value="location.description"
@input="setLocation({ ...location, description: ($event as any).target.value || undefined })"
>
</td>
<td>
<button
:disabled="removed.has(location.id)"
type="button"
@click="delLocation(location.id)"
>Remove</button>
<button
v-if="changes.some(c => c.data.id === location.id)"
type="button"
@click="revertLocation(location.id)"
>Revert</button>
</td>
</template>
<template v-else>
<td>{{ location.id }}</td>
<td>{{ location.name }}</td>
<td>{{ location.description }}</td>
</template>
</tr>
<tr v-if='edit'>
<td>
{{ toId(newLocationName) }}
</td>
<td>
<input
type="text"
v-model="newLocationName"
>
</td>
<td colspan="2">
<button
type="button"
@click="newLocation(newLocationName); newLocationName = ''"
>Add Location</button>
</td>
</tr>
</tbody>
</table>
<p v-if="changes.length">
Changes are not save yet.
<button
type="button"
@click="saveLocations"
>Save Changes</button>
</p>
<details>
<summary>Debug</summary>
<ol>
<li v-for="change in changes">
{{ JSON.stringify(change) }}
</li>
</ol>
</details>
</figure>
</template>
<script lang="ts" setup>
import type { ChangeRecord, ScheduleLocation } from '~/shared/types/schedule';
import { applyChangeArray } from '~/shared/utils/changes';
defineProps<{
edit?: boolean
}>();
function toId(name: string) {
return name.toLowerCase().replace(/ /g, "-");
}
const schedule = await useSchedule();
const changes = ref<ChangeRecord<ScheduleLocation>[]>([]);
const removed = computed(() => new Set(changes.value.filter(c => c.op === "del").map(c => c.data.id)));
function replaceChange(
change: ChangeRecord<ScheduleLocation>,
changes: ChangeRecord<ScheduleLocation>[],
) {
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<ScheduleLocation>[]) {
return changes.filter(change => change.data.id !== id);
}
const newLocationName = ref("");
function setLocation(location: ScheduleLocation) {
const change = { op: "set" as const, data: location };
changes.value = replaceChange(change, changes.value);
}
function delLocation(id: string) {
const change = { op: "del" as const, data: { id } };
changes.value = replaceChange(change, changes.value);
}
function revertLocation(id: string) {
changes.value = revertChange(id, changes.value);
}
function newLocation(name: string) {
const change = {
op: "set" as const,
data: {
id: toId(name),
name,
},
};
changes.value = replaceChange(change, changes.value);
}
async function saveLocations() {
try {
await $fetch("/api/schedule", {
method: "PATCH",
body: { locations: changes.value },
});
changes.value = [];
} catch (err: any) {
console.error(err);
alert(err?.data?.message ?? err.message);
}
}
const locations = computed(() => {
const data = [...schedule.value.locations];
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>

View file

@ -1,7 +1,8 @@
<template>
<main>
<h1>Edit</h1>
<p>Todo: implement editing</p>
<h2>Locations</h2>
<LocationsTable :edit="isAdmin" />
</main>
</template>
@ -9,5 +10,8 @@
definePageMeta({
middleware: ["authenticated"],
allowedAccountTypes: ["crew", "admin"],
})
});
const { data: session } = await useAccountSession();
const isAdmin = computed(() => session.value?.account.type === "admin")
</script>