web: add delete group button

Co-authored-by: Sphereso <Sphereso@users.noreply.github.com>
This commit is contained in:
Mika 2024-07-12 10:50:45 +02:00
parent 56ead89983
commit 0ce8e9765b
7 changed files with 244 additions and 99 deletions

View file

@ -13,6 +13,7 @@
"@vueuse/core": "^10.11.0",
"axios": "^1.7.2",
"lucide-vue-next": "^0.402.0",
"minisearch": "^7.0.0",
"roboto-fontface": "*",
"vue": "^3.4.21",
"vuetify": "^3.5.8"

View file

@ -23,6 +23,9 @@ importers:
lucide-vue-next:
specifier: ^0.402.0
version: 0.402.0(vue@3.4.31(typescript@5.5.3))
minisearch:
specifier: ^7.0.0
version: 7.0.0
roboto-fontface:
specifier: '*'
version: 0.10.0
@ -594,6 +597,9 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
minisearch@7.0.0:
resolution: {integrity: sha512-0OIJ3hUE+YBJNruDCqbTMFmk/IoB1CpZzuGfl11khFIel66ew9UoLF/+gfq3bdyrneqr3P7BTjFZApUbmk+9Dg==}
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
@ -1279,6 +1285,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
minisearch@7.0.0: {}
ms@2.1.2: {}
muggle-string@0.4.1: {}

View file

@ -1,7 +1,60 @@
<template>
<div class="mt-3">
<h2>{{ props.licenseGroupName }}:</h2>
<li v-for="license in licenses" :key="license.id">
<v-row no-gutters>
<v-col cols="11">
<h2>{{ licenseGroup.name }}:</h2>
</v-col>
<v-col>
<v-btn
block
class="ml-7"
flat
size="small"
color="isHovering ? 'red' : undefined"
variant="text"
v-model="deleteDialog"
@click="deleteDialog = true"
>
<Trash />
</v-btn>
<v-dialog v-model="deleteDialog" width="600" persistent>
<v-card max-width="600">
<v-card-title>
<span class="headline">Delete group</span>
</v-card-title>
<v-card-subtitle>
<span>Delete a group inside the database</span>
</v-card-subtitle>
<v-card-text>
<h4>This action is irreversible!</h4>
</v-card-text>
<v-card-actions>
<v-row>
<v-col cols="8" align="right" no-gutters>
<v-btn
class="ms-auto"
text="Cancel"
color="blue darken-1"
@click="deleteDialog = false"
></v-btn>
</v-col>
<v-col>
<v-btn
class="ms-auto"
text="Confirm Delete"
type="submit"
color="red darken-1"
@click="deleteMutate"
></v-btn>
</v-col>
</v-row>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</v-row>
<li v-for="license in licenseGroup.licenses" :key="license.id">
<ListViewElement :license="license" />
</li>
</div>
@ -9,9 +62,32 @@
<script setup lang="ts">
import ListViewElement from "./ListViewElement.vue";
import { License } from "@/types";
import { LicenseGroup } from "@/types";
import { Trash } from "lucide-vue-next";
import { ref } from "vue";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { axiosInstance } from "@/client";
const props = defineProps<{ licenseGroupName: string; licenses: License[] }>();
const { licenseGroup } = defineProps<{ licenseGroup: LicenseGroup }>();
const deleteDialog = ref(false);
const queryClient = useQueryClient();
const id = licenseGroup.id;
const { mutate: deleteMutate } = useMutation({
mutationFn: async () => {
await axiosInstance.delete(`/groups/${id}`);
},
onError: (error) => {
console.log(error);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["licenses"] });
deleteDialog.value = false;
},
});
</script>
<style scoped>

View file

@ -179,7 +179,6 @@
</v-card>
</v-dialog>
</div>
<!-- Search -->
<v-text-field
class="compact-form mr-2"
@ -190,6 +189,7 @@
hide-details
single-line
clearable
v-model="search"
rounded="pill"
></v-text-field>
@ -214,6 +214,7 @@ import UsersDialog from "./NavBarIcons/UsersDialog.vue";
import UsersEditActions from "./NavBarIcons/UsersEditActions.vue";
import { axiosInstance } from "@/client";
import { LicenseGroup, CreateLicenseDto, CreateGroupDto, User } from "@/types";
import { search } from "@/store";
const queryClient = useQueryClient();

