@@ -188,16 +188,17 @@
Sidebar,
SidebarContent,
SidebarFooter,
- SidebarHeader,
- SidebarInset,
- SidebarRail,
- SidebarTrigger,
SidebarGroup,
SidebarGroupLabel,
+ SidebarHeader,
+ SidebarInset,
SidebarMenu,
- SidebarMenuItem,
SidebarMenuButton,
+ SidebarMenuItem,
SidebarMenuLink,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
} from "@/components/ui/sidebar";
import {
DropdownMenu,
@@ -211,6 +212,18 @@
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";
+
+ import LabelCreateModal from "~/components/Label/CreateModal.vue";
+ import LocationCreateModal from "~/components/Location/CreateModal.vue";
+ import ItemBarcodeModal from "~/components/Item/BarcodeModal.vue";
+ import AppQuickMenuModal from "~/components/App/QuickMenuModal.vue";
+ import AppScannerModal from "~/components/App/ScannerModal.vue";
+ import AppLogo from "~/components/App/Logo.vue";
+ import AppHeaderDecor from "~/components/App/HeaderDecor.vue";
+ import AppHeaderText from "~/components/App/HeaderText.vue";
const { t, locale } = useI18n();
const username = computed(() => authCtx.user?.name || "User");
@@ -344,7 +357,7 @@
...dropdown.map(v => ({
text: computed(() => v.name.value),
dialogId: v.dialogId as NoParamDialogIDs,
- shortcut: v.shortcut.split("+")[1],
+ shortcut: v.shortcut.split("+")[1] as string,
type: "create" as const,
})),
...nav.map(v => ({
diff --git a/frontend/layouts/empty.vue b/frontend/layouts/empty.vue
index dbf13790..4898c517 100644
--- a/frontend/layouts/empty.vue
+++ b/frontend/layouts/empty.vue
@@ -1,4 +1,6 @@
-
+
diff --git a/frontend/lib/api/__test__/factories/index.ts b/frontend/lib/api/__test__/factories/index.ts
index 0cba8501..0611a7b9 100644
--- a/frontend/lib/api/__test__/factories/index.ts
+++ b/frontend/lib/api/__test__/factories/index.ts
@@ -9,7 +9,7 @@ import { Requests } from "../../../requests";
function itemField(id = null): ItemField {
return {
- // @ts-expect-error
+ // @ts-expect-error - not actually an issue
id,
name: faker.lorem.word(),
type: "text",
@@ -45,7 +45,7 @@ function label(): LabelCreate {
return {
name: faker.lorem.word(),
description: faker.lorem.sentence(),
- color: faker.internet.color(),
+ color: faker.color.rgb(),
};
}
diff --git a/frontend/lib/api/__test__/public.test.ts b/frontend/lib/api/__test__/public.test.ts
index 09a1aa16..5dd64e5e 100644
--- a/frontend/lib/api/__test__/public.test.ts
+++ b/frontend/lib/api/__test__/public.test.ts
@@ -1,4 +1,4 @@
-import { describe, test, expect } from "vitest";
+import { describe, expect, test } from "vitest";
import { factories } from "./factories";
describe("[GET] /api/v1/status", () => {
diff --git a/frontend/lib/api/__test__/user/group.test.ts b/frontend/lib/api/__test__/user/group.test.ts
index 4ad82b21..88b31356 100644
--- a/frontend/lib/api/__test__/user/group.test.ts
+++ b/frontend/lib/api/__test__/user/group.test.ts
@@ -1,5 +1,5 @@
import { faker } from "@faker-js/faker";
-import { describe, test, expect } from "vitest";
+import { describe, expect, test } from "vitest";
import { factories } from "../factories";
import { sharedUserClient } from "../test-utils";
diff --git a/frontend/lib/api/__test__/user/items.test.ts b/frontend/lib/api/__test__/user/items.test.ts
index 3a50f61e..87c3e894 100644
--- a/frontend/lib/api/__test__/user/items.test.ts
+++ b/frontend/lib/api/__test__/user/items.test.ts
@@ -1,5 +1,5 @@
import { faker } from "@faker-js/faker";
-import { describe, test, expect } from "vitest";
+import { describe, expect, test } from "vitest";
import type { ItemField, ItemUpdate, LocationOut } from "../../types/data-contracts";
import { AttachmentTypes } from "../../types/non-generated";
import type { UserClient } from "../../user";
@@ -55,9 +55,9 @@ describe("user should be able to create an item and add an attachment", () => {
expect(itmResp.status).toBe(200);
expect(data.attachments).toHaveLength(1);
- expect(data.attachments[0].title).toBe("test.txt");
+ expect(data.attachments[0]?.title).toBe("test.txt");
- const resp = await api.items.attachments.delete(data.id, data.attachments[0].id);
+ const resp = await api.items.attachments.delete(data.id, data.attachments[0]!.id);
expect(resp.response.status).toBe(204);
api.items.delete(item.id);
@@ -100,21 +100,21 @@ describe("user should be able to create an item and add an attachment", () => {
expect(item2.fields).toHaveLength(fields.length);
for (let i = 0; i < fields.length; i++) {
- expect(item2.fields[i].name).toBe(fields[i].name);
- expect(item2.fields[i].textValue).toBe(fields[i].textValue);
- expect(item2.fields[i].numberValue).toBe(fields[i].numberValue);
+ expect(item2.fields[i]?.name).toBe(fields[i]!.name);
+ expect(item2.fields[i]?.textValue).toBe(fields[i]!.textValue);
+ expect(item2.fields[i]?.numberValue).toBe(fields[i]!.numberValue);
}
- itemUpdate.fields = [fields[0], fields[1]];
+ itemUpdate.fields = [fields[0]!, fields[1]!];
const { response: updateResponse2, data: item3 } = await api.items.update(item.id, itemUpdate as ItemUpdate);
expect(updateResponse2.status).toBe(200);
expect(item3.fields).toHaveLength(2);
for (let i = 0; i < item3.fields.length; i++) {
- expect(item3.fields[i].name).toBe(itemUpdate.fields[i].name);
- expect(item3.fields[i].textValue).toBe(itemUpdate.fields[i].textValue);
- expect(item3.fields[i].numberValue).toBe(itemUpdate.fields[i].numberValue);
+ expect(item3.fields[i]?.name).toBe(itemUpdate.fields[i]!.name);
+ expect(item3.fields[i]?.textValue).toBe(itemUpdate.fields[i]!.textValue);
+ expect(item3.fields[i]?.numberValue).toBe(itemUpdate.fields[i]!.numberValue);
}
cleanup();
@@ -168,7 +168,7 @@ describe("user should be able to create an item and add an attachment", () => {
// Skip first one
const { response, data: loc } = await api.locations.create({
parentId: lastLocationId,
- name: locations[i],
+ name: locations[i]!,
description: "",
});
expect(response.status).toBe(201);
diff --git a/frontend/lib/api/__test__/user/stats.test.ts b/frontend/lib/api/__test__/user/stats.test.ts
index 51f4e2e1..baea7da9 100644
--- a/frontend/lib/api/__test__/user/stats.test.ts
+++ b/frontend/lib/api/__test__/user/stats.test.ts
@@ -28,7 +28,7 @@ type ImportObj = {
};
function toCsv(data: ImportObj[]): string {
- const headers = Object.keys(data[0]).join("\t");
+ const headers = Object.keys(data[0]!).join("\t");
const rows = data.map(row => {
return Object.values(row).join("\t");
});
diff --git a/frontend/lib/api/base/base-api.ts b/frontend/lib/api/base/base-api.ts
index b9d01ae5..b853342c 100644
--- a/frontend/lib/api/base/base-api.ts
+++ b/frontend/lib/api/base/base-api.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Requests } from "../../requests";
import { route } from ".";
@@ -47,7 +48,7 @@ export function parseDate(obj: T, keys: Array = []): T {
throw new Error(`Invalid date format: ${value}`);
}
- const [year, month, day] = split;
+ const [year, month, day] = split as [string, string, string];
const dt = new Date();
@@ -90,9 +91,9 @@ export class BaseAPI {
protected dropFields(obj: T, keys: Array = []): T {
const result = { ...obj };
[...keys, "createdAt", "updatedAt"].forEach(key => {
- // @ts-ignore - we are checking for the key above
+ // @ts-expect-error - TS doesn't know that we're checking for the key above
if (hasKey(result, key)) {
- // @ts-ignore - we are guarding against this above
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete result[key];
}
});
diff --git a/frontend/lib/api/classes/locations.ts b/frontend/lib/api/classes/locations.ts
index 0826611f..ec9dfb4e 100644
--- a/frontend/lib/api/classes/locations.ts
+++ b/frontend/lib/api/classes/locations.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base";
-import type { LocationOutCount, LocationCreate, LocationOut, LocationUpdate, TreeItem } from "../types/data-contracts";
+import type { LocationCreate, LocationOut, LocationOutCount, LocationUpdate, TreeItem } from "../types/data-contracts";
export type LocationsQuery = {
filterChildren: boolean;
diff --git a/frontend/lib/api/classes/maintenance.ts b/frontend/lib/api/classes/maintenance.ts
index 87b29062..22e9b079 100644
--- a/frontend/lib/api/classes/maintenance.ts
+++ b/frontend/lib/api/classes/maintenance.ts
@@ -1,8 +1,8 @@
import { BaseAPI, route } from "../base";
import type {
MaintenanceEntry,
- MaintenanceEntryWithDetails,
MaintenanceEntryUpdate,
+ MaintenanceEntryWithDetails,
MaintenanceFilterStatus,
} from "../types/data-contracts";
diff --git a/frontend/lib/datelib/datelib.test.ts b/frontend/lib/datelib/datelib.test.ts
index d2353bed..34f96f9c 100644
--- a/frontend/lib/datelib/datelib.test.ts
+++ b/frontend/lib/datelib/datelib.test.ts
@@ -1,5 +1,5 @@
-import { describe, test, expect } from "vitest";
-import { format, zeroTime, factorRange, parse } from "./datelib";
+import { describe, expect, test } from "vitest";
+import { factorRange, format, parse, zeroTime } from "./datelib";
describe("format", () => {
test("should format a date as a string", () => {
diff --git a/frontend/lib/datelib/datelib.ts b/frontend/lib/datelib/datelib.ts
index b87922c1..33f7674d 100644
--- a/frontend/lib/datelib/datelib.ts
+++ b/frontend/lib/datelib/datelib.ts
@@ -7,7 +7,7 @@ export function format(date: Date | string): string {
if (typeof date === "string") {
return date;
}
- return date.toISOString().split("T")[0];
+ return date.toISOString().split("T")[0]!;
}
export function zeroTime(date: Date): Date {
@@ -31,6 +31,6 @@ export function factory(offset = 0): Date {
}
export function parse(yyyyMMdd: string): Date {
- const parts = yyyyMMdd.split("-");
+ const parts = yyyyMMdd.split("-") as [string, string, string];
return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
}
diff --git a/frontend/lib/passwords/index.test.ts b/frontend/lib/passwords/index.test.ts
index 50c1b52f..3faea612 100644
--- a/frontend/lib/passwords/index.test.ts
+++ b/frontend/lib/passwords/index.test.ts
@@ -1,4 +1,4 @@
-import { describe, test, expect } from "vitest";
+import { describe, expect, test } from "vitest";
import { scorePassword } from ".";
describe("scorePassword tests", () => {
diff --git a/frontend/lib/passwords/index.ts b/frontend/lib/passwords/index.ts
index 27a3c451..ad682484 100644
--- a/frontend/lib/passwords/index.ts
+++ b/frontend/lib/passwords/index.ts
@@ -23,8 +23,8 @@ export function scorePassword(pass: string): number {
const letters: { [key: string]: number } = {};
for (let i = 0; i < pass.length; i++) {
- letters[pass[i]] = (letters[pass[i]] || 0) + 1;
- score += 5.0 / letters[pass[i]];
+ letters[pass[i]!] = (letters[pass[i]!] || 0) + 1;
+ score += 5.0 / letters[pass[i]!]!;
}
// bonus points for mixing it up
diff --git a/frontend/lib/requests/requests.ts b/frontend/lib/requests/requests.ts
index 8aecda1f..fed69986 100644
--- a/frontend/lib/requests/requests.ts
+++ b/frontend/lib/requests/requests.ts
@@ -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)) {
diff --git a/frontend/lib/utils.ts b/frontend/lib/utils.ts
index 13aac2ad..a3a1a7cf 100644
--- a/frontend/lib/utils.ts
+++ b/frontend/lib/utils.ts
@@ -25,7 +25,7 @@ export function getContrastTextColor(bgColor: string): string {
g = parseInt(hex.slice(2, 4), 16);
b = parseInt(hex.slice(4, 6), 16);
} else if (bgColor.startsWith("rgb")) {
- const match = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
+ const match = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/) as [string, string, string, string];
if (match) {
r = parseInt(match[1]);
g = parseInt(match[2]);
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index a3e320d1..ae6b57f4 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -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": {
diff --git a/frontend/package.json b/frontend/package.json
index 8163cb88..b9cf4f5b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -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"
}
}
diff --git a/frontend/pages/assets/[id].vue b/frontend/pages/assets/[id].vue
index 3dcf8ced..eacc6a4a 100644
--- a/frontend/pages/assets/[id].vue
+++ b/frontend/pages/assets/[id].vue
@@ -1,6 +1,9 @@