Skip to content

Commit d846d7a

Browse files
feat: improve components
1 parent 69befe5 commit d846d7a

File tree

3 files changed

+230
-86
lines changed

3 files changed

+230
-86
lines changed

src/components/editors/account/AccountEditor.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
let isProcessing = $state(false);
1919
let loadedFileName = $state('');
2020
21-
let accountData = $state({});
21+
let accountData = $state(null!);
2222
2323
// Constants
2424
const ACCEPTED_FILE_EXTENSION = '.hg';
@@ -164,7 +164,7 @@
164164
function resetEditor() {
165165
if (isProcessing) return;
166166
167-
accountData = {};
167+
accountData = null!;
168168
editorState = 'initial';
169169
loadedFileName = '';
170170
@@ -268,7 +268,7 @@
268268

269269
<input id="file-upload" type="file" accept={ACCEPTED_FILE_EXTENSION} disabled={isProcessing} class="hidden" bind:this={inputElement} onchange={clickHandler} />
270270
</form>
271-
{:else}
271+
{:else if accountData}
272272
<!-- File loaded successfully - show controls and editor -->
273273
<div class="space-y-6">
274274
<!-- File info and controls -->

src/components/editors/account/AccountEditorPane.svelte

Lines changed: 115 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,89 @@
77
import dataExpeditions from '$lib/../data/expeditions.json';
88
import { m } from '$lib/paraglide/messages';
99
10-
let { accountData = $bindable() }: { accountData: any } = $props();
10+
interface AccountData {
11+
UserSettingsData?: {
12+
UnlockedSeasonRewards?: string[];
13+
UnlockedTwitchRewards?: string[];
14+
UnlockedPlatformRewards?: string[];
15+
};
16+
}
1117
12-
let seasonRewards = $state([]);
13-
let twitchRewards = $state([]);
14-
let platformRewards = $state([]);
18+
interface RewardItem {
19+
name: string;
20+
id: string;
21+
expedition?: number;
22+
drop?: number;
23+
platform?: string;
24+
}
25+
26+
interface TableGroup {
27+
label: string;
28+
items: Array<{
29+
value: string;
30+
values: string[];
31+
}>;
32+
}
33+
34+
let { accountData = $bindable() }: { accountData: AccountData } = $props();
35+
36+
let seasonRewards = $state<string[]>([]);
37+
let twitchRewards = $state<string[]>([]);
38+
let platformRewards = $state<string[]>([]);
39+
let activeTab = $state('season_rewards');
40+
41+
const typedExpeditions = dataExpeditions as Record<string, string>;
42+
const seasonData = dataRewardsSeason as RewardItem[];
43+
const twitchData = dataRewardsTwitch as RewardItem[];
44+
const platformData = dataRewardsPlatform as RewardItem[];
45+
46+
// Computed values for better performance
47+
const seasonGroups = $derived<TableGroup[]>(
48+
Object.keys(typedExpeditions)
49+
.reverse()
50+
.map((expeditionID) => ({
51+
label: `${expeditionID}: ${typedExpeditions[expeditionID]}`,
52+
items: seasonData
53+
.filter((reward) => reward.expedition === parseInt(expeditionID))
54+
.map((reward) => ({
55+
value: reward.id,
56+
values: [reward.name, reward.id, reward.expedition?.toString() || '']
57+
}))
58+
}))
59+
.filter((group) => group.items.length > 0)
60+
);
61+
62+
const twitchGroups = $derived<TableGroup[]>(
63+
Array.from(new Set(twitchData.map((reward) => reward.drop)))
64+
.sort((a, b) => (b || 0) - (a || 0))
65+
.map((drop) => ({
66+
label: `${m.page_account_table_drop()} ${drop}`,
67+
items: twitchData
68+
.filter((reward) => reward.drop === drop)
69+
.map((reward) => ({
70+
value: reward.id,
71+
values: [reward.name, reward.id]
72+
}))
73+
}))
74+
);
75+
76+
const platformGroups = $derived<TableGroup[]>(
77+
Array.from(new Set(platformData.map((reward) => reward.platform)))
78+
.filter(Boolean)
79+
.map((platform) => ({
80+
label: platform!,
81+
items: platformData
82+
.filter((reward) => reward.platform === platform)
83+
.map((reward) => ({
84+
value: reward.id,
85+
values: [reward.name, reward.id, reward.platform || '']
86+
}))
87+
}))
88+
);
1589
1690
// Initialize reward arrays when accountData is loaded
1791
$effect(() => {
18-
if (accountData && accountData.UserSettingsData) {
92+
if (accountData?.UserSettingsData) {
1993
seasonRewards = accountData.UserSettingsData.UnlockedSeasonRewards || [];
2094
twitchRewards = accountData.UserSettingsData.UnlockedTwitchRewards || [];
2195
platformRewards = accountData.UserSettingsData.UnlockedPlatformRewards || [];
@@ -24,18 +98,38 @@
2498
2599
// Update accountData when reward arrays change
26100
$effect(() => {
27-
if (accountData && accountData.UserSettingsData) {
101+
if (accountData?.UserSettingsData) {
28102
accountData.UserSettingsData.UnlockedSeasonRewards = seasonRewards;
29103
accountData.UserSettingsData.UnlockedTwitchRewards = twitchRewards;
30104
accountData.UserSettingsData.UnlockedPlatformRewards = platformRewards;
31105
}
32106
});
33107
</script>
34108

35-
<Tabs.Root value="season_rewards">
109+
{#snippet statusBar(name: string, selected: number, total: number)}
110+
<div class="flex items-center justify-between rounded-lg bg-gray-100 px-4 py-2 dark:bg-gray-800/50">
111+
<div class="flex items-center space-x-4">
112+
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
113+
{name}
114+
</span>
115+
<span class="text-xs text-gray-500 dark:text-gray-400">
116+
{selected} / {total}
117+
</span>
118+
</div>
119+
<div class="flex items-center space-x-2">
120+
<div class="h-2 w-24 rounded-full bg-gray-200 dark:bg-gray-700">
121+
<div class="h-2 rounded-full bg-indigo-600 transition-all duration-300 ease-in-out" style="width: {total > 0 ? (selected / total) * 100 : 0}%" aria-hidden="true"></div>
122+
</div>
123+
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">
124+
{total > 0 ? Math.round((selected / total) * 100) : 0}%
125+
</span>
126+
</div>
127+
</div>
128+
{/snippet}
129+
130+
<Tabs.Root bind:value={activeTab}>
36131
<div class="mt-12 border-b border-white/10">
37-
<Tabs.List aria-label="Tabs" class="-mb-px flex">
38-
<!-- Current: "border-indigo-400 text-indigo-400", Default: "border-transparent text-gray-400 hover:border-white/20 hover:text-gray-300" -->
132+
<Tabs.List class="-mb-px flex">
39133
<Tabs.Trigger
40134
value="season_rewards"
41135
class="w-1/3 border-b-2 border-transparent px-1 py-4 text-center text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700 data-[state=active]:border-indigo-500 data-[state=active]:text-indigo-600 dark:text-gray-400 dark:hover:border-white/20 dark:hover:text-gray-300 dark:data-[state=active]:border-indigo-400 dark:data-[state=active]:text-indigo-400"
@@ -53,42 +147,19 @@
53147
>
54148
</Tabs.List>
55149
</div>
56-
<Tabs.Content value="season_rewards">
57-
<AccountEditorPaneTable
58-
header={[m.page_account_table_name(), m.page_account_table_id(), m.page_account_table_expedition()]}
59-
groups={Object.keys(dataExpeditions)
60-
.reverse()
61-
.map((expeditionID) => ({
62-
label: expeditionID + ': ' + dataExpeditions[expeditionID],
63-
items: dataRewardsSeason.filter((reward) => reward.expedition === parseInt(expeditionID)).map((reward) => ({ value: reward.id, values: [reward.name, reward.id, reward.expedition] }))
64-
}))}
65-
bind:values={seasonRewards}
66-
/>
150+
151+
<Tabs.Content value="season_rewards" class="mt-6 space-y-6">
152+
{@render statusBar(m.page_account_tab_season_rewards().toString(), seasonRewards.length, seasonData.length)}
153+
<AccountEditorPaneTable header={[m.page_account_table_name(), m.page_account_table_id(), m.page_account_table_expedition()]} groups={seasonGroups} bind:values={seasonRewards} />
67154
</Tabs.Content>
68-
<Tabs.Content value="twitch_rewards">
69-
<AccountEditorPaneTable
70-
header={[m.page_account_table_name(), m.page_account_table_id()]}
71-
groups={[
72-
...Array.from(new Set(dataRewardsTwitch.map((reward) => reward.drop)))
73-
.sort((a, b) => b - a)
74-
.map((drop) => ({
75-
label: m.page_account_table_drop() + ` ${drop}`,
76-
items: dataRewardsTwitch.filter((reward) => reward.drop === drop).map((reward) => ({ value: reward.id, values: [reward.name, reward.id] }))
77-
}))
78-
]}
79-
bind:values={twitchRewards}
80-
/>
155+
156+
<Tabs.Content value="twitch_rewards" class="mt-6 space-y-6">
157+
{@render statusBar(m.page_account_tab_twitch_rewards().toString(), twitchRewards.length, twitchData.length)}
158+
<AccountEditorPaneTable header={[m.page_account_table_name(), m.page_account_table_id()]} groups={twitchGroups} bind:values={twitchRewards} />
81159
</Tabs.Content>
82-
<Tabs.Content value="platform_rewards">
83-
<AccountEditorPaneTable
84-
header={[m.page_account_table_name(), m.page_account_table_id(), m.page_account_table_platform()]}
85-
groups={[
86-
...Array.from(new Set(dataRewardsPlatform.map((reward) => reward.platform))).map((platform) => ({
87-
label: platform,
88-
items: dataRewardsPlatform.filter((reward) => reward.platform === platform).map((reward) => ({ value: reward.id, values: [reward.name, reward.id, reward.platform] }))
89-
}))
90-
]}
91-
bind:values={platformRewards}
92-
/>
160+
161+
<Tabs.Content value="platform_rewards" class="mt-6 space-y-6">
162+
{@render statusBar(m.page_account_tab_platform_rewards().toString(), platformRewards.length, platformData.length)}
163+
<AccountEditorPaneTable header={[m.page_account_table_name(), m.page_account_table_id(), m.page_account_table_platform()]} groups={platformGroups} bind:values={platformRewards} />
93164
</Tabs.Content>
94165
</Tabs.Root>

0 commit comments

Comments
 (0)