Badges: center badge vertically + horizontally in print preview; scaffold print_margin_cfg
This commit is contained in:
192
src/lib/ae_elements/AE_Record_Controls.svelte
Normal file
192
src/lib/ae_elements/AE_Record_Controls.svelte
Normal file
@@ -0,0 +1,192 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* AE_Record_Controls.svelte
|
||||
* GENERIC Aether Record Management Controls
|
||||
* Manages: priority, hide, enable, alert, delete/disable
|
||||
*
|
||||
* Emits events — NO API calls. Parent is responsible for:
|
||||
* 1. Calling the API (update_ae_obj_v3, delete_ae_obj_id__*)
|
||||
* 2. Refreshing the object from cache/API
|
||||
* 3. Navigating away on delete
|
||||
*
|
||||
* Usage:
|
||||
* <AE_Record_Controls
|
||||
* obj={$lq__event_session_obj}
|
||||
* obj_label="session"
|
||||
* allow_delete={$ae_loc.manager_access}
|
||||
* allow_disable={$ae_loc.administrator_access}
|
||||
* on_toggle={(field, val) => { ... }}
|
||||
* on_delete={(method) => { ... }}
|
||||
* />
|
||||
*/
|
||||
import {
|
||||
Star,
|
||||
Eye,
|
||||
EyeOff,
|
||||
ToggleLeft,
|
||||
ToggleRight,
|
||||
Bell,
|
||||
BellOff,
|
||||
Trash2,
|
||||
MinusCircle,
|
||||
Settings
|
||||
} from '@lucide/svelte';
|
||||
|
||||
interface Props {
|
||||
// The object whose flags are being displayed (read-only — parent owns state)
|
||||
obj: any;
|
||||
|
||||
// Human-readable label for confirm dialogs ("session", "presenter", "location", etc.)
|
||||
obj_label?: string;
|
||||
|
||||
// Visibility — hide any control that doesn't apply for this object type
|
||||
show_alert?: boolean;
|
||||
show_priority?: boolean;
|
||||
show_enable?: boolean;
|
||||
show_hide?: boolean;
|
||||
show_labels?: boolean;
|
||||
|
||||
// Permission gates — parent passes booleans derived from $ae_loc
|
||||
allow_delete?: boolean; // Hard permanent delete (manager+)
|
||||
allow_disable?: boolean; // Soft disable/remove (administrator+)
|
||||
|
||||
// Callbacks — parent handles API + refresh + navigation
|
||||
on_toggle?: (field: string, new_val: boolean) => void;
|
||||
on_delete?: (method: 'delete' | 'disable') => void;
|
||||
|
||||
// Styling
|
||||
container_class?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
obj,
|
||||
obj_label = 'record',
|
||||
show_alert = true,
|
||||
show_priority = true,
|
||||
show_enable = true,
|
||||
show_hide = true,
|
||||
show_labels = true,
|
||||
allow_delete = false,
|
||||
allow_disable = false,
|
||||
on_toggle,
|
||||
on_delete,
|
||||
container_class = 'flex flex-row flex-wrap gap-1 items-center justify-evenly py-2 border-y border-surface-500/10'
|
||||
}: Props = $props();
|
||||
|
||||
function toggle(field: string) {
|
||||
if (on_toggle) on_toggle(field, !obj?.[field]);
|
||||
}
|
||||
|
||||
function handle_delete(method: 'delete' | 'disable') {
|
||||
const msg =
|
||||
method === 'delete'
|
||||
? `Permanently delete this ${obj_label}? This cannot be undone.`
|
||||
: `Remove (disable) this ${obj_label}?`;
|
||||
if (!confirm(msg)) return;
|
||||
if (on_delete) on_delete(method);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={container_class}>
|
||||
{#if show_labels}
|
||||
<span class="text-xs text-surface-500 flex items-center gap-1 uppercase font-bold tracking-wider mr-2">
|
||||
<Settings size="1.1em" /> Controls:
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<!-- Priority -->
|
||||
{#if show_priority}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggle('priority')}
|
||||
class="btn-icon btn-icon-sm transition"
|
||||
class:preset-filled-warning-500={obj?.priority}
|
||||
class:preset-tonal-secondary={!obj?.priority}
|
||||
class:hover:preset-filled-warning-500={!obj?.priority}
|
||||
title={obj?.priority ? 'Remove priority flag' : 'Mark as priority'}
|
||||
>
|
||||
<Star
|
||||
size="1.2em"
|
||||
class={obj?.priority ? 'fill-current' : 'opacity-50'}
|
||||
/>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- Hide / Visible -->
|
||||
{#if show_hide}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggle('hide')}
|
||||
class="btn-icon btn-icon-sm transition"
|
||||
class:preset-filled-warning-500={obj?.hide}
|
||||
class:preset-tonal-secondary={!obj?.hide}
|
||||
class:hover:preset-filled-warning-500={!obj?.hide}
|
||||
title={obj?.hide ? 'Unhide this record' : 'Hide this record'}
|
||||
>
|
||||
{#if obj?.hide}
|
||||
<EyeOff size="1.2em" class="text-warning-500" />
|
||||
{:else}
|
||||
<Eye size="1.2em" class="opacity-60" />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- Enable / Disable -->
|
||||
{#if show_enable}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggle('enable')}
|
||||
class="btn-icon btn-icon-sm transition"
|
||||
class:preset-filled-success-500={obj?.enable}
|
||||
class:preset-filled-error-500={!obj?.enable}
|
||||
class:hover:preset-filled-success-500={!obj?.enable}
|
||||
title={obj?.enable ? 'Disable this record' : 'Enable this record'}
|
||||
>
|
||||
{#if obj?.enable}
|
||||
<ToggleRight size="1.2em" class="text-success-300" />
|
||||
{:else}
|
||||
<ToggleLeft size="1.2em" class="text-error-300" />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- Alert -->
|
||||
{#if show_alert}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => toggle('alert')}
|
||||
class="btn-icon btn-icon-sm transition"
|
||||
class:preset-filled-error-500={obj?.alert}
|
||||
class:preset-tonal-secondary={!obj?.alert}
|
||||
class:hover:preset-filled-error-500={!obj?.alert}
|
||||
title={obj?.alert ? 'Remove alert status' : 'Mark as alert'}
|
||||
>
|
||||
{#if obj?.alert}
|
||||
<Bell size="1.2em" class="text-error-300" />
|
||||
{:else}
|
||||
<BellOff size="1.2em" class="opacity-40" />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- Delete / Disable buttons — only shown when permission granted -->
|
||||
{#if allow_delete}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_delete('delete')}
|
||||
class="btn-icon btn-icon-sm preset-filled-error-500 hover:preset-filled-error-600 transition"
|
||||
title="Permanently delete this {obj_label}"
|
||||
>
|
||||
<Trash2 size="1.2em" />
|
||||
</button>
|
||||
{:else if allow_disable}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_delete('disable')}
|
||||
class="btn-icon btn-icon-sm preset-filled-warning-500 hover:preset-filled-warning-600 transition"
|
||||
title="Disable / soft-remove this {obj_label}"
|
||||
>
|
||||
<MinusCircle size="1.2em" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user