From b9bfa5546bfb6029d351c8308a218a425a0fb8b6 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Sun, 12 Apr 2026 15:56:41 +0530 Subject: [PATCH 01/12] feat: add form and team type definitions Introduced new TypeScript type definitions for forms and teams, including `Form`, `FormField`, `FPTeam`, and `FPTeamMember`. These definitions enhance type safety and structure for managing forms and team data within the application. --- frontend/src/types/FormsPro/form.types.ts | 38 ++++++++++++++ .../src/types/FormsPro/form_field.types.ts | 52 +++++++++++++++++++ frontend/src/types/FormsPro/fpteam.types.ts | 20 +++++++ .../src/types/FormsPro/fpteam_member.types.ts | 14 +++++ 4 files changed, 124 insertions(+) create mode 100644 frontend/src/types/FormsPro/form.types.ts create mode 100644 frontend/src/types/FormsPro/form_field.types.ts create mode 100644 frontend/src/types/FormsPro/fpteam.types.ts create mode 100644 frontend/src/types/FormsPro/fpteam_member.types.ts diff --git a/frontend/src/types/FormsPro/form.types.ts b/frontend/src/types/FormsPro/form.types.ts new file mode 100644 index 0000000..1293ece --- /dev/null +++ b/frontend/src/types/FormsPro/form.types.ts @@ -0,0 +1,38 @@ +import { FormField } from "./form_field.types"; + +export interface Form { + name: string; + creation: string; + modified: string; + owner: string; + modified_by: string; + docstatus: 0 | 1 | 2; + parent?: string; + parentfield?: string; + parenttype?: string; + idx?: number; + /** Is Published? : Check */ + is_published?: 0 | 1; + /** Route : Data */ + route?: string; + /** Title : Data */ + title: string; + /** Linked Doctype : Link - DocType */ + linked_doctype: string; + /** Linked Team : Link - FP Team */ + linked_team_id: string; + /** Login Required : Check */ + login_required?: 0 | 1; + /** Allow Incomplete Forms : Check - Allow saving Draft forms */ + allow_incomplete?: 0 | 1; + /** Success Title : Data */ + success_title?: string; + /** Success Description : Text Editor */ + success_description?: string; + /** Description : Text Editor */ + description?: string; + /** Fields : Table - Form Field */ + fields?: FormField[]; + /** Meta Data : Code */ + metadata?: string; +} diff --git a/frontend/src/types/FormsPro/form_field.types.ts b/frontend/src/types/FormsPro/form_field.types.ts new file mode 100644 index 0000000..4d9ffe9 --- /dev/null +++ b/frontend/src/types/FormsPro/form_field.types.ts @@ -0,0 +1,52 @@ +export enum Fieldtype { + "ATTACH" = "Attach", + "DATA" = "Data", + "NUMBER" = "Number", + "EMAIL" = "Email", + "DATE" = "Date", + "DATE_TIME" = "Date Time", + "DATE_RANGE" = "Date Range", + "TIME_PICKER" = "Time Picker", + "PASSWORD" = "Password", + "SELECT" = "Select", + "MULTISELECT" = "Multiselect", + "SWITCH" = "Switch", + "TEXTAREA" = "Textarea", + "TEXT_EDITOR" = "Text Editor", + "LINK" = "Link", + "CHECKBOX" = "Checkbox", + "RATING" = "Rating", + "PHONE" = "Phone", + "TABLE" = "Table", +} + +export interface FormField { + name: string; + creation: string; + modified: string; + owner: string; + modified_by: string; + docstatus: 0 | 1 | 2; + parent?: string; + parentfield?: string; + parenttype?: string; + idx?: number; + /** Mandatory : Check */ + reqd?: 0 | 1; + /** Hidden : Check */ + hidden?: 0 | 1; + /** Label : Data */ + label: string; + /** Fieldtype : Select */ + fieldtype: Fieldtype; + /** Fieldname : Data */ + fieldname: string; + /** Description : Small Text */ + description?: string; + /** Options : Small Text */ + options?: string; + /** Default : Small Text */ + default?: string; + /** Conditional Logic : Code */ + conditional_logic?: string; +} diff --git a/frontend/src/types/FormsPro/fpteam.types.ts b/frontend/src/types/FormsPro/fpteam.types.ts new file mode 100644 index 0000000..faa490d --- /dev/null +++ b/frontend/src/types/FormsPro/fpteam.types.ts @@ -0,0 +1,20 @@ +import { FPTeamMember } from "./fpteam_member.types"; + +export interface FPTeam { + name: string; + creation: string; + modified: string; + owner: string; + modified_by: string; + docstatus: 0 | 1 | 2; + parent?: string; + parentfield?: string; + parenttype?: string; + idx?: number; + /** Logo : Attach Image */ + logo?: string; + /** Team Name : Data */ + team_name: string; + /** Users : Table MultiSelect - FP Team Member */ + users?: FPTeamMember[]; +} diff --git a/frontend/src/types/FormsPro/fpteam_member.types.ts b/frontend/src/types/FormsPro/fpteam_member.types.ts new file mode 100644 index 0000000..1fe67e6 --- /dev/null +++ b/frontend/src/types/FormsPro/fpteam_member.types.ts @@ -0,0 +1,14 @@ +export interface FPTeamMember { + name: string; + creation: string; + modified: string; + owner: string; + modified_by: string; + docstatus: 0 | 1 | 2; + parent?: string; + parentfield?: string; + parenttype?: string; + idx?: number; + /** User : Link - User */ + user?: string; +} From e921e261042ebbca9ce7e8f91710b0728a8f864d Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Sun, 12 Apr 2026 16:34:59 +0530 Subject: [PATCH 02/12] refactor: centralise field type metadata into a single registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce `frontend/src/config/fieldTypes.ts` as the single source of truth for field-type-specific metadata (component, layout, Frappe fieldtype mapping, boolean/date semantics). Everything that previously required touching 5+ files now lives in one place. - Replace scattered `FormFieldTypes` enum with the auto-generated `Fieldtype` enum from `form_field.types.ts` (frappe-types output), so `form_field.json` is the ground truth for valid names. - Rewrite `form_fields.ts` to re-export from the registry; remove the unused individual named field exports. - `mapDoctypeFieldForForm` now maps to typed `Fieldtype` values. - Replace the 8-branch `to_frappe_field` if/elif chain in `form_field.py` with a declarative `FORM_TO_FRAPPE_FIELDTYPE` dict — adding a new field type is one line. - Fieldtype dropdown in `FieldPropertiesForm` is derived from the registry, so only implemented types are offered. --- .../doctype/form_field/form_field.py | 49 ++-- .../field-editor/FieldPropertiesForm.vue | 5 +- .../form/submissions/SubmissionFieldValue.vue | 39 +-- frontend/src/config/fieldTypes.ts | 266 +++++++++++++++++ frontend/src/stores/editForm.ts | 4 +- frontend/src/types/formfield.ts | 24 +- frontend/src/utils/form_fields.ts | 274 ++++-------------- 7 files changed, 370 insertions(+), 291 deletions(-) create mode 100644 frontend/src/config/fieldTypes.ts diff --git a/forms_pro/forms_pro/doctype/form_field/form_field.py b/forms_pro/forms_pro/doctype/form_field/form_field.py index f347b6d..209b21d 100644 --- a/forms_pro/forms_pro/doctype/form_field/form_field.py +++ b/forms_pro/forms_pro/doctype/form_field/form_field.py @@ -4,6 +4,32 @@ # import frappe from frappe.model.document import Document +# Maps Forms Pro field types to Frappe CustomField fieldtypes. +# When adding a new field type: +# 1. Add the option to form_field.json → DF.Literal regenerates automatically +# 2. Add an entry here +# 3. Add an entry to FIELD_TYPE_DEFINITIONS in frontend/src/config/fieldTypes.ts +FORM_TO_FRAPPE_FIELDTYPE: dict[str, dict] = { + "Attach": {"fieldtype": "Attach"}, + "Data": {"fieldtype": "Data"}, + "Number": {"fieldtype": "Int"}, + "Email": {"fieldtype": "Data", "options": "Email"}, + "Date": {"fieldtype": "Date"}, + "Date Time": {"fieldtype": "Datetime"}, + "Date Range": {"fieldtype": "Data"}, + "Time Picker": {"fieldtype": "Time"}, + "Password": {"fieldtype": "Password"}, + "Select": {"fieldtype": "Select"}, + "Phone": {"fieldtype": "Phone"}, + "Switch": {"fieldtype": "Check"}, + "Textarea": {"fieldtype": "Text"}, + "Text Editor": {"fieldtype": "Text Editor"}, + "Link": {"fieldtype": "Link"}, + "Checkbox": {"fieldtype": "Check"}, + "Rating": {"fieldtype": "Rating"}, + "Table": {"fieldtype": "Table"}, +} + class FormField(Document): # begin: auto-generated types @@ -49,30 +75,13 @@ class FormField(Document): @property def to_frappe_field(self) -> dict: - _fieldtype = self.fieldtype - - if self.fieldtype == "Email": - _fieldtype = "Data" - self.options = "Email" - elif self.fieldtype == "Number": - _fieldtype = "Int" - elif self.fieldtype == "Date Time": - _fieldtype = "Datetime" - elif self.fieldtype == "Date Range": - _fieldtype = "Data" - elif self.fieldtype == "Time Picker": - _fieldtype = "Time" - elif self.fieldtype == "Switch" or self.fieldtype == "Checkbox": - _fieldtype = "Check" - elif self.fieldtype == "Textarea": - _fieldtype = "Text" - + mapping = FORM_TO_FRAPPE_FIELDTYPE.get(self.fieldtype, {}) return { "fieldname": self.fieldname, - "fieldtype": _fieldtype, + "fieldtype": mapping.get("fieldtype", self.fieldtype), "label": self.label, "reqd": self.reqd, - "options": self.options, + "options": mapping.get("options", self.options), "description": self.description, "default": self.default, } diff --git a/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue b/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue index 4183359..38cc19a 100644 --- a/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue +++ b/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue @@ -1,7 +1,8 @@ + + diff --git a/frontend/src/components/builder/FieldRenderer.vue b/frontend/src/components/builder/FieldRenderer.vue index 9af4ea7..38fc1a4 100644 --- a/frontend/src/components/builder/FieldRenderer.vue +++ b/frontend/src/components/builder/FieldRenderer.vue @@ -1,9 +1,10 @@ + diff --git a/frontend/src/config/fieldTypes.ts b/frontend/src/config/fieldTypes.ts index 3a42981..a83a6ed 100644 --- a/frontend/src/config/fieldTypes.ts +++ b/frontend/src/config/fieldTypes.ts @@ -43,12 +43,12 @@ export { Fieldtype }; /** * Controls how FieldRenderer positions the label relative to the input widget. * - * - "default" label on top, input below, description at the bottom - * - "inline" input first, label to the right (Switch, Checkbox) - * - "below-label" label on top, input immediately below before description (Text Editor, Attach) - * - "custom" the component handles its own full layout (Table) + * - "default" label on top, input below, description at the bottom + * - "inline" input first, label to the right (Switch, Checkbox) + * - "description-first" label on top, description below label, input at the bottom (Text Editor) + * - "custom" the component handles its own full layout (Table) */ -export type FieldLayout = "default" | "inline" | "below-label" | "custom"; +export type FieldLayout = "default" | "inline" | "description-first" | "custom"; export type FieldTypeDefinition = { /** Canonical name — must match a Fieldtype enum value */ @@ -205,7 +205,7 @@ export const FIELD_TYPE_DEFINITIONS: FieldTypeDefinition[] = [ bubbleMenu: true, starterkitOptions: { heading: { levels: [2, 3, 4] } }, }, - layout: "below-label", + layout: "description-first", frappeFieldtype: "Text Editor", isBoolean: false, isDate: false, From 3a011700ae3b17443b441346f8e054198a113f71 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Sun, 12 Apr 2026 16:47:26 +0530 Subject: [PATCH 04/12] refactor: derive boolean/date field behaviour from registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded field type arrays and string comparisons in conditionals.ts and SubmissionFieldValue.vue with registry lookups. - conditionals.ts: getFieldValue uses isBoolean flag instead of explicit Switch/Checkbox check; Number coercion uses Fieldtype enum - SubmissionFieldValue: isDateField and classNames derived from typeDef.isDate and typeDef.isBoolean — new field types with these flags set get correct behaviour automatically --- .../form/submissions/SubmissionFieldValue.vue | 19 ++++++++----------- frontend/src/utils/conditionals.ts | 10 ++++++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/form/submissions/SubmissionFieldValue.vue b/frontend/src/components/form/submissions/SubmissionFieldValue.vue index b1af7ce..86ed8fb 100644 --- a/frontend/src/components/form/submissions/SubmissionFieldValue.vue +++ b/frontend/src/components/form/submissions/SubmissionFieldValue.vue @@ -1,6 +1,7 @@