mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
migrate pages to shadcn (#628)
* feat: migrate tools page and label generator to shadcn * chore: lint issues * feat: also do profile page * feat: shadcn 404 page * feat: login page shadcn * fix: daisyui ironically breaks the z height for the login page * feat: componentise the language selector and add it to the login page * feat: use nuxtlink * feat: card and table made more shadcn * feat: shadcn statscard * chore: lint * feat: shadcn labelchip and locationcard * feat: shadcn locations page * refactor: remove unused new item page * chore: lint * feat: shadcn item card * fix: wrapping of location and lint * feat: ctrl enter in text area in form submits form * feat: begin shadcn locations page and remove pageqrcode comp in favour of integrating it into labelmaker * chore: lint + remove unused code * fix: remove uneeded margin * feat: shadcn labels page and fix some issues with location * feat: shadcn scanner * chore: lint * feat: begin shadcning item pages * feat: shadcn maintenance page * feat: begin shadcn search page * fix: quick switch blurry text and crashing page when switching + incorrect z height for create menu * feat: finish shadcn search page * chore: lint * feat: shadcn edit item page * fix: quickmenumodal bug * feat: shadcn item details page * feat: remove all non-color related daisyui classes * fix: type error * fix: quick menu modal again :(
This commit is contained in:
@@ -10,6 +10,14 @@
|
||||
import MdiPencil from "~icons/mdi/pencil";
|
||||
import MdiAccountMultiple from "~icons/mdi/account-multiple";
|
||||
import { getLocaleCode } from "~/composables/use-formatters";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { useDialog } from "@/components/ui/dialog-provider";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { badgeVariants } from "@/components/ui/badge";
|
||||
import LanguageSelector from "~/components/App/LanguageSelector.vue";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "@/components/ui/tooltip";
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
@@ -21,6 +29,8 @@
|
||||
const api = useUserApi();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const { openDialog, closeDialog } = useDialog();
|
||||
|
||||
const currencies = computedAsync(async () => {
|
||||
const resp = await api.group.currencies();
|
||||
if (resp.error) {
|
||||
@@ -35,9 +45,6 @@
|
||||
function setDisplayHeader() {
|
||||
preferences.value.displayHeaderDecor = !preferences.value.displayHeaderDecor;
|
||||
}
|
||||
function setLanguage(lang: string) {
|
||||
preferences.value.language = lang;
|
||||
}
|
||||
|
||||
// Currency Selection
|
||||
const currency = ref<CurrenciesCurrency>({
|
||||
@@ -56,10 +63,6 @@
|
||||
return fmtCurrency(1000, currency.value?.code ?? "USD", getLocaleCode());
|
||||
});
|
||||
|
||||
const dateExample = computed(() => {
|
||||
return fmtDate(new Date(Date.now() - 15 * 60000), "relative");
|
||||
});
|
||||
|
||||
const { data: group } = useAsyncData(async () => {
|
||||
const { data } = await api.group.get();
|
||||
return data;
|
||||
@@ -159,16 +162,11 @@
|
||||
|
||||
const passwordChange = reactive({
|
||||
loading: false,
|
||||
dialog: false,
|
||||
current: "",
|
||||
new: "",
|
||||
isValid: false,
|
||||
});
|
||||
|
||||
function openPassChange() {
|
||||
passwordChange.dialog = true;
|
||||
}
|
||||
|
||||
async function changePassword() {
|
||||
passwordChange.loading = true;
|
||||
if (!passwordChange.isValid) {
|
||||
@@ -184,7 +182,7 @@
|
||||
}
|
||||
|
||||
toast.success("Password changed successfully.");
|
||||
passwordChange.dialog = false;
|
||||
closeDialog("change-password");
|
||||
passwordChange.new = "";
|
||||
passwordChange.current = "";
|
||||
passwordChange.loading = false;
|
||||
@@ -201,7 +199,6 @@
|
||||
|
||||
const targetID = ref("");
|
||||
const notifier = ref<NotifierCreate | null>(null);
|
||||
const notifierDialog = ref(false);
|
||||
|
||||
function openNotifierDialog(v: NotifierOut | null) {
|
||||
if (v) {
|
||||
@@ -219,7 +216,7 @@
|
||||
};
|
||||
}
|
||||
|
||||
notifierDialog.value = true;
|
||||
openDialog("create-notifier");
|
||||
}
|
||||
|
||||
async function createNotifier() {
|
||||
@@ -243,7 +240,7 @@
|
||||
}
|
||||
|
||||
notifier.value = null;
|
||||
notifierDialog.value = false;
|
||||
closeDialog("create-notifier");
|
||||
|
||||
await notifiers.refresh();
|
||||
}
|
||||
@@ -264,7 +261,7 @@
|
||||
}
|
||||
|
||||
notifier.value = null;
|
||||
notifierDialog.value = false;
|
||||
closeDialog("create-notifier");
|
||||
targetID.value = "";
|
||||
|
||||
await notifiers.refresh();
|
||||
@@ -305,10 +302,12 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<BaseModal v-model="passwordChange.dialog">
|
||||
<template #title> {{ $t("profile.change_password") }} </template>
|
||||
<Dialog dialog-id="changePassword">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle> {{ $t("profile.change_password") }} </DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form @submit.prevent="changePassword">
|
||||
<FormPassword
|
||||
v-model="passwordChange.current"
|
||||
:label="$t('profile.current_password')"
|
||||
@@ -318,38 +317,42 @@
|
||||
<FormPassword v-model="passwordChange.new" :label="$t('profile.new_password')" placeholder="" />
|
||||
<PasswordScore v-model:valid="passwordChange.isValid" :password="passwordChange.new" />
|
||||
|
||||
<div class="flex">
|
||||
<BaseButton
|
||||
class="ml-auto"
|
||||
:loading="passwordChange.loading"
|
||||
:disabled="!passwordChange.isValid"
|
||||
type="submit"
|
||||
>
|
||||
{{ $t("global.submit") }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
</form>
|
||||
</BaseModal>
|
||||
<form @submit.prevent="changePassword">
|
||||
<DialogFooter>
|
||||
<Button :loading="passwordChange.loading" :disabled="!passwordChange.isValid" type="submit">
|
||||
{{ $t("global.submit") }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<BaseModal v-model="notifierDialog">
|
||||
<template #title> {{ $t("profile.notifier_modal", { type: notifier != null }) }} </template>
|
||||
<Dialog dialog-id="create-notifier">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle> {{ $t("profile.notifier_modal", { type: notifier != null }) }} </DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form @submit.prevent="createNotifier">
|
||||
<template v-if="notifier">
|
||||
<FormTextField v-model="notifier.name" :label="$t('global.name')" class="mb-2" />
|
||||
<FormTextField v-model="notifier.url" :label="$t('profile.url')" />
|
||||
<div class="max-w-[100px]">
|
||||
<FormCheckbox v-model="notifier.isActive" :label="$t('profile.enabled')" />
|
||||
<form @submit.prevent="createNotifier">
|
||||
<template v-if="notifier">
|
||||
<FormTextField v-model="notifier.name" :label="$t('global.name')" class="mb-2" />
|
||||
<FormTextField v-model="notifier.url" :label="$t('profile.url')" class="mb-2" />
|
||||
<div class="max-w-[100px]">
|
||||
<FormCheckbox v-model="notifier.isActive" :label="$t('profile.enabled')" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="mt-4 flex justify-between gap-2">
|
||||
<DialogFooter class="flex w-full">
|
||||
<Button :disabled="!(notifier && notifier.url)" type="button" @click="testNotifier">
|
||||
{{ $t("profile.test") }}
|
||||
</Button>
|
||||
<div class="grow"></div>
|
||||
<Button type="submit"> {{ $t("global.submit") }} </Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mt-4 flex justify-between gap-2">
|
||||
<BaseButton :disabled="!(notifier && notifier.url)" type="button" @click="testNotifier">
|
||||
{{ $t("profile.test") }}
|
||||
</BaseButton>
|
||||
<BaseButton type="submit"> {{ $t("global.submit") }} </BaseButton>
|
||||
</div>
|
||||
</form>
|
||||
</BaseModal>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<BaseContainer class="mb-6 flex flex-col gap-4">
|
||||
<BaseCard>
|
||||
@@ -365,37 +368,21 @@
|
||||
|
||||
<div class="p-4">
|
||||
<div class="flex gap-2">
|
||||
<BaseButton size="sm" @click="openPassChange"> {{ $t("profile.change_password") }} </BaseButton>
|
||||
<BaseButton size="sm" @click="generateToken"> {{ $t("profile.gen_invite") }} </BaseButton>
|
||||
<Button size="sm" @click="openDialog('changePassword')">
|
||||
{{ $t("profile.change_password") }}
|
||||
</Button>
|
||||
<Button size="sm" @click="generateToken"> {{ $t("profile.gen_invite") }} </Button>
|
||||
</div>
|
||||
<div v-if="token" class="flex items-center pl-1 pt-4">
|
||||
<CopyText class="btn btn-square btn-outline btn-primary btn-sm mr-2" :text="tokenUrl" />
|
||||
<div v-if="token" class="flex items-center gap-2 pl-1 pt-4">
|
||||
<CopyText :text="tokenUrl" />
|
||||
{{ tokenUrl }}
|
||||
</div>
|
||||
<div v-if="token" class="flex items-center pl-1 pt-4">
|
||||
<CopyText class="btn btn-square btn-outline btn-primary btn-sm mr-2" :text="token" />
|
||||
<div v-if="token" class="flex items-center gap-2 pl-1 pt-4">
|
||||
<CopyText :text="token" />
|
||||
{{ token }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control w-full p-5 pt-0">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ $t("profile.language") }}</span>
|
||||
</label>
|
||||
<select
|
||||
v-model="$i18n.locale"
|
||||
class="select select-bordered"
|
||||
@change="
|
||||
event => {
|
||||
setLanguage((event.target as HTMLSelectElement).value);
|
||||
}
|
||||
"
|
||||
>
|
||||
<option v-for="lang in $i18n.availableLocales" :key="lang" :value="lang">
|
||||
{{ $t(`languages.${lang}`) }} ({{ $t(`languages.${lang}`, 1, { locale: lang }) }})
|
||||
</option>
|
||||
</select>
|
||||
<p class="m-2 text-sm">{{ $t("profile.example") }}: {{ $t("global.created") }} {{ dateExample }}</p>
|
||||
</div>
|
||||
<LanguageSelector />
|
||||
</BaseCard>
|
||||
|
||||
<BaseCard>
|
||||
@@ -414,23 +401,29 @@
|
||||
<article v-for="n in notifiers.data.value" v-else :key="n.id" class="p-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<p class="mr-auto text-lg">{{ n.name }}</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<div class="tooltip" data-tip="Delete">
|
||||
<button class="btn btn-square btn-sm" @click="deleteNotifier(n.id)">
|
||||
<MdiDelete />
|
||||
</button>
|
||||
</div>
|
||||
<div class="tooltip" data-tip="Edit">
|
||||
<button class="btn btn-square btn-sm" @click="openNotifierDialog(n)">
|
||||
<MdiPencil />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<TooltipProvider :delay-duration="0" class="flex justify-end gap-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="destructive" size="icon" @click="deleteNotifier(n.id)">
|
||||
<MdiDelete />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent> Delete </TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" size="icon" @click="openNotifierDialog(n)">
|
||||
<MdiPencil />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent> Edit </TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-between py-1 text-sm">
|
||||
<p>
|
||||
<span v-if="n.isActive" class="badge badge-success"> {{ $t("profile.active") }} </span>
|
||||
<span v-else class="badge badge-error"> {{ $t("profile.inactive") }} </span>
|
||||
<span v-if="n.isActive" :class="badgeVariants()"> {{ $t("profile.active") }} </span>
|
||||
<span v-else :class="badgeVariants({ variant: 'destructive' })"> {{ $t("profile.inactive") }} </span>
|
||||
</p>
|
||||
<p>
|
||||
{{ $t("global.created") }}
|
||||
@@ -441,7 +434,7 @@
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<BaseButton size="sm" @click="openNotifierDialog"> {{ $t("global.create") }} </BaseButton>
|
||||
<Button size="sm" @click="openNotifierDialog"> {{ $t("global.create") }} </Button>
|
||||
</div>
|
||||
</BaseCard>
|
||||
|
||||
@@ -457,11 +450,32 @@
|
||||
</template>
|
||||
|
||||
<div v-if="group && currencies && currencies.length > 0" class="p-5 pt-0">
|
||||
<FormSelect v-model="currency" :label="$t('profile.currency_format')" :items="currencies" />
|
||||
<Label for="currency"> {{ $t("profile.currency_format") }} </Label>
|
||||
<Select
|
||||
id="currency"
|
||||
:model-value="currency.code"
|
||||
@update:model-value="
|
||||
event => {
|
||||
const newCurrency = currencies?.find(c => c.code === event);
|
||||
if (newCurrency) {
|
||||
currency = newCurrency;
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="c in currencies" :key="c.code" :value="c.code">
|
||||
{{ c.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p class="m-2 text-sm">{{ $t("profile.example") }}: {{ currencyExample }}</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<BaseButton size="sm" @click="updateGroup"> {{ $t("profile.update_group") }} </BaseButton>
|
||||
<Button size="sm" @click="updateGroup"> {{ $t("profile.update_group") }} </Button>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
@@ -479,11 +493,11 @@
|
||||
|
||||
<div class="px-4 pb-4">
|
||||
<div class="mb-3">
|
||||
<BaseButton size="sm" @click="setDisplayHeader">
|
||||
<Button variant="outline" size="sm" @click="setDisplayHeader">
|
||||
{{ $t("profile.display_header", { currentValue: preferences.displayHeaderDecor }) }}
|
||||
</BaseButton>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="homebox rounded-box grid grid-cols-1 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
<div class="homebox grid grid-cols-1 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
<div
|
||||
v-for="theme in themes"
|
||||
:key="theme.value"
|
||||
@@ -531,9 +545,9 @@
|
||||
</BaseSectionHeader>
|
||||
</template>
|
||||
<div class="border-t-2 border-gray-300 p-4 px-6">
|
||||
<BaseButton size="sm" class="btn-error" @click="deleteProfile">
|
||||
<Button size="sm" variant="destructive" @click="deleteProfile">
|
||||
{{ $t("profile.delete_account") }}
|
||||
</BaseButton>
|
||||
</Button>
|
||||
</div>
|
||||
</BaseCard>
|
||||
</BaseContainer>
|
||||
|
||||
Reference in New Issue
Block a user