fix(idaa/recovery_meetings): fix weekday chips, recurring fields, and timezone lookup
- Weekday chips: replace bind:checked (unreliable with dynamic bracket notation in
{#each}) with explicit onchange handlers + class: directives; read weekdays from
state in submit handler instead of FormData
- Recurring pattern/times: bind select and time inputs to working copy
so values display and edit correctly
- Times clearing: map empty string to null so times can be cleared once set
- liveQuery guard: skip event_obj sync while edit form is open to prevent
background refresh from overwriting in-progress user changes
- Timezone lookup: forward order_by_li, limit, offset through the full call chain
so priority sort and result count params are actually sent to the API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,9 @@ export async function get_ae_lookup_li_v3({
|
||||
for_id,
|
||||
include_disabled = false,
|
||||
only_priority = false,
|
||||
order_by_li = null,
|
||||
limit = null,
|
||||
offset = null,
|
||||
params = {},
|
||||
headers = {},
|
||||
log_lvl = 0
|
||||
@@ -27,6 +30,9 @@ export async function get_ae_lookup_li_v3({
|
||||
for_id?: string;
|
||||
include_disabled?: boolean;
|
||||
only_priority?: boolean;
|
||||
order_by_li?: Record<string, 'ASC' | 'DESC'> | null;
|
||||
limit?: number | null;
|
||||
offset?: number | null;
|
||||
params?: key_val;
|
||||
headers?: Record<string, string>;
|
||||
log_lvl?: number;
|
||||
@@ -43,6 +49,9 @@ export async function get_ae_lookup_li_v3({
|
||||
if (for_id) params['for_id'] = for_id;
|
||||
if (include_disabled) params['include_disabled'] = true;
|
||||
if (only_priority) params['only_priority'] = true;
|
||||
if (order_by_li) params['order_by_li'] = JSON.stringify(order_by_li);
|
||||
if (limit != null) params['limit'] = limit;
|
||||
if (offset != null) params['offset'] = offset;
|
||||
|
||||
// Lookup data is often global; ensure account context is handled if needed,
|
||||
// but GUIDE says it uses site Whitelist Policy.
|
||||
|
||||
@@ -48,6 +48,7 @@ export async function load_ae_obj_li__time_zone({
|
||||
hidden: hidden,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
order_by_li: order_by_li,
|
||||
params: params,
|
||||
only_priority: only_priority,
|
||||
log_lvl: log_lvl
|
||||
|
||||
@@ -80,6 +80,9 @@ export const get_ae_obj_li_for_lu = async function get_ae_obj_li_for_lu({
|
||||
lu_type: for_lu_type,
|
||||
include_disabled: enabled === 'all',
|
||||
only_priority: only_priority,
|
||||
order_by_li: order_by_li ?? undefined,
|
||||
limit: limit ?? undefined,
|
||||
offset: offset ?? undefined,
|
||||
params,
|
||||
headers: merged_headers,
|
||||
log_lvl
|
||||
|
||||
@@ -63,8 +63,10 @@
|
||||
}
|
||||
let results = await db_events.event.get($idaa_slct?.event_id ?? ''); // null or undefined does not reset things like '' does
|
||||
|
||||
// Check if results are different than the current $idaa_slct.event_obj
|
||||
if ($idaa_slct.event_obj && results) {
|
||||
// Check if results are different than the current $idaa_slct.event_obj.
|
||||
// Skip the sync while the edit form is open — overwriting would discard
|
||||
// the user's in-progress changes (e.g. weekday chips, pattern, times).
|
||||
if ($idaa_slct.event_obj && results && !$idaa_sess.recovery_meetings.edit__event_obj) {
|
||||
if (
|
||||
JSON.stringify($idaa_slct.event_obj) !==
|
||||
JSON.stringify(results)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}: Props = $props();
|
||||
|
||||
// *** Import Svelte specific
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, untrack } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { browser } from '$app/environment';
|
||||
import { goto } from '$app/navigation';
|
||||
@@ -72,6 +72,33 @@
|
||||
|
||||
let disable_submit_btn = $state(true);
|
||||
|
||||
// Weekday chip state — local $state is required because Svelte 5 does not reliably
|
||||
// propagate reactivity through dynamic bracket notation ($store[dynamicKey]) on
|
||||
// writable stores. Direct property access (.physical, .virtual) works fine; loop-variable
|
||||
// bracket access does not trigger class re-renders. $state is deep-reactive and does.
|
||||
const WEEKDAY_KEYS = [
|
||||
'weekday_sunday', 'weekday_monday', 'weekday_tuesday', 'weekday_wednesday',
|
||||
'weekday_thursday', 'weekday_friday', 'weekday_saturday'
|
||||
] as const;
|
||||
let weekdays = $state<Record<string, boolean>>({
|
||||
weekday_sunday: false, weekday_monday: false, weekday_tuesday: false,
|
||||
weekday_wednesday: false, weekday_thursday: false, weekday_friday: false,
|
||||
weekday_saturday: false
|
||||
});
|
||||
// Seed once from liveQuery when data first arrives. Plain JS flag (not $state) so
|
||||
// it doesn't cause the effect to re-run after the one-time init.
|
||||
let weekdays_initialized = false;
|
||||
$effect(() => {
|
||||
if (!weekdays_initialized && $lq__event_obj?.event_id) {
|
||||
untrack(() => {
|
||||
for (const key of WEEKDAY_KEYS) {
|
||||
weekdays[key] = !!$lq__event_obj[key];
|
||||
}
|
||||
weekdays_initialized = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Contact 1 is locked by default on existing meetings for non-trusted users.
|
||||
// This prevents accidental override of the meeting owner's contact info.
|
||||
// Trusted+ users see no lock UI; new meetings start unlocked.
|
||||
@@ -406,21 +433,21 @@
|
||||
event_do['recurring'] = true;
|
||||
event_do['recurring_pattern'] = event_meeting_fd.recurring_pattern;
|
||||
|
||||
// !! converts to boolean based on truthiness
|
||||
event_do['weekday_sunday'] = !!event_meeting_fd.weekday_sunday;
|
||||
event_do['weekday_monday'] = !!event_meeting_fd.weekday_monday;
|
||||
event_do['weekday_tuesday'] = !!event_meeting_fd.weekday_tuesday;
|
||||
event_do['weekday_wednesday'] = !!event_meeting_fd.weekday_wednesday;
|
||||
event_do['weekday_thursday'] = !!event_meeting_fd.weekday_thursday;
|
||||
event_do['weekday_friday'] = !!event_meeting_fd.weekday_friday;
|
||||
event_do['weekday_saturday'] = !!event_meeting_fd.weekday_saturday;
|
||||
// Read weekdays from local $state (not FormData) — bind:checked with dynamic bracket
|
||||
// notation in {#each} is unreliable in Svelte 5; explicit onchange handlers update
|
||||
// the weekdays $state object directly, so read from there instead.
|
||||
event_do['weekday_sunday'] = weekdays.weekday_sunday;
|
||||
event_do['weekday_monday'] = weekdays.weekday_monday;
|
||||
event_do['weekday_tuesday'] = weekdays.weekday_tuesday;
|
||||
event_do['weekday_wednesday'] = weekdays.weekday_wednesday;
|
||||
event_do['weekday_thursday'] = weekdays.weekday_thursday;
|
||||
event_do['weekday_friday'] = weekdays.weekday_friday;
|
||||
event_do['weekday_saturday'] = weekdays.weekday_saturday;
|
||||
|
||||
if (event_meeting_fd['recurring_start_time']) {
|
||||
event_do['recurring_start_time'] = event_meeting_fd.recurring_start_time;
|
||||
}
|
||||
if (event_meeting_fd['recurring_end_time']) {
|
||||
event_do['recurring_end_time'] = event_meeting_fd.recurring_end_time;
|
||||
}
|
||||
// Send null explicitly so the user can clear a time once set.
|
||||
// An empty string from the time input means "no time" — map that to null.
|
||||
event_do['recurring_start_time'] = event_meeting_fd.recurring_start_time || null;
|
||||
event_do['recurring_end_time'] = event_meeting_fd.recurring_end_time || null;
|
||||
|
||||
if (typeof recurring_text_new_html === 'string') {
|
||||
event_do['recurring_text'] = recurring_text_new_html;
|
||||
@@ -620,8 +647,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
});
|
||||
}
|
||||
|
||||
// Track whether the form has unsaved changes relative to the original loaded object
|
||||
// Track whether the form has unsaved changes relative to the original loaded object.
|
||||
// weekdays is checked separately since it's a local $state, not part of $idaa_slct.event_obj.
|
||||
$effect(() => {
|
||||
const weekdays_changed = WEEKDAY_KEYS.some(k => !!weekdays[k] !== !!orig_event_obj?.[k]);
|
||||
if (orig_event_obj === null || orig_event_obj === undefined) {
|
||||
obj_changed = false;
|
||||
} else if (
|
||||
@@ -629,7 +658,8 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
orig_event_obj?.id &&
|
||||
(JSON.stringify($idaa_slct.event_obj) !== JSON.stringify(orig_event_obj) ||
|
||||
description_changed ||
|
||||
notes_changed)
|
||||
notes_changed ||
|
||||
weekdays_changed)
|
||||
) {
|
||||
obj_changed = true;
|
||||
} else if (
|
||||
@@ -637,7 +667,8 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
orig_event_obj?.id &&
|
||||
JSON.stringify($idaa_slct.event_obj) === JSON.stringify(orig_event_obj) &&
|
||||
!description_changed &&
|
||||
!notes_changed
|
||||
!notes_changed &&
|
||||
!weekdays_changed
|
||||
) {
|
||||
obj_changed = false;
|
||||
}
|
||||
@@ -1365,13 +1396,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
Schedule
|
||||
</h3>
|
||||
|
||||
<!-- Recurring pattern -->
|
||||
<!-- Recurring pattern — bind to working copy so user changes aren't
|
||||
overwritten when $lq__event_obj fires during editing -->
|
||||
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="recurring_pattern">
|
||||
Repeats
|
||||
<select
|
||||
id="recurring_pattern"
|
||||
name="recurring_pattern"
|
||||
value={$lq__event_obj?.recurring_pattern}
|
||||
bind:value={$idaa_slct.event_obj.recurring_pattern}
|
||||
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-48"
|
||||
>
|
||||
<option value="weekly">Weekly</option>
|
||||
@@ -1397,14 +1429,19 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
] as day (day.name)}
|
||||
<label
|
||||
for={day.name}
|
||||
class="flex items-center justify-center w-12 py-2 rounded-lg cursor-pointer text-sm font-semibold preset-tonal-surface border border-surface-300-700 hover:preset-filled-surface-200-800 transition-all {$idaa_slct.event_obj[day.name] ? 'preset-filled-primary-200-800 border-primary-400' : ''}"
|
||||
class="flex items-center justify-center w-12 py-2 rounded-lg cursor-pointer text-sm font-semibold border transition-all"
|
||||
class:preset-filled-primary-200-800={weekdays[day.name]}
|
||||
class:border-primary-400={weekdays[day.name]}
|
||||
class:preset-tonal-surface={!weekdays[day.name]}
|
||||
class:border-surface-300-700={!weekdays[day.name]}
|
||||
>
|
||||
{day.label}
|
||||
<input
|
||||
type="checkbox"
|
||||
id={day.name}
|
||||
name={day.name}
|
||||
bind:checked={$idaa_slct.event_obj[day.name]}
|
||||
checked={weekdays[day.name]}
|
||||
onchange={() => { weekdays[day.name] = !weekdays[day.name]; }}
|
||||
class="sr-only"
|
||||
/>
|
||||
</label>
|
||||
@@ -1442,13 +1479,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<!-- bind to working copy (same reason as recurring_pattern above) -->
|
||||
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="recurring_start_time">
|
||||
Start Time
|
||||
<input
|
||||
type="time"
|
||||
id="recurring_start_time"
|
||||
name="recurring_start_time"
|
||||
value={$lq__event_obj?.recurring_start_time}
|
||||
bind:value={$idaa_slct.event_obj.recurring_start_time}
|
||||
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
|
||||
/>
|
||||
</label>
|
||||
@@ -1459,7 +1497,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
|
||||
type="time"
|
||||
id="recurring_end_time"
|
||||
name="recurring_end_time"
|
||||
value={$lq__event_obj?.recurring_end_time}
|
||||
bind:value={$idaa_slct.event_obj.recurring_end_time}
|
||||
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
|
||||
/>
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user