some work on the license manager

This commit is contained in:
2026-07-10 16:55:48 +02:00
parent af80c7d582
commit 4a15f1e351
9 changed files with 736 additions and 624 deletions
+84 -55
View File
@@ -1,20 +1,20 @@
<script setup lang="ts">
import { License } from '@/types'
import { toLocalDate } from '@/lib/utils'
import { newLicense } from '@/types/index.d'
import { toLocalDate, toShortISOString, hotkey } from '@/lib/utils'
import Fuse from 'fuse.js'
import { onMounted, ref, computed } from 'vue'
import AppLayout from '@/layouts/AppLayout.vue'
import AppHeader from '@/components/AppHeader.vue'
import { Head } from '@inertiajs/vue3'
import { Table, TableRow, TableBody, TableCell, TableFooter, TableHead, TableHeader } from '@/components/ui/crm-table'
import { Table, TableRow, TableBody, TableCell, TableHead, TableHeader } from '@/components/ui/crm-table'
import { Input } from '@/components/ui/crm-input'
import { Badge } from '@/components/ui/crm-badge'
import Button from '@/components/ui/crm-button/Button.vue'
import { ButtonGroup } from '@/components/ui/button-group'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Kbd, KbdGroup } from '@/components/ui/kbd'
import LicenseService from '@/services/LicenseService';
import { Check, Download, Trash2, Plus, Delete, Search } from 'lucide-vue-next'
import { Download, Trash2, Plus, Delete, Search, CheckCircle, CheckCircle2, XCircle, CircleQuestionMark, BadgeQuestionMark, Eye } from 'lucide-vue-next'
import { alertStore } from "@/stores/alertStore"
import { toast } from 'vue-sonner';
@@ -29,6 +29,8 @@ const alert = alertStore()
onMounted(() => {
licenses.value = props.licenseData
searchField.value = document.getElementById('search')
hotkey('n', createLicense)
})
const fuse = computed(() => {
@@ -39,22 +41,36 @@ const fuse = computed(() => {
})
const filteredLicenses = computed(() => {
let licenses = props.licenseData
let filteredLicenses = licenses.value
// Filter by search query
if (searchQuery.value) {
licenses = fuse.value.search(searchQuery.value).map(result => result.item)
filteredLicenses = fuse.value.search(searchQuery.value).map(result => result.item)
}
return licenses
return filteredLicenses
})
const downloadLicense = (id: number) => {
window.open('/licenses/download/' + id)
const createLicense = () => {
LicenseService.createLicense(newLicense()).then(license => {
if (license) licenses.value.unshift(license)
})
}
const deleteLicense = (id: number) => {
const updateLicense = (license: License) => {
LicenseService.updateLicense(license).then((response) => {
if (response) license = response
})
}
const downloadLicense = (license: License) => {
window.open('/licenses/download/' + license.id)
}
const deleteLicense = (license: License) => {
if (license.id == 0) {
licenses.value = licenses.value.filter(l => l != license)
return
}
alert.show(
"Möchtest Du diese Lizenz wirklich löschen?",
null,
@@ -62,8 +78,8 @@ const deleteLicense = (id: number) => {
actionText: "Löschen",
actionVariant: "destructive",
onAction: async () => {
LicenseService.deleteLicense(id).then(() => {
licenses.value = licenses.value.filter(license => license.id != id)
LicenseService.deleteLicense(license.id).then(() => {
licenses.value = licenses.value.filter(license => license.id != license.id)
}).catch((error) => {
toast.error('Fehler beim Löschen der Lizenz', { duration: 5000, description: error.message })
})
@@ -72,6 +88,10 @@ const deleteLicense = (id: number) => {
)
}
const remToPx = (rem: number) => {
return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
}
</script>
<template>
@@ -99,24 +119,25 @@ const deleteLicense = (id: number) => {
</span>
</template>
<template #right>
<!-- New button -->
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button @click="">
<Plus />
Neu
</Button>
</TooltipTrigger>
<TooltipContent>
<span>Neue Lizenz anlegen</span>
<KbdGroup class="ml-2">
<Kbd class="visible-mac"> N</Kbd>
<Kbd class="visible-pc">Ctrl N</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<div class="flex gap-2 items-center">
<TooltipProvider>
<!-- New button -->
<Tooltip>
<TooltipTrigger>
<Button @click="createLicense">
<Plus />
Neu
</Button>
</TooltipTrigger>
<TooltipContent>
<span>Neue Lizenz anlegen</span>
<KbdGroup class="ml-2">
<Kbd>N</Kbd>
</KbdGroup>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</template>
</AppHeader>
@@ -124,10 +145,12 @@ const deleteLicense = (id: number) => {
<Table>
<TableHeader>
<TableRow>
<TableHead class="w-1/100 text-center">Gültig bis</TableHead>
<TableHead>Produkt</TableHead>
<TableHead>Version</TableHead>
<TableHead colspan="2">Lizenznehmer</TableHead>
<TableHead class="w-1/100 text-center">Gültig</TableHead>
<TableHead class="w-1/100">Perm.</TableHead>
<TableHead class="pl-7">Ablaufdatum</TableHead>
<TableHead class="pl-7">Produkt</TableHead>
<TableHead class="pl-7">Version</TableHead>
<TableHead class="pl-7" colspan="2">Lizenznehmer</TableHead>
<TableHead class="w-1/100 print:hidden"></TableHead>
</TableRow>
</TableHeader>
@@ -136,39 +159,45 @@ const deleteLicense = (id: number) => {
<TableRow v-for="license in filteredLicenses" :key="license.id">
<TableCell class="text-center">
<!-- <Check class="text-success inline-block" /> -->
<Badge v-if="license.isPerpetual" variant="secondary">
Permanent
</Badge>
<Badge v-else-if="Math.random() > 0.5" variant="success">
{{ toLocalDate(license.expirationDate || '') }}
</Badge>
<Badge v-else variant="destructive">
{{ toLocalDate(license.expirationDate || '') }}
</Badge>
<CircleQuestionMark class="inline-block text-muted-foreground" :size="remToPx(1)"
v-if="license.validationInfo === undefined" />
<CheckCircle2 class="inline-block text-success0" :size="remToPx(1)"
v-else-if="license.validationInfo.isExpired === false" />
<XCircle class="inline-block text-destructive0" :size="remToPx(1)" v-else />
</TableCell>
<TableCell class="text-center">
<input type="checkbox" v-model="license.isPerpetual"
v-on:change="updateLicense(license)" />
</TableCell>
<TableCell class="whitespace-nowrap">
<div class="text-center" v-if="license.isPerpetual"></div>
<Input v-else type="date" :modelValue="toShortISOString(license.expirationDate || new Date())"
v-on:blur="updateLicense(license)" placeholder="Datum" />
</TableCell>
<TableCell>
{{ license.product }}
<Input type="text" :modelValue="license.product" placeholder="Produkt Name"
v-on:blur="updateLicense(license)" />
</TableCell>
<TableCell>
{{ license.version }}
<Input type="text" :modelValue="license.version" placeholder="1.0.0"
v-on:blur="updateLicense(license)" />
</TableCell>
<TableCell>
{{ license.name }}<br />
<Input type="text" v-model="license.name" placeholder="Firma"
v-on:blur="updateLicense(license)" />
</TableCell>
<TableCell>
<a :href="'mailto:' + license.email + '?subject=Ihre ' + license.product + '-Lizenz'"
class="text-muted-foreground text-sm">{{ license.email
}}</a>
<Input type="email" :modelValue="license.email" placeholder="E-Mail"
v-on:blur="updateLicense(license)" />
</TableCell>
<TableCell class="print:hidden">
<ButtonGroup>
<Button size="icon" variant="ghost" @click="downloadLicense(license.id)">
<Button class="size-7" variant="ghost" @click="downloadLicense(license)">
<Download class="text-muted-foreground" />
</Button>
<Button size="icon" variant="ghost" @click="deleteLicense(license.id)">
<Button class="size-7" variant="ghost" @click="deleteLicense(license)">
<Trash2 class="text-muted-foreground" />
</Button>
</ButtonGroup>