diff --git a/package-lock.json b/package-lock.json index 604e4b0f06741ab46f3f2aa0af49da34a03e622c..b21831104580c92057ec06df33efe494847e2289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.11", + "react-qr-code": "^2.0.12", "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" @@ -14103,6 +14104,11 @@ "teleport": ">=0.2.0" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -14375,6 +14381,24 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-qr-code": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz", + "integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x", + "react-native-svg": "*" + }, + "peerDependenciesMeta": { + "react-native-svg": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -27194,6 +27218,11 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" }, + "qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -27393,6 +27422,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "react-qr-code": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz", + "integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==", + "requires": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", diff --git a/package.json b/package.json index 47cee39ab8067101fd715b2880427cd06da8954a..beab91d79680ec10b03665800703486f174b4142 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.11", + "react-qr-code": "^2.0.12", "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" diff --git a/public/config/default.json b/public/config/default.json index 32c2a3631b52998ee1759a64af8a24c9df10c660..6eea71170ea067368e3863ecd2a3f3ada1f9a743 100644 --- a/public/config/default.json +++ b/public/config/default.json @@ -1,7 +1,12 @@ { "id": "default", "schedule": { - "always": true + "times": [ + { + "from": "2023-12-19T21:30", + "to": "2023-12-20T21:00" + } + ] }, "panels": [ { @@ -29,44 +34,60 @@ } }, { - "type": "bild", + "type": "gremium", "position": { "x": 9, "y": 0, "w": 16, - "h": 5 + "h": 4 }, "config": { - "url": "/content/warning.png", - "title": "Achtung, Testbetrieb", - "description": "Wie du vielleicht siehst, sieht dieser Bildschirm heute anders aus. Dürfen wir vorstellen: Der neue Infoscreen. Bitte beachte allerdings, dass wir gerade einen Testbetrieb machen. Bitte melde daher Fehler und auch sonstige Wünsche an die Admins unter root@oh14.de." + "gremien": [ + { + "name": "FSR", + "description": "Fachschaftsrat", + "link": "https://oh14.de/fsr", + "time": "Mittwoch, 12:15 Uhr", + "location": "Chaos-Zentrum-Informatik (OH14 - E39)" + }, + { + "name": "FOSS-AG", + "description": "Free and Open Source Software - AG", + "link": "https://foss-ag.de/", + "time": "Montag, 18:00 Uhr", + "location": "Chaos-Zentrum-Informatik (OH14 - E39)" + }, + { + "name": "CyberSec-AG", + "description": "Cybersecurity - AG", + "link": "https://oh14.de/cybersec", + "time": "Donnerstag, 18:00 Uhr", + "location": "Chaos-Zentrum-Informatik (OH14 - E39)" + } + ] } }, { - "type": "placeholder", + "type": "callout", "position": { "x": 9, - "y": 5, - "w": 8, - "h": 2 - } - }, - { - "type": "placeholder", - "position": { - "x": 17, - "y": 5, - "w": 8, + "y": 4, + "w": 16, "h": 2 + }, + "config": { + "type": "warning", + "title": "Achtung, Testbetrieb", + "description": "Der neue Infoscreen ist da! Bitte melde Fehler an die Admins unter root@oh14.de." } }, { "type": "mensaplan", "position": { "x": 9, - "y": 7, + "y": 6, "w": 16, - "h": 6 + "h": 7 }, "config": { "canteenId": 341, diff --git a/src/panels/Gremium/GremiumPanel.tsx b/src/panels/Gremium/GremiumPanel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..265fe088a5b4d25ddf67275ffabb598e55b9b91c --- /dev/null +++ b/src/panels/Gremium/GremiumPanel.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useRef, useState } from 'react' +import PanelWrapper from "../../meta/PanelWrapper"; +import PanelContent from "../../meta/PanelContent"; +import PanelTitle from '../../meta/PanelTitle' +import QRCode from "react-qr-code"; +import { Clock, MapPin } from '@phosphor-icons/react' + +export type GremiumPanelDefinition = { + gremien: [Gremium] +} + +type Gremium = { + name: string, + description: string, + link: string, + time: string, + location: string +} + +const GremiumPanel = (props: {definition: GremiumPanelDefinition}) => { + const [gremium, setGremium] = useState<Gremium>(props.definition.gremien[0]); + const cycle = useRef<number>(0); + + useEffect(() => { + const update = async () => { + setGremium(props.definition.gremien[cycle.current++ % props.definition.gremien.length]); + console.log(gremium); + console.log(cycle.current); + } + + update(); + const interval = setInterval(update, 20 * 1000); + + return () => { + clearInterval(interval); + } + }, []); + + return ( + <PanelWrapper> + <PanelTitle title={gremium.name}/> + <PanelContent> + <div className={"relative h-full"}> + <div className={"absolute -top-12 right-0 p-1 bg-white rounded"}> + <QRCode value={gremium.link} className={"h-24 w-24"}/> + </div> + <div className={"absolute bottom-0"}> + <p>{gremium.description}</p> + <p className={"text-sm text-gray-400"}> + <Clock size={20} className={"inline mb-1.5 mr-1"}/> + {gremium.time} + </p> + <p className={"text-sm text-gray-400"}> + <MapPin size={20} className={"inline mb-1.5 mr-1"}/> + {gremium.location} + </p> + </div> + </div> + </PanelContent> + </PanelWrapper> + ); +}; + +export default GremiumPanel; diff --git a/src/panels/_Panels.tsx b/src/panels/_Panels.tsx index 9dc83023583285325ad169116ae576b09ef9c5b8..eadb79eb63c536855bbcc66d8d23e05db61fafd4 100644 --- a/src/panels/_Panels.tsx +++ b/src/panels/_Panels.tsx @@ -10,13 +10,14 @@ import PanelWrapper from "../meta/PanelWrapper"; import BildPanel from "./Bild/BildPanel"; import MensaplanPanel from "./Mensaplan/MensaplanPanel"; import CalloutPanel from "./Callout/CalloutPanel"; +import GremiumPanel from "./Gremium/GremiumPanel"; /* * First, please claim a unique id for your panel here. Convention is that it is all lowercase, in snake-case to be * precise. So if you want to call your panel "My awesome Panel", please claim "my-awesome-panel". Add it by adding * `| "my-awesome-panel"` before the semicolon in the type below this comment. */ -export type PanelTypes = "fahrplan" | "bild" | "mensaplan" | "callout"; +export type PanelTypes = "fahrplan" | "bild" | "mensaplan" | "callout" | "gremium"; /* * Next, add your renderer. You'll get the definition that was written in the layout config as a prop. If you'd like to @@ -28,6 +29,7 @@ export const PanelRenderers: {[panelType: string]: React.FC<any & {definition: P "bild": BildPanel, "mensaplan": MensaplanPanel, "callout": CalloutPanel, + "gremium": GremiumPanel, "placeholder": () => ( <PanelWrapper className={"flex flex-col items-center justify-center text-zinc-400"}> Dieses Panel wird noch entwickelt