IDAA: inline Tailwind utilities — remove @apply style block (23 svelte-check warnings)

ae_idaa_comp__event_obj_id_edit.svelte: the component <style> block used
@reference + @apply for ~10 local classes (.section-card, .field-label,
.toggle-chip, .day-chip, etc.). svelte-check's CSS language service does
not understand Tailwind v4 @reference/@apply directives and emitted 23
'Unknown at rule' warnings.

Fix: all local class usages inlined as Tailwind utility strings directly
on each element (~80 template sites). The <style> block is removed.
Conditional classes on toggle-chip/day-chip converted to ternary expressions.

svelte-check now reports 0 errors and 0 warnings across all files.
This commit is contained in:
Scott Idem
2026-03-16 13:52:26 -04:00
parent 1291b225c6
commit b44e77ad62
3 changed files with 130 additions and 204 deletions

View File

@@ -75,6 +75,7 @@ lead record look like in the DB?
- **Input Field Audit:** Several input fields are missing `name`/`id` attributes or `data-testid`. Known examples: badge override fields in `ae_comp__badge_obj_view.svelte`; template name input in `ae_comp__badge_template_form.svelte`. Matters for: accessibility, autofill, label associations, and test targeting. (For tests, use `getByLabel()` rather than `input[value*=...]` which only checks the HTML attribute, not the Svelte-bound DOM property.)
## ✅ Completed (2026-03)
- [x] **[Launcher]** Hosted file download button `require_auth` prop — added `require_auth?: boolean` (default `true`) to `ae_comp__hosted_files_download_button.svelte`; all existing consumers unchanged. Launcher `launcher_file_cont.svelte` passes `require_auth={false}` so unauthenticated kiosk users can open/download files without being blocked. (2026-03-16)
- [x] **[Security]** `PUBLIC_AE_API_SECRET_KEY` audit complete. Key is `PUBLIC_*` by design (always in client bundle). Highest-risk anonymous path uses limited-permission `PUBLIC_AE_BOOTSTRAP_KEY`. Full server-side migration not justified given JWT + account_id auth layers. Current state acceptable. (2026-03-11)
- [x] **[UX]** Session Expired banner — `ae_auth_error` store wired to API helpers; root layout sets `flag_expired` on 401/403; non-blocking dismissible banner rendered. (2026-03-12)
- [x] **[UX]** Access Denied UI standardized — `element_access_denied.svelte` created; `/core` layout, `/events/settings`, and `/events/badges/review` updated to use it. (2026-03-12)

View File

