mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-03 16:20:27 +00:00
add location selector
This commit is contained in:
parent
55ac472c9e
commit
fb9e3d6735
5 changed files with 59 additions and 12 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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") });
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue