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 + 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}
+
+);
+
+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(
-
+
- ,
+
)