add location selector

This commit is contained in:
Hayden 2022-10-23 20:38:02 -08:00
parent 55ac472c9e
commit fb9e3d6735
5 changed files with 59 additions and 12 deletions

View file

@ -50,7 +50,7 @@
interface Props { interface Props {
label: string; label: string;
modelValue: string | ItemsObject; modelValue: string | ItemsObject;
items: string[] | ItemsObject[]; items: ItemsObject[] | Record<string, unknown>[] | string[];
itemText?: keyof ItemsObject; itemText?: keyof ItemsObject;
itemValue?: keyof ItemsObject; itemValue?: keyof ItemsObject;
search?: string; search?: string;

View file

@ -17,20 +17,18 @@
<Icon name="heroicons-map-pin" class="swap-off" /> <Icon name="heroicons-map-pin" class="swap-off" />
</label> </label>
{{ location.name }} {{ location.name }}
<span v-if="location.itemCount" class="badge badge-secondary badge-lg ml-auto text-secondary-content"> <span v-if="hasCount" class="badge badge-secondary badge-lg ml-auto text-secondary-content"> {{ count }}</span>
{{ location.itemCount }}</span
>
</h2> </h2>
</div> </div>
</NuxtLink> </NuxtLink>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { LocationOutCount } from "~~/lib/api/types/data-contracts"; import { LocationOut, LocationOutCount, LocationSummary } from "~~/lib/api/types/data-contracts";
defineProps({ const props = defineProps({
location: { location: {
type: Object as () => LocationOutCount, type: Object as () => LocationOutCount | LocationOut | LocationSummary,
required: true, required: true,
}, },
dense: { dense: {
@ -39,6 +37,16 @@
}, },
}); });
const hasCount = computed(() => {
return !!(props.location as LocationOutCount).itemCount;
});
const count = computed(() => {
if (hasCount.value) {
return (props.location as LocationOutCount).itemCount;
}
});
const card = ref(null); const card = ref(null);
const isHover = useElementHover(card); const isHover = useElementHover(card);
const { focused } = useFocus(card); const { focused } = useFocus(card);

View file

@ -1,9 +1,7 @@
import { BaseAPI, route } from "../base"; import { BaseAPI, route } from "../base";
import { LocationOutCount, LocationCreate, LocationOut } from "../types/data-contracts"; import { LocationOutCount, LocationCreate, LocationOut, LocationUpdate } from "../types/data-contracts";
import { Results } from "../types/non-generated"; import { Results } from "../types/non-generated";
export type LocationUpdate = LocationCreate;
export class LocationsApi extends BaseAPI { export class LocationsApi extends BaseAPI {
getAll() { getAll() {
return this.http.get<Results<LocationOutCount>>({ url: route("/locations") }); return this.http.get<Results<LocationOutCount>>({ url: route("/locations") });

View file

@ -191,11 +191,13 @@ export interface LocationCreate {
} }
export interface LocationOut { export interface LocationOut {
children: LocationSummary[];
createdAt: Date; createdAt: Date;
description: string; description: string;
id: string; id: string;
items: ItemSummary[]; items: ItemSummary[];
name: string; name: string;
parent: LocationSummary;
updatedAt: Date; updatedAt: Date;
} }
@ -216,6 +218,13 @@ export interface LocationSummary {
updatedAt: Date; updatedAt: Date;
} }
export interface LocationUpdate {
description: string;
id: string;
name: string;
parentId: string | null;
}
export interface PaginationResultRepoItemSummary { export interface PaginationResultRepoItemSummary {
items: ItemSummary[]; items: ItemSummary[];
page: number; page: number;

View file

@ -1,5 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { Detail, CustomDetail } from "~~/components/global/DetailsSection/types"; import { Detail, CustomDetail } from "~~/components/global/DetailsSection/types";
import { LocationSummary, LocationUpdate } from "~~/lib/api/types/data-contracts";
import { useLocationStore } from "~~/stores/locations";
definePageMeta({ definePageMeta({
middleware: ["auth"], middleware: ["auth"],
@ -20,6 +22,11 @@
navigateTo("/home"); navigateTo("/home");
return; return;
} }
if (data.parent) {
parent.value = locations.value.find(l => l.id === data.parent.id);
}
return data; return data;
}); });
@ -80,9 +87,11 @@
const updateModal = ref(false); const updateModal = ref(false);
const updating = ref(false); const updating = ref(false);
const updateData = reactive({ const updateData = reactive<LocationUpdate>({
id: locationId.value,
name: "", name: "",
description: "", description: "",
parentId: null,
}); });
function openUpdate() { function openUpdate() {
@ -93,6 +102,7 @@
async function update() { async function update() {
updating.value = true; updating.value = true;
updateData.parentId = parent.value?.id || null;
const { error, data } = await api.locations.update(locationId.value, updateData); const { error, data } = await api.locations.update(locationId.value, updateData);
if (error) { if (error) {
@ -105,6 +115,12 @@
updateModal.value = false; updateModal.value = false;
updating.value = false; updating.value = false;
} }
const locationStore = useLocationStore();
const locations = computed(() => locationStore.locations);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parent = ref<LocationSummary | any>({});
</script> </script>
<template> <template>
@ -114,6 +130,7 @@
<form v-if="location" @submit.prevent="update"> <form v-if="location" @submit.prevent="update">
<FormTextField v-model="updateData.name" :autofocus="true" label="Location Name" /> <FormTextField v-model="updateData.name" :autofocus="true" label="Location Name" />
<FormTextArea v-model="updateData.description" label="Location Description" /> <FormTextArea v-model="updateData.description" label="Location Description" />
<FormAutocomplete v-model="parent" :items="locations" item-text="name" item-value="id" label="Parent" />
<div class="modal-action"> <div class="modal-action">
<BaseButton type="submit" :loading="updating"> Update </BaseButton> <BaseButton type="submit" :loading="updating"> Update </BaseButton>
</div> </div>
@ -127,6 +144,14 @@
<span class="text-base-content"> <span class="text-base-content">
{{ location ? location.name : "" }} {{ location ? location.name : "" }}
</span> </span>
<div v-if="location && location.parent" class="text-sm breadcrumbs pb-0">
<ul class="text-base-content/70">
<li>
<NuxtLink :to="`/location/${location.parent.id}`"> {{ location.parent.name }}</NuxtLink>
</li>
<li>{{ location.name }}</li>
</ul>
</div>
</BaseSectionHeader> </BaseSectionHeader>
</template> </template>
@ -152,11 +177,18 @@
<DetailsSection :details="details" /> <DetailsSection :details="details" />
</BaseCard> </BaseCard>
<section v-if="location"> <section v-if="location && location.items.length > 0">
<BaseSectionHeader class="mb-5"> Items </BaseSectionHeader> <BaseSectionHeader class="mb-5"> Items </BaseSectionHeader>
<div class="grid gap-2 grid-cols-1 sm:grid-cols-2"> <div class="grid gap-2 grid-cols-1 sm:grid-cols-2">
<ItemCard v-for="item in location.items" :key="item.id" :item="item" /> <ItemCard v-for="item in location.items" :key="item.id" :item="item" />
</div> </div>
</section> </section>
<section v-if="location && location.children.length > 0">
<BaseSectionHeader class="mb-5"> Child Locations </BaseSectionHeader>
<div class="grid gap-2 grid-cols-1 sm:grid-cols-3">
<LocationCard v-for="item in location.children" :key="item.id" :location="item" />
</div>
</section>
</BaseContainer> </BaseContainer>
</template> </template>