mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-05 09:10:26 +00:00
wip: more UI fixes for consistency across themes
This commit is contained in:
parent
b66bc407fc
commit
b6c73733dc
12 changed files with 278 additions and 129 deletions
|
@ -29,7 +29,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc {
|
|||
|
||||
return repo.ItemQuery{
|
||||
Page: queryIntOrNegativeOne(params.Get("page")),
|
||||
PageSize: queryIntOrNegativeOne(params.Get("perPage")),
|
||||
PageSize: queryIntOrNegativeOne(params.Get("pageSize")),
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: queryUUIDList(params, "locations"),
|
||||
LabelIDs: queryUUIDList(params, "labels"),
|
||||
|
|
|
@ -1,26 +1,64 @@
|
|||
<script lang="ts" setup>
|
||||
// https://stackoverflow.com/questions/36721830/convert-hsl-to-rgb-and-hex
|
||||
function hslToHex(h: number, s: number, l: number) {
|
||||
l /= 100;
|
||||
const a = (s * Math.min(l, 1 - l)) / 100;
|
||||
const f = (n: number) => {
|
||||
const k = (n + h / 30) % 12;
|
||||
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
return Math.round(255 * color)
|
||||
.toString(16)
|
||||
.padStart(2, "0"); // convert to Hex and prefix "0" if needed
|
||||
};
|
||||
return `#${f(0)}${f(8)}${f(4)}`;
|
||||
}
|
||||
|
||||
function unstring(value: string): [number, number, number] {
|
||||
const [h, s, l] = value
|
||||
.replace("hsla(", "")
|
||||
.replace(")", "")
|
||||
.replace(/%/g, "")
|
||||
.split(",")
|
||||
.map(v => v.trim());
|
||||
|
||||
return [Number(h), Number(s), Number(l)];
|
||||
}
|
||||
|
||||
const primary = useCssVar("--p");
|
||||
const primaryHex = computed(() => hslToHex(...unstring(primary.value)));
|
||||
|
||||
const accent = useCssVar("--a");
|
||||
const accentHex = computed(() => hslToHex(...unstring(accent.value)));
|
||||
|
||||
const neutral = useCssVar("--n");
|
||||
const neutralHex = computed(() => hslToHex(...unstring(neutral.value)));
|
||||
|
||||
const base100 = useCssVar("--b1");
|
||||
const base100Hex = computed(() => hslToHex(...unstring(base100.value)));
|
||||
</script>
|
||||
<template>
|
||||
<svg viewBox="0 0 1440 237" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)" filter="url(#b)">
|
||||
<path fill="#fff" d="M0-103h1440v310H0z" />
|
||||
<path fill="#2C2E27" d="M0-103h1440v310H0z" />
|
||||
<path :fill="neutralHex" d="M0-103h1440v310H0z" />
|
||||
<g clip-path="url(#c)">
|
||||
<path
|
||||
d="M1344.93 230.569h-75.81v-45.486h30.32c5.98 0 11.89 1.177 17.41 3.463s10.53 5.636 14.76 9.86c8.53 8.53 13.32 20.099 13.32 32.163Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M1297.89 170.07c0-3.395-2.75-6.147-6.14-6.147-3.4 0-6.15 2.752-6.15 6.147 0 3.395 2.75 6.148 6.15 6.148 3.39 0 6.14-2.753 6.14-6.148ZM1328.45 170.07c0-3.395-2.75-6.147-6.15-6.147-3.39 0-6.15 2.752-6.15 6.147 0 3.395 2.76 6.148 6.15 6.148 3.4 0 6.15-2.753 6.15-6.148Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#d)">
|
||||
<path
|
||||
d="M1401.78 60c7.5 0 14.83 2.2231 21.06 6.388 6.24 4.165 11.09 10.0848 13.96 17.0109 2.87 6.9261 3.62 14.5474 2.16 21.9001-1.46 7.353-5.07 14.107-10.37 19.408-5.3 5.301-12.06 8.911-19.41 10.373-7.35 1.463-14.98.712-21.9-2.157-6.93-2.869-12.85-7.727-17.01-13.96-4.17-6.234-6.39-13.562-6.39-21.0587V60h37.9Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#e)">
|
||||
<path d="M1269.12 135.809h75.81V60h-75.81v75.809Z" fill="#FFDA56" />
|
||||
<path d="M1269.12 135.809h75.81V60h-75.81v75.809Z" :fill="accentHex" />
|
||||
</g>
|
||||
<g clip-path="url(#f)">
|
||||
<path
|
||||
|
@ -29,35 +67,35 @@
|
|||
/>
|
||||
</g>
|
||||
<g clip-path="url(#g)">
|
||||
<path d="M1079.6 135.809h75.81V60h-75.81v75.809Z" fill="#FFDA56" />
|
||||
<path d="M1079.6 135.809h75.81V60h-75.81v75.809Z" :fill="accentHex" />
|
||||
</g>
|
||||
<g clip-path="url(#h)">
|
||||
<path
|
||||
d="M890.08 60h75.808v45.485h-30.323c-5.973 0-11.888-1.176-17.406-3.462-5.519-2.2861-10.533-5.6365-14.757-9.8602C894.872 83.6327 890.08 72.0634 890.08 60Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M937.114 120.498c0 3.395 2.752 6.148 6.147 6.148 3.395 0 6.147-2.753 6.147-6.148 0-3.395-2.752-6.147-6.147-6.147-3.395 0-6.147 2.752-6.147 6.147ZM906.56 120.498c0 3.395 2.752 6.148 6.147 6.148 3.395 0 6.148-2.753 6.148-6.148 0-3.395-2.753-6.147-6.148-6.147-3.395 0-6.147 2.752-6.147 6.147Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#i)">
|
||||
<path d="M795.32 154.76v75.809h75.808V154.76H795.32Z" fill="#FFDA56" />
|
||||
<path d="M795.32 154.76v75.809h75.808V154.76H795.32Z" :fill="accentHex" />
|
||||
</g>
|
||||
<g clip-path="url(#j)">
|
||||
<path
|
||||
d="M776.368 154.76v75.809h-45.485v-30.324c0-5.973 1.177-11.888 3.463-17.406 2.286-5.519 5.636-10.533 9.86-14.757 8.53-8.53 20.099-13.322 32.162-13.322Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M715.87 201.794c-3.395 0-6.147 2.752-6.147 6.147 0 3.395 2.752 6.148 6.147 6.148 3.395 0 6.148-2.753 6.148-6.148 0-3.395-2.753-6.147-6.148-6.147ZM715.87 171.24c-3.395 0-6.147 2.752-6.147 6.147 0 3.395 2.752 6.148 6.147 6.148 3.395 0 6.148-2.753 6.148-6.148 0-3.395-2.753-6.147-6.148-6.147Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#k)">
|
||||
<path
|
||||
d="M871.128 97.9043c0 7.4967-2.223 14.8247-6.388 21.0587-4.165 6.233-10.084 11.091-17.01 13.96-6.927 2.869-14.548 3.62-21.901 2.157-7.352-1.462-14.106-5.072-19.407-10.373-5.301-5.301-8.911-12.055-10.374-19.408-1.462-7.3527-.712-14.974 2.157-21.9001 2.869-6.9261 7.727-12.8459 13.961-17.0109C818.399 62.2231 825.727 60 833.224 60h37.904v37.9043Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#l)">
|
||||
|
@ -72,17 +110,17 @@
|
|||
<g clip-path="url(#m)">
|
||||
<path
|
||||
d="M605.8 97.9043c0-7.4968 2.223-14.8252 6.388-21.0585 4.165-6.2333 10.085-11.0916 17.011-13.9605 6.926-2.8689 14.547-3.6195 21.9-2.157 7.353 1.4626 14.107 5.0726 19.408 10.3736 5.301 5.301 8.911 12.0549 10.373 19.4076 1.463 7.3527.712 14.9735-2.157 21.9005-2.869 6.926-7.727 12.846-13.96 17.01-6.234 4.165-13.562 6.389-21.059 6.389H605.8V97.9043Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#n)">
|
||||
<path
|
||||
d="M511.04 135.809V60h45.485v30.3234c0 5.9732-1.176 11.8876-3.462 17.4066-2.286 5.518-5.637 10.533-9.86 14.756-8.53 8.53-20.1 13.323-32.163 13.323Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M571.538 88.7746c3.395 0 6.147-2.7522 6.147-6.1473 0-3.395-2.752-6.1473-6.147-6.1473-3.395 0-6.147 2.7523-6.147 6.1473 0 3.3951 2.752 6.1473 6.147 6.1473ZM571.538 119.328c3.395 0 6.147-2.752 6.147-6.147 0-3.395-2.752-6.147-6.147-6.147-3.395 0-6.147 2.752-6.147 6.147 0 3.395 2.752 6.147 6.147 6.147Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#o)">
|
||||
|
@ -103,28 +141,28 @@
|
|||
<g clip-path="url(#q)">
|
||||
<path
|
||||
d="M132 230.569V154.76h45.485v30.323c0 5.974-1.176 11.888-3.462 17.407-2.286 5.518-5.636 10.533-9.86 14.756-8.53 8.53-20.1 13.323-32.163 13.323Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M192.498 183.535c3.395 0 6.148-2.753 6.148-6.148 0-3.395-2.753-6.147-6.148-6.147-3.395 0-6.147 2.752-6.147 6.147 0 3.395 2.752 6.148 6.147 6.148ZM192.498 214.089c3.395 0 6.148-2.753 6.148-6.148 0-3.395-2.753-6.147-6.148-6.147-3.395 0-6.147 2.752-6.147 6.147 0 3.395 2.752 6.148 6.147 6.148Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#r)">
|
||||
<path
|
||||
d="M302.569 135.809H226.76V90.3234h30.323c5.974 0 11.888 1.1765 17.407 3.4624 5.518 2.2858 10.533 5.6362 14.756 9.8602 8.53 8.53 13.323 20.099 13.323 32.163Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M255.535 75.3103c0-3.3951-2.753-6.1473-6.148-6.1473-3.395 0-6.147 2.7522-6.147 6.1473 0 3.3951 2.752 6.1473 6.147 6.1473 3.395 0 6.148-2.7522 6.148-6.1473ZM286.089 75.3103c0-3.3951-2.753-6.1473-6.148-6.1473-3.395 0-6.147 2.7522-6.147 6.1473 0 3.3951 2.752 6.1473 6.147 6.1473 3.395 0 6.148-2.7522 6.148-6.1473Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#s)">
|
||||
<path d="M207.809 60H132v75.809h75.809V60Z" fill="#FFDA56" />
|
||||
<path d="M207.809 60H132v75.809h75.809V60Z" :fill="accentHex" />
|
||||
</g>
|
||||
<g clip-path="url(#t)">
|
||||
<path d="M502-31v75.8085h75.809V-31H502Z" fill="#FFDA56" />
|
||||
<path d="M502-31v75.8085h75.809V-31H502Z" :fill="accentHex" />
|
||||
</g>
|
||||
<g clip-path="url(#u)">
|
||||
<path
|
||||
|
@ -141,7 +179,7 @@
|
|||
<g clip-path="url(#w)">
|
||||
<path
|
||||
d="M388.089 6.66425c0 7.49675-2.224 14.82515-6.388 21.05845-4.165 6.2333-10.085 11.0916-17.011 13.9605-6.926 2.8689-14.548 3.6195-21.9 2.157-7.353-1.4626-14.107-5.0726-19.408-10.3736-5.301-5.301-8.911-12.0549-10.374-19.4076-1.462-7.3527-.712-14.973986 2.157-21.90008 2.869-6.92612 7.728-12.84592 13.961-17.01092 6.233-4.165 13.562-6.388 21.058-6.388h37.905V6.66425Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#x)">
|
||||
|
@ -165,7 +203,7 @@
|
|||
<g clip-path="url(#A)">
|
||||
<path
|
||||
d="M956.648-31.24v75.8085h-45.485V14.2451c0-5.97318 1.177-11.88788 3.463-17.40639 2.286-5.5185 5.636-10.53271 9.86-14.75641 8.53-8.5301 20.099-13.3223 32.162-13.3223Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M896.15 15.7939c-3.395 0-6.147 2.7522-6.147 6.1473 0 3.3951 2.752 6.1473 6.147 6.1473 3.395 0 6.148-2.7522 6.148-6.1473 0-3.3951-2.753-6.1473-6.148-6.1473Z"
|
||||
|
@ -181,7 +219,7 @@
|
|||
<g clip-path="url(#C)">
|
||||
<path
|
||||
d="M1070.36 44.5685V-31.24h45.49V-.916588c0 5.973188-1.18 11.887888-3.47 17.406388-2.28 5.5185-5.63 10.5328-9.86 14.7564-8.53 8.5302-20.1 13.3223-32.16 13.3223Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M1130.86 28.0885c3.39 0 6.15-2.7522 6.15-6.1473 0-3.3951-2.76-6.1473-6.15-6.1473-3.4 0-6.15 2.7522-6.15 6.1473 0 3.3951 2.75 6.1473 6.15 6.1473Z"
|
||||
|
@ -191,13 +229,13 @@
|
|||
<g clip-path="url(#D)">
|
||||
<path
|
||||
d="M1203.02 44.5685c-7.49 0-14.82-2.223-21.05-6.388-6.24-4.165-11.1-10.0848-13.96-17.0109-2.87-6.9261-3.62-14.54737-2.16-21.900078 1.46-7.352702 5.07-14.106622 10.37-19.407622 5.3-5.301 12.06-8.911 19.41-10.3736 7.35-1.4625 14.97-.7119 21.9 2.157 6.93 2.8689 12.85 7.7272 17.01 13.9605 4.17 6.23332 6.39 13.561722 6.39 21.05848V44.5685h-37.91Z"
|
||||
fill="#F6FAFB"
|
||||
:fill="base100Hex"
|
||||
/>
|
||||
</g>
|
||||
<g clip-path="url(#E)">
|
||||
<path
|
||||
d="M1335.69-31.24v75.8085h-45.49V14.2451c0-5.97318 1.18-11.88788 3.47-17.40639 2.28-5.5185 5.63-10.53271 9.86-14.75641 8.53-8.5301 20.1-13.3223 32.16-13.3223Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M1275.19 15.7939c-3.39 0-6.15 2.7522-6.15 6.1473 0 3.3951 2.76 6.1473 6.15 6.1473 3.4 0 6.15-2.7522 6.15-6.1473 0-3.3951-2.75-6.1473-6.15-6.1473Z"
|
||||
|
@ -207,7 +245,7 @@
|
|||
<g clip-path="url(#F)">
|
||||
<path
|
||||
d="M292.808-31v75.8085h-45.485V14.4851c0-5.97316 1.177-11.88786 3.463-17.40636 2.286-5.51851 5.636-10.53274 9.86-14.75644C269.176-26.2078 280.745-31 292.808-31Z"
|
||||
fill="#507B5D"
|
||||
:fill="primaryHex"
|
||||
/>
|
||||
<path
|
||||
d="M232.31 16.0339c-3.395 0-6.147 2.7522-6.147 6.1473 0 3.3951 2.752 6.1473 6.147 6.1473 3.395 0 6.148-2.7522 6.148-6.1473 0-3.3951-2.753-6.1473-6.148-6.1473Z"
|
||||
|
|
90
frontend/components/App/ImportDialog.vue
Normal file
90
frontend/components/App/ImportDialog.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<BaseModal v-model="dialog">
|
||||
<template #title> Import CSV File </template>
|
||||
<p>
|
||||
Import a CSV file containing your items, labels, and locations. See documentation for more information on the
|
||||
required format.
|
||||
</p>
|
||||
|
||||
<form @submit.prevent="submitCsvFile">
|
||||
<div class="flex flex-col gap-2 py-6">
|
||||
<input ref="importRef" type="file" class="hidden" accept=".csv,.tsv" @change="setFile" />
|
||||
|
||||
<BaseButton type="button" @click="uploadCsv">
|
||||
<Icon class="h-5 w-5 mr-2" name="mdi-upload" />
|
||||
Upload
|
||||
</BaseButton>
|
||||
<p class="text-center pt-4 -mb-5">
|
||||
{{ importCsv?.name }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<BaseButton type="submit" :disabled="!importCsv"> Submit </BaseButton>
|
||||
</div>
|
||||
</form>
|
||||
</BaseModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Props = {
|
||||
modelValue: boolean;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const dialog = useVModel(props, "modelValue", emit);
|
||||
|
||||
const api = useUserApi();
|
||||
const toast = useNotifier();
|
||||
|
||||
const importCsv = ref<File | null>(null);
|
||||
const importLoading = ref(false);
|
||||
const importRef = ref<HTMLInputElement>();
|
||||
whenever(
|
||||
() => !dialog.value,
|
||||
() => {
|
||||
importCsv.value = null;
|
||||
}
|
||||
);
|
||||
|
||||
function setFile(e: Event) {
|
||||
importCsv.value = e.target.files[0];
|
||||
}
|
||||
|
||||
function uploadCsv() {
|
||||
importRef.value.click();
|
||||
}
|
||||
|
||||
const eventBus = useEventBus();
|
||||
|
||||
async function submitCsvFile() {
|
||||
if (!importCsv.value) {
|
||||
toast.error("Please select a file to import.");
|
||||
return;
|
||||
}
|
||||
|
||||
importLoading.value = true;
|
||||
|
||||
const { error } = await api.items.import(importCsv.value);
|
||||
|
||||
if (error) {
|
||||
toast.error("Import failed. Please try again later.");
|
||||
}
|
||||
|
||||
// Reset
|
||||
dialog.value = false;
|
||||
importLoading.value = false;
|
||||
importCsv.value = null;
|
||||
|
||||
if (importRef.value) {
|
||||
importRef.value.value = "";
|
||||
}
|
||||
|
||||
eventBus.emit(EventTypes.ClearStores);
|
||||
}
|
||||
</script>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card bg-white shadow-xl sm:rounded-lg">
|
||||
<div class="card bg-base-100 shadow-xl sm:rounded-lg">
|
||||
<div v-if="$slots.title" class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg font-medium leading-6">
|
||||
<slot name="title"></slot>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<template>
|
||||
<NuxtLink
|
||||
ref="badge"
|
||||
class="badge badge-secondary"
|
||||
class="badge badge-secondary text-secondary-content"
|
||||
:class="{
|
||||
'badge-lg p-4': size === 'lg',
|
||||
'p-3': size !== 'sm' && size !== 'lg',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<NuxtLink
|
||||
ref="card"
|
||||
:to="`/location/${location.id}`"
|
||||
class="card bg-white text-neutral rounded-md transition duration-300 shadow-md"
|
||||
class="card bg-base-100 text-base-content rounded-md transition duration-300 shadow-md"
|
||||
>
|
||||
<div
|
||||
class="card-body"
|
||||
|
@ -19,7 +19,7 @@
|
|||
<span class="mx-auto">
|
||||
{{ location.name }}
|
||||
</span>
|
||||
<span v-if="hasCount" class="badge badge-primary h-6 w-6 badge-lg"> {{ count }}</span>
|
||||
<span v-if="hasCount" class="badge badge-primary h-6 badge-lg"> {{ count }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<td
|
||||
v-for="h in headers"
|
||||
:key="`${h.value}-${i}`"
|
||||
class="bg-white"
|
||||
class="bg-base-100"
|
||||
:class="{
|
||||
'text-center': h.align === 'center',
|
||||
'text-right': h.align === 'right',
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
<template>
|
||||
<div>
|
||||
<!--
|
||||
Confirmation Modal is a singleton used by all components so we render
|
||||
it here to ensure it's always available. Possibly could move this further
|
||||
up the tree
|
||||
-->
|
||||
<ModalConfirm />
|
||||
<AppImportDialog v-model="modals.import" />
|
||||
<ItemCreateModal v-model="modals.item" />
|
||||
<LabelCreateModal v-model="modals.label" />
|
||||
<LocationCreateModal v-model="modals.location" />
|
||||
<AppToast />
|
||||
<div class="drawer drawer-mobile">
|
||||
<input id="my-drawer-2" type="checkbox" class="drawer-toggle" />
|
||||
<div class="drawer-content justify-center">
|
||||
<div class="drawer-content justify-center bg-base-300">
|
||||
<AppHeaderDecor class="-mt-10" />
|
||||
<slot></slot>
|
||||
|
||||
|
@ -12,7 +22,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="drawer-side shadow-lg w-60 flex flex-col justify-center py-10" style="background: white">
|
||||
<div class="drawer-side overflow-visible shadow-lg w-60 flex flex-col justify-center bg-base-200 py-10">
|
||||
<label for="my-drawer-2" class="drawer-overlay"></label>
|
||||
<!-- Top Section -->
|
||||
<div class="space-y-8">
|
||||
|
@ -26,25 +36,37 @@
|
|||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="mx-auto w-40 mb-6">
|
||||
<BaseButton class="btn-block btn-primary text-xl">
|
||||
<template #icon>
|
||||
<Icon name="mdi-plus-circle" class="h-6 w-6" />
|
||||
</template>
|
||||
Create
|
||||
</BaseButton>
|
||||
</div>
|
||||
<ul class="flex flex-col mx-auto gap-y-8">
|
||||
<li v-for="n in nav" :key="n.id" class="text-xl">
|
||||
<NuxtLink v-if="n.to" :to="n.to">
|
||||
<span class="mr-4">
|
||||
<Icon :name="n.icon" class="h-5 w-5" />
|
||||
<div class="dropdown overflow visible w-40">
|
||||
<label tabindex="0" class="btn btn-primary btn-block text-lg text-no-transform">
|
||||
<span>
|
||||
<Icon name="mdi-plus" class="mr-1 -ml-1" />
|
||||
</span>
|
||||
Create
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40">
|
||||
<li v-for="btn in dropdown" :key="btn.name">
|
||||
<button @click="btn.action">
|
||||
{{ btn.name }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="flex flex-col mx-auto gap-2 w-40 menu">
|
||||
<li v-for="n in nav" :key="n.id" class="text-xl">
|
||||
<NuxtLink
|
||||
v-if="n.to"
|
||||
class="rounded-btn"
|
||||
:to="n.to"
|
||||
:class="{
|
||||
'bg-secondary text-secondary-content': n.active?.value,
|
||||
}"
|
||||
>
|
||||
<Icon :name="n.icon" class="h-6 w-6 mr-4" />
|
||||
{{ n.name }}
|
||||
</NuxtLink>
|
||||
<button v-else @click="n.action">
|
||||
<span class="mr-4">
|
||||
<Icon :name="n.icon" class="h-5 w-5" />
|
||||
</span>
|
||||
<button v-else class="rounded-btn" @click="n.action">
|
||||
<Icon :name="n.icon" class="h-6 w-6 mr-4" />
|
||||
{{ n.name }}
|
||||
</button>
|
||||
</li>
|
||||
|
@ -52,32 +74,67 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Bottom -->
|
||||
<button class="mt-auto mb-6">Sign Out</button>
|
||||
<button class="mt-auto mb-6" @click="logout">Sign Out</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAuthStore } from "~~/stores/auth";
|
||||
import { useLabelStore } from "~~/stores/labels";
|
||||
import { useLocationStore } from "~~/stores/locations";
|
||||
|
||||
/**
|
||||
* Store Provider Initialization
|
||||
*/
|
||||
const modals = reactive({
|
||||
item: false,
|
||||
location: false,
|
||||
label: false,
|
||||
import: false,
|
||||
});
|
||||
|
||||
const dropdown = [
|
||||
{
|
||||
name: "Item / Asset",
|
||||
action: () => {
|
||||
modals.item = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Location",
|
||||
action: () => {
|
||||
modals.location = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Label",
|
||||
action: () => {
|
||||
modals.label = true;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const nav = [
|
||||
{
|
||||
icon: "mdi-home",
|
||||
active: computed(() => route.path === "/home"),
|
||||
id: 0,
|
||||
name: "Home",
|
||||
to: "/home",
|
||||
},
|
||||
{
|
||||
icon: "mdi-account",
|
||||
id: 1,
|
||||
active: computed(() => route.path === "/profile"),
|
||||
name: "Profile",
|
||||
to: "/profile",
|
||||
},
|
||||
{
|
||||
icon: "mdi-document",
|
||||
id: 3,
|
||||
active: computed(() => route.path === "/items"),
|
||||
name: "Items",
|
||||
to: "/items",
|
||||
},
|
||||
|
@ -85,14 +142,18 @@
|
|||
icon: "mdi-database",
|
||||
id: 2,
|
||||
name: "Import",
|
||||
action: () => {},
|
||||
},
|
||||
{
|
||||
icon: "mdi-database-export",
|
||||
id: 5,
|
||||
name: "Export",
|
||||
action: () => {},
|
||||
action: () => {
|
||||
modals.import = true;
|
||||
},
|
||||
},
|
||||
// {
|
||||
// icon: "mdi-database-export",
|
||||
// id: 5,
|
||||
// name: "Export",
|
||||
// action: () => {
|
||||
// console.log("Export");
|
||||
// },
|
||||
// },
|
||||
];
|
||||
|
||||
const labelStore = useLabelStore();
|
||||
|
@ -134,4 +195,16 @@
|
|||
rmLocationStoreObserver();
|
||||
eventBus.off(EventTypes.ClearStores, "stores");
|
||||
});
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const api = useUserApi();
|
||||
|
||||
async function logout() {
|
||||
const { error } = await authStore.logout(api);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigateTo("/");
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -24,57 +24,6 @@
|
|||
const itemTable = itemsTable(api);
|
||||
const stats = statCardData(api);
|
||||
|
||||
// const importDialog = ref(false);
|
||||
// const importCsv = ref(null);
|
||||
// const importLoading = ref(false);
|
||||
// const importRef = ref<HTMLInputElement>();
|
||||
// whenever(
|
||||
// () => !importDialog.value,
|
||||
// () => {
|
||||
// importCsv.value = null;
|
||||
// }
|
||||
// );
|
||||
|
||||
// function setFile(e: Event & { target: HTMLInputElement }) {
|
||||
// importCsv.value = e.target.files[0];
|
||||
// }
|
||||
|
||||
// function openDialog() {
|
||||
// importDialog.value = true;
|
||||
// }
|
||||
|
||||
// function uploadCsv() {
|
||||
// importRef.value.click();
|
||||
// }
|
||||
|
||||
// const eventBus = useEventBus();
|
||||
|
||||
// async function submitCsvFile() {
|
||||
// if (!importCsv.value) {
|
||||
// toast.error("Please select a file to import.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// importLoading.value = true;
|
||||
|
||||
// const { error } = await api.items.import(importCsv.value);
|
||||
|
||||
// if (error) {
|
||||
// toast.error("Import failed. Please try again later.");
|
||||
// }
|
||||
|
||||
// // Reset
|
||||
// importDialog.value = false;
|
||||
// importLoading.value = false;
|
||||
// importCsv.value = null;
|
||||
|
||||
// if (importRef.value) {
|
||||
// importRef.value.value = "";
|
||||
// }
|
||||
|
||||
// eventBus.emit(EventTypes.ClearStores);
|
||||
// }
|
||||
|
||||
const purchasePriceOverTime = purchasePriceOverTimeChart(api);
|
||||
|
||||
const inventoryByLocation = inventoryByLocationChart(api);
|
||||
|
|
|
@ -344,7 +344,7 @@
|
|||
</div>
|
||||
</dialog>
|
||||
<section class="px-3">
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-6">
|
||||
<BaseCard>
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
|
@ -410,7 +410,7 @@
|
|||
</BaseCard>
|
||||
|
||||
<NuxtPage :item="item" :page-key="itemId" />
|
||||
<div v-if="!hasNested">
|
||||
<template v-if="!hasNested">
|
||||
<BaseCard v-if="photos && photos.length > 0">
|
||||
<template #title> Photos </template>
|
||||
<div
|
||||
|
@ -470,7 +470,7 @@
|
|||
<template #title> Sold Details </template>
|
||||
<DetailsSection :details="soldDetails" />
|
||||
</BaseCard>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -21,14 +21,12 @@
|
|||
{
|
||||
id: "total",
|
||||
title: "Total Cost",
|
||||
subtitle: "Sum over all entries",
|
||||
value: fmtCurrency(log.value.costTotal),
|
||||
value: log.value.costTotal,
|
||||
},
|
||||
{
|
||||
id: "average",
|
||||
title: "Monthly Average",
|
||||
subtitle: "Average over all entries",
|
||||
value: fmtCurrency(log.value.costAverage),
|
||||
value: log.value.costAverage,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
@ -138,13 +136,14 @@
|
|||
</BaseCard>
|
||||
</div>
|
||||
<div class="side-slot space-y-6">
|
||||
<div v-for="stat in stats" :key="stat.id" class="stats block shadow-xl border-l-primary">
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ stat.title }}</div>
|
||||
<div class="stat-value text-primary">{{ stat.value }}</div>
|
||||
<div class="stat-desc">{{ stat.subtitle }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<StatCard
|
||||
v-for="stat in stats"
|
||||
:key="stat.id"
|
||||
class="stats block shadow-xl border-l-primary"
|
||||
:title="stat.title"
|
||||
:value="stat.value"
|
||||
type="currency"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = {
|
|||
secondary: "#ECF4E7",
|
||||
accent: "#FFDA56",
|
||||
neutral: "#2C2E27",
|
||||
"base-100": "#F6FAFB",
|
||||
"base-100": "#FFFFFF",
|
||||
info: "#3ABFF8",
|
||||
success: "#36D399",
|
||||
warning: "#FBBD23",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue