Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions my-app/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import { JsonToDatabase } from "./presenters/Tests/JsonToDatabase";
import { AllCoursesPresenter } from "./presenters/Tests/AllCoursesPresenter.jsx";


configure({ enforceActions: "observed", reactionScheduler: (f) => setTimeout(f, 0),});
/**
* This file contains the bootstrapping, as well as the router used in our webapp.
*/

configure({ enforceActions: "observed", reactionScheduler: (f) => setTimeout(f, 0),});
const reactiveModel = makeAutoObservable(model);
connectToFirebase(reactiveModel);

Expand All @@ -27,20 +30,21 @@ export function makeRouter(reactiveModel) {
//element: <SharedView />,
element: <SharedView model={reactiveModel} />,
},
{
path: "/button",
element: <JsonToDatabase model={reactiveModel} />,
},
{
path: "/all",
element: <AllCoursesPresenter model={reactiveModel} />,
},
// Testcases, which are disabled for deployment:
// {
// path: "/button",
// element: <JsonToDatabase model={reactiveModel} />,
// },
// {
// path: "/all",
// element: <AllCoursesPresenter model={reactiveModel} />,
// },

]);
}

createRoot(document.getElementById("root")).render(
<RouterProvider router={makeRouter(reactiveModel)} />
);

window.myModel = reactiveModel;
// give user access for debugging purpose
// window.myModel = reactiveModel;
72 changes: 40 additions & 32 deletions my-app/src/pages/App.jsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,71 @@
import React, { useState } from 'react';
import { SidebarPresenter } from '../presenters/SidebarPresenter.jsx';
import { SearchbarPresenter } from '../presenters/SearchbarPresenter.jsx';
import { ListViewPresenter } from '../presenters/ListViewPresenter.jsx';
import React, { useState } from "react";
import { SidebarPresenter } from "../presenters/SidebarPresenter.jsx";
import { SearchbarPresenter } from "../presenters/SearchbarPresenter.jsx";
import { ListViewPresenter } from "../presenters/ListViewPresenter.jsx";
import { FilterPresenter } from "../presenters/FilterPresenter.jsx";
import { slide as Menu } from 'react-burger-menu';


import { slide as Menu } from "react-burger-menu";

/**
* Contains the root of our one-page app.
* All root elements (Menu, Sidebar, Filter, List, Searchbar) are initialized here.
*
* @param {object} model The reactive model used in the application
* @returns
*/
function App({ model }) {
const [sidebarIsOpen, setSidebarIsOpen] = useState(model.sidebarIsOpen);

return (
/* The sidebar styling(under the menu)*/
/* The sidebar styling(under the menu)*/
<div className=" flex h-screen w-screen bg-gradient-to-t from-[#4f3646] to-[#6747c0] overflow-hidden">
{ /* If sidebar is open, set length to 400px, else it should not be visible */}
<div className={`${sidebarIsOpen ? 'w-[400px] min-w-[300px]' : 'w-[50px]'}`}>
{/* If sidebar is open, set length to 400px, else it should not be visible */}
<div
className={`${sidebarIsOpen ? "w-[400px] min-w-[300px]" : "w-[50px]"}`}
>
<Menu
width={window.innerWidth<700?'100%':Math.max(window.innerWidth * 0.26, 300)}
width={
window.innerWidth < 700
? "100%"
: Math.max(window.innerWidth * 0.26, 300)
}
isOpen={sidebarIsOpen}
onStateChange={(state) => setSidebarIsOpen(state.isOpen)}
className="bg-gradient-to-t from-[#4f3646] to-[#6747c0] z-0 "
className="bg-gradient-to-t from-[#4f3646] to-[#6747c0] z-0 "
noOverlay
styles={{
bmMenuWrap: {
zIndex: '10'
zIndex: "10",
},
bmBurgerButton: {
position: 'absolute',
top: '20px',
left: '8px',
width: '36px',
height: '30px',
zIndex: '20'
position: "absolute",
top: "20px",
left: "8px",
width: "36px",
height: "30px",
zIndex: "20",
},
}}
customBurgerIcon={ <img src="https://img.icons8.com/ios-filled/50/ffffff/menu-2.png" /> }
customBurgerIcon={
<img src="https://img.icons8.com/ios-filled/50/ffffff/menu-2.png" />
}
>
<SidebarPresenter model={model}/>
<SidebarPresenter model={model} />
</Menu>
</div>



<div className="flex-1 h-full flex flex-col ">


<div className="flex items-center bg-gradient-to-t from-[#6246a8] to-[#6747c0] text-white">
<SearchbarPresenter model={model}/>
<SearchbarPresenter model={model} />
</div>


<div
className="flex sm:flex-auto h-screen w-screen sm:w-full bg-gradient-to-t from-[#4f3646] to-[#6747c0] overflow-hidden">
<ListViewPresenter model={model}/>
<div className="flex sm:flex-auto h-screen w-screen sm:w-full bg-gradient-to-t from-[#4f3646] to-[#6747c0] overflow-hidden">
<ListViewPresenter model={model} />
</div>

<FilterPresenter model={model}/>
<FilterPresenter model={model} />
</div>
</div>)
</div>
);
}

