Polish CSS for mobile editor. Add user logout from admin panel.
This commit is contained in:
parent
ecdfa947ac
commit
51a5d501be
2
app.vue
2
app.vue
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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-shm
BIN
db.sqlite-shm
Binary file not shown.
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue