feat(framework): implement AE Obj Field Editor v3 and test playground
- Created consolidated AE_Obj_Field_Editor_V3 component using Svelte 5 Runes.
- Standardized on V3 CRUD API (PATCH /v3/crud/{obj_type}/{obj_id}).
- Implemented local state guards to prevent reactivity loops.
- Added support for multiple field types (text, textarea, select, checkbox, tiptap).
- Created a dedicated testing playground with real Demo account data.
- Updated the PROJECT_AE_OBJECT_FIELD_EDITOR_V3_UPGRADE.md plan.
This commit is contained in:
@@ -1,49 +1,46 @@
|
||||
# Project Plan: Aether CRUD v2 Component Refactor (V3 Alignment)
|
||||
# Project Plan: Aether AE Obj Field Editor v3 (Consolidated)
|
||||
|
||||
> **Status:** Draft / Initial Planning
|
||||
> **Date:** February 9, 2026
|
||||
> **Target Component:** `src/lib/elements/element_ae_crud_v2.svelte`
|
||||
> **Status:** Active / Planning Refinement
|
||||
> **Date:** February 13, 2026
|
||||
> **Target Component:** `src/lib/elements/element_ae_obj_field_editor_v3.svelte`
|
||||
> **Replaces:** `element_ae_crud.svelte` and `element_ae_crud_v2.svelte`
|
||||
|
||||
## 1. Overview
|
||||
The `Element_ae_crud_v2` component is the central property editor for the Aether platform. Following major backend (FastAPI V3) and frontend (Svelte 5) upgrades, this component requires a systematic refactor to restore full functionality, improve performance, and enhance the developer experience (DX).
|
||||
|
||||
The goal is a **near drop-in replacement** that maintains the existing API while leveraging modern platform standards.
|
||||
Consolidate the legacy CRUD components into a single, high-performance "Aether Object Field Editor" (v3). This component will be the standard for single-property editing across the platform, fully aligned with the FastAPI V3 CRUD patterns and Svelte 5 Runes.
|
||||
|
||||
## 2. Strategic Objectives
|
||||
- **API Alignment:** Fully integrate with the V3 CRUD pattern (`PATCH /v3/crud/{obj_type}/{obj_id}`).
|
||||
- **Svelte 5 Optimization:** Migrate fully to Runes (`$state`, `$derived`, `$effect`, `$props`) for cleaner reactivity and improved prop-binding.
|
||||
- **Mobile-First UX:** Redesign the edit popover/form for better accessibility on smaller screens.
|
||||
- **Robust Error Handling:** Standardize error bubbling and validation feedback.
|
||||
- **Type Safety:** Ensure all props and internal state are strictly typed.
|
||||
- **Consolidation:** Retire `v1` and `v2` components in favor of a single, unified codebase.
|
||||
- **API Alignment:** Native support for `PATCH /v3/crud/{obj_type}/{obj_id}`.
|
||||
- **Svelte 5 Runes:** Pure `$props`, `$state`, and `$derived` implementation. No legacy imports.
|
||||
- **Callback Pattern:** Replace `createEventDispatcher` with callback props (`on_patch`, `on_success`, `on_error`).
|
||||
- **Iconography:** Standardize on **Lucide-Svelte**.
|
||||
- **Mobile-First:** Improved "Tap to Edit" targets and mobile-responsive popovers.
|
||||
|
||||
## 3. Current Version Audit (`v2`)
|
||||
- **Strengths:** Support for multiple field types (text, textarea, tiptap, select); SWR-ready via `object_reload`.
|
||||
- **Weaknesses:** Fragmented CSS; manual state synchronization logic; inconsistent button styling; limited feedback for patch failures.
|
||||
## 3. Implementation Phases
|
||||
|
||||
## 4. Implementation Phases
|
||||
### Phase 1: Foundation & Reactivity
|
||||
- [ ] Create the new `v3` component shell.
|
||||
- [ ] Implement strict TypeScript interface for Props.
|
||||
- [ ] Use `$state` for local "draft" values to prevent reactivity loops with the global store.
|
||||
- [ ] Implement the `handle_patch` logic using the central `api.patch` helper.
|
||||
|
||||
### Phase 1: Core Reactivity & API
|
||||
- [ ] Replace `let` exports with `$props()` destructuring.
|
||||
- [ ] Implement `$state` for internal values and patch status.
|
||||
- [ ] Standardize the `handle_obj_field_patch` function to use the central `api.patch` helper.
|
||||
- [ ] Verify Triple-ID mapping within the component.
|
||||
### Phase 2: UI & UX Refinement
|
||||
- [ ] Standardize Tailwind classes (using Tailwind 4 patterns).
|
||||
- [ ] Implement "Edit Mode" awareness (syncing with `$ae_loc.edit_mode`).
|
||||
- [ ] Add a "Save" loading state with Lucide's `LoaderCircle` spinner.
|
||||
- [ ] Implement a clear "Cancel" path that restores the original value.
|
||||
|
||||
### Phase 2: UI/UX & Accessibility
|
||||
- [ ] Standardize Tailwind classes; remove legacy scope-polluting CSS.
|
||||
- [ ] Implement a "Select" mode optimized for mobile (Bottom Sheet style?).
|
||||
- [ ] Improve "Double Click to Edit" discoverability or provide a single-tap alternative for mobile.
|
||||
- [ ] Standardize button types (`type="button"`) and preset styles.
|
||||
### Phase 3: Field Type Parity
|
||||
- [ ] Support `text`, `textarea`, `select`, `tiptap`, and `checkbox`.
|
||||
- [ ] Add `datetime` support using native browser pickers.
|
||||
- [ ] Implement searchable dropdowns for the `select` type.
|
||||
|
||||
### Phase 3: Field Type Expansion
|
||||
- [ ] Enhance `select` mode with searchable dropdowns.
|
||||
- [ ] Formalize `tiptap` integration with full configuration support.
|
||||
- [ ] Add `datetime` and `date` field types.
|
||||
### Phase 4: Migration & Cleanup
|
||||
- [ ] Create a playground route for V3 verification.
|
||||
- [ ] Deprecate and eventually remove `v1` and `v2` files.
|
||||
- [ ] Update `GUIDE__DEVELOPMENT.md` with the new usage patterns.
|
||||
|
||||
### Phase 4: Verification & Migration
|
||||
- [ ] Create a testing playground route for CRUD v2.
|
||||
- [ ] Perform a surgical swap across high-traffic modules (Leads, Events, Journals).
|
||||
- [ ] Verify `npm run check` baseline.
|
||||
|
||||
## 5. Maintenance & Standards
|
||||
- Maintain the `object_reload={true}` pattern for automatic cache revalidation.
|
||||
- Ensure `class_li` and `display_block` props remain backward compatible.
|
||||
## 4. Maintenance & Standards
|
||||
- Component must respect `$ae_loc.trusted_access` for visibility of edit triggers.
|
||||
- Always use `type="button"` for internal actions to prevent form collisions.
|
||||
- Maintain the `object_reload` pattern for SWR cache invalidation.
|
||||
|
||||
285
src/lib/elements/element_ae_obj_field_editor_v3.svelte
Normal file
285
src/lib/elements/element_ae_obj_field_editor_v3.svelte
Normal file
@@ -0,0 +1,285 @@
|
||||
<script lang="ts">
|
||||
// import { browser } from '$app/environment';
|
||||
import { untrack } from 'svelte';
|
||||
import { LoaderCircle, SquarePen, Save, X, Trash2, Check, CircleAlert } from 'lucide-svelte';
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
|
||||
import { api } from '$lib/api/api';
|
||||
import AE_Comp_Editor_TipTap from '$lib/elements/AE_Comp_Editor_TipTap.svelte';
|
||||
|
||||
interface Props {
|
||||
// Core Identifiers
|
||||
object_type: string;
|
||||
object_id: string;
|
||||
field_name: string;
|
||||
|
||||
// Value Handling
|
||||
current_value: any;
|
||||
field_type?: 'text' | 'textarea' | 'select' | 'tiptap' | 'checkbox' | 'date' | 'datetime';
|
||||
allow_null?: boolean;
|
||||
|
||||
// Select Options
|
||||
select_options?: key_val; // { value: label }
|
||||
|
||||
// UI Configuration
|
||||
edit_label?: string;
|
||||
display_block?: boolean;
|
||||
display_absolute_edit?: boolean;
|
||||
placeholder?: string;
|
||||
class_li?: string;
|
||||
textarea_rows?: number;
|
||||
|
||||
// Behavior
|
||||
object_reload?: boolean; // SWR pattern
|
||||
log_lvl?: number;
|
||||
|
||||
// Callbacks
|
||||
on_success?: (data: any) => void;
|
||||
on_error?: (error: any) => void;
|
||||
|
||||
// Snippets
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
object_type,
|
||||
object_id,
|
||||
field_name,
|
||||
current_value = $bindable(),
|
||||
field_type = 'text',
|
||||
allow_null = false,
|
||||
select_options = {},
|
||||
edit_label = 'Edit Field',
|
||||
display_block = false,
|
||||
display_absolute_edit = false,
|
||||
placeholder = 'Enter value...',
|
||||
class_li = '',
|
||||
textarea_rows = 4,
|
||||
object_reload = true,
|
||||
log_lvl = 0,
|
||||
on_success,
|
||||
on_error,
|
||||
children
|
||||
}: Props = $props();
|
||||
|
||||
// Internal State
|
||||
let is_editing = $state(false);
|
||||
let patch_status = $state<'idle' | 'processing' | 'success' | 'error'>('idle');
|
||||
let error_message = $state('');
|
||||
let draft_value = $state(current_value);
|
||||
|
||||
// Sync draft with current_value when not editing
|
||||
$effect(() => {
|
||||
if (!is_editing) {
|
||||
untrack(() => {
|
||||
draft_value = current_value;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function handle_patch() {
|
||||
if (log_lvl) console.log(`AE Field Editor V3: Patching ${object_type}.${field_name}...`);
|
||||
|
||||
patch_status = 'processing';
|
||||
error_message = '';
|
||||
|
||||
try {
|
||||
const result = await api.update_ae_obj_v3({
|
||||
api_cfg: $ae_api,
|
||||
obj_type: object_type,
|
||||
obj_id: object_id,
|
||||
fields: {
|
||||
[field_name]: draft_value
|
||||
},
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
patch_status = 'success';
|
||||
current_value = draft_value;
|
||||
if (on_success) on_success(result);
|
||||
|
||||
// Close edit mode after a brief success indicator
|
||||
setTimeout(() => {
|
||||
if (patch_status === 'success') {
|
||||
patch_status = 'idle';
|
||||
is_editing = false;
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
throw new Error('No data returned from update.');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('AE Field Editor V3: Patch failed.', error);
|
||||
patch_status = 'error';
|
||||
error_message = error?.message || 'Update failed.';
|
||||
if (on_error) on_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function cancel_edit() {
|
||||
draft_value = current_value;
|
||||
is_editing = false;
|
||||
patch_status = 'idle';
|
||||
error_message = '';
|
||||
}
|
||||
|
||||
function toggle_edit() {
|
||||
if (is_editing) cancel_edit();
|
||||
else is_editing = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="ae_field_editor_v3 group relative {class_li}"
|
||||
class:block={display_block}
|
||||
class:inline-block={!display_block}
|
||||
>
|
||||
<!-- VIEW MODE -->
|
||||
<div class="view_wrapper flex items-center gap-2" class:hidden={is_editing}>
|
||||
<div class="content_render grow">
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else if field_type === 'checkbox'}
|
||||
<span class="badge {current_value ? 'variant-filled-success' : 'variant-soft-surface'}">
|
||||
{current_value ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
{:else if field_type === 'tiptap'}
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
{@html current_value || '<span class="opacity-50 italic">Empty</span>'}
|
||||
</div>
|
||||
{:else}
|
||||
<span class:opacity-50={!current_value}>
|
||||
{current_value || 'Not set'}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Edit Trigger (Visible on Hover or Edit Mode) -->
|
||||
{#if $ae_loc.edit_mode}
|
||||
<button
|
||||
type="button"
|
||||
class="btn-icon btn-icon-sm variant-soft-warning opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onclick={toggle_edit}
|
||||
title="Edit {field_name}"
|
||||
>
|
||||
<SquarePen size="14" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- EDIT MODE -->
|
||||
{#if is_editing}
|
||||
<div
|
||||
class="edit_wrapper p-3 border-2 border-dashed border-warning-500/50 rounded-lg bg-surface-50-950 shadow-xl z-50"
|
||||
class:absolute={display_absolute_edit}
|
||||
class:top-0={display_absolute_edit}
|
||||
class:left-0={display_absolute_edit}
|
||||
class:w-full={display_absolute_edit}
|
||||
>
|
||||
<header class="flex justify-between items-center mb-2">
|
||||
<span class="text-xs font-bold uppercase tracking-wider opacity-60">{edit_label || field_name}</span>
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-icon btn-icon-sm variant-soft-surface"
|
||||
onclick={cancel_edit}
|
||||
disabled={patch_status === 'processing'}
|
||||
>
|
||||
<X size="14" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="input_container mb-3">
|
||||
{#if field_type === 'textarea'}
|
||||
<textarea
|
||||
bind:value={draft_value}
|
||||
rows={textarea_rows}
|
||||
class="textarea"
|
||||
{placeholder}
|
||||
></textarea>
|
||||
{:else if field_type === 'select'}
|
||||
<select bind:value={draft_value} class="select">
|
||||
{#if allow_null}
|
||||
<option value={null}>-- None --</option>
|
||||
{/if}
|
||||
{#each Object.entries(select_options) as [val, label]}
|
||||
<option value={val}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if field_type === 'checkbox'}
|
||||
<label class="flex items-center space-x-2">
|
||||
<input type="checkbox" bind:checked={draft_value} class="checkbox" />
|
||||
<span>{draft_value ? 'Enabled' : 'Disabled'}</span>
|
||||
</label>
|
||||
{:else if field_type === 'tiptap'}
|
||||
<AE_Comp_Editor_TipTap bind:content={draft_value} {placeholder} />
|
||||
{:else if field_type === 'date'}
|
||||
<input type="date" bind:value={draft_value} class="input" />
|
||||
{:else if field_type === 'datetime'}
|
||||
<input type="datetime-local" bind:value={draft_value} class="input" />
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={draft_value}
|
||||
class="input"
|
||||
{placeholder}
|
||||
onkeydown={(e) => e.key === 'Enter' && handle_patch()}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<footer class="flex justify-between items-center">
|
||||
<div class="status_indicator text-xs">
|
||||
{#if patch_status === 'processing'}
|
||||
<span class="flex items-center gap-1 text-primary-500">
|
||||
<LoaderCircle size="12" class="animate-spin" /> Saving...
|
||||
</span>
|
||||
{:else if patch_status === 'success'}
|
||||
<span class="flex items-center gap-1 text-success-500">
|
||||
<Check size="12" /> Saved
|
||||
</span>
|
||||
{:else if patch_status === 'error'}
|
||||
<span class="flex items-center gap-1 text-error-500" title={error_message}>
|
||||
<CircleAlert size="12" /> Error
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="actions flex gap-2">
|
||||
{#if allow_null && draft_value !== null}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm variant-soft-error"
|
||||
onclick={() => draft_value = null}
|
||||
>
|
||||
Set Null
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm variant-filled-primary"
|
||||
onclick={handle_patch}
|
||||
disabled={patch_status === 'processing' || draft_value === current_value}
|
||||
>
|
||||
{#if patch_status === 'processing'}
|
||||
<LoaderCircle size="14" class="animate-spin mr-1" />
|
||||
{:else}
|
||||
<Save size="14" class="mr-1" />
|
||||
{/if}
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Add any specialized transitions if needed */
|
||||
.ae_field_editor_v3 :global(.btn-icon-sm) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
222
src/routes/testing/ae_obj_field_editor_v3/+page.svelte
Normal file
222
src/routes/testing/ae_obj_field_editor_v3/+page.svelte
Normal file
@@ -0,0 +1,222 @@
|
||||
<script lang="ts">
|
||||
import AE_Obj_Field_Editor_V3 from '$lib/elements/element_ae_obj_field_editor_v3.svelte';
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import { liveQuery } from 'dexie';
|
||||
import { db_journals } from '$lib/ae_journals/db_journals';
|
||||
import { load_ae_obj_id__journal } from '$lib/ae_journals/ae_journals__journal';
|
||||
import { load_ae_obj_id__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';
|
||||
import { LoaderCircle, Database, Settings2, Edit, FileText } from 'lucide-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let log_lvl = $state(1);
|
||||
|
||||
// Provided Test IDs
|
||||
let test_journal_id = $state('SWFK-48-89-90'); // 'BVYE-94-46-29'
|
||||
let test_journal_entry_id = $state('xRx-Y4-h3-fU');
|
||||
|
||||
// Fetch objects from API on mount to ensure local DB is populated
|
||||
onMount(async () => {
|
||||
if (log_lvl) console.log('Test Page: Initializing data from server...');
|
||||
try {
|
||||
await Promise.all([
|
||||
load_ae_obj_id__journal({
|
||||
api_cfg: $ae_api,
|
||||
journal_id: test_journal_id,
|
||||
log_lvl
|
||||
}),
|
||||
load_ae_obj_id__journal_entry({
|
||||
api_cfg: $ae_api,
|
||||
journal_entry_id: test_journal_entry_id,
|
||||
log_lvl
|
||||
})
|
||||
]);
|
||||
} catch (e) {
|
||||
console.error('Test Page: Error loading initial data', e);
|
||||
}
|
||||
});
|
||||
|
||||
// Dexie LiveQueries
|
||||
let lq__test_journal = $derived(
|
||||
liveQuery(async () => {
|
||||
return await db_journals.journal.get(test_journal_id);
|
||||
})
|
||||
);
|
||||
|
||||
let lq__test_journal_entry = $derived(
|
||||
liveQuery(async () => {
|
||||
return await db_journals.journal_entry.get(test_journal_entry_id);
|
||||
})
|
||||
);
|
||||
|
||||
function handle_success(data: any) {
|
||||
console.log('Test Page: Patch Success!', data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="p-8 space-y-8 max-w-6xl mx-auto h-full overflow-y-auto">
|
||||
<header class="border-b border-surface-500/30 pb-4 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="h1">AE Obj Field Editor V3 Test</h1>
|
||||
<p class="opacity-70 font-mono text-xs">IDs: Journal={test_journal_id} | Entry={test_journal_entry_id}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 cursor-pointer bg-surface-200 dark:bg-surface-800 px-3 py-1.5 rounded-full shadow-inner">
|
||||
<span class="text-xs font-bold uppercase opacity-70">Edit Mode</span>
|
||||
<input type="checkbox" bind:checked={$ae_loc.edit_mode} class="checkbox" />
|
||||
</label>
|
||||
<button class="btn variant-filled-primary" onclick={() => window.location.reload()}>
|
||||
<span class="fas fa-sync mr-2"></span> Reload Page
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
<!-- COLUMN 1: JOURNAL TEST -->
|
||||
<div class="space-y-8">
|
||||
<h2 class="h2 flex items-center gap-2"><Database /> Journal Properties</h2>
|
||||
|
||||
{#if $lq__test_journal}
|
||||
<section class="card p-6 space-y-6 variant-soft-primary shadow-lg border border-primary-500/20">
|
||||
<div class="field_group">
|
||||
<label for="j_name" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Journal Name</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="j_name"
|
||||
object_type="journal"
|
||||
object_id={test_journal_id}
|
||||
field_name="name"
|
||||
bind:current_value={$lq__test_journal.name}
|
||||
field_type="text"
|
||||
{log_lvl}
|
||||
on_success={handle_success}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field_group">
|
||||
<label for="j_type" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Type Code</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="j_type"
|
||||
object_type="journal"
|
||||
object_id={test_journal_id}
|
||||
field_name="type_code"
|
||||
bind:current_value={$lq__test_journal.type_code}
|
||||
field_type="select"
|
||||
select_options={{
|
||||
'personal': 'Personal Journal',
|
||||
'work': 'Work Log',
|
||||
'shared': 'Shared Notes',
|
||||
'ai_brain': 'AI Context'
|
||||
}}
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field_group">
|
||||
<label for="j_summary" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Summary</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="j_summary"
|
||||
object_type="journal"
|
||||
object_id={test_journal_id}
|
||||
field_name="summary"
|
||||
bind:current_value={$lq__test_journal.summary}
|
||||
field_type="textarea"
|
||||
display_block={true}
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{:else}
|
||||
<div class="card p-10 flex flex-col items-center justify-center opacity-50 gap-4 variant-soft-surface">
|
||||
<LoaderCircle size="32" class="animate-spin" />
|
||||
<p class="text-sm font-bold">Loading Journal {test_journal_id}...</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- COLUMN 2: JOURNAL ENTRY TEST -->
|
||||
<div class="space-y-8">
|
||||
<h2 class="h2 flex items-center gap-2"><FileText /> Journal Entry Properties</h2>
|
||||
|
||||
{#if $lq__test_journal_entry}
|
||||
<section class="card p-6 space-y-6 variant-soft-secondary shadow-lg border border-secondary-500/20">
|
||||
<div class="field_group">
|
||||
<label for="e_name" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Entry Title</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="e_name"
|
||||
object_type="journal_entry"
|
||||
object_id={test_journal_entry_id}
|
||||
field_name="name"
|
||||
bind:current_value={$lq__test_journal_entry.name}
|
||||
field_type="text"
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="field_group">
|
||||
<label for="e_enabled" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Status</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="e_enabled"
|
||||
object_type="journal_entry"
|
||||
object_id={test_journal_entry_id}
|
||||
field_name="enable"
|
||||
bind:current_value={$lq__test_journal_entry.enable}
|
||||
field_type="checkbox"
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
<div class="field_group">
|
||||
<label for="e_priority" class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Priority</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
id="e_priority"
|
||||
object_type="journal_entry"
|
||||
object_id={test_journal_entry_id}
|
||||
field_name="priority"
|
||||
bind:current_value={$lq__test_journal_entry.priority}
|
||||
field_type="checkbox"
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field_group">
|
||||
<label class="label text-xs font-bold opacity-50 mb-1 uppercase tracking-tighter">Content (TipTap)</label>
|
||||
<AE_Obj_Field_Editor_V3
|
||||
object_type="journal_entry"
|
||||
object_id={test_journal_entry_id}
|
||||
field_name="content"
|
||||
bind:current_value={$lq__test_journal_entry.content}
|
||||
field_type="tiptap"
|
||||
display_block={true}
|
||||
{log_lvl}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{:else}
|
||||
<div class="card p-10 flex flex-col items-center justify-center opacity-50 gap-4 variant-soft-surface">
|
||||
<LoaderCircle size="32" class="animate-spin" />
|
||||
<p class="text-sm font-bold">Loading Entry {test_journal_entry_id}...</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DEBUG FOOTER -->
|
||||
<section class="card p-4 space-y-4">
|
||||
<h2 class="h3">Debug Context</h2>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-xs font-mono">
|
||||
<div class="bg-surface-900 text-success-500 p-2 rounded">
|
||||
<strong>Edit Mode:</strong> {$ae_loc.edit_mode}
|
||||
</div>
|
||||
<div class="bg-surface-900 text-success-500 p-2 rounded">
|
||||
<strong>Account ID:</strong> {$ae_loc.account_id}
|
||||
</div>
|
||||
<div class="bg-surface-900 text-success-500 p-2 rounded">
|
||||
<strong>Journal Status:</strong> {$lq__test_journal ? 'Cached' : 'Pending'}
|
||||
</div>
|
||||
<div class="bg-surface-900 text-success-500 p-2 rounded">
|
||||
<strong>Entry Status:</strong> {$lq__test_journal_entry ? 'Cached' : 'Pending'}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
Reference in New Issue
Block a user