feat(badges): print/review pages, 4-button list, Lucide icons, permissions doc
Badge search results list (ae_comp__badge_obj_li): - 4 action buttons per row: Print, Review (nav link), Copy Link (clipboard), Email Link - Visibility rules: unprinted-only for non-edit mode; all non-hidden for trusted+edit - Plain name display (User/EyeOff icon) — name is no longer a print link - Obscured email for non-trusted users - Debug row (ID, CR, UP, PC, FP, LP) in edit mode - All icons converted to Lucide (Font Awesome removed) Badge print page (/print): - 3 header action buttons: Print Now, Review (nav), Email Link - Removed old [badge_id]/+page.svelte placeholder (moved to trash) - Added is_trusted, is_edit_mode, print state derived vars - "Already printed Nx — last [timestamp]" warning inline with name - Removed unused imports (browser, onMount, events_slct) Badge review page (/review): - 3 header action buttons: Print (nav), Copy Link (clipboard), Email Link - Added events_loc for email placeholder + title event name - Added is_edit_mode, print_count, is_printed, copy_status - FA icons replaced with Lucide (ShieldCheck, UserCheck, User) - Title now includes event name (was missing) Infrastructure: - print/+page.ts and review/+page.ts added (non-blocking badge loaders) - ae_comp__badge_review_form.svelte stub created (fields pending) - Fixed: components no longer write to $ae_loc.edit_mode (critical bug) Docs: - NEW: AE__Permissions_and_Security.md — full permissions hierarchy reference - NEW: PROJECT__AE_Events_Badges_Review_Print.md — agent task brief for review form + print font controls - UPDATED: MODULE__AE_Events_Badges.md rev 5 — field permissions spec, header buttons, still-needed list by priority Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
let show_upload_badge_modal: boolean = $state(false);
|
||||
|
||||
// Guard: Only allow administrators in edit mode
|
||||
if (!$ae_loc.administrator_access || !$ae_loc.edit_mode) {
|
||||
if (!$ae_loc.administrator_access) {
|
||||
if (browser) {
|
||||
alert(
|
||||
'Access Denied: Administrative privileges and Edit Mode required.'
|
||||
|
||||
@@ -8,6 +8,73 @@
|
||||
|
||||
let { mod_badges_json = $bindable({}), onsave }: Props = $props();
|
||||
|
||||
/**
|
||||
* edit_permissions — controls which fields each access level may edit in the badge review form.
|
||||
* Stored as mod_badges_json.edit_permissions.
|
||||
*
|
||||
* Structure:
|
||||
* authenticated.can_edit — fields attendees (passcode-validated) may edit
|
||||
* trusted.can_edit — fields trusted staff may edit
|
||||
* administrator.can_edit — '*' (all) or a specific field list
|
||||
*
|
||||
* Default attendee fields: full_name_override, professional_title_override,
|
||||
* affiliations_override, location_override
|
||||
* Default trusted fields: above + email, badge_type_code
|
||||
*/
|
||||
|
||||
const all_attendee_fields = [
|
||||
{ key: 'full_name_override', label: 'Full Name (override)' },
|
||||
{ key: 'professional_title_override', label: 'Professional Title (override)' },
|
||||
{ key: 'affiliations_override', label: 'Affiliations (override)' },
|
||||
{ key: 'location_override', label: 'Location (override)' }
|
||||
];
|
||||
|
||||
const all_staff_fields = [
|
||||
...all_attendee_fields,
|
||||
{ key: 'email', label: 'Email' },
|
||||
{ key: 'badge_type_code', label: 'Badge Type Code' }
|
||||
];
|
||||
|
||||
// Ensure edit_permissions sub-object exists
|
||||
function ensure_permissions() {
|
||||
if (!mod_badges_json) return;
|
||||
if (!mod_badges_json.edit_permissions) {
|
||||
mod_badges_json.edit_permissions = {};
|
||||
}
|
||||
if (!mod_badges_json.edit_permissions.authenticated) {
|
||||
mod_badges_json.edit_permissions.authenticated = {
|
||||
can_edit: ['full_name_override', 'professional_title_override', 'affiliations_override', 'location_override']
|
||||
};
|
||||
}
|
||||
if (!mod_badges_json.edit_permissions.trusted) {
|
||||
mod_badges_json.edit_permissions.trusted = {
|
||||
can_edit: ['full_name_override', 'professional_title_override', 'affiliations_override', 'location_override', 'email', 'badge_type_code']
|
||||
};
|
||||
}
|
||||
if (!mod_badges_json.edit_permissions.administrator) {
|
||||
mod_badges_json.edit_permissions.administrator = { can_edit: '*' };
|
||||
}
|
||||
}
|
||||
|
||||
function is_field_enabled(level: 'authenticated' | 'trusted', field_key: string): boolean {
|
||||
const cfg = mod_badges_json?.edit_permissions?.[level]?.can_edit;
|
||||
if (!cfg) return false;
|
||||
if (cfg === '*') return true;
|
||||
return Array.isArray(cfg) && cfg.includes(field_key);
|
||||
}
|
||||
|
||||
function toggle_field(level: 'authenticated' | 'trusted', field_key: string) {
|
||||
ensure_permissions();
|
||||
if (!mod_badges_json?.edit_permissions?.[level]) return;
|
||||
let fields: string[] = mod_badges_json.edit_permissions[level].can_edit;
|
||||
if (!Array.isArray(fields)) fields = [];
|
||||
if (fields.includes(field_key)) {
|
||||
mod_badges_json.edit_permissions[level].can_edit = fields.filter((f: string) => f !== field_key);
|
||||
} else {
|
||||
mod_badges_json.edit_permissions[level].can_edit = [...fields, field_key];
|
||||
}
|
||||
}
|
||||
|
||||
function save() {
|
||||
if (onsave && mod_badges_json) onsave(mod_badges_json);
|
||||
}
|
||||
@@ -100,7 +167,61 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<details class="space-y-3">
|
||||
<summary class="cursor-pointer font-medium text-sm">
|
||||
Badge Review — Editable Field Permissions
|
||||
</summary>
|
||||
<div class="space-y-4 pt-2 pl-2">
|
||||
<p class="text-xs text-gray-500">
|
||||
Controls which fields each access level may edit on the Badge Review page.
|
||||
Staff (Trusted) defaults include all attendee fields plus Email and Badge Type Code.
|
||||
Administrators can always edit everything.
|
||||
</p>
|
||||
|
||||
<!-- Attendee (passcode-validated) -->
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-medium">Attendees (passcode link)</p>
|
||||
<div class="grid grid-cols-2 gap-x-4 gap-y-1">
|
||||
{#each all_attendee_fields as field}
|
||||
<label class="label flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
checked={is_field_enabled('authenticated', field.key)}
|
||||
onchange={() => toggle_field('authenticated', field.key)}
|
||||
/>
|
||||
<span>{field.label}</span>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Staff (Trusted) -->
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-medium">Staff (Trusted access)</p>
|
||||
<div class="grid grid-cols-2 gap-x-4 gap-y-1">
|
||||
{#each all_staff_fields as field}
|
||||
<label class="label flex items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
checked={is_field_enabled('trusted', field.key)}
|
||||
onchange={() => toggle_field('trusted', field.key)}
|
||||
/>
|
||||
<span>{field.label}</span>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Administrator note -->
|
||||
<p class="text-xs text-gray-400 italic">
|
||||
Administrators always have access to all fields (not configurable).
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
{/if} <!-- end {#if mod_badges_json} -->
|
||||
|
||||
<button type="button" class="btn preset-tonal-primary" onclick={save}
|
||||
>Save</button
|
||||
|
||||
Reference in New Issue
Block a user