@@ -1,6 +1,6 @@
{
"name": "osit-aether-app-svelte",
"version": "3.00.03",
"version": "3.00.04",
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
"homepage": "https://oneskyit.com/",
"private": true,

View File

@@ -748,16 +748,16 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
SECTION 1 — Meeting Information
Fields: name, description, type
============================================================ -->
<div class="section-card" id="section__general_information">
<h3 class="section-heading">
<div class="p-3 space-y-3 rounded-md bg-surface-100-900 border border-surface-200-800" id="section__general_information">
<h3 class="text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800">
<span class="fas fa-info-circle"></span>
Meeting Information
</h3>
<!-- Name -->
<label class="field-label" for="name">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="name">
Name of Recovery Meeting
<span class="field-required">*</span>
<span class="text-error-500 ml-0.5">*</span>
<input
type="text"
id="name"
@@ -767,12 +767,12 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.name ?? ''}
placeholder="Name of Recovery Meeting"
autocomplete="off"
class="field-input w-full"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-full"
/>
</label>
<!-- Description (rich text) -->
<label class="field-label" for="description">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="description">
Short Description
<AE_Comp_Editor_TipTap
bind:content={description_new_html}
@@ -783,13 +783,13 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Type -->
<div class="space-y-1">
<label class="field-label" for="type">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="type">
Type of Recovery Meeting
<select
id="type"
name="type"
value={$lq__event_obj?.type}
class="field-select w-52"
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
>
<option value="IDAA">IDAA</option>
<option value="Caduceus">Caduceus</option>
@@ -811,20 +811,19 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
Virtual platform fields shown when virtual=true OR more__virtual_fields=true.
attend_text is always shown (applies to both physical and virtual).
============================================================ -->
<div class="section-card" id="section__how_to_attend">
<h3 class="section-heading">
<div class="p-3 space-y-3 rounded-md bg-surface-100-900 border border-surface-200-800" id="section__how_to_attend">
<h3 class="text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800">
<span class="fas fa-map-marker-alt"></span>
How to Attend
</h3>
<!-- Physical / Virtual toggles -->
<fieldset>
<legend class="field-label mb-1">Is this meeting in-person, virtual, or both?</legend>
<legend class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300 mb-1">Is this meeting in-person, virtual, or both?</legend>
<div class="flex flex-row flex-wrap gap-2">
<label
for="physical"
class="toggle-chip"
class:toggle-chip--on={$idaa_slct.event_obj.physical}
class="flex items-center gap-2 px-3 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.physical ? 'preset-filled-success-200-800 border-success-400' : ''}"
>
<span class="fas fa-home"></span>
Face-to-Face / In Person
@@ -838,8 +837,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
</label>
<label
for="virtual"
class="toggle-chip"
class:toggle-chip--on={$idaa_slct.event_obj.virtual}
class="flex items-center gap-2 px-3 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.virtual ? 'preset-filled-success-200-800 border-success-400' : ''}"
>
<span class="fas fa-laptop"></span>
Virtual / Online
@@ -871,10 +869,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
{/if}
<div
class="subsection space-y-3"
class="pl-3 border-l-2 border-surface-300-700 space-y-3"
class:hidden={!$idaa_slct.event_obj?.physical && !$idaa_slct.event_obj?.more__location_fields}
>
<h4 class="subsection-heading">
<h4 class="text-sm font-semibold text-surface-600-400 flex items-center gap-1.5">
<span class="fas fa-home"></span> Physical Location
</h4>
@@ -882,7 +880,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<input type="hidden" name="address_location_id" value={$lq__event_obj?.address_location_id} />
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="address_name">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_name">
Location Name
<input
type="text"
@@ -893,11 +891,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.name
?? $lq__event_obj?.address_name
?? ''}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="address_line_1">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_line_1">
Address Line 1
<input
type="text"
@@ -908,11 +906,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.line_1
?? $lq__event_obj?.address_line_1
?? ''}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="address_line_2">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_line_2">
Address Line 2
<input
type="text"
@@ -923,11 +921,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.line_2
?? $lq__event_obj?.address_line_2
?? ''}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="address_line_3">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_line_3">
Address Line 3
<input
type="text"
@@ -938,11 +936,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.line_3
?? $lq__event_obj?.address_line_3
?? ''}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="address_city">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_city">
City
<input
type="text"
@@ -953,11 +951,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.city
?? $lq__event_obj?.address_city
?? ''}
class="field-input w-44"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-44"
/>
</label>
<label class="field-label" for="address_country_subdivision_code">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_country_subdivision_code">
State / Province
{#if lu_country_subdivision_list?.length}
<select
@@ -966,7 +964,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.country_subdivision_code
?? $lq__event_obj?.address_country_subdivision_code
?? ''}
class="field-select w-56"
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-56"
>
<option value="">-- None --</option>
{#each lu_country_subdivision_list as sub, i (i)}
@@ -983,12 +981,12 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.country_subdivision_code
?? $lq__event_obj?.address_country_subdivision_code
?? ''}
class="field-input w-44"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-44"
/>
{/if}
</label>
<label class="field-label" for="address_postal_code">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_postal_code">
Postal / Zip Code
<input
type="text"
@@ -999,11 +997,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.postal_code
?? $lq__event_obj?.address_postal_code
?? ''}
class="field-input w-36"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
/>
</label>
<label class="field-label" for="address_country_alpha_2_code">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="address_country_alpha_2_code">
Country
{#if lu_country_list?.length}
<select
@@ -1012,7 +1010,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.country_alpha_2_code
?? $lq__event_obj?.address_country_alpha_2_code
?? ''}
class="field-select w-64"
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-64"
>
<option value="">-- None --</option>
{#each lu_country_list as country (country.alpha_2_code)}
@@ -1029,14 +1027,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
value={$lq__event_obj?.location_address_json?.country_alpha_2_code
?? $lq__event_obj?.address_country_alpha_2_code
?? ''}
class="field-input w-36"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
/>
{/if}
</label>
</div>
<!-- Additional location notes (rich text) -->
<label class="field-label" for="location_text">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="location_text">
Additional Location Information
<AE_Comp_Editor_TipTap
bind:content={location_text_new_html}
@@ -1063,10 +1061,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
{/if}
<div
class="subsection space-y-3"
class="pl-3 border-l-2 border-surface-300-700 space-y-3"
class:hidden={!$idaa_slct.event_obj.virtual && !$idaa_slct.event_obj?.more__virtual_fields}
>
<h4 class="subsection-heading">
<h4 class="text-sm font-semibold text-surface-600-400 flex items-center gap-1.5">
<span class="fas fa-laptop"></span> Virtual / Online
</h4>
@@ -1125,9 +1123,9 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
to {} afterward, leaving attend_json.zoom undefined. The bind:value bindings
below crash with a TypeError if zoom is undefined — the guard prevents that. -->
{#if $idaa_sess.recovery_meetings.attend_platform === 'Zoom' && $idaa_slct.event_obj.attend_json?.zoom}
<div class="platform-fields flex flex-row flex-wrap gap-3">
<label class="field-label" for="attend_url_code">
Zoom Meeting ID <span class="field-required">*</span>
<div class="p-2 rounded bg-surface-50-950 border border-surface-200-800 flex flex-row flex-wrap gap-3">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_code">
Zoom Meeting ID <span class="text-error-500 ml-0.5">*</span>
<input
type="number"
id="attend_url_code"
@@ -1142,10 +1140,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
$idaa_trig = 'update_zoom_full_url';
}
}}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="attend_url_passcode">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_passcode">
Zoom Passcode
<input
type="text"
@@ -1161,12 +1159,12 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
$idaa_slct.event_obj.attend_url_passcode = '';
}
}}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="attend_url_passcode_enc">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_passcode_enc">
Zoom Encrypted Passcode
<span class="field-hint">Found in the Zoom invite URL after <code>?pwd=</code></span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Found in the Zoom invite URL after <code>?pwd=</code></span>
<input
type="text"
id="attend_url_passcode_enc"
@@ -1179,12 +1177,12 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
$idaa_trig = 'update_zoom_full_url';
}
}}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="attend_domain">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_domain">
Zoom Domain / Vanity URL
<span class="field-hint">Default: zoom.us — change only if using a custom domain</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Default: zoom.us — change only if using a custom domain</span>
<input
type="text"
id="attend_domain"
@@ -1199,12 +1197,12 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
: 'zoom.us';
$idaa_trig = 'update_zoom_full_url';
}}
class="field-input w-56"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-56"
/>
</label>
<label class="field-label w-full" for="zoom_attend_url">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300 w-full" for="zoom_attend_url">
Generated Zoom Meeting URL
<span class="field-hint">Auto-generated from the fields above — do not edit directly</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Auto-generated from the fields above — do not edit directly</span>
<input
type="url"
id="zoom_attend_url"
@@ -1212,7 +1210,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="url"
readonly
bind:value={$idaa_slct.event_obj.attend_json.zoom.full_url}
class="field-input w-full font-mono text-sm"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-full font-mono text-sm"
placeholder="https://zoom.us/j/..."
/>
</label>
@@ -1220,8 +1218,8 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Jitsi fields -->
{:else if $idaa_sess.recovery_meetings.attend_platform === 'Jitsi'}
<div class="platform-fields flex flex-row flex-wrap gap-3">
<label class="field-label" for="attend_url_code">
<div class="p-2 rounded bg-surface-50-950 border border-surface-200-800 flex flex-row flex-wrap gap-3">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_code">
Jitsi Meeting Name / Code
<input
type="text"
@@ -1230,10 +1228,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="off"
placeholder="Meeting room name"
bind:value={$idaa_slct.event_obj.attend_json.jitsi.name}
class="field-input w-72"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72"
/>
</label>
<label class="field-label" for="attend_url_passcode">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_passcode">
Jitsi Passcode
<input
type="text"
@@ -1242,10 +1240,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="off"
placeholder="Optional passcode"
bind:value={$idaa_slct.event_obj.attend_json.jitsi.passcode}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label w-full" for="attend_url">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300 w-full" for="attend_url">
Jitsi Meeting URL
<input
type="url"
@@ -1254,15 +1252,15 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="url"
placeholder="https://meet.jit.si/..."
bind:value={$idaa_slct.event_obj.attend_json.jitsi.full_url}
class="field-input w-full font-mono text-sm"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-full font-mono text-sm"
/>
</label>
</div>
<!-- Other / Generic virtual platform fields -->
{:else}
<div class="platform-fields flex flex-row flex-wrap gap-3">
<label class="field-label w-full" for="attend_url">
<div class="p-2 rounded bg-surface-50-950 border border-surface-200-800 flex flex-row flex-wrap gap-3">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300 w-full" for="attend_url">
Meeting URL
<input
type="url"
@@ -1271,10 +1269,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="url"
placeholder="URL to join the meeting"
value={$lq__event_obj?.attend_url ?? ''}
class="field-input w-full font-mono text-sm"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-full font-mono text-sm"
/>
</label>
<label class="field-label" for="attend_url_passcode">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_url_passcode">
URL Passcode
<input
type="text"
@@ -1283,10 +1281,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="off"
placeholder="Passcode to join at URL"
value={$lq__event_obj?.attend_url_passcode ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="attend_phone">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_phone">
Dial-In Phone Number
<input
type="tel"
@@ -1295,10 +1293,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Phone number for dial-in"
value={$lq__event_obj?.attend_phone ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="attend_phone_passcode">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_phone_passcode">
Dial-In Passcode
<input
type="text"
@@ -1307,7 +1305,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="off"
placeholder="Passcode for dial-in"
value={$lq__event_obj?.attend_phone_passcode ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
</div>
@@ -1315,9 +1313,9 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
</div>
<!-- ── Additional attendance notes (applies to any meeting type) ── -->
<label class="field-label" for="attend_text">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="attend_text">
Additional Attendance Information
<span class="field-hint">Any extra instructions for joining — shown to all members regardless of meeting type</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Any extra instructions for joining — shown to all members regardless of meeting type</span>
<AE_Comp_Editor_TipTap
bind:content={attend_text_new_html}
class_li="field-richtext"
@@ -1334,20 +1332,20 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
recurring_text is auto-generated with *gen* prefix if left blank.
Members only see custom recurring_text (no *gen*); admins see both.
============================================================ -->
<div class="section-card" id="section__schedule">
<h3 class="section-heading">
<div class="p-3 space-y-3 rounded-md bg-surface-100-900 border border-surface-200-800" id="section__schedule">
<h3 class="text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800">
<span class="fas fa-calendar-alt"></span>
Schedule
</h3>
<!-- Recurring pattern -->
<label class="field-label" for="recurring_pattern">
<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}
class="field-select w-48"
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-48"
>
<option value="weekly">Weekly</option>
<option value="every other week">Every Other Week</option>
@@ -1359,7 +1357,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Days of week — toggle chips so selected days are immediately visible,
matching how they appear as inline text in the list and detail views -->
<fieldset>
<legend class="field-label mb-1">Days of Week</legend>
<legend class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300 mb-1">Days of Week</legend>
<div class="flex flex-row flex-wrap gap-2">
{#each [
{ name: 'weekday_sunday', label: 'Sun' },
@@ -1372,8 +1370,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
] as day (day.name)}
<label
for={day.name}
class="day-chip"
class:day-chip--on={$idaa_slct.event_obj[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' : ''}"
>
{day.label}
<input
@@ -1391,15 +1388,15 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Timezone + Start / End time — grouped together since they form a single
logical "when does this meeting happen" unit displayed together in the view -->
<div class="flex flex-row flex-wrap gap-3 items-end">
<label class="field-label" for="timezone">
Timezone <span class="field-required">*</span>
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="timezone">
Timezone <span class="text-error-500 ml-0.5">*</span>
{#if $ae_loc?.lu_time_zone_list?.length}
<select
id="timezone"
name="timezone"
required
value={$lq__event_obj?.timezone ?? $ae_loc?.current_timezone}
class="field-select w-56"
class="select p-1 preset-tonal-surface hover:preset-filled-surface-100-900 w-56"
>
<option value="" class:hidden={!$ae_loc.trusted_access}>-- None --</option>
{#each $ae_loc.lu_time_zone_list as tz (tz.name)}
@@ -1413,30 +1410,30 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
name="timezone"
required
value={$lq__event_obj?.timezone ?? $ae_loc?.current_timezone ?? ''}
class="field-input w-44"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-44"
/>
{/if}
</label>
<label class="field-label" for="recurring_start_time">
<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}
class="field-input w-36"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
/>
</label>
<label class="field-label" for="recurring_end_time">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="recurring_end_time">
End Time
<input
type="time"
id="recurring_end_time"
name="recurring_end_time"
value={$lq__event_obj?.recurring_end_time}
class="field-input w-36"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-36"
/>
</label>
</div>
@@ -1448,10 +1445,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
The "Add More Details?" button promotes the auto-generated text to
a custom editable value by stripping the *gen* prefix. -->
{#if $ae_loc.administrator_access || ($lq__event_obj && ($idaa_slct.event_obj?.show_recurring_text || ($lq__event_obj?.recurring_text && !$lq__event_obj?.recurring_text.includes('*gen*'))))}
<div class="subsection space-y-2">
<label class="field-label" for="recurring_text">
<div class="pl-3 border-l-2 border-surface-300-700 space-y-2">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="recurring_text">
Additional Schedule Details
<span class="field-hint">Optional — use only if the options above don't fully describe the schedule. This text is shown directly to members.</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Optional — use only if the options above don't fully describe the schedule. This text is shown directly to members.</span>
<AE_Comp_Editor_TipTap
bind:content={recurring_text_new_html}
class_li="field-richtext"
@@ -1500,8 +1497,8 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
Contact 1 name/email are locked by default for members to prevent accidental
override of the meeting owner's identity.
============================================================ -->
<div class="section-card" id="section__contacts">
<h3 class="section-heading">
<div class="p-3 space-y-3 rounded-md bg-surface-100-900 border border-surface-200-800" id="section__contacts">
<h3 class="text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800">
<span class="fas fa-address-book"></span>
Contacts
</h3>
@@ -1509,9 +1506,9 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Meeting Owner (external_person_id = Novi UUID of owner) -->
<!-- Trusted+ can reassign ownership; members see it as read-only context. -->
{#if $ae_loc.trusted_access}
<label class="field-label" for="external_person_id">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="external_person_id">
Meeting Owner — Novi UUID
<span class="field-hint">The Novi UUID of the IDAA member who owns this meeting. Determines who may edit it.</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">The Novi UUID of the IDAA member who owns this meeting. Determines who may edit it.</span>
<input
type="text"
id="external_person_id"
@@ -1519,14 +1516,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="off"
placeholder="Novi UUID"
value={$lq__event_obj?.external_person_id ?? $idaa_loc.novi_uuid ?? ''}
class="field-input w-72 font-mono text-sm"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-72 font-mono text-sm"
/>
</label>
{:else}
<!-- Hidden input passes the owner UUID through form submission unchanged -->
<input type="hidden" name="external_person_id" value={$lq__event_obj?.external_person_id ?? $idaa_loc.novi_uuid ?? ''} />
<div class="space-y-1">
<p class="field-label">Meeting Owner</p>
<p class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300">Meeting Owner</p>
<div class="flex items-center gap-2 text-sm text-surface-600-400">
<span class="fas fa-lock text-xs"></span>
{#if $lq__event_obj?.external_person_full_name}
@@ -1547,9 +1544,9 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
locked readonly for members. This is intentional: the primary contact
for a meeting is nearly always the owner. Members unlock via confirm
if they truly need different contact details. -->
<div class="subsection space-y-3">
<div class="pl-3 border-l-2 border-surface-300-700 space-y-3">
<div class="flex items-center gap-2 flex-wrap">
<h4 class="subsection-heading">
<h4 class="text-sm font-semibold text-surface-600-400 flex items-center gap-1.5">
<span class="fas fa-user"></span> Contact 1 (Primary)
</h4>
{#if !$ae_loc.trusted_access && contact_1_locked}
@@ -1576,7 +1573,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
</div>
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="contact_1_full_name">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_1_full_name">
Full Name
<input
type="text"
@@ -1586,11 +1583,11 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
placeholder="Full name"
readonly={!$ae_loc.trusted_access && contact_1_locked}
value={$lq__event_obj?.contact_li_json?.[0]?.full_name ?? $idaa_loc.novi_full_name ?? ''}
class="field-input w-64"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-64"
class:opacity-60={!$ae_loc.trusted_access && contact_1_locked}
/>
</label>
<label class="field-label" for="contact_1_email">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_1_email">
Email
<input
type="email"
@@ -1600,14 +1597,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
placeholder="Email address"
readonly={!$ae_loc.trusted_access && contact_1_locked}
value={$lq__event_obj?.contact_li_json?.[0]?.email ?? $idaa_loc.novi_email ?? ''}
class="field-input w-64"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-64"
class:opacity-60={!$ae_loc.trusted_access && contact_1_locked}
/>
</label>
</div>
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="contact_1_phone_mobile">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_1_phone_mobile">
Mobile Phone
<input
type="tel"
@@ -1616,10 +1613,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Mobile / Cell"
value={$lq__event_obj?.contact_li_json?.[0]?.phone_mobile ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="contact_1_phone_home">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_1_phone_home">
Home Phone
<input
type="tel"
@@ -1628,10 +1625,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Home phone"
value={$lq__event_obj?.contact_li_json?.[0]?.phone_home ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="contact_1_phone_office">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_1_phone_office">
Office Phone
<input
type="tel"
@@ -1640,28 +1637,28 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Office phone"
value={$lq__event_obj?.contact_li_json?.[0]?.phone_office ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
</div>
</div>
<!-- ── Contact 2 (Optional secondary) ────────────────────── -->
<div class="subsection space-y-3">
<div class="pl-3 border-l-2 border-surface-300-700 space-y-3">
<button
type="button"
class="flex flex-row items-center gap-2 text-left w-full"
onclick={() => (show_contact_2 = !show_contact_2)}
>
<span class="fas fa-{show_contact_2 ? 'caret-down' : 'caret-right'} text-xs opacity-60"></span>
<h4 class="subsection-heading mb-0!">
<h4 class="text-sm font-semibold text-surface-600-400 flex items-center gap-1.5 mb-0!">
<span class="fas fa-user-plus"></span> Contact 2 (Optional)
</h4>
</button>
{#if show_contact_2}
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="contact_2_full_name">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_2_full_name">
Full Name
<input
type="text"
@@ -1670,10 +1667,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="name"
placeholder="Full name"
value={$lq__event_obj?.contact_li_json?.[1]?.full_name ?? ''}
class="field-input w-64"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-64"
/>
</label>
<label class="field-label" for="contact_2_email">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_2_email">
Email
<input
type="email"
@@ -1682,13 +1679,13 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="email"
placeholder="Email address"
value={$lq__event_obj?.contact_li_json?.[1]?.email ?? ''}
class="field-input w-64"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-64"
/>
</label>
</div>
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="contact_2_phone_mobile">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_2_phone_mobile">
Mobile Phone
<input
type="tel"
@@ -1697,10 +1694,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Mobile / Cell"
value={$lq__event_obj?.contact_li_json?.[1]?.phone_mobile ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="contact_2_phone_home">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_2_phone_home">
Home Phone
<input
type="tel"
@@ -1709,10 +1706,10 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Home phone"
value={$lq__event_obj?.contact_li_json?.[1]?.phone_home ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
<label class="field-label" for="contact_2_phone_office">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="contact_2_phone_office">
Office Phone
<input
type="tel"
@@ -1721,7 +1718,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
autocomplete="tel"
placeholder="Office phone"
value={$lq__event_obj?.contact_li_json?.[1]?.phone_office ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
</div>
@@ -1743,14 +1740,14 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
Fields: status, hide, priority, sort, group, enable, notes
============================================================ -->
{#if $ae_loc.trusted_access}
<div class="section-card section-card--admin" id="section__admin_options">
<div class="p-3 space-y-3 rounded-md bg-surface-100-900 border border-surface-200-800 border-error-400" id="section__admin_options">
<button
type="button"
class="flex flex-row items-center gap-2 text-left w-full"
onclick={() => (show_admin_options = !show_admin_options)}
>
<span class="fas fa-{show_admin_options ? 'caret-down' : 'caret-right'} text-xs opacity-60"></span>
<h3 class="section-heading mb-0!">
<h3 class="text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800 mb-0!">
<span class="fas fa-cogs"></span>
Admin Options
</h3>
@@ -1758,7 +1755,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
{#if show_admin_options}
<!-- Status -->
<label class="field-label" for="status">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="status">
Status
<input
type="text"
@@ -1766,7 +1763,7 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
name="status"
placeholder="e.g. active, pending, closed"
value={$lq__event_obj?.status ?? ''}
class="field-input w-52"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-52"
/>
</label>
@@ -1806,36 +1803,36 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
<!-- Sort + Group -->
<div class="flex flex-row flex-wrap gap-3">
<label class="field-label" for="sort">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="sort">
Sort Order
<span class="field-hint">Lower numbers appear first</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Lower numbers appear first</span>
<input
type="number"
id="sort"
name="sort"
placeholder="0"
value={$lq__event_obj?.sort ?? ''}
class="field-input w-28"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-28"
/>
</label>
<label class="field-label" for="group">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="group">
Group
<span class="field-hint">Optional grouping label</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Optional grouping label</span>
<input
type="text"
id="group"
name="group"
placeholder="e.g. North America"
value={$lq__event_obj?.group ?? ''}
class="field-input w-48"
class="input preset-tonal-surface hover:preset-filled-surface-100-900 w-48"
/>
</label>
</div>
<!-- Notes — internal, not visible to members -->
<label class="field-label" for="notes">
<label class="flex flex-col gap-1 text-sm font-semibold text-surface-700-300" for="notes">
Admin Notes
<span class="field-hint">Internal only — not visible to members</span>
<span class="block text-xs font-normal text-surface-500 -mt-0.5">Internal only — not visible to members</span>
<AE_Comp_Editor_TipTap
bind:content={notes_new_html}
class_li="field-richtext"
@@ -1939,76 +1936,4 @@ Copy and paste link: <a href="${link_base_url}?event_id=${event_do.event_id}">${
</form>
</section>
<style>
/* @reference tells Tailwind v4 where to resolve @apply utilities in component styles */
@reference "../../../../app.css";
.section-card {
@apply p-3 space-y-3 rounded-md;
@apply bg-surface-100-900 border border-surface-200-800;
}
.section-card--admin {
@apply border-error-400;
}
.section-heading {
@apply text-lg font-semibold text-surface-600-400 flex items-center gap-2 pb-1 border-b border-surface-200-800;
}
.field-label {
@apply flex flex-col gap-1 text-sm font-semibold text-surface-700-300;
}
.field-required {
@apply text-error-500 ml-0.5;
}
.field-input {
@apply input preset-tonal-surface hover:preset-filled-surface-100-900;
}
.field-select {
@apply select p-1 preset-tonal-surface hover:preset-filled-surface-100-900;
}
.field-hint {
@apply block text-xs font-normal text-surface-500 -mt-0.5;
}
.subsection {
@apply pl-3 border-l-2 border-surface-300-700;
}
.subsection-heading {
@apply text-sm font-semibold text-surface-600-400 flex items-center gap-1.5;
}
.platform-fields {
@apply p-2 rounded bg-surface-50-950 border border-surface-200-800;
}
/* Toggle chip — used for physical/virtual checkboxes */
.toggle-chip {
@apply flex items-center gap-2 px-3 py-2 rounded-lg cursor-pointer;
@apply text-sm font-semibold;
@apply preset-tonal-surface border border-surface-300-700;
@apply hover:preset-filled-surface-200-800 transition-all;
}
.toggle-chip--on {
@apply preset-filled-success-200-800 border-success-400;
}
/* Day-of-week chip — compact version of toggle-chip for the 7-day row */
.day-chip {
@apply flex items-center justify-center w-12 py-2 rounded-lg cursor-pointer;
@apply text-sm font-semibold;
@apply preset-tonal-surface border border-surface-300-700;
@apply hover:preset-filled-surface-200-800 transition-all;
}
.day-chip--on {
@apply preset-filled-primary-200-800 border-primary-400;
}
</style>