feat: begin upgrading deps, still very buggy

This commit is contained in:
tonyaellie
2025-08-29 13:46:45 +01:00
parent 52a6a31098
commit 73179ea8f5
51 changed files with 12964 additions and 5678 deletions

View File

@@ -4,7 +4,7 @@
},
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig, pnpm-lock.yaml, postcss.config.js, tailwind.config.js",
"package.json": "package-lock.json, yarn.lock, eslint.config.mjs, tsconfig.json, .prettierrc, .editorconfig, pnpm-lock.yaml, postcss.config.js, tailwind.config.js",
"docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml",
"README.md": "LICENSE, SECURITY.md"
},
@@ -22,6 +22,7 @@
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"eslint.format.enable": true,
"eslint.useFlatConfig": true,
"css.validate": false,
"tailwindCSS.includeLanguages": {
"vue": "html",

View File

@@ -1,55 +0,0 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:vue/essential",
"plugin:@typescript-eslint/recommended",
"@nuxtjs/eslint-config-typescript",
"plugin:vue/vue3-recommended",
"plugin:prettier/recommended",
"plugin:tailwindcss/recommended",
],
parserOptions: {
ecmaVersion: "latest",
parser: "@typescript-eslint/parser",
sourceType: "module",
},
plugins: ["vue", "@typescript-eslint"],
rules: {
"no-console": 0,
"no-unused-vars": "off",
"vue/multi-word-component-names": "off",
"vue/no-setup-props-destructure": 0,
"vue/no-multiple-template-root": 0,
"vue/no-v-model-argument": 0,
"vue/no-v-html": 0,
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/ban-ts-comment": 0,
"tailwindcss/no-custom-classname": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
ignoreRestSiblings: true,
destructuredArrayIgnorePattern: "_",
caughtErrors: "none",
},
],
"prettier/prettier": [
"warn",
{
arrowParens: "avoid",
semi: true,
tabWidth: 2,
useTabs: false,
vueIndentScriptAndStyle: true,
singleQuote: false,
trailingComma: "es5",
printWidth: 120,
},
],
},
};

View File

@@ -6,7 +6,7 @@
<NuxtLayout>
<Html :lang="locale" :data-theme="theme || 'homebox'" />
<Link rel="icon" type="image/svg" href="/favicon.svg"></Link>
<Link rel="icon" type="image/svg" href="/favicon.svg" />
<Link rel="apple-touch-icon" href="/apple-touch-icon.png" size="180x180" />
<Link rel="mask-icon" href="/mask-icon.svg" color="#5b7f67" />
<Meta name="theme-color" content="#5b7f67" />

View File

