Skip to content

Commit 616f186

Browse files
hmbachertheelims
andauthored
Replaced D'n'D component, rework of network settings, enhanced a couple of UI components, some minor fixes (#100)
* first commit * Implement fans configuration feature with UI and backend support * feat: Add sensor management and control functionality - Implemented a new page for managing sensors in the frontend. - Created an EditSensor component for editing sensor details with validation. - Developed ControllerSettingsService to manage controller settings via HTTP endpoints. - Introduced FanController to handle fan control logic based on temperature readings. - Added OneWireESP32 library for interfacing with DS18B20 temperature sensors. - Implemented TempSensorsService to manage temperature sensors and their data acquisition. - Established persistence for sensor settings and states using file storage. - Enhanced error handling and logging throughout the services. * feat: Enhance temperature sensor handling with error tracking and event updates * migrated from ESP32C3 to ESP32S3 * feat: Implement FFC MQTT Configuration and Alarm Logging - Added FFCMqttConfig component for configuring MQTT settings. - Removed GatewayMQTTConfig component to streamline MQTT configuration. - Updated MQTT routing to use FFCMqttConfig. - Introduced new alarms page for managing alarm events. - Created backend services for alarm logging and MQTT settings. - Integrated alarm notifications via MQTT. - Enhanced temperature sensor service to publish alarms on sensor status changes. - Added utility functions for ISO 8601 date conversion. - Updated UI to include alarm log and relevant error handling. * feat: Integrate alarm state management and update alarm handling in UI * feat: Add RPM sensor functionality and integrate with fan controller * feat: Refactor RPM sensor to include alarm service and enhance error handling * feat: Implement sensor discovery functionality and update controller state management * feat: Remove unused icons and configuration files; update logo in UI * feat: Update navigation to settings page and enhance alarm display with ID * feat: Enhance alarm notifications and add fan/temperature monitoring settings * chore: Update TODO file for project organization and future tasks * feat: Add configuration file and update PWM settings for fan control; enhance temperature sensor discovery logging * - Removed obsolete (default) docs - Changed to PCB v2.0 settings - Updated version string * chore: Add initial TODO file with features and known bugs * Reworked network settings * Fixed complier/IDE warnings * Updated pioarduino platfom and switched to explicit versioning * Cleanup up async worker * Update development server configuration and dependencies * Updated SSL cert creation config, updated pipeline scripts * Fixed vite dev server issue * Fixed bug if now handlers are passed * Added coredump * Finalized Wifi Sta settings * Optimized dirty flag for SettingsCard and Collapsible * Delete lib/framework/WWWData.h * Fixed preprocessor directive * Fixed makro redefinition (cherry picked from commit bda977530a219d6f820a7dec7acd86008004d1ef) * Enhanced logo visualization on login page and on main page * Added $src path alias * Added missing page.ts for Core Dump * Changes to prevent Svelte warning 'binding_property_non_reactive' * Fixed PSRAM chart creation * Fixed PSRAM metric * Implemented delayed wifi reconnect, to allow previous POST request to be successfull * Optimized language * Implemented draggable list with svelte-dnd-action # Conflicts: # interface/src/routes/gateway/smoke-detectors/+page.svelte # platformio.ini * Fixed overflow properties # Conflicts: # interface/src/routes/gateway/smoke-detectors/+page.svelte * Fixed Websockets implementation in frontend * Changed drag & drop styling # Conflicts: # interface/src/lib/components/DraggableList.svelte * Fixed re-ordering of Smoke detectors # Conflicts: # interface/src/routes/gateway/smoke-detectors/+page.svelte # src/GatewayDevicesService.cpp * Cleanup up obsolete comments * Show HTML in Info and Confirm dialog * Removed deprecated commands in build script * Enhanced execution of pre-build scripts # Conflicts: # scripts/generate_enums.py * Add print statements to indicate skipped execution for non-build tasks in scripts # Conflicts: # scripts/generate_enums.py * Latest Arduino --------- Co-authored-by: elims <[email protected]>
1 parent 622bdd0 commit 616f186

30 files changed

+948
-484
lines changed

interface/package-lock.json

Lines changed: 9 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

interface/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.2.0",
44
"private": true,
55
"scripts": {
6-
"dev": "vite dev",
6+
"dev": "vite dev --host",
77
"build": "vite build",
88
"preview": "vite preview",
99
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
@@ -44,7 +44,7 @@
4444
"jwt-decode": "^4.0.0",
4545
"luxon": "^3.7.1",
4646
"msgpack-lite": "^0.1.26",
47-
"svelte-dnd-list": "^0.1.8",
47+
"svelte-dnd-action": "^0.9.65",
4848
"svelte-modals": "^2.0.1"
4949
}
5050
}

