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..30ebfad1706fc85e329896fa49d45b826e252bc1 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,36 @@ } }, { - "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." - } + "config": {} }, { - "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/public/content/clock.svg b/public/content/clock.svg new file mode 100644 index 0000000000000000000000000000000000000000..defbe19817aa47576e85323c8cd6a6a24c0b3f79 --- /dev/null +++ b/public/content/clock.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M12 7V12L9.5 13.5M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +</svg> \ No newline at end of file diff --git a/public/content/location-pin.svg b/public/content/location-pin.svg new file mode 100644 index 0000000000000000000000000000000000000000..379f9740987bec6fa50eeda2d76008a78f2e7bdf --- /dev/null +++ b/public/content/location-pin.svg @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M12 21C15.5 17.4 19 14.1764 19 10.2C19 6.22355 15.866 3 12 3C8.13401 3 5 6.22355 5 10.2C5 14.1764 8.5 17.4 12 21Z" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M12 13C13.6569 13 15 11.6569 15 10C15 8.34315 13.6569 7 12 7C10.3431 7 9 8.34315 9 10C9 11.6569 10.3431 13 12 13Z" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +</svg> \ No newline at end of file diff --git a/src/panels/Gremium/GremiumPanel.tsx b/src/panels/Gremium/GremiumPanel.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1b6883f04154d51fc68ab05b961d405956afb2f2 --- /dev/null +++ b/src/panels/Gremium/GremiumPanel.tsx @@ -0,0 +1,86 @@ +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"; + +export type GremiumPanelDefinition = { +} + +type Gremium = { + name: string, + description: string, + link: string, + time: string, + location: string +} + +const gremien: Gremium[] = [ + { + 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)" + } +] + +const GremiumPanel = (props: {definition: GremiumPanelDefinition}) => { + const [gremium, setGremium] = useState<Gremium>(gremien[0]); + const cycle = useRef<number>(0); + + useEffect(() => { + const update = async () => { + setGremium(gremien[cycle.current++ % 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"}> + <img className={"inline h-5 mr-2"} src={"/content/clock.svg"} alt={""}/> + {gremium.time} + </p> + <p className={"text-sm text-gray-400"}> + <img className={"inline h-5 mr-2"} src={"/content/location-pin.svg"} alt={""}/> + {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