diff --git a/.gitignore b/.gitignore index 3aafa7b..5e766c3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ tags node_modules __pycache__ +venv/ +.venv/ *dist/ .vscode/ \ No newline at end of file diff --git a/custom_ui/api/proxy.py b/custom_ui/api/proxy.py index bfa25d9..d84c4eb 100644 --- a/custom_ui/api/proxy.py +++ b/custom_ui/api/proxy.py @@ -5,28 +5,27 @@ from urllib.parse import urlparse allowed_hosts = ["api.zippopotam.us"] # Update this list with trusted domains as needed @frappe.whitelist(allow_guest=True) -def proxy_request(url, method="GET", data=None, headers=None): +def request(url, method="GET", data=None, headers=None): """ Generic proxy for external API requests. WARNING: Only allow requests to trusted domains. """ parsed_url = urlparse(url) if parsed_url.hostname not in allowed_hosts: - frappe.throw(f"Rquests to {parsed_url.hostname} are not allowed.", frappe.PermissionError) - + frappe.throw(f"Requests to {parsed_url.hostname} are not allowed.", frappe.PermissionError) + try: + resp = requests.request( + method=method.upper(), + url=url, + json=frappe.parse_json(data) if data else None, + headers=frappe.parse_json(headers) if headers else None, + timeout=10 + ) + resp.raise_for_status() try: - resp = requests.request( - method=method.upper(), - url=url, - json=frappe.parse_json(data) if data else None, - headers=frappe.parse_json(headers) if headers else None, - timeout=10 - ) - resp.raise_for_status() - try: - return resp.json() - except ValueError: - return {"text": resp.text} - except requests.exceptions.RequestException as e: - frappe.log_error(message=str(e), title="Proxy Request Failed") - frappe.throw("Failed to fetch data from external API.") \ No newline at end of file + return resp.json() + except ValueError: + return {"text": resp.text} + except requests.exceptions.RequestException as e: + frappe.log_error(message=str(e), title="Proxy Request Failed") + frappe.throw("Failed to fetch data from external API.") \ No newline at end of file diff --git a/frontend/documentation/components/Form.md b/frontend/documentation/components/Form.md index e69bf3f..cf8d4f4 100644 --- a/frontend/documentation/components/Form.md +++ b/frontend/documentation/components/Form.md @@ -1,8 +1,17 @@ +````markdown # Form Component Documentation ## Overview -A highly flexible and configurable dynamic form component built with Vuetify. This component generates forms based on field configuration objects and supports various input types, validation, responsive layouts, and both controlled and uncontrolled form state management. +A highly flexible and configurable dynamic form component built with **PrimeVue**. This component generates forms based on field configuration objects and supports various input types including **AutoComplete with custom values**, validation, responsive layouts, and both controlled and uncontrolled form state management. + +## ✨ New Features (PrimeVue Migration) + +- **AutoComplete component** - Users can select from suggestions OR enter completely custom values +- **Better date/time pickers** with calendar popup and time selection +- **Improved accessibility** with ARIA support +- **More flexible styling** with CSS custom properties +- **Enhanced mobile responsiveness** with CSS Grid ## Basic Usage @@ -17,92 +26,116 @@ A highly flexible and configurable dynamic form component built with Vuetify. Th ``` ## Props ### `fields` (Array) - Required + - **Description:** Array of field configuration objects that define the form structure - **Type:** `Array` - **Required:** `true` ### `formData` (Object) + - **Description:** External form data object for controlled form state - **Type:** `Object` - **Default:** `null` - **Note:** When provided, the form operates in controlled mode. When null, uses internal state. ### `onChange` (Function) + - **Description:** Global change handler function called when any field changes - **Type:** `Function` - **Signature:** `(fieldName: string, value: any, formData: Object) => void` ### `onSubmit` (Function) + - **Description:** Submit handler function called when form is submitted - **Type:** `Function` - **Signature:** `(formData: Object) => Promise | void` ### `showSubmitButton` (Boolean) + - **Description:** Controls visibility of the submit button - **Type:** `Boolean` - **Default:** `true` ### `showCancelButton` (Boolean) + - **Description:** Controls visibility of the cancel button - **Type:** `Boolean` - **Default:** `false` ### `submitButtonText` (String) + - **Description:** Text displayed on the submit button - **Type:** `String` - **Default:** `'Submit'` ### `cancelButtonText` (String) + - **Description:** Text displayed on the cancel button - **Type:** `String` - **Default:** `'Cancel'` ### `validateOnChange` (Boolean) + - **Description:** Enables real-time validation as fields change - **Type:** `Boolean` - **Default:** `true` @@ -112,6 +145,7 @@ const handleFieldChange = (event) => { Each field object in the `fields` array supports the following properties: ### Basic Properties + - **`name`** (String, required) - Unique identifier for the field - **`label`** (String, required) - Display label for the field - **`type`** (String, required) - Field input type @@ -123,23 +157,27 @@ Each field object in the `fields` array supports the following properties: - **`defaultValue`** (Any) - Initial value for the field ### Layout Properties + - **`cols`** (Number, default: `12`) - Column width on extra small screens - **`sm`** (Number, default: `12`) - Column width on small screens - **`md`** (Number, default: `6`) - Column width on medium screens - **`lg`** (Number, default: `6`) - Column width on large screens ### Validation Properties + - **`validate`** (Function) - Custom validation function - **Signature:** `(value: any) => string | null` - **Returns:** Error message string or null if valid ### Field-Specific Properties + - **`onChangeOverride`** (Function) - Field-specific change handler that overrides global onChange - **Signature:** `(value: any, fieldName: string, formData: Object) => void` ## Field Types ### Text Input (`type: 'text'`) + Standard text input field with optional format validation. ```javascript @@ -153,9 +191,11 @@ Standard text input field with optional format validation. ``` **Additional Properties:** + - **`format`** (String) - Input format validation (`'email'` for email validation) ### Number Input (`type: 'number'`) + Numeric input field with optional min/max constraints. ```javascript @@ -170,11 +210,13 @@ Numeric input field with optional min/max constraints. ``` **Additional Properties:** + - **`min`** (Number) - Minimum allowed value - **`max`** (Number) - Maximum allowed value - **`step`** (Number) - Step increment for the input ### Textarea (`type: 'textarea'`) + Multi-line text input for longer content. ```javascript @@ -188,10 +230,12 @@ Multi-line text input for longer content. ``` **Additional Properties:** + - **`rows`** (Number, default: `3`) - Number of visible text lines ### Select Dropdown (`type: 'select'`) -Dropdown selection field with predefined options. + +Dropdown selection field with predefined options and built-in filtering. ```javascript { @@ -203,14 +247,87 @@ Dropdown selection field with predefined options. { label: 'United States', value: 'us' }, { label: 'Canada', value: 'ca' }, { label: 'Mexico', value: 'mx' } - ] + ], + filter: true, // Enable search filtering + showClear: true // Allow clearing selection } ``` **Additional Properties:** + - **`options`** (Array, required) - Array of option objects with `label` and `value` properties +- **`optionLabel`** (String, default: `'label'`) - Property name for display text +- **`optionValue`** (String, default: `'value'`) - Property name for value +- **`filter`** (Boolean, default: `true`) - Enable filtering/search +- **`showClear`** (Boolean, default: `true`) - Show clear button + +### ⭐ AutoComplete (`type: 'autocomplete'`) - NEW! + +**The game-changer!** AutoComplete field that allows users to select from suggestions OR enter completely custom values not in the predefined list. + +```javascript +// Basic AutoComplete - allows custom entries +{ + name: 'skills', + label: 'Skills', + type: 'autocomplete', + placeholder: 'Add your skills', + options: [ + { label: 'JavaScript', value: 'js' }, + { label: 'Python', value: 'python' }, + { label: 'Vue.js', value: 'vue' } + ], + optionLabel: 'label', + optionValue: 'value', + forceSelection: false, // KEY: Allows custom values! + multiple: true, // Multiple selection + helpText: 'Select existing skills or add your own custom skills' +} + +// Strict selection (only predefined options) +{ + name: 'department', + label: 'Department', + type: 'autocomplete', + options: [ + { label: 'Engineering', value: 'eng' }, + { label: 'Marketing', value: 'mkt' }, + { label: 'Sales', value: 'sales' } + ], + forceSelection: true, // Only allows predefined options + dropdown: true, // Show dropdown button + required: true +} + +// Dynamic/Remote data loading +{ + name: 'users', + label: 'Select Users', + type: 'autocomplete', + options: [], // Initially empty + onSearch: async (query, callback) => { + if (query.length > 2) { + const response = await fetch(`/api/users?search=${query}`); + const users = await response.json(); + callback(users); // Update options dynamically + } + } +} +``` + +**Additional Properties:** + +- **`options`** (Array) - Array of suggestion objects +- **`optionLabel`** (String, default: `'label'`) - Property for display text +- **`optionValue`** (String, default: `'value'`) - Property for value +- **`forceSelection`** (Boolean, default: `false`) - If true, only allows predefined options. If false, allows custom values! +- **`multiple`** (Boolean, default: `false`) - Allow multiple selections +- **`dropdown`** (Boolean, default: `true`) - Show dropdown button +- **`onSearch`** (Function) - Custom search function for dynamic data loading + - **Signature:** `(query: string, callback: Function) => void` ### Checkbox (`type: 'checkbox'`) + Boolean checkbox input. ```javascript @@ -223,6 +340,7 @@ Boolean checkbox input. ``` ### Radio Group (`type: 'radio'`) + Radio button group for single selection from multiple options. ```javascript @@ -240,9 +358,11 @@ Radio button group for single selection from multiple options. ``` **Additional Properties:** + - **`options`** (Array, required) - Array of option objects with `label` and `value` properties ### Date Input (`type: 'date'`) + Date picker input field. ```javascript @@ -257,10 +377,12 @@ Date picker input field. ``` **Additional Properties:** + - **`min`** (String) - Minimum allowed date (YYYY-MM-DD format) - **`max`** (String) - Maximum allowed date (YYYY-MM-DD format) ### DateTime Input (`type: 'datetime'`) + Date and time picker input field. ```javascript @@ -273,10 +395,12 @@ Date and time picker input field. ``` **Additional Properties:** + - **`min`** (String) - Minimum allowed datetime - **`max`** (String) - Maximum allowed datetime ### File Input (`type: 'file'`) + File upload input field. ```javascript @@ -290,26 +414,31 @@ File upload input field. ``` **Additional Properties:** + - **`accept`** (String) - File types to accept (MIME types or file extensions) - **`multiple`** (Boolean, default: `false`) - Allow multiple file selection ## Events ### `update:formData` + - **Description:** Emitted when form data changes (controlled mode only) - **Payload:** Updated form data object - **Usage:** `@update:formData="handleFormDataUpdate"` ### `submit` + - **Description:** Emitted when form is successfully submitted - **Payload:** Form data object - **Usage:** `@submit="handleSubmit"` ### `cancel` + - **Description:** Emitted when cancel button is clicked - **Usage:** `@cancel="handleCancel"` ### `change` + - **Description:** Emitted when any field value changes - **Payload:** Object with `fieldName`, `value`, and `formData` properties - **Usage:** `@change="handleFieldChange"` @@ -324,36 +453,41 @@ The component exposes several methods that can be accessed via template refs: ``` ### `validateForm()` + - **Description:** Validates the entire form and returns validation status - **Returns:** `Boolean` - `true` if valid, `false` if invalid - **Side Effect:** Updates form error state ### `getCurrentFormData()` + - **Description:** Gets the current form data object - **Returns:** `Object` - Current form data ### `resetForm()` + - **Description:** Resets form to initial state and clears all errors - **Returns:** `void` ### `setFieldError(fieldName, error)` + - **Description:** Sets an error message for a specific field - **Parameters:** - `fieldName` (String) - The field name - `error` (String) - Error message to display ### `clearFieldError(fieldName)` + - **Description:** Clears the error for a specific field - **Parameters:** - `fieldName` (String) - The field name to clear @@ -361,55 +495,56 @@ const resetForm = () => formRef.value.resetForm() ## Usage Examples ### Basic Contact Form + ```vue ``` -### User Registration Form +### AutoComplete Examples + ```vue + + +``` + +### User Registration Form + +```vue +