Skip to content

Commit

Permalink
Bug fixes and improvements (#17)
Browse files Browse the repository at this point in the history
* Fix message creation with no sender/receivers

* Add message story/plot selection

* Re-fetch table data on navigation

Don't render all navigation tab contents in the background. Instead
render them on demand based on route.

Use SWR to fetch table data, so that cached data will be instantly shown
on navigation if available, while the request for up-to-date data
completes.

* Fix select input state on modal close

Store all select input values to React state.

* Use SWR to fetch data in create message modal

* Update bootstrap, add dark mode support

* Remove hardcoded filters

* Add loading indicator

* Use SWR in character page
  • Loading branch information
nicou authored Apr 26, 2024
1 parent 68aab78 commit 4165209
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 454 deletions.
75 changes: 63 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.1.3",
"bootstrap": "^5.3.3",
"interactjs": "^1.10.11",
"lodash-es": "^4.17.21",
"moment": "^2.29.2",
"react": "^17.0.2",
"react-bootstrap": "^2.2.2",
Expand All @@ -22,6 +23,7 @@
"react-scripts": "5.0.0",
"react-scroll": "^1.8.6",
"react-select": "5.8.0",
"swr": "^2.2.5",
"web-vitals": "^2.1.4"
},
"overrides": {
Expand Down
7 changes: 7 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@
position: fixed;
top: 20px;
right: 20px;
}

.floating-button-container > div > button {
background-color: #fff;
}

[data-bs-theme='dark'] .floating-button-container > div > button {
background-color: #212529;
}

.floating-button-container > *:not(:last-child) {
Expand Down
75 changes: 23 additions & 52 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,14 @@ import { Toaster } from 'react-hot-toast';

import { useLocation, useNavigate, Routes, Route } from "react-router-dom";

const TabKeys = {
Characters: "Characters",
Fleet: "Fleet",
Artifacts: "Artifacts",
Plots: "Plots",
Events: "Events",
Messages: "Messages",
};

const tableRoutes = Object.keys(TabKeys).map((k) => "/" + k.toLowerCase());

function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

function App() {
const [key, setKey] = React.useState(null);
const [showMessageNew, setShowMessageNew] = React.useState(false);
const [isDarkMode, setIsDarkMode] = React.useState(window.matchMedia('(prefers-color-scheme: dark)').matches);

const navigate = useNavigate();
const location = useLocation();
Expand All @@ -49,9 +39,6 @@ function App() {
navigate("/characters")
}
}, [location.pathname, navigate]);

const shouldRenderTable =
tableRoutes.includes(location.pathname) || location.pathname === "/";

