fix: profile bugs and add full path (#792)

* fix template rendering

* tidy

* fix missing currencies and profile errors

* endpoint for fullpath of an item

* endpoint test

* fix assertions
This commit is contained in:
Hayden 2024-02-25 16:04:24 -06:00 committed by GitHub
parent 3ed50f5a1b
commit c708b1759e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 416 additions and 105 deletions

View file

@ -155,4 +155,42 @@ 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();
});
});

View file

@ -5,6 +5,7 @@ import {
ItemCreate,
ItemOut,
ItemPatch,
ItemPath,
ItemSummary,
ItemUpdate,
MaintenanceEntry,
@ -105,6 +106,10 @@ export class ItemsApi extends BaseAPI {
this.maintenance = new MaintenanceAPI(http);
}
fullpath(id: string) {
return this.http.get<ItemPath[]>({ url: route(`/items/${id}/path`) });
}
getAll(q: ItemsQuery = {}) {
return this.http.get<PaginationResult<ItemSummary>>({ url: route("/items", q) });
}

View file

@ -128,6 +128,12 @@ export interface ItemPatch {
quantity?: number | null;
}
export interface ItemPath {
id: string;
name: string;
type: ItemType;
}
export interface ItemSummary {
archived: boolean;
createdAt: Date | string;
@ -145,6 +151,11 @@ export interface ItemSummary {
updatedAt: Date | string;
}
export enum ItemType {
ItemTypeLocation = "location",
ItemTypeItem = "item",
}
export interface ItemUpdate {
archived: boolean;
assetId: string;

View file

@ -146,11 +146,6 @@
}
const ret: Details = [
{
name: "Description",
type: "markdown",
text: item.value?.description,
},
{
name: "Quantity",
text: item.value?.quantity,
@ -405,6 +400,20 @@
];
});
const fullpath = computedAsync(async () => {
if (!item.value) {
return [];
}
const resp = await api.items.fullpath(item.value.id);
if (resp.error) {
toast.error("Failed to load item");
return [];
}
return resp.data;
});
const items = computedAsync(async () => {
if (!item.value) {
return [];
@ -442,33 +451,45 @@
</dialog>
<section>
<BaseSectionHeader>
<Icon name="mdi-package-variant" class="mr-2 -mt-1 text-base-content" />
<span class="text-base-content">
{{ item ? item.name : "" }}
</span>
<div v-if="item.parent" class="text-sm breadcrumbs pb-0">
<ul class="text-base-content/70">
<li>
<NuxtLink :to="`/item/${item.parent.id}`"> {{ item.parent.name }}</NuxtLink>
</li>
<li>{{ item.name }}</li>
</ul>
</div>
<template #description>
<Markdown :source="item.description"> </Markdown>
<div class="flex flex-wrap gap-2 mt-3">
<NuxtLink v-if="item.location" ref="badge" class="badge p-3" :to="`/location/${item.location.id}`">
<Icon name="heroicons-map-pin" class="mr-2 swap-on"></Icon>
{{ item.location.name }}
</NuxtLink>
<template v-if="item.labels && item.labels.length > 0">
<LabelChip v-for="label in item.labels" :key="label.id" class="badge-primary" :label="label" />
</template>
<div class="bg-base-100 rounded p-3">
<header class="mb-2">
<div class="flex flex-wrap items-end gap-2">
<div class="avatar placeholder mb-auto">
<div class="bg-neutral-focus text-neutral-content rounded-full w-12">
<Icon name="mdi-package-variant" class="h-7 w-7" />
</div>
</div>
<div>
<div v-if="fullpath && fullpath.length > 0" class="text-sm breadcrumbs pt-0 pb-0">
<ul class="text-base-content/70">
<li v-for="part in fullpath" :key="part.id">
<NuxtLink :to="`/${part.type}/${part.id}`"> {{ part.name }}</NuxtLink>
</li>
</ul>
</div>
<h1 class="text-2xl pb-1">
{{ item ? item.name : "" }}
</h1>
<div class="flex gap-1 flex-wrap text-xs">
<div>
Created
<DateTime :date="item?.createdAt" />
</div>
-
<div>
Updated
<DateTime :date="item?.updatedAt" />
</div>
</div>
</div>
</div>
</template>
</BaseSectionHeader>
</header>
<div class="divider my-0 mb-1"></div>
<div class="p-1 prose max-w-[100%]">
<Markdown v-if="item && item.description" class="text-base" :source="item.description"> </Markdown>
</div>
</div>
<div class="flex flex-wrap items-center justify-between mb-6 mt-3">
<div class="btn-group">
<NuxtLink

View file

@ -420,7 +420,7 @@
</BaseSectionHeader>
</template>
<div v-if="group" class="p-5 pt-0">
<div v-if="group && currencies && currencies.length > 0" class="p-5 pt-0">
<FormSelect v-model="currency" label="Currency Format" :items="currencies" />
<p class="m-2 text-sm">Example: {{ currencyExample }}</p>