152 lines
6.9 KiB
TypeScript
152 lines
6.9 KiB
TypeScript
import { z } from "zod/v4";
|
|
import type { User } from "~/types/auth";
|
|
import type { Campaign } from "~/types/campaign";
|
|
import { div, dom, icon, span, text } from "#shared/dom.util";
|
|
import { button, loading } from "#shared/components.util";
|
|
import { CharacterCompiler } from "#shared/character.util";
|
|
import { tooltip } from "./floating.util";
|
|
import type { Character } from "~/types/character";
|
|
|
|
export const CampaignValidation = z.object({
|
|
id: z.number(),
|
|
name: z.string().nonempty(),
|
|
description: z.string()
|
|
});
|
|
|
|
/*
|
|
|
|
const { data: campaign, error, status } = await useFetch(`/api/campaign/${id}`);
|
|
const copied = ref(false);
|
|
const stopCopy = useDebounceFn(() => copied.value = false, 5000);
|
|
|
|
async function copyLink()
|
|
{
|
|
await navigator.clipboard.writeText(`https://d-any.com/campaign/join/${ encodeURIComponent(campaign.value!.link) }`);
|
|
copied.value = true;
|
|
stopCopy();
|
|
}
|
|
|
|
*/
|
|
|
|
class CharacterPrinter
|
|
{
|
|
private compiler?: CharacterCompiler;
|
|
container: HTMLElement = div('flex flex-col gap-2');
|
|
constructor(character: number, name: string)
|
|
{
|
|
this.container.replaceChildren(div('flex flex-row gap-1', [span(undefined, 'joue'), span('font-bold', name)]), loading('small'));
|
|
useRequestFetch()(`/api/character/${character}`).then((character) => {
|
|
if(character)
|
|
{
|
|
this.compiler = new CharacterCompiler(character);
|
|
this.container.replaceChildren(div('flex flex-row gap-1', [span(undefined, 'joue'), span('font-bold', name)]), );
|
|
}
|
|
else throw new Error();
|
|
}).catch((e) => {
|
|
console.error(e);
|
|
this.container.replaceChildren(span('text-sm italic text-light-red dark:text-dark-red', 'Données indisponible'));
|
|
})
|
|
}
|
|
}
|
|
type PlayerState = {
|
|
status: boolean;
|
|
statusDOM: HTMLElement;
|
|
statusTooltip: Text;
|
|
user: { id: number, username: string };
|
|
};
|
|
function defaultPlayerState(user: { id: number, username: string }): PlayerState
|
|
{
|
|
const statusTooltip = text('Absent');
|
|
return {
|
|
status: false,
|
|
statusDOM: tooltip(span('rounded-full w-3 h-3 block border-light-50 dark:border-dark-50 border-2 border-dashed'), statusTooltip, 'right'),
|
|
statusTooltip,
|
|
user
|
|
}
|
|
}
|
|
export class CampaignSheet
|
|
{
|
|
user: ComputedRef<User | null>;
|
|
campaign?: Campaign;
|
|
container: HTMLElement = div('flex flex-1 h-full w-full items-start justify-center');
|
|
dm!: PlayerState;
|
|
players!: Array<PlayerState & { characters: CharacterPrinter[] }>
|
|
constructor(id: string, user: ComputedRef<User | null>)
|
|
{
|
|
this.user = user;
|
|
const load = div("flex justify-center items-center w-full h-full", [ loading('large') ]);
|
|
this.container.replaceChildren(load);
|
|
useRequestFetch()(`/api/campaign/${id}`).then((campaign) => {
|
|
if(campaign)
|
|
{
|
|
this.campaign = campaign;
|
|
this.dm = defaultPlayerState(campaign.owner);
|
|
this.players = campaign.members.map(e => ({
|
|
...defaultPlayerState(e.member),
|
|
characters: campaign.characters.filter(_e => _e.character?.owner === e.member.id).map(_e => new CharacterPrinter(_e.character!.id, _e.character!.name)),
|
|
}));
|
|
document.title = `d[any] - Campagne ${campaign.name}`;
|
|
this.render();
|
|
|
|
load.remove();
|
|
}
|
|
else throw new Error();
|
|
}).catch((e) => {
|
|
console.error(e);
|
|
this.container.replaceChildren(div('flex flex-col items-center justify-center flex-1 h-full gap-4', [
|
|
span('text-2xl font-bold tracking-wider', 'Campagne introuvable'),
|
|
span(undefined, 'Cette campagne n\'existe pas ou est privé.'),
|
|
div('flex flex-row gap-4 justify-center items-center', [
|
|
button(text('Mes campagnes'), () => useRouter().push({ name: 'campaign' }), 'px-2 py-1'),
|
|
button(text('Créer une campagne'), () => useRouter().push({ name: 'campaign-create' }), 'px-2 py-1')
|
|
])
|
|
]));
|
|
});
|
|
}
|
|
private render()
|
|
{
|
|
const campaign = this.campaign;
|
|
|
|
if(!campaign)
|
|
return;
|
|
|
|
this.container.replaceChildren(div('flex flex-col w-full h-full items-center gap-4', [
|
|
div('flex flex-row gap-8 items-center', [
|
|
div('text-2xl font-semibold', [text(campaign.name)]),
|
|
div('border border-light-35 dark:border-dark-35 p-1 flex flex-row items-center gap-2', [
|
|
dom('pre', { class: 'ps-1 w-[400px] truncate' }, [ text(`https://d-any.com/campaign/join/${ encodeURIComponent(campaign.link) }`) ]),
|
|
button(icon('radix-icons:clipboard', { width: 16, height: 16 }), () => {}, 'p-1'),
|
|
])
|
|
]),
|
|
div('flex flex-row gap-4 flex-1', [
|
|
div('flex flex-col w-64', [
|
|
div('flex flex-row items-center gap-4', [ span('text-lg font-bold tracking-tight flex-1', 'Maitre de jeu'), span('border-b border-dashed border-light-35 dark:border-dark-35 flex-1') ]),
|
|
div('flex flex-col divide-y divide-light-25 dark:divide-dark-25', [
|
|
div('flex flex-col py-1 my-1', [
|
|
div('flex flex-row items-center justify-between', [
|
|
span(undefined, this.dm.user.username),
|
|
this.dm.statusDOM,
|
|
])
|
|
])
|
|
]),
|
|
div('flex flex-row items-center gap-4', [ span('text-lg font-bold tracking-tight', 'Joueurs'), span('border-b border-dashed border-light-35 dark:border-dark-35 flex-1') ]),
|
|
div('flex flex-col divide-y divide-light-25 dark:divide-dark-25',
|
|
this.players.length > 0 ? this.players.map((player) => div('flex flex-col py-1 my-1', [
|
|
div('flex flex-row items-center justify-between', [
|
|
span(undefined, player.user.username),
|
|
player.statusDOM,
|
|
]),
|
|
player.characters && player.characters.length > 0 ? div('flex flex-col',
|
|
player.characters.map((character) => div('flex flex-row items-center justify-between', [
|
|
character.container
|
|
]))
|
|
) : span('text-sm italic text-light-70 dark:text-dark-70', 'Pas de personnages'),
|
|
])) : [span('text-sm italic py-2 text-center', 'Invitez des joueurs via le lien')],
|
|
)
|
|
]),
|
|
div('border-l border-light-35 dark:border-dark-35'),
|
|
div('flex flex-col divide-y divide-light-25 dark:divide-dark-25 w-[800px]')
|
|
])
|
|
]));
|
|
}
|
|
} |