feat(relay): implement relay card

This commit is contained in:
2026-05-15 03:26:48 +02:00
parent 4c96815106
commit 94105a040c
5 changed files with 205 additions and 85 deletions

View File

@@ -0,0 +1,69 @@
<template>
<div
:class="
'relay flex flex-col gap-10 bg-background-100 rounded-lg border-2 p-6 transition-all duration-300 ' +
relayClass
"
>
<div class="flex flex-row justify-between items-center">
<div class="flex flex-row gap-3 items-center">
<i class="pi pi-circle-fill"></i> <i class="pi pi-power-off"></i>
</div>
<div>
<Badge
:value="'Relay ' + props.relay.id"
:severity="isRelayOn ? 'primary' : 'secondary'"
/>
</div>
</div>
<div class="flex flex-row justify-between items-center">
<div>{{ props.relay.label }}</div>
<ToggleSwitch v-model="isRelayOn" v-on:click="toggleRelay(relay.id)" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useRelay } from '../composables/useRelay';
import { RelayState, type Relay } from '../types/relay';
import { Badge, ToggleSwitch } from 'primevue';
const props = defineProps<{
relay: Relay;
}>();
const isRelayOn = computed(() => props.relay.state === RelayState.On);
const relayClass = computed(() => {
if (props.relay.state === RelayState.Off) {
return 'border-secondary shadow-md relay-off';
}
return 'border-primary shadow-lg shadow-primary-200 relay-on';
});
const { toggleRelay } = useRelay();
</script>
<style lang="less" scoped>
.relay {
width: 15rem;
&:hover {
scale: 1.02;
}
}
i {
font-weight: 700;
font-size: 1.5rem;
&.pi-circle-fill {
font-size: 1.15rem;
}
.relay-on & {
color: var(--color-primary);
}
.relay-off & {
color: var(--color-secondary-400);
}
}
</style>

View File

@@ -1,16 +1,13 @@
<template>
<footer
class="bg-background-900 border-t border-background-900 px-6 py-4 text-sm text-text"
class="bg-background-200 border-t border-background-200 px-6 py-4 text-sm text-text"
>
<div class="max-w-4xl mx-auto text-center">
&copy; {{ currentYear }} {{ appName }} &dash; Lucien Cartier-Tilet.
<a href="https://labs.phundrak.com/phundrak/sta" class="text-accent">
Source code
</a>
<a href="https://labs.phundrak.com/phundrak/sta"> Source code </a>
under the
<a
href="https://labs.phundrak.com/phundrak/sta/src/branch/develop/LICENSE.md"
class="text-accent"
>
AGPL 3.0 license </a
>.
@@ -33,4 +30,14 @@ const appName = computed(() =>
);
</script>
<style lang="less"></style>
<style scoped="scoped">
a {
color: var(--color-secondary-500);
}
@layer base {
a {
@apply underline decoration-wavy underline-offset-2;
}
}
</style>

View File

@@ -1,13 +1,11 @@
<template>
<header
class="sticky top-0 z-10 bg-background-900 border-b border-background-900 shadow-sm px-6 py-4"
class="sticky top-0 z-10 bg-background-200 border-b border-background-200 shadow-sm px-6 py-4"
>
<nav class="flex items-center justify-between max-w-4xl mx-auto">
<span class="text-lg"> STA </span>
<span class="text-lg font-heading">
STA &dash; Smart Temperature & Appliance Control
</span>
</nav>
</header>
</template>
<script setup lang="ts"></script>
<style lang="less"></style>

View File

@@ -1,24 +1,30 @@
<template>
<div v-if="isLoading && !relays">
<ProgressSpinner class="--p-progressspinner-color-primary" />
</div>
<div v-else-if="error" class="bg-accent text-background py-4 px-3 rounded-md">
{{ error }}
</div>
<div v-else class="flex flex-row flex-wrap gap-2">
<div v-for="relay in relays">
{{ relay.id }}
<div class="flex flex-col gap-8">
<div v-if="isLoading && !relays">
<ProgressSpinner class="--p-progressspinner-color-primary" />
</div>
<div
v-else-if="error"
class="bg-accent text-background py-4 px-3 rounded-md"
>
{{ error }}
</div>
<div v-else class="flex flex-row flex-wrap gap-4">
<RelayCard v-for="relay in relays" :relay="relay" />
</div>
<div class="flex flex-row flex-wrap justify-evenly" style="display: none">
<Button severity="primary" class="min-w-2xs">Tout activer</Button>
<Button severity="secondary" class="min-w-2xs">Tout désactiver</Button>
</div>
</div>
<div class="flex flex-row flex-wrap"></div>
</template>
<script setup lang="ts">
import { useRelayPolling } from '../composables/useRelayPolling';
import { ProgressSpinner } from 'primevue';
import RelayCard from '../components/RelayCard.vue';
import { Button } from 'primevue';
const { relays, isLoading, error, refresh } = useRelayPolling();
refresh();
</script>
<style lang="less"></style>

View File

