+
+
+
diff --git a/frontend/components/Chart/Line.vue b/frontend/components/Chart/Line.vue
new file mode 100644
index 0000000..c36ef93
--- /dev/null
+++ b/frontend/components/Chart/Line.vue
@@ -0,0 +1,113 @@
+
+
+ {{ styles }}
+
+
+
+
+
+
+
diff --git a/frontend/components/Form/Autocomplete2.vue b/frontend/components/Form/Autocomplete2.vue
index d9fe64a..b71c30e 100644
--- a/frontend/components/Form/Autocomplete2.vue
+++ b/frontend/components/Form/Autocomplete2.vue
@@ -10,16 +10,8 @@
class="w-full input input-bordered"
@change="search = $event.target.value"
/>
-
-
-
-
+
-
+
@@ -61,7 +53,6 @@
diff --git a/frontend/components/Form/Password.vue b/frontend/components/Form/Password.vue
index 6303c0c..91b59c8 100644
--- a/frontend/components/Form/Password.vue
+++ b/frontend/components/Form/Password.vue
@@ -1,23 +1,21 @@
-
+
-
+
+
+
+
+
+ {{ name }}
+
diff --git a/frontend/components/Item/AttachmentsList.vue b/frontend/components/Item/AttachmentsList.vue
index 69176ee..ddad600 100644
--- a/frontend/components/Item/AttachmentsList.vue
+++ b/frontend/components/Item/AttachmentsList.vue
@@ -6,15 +6,15 @@
class="flex items-center justify-between py-3 pl-3 pr-4 text-sm"
>
-
+
{{ attachment.document.title }}
@@ -22,10 +22,7 @@
diff --git a/frontend/components/Location/Card.vue b/frontend/components/Location/Card.vue
index c29bed8..d0b6543 100644
--- a/frontend/components/Location/Card.vue
+++ b/frontend/components/Location/Card.vue
@@ -13,13 +13,18 @@
>
-
-
+
+
{{ location.name }}
-
+
{{ count }}
@@ -28,9 +33,7 @@
diff --git a/frontend/components/Location/Selector.vue b/frontend/components/Location/Selector.vue
index bcba455..82b7954 100644
--- a/frontend/components/Location/Selector.vue
+++ b/frontend/components/Location/Selector.vue
@@ -8,7 +8,7 @@
v-if="selected"
:class="['absolute inset-y-0 right-0 flex items-center pr-4', active ? 'text-white' : 'text-primary']"
>
-
+
@@ -20,10 +20,8 @@
+
+
diff --git a/frontend/components/global/DateTime.vue b/frontend/components/global/DateTime.vue
index b23cac2..0388a67 100644
--- a/frontend/components/global/DateTime.vue
+++ b/frontend/components/global/DateTime.vue
@@ -3,7 +3,7 @@
diff --git a/frontend/components/global/DetailsSection/DetailsSection.vue b/frontend/components/global/DetailsSection/DetailsSection.vue
index dd4faf8..112970f 100644
--- a/frontend/components/global/DetailsSection/DetailsSection.vue
+++ b/frontend/components/global/DetailsSection/DetailsSection.vue
@@ -16,7 +16,7 @@
@@ -33,12 +33,7 @@
v-if="detail.copyable"
class="opacity-0 group-hover:opacity-100 ml-4 my-0 duration-75 transition-opacity"
>
-
+
@@ -51,7 +46,6 @@
diff --git a/frontend/components/global/StatCard/StatCard.vue b/frontend/components/global/StatCard/StatCard.vue
index e4cf290..37a386f 100644
--- a/frontend/components/global/StatCard/StatCard.vue
+++ b/frontend/components/global/StatCard/StatCard.vue
@@ -12,7 +12,7 @@
diff --git a/frontend/lib/api/__test__/factories/index.ts b/frontend/lib/api/__test__/factories/index.ts
index 0cba850..ff821f9 100644
--- a/frontend/lib/api/__test__/factories/index.ts
+++ b/frontend/lib/api/__test__/factories/index.ts
@@ -2,7 +2,7 @@ import { faker } from "@faker-js/faker";
import { expect } from "vitest";
import { overrideParts } from "../../base/urls";
import { PublicApi } from "../../public";
-import type { ItemField, LabelCreate, LocationCreate, UserRegistration } from "../../types/data-contracts";
+import { ItemField, LabelCreate, LocationCreate, UserRegistration } from "../../types/data-contracts";
import * as config from "../../../../test/config";
import { UserClient } from "../../user";
import { Requests } from "../../../requests";
@@ -15,7 +15,7 @@ function itemField(id = null): ItemField {
type: "text",
textValue: faker.lorem.sentence(),
booleanValue: false,
- numberValue: faker.number.int(),
+ numberValue: faker.datatype.number(),
timeValue: "",
};
}
@@ -28,7 +28,7 @@ function user(): UserRegistration {
return {
email: faker.internet.email(),
password: faker.internet.password(),
- name: faker.person.firstName(),
+ name: faker.name.firstName(),
token: "",
};
}
@@ -36,7 +36,7 @@ function user(): UserRegistration {
function location(parentId: string | null = null): LocationCreate {
return {
parentId,
- name: faker.location.city(),
+ name: faker.address.city(),
description: faker.lorem.sentence(),
};
}
diff --git a/frontend/lib/api/__test__/test-utils.ts b/frontend/lib/api/__test__/test-utils.ts
index 673773d..41ef871 100644
--- a/frontend/lib/api/__test__/test-utils.ts
+++ b/frontend/lib/api/__test__/test-utils.ts
@@ -1,6 +1,5 @@
import { beforeAll, expect } from "vitest";
-import { faker } from "@faker-js/faker";
-import type { UserClient } from "../user";
+import { UserClient } from "../user";
import { factories } from "./factories";
const cache = {
@@ -16,9 +15,9 @@ export async function sharedUserClient(): Promise
{
return factories.client.user(cache.token);
}
const testUser = {
- email: faker.internet.email(),
- name: faker.person.fullName(),
- password: faker.internet.password(),
+ email: "__test__@__test__.com",
+ name: "__test__",
+ password: "__test__",
token: "",
};
diff --git a/frontend/lib/api/__test__/user/group.test.ts b/frontend/lib/api/__test__/user/group.test.ts
index 4ad82b2..181cfd5 100644
--- a/frontend/lib/api/__test__/user/group.test.ts
+++ b/frontend/lib/api/__test__/user/group.test.ts
@@ -2,12 +2,13 @@ import { faker } from "@faker-js/faker";
import { describe, test, expect } from "vitest";
import { factories } from "../factories";
import { sharedUserClient } from "../test-utils";
+import { currencies } from "~~/lib/data/currency";
describe("first time user workflow (register, login, join group)", () => {
test("user should be able to update group", async () => {
const { client } = await factories.client.singleUse();
- const name = faker.person.firstName();
+ const name = faker.name.firstName();
const { response, data: group } = await client.group.update({
name,
@@ -28,6 +29,20 @@ describe("first time user workflow (register, login, join group)", () => {
expect(group.currency).toBe("USD");
});
+ test("currencies should be in sync with backend", async () => {
+ const { client } = await factories.client.singleUse();
+
+ for (const currency of currencies) {
+ const { response, data: group } = await client.group.update({
+ name: faker.name.firstName(),
+ currency: currency.code,
+ });
+
+ expect(response.status).toBe(200);
+ expect(group.currency).toBe(currency.code);
+ }
+ });
+
test("user should be able to join create join token and have user signup", async () => {
const api = factories.client.public();
diff --git a/frontend/lib/api/__test__/user/items.test.ts b/frontend/lib/api/__test__/user/items.test.ts
index 4f8973f..304a54e 100644
--- a/frontend/lib/api/__test__/user/items.test.ts
+++ b/frontend/lib/api/__test__/user/items.test.ts
@@ -1,15 +1,15 @@
import { faker } from "@faker-js/faker";
import { describe, test, expect } from "vitest";
-import type { ItemField, ItemUpdate, LocationOut } from "../../types/data-contracts";
+import { ItemField, ItemUpdate, LocationOut } from "../../types/data-contracts";
import { AttachmentTypes } from "../../types/non-generated";
-import type { UserClient } from "../../user";
+import { UserClient } from "../../user";
import { factories } from "../factories";
import { sharedUserClient } from "../test-utils";
describe("user should be able to create an item and add an attachment", () => {
let increment = 0;
/**
- * useLocation sets up a location resource for testing, and returns a function
+ * useLocatio sets up a location resource for testing, and returns a function
* that can be used to delete the location from the backend server.
*/
async function useLocation(api: UserClient): Promise<[LocationOut, () => Promise]> {
@@ -135,9 +135,9 @@ describe("user should be able to create an item and add an attachment", () => {
const { response, data } = await api.items.maintenance.create(item.id, {
name: faker.vehicle.model(),
description: faker.lorem.paragraph(1),
- completedDate: faker.date.past(),
+ completedDate: faker.date.past(1),
scheduledDate: "null",
- cost: faker.number.int(100).toString(),
+ cost: faker.datatype.number(100).toString(),
});
expect(response.status).toBe(201);
@@ -155,42 +155,4 @@ describe("user should be able to create an item and add an attachment", () => {
cleanup();
});
-
- test("full path of item should be retrievable", async () => {
- const api = await sharedUserClient();
- const [location, cleanup] = await useLocation(api);
-
- const locations = [location.name, faker.animal.dog(), faker.animal.cat(), faker.animal.cow(), faker.animal.bear()];
-
- let lastLocationId = location.id;
- for (let i = 1; i < locations.length; i++) {
- // Skip first one
- const { response, data: loc } = await api.locations.create({
- parentId: lastLocationId,
- name: locations[i],
- description: "",
- });
- expect(response.status).toBe(201);
-
- lastLocationId = loc.id;
- }
-
- const { response, data: item } = await api.items.create({
- name: faker.vehicle.model(),
- labelIds: [],
- description: faker.lorem.paragraph(1),
- locationId: lastLocationId,
- });
- expect(response.status).toBe(201);
-
- const { response: pathResponse, data: fullpath } = await api.items.fullpath(item.id);
- expect(pathResponse.status).toBe(200);
-
- const names = fullpath.map(p => p.name);
-
- expect(names).toHaveLength(locations.length + 1);
- expect(names).toEqual([...locations, item.name]);
-
- cleanup();
- });
});
diff --git a/frontend/lib/api/__test__/user/labels.test.ts b/frontend/lib/api/__test__/user/labels.test.ts
index 851f6b1..5564e6b 100644
--- a/frontend/lib/api/__test__/user/labels.test.ts
+++ b/frontend/lib/api/__test__/user/labels.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest";
-import type { LabelOut } from "../../types/data-contracts";
-import type { UserClient } from "../../user";
+import { LabelOut } from "../../types/data-contracts";
+import { UserClient } from "../../user";
import { factories } from "../factories";
import { sharedUserClient } from "../test-utils";
diff --git a/frontend/lib/api/__test__/user/locations.test.ts b/frontend/lib/api/__test__/user/locations.test.ts
index 834a567..3012faf 100644
--- a/frontend/lib/api/__test__/user/locations.test.ts
+++ b/frontend/lib/api/__test__/user/locations.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest";
-import type { LocationOut } from "../../types/data-contracts";
-import type { UserClient } from "../../user";
+import { LocationOut } from "../../types/data-contracts";
+import { UserClient } from "../../user";
import { factories } from "../factories";
import { sharedUserClient } from "../test-utils";
diff --git a/frontend/lib/api/__test__/user/notifier.test.ts b/frontend/lib/api/__test__/user/notifier.test.ts
index 664b85e..ed8e2ca 100644
--- a/frontend/lib/api/__test__/user/notifier.test.ts
+++ b/frontend/lib/api/__test__/user/notifier.test.ts
@@ -8,8 +8,8 @@ describe("basic notifier workflows", () => {
// Create Notifier
const result = await client.notifiers.create({
- name: faker.word.words(2),
- url: "discord://" + faker.string.alphanumeric(10),
+ name: faker.name.firstName(),
+ url: "discord://" + faker.random.alphaNumeric(10),
isActive: true,
});
@@ -22,8 +22,8 @@ describe("basic notifier workflows", () => {
// Update Notifier with new URL
{
const updateData = {
- name: faker.word.words(2),
- url: "discord://" + faker.string.alphanumeric(10),
+ name: faker.name.firstName(),
+ url: "discord://" + faker.random.alphaNumeric(10),
isActive: true,
};
@@ -37,7 +37,7 @@ describe("basic notifier workflows", () => {
// Update Notifier with empty URL
{
const updateData = {
- name: faker.word.words(2),
+ name: faker.name.firstName(),
url: null,
isActive: true,
};
diff --git a/frontend/lib/api/__test__/user/stats.test.ts b/frontend/lib/api/__test__/user/stats.test.ts
index 51f4e2e..a13a03d 100644
--- a/frontend/lib/api/__test__/user/stats.test.ts
+++ b/frontend/lib/api/__test__/user/stats.test.ts
@@ -1,6 +1,6 @@
import { faker } from "@faker-js/faker";
import { beforeAll, describe, expect, test } from "vitest";
-import type { UserClient } from "../../user";
+import { UserClient } from "../../user";
import { factories } from "../factories";
type ImportObj = {
@@ -40,8 +40,8 @@ function importFileGenerator(entries: number): ImportObj[] {
const pick = (arr: string[]) => arr[Math.floor(Math.random() * arr.length)];
- const labels = faker.word.words(5).split(" ").join(";");
- const locations = faker.word.words(3).split(" ");
+ const labels = faker.random.words(5).split(" ").join(";");
+ const locations = faker.random.words(3).split(" ");
const half = Math.floor(entries / 2);
@@ -53,21 +53,21 @@ function importFileGenerator(entries: number): ImportObj[] {
[`HB.import_ref`]: faker.database.mongodbObjectId(),
[`HB.location`]: pick(locations),
[`HB.labels`]: labels,
- [`HB.quantity`]: Number(faker.number.int(2)),
- [`HB.name`]: faker.word.words(3),
+ [`HB.quantity`]: Number(faker.random.numeric(2)),
+ [`HB.name`]: faker.random.words(3),
[`HB.description`]: "",
[`HB.insured`]: faker.datatype.boolean(),
- [`HB.serial_number`]: faker.string.alphanumeric(5),
- [`HB.model_number`]: faker.string.alphanumeric(5),
- [`HB.manufacturer`]: faker.string.alphanumeric(5),
+ [`HB.serial_number`]: faker.random.alphaNumeric(5),
+ [`HB.model_number`]: faker.random.alphaNumeric(5),
+ [`HB.manufacturer`]: faker.random.alphaNumeric(5),
[`HB.notes`]: "",
- [`HB.purchase_from`]: faker.person.fullName(),
- [`HB.purchase_price`]: faker.number.int(100),
+ [`HB.purchase_from`]: faker.name.fullName(),
+ [`HB.purchase_price`]: faker.datatype.number(100),
[`HB.purchase_time`]: faker.date.past().toDateString(),
[`HB.lifetime_warranty`]: half > i,
[`HB.warranty_details`]: "",
- [`HB.sold_to`]: faker.person.fullName(),
- [`HB.sold_price`]: faker.number.int(100),
+ [`HB.sold_to`]: faker.name.fullName(),
+ [`HB.sold_price`]: faker.datatype.number(100),
[`HB.sold_time`]: formatDate(faker.date.past()),
[`HB.sold_notes`]: "",
});
diff --git a/frontend/lib/api/base/base-api.ts b/frontend/lib/api/base/base-api.ts
index b9d01ae..3847a4b 100644
--- a/frontend/lib/api/base/base-api.ts
+++ b/frontend/lib/api/base/base-api.ts
@@ -1,5 +1,4 @@
-import type { Requests } from "../../requests";
-import { route } from ".";
+import { Requests } from "../../requests";
const ZERO_DATE = "0001-01-01T00:00:00Z";
@@ -71,12 +70,12 @@ export class BaseAPI {
this.attachmentToken = attachmentToken;
}
- // if an attachmentToken is present, it will be added to URL as a query param
+ // if a attachmentToken is present it will be added to URL as a query param
// this is done with a simple appending of the query param to the URL. If your
// URL already has a query param, this will not work.
authURL(url: string): string {
if (this.attachmentToken) {
- return route(url, { access_token: this.attachmentToken });
+ return `/api/v1${url}?access_token=${this.attachmentToken}`;
}
return url;
}
diff --git a/frontend/lib/api/base/urls.ts b/frontend/lib/api/base/urls.ts
index 47a1c5b..31e263d 100644
--- a/frontend/lib/api/base/urls.ts
+++ b/frontend/lib/api/base/urls.ts
@@ -11,13 +11,13 @@ export function overrideParts(host: string, prefix: string) {
export type QueryValue = string | string[] | number | number[] | boolean | null | undefined;
/**
- * route is the main URL builder for the API. It will use a predefined host and prefix (global)
- * in the urls.ts file and then append the passed-in path parameter using the `URL` class from the
+ * route is a the main URL builder for the API. It will use a predefined host and prefix (global)
+ * in the urls.ts file and then append the passed in path parameter uring the `URL` class from the
* browser. It will also append any query parameters passed in as the second parameter.
*
* The default host `http://localhost.com` is removed from the path if it is present. This allows us
* to bootstrap the API with different hosts as needed (like for testing) but still allows us to use
- * relative URLs in production because the API and client bundle are served from the same server/host.
+ * relative URLs in pruduction because the API and client bundle are served from the same server/host.
*/
export function route(rest: string, params: Record = {}): string {
const url = new URL(parts.prefix + rest, parts.host);
diff --git a/frontend/lib/api/classes/actions.ts b/frontend/lib/api/classes/actions.ts
index 3975a1d..f30e332 100644
--- a/frontend/lib/api/classes/actions.ts
+++ b/frontend/lib/api/classes/actions.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base";
-import type { ActionAmountResult } from "../types/data-contracts";
+import { ActionAmountResult } from "../types/data-contracts";
export class ActionsAPI extends BaseAPI {
ensureAssetIDs() {
@@ -19,10 +19,4 @@ export class ActionsAPI extends BaseAPI {
url: route("/actions/ensure-import-refs"),
});
}
-
- setPrimaryPhotos() {
- return this.http.post({
- url: route("/actions/set-primary-photos"),
- });
- }
}
diff --git a/frontend/lib/api/classes/assets.ts b/frontend/lib/api/classes/assets.ts
index c22d01b..8820bb3 100644
--- a/frontend/lib/api/classes/assets.ts
+++ b/frontend/lib/api/classes/assets.ts
@@ -1,11 +1,11 @@
import { BaseAPI, route } from "../base";
-import type { ItemSummary } from "../types/data-contracts";
-import type { PaginationResult } from "../types/non-generated";
+import { ItemSummary } from "../types/data-contracts";
+import { PaginationResult } from "../types/non-generated";
export class AssetsApi extends BaseAPI {
async get(id: string, page = 1, pageSize = 50) {
return await this.http.get>({
- url: route(`/assets/${id}`, { page, pageSize }),
+ url: route(`/asset/${id}`, { page, pageSize }),
});
}
}
diff --git a/frontend/lib/api/classes/group.ts b/frontend/lib/api/classes/group.ts
index a33dbf9..7468f09 100644
--- a/frontend/lib/api/classes/group.ts
+++ b/frontend/lib/api/classes/group.ts
@@ -1,11 +1,5 @@
import { BaseAPI, route } from "../base";
-import type {
- CurrenciesCurrency,
- Group,
- GroupInvitation,
- GroupInvitationCreate,
- GroupUpdate,
-} from "../types/data-contracts";
+import { Group, GroupInvitation, GroupInvitationCreate, GroupUpdate } from "../types/data-contracts";
export class GroupApi extends BaseAPI {
createInvitation(data: GroupInvitationCreate) {
@@ -27,10 +21,4 @@ export class GroupApi extends BaseAPI {
url: route("/groups"),
});
}
-
- currencies() {
- return this.http.get({
- url: route("/currencies"),
- });
- }
}
diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts
index a5d3f2e..29403a2 100644
--- a/frontend/lib/api/classes/items.ts
+++ b/frontend/lib/api/classes/items.ts
@@ -1,11 +1,9 @@
import { BaseAPI, route } from "../base";
import { parseDate } from "../base/base-api";
-import type {
+import {
ItemAttachmentUpdate,
ItemCreate,
ItemOut,
- ItemPatch,
- ItemPath,
ItemSummary,
ItemUpdate,
MaintenanceEntry,
@@ -13,28 +11,24 @@ import type {
MaintenanceEntryUpdate,
MaintenanceLog,
} from "../types/data-contracts";
-import type { AttachmentTypes, PaginationResult } from "../types/non-generated";
-import type { Requests } from "~~/lib/requests";
+import { AttachmentTypes, PaginationResult } from "../types/non-generated";
+import { Requests } from "~~/lib/requests";
export type ItemsQuery = {
- orderBy?: string;
includeArchived?: boolean;
page?: number;
pageSize?: number;
locations?: string[];
labels?: string[];
- parentIds?: string[];
q?: string;
fields?: string[];
};
export class AttachmentsAPI extends BaseAPI {
- add(id: string, file: File | Blob, filename: string, type: AttachmentTypes | null = null) {
+ add(id: string, file: File | Blob, filename: string, type: AttachmentTypes) {
const formData = new FormData();
formData.append("file", file);
- if (type) {
- formData.append("type", type);
- }
+ formData.append("type", type);
formData.append("name", filename);
return this.http.post({
@@ -106,10 +100,6 @@ export class ItemsApi extends BaseAPI {
this.maintenance = new MaintenanceAPI(http);
}
- fullpath(id: string) {
- return this.http.get({ url: route(`/items/${id}/path`) });
- }
-
getAll(q: ItemsQuery = {}) {
return this.http.get>({ url: route("/items", q) });
}
@@ -147,20 +137,6 @@ export class ItemsApi extends BaseAPI {
return payload;
}
- async patch(id: string, item: ItemPatch) {
- const resp = await this.http.patch({
- url: route(`/items/${id}`),
- body: this.dropFields(item),
- });
-
- if (!resp.data) {
- return resp;
- }
-
- resp.data = parseDate(resp.data, ["purchaseTime", "soldTime", "warrantyExpires"]);
- return resp;
- }
-
import(file: File | Blob) {
const formData = new FormData();
formData.append("csv", file);
diff --git a/frontend/lib/api/classes/labels.ts b/frontend/lib/api/classes/labels.ts
index 6f7eaf0..61bcab7 100644
--- a/frontend/lib/api/classes/labels.ts
+++ b/frontend/lib/api/classes/labels.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base";
-import type { LabelCreate, LabelOut } from "../types/data-contracts";
+import { LabelCreate, LabelOut } from "../types/data-contracts";
export class LabelsApi extends BaseAPI {
getAll() {
diff --git a/frontend/lib/api/classes/locations.ts b/frontend/lib/api/classes/locations.ts
index 0826611..acbbcdb 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 { LocationOutCount, LocationCreate, LocationOut, LocationUpdate, TreeItem } from "../types/data-contracts";
export type LocationsQuery = {
filterChildren: boolean;
diff --git a/frontend/lib/api/classes/notifiers.ts b/frontend/lib/api/classes/notifiers.ts
index 37044c0..c9e4a25 100644
--- a/frontend/lib/api/classes/notifiers.ts
+++ b/frontend/lib/api/classes/notifiers.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base";
-import type { NotifierCreate, NotifierOut, NotifierUpdate } from "../types/data-contracts";
+import { NotifierCreate, NotifierOut, NotifierUpdate } from "../types/data-contracts";
export class NotifiersAPI extends BaseAPI {
getAll() {
diff --git a/frontend/lib/api/classes/stats.ts b/frontend/lib/api/classes/stats.ts
index f6e5cec..7959269 100644
--- a/frontend/lib/api/classes/stats.ts
+++ b/frontend/lib/api/classes/stats.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base";
-import type { GroupStatistics, TotalsByOrganizer, ValueOverTime } from "../types/data-contracts";
+import { GroupStatistics, TotalsByOrganizer, ValueOverTime } from "../types/data-contracts";
function YYYY_MM_DD(date?: Date): string {
if (!date) {
diff --git a/frontend/lib/api/classes/users.ts b/frontend/lib/api/classes/users.ts
index 292fa7e..21006d0 100644
--- a/frontend/lib/api/classes/users.ts
+++ b/frontend/lib/api/classes/users.ts
@@ -1,6 +1,6 @@
import { BaseAPI, route } from "../base";
-import type { ChangePassword, UserOut } from "../types/data-contracts";
-import type { Result } from "../types/non-generated";
+import { ChangePassword, UserOut } from "../types/data-contracts";
+import { Result } from "../types/non-generated";
export class UserApi extends BaseAPI {
public self() {
diff --git a/frontend/lib/api/public.ts b/frontend/lib/api/public.ts
index 513e492..ae8735a 100644
--- a/frontend/lib/api/public.ts
+++ b/frontend/lib/api/public.ts
@@ -1,5 +1,5 @@
import { BaseAPI, route } from "./base";
-import type { APISummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts";
+import { ApiSummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts";
export type StatusResult = {
health: boolean;
@@ -10,7 +10,7 @@ export type StatusResult = {
export class PublicApi extends BaseAPI {
public status() {
- return this.http.get({ url: route("/status") });
+ return this.http.get({ url: route("/status") });
}
public login(username: string, password: string, stayLoggedIn = false) {
diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts
index 384ffb6..98fad70 100644
--- a/frontend/lib/api/types/data-contracts.ts
+++ b/frontend/lib/api/types/data-contracts.ts
@@ -10,11 +10,9 @@
* ---------------------------------------------------------------
*/
-export interface CurrenciesCurrency {
- code: string;
- local: string;
- name: string;
- symbol: string;
+export interface MidErrorResponse {
+ error: string;
+ fields: Record;
}
export interface DocumentOut {
@@ -49,13 +47,11 @@ export interface ItemAttachment {
createdAt: Date | string;
document: DocumentOut;
id: string;
- primary: boolean;
type: string;
updatedAt: Date | string;
}
export interface ItemAttachmentUpdate {
- primary: boolean;
title: string;
type: string;
}
@@ -71,7 +67,7 @@ export interface ItemCreate {
* @maxLength 255
*/
name: string;
- parentId?: string | null;
+ parentId: string | null;
}
export interface ItemField {
@@ -88,23 +84,23 @@ export interface ItemOut {
/** @example "0" */
assetId: string;
attachments: ItemAttachment[];
+ children: ItemSummary[];
createdAt: Date | string;
description: string;
fields: ItemField[];
id: string;
- imageId: string;
insured: boolean;
labels: LabelSummary[];
/** Warranty */
lifetimeWarranty: boolean;
/** Edges */
- location?: LocationSummary | null;
+ location: LocationSummary | null;
manufacturer: string;
modelNumber: string;
name: string;
/** Extras */
notes: string;
- parent?: ItemSummary | null;
+ parent: ItemSummary | null;
purchaseFrom: string;
/** @example "0" */
purchasePrice: string;
@@ -123,27 +119,15 @@ export interface ItemOut {
warrantyExpires: Date | string;
}
-export interface ItemPatch {
- id: string;
- quantity?: number | null;
-}
-
-export interface ItemPath {
- id: string;
- name: string;
- type: ItemType;
-}
-
export interface ItemSummary {
archived: boolean;
createdAt: Date | string;
description: string;
id: string;
- imageId: string;
insured: boolean;
labels: LabelSummary[];
/** Edges */
- location?: LocationSummary | null;
+ location: LocationSummary | null;
name: string;
/** @example "0" */
purchasePrice: string;
@@ -151,11 +135,6 @@ export interface ItemSummary {
updatedAt: Date | string;
}
-export enum ItemType {
- ItemTypeLocation = "location",
- ItemTypeItem = "item",
-}
-
export interface ItemUpdate {
archived: boolean;
assetId: string;
@@ -173,7 +152,7 @@ export interface ItemUpdate {
name: string;
/** Extras */
notes: string;
- parentId?: string | null;
+ parentId: string | null;
purchaseFrom: string;
/** @example "0" */
purchasePrice: string;
@@ -189,6 +168,7 @@ export interface ItemUpdate {
soldTime: Date | string;
soldTo: string;
warrantyDetails: string;
+ /** Sold */
warrantyExpires: Date | string;
}
@@ -207,6 +187,7 @@ export interface LabelOut {
createdAt: Date | string;
description: string;
id: string;
+ items: ItemSummary[];
name: string;
updatedAt: Date | string;
}
@@ -222,7 +203,7 @@ export interface LabelSummary {
export interface LocationCreate {
description: string;
name: string;
- parentId?: string | null;
+ parentId: string | null;
}
export interface LocationOut {
@@ -230,6 +211,7 @@ export interface LocationOut {
createdAt: Date | string;
description: string;
id: string;
+ items: ItemSummary[];
name: string;
parent: LocationSummary;
updatedAt: Date | string;
@@ -256,34 +238,40 @@ export interface LocationUpdate {
description: string;
id: string;
name: string;
- parentId?: string | null;
+ parentId: string | null;
}
export interface MaintenanceEntry {
+ /** Sold */
completedDate: Date | string;
/** @example "0" */
cost: string;
description: string;
id: string;
name: string;
+ /** Sold */
scheduledDate: Date | string;
}
export interface MaintenanceEntryCreate {
+ /** Sold */
completedDate: Date | string;
/** @example "0" */
cost: string;
description: string;
name: string;
+ /** Sold */
scheduledDate: Date | string;
}
export interface MaintenanceEntryUpdate {
+ /** Sold */
completedDate: Date | string;
/** @example "0" */
cost: string;
description: string;
name: string;
+ /** Sold */
scheduledDate: Date | string;
}
@@ -321,7 +309,7 @@ export interface NotifierUpdate {
* @maxLength 255
*/
name: string;
- url?: string | null;
+ url: string | null;
}
export interface PaginationResultItemSummary {
@@ -380,7 +368,11 @@ export interface UserRegistration {
token: string;
}
-export interface APISummary {
+export interface ActionAmountResult {
+ completed: number;
+}
+
+export interface ApiSummary {
allowRegistration: boolean;
build: Build;
demo: boolean;
@@ -390,10 +382,6 @@ export interface APISummary {
versions: string[];
}
-export interface ActionAmountResult {
- completed: number;
-}
-
export interface Build {
buildTime: string;
commit: string;
@@ -439,8 +427,3 @@ export interface TokenResponse {
export interface Wrapped {
item: any;
}
-
-export interface ValidateErrorResponse {
- error: string;
- fields: string;
-}
diff --git a/frontend/lib/api/user.ts b/frontend/lib/api/user.ts
index d477d48..1107acd 100644
--- a/frontend/lib/api/user.ts
+++ b/frontend/lib/api/user.ts
@@ -9,7 +9,7 @@ import { StatsAPI } from "./classes/stats";
import { AssetsApi } from "./classes/assets";
import { ReportsAPI } from "./classes/reports";
import { NotifiersAPI } from "./classes/notifiers";
-import type { Requests } from "~~/lib/requests";
+import { Requests } from "~~/lib/requests";
export class UserClient extends BaseAPI {
locations: LocationsApi;
diff --git a/frontend/lib/data/currency.ts b/frontend/lib/data/currency.ts
new file mode 100644
index 0000000..90b56fb
--- /dev/null
+++ b/frontend/lib/data/currency.ts
@@ -0,0 +1,137 @@
+export type Codes =
+ | "AUD"
+ | "BGN"
+ | "CHF"
+ | "CZK"
+ | "DKK"
+ | "EUR"
+ | "GBP"
+ | "INR"
+ | "JPY"
+ | "NOK"
+ | "NZD"
+ | "PLN"
+ | "RMB"
+ | "RON"
+ | "SEK"
+ | "TRY"
+ | "USD"
+ | "ZAR";
+
+export type Currency = {
+ code: Codes;
+ local: string;
+ symbol: string;
+ name: string;
+};
+
+export const currencies: Currency[] = [
+ {
+ code: "AUD",
+ local: "en-AU",
+ symbol: "$",
+ name: "Australian Dollar",
+ },
+ {
+ code: "GBP",
+ local: "en-GB",
+ symbol: "£",
+ name: "British Pound",
+ },
+ {
+ code: "RMB",
+ local: "zh-CN",
+ symbol: "¥",
+ name: "Chinese Yuan",
+ },
+ {
+ code: "DKK",
+ local: "da-DK",
+ symbol: "kr",
+ name: "Danish Krone",
+ },
+ {
+ code: "EUR",
+ local: "de-DE",
+ symbol: "€",
+ name: "Euro",
+ },
+ {
+ code: "INR",
+ local: "en-IN",
+ symbol: "₹",
+ name: "Indian Rupee",
+ },
+ {
+ code: "JPY",
+ local: "ja-JP",
+ symbol: "¥",
+ name: "Japanese Yen",
+ },
+ {
+ code: "NOK",
+ local: "nb-NO",
+ symbol: "kr",
+ name: "Norwegian Krone",
+ },
+ {
+ code: "NZD",
+ local: "en-NZ",
+ symbol: "NZ$",
+ name: "New Zealand Dollar",
+ },
+ {
+ code: "PLN",
+ local: "pl-PL",
+ symbol: "zł",
+ name: "Polish Zloty",
+ },
+ {
+ code: "RON",
+ local: "ro-RO",
+ symbol: "lei",
+ name: "Romanian Leu",
+ },
+ {
+ code: "ZAR",
+ local: "en-ZA",
+ symbol: "R",
+ name: "South African Rand",
+ },
+ {
+ code: "SEK",
+ local: "sv-SE",
+ symbol: "kr",
+ name: "Swedish Krona",
+ },
+ {
+ code: "TRY",
+ local: "tr-TR",
+ symbol: "₺",
+ name: "Turkish Lira",
+ },
+ {
+ code: "USD",
+ local: "en-US",
+ symbol: "$",
+ name: "US Dollar",
+ },
+ {
+ code: "BGN",
+ local: "bg-BG",
+ symbol: "lv",
+ name: "Bulgarian lev",
+ },
+ {
+ code: "CHF",
+ local: "de-CH",
+ symbol: "chf",
+ name: "Swiss Francs",
+ },
+ {
+ code: "CZK",
+ local: "cs-CZ",
+ symbol: "Kč",
+ name: "Czech Koruna",
+ },
+];
diff --git a/frontend/lib/datelib/datelib.test.ts b/frontend/lib/datelib/datelib.test.ts
deleted file mode 100644
index 171d2cc..0000000
--- a/frontend/lib/datelib/datelib.test.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { describe, test, expect } from "vitest";
-import { format, zeroTime, factorRange, parse } from "./datelib";
-
-describe("format", () => {
- test("should format a date as a string", () => {
- const date = new Date(2020, 1, 1);
- expect(format(date)).toBe("2020-02-01");
- });
-
- test("should return the string if a string is passed in", () => {
- expect(format("2020-02-01")).toBe("2020-02-01");
- });
-});
-
-describe("zeroTime", () => {
- test("should zero out the time", () => {
- const date = new Date(2020, 1, 1, 12, 30, 30);
- const zeroed = zeroTime(date);
- expect(zeroed.getHours()).toBe(0);
- expect(zeroed.getMinutes()).toBe(0);
- expect(zeroed.getSeconds()).toBe(0);
- });
-});
-
-describe("factorRange", () => {
- test("should return a range of dates", () => {
- const [start, end] = factorRange(10);
- // Start should be today
- expect(start).toBeInstanceOf(Date);
- expect(start.getFullYear()).toBe(new Date().getFullYear());
-
- // End should be 10 days from now
- expect(end).toBeInstanceOf(Date);
- expect(end.getFullYear()).toBe(new Date().getFullYear());
- });
-});
-
-describe("parse", () => {
- test("should parse a date string", () => {
- const date = parse("2020-02-01");
- expect(date).toBeInstanceOf(Date);
- });
-});
diff --git a/frontend/lib/datelib/datelib.ts b/frontend/lib/datelib/datelib.ts
deleted file mode 100644
index c70dbf9..0000000
--- a/frontend/lib/datelib/datelib.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { addDays } from "date-fns";
-
-/*
- * Formats a date as a string
- * */
-export function format(date: Date | string): string {
- if (typeof date === "string") {
- return date;
- }
- return date.toISOString().split("T")[0];
-}
-
-export function zeroTime(date: Date): Date {
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
-}
-
-export function factorRange(offset: number = 7): [Date, Date] {
- const date = zeroTime(new Date());
-
- return [date, addDays(date, offset)];
-}
-
-export function factory(offset = 0): Date {
- if (offset) {
- return addDays(zeroTime(new Date()), offset);
- }
-
- return zeroTime(new Date());
-}
-
-export function parse(yyyyMMdd: string): Date {
- const parts = yyyyMMdd.split("-");
- return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));
-}
diff --git a/frontend/lib/requests/requests.ts b/frontend/lib/requests/requests.ts
index 8aecda1..32b79bc 100644
--- a/frontend/lib/requests/requests.ts
+++ b/frontend/lib/requests/requests.ts
@@ -3,7 +3,6 @@ export enum Method {
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
- PATCH = "PATCH",
}
export type ResponseInterceptor = (r: Response, rq?: RequestInit) => void;
@@ -58,16 +57,12 @@ export class Requests {
return this.do(Method.PUT, args);
}
- public patch(args: RequestArgs): Promise> {
- return this.do(Method.PATCH, args);
- }
-
public delete(args: RequestArgs): Promise> {
return this.do(Method.DELETE, args);
}
private methodSupportsBody(method: Method): boolean {
- return method === Method.POST || method === Method.PUT || method === Method.PATCH;
+ return method === Method.POST || method === Method.PUT;
}
private async do(method: Method, rargs: RequestArgs): Promise> {
diff --git a/frontend/middleware/auth.ts b/frontend/middleware/auth.ts
index 97a9920..dd41635 100644
--- a/frontend/middleware/auth.ts
+++ b/frontend/middleware/auth.ts
@@ -3,20 +3,13 @@ export default defineNuxtRouteMiddleware(async () => {
const api = useUserApi();
if (!ctx.isAuthorized()) {
- if (window.location.pathname !== "/") {
- console.debug("[middleware/auth] isAuthorized returned false, redirecting to /");
- return navigateTo("/");
- }
+ return navigateTo("/");
}
if (!ctx.user) {
- console.log("Fetching user data");
const { data, error } = await api.user.self();
if (error) {
- if (window.location.pathname !== "/") {
- console.debug("[middleware/user] user is null and fetch failed, redirecting to /");
- return navigateTo("/");
- }
+ return navigateTo("/");
}
ctx.user = data.item;
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index dc45baa..479d1f4 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -3,28 +3,17 @@ import { defineNuxtConfig } from "nuxt/config";
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
ssr: false,
- modules: [
- "@nuxtjs/tailwindcss",
- "@pinia/nuxt",
- "@vueuse/nuxt",
- "@vite-pwa/nuxt",
- "./nuxt.proxyoverride.ts",
- "unplugin-icons/nuxt",
- ],
+ modules: ["@nuxtjs/tailwindcss", "@pinia/nuxt", "@vueuse/nuxt", "@vite-pwa/nuxt"],
nitro: {
devProxy: {
"/api": {
target: "http://localhost:7745/api",
- ws: true,
changeOrigin: true,
},
},
},
css: ["@/assets/css/main.css"],
pwa: {
- workbox: {
- navigateFallbackDenylist: [/^\/api/],
- },
injectRegister: "script",
injectManifest: {
swSrc: "sw.js",
diff --git a/frontend/nuxt.proxyoverride.ts b/frontend/nuxt.proxyoverride.ts
deleted file mode 100644
index 8650dd6..0000000
--- a/frontend/nuxt.proxyoverride.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-// https://gist.github.com/ucw/67f7291c64777fb24341e8eae72bcd24
-import type { IncomingMessage } from "http";
-import type internal from "stream";
-import { defineNuxtModule, logger } from "@nuxt/kit";
-// Related To
-// - https://github.com/nuxt/nuxt/issues/15417
-// - https://github.com/nuxt/cli/issues/107
-//
-// fix from
-// - https://gist.github.com/ucw/67f7291c64777fb24341e8eae72bcd24
-// eslint-disable-next-line
-import { createProxyServer } from "http-proxy";
-
-export default defineNuxtModule({
- defaults: {
- target: "ws://localhost:7745",
- path: "/api/v1/ws",
- },
- meta: {
- configKey: "websocketProxy",
- name: "Websocket proxy",
- },
- setup(resolvedOptions, nuxt) {
- if (!nuxt.options.dev || !resolvedOptions.target) {
- return;
- }
-
- nuxt.hook("listen", server => {
- const proxy = createProxyServer({
- ws: true,
- secure: false,
- changeOrigin: true,
- target: resolvedOptions.target,
- });
-
- const proxyFn = (req: IncomingMessage, socket: internal.Duplex, head: Buffer) => {
- if (req.url && req.url.startsWith(resolvedOptions.path)) {
- proxy.ws(req, socket, head);
- }
- };
-
- server.on("upgrade", proxyFn);
-
- nuxt.hook("close", () => {
- server.off("upgrade", proxyFn);
- proxy.close();
- });
-
- logger.info(`Websocket dev proxy started on ${resolvedOptions.path}`);
- });
- },
-});
diff --git a/frontend/package.json b/frontend/package.json
index f1a05ed..2bda309 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,50 +14,44 @@
"test:watch": " TEST_SHUTDOWN_API_SERVER=false vitest --config ./test/vitest.config.ts"
},
"devDependencies": {
- "@faker-js/faker": "^8.4.1",
- "@iconify-json/mdi": "^1.2.3",
- "@nuxtjs/eslint-config-typescript": "^12.1.0",
- "@types/dompurify": "^3.2.0",
- "@types/markdown-it": "^13.0.9",
- "@typescript-eslint/eslint-plugin": "^6.21.0",
- "@typescript-eslint/parser": "^6.21.0",
- "@vite-pwa/nuxt": "^0.5.0",
- "eslint": "^8.57.1",
- "eslint-config-prettier": "^9.1.0",
- "eslint-plugin-prettier": "^5.2.6",
- "eslint-plugin-vue": "^9.33.0",
- "h3": "^1.7.1",
+ "@faker-js/faker": "^7.5.0",
+ "@nuxtjs/eslint-config-typescript": "^12.0.0",
+ "@types/dompurify": "^3.0.0",
+ "@types/markdown-it": "^12.2.3",
+ "@typescript-eslint/eslint-plugin": "^5.36.2",
+ "@typescript-eslint/parser": "^5.36.2",
+ "@vite-pwa/nuxt": "^0.0.7",
+ "eslint": "^8.23.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.2.1",
+ "eslint-plugin-vue": "^9.4.0",
"isomorphic-fetch": "^3.0.0",
- "nuxt": "3.6.5",
- "prettier": "^3.5.3",
- "typescript": "^5.8.3",
- "unplugin-icons": "^0.18.5",
+ "nuxt": "3.2.3",
+ "prettier": "^2.7.1",
+ "typescript": "^5.0.0",
"vite-plugin-eslint": "^1.8.1",
- "vitest": "^1.6.1"
+ "vitest": "^0.29.0"
},
"dependencies": {
- "@headlessui/vue": "^1.7.23",
- "@nuxtjs/tailwindcss": "^6.13.2",
- "@pinia/nuxt": "^0.5.5",
- "@tailwindcss/aspect-ratio": "^0.4.2",
- "@tailwindcss/forms": "^0.5.10",
- "@tailwindcss/typography": "^0.5.16",
- "@types/lunr": "^2.3.7",
- "@vuepic/vue-datepicker": "^8.8.1",
- "@vueuse/nuxt": "^10.11.1",
- "@vueuse/router": "^10.11.1",
- "autoprefixer": "^10.4.21",
- "daisyui": "^2.52.0",
- "date-fns": "^3.6.0",
- "dompurify": "^3.2.5",
- "h3": "^1.15.1",
- "http-proxy": "^1.18.1",
- "lunr": "^2.3.9",
- "markdown-it": "^14.1.0",
- "pinia": "^2.3.1",
- "postcss": "^8.5.3",
- "tailwindcss": "^3.4.17",
- "vue": "3.4.8",
- "vue-router": "^4.5.0"
+ "@headlessui/vue": "^1.7.9",
+ "@iconify/vue": "^3.2.1",
+ "@nuxtjs/tailwindcss": "^6.1.3",
+ "@pinia/nuxt": "^0.4.1",
+ "@tailwindcss/aspect-ratio": "^0.4.0",
+ "@tailwindcss/forms": "^0.5.2",
+ "@tailwindcss/typography": "^0.5.4",
+ "@vueuse/nuxt": "^9.1.1",
+ "@vueuse/router": "^9.9.0",
+ "autoprefixer": "^10.4.8",
+ "chart.js": "^4.0.1",
+ "daisyui": "^2.24.0",
+ "dompurify": "^3.0.0",
+ "markdown-it": "^13.0.1",
+ "pinia": "^2.0.21",
+ "postcss": "^8.4.16",
+ "tailwindcss": "^3.1.8",
+ "vue": "^3.2.45",
+ "vue-chartjs": "^4.1.2",
+ "vue-router": "4"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/pages/home/charts.ts b/frontend/pages/home/charts.ts
new file mode 100644
index 0000000..ff9c8c7
--- /dev/null
+++ b/frontend/pages/home/charts.ts
@@ -0,0 +1,71 @@
+import { TChartData } from "vue-chartjs/dist/types";
+import { UserClient } from "~~/lib/api/user";
+
+export function purchasePriceOverTimeChart(api: UserClient) {
+ const { data: timeseries } = useAsyncData(async () => {
+ const { data } = await api.stats.totalPriceOverTime();
+ return data;
+ });
+
+ const primary = useCssVar("--p");
+
+ return computed(() => {
+ if (!timeseries.value) {
+ return {
+ labels: ["Purchase Price"],
+ datasets: [
+ {
+ label: "Purchase Price",
+ data: [],
+ backgroundColor: primary.value,
+ borderColor: primary.value,
+ },
+ ],
+ } as TChartData<"line", number[], unknown>;
+ }
+
+ let start = timeseries.value?.valueAtStart;
+
+ return {
+ labels: timeseries?.value.entries.map(t => new Date(t.date).toDateString()) || [],
+ datasets: [
+ {
+ label: "Purchase Price",
+ data:
+ timeseries.value?.entries.map(t => {
+ start += t.value;
+ return start;
+ }) || [],
+ backgroundColor: primary.value,
+ borderColor: primary.value,
+ },
+ ],
+ } as TChartData<"line", number[], unknown>;
+ });
+}
+
+export function inventoryByLocationChart(api: UserClient) {
+ const { data: donutSeries } = useAsyncData(async () => {
+ const { data } = await api.stats.locations();
+ return data;
+ });
+
+ const primary = useCssVar("--p");
+ const secondary = useCssVar("--s");
+ const neutral = useCssVar("--n");
+
+ return computed(() => {
+ return {
+ labels: donutSeries.value?.map(l => l.name) || [],
+ datasets: [
+ {
+ label: "Value",
+ data: donutSeries.value?.map(l => l.total) || [],
+ backgroundColor: [primary.value, secondary.value, neutral.value],
+ borderColor: [primary.value, secondary.value, neutral.value],
+ hoverOffset: 4,
+ },
+ ],
+ };
+ });
+}
diff --git a/frontend/pages/home/index.vue b/frontend/pages/home/index.vue
index c114eee..5ce3f0d 100644
--- a/frontend/pages/home/index.vue
+++ b/frontend/pages/home/index.vue
@@ -22,11 +22,55 @@
const itemTable = itemsTable(api);
const stats = statCardData(api);
+
+ // const purchasePriceOverTime = purchasePriceOverTimeChart(api);
+
+ // const inventoryByLocation = inventoryByLocationChart(api);
+
+ // const refDonutEl = ref();
+
+ // const donutElWidth = computed(() => {
+ // return refDonutEl.value?.clientWidth || 0;
+ // });
+
+
Quick Statistics
diff --git a/frontend/pages/home/statistics.ts b/frontend/pages/home/statistics.ts
index 40ec1c2..e1c7bf1 100644
--- a/frontend/pages/home/statistics.ts
+++ b/frontend/pages/home/statistics.ts
@@ -1,4 +1,4 @@
-import type { UserClient } from "~~/lib/api/user";
+import { UserClient } from "~~/lib/api/user";
type StatCard = {
label: string;
diff --git a/frontend/pages/home/table.ts b/frontend/pages/home/table.ts
index eaff6da..127ecbb 100644
--- a/frontend/pages/home/table.ts
+++ b/frontend/pages/home/table.ts
@@ -1,20 +1,14 @@
-import type { UserClient } from "~~/lib/api/user";
+import { UserClient } from "~~/lib/api/user";
export function itemsTable(api: UserClient) {
- const { data: items, refresh } = useAsyncData(async () => {
+ const { data: items } = useAsyncData(async () => {
const { data } = await api.items.getAll({
page: 1,
pageSize: 5,
- orderBy: "createdAt",
});
return data.items;
});
- onServerEvent(ServerEvent.ItemMutation, () => {
- console.log("item mutation");
- refresh();
- });
-
return computed(() => {
return {
items: items.value || [],
diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue
index 2f761e2..2258c4c 100644
--- a/frontend/pages/index.vue
+++ b/frontend/pages/index.vue
@@ -1,31 +1,16 @@
@@ -445,10 +404,10 @@
@@ -457,45 +416,33 @@
-
-
-
-
-
-
-
- {{ item ? item.name : "" }}
-
-
-
- Created
-
-
- -
-
- Updated
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {{ item ? item.name : "" }}
+
+
+
+
+ {{ item.parent.name }}
+
+ {{ item.name }}
+
+
+
+
+
+
+
+ {{ item.location.name }}
+
+
+
+
+
+
+
@@ -524,21 +475,7 @@
-
-
- {{ detail.text }}
-
-
-
-
-
-
-
-
-
-
+
@@ -609,8 +546,8 @@
-
diff --git a/frontend/pages/item/[id]/index/edit.vue b/frontend/pages/item/[id]/index/edit.vue
index 93fda07..f40ce91 100644
--- a/frontend/pages/item/[id]/index/edit.vue
+++ b/frontend/pages/item/[id]/index/edit.vue
@@ -1,13 +1,10 @@
@@ -418,6 +365,7 @@
Attachment Edit
+ {{ editState.type }}
-
-
-
- Primary Photo
- This options is only available for photos. Only one photo can be primary. If you select this option, the
- current primary photo, if any will be unselected.
-
-
Update
-
-
-
-
-
- Advanced
-
-
-
-
-
-
- Save
-
-
-
- Delete
-
-
-
+
+
Edit Details
-
+
+
+
+
+ Advanced
+
+
+
+
+
+
+ Save
+
+
@@ -579,12 +514,12 @@
diff --git a/frontend/pages/item/[id]/index/maintenance.vue b/frontend/pages/item/[id]/index/maintenance.vue
index 9feb7bc..cf6055d 100644
--- a/frontend/pages/item/[id]/index/maintenance.vue
+++ b/frontend/pages/item/[id]/index/maintenance.vue
@@ -1,14 +1,7 @@
@@ -182,7 +163,7 @@
{{ entry.id ? "Edit Entry" : "New Entry" }}
-