Skip to content

Commit 5039e5f

Browse files
feat: overhaul scenario info UI (#313)
1 parent 00b9820 commit 5039e5f

File tree

14 files changed

+1053
-3
lines changed

14 files changed

+1053
-3
lines changed

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"firebase": "^9.1.1",
2222
"firebase-admin": "^9.11.1",
2323
"mongoose": "^8.7.0",
24+
"multer": "^2.0.2",
2425
"uuid": "^11.1.0"
2526
},
2627
"devDependencies": {

backend/yarn.lock

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,11 @@ anymatch@~3.1.2:
16601660
normalize-path "^3.0.0"
16611661
picomatch "^2.0.4"
16621662

1663+
append-field@^1.0.0:
1664+
version "1.0.0"
1665+
resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56"
1666+
integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==
1667+
16631668
argparse@^1.0.7:
16641669
version "1.0.10"
16651670
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -1892,6 +1897,13 @@ buffer-from@^1.0.0:
18921897
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
18931898
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
18941899

1900+
busboy@^1.6.0:
1901+
version "1.6.0"
1902+
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
1903+
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
1904+
dependencies:
1905+
streamsearch "^1.1.0"
1906+
18951907
18961908
version "3.1.2"
18971909
resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
@@ -2056,6 +2068,16 @@ [email protected]:
20562068
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
20572069
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
20582070

2071+
concat-stream@^2.0.0:
2072+
version "2.0.0"
2073+
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
2074+
integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
2075+
dependencies:
2076+
buffer-from "^1.0.0"
2077+
inherits "^2.0.3"
2078+
readable-stream "^3.0.2"
2079+
typedarray "^0.0.6"
2080+
20592081
configstore@^5.0.0:
20602082
version "5.0.1"
20612083
resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz"
@@ -3979,6 +4001,18 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
39794001
dependencies:
39804002
brace-expansion "^1.1.7"
39814003

4004+
minimist@^1.2.6:
4005+
version "1.2.8"
4006+
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
4007+
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
4008+
4009+
mkdirp@^0.5.6:
4010+
version "0.5.6"
4011+
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
4012+
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
4013+
dependencies:
4014+
minimist "^1.2.6"
4015+
39824016
mongodb-connection-string-url@^3.0.0:
39834017
version "3.0.1"
39844018
resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz"
@@ -4071,6 +4105,19 @@ [email protected], ms@^2.1.1, ms@^2.1.3:
40714105
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
40724106
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
40734107

4108+
multer@^2.0.2:
4109+
version "2.0.2"
4110+
resolved "https://registry.yarnpkg.com/multer/-/multer-2.0.2.tgz#08a8aa8255865388c387aaf041426b0c87bf58dd"
4111+
integrity sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==
4112+
dependencies:
4113+
append-field "^1.0.0"
4114+
busboy "^1.6.0"
4115+
concat-stream "^2.0.0"
4116+
mkdirp "^0.5.6"
4117+
object-assign "^4.1.1"
4118+
type-is "^1.6.18"
4119+
xtend "^4.0.2"
4120+
40744121
natural-compare@^1.4.0:
40754122
version "1.4.0"
40764123
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -4148,7 +4195,7 @@ npm-run-path@^4.0.1:
41484195
dependencies:
41494196
path-key "^3.0.0"
41504197

4151-
object-assign@^4:
4198+
object-assign@^4, object-assign@^4.1.1:
41524199
version "4.1.1"
41534200
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
41544201
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -4456,7 +4503,7 @@ react-is@^18.0.0:
44564503
resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz"
44574504
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
44584505

4459-
readable-stream@^3.1.1:
4506+
readable-stream@^3.0.2, readable-stream@^3.1.1:
44604507
version "3.6.2"
44614508
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
44624509
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -4771,6 +4818,11 @@ [email protected]:
47714818
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz"
47724819
integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
47734820

4821+
streamsearch@^1.1.0:
4822+
version "1.1.0"
4823+
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
4824+
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
4825+
47744826
streamx@^2.15.0:
47754827
version "2.22.0"
47764828
resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7"
@@ -4976,7 +5028,7 @@ type-fest@^0.21.3:
49765028
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
49775029
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
49785030

4979-
type-is@~1.6.18:
5031+
type-is@^1.6.18, type-is@~1.6.18:
49805032
version "1.6.18"
49815033
resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
49825034
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@@ -4991,6 +5043,11 @@ typedarray-to-buffer@^3.1.5:
49915043
dependencies:
49925044
is-typedarray "^1.0.0"
49935045

5046+
typedarray@^0.0.6:
5047+
version "0.0.6"
5048+
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
5049+
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
5050+
49945051
undefsafe@^2.0.5:
49955052
version "2.0.5"
49965053
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
@@ -5171,6 +5228,11 @@ xdg-basedir@^4.0.0:
51715228
resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz"
51725229
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
51735230

5231+
xtend@^4.0.2:
5232+
version "4.0.2"
5233+
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
5234+
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
5235+
51745236
y18n@^5.0.5:
51755237
version "5.0.8"
51765238
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"

frontend/src/App.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import LoginPage from "./features/login/LoginPage/LoginPage";
1010
import ManageGroupsPage from "./features/groups/ManageGroupsPage";
1111
import PlayScenarioResolver from "./features/playScenario/PlayScenarioResolver";
1212
import ScenarioSelectionPage from "./features/scenarioSelection/ScenarioSelectionPage";
13+
14+
import ScenarioInfo from "./features/scenarioInfo/ScenarioInfo";
15+
import PlayPage from "./features/play/PlayPage";
16+
1317
import Dashboard from "./features/dashboard/Dashboard";
18+
1419
import { ScenePage } from "./features/sceneSelection/SceneSelectionPage";
1520
import theme from "./theme/App.theme";
1621

@@ -50,6 +55,18 @@ export default function App() {
5055
<PlayScenarioResolver />
5156
</ProtectedRoute>
5257

58+
<ProtectedRoute path="/scenario-info">
59+
<ScenarioContextProvider>
60+
<ScenarioInfo />
61+
</ScenarioContextProvider>
62+
</ProtectedRoute>
63+
64+
<ProtectedRoute path="/play-page">
65+
<ScenarioContextProvider>
66+
<PlayPage />
67+
</ScenarioContextProvider>
68+
</ProtectedRoute>
69+
5370
<ProtectedRoute exact path="/">
5471
<ScenarioContextProvider>
5572
<ScenarioSelectionPage />
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { useState, useContext } from "react";
2+
import { useHistory } from "react-router-dom";
3+
import "./playPage.css";
4+
import HorizontalGradientLine from "./components/HorizontalGradientLine";
5+
import Thumbnail from "../authoring/components/Thumbnail";
6+
import ScenarioContext from "../../context/ScenarioContext";
7+
8+
function PlayPage() {
9+
const [searchTerm, setSearchTerm] = useState("");
10+
const history = useHistory();
11+
const scenarioContext = useContext(ScenarioContext);
12+
13+
const scenarios = scenarioContext?.scenarios || [];
14+
15+
const filteredScenarios = scenarios.filter((scenario) =>
16+
scenario.name.toLowerCase().includes(searchTerm.toLowerCase())
17+
);
18+
19+
const handleScenarioPlay = (scenario) => {
20+
history.push(`/scenario-info?id=${scenario._id}`);
21+
};
22+
23+
return (
24+
<div className="play-container" data-theme="dark">
25+
{/* Top Navigation Bar */}
26+
<div className="top-nav-bar">
27+
<div className="nav-left">
28+
<button className="logout-btn" onClick={() => history.push("/")}>
29+
<svg
30+
className="logout-icon"
31+
xmlns="http://www.w3.org/2000/svg"
32+
viewBox="0 0 24 24"
33+
>
34+
<path
35+
fill="currentColor"
36+
d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"
37+
/>
38+
</svg>
39+
<span>Logout</span>
40+
</button>
41+
</div>
42+
<div className="nav-right">
43+
<button className="nav-btn nav-btn-active">Play</button>
44+
<button className="nav-btn">Create</button>
45+
</div>
46+
</div>
47+
48+
{/* Header */}
49+
<div className="play-header">
50+
<h1 className="play-title text-white font-light">Play</h1>
51+
</div>
52+
53+
{/* Search Section */}
54+
<div className="search-section">
55+
<div className="search-container-play">
56+
<label className="search-input-wrapper-play flex items-center">
57+
<svg
58+
className="search-icon-play"
59+
xmlns="http://www.w3.org/2000/svg"
60+
viewBox="0 0 24 24"
61+
>
62+
<g
63+
strokeLinejoin="round"
64+
strokeLinecap="round"
65+
strokeWidth="2.5"
66+
fill="none"
67+
stroke="currentColor"
68+
>
69+
<circle cx="11" cy="11" r="8"></circle>
70+
<path d="m21 21-4.3-4.3"></path>
71+
</g>
72+
</svg>
73+
<input
74+
type="search"
75+
placeholder="Search scenario"
76+
value={searchTerm}
77+
onChange={(e) => setSearchTerm(e.target.value)}
78+
className="search-input-play flex-1"
79+
required
80+
/>
81+
</label>
82+
</div>
83+
</div>
84+
85+
{/* Gradient Line */}
86+
<div className="gradient-line-container-play">
87+
<HorizontalGradientLine />
88+
</div>
89+
90+
{/* Scenarios Grid */}
91+
<div className="scenarios-grid">
92+
{filteredScenarios.map((scenario) => (
93+
<div
94+
key={scenario._id}
95+
className="scenario-card"
96+
onClick={() => handleScenarioPlay(scenario)}
97+
>
98+
{/* Scenario Thumbnail */}
99+
<div className="scenario-card-thumbnail">
100+
<Thumbnail components={scenario.thumbnail?.components || []} />
101+
</div>
102+
103+
{/* Scenario Name */}
104+
<div className="scenario-card-name">
105+
<p className="scenario-name-text">{scenario.name}</p>
106+
</div>
107+
</div>
108+
))}
109+
</div>
110+
</div>
111+
);
112+
}
113+
114+
export default PlayPage;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* Horizontal Gradient Line Styles */
2+
.horizontal-gradient-line {
3+
width: 100%;
4+
height: 1px;
5+
background: linear-gradient(
6+
to right,
7+
transparent 0%,
8+
#ffffff 50%,
9+
transparent 100%
10+
);
11+
margin: 2vh 0;
12+
opacity: 0.3;
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import "./HorizontalGradientLine.css";
2+
3+
const HorizontalGradientLine = () => {
4+
return <div className="horizontal-gradient-line" />;
5+
};
6+
7+
export default HorizontalGradientLine;

0 commit comments

Comments
 (0)