Polish CSS for mobile editor. Add user logout from admin panel.

This commit is contained in:
Peaceultime 2024-12-03 14:22:02 +01:00
parent ecdfa947ac
commit 51a5d501be
12 changed files with 238 additions and 161 deletions

View File

@ -4,7 +4,7 @@
<NuxtLoadingIndicator /> <NuxtLoadingIndicator />
<TooltipProvider> <TooltipProvider>
<NuxtLayout> <NuxtLayout>
<div class="xl:ps-12 xl:pe-12 ps-6 pe-4 flex flex-1 justify-center overflow-auto max-h-full relative"> <div class="xl:px-12 xl:py-8 lg:px-8 lg:py-6 px-6 py-3 flex flex-1 justify-center overflow-auto max-h-full relative">
<NuxtPage></NuxtPage> <NuxtPage></NuxtPage>
</div> </div>
</NuxtLayout> </NuxtLayout>

View File

@ -1,5 +1,5 @@
<template> <template>
<TreeRoot v-bind="forward" v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 p-2 xl:text-base text-sm overflow-auto max-h-full"> <TreeRoot v-bind="forward" v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 overflow-auto max-h-full">
<DraggableTreeItem v-for="item in flattenItems" :key="item._id" v-bind="item" class="flex items-center outline-none relative cursor-pointer hover:bg-light-20 dark:hover:bg-dark-20 data-[selected]:bg-light-35 dark:data-[selected]:bg-dark-35" @select.prevent @toggle.prevent> <DraggableTreeItem v-for="item in flattenItems" :key="item._id" v-bind="item" class="flex items-center outline-none relative cursor-pointer hover:bg-light-20 dark:hover:bg-dark-20 data-[selected]:bg-light-35 dark:data-[selected]:bg-dark-35" @select.prevent @toggle.prevent>
<template #default="{ handleToggle, handleSelect, isExpanded, isSelected, isDragging, isDraggedOver }"> <template #default="{ handleToggle, handleSelect, isExpanded, isSelected, isDragging, isDraggedOver }">
<slot :handleToggle="handleToggle" <slot :handleToggle="handleToggle"

View File

@ -1,6 +1,6 @@
<template> <template>
<DropdownMenuRoot> <DropdownMenuRoot>
<DropdownMenuTrigger :disabled="disabled" ><slot /></DropdownMenuTrigger> <DropdownMenuTrigger :disabled="disabled"><slot /></DropdownMenuTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuContent :align="align" :side="side" class="z-50 outline-none bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35"> <DropdownMenuContent :align="align" :side="side" class="z-50 outline-none bg-light-20 dark:bg-dark-20 will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade border border-light-35 dark:border-dark-35">

View File

@ -1,5 +1,6 @@
<template> <template>
<Label class="flex justify-center items-center my-2">{{ label }} <Label class="flex justify-center items-center my-2">
<span class="md:text-base text-sm">{{ label }}</span>
<SwitchRoot v-model:checked="model" :disabled="disabled" <SwitchRoot v-model:checked="model" :disabled="disabled"
class="group mx-3 w-12 h-6 select-none transition-all border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 outline-none class="group mx-3 w-12 h-6 select-none transition-all border border-light-35 dark:border-dark-35 bg-light-20 dark:bg-dark-20 outline-none
data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 dark:hover:border-dark-50 focus:shadow-raw focus:shadow-light-40 dark:focus:shadow-dark-40 data-[state=checked]:bg-light-35 dark:data-[state=checked]:bg-dark-35 hover:border-light-50 dark:hover:border-dark-50 focus:shadow-raw focus:shadow-light-40 dark:focus:shadow-dark-40

View File

