All checks were successful
Build Docker Image / build (push) Successful in 1m18s
- Added optional French names, affiliations, origins, and epithets to character records. - Updated character import logic to handle new French fields. - Enhanced character search and display components to show French names and epithets based on selected language. - Modified database schema to include French fields for characters. - Improved error handling in daily character setup to check for existing characters. - Refactored components to utilize helper functions for displaying names and attributes based on language.
113 lines
4.1 KiB
Svelte
113 lines
4.1 KiB
Svelte
<script lang="ts">
|
|
import type { CharacterWithRelations } from "$lib/server/daily-character";
|
|
import { language, t } from '$lib/i18n';
|
|
|
|
export let yesterdayCharacter: CharacterWithRelations | null;
|
|
|
|
$: isFrench = $language === 'fr';
|
|
|
|
function parseEpithets(value: unknown): string[] {
|
|
if (Array.isArray(value)) {
|
|
return value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
try {
|
|
const parsed = JSON.parse(value);
|
|
if (Array.isArray(parsed)) {
|
|
return parsed.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
|
|
}
|
|
} catch {
|
|
if (value.length > 0) {
|
|
return [value];
|
|
}
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
function getDisplayName(character: CharacterWithRelations): string {
|
|
if (isFrench && typeof character.frName === 'string' && character.frName.length > 0) {
|
|
return character.frName;
|
|
}
|
|
|
|
return character.name;
|
|
}
|
|
|
|
function getDisplayEpithets(character: CharacterWithRelations): string[] {
|
|
const frenchEpithets = parseEpithets(character.frEpithets);
|
|
if (isFrench && frenchEpithets.length > 0) {
|
|
return frenchEpithets;
|
|
}
|
|
|
|
return parseEpithets(character.epithets);
|
|
}
|
|
|
|
function getWikiUrl(character: CharacterWithRelations): string {
|
|
if (isFrench && typeof character.frUrl === 'string' && character.frUrl.length > 0) {
|
|
return character.frUrl;
|
|
}
|
|
|
|
return character.url || '';
|
|
}
|
|
|
|
</script>
|
|
|
|
<section class="mt-8 rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
|
|
{#if yesterdayCharacter}
|
|
<div class="flex flex-col items-center gap-5 text-center sm:flex-row sm:text-left">
|
|
{#if yesterdayCharacter.pictureUrl}
|
|
<img
|
|
src={yesterdayCharacter.pictureUrl}
|
|
alt={getDisplayName(yesterdayCharacter)}
|
|
class="h-20 w-20 rounded-full border border-amber-200/40 object-cover"
|
|
/>
|
|
{:else}
|
|
<div class="flex h-20 w-20 items-center justify-center rounded-full border border-amber-200/40 bg-slate-900/70 text-xs uppercase tracking-[0.25em] text-amber-100">
|
|
{$t.game.components.yesterdayCharacter.photo}
|
|
</div>
|
|
{/if}
|
|
<div class="flex-1">
|
|
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">{$t.game.components.yesterdayCharacter.title}</p>
|
|
<p class="mt-2 text-lg font-semibold text-white">{getDisplayName(yesterdayCharacter)}</p>
|
|
{#if getDisplayEpithets(yesterdayCharacter).length > 0}
|
|
<p class="mt-1 text-sm text-slate-400">
|
|
{getDisplayEpithets(yesterdayCharacter).join(', ')}
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
{#if isFrench}
|
|
<a
|
|
href="https://onepiece.fandom.com/fr/wiki/{getWikiUrl(yesterdayCharacter)}"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="w-full rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50 sm:w-auto"
|
|
>
|
|
{$t.game.components.yesterdayCharacter.openPage}
|
|
</a>
|
|
{:else}
|
|
<a
|
|
href="https://onepiece.fandom.com/wiki/{getWikiUrl(yesterdayCharacter)}"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="w-full rounded-full border border-amber-200/40 bg-transparent px-5 py-3 text-sm font-semibold text-amber-100 transition hover:border-amber-200 hover:text-amber-50 sm:w-auto"
|
|
>
|
|
{$t.game.components.yesterdayCharacter.openPage}
|
|
</a>
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div class="flex flex-col items-center gap-5 text-center sm:flex-row sm:text-left">
|
|
<div class="flex h-20 w-20 items-center justify-center rounded-full border border-amber-200/40 bg-slate-900/70 text-xs uppercase tracking-[0.25em] text-amber-100">
|
|
{$t.game.components.yesterdayCharacter.photo}
|
|
</div>
|
|
<div class="flex-1">
|
|
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">{$t.game.components.yesterdayCharacter.title}</p>
|
|
<p class="mt-2 text-lg font-semibold text-white">{$t.game.components.yesterdayCharacter.none}</p>
|
|
<p class="mt-1 text-sm text-slate-200">{$t.game.components.yesterdayCharacter.noneAvailable}</p>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</section>
|