export default App;
98 changes: 25 additions & 73 deletions my-app/src/pages/SharedView.jsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,39 @@
/*import React, { useEffect } from "react";
import { model } from "../model";

/**
* Reads favs from URL hash query string and populates model.favourites

function SharedView() {
useEffect(() => {
const processFavouritesFromURL = () => {
const hash = window.location.hash;
const queryString = hash.includes("?") ? hash.split("?")[1] : "";
const params = new URLSearchParams(queryString);
const favCodes = (params.get("favs") || "").split(",").filter(Boolean);

console.log("Parsed fav codes:", favCodes);

if (!model.courses || model.courses.length === 0) {
console.warn("Courses not yet loaded, waiting...");
return;
}

console.log("Courses loaded. Processing shared favourites.");

const favCourses = favCodes
.map(code => model.getCourse(code))
.filter(course => course !== undefined);

model.favourites = favCourses;

console.log("Updated model.favourites:", favCourses);
};

const interval = setInterval(() => {
if (model.courses && model.courses.length > 0) {
processFavouritesFromURL();
clearInterval(interval); // Stop polling once done
}
}, 200); // Poll every 200ms until courses are ready

return () => clearInterval(interval);
}, []);

return (
<div className="text-center text-xl p-6 text-white">
<p>This is a shared view of someone's favourite courses.</p>
<p>Click the <b>Favourites</b> button to see the list!</p>
</div>
);
}

export default SharedView;
*/
* The SharedView processes a URL containing a favourite selection
* and starts the main app with the given parameters.
*/

import React, { useEffect } from "react";
import MainAppLayout from "./App.jsx"; // or wherever it's defined
import App from "./App.jsx";

function SharedView({ model }) {
useEffect(() => {
const processFavouritesFromURL = () => {
const hash = window.location.hash;
const queryString = hash.includes("?") ? hash.split("?")[1] : "";
const params = new URLSearchParams(queryString);
const favCodes = (params.get("favs") || "").split(",").filter(Boolean);
useEffect(() => {
const processFavouritesFromURL = () => {
const hash = window.location.hash;
const queryString = hash.includes("?") ? hash.split("?")[1] : "";
const params = new URLSearchParams(queryString);
const favCodes = (params.get("favs") || "").split(",").filter(Boolean);

if (!model.courses || model.courses.length === 0) return;
if (!model.courses || model.courses.length === 0) return;

const favCourses = favCodes
.map(code => model.getCourse(code))
.filter(Boolean);
const favCourses = favCodes
.map((code) => model.getCourse(code))
.filter(Boolean);

model.favourites = favCourses;
};
model.favourites = favCourses;
};

const interval = setInterval(() => {
if (model.courses && model.courses.length > 0) {
processFavouritesFromURL();
clearInterval(interval);
}
}, 200);
const interval = setInterval(() => {
if (model.courses && model.courses.length > 0) {
processFavouritesFromURL();
clearInterval(interval);
}
}, 200);

return () => clearInterval(interval);
}, [model]);
return () => clearInterval(interval);
}, [model]);

return <MainAppLayout model={model} />;
return <App model={model} />;
}

export default SharedView;
Loading