const onSelectTab = (k) => {
setKey(k);
Expand All @@ -64,50 +51,21 @@ function App() {
setKey(k);
};

const renderTable = () => {
const style = shouldRenderTable ? {} : { display: "none" };
return (
<Tabs id="tabs" activeKey={key} onSelect={onSelectTab} className="mb-3">
<Tab eventKey="Characters" title="Characters">
<div style={style}>
<Characters changeTab={changeTab} />
</div>
</Tab>
<Tab eventKey="Fleet" title="Fleet">
<div style={style}>
<Fleet />
</div>
</Tab>
<Tab eventKey="Artifacts" title="Artifacts">
<div style={style}>
<Artifacts />
</div>
</Tab>
<Tab eventKey="Plots" title="Plots">
<div style={style}>
<Plots />
</div>
</Tab>
<Tab eventKey="Events" title="Events">
<div style={style}>
<Events />
</div>
</Tab>
<Tab eventKey="Messages" title="Messages">
<div style={style}>
<Messages />
</div>
</Tab>
</Tabs>
);
};
React.useEffect(() => {
if (isDarkMode) {
document.querySelector('html').setAttribute('data-bs-theme', 'dark');
} else {
document.querySelector('html').removeAttribute('data-bs-theme');
}
}, [isDarkMode]);

return (
<div className="App">
<h1 className="Title">
<span>Admin Story DB: </span>
<span className={`title-tab ${key}`}>{key}</span>
<ButtonGroup>
<Button style={{ display: "none" }} size="sm" className="float-char-btn" title="Toggle Dark Mode" variant="outline-secondary" onClick={() => setIsDarkMode(!isDarkMode)}>{isDarkMode ? "🌞 Light mode" : "🌚 Dark mode"}</Button>
<Button size="md" className="float-char-btn" title="Create New Plot" variant="outline-secondary" onClick={null}><TbMessagePlus className="plot-button" size="24px"/><span>New plot</span></Button>
<Button size="md" className="float-char-btn" title="Create New Event" variant="outline-secondary" onClick={null}><LuCalendarPlus className="event-button" size="24px"/><span>New event</span></Button>
<Button size="md" className="float-char-btn" title="Create New Message" variant="outline-secondary" onClick={() => setShowMessageNew(true)}><LuMailPlus className="message-button" size="24px"/><span>New message</span></Button>
Expand All @@ -117,13 +75,26 @@ function App() {
showMessageNew={showMessageNew}
handleClose={() => setShowMessageNew(false)}
/>
{renderTable()}
<Tabs id="tabs" activeKey={key} onSelect={onSelectTab} className="mb-3">
<Tab eventKey="Characters" title="Characters" />
<Tab eventKey="Fleet" title="Fleet" />
<Tab eventKey="Artifacts" title="Artifacts" />
<Tab eventKey="Plots" title="Plots" />
<Tab eventKey="Events" title="Events" />
<Tab eventKey="Messages" title="Messages" />
</Tabs>
<Routes>
<Route path="/fleet" element={<Fleet />} />
<Route path="/fleet/:id" element={<Ship changeTab={changeTab} />} />
<Route path="/characters" element={<Characters changeTab={changeTab} />} />
<Route path="/characters/:id" element={<Character changeTab={changeTab} />} />
<Route path="/artifacts" element={<Artifacts />} />
<Route path="/artifacts/:id" element={<Artifact changeTab={changeTab} />} />
<Route path="/plots" element={<Plots />} />
<Route path="/plots/:id" element={<Plot changeTab={changeTab} />} />
<Route path="/events" element={<Events />} />
<Route path="/events/:id" element={<Event changeTab={changeTab} />} />
<Route path="/messages" element={<Messages />} />
<Route path="/messages/:id" element={<Message changeTab={changeTab} />} />
<Route path="*" element={<></>} />
</Routes>
Expand Down
6 changes: 6 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export function apiUrl(path) {
return process.env.REACT_APP_ODYSSEUS_API_URL + path;
}

export async function apiGetRequest(url) {
const response = await fetch(apiUrl(url));
const data = await response.json();
return data;
}
5 changes: 3 additions & 2 deletions src/api/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ export async function upsertMessage(message) {
after_jump: message.after_jump ?? null,
gm_notes: message.gm_notes?.trim() ?? null,
// Should only contain IDs, not the full object
receivers: message.receivers.map((receiver) => {
sender_person_id: message.sender_person_id?.id ?? null,
receivers: message.receivers?.map((receiver) => {
if (typeof receiver === 'string') {
return receiver;
}
if (typeof receiver === 'object' && receiver.id !== undefined) {
return receiver.id;
}
throw new Error(`Invalid receiver: ${receiver}`);
}),
}) ?? [],
};

const response = await fetch(apiUrl("/story/messages"), {
Expand Down
30 changes: 12 additions & 18 deletions src/components/Artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,30 @@ import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import { Link } from "react-router-dom";
import { apiUrl } from "../api";
import { apiGetRequest } from "../api";
import { toSelectOptions } from "../utils/helpers";
import TableLoading from "./TableLoading";
import useSWR from "swr";

import './Artifacts.css';

const getArtifacts = async () => {
const response = await fetch(apiUrl("/science/artifact"));
const artifacts = await response.json();
return artifacts;
}

export default function Artifacts() {
const [artifacts, setArtifacts] = React.useState([]);
const [page, setPage] = React.useState(1);
const [sizePerPage, setSizePerPage] = React.useState(15);

React.useEffect(() => {
getArtifacts().then(data => setArtifacts(data));
}, []);
const { data: artifacts, error, isLoading } = useSWR(
"/science/artifact",
apiGetRequest
);

if (isLoading) return <TableLoading />;
if (error) return <div>Failed to load data</div>;

function getRowIndex(cell, row, rowIndex) {
return (page-1) * sizePerPage + rowIndex + 1;
}

const originSelectOptions = {
"Elder": 'Elder',
"EOC": 'EOC',
"Machine": 'Machine',
"Earth": 'Earth',
"Unknown": 'Unknown',
};
const originSelectOptions = toSelectOptions(artifacts, 'type');

const selectOptions = {
true: 'Yes',
Expand Down
1 change: 0 additions & 1 deletion src/components/Character.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,3 @@ h1.character {
.character .data-found {
color: #f26bf7;
}

Loading

0 comments on commit 4165209

Please sign in to comment.