@ -7,6 +7,7 @@ export interface ShortcutConfig {
handler: Function handler: Function
usingInput?: string | boolean usingInput?: string | boolean
whenever?: WatchSource<boolean>[] whenever?: WatchSource<boolean>[]
prevent?: boolean
} }
export interface ShortcutsConfig { export interface ShortcutsConfig {
@ -29,6 +30,7 @@ interface Shortcut {
altKey: boolean altKey: boolean
// code?: string // code?: string
// keyCode?: number // keyCode?: number
prevent?: boolean
} }
const chainedShortcutRegex = /^[^-]+.*-.*[^-]+$/ const chainedShortcutRegex = /^[^-]+.*-.*[^-]+$/
@ -61,7 +63,8 @@ export const useShortcuts = (config: ShortcutsConfig, options: ShortcutsOptions
if (shortcut.key !== chainedKey) { continue } if (shortcut.key !== chainedKey) { continue }
if (shortcut.condition.value) { if (shortcut.condition.value) {
e.preventDefault() e.stopPropagation
shortcut.prevent && e.preventDefault()
shortcut.handler() shortcut.handler()
} }
clearChainedInput() clearChainedInput()
@ -139,7 +142,7 @@ export const useShortcuts = (config: ShortcutsConfig, options: ShortcutsOptions
if (typeof shortcutConfig === 'function') { if (typeof shortcutConfig === 'function') {
shortcut.handler = shortcutConfig shortcut.handler = shortcutConfig
} else if (typeof shortcutConfig === 'object') { } else if (typeof shortcutConfig === 'object') {
shortcut = { ...shortcut, handler: shortcutConfig.handler } shortcut = { ...shortcut, handler: shortcutConfig.handler, prevent: shortcutConfig.prevent }
} }
if (!shortcut.handler) { if (!shortcut.handler) {

BIN
db.sqlite

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,13 +13,15 @@
<div class="flex items-center px-2"> <div class="flex items-center px-2">
<Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip> <Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip>
<Tooltip v-if="!loggedIn" :message="'Se connecter'" side="right"> <Tooltip v-if="!loggedIn" :message="'Se connecter'" side="right">
<div class="hover:border-opacity-70 flex"> <NuxtLink :to="{ name: 'user-login' }">
<div class="hover:border-opacity-70 flex items-center">
<Icon :icon="'radix-icons:person'" class="w-7 h-7 p-1" /> <Icon :icon="'radix-icons:person'" class="w-7 h-7 p-1" />
</div> </div>
</NuxtLink>
</Tooltip> </Tooltip>
<Tooltip v-else :message="'Mon profil'" side="right"> <Tooltip v-else :message="'Mon profil'" side="right">
<DropdownMenu :options="options" side="bottom" align="end"> <DropdownMenu :options="options" side="bottom" align="end">
<div class="hover:border-opacity-70 flex"> <div class="hover:border-opacity-70 flex items-center">
<Icon :icon="'radix-icons:avatar'" class="w-7 h-7 p-1" /> <Icon :icon="'radix-icons:avatar'" class="w-7 h-7 p-1" />
</div> </div>
</DropdownMenu> </DropdownMenu>
@ -28,8 +30,8 @@
</div> </div>
<div class="flex flex-1 flex-row relative h-screen overflow-hidden"> <div class="flex flex-1 flex-row relative h-screen overflow-hidden">
<CollapsibleContent asChild forceMount> <CollapsibleContent asChild forceMount>
<div class="bg-light-0 md:py-11 dark:bg-dark-0 z-40 xl:w-96 md:w-[15em] w-full border-r border-light-30 dark:border-dark-30 flex flex-col justify-between max-md:absolute max-md:-top-0 max-md:-bottom-0 md:left-0 max-md:data-[state=closed]:-left-full max-md:transition-[left] py-8 max-md:z-40 max-md:data-[state=open]:left-0"> <div class="bg-light-0 dark:bg-dark-0 z-40 xl:w-96 md:w-[15em] w-full border-r border-light-30 dark:border-dark-30 flex flex-col justify-between max-md:absolute max-md:-top-0 max-md:-bottom-0 md:left-0 max-md:data-[state=closed]:-left-full max-md:transition-[left] max-md:z-40 max-md:data-[state=open]:left-0">
<div class="relative bottom-6 flex flex-col gap-4 xl:px-6 px-3"> <div class="flex flex-col gap-4 xl:px-6 px-3 py-4">
<div class="flex justify-between items-center max-md:hidden"> <div class="flex justify-between items-center max-md:hidden">
<NuxtLink class=" text-light-100 dark:text-dark-100 hover:text-opacity-70 max-md:ps-6" aria-label="Accueil" :to="{ path: '/', force: true }"> <NuxtLink class=" text-light-100 dark:text-dark-100 hover:text-opacity-70 max-md:ps-6" aria-label="Accueil" :to="{ path: '/', force: true }">
<Avatar src="/logo.dark.svg" class="dark:block hidden" /> <Avatar src="/logo.dark.svg" class="dark:block hidden" />
@ -74,7 +76,7 @@
</template> </template>
</Tree> </Tree>
</div> </div>
<div class="xl:px-12 px-6 text-start text-xs text-light-60 dark:text-dark-60 relative top-4"> <div class="xl:px-12 px-6 py-4 text-center text-xs text-light-60 dark:text-dark-60">
<NuxtLink class="hover:underline italic" :to="{ name: 'roadmap' }">Roadmap</NuxtLink> - <NuxtLink class="hover:underline italic" :to="{ name: 'legal' }">Mentions légales</NuxtLink> <NuxtLink class="hover:underline italic" :to="{ name: 'roadmap' }">Roadmap</NuxtLink> - <NuxtLink class="hover:underline italic" :to="{ name: 'legal' }">Mentions légales</NuxtLink>
<p>Copyright Peaceultime - 2024</p> <p>Copyright Peaceultime - 2024</p>
</div> </div>

View File

@ -135,6 +135,27 @@ async function editPermissions(user: User)
}); });
} }
} }
async function logout(user: User)
{
try
{
await $fetch(`/api/admin/user/${user.id}/logout`, {
method: 'POST',
});
user.session.length = 0;
toaster.add({
duration: 10000, type: 'success', content: 'L\'utilisateur vient d\'être déconnecté.', timer: true,
});
}
catch(e)
{
toaster.add({
duration: 10000, type: 'error', content: (e as any).message, timer: true,
});
}
}
</script> </script>
<template> <template>
@ -178,7 +199,7 @@ async function editPermissions(user: User)
</DialogTitle> </DialogTitle>
<div class="flex flex-1 justify-end gap-4"> <div class="flex flex-1 justify-end gap-4">
<DialogClose asChild><Button>Non</Button></DialogClose> <DialogClose asChild><Button>Non</Button></DialogClose>
<DialogClose asChild><Button class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Oui</Button></DialogClose> <DialogClose asChild><Button @click="() => logout(user)" class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Oui</Button></DialogClose>
</div> </div>
</DialogContent> </DialogContent>
</DialogPortal> </DialogPortal>