interface/src/lib/components/Collapsible.svelte

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,54 @@
22
import { slide } from 'svelte/transition';
33
import { cubicOut } from 'svelte/easing';
44
import Down from '~icons/tabler/chevron-down';
5+
import Alert from '~icons/tabler/alert-hexagon';
56
6-
let { icon, title, children, opened, closed, open = false, class: className = '' } = $props();
7+
interface Props {
8+
open?: boolean;
9+
opened?: any;
10+
closed?: any;
11+
collapsible?: boolean;
12+
icon?: import('svelte').Snippet;
13+
title?: import('svelte').Snippet;
14+
children?: import('svelte').Snippet;
15+
class?: string;
16+
isDirty?: boolean;
17+
}
18+
19+
let {
20+
open = $bindable(false),
21+
opened,
22+
closed,
23+
icon,
24+
title,
25+
children,
26+
class: className = '',
27+
isDirty = false
28+
}: Props = $props();
729
830
function openCollapsible() {
931
open = !open;
1032
if (open) {
11-
opened();
33+
if (opened) opened();
1234
} else {
13-
closed();
35+
if (closed) closed();
1436
}
1537
}
1638
</script>
1739

1840
<div class="{className} relative grid w-full max-w-2xl self-center overflow-hidden">
41+
{#if isDirty}
42+
<div class="absolute left-0 top-0 w-1.5 h-full bg-red-300"></div>
43+
{/if}
1944
<div class="min-h-16 flex w-full items-center justify-between space-x-3 p-4 text-xl font-medium">
20-
<span class="inline-flex items-baseline">
45+
<span class="inline-flex items-center">
2146
{@render icon?.()}
2247
{@render title?.()}
48+
{#if isDirty}
49+
<div data-tip="There are unsaved changes." class="tooltip tooltip-right tooltip-error">
50+
<Alert class="text-error lex-shrink-0 ml-2 h-6 w-6 self-end cursor-help" />
51+
</div>
52+
{/if}
2353
</span>
2454
<button class="btn btn-circle btn-ghost btn-sm" onclick={() => openCollapsible()}>
2555
<Down

interface/src/lib/components/ConfirmDialog.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// provided by <Modals />
99
1010
interface Props {
11-
isOpen: boolean;
11+
isOpen?: boolean;
1212
title: string;
1313
message: string;
1414
onConfirm: any;
@@ -40,19 +40,19 @@
4040
>
4141
<h2 class="text-base-content text-start text-2xl font-bold">{title}</h2>
4242
<div class="divider my-2"></div>
43-
<p class="text-base-content mb-1 text-start">{message}</p>
43+
<p class="text-base-content mb-1 text-start">{@html message}</p>
4444
<div class="divider my-2"></div>
4545
<div class="flex justify-end gap-2">
4646
<button
4747
class="btn btn-primary inline-flex items-center"
4848
onclick={() => {
4949
modals.close();
50-
}}><labels.cancel.icon class="mr-2 h-5 w-5" /><span>{labels?.cancel.label}</span></button
50+
}}><labels.cancel.icon class="h-5 w-5" /><span>{labels?.cancel.label}</span></button
5151
>
5252
<button
5353
class="btn btn-warning text-warning-content inline-flex items-center"
5454
onclick={onConfirm}
55-
><SvelteComponent class="mr-2 h-5 w-5" /><span>{labels?.confirm.label}</span></button
55+
><SvelteComponent class="h-5 w-5" /><span>{labels?.confirm.label}</span></button
5656
>
5757
</div>
5858
</div>
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<script lang="ts">
2+
import { dndzone } from 'svelte-dnd-action';
3+
import type { Snippet } from 'svelte';
4+
5+
interface Props {
6+
items: any[];
7+
onReorder?: (reorderedItems: any[]) => void;
8+
flipDurationMs?: number;
9+
dragDisabled?: boolean;
10+
class?: string;
11+
children: Snippet<[{ item: any; index: number; originalItem: any }]>;
12+
}
13+
14+
let {
15+
items,
16+
onReorder = () => {},
17+
flipDurationMs = 200,
18+
dragDisabled = false,
19+
class: className = '',
20+
children
21+
}: Props = $props();
22+
23+
// Create a state array with IDs for drag-and-drop functionality
24+
let itemsWithIds: any[] = $state([]);
25+
26+
// Update the drag-and-drop array whenever items change
27+
$effect(() => {
28+
itemsWithIds = items.map((item, index) => ({
29+
...item,
30+
id: item.id || `dnd-item-${index}-${Date.now()}` // Generate unique ID with timestamp
31+
}));
32+
});
33+
34+
function handleSort(e: any) {
35+
// Update the visual drag-and-drop array immediately
36+
itemsWithIds = e.detail.items;
37+
}
38+
39+
function handleFinalizeSort(e: any) {
40+
// Remove only temporary IDs, preserve original device IDs
41+
const reorderedItems = e.detail.items.map((item: any) => {
42+
// If this is a temporary ID we added (string starting with 'dnd-item-'), remove it
43+
if (typeof item.id === 'string' && item.id.startsWith('dnd-item-')) {
44+
const { id, ...itemWithoutTempId } = item;
45+
return itemWithoutTempId;
46+
}
47+
// Otherwise, keep the item as-is (preserving original numeric IDs)
48+
return item;
49+
});
50+
51+
// Call the parent's reorder handler
52+
onReorder(reorderedItems);
53+
}
54+
55+
</script>
56+
57+
<section
58+
use:dndzone={{
59+
items: itemsWithIds,
60+
flipDurationMs,
61+
dropTargetStyle: {}, // This is to actively clear default styles
62+
dropTargetClasses: ['dragzone-outline'], // This applies custom styling
63+
dragDisabled
64+
}}
65+
onconsider={handleSort}
66+
onfinalize={handleFinalizeSort}
67+
class={className}
68+
>
69+
{#each itemsWithIds as item, index (item.id)}
70+
{@render children({ item, index, originalItem: items[index] })}
71+
{/each}
72+
</section>
73+
74+
<style>
75+
@reference "$src/app.css";
76+
:global(.dragzone-outline) {
77+
@apply outline-solid outline-2 outline-(--color-primary);
78+
}
79+
:global(#dnd-action-dragged-el) {
80+
@apply outline-solid outline-2 outline-current;
81+
82+
}
83+
</style>

interface/src/lib/components/InfoDialog.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
>
3636
<h2 class="text-base-content text-start text-2xl font-bold">{title}</h2>
3737
<div class="divider my-2"></div>
38-
<p class="text-base-content mb-1 text-start">{message}</p>
38+
<p class="text-base-content mb-1 text-start">{@html message}</p>
3939
<div class="divider my-2"></div>
4040
<div class="flex justify-end gap-2">
4141
<button

interface/src/lib/components/InputPassword.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@
77
id?: string;
88
}
99
10-
let { value = $bindable(''), id = '' }: Props = $props();
10+
let { value = $bindable('') as string, id = '' as string }: Props = $props();
11+
1112
function handleInput(e: any) {
1213
value = e.target.value;
1314
}
1415
</script>
1516

1617
<div class="relative">
17-
<input {type} class="input w-full" {value} oninput={handleInput} {id} />
18+
<input {type} class="input input-bordered w-full" {value} oninput={handleInput} {id} />
1819
<div class="absolute inset-y-0 right-0 flex items-center pr-1">
1920
<!-- svelte-ignore a11y_click_events_have_key_events -->
2021
<svg
2122
xmlns="http://www.w3.org/2000/svg"
2223
class="text-base-content/50 h-6 {show ? 'block' : 'hidden'}"
2324
onclick={() => (show = false)}
25+
role="button"
26+
aria-label="Hide password"
27+
tabindex="0"
2428
width="40"
2529
height="40"
2630
viewBox="0 0 24 24"
@@ -43,6 +47,9 @@
4347
xmlns="http://www.w3.org/2000/svg"
4448
class="text-base-content/50 h-6 {show ? 'hidden' : 'block'}"
4549
onclick={() => (show = true)}
50+
role="button"
51+
aria-label="Show password"
52+
tabindex="0"
4653
width="40"
4754
height="40"
4855
viewBox="0 0 24 24"

interface/src/lib/components/SettingsCard.svelte

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,47 @@
22
import { slide } from 'svelte/transition';
33
import { cubicOut } from 'svelte/easing';
44
import Down from '~icons/tabler/chevron-down';
5+
import Alert from '~icons/tabler/alert-hexagon';
6+
57
interface Props {
68
open?: boolean;
79
collapsible?: boolean;
810
icon?: import('svelte').Snippet;
911
title?: import('svelte').Snippet;
1012
children?: import('svelte').Snippet;
13+
maxwidth?: string;
14+
isDirty?: boolean;
1115
}
1216
1317
let {
1418
open = $bindable(true),
1519
collapsible = true,
1620
icon,
1721
title,
18-
children
22+
children,
23+
maxwidth = 'max-w-2xl',
24+
isDirty = false
1925
}: Props = $props();
2026
</script>
2127

2228
{#if collapsible}
2329
<div
24-
class="bg-base-200 rounded-box shadow-primary/50 relative grid w-full max-w-2xl self-center overflow-hidden shadow-lg"
30+
class="bg-base-200 rounded-box shadow-primary/50 relative grid w-full {maxwidth} self-center overflow-hidden shadow-lg m-10"
2531
>
32+
{#if isDirty}
33+
<div class="absolute left-0 top-0 w-1.5 h-full bg-red-300"></div>
34+
{/if}
2635
<div
2736
class="min-h-16 flex w-full items-center justify-between space-x-3 p-4 text-xl font-medium"
2837
>
29-
<span class="inline-flex items-baseline">
38+
<span class="inline-flex items-center">
3039
{@render icon?.()}
3140
{@render title?.()}
41+
{#if isDirty}
42+
<div data-tip="There are unsaved changes." class="tooltip tooltip-right tooltip-error">
43+
<Alert class="text-error lex-shrink-0 ml-2 h-6 w-6 self-end cursor-help" />
44+
</div>
45+
{/if}
3246
</span>
3347
<button
3448
class="btn btn-circle btn-ghost btn-sm"
@@ -54,12 +68,20 @@
5468
</div>
5569
{:else}
5670
<div
57-
class="bg-base-200 rounded-box shadow-primary/50 relative grid w-full max-w-2xl self-center overflow-hidden shadow-lg"
71+
class="bg-base-200 rounded-box shadow-primary/50 relative grid w-full {maxwidth} self-center overflow-hidden shadow-lg m-10"
5872
>
73+
{#if isDirty}
74+
<div class="absolute left-0 top-0 w-1.5 h-full bg-red-300"></div>
75+
{/if}
5976
<div class="min-h-16 w-full p-4 text-xl font-medium">
60-
<span class="inline-flex items-baseline">
77+
<span class="inline-flex items-center">
6178
{@render icon?.()}
6279
{@render title?.()}
80+
{#if isDirty}
81+
<div data-tip="There are unsaved changes." class="tooltip tooltip-right tooltip-error">
82+
<Alert class="text-error lex-shrink-0 ml-2 h-6 w-6 self-end cursor-help" />
83+
</div>
84+
{/if}
6385
</span>
6486
</div>
6587
<div class="flex flex-col gap-2 p-4 pt-0">
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
<script lang="ts">
22
import Loader from '~icons/tabler/loader-2';
3+
4+
let { text = "Loading..."} = $props();
5+
36
</script>
47

5-
<div class="flex h-full w-full flex-col items-center justify-center p-6">
8+
<div class="flex w-full flex-col items-center justify-center p-6">
69
<Loader class="text-primary h-14 w-auto animate-spin stroke-2" />
7-
<p class="text-xl">Loading...</p>
10+
<p class="text-xl">{text}</p>
811
</div>

interface/src/lib/components/UpdateIndicator.svelte

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
1818
let { update = $bindable(false) }: Props = $props();
1919
20-
let firmwareVersion: string;
20+
let firmwareVersion: string = $state('');
2121
let firmwareDownloadLink: string;
2222
2323
async function getGithubAPI() {
@@ -31,6 +31,7 @@
3131
}
3232
});
3333
if (response.status !== 200) {
34+
notifications.error('Failed to fetch latest release from GitHub.', 5000);
3435
throw new Error(`Failed to fetch latest release from ${githubUrl}`);
3536
}
3637
const results = await response.json();
@@ -54,7 +55,7 @@
5455
}
5556
}
5657
} catch (error) {
57-
console.error('Error:', error);
58+
console.warn(error);
5859
}
5960
}
6061
@@ -112,6 +113,6 @@
112113
class="indicator-item indicator-top indicator-center badge badge-info badge-xs top-2 scale-75 lg:top-1"
113114
>{firmwareVersion}</span
114115
>
115-
<Firmware class="inline-block h-7 w-7" />
116+
<Firmware class="h-7 w-7" />
116117
</button>
117118
{/if}

0 commit comments

Comments
 (0)