Compare commits

...

8 Commits

Author SHA1 Message Date
Krosmez
975b88fc6e Merge branch 'dev' into feature/card 2024-04-09 11:36:20 -04:00
Martin Gasque
307b55dbe4 Character sheet improvements 2024-04-09 16:15:22 +02:00
Martin Gasque
9b363d9d93 Add Character page 2024-04-09 12:51:42 +02:00
Martin Gasque
0e0c1c5eac add secret.tech.data file
got to think about logic now
2024-04-08 16:01:13 +02:00
Martin Gasque
db690fbc0e Update .gitignore 2024-04-08 12:30:58 +02:00
Martin Gasque
9b89d7d7b0 cleaner data sets 2024-04-08 12:30:31 +02:00
Martin Gasque
8241967ab6 Add jutsu data, level datas & clean Character page
Add html-react-parser to the project, should npm install at boot
2024-04-08 12:30:23 +02:00
Martin Gasque
7acab90068 add admin page 2024-04-08 12:29:02 +02:00
13 changed files with 44326 additions and 7 deletions

28
scripts/concatFiles.py Normal file
View File

@ -0,0 +1,28 @@
import os
import json
# Directory path
directory = './'
# Output file path
output_file = './concat.js'
# List to store JSON data
json_data = []
# Iterate over files in the directory
for filename in os.listdir(directory):
if filename.endswith('.json'):
file_path = os.path.join(directory, filename)
try:
with open(file_path, 'r') as file:
data = json.load(file)
if data.get('type') == 'spell':
json_data.append(data)
except Exception as e:
print(f"Error reading file {file_path}: {str(e)}")
# Write the concatenated JSON data to the output file
with open(output_file, 'w') as file:
json.dump(json_data, file)

View File

@ -21,6 +21,11 @@ const Navbar = ({ className, onClick, handleClose }) => {
Hanzo Character Maker Hanzo Character Maker
</Link> </Link>
</li> </li>
<li className="navbarList__item">
<Link className="navbar__link" to="admin">
Admin
</Link>
</li>
</ul> </ul>
</nav> </nav>
); );

16124
src/data/dataFiltered.js Normal file

File diff suppressed because one or more lines are too long

17892
src/data/dataSorted.js Normal file

File diff suppressed because one or more lines are too long

2033
src/data/jutsuData.js Normal file

File diff suppressed because it is too large Load Diff

138
src/data/levels.js Normal file
View File

@ -0,0 +1,138 @@
import { type } from "@testing-library/user-event/dist/type";
const levels = [
{
_id: 0,
name: "0",
unlocks: [],
},
{
_id: 1,
name: "1",
unlocks: [
{
name: "Archetype",
type: "choice",
_id: 105,
options: [
{
folder_id: "xdVnM5YTVFEewTje",
name: "Maitre des lames",
description: "Assassin redoutable",
},
{
folder_id: "A0uWlUA0R29NFyzt", //Mauvaise organisation des dossiers => liste des Taïjutsu et non liste des tech. de MDT
name: "Maitre du Taîjutsu",
description: "Combattant au corps à corps",
},
{
folder_id: "iu3QsWHoalitMfaF",
name: "Maitre du chakra",
description: "Puissant manipulateurs des éléments",
},
{
folder_id: "6ugsJPZf1DtuJKcF",
name: "Maitre des 1000 objets",
description: "Bricoleurs de génie",
},
],
},
{
name: "Jutsus",
type: "mandatory",
abilityType: "action",
options: [
{
folder_id: "kQxG5RFU69FLBPr1",
name: "Feu (Katon)",
},
{
folder_id: "YIUe7c9tlVNf0QOl",
name: "Eau (Suiton)",
},
{
folder_id: "SYYVLYXoq7K9DW6r",
name: "Vent (Fuuton)",
},
{
folder_id: "Dw3zZbKivXfgPtZv",
name: "Terre (Doton)",
},
],
},
{
name: "Techniques secrètes",
type: "choice",
folder_id: "Vpz6mBEc10DHh6lT",
abilityType: "choiceDependent",
},
{
name: "Attaque à deux armes",
type: "mandatory",
item_id: "h3zlLt2yebORkfwU",
abilityType: "passive",
},
],
},
{
_id: 2,
name: "2",
unlocks: [
{
name: "Ruse",
type: "mandatory",
item_id: "hLeouAsaMvRQTbUQ",
abilityType: "bAction",
},
{
name: "Style de combat",
item_id: "6HB0Shhw6cUZNlX7",
type: "choice",
folder: "k6WTAkLIwEsTtyM6",
abilityType: "passive",
},
],
},
{
_id: 3,
name: "3",
unlocks: [],
},
{
_id: 4,
name: "4",
unlocks: [],
},
{
_id: 5,
name: "5",
unlocks: [],
},
{
_id: 6,
name: "6",
unlocks: [],
},
{
_id: 7,
name: "7",
unlocks: [],
},
{
_id: 8,
name: "8",
unlocks: [],
},
{
_id: 9,
name: "9",
unlocks: [],
},
{
_id: 10,
name: "10",
unlocks: [],
},
];
export default levels;

7760
src/data/tsData.js Normal file

File diff suppressed because one or more lines are too long

62
src/layouts/Body/index.js Normal file
View File

@ -0,0 +1,62 @@
import "./styles.css";
import { useState } from "react";
import allData from "../../data/dataFiltered.js";
import levels from "../../data/levels.js";
const Body = (props) => {
const [level, setLevel] = useState();
const [archetype, setArchetype] = useState();
// setArchetype("");
let unlocks = [];
if (props.level) {
if (levels[props.level].unlocks) {
unlocks.push(levels[props.level].unlocks);
}
}
const changeArchetype = (e) => {
setArchetype(e.target.value);
};
return (
<div>
<h1>Body</h1>
{unlocks.map((unlock) => {
return unlock.map((item) => {
if (item._id === 105) {
return (
<div key={item._id}>
<label htmlFor="archetypes">Level {props.level}</label>
<select name="archetypes" onChange={changeArchetype}>
{item.options.map((option) => {
return (
<option key={option.name} value={option.folder_id}>
{option.name} - {option.folder_id}
</option>
);
})}
</select>
<h2>Compétences d'archétype</h2>
{allData
.filter((comp) => comp.folder === archetype)
.map((comp) => {
if (comp._key.includes("!folders")) {
//Ici recursivement récupérer les compétences d'archetype car certaines sont rangés dans des dossiers, sous-dossiers
}
return (
<div key={comp._id}>
<h3>{comp.name}</h3>
<p>{comp.description}</p>
</div>
);
})}
</div>
);
}
});
})}
</div>
);
};
export default Body;

View File

@ -0,0 +1 @@
.footer {}

231
src/pages/Admin/index.js Normal file
View File

@ -0,0 +1,231 @@
import "./styles.css";
import parse from "html-react-parser";
import { render } from "react-dom";
import { useEffect, useState } from "react";
import Container from "../../layouts/Container/index.js";
import allData from "../../data/dataFiltered.js";
const Admin = () => {
const [data, setData] = useState();
useEffect(() => {
const filterDataByFolder = (folderId) => {
const filteredData = allData.filter((item) => item.folder === folderId);
return filteredData.map((item) => {
const subItems = filterDataByFolder(item._id);
return {
...item,
subItems,
};
});
};
const mainFolders = allData.filter((item) => item.folder === null);
const filteredData = mainFolders.map((item) => {
const subItems = filterDataByFolder(item._id);
return {
...item,
subItems,
};
});
setData(filteredData);
}, [setData]);
let trash = [];
const addToTrash = (itemId) => (e) => {
trash.push(itemId);
e.target.parentNode.style.backgroundColor = "green";
};
const recoverFromTrash = (itemId) => (e) => {
trash = trash.filter((item) => item._id !== itemId);
e.target.parentNode.style.backgroundColor = "white";
};
const createNewJSON = () => {
const newJSON = allData.filter((item) => !trash.includes(item._id));
console.log(newJSON);
};
const showTrash = () => {
console.log(trash);
};
const changeObjectName = (itemId) => (e) => {
console.log(itemId);
console.log(e.target.parentNode.querySelector("input").value);
let newName = e.target.parentNode.querySelector("input").value;
allData.map((item) => {
if (item._id === itemId) {
item.name = newName;
}
});
console.log(allData.filter((item) => item._id === itemId)); // check if the name has been changed
};
const changeObjectDescription = (itemId) => (e) => {
console.log(itemId);
console.log(e.target.parentNode.querySelector("textarea").value);
let newDesc = e.target.parentNode.querySelector("textarea").value;
allData.map((item) => {
if (item._id === itemId) {
item.system.description.value = newDesc;
}
});
console.log(allData.filter((item) => item._id === itemId)); // check if the name has been changed
};
// const getItem = (itemId) => {
// console.log(allData.filter((item) => item._id === itemId));
// };
// const getFolder = (folderId) => {
// console.log(allData.filter((item) => item.folder === folderId));
// return allData.filter((item) => item.folder === folderId);
// };
// const getItemsInFolder = (folderId) => {
// let d = [];
// let folder = getFolder(folderId);
// for (let i = 0; i < folder.length; i++) {
// if (getFolder(folder[i]._id).length !== 0)
// getItemsInFolder(folder[i]._id);
// else d.push(getItem(folder[i]._id));
// }
// console.log(d);
// };
const renderSubItems = (subItems) => {
if (subItems?.length === 0) {
return subItems?.length !== 0 ? (
<div>
<img src={subItems.img} alt="noIMG" />
<div>
<h4>
{subItems?._id} - {subItems?.name}
</h4>
<h6>Change name</h6>
<input type="text" />
<button onClick={changeObjectName(subItems?._id)}>Valider</button>
</div>
<button onClick={recoverFromTrash(subItems?._id)}>Recover</button>
<button onClick={addToTrash(subItems?._id)}>Delete</button>
</div>
) : (
<></>
);
} else {
return subItems?.map((subItem, subId) => {
return (
<div key={`subItem-${subId}`}>
{subItems?.length !== 0 ? (
<div
style={{
color: "green",
display: "flex",
justifyContent: "space-between",
}}
>
<img src={subItem.img} alt="noIMG" />
<div style={{ maxWidth: "20%" }}>
<h5
className={
subItem?._key === `!folders!${subItem?._id}`
? "selected"
: ""
}
>
<i>{subItem?._id}</i> - {subItem?.name}
</h5>
<h6>Change name</h6>
<input type="text" />
<button onClick={changeObjectName(subItem?._id)}>
Valider
</button>
</div>
<div style={{ maxWidth: "20%" }}>
{subItem.system && parse(subItem.system.description.value)}
<h6>Change description</h6>
<textarea id={subItem._id} name="desc" rows="5" cols="33" />
<button onClick={changeObjectDescription(subItem?._id)}>
Valider
</button>
</div>
<button onClick={recoverFromTrash(subItem?._id)}>
Recover
</button>
<button onClick={addToTrash(subItem?._id)}>Delete</button>
</div>
) : (
<></>
)}
{renderSubItems(subItem?.subItems)}
</div>
);
});
}
};
return (
<div className="admin">
<Container>
{data?.map((item, id) => (
<div key={`top-${id}`}>
<div>
<img src={item.img} alt="noIMG" />
<h3>
{item?._id} - {item?.name}
</h3>
<button onClick={recoverFromTrash(item._id)}>Recover</button>
<button onClick={addToTrash(item._id)}>Delete</button>
</div>
{renderSubItems(item?.subItems)}
</div>
))}
<button onClick={createNewJSON}>Valider</button>
<button onClick={showTrash}>Afficher corbeille</button>
</Container>
</div>
);
};
// Example object for seeing the data structure and what to set changeable
// {
// _id: "kLHR7GDjdqHOA4bl",
// name: "Billes explosives",
// type: "consumable",
// img: "/icons/magic/fire/explosion-fireball-small-orange.webp",
// folder: "e2iHmimKcOR3YCha",
// description: "<p><span style="font-size:13px;font-family:Arial;text-decoration-skip-ink:none">Le shinobi peut créer un sac de billes explosives. Au début, elle ne contient qu'une seule bille, mais si ce gadget est séléctionné a plus haut niveau, il multiple les dégats finaux.</span></p>\n<p><span style="font-size:13px;font-family:Arial;text-decoration-skip-ink:none">Le shinobi défait un sac de bille sur son adversaire qui prends des explosions de plein fouet.</span></p>\n<p>Chaque bille inflige 1d8+INT dégat de feu.</p>",
// source: "",
// quantity: 1,
// weight: 0,
// price: 0,
// rarity: "uncommon",
// activation: { type: "action", cost: 1, condition: "" },
// duration: { value: null, units: "" },
// range: { value: null, long: null, units: "" },
// uses: {
// value: 1,
// max: "1",
// per: "charges",
// recovery: "",
// autoDestroy: false,
// },
// consume: { type: "", target: "", amount: null },
// ability: "",
// actionType: "msak",
// attackBonus: "0",
// critical: { threshold: null},
// damage: { parts: [["1d8+@abilites.int.mod", "fire"]], versatile: "" },
// formula: "",
// save: { ability: "", dc: null, scaling: "spell" },
// },
export default Admin;

