You've already forked obsidian-visualiser
Add public characters and visibility flag
This commit is contained in:
@@ -68,6 +68,12 @@ const data = ref<Character>({
|
||||
spells: [],
|
||||
notes: "",
|
||||
},
|
||||
values: {
|
||||
hp: 0,
|
||||
armor: 0,
|
||||
mana: 0,
|
||||
},
|
||||
visibility: "private"
|
||||
});
|
||||
const spellFilter = ref<{
|
||||
ranks: Array<1 | 2 | 3>,
|
||||
@@ -111,6 +117,8 @@ if(id !== 'new')
|
||||
data.value = { name: character.name, progress: Object.assign(data.value.progress, character.progress) } as Character;
|
||||
}
|
||||
|
||||
console.log(data.value.progress);
|
||||
|
||||
function selectRaceOption(level: Level, choice: number)
|
||||
{
|
||||
const character = data.value;
|
||||
@@ -265,6 +273,13 @@ useShortcuts({
|
||||
<NumberFieldInput class="tabular-nums w-20 bg-transparent px-3 py-1 outline-none caret-light-50 dark:caret-dark-50" />
|
||||
</NumberFieldRoot>
|
||||
</Label>
|
||||
<Label class="flex items-start justify-between flex-col gap-2">
|
||||
<span class="pb-1 mx-6 md:p-0">Visibilité</span>
|
||||
<Select class="!my-0" v-model="data.visibility">
|
||||
<SelectItem label="Privé" value="private" />
|
||||
<SelectItem label="Public" value="public" />
|
||||
</Select>
|
||||
</Label>
|
||||
</div>
|
||||
<div class="self-center">
|
||||
<Tooltip side="right" message="Ctrl+S"><Button @click="() => save(true)">Enregistrer</Button></Tooltip>
|
||||
@@ -277,7 +292,7 @@ useShortcuts({
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="m-2 overflow-auto">
|
||||
<Combobox label="Peuple de votre personnage" :v-model="data.progress.race.index!" :default-value="data.progress.race.index" :options="config.peoples.map((people, index) => [people.name, index])" @update:model-value="(index) => { data.progress.race.index = index as number | undefined; data.progress.race.progress = [[1, 0]]}" />
|
||||
<Combobox label="Peuple de votre personnage" v-model="data.progress.race.index" :options="config.peoples.map((people, index) => [people.name, index])" @update:model-value="(index) => { data.progress.race.index = index as number | undefined; data.progress.race.progress = [[1, 0]]}" />
|
||||
<template v-if="data.progress.race.index !== undefined">
|
||||
<div class="w-full border-b border-light-30 dark:border-dark-30 pb-4">
|
||||
<span class="text-sm text-light-70 dark:text-dark-70">{{ characterConfig.peoples[data.progress.race.index].description }}</span>
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import type { Progression } from '~/types/character';
|
||||
import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||
|
||||
definePageMeta({
|
||||
guestsGoesTo: '/user/login',
|
||||
})
|
||||
const { add } = useToast();
|
||||
const { user } = useUserSession();
|
||||
const loading = ref(true);
|
||||
const characters = ref<Array<{ id: number, name: string, progress: Progression }>>([]);
|
||||
characters.value = await useRequestFetch()('/api/character');
|
||||
loading.value = false;
|
||||
|
||||
const { data: characters, error, status } = await useFetch(`/api/character`);
|
||||
|
||||
async function deleteCharacter(id: number)
|
||||
{
|
||||
loading.value = true;
|
||||
status.value = "pending";
|
||||
await useRequestFetch()(`/api/character/${id}`, { method: 'delete' });
|
||||
loading.value = false;
|
||||
status.value = "success";
|
||||
add({ content: 'Personnage supprimé', type: 'info', duration: 25000, timer: true, });
|
||||
characters.value = characters.value?.filter(e => e.id !== id);
|
||||
}
|
||||
async function duplicateCharacter(id: number)
|
||||
{
|
||||
loading.value = true;
|
||||
status.value = "pending";
|
||||
const newId = await useRequestFetch()(`/api/character/${id}/duplicate`, { method: 'post' });
|
||||
loading.value = false;
|
||||
status.value = "success";
|
||||
add({ content: 'Personnage dupliqué', type: 'info', duration: 25000, timer: true, });
|
||||
useRouter().push({ name: 'character-id', params: { id: newId } });
|
||||
}
|
||||
@@ -34,37 +32,64 @@ async function duplicateCharacter(id: number)
|
||||
<Title>d[any] - Mes personnages</Title>
|
||||
</Head>
|
||||
<div class="flex flex-col">
|
||||
<NuxtLink v-if="user?.state === 1" :to="{ name: 'character-id-edit', params: { id: 'new' } }" class="flex align-center justify-center"><Button>Nouveau personnage</Button></NuxtLink>
|
||||
<Tooltip v-else side="top" message="Veuillez valider votre email avant de pouvoir créer un personnage."><Button disabled>Nouveau personnage</Button></Tooltip>
|
||||
<div v-if="loading" class="flex flex-1 justify-center align-center">
|
||||
<div class="flex align-center justify-center">
|
||||
<NuxtLink v-if="user?.state === 1" :to="{ name: 'character-id-edit', params: { id: 'new' } }"><Button>Nouveau personnage</Button></NuxtLink>
|
||||
<Tooltip v-else side="top" message="Veuillez valider votre email avant de pouvoir créer un personnage."><Button disabled>Nouveau personnage</Button></Tooltip>
|
||||
</div>
|
||||
<div v-if="status === 'pending'" class="flex flex-1 justify-center align-center">
|
||||
<Loading size="large" />
|
||||
</div>
|
||||
<div v-else class="grid p-6 grid-cols-4 gap-4">
|
||||
<div class="border border-light-30 dark:border-dark-30 p-1 flex flex-row gap-4" v-for="character of characters">
|
||||
<Avatar size="large" icon="radix-icons:person" src="" class="m-2" />
|
||||
<div class="flex flex-col justify-between w-64">
|
||||
<NuxtLink class="flex-1 text-xl font-bold hover:text-accent-blue truncate" :to="{ name: 'character-id', params: { id: character.id } }" :title="character.name">{{ character.name }}</NuxtLink>
|
||||
<span class="flex-1 text-sm truncate">Niveau {{ character.progress.level }}</span>
|
||||
<div class="flex flex-row gap-8">
|
||||
<NuxtLink class="text-accent-blue hover:text-opacity-50" :to="{ name: 'character-id-edit', params: { id: character.id } }">Editer</NuxtLink>
|
||||
<span class="text-accent-blue hover:text-opacity-50 cursor-pointer" @click="duplicateCharacter(character.id)">Dupliquer</span>
|
||||
<AlertDialogRoot>
|
||||
<AlertDialogTrigger asChild><span class="text-light-red dark:text-dark-red hover:text-opacity-50 cursor-pointer">Supprimer</span></AlertDialogTrigger>
|
||||
<AlertDialogPortal>
|
||||
<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">
|
||||
<AlertDialogTitle class="text-3xl font-light relative -top-2">Supprimer {{ character.name }} ?</AlertDialogTitle>
|
||||
<div class="flex flex-1 justify-end gap-4">
|
||||
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
|
||||
<AlertDialogAction asChild><Button @click="() => deleteCharacter(character.id)" 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>
|
||||
</div>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialogRoot>
|
||||
</div>
|
||||
<div v-else-if="status === 'success'" class="grid p-6 2xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4 w-full">
|
||||
<div class="border border-light-30 dark:border-dark-30 p-3 flex flex-row gap-4" v-for="character of characters">
|
||||
<Avatar size="large" icon="radix-icons:person" src="" />
|
||||
<div class="flex flex-1 flex-shrink flex-col truncate">
|
||||
<NuxtLink class="text-xl font-bold hover:text-accent-blue truncate" :to="{ name: 'character-id', params: { id: character.id } }" :title="character.name">{{ character.name }}</NuxtLink>
|
||||
<span class="text-sm truncate">Niveau {{ character.progress.level }}</span>
|
||||
</div>
|
||||
<AlertDialogRoot>
|
||||
<DropdownMenuRoot>
|
||||
<DropdownMenuTrigger class="self-start">
|
||||
<Button icon><Icon icon="radix-icons:dots-vertical" /></Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent align="end" side="bottom" 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">
|
||||
<DropdownMenuItem @select="useRouter().push({ name: 'character-id-edit', params: { id: character.id } })" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-baseline py-1.5 relative ps-7 pe-4 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35">
|
||||
<Icon icon="radix-icons:pencil-1" class="absolute left-1.5" />
|
||||
<span>Editer</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @select="duplicateCharacter(character.id)" class="cursor-pointer text-base text-light-100 dark:text-dark-100 leading-none flex items-center py-1.5 relative ps-7 pe-4 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-35 dark:data-[highlighted]:bg-dark-35">
|
||||
<Icon icon="radix-icons:clipboard-copy" class="absolute left-1.5" />
|
||||
<span>Dupliquer</span>
|
||||
</DropdownMenuItem>
|
||||
<AlertDialogTrigger>
|
||||
<DropdownMenuItem class="cursor-pointer text-base text-light-red dark:text-dark-red leading-none flex items-center py-1.5 relative ps-7 pe-4 select-none outline-none data-[disabled]:text-light-60 dark:data-[disabled]:text-dark-60 data-[disabled]:pointer-events-none data-[highlighted]:bg-light-red dark:data-[highlighted]:bg-dark-red data-[highlighted]:bg-opacity-30 dark:data-[highlighted]:bg-opacity-30">
|
||||
<Icon icon="radix-icons:trash" class="absolute left-1.5" />
|
||||
<span>Supprimer</span>
|
||||
</DropdownMenuItem>
|
||||
</AlertDialogTrigger>
|
||||
|
||||
<DropdownMenuArrow class="fill-light-35 dark:fill-dark-35" />
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuRoot>
|
||||
<AlertDialogPortal>
|
||||
<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">
|
||||
<AlertDialogTitle class="text-3xl font-light relative -top-2">Supprimer {{ character.name }} ?</AlertDialogTitle>
|
||||
<div class="flex flex-1 justify-end gap-4">
|
||||
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
|
||||
<AlertDialogAction asChild><Button @click="() => deleteCharacter(character.id)" 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>
|
||||
</div>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialogRoot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>Erreur de chargement</span>
|
||||
<span>{{ error?.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
27
pages/character/list.client.vue
Normal file
27
pages/character/list.client.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
const { data: characters, error, status } = await useFetch(`/api/character`, { params: { visibility: "public" } });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head>
|
||||
<Title>d[any] - Liste des personnages</Title>
|
||||
</Head>
|
||||
<div class="flex flex-col">
|
||||
<div v-if="status === 'pending'" class="flex flex-1 justify-center align-center">
|
||||
<Loading size="large" />
|
||||
</div>
|
||||
<div v-else-if="status === 'success'" class="grid p-6 2xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4 w-full">
|
||||
<div class="border border-light-30 dark:border-dark-30 p-3 flex flex-row gap-4" v-for="character of characters">
|
||||
<Avatar size="large" icon="radix-icons:person" src="" />
|
||||
<div class="flex flex-1 flex-shrink flex-col truncate">
|
||||
<NuxtLink class="text-xl font-bold hover:text-accent-blue truncate" :to="{ name: 'character-id', params: { id: character.id } }" :title="character.name">{{ character.name }}</NuxtLink>
|
||||
<span class="text-sm truncate">Niveau {{ character.progress.level }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>Erreur de chargement</span>
|
||||
<span>{{ error?.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user