Compare commits
3 Commits
59923864b0
...
9b89d7d7b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b89d7d7b0 | ||
|
|
8241967ab6 | ||
|
|
7acab90068 |
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -19,7 +19,7 @@ packages/react-devtools-timeline/dist# See https://help.github.com/articles/igno
|
|||
/coverage
|
||||
|
||||
# production
|
||||
/src/data
|
||||
# /src/data # to uncomment
|
||||
/build
|
||||
temp
|
||||
dist
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"html-react-parser": "^5.1.10",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
|
|
|
|||
28
scripts/concatFiles.py
Normal file
28
scripts/concatFiles.py
Normal 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)
|
||||
|
||||
|
|
@ -21,6 +21,11 @@ const Navbar = ({ className, onClick, handleClose }) => {
|
|||
Hanzo Character Maker
|
||||
</Link>
|
||||
</li>
|
||||
<li className="navbarList__item">
|
||||
<Link className="navbar__link" to="admin">
|
||||
Admin
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
|
|
|
|||
16124
src/data/dataFiltered.js
Normal file
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
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
2033
src/data/jutsuData.js
Normal file
File diff suppressed because it is too large
Load Diff
96
src/data/levels.js
Normal file
96
src/data/levels.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { type } from "@testing-library/user-event/dist/type";
|
||||
|
||||
const levels = [
|
||||
{
|
||||
_id: "1",
|
||||
name: "1",
|
||||
unlocks: [
|
||||
{
|
||||
name: "Archetype",
|
||||
type: "choice",
|
||||
options: [
|
||||
{
|
||||
folder_id: "xdVnM5YTVFEewTje",
|
||||
name: "Maitre des lames",
|
||||
description: "assassin",
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
{
|
||||
folder_id: "iu3QsWHoalitMfaF",
|
||||
name: "Maitre du chakra",
|
||||
description: "mage",
|
||||
},
|
||||
{
|
||||
folder_id: "6ugsJPZf1DtuJKcF",
|
||||
name: "Maitre des 1000 objets",
|
||||
description: "Bricoleur",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Jutsus",
|
||||
type: "mandatory",
|
||||
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)",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
_id: "2",
|
||||
name: "2",
|
||||
},
|
||||
{
|
||||
_id: "3",
|
||||
name: "3",
|
||||
},
|
||||
{
|
||||
_id: "4",
|
||||
name: "4",
|
||||
},
|
||||
{
|
||||
_id: "5",
|
||||
name: "5",
|
||||
},
|
||||
{
|
||||
_id: "6",
|
||||
name: "6",
|
||||
},
|
||||
{
|
||||
_id: "7",
|
||||
name: "7",
|
||||
},
|
||||
{
|
||||
_id: "8",
|
||||
name: "8",
|
||||
},
|
||||
{
|
||||
_id: "9",
|
||||
name: "9",
|
||||
},
|
||||
{
|
||||
_id: "10",
|
||||
name: "10",
|
||||
},
|
||||
];
|
||||
|
||||
export default levels;
|
||||
210
src/pages/Admin/index.js
Normal file
210
src/pages/Admin/index.js
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
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 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>
|
||||
);
|
||||
};
|
||||
|
||||
// {
|
||||
// _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;
|
||||
10
src/pages/Admin/styles.css
Normal file
10
src/pages/Admin/styles.css
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.character {}
|
||||
|
||||
.selected {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@ import "./styles.css";
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
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 [data, setData] = useState();
|
||||
|
|
@ -36,9 +37,11 @@ const Character = () => {
|
|||
const renderSubItems = (subItems) => {
|
||||
if (subItems?.length === 0) {
|
||||
return subItems?.length !== 0 ? (
|
||||
<div>
|
||||
<h4>
|
||||
{subItems?._id} - {subItems?.name}
|
||||
</h4>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
|
@ -47,13 +50,17 @@ const Character = () => {
|
|||
return (
|
||||
<div key={`subItem-${subId}`} style={{ color: "green" }}>
|
||||
{subItems?.length !== 0 ? (
|
||||
<div>
|
||||
<h5
|
||||
className={
|
||||
subItem?._key === `!folders!${subItem?._id}` ? "selected" : ""
|
||||
subItem?._key === `!folders!${subItem?._id}`
|
||||
? "selected"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{subItem?._id} - {subItem?.name} - {subItem?.folder}
|
||||
</h5>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
|
@ -67,14 +74,29 @@ const Character = () => {
|
|||
return (
|
||||
<div className="character">
|
||||
<Container>
|
||||
{data?.map((item, id) => (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-around",
|
||||
flexDirection: "row",
|
||||
}}
|
||||
>
|
||||
{levels.map((item, id) => (
|
||||
<div key={`level-${id}`}>
|
||||
<h2>{item?.name}</h2>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* {data?.map((item, id) => (
|
||||
<div key={`top-${id}`}>
|
||||
<div>
|
||||
<h3>
|
||||
{item?._id} - {item?.name}
|
||||
</h3>
|
||||
</div>
|
||||
{renderSubItems(item?.subItems)}
|
||||
</div>
|
||||
))}
|
||||
))} */}
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Route, Routes } from "react-router-dom";
|
||||
|
||||
import About from "../About";
|
||||
import Admin from "../Admin";
|
||||
import BasicLayout from "../../layouts/BasicLayout";
|
||||
import Character from "../Character";
|
||||
import Home from "../Home";
|
||||
|
|
@ -12,6 +13,7 @@ const Root = () => {
|
|||
<Route index element={<Home />} />
|
||||
<Route path="about" element={<About />} />
|
||||
<Route path="character" element={<Character />} />
|
||||
<Route path="admin" element={<Admin />} />
|
||||
<Route path="*" element={<>ERROR 404</>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user