forked from mirrors/homebox
feat: auth-roles, image-gallery, click-to-open (#166)
* schema changes * db generate * db migration * add role based middleware * implement attachment token access * generate docs * implement role based auth * replace attachment specific tokens with gen token * run linter * cleanup temporary token implementation
This commit is contained in:
parent
974d6914a2
commit
de419dc37d
48 changed files with 3127 additions and 244 deletions
|
@ -105,6 +105,7 @@
|
|||
authStore.$patch({
|
||||
token: data.token,
|
||||
expires: data.expiresAt,
|
||||
attachmentToken: data.attachmentToken,
|
||||
});
|
||||
|
||||
navigateTo("/home");
|
||||
|
|
|
@ -27,17 +27,32 @@
|
|||
});
|
||||
|
||||
type FilteredAttachments = {
|
||||
photos: ItemAttachment[];
|
||||
attachments: ItemAttachment[];
|
||||
warranty: ItemAttachment[];
|
||||
manuals: ItemAttachment[];
|
||||
receipts: ItemAttachment[];
|
||||
};
|
||||
|
||||
type Photo = {
|
||||
src: string;
|
||||
};
|
||||
|
||||
const photos = computed<Photo[]>(() => {
|
||||
return (
|
||||
item.value?.attachments.reduce((acc, cur) => {
|
||||
if (cur.type === "photo") {
|
||||
acc.push({
|
||||
src: api.authURL(`/items/${item.value.id}/attachments/${cur.id}`),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, [] as Photo[]) || []
|
||||
);
|
||||
});
|
||||
|
||||
const attachments = computed<FilteredAttachments>(() => {
|
||||
if (!item.value) {
|
||||
return {
|
||||
photos: [],
|
||||
attachments: [],
|
||||
manuals: [],
|
||||
warranty: [],
|
||||
|
@ -48,8 +63,9 @@
|
|||
return item.value.attachments.reduce(
|
||||
(acc, attachment) => {
|
||||
if (attachment.type === "photo") {
|
||||
acc.photos.push(attachment);
|
||||
} else if (attachment.type === "warranty") {
|
||||
return acc;
|
||||
}
|
||||
if (attachment.type === "warranty") {
|
||||
acc.warranty.push(attachment);
|
||||
} else if (attachment.type === "manual") {
|
||||
acc.manuals.push(attachment);
|
||||
|
@ -61,7 +77,6 @@
|
|||
return acc;
|
||||
},
|
||||
{
|
||||
photos: [] as ItemAttachment[],
|
||||
attachments: [] as ItemAttachment[],
|
||||
warranty: [] as ItemAttachment[],
|
||||
manuals: [] as ItemAttachment[],
|
||||
|
@ -144,7 +159,6 @@
|
|||
}
|
||||
|
||||
return (
|
||||
attachments.value.photos.length > 0 ||
|
||||
attachments.value.attachments.length > 0 ||
|
||||
attachments.value.warranty.length > 0 ||
|
||||
attachments.value.manuals.length > 0 ||
|
||||
|
@ -163,10 +177,6 @@
|
|||
});
|
||||
};
|
||||
|
||||
if (attachments.value.photos.length > 0) {
|
||||
push("Photos");
|
||||
}
|
||||
|
||||
if (attachments.value.attachments.length > 0) {
|
||||
push("Attachments");
|
||||
}
|
||||
|
@ -292,10 +302,43 @@
|
|||
toast.success("Item deleted");
|
||||
navigateTo("/home");
|
||||
}
|
||||
|
||||
const refDialog = ref<HTMLDialogElement>();
|
||||
const dialoged = reactive({
|
||||
src: "",
|
||||
});
|
||||
|
||||
function openDialog(img: Photo) {
|
||||
refDialog.value.showModal();
|
||||
dialoged.src = img.src;
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
refDialog.value.close();
|
||||
}
|
||||
|
||||
const refDialogBody = ref<HTMLDivElement>();
|
||||
onClickOutside(refDialogBody, () => {
|
||||
closeDialog();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContainer v-if="item" class="pb-8">
|
||||
<dialog ref="refDialog" class="z-[999] fixed bg-transparent">
|
||||
<div ref="refDialogBody" class="relative">
|
||||
<div class="absolute right-0 -mt-3 -mr-3 sm:-mt-4 sm:-mr-4 space-x-1">
|
||||
<a class="btn btn-sm sm:btn-md btn-primary btn-circle" :href="dialoged.src" download>
|
||||
<Icon class="h-5 w-5" name="mdi-download" />
|
||||
</a>
|
||||
<button class="btn btn-sm sm:btn-md btn-primary btn-circle" @click="closeDialog()">
|
||||
<Icon class="h-5 w-5" name="mdi-close" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<img class="max-w-[80vw] max-h-[80vh]" :src="dialoged.src" />
|
||||
</div>
|
||||
</dialog>
|
||||
<section class="px-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="form-control"></div>
|
||||
|
@ -353,6 +396,15 @@
|
|||
<DetailsSection :details="itemDetails" />
|
||||
</BaseCard>
|
||||
|
||||
<BaseCard>
|
||||
<template #title> Photos </template>
|
||||
<div class="container p-4 flex flex-wrap gap-2 mx-auto max-h-[500px] overflow-scroll">
|
||||
<button v-for="(img, i) in photos" :key="i" @click="openDialog(img)">
|
||||
<img class="rounded max-h-[200px]" :src="img.src" />
|
||||
</button>
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
<BaseCard v-if="showAttachments">
|
||||
<template #title> Attachments </template>
|
||||
<DetailsSection :details="attachmentDetails">
|
||||
|
@ -377,13 +429,6 @@
|
|||
:item-id="item.id"
|
||||
/>
|
||||
</template>
|
||||
<template #photos>
|
||||
<ItemAttachmentsList
|
||||
v-if="attachments.photos.length > 0"
|
||||
:attachments="attachments.photos"
|
||||
:item-id="item.id"
|
||||
/>
|
||||
</template>
|
||||
<template #receipts>
|
||||
<ItemAttachmentsList
|
||||
v-if="attachments.receipts.length > 0"
|
||||
|
@ -419,3 +464,10 @@
|
|||
</section>
|
||||
</BaseContainer>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Style dialog background */
|
||||
dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue