6 changed files with 1371 additions and 0 deletions
@ -0,0 +1,26 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="cs" data-bs-theme="dark"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>MakerFest Dashboard - SŠ Štětí</title> |
||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> |
||||
|
<style> |
||||
|
body { background-color: #121212; color: #e0e0e0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } |
||||
|
.weather-card { |
||||
|
background: linear-gradient(145deg, #1e1e1e, #252525); |
||||
|
border: 1px solid #333; |
||||
|
border-radius: 15px; |
||||
|
transition: all 0.3s ease; |
||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.5); |
||||
|
} |
||||
|
.weather-card:hover { transform: translateY(-5px); border-color: #0d6efd; box-shadow: 0 8px 25px rgba(13, 110, 253, 0.3); } |
||||
|
.temp-display { font-size: 3.5rem; font-weight: bold; } |
||||
|
.glow-blue { box-shadow: 0 0 15px rgba(0, 123, 255, 0.2); } |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<script type="module" src="/src/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
||||
File diff suppressed because it is too large
@ -0,0 +1,18 @@ |
|||||
|
{ |
||||
|
"name": "makerfest-dashboard", |
||||
|
"version": "1.0.0", |
||||
|
"private": true, |
||||
|
"scripts": { |
||||
|
"dev": "vite", |
||||
|
"build": "vite build", |
||||
|
"preview": "vite preview" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"vue": "^3.4.0", |
||||
|
"bootstrap": "^5.3.3" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@vitejs/plugin-vue": "^5.0.0", |
||||
|
"vite": "^5.0.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
<template> |
||||
|
<div class="container-fluid py-4 min-vh-100 d-flex flex-column"> |
||||
|
<header class="text-center mb-5"> |
||||
|
<h1 class="display-4 fw-bold text-primary">MakerFest 2026</h1> |
||||
|
<p class="lead">SŠ Štětí & PaperBox Robotic Team</p> |
||||
|
<div class="badge bg-secondary">Aktualizace každých 5 minut | Poslední: {{ lastUpdate }}</div> |
||||
|
</header> |
||||
|
|
||||
|
<main class="row g-4 flex-grow-1"> |
||||
|
<div v-for="city in cities" :key="city.id" class="col-md-4"> |
||||
|
<div class="weather-card h-100 p-4 d-flex flex-column align-items-center text-center"> |
||||
|
<h2 class="h1 mb-3">{{ city.name }}</h2> |
||||
|
<div v-if="city.data"> |
||||
|
<img :src="'https://openweathermap.org/img/wn/' + city.data.weather[0].icon + '@4x.png'" :alt="city.data.weather[0].description"> |
||||
|
<div class="temp-display">{{ Math.round(city.data.main.temp) }}°C</div> |
||||
|
<p class="h4 text-capitalize">{{ city.data.weather[0].description }}</p> |
||||
|
|
||||
|
<div class="mt-4 w-100 border-top pt-3 row"> |
||||
|
<div class="col-6"> |
||||
|
<small class="text-muted d-block">Vlhkost</small> |
||||
|
<strong>{{ city.data.main.humidity }}%</strong> |
||||
|
</div> |
||||
|
<div class="col-6"> |
||||
|
<small class="text-muted d-block">Vítr</small> |
||||
|
<strong>{{ city.data.wind.speed }} m/s</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
<p class="mt-3 mb-0 small text-muted">Pocitově: {{ Math.round(city.data.main.feels_like) }}°C</p> |
||||
|
</div> |
||||
|
<div v-else class="spinner-border text-primary my-auto" role="status"> |
||||
|
<span class="visually-hidden">Načítání...</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</main> |
||||
|
|
||||
|
<footer class="text-center mt-5 text-muted"> |
||||
|
<small>© 2026 SŠ Štětí - Robotický tým PaperBox</small> |
||||
|
</footer> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, onUnmounted } from 'vue' |
||||
|
|
||||
|
const API_KEY = 'f70d944f638a7ae77de89435d4d23c01' |
||||
|
const lastUpdate = ref(new Date().toLocaleTimeString()) |
||||
|
|
||||
|
const cities = ref([ |
||||
|
{ id: 'steti', name: 'Štětí', query: 'Steti,CZ', data: null }, |
||||
|
{ id: 'usti', name: 'Ústí nad Labem', query: 'Usti nad Labem,CZ', data: null }, |
||||
|
{ id: 'praha', name: 'Praha', query: 'Prague,CZ', data: null } |
||||
|
]) |
||||
|
|
||||
|
async function fetchWeather() { |
||||
|
for (const city of cities.value) { |
||||
|
try { |
||||
|
const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city.query}&units=metric&lang=cz&appid=${API_KEY}`) |
||||
|
if (!response.ok) throw new Error(`Chyba pro ${city.name}`) |
||||
|
city.data = await response.json() |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
} |
||||
|
} |
||||
|
lastUpdate.value = new Date().toLocaleTimeString() |
||||
|
} |
||||
|
|
||||
|
let interval = null |
||||
|
onMounted(() => { |
||||
|
fetchWeather() |
||||
|
interval = setInterval(fetchWeather, 300000) // 5 minut |
||||
|
}) |
||||
|
|
||||
|
onUnmounted(() => { |
||||
|
if (interval) clearInterval(interval) |
||||
|
}) |
||||
|
</script> |
||||
@ -0,0 +1,4 @@ |
|||||
|
import { createApp } from 'vue' |
||||
|
import App from './App.vue' |
||||
|
|
||||
|
createApp(App).mount('#app') |
||||
@ -0,0 +1,8 @@ |
|||||
|
import { defineConfig } from 'vite' |
||||
|
import vue from '@vitejs/plugin-vue' |
||||
|
|
||||
|
export default defineConfig({ |
||||
|
plugins: [vue()], |
||||
|
root: './', |
||||
|
base: './' |
||||
|
}) |
||||
Loading…
Reference in new issue