feat(relay): implement relay card
This commit is contained in:
69
src/components/RelayCard.vue
Normal file
69
src/components/RelayCard.vue
Normal 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>
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<footer
|
<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">
|
<div class="max-w-4xl mx-auto text-center">
|
||||||
© {{ currentYear }} {{ appName }} ‐ Lucien Cartier-Tilet.
|
© {{ currentYear }} {{ appName }} ‐ Lucien Cartier-Tilet.
|
||||||
<a href="https://labs.phundrak.com/phundrak/sta" class="text-accent">
|
<a href="https://labs.phundrak.com/phundrak/sta"> Source code </a>
|
||||||
Source code
|
|
||||||
</a>
|
|
||||||
under the
|
under the
|
||||||
<a
|
<a
|
||||||
href="https://labs.phundrak.com/phundrak/sta/src/branch/develop/LICENSE.md"
|
href="https://labs.phundrak.com/phundrak/sta/src/branch/develop/LICENSE.md"
|
||||||
class="text-accent"
|
|
||||||
>
|
>
|
||||||
AGPL 3.0 license </a
|
AGPL 3.0 license </a
|
||||||
>.
|
>.
|
||||||
@@ -33,4 +30,14 @@ const appName = computed(() =>
|
|||||||
);
|
);
|
||||||
</script>
|
</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>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<header
|
<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">
|
<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 ‐ Smart Temperature & Appliance Control
|
||||||
|
</span>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<style lang="less"></style>
|
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isLoading && !relays">
|
<div class="flex flex-col gap-8">
|
||||||
<ProgressSpinner class="--p-progressspinner-color-primary" />
|
<div v-if="isLoading && !relays">
|
||||||
</div>
|
<ProgressSpinner class="--p-progressspinner-color-primary" />
|
||||||
<div v-else-if="error" class="bg-accent text-background py-4 px-3 rounded-md">
|
</div>
|
||||||
{{ error }}
|
<div
|
||||||
</div>
|
v-else-if="error"
|
||||||
<div v-else class="flex flex-row flex-wrap gap-2">
|
class="bg-accent text-background py-4 px-3 rounded-md"
|
||||||
<div v-for="relay in relays">
|
>
|
||||||
{{ relay.id }}
|
{{ 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>
|
</div>
|
||||||
<div class="flex flex-row flex-wrap"></div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRelayPolling } from '../composables/useRelayPolling';
|
import { useRelayPolling } from '../composables/useRelayPolling';
|
||||||
import { ProgressSpinner } from 'primevue';
|
import { ProgressSpinner } from 'primevue';
|
||||||
|
import RelayCard from '../components/RelayCard.vue';
|
||||||
|
import { Button } from 'primevue';
|
||||||
|
|
||||||
const { relays, isLoading, error, refresh } = useRelayPolling();
|
const { relays, isLoading, error, refresh } = useRelayPolling();
|
||||||
refresh();
|
refresh();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less"></style>
|
|
||||||
|
|||||||
162
src/style.css
162
src/style.css
@@ -18,69 +18,109 @@
|
|||||||
--text-4xl: 3.158rem;
|
--text-4xl: 3.158rem;
|
||||||
--text-5xl: 4.210rem;
|
--text-5xl: 4.210rem;
|
||||||
|
|
||||||
--color-text: oklch(13.42% 0.025 277.52);
|
--color-text: oklch(20.55% 0.026 159.60);
|
||||||
--color-text-50: oklch(13.49% 0.027 277.57);
|
--color-text-50: oklch(96.73% 0.012 164.80);
|
||||||
--color-text-100: oklch(18.12% 0.047 276.94);
|
--color-text-100: oklch(93.53% 0.024 163.13);
|
||||||
--color-text-200: oklch(26.14% 0.086 274.25);
|
--color-text-200: oklch(87.08% 0.048 162.29);
|
||||||
--color-text-300: oklch(33.63% 0.119 273.77);
|
--color-text-300: oklch(80.85% 0.075 161.20);
|
||||||
--color-text-400: oklch(40.55% 0.152 273.78);
|
--color-text-400: oklch(74.56% 0.099 159.20);
|
||||||
--color-text-500: oklch(47.37% 0.181 273.51);
|
--color-text-500: oklch(68.48% 0.121 157.47);
|
||||||
--color-text-600: oklch(57.68% 0.143 277.26);
|
--color-text-600: oklch(58.25% 0.101 157.47);
|
||||||
--color-text-700: oklch(68.53% 0.103 278.92);
|
--color-text-700: oklch(47.56% 0.080 158.24);
|
||||||
--color-text-800: oklch(79.24% 0.067 280.95);
|
--color-text-800: oklch(35.96% 0.056 158.77);
|
||||||
--color-text-900: oklch(89.69% 0.031 281.79);
|
--color-text-900: oklch(23.61% 0.032 159.65);
|
||||||
--color-text-950: oklch(94.88% 0.016 282.27);
|
--color-text-950: oklch(16.99% 0.020 157.52);
|
||||||
|
|
||||||
--color-background: oklch(98.32% 0.005 286.30);
|
--color-background: oklch(98.85% 0.003 174.49);
|
||||||
--color-background-50: oklch(13.12% 0.030 281.49);
|
--color-background-50: oklch(96.66% 0.009 179.60);
|
||||||
--color-background-100: oklch(17.61% 0.050 279.92);
|
--color-background-100: oklch(93.48% 0.020 172.77);
|
||||||
--color-background-200: oklch(25.07% 0.093 277.28);
|
--color-background-200: oklch(86.98% 0.039 173.82);
|
||||||
--color-background-300: oklch(32.27% 0.129 276.41);
|
--color-background-300: oklch(80.46% 0.058 172.26);
|
||||||
--color-background-400: oklch(39.10% 0.162 275.91);
|
--color-background-400: oklch(74.00% 0.077 170.71);
|
||||||
--color-background-500: oklch(45.65% 0.193 275.59);
|
--color-background-500: oklch(67.67% 0.094 169.62);
|
||||||
--color-background-600: oklch(56.14% 0.154 280.23);
|
--color-background-600: oklch(57.52% 0.079 169.17);
|
||||||
--color-background-700: oklch(67.14% 0.113 282.93);
|
--color-background-700: oklch(46.93% 0.062 169.68);
|
||||||
--color-background-800: oklch(78.44% 0.072 284.61);
|
--color-background-800: oklch(35.70% 0.045 170.66);
|
||||||
--color-background-900: oklch(89.30% 0.034 285.69);
|
--color-background-900: oklch(23.47% 0.026 169.60);
|
||||||
--color-background-950: oklch(94.69% 0.017 286.06);
|
--color-background-950: oklch(16.82% 0.014 169.51);
|
||||||
|
|
||||||
--color-primary: oklch(47.97% 0.175 276.79);
|
--color-primary: oklch(70.75% 0.113 157.63);
|
||||||
--color-primary-50: oklch(13.64% 0.027 282.25);
|
--color-primary-50: oklch(96.73% 0.012 164.80);
|
||||||
--color-primary-100: oklch(17.88% 0.046 280.64);
|
--color-primary-100: oklch(93.53% 0.024 163.13);
|
||||||
--color-primary-200: oklch(25.68% 0.085 277.64);
|
--color-primary-200: oklch(87.05% 0.049 161.02);
|
||||||
--color-primary-300: oklch(32.98% 0.118 277.13);
|
--color-primary-300: oklch(80.82% 0.076 160.38);
|
||||||
--color-primary-400: oklch(39.93% 0.151 276.24);
|
--color-primary-400: oklch(74.54% 0.100 158.60);
|
||||||
--color-primary-500: oklch(46.58% 0.180 276.13);
|
--color-primary-500: oklch(68.46% 0.122 157.00);
|
||||||
--color-primary-600: oklch(57.13% 0.142 279.97);
|
--color-primary-600: oklch(58.22% 0.102 156.89);
|
||||||
--color-primary-700: oklch(68.02% 0.103 282.69);
|
--color-primary-700: oklch(47.54% 0.081 157.46);
|
||||||
--color-primary-800: oklch(78.84% 0.066 283.81);
|
--color-primary-800: oklch(35.94% 0.057 157.56);
|
||||||
--color-primary-900: oklch(89.54% 0.031 285.75);
|
--color-primary-900: oklch(23.61% 0.032 159.65);
|
||||||
--color-primary-950: oklch(94.66% 0.016 286.09);
|
--color-primary-950: oklch(16.99% 0.020 157.52);
|
||||||
|
|
||||||
--color-secondary: oklch(70.78% 0.104 302.18);
|
--color-secondary: oklch(77.49% 0.049 254.33);
|
||||||
--color-secondary-50: oklch(14.31% 0.027 303.00);
|
--color-secondary-50: oklch(95.88% 0.009 247.92);
|
||||||
--color-secondary-100: oklch(18.93% 0.046 300.07);
|
--color-secondary-100: oklch(91.80% 0.017 250.85);
|
||||||
--color-secondary-200: oklch(27.46% 0.086 298.89);
|
--color-secondary-200: oklch(83.27% 0.035 253.73);
|
||||||
--color-secondary-300: oklch(35.53% 0.118 298.36);
|
--color-secondary-300: oklch(74.79% 0.055 252.87);
|
||||||
--color-secondary-400: oklch(43.08% 0.151 298.00);
|
--color-secondary-400: oklch(66.02% 0.075 253.94);
|
||||||
--color-secondary-500: oklch(50.51% 0.179 298.28);
|
--color-secondary-500: oklch(57.42% 0.096 253.86);
|
||||||
--color-secondary-600: oklch(60.08% 0.145 300.36);
|
--color-secondary-600: oklch(48.91% 0.081 254.25);
|
||||||
--color-secondary-700: oklch(70.18% 0.106 301.86);
|
--color-secondary-700: oklch(40.26% 0.064 253.43);
|
||||||
--color-secondary-800: oklch(80.18% 0.070 302.91);
|
--color-secondary-800: oklch(30.86% 0.044 254.23);
|
||||||
--color-secondary-900: oklch(90.24% 0.032 303.45);
|
--color-secondary-900: oklch(20.97% 0.024 251.59);
|
||||||
--color-secondary-950: oklch(95.04% 0.017 304.80);
|
--color-secondary-950: oklch(15.30% 0.015 257.65);
|
||||||
|
|
||||||
--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-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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user