|
8 | 8 | let goalId = $derived($page.params.goalId); |
9 | 9 | let goal = $state(null); |
10 | 10 | let matchData = $state([]); |
| 11 | + let items = $state([]); |
11 | 12 | let isLoading = $state(true); |
12 | 13 | let error = $state(""); |
13 | 14 |
|
|
115 | 116 | isLoading = true; |
116 | 117 | error = ""; |
117 | 118 | try { |
118 | | - goal = await invoke("get_goal", { goalId: parseInt(goalId) }); |
119 | | - matchData = await invoke("get_goal_histogram_data", { goalId: parseInt(goalId) }); |
| 119 | + [goal, matchData, items] = await Promise.all([ |
| 120 | + invoke("get_goal", { goalId: parseInt(goalId) }), |
| 121 | + invoke("get_goal_histogram_data", { goalId: parseInt(goalId) }), |
| 122 | + invoke("get_all_items"), |
| 123 | + ]); |
120 | 124 | } catch (e) { |
121 | 125 | error = `Failed to load goal data: ${e}`; |
122 | 126 | } finally { |
123 | 127 | isLoading = false; |
124 | 128 | } |
125 | 129 | } |
126 | 130 |
|
| 131 | + function getItemName(itemId) { |
| 132 | + const item = items.find(i => i.id === itemId); |
| 133 | + return item ? item.display_name : `Item ${itemId}`; |
| 134 | + } |
| 135 | +
|
| 136 | + function formatSeconds(totalSeconds) { |
| 137 | + const minutes = Math.floor(totalSeconds / 60); |
| 138 | + const seconds = totalSeconds % 60; |
| 139 | + return `${minutes}:${seconds.toString().padStart(2, "0")}`; |
| 140 | + } |
| 141 | +
|
127 | 142 | function resetFilters() { |
128 | 143 | selectedHeroId = ""; |
129 | 144 | startDate = ""; |
|
140 | 155 | return "Last Hits"; |
141 | 156 | case "Level": |
142 | 157 | return "Level"; |
| 158 | + case "ItemTiming": |
| 159 | + return "Item Timing"; |
143 | 160 | default: |
144 | 161 | return metric; |
145 | 162 | } |
|
155 | 172 | return "CS"; |
156 | 173 | case "Level": |
157 | 174 | return ""; |
| 175 | + case "ItemTiming": |
| 176 | + return "M:SS"; |
158 | 177 | default: |
159 | 178 | return ""; |
160 | 179 | } |
|
163 | 182 | function formatGoalDescription(g) { |
164 | 183 | if (!g) return ""; |
165 | 184 | const heroName = g.hero_id !== null ? getHeroName(g.hero_id) : "Any Hero"; |
166 | | - const metricLabel = getMetricLabel(g.metric); |
| 185 | + if (g.metric === "ItemTiming") { |
| 186 | + const itemName = g.item_id !== null ? getItemName(g.item_id) : "Unknown Item"; |
| 187 | + const timeStr = formatSeconds(g.target_value); |
| 188 | + return `${heroName}: ${itemName} by ${timeStr}`; |
| 189 | + } |
167 | 190 | const unit = getMetricUnit(g.metric); |
168 | 191 | const valueStr = unit ? `${g.target_value} ${unit}` : `Level ${g.target_value}`; |
169 | 192 | return `${heroName}: ${valueStr} by ${g.target_time_minutes} min`; |
170 | 193 | } |
| 194 | +
|
| 195 | + function formatStatValue(value, metric) { |
| 196 | + if (metric === "ItemTiming") return formatSeconds(value); |
| 197 | + return value; |
| 198 | + } |
171 | 199 | </script> |
172 | 200 |
|
173 | 201 | {#if isLoading} |
|
245 | 273 | </div> |
246 | 274 | <div class="stat-card"> |
247 | 275 | <div class="stat-label">Average</div> |
248 | | - <div class="stat-value">{stats().avgValue} {getMetricUnit(goal.metric)}</div> |
| 276 | + <div class="stat-value">{formatStatValue(stats().avgValue, goal.metric)} {goal.metric !== "ItemTiming" ? getMetricUnit(goal.metric) : ""}</div> |
249 | 277 | </div> |
250 | 278 | <div class="stat-card"> |
251 | 279 | <div class="stat-label">Range</div> |
252 | 280 | <div class="stat-value"> |
253 | | - {stats().minValue} - {stats().maxValue} |
| 281 | + {formatStatValue(stats().minValue, goal.metric)} – {formatStatValue(stats().maxValue, goal.metric)} |
254 | 282 | </div> |
255 | 283 | </div> |
256 | 284 | </div> |
|
311 | 339 | font-size="14" |
312 | 340 | font-weight="bold" |
313 | 341 | > |
314 | | - Target: {goal.target_value} |
| 342 | + Target: {goal.metric === "ItemTiming" ? formatSeconds(goal.target_value) : goal.target_value} |
315 | 343 | </text> |
316 | 344 | {/if} |
317 | 345 |
|
|
353 | 381 | fill="#a0a0a0" |
354 | 382 | font-size="10" |
355 | 383 | > |
356 | | - {bin.start} |
| 384 | + {goal.metric === "ItemTiming" ? formatSeconds(bin.start) : bin.start} |
357 | 385 | </text> |
358 | 386 | {/each} |
359 | 387 |
|
|
366 | 394 | font-size="14" |
367 | 395 | font-weight="bold" |
368 | 396 | > |
369 | | - {getMetricLabel(goal.metric)} ({getMetricUnit(goal.metric) || "value"}) |
| 397 | + {goal.metric === "ItemTiming" |
| 398 | + ? `${goal.item_id !== null ? getItemName(goal.item_id) : "Item"} Timing (M:SS)` |
| 399 | + : `${getMetricLabel(goal.metric)} (${getMetricUnit(goal.metric) || "value"})`} |
370 | 400 | </text> |
371 | 401 | <text |
372 | 402 | x="30" |
|
0 commit comments