mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2026-01-03 19:44:55 +01:00
Topbar remake (#752)
* feat: begin work on topbar refresh * feat: implement search input functionality * feat: add ScannerModal component based on scanner page * feat: add toggle to enable legacy topbar, remove scanner page * feat: update scanner menu item to use SidebarMenuButton for legacy header support * chore: lint * style: make margin and padding more consistent * feat: fun hack * fix: remove uneeded log and class=""
This commit is contained in:
106
frontend/components/App/HeaderText.vue
Normal file
106
frontend/components/App/HeaderText.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<svg
|
||||
viewBox="0 0 263 45"
|
||||
role="img"
|
||||
fill="none"
|
||||
preserveAspectRatio="none"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs id="defs15" />
|
||||
<g id="layer1">
|
||||
<path
|
||||
d="M 0,40.557 V 0 H 9.8019995 V 16.278 H 25.783999 V 0 h 9.782 v 40.557 h -9.782 V 24.259 H 9.8019995 v 16.298 z m 55.914,0.574 c -3.195,0 -5.941001,-0.653 -8.238,-1.96 -2.284,-1.32 -4.046,-3.156 -5.287,-5.506 -1.228,-2.363 -1.842,-5.102 -1.842,-8.218 0,-3.129 0.614,-5.868 1.842,-8.218 1.241,-2.364 3.003,-4.199 5.287,-5.506 2.296999,-1.32 5.043,-1.9799996 8.238,-1.9799996 3.195,0 5.934999,0.6599996 8.215999,1.9799996 2.3,1.307 4.06,3.142 5.290001,5.506 1.239999,2.35 1.86,5.089 1.86,8.218 0,3.116 -0.620001,5.855 -1.86,8.218 -1.230001,2.35 -2.99,4.186 -5.290001,5.506 -2.281,1.307 -5.020999,1.96 -8.215999,1.96 z m 0.06,-7.307 c 1.162,0 2.145,-0.357 2.949999,-1.07 0.806,-0.712 1.420001,-1.703 1.842,-2.97 0.436001,-1.267 0.654,-2.733 0.654,-4.396 0,-1.69 -0.218,-3.169 -0.654,-4.436 -0.421999,-1.268 -1.036,-2.258 -1.842,-2.971 -0.805,-0.713 -1.787999,-1.069 -2.95,-1.069 -1.202,0 -2.218,0.356 -3.05,1.069 -0.819,0.713 -1.445999,1.703 -1.881,2.971 -0.423,1.267 -0.634,2.746 -0.634,4.436 0,1.663 0.211,3.129 0.634,4.396 0.435001,1.267 1.062,2.258 1.881,2.97 0.832001,0.713 1.848,1.07 3.050001,1.07 z m 20.265999,6.733 V 10.139 h 9.21 v 5.585 h 0.34 c 0.63,-1.849 1.7,-3.308 3.21,-4.377 1.5,-1.069 3.3,-1.6039996 5.38,-1.6039996 2.11,0 3.92,0.5409996 5.43,1.6239996 C 101.31,12.449 102.27,13.902 102.68,15.724 H 103 c 0.56,-1.809 1.67,-3.255 3.32,-4.337 1.65,-1.096 3.6,-1.6439996 5.85,-1.6439996 2.87,0 5.21,0.9239996 7.01,2.7719996 1.79,1.836 2.69,4.357 2.69,7.565 v 20.477 h -9.68 V 22.298 c 0,-1.518 -0.39,-2.673 -1.17,-3.465 -0.78,-0.806 -1.79,-1.208 -3.03,-1.208 -1.34,0 -2.39,0.435 -3.15,1.307 -0.75,0.858 -1.13,2.013 -1.13,3.465 v 18.16 H 94.399999 V 22.199 c 0,-1.412 -0.38,-2.528 -1.15,-3.346 -0.76,-0.819 -1.77,-1.228 -3.03,-1.228 -0.84,0 -1.59,0.204 -2.23,0.614 -0.65,0.396 -1.16,0.963 -1.53,1.703 -0.36,0.739 -0.53,1.61 -0.53,2.614 V 40.557 Z M 142.09,41.131 c -3.18,0 -5.93,-0.627 -8.24,-1.881 -2.3,-1.267 -4.07,-3.07 -5.31,-5.406 -1.22,-2.35 -1.84,-5.143 -1.84,-8.377 0,-3.142 0.62,-5.888 1.86,-8.238 1.24,-2.364 2.99,-4.199 5.25,-5.506 2.26,-1.32 4.92,-1.9799996 7.98,-1.9799996 2.17,0 4.15,0.3369996 5.94,1.0099996 1.8,0.673 3.35,1.67 4.66,2.99 1.3,1.32 2.32,2.951 3.05,4.892 0.72,1.927 1.09,4.139 1.09,6.634 v 2.416 h -26.44 v -5.624 h 17.42 c -0.01,-1.03 -0.25,-1.948 -0.73,-2.753 -0.47,-0.805 -1.13,-1.432 -1.96,-1.881 -0.82,-0.462 -1.76,-0.693 -2.83,-0.693 -1.08,0 -2.05,0.244 -2.91,0.732 -0.86,0.476 -1.54,1.129 -2.04,1.961 -0.5,0.818 -0.77,1.749 -0.79,2.792 v 5.723 c 0,1.241 0.24,2.33 0.73,3.268 0.49,0.924 1.18,1.643 2.08,2.158 0.9,0.515 1.97,0.773 3.21,0.773 0.86,0 1.63,-0.119 2.33,-0.357 0.7,-0.237 1.3,-0.587 1.81,-1.049 0.5,-0.462 0.87,-1.03 1.12,-1.703 l 8.9,0.257 c -0.37,1.994 -1.19,3.73 -2.44,5.208 -1.24,1.466 -2.87,2.608 -4.89,3.426 -2.02,0.806 -4.36,1.208 -7.01,1.208 z m 19.38,-0.574 V 0 h 16.91 c 3.04,0 5.58,0.429 7.63,1.287 2.06,0.858 3.6,2.059 4.63,3.604 1.04,1.545 1.57,3.334 1.57,5.367 0,1.545 -0.33,2.924 -0.97,4.139 -0.65,1.201 -1.54,2.198 -2.68,2.99 -1.13,0.792 -2.45,1.347 -3.94,1.664 v 0.396 c 1.64,0.079 3.15,0.521 4.54,1.326 1.4,0.806 2.52,1.928 3.36,3.367 0.85,1.426 1.27,3.116 1.27,5.07 0,2.178 -0.55,4.125 -1.66,5.842 -1.11,1.703 -2.72,3.049 -4.82,4.04 -2.09,0.977 -4.64,1.465 -7.64,1.465 z m 9.8,-7.902 h 6.06 c 2.13,0 3.69,-0.402 4.7,-1.208 1.01,-0.805 1.52,-1.927 1.52,-3.366 0,-1.043 -0.24,-1.941 -0.73,-2.693 -0.49,-0.766 -1.18,-1.354 -2.08,-1.763 -0.9,-0.422 -1.97,-0.634 -3.23,-0.634 h -6.24 z m 0,-15.981 h 5.43 c 1.07,0 2.02,-0.178 2.85,-0.535 0.83,-0.356 1.48,-0.871 1.94,-1.544 0.48,-0.674 0.72,-1.485 0.72,-2.436 0,-1.36 -0.49,-2.4289996 -1.45,-3.2079996 C 179.8,8.172 178.5,7.782 176.86,7.782 h -5.59 z m 71.41,-6.535 5.02,10.021 5.18,-10.021 h 9.77 l -8.5,15.209 8.81,15.209 h -9.68 l -5.58,-10.1 -5.45,10.1 h -9.82 l 8.83,-15.209 -8.4,-15.209 z"
|
||||
class="fill-secondary-foreground"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 231.99,14.401 c 1.13,1.156 1.94,2.497 2.61,3.909 0.22,0.457 0.17,0.997 -0.13,1.406 -0.3,0.41 -0.79,0.623 -1.3,0.556 0,0.001 0,0.001 0,0.001"
|
||||
fill="#808080"
|
||||
/>
|
||||
<path
|
||||
d="m 231.99,14.401 c 1.13,1.156 1.94,2.497 2.61,3.909 0.22,0.457 0.17,0.997 -0.13,1.406 -0.3,0.41 -0.79,0.623 -1.3,0.556 0,0.001 0,0.001 0,0.001"
|
||||
stroke="#000000"
|
||||
stroke-width="0.95462"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 213.91,4.81 c -0.59,0.028 -1.18,0.188 -1.71,0.479 -3.54,1.916 -11.09,6.001 -14.77,7.993 -0.64,0.345 -1.16,0.849 -1.52,1.448 l 18.33,9.816 c 0,0 -1.16,1.926 -2.18,3.639 -0.88,1.464 -2.74,1.988 -4.25,1.194 l -12.49,-6.564 v 10.247 c 0,1.047 0.6,2.013 1.57,2.537 3.53,1.914 12.12,6.561 15.65,8.475 0.97,0.523 2.16,0.523 3.13,0 3.54,-1.914 12.12,-6.561 15.66,-8.475 0.96,-0.524 1.56,-1.49 1.56,-2.537 V 16.813 c 0,-1.474 -0.81,-2.829 -2.1,-3.531 C 227.11,11.29 219.56,7.205 216.02,5.289 215.36,4.934 214.63,4.774 213.91,4.81"
|
||||
fill="#dadada"
|
||||
/>
|
||||
<path
|
||||
d="m 213.91,4.81 c -0.59,0.028 -1.18,0.188 -1.71,0.479 -3.54,1.916 -11.09,6.001 -14.77,7.993 -0.64,0.345 -1.16,0.849 -1.52,1.448 l 18.33,9.816 c 0,0 -1.16,1.926 -2.18,3.639 -0.88,1.464 -2.74,1.988 -4.25,1.194 l -12.49,-6.564 v 10.247 c 0,1.047 0.6,2.013 1.57,2.537 3.53,1.914 12.12,6.561 15.65,8.475 0.97,0.523 2.16,0.523 3.13,0 3.54,-1.914 12.12,-6.561 15.66,-8.475 0.96,-0.524 1.56,-1.49 1.56,-2.537 V 16.813 c 0,-1.474 -0.81,-2.829 -2.1,-3.531 C 227.11,11.29 219.56,7.205 216.02,5.289 215.36,4.934 214.63,4.774 213.91,4.81"
|
||||
stroke="#000000"
|
||||
stroke-width="0.954666"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M 214.24,24.546 V 4.891 c -0.6,0.029 -1.51,0.107 -2.04,0.398 -3.54,1.916 -11.09,6.001 -14.77,7.993 -0.64,0.345 -1.16,0.849 -1.52,1.448 z"
|
||||
fill="#808080"
|
||||
stroke="#000000"
|
||||
stroke-width="0.954666"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 195.68,15.164 -1.73,3.31 c -0.82,1.569 -0.21,3.506 1.35,4.33 l 0.02,0.011 m 0,0 12.49,6.564 c 1.51,0.794 3.37,0.27 4.25,-1.194 1.02,-1.713 2.18,-3.639 2.18,-3.639 l -18.33,-9.816 -0.01,0.016 -0.22,0.418"
|
||||
fill="#dadada"
|
||||
/>
|
||||
<path
|
||||
d="m 195.68,15.164 -1.73,3.31 c -0.82,1.569 -0.21,3.506 1.35,4.33 l 0.02,0.011 m 0,0 12.49,6.564 c 1.51,0.794 3.37,0.27 4.25,-1.194 1.02,-1.713 2.18,-3.639 2.18,-3.639 l -18.33,-9.816 -0.01,0.016 -0.22,0.418"
|
||||
stroke="#000000"
|
||||
stroke-width="0.954666"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M 214.11,4.966 V 24.158"
|
||||
stroke="#000000"
|
||||
stroke-width="0.709918"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 230.33,21.532 c 0,-0.193 -0.1,-0.371 -0.27,-0.467 -0.17,-0.097 -0.37,-0.097 -0.54,0 l -11.28,6.515 c -0.17,0.096 -0.27,0.274 -0.27,0.467 v 7.152 c 0,0.193 0.1,0.371 0.27,0.467 0.16,0.097 0.37,0.097 0.54,0 l 11.28,-6.515 c 0.17,-0.096 0.27,-0.274 0.27,-0.467 z"
|
||||
class="fill-primary"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 230.33,21.532 c 0,-0.193 -0.1,-0.371 -0.27,-0.467 -0.17,-0.097 -0.37,-0.097 -0.54,0 l -11.28,6.515 c -0.17,0.096 -0.27,0.274 -0.27,0.467 v 7.152 c 0,0.193 0.1,0.371 0.27,0.467 0.16,0.097 0.37,0.097 0.54,0 l 11.28,-6.515 c 0.17,-0.096 0.27,-0.274 0.27,-0.467 z m -11.5,6.699 v 6.415 L 229.47,28.5 v -6.415 z"
|
||||
fill="#000000"
|
||||
/>
|
||||
<path
|
||||
d="M 214.11,44.374 V 24.587"
|
||||
stroke="#000000"
|
||||
stroke-width="0.954758"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="m 214.24,24.635 17.71,-10.12"
|
||||
stroke="#000000"
|
||||
stroke-width="0.954712"
|
||||
stroke-miterlimit="5.42683"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -83,6 +83,17 @@
|
||||
>
|
||||
{{ navigate.text }}
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
value="scanner"
|
||||
@select="
|
||||
() => {
|
||||
closeDialog('quick-menu');
|
||||
openDialog('scanner');
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ t("menu.scanner") }}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
|
||||
@@ -1,18 +1,49 @@
|
||||
<template>
|
||||
<Dialog dialog-id="scanner">
|
||||
<DialogScrollContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ t("scanner.title") }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div>
|
||||
<div
|
||||
v-if="errorMessage"
|
||||
class="mb-5 flex items-center gap-2 rounded-md border border-destructive bg-destructive/10 p-4 text-destructive"
|
||||
role="alert"
|
||||
>
|
||||
<MdiAlertCircleOutline class="text-destructive" />
|
||||
<span class="text-sm font-medium">{{ errorMessage }}</span>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line tailwindcss/no-custom-classname -->
|
||||
<video ref="video" class="aspect-video w-full rounded-lg bg-muted shadow" poster="data:image/gif,AAAA"></video>
|
||||
<div class="mt-4">
|
||||
<Select v-model="selectedSource">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="t('scanner.select_video_source')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="source in sources" :key="source.deviceId" :value="source.deviceId">
|
||||
{{ source.label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</DialogScrollContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { BrowserMultiFormatReader, NotFoundException } from "@zxing/library";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Dialog, DialogHeader, DialogTitle, DialogScrollContent } from "@/components/ui/dialog";
|
||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
|
||||
import MdiAlertCircleOutline from "~icons/mdi/alert-circle-outline";
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
});
|
||||
useHead({
|
||||
title: "Homebox | Scanner",
|
||||
});
|
||||
import { useDialog } from "@/components/ui/dialog-provider";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { activeDialog } = useDialog();
|
||||
const open = computed(() => activeDialog.value === "scanner");
|
||||
|
||||
const sources = ref<MediaDeviceInfo[]>([]);
|
||||
const selectedSource = ref<string | null>(null);
|
||||
@@ -26,7 +57,8 @@
|
||||
errorMessage.value = t("scanner.error");
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const startScanner = async () => {
|
||||
errorMessage.value = null;
|
||||
if (!(navigator && navigator.mediaDevices && "enumerateDevices" in navigator.mediaDevices)) {
|
||||
errorMessage.value = t("scanner.unsupported");
|
||||
return;
|
||||
@@ -51,12 +83,25 @@
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const stopScanner = () => {
|
||||
codeReader.reset();
|
||||
sources.value = [];
|
||||
selectedSource.value = null;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
watch(open, async isOpen => {
|
||||
if (isOpen) {
|
||||
await startScanner();
|
||||
} else {
|
||||
stopScanner();
|
||||
}
|
||||
});
|
||||
|
||||
// stop the code reader when navigating away
|
||||
onBeforeUnmount(() => codeReader.reset());
|
||||
|
||||
watch(selectedSource, async newSource => {
|
||||
if (!open.value || !newSource) return;
|
||||
codeReader.reset();
|
||||
|
||||
try {
|
||||
@@ -84,39 +129,11 @@
|
||||
handleError(err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card class="mx-auto w-full max-w-screen-md">
|
||||
<CardHeader>
|
||||
<CardTitle>{{ t("scanner.title") }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div
|
||||
v-if="errorMessage"
|
||||
class="mb-5 flex items-center gap-2 rounded-md border border-destructive bg-destructive/10 p-4 text-destructive"
|
||||
role="alert"
|
||||
>
|
||||
<MdiAlertCircleOutline class="text-destructive" />
|
||||
<span class="text-sm font-medium">{{ errorMessage }}</span>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line tailwindcss/no-custom-classname -->
|
||||
<video ref="video" class="aspect-video w-full rounded-lg bg-muted shadow" poster="data:image/gif,AAAA"></video>
|
||||
<div class="mt-4">
|
||||
<Select v-model="selectedSource">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="t('scanner.select_video_source')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="source in sources" :key="source.deviceId" :value="source.deviceId">
|
||||
{{ source.label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
onUnmounted(() => {
|
||||
stopScanner();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
video {
|
||||
@@ -8,7 +8,7 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="cmp" class="container mx-auto mt-10 max-w-7xl px-3">
|
||||
<component :is="cmp" class="container mx-auto my-4 max-w-7xl px-4">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<Card>
|
||||
<NuxtLink :to="`/location/${location.id}`" class="group/location-card transition duration-300">
|
||||
<div
|
||||
class=""
|
||||
:class="{
|
||||
'p-4': !dense,
|
||||
'px-3 py-2': dense,
|
||||
@@ -20,7 +19,7 @@
|
||||
<span class="mx-auto">
|
||||
{{ location.name }}
|
||||
</span>
|
||||
<Badge class="" :class="{ 'opacity-0': !hasCount }">
|
||||
<Badge :class="{ 'opacity-0': !hasCount }">
|
||||
{{ count }}
|
||||
</Badge>
|
||||
</h2>
|
||||
|
||||
@@ -12,7 +12,7 @@ export type LocationViewPreferences = {
|
||||
theme: DaisyTheme;
|
||||
itemsPerTablePage: number;
|
||||
tableHeaders?: TableHeaderType[];
|
||||
displayHeaderDecor: boolean;
|
||||
displayLegacyHeader: boolean;
|
||||
language?: string;
|
||||
overrideFormatLocale?: string;
|
||||
};
|
||||
@@ -31,7 +31,7 @@ export function useViewPreferences(): Ref<LocationViewPreferences> {
|
||||
itemDisplayView: "card",
|
||||
theme: "homebox",
|
||||
itemsPerTablePage: 10,
|
||||
displayHeaderDecor: true,
|
||||
displayLegacyHeader: false,
|
||||
language: null,
|
||||
overrideFormatLocale: null,
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<LabelCreateModal />
|
||||
<LocationCreateModal />
|
||||
<AppQuickMenuModal :actions="quickMenuActions" />
|
||||
<AppScannerModal />
|
||||
<SidebarProvider :default-open="sidebarState">
|
||||
<Sidebar collapsible="icon">
|
||||
<SidebarHeader class="items-center">
|
||||
@@ -69,6 +70,20 @@
|
||||
<span>{{ n.name.value }}</span>
|
||||
</SidebarMenuLink>
|
||||
</SidebarMenuItem>
|
||||
|
||||
<!-- makes scanner accessible easily if using legacy header -->
|
||||
<SidebarMenuItem v-if="preferences.displayLegacyHeader">
|
||||
<SidebarMenuButton
|
||||
:class="{
|
||||
'text-nowrap': typeof locale === 'string' && locale.startsWith('zh-'),
|
||||
}"
|
||||
:tooltip="$t('menu.scanner')"
|
||||
@click.prevent="openDialog('scanner')"
|
||||
>
|
||||
<MdiQrcodeScan />
|
||||
<span>{{ $t("menu.scanner") }}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
@@ -90,21 +105,47 @@
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
<SidebarInset class="min-h-screen bg-background-accent">
|
||||
<div class="justify-center pt-20 lg:pt-0">
|
||||
<AppHeaderDecor v-if="preferences.displayHeaderDecor" class="-mt-10 hidden lg:block" />
|
||||
<SidebarTrigger class="absolute left-2 top-2 hidden lg:flex" variant="default" />
|
||||
<div class="fixed top-0 z-20 flex h-16 w-full items-center gap-2 bg-primary p-2 shadow-md lg:hidden">
|
||||
<SidebarTrigger class="hover:bg-foreground" />
|
||||
<NuxtLink to="/home">
|
||||
<h2 class="flex text-3xl font-bold tracking-tight text-primary-foreground">
|
||||
HomeB
|
||||
<AppLogo class="-mb-3 w-8" />
|
||||
x
|
||||
</h2>
|
||||
</NuxtLink>
|
||||
<div class="relative flex h-full flex-col justify-center">
|
||||
<div v-if="preferences.displayLegacyHeader">
|
||||
<AppHeaderDecor class="-mt-10 hidden lg:block" />
|
||||
<SidebarTrigger class="absolute left-2 top-2 hidden lg:flex" variant="default" />
|
||||
</div>
|
||||
<div
|
||||
class="sticky top-0 z-20 flex h-28 translate-y-[-0.5px] flex-col bg-secondary p-2 shadow-md sm:h-16 sm:flex-row"
|
||||
:class="{
|
||||
'lg:hidden': preferences.displayLegacyHeader,
|
||||
}"
|
||||
>
|
||||
<div class="flex h-1/2 items-center gap-2 sm:h-auto">
|
||||
<SidebarTrigger variant="default" />
|
||||
<NuxtLink to="/home">
|
||||
<AppHeaderText class="h-6" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="sm:grow"></div>
|
||||
<div class="flex h-1/2 grow items-center justify-end gap-2 sm:h-auto">
|
||||
<Input
|
||||
v-model:model-value="search"
|
||||
class="h-9 grow sm:max-w-sm"
|
||||
:placeholder="$t('global.search')"
|
||||
type="search"
|
||||
@keyup.enter="triggerSearch"
|
||||
/>
|
||||
<div>
|
||||
<Button size="icon" @click="triggerSearch">
|
||||
<MdiMagnify />
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button size="icon" @click="openDialog('scanner')">
|
||||
<MdiQrcodeScan />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
<div class="grow"></div>
|
||||
|
||||
<footer v-if="status" class="bottom-0 w-full pb-4 text-center">
|
||||
<p class="text-center text-sm">
|
||||
@@ -164,6 +205,8 @@
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Shortcut } from "~/components/ui/shortcut";
|
||||
import { useDialog } from "~/components/ui/dialog-provider";
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { Button } from "~/components/ui/button";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const username = computed(() => authCtx.user?.name || "User");
|
||||
@@ -185,6 +228,19 @@
|
||||
return data;
|
||||
});
|
||||
|
||||
const search = ref("");
|
||||
|
||||
const triggerSearch = () => {
|
||||
if (search.value) {
|
||||
navigateTo(`/items?q=${encodeURIComponent(search.value)}`);
|
||||
search.value = "";
|
||||
// remove focus from input
|
||||
if (document.activeElement && "blur" in document.activeElement) {
|
||||
(document.activeElement as HTMLElement).blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Preload currency format
|
||||
useFormatCurrency();
|
||||
|
||||
@@ -233,13 +289,6 @@
|
||||
name: computed(() => t("menu.search")),
|
||||
to: "/items",
|
||||
},
|
||||
{
|
||||
icon: MdiQrcodeScan,
|
||||
id: 3,
|
||||
active: computed(() => route.path === "/scanner"),
|
||||
name: computed(() => t("menu.scanner")),
|
||||
to: "/scanner",
|
||||
},
|
||||
{
|
||||
icon: MdiWrench,
|
||||
id: 4,
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
"current_password": "Current Password",
|
||||
"delete_account": "Delete Account",
|
||||
"delete_account_sub": "Delete your account and all its associated data. This can not be undone.",
|
||||
"display_header": "{ currentValue, select, true {Hide Header} false {Show Header} other {Not Hit}}",
|
||||
"display_legacy_header": "{ currentValue, select, true {Disable Legacy Header} false {Enable Legacy Header} other {Not Hit}}",
|
||||
"enabled": "Enabled",
|
||||
"example": "Example",
|
||||
"gen_invite": "Generate Invite Link",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BaseContainer class="flex flex-col gap-12 pb-16">
|
||||
<BaseContainer class="flex flex-col gap-4">
|
||||
<section>
|
||||
<Subtitle> {{ $t("home.quick_statistics") }} </Subtitle>
|
||||
<div class="grid grid-cols-2 gap-2 md:grid-cols-4 md:gap-6">
|
||||
|
||||
@@ -524,7 +524,7 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContainer v-if="item" class="pb-8">
|
||||
<BaseContainer v-if="item">
|
||||
<!-- set page title -->
|
||||
<Title>{{ item.name }}</Title>
|
||||
|
||||
@@ -736,7 +736,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="items && items.length > 0" class="my-6">
|
||||
<section v-if="items && items.length > 0" class="mt-6">
|
||||
<ItemViewSelectable :items="items" />
|
||||
</section>
|
||||
</BaseContainer>
|
||||
|
||||
@@ -550,7 +550,7 @@
|
||||
<TooltipContent>{{ $t("items.show_advanced_view_options") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<Button class="" size="sm" @click="saveItem">
|
||||
<Button size="sm" @click="saveItem">
|
||||
<MdiContentSaveOutline />
|
||||
{{ $t("global.save") }}
|
||||
</Button>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContainer class="mb-6 flex flex-col gap-8">
|
||||
<BaseContainer class="flex flex-col gap-8">
|
||||
<MaintenanceListView :current-item-id="props.item.id"></MaintenanceListView>
|
||||
</BaseContainer>
|
||||
</template>
|
||||
|
||||
@@ -369,11 +369,11 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContainer class="mb-16">
|
||||
<BaseContainer>
|
||||
<div v-if="locations && labels">
|
||||
<div class="flex flex-wrap items-end gap-4 md:flex-nowrap">
|
||||
<div class="w-full">
|
||||
<Input v-model="query" :placeholder="$t('global.search')" class="h-12" />
|
||||
<Input v-model:model-value="query" :placeholder="$t('global.search')" class="h-12" />
|
||||
<div v-if="byAssetId" class="pl-2 pt-2 text-sm">
|
||||
<p>{{ $t("items.query_id", { id: parsedAssetId }) }}</p>
|
||||
</div>
|
||||
|
||||
@@ -83,36 +83,38 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseContainer class="mb-16">
|
||||
<BaseSectionHeader> {{ $t("menu.locations") }} </BaseSectionHeader>
|
||||
<BaseContainer>
|
||||
<div class="mb-2 flex justify-between">
|
||||
<BaseSectionHeader> {{ $t("menu.locations") }} </BaseSectionHeader>
|
||||
<div>
|
||||
<TooltipProvider :delay-duration="0">
|
||||
<ButtonGroup>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button size="icon" variant="outline" data-pos="start" @click="openAll">
|
||||
<MdiExpandAllOutline />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("locations.expand_tree") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button size="icon" variant="outline" data-pos="end" @click="closeAll">
|
||||
<MdiCollapseAllOutline />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("locations.collapse_tree") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
</div>
|
||||
<BaseCard>
|
||||
<div class="p-4">
|
||||
<div class="mb-2 flex justify-end">
|
||||
<TooltipProvider :delay-duration="0">
|
||||
<ButtonGroup>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button size="icon" variant="outline" data-pos="start" @click="openAll">
|
||||
<MdiExpandAllOutline />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("locations.expand_tree") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button size="icon" variant="outline" data-pos="end" @click="closeAll">
|
||||
<MdiCollapseAllOutline />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t("locations.collapse_tree") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<LocationTreeRoot v-if="tree" :locs="tree" :tree-id="locationTreeId" />
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BaseContainer class="mb-6 flex flex-col gap-8">
|
||||
<BaseContainer class="flex flex-col gap-4">
|
||||
<BaseSectionHeader> {{ $t("menu.maintenance") }} </BaseSectionHeader>
|
||||
<MaintenanceListView></MaintenanceListView>
|
||||
</BaseContainer>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
const preferences = useViewPreferences();
|
||||
function setDisplayHeader() {
|
||||
preferences.value.displayHeaderDecor = !preferences.value.displayHeaderDecor;
|
||||
preferences.value.displayLegacyHeader = !preferences.value.displayLegacyHeader;
|
||||
}
|
||||
|
||||
// Currency Selection
|
||||
@@ -354,7 +354,7 @@
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<BaseContainer class="mb-6 flex flex-col gap-4">
|
||||
<BaseContainer class="flex flex-col gap-4">
|
||||
<BaseCard>
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
@@ -389,7 +389,7 @@
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
<MdiMegaphone class="-mt-1 mr-2" />
|
||||
<span class=""> {{ $t("profile.notifiers") }} </span>
|
||||
<span> {{ $t("profile.notifiers") }} </span>
|
||||
<template #description> {{ $t("profile.notifiers_sub") }} </template>
|
||||
</BaseSectionHeader>
|
||||
</template>
|
||||
@@ -442,7 +442,7 @@
|
||||
<template #title>
|
||||
<BaseSectionHeader class="pb-0">
|
||||
<MdiAccountMultiple class="-mt-1 mr-2" />
|
||||
<span class=""> {{ $t("profile.group_settings") }} </span>
|
||||
<span> {{ $t("profile.group_settings") }} </span>
|
||||
<template #description>
|
||||
{{ $t("profile.group_settings_sub") }}
|
||||
</template>
|
||||
@@ -484,7 +484,7 @@
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
<MdiFill class="mr-2" />
|
||||
<span class=""> {{ $t("profile.theme_settings") }} </span>
|
||||
<span> {{ $t("profile.theme_settings") }} </span>
|
||||
<template #description>
|
||||
{{ $t("profile.theme_settings_sub") }}
|
||||
</template>
|
||||
@@ -494,7 +494,7 @@
|
||||
<div class="px-4 pb-4">
|
||||
<div class="mb-3">
|
||||
<Button variant="secondary" size="sm" @click="setDisplayHeader">
|
||||
{{ $t("profile.display_header", { currentValue: preferences.displayHeaderDecor }) }}
|
||||
{{ $t("profile.display_legacy_header", { currentValue: preferences.displayLegacyHeader }) }}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="homebox grid grid-cols-1 gap-4 font-sans sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
@@ -538,7 +538,7 @@
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
<MdiDelete class="-mt-1 mr-2" />
|
||||
<span class=""> {{ $t("profile.delete_account") }} </span>
|
||||
<span> {{ $t("profile.delete_account") }} </span>
|
||||
<template #description> {{ $t("profile.delete_account_sub") }} </template>
|
||||
</BaseSectionHeader>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<AppImportDialog />
|
||||
<BaseContainer class="mb-6 flex flex-col gap-4">
|
||||
<BaseContainer class="flex flex-col gap-4">
|
||||
<BaseCard>
|
||||
<template #title>
|
||||
<BaseSectionHeader>
|
||||
|
||||
@@ -126,18 +126,18 @@ module.exports = {
|
||||
typography: {
|
||||
DEFAULT: {
|
||||
css: {
|
||||
color: 'hsl(var(--foreground))',
|
||||
color: "hsl(var(--foreground))",
|
||||
a: {
|
||||
color: 'hsl(var(--primary))',
|
||||
'&:hover': {
|
||||
color: 'hsl(var(--primary) / 0.8)',
|
||||
color: "hsl(var(--primary))",
|
||||
"&:hover": {
|
||||
color: "hsl(var(--primary) / 0.8)",
|
||||
},
|
||||
},
|
||||
h1: {
|
||||
color: 'hsl(var(--foreground))',
|
||||
color: "hsl(var(--foreground))",
|
||||
},
|
||||
h2: {
|
||||
color: 'hsl(var(--foreground))',
|
||||
color: "hsl(var(--foreground))",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user