refactor: Convert StorageHierarchy component to TypeScript with proper typing

This commit is contained in:
garionion (aider) 2025-02-28 21:35:17 +01:00
parent e3d8f9e8ee
commit 9213034093
2 changed files with 125 additions and 86 deletions

View file

@ -50,16 +50,43 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch, onMounted, defineComponent } from 'vue'; import { ref, computed, watch, onMounted } from 'vue';
import { useStorageStore } from '@/stores/storage'; import { useStorageStore } from '@/stores/storage';
// Component for recursive display of storage boxes // Define types for our data
interface StorageSpace {
id: number;
parent: {
valid: boolean;
int64: number;
} | null;
location: string | null;
}
interface StorageObject {
id: number;
storagespaceId: number;
name: string;
description: string | null;
serialnumber: string | null;
}
// Create a separate component for storage boxes
const StorageBox = defineComponent({ const StorageBox = defineComponent({
name: 'StorageBox', name: 'StorageBox',
props: { props: {
storage: Object, storage: {
objects: Array, type: Object as () => StorageSpace,
nestedStorages: Array required: true
},
objects: {
type: Array as () => StorageObject[],
required: true
},
nestedStorages: {
type: Array as () => StorageSpace[],
required: true
}
}, },
setup(props) { setup(props) {
const objectsInCurrentStorage = computed(() => { const objectsInCurrentStorage = computed(() => {
@ -72,87 +99,81 @@ const StorageBox = defineComponent({
); );
}); });
return () => ( return { objectsInCurrentStorage, childStorages };
<v-expansion-panel> },
<v-expansion-panel-title> template: `
<div class="d-flex align-center"> <v-expansion-panel>
<v-icon class="mr-2">mdi-package-variant-closed</v-icon> <v-expansion-panel-title>
<span>{ props.storage.location || `Storage #${props.storage.id}` }</span> <div class="d-flex align-center">
<v-chip class="ml-2" size="small" color="primary" variant="outlined"> <v-icon class="mr-2">mdi-package-variant-closed</v-icon>
{ objectsInCurrentStorage.value.length } objects <span>{{ storage.location || \`Storage #\${storage.id}\` }}</span>
</v-chip> <v-chip class="ml-2" size="small" color="primary" variant="outlined">
</div> {{ objectsInCurrentStorage.length }} objects
</v-expansion-panel-title> </v-chip>
<v-expansion-panel-text> </div>
<div class="mb-4"> </v-expansion-panel-title>
<h3 class="text-subtitle-1 mb-2">Objects in this storage:</h3> <v-expansion-panel-text>
{objectsInCurrentStorage.value.length > 0 ? ( <div class="mb-4">
<v-list lines="two"> <h3 class="text-subtitle-1 mb-2">Objects in this storage:</h3>
{objectsInCurrentStorage.value.map(obj => ( <v-list v-if="objectsInCurrentStorage.length > 0" lines="two">
<v-list-item <v-list-item
:key="obj.id" v-for="obj in objectsInCurrentStorage"
:title="obj.name" :key="obj.id"
:subtitle="obj.description || 'No description'" :title="obj.name"
> :subtitle="obj.description || 'No description'"
<template v-slot:prepend> >
<v-icon>mdi-cube-outline</v-icon> <template v-slot:prepend>
</template> <v-icon>mdi-cube-outline</v-icon>
<template v-slot:append> </template>
<v-chip size="small" color="grey" variant="flat"> <template v-slot:append>
SN: { obj.serialnumber || 'N/A' } <v-chip size="small" color="grey" variant="flat">
</v-chip> SN: {{ obj.serialnumber || 'N/A' }}
</template> </v-chip>
</v-list-item> </template>
))} </v-list-item>
</v-list> </v-list>
) : ( <v-alert v-else type="info" variant="tonal" density="compact">
<v-alert type="info" variant="tonal" density="compact"> No objects in this storage
No objects in this storage </v-alert>
</v-alert> </div>
)}
</div> <div v-if="childStorages.length > 0" class="mt-4">
<h3 class="text-subtitle-1 mb-2">Nested storage spaces:</h3>
{childStorages.value.length > 0 && ( <v-expansion-panels>
<div class="mt-4"> <storage-box
<h3 class="text-subtitle-1 mb-2">Nested storage spaces:</h3> v-for="childStorage in childStorages"
<v-expansion-panels> :key="childStorage.id"
{childStorages.value.map(childStorage => ( :storage="childStorage"
<StorageBox :objects="objects"
key={childStorage.id} :nested-storages="nestedStorages"
storage={childStorage} />
objects={props.objects} </v-expansion-panels>
nestedStorages={props.nestedStorages} </div>
/> </v-expansion-panel-text>
))} </v-expansion-panel>
</v-expansion-panels> `
</div>
)}
</v-expansion-panel-text>
</v-expansion-panel>
);
}
}); });
// Main component logic // Main component logic
const storageStore = useStorageStore(); const storageStore = useStorageStore();
const loading = ref(false); const loading = ref(false);
const error = ref(null); const error = ref<string | null>(null);
const selectedStorageId = ref(null); const selectedStorageId = ref<number | null>(null);
const storageSpaces = computed(() => storageStore.storageSpaces); const storageSpaces = computed<StorageSpace[]>(() => storageStore.storageSpaces);
const objects = computed(() => storageStore.objects); const objects = computed<StorageObject[]>(() => storageStore.objects);
const selectedStorage = computed(() => { const selectedStorage = computed<StorageSpace | null>(() => {
if (!selectedStorageId.value) return null; if (!selectedStorageId.value) return null;
return storageSpaces.value.find(s => s.id === selectedStorageId.value); return storageSpaces.value.find(s => s.id === selectedStorageId.value) || null;
}); });
const objectsInStorage = computed(() => { const objectsInStorage = computed<StorageObject[]>(() => {
if (!selectedStorageId.value) return []; if (!selectedStorageId.value) return [];
return objects.value; return objects.value;
}); });
const nestedStorages = computed(() => { const nestedStorages = computed<StorageSpace[]>(() => {
return storageSpaces.value; return storageSpaces.value;
}); });
@ -162,13 +183,13 @@ watch(selectedStorageId, async (newId) => {
} }
}); });
async function fetchStorageData(storageId) { async function fetchStorageData(storageId: number): Promise<void> {
loading.value = true; loading.value = true;
error.value = null; error.value = null;
try { try {
await storageStore.fetchStorageHierarchy(storageId); await storageStore.fetchStorageHierarchy(storageId);
} catch (err) { } catch (err: any) {
error.value = err.message || 'Failed to load storage data'; error.value = err.message || 'Failed to load storage data';
} finally { } finally {
loading.value = false; loading.value = false;
@ -184,7 +205,7 @@ onMounted(async () => {
if (storageSpaces.value.length > 0) { if (storageSpaces.value.length > 0) {
selectedStorageId.value = storageSpaces.value[0].id; selectedStorageId.value = storageSpaces.value[0].id;
} }
} catch (err) { } catch (err: any) {
error.value = err.message || 'Failed to load storage spaces'; error.value = err.message || 'Failed to load storage spaces';
} finally { } finally {
loading.value = false; loading.value = false;

View file

@ -1,31 +1,49 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
// Define types for our data
interface StorageSpace {
id: number;
parent: {
valid: boolean;
int64: number;
} | null;
location: string | null;
}
interface StorageObject {
id: number;
storagespaceId: number;
name: string;
description: string | null;
serialnumber: string | null;
}
export const useStorageStore = defineStore('storage', { export const useStorageStore = defineStore('storage', {
state: () => ({ state: () => ({
storageSpaces: [] as any[], storageSpaces: [] as StorageSpace[],
objects: [] as any[], objects: [] as StorageObject[],
loading: false, loading: false,
error: null as string | null, error: null as string | null,
}), }),
getters: { getters: {
getStorageById: (state) => (id: number) => { getStorageById: (state) => (id: number): StorageSpace | undefined => {
return state.storageSpaces.find(storage => storage.id === id); return state.storageSpaces.find(storage => storage.id === id);
}, },
getObjectsInStorage: (state) => (storageId: number) => { getObjectsInStorage: (state) => (storageId: number): StorageObject[] => {
return state.objects.filter(obj => obj.storagespaceId === storageId); return state.objects.filter(obj => obj.storagespaceId === storageId);
}, },
getChildStorages: (state) => (parentId: number) => { getChildStorages: (state) => (parentId: number): StorageSpace[] => {
return state.storageSpaces.filter(storage => return state.storageSpaces.filter(storage =>
storage.parent && storage.parent === parentId storage.parent && storage.parent.valid && storage.parent.int64 === parentId
); );
} }
}, },
actions: { actions: {
async fetchStorageSpaces() { async fetchStorageSpaces(): Promise<void> {
this.loading = true; this.loading = true;
this.error = null; this.error = null;
@ -54,7 +72,7 @@ export const useStorageStore = defineStore('storage', {
} }
}, },
async fetchObjectsInStorage(storageId: number) { async fetchObjectsInStorage(storageId: number): Promise<void> {
this.loading = true; this.loading = true;
this.error = null; this.error = null;
@ -70,7 +88,7 @@ export const useStorageStore = defineStore('storage', {
} }
const data = await response.json(); const data = await response.json();
this.objects = data; this.objects = data as StorageObject[];
} catch (error: any) { } catch (error: any) {
this.error = error.message; this.error = error.message;
throw error; throw error;
@ -79,7 +97,7 @@ export const useStorageStore = defineStore('storage', {
} }
}, },
async fetchStorageHierarchy(rootStorageId: number) { async fetchStorageHierarchy(rootStorageId: number): Promise<void> {
this.loading = true; this.loading = true;
this.error = null; this.error = null;
@ -101,7 +119,7 @@ export const useStorageStore = defineStore('storage', {
} }
const data = await response.json(); const data = await response.json();
this.objects = data; this.objects = data as StorageObject[];
} catch (error: any) { } catch (error: any) {
this.error = error.message; this.error = error.message;
throw error; throw error;