View File

@ -1,22 +1,27 @@
<template> <template>
<!-- TODO: Put back the Collapsible components, I removed it before I decided to add the file editor into this page --> <Head>
<Title>d[any] - Modification</Title>
</Head>
<ClientOnly> <ClientOnly>
<div class="xl:-ms-12 xl:-me-12 -ms-6 -me-4 flex flex-1"> <CollapsibleRoot asChild class="flex flex-1 flex-col xl:-mx-12 xl:-my-8 lg:-mx-8 lg:-my-6 -mx-6 -my-3 overflow-hidden" v-model="open">
<div>
<div class="z-50 md:hidden flex w-full items-center justify-between h-12 border-b border-light-35 dark:border-dark-35"> <div class="z-50 md:hidden flex w-full items-center justify-between h-12 border-b border-light-35 dark:border-dark-35">
<div class="flex items-center px-2"> <div class="flex items-center px-2">
<Button icon class="ms-2 !bg-transparent group" disabled> <CollapsibleTrigger asChild>
<Button icon class="ms-2 !bg-transparent group">
<Icon class="group-data-[state=open]:hidden" icon="radix-icons:hamburger-menu" /> <Icon class="group-data-[state=open]:hidden" icon="radix-icons:hamburger-menu" />
<Icon class="group-data-[state=closed]:hidden" icon="radix-icons:cross-1" /> <Icon class="group-data-[state=closed]:hidden" icon="radix-icons:cross-1" />
</Button> </Button>
<span class="text-light-100 dark:text-dark-100 hover:text-opacity-70 max-md:ps-6" aria-label="Accueil">Accueil</span> </CollapsibleTrigger>
</div> </div>
<div class="flex items-center px-2"> <div class="flex items-center px-2">
<Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip> <Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip>
</div> </div>
</div> </div>
<div class="flex flex-1 flex-row relative h-screen overflow-hidden"> <div class="flex flex-1 flex-row relative overflow-hidden">
<div class="bg-light-0 md:py-11 dark:bg-dark-0 z-40 xl:w-96 md:w-[15em] w-full border-r border-light-30 dark:border-dark-30 flex flex-col justify-between max-md:absolute max-md:-top-0 max-md:-bottom-0 md:left-0 max-md:data-[state=closed]:-left-full max-md:transition-[left] py-8 max-md:z-40 max-md:data-[state=open]:left-0"> <CollapsibleContent asChild forceMount>
<div class="relative bottom-6 flex flex-col gap-4 xl:px-6 px-3"> <div class=" overflow-hidden bg-light-0 dark:bg-dark-0 z-40 xl:w-96 md:w-[15em] max-h-full w-full border-r border-light-30 dark:border-dark-30 flex flex-col justify-between max-md:absolute max-md:-top-0 max-md:-bottom-0 md:left-0 max-md:data-[state=closed]:-left-full max-md:transition-[left] max-md:z-40 max-md:data-[state=open]:left-0">
<div class="flex flex-col gap-4 xl:px-6 px-3 py-4">
<div class="flex justify-between items-center max-md:hidden"> <div class="flex justify-between items-center max-md:hidden">
<div class=" text-light-100 dark:text-dark-100 hover:text-opacity-70 max-md:ps-6" aria-label="Accueil"> <div class=" text-light-100 dark:text-dark-100 hover:text-opacity-70 max-md:ps-6" aria-label="Accueil">
<Avatar src="/logo.dark.svg" class="dark:block hidden" /> <Avatar src="/logo.dark.svg" class="dark:block hidden" />
@ -27,8 +32,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex-1 xl:px-6 px-3 max-w-full max-h-full" v-if="navigation"> <div class="flex flex-1 flex-col max-w-full max-h-full overflow-hidden py-3" v-if="navigation">
<div class="flex flex-1 flex-row justify-between items-center mb-4"> <div class="flex flex-row justify-between items-center mb-4 px-6">
<div class="flex flex-1 flex-row justify-start items-center gap-4"> <div class="flex flex-1 flex-row justify-start items-center gap-4">
<Tooltip side="top" message="Annuler (Ctrl+Shift+W)" ><Button icon @click="router.go(-1)"><Icon class="w-5 h-5" icon="radix-icons:arrow-left" /></Button></Tooltip> <Tooltip side="top" message="Annuler (Ctrl+Shift+W)" ><Button icon @click="router.go(-1)"><Icon class="w-5 h-5" icon="radix-icons:arrow-left" /></Button></Tooltip>
<Tooltip side="top" message="Enregistrer (Ctrl+S)" ><Button icon :loading="saveStatus === 'pending'" @click="save(true)"><Icon class="w-5 h-5" icon="radix-icons:check" /></Button></Tooltip> <Tooltip side="top" message="Enregistrer (Ctrl+S)" ><Button icon :loading="saveStatus === 'pending'" @click="save(true)"><Icon class="w-5 h-5" icon="radix-icons:check" /></Button></Tooltip>
@ -36,10 +41,10 @@
</div> </div>
<div class="flex flex-row justify-end items-center gap-4"> <div class="flex flex-row justify-end items-center gap-4">
<AlertDialogRoot v-if="selected"> <AlertDialogRoot v-if="selected">
<AlertDialogTrigger as="span"><Tooltip side="top" message="Supprimer"><Button icon class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red" ><Icon class="w-5 h-5" icon="radix-icons:trash" /></Button></Tooltip></AlertDialogTrigger> <Tooltip side="top" message="Supprimer"><AlertDialogTrigger as="span"><Button icon class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red" ><Icon class="w-5 h-5" icon="radix-icons:trash" /></Button></AlertDialogTrigger></Tooltip>
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" /> <AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
<AlertDialogContent class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100 flex flex-row items-center"> <AlertDialogContent class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100 flex md:flex-row flex-col gap-4 items-center">
<AlertDialogTitle class="text-xl font-semibold">Supprimer <span>{{ selected.title }}</span><span v-if="selected.children"> et tous ces enfants</span> ?</AlertDialogTitle> <AlertDialogTitle class="text-xl font-semibold">Supprimer <span>{{ selected.title }}</span><span v-if="selected.children"> et tous ces enfants</span> ?</AlertDialogTitle>
<div class="flex flex-1 flex-row gap-4 justify-end"> <div class="flex flex-1 flex-row gap-4 justify-end">
<AlertDialogAction asChild @click="navigation = tree.remove(navigation, getPath(selected))"><Button class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction> <AlertDialogAction asChild @click="navigation = tree.remove(navigation, getPath(selected))"><Button class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction>
@ -48,6 +53,7 @@
</AlertDialogContent> </AlertDialogContent>
</AlertDialogPortal> </AlertDialogPortal>
</AlertDialogRoot> </AlertDialogRoot>
<Tooltip side="top" message="Nouveau">
<DropdownMenu align="center" side="bottom" :options="[{ <DropdownMenu align="center" side="bottom" :options="[{
type: 'item', type: 'item',
label: 'Markdown', label: 'Markdown',
@ -71,20 +77,21 @@
icon: 'radix-icons:file-text', icon: 'radix-icons:file-text',
select: () => add('file'), select: () => add('file'),
}]"> }]">
<Tooltip side="top" message="Nouveau" ><Button icon><Icon class="w-5 h-5" icon="radix-icons:plus" /></Button></Tooltip> <Button icon><Icon class="w-5 h-5" icon="radix-icons:plus" /></Button>
</DropdownMenu> </DropdownMenu>
</Tooltip>
</div> </div>
</div> </div>
<DraggableTree class="-mx-3 list-none select-none text-light-100 dark:text-dark-100 p-2 xl:text-base text-sm overflow-auto" <DraggableTree class="ps-4 pe-2 xl:text-base text-sm"
:items="navigation ?? undefined" :get-key="(item: Partial<ProjectExtendedItem>) => item.path !== undefined ? getPath(item as ProjectExtendedItem) : ''" @updateTree="drop" :items="navigation ?? undefined" :get-key="(item: Partial<ProjectExtendedItem>) => item.path !== undefined ? getPath(item as ProjectExtendedItem) : ''" @updateTree="drop"
v-model="selected" :defaultExpanded="defaultExpanded" > v-model="selected" :defaultExpanded="defaultExpanded" >
<template #default="{ handleToggle, handleSelect, isExpanded, isSelected, isDragging, item }"> <template #default="{ handleToggle, handleSelect, isExpanded, isSelected, isDragging, item }">
<div class="flex flex-1 items-center px-2" :class="{ 'opacity-50': isDragging }" :style="{ 'padding-left': `${item.level - 0.5}em` }"> <div class="flex flex-1 items-center px-2 max-w-full pe-4" :class="{ 'opacity-50': isDragging }" :style="{ 'padding-left': `${item.level - 0.5}em` }">
<span class="py-2 px-2" @click="handleToggle" v-if="item.hasChildren" > <span class="py-2 px-2" @click="handleToggle" v-if="item.hasChildren" >
<Icon :icon="isExpanded ? 'lucide:folder-open' : 'lucide:folder'"/> <Icon :icon="isExpanded ? 'lucide:folder-open' : 'lucide:folder'"/>
</span> </span>
<Icon v-else-if="iconByType[item.value.type]" :icon="iconByType[item.value.type]" class="group-[:hover]:text-accent-purple mx-2" @click="handleSelect" /> <Icon v-else-if="iconByType[item.value.type]" :icon="iconByType[item.value.type]" class="group-[:hover]:text-accent-purple mx-2" @click="handleSelect" />
<div class="pl-3 py-1 flex-1 truncate" :title="item.value.title" @click="handleSelect"> <div class="pl-3 py-1 flex-1 truncate" :title="item.value.title" @click="handleSelect" :class="{ 'font-semibold': item.hasChildren }">
{{ item.value.title }} {{ item.value.title }}
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
@ -111,24 +118,25 @@
</DraggableTree> </DraggableTree>
</div> </div>
</div> </div>
<div class="flex flex-1 flex-row"> </CollapsibleContent>
<div v-if="selected" class="xl:p-12 lg:p-8 py-4 flex flex-1 flex-col items-start justify-start max-h-full"> <div class="flex flex-1 flex-row max-h-full overflow-hidden">
<div v-if="selected" class="xl:px-12 xl:py-8 lg:px-8 lg:py-6 px-6 py-3 flex flex-1 flex-col items-start justify-start max-h-full relative">
<Head> <Head>
<Title>d[any] - Modification de {{ selected.title }}</Title> <Title>d[any] - Modification de {{ selected.title }}</Title>
</Head> </Head>
<div class=""> <div>
<input type="text" v-model="selected.title" placeholder="Titre" class="inline h-16 w-full caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 appearance-none outline-none py-1 text-5xl font-thin bg-transparent" /> <input type="text" v-model="selected.title" placeholder="Titre" class="inline md:h-16 h-12 w-full caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 appearance-none outline-none py-1 md:text-5xl text-4xl font-thin bg-transparent" />
<div class="flex flex-col justify-start items-start"> <div class="flex flex-col justify-start items-start">
<Switch label="Chemin personnalisé" v-model="selected.customPath" /> <Switch label="Chemin personnalisé" v-model="selected.customPath" />
<span> <span>
<pre v-if="selected.customPath" class="flex items-center">/{{ selected.parent !== '' ? selected.parent + '/' : '' }}<TextInput v-model="selected.name" @input="(e) => { <pre v-if="selected.customPath" class="flex md:items-center md:text-base md:text-nowrap text-wrap md:flex-nowrap flex-wrap text-sm">/{{ selected.parent !== '' ? selected.parent + '/' : '' }}<TextInput v-model="selected.name" @input="(e) => {
if(selected && selected.customPath) if(selected && selected.customPath)
{ {
selected.name = parsePath(selected.name); selected.name = parsePath(selected.name);
rebuildPath(selected.children, getPath(selected)); rebuildPath(selected.children, getPath(selected));
} }
}" class="mx-0"/></pre> }" class="mx-0"/></pre>
<pre v-else>/{{ getPath(selected) }}</pre> <pre v-else class="md:text-base text-smmd:text-nowrap text-wrap ">/{{ getPath(selected) }}</pre>
</span> </span>
</div> </div>
</div> </div>
@ -159,6 +167,7 @@
</div> </div>
</div> </div>
</div> </div>
</CollapsibleRoot>
</ClientOnly> </ClientOnly>
</template> </template>
@ -188,6 +197,7 @@ definePageMeta({
}); });
const router = useRouter(); const router = useRouter();
const open = ref(true);
const toaster = useToast(); const toaster = useToast();
const saveStatus = ref<'idle' | 'pending' | 'success' | 'error'>('idle'); const saveStatus = ref<'idle' | 'pending' | 'success' | 'error'>('idle');

View File

@ -0,0 +1,40 @@
import { hasPermissions } from "~/shared/auth.util";
import useDatabase from '~/composables/useDatabase';
import { and, eq, notInArray } from "drizzle-orm";
import { z } from "zod";
import { userSessionsTable } from "~/db/schema";
const schema = z.array(z.string());
export default defineEventHandler(async (e) => {
const session = await getUserSession(e);
if(!session || !session.user || !hasPermissions(session.user.permissions, ['admin']))
{
throw createError({
statusCode: 401,
message: 'Unauthorized',
});
}
const param = getRouterParam(e, 'id');
if(!param)
{
throw createError({
statusCode: 403,
message: 'Forbidden',
});
}
try {
const id = parseInt(param, 10);
const db = useDatabase();
db.delete(userSessionsTable).where(eq(userSessionsTable.user_id, id)).run();
} catch(e) {
console.error(e);
throw e;
}
});