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