Several fixes to CSS, better responsive overall, improved security and error page

This commit is contained in:
Peaceultime 2024-11-10 17:44:42 +01:00
parent 721e7ff3db
commit 057efb848c
20 changed files with 135 additions and 66 deletions

View File

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

View File

@ -1,6 +1,6 @@
<template>
<TreeRoot v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 p-2 font-medium xl:text-base text-sm cursor-pointer" :items="model" :get-key="(item) => item.link ?? item.label">
<TreeItem v-for="item in flattenItems" v-slot="{ isExpanded }" :key="item._id" :style="{ 'padding-left': `${item.level - 0.5}em` }" v-bind="item.bind" class="flex items-center px-2 outline-none relative">
<TreeRoot v-slot="{ flattenItems }" class="list-none select-none text-light-100 dark:text-dark-100 p-2 font-medium xl:text-base text-sm" :items="model" :get-key="(item) => item.link ?? item.label">
<TreeItem v-for="item in flattenItems" v-slot="{ isExpanded }" :key="item._id" :style="{ 'padding-left': `${item.level - 0.5}em` }" v-bind="item.bind" class="flex items-center px-2 outline-none relative cursor-pointer">
<NuxtLink :href="item.value.link && !item.hasChildren ? { name: 'explore-path', params: { path: item.value.link } } : undefined" no-prefetch class="flex flex-1 items-center" active-class="text-accent-blue">
<Icon v-if="item.hasChildren" icon="radix-icons:chevron-right" :class="{ 'rotate-90': isExpanded }" class="h-4 w-4 transition-transform absolute" :style="{ 'left': `${item.level - 1}em` }" />
<div class="pl-3 py-1 flex-1 truncate border-light-35 dark:border-dark-35 hover:border-accent-blue" :class="{ 'border-s': !item.hasChildren }" :data-tag="item.value.tag">

View File

@ -1,5 +1,5 @@
<template>
<slot v-bind="$attrs"></slot>
<span class="text-accent-blue inline-flex items-center cursor-pointer hover:text-opacity-85"><slot v-bind="$attrs"></slot></span>
<!-- <Suspense suspensible>
<NuxtLink no-prefetch class="text-accent-blue inline-flex items-center" v-if="data && data[0]"
:to="{ path: `/explorer/${project}/${data[0].path}`, hash: hash }" :class="class">

View File

@ -1,5 +1,5 @@
<template>
<h1 :id="parseId(id)" class="text-5xl font-thin mt-3 mb-8 first:pt-0 pt-2 relative sm:right-8 right-4">
<h1 :id="parseId(id)" class="text-5xl font-thin mt-3 mb-8 first:pt-0 pt-2 relative lg:right-8 sm:right-4 right-2">
<slot />
</h1>
</template>

View File

@ -28,7 +28,9 @@
</span>
</template>
</HoverPopup> -->
<slot />
<span class="before:content-['#'] cursor-default bg-accent-blue bg-opacity-10 hover:bg-opacity-20 text-accent-blue text-sm px-1 ms-1 pb-0.5 rounded-full rounded-se-none border border-accent-blue border-opacity-30">
<slot></slot>
</span>
</template>
<!-- <script setup lang="ts">

BIN
db.sqlite

Binary file not shown.

Binary file not shown.

Binary file not shown.

22
error.vue Normal file
View File

@ -0,0 +1,22 @@
<template>
<div class="text-light-100 dark:text-dark-100 flex bg-light-0 dark:bg-dark-0 h-screen overflow-hidden justify-center items-center flex-col gap-4">
<NuxtRouteAnnouncer/>
<div class="flex gap-4 items-center">
<Icon icon="si:error-line" class="w-12 h-12 text-light-60 dark:text-dark-60"/>
<div class="text-3xl">Une erreur est survenue.</div>
</div>
<pre class="">Erreur {{ error?.statusCode }}: {{ error?.message }}</pre>
<NuxtLink :href="{ name: 'index' }"><Button>Revenir en lieu sûr</Button></NuxtLink>
</div>
</template>
<script setup lang="ts">
import type { NuxtError } from '#app'
import { Icon } from '@iconify/vue/dist/iconify.js';
const props = defineProps({
error: Object as () => NuxtError
})
const handleError = () => clearError({ redirect: '/' })
</script>

View File

