forked from mirrors/homebox
feat: group statistics endpoint (#123)
* group statistics endpoint * remove item store * return possible errors * add statistics tests
This commit is contained in:
parent
a886fa86ca
commit
7e0f1fac23
13 changed files with 201 additions and 74 deletions
|
@ -9,7 +9,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useItemStore } from "~~/stores/items";
|
||||
import { useLabelStore } from "~~/stores/labels";
|
||||
import { useLocationStore } from "~~/stores/locations";
|
||||
|
||||
|
@ -39,23 +38,11 @@
|
|||
},
|
||||
});
|
||||
|
||||
const itemStore = useItemStore();
|
||||
const reItem = /\/api\/v1\/items\/.*/gm;
|
||||
const rmItemStoreObserver = defineObserver("itemStore", {
|
||||
handler: r => {
|
||||
if (r.status === 201 || r.url.match(reItem)) {
|
||||
itemStore.refresh();
|
||||
}
|
||||
console.debug("itemStore handler called by observer");
|
||||
},
|
||||
});
|
||||
|
||||
const eventBus = useEventBus();
|
||||
eventBus.on(
|
||||
EventTypes.ClearStores,
|
||||
() => {
|
||||
labelStore.refresh();
|
||||
itemStore.refresh();
|
||||
locationStore.refresh();
|
||||
},
|
||||
"stores"
|
||||
|
@ -64,7 +51,6 @@
|
|||
onUnmounted(() => {
|
||||
rmLabelStoreObserver();
|
||||
rmLocationStoreObserver();
|
||||
rmItemStoreObserver();
|
||||
eventBus.off(EventTypes.ClearStores, "stores");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BaseAPI, route } from "../base";
|
||||
import { Group, GroupInvitation, GroupInvitationCreate, GroupUpdate } from "../types/data-contracts";
|
||||
import { Group, GroupInvitation, GroupInvitationCreate, GroupStatistics, GroupUpdate } from "../types/data-contracts";
|
||||
|
||||
export class GroupApi extends BaseAPI {
|
||||
createInvitation(data: GroupInvitationCreate) {
|
||||
|
@ -21,4 +21,10 @@ export class GroupApi extends BaseAPI {
|
|||
url: route("/groups"),
|
||||
});
|
||||
}
|
||||
|
||||
statistics() {
|
||||
return this.http.get<GroupStatistics>({
|
||||
url: route("/groups/statistics"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,13 @@ export interface Group {
|
|||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface GroupStatistics {
|
||||
totalItems: number;
|
||||
totalLabels: number;
|
||||
totalLocations: number;
|
||||
totalUsers: number;
|
||||
}
|
||||
|
||||
export interface GroupUpdate {
|
||||
currency: string;
|
||||
name: string;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { useAuthStore } from "~~/stores/auth";
|
||||
import { useItemStore } from "~~/stores/items";
|
||||
import { useLabelStore } from "~~/stores/labels";
|
||||
import { useLocationStore } from "~~/stores/locations";
|
||||
|
||||
|
@ -16,33 +15,33 @@
|
|||
|
||||
const auth = useAuthStore();
|
||||
|
||||
const itemsStore = useItemStore();
|
||||
const items = computed(() => itemsStore.items);
|
||||
|
||||
const locationStore = useLocationStore();
|
||||
const locations = computed(() => locationStore.locations);
|
||||
|
||||
const labelsStore = useLabelStore();
|
||||
const labels = computed(() => labelsStore.labels);
|
||||
|
||||
const totalItems = computed(() => items.value?.length || 0);
|
||||
const totalLocations = computed(() => locations.value?.length || 0);
|
||||
const totalLabels = computed(() => labels.value?.length || 0);
|
||||
const { data: statistics } = useAsyncData(async () => {
|
||||
const { data } = await api.group.statistics();
|
||||
return data;
|
||||
});
|
||||
|
||||
const stats = [
|
||||
{
|
||||
label: "Locations",
|
||||
value: totalLocations,
|
||||
},
|
||||
{
|
||||
label: "Items",
|
||||
value: totalItems,
|
||||
},
|
||||
{
|
||||
label: "Labels",
|
||||
value: totalLabels,
|
||||
},
|
||||
];
|
||||
const stats = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: "Locations",
|
||||
value: statistics.value?.totalLocations || 0,
|
||||
},
|
||||
{
|
||||
label: "Items",
|
||||
value: statistics.value?.totalItems || 0,
|
||||
},
|
||||
{
|
||||
label: "Labels",
|
||||
value: statistics.value?.totalLabels || 0,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const importDialog = ref(false);
|
||||
const importCsv = ref(null);
|
||||
|
@ -141,7 +140,7 @@
|
|||
class="grid grid-cols-1 divide-y divide-base-300 border-t border-base-300 sm:grid-cols-3 sm:divide-y-0 sm:divide-x"
|
||||
>
|
||||
<div v-for="stat in stats" :key="stat.label" class="px-6 py-5 text-center text-sm font-medium">
|
||||
<span class="text-base-900 font-bold">{{ stat.value.value }}</span>
|
||||
<span class="text-base-900 font-bold">{{ stat.value }}</span>
|
||||
{{ " " }}
|
||||
<span class="text-base-600">{{ stat.label }}</span>
|
||||
</div>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { ItemOut } from "~~/lib/api/types/data-contracts";
|
||||
|
||||
export const useItemStore = defineStore("items", {
|
||||
state: () => ({
|
||||
allItems: null as ItemOut[] | null,
|
||||
client: useUserApi(),
|
||||
}),
|
||||
getters: {
|
||||
/**
|
||||
* items represents the items that are currently in the store. The store is
|
||||
* synched with the server by intercepting the API calls and updating on the
|
||||
* response.
|
||||
*/
|
||||
items(state): ItemOut[] {
|
||||
if (state.allItems === null) {
|
||||
Promise.resolve(this.refresh());
|
||||
}
|
||||
return state.allItems;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async refresh(): Promise<ItemOut[]> {
|
||||
const result = await this.client.items.getAll();
|
||||
if (result.error) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this.allItems = result.data.items;
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue