init
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { Card } from '@3ba/vue';
|
||||
import Footer from "./components/Footer.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<router-view v-slot="{ Component,route }">
|
||||
<component :is="Component" />
|
||||
</router-view>
|
||||
</main>
|
||||
|
||||
<Footer>Scott 0-val</Footer>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import '../css/app.css'
|
||||
import "@3ba/styles";
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.mount('#app');
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
|
||||
export type AlertTone = "info" | "success" | "warn" | "danger";
|
||||
|
||||
const ICON: Record<AlertTone, string> = { info: "i", success: "✓", warn: "!", danger: "✕" };
|
||||
|
||||
const props = withDefaults(defineProps<{ tone?: AlertTone; title?: string, timestamp: string}>(), { tone: "info", timestamp: "1" });
|
||||
const icon = computed(() => ICON[props.tone]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="aero-alert" :class="`aero-alert-${tone}`">
|
||||
<div style="width: 100%;">
|
||||
<div style="display: flex; flex-direction: column; justify-content: space-between; width: 100%;">
|
||||
|
||||
<div v-if="title" style="display: flex; flex-direction: row; text-align: left; gap: 15px;" :style="{ fontWeight: 700, marginBottom: '2px' }" >
|
||||
|
||||
<div class="aero-alert-icon">{{ icon }}</div>
|
||||
{{ title }}
|
||||
</div>
|
||||
<div v-if="timestamp" :style="{ fontWeight: 700, marginBottom: '2px' }" style="text-align: left; color: gray">{{ timestamp }}</div>
|
||||
</div>
|
||||
<div :style="{ opacity: 0.9 }" style="text-align: left;"><slot /></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div v-if="this.day">
|
||||
<template v-if="tomorrow.length > 0">
|
||||
<h1>Ide kell menni éjfélkor inni:</h1>
|
||||
<ul>
|
||||
<li v-for="item in tomorrow">{{ item }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
<h1 v-else>Nem kötelező senkinél sem inni éjfélkor (de ajánlott)</h1>
|
||||
</div>
|
||||
<div v-else>
|
||||
<template v-if="today.length > 0">
|
||||
<h2>Ma itt kell inni:</h2>
|
||||
<ul >
|
||||
<li v-for="item in today">{{ item }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
<h2 v-else>Ma nem kell senkinél inni (de ajánlott)</h2>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
let interval;
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
today: [],
|
||||
tomorrow: [],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
day: {
|
||||
type: Boolean,
|
||||
default: true, //true -> tomorrow
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const call = () =>
|
||||
{
|
||||
axios
|
||||
.get('/api/fetch_rooms')
|
||||
.then(response => {
|
||||
this.tomorrow = response.data.tomorrow;
|
||||
this.today = response.data.today;
|
||||
});
|
||||
}
|
||||
call();
|
||||
interval = setInterval( call , 3000 );
|
||||
},
|
||||
unmounted() {
|
||||
clearInterval(interval)
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
li {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<ul class="vuejs-countdown">
|
||||
<li>
|
||||
<p class="digit">{{ hours }}</p>
|
||||
<p class="text">H</p>
|
||||
</li>
|
||||
<li>
|
||||
<p class="digit">{{ minutes }}</p>
|
||||
<p class="text">M</p>
|
||||
</li>
|
||||
<li>
|
||||
<p class="digit">{{ seconds }}</p>
|
||||
<p class="text">S</p>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let interval = null;
|
||||
|
||||
export default {
|
||||
name: 'vuejsCountDown',
|
||||
props: {
|
||||
deadline: {
|
||||
type: String
|
||||
},
|
||||
end: {
|
||||
type: String
|
||||
},
|
||||
stop: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
now: new Date,
|
||||
date: null,
|
||||
diff: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
interval = setInterval(() => {
|
||||
this.now = new Date;
|
||||
}, 500);
|
||||
},
|
||||
computed: {
|
||||
twoDigits(value) {
|
||||
if ( value.toString().length <= 1 ) {
|
||||
return '0'+value.toString()
|
||||
}
|
||||
else
|
||||
{
|
||||
return value.toString()
|
||||
}
|
||||
},
|
||||
|
||||
seconds() {
|
||||
let value = 60 - this.now.getSeconds() - 1
|
||||
return value < 10 ? '0' + value.toString() : value.toString()
|
||||
},
|
||||
|
||||
minutes() {
|
||||
let value = 60 - this.now.getMinutes() - 1
|
||||
return value < 10 ? '0' + value.toString() : value.toString()
|
||||
},
|
||||
|
||||
hours() {
|
||||
let value = 24 - this.now.getHours() - 1
|
||||
return value < 10 ? '0' + value.toString() : value.toString()
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.vuejs-countdown {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.vuejs-countdown li {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.vuejs-countdown li p {
|
||||
margin: 0;
|
||||
}
|
||||
.vuejs-countdown li:after {
|
||||
content: ":";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -13px;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
.vuejs-countdown li:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
.vuejs-countdown li:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
.vuejs-countdown li:last-of-type:after {
|
||||
content: "";
|
||||
}
|
||||
.vuejs-countdown .digit {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.vuejs-countdown .text {
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<footer>
|
||||
<slot>All rights reserved</slot>
|
||||
<template v-if="this.date">
|
||||
|
||||
• {{ this.year }}
|
||||
</template>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
new Date().getFullYear()
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
year: 0,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
date: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.year = new Date().getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
withDefaults(
|
||||
defineProps<{ brand?: string; hrefs?: string[], links?: string[]; active?: number }>(),
|
||||
{ brand: "ZO", hrefs: () => ["/home", "/", "/hearsay", "/legacy"], links: () => ["Haza", "Szülinapok", "Hírek", "Legacy"], active: 0 },
|
||||
);
|
||||
const emit = defineEmits<{ navigate: [index: number] }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="glass glass-static"
|
||||
:style="{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 18px 10px 14px', borderRadius: '999px', gap: '16px' }" >
|
||||
|
||||
<div :style="{ display: 'flex', gap: '24px', fontSize: '13px', fontWeight: 500 }">
|
||||
<a
|
||||
v-for="(l, i) in links"
|
||||
:key="l"
|
||||
:href="hrefs[i]"
|
||||
|
||||
class="aero-text-shadow"
|
||||
:style="{
|
||||
color: 'var(--t-text)',
|
||||
textDecoration: 'none',
|
||||
opacity: i === active ? 1 : 0.78,
|
||||
borderBottom: i === active ? '2px solid var(--t-accent-2)' : '2px solid transparent',
|
||||
paddingBottom: '2px',
|
||||
}"
|
||||
>{{ l }}</a
|
||||
>
|
||||
</div>
|
||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '12px' }">
|
||||
<div class="aero-text-shadow" :style="{ fontSize: '20px', fontWeight: 300, letterSpacing: '0.28em' }">{{ brand }}</div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<script setup>
|
||||
import { Card, Btn } from "@3ba/vue";
|
||||
import Alert from "../components/Alert.vue"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<template v-for="hearsay in news">
|
||||
<Alert style="display: flex; text-align: left;" :tone="info ? 'info' : 'warn'" :timestamp="hearsay.updated_at" :title="hearsay.title + ' (' + hearsay.author + ')'"
|
||||
v-if="(hearsay.type==='important' && important) || (hearsay.type==='info' && info)">{{ hearsay.body }}</Alert>
|
||||
</template>
|
||||
<h2 v-if="!has_important && important">Nincs semmi halaszthatatlan hír</h2>
|
||||
<h2 v-if="!has_info && info">Nincs semmi tájékoztató jellegű hír</h2>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
|
||||
let interval;
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
news: [],
|
||||
has_info: false,
|
||||
has_important: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
important: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
info: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const call = () =>
|
||||
{
|
||||
axios
|
||||
.get('/api/fetch_news')
|
||||
.then(response => {
|
||||
this.news = response.data;
|
||||
for (const i of response.data) {
|
||||
if (i.type=='info') this.has_info = true;
|
||||
if (i.type=='important') this.has_important = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
call();
|
||||
|
||||
interval = setInterval( call , 10000 );
|
||||
},
|
||||
unmounted() {
|
||||
clearInterval(interval);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import "@3ba/styles";
|
||||
import { AeroThemeProvider, AeroSky, Card, Btn } from "@3ba/vue";
|
||||
import Navbar from "../components/Navbar.vue"
|
||||
import Countdown from "../components/Countdown.vue";
|
||||
import BirthdayAlert from "../components/BirthdayAlert.vue";
|
||||
import Footer from "../components/Footer.vue";
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :style="{ minHeight: '100vh', padding: '32px', }">
|
||||
<div style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<Navbar :active="1"></Navbar>
|
||||
<Card>
|
||||
<BirthdayAlert></BirthdayAlert>
|
||||
</Card>
|
||||
<Card>
|
||||
<BirthdayAlert :day="false"></BirthdayAlert>
|
||||
</Card>
|
||||
<Card>
|
||||
<h1>Következő éjfélig hátralévő idő:</h1>
|
||||
<Countdown></Countdown>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { AeroThemeProvider, AeroSky, Card, Btn } from "@3ba/vue";
|
||||
import Navbar from "../components/Navbar.vue";
|
||||
import "@3ba/styles";
|
||||
import Footer from "../components/Footer.vue";
|
||||
</script>
|
||||
<template>
|
||||
<div :style="{ minHeight: '100vh', padding: '32px', }">
|
||||
<div style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<Navbar :active="0"></Navbar>
|
||||
<Card>
|
||||
<h2>Mi ez az oldal?</h2>
|
||||
<div style="text-align: left;">
|
||||
Ez az oldal megmondja melyik szobába kell menni éjfélkor szülinapot ünnepelni. Ha nem tudod melyik intézményről van szó, akkor valószínűleg rossz helyen jársz.
|
||||
</div>
|
||||
<h2>Miért ilyen ocsmány?</h2>
|
||||
<div style="text-align: left;">
|
||||
Azért, mert próbálkoztam a Vue frontend frameworkkel a vicc kedvéért és nagyon megkedveltem a <a href="https://frutigeraeroarchive.org/" target="_blank">Frutiger Aero</a> stílust.
|
||||
</div>
|
||||
<h2>Mi az a csúnya szó?</h2>
|
||||
<div style="text-align: left;">
|
||||
A Kréta forráskód szivárogtatásában talált, méltán híres <a href="https://github.com/HexadecimalHUN/DirtyWords/blob/main/DirtyWords.xml">dirtywords.xml</a>-ből random generált kifejezések
|
||||
</div>
|
||||
<h2>Credit?</h2>
|
||||
<div style="text-align: left;">
|
||||
A Vue komponensek nagy része <a href="https://github.com/3ba/aero">ezen a Github repo</a>-n alapszik, szóval nagy köszönet bárki is csinálta.
|
||||
</div>
|
||||
<h2>Licence?</h2>
|
||||
<div style="text-align: left;">
|
||||
This is a non-commercial website, with no plans to ever become one. That should cover my ass for the font and the background image(s) I think.
|
||||
</div>
|
||||
</Card>
|
||||
<Card>
|
||||
<h2>Elérhetőségek/contact</h2>
|
||||
<ul style="text-align: left; font-size: 1.2rem;">
|
||||
<li>- Élőben 734 (nyáron, remélem nem felejtem ezt el frissíteni)</li>
|
||||
<li>- <a href="mailto:peter@sc0tt.org">peter@sc0tt.org</a></li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card>
|
||||
<div>
|
||||
Csúnya szó: {{ DirtyWord }}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
DirtyWord: '',
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
axios
|
||||
.get('https://scapi.sc0tt.org/dirtywords')
|
||||
.then(response => {
|
||||
this.DirtyWord = response.data.dirty;
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a.visited {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { AeroThemeProvider, AeroSky, Card, Btn } from "@3ba/vue";
|
||||
import Navbar from "../components/Navbar.vue";
|
||||
import "@3ba/styles";
|
||||
import Footer from "../components/Footer.vue";
|
||||
import News from "../components/News.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :style="{ minHeight: '100vh', padding: '32px', }">
|
||||
<div style="display: flex; flex-direction: column; gap: 20px;">
|
||||
<Navbar :active="2"></Navbar>
|
||||
<Card>
|
||||
<News important></News>
|
||||
</Card>
|
||||
<Card>
|
||||
<News info></News>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import {
|
||||
AeroThemeProvider,
|
||||
AeroSky,
|
||||
Navbar,
|
||||
ThemeSwitcher,
|
||||
Card,
|
||||
Stat,
|
||||
Btn,
|
||||
Toggle,
|
||||
Slider,
|
||||
Select,
|
||||
Badge,
|
||||
Progress,
|
||||
Tabs,
|
||||
Alert,
|
||||
Toast,
|
||||
MediaPlayer,
|
||||
Stack,
|
||||
SectionLabel,
|
||||
} from "@3ba/vue";
|
||||
import type { AeroThemeSetting } from "@3ba/vue";
|
||||
|
||||
const theme = ref<AeroThemeSetting>("morning");
|
||||
const bubbles = ref(true);
|
||||
const volume = ref(64);
|
||||
const tab = ref(0);
|
||||
const scene = ref("Lagoon");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AeroThemeProvider :theme="theme" :bubbles="bubbles">
|
||||
<AeroSky :style="{ minHeight: '100vh', padding: '28px 22px', boxSizing: 'border-box' }">
|
||||
<div :style="{ maxWidth: '980px', margin: '0 auto', display: 'grid', gap: '22px' }">
|
||||
<Navbar />
|
||||
|
||||
<Stack row wrap justify="space-between" align="center" :gap="16">
|
||||
<div>
|
||||
<SectionLabel>Frutiger Aero · Vue</SectionLabel>
|
||||
<h1 class="aero-text-shadow" :style="{ margin: '6px 0 0', fontWeight: 300, fontSize: '34px' }">A sky for every hour</h1>
|
||||
</div>
|
||||
<ThemeSwitcher v-model="theme" />
|
||||
</Stack>
|
||||
|
||||
<Stack row wrap :gap="16">
|
||||
<Stat label="Visitors" value="12.4k" delta="8%" glyph="☀" />
|
||||
<Stat label="Signups" value="1,208" delta="3%" glyph="✦" />
|
||||
<Stat label="Bounce" value="22%" delta="1%" :up="false" glyph="↯" />
|
||||
</Stack>
|
||||
|
||||
<div :style="{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '16px' }">
|
||||
<Card title="Controls" subtitle="Everything recolors with the theme">
|
||||
<Stack :gap="16">
|
||||
<Toggle v-model="bubbles" label="Drifting bubbles" />
|
||||
<Slider v-model="volume" label="Volume" show-value unit="%" />
|
||||
<Select v-model="scene" :options="['Lagoon', 'Meadow', 'Aurora', 'Twilight']" />
|
||||
<Stack row :gap="10" wrap>
|
||||
<Btn variant="primary">Primary</Btn>
|
||||
<Btn variant="positive">Positive</Btn>
|
||||
<Btn variant="ghost">Ghost</Btn>
|
||||
<Badge tone="accent" dot>Live</Badge>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card title="Status" subtitle="Feedback surfaces">
|
||||
<Stack :gap="14">
|
||||
<Tabs v-model="tab" :items="['Overview', 'Activity', 'Alerts']" />
|
||||
<Progress :value="volume" label="Sync" />
|
||||
<Alert tone="success" title="All systems nominal">The sky is clear and the bubbles are rising.</Alert>
|
||||
<Toast title="Saved to the cloud" body="Your changes are floating safely." action="Undo" />
|
||||
</Stack>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<MediaPlayer :at="volume" />
|
||||
</div>
|
||||
</AeroSky>
|
||||
</AeroThemeProvider>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import App from '../App.vue';
|
||||
import BirthdayView from '../pages/BirthdayView.vue';
|
||||
import TestingView from '../pages/TestingView.vue';
|
||||
import HomeView from '../pages/HomeView.vue';
|
||||
import NewsView from '../pages/NewsView.vue';
|
||||
|
||||
const routes = [
|
||||
{ path: '/',
|
||||
children:
|
||||
[
|
||||
{ path: '', component: BirthdayView },
|
||||
{ path: 'aero', component: TestingView },
|
||||
{ path: 'home', component: HomeView },
|
||||
{ path: 'hearsay', component: NewsView },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user