diff --git a/static/frontend/src/App.css b/static/frontend/src/App.css index b9d355d..2d6ca43 100644 --- a/static/frontend/src/App.css +++ b/static/frontend/src/App.css @@ -1,42 +1,74 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; +.glass { + background: rgba(31, 41, 55, 0.2); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(147, 51, 234, 0.3); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; } -.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); +.glass:hover { + background: rgba(147, 51, 234, 0.2); + border-color: rgba(147, 51, 234, 0.7); + box-shadow: 0 6px 12px rgba(147, 51, 234, 0.3); } -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +.glass_enabled { + background: rgba(30, 240, 135, 0.2); + border-color: rgba(51, 234, 127, 0.7); + box-shadow: 0 6px 12px rgba(30, 240, 135, 0.3); } -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } +.gauge { + width: 64px; + height: 64px; + background: conic-gradient(#a758f1 0deg, #7f0bfa var(--value), #4b5563 0); + box-shadow: 0 6px 12px rgba(147, 51, 234, 0.3); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + position: relative; } -.card { - padding: 2em; +.gauge::before { + content: ''; + width: 48px; + height: 48px; + background: rgba(31, 41, 55, 0.9); + border-radius: 50%; + position: absolute; } -.read-the-docs { - color: #888; +.gauge span { + color: #d8b4fe; + font-size: 12px; + font-weight: bold; + z-index: 1; } + +.clock, +.number { + font-size: 3rem; + color: #d8b4fe; + font-weight: bold; +} + +input[type="range"] { + accent-color: #9333ea; + box-shadow: 0 6px 12px rgba(147, 51, 234, 0.3); +} + +.tile-content { + min-height: 12rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 1rem; +} + +.gauge-tile-content { + justify-content: flex-start; + align-items: flex-start; +} \ No newline at end of file diff --git a/static/frontend/src/App.jsx b/static/frontend/src/App.jsx index f67355a..99a21c1 100644 --- a/static/frontend/src/App.jsx +++ b/static/frontend/src/App.jsx @@ -1,35 +1,203 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' +import { useState, useEffect } from 'react' import './App.css' -function App() { - const [count, setCount] = useState(0) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) -} +const TextTile = ({ label }) => ( +
+ {label} +
+); + +const ImageTile = ({ imageUrl, label }) => ( +
+ {label} + {label} +
+); + +const ClockTile = ({ id }) => { + const [time, setTime] = useState(""); + const [date, setDate] = useState(""); + + useEffect(() => { + const updateClock = () => { + const now = new Date(); + const time = now.toLocaleTimeString('ru-RU', { hour12: false }); + const options = { weekday: 'short', day: 'numeric', month: 'long', year: 'numeric' }; + const date = now.toLocaleDateString('ru-RU', options) + .replace(/,/, '') + .replace(/(\d+)\s(\S+)/, '$1 $2') + .replace(/г\./, ''); + setTime(time); + setDate(date.charAt(0).toUpperCase() + date.slice(1)); + }; + updateClock(); + const interval = setInterval(updateClock, 1000); + return () => clearInterval(interval); + }, []); + + return ( +
+ {time} + {date} +
+ ); +}; + +const GaugeTile = ({ gauges }) => ( +
+
+ {gauges.map((g, index) => ( +
+
+ {Math.round((g.value / 360) * 100)}% +
+ {g.label} +
+ ))} +
+
+); + +const SliderTile = ({ sliders, tileId, updateSliderValue }) => { + return ( +
+ Slider Controls +
+ {sliders.map((s, index) => ( +
+
+ {s.label} + {s.value} +
+ updateSliderValue(tileId, index, e.target.value)} + className="w-full" + /> +
+ ))} +
+
+ ); +}; + +const NumberTile = ({ value, label }) => ( +
+ {value} + {label} +
+); + +const App = () => { + const [tiles, setTiles] = useState([ + { id: 1, type: "text", label: "Tile 1" }, + { id: 2, type: "gauge", gauges: [{ value: 75, label: "Metric 1" }, { value: 120, label: "Metric 2" }] }, + { id: 3, type: "image", imageUrl: "https://cloud.viadev.su/storage/branding_media/d076be24-e369-41ca-9b24-5f94efc5ecfd.png", label: "Image 1" }, + { id: 4, type: "text", label: "Tile 4" }, + { id: 5, type: "image", imageUrl: "https://cloud.viadev.su/storage/branding_media/d076be24-e369-41ca-9b24-5f94efc5ecfd.png", label: "Image 2" }, + { id: 6, type: "clock" }, + { id: 7, type: "slider", sliders: [{ value: 50, label: "Slider 1" }, { value: 75, label: "Slider 2" }] }, + { id: 8, type: "text", label: "Tile 5 (Double)" }, + { id: 9, type: "text", label: "Tile 6" }, + { id: 10, type: "number", value: 42, label: "Data Point" } + ]); + + const updateGaugeValue = (tileId, gaugeIndex) => { + setTiles(prevTiles => + prevTiles.map(tile => { + if (tile.id === tileId && tile.type === "gauge") { + const newGauges = [...tile.gauges]; + newGauges[gaugeIndex] = { + ...newGauges[gaugeIndex], + value: (newGauges[gaugeIndex].value + 10) % 360 + }; + return { ...tile, gauges: newGauges }; + } + return tile; + }) + ); + }; + + const updateSliderValue = (tileId, sliderIndex, newValue) => { + setTiles(prevTiles => + prevTiles.map(tile => { + if (tile.id === tileId && tile.type === "slider") { + const newSliders = [...tile.sliders]; + newSliders[sliderIndex] = { + ...newSliders[sliderIndex], + value: parseInt(newValue) + }; + return { ...tile, sliders: newSliders }; + } + return tile; + }) + ); + }; + + const updateNumberValue = (tileId) => { + setTiles(prevTiles => + prevTiles.map(tile => { + if (tile.id === tileId && tile.type === "number") { + return { ...tile, value: tile.value + 1 }; + } + return tile; + }) + ); + }; + + return ( +
+
+ {tiles.map(tile => ( +
+ {tile.type === "text" && } + {tile.type === "image" && } + {tile.type === "clock" && } + {tile.type === "gauge" && } + {tile.type === "slider" && } + {tile.type === "number" && } + {(tile.type === "gauge" || tile.type === "slider" || tile.type === "number") && ( +
+ {tile.type === "gauge" && tile.gauges.map((_, index) => ( + + ))} + {tile.type === "slider" && tile.sliders.map((_, index) => ( + + ))} + {tile.type === "number" && ( + + )} +
+ )} +
+ ))} +
+
+ ); +}; export default App diff --git a/static/frontend/src/main.jsx b/static/frontend/src/main.jsx index b9a1a6d..0311944 100644 --- a/static/frontend/src/main.jsx +++ b/static/frontend/src/main.jsx @@ -4,7 +4,7 @@ import './index.css' import App from './App.jsx' createRoot(document.getElementById('root')).render( - + - , + )