diff --git a/public/data/Faelyn Eichenhauch.json b/public/data/Faelyn Eichenhauch.json index bbf330e..4baf908 100644 --- a/public/data/Faelyn Eichenhauch.json +++ b/public/data/Faelyn Eichenhauch.json @@ -61,19 +61,23 @@ } ], "attributeAdjustmentSelected": "ATTR_3", - "ae": 0, - "kp": 0, - "lp": 0, + "ae": 15, + "kp": 15, + "lp": 15, + "sp": 2, "permanentAE": { - "lost": 0, + "lost": 3, "redeemed": 0 }, "permanentKP": { - "lost": 0, + "lost": 2, "redeemed": 0 }, "permanentLP": { - "lost": 0 + "lost": 12 + }, + "permanentSP": { + "lost": 1 } }, "activatable": { diff --git a/src/modules/main/modules/sheetHeader/components/SheetHeader.tsx b/src/modules/main/modules/sheetHeader/components/SheetHeader.tsx index b4a3f5c..b4acbcf 100644 --- a/src/modules/main/modules/sheetHeader/components/SheetHeader.tsx +++ b/src/modules/main/modules/sheetHeader/components/SheetHeader.tsx @@ -6,58 +6,90 @@ import { loadRace, loadRaceVariant } from "@/utils/loaders"; -import {useEffect, useState} from 'react'; +import {useCallback, useEffect, useState} from 'react'; export default function SheetHeader({jsonData}: { jsonData: CharacterData }) { const [raceName, setRaceName] = useState(jsonData.r); const [raceVariantName, setRaceVariantName] = useState(jsonData.rv || ''); const [professionName, setProfessionName] = useState(jsonData.p); const [attributes, setAttributes] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const resetToDefaults = useCallback(() => { + setRaceName(jsonData.r); + setRaceVariantName(jsonData.rv || ''); + setProfessionName(jsonData.p); + setAttributes([]); + }, [jsonData.r, jsonData.rv, jsonData.p]); + + const loadData = useCallback(async (signal: AbortSignal) => { + setLoading(true); + setError(null); + + try { + // Load race and profession using the new loader functions + const [race, raceVariant, profession, loadedAttributes] = await Promise.all([ + loadRace(jsonData.r), + loadRaceVariant(jsonData.rv || ''), + loadProfession(jsonData.p), + loadAttributesWithValues(jsonData.attr.values), + ]); + + // Check if component is still mounted and request wasn't cancelled + if (signal.aborted) return; + + // Update state with loaded data + setRaceName(race?.name || jsonData.r); + setRaceVariantName(raceVariant?.name || ''); + + // Handle profession name with gender preference + if (profession?.name) { + setProfessionName(profession.name.m || profession.name.f || jsonData.p); + } else { + setProfessionName(jsonData.p); + } + + setAttributes(loadedAttributes); + } catch (err) { + if (signal.aborted) return; + + const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'; + console.error('Error loading character data:', errorMessage); + setError(errorMessage); + + // Reset to default values on error + resetToDefaults(); + } finally { + if (!signal.aborted) { + setLoading(false); + } + } + }, [jsonData.r, jsonData.rv, jsonData.p, jsonData.attr, resetToDefaults]); useEffect(() => { - let isMounted = true; + const abortController = new AbortController(); - const loadData = async () => { - try { - // Load race and profession using the new loader functions - const [race, raceVariant, profession] = await Promise.all([ - loadRace(jsonData.r), - loadRaceVariant(jsonData.rv || ''), - loadProfession(jsonData.p) - ]); - - // Process attributes using the new loadAttributesWithValues function - const loadedAttributes = await loadAttributesWithValues(jsonData.attr.values); - - if (isMounted) { - setRaceName(race?.name || jsonData.r); - setRaceVariantName(raceVariant?.name || ''); - // For profession, handle gendered name - if (profession?.name) { - setProfessionName(profession.name.m || profession.name.f || jsonData.p); - } else { - setProfessionName(jsonData.p); - } - // Set the attributes with their values, names, and short forms - setAttributes(loadedAttributes); - } - } catch (error) { - console.error('Error loading data:', error); - if (isMounted) { - setRaceName(jsonData.r); - setProfessionName(jsonData.p); - } - } - }; - - loadData(); + loadData(abortController.signal); return () => { - isMounted = false; + abortController.abort(); }; - }, [jsonData.r, jsonData.rv, jsonData.p, jsonData.attr]); + }, [loadData]); - console.log(jsonData) + // Optional: Add loading state handling + if (loading) { + return
Loading character data...
; + } + + // Optional: Add error state handling + if (error) { + return ( +
+ Error loading character data: {error} +
+ ); + } return ( <> @@ -66,19 +98,19 @@ export default function SheetHeader({jsonData}: { jsonData: CharacterData }) {

{jsonData.name}

- Name + Name

{raceName} {raceVariantName ? <>({raceVariantName}) : ''}

- Spezies + Spezies

{professionName}

- Profession + Profession
@@ -103,7 +135,8 @@ export default function SheetHeader({jsonData}: { jsonData: CharacterData }) { ))} -
+ {jsonData.attr.lp && ( +
LP -
- 15 / 15 -
-
+
+
+
+ {jsonData.attr.lp} / {jsonData.attr.lp - jsonData.attr.permanentLP.lost} +
+
+
+ )} -
- - AP - - Astralpunkte - - -
- 16 / 16 -
-
+ {jsonData.attr.ae > 0 && ( +
+ + AP + + Astralpunkte + + +
+
+
+ {jsonData.attr.ae} / {jsonData.attr.ae - jsonData.attr.permanentAE.lost} +
+
+
+ )} -
- - KP - - Karmapunkte - - -
- 16 / 16 -
-
+ {jsonData.attr.kp > 0 && ( +
+ + KP + + Karmapunkte + + +
+
+
+ {jsonData.attr.kp} / {jsonData.attr.kp - jsonData.attr.permanentKP.lost} +
+
+
+ )} -
- - SP - - Schicksalspunkte - - -
-
-
-
-
-
+ {jsonData.attr.sp > 0 && ( +
+ + SP + + Schicksalspunkte + + +
+ {Array.from({length: jsonData.attr.sp - jsonData.attr.permanentSP.lost}, (_, index) => ( +
+ ))} + {Array.from({length: jsonData.attr.permanentSP.lost}, (_, index) => ( +
+ ))} +
+
+ )}
diff --git a/src/types/CharacterJson.ts b/src/types/CharacterJson.ts index 5b97bbf..d349f2d 100644 --- a/src/types/CharacterJson.ts +++ b/src/types/CharacterJson.ts @@ -14,11 +14,11 @@ export interface Attributes { ae: number; kp: number; lp: number; + sp: number; permanentAE: PermanentAttribute; permanentKP: PermanentAttribute; - permanentLP: { - lost: number; - }; + permanentLP: PermanentAttribute; + permanentSP: PermanentAttribute; } export interface PersonalDetails {