Enable character JSON file import and dynamic data loading
This commit is contained in:
1
public/data/Faelyn Eichenhauch.json
Normal file
1
public/data/Faelyn Eichenhauch.json
Normal file
@@ -0,0 +1 @@
|
||||
{"clientVersion":"1.5.2","dateCreated":"2025-06-27T19:06:06.297Z","dateModified":"2025-06-27T21:24:26.013Z","id":"H_1751051166297","phase":3,"locale":"de-DE","name":"Faelyn Eichenhauch","ap":{"total":1400},"el":"EL_5","r":"R_3","c":"C_5","p":"P_24","sex":"m","pers":{"family":"Eichenhauch","placeofbirth":"Fasar","age":"57","haircolor":8,"eyecolor":11,"size":"188","weight":"82","socialstatus":2,"cultureAreaKnowledge":"Fasar / Punin"},"attr":{"values":[{"id":"ATTR_1","value":12},{"id":"ATTR_2","value":14},{"id":"ATTR_3","value":14},{"id":"ATTR_4","value":12},{"id":"ATTR_5","value":10},{"id":"ATTR_6","value":13},{"id":"ATTR_7","value":14},{"id":"ATTR_8","value":11}],"attributeAdjustmentSelected":"ATTR_3","ae":0,"kp":0,"lp":0,"permanentAE":{"lost":0,"redeemed":0},"permanentKP":{"lost":0,"redeemed":0},"permanentLP":{"lost":0}},"activatable":{"ADV_50":[{}],"ADV_48":[],"ADV_11":[],"ADV_70":[{}],"ADV_6":[{"tier":1}],"ADV_13":[{"tier":1}],"ADV_18":[{"sid":4},{"sid":3}],"ADV_37":[{}],"ADV_23":[{"tier":1}],"DISADV_5":[],"DISADV_37":[{"sid":8}],"DISADV_46":[{}],"DISADV_44":[],"DISADV_36":[{"sid":1}],"DISADV_25":[],"DISADV_7":[],"DISADV_48":[],"DISADV_26":[],"SA_27":[{"sid":9},{"sid":14},{"sid":7}],"SA_29":[{"sid":8,"tier":4},{"sid":23,"tier":2},{"sid":1,"tier":2}],"SA_1":[{}],"SA_40":[{}],"SA_9":[{"sid2":2,"sid":"TAL_28"}],"SA_12":[{"sid":9}],"SA_1011":[{}],"SA_22":[{"sid":"Festum"},{"sid":"Waldgebiet westlich von Punin"},{"sid":"Punin"}],"SA_1035":[{}],"SA_70":[{"sid":"SPELL_30"}],"SA_76":[{}],"SA_681":[],"SA_83":[{}]},"talents":{"TAL_4":7,"TAL_8":5,"TAL_10":6,"TAL_20":5,"TAL_23":7,"TAL_27":5,"TAL_34":5,"TAL_36":10,"TAL_40":4,"TAL_41":5,"TAL_42":4,"TAL_47":3,"TAL_54":4,"TAL_16":4,"TAL_44":1,"TAL_18":4,"TAL_19":4,"TAL_32":3,"TAL_33":6,"TAL_46":1,"TAL_37":1,"TAL_38":6,"TAL_39":2,"TAL_11":1,"TAL_28":12,"TAL_3":5,"TAL_24":4,"TAL_30":5,"TAL_29":6,"TAL_50":5,"TAL_51":8,"TAL_48":2},"ct":{"CT_3":11,"CT_13":13},"spells":{"SPELL_6":7,"SPELL_7":5,"SPELL_9":5,"SPELL_20":6,"SPELL_30":5,"SPELL_35":6,"SPELL_40":6,"SPELL_113":6,"SPELL_81":5,"SPELL_43":6},"cantrips":["CANTRIP_1","CANTRIP_46","CANTRIP_4","CANTRIP_17","CANTRIP_34","CANTRIP_35","CANTRIP_38"],"liturgies":{},"blessings":[],"belongings":{"items":{},"armorZones":{},"purse":{"d":"","s":"","h":"","k":""}},"rules":{"higherParadeValues":0,"attributeValueLimit":false,"enableAllRuleBooks":true,"enabledRuleBooks":[],"enableLanguageSpecializations":false},"pets":{}}
|
24
src/App.tsx
24
src/App.tsx
@@ -3,6 +3,7 @@ import Main from "./modules/main/components/Main.tsx";
|
||||
import Footer from "./modules/footer/components/Footer.tsx";
|
||||
import {useEffect, useState} from "react";
|
||||
import type {CharacterData} from "./types/CharacterJson.ts";
|
||||
import Header from "./modules/header/components/Header.tsx";
|
||||
|
||||
function App() {
|
||||
const [jsonData, setJsonData] = useState<CharacterData | null>(null);
|
||||
@@ -11,8 +12,12 @@ function App() {
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const data = await import('./assets/data/Faelyn Eichenhauch.json');
|
||||
setJsonData(data.default);
|
||||
const response = await fetch('/data/Faelyn Eichenhauch.json');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
setJsonData(data);
|
||||
} catch (error) {
|
||||
console.error('Error loading JSON file:', error);
|
||||
} finally {
|
||||
@@ -23,15 +28,24 @@ function App() {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const handleFileLoad = (data: CharacterData) => {
|
||||
setJsonData(data);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
if (loading) return <div>Loading...</div>;
|
||||
|
||||
if (!jsonData) return <div>Error loading JSON file.</div>;
|
||||
if (!jsonData) return (
|
||||
<>
|
||||
<Header onFileLoad={handleFileLoad}/>
|
||||
|
||||
<Footer/>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<Header/>*/}
|
||||
|
||||
<Main jsonData={jsonData}/>
|
||||
|
||||
<Footer/>
|
||||
|
@@ -1,284 +0,0 @@
|
||||
{
|
||||
"clientVersion": "1.5.2",
|
||||
"dateCreated": "2025-06-27T19:06:06.297Z",
|
||||
"dateModified": "2025-06-27T21:24:26.013Z",
|
||||
"id": "H_1751051166297",
|
||||
"phase": 3,
|
||||
"locale": "de-DE",
|
||||
"name": "Faelyn Eichenhauch",
|
||||
"ap": {
|
||||
"total": 1400
|
||||
},
|
||||
"el": "EL_5",
|
||||
"r": "R_3",
|
||||
"c": "C_5",
|
||||
"p": "P_24",
|
||||
"sex": "m",
|
||||
"pers": {
|
||||
"family": "Eichenhauch",
|
||||
"placeofbirth": "Fasar",
|
||||
"age": "57",
|
||||
"haircolor": 8,
|
||||
"eyecolor": 11,
|
||||
"size": "188",
|
||||
"weight": "82",
|
||||
"socialstatus": 2,
|
||||
"cultureAreaKnowledge": "Fasar / Punin"
|
||||
},
|
||||
"attr": {
|
||||
"values": [
|
||||
{
|
||||
"id": "ATTR_1",
|
||||
"value": 12
|
||||
},
|
||||
{
|
||||
"id": "ATTR_2",
|
||||
"value": 14
|
||||
},
|
||||
{
|
||||
"id": "ATTR_3",
|
||||
"value": 14
|
||||
},
|
||||
{
|
||||
"id": "ATTR_4",
|
||||
"value": 12
|
||||
},
|
||||
{
|
||||
"id": "ATTR_5",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"id": "ATTR_6",
|
||||
"value": 13
|
||||
},
|
||||
{
|
||||
"id": "ATTR_7",
|
||||
"value": 14
|
||||
},
|
||||
{
|
||||
"id": "ATTR_8",
|
||||
"value": 11
|
||||
}
|
||||
],
|
||||
"attributeAdjustmentSelected": "ATTR_3",
|
||||
"ae": 0,
|
||||
"kp": 0,
|
||||
"lp": 0,
|
||||
"permanentAE": {
|
||||
"lost": 0,
|
||||
"redeemed": 0
|
||||
},
|
||||
"permanentKP": {
|
||||
"lost": 0,
|
||||
"redeemed": 0
|
||||
},
|
||||
"permanentLP": {
|
||||
"lost": 0
|
||||
}
|
||||
},
|
||||
"activatable": {
|
||||
"ADV_50": [
|
||||
{}
|
||||
],
|
||||
"ADV_48": [],
|
||||
"ADV_11": [],
|
||||
"ADV_70": [
|
||||
{}
|
||||
],
|
||||
"ADV_6": [
|
||||
{
|
||||
"tier": 1
|
||||
}
|
||||
],
|
||||
"ADV_13": [
|
||||
{
|
||||
"tier": 1
|
||||
}
|
||||
],
|
||||
"ADV_18": [
|
||||
{
|
||||
"sid": 4
|
||||
},
|
||||
{
|
||||
"sid": 3
|
||||
}
|
||||
],
|
||||
"ADV_37": [
|
||||
{}
|
||||
],
|
||||
"ADV_23": [
|
||||
{
|
||||
"tier": 1
|
||||
}
|
||||
],
|
||||
"DISADV_5": [],
|
||||
"DISADV_37": [
|
||||
{
|
||||
"sid": 8
|
||||
}
|
||||
],
|
||||
"DISADV_46": [
|
||||
{}
|
||||
],
|
||||
"DISADV_44": [],
|
||||
"DISADV_36": [
|
||||
{
|
||||
"sid": 1
|
||||
}
|
||||
],
|
||||
"DISADV_25": [],
|
||||
"DISADV_7": [],
|
||||
"DISADV_48": [],
|
||||
"DISADV_26": [],
|
||||
"SA_27": [
|
||||
{
|
||||
"sid": 9
|
||||
},
|
||||
{
|
||||
"sid": 14
|
||||
},
|
||||
{
|
||||
"sid": 7
|
||||
}
|
||||
],
|
||||
"SA_29": [
|
||||
{
|
||||
"sid": 8,
|
||||
"tier": 4
|
||||
},
|
||||
{
|
||||
"sid": 23,
|
||||
"tier": 2
|
||||
},
|
||||
{
|
||||
"sid": 1,
|
||||
"tier": 2
|
||||
}
|
||||
],
|
||||
"SA_1": [
|
||||
{}
|
||||
],
|
||||
"SA_40": [
|
||||
{}
|
||||
],
|
||||
"SA_9": [
|
||||
{
|
||||
"sid2": 2,
|
||||
"sid": "TAL_28"
|
||||
}
|
||||
],
|
||||
"SA_12": [
|
||||
{
|
||||
"sid": 9
|
||||
}
|
||||
],
|
||||
"SA_1011": [
|
||||
{}
|
||||
],
|
||||
"SA_22": [
|
||||
{
|
||||
"sid": "Festum"
|
||||
},
|
||||
{
|
||||
"sid": "Waldgebiet westlich von Punin"
|
||||
},
|
||||
{
|
||||
"sid": "Punin"
|
||||
}
|
||||
],
|
||||
"SA_1035": [
|
||||
{}
|
||||
],
|
||||
"SA_70": [
|
||||
{
|
||||
"sid": "SPELL_30"
|
||||
}
|
||||
],
|
||||
"SA_76": [
|
||||
{}
|
||||
],
|
||||
"SA_681": [],
|
||||
"SA_83": [
|
||||
{}
|
||||
]
|
||||
},
|
||||
"talents": {
|
||||
"TAL_4": 7,
|
||||
"TAL_8": 5,
|
||||
"TAL_10": 6,
|
||||
"TAL_20": 5,
|
||||
"TAL_23": 7,
|
||||
"TAL_27": 5,
|
||||
"TAL_34": 5,
|
||||
"TAL_36": 10,
|
||||
"TAL_40": 4,
|
||||
"TAL_41": 5,
|
||||
"TAL_42": 4,
|
||||
"TAL_47": 3,
|
||||
"TAL_54": 4,
|
||||
"TAL_16": 4,
|
||||
"TAL_44": 1,
|
||||
"TAL_18": 4,
|
||||
"TAL_19": 4,
|
||||
"TAL_32": 3,
|
||||
"TAL_33": 6,
|
||||
"TAL_46": 1,
|
||||
"TAL_37": 1,
|
||||
"TAL_38": 6,
|
||||
"TAL_39": 2,
|
||||
"TAL_11": 1,
|
||||
"TAL_28": 12,
|
||||
"TAL_3": 5,
|
||||
"TAL_24": 4,
|
||||
"TAL_30": 5,
|
||||
"TAL_29": 6,
|
||||
"TAL_50": 5,
|
||||
"TAL_51": 8,
|
||||
"TAL_48": 2
|
||||
},
|
||||
"ct": {
|
||||
"CT_3": 11,
|
||||
"CT_13": 13
|
||||
},
|
||||
"spells": {
|
||||
"SPELL_6": 7,
|
||||
"SPELL_7": 5,
|
||||
"SPELL_9": 5,
|
||||
"SPELL_20": 6,
|
||||
"SPELL_30": 5,
|
||||
"SPELL_35": 6,
|
||||
"SPELL_40": 6,
|
||||
"SPELL_113": 6,
|
||||
"SPELL_81": 5,
|
||||
"SPELL_43": 6
|
||||
},
|
||||
"cantrips": [
|
||||
"CANTRIP_1",
|
||||
"CANTRIP_46",
|
||||
"CANTRIP_4",
|
||||
"CANTRIP_17",
|
||||
"CANTRIP_34",
|
||||
"CANTRIP_35",
|
||||
"CANTRIP_38"
|
||||
],
|
||||
"liturgies": {},
|
||||
"blessings": [],
|
||||
"belongings": {
|
||||
"items": {},
|
||||
"armorZones": {},
|
||||
"purse": {
|
||||
"d": "",
|
||||
"s": "",
|
||||
"h": "",
|
||||
"k": ""
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"higherParadeValues": 0,
|
||||
"attributeValueLimit": false,
|
||||
"enableAllRuleBooks": true,
|
||||
"enabledRuleBooks": [],
|
||||
"enableLanguageSpecializations": false
|
||||
},
|
||||
"pets": {}
|
||||
}
|
@@ -1,10 +1,15 @@
|
||||
import ImportButton from "./ImportButton";
|
||||
import type { CharacterData } from '../../../types/CharacterJson.ts';
|
||||
|
||||
export default function Header() {
|
||||
interface HeaderProps {
|
||||
onFileLoad: (data: CharacterData) => void;
|
||||
}
|
||||
|
||||
export default function Header({ onFileLoad }: HeaderProps) {
|
||||
return (
|
||||
<header className="bg-white dark:bg-gray-800">
|
||||
<nav className="container mx-auto flex justify-between items-center py-6 px-6">
|
||||
<ImportButton/>
|
||||
<ImportButton onFileLoad={onFileLoad}/>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
|
@@ -1,6 +1,60 @@
|
||||
export default function ImportButton() {
|
||||
return (
|
||||
<input type="file"
|
||||
className="file:cursor-pointer file:mr-4 file:rounded-full file:border-0 file:bg-violet-50 file:px-4 file:py-2 file:text-sm file:font-semibold file:text-violet-700 hover:file:bg-violet-100 dark:file:bg-violet-600 dark:file:text-violet-100 dark:hover:file:bg-violet-500"/>
|
||||
)
|
||||
import { useRef } from 'react';
|
||||
import type { CharacterData } from '../../../../types/CharacterJson.ts';
|
||||
|
||||
interface ImportButtonProps {
|
||||
onFileLoad: (data: CharacterData) => void;
|
||||
}
|
||||
|
||||
export default function ImportButton({ onFileLoad }: ImportButtonProps) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
if (!file) return;
|
||||
|
||||
// Check if the file is a JSON file
|
||||
if (file.type !== 'application/json') {
|
||||
alert('Please select a JSON file');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read the file content
|
||||
const fileContent = await file.text();
|
||||
|
||||
// Parse and validate the JSON
|
||||
const parsedData = JSON.parse(fileContent) as CharacterData;
|
||||
|
||||
// Call the callback to update the parent component's state
|
||||
onFileLoad(parsedData);
|
||||
} catch (error) {
|
||||
alert('Invalid JSON file. Please select a valid JSON file.');
|
||||
console.error('Error processing file:', error);
|
||||
}
|
||||
|
||||
// Reset the input
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".json"
|
||||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
id="character-import"
|
||||
/>
|
||||
<label
|
||||
htmlFor="character-import"
|
||||
className="cursor-pointer bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Charakter laden
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user