View File

@ -0,0 +1,10 @@
.character {}
.selected {
color: blue;
}
img {
width: 100px;
height: 100px;
}

View File

@ -2,12 +2,14 @@ import "./styles.css";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Body from "../../../src/layouts/Body/index.js";
import Container from "../../layouts/Container/index.js"; import Container from "../../layouts/Container/index.js";
import allData from "../../data/data.js"; import allData from "../../data/dataFiltered.js";
import levels from "../../data/levels.js";
const Character = () => { const Character = () => {
const [data, setData] = useState(); const [data, setData] = useState();
const [selectedLevel, setSelectedLevel] = useState();
useEffect(() => { useEffect(() => {
const filterDataByFolder = (folderId) => { const filterDataByFolder = (folderId) => {
const filteredData = allData.filter((item) => item.folder === folderId); const filteredData = allData.filter((item) => item.folder === folderId);
@ -31,8 +33,22 @@ const Character = () => {
}); });
setData(filteredData); setData(filteredData);
setSelectedLevel(0);
}, [setData]); }, [setData]);
let levelDom = document.getElementsByClassName("level");
for (let i = 0; i < levelDom.length; i++) {
// eslint-disable-next-line no-loop-func
levelDom[i].addEventListener("click", () => {
for (let j = 0; j < levelDom.length; j++) {
levelDom[j].classList.remove("selected");
}
levelDom[i].classList.add("selected");
setSelectedLevel(i + 1);
});
}
const renderSubItems = (subItems) => { const renderSubItems = (subItems) => {
if (subItems?.length === 0) { if (subItems?.length === 0) {
return subItems?.length !== 0 ? ( return subItems?.length !== 0 ? (
@ -69,14 +85,31 @@ const Character = () => {
return ( return (
<div className="character"> <div className="character">
<Container> <Container>
{data?.map((item, id) => ( <div
style={{
display: "flex",
justifyContent: "space-around",
flexDirection: "row",
}}
>
{levels.map((item, id) => (
<div className={`level level-${id}`} key={`level-${id}`}>
<h2>{item?.name}</h2>
</div>
))}
</div>
{/* {data?.map((item, id) => (
<div key={`top-${id}`}> <div key={`top-${id}`}>
<div>
<h3> <h3>
{item?._id} - {item?.name} {item?._id} - {item?.name}
</h3> </h3>
</div>
{renderSubItems(item?.subItems)} {renderSubItems(item?.subItems)}
</div> </div>
))} ))} */}
<Body level={selectedLevel} />
</Container> </Container>
</div> </div>
); );

View File

@ -1,6 +1,7 @@
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import About from "../About"; import About from "../About";
import Admin from "../Admin";
import BasicLayout from "../../layouts/BasicLayout"; import BasicLayout from "../../layouts/BasicLayout";
import Character from "../Character"; import Character from "../Character";
import Home from "../Home"; import Home from "../Home";
@ -12,6 +13,7 @@ const Root = () => {
<Route index element={<Home />} /> <Route index element={<Home />} />
<Route path="about" element={<About />} /> <Route path="about" element={<About />} />
<Route path="character" element={<Character />} /> <Route path="character" element={<Character />} />
<Route path="admin" element={<Admin />} />
<Route path="*" element={<>ERROR 404</>} /> <Route path="*" element={<>ERROR 404</>} />
</Route> </Route>
</Routes> </Routes>