Fix detached action redirect loops, abstract Spa_Action detection, jqhtml update

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-10 23:46:50 +00:00
parent 2f2cf41139
commit 3c25b2ff80
87 changed files with 419 additions and 72 deletions

View File

@@ -1001,11 +1001,14 @@ class Spa {
const $detached = $('<div>');
// Instantiate the action on the detached element
// This triggers the full component lifecycle: on_create -> render -> on_render -> on_load -> on_ready
$detached.component(action_name, args);
// Skip render and on_ready phases - detached actions are for data extraction only
// (e.g., getting title/breadcrumbs). Running on_ready() could trigger side effects
// like Spa.dispatch() which would cause redirect loops.
const options = { skip_render_and_ready: true };
$detached.component(action_name, args, options);
const action = $detached.component();
// Wait for the action to be fully ready (including on_load completion)
// Wait for on_load to complete (data fetching)
await action.ready();
console_debug('Spa', `load_detached_action: ${action_name} ready`);

View File

@@ -78,4 +78,71 @@ class Spa_Action extends Component {
const url = this.url(params);
Spa.dispatch(url);
}
// =========================================================================
// Page Title & Breadcrumb System
// =========================================================================
/**
* Page title displayed in the header/title area
*
* Override this in every action to provide a meaningful page title.
* For entity pages, include entity details (e.g., "Contact: John Smith C001")
*
* @returns {Promise<string>} The page title
*/
async page_title() {
return '(title not set)';
}
/**
* Breadcrumb label for this action
*
* Used when this action appears as a parent in another action's breadcrumb chain.
* For entity pages (viewing a specific user, contact, etc.), return the entity name.
* For list/index pages, return the section name.
*
* Default: Returns page_title()
*
* @returns {Promise<string>} The breadcrumb label
*/
async breadcrumb_label() {
return await this.page_title();
}
/**
* Breadcrumb label when this action is the active/last crumb
*
* Use this to show a descriptive action name instead of the entity name
* when the entity name is already visible in the page title above.
*
* Example: For a user profile view page:
* - page_title() = "User Profile: John Smith U001"
* - breadcrumb_label() = "John Smith" (for when it's a parent)
* - breadcrumb_label_active() = "View User Profile" (avoids redundancy with title)
*
* Default: Returns breadcrumb_label()
*
* @returns {Promise<string>} The active breadcrumb label
*/
async breadcrumb_label_active() {
return await this.breadcrumb_label();
}
/**
* Parent action URL for breadcrumb chain
*
* Return the URL of the parent action using Rsx.Route().
* Return null if this action is a root (no parent breadcrumb).
*
* Example:
* async breadcrumb_parent() {
* return Rsx.Route('Settings_Users_Action');
* }
*
* @returns {Promise<string|null>} Parent URL or null if root
*/
async breadcrumb_parent() {
return null;
}
}