mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 21:33:02 +01:00
* feat: implement example of data table * feat: load item data into table * chore: begin switching dialogs * feat: implement old dialog for controlling headers and page size * feat: get table into relatively usable state * feat: enhance dropdown actions for multi-selection and CSV download * feat: enhance table cell and dropdown button styles for better usability * feat: json download for table * feat: add expanded row component for item details in data table * chore: add translation support * feat: restore table on home page * fix: oops need ids * feat: move card view to use tanstack to allow for pagination * feat: switch the items search to use ItemViewSelectable * fix: update pagination handling and improve button click logic * feat: improve selectable table * feat: add indeterminate to checkbox * feat: overhaul maintenance dialog to use new system and add maintenance options to table * feat: add label ids and location id to item patch api * feat: change location and labels in table view * feat: add quick actions preference and enable toggle in table settings * fix: lint * fix: remove sized 1 pages * fix: attempt to fix type error * fix: various issues * fix: remove * fix: refactor item fetching logic to use useAsyncData for improved reactivity and improve use confirm * fix: sort backend issues * fix: enhance CSV export functionality by escaping fields to prevent formula injection * fix: put aria sort on th not button * chore: update api types
146 lines
4.6 KiB
Vue
146 lines
4.6 KiB
Vue
<template>
|
|
<Dialog :dialog-id="DialogID.EditMaintenance">
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{{ entry.id ? $t("maintenance.modal.edit_title") : $t("maintenance.modal.new_title") }}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<form class="flex flex-col gap-2" @submit.prevent="dispatchFormSubmit">
|
|
<FormTextField v-model="entry.name" autofocus :label="$t('maintenance.modal.entry_name')" />
|
|
<DatePicker v-model="entry.completedDate" :label="$t('maintenance.modal.completed_date')" />
|
|
<DatePicker v-model="entry.scheduledDate" :label="$t('maintenance.modal.scheduled_date')" />
|
|
<FormTextArea v-model="entry.description" :label="$t('maintenance.modal.notes')" />
|
|
<FormTextField v-model="entry.cost" autofocus :label="$t('maintenance.modal.cost')" />
|
|
|
|
<DialogFooter>
|
|
<Button type="submit">
|
|
<MdiPost />
|
|
{{ entry.id ? $t("maintenance.modal.edit_action") : $t("maintenance.modal.new_action") }}
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from "vue-i18n";
|
|
import { DialogID } from "@/components/ui/dialog-provider/utils";
|
|
import { toast } from "@/components/ui/sonner";
|
|
import MdiPost from "~icons/mdi/post";
|
|
import DatePicker from "~~/components/Form/DatePicker.vue";
|
|
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
import { useDialog } from "@/components/ui/dialog-provider";
|
|
import FormTextField from "~/components/Form/TextField.vue";
|
|
import FormTextArea from "~/components/Form/TextArea.vue";
|
|
import Button from "@/components/ui/button/Button.vue";
|
|
|
|
const { closeDialog, registerOpenDialogCallback } = useDialog();
|
|
|
|
const { t } = useI18n();
|
|
const api = useUserApi();
|
|
|
|
const entry = reactive({
|
|
id: null as string | null,
|
|
name: "",
|
|
completedDate: null as Date | null,
|
|
scheduledDate: null as Date | null,
|
|
description: "",
|
|
cost: "",
|
|
itemIds: null as string[] | null,
|
|
});
|
|
|
|
async function dispatchFormSubmit() {
|
|
if (entry.id) {
|
|
await editEntry();
|
|
return;
|
|
}
|
|
|
|
await createEntry();
|
|
}
|
|
|
|
async function createEntry() {
|
|
if (!entry.itemIds) {
|
|
return;
|
|
}
|
|
|
|
await Promise.allSettled(
|
|
entry.itemIds.map(async itemId => {
|
|
const { error } = await api.items.maintenance.create(itemId, {
|
|
name: entry.name,
|
|
completedDate: entry.completedDate ?? "",
|
|
scheduledDate: entry.scheduledDate ?? "",
|
|
description: entry.description,
|
|
cost: parseFloat(entry.cost) ? entry.cost : "0",
|
|
});
|
|
|
|
if (error) {
|
|
toast.error(t("maintenance.toast.failed_to_create"));
|
|
return;
|
|
}
|
|
})
|
|
);
|
|
|
|
closeDialog(DialogID.EditMaintenance, true);
|
|
}
|
|
|
|
async function editEntry() {
|
|
if (!entry.id) {
|
|
return;
|
|
}
|
|
|
|
const { error } = await api.maintenance.update(entry.id, {
|
|
name: entry.name,
|
|
completedDate: entry.completedDate ?? "null",
|
|
scheduledDate: entry.scheduledDate ?? "null",
|
|
description: entry.description,
|
|
cost: entry.cost,
|
|
});
|
|
|
|
if (error) {
|
|
toast.error(t("maintenance.toast.failed_to_update"));
|
|
return;
|
|
}
|
|
|
|
closeDialog(DialogID.EditMaintenance, true);
|
|
}
|
|
|
|
onMounted(() => {
|
|
const cleanup = registerOpenDialogCallback(DialogID.EditMaintenance, params => {
|
|
switch (params.type) {
|
|
case "create":
|
|
entry.id = null;
|
|
entry.name = "";
|
|
entry.completedDate = null;
|
|
entry.scheduledDate = null;
|
|
entry.description = "";
|
|
entry.cost = "";
|
|
entry.itemIds = typeof params.itemId === "string" ? [params.itemId] : params.itemId;
|
|
break;
|
|
case "update":
|
|
entry.id = params.maintenanceEntry.id;
|
|
entry.name = params.maintenanceEntry.name;
|
|
entry.completedDate = new Date(params.maintenanceEntry.completedDate);
|
|
entry.scheduledDate = new Date(params.maintenanceEntry.scheduledDate);
|
|
entry.description = params.maintenanceEntry.description;
|
|
entry.cost = params.maintenanceEntry.cost;
|
|
entry.itemIds = null;
|
|
break;
|
|
case "duplicate":
|
|
entry.id = null;
|
|
entry.name = params.maintenanceEntry.name;
|
|
entry.completedDate = null;
|
|
entry.scheduledDate = null;
|
|
entry.description = params.maintenanceEntry.description;
|
|
entry.cost = params.maintenanceEntry.cost;
|
|
entry.itemIds = [params.itemId];
|
|
break;
|
|
}
|
|
});
|
|
|
|
onUnmounted(cleanup);
|
|
});
|
|
</script>
|