feat(pres_mgmt): replace time_hours/time_format/datetime_format with single use_12h toggle

Three redundant store fields encoding the same AM/PM choice replaced with a single
`use_12h: boolean` in PresMgmtLocState. iso_datetime_formatter gains a third param
(use_12h: boolean | null = null) that auto-resolves 24h↔12h format name variants via
a symmetric FORMAT_PAIRS lookup — null default leaves all ~100 existing call sites intact.

Toggle surfaces in three places: Clock icon in session time chip (hidden button, same
visual), event Options modal Display section, and session Options modal Display section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-05-15 14:29:57 -04:00
parent 1296b1077e
commit 631a77158c
6 changed files with 104 additions and 33 deletions

View File

@@ -1,9 +1,33 @@
import dayjs from 'dayjs';
// Format pairs: [24h base, 12h variant]. Only formats with both variants are listed.
// Formats without a counterpart (ISO, date-only, week, etc.) are intentionally omitted —
// iso_datetime_formatter passes those through unchanged regardless of use_12h.
const FORMAT_PAIRS: [string, string][] = [
['datetime_iso_no_seconds', 'datetime_iso_12_no_seconds'],
['datetime_short', 'datetime_12_short'],
['datetime_medium', 'datetime_12_medium'],
['datetime_long', 'datetime_12_long'],
['datetime_medium_sec', 'datetime_12_medium_sec'],
['time_long', 'time_12_long'],
['time_short', 'time_12_short'],
['time_short_no_leading', 'time_12_short_no_leading'],
];
// Build lookup maps from the pairs above. Both directions are derived from the same source.
const TO_12H: Record<string, string> = Object.fromEntries(
FORMAT_PAIRS.map(([h24, h12]) => [h24, h12])
);
const TO_24H: Record<string, string> = Object.fromEntries(
FORMAT_PAIRS.map(([h24, h12]) => [h12, h24])
);
export const iso_datetime_formatter = function iso_datetime_formatter(
raw_datetime: null | string | Date = null,
named_format: string = 'datetime_iso_no_seconds', // date_iso, datetime_iso_no_seconds
time_24_hours: boolean = false
// Pass true/false to resolve to the correct 12h or 24h variant automatically.
// null (default) leaves named_format unchanged — all existing call sites unaffected.
use_12h: boolean | null = null
) {
// console.log('*** iso_datetime_formatter() ***');
@@ -50,6 +74,12 @@ export const iso_datetime_formatter = function iso_datetime_formatter(
raw_datetime = new Date(); // Get the current datetime if one was not passed.
}
if (use_12h !== null) {
named_format = use_12h
? (TO_12H[named_format] ?? named_format)
: (TO_24H[named_format] ?? named_format);
}
let datetime_string = null;
switch (named_format) {

View File

@@ -84,9 +84,7 @@ export interface PresMgmtLocState {
lock_config: boolean;
// --- Query / search preferences ---
datetime_format: string;
time_format: string;
time_hours: 12 | 24;
use_12h: boolean;
qry_enabled: 'all' | 'not_enabled' | 'enabled';
qry_hidden: 'all' | 'hidden' | 'not_hidden';
qry_limit__files: number;
@@ -265,9 +263,7 @@ export const pres_mgmt_loc_defaults: PresMgmtLocState = {
lock_config: false,
// Query / search
datetime_format: 'datetime_12_long',
time_format: 'time_12_short',
time_hours: 12,
use_12h: true,
qry_enabled: 'enabled',
qry_hidden: 'not_hidden',
qry_limit__files: 75,

View File

@@ -10,6 +10,7 @@ import { goto } from '$app/navigation';
import { Modal } from 'flowbite-svelte';
import {
Archive,
Clock,
Info,
MapPin,
Plane,
@@ -194,6 +195,25 @@ async function on_delete(method: 'delete' | 'disable') {
</span>
</button>
<button
type="button"
onclick={() => {
pres_mgmt_loc.current.use_12h = !pres_mgmt_loc.current.use_12h;
}}
class="btn btn-sm w-full justify-between"
class:ae_btn_surface={pres_mgmt_loc.current.use_12h}
class:ae_btn_surface_outlined={!pres_mgmt_loc.current.use_12h}>
{#if pres_mgmt_loc.current.use_12h}<ToggleRight
size="1em"
class="mr-1" />{:else}<ToggleLeft
size="1em"
class="mr-1" />{/if}
<span class="grow">
<Clock size="1em" class="mr-1" />
{pres_mgmt_loc.current.use_12h ? '12-Hour Time' : '24-Hour Time'}
</span>
</button>
<!-- <button
type="button"
onclick={() => {

View File

@@ -15,9 +15,10 @@ let {
import { goto } from '$app/navigation';
import { Modal } from 'flowbite-svelte';
import { Info, Send, Settings, ToggleRight, X } from '@lucide/svelte';
import { Clock, Info, Send, Settings, ToggleLeft, ToggleRight, X } from '@lucide/svelte';
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
import { events_loc, events_slct } from '$lib/stores/ae_events_stores';
import { pres_mgmt_loc } from '$lib/stores/ae_events_stores__pres_mgmt.svelte';
import { events_func } from '$lib/ae_events/ae_events_functions';
import { api } from '$lib/api/api';
@@ -166,6 +167,32 @@ async function toggle_hide_launcher() {
{/snippet}
<div class="flex flex-col gap-4 p-4">
<!-- Display -->
<section>
<h4
class="text-surface-500 mb-2 text-xs font-semibold tracking-wider uppercase">
Display
</h4>
<button
type="button"
onclick={() => {
pres_mgmt_loc.current.use_12h = !pres_mgmt_loc.current.use_12h;
}}
class="btn btn-sm w-full justify-between"
class:ae_btn_surface={pres_mgmt_loc.current.use_12h}
class:ae_btn_surface_outlined={!pres_mgmt_loc.current.use_12h}>
{#if pres_mgmt_loc.current.use_12h}<ToggleRight
size="1em"
class="mr-1" />{:else}<ToggleLeft
size="1em"
class="mr-1" />{/if}
<span class="grow">
<Clock size="1em" class="mr-1" />
{pres_mgmt_loc.current.use_12h ? '12-Hour Time' : '24-Hour Time'}
</span>
</button>
</section>
<!-- Launcher Settings -->
{#if $ae_loc.trusted_access}
<section>

View File

@@ -220,8 +220,8 @@ $effect(() => {
</div>
{:else}
<!-- Skeleton placeholder while LiveQuery resolves -->
<div class="bg-surface-200-800 h-7 w-2/3 animate-pulse rounded">
</div>
<!-- <div class="bg-surface-200-800 h-7 w-2/3 animate-pulse rounded">
</div> -->
{/if}
<!-- Date/Time + Room as info chips -->
@@ -229,19 +229,28 @@ $effect(() => {
<div class="flex flex-wrap items-center gap-2">
<span
class="bg-primary-500/10 text-primary-700 dark:text-primary-300 inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold transition-colors duration-200">
<Clock size="1em" class="text-xs" />
<button
type="button"
onclick={() => (pres_mgmt_loc.current.use_12h = !pres_mgmt_loc.current.use_12h)}
title={pres_mgmt_loc.current.use_12h ? 'Switch to 24-hour time' : 'Switch to 12-hour time'}
class="cursor-pointer rounded focus-visible:ring-1 focus-visible:ring-current"
aria-label={pres_mgmt_loc.current.use_12h ? 'Switch to 24-hour time' : 'Switch to 12-hour time'}>
<Clock size="1em" class="text-xs" />
</button>
{ae_util.iso_datetime_formatter(
$lq__event_session_obj.start_datetime,
'dddd'
)},
{ae_util.iso_datetime_formatter(
$lq__event_session_obj.start_datetime,
pres_mgmt_loc.current.datetime_format
'datetime_long',
pres_mgmt_loc.current.use_12h
)}
&ndash;
{ae_util.iso_datetime_formatter(
$lq__event_session_obj.end_datetime,
pres_mgmt_loc.current.time_format
'time_short',
pres_mgmt_loc.current.use_12h
)}
</span>
</div>
@@ -270,7 +279,7 @@ $effect(() => {
})}>
<span class="text-xs font-semibold opacity-60">
<Clock size="0.9em" class="inline mr-1" />Start:
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, pres_mgmt_loc.current.datetime_format)}
{ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, 'datetime_long', pres_mgmt_loc.current.use_12h)}
</span>
</Element_ae_obj_field_editor>
</div>
@@ -290,7 +299,7 @@ $effect(() => {
})}>
<span class="text-xs font-semibold opacity-60">
<Clock size="0.9em" class="inline mr-1" />End:
{ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, pres_mgmt_loc.current.datetime_format)}
{ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, 'datetime_long', pres_mgmt_loc.current.use_12h)}
</span>
</Element_ae_obj_field_editor>
</div>

View File

@@ -171,11 +171,10 @@ import {
'dddd'
)}
@
<!-- , -->
<!-- {ae_util.iso_datetime_formatter(event_presentation_obj.start_datetime, pres_mgmt_loc.current.datetime_format)} -->
{ae_util.iso_datetime_formatter(
event_presentation_obj.start_datetime,
pres_mgmt_loc.current.time_format
'time_short',
pres_mgmt_loc.current.use_12h
)}
</span>
@@ -247,19 +246,7 @@ import {
<button
type="button"
onclick={() => {
if (pres_mgmt_loc.current.time_hours == 12) {
pres_mgmt_loc.current.time_hours = 24;
pres_mgmt_loc.current.datetime_format =
'datetime_long';
pres_mgmt_loc.current.time_format =
'time_short';
} else {
pres_mgmt_loc.current.time_hours = 12;
pres_mgmt_loc.current.datetime_format =
'datetime_12_long';
pres_mgmt_loc.current.time_format =
'time_12_short';
}
pres_mgmt_loc.current.use_12h = !pres_mgmt_loc.current.use_12h;
}}>
time
</button>
@@ -283,7 +270,8 @@ import {
)}
{ae_util.iso_datetime_formatter(
event_presentation_obj.start_datetime,
pres_mgmt_loc.current.time_format
'time_short',
pres_mgmt_loc.current.use_12h
)}
</Element_ae_obj_field_editor>
-
@@ -301,7 +289,8 @@ import {
})}>
{ae_util.iso_datetime_formatter(
event_presentation_obj.end_datetime,
pres_mgmt_loc.current.time_format
'time_short',
pres_mgmt_loc.current.use_12h
)}
</Element_ae_obj_field_editor>
</div>