diff --git a/src/lib/components/WeatherCalendar.svelte b/src/lib/components/WeatherCalendar.svelte index f36bb5a5..ce99c2e0 100644 --- a/src/lib/components/WeatherCalendar.svelte +++ b/src/lib/components/WeatherCalendar.svelte @@ -219,9 +219,7 @@ {weekdays[day.date.weekday % 7]} - {day.date.day} + {day.date.day} {#if day.weather} diff --git a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/+page.server.ts b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/+page.server.ts index 4506880b..83eae990 100644 --- a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/+page.server.ts +++ b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/+page.server.ts @@ -17,7 +17,8 @@ export const load: PageServerLoad = async ({ url, params, parent, locals: { supa const startParam = url.searchParams.get('start'); const endParam = url.searchParams.get('end'); - const timezoneParam = url.searchParams.get('timezone') || 'UTC'; + const inferredTimezone = dataTable === 'cw_traffic2' ? 'Asia/Tokyo' : 'UTC'; + const timezoneParam = url.searchParams.get('timezone') || inferredTimezone; let startDate: Date; let endDate: Date; const nowInZone = DateTime.now().setZone(timezoneParam); diff --git a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.server.ts b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.server.ts index 582e3dad..892559ed 100644 --- a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.server.ts +++ b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.server.ts @@ -6,6 +6,7 @@ import { LocationService } from '$lib/services/LocationService'; import { DeviceService } from '$lib/services/DeviceService'; import { DeviceRepository } from '$lib/repositories/DeviceRepository'; import { LocationRepository } from '$lib/repositories/LocationRepository'; +import { DeviceOwnersRepository } from '$lib/repositories/DeviceOwnersRepository'; /** * Load the device owner’s ID and all locations for the current user. @@ -51,16 +52,30 @@ export const actions: Actions = { return { success: false, error: 'Authentication required' }; } + if (!sessionResult.user.id) { + return { success: false, error: 'User ID not found in session' }; + } + if (!devEui) { return { success: false, error: 'Device EUI is required' }; } const deviceRepository = new DeviceRepository(locals.supabase, new ErrorHandlingService()); const deviceService = new DeviceService(deviceRepository); const device = await deviceService.getDeviceByEui(devEui); + const deviceOwnersRepository = new DeviceOwnersRepository( + locals.supabase, + new ErrorHandlingService() + ); + const owners = await deviceOwnersRepository.findByDeviceEui(devEui); + if (!device) { return { success: false, error: 'Device not found' }; } - if (device.user_id !== sessionResult.user.id) { + + let isOwner = owners.find( + (owner) => owner.user_id === sessionResult?.user?.id && owner.permission_level === 1 + ); + if (device.user_id !== sessionResult.user.id && !isOwner) { return { success: false, error: 'Unauthorized to update this device' }; } diff --git a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.svelte b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.svelte index fbe80a53..02fcb1c1 100644 --- a/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.svelte +++ b/src/routes/app/dashboard/location/[location_id]/devices/[devEui]/settings/+page.svelte @@ -14,7 +14,12 @@ let { data } = $props(); const device = $derived(data.device); const ownerId = $derived(data.ownerId); - const isOwner = $derived(device?.user_id === ownerId); + const isOwner = $derived( + device?.user_id === ownerId || + device?.cw_device_owners?.some( + (owner) => owner.user_id === ownerId && owner.permission_level === 1 + ) + ); // @todo Use a proper sensor datasheet link when wiki pages are created const deviceLinkId = $derived( @@ -51,156 +56,157 @@ {$_('Device Settings')} - CropWatch -
-
-
-

{$_('General')}

-

{$_('Manage the device information.')}

-
-
-
{ - return async ({ result, update }) => { - if (result.data.success) { - success('Settings updated successfully!'); - update(); - } else { - error('Failed to update settings'); - } - }; - }} - use:formValidation - > -
- - {$_('Device Type')} - - {#if device?.cw_device_type?.name} - - {device.cw_device_type.name} - - {:else} - {$_('Unknown')} - {/if} -
-
- - {$_('EUI')} - - {device?.dev_eui || $_('Unknown')} -
-
- - {$_('Installed Date')} - - {device?.installed_at ? formatDateOnly(device.installed_at) : $_('Unknown')} -
-
- - {$_('Coordinates')} - - {#if device?.lat && device?.long} - - {device.lat}, {device.long} - {:else} - {$_('Unknown')} - {/if} -
-
- - {#if isOwner} - - {:else} - {device?.name} - {/if} -
-
- - {#await data.locations} - {$_('Loading...')} - {:then locations} +
+
+
+
+

{$_('General')}

+

{$_('Manage the device information.')}

+
+
+ { + return async ({ result, update }) => { + if (result.data.success) { + success('Settings updated successfully!'); + update(); + } else { + error('Failed to update settings'); + } + }; + }} + use:formValidation + > +
+ + {$_('Device Type')} + + {#if device?.cw_device_type?.name} + + {device.cw_device_type.name} + + {:else} + {$_('Unknown')} + {/if} +
+
+ + {$_('EUI')} + + {device?.dev_eui || $_('Unknown')} +
+
+ + {$_('Installed Date')} + + {device?.installed_at ? formatDateOnly(device.installed_at) : $_('Unknown')} +
+
+ + {$_('Coordinates')} + + {#if device?.lat && device?.long} + + {device.lat}, {device.long} + {:else} + {$_('Unknown')} + {/if} +
+
+ {#if isOwner} - + {:else} - {locations.find((loc) => loc.location_id === device?.location_id)?.name || - 'Unknown Location'} - {#if device?.location_id} - ({device.location_id}) + {device?.name} + {/if} +
+
+ + {#await data.locations} + {$_('Loading...')} + {:then locations} + {#if isOwner} + + {:else} + {locations.find((loc) => loc.location_id === device?.location_id)?.name || + 'Unknown Location'} + {#if device?.location_id} + ({device.location_id}) + {/if} {/if} + {/await} +
+
+ {#if isOwner} + {/if} - {/await} -
-
- {#if isOwner} - - {/if} -
- -
+
+ +
-{#if isOwner} - -
-

{$_('Dangerous Zone')}

-
- -
- - {#snippet title()} - {$_('Delete Device & Associated Data')} - {/snippet} - {#snippet body()} - {$_('delete_device_warning')} - {/snippet} - {#snippet footer()} - + {#if isOwner} +
+

{$_('Dangerous Zone')}

+
- {/snippet} -
-
-{/if} + + + {#snippet title()} + {$_('Delete Device & Associated Data')} + {/snippet} + {#snippet body()} + {$_('delete_device_warning')} + {/snippet} + {#snippet footer()} + + + {/snippet} + + + {/if} + diff --git a/src/routes/auth/+layout.svelte b/src/routes/auth/+layout.svelte new file mode 100644 index 00000000..13ce5c22 --- /dev/null +++ b/src/routes/auth/+layout.svelte @@ -0,0 +1,116 @@ + + +{@render children?.()} + + diff --git a/src/routes/auth/check-email/+page.svelte b/src/routes/auth/check-email/+page.svelte index de643205..268782f5 100644 --- a/src/routes/auth/check-email/+page.svelte +++ b/src/routes/auth/check-email/+page.svelte @@ -17,7 +17,7 @@ class="bg-background-light/30 dark:bg-background-dark/30 relative z-10 flex min-h-screen items-center justify-center px-4 py-12 sm:px-6 lg:px-8" >
-
diff --git a/src/routes/auth/error/+page.svelte b/src/routes/auth/error/+page.svelte index 41f81bfd..9253eb83 100644 --- a/src/routes/auth/error/+page.svelte +++ b/src/routes/auth/error/+page.svelte @@ -14,7 +14,7 @@ class="bg-background-light/30 dark:bg-background-dark/30 relative z-10 flex min-h-screen items-center justify-center p-5 transition-colors duration-300" >
@@ -43,15 +43,12 @@

-
diff --git a/src/routes/auth/login/+page.svelte b/src/routes/auth/login/+page.svelte index 49d4f620..198a755f 100644 --- a/src/routes/auth/login/+page.svelte +++ b/src/routes/auth/login/+page.svelte @@ -116,7 +116,7 @@

{$_('Login')}

@@ -133,10 +133,7 @@ class="mb-4 rounded-md bg-green-100 p-4 text-center text-green-700 dark:bg-green-900/30 dark:text-green-400" >

{successMessage}

-
@@ -219,10 +216,10 @@
@@ -235,30 +232,30 @@ class="mt-6 flex flex-col gap-2 border-t border-gray-200 pt-4 text-center text-sm text-gray-600 dark:border-gray-700 dark:text-gray-400" >
{/if} diff --git a/src/routes/auth/register/+page.svelte b/src/routes/auth/register/+page.svelte index 05d664e1..8e390e00 100644 --- a/src/routes/auth/register/+page.svelte +++ b/src/routes/auth/register/+page.svelte @@ -272,6 +272,8 @@ cancel(); return; } + + isSubmitting = true; const data = new FormData(event.currentTarget); try { @@ -429,7 +431,7 @@ class="bg-background-light/30 dark:bg-background-dark/30 relative z-10 flex min-h-screen items-center justify-center px-4 py-12 sm:px-6 lg:px-8" >

@@ -713,7 +715,7 @@