@ -1,5 +1,5 @@
<template>
<CollapsibleRoot class="flex flex-1 flex-col" v-model="open">
<CollapsibleRoot class="flex flex-1 flex-col" :v-model="open">
<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">
<CollapsibleTrigger asChild>
@ -13,7 +13,7 @@
<div class="flex items-center px-2">
<Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip>
<Tooltip :message="loggedIn ? 'Mon profil' : 'Se connecter'" side="right">
<NuxtLink class="" :to="{ path: '/user/profile', force: true }">
<NuxtLink class="" :to="{ name: 'user-profile' }">
<div class="hover:border-opacity-70 flex">
<Icon :icon="loggedIn ? 'radix-icons:avatar' : 'radix-icons:person'" class="w-7 h-7 p-1" />
</div>
@ -27,12 +27,13 @@
<div class="relative bottom-6 flex flex-col gap-4 xl:px-6 px-3">
<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 }">
<Avatar src="/logo.svg" />
<Avatar src="/logo.dark.svg" class="dark:block hidden" />
<Avatar src="/logo.light.svg" class="block dark:hidden" />
</NuxtLink>
<div class="flex gap-4 items-center">
<Tooltip message="Changer de theme" side="left"><ThemeSwitch /></Tooltip>
<Tooltip :message="loggedIn ? 'Mon profil' : 'Se connecter'" side="right">
<NuxtLink class="" :to="{ path: '/user/profile', force: true }">
<NuxtLink class="" :to="{ name: 'user-profile' }">
<div class="bg-light-20 dark:bg-dark-20 hover:border-opacity-70 flex border p-px border-light-50 dark:border-dark-50">
<Icon :icon="loggedIn ? 'radix-icons:avatar' : 'radix-icons:person'" class="w-7 h-7 p-1" />
</div>
@ -43,7 +44,7 @@
</div>
<Tree v-if="pages" v-model="pages" class="flex-1 xl:px-6 px-3 max-w-full max-h-full overflow-y-auto overflow-x-hidden"/>
<div class="xl:px-12 px-6 text-start text-xs text-light-60 dark:text-dark-60 relative top-4">
<NuxtLink class="hover:underline italic" :to="{ path: '/legal', force: true }">Mentions légales</NuxtLink>
<NuxtLink class="hover:underline italic" :to="{ name: 'legal' }">Mentions légales</NuxtLink>
<p>Copyright Peaceultime - 2024</p>
</div>
</div>
@ -63,6 +64,12 @@ const { data: pages } = await useLazyFetch('/api/navigation', {
transform: transform,
});
watch(useRouter().currentRoute, () => {
console.log(open.value);
console.log('Changing');
open.value = false;
});
function transform(list: any[]): any[]
{
return list?.map(e => ({ label: e.title, children: transform(e.children), link: e.path, tag: e.private ? 'Privé' : e.type }))

View File

@ -46,7 +46,7 @@ async function fetch()
<ProseH2>Administration</ProseH2>
<Select label="Job" v-model="job">
<SelectItem label="Synchroniser" value="sync" />
<SelectItem label="Nettoyer la base" value="clear" />
<SelectItem label="Nettoyer la base" value="clear" disabled />
<SelectItem label="Reconstruire" value="rebuild" disabled />
</Select>
<Button class="self-center" @click="() => !!job && fetch()" :loading="status === 'pending'">

View File

@ -1,22 +1,39 @@
<template>
<div v-if="status === 'pending'" class="flex"><Loading /></div>
<div class="flex flex-1 justify-start items-start" v-if="page">
<div v-if="status === 'pending'" class="flex">
<Head>
<Title>d[any] - Chargement</Title>
</Head>
<Loading />
</div>
<div class="flex flex-1 justify-start items-start" v-else-if="page">
<Head>
<Title>d[any] - {{ page.title }}</Title>
</Head>
<template v-if="page.type === 'markdown'">
<div class="flex flex-1 justify-start items-start flex-col px-24 py-6">
<div class="flex flex-1 justify-start items-start flex-col xl:px-24 md:px-8 px-4 py-6">
<div class="flex flex-1 flex-row justify-between items-center">
<ProseH1>{{ page.title }}</ProseH1>
<Button v-if="isOwner"><NuxtLink :href="{ name: 'explore-edit-path', params: { path: path } }">Modifier</NuxtLink></Button>
<NuxtLink :href="{ name: 'explore-edit-path', params: { path: path } }"><Button v-if="isOwner">Modifier</Button></NuxtLink>
</div>
<Markdown :content="page.content" />
</div>
</template>
<template v-else>
<span>Contenu non traitable</span>
<span>En cours de développement</span>
</template>
</div>
<div v-else-if="status === 'pending'"><Loading /></div>
<div v-else-if="status === 'error'">{{ error?.message }}</div>
<div v-else>Impossible de retrouver le contenu demandé</div>
<div v-else-if="status === 'error'">
<Head>
<Title>d[any] - Erreur</Title>
</Head>
<span>{{ error?.message }}</span>
</div>
<div v-else>
<Head>
<Title>d[any] - Erreur</Title>
</Head>
<div><ProseH2>Impossible d'afficher le contenu demandé</ProseH2></div>
</div>
</template>
<script setup lang="ts">

View File

@ -1,32 +1,35 @@
<template>
<div v-if="page" class="p-12 flex flex-1 flex-col items-start justify-start">
<div v-if="page" class="xl:p-12 lg:p-8 py-4 flex flex-1 flex-col items-start justify-start max-h-full">
<Head>
<Title>Modification de {{ page.title }}</Title>
</Head>
<div class="flex justify-between items-center w-full px-4 max-h-full">
<div class="flex gap-16 items-center justify-start">
<input type="text" v-model="page.title" placeholder="Titre" class="mx-4 h-16 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 px-3 py-1 text-5xl font-thin bg-transparent" />
</div>
<div class="flex gap-4">
<Tooltip message="Consultable uniquement par le propriétaire" side="bottom"><Switch label="Privé" v-model="page.private" /></Tooltip>
<Tooltip message="Afficher dans le menu de navigation" side="bottom"><Switch label="Navigable" v-model="page.navigable" /></Tooltip>
<Button @click="() => save()" :loading="saveStatus === 'pending'" class="border-light-blue dark:border-dark-blue hover:border-light-blue dark:hover:border-dark-blue focus:shadow-light-blue dark:focus:shadow-dark-blue">Enregistrer</Button>
</div>
</div>
<div class="border-t border-light-35 dark:border-dark-35 mt-4 flex-1 w-full max-h-full flex pt-4">
<template v-if="page.type === 'markdown'">
<textarea v-model="page.content" class="flex-1 bg-transparent appearance-none outline-none max-h-full resize-none"></textarea>
<div class="flex-1 border-s border-light-35 dark:border-dark-35 ms-4 pt-4 ps-4 max-h-full">
<Markdown class="max-h-full overflow-auto" :content="page.content" />
</div>
</template>
<template v-else-if="page.type === 'canvas'">
<span class="flex-1 items-center"><ProseH1>Editeur de graphe en cours de développement</ProseH1></span>
</template>
<template v-else-if="page.type === 'file'">
<span>Modifier le contenu :</span><input type="file" @change="(e) => console.log(e)" />
</template>
<div class="flex flex-col xl:flex-row xl:justify-between justify-center items-center w-full px-4 pb-4 border-b border-light-35 dark:border-dark-35 bg-light-0 dark:bg-dark-0">
<input type="text" v-model="page.title" placeholder="Titre" class="flex-1 mx-4 h-16 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 px-3 py-1 text-5xl font-thin bg-transparent" />
<div class="flex gap-4 self-end xl:self-auto">
<Tooltip message="Consultable uniquement par le propriétaire" side="bottom"><Switch label="Privé" v-model="page.private" /></Tooltip>
<Tooltip message="Afficher dans le menu de navigation" side="bottom"><Switch label="Navigable" v-model="page.navigable" /></Tooltip>
<Button @click="() => save()" :loading="saveStatus === 'pending'" class="border-light-blue dark:border-dark-blue hover:border-light-blue dark:hover:border-dark-blue focus:shadow-light-blue dark:focus:shadow-dark-blue">Enregistrer</Button>
</div>
</div>
<div class="my-4 flex-1 w-full max-h-full flex">
<template v-if="page.type === 'markdown'">
<SplitterGroup direction="horizontal" class="flex-1 w-full flex">
<SplitterPanel asChild>
<textarea v-model="page.content" class="flex-1 bg-transparent appearance-none outline-none max-h-full resize-none !overflow-y-auto"></textarea>
</SplitterPanel>
<SplitterResizeHandle class="bg-light-35 dark:bg-dark-35 w-px xl!mx-4 mx-2" />
<SplitterPanel asChild>
<div class="flex-1 max-h-full !overflow-y-auto"><Markdown :content="page.content" /></div>
</SplitterPanel>
</SplitterGroup>
</template>
<template v-else-if="page.type === 'canvas'">
<span class="flex-1 items-center"><ProseH1>Editeur de graphe en cours de développement</ProseH1></span>
</template>
<template v-else-if="page.type === 'file'">
<span>Modifier le contenu :</span><input type="file" @change="(e) => console.log(e)" />
</template>
</div>
</div>
<div v-else-if="status === 'pending'" class="flex">
<Head>
@ -40,12 +43,18 @@
<script setup lang="ts">
const route = useRouter().currentRoute;
const path = computed(() => Array.isArray(route.value.params.path) ? route.value.params.path[0] : route.value.params.path);
const { user, loggedIn } = useUserSession();
const toaster = useToast();
const saveStatus = ref<'idle' | 'pending' | 'success' | 'error'>('idle');
const { data: page, status, error } = await useLazyFetch(`/api/file/${encodeURIComponent(path.value)}`, { watch: [ route, path ]});
if(!loggedIn || (page.value && page.value.owner !== user.value?.id))
{
useRouter().replace({ name: 'explore-path', params: { path: path.value } });
}
async function save(): Promise<void>
{
saveStatus.value = 'pending';

View File

@ -11,26 +11,11 @@ watch(loading, (value) => {
<template>
<Head>
<Title>Accueil</Title>
<Title>d[any] - Accueil</Title>
</Head>
<div class="h-full w-full flex flex-1 flex-col justify-center items-center">
<div class="w-1/2 flex flex-1 flex-col justify-center items-center gap-2">
<Collapsible label="Options">
<div class="flex flex-col justify-start items-start">
<div class="w-full flex flex-row justify-center">
<Switch v-model="disabled" onIcon="radix-icons:lock-closed" offIcon="radix-icons:lock-open-2" />
<ThemeSwitch />
</div>
<SliderInput :disabled="disabled" :label="`Prix: ${price.toFixed(2)}€`" :min="0" :max="1500" :step="0.25"
v-model="price" />
<TextInput label="Saisir un pseudonyme" :disabled="disabled" v-model="username" />
<NumberPicker label="Age" :disabled="disabled" />
</div>
</Collapsible>
<Button :loading="loading" @click="() => loading = true">
<span v-if="!loading">Load data</span>
</Button>
</div>
<Avatar src="/logo.dark.svg" class="dark:block hidden w-48 h-48" />
<Avatar src="/logo.light.svg" class="block dark:hidden w-48 h-48" />
<h1 class="text-5xl font-thin font-mono">Bienvenue</h1>
</div>
</template>

View File

@ -11,7 +11,7 @@
<TextInput type="text" label="Utilisateur ou email" autocomplete="username" v-model="state.usernameOrEmail"/>
<TextInput type="password" label="Mot de passe" autocomplete="current-password" v-model="state.password"/>
<Button class="border border-light-35 dark:border-dark-35 self-center" :loading="status === 'pending'">Se connecter</Button>
<NuxtLink class="mt-4 text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ path: `/user/register`, force: true }">Pas de compte ?</NuxtLink>
<NuxtLink class="mt-4 text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ name: 'user-register' }">Pas de compte ?</NuxtLink>
</form>
</div>
</template>

View File

@ -57,7 +57,7 @@ const deleting = ref(false);
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
<Button v-if="hasPermissions(user.permissions, ['admin'])"><NuxtLink :href="{ name: 'admin' }">Administration</NuxtLink></Button>
<NuxtLink v-if="hasPermissions(user.permissions, ['admin'])" :href="{ name: 'admin' }"><Button>Administration</Button></NuxtLink>
</div>
<div class="flex" v-if="user.permissions">
<ProseTable class="!m-0">

View File

@ -27,7 +27,7 @@
</div>
<TextInput type="password" label="Confirmation du mot de passe" autocomplete="new-password" v-model="confirmPassword" class="w-full md:w-auto"/>
<Button class="border border-light-35 dark:border-dark-35 max-w-48 w-full order-9 col-span-2 md:col-span-1 m-auto" :loading="status === 'pending'">S'inscrire</Button>
<span class="mt-4 order-10 flex justify-center items-center gap-4 col-span-2 md:col-span-1 m-auto">Vous avez déjà un compte ?<NuxtLink class="text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ path: `/user/login`, force: true }">Se connecter</NuxtLink></span>
<span class="mt-4 order-10 flex justify-center items-center gap-4 col-span-2 md:col-span-1 m-auto">Vous avez déjà un compte ?<NuxtLink class="text-center block text-sm font-semibold tracking-wide hover:text-accent-blue" :to="{ name: 'user-login' }">Se connecter</NuxtLink></span>
</form>
</div>
</template>

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

20
public/logo.light.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,4 +1,11 @@
export default defineEventHandler(async (e) => {
const session = await getUserSession(e);
if(!session.user || !hasPermissions(session.user.permissions, ['admin']))
{
setResponseStatus(e, 404);
return;
}
const id = getRouterParam(e, 'id');
if(!id)