Compare commits
6 Commits
ff89115f36
...
ui-update
| Author | SHA1 | Date | |
|---|---|---|---|
| f40ab6eb63 | |||
| 7f8fee99ce | |||
| db934eb9e7 | |||
| 0205916b0a | |||
| 64a33fcd31 | |||
| 17cc07a684 |
57
.devcontainer/devcontainer.json
Normal file
57
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "bun",
|
||||
"image": "oven/bun:debian",
|
||||
"privileged": true,
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/common-utils:1": {
|
||||
"version": "latest",
|
||||
"configureZshAsDefaultShell": true,
|
||||
"username": "bun",
|
||||
"userUid": "1000",
|
||||
"userGid": "1000"
|
||||
},
|
||||
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
|
||||
"packages": "stow,tmux,ripgrep,python3-venv,python3-virtualenv"
|
||||
},
|
||||
},
|
||||
"remoteUser": "bun",
|
||||
"workspaceFolder": "/home/bun/ws",
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bun/ws,type=bind",
|
||||
"containerEnv": {
|
||||
},
|
||||
"runArgs": [
|
||||
"--net=host",
|
||||
"-e",
|
||||
"DISPLAY=${env:DISPLAY}",
|
||||
"-e",
|
||||
"TERM=${env:TERM}",
|
||||
"-e",
|
||||
"SHELL=${env:SHELL}",
|
||||
"-v",
|
||||
"${env:SSH_AUTH_SOCK}:/tmp/ssh-agent.socket",
|
||||
"-e",
|
||||
"SSH_AUTH_SOCK=/tmp/ssh-agent.socket"
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"source": "/tmp/.X11-unix",
|
||||
"target": "/tmp/.X11-unix",
|
||||
"type": "bind",
|
||||
"consistency": "cached"
|
||||
},
|
||||
{
|
||||
"source": "/dev/dri",
|
||||
"target": "/dev/dri",
|
||||
"type": "bind",
|
||||
"consistency": "cached"
|
||||
},
|
||||
{
|
||||
"source": "/dev/shm",
|
||||
"target": "/dev/shm",
|
||||
"type": "bind",
|
||||
"consistency": "cached"
|
||||
}
|
||||
],
|
||||
"postCreateCommand": {
|
||||
}
|
||||
}
|
||||
3
bun.lock
3
bun.lock
@@ -10,6 +10,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"react-redux": "^9.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"zustand": "^5.0.6",
|
||||
@@ -634,6 +635,8 @@
|
||||
|
||||
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
||||
|
||||
"react-fast-marquee": ["react-fast-marquee@1.6.5", "", { "peerDependencies": { "react": ">= 16.8.0 || ^18.0.0", "react-dom": ">= 16.8.0 || ^18.0.0" } }, "sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg=="],
|
||||
|
||||
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"react-redux": "^9.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"zustand": "^5.0.6"
|
||||
|
||||
39
src/App.css
39
src/App.css
@@ -1,42 +1,7 @@
|
||||
#root {
|
||||
height: 100vh;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import "@/App.css";
|
||||
import Flatastic from "@/components/Flatastic/Flatastic";
|
||||
import HomeAssistant from "@/components/HomeAssistant/HomeAssistant";
|
||||
import Timetable from "@/components/Timetable/Timetable";
|
||||
|
||||
import Dashboard from "@/components/Dashboard/Dashboard";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<Timetable />
|
||||
<Flatastic />
|
||||
<HomeAssistant />
|
||||
<Dashboard />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
28
src/components/Dashboard/Dashboard.tsx
Normal file
28
src/components/Dashboard/Dashboard.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import Flatastic from "@/components/Flatastic/Flatastic";
|
||||
import HomeAssistant from "@/components/HomeAssistant/HomeAssistant";
|
||||
import Timetable from "@/components/Timetable/Timetable";
|
||||
import Footer from "@/components/Footer/Footer";
|
||||
|
||||
import style from "./style.module.css";
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<div className={style.dashboard}>
|
||||
<div className={style.cardWrapper}>
|
||||
<div className={style.card}>
|
||||
<Timetable />
|
||||
</div>
|
||||
<div className={style.card}>
|
||||
<Flatastic />
|
||||
</div>
|
||||
<div className={style.card}>
|
||||
<HomeAssistant />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={style.footer}>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
25
src/components/Dashboard/style.module.css
Normal file
25
src/components/Dashboard/style.module.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cardWrapper {
|
||||
margin: 30px;
|
||||
height: 100%;
|
||||
gap: 30px;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 10px;
|
||||
padding: 1px 100px 30px 100px;
|
||||
border: 1px solid rgba(220, 220, 220, 0.4);
|
||||
box-shadow: 5px 5px 7px rgba(220, 220, 220, 0.5);
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: rgba(220, 220, 220, 0.5);
|
||||
}
|
||||
@@ -58,11 +58,13 @@ export default function DepartureList(props: {
|
||||
|
||||
return (
|
||||
<div className={style.container}>
|
||||
<h2>{name} Departures</h2>
|
||||
<div className={style.heading}>
|
||||
<h2>{name}</h2>
|
||||
</div>
|
||||
<div className={style.departureLists}>
|
||||
{departureTables(left)}
|
||||
{departureTables(right)}
|
||||
</div>
|
||||
{departureTables(left)}
|
||||
{departureTables(right)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
border: 1px solid rgba(220, 220, 220, 0.4);
|
||||
box-shadow: 5px 5px 7px rgba(220, 220, 220, 0.5);
|
||||
border-radius: 10px;
|
||||
padding: 0 10px 20px 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.departureLists {
|
||||
@@ -9,4 +14,9 @@
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 120px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useEffect } from "react";
|
||||
|
||||
import type { FlatasticChore } from "@/types/flatasticChore";
|
||||
|
||||
import style from "./style.module.css";
|
||||
|
||||
const idToNameMap: Record<number, string> = {
|
||||
1836104: "Gruber",
|
||||
1836101: "Darius",
|
||||
@@ -25,11 +27,13 @@ export default function Flatastic() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Flatastic Chores</h1>
|
||||
<ul>
|
||||
<ul className={style.choreList}>
|
||||
{chores.map((chore: FlatasticChore) => (
|
||||
<li key={chore.id} style={{ textAlign: "left" }}>
|
||||
{idToNameMap[chore.currentUser]}: {chore.title} -
|
||||
Points: {chore.points}
|
||||
<li key={chore.id} className={style.chore}>
|
||||
<span className={style.userName}>
|
||||
{idToNameMap[chore.currentUser]}
|
||||
</span>
|
||||
: {chore.title} - {"🪙".repeat(chore.points)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
.choreList {
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
border-radius: 10px;
|
||||
gap: 10px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.chore {
|
||||
padding: 5px 10px;
|
||||
border: 1px solid rgba(220, 220, 220, 0.4);
|
||||
box-shadow: 5px 5px 7px rgba(220, 220, 220, 0.5);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.userName {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
17
src/components/Footer/Footer.tsx
Normal file
17
src/components/Footer/Footer.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import Marquee from "react-fast-marquee";
|
||||
|
||||
import style from "./style.module.css";
|
||||
|
||||
export default function Footer() {
|
||||
let pasta =
|
||||
'Ok, so I was playing the hit game Among Us the other day, and when the game started, a red bean-shaped character that appeared to be wearing a spacesuit told me "shh," while having his index finger in front of where his mouth should be. I believe this act made this red bean character extremely suspicious. To understand why this red bean character is suspicious, we first must understand how the game “Among Us” works. The game consists of 10 bean-shaped characters, called crewmates, that are given tasks for them to complete. As these characters do their tasks, they may witness abnormal things that are not supposed to happen, such as the lights turning off on their own, sudden reactor meltdown and other crewmates dying. These acts show that there is an imposter among the crewmates that is sabotaging and is trying to kill everyone. Now why is this important to determine why the red bean is suspicious? Well now we know how the game works, now we must analyze the red bean’s actions. At the beginning of the game, the red bean tells us “shh” while having his index finger in front of where his mouth should be. This action suggests that the red bean wants us to be quiet, or keep our mouths shut. Now why would the red bean want us to do this? This could be because the red bean wants to limit our communication in order to prevent us from spreading information. What information does the red bean want to prevent from spreading? We can assume that the reason why the red bean wants to prevent us from spreading information, is because he is actually the imposter, and he is planning on committing the crimes mentioned earlier. He does not want others to find out about actions he will cause, therefore he does not want us to communicate with each other. This concludes the reason for why I believe the red bean from the hit game Among Us is suspicious. So if you happen to see a red bean-shaped character wearing a spacesuit, please be careful.';
|
||||
|
||||
return (
|
||||
<div className={style.container}>
|
||||
<div className={style.info}>BREAKING</div>
|
||||
<div className={style.marquee}>
|
||||
<Marquee>{pasta}</Marquee>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
src/components/Footer/style.module.css
Normal file
17
src/components/Footer/style.module.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: red;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.marquee {
|
||||
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
import { useEffect } from "react";
|
||||
import { useHomeAssistantStore } from "@/store/homeAssistant";
|
||||
|
||||
import style from "./style.module.css";
|
||||
|
||||
export default function Timetable() {
|
||||
const fetchHomeAssistantData = useHomeAssistantStore((state) => state.fetch);
|
||||
const tentTemperature = useHomeAssistantStore((state) => state.tentTemperature);
|
||||
const fetchHomeAssistantData = useHomeAssistantStore(
|
||||
(state) => state.fetch,
|
||||
);
|
||||
const tentTemperature = useHomeAssistantStore(
|
||||
(state) => state.tentTemperature,
|
||||
);
|
||||
const tentHumidity = useHomeAssistantStore((state) => state.tentHumidity);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -17,8 +23,17 @@ export default function Timetable() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Tent</h1>
|
||||
<p>Temperature: {tentTemperature}°C</p>
|
||||
<p>Humidity: {tentHumidity}%</p>
|
||||
<div className={style.cardContainer}>
|
||||
<div className={style.card}>
|
||||
<h4>Temperature</h4>
|
||||
<p>{tentTemperature}°C</p>
|
||||
</div>
|
||||
|
||||
<div className={style.card}>
|
||||
<h4>Humidity</h4>
|
||||
<p>{tentHumidity}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
18
src/components/HomeAssistant/style.module.css
Normal file
18
src/components/HomeAssistant/style.module.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.cardContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border: 1px solid rgba(220, 220, 220, 0.4);
|
||||
box-shadow: 5px 5px 7px rgba(220, 220, 220, 0.5);
|
||||
border-radius: 10px;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import { useEffect } from "react";
|
||||
import DepartureList from "@/components/DepartureList/DepartureList";
|
||||
import { useKVVStore } from "@/store/kvv";
|
||||
|
||||
import style from "./style.module.css";
|
||||
|
||||
export default function Timetable() {
|
||||
const fetchTimetable = useKVVStore((state) => state.fetch);
|
||||
const pStreet = useKVVStore((state) => state.pStreet);
|
||||
@@ -16,8 +18,8 @@ export default function Timetable() {
|
||||
}, [fetchTimetable]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Timetable</h1>
|
||||
<div className={style.wrapper}>
|
||||
<h1>Timetable 🚉</h1>
|
||||
<DepartureList departures={pStreet.departureList} name="P-Street" />
|
||||
<DepartureList departures={hStreet.departureList} name="H-Street" />
|
||||
</div>
|
||||
|
||||
@@ -10,12 +10,35 @@ export default function TimetableRow({
|
||||
const hour = String(departure.dateTime.hour).padStart(2, "0");
|
||||
const minute = String(departure.dateTime.minute).padStart(2, "0");
|
||||
const dateTimeString = `${hour}:${minute}`;
|
||||
const lineNumber = departure.servingLine.number;
|
||||
|
||||
return (
|
||||
<tr className={styles.timetableRow}>
|
||||
<td>{dateTimeString}</td>
|
||||
<td>{departure.servingLine.number}</td>
|
||||
<td>({departure.servingLine.direction})</td>
|
||||
<td className={styles.departureTime}>{dateTimeString}</td>
|
||||
<td
|
||||
className={`${lineNumberToStyle(lineNumber)} ${styles.lineNumber}`}
|
||||
>
|
||||
{lineNumber}
|
||||
</td>
|
||||
<td>{departure.servingLine.direction}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
const lineNumberToStyle = (number) => {
|
||||
switch (number) {
|
||||
case "S2":
|
||||
return styles.S2;
|
||||
break;
|
||||
case "S5":
|
||||
case "S51":
|
||||
return styles.S5;
|
||||
break;
|
||||
case "S1":
|
||||
case "S11":
|
||||
return styles.S1;
|
||||
break;
|
||||
default:
|
||||
return styles.lineNumberDefault;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
td {
|
||||
padding: 4px 7px;
|
||||
}
|
||||
|
||||
.timetableRow {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.departureTime {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lineNumber {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.S2 {
|
||||
background-color: rgba(200, 100, 200, 0.2);
|
||||
}
|
||||
|
||||
.S5 {
|
||||
background-color: rgba(230, 20, 20, 0.2);
|
||||
}
|
||||
|
||||
.S1 {
|
||||
background-color: rgba(20, 180, 90, 0.2);
|
||||
}
|
||||
|
||||
.lineNumberDefault {
|
||||
background-color: rgba(100, 100, 100, 0.2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user