diff --git a/.gitignore b/.gitignore index 79c167c..214310f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ venv/ __pycache__ node_modules/ coverage/ +.bat diff --git a/dnd-app/backend/buildSearch.js b/dnd-app/backend/buildSearch.js index ec277e1..e1fbd7d 100644 --- a/dnd-app/backend/buildSearch.js +++ b/dnd-app/backend/buildSearch.js @@ -99,7 +99,6 @@ async function buildSlugDb() { "armor", "sections", "spells", - "spelllist", ]; await Promise.all(calls.map((prefix) => fetchAndInsertData(prefix, url))); diff --git a/dnd-app/frontend/index.html b/dnd-app/frontend/index.html index b73747c..4af26d0 100644 --- a/dnd-app/frontend/index.html +++ b/dnd-app/frontend/index.html @@ -4,7 +4,7 @@ - Dnd Companion + Arcane Atlas
diff --git a/dnd-app/frontend/public/icons/armor.png b/dnd-app/frontend/public/icons/armor.png deleted file mode 100644 index 3aaca63..0000000 Binary files a/dnd-app/frontend/public/icons/armor.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/backgrounds.png b/dnd-app/frontend/public/icons/backgrounds.png deleted file mode 100644 index fc3b72c..0000000 Binary files a/dnd-app/frontend/public/icons/backgrounds.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/classes.png b/dnd-app/frontend/public/icons/classes.png deleted file mode 100644 index 3c6c60b..0000000 Binary files a/dnd-app/frontend/public/icons/classes.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/conditions.png b/dnd-app/frontend/public/icons/conditions.png deleted file mode 100644 index 372f998..0000000 Binary files a/dnd-app/frontend/public/icons/conditions.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/feats.png b/dnd-app/frontend/public/icons/feats.png deleted file mode 100644 index 8acfa4e..0000000 Binary files a/dnd-app/frontend/public/icons/feats.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/magicitems.png b/dnd-app/frontend/public/icons/magicitems.png deleted file mode 100644 index 2dab7cd..0000000 Binary files a/dnd-app/frontend/public/icons/magicitems.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/monsters.png b/dnd-app/frontend/public/icons/monsters.png deleted file mode 100644 index 2e0acc8..0000000 Binary files a/dnd-app/frontend/public/icons/monsters.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/planes.png b/dnd-app/frontend/public/icons/planes.png deleted file mode 100644 index 6351634..0000000 Binary files a/dnd-app/frontend/public/icons/planes.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/races.png b/dnd-app/frontend/public/icons/races.png deleted file mode 100644 index 819c79f..0000000 Binary files a/dnd-app/frontend/public/icons/races.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/sections.png b/dnd-app/frontend/public/icons/sections.png deleted file mode 100644 index 5c8a073..0000000 Binary files a/dnd-app/frontend/public/icons/sections.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/spelllist.png b/dnd-app/frontend/public/icons/spelllist.png deleted file mode 100644 index e837c99..0000000 Binary files a/dnd-app/frontend/public/icons/spelllist.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/spells.png b/dnd-app/frontend/public/icons/spells.png deleted file mode 100644 index 8f62725..0000000 Binary files a/dnd-app/frontend/public/icons/spells.png and /dev/null differ diff --git a/dnd-app/frontend/public/icons/weapons.png b/dnd-app/frontend/public/icons/weapons.png deleted file mode 100644 index 3360b86..0000000 Binary files a/dnd-app/frontend/public/icons/weapons.png and /dev/null differ diff --git a/dnd-app/frontend/public/logo.png b/dnd-app/frontend/public/logo.png index cb3bf70..e1b2250 100644 Binary files a/dnd-app/frontend/public/logo.png and b/dnd-app/frontend/public/logo.png differ diff --git a/dnd-app/frontend/src/DetailRoutes/Armor.jsx b/dnd-app/frontend/src/DetailRoutes/Armor.jsx index 6ffe3dd..4eb86d8 100644 --- a/dnd-app/frontend/src/DetailRoutes/Armor.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Armor.jsx @@ -1,19 +1,9 @@ /* eslint-disable no-mixed-spaces-and-tabs */ import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - Row, - Spinner, -} from "reactstrap"; +import { Badge, Card, CardBody, CardText, CardTitle, Col, Container, Row } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -44,87 +34,72 @@ export function Armor() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - {data.name} - {data.category ? ( - <> - {data.category} - - {data.ac_string} - - + + + + {data.name} + {data.category ? ( + <> + {data.category} + {data.ac_string} + + ) : ( + "" + )} + + {data.cost ? ( + + Price: + {data.cost} + ) : ( "" )} - - {data.cost ? ( - - Price: - {data.cost} - - ) : ( - "" - )} + + Strength Requirement: + {data.strength_requirement || "None"} + + + Stealth Disadvantage: + {data.stealth_disadvantage ? "Yes" : "No"} + + {data.base_ac ? ( - Strength Requirement: - {data.strength_requirement || "None"} + Armor Class: + {data.base_ac} + ) : ( + "" + )} + {data.plus_max ? ( - Stealth Disadvantage: - {data.stealth_disadvantage - ? "Yes" - : "No"} + Max Dex: + {"+"} + {data.plus_max} - {data.base_ac ? ( - - Armor Class: - {data.base_ac} - - ) : ( - "" - )} - {data.plus_max ? ( - - Max Dex: - {"+"} - {data.plus_max} - - ) : ( - "" - )} - {data.weight ? ( - - Weight: - {data.weight} - - ) : ( - "" - )} - - - Source:{" "} - - - {data.document__title} - - - - - - - + ) : ( + "" + )} + {data.weight ? ( + + Weight: + {data.weight} + + ) : ( + "" + )} + + + Source:{" "} + + {data.document__title} + + + + + )} ); diff --git a/dnd-app/frontend/src/DetailRoutes/Backgrounds.jsx b/dnd-app/frontend/src/DetailRoutes/Backgrounds.jsx index 92c7b0c..7f462f0 100644 --- a/dnd-app/frontend/src/DetailRoutes/Backgrounds.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Backgrounds.jsx @@ -1,18 +1,8 @@ import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - Row, - Spinner, -} from "reactstrap"; +import { Badge, Card, CardBody, CardText, CardTitle, Col, Container, Row } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -46,67 +36,49 @@ export function Backgrounds() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - {data.name} - {data.type} - {data.desc} -
- - Equipment:{" "} - {data.equipment || "None"} - - - - - - Skill Proficiencies: - {" "} - {data.skill_proficiencies || "None"} - - - - - Tool Proficiencies:{" "} - {data.tool_proficiencies || "None"} - - - - - Languages:{" "} - {data.languages || "None"} - - - -
+ + + + {data.name} + {data.type} + {data.desc} +
+ + Equipment: {data.equipment || "None"} + + + + + Skill Proficiencies: {data.skill_proficiencies || "None"} + + + + + Tool Proficiencies: {data.tool_proficiencies || "None"} + + + + + Languages: {data.languages || "None"} + + + +
- - {data.feature}:{" "} - {data.feature_desc} - - - Source:{" "} - - - {data.document__title} - - - -
-
- -
+ + {data.feature}: {data.feature_desc} + + + Source:{" "} + + {data.document__title} + + + + +
)} ); diff --git a/dnd-app/frontend/src/DetailRoutes/BasicCard.jsx b/dnd-app/frontend/src/DetailRoutes/BasicCard.jsx index 73481a9..3ea221b 100644 --- a/dnd-app/frontend/src/DetailRoutes/BasicCard.jsx +++ b/dnd-app/frontend/src/DetailRoutes/BasicCard.jsx @@ -1,19 +1,9 @@ import { useEffect, useState } from "react"; +import ReactMarkdown from "react-markdown"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - Row, - Spinner, -} from "reactstrap"; +import { Badge, Card, CardBody, CardText, CardTitle, Container } from "reactstrap"; import DndApi from "../api"; -import ReactMarkdown from "react-markdown"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -45,34 +35,23 @@ export function BasicCard({ type }) { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - {data.name} - {data.type} - {data.desc} - - Source:{" "} - - - {data.document__title} - - - - - - - + + + + {data.name} + {data.type} + {data.desc} + + Source:{" "} + + {data.document__title} + + + + + )} ); diff --git a/dnd-app/frontend/src/DetailRoutes/Classes.jsx b/dnd-app/frontend/src/DetailRoutes/Classes.jsx index 3b98e15..ab22abd 100644 --- a/dnd-app/frontend/src/DetailRoutes/Classes.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Classes.jsx @@ -2,24 +2,10 @@ import { useEffect, useState } from "react"; import Markdown from "react-markdown"; import { useParams } from "react-router-dom"; -import { - Accordion, - AccordionBody, - AccordionHeader, - AccordionItem, - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - Row, - Spinner, -} from "reactstrap"; -import DndApi from "../api"; +import { Accordion, AccordionBody, AccordionHeader, AccordionItem, Badge, Card, CardBody, CardText, CardTitle, Container } from "reactstrap"; import remarkGfm from "remark-gfm"; +import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -56,56 +42,45 @@ export function Classes() { } }; - console.log(data); - return ( {loading ? ( - - - - - - - + ) : ( - - - - - - {data.name} -
- {data.table || ""} -
- {data.desc || ""} -
- - {data.archetypes - ? data.archetypes.map((sub) => { - return ( - - - {sub.name} - - - {sub.desc} - - - ); - }) - : ""} - - - Source:{" "} - - {data.document__title} - - -
-
- -
+ + + + {data.name} +
+ {data.table || ""} +
+ {data.desc || ""} +
+ + {data.archetypes + ? data.archetypes.map((sub) => { + return ( + + + {sub.name} + + + {sub.desc} + + + ); + }) + : ""} + + + Source:{" "} + + {data.document__title} + + +
+
+
)}
); diff --git a/dnd-app/frontend/src/DetailRoutes/DetailRoutes.jsx b/dnd-app/frontend/src/DetailRoutes/DetailRoutes.jsx index e46b59b..da811b3 100644 --- a/dnd-app/frontend/src/DetailRoutes/DetailRoutes.jsx +++ b/dnd-app/frontend/src/DetailRoutes/DetailRoutes.jsx @@ -9,6 +9,7 @@ import { BasicCard } from "./BasicCard"; import { MagicItems } from "./MagicItems"; import { Races } from "./Races"; import { Classes } from "./Classes"; +import { Weapons } from "./Weapons"; /** * Component that defines the detail routes for different sections of the application. @@ -25,17 +26,12 @@ export function DetailRoutes() { } /> } /> } /> + } /> } /> } /> } /> - } - /> - } - /> + } /> + } /> ); } diff --git a/dnd-app/frontend/src/DetailRoutes/Feats.jsx b/dnd-app/frontend/src/DetailRoutes/Feats.jsx index e3cd9c6..99b7246 100644 --- a/dnd-app/frontend/src/DetailRoutes/Feats.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Feats.jsx @@ -1,21 +1,8 @@ import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardSubtitle, - CardText, - CardTitle, - Col, - Container, - ListGroup, - ListGroupItem, - Row, - Spinner, -} from "reactstrap"; +import { Badge, Card, CardBody, CardSubtitle, CardText, CardTitle, Container, ListGroup, ListGroupItem } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -47,48 +34,31 @@ export function Feats() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - {data.name} - - {data.prerequisite} - -
- {data.desc} - - {data.effects_desc.map((effect, index) => ( - - {effect} - - ))} - - - Source:{" "} - - - {data.document__title} - - - -
-
- -
+ + + + {data.name} + + {data.prerequisite} + +
+ {data.desc} + + {data.effects_desc.map((effect, index) => ( + {effect} + ))} + + + Source:{" "} + + {data.document__title} + + +
+
+
)}
); diff --git a/dnd-app/frontend/src/DetailRoutes/MagicItems.jsx b/dnd-app/frontend/src/DetailRoutes/MagicItems.jsx index c882586..718845b 100644 --- a/dnd-app/frontend/src/DetailRoutes/MagicItems.jsx +++ b/dnd-app/frontend/src/DetailRoutes/MagicItems.jsx @@ -1,22 +1,8 @@ import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - PopoverBody, - PopoverHeader, - Row, - Spinner, - Table, - UncontrolledPopover, -} from "reactstrap"; +import { Badge, Card, CardBody, CardText, CardTitle, Container, PopoverBody, PopoverHeader, Table, UncontrolledPopover } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); import("../assets/styles/MagicItems.css"); @@ -61,99 +47,71 @@ export function MagicItems() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - - {data.name} - {" "} - - {data.rarity} - - - - Item Rarity - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RarityLevelValue (GP)
Common1+50-100
Uncommon1+101-500
Rare5+501-5,000
Very Rare11+5,001-50,000
Legendary17+50,001+
-
-
-
- {data.type} -
- {data.desc} - - Source:{" "} - - - {data.document__title} - - - -
-
- -
+ + + + + {data.name} + {" "} + + {data.rarity} + + + Item Rarity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RarityLevelValue (GP)
Common1+50-100
Uncommon1+101-500
Rare5+501-5,000
Very Rare11+5,001-50,000
Legendary17+50,001+
+
+
+
+ {data.type} +
+ {data.desc} + + Source:{" "} + + {data.document__title} + + +
+
+
)}
); diff --git a/dnd-app/frontend/src/DetailRoutes/Monster.jsx b/dnd-app/frontend/src/DetailRoutes/Monster.jsx index 4d4728f..ada4674 100644 --- a/dnd-app/frontend/src/DetailRoutes/Monster.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Monster.jsx @@ -1,24 +1,11 @@ /* eslint-disable no-mixed-spaces-and-tabs */ import { useEffect, useState } from "react"; +import ReactMarkdown from "react-markdown"; import { useParams } from "react-router-dom"; -import { - Badge, - Card, - CardBody, - CardImg, - CardSubtitle, - CardText, - CardTitle, - Col, - Container, - ListGroup, - ListGroupItem, - Row, - Spinner, -} from "reactstrap"; +import { Badge, Card, CardBody, CardText, CardTitle, Col, Container, ListGroup, ListGroupItem, Row } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import { getScore } from "../tools"; -import ReactMarkdown from "react-markdown"; import("../assets/styles/Details.css"); @@ -49,267 +36,258 @@ export function Monster() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - - - {data.name} - {!data.desc && !data.legendary_desc ? "" : {data.desc || data.legendary_desc}} -
- - - Type: {data.type} {data.subtype ? `(${data.subtype})` : ""} - - - Size: {data.size} - - - Alignment: {data.alignment} - - - Armor Class: {data.armor_class} - - - Hit Points: {data.hit_points} - - - Hit Dice: {data.hit_dice} + + + + + {data.name} + {!data.desc && !data.legendary_desc ? "" : {data.desc || data.legendary_desc}} +
+ + + Type: {data.type} {data.subtype ? `(${data.subtype})` : ""} + + + Size: {data.size} + + + Alignment: {data.alignment} + + + Armor Class: {data.armor_class} + + + Hit Points: {data.hit_points} + + + Hit Dice: {data.hit_dice} + + + Speed:{" "} + {Object.keys(data.speed).map((speed, index) => ( + + {speed} {data.speed[speed]} ft. + + ))} + + + Senses: {data.senses} + + +
+
Actions
+ {data.actions.map((action, index) => ( +
+ + {action.name}: {action.desc} - - Speed:{" "} - {Object.keys(data.speed).map((speed, index) => ( - - {speed} {data.speed[speed]} ft. +
+ ))} +
+
Special Abilities
+ {!data.special_abilities.length > 0 + ? "None" + : data.special_abilities.map((ability, index) => ( +
+ + {ability.name}: {ability.desc} +
+ ))} + {!data.legendary_actions.length > 0 ? ( + <> + ) : ( + <> +
+
Legendary Actions
+ + {data.legendary_actions.map((action, index) => ( +

+ {action.name} + {": "} + {action.desc} +

))}
- - Senses: {data.senses} - -
-
-
Actions
- {data.actions.map((action, index) => ( -
- - {action.name}: {action.desc} - -
- ))} -
-
Special Abilities
- {!data.special_abilities - ? "None" - : data.special_abilities.map((ability, index) => ( -
- - {ability.name}: {ability.desc} - -
- ))} - {!data.legendary_actions ? ( + + )} + +
+ + + Languages: {data.languages} + + + Challenge Rating: {data.challenge_rating} + + +
+ + +
Immunities/Resistances
+ + + Damage Vulnerabilities: {data.damage_vulnerabilities || "None"} + + + Damage Resistances: {data.damage_resistances || "None"} + + + Damage Immunities: {data.damage_immunities || "None"} + + + Condition Immunities: {data.condition_immunities || "None"} + + + + +
Other Actions
+ + + Bonus Actions:{" "} + {!data.bonus_actions + ? "None" + : data.bonus_actions.map((action, index) => ( +

+ {action.name} + {": "} + {action.desc} +

+ ))} +
+ + Reactions:{" "} + {data.reactions + ? Object.keys(data.reactions).map((action, index) => ( +

+ {data.reactions[action].name}: {data.reactions[action].desc} +

+ )) + : "None"} +
+
+ +
+
+ + +
Abilities
+ + + Strength: {data.strength} ({getScore(data.strength)}) + + + Dexterity: {data.dexterity} ({getScore(data.dexterity)}) + + + Constitution: {data.constitution} ({getScore(data.constitution)}) + + + Intelligence: {data.intelligence} ({getScore(data.intelligence)}) + + + Wisdom: {data.wisdom} ({getScore(data.wisdom)}) + + + Charisma: {data.charisma} ({getScore(data.charisma)}) + + + + +
Saves
+ + + Strength Save: {data.strength_save || getScore(data.strength)} + + + Dexterity Save: {data.dexterity_save || getScore(data.dexterity)} + + + Constitution Save: {data.constitution_save || getScore(data.constitution)} + + + Intelligence Save: {data.intelligence_save || getScore(data.intelligence)} + + + Wisdom Save: {data.wisdom_save || getScore(data.wisdom)} + + + Charisma Save: {data.charisma_save || getScore(data.charisma)} + + + Perception: {data.perception || "0"} + + + +
+
+ + {!data.skill ? ( <> ) : ( <> -
-
Legendary Actions
- - {data.legendary_actions.map((action, index) => ( -

- {action.name} - {": "} - {action.desc} -

- ))} -
- - )} - -
- - - Languages: {data.languages} - - - Challenge Rating: {data.challenge_rating} - - -
- - -
Immunities/Resistances
- - - Damage Vulnerabilities: {data.damage_vulnerabilities || "None"} - - - Damage Resistances: {data.damage_resistances || "None"} - - - Damage Immunities: {data.damage_immunities || "None"} - - - Condition Immunities: {data.condition_immunities || "None"} - - - - -
Other Actions
- - - Bonus Actions:{" "} - {!data.bonus_actions - ? "None" - : data.bonus_actions.map((action, index) => ( -

- {action.name} + + Skills:{" "} + + {Object.keys(data.skills).map( + (skill, index) => + ( + + {skill} {": "} - {action.desc} -

- ))} -
- - Reactions:{" "} - {data.reactions - ? Object.keys(data.reactions).map((action, index) => ( -

- {action} {data.reactions[action]} ft. -

- )) - : "None"} -
-
- -
-
- - -
Abilities
- - - Strength: {data.strength} ({getScore(data.strength)}) - - - Dexterity: {data.dexterity} ({getScore(data.dexterity)}) - - - Constitution: {data.constitution} ({getScore(data.constitution)}) - - - Intelligence: {data.intelligence} ({getScore(data.intelligence)}) - - - Wisdom: {data.wisdom} ({getScore(data.wisdom)}) - - - Charisma: {data.charisma} ({getScore(data.charisma)}) - - - - -
Saves
- - - Strength Save: {data.strength_save || getScore(data.strength)} - - - Dexterity Save: {data.dexterity_save || getScore(data.dexterity)} - - - Constitution Save: {data.constitution_save || getScore(data.constitution)} - - - Intelligence Save: {data.intelligence_save || getScore(data.intelligence)} - - - Wisdom Save: {data.wisdom_save || getScore(data.wisdom)} - - - Charisma Save: {data.charisma_save || getScore(data.charisma)} - - - Perception: {data.perception || "0"} - - - -
-
- - {!data.skill ? ( - <> - ) : ( - <> - - Skills:{" "} - - {Object.keys(data.skills).map( - (skill, index) => - ( - - {skill} - {": "} - {data.skills[skill]} - - ) || "None" - )} - - {!data.armor_desc ? ( - <> - ) : ( - - Armor Description: {data.armor_desc} - - )} - {!data.group ? ( - <> - ) : ( - <> - - Group: {data.group} - - + {data.skills[skill]} + + ) || "None" )} - - - )} - - - {!data.environment ? ( - <> - ) : ( - <> -
-
Environments
- - {data.environments.map((environment, index) => ( - - {environment} - - ))} - +
+ {!data.armor_desc ? ( + <> + ) : ( + + Armor Description: {data.armor_desc} + + )} + {!data.group ? ( + <> + ) : ( + <> + + Group: {data.group} + + + )} + )} - - Source:{" "} - - {data.document__title} - - -
- -
- -
+ + + {!data.environment ? ( + <> + ) : ( + <> +
+
Environments
+ + {data.environments.map((environment, index) => ( + + {environment} + + ))} + + + )} + + Source:{" "} + + {data.document__title} + + + + + +
)} ); diff --git a/dnd-app/frontend/src/DetailRoutes/Races.jsx b/dnd-app/frontend/src/DetailRoutes/Races.jsx index 9d6c922..9c40979 100644 --- a/dnd-app/frontend/src/DetailRoutes/Races.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Races.jsx @@ -10,15 +10,14 @@ import { Badge, Card, CardBody, - CardImg, CardText, CardTitle, Col, Container, Row, - Spinner, } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -58,51 +57,39 @@ export function Races() { return ( {loading ? ( - - - - - - - + ) : ( - - - - - Ability Score -
+ + + + {data.name} +
+ Ability Scores: {data.asi.map((item, index) => ( - -
- - {item.attributes} - {item.value} - -
-
+ +

+ {item.attributes} +{item.value} +

+ ))}
-
-
- Size - {data.size_raw} -
- Speed - - {Object.keys(data.speed).map((speed, index) => ( -

- {speed} {data.speed[speed]} ft. -

- ))} -
-
- - - - - {data.name} +
+ + + Size: + {data.size_raw} + + + Speed: + {Object.keys(data.speed).map((speed, index) => ( + + {speed} {data.speed[speed]} ft. + + ))} + + +
{data.desc || ""} {data.asi_desc ? {data.asi_desc} : ""} {data.age ? {data.age} : ""} @@ -112,30 +99,32 @@ export function Races() { {data.languages || "Languages: None"} {data.vision ? {data.vision} : ""} {data.traits ? {data.traits} : ""} - - {data.subraces - ? data.subraces.map((race) => { - return ( - - - {race.name} - - - {race.desc} - {race.asi_desc ? {race.asi_desc} : ""} - {race.age ? {race.age} : ""} - {race.alignment ? {race.age} : ""} - {race.size ? {race.age} : ""} - {race.speed_desc ? {race.speed_desc} : ""} - {race.languages ? {race.languages} : ""} - {race.vision ? {race.vision} : ""} - {race.traits ? {race.traits} : ""} - - - ); - }) - : ""} - + {!data.subraces ? ( + <> + ) : ( + + {data.subraces.map((race) => { + return ( + + + {race.name} + + + {race.desc} + {race.asi_desc ? {race.asi_desc} : ""} + {race.age ? {race.age} : ""} + {race.alignment ? {race.age} : ""} + {race.size ? {race.age} : ""} + {race.speed_desc ? {race.speed_desc} : ""} + {race.languages ? {race.languages} : ""} + {race.vision ? {race.vision} : ""} + {race.traits ? {race.traits} : ""} + + + ); + })} + + )} Source:{" "} @@ -144,8 +133,7 @@ export function Races() {
- -
+
)} ); diff --git a/dnd-app/frontend/src/DetailRoutes/SpellCard.jsx b/dnd-app/frontend/src/DetailRoutes/SpellCard.jsx index 8ef9fd0..d46383a 100644 --- a/dnd-app/frontend/src/DetailRoutes/SpellCard.jsx +++ b/dnd-app/frontend/src/DetailRoutes/SpellCard.jsx @@ -4,7 +4,6 @@ import { Badge, Card, CardBody, - CardImg, CardSubtitle, CardText, CardTitle, @@ -15,10 +14,10 @@ import { PopoverBody, PopoverHeader, Row, - Spinner, UncontrolledPopover, } from "reactstrap"; import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); import("../assets/styles/SpellCard.css"); @@ -50,372 +49,188 @@ export function SpellCard() { return ( {loading ? ( - + ) : ( - - - - - - {data.name} - - - {data.school} {data.level} - - {data.requires_verbal_components ? ( - <> - - V - - - - Verbal - - - Most spells require the - chanting of mystic words. - The words themselves aren’t - the source of the spell’s - power; rather, the - particular combination of - sounds, with specific pitch - and resonance, sets the - threads of magic in motion. - Thus, a character who is - gagged or in an area of - silence, such as one created - by the silence spell, can’t - cast a spell with a verbal - component. - - - - ) : ( - <> - )} - {data.requires_somatic_components ? ( - <> - - S - - - - Somatic - - - Spellcasting gestures might - include a forceful - gesticulation or an - intricate set of gestures. - If a spell requires a - somatic component, the - caster must have free use of - at least one hand to perform - these gestures. - - - - ) : ( - <> - )} - {data.requires_material_components ? ( - <> - - M - - - - Material - - - Casting some spells requires - particular objects, - specified in parentheses in - the component entry. A - character can use a - component pouch or a - spellcasting focus (found in - “Equipment”) in place of the - components specified for a - spell. But if a cost is - indicated for a component, a - character must have that - specific component before he - or she can cast the spell. - If a spell states that a - material component is - consumed by the spell, the - caster must provide this - component for each casting - of the spell. A spellcaster - must have a hand free to - access a spell’s material - components—or to hold a - spellcasting focus—but it - can be the same hand that he - or she uses to perform - somatic components. - - - - ) : ( - <> - )} - {data.can_be_cast_as_ritual ? ( - <> - - R - - - - Ritual - - - Certain spells have a - special tag: ritual. Such a - spell can be cast following - the normal rules for - spellcasting, or the spell - can be cast as a ritual. The - ritual version of a spell - takes 10 minutes longer to - cast than normal. It also - doesn’t expend a spell slot, - which means the ritual - version of a spell can’t be - cast at a higher level. To - cast a spell as a ritual, a - spellcaster must have a - feature that grants the - ability to do so. The cleric - and the druid, for example, - have such a feature. The - caster must also have the - spell prepared or on his or - her list of spells known, - unless the character’s - ritual feature specifies - otherwise, as the wizard’s - does. - - - - ) : ( - <> - )} - {data.requires_concentration ? ( - <> - - C - - - - Concentration - - - Some spells require you to - maintain concentration in - order to keep their magic - active. If you lose - concentration, such a spell - ends. If a spell must be - maintained with - concentration, that fact - appears in its Duration - entry, and the spell - specifies how long you can - concentrate on it. You can - end concentration at any - time (no action required). - Normal activity, such as - moving and attacking, - doesn’t interfere with - concentration. The following - factors can break - concentration: - - - - Casting another - spell that - requires - concentration. - {" "} - You lose - concentration on a - spell if you cast - another spell that - requires - concentration. You - can’t concentrate on - two spells at once. - - - - Taking damage. - {" "} - Whenever you take - damage while you are - concentrating on a - spell, you must make - a Constitution - saving throw to - maintain your - concentration. The - DC equals 10 or half - the damage you take, - whichever number is - higher. If you take - damage from multiple - sources, such as an - arrow and a dragon’s - breath, you make a - separate saving - throw for each - source of damage. - - - - Being - incapacitated or - killed. - {" "} - You lose - concentration on a - spell if you are - incapacitated or if - you die.{" "} - - - The GM might also decide - that certain environmental - phenomena, such as a wave - crashing over you while - you’re on a storm--tossed - ship, require you to succeed - on a DC 10 Constitution - saving throw to maintain - concentration on a spell. - - - - ) : ( - <> - )} - - - {data.spell_lists.map((b) => { - return ( - - {b} - - ); - })} - - {data.desc} - - - Range: {data.range} - - - Duration:{" "} - {data.duration} - - - Casting Time:{" "} - {data.casting_time} - - - {data.higher_level !== "" ? ( + + + + {data.name} + + + {data.school} {data.level} + + {data.requires_verbal_components ? ( <> -
- - Higher Level:{" "} - {data.higher_level} - + + V + + + Verbal + + Most spells require the chanting of mystic words. The words themselves aren’t the source of the + spell’s power; rather, the particular combination of sounds, with specific pitch and resonance, sets + the threads of magic in motion. Thus, a character who is gagged or in an area of silence, such as one + created by the silence spell, can’t cast a spell with a verbal component. + + ) : ( <> )} -
- -
Additional Info
- - Material Components:{" "} - {(data.requires_material_components && - `${data.material}`) || - `None`} - - - Classes:{" "} - {data.dnd_class} - - - Cast as ritual: - {data.can_be_cast_as_ritual - ? "True" - : "False"} - -
- - Source:{" "} - - - {data.document__title} - - - -
-
- -
+ {data.requires_somatic_components ? ( + <> + + S + + + Somatic + + Spellcasting gestures might include a forceful gesticulation or an intricate set of gestures. If a + spell requires a somatic component, the caster must have free use of at least one hand to perform + these gestures. + + + + ) : ( + <> + )} + {data.requires_material_components ? ( + <> + + M + + + Material + + Casting some spells requires particular objects, specified in parentheses in the component entry. A + character can use a component pouch or a spellcasting focus (found in “Equipment”) in place of the + components specified for a spell. But if a cost is indicated for a component, a character must have + that specific component before he or she can cast the spell. If a spell states that a material + component is consumed by the spell, the caster must provide this component for each casting of the + spell. A spellcaster must have a hand free to access a spell’s material components—or to hold a + spellcasting focus—but it can be the same hand that he or she uses to perform somatic components. + + + + ) : ( + <> + )} + {data.can_be_cast_as_ritual ? ( + <> + + R + + + Ritual + + Certain spells have a special tag: ritual. Such a spell can be cast following the normal rules for + spellcasting, or the spell can be cast as a ritual. The ritual version of a spell takes 10 minutes + longer to cast than normal. It also doesn’t expend a spell slot, which means the ritual version of a + spell can’t be cast at a higher level. To cast a spell as a ritual, a spellcaster must have a feature + that grants the ability to do so. The cleric and the druid, for example, have such a feature. The + caster must also have the spell prepared or on his or her list of spells known, unless the character’s + ritual feature specifies otherwise, as the wizard’s does. + + + + ) : ( + <> + )} + {data.requires_concentration ? ( + <> + + C + + + Concentration + + Some spells require you to maintain concentration in order to keep their magic active. If you lose + concentration, such a spell ends. If a spell must be maintained with concentration, that fact appears + in its Duration entry, and the spell specifies how long you can concentrate on it. You can end + concentration at any time (no action required). Normal activity, such as moving and attacking, doesn’t + interfere with concentration. The following factors can break concentration: + + + Casting another spell that requires concentration. You lose concentration on + a spell if you cast another spell that requires concentration. You can’t concentrate on two + spells at once. + + + Taking damage. Whenever you take damage while you are concentrating on a + spell, you must make a Constitution saving throw to maintain your concentration. The DC equals + 10 or half the damage you take, whichever number is higher. If you take damage from multiple + sources, such as an arrow and a dragon’s breath, you make a separate saving throw for each + source of damage. + + + Being incapacitated or killed. You lose concentration on a spell if you are + incapacitated or if you die.{" "} + + + The GM might also decide that certain environmental phenomena, such as a wave crashing over you while + you’re on a storm--tossed ship, require you to succeed on a DC 10 Constitution saving throw to + maintain concentration on a spell. + + + + ) : ( + <> + )} + + + {data.spell_lists.map((b) => { + return ( + + {b} + + ); + })} + + {data.desc} + + + Range: {data.range} + + + Duration: {data.duration} + + + Casting Time: {data.casting_time} + + + {data.higher_level !== "" ? ( + <> +
+ + Higher Level: {data.higher_level} + + + ) : ( + <> + )} +
+ +
Additional Info
+ + Material Components: {(data.requires_material_components && `${data.material}`) || `None`} + + + Classes: {data.dnd_class} + + + Cast as ritual: + {data.can_be_cast_as_ritual ? "True" : "False"} + +
+ + Source:{" "} + + {data.document__title} + + + + +
)} ); diff --git a/dnd-app/frontend/src/DetailRoutes/Weapons.jsx b/dnd-app/frontend/src/DetailRoutes/Weapons.jsx index 6652f50..1563f11 100644 --- a/dnd-app/frontend/src/DetailRoutes/Weapons.jsx +++ b/dnd-app/frontend/src/DetailRoutes/Weapons.jsx @@ -1,17 +1,10 @@ /* eslint-disable no-mixed-spaces-and-tabs */ -import { - Badge, - Card, - CardBody, - CardImg, - CardText, - CardTitle, - Col, - Container, - ListGroup, - ListGroupItem, - Row, -} from "reactstrap"; +import { useEffect, useState } from "react"; +import ReactMarkdown from "react-markdown"; +import { useParams } from "react-router-dom"; +import { Badge, Card, CardBody, CardText, CardTitle, Col, Container, ListGroup, ListGroupItem, Row } from "reactstrap"; +import DndApi from "../api"; +import { Loading } from "../Loading"; import("../assets/styles/Details.css"); @@ -20,20 +13,36 @@ import("../assets/styles/Details.css"); * @param {{data}} data - The data object containing information about the weapon. * @returns JSX element displaying the weapon information. */ -export function Weapons({ data }) { +export function Weapons() { + const { slug } = useParams(); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + + useEffect(() => { + /** + * Asynchronously fetches information about a specific weapon from an external API. + * Sets loading state to true before making the API call and sets it to false after receiving the response. + * @returns None + */ + async function getInfo() { + setLoading(true); + let res = await DndApi.getFromExternal({ type: "weapons", slug }); + setData(res); + setLoading(false); + } + getInfo(); + }, [slug]); + return ( - - - + {loading ? ( + + ) : ( + {data.name} - {data.category ? ( - {data.category} - ) : ( - "" - )} + {data.category ? {data.category} : ""} {data.cost ? ( @@ -46,10 +55,7 @@ export function Weapons({ data }) { {data.damage_dice ? ( Damage: - {data.damage_dice}{" "} - - {data.damage_type} - + {data.damage_dice} {data.damage_type} ) : ( "" @@ -75,10 +81,7 @@ export function Weapons({ data }) { Properties: {data.properties.map((item) => ( - + {item} )) || "None"} @@ -87,15 +90,13 @@ export function Weapons({ data }) { Source:{" "} - - {data.document__title} - + {data.document__title} - - + + )} ); } diff --git a/dnd-app/frontend/src/Home.jsx b/dnd-app/frontend/src/Home.jsx index 584ecca..25c3408 100644 --- a/dnd-app/frontend/src/Home.jsx +++ b/dnd-app/frontend/src/Home.jsx @@ -23,7 +23,7 @@ export function Home() { - Welcome to a Dungeons and Dragons Companion + Welcome to Arcane Atlas
diff --git a/dnd-app/frontend/src/Loading.jsx b/dnd-app/frontend/src/Loading.jsx new file mode 100644 index 0000000..a1c7aa4 --- /dev/null +++ b/dnd-app/frontend/src/Loading.jsx @@ -0,0 +1,18 @@ +import { Progress, Spinner, Toast, ToastBody, ToastHeader } from "reactstrap"; + +import "./assets/styles/Loading.css"; + +export function Loading() { + return ( + <> + + Loading...}> + Loading... + + + + + + + ); +} diff --git a/dnd-app/frontend/src/NavBar.jsx b/dnd-app/frontend/src/NavBar.jsx index 112f391..7d64af2 100644 --- a/dnd-app/frontend/src/NavBar.jsx +++ b/dnd-app/frontend/src/NavBar.jsx @@ -24,19 +24,13 @@ export function NavBar() { const toggle = () => setDropdownOpen((prevState) => !prevState); return ( - + - +