New Toaster class, Ability and Resistance removed from config file and choices improvement

This commit is contained in:
2025-08-31 23:52:11 +02:00
parent 17bc232602
commit c93cc4078c
24 changed files with 699 additions and 684 deletions

View File

@@ -31,9 +31,10 @@
</script>
<script setup lang="ts">
import { format } from '~/shared/general.util';
import { iconByType } from '~/shared/content.util';
import { format } from '#shared/general.util';
import { iconByType } from '#shared/content.util';
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
interface File
{
@@ -69,8 +70,6 @@ definePageMeta({
rights: ['admin'],
});
const toaster = useToast();
const { data: users } = useFetch('/api/admin/users', {
transform: (users) => {
//@ts-ignore
@@ -125,13 +124,13 @@ async function editPermissions(user: User)
body: permissionCopy.value,
});
user.permission = permissionCopy.value;
toaster.add({
Toaster.add({
duration: 10000, type: 'success', content: 'Permissions mises à jour.', timer: true,
});
}
catch(e)
{
toaster.add({
Toaster.add({
duration: 10000, type: 'error', content: (e as any).message, timer: true,
});
}
@@ -146,13 +145,13 @@ async function logout(user: User)
user.session.length = 0;
toaster.add({
Toaster.add({
duration: 10000, type: 'success', content: 'L\'utilisateur vient d\'être déconnecté.', timer: true,
});
}
catch(e)
{
toaster.add({
Toaster.add({
duration: 10000, type: 'error', content: (e as any).message, timer: true,
});
}

View File

@@ -15,13 +15,13 @@ const schemaList: Record<string, z.ZodObject<any> | null> = {
<script setup lang="ts">
import { z } from 'zod/v4';
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
definePageMeta({
rights: ['admin'],
})
const job = ref<string>('');
const toaster = useToast();
const payload = reactive<Record<string, any>>({
data: JSON.stringify({ username: "Peaceultime", id: 1, timestamp: Date.now() }),
to: 'clem31470@gmail.com',
@@ -51,7 +51,7 @@ async function fetch()
error.value = null;
success.value = true;
toaster.add({ duration: 10000, content: data.value ?? 'Job executé avec succès', type: 'success', timer: true, });
Toaster.add({ duration: 10000, content: data.value ?? 'Job executé avec succès', type: 'success', timer: true, });
}
catch(e)
{
@@ -59,7 +59,7 @@ async function fetch()
error.value = e as Error;
success.value = false;
toaster.add({ duration: 10000, content: error.value.message, type: 'error', timer: true, });
Toaster.add({ duration: 10000, content: error.value.message, type: 'error', timer: true, });
}
}
</script>

View File

@@ -5,7 +5,7 @@ import PreviewA from '~/components/prose/PreviewA.vue';
import { clamp } from '#shared/general.util';
import type { SpellConfig } from '~/types/character';
import type { CharacterConfig } from '~/types/character';
import { CharacterCompiler, defaultCharacter, elementTexts, spellTypeTexts } from '~/shared/character.util';
import { abilityTexts, CharacterCompiler, defaultCharacter, elementTexts, spellTypeTexts } from '~/shared/character.util';
import { getText } from '~/shared/i18n';
import { fakeA } from '~/shared/proses';
@@ -28,6 +28,11 @@ text-light-green dark:text-dark-green border-light-green dark:border-dark-green
text-light-yellow dark:text-dark-yellow border-light-yellow dark:border-dark-yellow bg-light-yellow dark:bg-dark-yellow
text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-purple bg-light-purple dark:bg-dark-purple
*/
function manageSpell()
{
}
</script>
<template>
@@ -88,7 +93,7 @@ text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-pur
<div class="flex flex-col">
<span class="text-lg font-semibold border-b border-light-30 dark:border-dark-30 mb-2">Compétences</span>
<div class="grid grid-cols-3 gap-1">
<div class="flex flex-col px-2 items-center text-sm text-light-70 dark:text-dark-70" v-for="(value, ability) of character.abilities"><span class="font-bold text-base text-light-100 dark:text-dark-100">+{{ value }}</span><span>{{ characterConfig.abilities[ability].name }}</span></div>
<div class="flex flex-col px-2 items-center text-sm text-light-70 dark:text-dark-70" v-for="(value, ability) of character.abilities"><span class="font-bold text-base text-light-100 dark:text-dark-100">+{{ value }}</span><span>{{ abilityTexts[ability] }}</span></div>
</div>
</div>
<div class="flex flex-col gap-2">
@@ -157,7 +162,7 @@ text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-pur
</TabsContent>
<TabsContent v-if="character.spellslots > 0" value="spells" class="overflow-y-auto max-h-full">
<div class="flex flex-1 flex-col ps-8 gap-4 py-2">
<div class="flex flex-1 justify-between items-center"><span class="italic text-light-70 dark:text-dark-70 text-sm">{{ character.variables.spells.length }} / {{ character.spellslots }} sorts maitrisés</span><Button icon><Icon icon="radix-icons:plus" class="w-6 h-6"/></Button></div>
<div class="flex flex-1 justify-between items-baseline px-2"><div></div><div class="flex gap-4 items-baseline"><span class="italic text-light-70 dark:text-dark-70 text-sm">{{ character.variables.spells.length }} / {{ character.spellslots }} sorts maitrisés</span><Button class="!font-normal" @click="manageSpell">Modifier</Button></div></div>
<div class="flex flex-col" v-if="[...(character.lists.spells ?? []), ...character.variables.spells].length > 0">
<div class="pb-4 px-2 mt-4 border-b last:border-none border-light-30 dark:border-dark-30 flex flex-col" v-for="spell of [...(character.lists.spells ?? []), ...character.variables.spells].map(e => config.spells.find((f: SpellConfig) => f.id === e)).filter(e => !!e)">
<div class="flex flex-row justify-between">
@@ -167,8 +172,8 @@ text-light-purple dark:text-dark-purple border-light-purple dark:border-dark-pur
<span v-for="element of spell.elements" :class="elementTexts[element].class" class="border !border-opacity-50 rounded-full !bg-opacity-20 px-2 py-px">{{ elementTexts[element].text }}</span>
</div>
<div class="flex flex-row text-sm gap-1">
<span class="">Rang {{ spell.rank }}</span><span>/</span>
<span class="">{{ spellTypeTexts[spell.type] }}</span><span>/</span>
<span class="" v-if="spell.rank !== 4">Rang {{ spell.rank }}</span><span v-if="spell.rank !== 4">/</span>
<span class="" v-if="spell.rank !== 4">{{ spellTypeTexts[spell.type] }}</span><span v-if="spell.rank !== 4">/</span>
<span class="">{{ spell.cost }} mana</span><span>/</span>
<span class="capitalize">{{ typeof spell.speed === 'string' ? spell.speed : `${spell.speed} minutes` }}</span>
</div>

View File

@@ -1,12 +1,11 @@
<script setup lang="ts">
import characterConfig from '#shared/character-config.json';
import { Toaster } from '#shared/components.util';
import type { CharacterConfig } from '~/types/character';
definePageMeta({
guestsGoesTo: '/user/login',
})
const { add } = useToast();
const { data: characters, error, status } = await useFetch(`/api/character`);
const config = characterConfig as CharacterConfig;
@@ -15,7 +14,7 @@ async function deleteCharacter(id: number)
status.value = "pending";
await useRequestFetch()(`/api/character/${id}`, { method: 'delete' });
status.value = "success";
add({ content: 'Personnage supprimé', type: 'info', duration: 25000, timer: true, });
Toaster.add({ content: 'Personnage supprimé', type: 'info', duration: 25000, timer: true, });
characters.value = characters.value?.filter(e => e.id !== id);
}
async function duplicateCharacter(id: number)
@@ -23,7 +22,7 @@ async function duplicateCharacter(id: number)
status.value = "pending";
const newId = await useRequestFetch()(`/api/character/${id}/duplicate`, { method: 'post' });
status.value = "success";
add({ content: 'Personnage dupliqué', type: 'info', duration: 25000, timer: true, });
Toaster.add({ content: 'Personnage dupliqué', type: 'info', duration: 25000, timer: true, });
useRouter().push({ name: 'character-id', params: { id: newId } });
}
</script>

View File

@@ -39,13 +39,13 @@ import { Content, Editor } from '#shared/content.util';
import { button, loading } from '#shared/components.util';
import { dom, icon, text } from '#shared/dom.util';
import { modal, popper, tooltip } from '#shared/floating.util';
import { Toaster } from '#shared/components.util';
definePageMeta({
rights: ['admin', 'editor'],
layout: 'null',
});
const toaster = useToast();
const { user } = useUserSession();
const tree = useTemplateRef('tree'), container = useTemplateRef('container');
let editor: Editor;
@@ -53,9 +53,9 @@ let editor: Editor;
function pull()
{
Content.pull().then(e => {
toaster.add({ type: 'success', content: 'Données mises à jour avec succès.', timer: true, duration: 7500 });
Toaster.add({ type: 'success', content: 'Données mises à jour avec succès.', timer: true, duration: 7500 });
}).catch(e => {
toaster.add({ type: 'success', content: 'Une erreur est survenue durant la récupération des données.', timer: true, duration: 7500 });
Toaster.add({ type: 'success', content: 'Une erreur est survenue durant la récupération des données.', timer: true, duration: 7500 });
console.error(e);
});
}
@@ -64,10 +64,10 @@ function push()
const { close } = modal([dom('div', { class: 'flex flex-col gap-4 justify-center items-center' }, [ dom('div', { class: 'text-xl', text: 'Mise à jour des données' }), loading('large') ])], { priority: false, closeWhenOutside: true, });
Content.push().then(e => {
close();
toaster.add({ type: 'success', content: 'Données mises à jour avec succès.', timer: true, duration: 7500 });
Toaster.add({ type: 'success', content: 'Données mises à jour avec succès.', timer: true, duration: 7500 });
}).catch(e => {
close();
toaster.add({ type: 'success', content: 'Une erreur est survenue durant l\'enregistrement des données.', timer: true, duration: 7500 });
Toaster.add({ type: 'success', content: 'Une erreur est survenue durant l\'enregistrement des données.', timer: true, duration: 7500 });
console.error(e);
});
}

View File

@@ -25,8 +25,6 @@ definePageMeta({
usersGoesTo: '/user/profile',
});
const toaster = useToast();
const email = ref(''), status = ref<'idle' | 'pending' | 'success' | 'error'>('idle');
async function submit()

View File

@@ -25,6 +25,7 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
definePageMeta({
layout: 'login',
@@ -33,7 +34,6 @@ definePageMeta({
const query = useRouter().currentRoute.value.query;
const toaster = useToast();
const status = ref<'idle' | 'pending' | 'success' | 'error'>('idle'), manualError = ref(false);
const oldPasswd = ref(''), newPasswd = ref(''), repeatPasswd = ref('');
@@ -70,7 +70,7 @@ async function submit()
{
status.value = 'success';
toaster.add({ content: 'Votre mot de passe a été modifié avec succès.', duration: 10000, timer: true, type: 'success' });
Toaster.add({ content: 'Votre mot de passe a été modifié avec succès.', duration: 10000, timer: true, type: 'success' });
useRouter().push({ name: 'user-login' });
}
else
@@ -81,7 +81,7 @@ async function submit()
status.value = 'error';
const err = e as any;
toaster.add({ content: err?.data?.message ?? err?.message ?? 'Erreur inconnue', duration: 10000, timer: true, type: 'error' });
Toaster.add({ content: err?.data?.message ?? err?.message ?? 'Erreur inconnue', duration: 10000, timer: true, type: 'error' });
}
}
</script>

View File

@@ -26,13 +26,13 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
definePageMeta({
layout: 'login',
guestsGoesTo: '/user/login',
});
const toaster = useToast();
const { user } = useUserSession();
const status = ref<'idle' | 'pending' | 'success' | 'error'>('idle'), manualError = ref(false);
const oldPasswd = ref(''), newPasswd = ref(''), repeatPasswd = ref('');
@@ -70,19 +70,19 @@ async function submit()
{
status.value = 'success';
toaster.add({ content: 'Votre mot de passe a été modifié avec succès.', duration: 10000, timer: true, type: 'success' });
Toaster.add({ content: 'Votre mot de passe a été modifié avec succès.', duration: 10000, timer: true, type: 'success' });
useRouter().push({ name: 'user-profile' });
}
else
{
status.value = 'error';
toaster.add({ content: result.error ?? 'Erreur inconnue', duration: 10000, timer: true, type: 'error' });
Toaster.add({ content: result.error ?? 'Erreur inconnue', duration: 10000, timer: true, type: 'error' });
}
} catch(e) {
status.value = 'error';
toaster.add({ content: (e as Error).message ?? e, duration: 10000, timer: true, type: 'error' });
Toaster.add({ content: (e as Error).message ?? e, duration: 10000, timer: true, type: 'error' });
}
}
</script>

View File

@@ -21,14 +21,13 @@
import type { ZodError } from 'zod/v4';
import { schema, type Login } from '~/schemas/login';
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
definePageMeta({
layout: 'login',
usersGoesTo: '/user/profile',
});
const { add: addToast, clear: clearToasts } = useToast();
const state = reactive<Login>({
usernameOrEmail: '',
password: ''
@@ -47,9 +46,9 @@ const toastMessage = ref('');
async function submit()
{
if(state.usernameOrEmail === "")
return addToast({ content: 'Veuillez saisir un nom d\'utilisateur ou un email', timer: true, duration: 10000 });
return Toaster.add({ content: 'Veuillez saisir un nom d\'utilisateur ou un email', timer: true, duration: 10000 });
if(state.password === "")
return addToast({ content: 'Veuillez saisir un mot de passe', timer: true, duration: 10000 });
return Toaster.add({ content: 'Veuillez saisir un mot de passe', timer: true, duration: 10000 });
const data = schema.safeParse(state);
@@ -64,8 +63,8 @@ async function submit()
}
else if(status.value === 'success' && login.success)
{
clearToasts();
addToast({ duration: 10000, content: 'Vous êtes maintenant connecté', timer: true, type: 'success' });
Toaster.clear();
Toaster.add({ duration: 10000, content: 'Vous êtes maintenant connecté', timer: true, type: 'success' });
await navigateTo('/user/profile');
}
}
@@ -85,12 +84,12 @@ function handleErrors(error: Error | ZodError)
{
for(const err of (error as ZodError).issues)
{
return addToast({ content: err.message, timer: true, duration: 10000, type: 'error' });
return Toaster.add({ content: err.message, timer: true, duration: 10000, type: 'error' });
}
}
else
{
return addToast({ content: error?.message ?? 'Une erreur est survenue', timer: true, duration: 10000, type: 'error' });
return Toaster.add({ content: error?.message ?? 'Une erreur est survenue', timer: true, duration: 10000, type: 'error' });
}
}
</script>

View File

@@ -1,11 +1,11 @@
<script setup lang="ts">
import { hasPermissions } from "#shared/auth.util";
import { Toaster } from '#shared/components.util';
definePageMeta({
guestsGoesTo: '/user/login',
})
const { user, clear } = useUserSession();
const toaster = useToast();
const loading = ref<boolean>(false);
async function revalidateUser()
@@ -15,7 +15,7 @@ async function revalidateUser()
method: 'post'
});
loading.value = false;
toaster.add({ closeable: false, duration: 10000, timer: true, content: 'Un mail vous a été envoyé.', type: 'info' });
Toaster.add({ closeable: false, duration: 10000, timer: true, content: 'Un mail vous a été envoyé.', type: 'info' });
}
async function deleteUser()
{

View File

@@ -30,6 +30,7 @@
import { ZodError } from 'zod/v4';
import { schema, type Registration } from '~/schemas/registration';
import { Icon } from '@iconify/vue/dist/iconify.js';
import { Toaster } from '#shared/components.util';
definePageMeta({
layout: 'login',
@@ -42,7 +43,6 @@ const state = reactive<Registration>({
password: ''
});
const { add: addToast, clear: clearToasts } = useToast();
const confirmPassword = ref("");
const checkedLength = computed(() => state.password.length >= 8 && state.password.length <= 128);
@@ -62,13 +62,13 @@ const { data: result, status, error, refresh } = await useFetch('/api/auth/regis
async function submit()
{
if(state.username === '')
return addToast({ content: 'Veuillez saisir un nom d\'utilisateur', timer: true, duration: 10000 });
return Toaster.add({ content: 'Veuillez saisir un nom d\'utilisateur', timer: true, duration: 10000 });
if(state.email === '')
return addToast({ content: 'Veuillez saisir une adresse mail', timer: true, duration: 10000 });
return Toaster.add({ content: 'Veuillez saisir une adresse mail', timer: true, duration: 10000 });
if(state.password === "")
return addToast({ content: 'Veuillez saisir un mot de passe', timer: true, duration: 10000 });
return Toaster.add({ content: 'Veuillez saisir un mot de passe', timer: true, duration: 10000 });
if(state.password !== confirmPassword.value)
return addToast({ content: 'Les deux mots de passe saisis ne correspondent pas', timer: true, duration: 10000 });
return Toaster.add({ content: 'Les deux mots de passe saisis ne correspondent pas', timer: true, duration: 10000 });
const data = schema.safeParse(state);
@@ -83,8 +83,8 @@ async function submit()
}
else if(status.value === 'success' && login.success)
{
clearToasts();
addToast({ duration: 10000, content: 'Vous avez été enregistré. Pensez à valider votre adresse mail.', timer: true, type: 'success' });
Toaster.clear();
Toaster.add({ duration: 10000, content: 'Vous avez été enregistré. Pensez à valider votre adresse mail.', timer: true, type: 'success' });
await navigateTo('/user/profile');
}
}
@@ -104,12 +104,12 @@ function handleErrors(error: Error | ZodError)
{
for(const err of (error as ZodError).issues)
{
return addToast({ content: err.message, timer: true, duration: 10000, type: 'error' });
return Toaster.add({ content: err.message, timer: true, duration: 10000, type: 'error' });
}
}
else
{
return addToast({ content: error?.message ?? 'Une erreur est survenue', timer: true, duration: 10000, type: 'error' });
return Toaster.add({ content: error?.message ?? 'Une erreur est survenue', timer: true, duration: 10000, type: 'error' });
}
}
</script>