@@ -18,69 +18,109 @@
--text-4xl: 3.158rem;
--text-5xl: 4.210rem;
--color-text: oklch(13.42% 0.025 277.52);
--color-text-50: oklch(13.49% 0.027 277.57);
--color-text-100: oklch(18.12% 0.047 276.94);
--color-text-200: oklch(26.14% 0.086 274.25);
--color-text-300: oklch(33.63% 0.119 273.77);
--color-text-400: oklch(40.55% 0.152 273.78);
--color-text-500: oklch(47.37% 0.181 273.51);
--color-text-600: oklch(57.68% 0.143 277.26);
--color-text-700: oklch(68.53% 0.103 278.92);
--color-text-800: oklch(79.24% 0.067 280.95);
--color-text-900: oklch(89.69% 0.031 281.79);
--color-text-950: oklch(94.88% 0.016 282.27);
--color-text: oklch(20.55% 0.026 159.60);
--color-text-50: oklch(96.73% 0.012 164.80);
--color-text-100: oklch(93.53% 0.024 163.13);
--color-text-200: oklch(87.08% 0.048 162.29);
--color-text-300: oklch(80.85% 0.075 161.20);
--color-text-400: oklch(74.56% 0.099 159.20);
--color-text-500: oklch(68.48% 0.121 157.47);
--color-text-600: oklch(58.25% 0.101 157.47);
--color-text-700: oklch(47.56% 0.080 158.24);
--color-text-800: oklch(35.96% 0.056 158.77);
--color-text-900: oklch(23.61% 0.032 159.65);
--color-text-950: oklch(16.99% 0.020 157.52);
--color-background: oklch(98.32% 0.005 286.30);
--color-background-50: oklch(13.12% 0.030 281.49);
--color-background-100: oklch(17.61% 0.050 279.92);
--color-background-200: oklch(25.07% 0.093 277.28);
--color-background-300: oklch(32.27% 0.129 276.41);
--color-background-400: oklch(39.10% 0.162 275.91);
--color-background-500: oklch(45.65% 0.193 275.59);
--color-background-600: oklch(56.14% 0.154 280.23);
--color-background-700: oklch(67.14% 0.113 282.93);
--color-background-800: oklch(78.44% 0.072 284.61);
--color-background-900: oklch(89.30% 0.034 285.69);
--color-background-950: oklch(94.69% 0.017 286.06);
--color-background: oklch(98.85% 0.003 174.49);
--color-background-50: oklch(96.66% 0.009 179.60);
--color-background-100: oklch(93.48% 0.020 172.77);
--color-background-200: oklch(86.98% 0.039 173.82);
--color-background-300: oklch(80.46% 0.058 172.26);
--color-background-400: oklch(74.00% 0.077 170.71);
--color-background-500: oklch(67.67% 0.094 169.62);
--color-background-600: oklch(57.52% 0.079 169.17);
--color-background-700: oklch(46.93% 0.062 169.68);
--color-background-800: oklch(35.70% 0.045 170.66);
--color-background-900: oklch(23.47% 0.026 169.60);
--color-background-950: oklch(16.82% 0.014 169.51);
--color-primary: oklch(47.97% 0.175 276.79);
--color-primary-50: oklch(13.64% 0.027 282.25);
--color-primary-100: oklch(17.88% 0.046 280.64);
--color-primary-200: oklch(25.68% 0.085 277.64);
--color-primary-300: oklch(32.98% 0.118 277.13);
--color-primary-400: oklch(39.93% 0.151 276.24);
--color-primary-500: oklch(46.58% 0.180 276.13);
--color-primary-600: oklch(57.13% 0.142 279.97);
--color-primary-700: oklch(68.02% 0.103 282.69);
--color-primary-800: oklch(78.84% 0.066 283.81);
--color-primary-900: oklch(89.54% 0.031 285.75);
--color-primary-950: oklch(94.66% 0.016 286.09);
--color-primary: oklch(70.75% 0.113 157.63);
--color-primary-50: oklch(96.73% 0.012 164.80);
--color-primary-100: oklch(93.53% 0.024 163.13);
--color-primary-200: oklch(87.05% 0.049 161.02);
--color-primary-300: oklch(80.82% 0.076 160.38);
--color-primary-400: oklch(74.54% 0.100 158.60);
--color-primary-500: oklch(68.46% 0.122 157.00);
--color-primary-600: oklch(58.22% 0.102 156.89);
--color-primary-700: oklch(47.54% 0.081 157.46);
--color-primary-800: oklch(35.94% 0.057 157.56);
--color-primary-900: oklch(23.61% 0.032 159.65);
--color-primary-950: oklch(16.99% 0.020 157.52);
--color-secondary: oklch(70.78% 0.104 302.18);
--color-secondary-50: oklch(14.31% 0.027 303.00);
--color-secondary-100: oklch(18.93% 0.046 300.07);
--color-secondary-200: oklch(27.46% 0.086 298.89);
--color-secondary-300: oklch(35.53% 0.118 298.36);
--color-secondary-400: oklch(43.08% 0.151 298.00);
--color-secondary-500: oklch(50.51% 0.179 298.28);
--color-secondary-600: oklch(60.08% 0.145 300.36);
--color-secondary-700: oklch(70.18% 0.106 301.86);
--color-secondary-800: oklch(80.18% 0.070 302.91);
--color-secondary-900: oklch(90.24% 0.032 303.45);
--color-secondary-950: oklch(95.04% 0.017 304.80);
--color-accent: oklch(66.24% 0.137 313.05);
--color-accent-50: oklch(14.75% 0.029 314.19);
--color-accent-100: oklch(19.76% 0.049 312.79);
--color-accent-200: oklch(29.04% 0.090 311.91);
--color-accent-300: oklch(37.77% 0.123 311.83);
--color-accent-400: oklch(45.96% 0.157 311.53);
--color-accent-500: oklch(53.87% 0.186 311.54);
--color-accent-600: oklch(62.58% 0.152 312.52);
--color-accent-700: oklch(71.89% 0.112 313.25);
--color-accent-800: oklch(81.23% 0.074 313.55);
--color-accent-900: oklch(90.72% 0.035 314.09);
--color-accent-950: oklch(95.28% 0.018 314.73);
--color-secondary: oklch(77.49% 0.049 254.33);
--color-secondary-50: oklch(95.88% 0.009 247.92);
--color-secondary-100: oklch(91.80% 0.017 250.85);
--color-secondary-200: oklch(83.27% 0.035 253.73);
--color-secondary-300: oklch(74.79% 0.055 252.87);
--color-secondary-400: oklch(66.02% 0.075 253.94);
--color-secondary-500: oklch(57.42% 0.096 253.86);
--color-secondary-600: oklch(48.91% 0.081 254.25);
--color-secondary-700: oklch(40.26% 0.064 253.43);
--color-secondary-800: oklch(30.86% 0.044 254.23);
--color-secondary-900: oklch(20.97% 0.024 251.59);
--color-secondary-950: oklch(15.30% 0.015 257.65);
--color-accent: oklch(62.74% 0.101 280.46);
--color-accent-50: oklch(95.09% 0.012 281.08);
--color-accent-100: oklch(90.22% 0.024 283.36);
--color-accent-200: oklch(80.23% 0.051 282.68);
--color-accent-300: oklch(69.81% 0.082 281.67);
--color-accent-400: oklch(59.46% 0.112 280.05);
--color-accent-500: oklch(49.09% 0.144 277.36);
--color-accent-600: oklch(42.01% 0.120 277.54);
--color-accent-700: oklch(34.62% 0.096 277.83);
--color-accent-800: oklch(27.07% 0.066 278.62);
--color-accent-900: oklch(18.71% 0.036 279.84);
--color-accent-950: oklch(14.04% 0.022 283.20);
}
:root {
--p-button-primary-background: var(--color-primary) !important;
--p-button-primary-border-color: var(--color-primary) !important;
--p-button-primary-hover-background: var(--color-primary-400) !important;
--p-button-primary-hover-border-color: var(--color-primary-400) !important;
--p-button-primary-active-background: var(--color-primary-300) !important;
--p-button-primary-active-border-color: var(--color-primary-300) !important;
--p-button-primary-color: var(--color-text) !important;
--p-button-primary-hover-color: var(--color-text) !important;
--p-button-primary-active-color: var(--color-text) !important;
--p-button-secondary-background: var(--color-secondary) !important;
--p-button-secondary-border-color: var(--color-secondary) !important;
--p-button-secondary-hover-background: var(--color-secondary-400) !important;
--p-button-secondary-hover-border-color: var(--color-secondary-400) !important;
--p-button-secondary-active-background: var(--color-secondary-300) !important;
--p-button-secondary-active-border-color: var(--color-secondary-300) !important;
--p-button-secondary-color: var(--color-text) !important;
--p-button-secondary-hover-color: var(--color-text) !important;
--p-button-secondary-active-color: var(--color-text) !important;
--p-toggleswitch-border-color: var(--color-secondary-700) !important;
--p-toggleswitch-background: var(--color-secondary-50) !important;
--p-toggleswitch-handle-background: var(--color-secondary-700) !important;
--p-toggleswitch-hover-border-color: var(--color-secondary-500) !important;
--p-toggleswitch-hover-background: var(--color-secondary-50) !important;
--p-toggleswitch-handle-hover-background: var(--color-secondary-500) !important;
--p-toggleswitch-checked-background: var(--color-primary-400) !important;
--p-toggleswitch-handle-checked-background: var(--color-primary-800) !important;
--p-toggleswitch-checked-hover-background: var(--color-primary-300) !important;
--p-toggleswitch-handle-checked-hover-background: var(--color-primary-700) !important;
--p-badge-primary-background: var(--color-primary) !important;
--p-badge-primary-color: var(--color-text) !important;
--p-badge-secondary-background: var(--color-secondary-400) !important;
--p-badge-secondary-color: var(--color-text) !important;
}