View file

@ -1,4 +1,5 @@
<template>
<template v-if="visible">
<v-sheet elevation="4" width="96vw" rounded="lg" class="mt-3">
<v-expansion-panels>
<v-expansion-panel>
@ -53,7 +54,9 @@
class="logo mr-1"
width="24"
/>
<span v-if="license.amount == null || license.amount == undefined">
<span
v-if="license.amount == null || license.amount == undefined"
>
<Infinity />
</span>
<span v-else>
@ -87,12 +90,15 @@
</v-expansion-panels>
</v-sheet>
</template>
</template>
<script setup lang="ts">
import { License } from "@/types";
import { Infinity, KeyRound, CalendarCheck, CalendarX } from "lucide-vue-next";
import UpdateDialog from "./UpdateDialog.vue";
import DeleteDialog from "./DeleteDialog.vue";
import { ComputedRef, inject, ref, watch } from "vue";
import { key } from "@/store";
const { license } = defineProps<{
license: License;
@ -104,6 +110,21 @@ const { license } = defineProps<{
amount?: number;
notes?: string;*/
}>();
const visible = ref(true);
const { searching, visibleIds } = inject(key) as {
searching: ComputedRef<boolean>;
visibleIds: ComputedRef<string[]>;
};
watch([searching, visibleIds], ([searching, visibleIds]) => {
if (!searching) {
visible.value = true;
} else {
visible.value = visibleIds.includes(license.id);
}
});
</script>
<style scoped>

View file

@ -6,10 +6,7 @@
<div v-else-if="isError">Error: {{ error?.message }}</div>
<ul v-else-if="data">
<li v-for="group in data" :key="group.id">
<CategoryContainer
:licenseGroupName="group.name"
:licenses="group.licenses"
/>
<CategoryContainer :licenseGroup="group" />
</li>
</ul>
</div>
@ -21,7 +18,10 @@ import HeaderBar from "./HeaderBar.vue";
import CategoryContainer from "./CategoryContainer.vue";
import { useQuery } from "@tanstack/vue-query";
import { axiosInstance } from "@/client";
import { LicenseGroup } from "@/types";
import { LicenseGroup, License } from "@/types";
import { search, key } from "@/store";
import MiniSearch from "minisearch";
import { computed, provide } from "vue";
const { isPending, isError, data, error } = useQuery({
queryKey: ["licenses"],
@ -31,6 +31,36 @@ const { isPending, isError, data, error } = useQuery({
return res.data;
},
});
const searchEngine = computed(() => {
let minisearch = new MiniSearch({
fields: ["name", "description", "id"],
searchOptions: {
boost: { name: 2 },
prefix: true,
},
});
let licenses: License[] = [];
data.value?.forEach((group) => {
group.licenses.forEach((license) => licenses.push(license));
});
console.log(licenses);
minisearch.addAll(licenses);
return minisearch;
});
const searching = computed(() => search.value !== "");
const visibleIds = computed(() => {
return searchEngine.value.search(search.value).map((searchResult) => {
return searchResult.id;
});
});
provide(key, {
visibleIds,
searching,
});
</script>
<style scoped>

View file

@ -1,15 +1,23 @@
import { reactive } from 'vue';
import { ComputedRef, InjectionKey, reactive, ref } from "vue";
export const store = reactive<{token: string | null, setToken: (token: string | null) => void}>({
token: localStorage.getItem('token') || null,
export const store = reactive<{
token: string | null;
setToken: (token: string | null) => void;
}>({
token: localStorage.getItem("token") || null,
setToken: (token: string | null) => {
store.token = token
store.token = token;
if (token) {
localStorage.setItem('token', token)
localStorage.setItem("token", token);
} else {
localStorage.removeItem('token')
}
localStorage.removeItem("token");
}
},
});
export const search = ref("");
export const key = Symbol() as InjectionKey<{
searching: ComputedRef<boolean>;
visibleIds: ComputedRef<string[]>;
}>;