feat: add French localization support for character attributes and improve character display logic
All checks were successful
Build Docker Image / build (push) Successful in 1m18s
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.
This commit is contained in:
@@ -14,6 +14,7 @@ CREATE TABLE `character` (
|
||||
`gender` text,
|
||||
`age` integer,
|
||||
`affiliations` text,
|
||||
`fr_affiliations` text,
|
||||
`devil_fruit_id` text,
|
||||
`haki_observation` integer DEFAULT false,
|
||||
`haki_armament` integer DEFAULT false,
|
||||
@@ -52,6 +53,7 @@ CREATE TABLE `character_override` (
|
||||
`gender` text,
|
||||
`age` integer,
|
||||
`affiliations` text,
|
||||
`fr_affiliations` text,
|
||||
`devil_fruit_id` text,
|
||||
`haki_observation` integer,
|
||||
`haki_armament` integer,
|
||||
@@ -59,9 +61,11 @@ CREATE TABLE `character_override` (
|
||||
`bounty` integer,
|
||||
`height` real,
|
||||
`origin` text,
|
||||
`fr_origin` text,
|
||||
`first_appearance` integer,
|
||||
`picture_url` text,
|
||||
`epithets` text,
|
||||
`fr_epithets` text,
|
||||
`status` text,
|
||||
`arc_id` text,
|
||||
`url` text,
|
||||
@@ -79,6 +83,7 @@ CREATE TABLE `character_scrape_validation` (
|
||||
`gender` text,
|
||||
`age` integer,
|
||||
`affiliations` text,
|
||||
`fr_affiliations` text,
|
||||
`devil_fruit_id` text,
|
||||
`haki_observation` integer DEFAULT false,
|
||||
`haki_armament` integer DEFAULT false,
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "8ffd14bd-bf33-410f-9778-92bc1abc8938",
|
||||
"id": "4b4f14a1-b37b-44f4-aed3-7289bd8cb6a0",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"arc": {
|
||||
@@ -101,6 +101,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"fr_affiliations": {
|
||||
"name": "fr_affiliations",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"devil_fruit_id": {
|
||||
"name": "devil_fruit_id",
|
||||
"type": "text",
|
||||
@@ -372,6 +379,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"fr_affiliations": {
|
||||
"name": "fr_affiliations",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"devil_fruit_id": {
|
||||
"name": "devil_fruit_id",
|
||||
"type": "text",
|
||||
@@ -421,6 +435,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"fr_origin": {
|
||||
"name": "fr_origin",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"first_appearance": {
|
||||
"name": "first_appearance",
|
||||
"type": "integer",
|
||||
@@ -442,6 +463,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"fr_epithets": {
|
||||
"name": "fr_epithets",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
@@ -569,6 +597,13 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"fr_affiliations": {
|
||||
"name": "fr_affiliations",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"devil_fruit_id": {
|
||||
"name": "devil_fruit_id",
|
||||
"type": "text",
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1773447741334,
|
||||
"tag": "0000_keen_rockslide",
|
||||
"when": 1773602933375,
|
||||
"tag": "0000_huge_doctor_octopus",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,144 +1,255 @@
|
||||
[
|
||||
"aladdin_aladdin",
|
||||
"alvida_alvida",
|
||||
"aramaki_aramaki",
|
||||
"arlong_arlong",
|
||||
"ashura_doji_ashura_doji",
|
||||
"baby_5_baby_5",
|
||||
"baggy_baggy",
|
||||
"bartholomew_kuma_bartholomew_kuma",
|
||||
"bartolomeo_bartolomeo",
|
||||
"basil_hawkins_basil_hawkins",
|
||||
"batman_batman",
|
||||
"bellamy_bellamy",
|
||||
"belo_betty_belo_betty",
|
||||
"ben_beckman_ben_beckman",
|
||||
"bentham_bentham",
|
||||
"bepo_bepo",
|
||||
"black_maria_black_maria",
|
||||
"boa_hancock_boa_hancock",
|
||||
"boa_marigold_boa_marigold",
|
||||
"boa_sandersonia_boa_sandersonia",
|
||||
"borsalino_borsalino",
|
||||
"brogy_brogy",
|
||||
"brook_brook",
|
||||
"camie_camie",
|
||||
"capone_bege_capone_bege",
|
||||
"caribou_caribou",
|
||||
"carrot_carrot",
|
||||
"catarina_devon_catarina_devon",
|
||||
"cavendish_cavendish",
|
||||
"cesar_clown_cesar_clown",
|
||||
"chinjao_chinjao",
|
||||
"coby_coby",
|
||||
"corazon_corazon",
|
||||
"crocodile_crocodile",
|
||||
"crocus_crocus",
|
||||
"curly_dadan_curly_dadan",
|
||||
"dalton_dalton",
|
||||
"daz_bones_daz_bones",
|
||||
"denjiro_denjiro",
|
||||
"diamante_diamante",
|
||||
"doc_q_doc_q",
|
||||
"don_quichotte_doflamingo_don_quichotte_doflamingo",
|
||||
"don_quichotte_rossinante_don_quichotte_rossinante",
|
||||
"dorry_dorry",
|
||||
"dracule_mihawk_dracule_mihawk",
|
||||
"duval_duval",
|
||||
"edward_newgate_edward_newgate",
|
||||
"edward_weevil_edward_weevil",
|
||||
"emporio_ivankov_emporio_ivankov",
|
||||
"enel_enel",
|
||||
"eustass_kid_eustass_kid",
|
||||
"fisher_tiger_fisher_tiger",
|
||||
"foxy_foxy",
|
||||
"franky_franky",
|
||||
"fujitora_fujitora",
|
||||
"gan_forr_gan_forr",
|
||||
"gecko_moria_gecko_moria",
|
||||
"gin_gin",
|
||||
"gol_d_roger_gol_d_roger",
|
||||
"haguar_d_sauro_haguar_d_sauro",
|
||||
"hajrudin_hajrudin",
|
||||
"hannyabal_hannyabal",
|
||||
"hatchan_hatchan",
|
||||
"hina_hina",
|
||||
"hody_jones_hody_jones",
|
||||
"hyogoro_hyogoro",
|
||||
"iceburg_iceburg",
|
||||
"imu_imu",
|
||||
"inazuma_inazuma",
|
||||
"inuarashi_inuarashi",
|
||||
"issho_issho",
|
||||
"izo_izo",
|
||||
"jabra_jabra",
|
||||
"jack_jack",
|
||||
"jesus_burgess_jesus_burgess",
|
||||
"jewelry_bonney_jewelry_bonney",
|
||||
"jinbei_jinbei",
|
||||
"joy_boy_joy_boy",
|
||||
"kaidou_kaidou",
|
||||
"kaku_kaku",
|
||||
"kalgara_kalgara",
|
||||
"kalifa_kalifa",
|
||||
"karasu_karasu",
|
||||
"karoo_karoo",
|
||||
"kawamatsu_kawamatsu",
|
||||
"kaya_kaya",
|
||||
"killer_killer",
|
||||
"kinemon_kinemon",
|
||||
"koala_koala",
|
||||
"koby_koby",
|
||||
"kong_kong",
|
||||
"kozuki_hiyori_kozuki_hiyori",
|
||||
"kozuki_momonosuke_kozuki_momonosuke",
|
||||
"kozuki_oden_kozuki_oden",
|
||||
"krieg_krieg",
|
||||
"kureha_kureha",
|
||||
"kuro_kuro",
|
||||
"kurozumi_orochi_kurozumi_orochi",
|
||||
"kuzan_kuzan",
|
||||
"kyros_kyros",
|
||||
"laboon_laboon",
|
||||
"laffitte_laffitte",
|
||||
"lao_g_lao_g",
|
||||
"leo_leo",
|
||||
"lindbergh_lindbergh",
|
||||
"loki_loki",
|
||||
"lucky_roux_lucky_roux",
|
||||
"magellan_magellan",
|
||||
"makino_makino",
|
||||
"marco_marco",
|
||||
"marshall_d_teach_marshall_d_teach",
|
||||
"monkey_d_dragon_monkey_d_dragon",
|
||||
"monkey_d_garp_monkey_d_garp",
|
||||
"monkey_d_luffy_monkey_d_luffy",
|
||||
"montblanc_norland_montblanc_norland",
|
||||
"morgans_morgans",
|
||||
"morley_morley",
|
||||
"mr_3_mr_3",
|
||||
"nami_nami",
|
||||
"nefertari_cobra_nefertari_cobra",
|
||||
"nefertari_vivi_nefertari_vivi",
|
||||
"nekomamushi_nekomamushi",
|
||||
"neptune_neptune",
|
||||
"nico_robin_nico_robin",
|
||||
"oars_oars",
|
||||
"otohime_otohime",
|
||||
"page_one_page_one",
|
||||
"pandaman_pandaman",
|
||||
"pekoms_pekoms",
|
||||
"pell_pell",
|
||||
"perona_perona",
|
||||
"pica_pica",
|
||||
"portgas_d_ace_portgas_d_ace",
|
||||
"queen_queen",
|
||||
"raizo_raizo",
|
||||
"rebecca_rebecca",
|
||||
"rob_lucci_rob_lucci",
|
||||
"rocks_d_xebec_rocks_d_xebec",
|
||||
"roronoa_zoro_roronoa_zoro",
|
||||
"sabo_sabo",
|
||||
"vegapunk_vegapunk",
|
||||
"yamato_yamato"
|
||||
"absalom_absalom",
|
||||
"king_king",
|
||||
"alvida_alvida",
|
||||
"aramaki_aramaki",
|
||||
"arlong_arlong",
|
||||
"ashura_doji_ashura_doji",
|
||||
"vegapunk/atlas_atlas",
|
||||
"avalo_pizarro_avalo_pizarro",
|
||||
"baby_5_baby_5",
|
||||
"buggy_buggy",
|
||||
"bartholomew_kuma_bartholomew_kuma",
|
||||
"bartolomeo_bartolomeo",
|
||||
"basil_hawkins_basil_hawkins",
|
||||
"bell-mère_bell-mère",
|
||||
"bellamy_bellamy",
|
||||
"belo_betty_belo_betty",
|
||||
"benn_beckman_ben_beckman",
|
||||
"bentham_bentham",
|
||||
"bepo_bepo",
|
||||
"black_maria_black_maria",
|
||||
"blueno_blueno",
|
||||
"boa_hancock_boa_hancock",
|
||||
"boa_marigold_boa_marigold",
|
||||
"boa_sandersonia_boa_sandersonia",
|
||||
"borsalino_borsalino",
|
||||
"brogy_brogy",
|
||||
"brook_brook",
|
||||
"buckingham_stussy_buckingham_stussy",
|
||||
"buffalo_buffalo",
|
||||
"camie_camie",
|
||||
"capone_bege_capone_bege",
|
||||
"carmel_carmel",
|
||||
"caribou_caribou",
|
||||
"carrot_carrot",
|
||||
"catarina_devon_catarina_devon",
|
||||
"cavendish_cavendish",
|
||||
"caesar_clown_caesar_clown",
|
||||
"charlotte_brûlée_charlotte_brûlée",
|
||||
"charlotte_cracker_charlotte_cracker",
|
||||
"charlotte_katakuri_charlotte_katakuri",
|
||||
"charlotte_linlin_charlotte_linlin",
|
||||
"charlotte_mont-d'or_charlotte_mont-d'or",
|
||||
"charlotte_oven_charlotte_oven",
|
||||
"charlotte_perospero_charlotte_perospero",
|
||||
"charlotte_pudding_charlotte_pudding",
|
||||
"charlotte_smoothie_charlotte_smoothie",
|
||||
"chinjao_chinjao",
|
||||
"clou_d_clover_clou_d_clover",
|
||||
"crocodile_crocodile",
|
||||
"crocus_crocus",
|
||||
"curly_dadan_curly_dadan",
|
||||
"dalton_dalton",
|
||||
"daz_bonez_daz_bonez",
|
||||
"denjiro_denjiro",
|
||||
"diamante_diamante",
|
||||
"doc_q_doc_q",
|
||||
"donquixote_doflamingo_donquixote_doflamingo",
|
||||
"donquixote_rosinante_donquixote_rosinante",
|
||||
"dorry_dorry",
|
||||
"dracule_mihawk_dracule_mihawk",
|
||||
"vegapunk/edison_edison",
|
||||
"edward_newgate_edward_newgate",
|
||||
"edward_weevil_edward_weevil",
|
||||
"emporio_ivankov_emporio_ivankov",
|
||||
"enel_enel",
|
||||
"eustass_kid_eustass_kid",
|
||||
"fisher_tiger_fisher_tiger",
|
||||
"foxy_foxy",
|
||||
"franky_franky",
|
||||
"fukaboshi_fukaboshi",
|
||||
"fukurou_fukurou",
|
||||
"galdino_galdino",
|
||||
"gan_fall_gan_fall",
|
||||
"gecko_moria_gecko_moria",
|
||||
"gem_gem",
|
||||
"genzo_genzo",
|
||||
"gin_gin",
|
||||
"ginny_ginny",
|
||||
"gol_d_roger_gol_d_roger",
|
||||
"guernika_guernika",
|
||||
"hack_hack",
|
||||
"jaguar_d_saul_jaguar_d_saul",
|
||||
"hajrudin_hajrudin",
|
||||
"hannyabal_hannyabal",
|
||||
"harald_harald",
|
||||
"haredas_haredas",
|
||||
"heracles_heracles",
|
||||
"helmeppo_helmeppo",
|
||||
"hibari_hibari",
|
||||
"hiriluk_hiriluk",
|
||||
"hina_hina",
|
||||
"hody_jones_hody_jones",
|
||||
"hogback_hogback",
|
||||
"hyougoro_hyougoro",
|
||||
"iceburg_iceburg",
|
||||
"igaram_igaram",
|
||||
"imu_imu",
|
||||
"inazuma_inazuma",
|
||||
"inuarashi_inuarashi",
|
||||
"issho_issho",
|
||||
"izou_izou",
|
||||
"jabra_jabra",
|
||||
"jack_jack",
|
||||
"jango_jango",
|
||||
"jesus_burgess_jesus_burgess",
|
||||
"jewelry_bonney_jewelry_bonney",
|
||||
"jinbe_jinbe",
|
||||
"giolla_giolla",
|
||||
"joy_boy_joy_boy",
|
||||
"jozu_jozu",
|
||||
"kaidou_kaidou",
|
||||
"kaku_kaku",
|
||||
"kalgara_kalgara",
|
||||
"kalifa_kalifa",
|
||||
"karasu_karasu",
|
||||
"karoo_karoo",
|
||||
"kawamatsu_kawamatsu",
|
||||
"kaya_kaya",
|
||||
"kelly_funk_kelly_funk",
|
||||
"kikunojo_kikunojo",
|
||||
"killer_killer",
|
||||
"kin'emon_kin'emon",
|
||||
"koala_koala",
|
||||
"koby_koby",
|
||||
"kokoro_kokoro",
|
||||
"kouzuki_hiyori_kouzuki_hiyori",
|
||||
"kouzuki_momonosuke_kouzuki_momonosuke",
|
||||
"kouzuki_oden_kouzuki_oden",
|
||||
"kouzuki_sukiyaki_kouzuki_sukiyaki",
|
||||
"kouzuki_toki_kouzuki_toki",
|
||||
"krieg_krieg",
|
||||
"kumadori_kumadori",
|
||||
"kureha_kureha",
|
||||
"kuro_kuro",
|
||||
"kurozumi_kanjuro_kurozumi_kanjuro",
|
||||
"kurozumi_orochi_kurozumi_orochi",
|
||||
"kurozumi_tama_kurozumi_tama",
|
||||
"kuzan_kuzan",
|
||||
"kyros_kyros",
|
||||
"laboon_laboon",
|
||||
"laffitte_laffitte",
|
||||
"lao_g_lao_g",
|
||||
"leo_leo",
|
||||
"vegapunk/lilith_lilith",
|
||||
"lindbergh_lindbergh",
|
||||
"loki_loki",
|
||||
"lucky_roux_lucky_roux",
|
||||
"magellan_magellan",
|
||||
"makino_makino",
|
||||
"mansherry_mansherry",
|
||||
"marco_marco",
|
||||
"marshall_d_teach_marshall_d_teach",
|
||||
"merry_merry",
|
||||
"momoo_momoo",
|
||||
"mocha_mocha",
|
||||
"monet_monet",
|
||||
"monkey_d_dragon_monkey_d_dragon",
|
||||
"monkey_d_garp_monkey_d_garp",
|
||||
"monkey_d_luffy_monkey_d_luffy",
|
||||
"mont_blanc_cricket_mont_blanc_cricket",
|
||||
"mont_blanc_noland_mont_blanc_noland",
|
||||
"morgans_morgans",
|
||||
"morgan_morgan",
|
||||
"morley_morley",
|
||||
"nami_nami",
|
||||
"nefertari_cobra_nefertari_cobra",
|
||||
"nefertari_vivi_nefertari_vivi",
|
||||
"nekomamushi_nekomamushi",
|
||||
"neptune_neptune",
|
||||
"nico_olvia_nico_olvia",
|
||||
"nico_robin_nico_robin",
|
||||
"nojiko_nojiko",
|
||||
"hatchan_hatchan",
|
||||
"otohime_otohime",
|
||||
"oars_oars",
|
||||
"page_one_page_one",
|
||||
"pandaman_pandaman",
|
||||
"paulie_paulie",
|
||||
"pedro_pedro",
|
||||
"pekoms_pekoms",
|
||||
"pell_pell",
|
||||
"perona_perona",
|
||||
"pica_pica",
|
||||
"portgas_d_ace_portgas_d_ace",
|
||||
"vegapunk/pythagoras_pythagoras",
|
||||
"queen_queen",
|
||||
"raizo_raizo",
|
||||
"rebecca_rebecca",
|
||||
"riku_doldo_iii_riku_doldo_iii",
|
||||
"rob_lucci_rob_lucci",
|
||||
"rocks_d_xebec_rocks_d_xebec",
|
||||
"roronoa_zoro_roronoa_zoro",
|
||||
"s-bear_s-bear",
|
||||
"s-hawk_s-hawk",
|
||||
"s-snake_s-snake",
|
||||
"sabo_sabo",
|
||||
"sadi_sadi",
|
||||
"donquixote_mjosgard_donquixote_mjosgard",
|
||||
"rimoshifu_killingham_rimoshifu_killingham",
|
||||
"manmayer_gunko_manmayer_gunko",
|
||||
"shepherd_sommers_shepherd_sommers",
|
||||
"sakazuki_sakazuki",
|
||||
"sanjuan_wolf_sanjuan_wolf",
|
||||
"sasaki_sasaki",
|
||||
"scratchmen_apoo_scratchmen_apoo",
|
||||
"sengoku_sengoku",
|
||||
"senor_pink_senor_pink",
|
||||
"sentomaru_sentomaru",
|
||||
"vegapunk/shaka_shaka",
|
||||
"shakuyaku_shakuyaku",
|
||||
"shanks_shanks",
|
||||
"shiryu_shiryu",
|
||||
"shimotsuki_kuina_shimotsuki_kuina",
|
||||
"shimotsuki_yasuie_shimotsuki_yasuie",
|
||||
"shinobu_shinobu",
|
||||
"shirahoshi_shirahoshi",
|
||||
"silvers_rayleigh_silvers_rayleigh",
|
||||
"smoker_smoker",
|
||||
"spandam_spandam",
|
||||
"speed_speed",
|
||||
"stussy_stussy",
|
||||
"sugar_sugar",
|
||||
"tamago_tamago",
|
||||
"tashigi_tashigi",
|
||||
"toko_toko",
|
||||
"tom_tom",
|
||||
"tony_tony_chopper_tony_tony_chopper",
|
||||
"trafalgar_d_water_law_trafalgar_d_water_law",
|
||||
"trebol_trebol",
|
||||
"tsuru_tsuru",
|
||||
"ulti_ulti",
|
||||
"urouge_urouge",
|
||||
"usopp_usopp",
|
||||
"uta_uta",
|
||||
"van_augur_van_augur",
|
||||
"vander_decken_ix_vander_decken_ix",
|
||||
"vegapunk_vegapunk",
|
||||
"vergo_vergo",
|
||||
"vinsmoke_ichiji_vinsmoke_ichiji",
|
||||
"vinsmoke_judge_vinsmoke_judge",
|
||||
"vinsmoke_niji_vinsmoke_niji",
|
||||
"vinsmoke_reiju_vinsmoke_reiju",
|
||||
"sanji_sanji",
|
||||
"vinsmoke_yonji_vinsmoke_yonji",
|
||||
"viola_viola",
|
||||
"wadatsumi_wadatsumi",
|
||||
"wapol_wapol",
|
||||
"wyper_wyper",
|
||||
"x_drake_x_drake",
|
||||
"yamato_yamato",
|
||||
"yasopp_yasopp",
|
||||
"vegapunk/york_york",
|
||||
"zeff_zeff"
|
||||
]
|
||||
@@ -9,6 +9,7 @@ type Status = 'Alive' | 'Dead' | 'Unknown';
|
||||
type ArcRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
frName?: string | null;
|
||||
startChapter: number;
|
||||
endChapter?: number | null;
|
||||
url?: string | null;
|
||||
@@ -170,6 +171,7 @@ async function importFromJson(): Promise<void> {
|
||||
.values({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
frName: toNullable(item.frName),
|
||||
startChapter: item.startChapter,
|
||||
endChapter: toNullable(item.endChapter),
|
||||
url: toNullable(item.url)
|
||||
@@ -178,6 +180,7 @@ async function importFromJson(): Promise<void> {
|
||||
target: arc.id,
|
||||
set: {
|
||||
name: item.name,
|
||||
frName: toNullable(item.frName),
|
||||
startChapter: item.startChapter,
|
||||
endChapter: toNullable(item.endChapter),
|
||||
url: toNullable(item.url)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import fs from 'fs';
|
||||
import { character, characterHistory } from '../src/lib/server/db/schema';
|
||||
|
||||
@@ -24,13 +24,14 @@ function getErrorMessage(error: unknown): string {
|
||||
|
||||
async function setDailyCharacters(): Promise<void> {
|
||||
try {
|
||||
const dailyCharacterIds = readJsonFile('./scripts/daily-characters.json');
|
||||
const dailyCharacterIdsRaw = readJsonFile('./scripts/daily-characters.json');
|
||||
|
||||
if (!dailyCharacterIds || dailyCharacterIds.length === 0) {
|
||||
console.error('❌ No daily characters found in daily-characters.json');
|
||||
process.exit(1);
|
||||
if (!dailyCharacterIdsRaw || dailyCharacterIdsRaw.length === 0) {
|
||||
throw new Error('No daily characters found in daily-characters.json');
|
||||
}
|
||||
|
||||
const dailyCharacterIds = dailyCharacterIdsRaw;
|
||||
|
||||
console.log(`\n=== Setting Daily Mode Characters ===\n`);
|
||||
console.log(`Found ${dailyCharacterIds.length} characters to set as daily\n`);
|
||||
|
||||
@@ -45,16 +46,36 @@ async function setDailyCharacters(): Promise<void> {
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
const existingCharacters = await db
|
||||
.select({ id: character.id })
|
||||
.from(character)
|
||||
.where(inArray(character.id, dailyCharacterIds));
|
||||
|
||||
const existingIdSet = new Set(existingCharacters.map((c) => c.id));
|
||||
const missingIds = dailyCharacterIds.filter((id) => !existingIdSet.has(id));
|
||||
|
||||
if (missingIds.length > 0) {
|
||||
errorCount += missingIds.length;
|
||||
console.error(`✗ ${missingIds.length} character ID(s) were not found in database:`);
|
||||
for (const missingId of missingIds) {
|
||||
console.error(` - ${missingId}`);
|
||||
}
|
||||
console.error('');
|
||||
}
|
||||
|
||||
for (let i = 0; i < dailyCharacterIds.length; i++) {
|
||||
const charId = dailyCharacterIds[i];
|
||||
if (!existingIdSet.has(charId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db
|
||||
await db
|
||||
.update(character)
|
||||
.set({ isInDailyMode: true })
|
||||
.where(eq(character.id, charId));
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rUpdated: ${successCount}/${dailyCharacterIds.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error updating character ${i + 1}:`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { CharacterWithRelations } from '$lib/server/daily-character';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
|
||||
let {
|
||||
characters,
|
||||
@@ -20,6 +20,46 @@
|
||||
searchContainer: null as HTMLDivElement | null
|
||||
});
|
||||
|
||||
const isFrench = $derived($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 normalizeSearchText(value: string): string {
|
||||
return value
|
||||
.normalize('NFD')
|
||||
@@ -40,25 +80,12 @@
|
||||
const searchTerm = normalizeSearchText(state.searchInput);
|
||||
|
||||
return characters.filter((char) => {
|
||||
const nameMatches = normalizeSearchText(char.name).includes(searchTerm);
|
||||
|
||||
let epithetsMatches = false;
|
||||
if (char.epithets) {
|
||||
try {
|
||||
const parsedEpithets =
|
||||
typeof char.epithets === 'string' ? JSON.parse(char.epithets) : char.epithets;
|
||||
|
||||
if (Array.isArray(parsedEpithets)) {
|
||||
epithetsMatches = parsedEpithets.some((epithet: string) =>
|
||||
normalizeSearchText(epithet).includes(searchTerm)
|
||||
);
|
||||
} else if (typeof parsedEpithets === 'string') {
|
||||
epithetsMatches = normalizeSearchText(parsedEpithets).includes(searchTerm);
|
||||
}
|
||||
} catch {
|
||||
epithetsMatches = normalizeSearchText(String(char.epithets)).includes(searchTerm);
|
||||
}
|
||||
}
|
||||
const displayName = getDisplayName(char);
|
||||
const displayEpithets = getDisplayEpithets(char);
|
||||
const nameMatches = normalizeSearchText(displayName).includes(searchTerm);
|
||||
const epithetsMatches = displayEpithets.some((epithet) =>
|
||||
normalizeSearchText(epithet).includes(searchTerm)
|
||||
);
|
||||
|
||||
return (nameMatches || epithetsMatches) &&
|
||||
!selectedCharacters.some((selected) => selected.id === char.id);
|
||||
@@ -161,7 +188,7 @@
|
||||
{#if character.pictureUrl}
|
||||
<img
|
||||
src={character.pictureUrl}
|
||||
alt={character.name}
|
||||
alt={getDisplayName(character)}
|
||||
loading="lazy"
|
||||
class="w-12 h-12 rounded-full object-cover border border-amber-200/30"
|
||||
/>
|
||||
@@ -171,16 +198,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-1">
|
||||
<span class="font-semibold text-amber-100">{character.name}</span>
|
||||
{#if character.epithets}
|
||||
{@const parsedEpithets = typeof character.epithets === 'string'
|
||||
? JSON.parse(character.epithets)
|
||||
: character.epithets}
|
||||
{#if Array.isArray(parsedEpithets) && parsedEpithets.length > 0}
|
||||
<span class="font-semibold text-amber-100">{getDisplayName(character)}</span>
|
||||
{#if getDisplayEpithets(character).length > 0}
|
||||
<span class="ml-2 text-xs text-slate-400">
|
||||
• {parsedEpithets.join(', ')}
|
||||
• {getDisplayEpithets(character).join(', ')}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { formatBounty } from '$lib';
|
||||
import { resolve } from '$app/paths';
|
||||
import type { CharacterWithRelations } from '$lib/server/daily-character';
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
|
||||
export let selectedCharacters: CharacterWithRelations[];
|
||||
export let dailyCharacter: CharacterWithRelations;
|
||||
@@ -62,6 +63,52 @@
|
||||
const dailyPrimary = firstAffiliation(dailyAffiliations);
|
||||
return characterPrimary === dailyPrimary;
|
||||
}
|
||||
|
||||
$: isFrench = $language === 'fr';
|
||||
|
||||
function getDisplayName(character: CharacterWithRelations): string {
|
||||
if (isFrench && typeof character.frName === 'string' && character.frName.length > 0) {
|
||||
return character.frName;
|
||||
}
|
||||
|
||||
return character.name;
|
||||
}
|
||||
|
||||
function getWikiUrl(character: CharacterWithRelations): string {
|
||||
if (isFrench && typeof character.frUrl === 'string' && character.frUrl.length > 0) {
|
||||
return character.frUrl;
|
||||
}
|
||||
|
||||
return character.url || '';
|
||||
}
|
||||
|
||||
function getWikiBaseUrl(): string {
|
||||
return isFrench ? 'https://onepiece.fandom.com/fr/wiki/' : 'https://onepiece.fandom.com/wiki/';
|
||||
}
|
||||
|
||||
function getDisplayOrigin(character: CharacterWithRelations): string | null {
|
||||
if (isFrench && typeof character.frOrigin === 'string' && character.frOrigin.length > 0) {
|
||||
return character.frOrigin;
|
||||
}
|
||||
|
||||
return character.origin;
|
||||
}
|
||||
|
||||
function hasMatchingOrigin(characterEntry: CharacterWithRelations, dailyEntry: CharacterWithRelations): boolean {
|
||||
return getDisplayOrigin(characterEntry) === getDisplayOrigin(dailyEntry);
|
||||
}
|
||||
|
||||
function getDisplayArcName(character: CharacterWithRelations): string | null {
|
||||
if (isFrench && typeof character.frArcName === 'string' && character.frArcName.length > 0) {
|
||||
return character.frArcName;
|
||||
}
|
||||
|
||||
return character.arcName;
|
||||
}
|
||||
|
||||
function hasMatchingArc(characterEntry: CharacterWithRelations, dailyEntry: CharacterWithRelations): boolean {
|
||||
return getDisplayArcName(characterEntry) === getDisplayArcName(dailyEntry);
|
||||
}
|
||||
</script>
|
||||
|
||||
<section
|
||||
@@ -197,14 +244,14 @@
|
||||
>
|
||||
{#if character.pictureUrl}
|
||||
<a
|
||||
href={'https://onepiece.fandom.com/fr/wiki/' + character.url}
|
||||
href={getWikiBaseUrl() + getWikiUrl(character)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block h-full w-full"
|
||||
>
|
||||
<img
|
||||
src={character.pictureUrl}
|
||||
alt={character.name}
|
||||
alt={getDisplayName(character)}
|
||||
class="h-full w-full cursor-pointer object-cover transition-opacity hover:opacity-80"
|
||||
/>
|
||||
</a>
|
||||
@@ -214,7 +261,7 @@
|
||||
>
|
||||
<span
|
||||
class="line-clamp-3 text-center text-xs font-semibold sm:text-sm md:text-xl"
|
||||
>{character.name}</span
|
||||
>{getDisplayName(character)}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -407,13 +454,12 @@
|
||||
<!-- Origine -->
|
||||
{#if columnVisibility.origin !== false}
|
||||
<div
|
||||
class="h-16 w-16 shrink-0 rounded-lg border border-white/10 sm:h-20 sm:w-20 md:h-24 md:w-24 {character.origin ===
|
||||
dailyCharacter.origin
|
||||
class="h-16 w-16 shrink-0 rounded-lg border border-white/10 sm:h-20 sm:w-20 md:h-24 md:w-24 {hasMatchingOrigin(character, dailyCharacter)
|
||||
? 'bg-emerald-600/90'
|
||||
: 'bg-red-900/60'} flex items-center justify-center p-1 sm:p-2"
|
||||
>
|
||||
<p class="text-center text-[10px] font-bold text-white sm:text-xs md:text-sm">
|
||||
{character.origin || $t.game.components.guessHistory.unknown}
|
||||
{getDisplayOrigin(character) || $t.game.components.guessHistory.unknown}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -421,12 +467,11 @@
|
||||
<!-- Arc -->
|
||||
{#if columnVisibility.arc !== false}
|
||||
<div
|
||||
class="h-16 w-16 shrink-0 rounded-lg border border-white/10 sm:h-20 sm:w-20 md:h-24 md:w-24 {character.arcName ===
|
||||
dailyCharacter.arcName
|
||||
class="h-16 w-16 shrink-0 rounded-lg border border-white/10 sm:h-20 sm:w-20 md:h-24 md:w-24 {hasMatchingArc(character, dailyCharacter)
|
||||
? 'bg-emerald-600/90'
|
||||
: 'bg-red-900/60'} relative flex items-center justify-center overflow-hidden p-1 sm:p-2"
|
||||
>
|
||||
{#if character.arcName !== dailyCharacter.arcName && character.firstAppearance && dailyCharacter.firstAppearance && character.firstAppearance !== dailyCharacter.firstAppearance}
|
||||
{#if !hasMatchingArc(character, dailyCharacter) && character.firstAppearance && dailyCharacter.firstAppearance && character.firstAppearance !== dailyCharacter.firstAppearance}
|
||||
<div
|
||||
class="pointer-events-none absolute h-full w-full opacity-30"
|
||||
style="
|
||||
@@ -440,7 +485,7 @@
|
||||
<p
|
||||
class="relative z-10 text-center text-[10px] font-bold text-white sm:text-xs md:text-sm"
|
||||
>
|
||||
{character.arcName || $t.game.components.guessHistory.unknown}
|
||||
{getDisplayArcName(character) || $t.game.components.guessHistory.unknown}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { CharacterWithRelations } from "$lib/server/daily-character";
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
|
||||
export let dailyCharacter: CharacterWithRelations;
|
||||
export let selectedCharacters: CharacterWithRelations[];
|
||||
@@ -16,6 +16,15 @@
|
||||
$: isOriginAvailable = selectedCharacters.length >= 5;
|
||||
$: isFruitAvailable = selectedCharacters.length >= 10;
|
||||
$: isAffiliationAvailable = selectedCharacters.length >= 15;
|
||||
$: isFrench = $language === 'fr';
|
||||
|
||||
function getDisplayOrigin(character: CharacterWithRelations): string | null {
|
||||
if (isFrench && typeof character.frOrigin === 'string' && character.frOrigin.length > 0) {
|
||||
return character.frOrigin;
|
||||
}
|
||||
|
||||
return character.origin;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -47,7 +56,7 @@
|
||||
>
|
||||
<p class="text-sm font-medium text-amber-100">{$t.game.components.hints.origin}</p>
|
||||
{#if showHintOrigin}
|
||||
<p class="mt-2 text-xs text-white font-semibold">{dailyCharacter.origin || $t.game.components.hints.unknown}</p>
|
||||
<p class="mt-2 text-xs text-white font-semibold">{getDisplayOrigin(dailyCharacter) || $t.game.components.hints.unknown}</p>
|
||||
{:else if Math.max(0, 5 - selectedCharacters.length) > 0}
|
||||
<p class="mt-2 text-xs text-slate-400">{Math.max(0, 5 - selectedCharacters.length)} {$t.game.components.hints.beforeUnlock}</p>
|
||||
{:else}
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
<script lang="ts">
|
||||
import type { CharacterWithRelations } from "$lib/server/daily-character";
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
|
||||
export let selectedCharacter: CharacterWithRelations;
|
||||
export let selectedCharacters: CharacterWithRelations[];
|
||||
export let isGeckoMoriaWin: boolean = false;
|
||||
|
||||
$: isFrench = $language === 'fr';
|
||||
|
||||
function getDisplayName(character: CharacterWithRelations): string {
|
||||
if (isFrench && typeof character.frName === 'string' && character.frName.length > 0) {
|
||||
return character.frName;
|
||||
}
|
||||
|
||||
return character.name;
|
||||
}
|
||||
|
||||
function getWikiUrl(character: CharacterWithRelations): string {
|
||||
if (isFrench && typeof character.frUrl === 'string' && character.frUrl.length > 0) {
|
||||
return character.frUrl;
|
||||
}
|
||||
|
||||
return character.url || '';
|
||||
}
|
||||
|
||||
function getWikiBaseUrl(): string {
|
||||
return isFrench ? 'https://onepiece.fandom.com/fr/wiki/' : 'https://onepiece.fandom.com/wiki/';
|
||||
}
|
||||
|
||||
const pickMessage = (messages: readonly string[]) => messages[Math.floor(Math.random() * messages.length)];
|
||||
|
||||
const getAttemptMessage = (attempts: number): string => {
|
||||
@@ -48,19 +70,19 @@
|
||||
<div class="mt-3">
|
||||
{#if selectedCharacter.pictureUrl}
|
||||
<a
|
||||
href={"https://onepiece.fandom.com/fr/wiki/" + selectedCharacter.url}
|
||||
href={getWikiBaseUrl() + getWikiUrl(selectedCharacter)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-block"
|
||||
>
|
||||
<img
|
||||
src={selectedCharacter.pictureUrl}
|
||||
alt={selectedCharacter.name}
|
||||
alt={getDisplayName(selectedCharacter)}
|
||||
class="w-20 h-20 mx-auto rounded-full border-2 border-slate-600 shadow-lg object-cover hover:border-slate-500 transition-colors cursor-pointer opacity-80"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
<p class="mt-2 text-lg font-bold text-slate-200">{selectedCharacter.name}</p>
|
||||
<p class="mt-2 text-lg font-bold text-slate-200">{getDisplayName(selectedCharacter)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,19 +96,19 @@
|
||||
<div class="mt-3">
|
||||
{#if selectedCharacter.pictureUrl}
|
||||
<a
|
||||
href={"https://onepiece.fandom.com/fr/wiki/" + selectedCharacter.url}
|
||||
href={getWikiBaseUrl() + getWikiUrl(selectedCharacter)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-block"
|
||||
>
|
||||
<img
|
||||
src={selectedCharacter.pictureUrl}
|
||||
alt={selectedCharacter.name}
|
||||
alt={getDisplayName(selectedCharacter)}
|
||||
class="w-20 h-20 mx-auto rounded-full border-2 border-emerald-400 shadow-lg object-cover hover:border-emerald-300 transition-colors cursor-pointer"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
<p class="mt-2 text-lg font-bold text-white">{selectedCharacter.name}</p>
|
||||
<p class="mt-2 text-lg font-bold text-white">{getDisplayName(selectedCharacter)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,57 @@
|
||||
<script lang="ts">
|
||||
import type { CharacterWithRelations } from "$lib/server/daily-character";
|
||||
import { t } from '$lib/i18n';
|
||||
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">
|
||||
@@ -11,7 +60,7 @@
|
||||
{#if yesterdayCharacter.pictureUrl}
|
||||
<img
|
||||
src={yesterdayCharacter.pictureUrl}
|
||||
alt={yesterdayCharacter.name}
|
||||
alt={getDisplayName(yesterdayCharacter)}
|
||||
class="h-20 w-20 rounded-full border border-amber-200/40 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
@@ -21,23 +70,32 @@
|
||||
{/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">{yesterdayCharacter.name}</p>
|
||||
{#if yesterdayCharacter.epithets}
|
||||
<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">
|
||||
{typeof yesterdayCharacter.epithets === 'string'
|
||||
? JSON.parse(yesterdayCharacter.epithets).join(', ')
|
||||
: (yesterdayCharacter.epithets as string[]).join(', ')}
|
||||
{getDisplayEpithets(yesterdayCharacter).join(', ')}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<a
|
||||
href={"https://onepiece.fandom.com/fr/wiki/" + yesterdayCharacter.url}
|
||||
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 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">
|
||||
|
||||
@@ -8,9 +8,11 @@ const RANDOM_SEED = Math.random();
|
||||
const characterWithRelationsSelect = {
|
||||
id: character.id,
|
||||
name: character.name,
|
||||
frName: character.frName,
|
||||
gender: character.gender,
|
||||
age: character.age,
|
||||
affiliations: character.affiliations,
|
||||
frAffiliations: character.frAffiliations,
|
||||
devilFruitId: character.devilFruitId,
|
||||
devilFruitName: devilFruit.name,
|
||||
devilFruitType: devilFruit.type,
|
||||
@@ -20,19 +22,24 @@ const characterWithRelationsSelect = {
|
||||
bounty: character.bounty,
|
||||
height: character.height,
|
||||
origin: character.origin,
|
||||
frOrigin: character.frOrigin,
|
||||
firstAppearance: character.firstAppearance,
|
||||
pictureUrl: character.pictureUrl,
|
||||
epithets: character.epithets,
|
||||
frEpithets: character.frEpithets,
|
||||
status: character.status,
|
||||
url: character.url,
|
||||
frUrl: character.frUrl,
|
||||
arcId: character.arcId,
|
||||
arcName: arc.name
|
||||
arcName: arc.name,
|
||||
frArcName: arc.frName,
|
||||
};
|
||||
|
||||
export type CharacterWithRelations = Character & {
|
||||
devilFruitName: string | null;
|
||||
devilFruitType: string | null;
|
||||
arcName: string | null;
|
||||
frArcName: string | null;
|
||||
};
|
||||
|
||||
type RelationMaps = {
|
||||
@@ -68,8 +75,10 @@ function mergeCharacterWithOverride(
|
||||
if (relationMaps) {
|
||||
if (mergedCharacter.arcId) {
|
||||
mergedCharacter.arcName = relationMaps.arcNameById.get(mergedCharacter.arcId) ?? null;
|
||||
mergedCharacter.frArcName = relationMaps.arcNameById.get(mergedCharacter.arcId) ?? null;
|
||||
} else {
|
||||
mergedCharacter.arcName = null;
|
||||
mergedCharacter.frArcName = null;
|
||||
}
|
||||
|
||||
if (mergedCharacter.devilFruitId) {
|
||||
|
||||
@@ -44,6 +44,7 @@ export const character = sqliteTable('character', {
|
||||
gender: text('gender'),
|
||||
age: integer('age'),
|
||||
affiliations: text('affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
frAffiliations: text('fr_affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
devilFruitId: text('devil_fruit_id').references(() => devilFruit.id),
|
||||
hakiObservation: integer('haki_observation', { mode: 'boolean' }).default(false),
|
||||
hakiArmament: integer('haki_armament', { mode: 'boolean' }).default(false),
|
||||
@@ -72,6 +73,7 @@ export const characterOverride = sqliteTable('character_override', {
|
||||
gender: text('gender'),
|
||||
age: integer('age'),
|
||||
affiliations: text('affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
frAffiliations: text('fr_affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
devilFruitId: text('devil_fruit_id').references(() => devilFruit.id, { onDelete: 'set null' }),
|
||||
hakiObservation: integer('haki_observation', { mode: 'boolean' }),
|
||||
hakiArmament: integer('haki_armament', { mode: 'boolean' }),
|
||||
@@ -79,9 +81,11 @@ export const characterOverride = sqliteTable('character_override', {
|
||||
bounty: integer('bounty'),
|
||||
height: real('height'),
|
||||
origin: text('origin'),
|
||||
frOrigin: text('fr_origin'),
|
||||
firstAppearance: integer('first_appearance'),
|
||||
pictureUrl: text('picture_url'),
|
||||
epithets: text('epithets', { mode: 'json' }).$type<string[]>(),
|
||||
frEpithets: text('fr_epithets', { mode: 'json' }).$type<string[]>(),
|
||||
status: text('status').$type<Status | null>(),
|
||||
arcId: text('arc_id').references(() => arc.id, { onDelete: 'set null' }),
|
||||
url: text('url'),
|
||||
@@ -99,6 +103,7 @@ export const characterScrapeValidation = sqliteTable('character_scrape_validatio
|
||||
gender: text('gender'),
|
||||
age: integer('age'),
|
||||
affiliations: text('affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
frAffiliations: text('fr_affiliations', { mode: 'json' }).$type<string[]>(),
|
||||
devilFruitId: text('devil_fruit_id').references(() => devilFruit.id, { onDelete: 'set null' }),
|
||||
hakiObservation: integer('haki_observation', { mode: 'boolean' }).default(false),
|
||||
hakiArmament: integer('haki_armament', { mode: 'boolean' }).default(false),
|
||||
|
||||
@@ -2,9 +2,58 @@
|
||||
export let data;
|
||||
|
||||
import { resolve } from '$app/paths';
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
import type { CharacterWithRelations } from '$lib/server/daily-character';
|
||||
|
||||
$: yesterdayCharacter = data.yesterdayCharacter;
|
||||
$: 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 | null): string {
|
||||
if (isFrench && typeof character?.frName === 'string' && character.frName.length > 0) {
|
||||
return character.frName;
|
||||
}
|
||||
|
||||
return character?.name || '';
|
||||
}
|
||||
|
||||
function getDisplayEpithets(character: CharacterWithRelations | null): string[] {
|
||||
const frenchEpithets = parseEpithets(character?.frEpithets);
|
||||
if (isFrench && frenchEpithets.length > 0) {
|
||||
return frenchEpithets;
|
||||
}
|
||||
|
||||
return parseEpithets(character?.epithets);
|
||||
}
|
||||
|
||||
function getWikiUrl(character: CharacterWithRelations | null): string {
|
||||
if (isFrench && typeof character?.frUrl === 'string' && character.frUrl.length > 0) {
|
||||
return character.frUrl;
|
||||
}
|
||||
|
||||
return character?.url || '';
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -57,7 +106,7 @@
|
||||
{#if yesterdayCharacter.pictureUrl}
|
||||
<img
|
||||
src={yesterdayCharacter.pictureUrl}
|
||||
alt={yesterdayCharacter.name}
|
||||
alt={getDisplayName(yesterdayCharacter)}
|
||||
class="h-20 w-20 rounded-full border border-amber-200/40 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
@@ -67,23 +116,32 @@
|
||||
{/if}
|
||||
<div class="flex-1">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100">{$t.game.home.yesterdayCharacter}</p>
|
||||
<p class="mt-2 text-lg font-semibold text-white">{yesterdayCharacter.name}</p>
|
||||
{#if yesterdayCharacter.epithets}
|
||||
<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">
|
||||
{typeof yesterdayCharacter.epithets === 'string'
|
||||
? JSON.parse(yesterdayCharacter.epithets).join(', ')
|
||||
: (yesterdayCharacter.epithets as string[]).join(', ')}
|
||||
{getDisplayEpithets(yesterdayCharacter).join(', ')}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<a
|
||||
href={"https://onepiece.fandom.com/fr/wiki/" + yesterdayCharacter.url}
|
||||
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.home.openPage}
|
||||
</a>
|
||||
{#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.home.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.home.openPage}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center gap-5 text-center sm:flex-row sm:text-left">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import WinPanel from '$lib/components/WinPanel.svelte';
|
||||
import HintsPanel from '$lib/components/HintsPanel.svelte';
|
||||
import type { CharacterWithRelations } from '$lib/server/daily-character.js';
|
||||
import { t } from '$lib/i18n';
|
||||
import { language, t } from '$lib/i18n';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -213,16 +213,32 @@
|
||||
}
|
||||
|
||||
$: allCharacters = data.characters || [];
|
||||
$: isFrench = $language === 'fr';
|
||||
|
||||
function getDisplayArcName(character: CharacterWithRelations, useFrench: boolean): string | null {
|
||||
if (useFrench && typeof character.frArcName === 'string' && character.frArcName.length > 0) {
|
||||
return character.frArcName;
|
||||
}
|
||||
|
||||
return character.arcName;
|
||||
}
|
||||
|
||||
// Extract unique arcs from all characters
|
||||
$: {
|
||||
const useFrench = isFrench;
|
||||
const arcMap = new Map<string, ArcFilterOption>(
|
||||
allCharacters
|
||||
.filter(
|
||||
(char: CharacterWithRelations): char is CharacterWithRelations & { arcId: string; arcName: string } =>
|
||||
typeof char.arcId === 'string' && char.arcId.length > 0 && typeof char.arcName === 'string' && char.arcName.length > 0
|
||||
(char: CharacterWithRelations): char is CharacterWithRelations & { arcId: string } =>
|
||||
typeof char.arcId === 'string' &&
|
||||
char.arcId.length > 0 &&
|
||||
typeof getDisplayArcName(char, useFrench) === 'string' &&
|
||||
getDisplayArcName(char, useFrench)!.length > 0
|
||||
)
|
||||
.map((char: CharacterWithRelations & { arcId: string; arcName: string }) => [char.arcId, { id: char.arcId, name: char.arcName }])
|
||||
.map((char: CharacterWithRelations & { arcId: string }) => [
|
||||
char.arcId,
|
||||
{ id: char.arcId, name: getDisplayArcName(char, useFrench) as string }
|
||||
])
|
||||
);
|
||||
|
||||
availableArcs = [...arcMap.values()].sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
Reference in New Issue
Block a user