@@ -47,7 +47,7 @@
import { useMediaQuery } from "@vueuse/core";
import type { DialogID } from "@/components/ui/dialog-provider/utils";
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from "@/components/ui/drawer";
import { Dialog, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Dialog, DialogScrollContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
const isDesktop = useMediaQuery("(min-width: 768px)");

View File

@@ -33,7 +33,7 @@
</ButtonGroup>
</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>
<video ref="video" class="aspect-video w-full rounded-lg bg-muted shadow" poster="data:image/gif,AAAA" />
<div class="mt-4">
<Select v-model="selectedSource">
<SelectTrigger class="w-full">

View File

@@ -19,9 +19,9 @@
>
<div :data-theme="theme.value" class="w-full cursor-pointer bg-background-accent text-foreground">
<div class="grid grid-cols-5 grid-rows-3">
<div class="col-start-1 row-start-1 bg-background"></div>
<div class="col-start-1 row-start-2 bg-sidebar"></div>
<div class="col-start-1 row-start-3 bg-background-accent"></div>
<div class="col-start-1 row-start-1 bg-background" />
<div class="col-start-1 row-start-2 bg-sidebar" />
<div class="col-start-1 row-start-3 bg-background-accent" />
<div class="col-span-4 col-start-2 row-span-3 row-start-1 flex flex-col gap-1 bg-background p-2">
<div class="font-bold">{{ theme.label }}</div>
<div class="flex flex-wrap gap-1">

View File

@@ -3,7 +3,7 @@
<CardHeader v-if="$slots.title" class="px-4 py-5 sm:px-6">
<component :is="collapsable ? 'button' : 'div'" v-on="collapsable ? { click: toggle } : {}">
<h3 class="flex items-center text-lg font-medium leading-6">
<slot name="title"></slot>
<slot name="title" />
<template v-if="collapsable">
<span class="ml-2 transition-transform" :class="{ 'rotate-180': collapsed }">
<MdiChevronDown class="size-6" />
@@ -13,10 +13,10 @@
</component>
<div>
<p v-if="$slots.subtitle" class="mt-1 max-w-2xl text-sm text-gray-500">
<slot name="subtitle"></slot>
<slot name="subtitle" />
</p>
<template v-if="$slots['title-actions']">
<slot name="title-actions"></slot>
<slot name="title-actions" />
</template>
</div>
</CardHeader>

View File

@@ -2,24 +2,24 @@
<div class="flex flex-col gap-10 py-6 md:flex-row">
<div class="flex-1">
<h4 class="mb-1 text-lg font-semibold">
<slot name="title"></slot>
<slot name="title" />
</h4>
<p class="text-sm">
<slot></slot>
<slot />
</p>
</div>
<div class="flex items-center">
<template v-if="to">
<NuxtLink :to="to" :class="buttonVariants({ size: 'lg' })" class="min-w-52 grow">
<slot name="button">
<slot name="title"></slot>
<slot name="title" />
</slot>
</NuxtLink>
</template>
<template v-else>
<Button class="min-w-52 grow" size="lg" @click="$emit('action')">
<slot name="button">
<slot name="title"></slot>
<slot name="title" />
</slot>
</Button>
</template>

View File

@@ -108,7 +108,7 @@
<MdiClose class="size-3" />
</Button>
</div>
<input :id="id" ref="colorInput" v-model="value" type="color" class="sr-only" tabindex="-1" />
<input :id="id" ref="colorInput" v-model="value" type="color" class="sr-only" tabindex="-1" >
</div>
</div>
<div v-else class="sm:grid sm:grid-cols-4 sm:items-start sm:gap-4">
@@ -149,7 +149,7 @@
<MdiClose class="size-3" />
</Button>
</div>
<input :id="id" ref="colorInput" v-model="value" type="color" class="sr-only" tabindex="-1" />
<input :id="id" ref="colorInput" v-model="value" type="color" class="sr-only" tabindex="-1" >
</div>
</div>
</template>

View File

@@ -1,7 +1,6 @@
<template>
<div class="relative">
<FormTextField v-model="value" :placeholder="localizedPlaceholder" :label="localizedLabel" :type="inputType">
</FormTextField>
<FormTextField v-model="value" :placeholder="localizedPlaceholder" :label="localizedLabel" :type="inputType" />
<TooltipProvider :delay-duration="0">
<Tooltip>
<TooltipTrigger as-child>

View File

@@ -2,7 +2,7 @@
<div v-if="!inline" class="flex w-full flex-col gap-1.5">
<Label :for="id" class="flex w-full px-1">
<span>{{ label }}</span>
<span class="grow"></span>
<span class="grow" />
<span :class="{ 'text-destructive': isLengthInvalid }">
{{ lengthIndicator }}
</span>
@@ -18,7 +18,7 @@
<div v-else class="sm:grid sm:grid-cols-4 sm:items-start sm:gap-4">
<Label :for="id" class="flex w-full px-1 py-2">
<span>{{ label }}</span>
<span class="grow"></span>
<span class="grow" />
<span :class="{ 'text-destructive': isLengthInvalid }">
{{ lengthIndicator }}
</span>

View File

@@ -2,7 +2,7 @@
<div v-if="!inline" class="flex w-full flex-col gap-1.5">
<Label :for="id" class="flex w-full px-1">
<span> {{ label }} </span>
<span class="grow"></span>
<span class="grow" />
<span
:class="{
'text-destructive':
@@ -26,7 +26,7 @@
<div v-else class="sm:grid sm:grid-cols-4 sm:items-start sm:gap-4">
<Label class="flex w-full px-1 py-2" :for="id">
<span> {{ label }} </span>
<span class="grow"></span>
<span class="grow" />
<span
:class="{
'text-destructive':

View File

@@ -78,7 +78,7 @@
>
<template v-if="h.type === 'name'">
<div class="flex items-center space-x-4">
<img :src="p.imageBase64" class="w-16 rounded object-fill shadow-sm" alt="Product's photo" />
<img :src="p.imageBase64" class="w-16 rounded object-fill shadow-sm" alt="Product's photo" >
<span class="text-sm font-medium">
{{ p.item.name }}
</span>

View File

@@ -2,7 +2,7 @@
<Card class="overflow-hidden">
<NuxtLink :to="`/item/${item.id}`">
<div class="relative h-[200px]">
<img v-if="imageUrl" class="h-[200px] w-full object-cover shadow-md" loading="lazy" :src="imageUrl" alt="" />
<img v-if="imageUrl" class="h-[200px] w-full object-cover shadow-md" loading="lazy" :src="imageUrl" alt="" >
<div class="absolute inset-x-1 bottom-1">
<Badge class="text-wrap bg-secondary text-secondary-foreground hover:bg-secondary/70 hover:underline">
<NuxtLink v-if="item.location" :to="`/location/${item.location.id}`">
@@ -32,7 +32,7 @@
{{ $t("global.archived") }}
</TooltipContent>
</Tooltip>
<div class="grow"></div>
<div class="grow" />
<Tooltip>
<TooltipTrigger>
<Badge>

View File

@@ -102,7 +102,7 @@
:src="photo.fileBase64"
class="w-full rounded object-fill shadow-sm"
:alt="$t('components.item.create_modal.uploaded')"
/>
>
</div>
<div class="mt-2 flex items-center gap-2">
<TooltipProvider class="flex gap-2" :delay-duration="0">
@@ -190,6 +190,10 @@
import { useDialog, useDialogHotkey } from "~/components/ui/dialog-provider";
import LabelSelector from "~/components/Label/Selector.vue";
import ItemSelector from "~/components/Item/Selector.vue";
import { TooltipProvider, Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip";
import LocationSelector from "~/components/Location/Selector.vue";
import FormTextField from "~/components/Form/TextField.vue";
import FormTextArea from "~/components/Form/TextArea.vue";
interface PhotoPreview {
photoName: string;

View File

@@ -75,8 +75,8 @@
<Dialog :dialog-id="DialogID.ItemImage">
<DialogContent class="w-auto border-transparent bg-transparent p-0" disable-close>
<picture>
<source :srcset="image.originalSrc" :type="image.originalType" />
<img :src="image.thumbnailSrc" alt="attachment image" />
<source :srcset="image.originalSrc" :type="image.originalType" >
<img :src="image.thumbnailSrc" alt="attachment image" >
</picture>
<Button variant="destructive" size="icon" class="absolute right-[84px] top-1" @click="deleteAttachment">
<MdiDelete />

View File

@@ -47,7 +47,6 @@
import { Label } from "~/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { cn } from "~/lib/utils";
import { useId } from "#imports";
const { t } = useI18n();

View File

@@ -51,7 +51,7 @@
'hover:bg-accent hover:text-accent-foreground': hasChildren,
}"
>
<div v-if="!hasChildren" class="size-6"></div>
<div v-if="!hasChildren" class="size-6" />
<div v-else class="group/node relative size-6" :data-swap="openRef">
<div
class="absolute inset-0 flex items-center justify-center transition-transform duration-300 group-data-[swap=true]/node:rotate-90"

View File

@@ -125,7 +125,7 @@
</section>
<section>
<!-- begin -->
<MaintenanceEditModal ref="maintenanceEditModal" @changed="refreshList"></MaintenanceEditModal>
<MaintenanceEditModal ref="maintenanceEditModal" @changed="refreshList" />
<div class="container space-y-6">
<BaseCard v-for="e in maintenanceDataList" :key="e.id">
<BaseSectionHeader class="border-b p-6">

View File

@@ -19,6 +19,16 @@
<script setup lang="ts">
import { useDialog } from "./ui/dialog-provider";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
const { text, isRevealed, confirm, cancel } = useConfirm();
const { addAlert, removeAlert } = useDialog();

View File

@@ -22,7 +22,7 @@
</div>
<Checkbox :model-value="true" @update:model-value="_ => (selected = selected.filter(s => s.id !== v.id))" />
</Label>
<hr v-if="selected.length > 0" />
<hr v-if="selected.length > 0" >
<Label
v-for="v in unselected"
:key="v.id"

View File

@@ -104,7 +104,7 @@
{{ $t("components.global.label_maker.confirm_description") }}
</DialogDescription>
</DialogHeader>
<img :src="getLabelUrl(false)" />
<img :src="getLabelUrl(false)" >
<DialogFooter>
<ButtonGroup>
<Button v-if="status?.labelPrinting || false" type="submit" :disabled="serverPrinting" @click="serverPrint">

View File

@@ -25,7 +25,7 @@
<template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="markdown text-wrap break-words" v-html="raw"></div>
<div class="markdown text-wrap break-words" v-html="raw" />
</template>
<style scoped>

View File

@@ -24,7 +24,7 @@
{{ $t("components.global.page_qr_code.page_url") }}
</DialogTitle>
</DialogHeader>
<img :src="getQRCodeUrl()" />
<img :src="getQRCodeUrl()" >
</DialogContent>
</Dialog>

View File

@@ -1,3 +1,3 @@
<template>
<div class="max-w-full"></div>
<div class="max-w-full" />
</template>

View File

@@ -34,7 +34,6 @@ export interface IAuthContext {
}
class AuthContext implements IAuthContext {
// eslint-disable-next-line no-use-before-define
private static _instance?: AuthContext;
private static readonly cookieTokenKey = "hb.auth.session";

View File

@@ -44,7 +44,6 @@ export function useBreakpoints(): Breakpoints {
}
class ThemeObserver {
// eslint-disable-next-line no-use-before-define
private static instance?: ThemeObserver;
private readonly observer: MutationObserver;

View File

@@ -1,5 +1,5 @@
import type { ComputedRef } from "vue";
import { type DaisyTheme } from "~~/lib/data/themes";
import type { DaisyTheme } from "~~/lib/data/themes";
export interface UseTheme {
theme: ComputedRef<DaisyTheme>;

View File

@@ -0,0 +1,58 @@
import withNuxt from './.nuxt/eslint.config.mjs';
import tailwind from 'eslint-plugin-tailwindcss';
import prettier from 'eslint-plugin-prettier';
import { includeIgnoreFile } from '@eslint/compat';
import { fileURLToPath } from 'node:url';
const gitignorePath = fileURLToPath(new URL('../.gitignore', import.meta.url));
export default withNuxt([
includeIgnoreFile(gitignorePath, 'Imported ../.gitignore patterns'),
...tailwind.configs['flat/recommended'],
{
plugins: {
prettier,
},
rules: {
'vue/no-undef-components': [
'error',
{
ignorePatterns: [],
},
],
'no-console': 0,
'no-unused-vars': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-setup-props-destructure': 0,
'vue/no-multiple-template-root': 0,
'vue/no-v-model-argument': 0,
'vue/no-v-html': 0,
'tailwind/no-custom-classname': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{
ignoreRestSiblings: true,
destructuredArrayIgnorePattern: '_',
caughtErrors: 'none',
},
],
'prettier/prettier': [
'warn',
{
arrowParens: 'avoid',
semi: true,
tabWidth: 2,
useTabs: false,
vueIndentScriptAndStyle: true,
singleQuote: false,
trailingComma: 'es5',
printWidth: 120,
},
],
},
},
]);

View File

@@ -1 +1 @@
/// <reference types="unplugin-icons/types/vue" />
/// <reference types="unplugin-icons/types/vue" />

View File

@@ -6,7 +6,7 @@
up the tree
-->
<ModalConfirm />
<AppOutdatedModal v-if="status" :status="status" />
<OutdatedModal v-if="status" :status="status" />
<ItemCreateModal />
<LabelCreateModal />
<LocationCreateModal />
@@ -124,7 +124,7 @@
<AppHeaderText class="h-6" />
</NuxtLink>
</div>
<div class="sm:grow"></div>
<div class="sm:grow" />
<div class="flex h-1/2 grow items-center justify-end gap-2 sm:h-auto">
<Input
v-model:model-value="search"
@@ -146,8 +146,8 @@
</div>
</div>
<slot></slot>
<div class="grow"></div>
<slot />
<div class="grow" />
<footer v-if="status" class="bottom-0 w-full pb-4 text-center">
<p class="text-center text-sm">
@@ -157,9 +157,9 @@
$t('global.footer.version_link', { version: status.build.version, build: status.build.commit })
)
"
></span>
/>
~
<span v-html="DOMPurify.sanitize($t('global.footer.api_link'))"></span>
<span v-html="DOMPurify.sanitize($t('global.footer.api_link'))" />
</p>
</footer>
</div>
@@ -186,6 +186,7 @@
import {
Sidebar,
SidebarProvider,
SidebarContent,
SidebarFooter,
SidebarHeader,
@@ -211,6 +212,9 @@
import { Button } from "~/components/ui/button";
import { toast } from "@/components/ui/sonner";
import { DialogID, type NoParamDialogIDs, type OptionalDialogIDs } from "~/components/ui/dialog-provider/utils";
import ModalConfirm from "~/components/ModalConfirm.vue";
import OutdatedModal from "~/components/App/OutdatedModal.vue";
import ItemCreateModal from "~/components/Item/CreateModal.vue";
const { t, locale } = useI18n();
const username = computed(() => authCtx.user?.name || "User");

View File

@@ -82,7 +82,7 @@ export class Requests {
const token = this.token();
if (token !== "" && payload.headers !== undefined) {
// @ts-expect-error - we know that the header is there
payload.headers["Authorization"] = token; // eslint-disable-line dot-notation
payload.headers["Authorization"] = token;
}
if (this.methodSupportsBody(method)) {

View File

@@ -4,6 +4,10 @@ import { defineNuxtConfig } from "nuxt/config";
export default defineNuxtConfig({
ssr: false,
components: {
dirs: [],
},
build: {
transpile: ["vue-i18n"],
},
@@ -15,8 +19,13 @@ export default defineNuxtConfig({
"@vite-pwa/nuxt",
"unplugin-icons/nuxt",
"shadcn-nuxt",
"@nuxt/eslint",
],
eslint: {
config: {},
},
nitro: {
devProxy: {
"/api": {

View File

@@ -5,78 +5,77 @@
"dev": "nuxt dev",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" .",
"lint:fix": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" . --fix",
"lint:ci": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" . --max-warnings 1",
"typecheck": "pnpm vue-tsc --noEmit",
"lint": "eslint --ext \".ts,.js,.vue\" --ignore-pattern \"components/ui\" .",
"lint:fix": "eslint --ext \".ts,.js,.vue\" --ignore-pattern \"components/ui\" . --fix",
"lint:ci": "eslint --ext \".ts,.js,.vue\" --ignore-pattern \"components/ui\" . --max-warnings 1",
"typecheck": "pnpm nuxi typecheck --noEmit",
"test:ci": "TEST_SHUTDOWN_API_SERVER=true vitest --run --config ./test/vitest.config.ts",
"test:local": "TEST_SHUTDOWN_API_SERVER=false && vitest --run --config ./test/vitest.config.ts",
"test:watch": " TEST_SHUTDOWN_API_SERVER=false vitest --config ./test/vitest.config.ts"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@eslint/compat": "^1.3.2",
"@faker-js/faker": "^10.0.0",
"@iconify-json/mdi": "^1.2.3",
"@intlify/unplugin-vue-i18n": "^4.0.0",
"@nuxtjs/eslint-config-typescript": "^12.1.0",
"@playwright/test": "^1.52.0",
"@types/markdown-it": "^14.1.0",
"@intlify/unplugin-vue-i18n": "^6.0.8",
"@nuxt/eslint": "^1.9.0",
"@playwright/test": "^1.55.0",
"@types/markdown-it": "^14.1.2",
"@types/semver": "^7.7.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vite-pwa/nuxt": "^0.5.0",
"@vue/runtime-core": "^3.5.13",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-tailwindcss": "^3.18.0",
"eslint-plugin-vue": "^9.33.0",
"@vite-pwa/nuxt": "^1.0.4",
"@vue/runtime-core": "^3.5.20",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-tailwindcss": "^3.18.2",
"globals": "^16.3.0",
"h3": "^1.7.1",
"intl-messageformat": "^10.7.16",
"isomorphic-fetch": "^3.0.0",
"nuxt": "3.12.4",
"prettier": "^3.5.3",
"shadcn-nuxt": "0.11.3",
"typescript": "5.6.2",
"unplugin-icons": "^0.18.5",
"nuxt": "4.0.3",
"prettier": "^3.6.2",
"shadcn-nuxt": "2.2.0",
"typescript": "5.9.2",
"unplugin-icons": "^22.2.0",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^1.6.1",
"vue-i18n": "^9.14.4",
"vue-tsc": "2.1.6"
"vitest": "^3.2.4",
"vue-i18n": "^11.1.11",
"vue-tsc": "3.0.6"
},
"dependencies": {
"@mdit/plugin-img-size": "^0.22.2",
"@nuxtjs/color-mode": "^3.5.2",
"@nuxtjs/tailwindcss": "^6.13.2",
"@pinia/nuxt": "^0.5.5",
"@nuxtjs/tailwindcss": "^6.14.0",
"@pinia/nuxt": "^0.11.2",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@vuepic/vue-datepicker": "^8.8.1",
"@vueuse/core": "^12.8.2",
"@vueuse/nuxt": "^10.11.1",
"@vueuse/router": "^10.11.1",
"@vuepic/vue-datepicker": "^11.0.2",
"@vueuse/core": "^13.8.0",
"@vueuse/nuxt": "^13.8.0",
"@vueuse/router": "^13.8.0",
"@zxing/library": "^0.21.3",
"autoprefixer": "^10.4.21",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"dompurify": "^3.2.5",
"date-fns": "^4.1.0",
"dompurify": "^3.2.6",
"fuzzysort": "^3.1.0",
"h3": "^1.15.1",
"h3": "^1.15.4",
"http-proxy": "^1.18.1",
"lucide-vue-next": "^0.474.0",
"lucide-vue-next": "^0.542.0",
"markdown-it": "^14.1.0",
"pinia": "^2.3.1",
"postcss": "^8.5.3",
"reka-ui": "^2.2.0",
"semver": "^7.7.1",
"tailwind-merge": "^2.6.0",
"pinia": "^3.0.3",
"postcss": "^8.5.6",
"reka-ui": "^2.5.0",
"semver": "^7.7.2",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"vaul-vue": "^0.4.1",
"vite": "^6.3.5",
"vue": "3.4.8",
"vue-router": "^4.5.0",
"vue-sonner": "^1.3.2"
"vite": "^7.1.3",
"vue": "3.5.20",
"vue-router": "^4.5.1",
"vue-sonner": "^2.0.8"
}
}

View File

@@ -10,10 +10,16 @@ type StatCard = {
export function statCardData(api: UserClient) {
const { t } = useI18n();
const { data: statistics } = useAsyncData(async () => {
const { data } = await api.stats.group();
return data;
});
const { data: statistics } = useAsyncData(
"statistics",
async () => {
const { data } = await api.stats.group();
return data;
},
{
deep: true,
}
);
return computed(() => {
return [

View File

@@ -1,14 +1,20 @@
import type { UserClient } from "~~/lib/api/user";
export function itemsTable(api: UserClient) {
const { data: items, refresh } = useAsyncData(async () => {
const { data } = await api.items.getAll({
page: 1,
pageSize: 5,
orderBy: "createdAt",
});
return data.items;
});
const { data: items, refresh } = useAsyncData(
"items",
async () => {
const { data } = await api.items.getAll({
page: 1,
pageSize: 5,
orderBy: "createdAt",
});
return data.items;
},
{
deep: true,
}
);
onServerEvent(ServerEvent.ItemMutation, () => {
console.log("item mutation");

View File

@@ -176,7 +176,7 @@
<path
fill-opacity="1"
d="M0,32L80,69.3C160,107,320,181,480,181.3C640,181,800,107,960,117.3C1120,128,1280,224,1360,272L1440,320L1440,0L1360,0C1280,0,1120,0,960,0C800,0,640,0,480,0C320,0,160,0,80,0L0,0Z"
></path>
/>
</svg>
</div>
<div>

View File

@@ -645,7 +645,7 @@
</header>
<Separator v-if="item.description" />
<div v-if="item.description" class="prose max-w-full p-1">
<Markdown class="text-base" :source="item.description"> </Markdown>
<Markdown class="text-base" :source="item.description" />
</div>
</Card>
@@ -711,8 +711,8 @@
<div class="scroll-bg container mx-auto flex max-h-[500px] flex-wrap gap-2 overflow-y-scroll border-t p-4">
<button v-for="(img, i) in photos" :key="i" @click="openImageDialog(img, item.id)">
<picture>
<source :srcset="img.originalSrc" :type="img.originalType" />
<img class="max-h-[200px] rounded" :src="img.thumbnailSrc" alt="attachment image" />
<source :srcset="img.originalSrc" :type="img.originalType" >
<img class="max-h-[200px] rounded" :src="img.thumbnailSrc" alt="attachment image" >
</picture>
</button>
</div>

View File

@@ -52,7 +52,7 @@
return;
}
if (locations && data.location?.id) {
if (locations.value && data.location?.id) {
// @ts-expect-error - we know the locations is valid
const location = locations.value.find(l => l.id === data.location.id);
if (location) {
@@ -679,7 +679,7 @@
class="grid h-24 w-full place-content-center border-2 border-dashed border-primary"
@click="clickUpload"
>
<input ref="refAttachmentInput" hidden type="file" @change="uploadImage" />
<input ref="refAttachmentInput" hidden type="file" @change="uploadImage" >
<p>{{ $t("items.drag_and_drop") }}</p>
</button>
</div>

View File

@@ -8,6 +8,6 @@
<template>
<BaseContainer class="flex flex-col gap-8">
<MaintenanceListView :current-item-id="props.item.id"></MaintenanceListView>
<MaintenanceListView :current-item-id="props.item.id" />
</BaseContainer>
</template>

View File

@@ -398,27 +398,27 @@
<PopoverContent class="z-40 flex flex-col gap-2">
<Label class="flex cursor-pointer items-center">
<Switch v-model="includeArchived" class="ml-auto" />
<div class="grow"></div>
<div class="grow" />
{{ $t("items.include_archive") }}
</Label>
<Label class="flex cursor-pointer items-center">
<Switch v-model="fieldSelector" class="ml-auto" />
<div class="grow"></div>
<div class="grow" />
{{ $t("items.field_selector") }}
</Label>
<Label class="flex cursor-pointer items-center">
<Switch v-model="negateLabels" class="ml-auto" />
<div class="grow"></div>
<div class="grow" />
{{ $t("items.negate_labels") }}
</Label>
<Label class="flex cursor-pointer items-center">
<Switch v-model="onlyWithoutPhoto" class="ml-auto" />
<div class="grow"></div>
<div class="grow" />
{{ $t("items.only_without_photo") }}
</Label>
<Label class="flex cursor-pointer items-center">
<Switch v-model="onlyWithPhoto" class="ml-auto" />
<div class="grow"></div>
<div class="grow" />
{{ $t("items.only_with_photo") }}
</Label>
<Label class="flex cursor-pointer flex-col gap-2">
@@ -440,7 +440,7 @@
<Button @click="reset"> {{ $t("items.reset_search") }} </Button>
</PopoverContent>
</Popover>
<div class="grow"></div>
<div class="grow" />
<Popover>
<PopoverTrigger as-child>
<Button size="sm" variant="outline"> {{ $t("items.tips") }}</Button>

View File

@@ -213,8 +213,7 @@
</div>
</header>
<Separator v-if="location && location.description" />
<Markdown v-if="location && location.description" class="mt-3 text-base" :source="location.description">
</Markdown>
<Markdown v-if="location && location.description" class="mt-3 text-base" :source="location.description" />
</Card>
<section v-if="location && items">
<ItemViewSelectable :items="items" />

View File

@@ -15,7 +15,7 @@
<div>
<BaseContainer class="flex flex-col gap-4">
<BaseSectionHeader> {{ $t("menu.maintenance") }} </BaseSectionHeader>
<MaintenanceListView></MaintenanceListView>
<MaintenanceListView />
</BaseContainer>
</div>
</template>

View File

@@ -360,7 +360,7 @@
<Button variant="secondary" :disabled="!(notifier && notifier.url)" type="button" @click="testNotifier">
{{ $t("profile.test") }}
</Button>
<div class="grow"></div>
<div class="grow" />
<Button type="submit"> {{ $t("global.submit") }} </Button>
</DialogFooter>
</div>

View File

@@ -344,12 +344,12 @@
<p>
{{ $t("reports.label_generator.instruction_2") }}
</p>
<p v-html="DOMPurify.sanitize($t('reports.label_generator.instruction_3'))"></p>
<p v-html="DOMPurify.sanitize($t('reports.label_generator.instruction_3'))" />
<h2>{{ $t("reports.label_generator.tips") }}</h2>
<ul>
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_1'))"></li>
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_2'))"></li>
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_3'))"></li>
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_1'))" />
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_2'))" />
<li v-html="DOMPurify.sanitize($t('reports.label_generator.tip_3'))" />
</ul>
<div class="flex flex-wrap gap-2">
<NuxtLink href="/tools">{{ $t("menu.tools") }}</NuxtLink>
@@ -435,7 +435,7 @@
width: `${out.card.height * 0.9}${out.measure}`,
height: `${out.card.height * 0.9}${out.measure}`,
}"
/>
>
</div>
<div class="ml-2 flex flex-col justify-center">
<div class="font-bold">{{ item.assetID }}</div>

View File

@@ -40,7 +40,7 @@
<DetailAction @action="openDialog(DialogID.Import)">
<template #title> {{ $t("tools.import_export_set.import") }} </template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="DOMPurify.sanitize($t('tools.import_export_set.import_sub'))"></div>
<div v-html="DOMPurify.sanitize($t('tools.import_export_set.import_sub'))" />
<template #button> {{ $t("tools.import_export_set.import_button") }} </template>
</DetailAction>
<DetailAction @action="getExportCSV()">
@@ -57,7 +57,7 @@
<span> {{ $t("tools.actions") }} </span>
<template #description>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="DOMPurify.sanitize($t('tools.actions_sub'))"></div>
<div v-html="DOMPurify.sanitize($t('tools.actions_sub'))" />
</template>
</BaseSectionHeader>
</template>
@@ -75,19 +75,19 @@
<DetailAction @action="resetItemDateTimes">
<template #title> {{ $t("tools.actions_set.zero_datetimes") }} </template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="DOMPurify.sanitize($t('tools.actions_set.zero_datetimes_sub'))"></div>
<div v-html="DOMPurify.sanitize($t('tools.actions_set.zero_datetimes_sub'))" />
<template #button> {{ $t("tools.actions_set.zero_datetimes_button") }} </template>
</DetailAction>
<DetailAction @action="setPrimaryPhotos">
<template #title> {{ $t("tools.actions_set.set_primary_photo") }} </template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="DOMPurify.sanitize($t('tools.actions_set.set_primary_photo_sub'))"></div>
<div v-html="DOMPurify.sanitize($t('tools.actions_set.set_primary_photo_sub'))" />
<template #button> {{ $t("tools.actions_set.set_primary_photo_button") }} </template>
</DetailAction>
<DetailAction @action="createMissingThumbnails">
<template #title> {{ $t("tools.actions_set.create_missing_thumbnails") }} </template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="DOMPurify.sanitize($t('tools.actions_set.create_missing_thumbnails_sub'))"></div>
<div v-html="DOMPurify.sanitize($t('tools.actions_set.create_missing_thumbnails_sub'))" />
<template #button> {{ $t("tools.actions_set.create_missing_thumbnails_button") }} </template>
</DetailAction>
</div>

View File

@@ -49,7 +49,7 @@ export const messages = () => {
};
export const messageCompiler: (
message: String | any,
message: string | any,
{
locale,
key,

10387
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
try {
console.log('Setting theme');
const theme = JSON.parse(
localStorage.getItem('homebox/preferences/location')
).theme;
console.log("Setting theme");
const theme = JSON.parse(localStorage.getItem("homebox/preferences/location")).theme;
if (theme) {
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.classList.add('theme-' + theme);
document.documentElement.setAttribute("data-theme", theme);
document.documentElement.classList.add("theme-" + theme);
}
} catch (e) {
console.error('Failed to set theme', e);
console.error("Failed to set theme", e);
}

View File

@@ -15,5 +15,8 @@
"devDependencies": {
"vitepress": "^1.6.3"
},
"packageManager": "pnpm@9.1.4+sha512.9df9cf27c91715646c7d675d1c9c8e41f6fce88246f1318c1aa6a1ed1aeb3c4f032fcdf4ba63cc69c4fe6d634279176b5358727d8f2cc1e65b65f43ce2f8bfb0"
"packageManager": "pnpm@9.1.4+sha512.9df9cf27c91715646c7d675d1c9c8e41f6fce88246f1318c1aa6a1ed1aeb3c4f032fcdf4ba63cc69c4fe6d634279176b5358727d8f2cc1e65b65f43ce2f8bfb0",
"dependencies": {
"nuxt": "^4.0.0"
}
}

7827
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff