Fix Form_Utils to use component.$sid() instead of data-sid selector
Add response helper functions and use _message as reserved metadata key 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -107,7 +107,8 @@ class Form_Utils {
|
||||
// Resolve the promise once all animations are complete
|
||||
Promise.all(animations).then(() => {
|
||||
// Scroll to error container if it exists
|
||||
const $error_container = $parent.find('[data-sid="error_container"]').first();
|
||||
const component = $parent.component();
|
||||
const $error_container = component ? component.$sid('error_container') : $();
|
||||
if ($error_container.length > 0) {
|
||||
const container_top = $error_container.offset().top;
|
||||
|
||||
@@ -251,9 +252,10 @@ class Form_Utils {
|
||||
}
|
||||
|
||||
// Convert Laravel validator format {field: [msg1, msg2]} to {field: msg1}
|
||||
// Skip reserved keys (prefixed with underscore) - these are metadata, not field errors
|
||||
const normalized = {};
|
||||
for (const field in errors) {
|
||||
if (errors.hasOwnProperty(field)) {
|
||||
if (errors.hasOwnProperty(field) && !field.startsWith('_')) {
|
||||
const value = errors[field];
|
||||
if (Array.isArray(value) && value.length > 0) {
|
||||
normalized[field] = value[0];
|
||||
@@ -345,7 +347,8 @@ class Form_Utils {
|
||||
*/
|
||||
static _apply_combined_error($parent, summary_msg, unmatched_errors) {
|
||||
const animations = [];
|
||||
const $error_container = $parent.find('[data-sid="error_container"]').first();
|
||||
const component = $parent.component();
|
||||
const $error_container = component ? component.$sid('error_container') : $();
|
||||
const $target = $error_container.length > 0 ? $error_container : $parent;
|
||||
|
||||
// Create alert with summary message and bulleted list of unmatched errors
|
||||
@@ -386,7 +389,8 @@ class Form_Utils {
|
||||
const animations = [];
|
||||
|
||||
// Look for a specific error container div (e.g., in Rsx_Form component)
|
||||
const $error_container = $parent.find('[data-sid="error_container"]').first();
|
||||
const component = $parent.component();
|
||||
const $error_container = component ? component.$sid('error_container') : $();
|
||||
const $target = $error_container.length > 0 ? $error_container : $parent;
|
||||
|
||||
if (typeof messages === 'string') {
|
||||
|
||||
@@ -28,16 +28,16 @@ class Error_Response extends Rsx_Response_Abstract
|
||||
if ($metadata === null) {
|
||||
$this->metadata = [];
|
||||
} elseif (is_string($metadata)) {
|
||||
$this->metadata = ['message' => $metadata];
|
||||
$this->metadata = ['_message' => $metadata];
|
||||
} elseif (is_array($metadata)) {
|
||||
$this->metadata = $metadata;
|
||||
} else {
|
||||
$this->metadata = ['message' => (string)$metadata];
|
||||
$this->metadata = ['_message' => (string)$metadata];
|
||||
}
|
||||
|
||||
// Set reason from message or use default
|
||||
if (isset($this->metadata['message'])) {
|
||||
$this->reason = $this->metadata['message'];
|
||||
if (isset($this->metadata['_message'])) {
|
||||
$this->reason = $this->metadata['_message'];
|
||||
} else {
|
||||
$this->reason = Ajax::get_default_message($error_code);
|
||||
}
|
||||
|
||||
@@ -1089,6 +1089,57 @@ function response_not_found(?string $message = null)
|
||||
return response_error(\App\RSpade\Core\Ajax\Ajax::ERROR_NOT_FOUND, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form validation error response
|
||||
*
|
||||
* Use this for validation errors with field-specific messages.
|
||||
* The client-side form handling will apply errors to matching fields.
|
||||
*
|
||||
* @param string $message Summary message for the error
|
||||
* @param array $field_errors Field-specific errors as ['field_name' => 'error message']
|
||||
* @return \App\RSpade\Core\Response\Error_Response
|
||||
*/
|
||||
function response_form_error(string $message, array $field_errors = [])
|
||||
{
|
||||
return response_error(
|
||||
\App\RSpade\Core\Ajax\Ajax::ERROR_VALIDATION,
|
||||
array_merge(['_message' => $message], $field_errors)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an authentication required error response
|
||||
*
|
||||
* Use this when the user is not logged in and needs to authenticate.
|
||||
* Distinct from response_unauthorized() which is for permission denied.
|
||||
*
|
||||
* @param string|null $message Custom error message (optional)
|
||||
* @return \App\RSpade\Core\Response\Error_Response
|
||||
*/
|
||||
function response_auth_required(?string $message = null)
|
||||
{
|
||||
return response_error(\App\RSpade\Core\Ajax\Ajax::ERROR_AUTH_REQUIRED, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fatal error response
|
||||
*
|
||||
* Use this for unrecoverable errors that prevent the operation from completing.
|
||||
* These are typically logged and displayed prominently to the user.
|
||||
*
|
||||
* @param string|null $message Error message
|
||||
* @param array $details Additional error details (e.g., file, line, backtrace)
|
||||
* @return \App\RSpade\Core\Response\Error_Response
|
||||
*/
|
||||
function response_fatal_error(?string $message = null, array $details = [])
|
||||
{
|
||||
$metadata = $details;
|
||||
if ($message !== null) {
|
||||
$metadata['_message'] = $message;
|
||||
}
|
||||
return response_error(\App\RSpade\Core\Ajax\Ajax::ERROR_FATAL, $metadata ?: $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request is from a loopback IP address
|
||||
*
|
||||
|
||||
@@ -97,17 +97,21 @@ class Frontend_Clients_Edit {
|
||||
|
||||
### Change
|
||||
|
||||
Adopted unified `response_error()` function with error code constants. Same constant names on server and client for zero mental translation.
|
||||
|
||||
**Note**: The fragmented helper functions (`response_form_error()`, `response_auth_required()`, etc.) shown in earlier documentation were a planned design that was superseded before implementation. Only `response_error()` exists.
|
||||
Adopted unified `response_error()` function with error code constants, plus convenience helpers. Same constant names on server and client for zero mental translation.
|
||||
|
||||
### Pattern
|
||||
|
||||
```php
|
||||
// Server - single function with constants
|
||||
// Server - base function with constants
|
||||
return response_error(Ajax::ERROR_VALIDATION, ['email' => 'Invalid']);
|
||||
return response_error(Ajax::ERROR_NOT_FOUND, 'Project not found');
|
||||
return response_error(Ajax::ERROR_UNAUTHORIZED); // Auto-message
|
||||
|
||||
// Convenience helpers (recommended for clarity)
|
||||
return response_form_error('Validation failed', ['email' => 'Invalid']);
|
||||
return response_not_found('Project not found');
|
||||
return response_unauthorized();
|
||||
return response_auth_required();
|
||||
return response_fatal_error('Something went wrong');
|
||||
```
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -912,7 +912,7 @@ async on_load() {
|
||||
// Controller: save() receives all form values, validates, persists
|
||||
#[Ajax_Endpoint]
|
||||
public static function save(Request $request, array $params = []) {
|
||||
if (empty($params['title'])) return response_form_error('Error', ['title' => 'Required']);
|
||||
if (empty($params['title'])) return response_form_error('Validation failed', ['title' => 'Required']);
|
||||
$record = $params['id'] ? My_Model::find($params['id']) : new My_Model();
|
||||
$record->title = $params['title'];
|
||||
$record->save();
|
||||
|
||||
Reference in New Issue
Block a user