chore(version-15): bring latest develop changes to version-15#143
Merged
Conversation
- Introduced a new section in pyproject.toml for frappe dependencies, specifying version constraints for better compatibility in benchmarking tasks.
* chore: bump frappe-ui to `0.1.262` * feat: add 'Table' option to fieldtype selection - Updated form_field.json and form_field.py to include 'Table' in the list of selectable field types for enhanced functionality. - Adjusted the modified timestamp in form_field.json to reflect recent changes. * feat: implement Table field component and enhance field options handling - Added a new Table component for rendering tabular data input. - Updated RenderField and FieldRenderer components to support dynamic options for Select and Link fields. - Introduced useFieldOptions composable for improved option management. - Enhanced form_fields utility to include Table as a selectable field type. - Updated auto-imports and TypeScript definitions to accommodate new functionality. * feat: enhance Table component fieldtype mapping - Integrated utility function to map fieldtype for Table component, ensuring proper handling of field types. - Defaulted unmapped fieldtypes to "Data" for improved consistency in table rendering. * fix: update TableField component to use the correct Table component - Replaced ListView with Table in the TableField definition to ensure proper rendering of tabular data. - Adjusted imports in form_fields utility to reflect the change in component usage. * refactor: remove onMounted hook from useFieldOptions - Eliminated the onMounted lifecycle hook from the useFieldOptions composable to streamline the loading process of options. - Adjusted imports to reflect the removal of the unused onMounted function.
- Changed the frappe dependency version constraint from ">=17.0.0-dev,<18.0.0" to ">=16.0.0-dev,<18.0.0" for better compatibility.
- Changed `:model-value` to `:content` for TextEditor components across multiple files to ensure consistency in prop usage. - Added an empty declaration block in auto-imports.d.ts for improved TypeScript support.
* chore: bump lucide-icons * feat: restructure home dashboard and sidebar - Introduced a new home dashboard layout with a dedicated Dashboard.vue component. - Migrated existing dashboard functionality from the previous Dashboard.vue to the new home structure. - Added sidebar items management through a new sidebarItems.ts file for better organization. - Updated TeamSwitcher component to conditionally render based on the current team. - Removed unused code and components to streamline the application. * refactor: enhance layout structure in BaseLayout and Dashboard components - Wrapped the slot in BaseLayout with a div for improved layout consistency. - Removed unnecessary padding class from the Dashboard component for a cleaner design. - Ensured consistent formatting in watch options for better readability. * fix: manageform page layout * feat: team invitations via User Invitations * feat: implement team member removal and permission toggling - Added functionality to remove members from a team and toggle their edit permissions. - Introduced `remove_member_from_team` and `toggle_can_edit_team` API endpoints. - Created `RemoveMemberDialog` component for user confirmation before removal. - Updated `TeamMemberList` to include removal actions and permission toggling. - Enhanced `FPTeam` model to manage team member permissions effectively. * feat: add team details update functionality - Implemented a new API endpoint `save` to update team fields, allowing modifications to `team_name` and `logo`. - Created `ManageTeamHeader` component for editing team name and logo upload functionality. - Integrated the new save functionality into the team store for seamless updates. - Updated `ManageTeam` page to include the new header component for enhanced team management. * refactor: clean up imports in team store - Removed unused `call` import from the team store file to streamline dependencies. * chore: better code - Added checks to prevent duplicate team member invitations and ensure team owners cannot have their permissions toggled. - Updated `FPTeam` model to prevent removal of the team owner from the team. - Modified `RemoveMemberDialog` and `TeamMemberList` components to improve member removal functionality and user experience. * fix: tests * chore: better code
* feat: Image uploader component with crop * feat: integrate ImageUploader for team logo management - Replaced the existing logo upload button with an ImageUploader component for enhanced functionality. - Added error handling and upload progress display within the ImageUploader. - Updated the button label dynamically based on the upload state and existing logo presence.
- Added a search input to the TeamSwitcher component, allowing users to filter teams by name. - Updated the team options computation to include search query handling. - Integrated a new search icon and improved layout for better user experience. - Refactored dropdown item templates to accommodate the search input and maintain consistent styling.
- Replaced FileUploader with ImageUploader component for improved logo management. - Updated type handling for uploaded files to align with new component. - Enhanced error handling and upload progress display in the logo upload section. - Adjusted button label to reflect upload status dynamically.
- Modified the layout classes for improved design consistency and responsiveness. - Changed the logout button icon from a string to a component reference for better integration with the icon library.
- Integrated Breadcrumbs component into the Dashboard for improved navigation. - Defined breadcrumb items to enhance user context within the application.
* feat: add submissions page and update sidebar navigation - Introduced a new "Manage Form Submissions" page for viewing form submissions. - Updated the router to include a route for submissions. - Refactored sidebar items to include navigation to the new submissions page. - Enhanced the overview page with breadcrumb navigation for better user context. * feat: submissions page v1 * feat: add Drawer component for UI enhancement - Introduced a new Drawer component to provide a sliding panel interface. - Implemented customizable properties for size, position, title, and actions. - Added keyboard accessibility with Escape key support for closing the drawer. - Included transition effects for smooth opening and closing animations. * feat: extend Drawer component with additional size options - Added "xl" size option to the Drawer component for enhanced customization. - Updated size classes for both horizontal and vertical orientations to include new "xl" dimensions. * feat: add submission details view and field value component - Introduced SubmissionDetails component to display detailed information about a specific submission. - Created SubmissionFieldValue component to render individual field values with appropriate formatting based on field type. - Enhanced SubmissionList component to include a drawer for viewing submission details upon row click. - Updated API integration to fetch submission data securely based on user permissions. * feat: enhance SubmissionFieldValue component with dynamic class handling - Added computed property to determine class names based on field type, improving layout for Switch and Checkbox types. - Updated template structure to utilize dynamic classes for better styling and responsiveness. * fix: validate doctype against linked_doctype in get_submission_response A client could pass an arbitrary doctype to access submissions from a different form. Now we fetch both linked_team_id and linked_doctype from the Form doc and reject requests where the supplied doctype doesn't match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: throw when form not found in get_all_submissions If form_id is invalid, frappe.db.get_value returns None and calling has_permission(doc=None) has undefined behaviour. Explicitly throw DoesNotExistError before the permission check. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: show dash placeholder for null textarea value in SubmissionFieldValue Textarea was rendering blank when value is null/undefined, inconsistent with the default case which uses value ?? "–". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: remove unused isLoading ref in SubmissionList isLoading was declared and set but never bound in the template, so the loading state had no effect and was also never cleared on fetch error. Removed it entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: show loading state in drawer when formData not yet available If a row is clicked before formData loads, the drawer was empty. Now shows a Loading... placeholder until linked_doctype is available. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add aria-labelledby to Drawer dialog element Adds id="drawer-title" to the title heading and wires aria-labelledby on the dialog div so assistive tech can announce the drawer title. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: form sharing methods - Introduced `add_form_access` and `set_form_permission` functions to manage user permissions on forms. - Updated the frontend to utilize the new API endpoints for adding access and setting permissions. - Enhanced permission validation to ensure only authorized users can share forms. * fix: validate permission_to allowlist and add docstrings to sharing API - Validate `permission_to` against an explicit allowlist in `set_form_permission` to prevent unexpected kwargs from being forwarded to `add_docshare` - Use `int(bool(value))` for safe coercion of the permission value - Expand docstrings on `add_form_access` and `set_form_permission` with full Args/Raises sections and inline comments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update gitignore and pre-commit config for current project structure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: replace license.txt with LICENSE Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: add community health files and GitHub templates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update README with contributing and security references Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update Python version references to 3.14 across all configs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: set ruff target-version to py313 (py314 not yet supported) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](actions/cache@v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v3...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v4...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pre-commit/action](https://github.com/pre-commit/action) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/pre-commit/action/releases) - [Commits](pre-commit/action@v3.0.0...v3.0.1) --- updated-dependencies: - dependency-name: pre-commit/action dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v3...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [jsdom](https://github.com/jsdom/jsdom) from 25.0.1 to 29.0.2. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](jsdom/jsdom@v25.0.1...v29.0.2) --- updated-dependencies: - dependency-name: jsdom dependency-version: 29.0.2 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [zod](https://github.com/colinhacks/zod) from 4.1.12 to 4.3.6. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](colinhacks/zod@v4.1.12...v4.3.6) --- updated-dependencies: - dependency-name: zod dependency-version: 4.3.6 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [vue](https://github.com/vuejs/core) from 3.5.21 to 3.5.32. - [Release notes](https://github.com/vuejs/core/releases) - [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md) - [Commits](vuejs/core@v3.5.21...v3.5.32) --- updated-dependencies: - dependency-name: vue dependency-version: 3.5.32 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
) Bumps [socket.io-client](https://github.com/socketio/socket.io) from 4.8.1 to 4.8.3. - [Release notes](https://github.com/socketio/socket.io/releases) - [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.1...socket.io-client@4.8.3) --- updated-dependencies: - dependency-name: socket.io-client dependency-version: 4.8.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.6 to 8.5.8. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](postcss/postcss@8.5.6...8.5.8) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
The vulnerable dependency check has been extracted from the linter workflow into its own dedicated workflow file. This change improves organization and allows for more focused execution of dependency checks during pull requests and manual triggers.
* chore(deps): add torph * refactor(editForm): expose isSaving and return promise from togglePublish Consumers need to await the publish toggle to freeze UI state until the server response settles. isSaving surfaces setValue.loading so components don't reach into formResource internals. * fix(FormBuilderHeader): prevent action button label from flickering frappe-ui's setValue.submit mutates doc optimistically, making isDirty briefly true before originalDoc syncs — causing two reactive renders and two TextMorph transitions per click. Freeze the button config on click and release only after the settled state is stable. Also integrates TextMorph for animated label transitions and fixes the :loading binding to reflect setValue state (not just fetch state). * fix(FormBuilderHeader): add aria-label to action button for Playwright compatibility TextMorph keeps exiting chars in DOM during animation, so Playwright resolves the button's accessible name as merged old+new text. aria-label bypasses inner text entirely, giving tests a stable selector.
Bumps [pinia](https://github.com/vuejs/pinia) from 3.0.3 to 3.0.4. - [Release notes](https://github.com/vuejs/pinia/releases) - [Commits](vuejs/pinia@v3.0.3...v3.0.4) --- updated-dependencies: - dependency-name: pinia dependency-version: 3.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.10 to 8.5.14. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](postcss/postcss@8.5.10...8.5.14) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
#120) Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.4.21 to 10.5.0. - [Release notes](https://github.com/postcss/autoprefixer/releases) - [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md) - [Commits](postcss/autoprefixer@10.4.21...10.5.0) --- updated-dependencies: - dependency-name: autoprefixer dependency-version: 10.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@lottiefiles/dotlottie-vue](https://github.com/LottieFiles/dotlottie-web/tree/HEAD/packages/vue) from 0.11.11 to 0.11.12. - [Release notes](https://github.com/LottieFiles/dotlottie-web/releases) - [Changelog](https://github.com/LottieFiles/dotlottie-web/blob/main/packages/vue/CHANGELOG.md) - [Commits](https://github.com/LottieFiles/dotlottie-web/commits/@lottiefiles/dotlottie-vue@0.11.12/packages/vue) --- updated-dependencies: - dependency-name: "@lottiefiles/dotlottie-vue" dependency-version: 0.11.12 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(FormField): add row_index and column_index for multi-column layout Add row_index and column_index Int fields to FormField schema to support side-by-side field placement. Both fields are layout-only and excluded from DocType sync via to_frappe_field. Includes backfill patch (direct SQL, idempotent) and tests covering sync regression and patch correctness. * feat(store): add layout helpers and useGroupedRows composable Add row_index/column_index to FormField type. Add moveField, insertNewRow, and compact helpers to editForm store; update addField, addFieldFromDoctype, and removeField to maintain layout invariants. Extract useGroupedRows as a shared composable for use in both the builder and submission renderer. * fix(store): guard addFieldFromDoctype, validate membership in move helpers Add null guard to addFieldFromDoctype matching addField's pattern. Add fs.includes() guard to moveField and insertNewRow to prevent corrupting layout when a stale field ref is passed. Extract lastRowIndex helper using reduce to avoid spread-on-large-array. * feat(builder): row-based canvas render using groupedRows Replace flat vuedraggable list with row/column grid derived from useGroupedRows. Fields with the same row_index render side-by-side as flex items. Extract FieldCard.vue from FormBuilderContent. Drag-and-drop temporarily removed; restored in next chunk. * fix(builder): stable v-for keys and restore row vertical spacing Key rows by row_index value instead of array index to prevent wrong-row DOM patching on delete. Key FieldCard by row_index+column_index instead of idx which is undefined on new fields before save. Restore gap-3 between rows to match previous my-3 spacing. * feat(builder): restore drag-and-drop with nested vuedraggable Replace flat draggable with per-row inner draggables using group="fields" for cross-row drag. onFieldChange handles evt.moved (within-row reorder via direct column_index renumber) and evt.added (cross-row via moveField). Source row evt.removed is a no-op; target evt.added owns the move. * feat(FormBuilder): row drop zones, eject-to-row, and layout fixes - RowDropZone: thin drop target between rows; expands on drag-start so fields can be inserted between existing rows instead of merging as a new column. Uses an empty vuedraggable list with put=true/pull=false and clears its buffer on nextTick after emitting. - Eject to own row: SquareSplitVertical button appears on FieldActions when a field shares its row with at least one other field. Clicking calls insertNewRow(field, row_index + 1), pushing the field to a new row immediately below. - Code-review fixes: extract rowIndexOf() helper (eliminates repeated row[0]?.row_index ?? rIdx), remove outer wrapper div (key + classes moved to draggableComponent via <template v-for>), oldIdx guard now returns early instead of silently skipping the splice. Note: cross-row visual swap requires the SortableJS Swap plugin which is not bundled in vuedraggable 4.x — within-row reorder already behaves correctly as a swap. * feat(FormBuilder): hover highlight on drop zones, hide bottom zone when dragging last row RowDropZone: MutationObserver watches for SortableJS placeholder insertion to detect hover state (more reliable than pointer events during drag). Zone expands and shows dashed border while dragging; turns blue when hovered. FormBuilderContent: track draggingFromRow per @start event. Bottom drop zone is hidden when the field being dragged is already from the last row (dropping it there would be a no-op). * feat(FormRenderer): row-based render with mobile stacking Group submission fields by row_index/column_index via useGroupedRows. Rows render flex-col on mobile, flex-row on md+. Use v-if on row + field so conditionally hidden fields are unmounted (clears stale v-model state and skips reqd computation). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(layout): column stacking via cell_index third axis Add cell_index to FormField so multiple fields can stack vertically inside a single column. Sort key becomes (row_index, column_index, cell_index). Drag a field onto another column's cell list to drop above/below existing cells; ColumnDropZone (vertical strip between columns) handles new-column creation. - Backend: cell_index Int + regenerated types + extended layout-not- synced test - Composable: useGroupedRows returns FormField[][][] (rows -> cols -> cells) - Store: 3-axis compact preserves multi-cell column grouping by remapping distinct column_index values per row; new insertCell; moveField/insertNewRow/addField set cell_index = 0 - FormBuilderContent: row -> column -> cell render with ColumnDropZone between columns; per-column draggable cell list (group "fields") - FormRenderer: 3D render with v-if visibility per row/column/cell CellDropZone deemed redundant — vuedraggable's native cell-list insertion already shows the indicator and computes newIndex. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(FormBuilder): preserve last-row drag, polish drop zones - v-show (was v-if) on trailing RowDropZone — keeps SortableJS instance alive across drag start, fixes last-row reorder - global pointerup/dragend reset drag state when drop misses a target - drop zones: subtle blue tint (was dashed border), gap-4 between cells * fix(FormBuilder): sleek drop zones, no layout shift on drag - outer drop zones reserve constant size (h-6 row, w-4 col); visual hairline rendered via ::before pseudo only - removed parent flex gaps; zones now provide all inter-row/col spacing - no flow shift when drag starts — form stays still - highlighted state: 4px tinted bar, dragging: 1px hairline, idle: hidden * fix(FormBuilder): scope drop zone transitions, respect reduced-motion - before:transition-all → before:transition-[height,background-color] (row) / [width,background-color] (col): avoid animating unrelated props - add before:ease-out for entrance feel - motion-reduce:before:transition-none honors prefers-reduced-motion * fix(FormBuilder): smooth drag reorder via SortableJS animation Add :animation="150" to column draggable. SortableJS default 0ms made sibling swaps instant, producing jittery snap-snap-snap during drag. 150ms shuffle animation smooths sibling reorder. * feat(FormBuilder): add data-* test hooks and force pointer-event fallback - FieldCard, RowDropZone, ColumnDropZone, FormBuilderContent, FormRenderer now expose stable data-form-builder-component and data-form-renderer-component selectors plus row/column/cell index attributes for layout introspection. - Eject button on FieldActions tagged with a stable selector. - Set forceFallback: true on the cell column and drop zone draggables so SortableJS uses pointer events instead of native HTML5 drag, making drag behavior deterministic in headless browsers (and unblocks Playwright). * test(e2e): cover row/column/cell layout flows Adds frontend/e2e/specs/form-layout.spec.ts with 8 scenarios: 1. Drag-stack a cell into an existing column. 2. Drop on a column drop zone creates a new column. 3. Drop on a row drop zone creates a new row. 4. Eject button moves a stacked cell to its own row. 5. Cross-row drag collapses the source column. 6. Within-column reorder renumbers cell_index. 7. Mobile viewport (375x800) stacks columns vertically (flex-col). 8. Conditional hide unmounts the column entirely (no empty gap). Extends FormBuilderPage with layout introspection (rowCount, columnCount, cellCount, fieldCard, waitForFields) and drag helpers (dragFieldOntoCell, dragFieldToColumnZone, dragFieldToRowZone, ejectField). Drag uses a real mouse path with intermediate steps; cross-row drags route through an L-shaped waypoint via the row drop zone gap so SortableJS doesn't accidentally swap into a neighbor column en route. * docs(patch): note backfill predicate handles Int NOT NULL DEFAULT 0 Frappe's schema sync materializes new Int columns as NOT NULL DEFAULT 0, so legacy FormField rows pre-dating row_index/column_index land on 0 after migration, never NULL. The existing != predicate is sufficient; adding an IS NULL branch (per CodeRabbit suggestion) would be unreachable. Comment captures the reasoning so future maintainers don't re-litigate it. * fix(FormBuilder): address review feedback on layout edges Three changes from CodeRabbit pass: - FormBuilderContent: trailing RowDropZone no longer hides when the drag starts in the last row. The previous guard (`draggingFromRow !== rowIndexOf(row, rIdx)`) blocked the only path to a new row below when dragging the only field in the last row. Removed the guard and the now-unused `draggingFromRow` ref. - FieldCard / FieldActions: rename `isMultiColumn` -> `canEject`. The variable already returned true for stacked single-column rows (which is correct -- eject means "move to own row" and is valid for stacked cells), so the name was misleading. Behavior unchanged. - FormField type contract: add `cell_index?: number`. The exported DocType interface was lagging behind the schema; consumers couldn't represent stacked-cell positions. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- ColumnDropZone: reduced width from 4 to 3 for better alignment. - RowDropZone: reduced height from 6 to 3 to enhance visual consistency. These changes refine the appearance of drop zones during drag-and-drop interactions, ensuring a more cohesive user experience.
Cross-row drag waypoint was computed from card midpoints, which fell outside the RowDropZone after its height shrank in 3c72b20, breaking the cross-row drop tests. Anchor the transit waypoint to the actual row-drop-zone whose centre lies in the gap between source and target rows. Also harden the mobile-stack assertion with a networkidle wait. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Added a new skill, `/writing-tests`, to guide developers on using `frappe_factory_bot` for creating backend test fixtures in Forms Pro. The skill emphasizes the importance of using factories instead of direct document creation methods, providing rules and examples for authoring and consuming factories in tests. Updated CLAUDE.md to include this new skill and its usage guidelines.
The previous y-distance heuristic in dragFieldOntoCell compared the vertical gap between source and target against sourceBox.height. After 3c72b20 shrank RowDropZone from h-6 (24px) to h-3 (12px), adjacent rows sit close enough that the gap is smaller than a card's height, so cross-row drags were misclassified as same-row. The helper then skipped the intermediate waypoint, and the direct path from source to target crossed the row drop zone, causing the drop to commit there and fire insertNewRow instead of stacking into the target column. Replace the heuristic with a direct comparison of data-row-index attributes on the source and target FieldCards. Robust to any future drop-zone size changes.
Adds lessons surfaced while building FormFactory / LinkedFormDoctypeFactory
for the export submissions feature.
Skill changes:
- New rule 5b: lazy-create related docs inside default_attributes
(self.overrides.get(fk) or RelatedFactory.create().name), not only in
traits. Required when an FK has no sensible literal default.
- New rule 7: do not override create() to wrap heavy side effects;
build a separate factory for the dependency instead.
- Naming exception for doctypes used in narrow domain contexts (e.g.
the generic DocType acting as a placeholder for a Form's linked
DocType becomes LinkedFormDoctypeFactory).
- New 'Gotchas' section:
* __del_override__ fires on garbage collection — chained
Factory.create().name discards the doc, GCs it, and runs cleanup
before the FK is used downstream.
* before_insert / validate hooks can clobber factory overrides
(e.g. Form.before_insert forces is_published=False); patch
post-insert and save again.
- Reference list now points at form_factory / linked_form_doctype_factory
as the pattern for doctypes with non-trivial dependencies.
Factory changes:
- fp_team_factory, user_factory, user_invitation_factory now use
BaseFactory[T] generic typing as the skill describes (previously
the skill claimed it but the factories did not).
* feat(api): export submissions as CSV/Excel
Adds forms_pro.api.export.export_submissions, a whitelisted method that
streams submissions of a Form as CSV or Excel via Frappe's DataExporter.
Authorization is gated on Form.write; the linked DocType's export perm
is intentionally bypassed by a scoped set_user(Administrator) swap.
saved_user / saved_sid / saved_data are snapshotted and restored in
finally so the response cookie stays valid (set_user is otherwise built
for background jobs, not web requests).
Other notes:
- Filter on fp_linked_form so a form does not leak rows of other forms
sharing the same linked DocType.
- Validate file_type at runtime (Literal is static-only).
- Skip display-only form fields (Heading 1/2/3, etc.) via a new
FormField.stores_value property derived from frappe.model.no_value_fields.
- secure_filename + timestamp for the download filename.
- Access Log row written under the real user before the privilege swap.
* feat(ui): add Export dropdown to SubmissionList
CSV and Excel options trigger a real browser navigation to the export
API so Frappe sends Content-Disposition: attachment and the file
actually downloads (an XHR via createResource would just return the
body as JSON and never trigger a download).
* test(export): cover dispatch, scoping, perms, audit, session restore
Adds two factories and an integration test module for export_submissions:
- LinkedFormDoctypeFactory: builds a placeholder custom DocType
pre-wired with fp_submission_status and fp_linked_form, so a Form
pointing at it accepts submissions immediately.
- FormFactory: standard frappe_factory_bot pattern; default attrs
honor self.overrides.get(...) for linked_doctype / linked_team_id
to avoid orphaning related records.
test_export covers:
- file_type dispatch (Excel != CSV) and runtime validation.
- fp_linked_form scoping across two forms on the same linked DocType.
- CSV header carries form field labels; display-only fields excluded.
- Permission gate rejects users without Form.write.
- Access Log row written with the real user attribution.
- Session user restored on success and when DataExporter raises.
* fix(export): address Semgrep + CodeRabbit findings
- Suppress the frappe-semgrep-rules.rules.security.frappe-setuser
blocking rule on both set_user calls with inline nosemgrep markers
and a manual-audit comment. Authorization is gated on
has_permission('Form', 'write', form_id); the swap exists only to
bypass DataExporter's can_export on the linked DocType, which Forms
Pro intentionally does not grant at the role level. Session state
is snapshotted and fully restored.
- Re-raise on session-restore failure. Previously a failure inside
set_user(saved_user) was logged and swallowed, which would leave the
request running as Administrator while sid/data were already
restored. Now: log, complete the sid/data restore, then re-raise so
the request never proceeds with elevated privileges.
- Guard the export trigger in SubmissionList.vue against a missing
currentFormId; show a toast and abort instead of POSTing an empty
form_id to the backend.
* revert(export): drop restore_failure re-raise plumbing
Reverts the re-raise scaffolding from the CodeRabbit fix. set_user
itself raising is vanishingly rare and the log_error already surfaces
it; the extra ceremony added more noise than safety. sid/data restore
still runs in finally.
* chore: sanitize code
Bumps [vue-tsc](https://github.com/vuejs/language-tools/tree/HEAD/packages/tsc) from 3.2.4 to 3.2.8. - [Release notes](https://github.com/vuejs/language-tools/releases) - [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/language-tools/commits/v3.2.8/packages/tsc) --- updated-dependencies: - dependency-name: vue-tsc dependency-version: 3.2.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
Bumps [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) from 1.62.0 to 1.64.0. - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.64.0/npm/oxlint) --- updated-dependencies: - dependency-name: oxlint dependency-version: 1.64.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
…128) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.6.0 to 25.7.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.7.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
* fix(builder): replace live previews with icon palette to kill autofill
* test(e2e): cover Add Fields palette layout, search, and add-to-canvas
* polish(builder): a11y + press feedback on Add Fields palette buttons
* polish(builder): tooltip on palette buttons explaining click action
* polish(builder): fix scale transition and drop redundant tooltip on palette buttons
- transition-colors → transition-all so active:scale-[0.98] animates
- remove constant-text Tooltip wrapper (icon + label already convey action)
- drop redundant `as Fieldtype` cast and unused Fieldtype import
- type-only import for FormFields
* polish(builder): a11y + semantic colors on sidebar tabs
- tablist semantics on rail (role=tablist/tab/tabpanel, aria-selected, aria-controls)
- aria-label on icon-only tab buttons (SR-accessible)
- left accent bar on active tab for stronger emphasis (bg-surface-gray-7)
- section-fade transition (120ms ease-out enter, ease-in leave) on content panel
- respect prefers-reduced-motion
- 100vh → 100dvh on sidebar height (mobile-safe)
- swap to frappe-ui semantic tokens: bg-surface-gray-{1,2,3}, bg-surface-white, border-outline-gray-{1,2}
- default active section via id lookup, sidebarSections const (never mutated)
frappe-ui Rating uses 1..N stars; Frappe stores Rating as a 0..1 fraction and clamps to [0, 1]. Without conversion, clicking N stars stored 1.0 and on reload only the first star rendered, losing the real selection. Wrap frappe-ui Rating in a component that converts 0..1 <-> 1..5 in both directions, and scale the value in the read-only submission view.
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.59.1 to 1.60.0. - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](microsoft/playwright@v1.59.1...v1.60.0) --- updated-dependencies: - dependency-name: "@playwright/test" dependency-version: 1.60.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Updates the requirements on [faker](https://github.com/joke2k/faker) to permit the latest version. - [Release notes](https://github.com/joke2k/faker/releases) - [Changelog](https://github.com/joke2k/faker/blob/v40.18.0/CHANGELOG.md) - [Commits](joke2k/faker@v40.13.0...v40.18.0) --- updated-dependencies: - dependency-name: faker dependency-version: 40.18.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 14.2.1 to 14.3.0. - [Release notes](https://github.com/vueuse/vueuse/releases) - [Commits](https://github.com/vueuse/vueuse/commits/v14.3.0/packages/core) --- updated-dependencies: - dependency-name: "@vueuse/core" dependency-version: 14.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
Bumps [vue-router](https://github.com/vuejs/router) from 4.5.1 to 4.6.4. - [Release notes](https://github.com/vuejs/router/releases) - [Commits](vuejs/router@v4.5.1...v4.6.4) --- updated-dependencies: - dependency-name: vue-router dependency-version: 4.6.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Harsh Tandiya <harsh.tandiya@gmail.com>
#142) * feat(perms): add require_permission decorator * refactor(api): split form.py into form/ package * refactor(api): split team/submission/user/export/settings into packages Extract pydantic schemas into per-package schema.py modules where applicable (submission, user). team/export/settings have no inline schemas. Each package __init__.py explicitly re-exports endpoints so the forms_pro.api.<family>.<endpoint> whitelist URLs continue to resolve. Also update test_submission_validation.py to import private helpers from forms_pro.api.submission.endpoints (the public __init__.py exposes only whitelisted endpoints). * test: relocate tests to per-package and doctype folders - test_form_field.py → doctype/form_field/ (Frappe doctype-test convention). - test_invitations.py → api/team/, test_submission_validation.py → api/submission/, test_export.py → api/export/ (co-located with their endpoint families). - test_roles.py and tests/factories/ stay central (cross-cutting). Also update test_export.py's monkeypatch path to forms_pro.api.export.endpoints.DataExporter — the public __init__.py no longer re-exports the imported symbol. * feat(api): add get_form_for_view with read permission gate * feat(api): add get_form_for_edit with write permission gate * feat(api): add get_team_for_manage with read permission gate * refactor(api/form): retrofit endpoints with require_permission Apply @require_permission decorator to get_form_shared_with, remove_form_access, add_form_access, set_form_permission. Removes the manual frappe.has_permission + frappe.throw block in each. Behaviour diffs: - Missing form now raises DoesNotExistError (HTTP 404). Previously the endpoint would fall through to a downstream LinkValidationError or a ValidationError-as-deny via has_permission(missing). - get_form_shared_with and remove_form_access now raise PermissionError (HTTP 403) on deny. Previously both raised the default ValidationError (HTTP 417) because frappe.throw was called without an exc class. Same Form, same ptype, same docname source — the decorator is a strict behaviour-preserving wrapper save for the two corrections above. * refactor(api/team): retrofit endpoints with require_permission Apply @require_permission to get_team_members, invite_team_members, toggle_can_edit_team, save, and remove_member_from_team. Each preserves its existing doctype + ptype semantics. Adds HTTP 404 on missing team via the decorator's existence check (previously a downstream Frappe LinkValidationError or DoesNotExistError from frappe.get_doc). Skipped (intentionally): create_team (no docname yet), switch_team (session mutation; existing read check kept inline), get_team_forms (no current gate), add_member_to_team_via_invitation (invitation-token auth model differs). * refactor(api/submission): retrofit list endpoints with require_permission Apply @require_permission("Form", "read", param="form_id") to get_user_submissions and get_all_submissions. Drop manual FP Team write check + 404 throw from get_all_submissions, and the dead Guest early-return from get_user_submissions (endpoint is not allow_guest). get_submission and get_submission_response retain their manual permission checks: their target doctype is dynamic (passed as a parameter), which the current decorator (fixed doctype string) cannot express. Adds forms_pro/api/submission/test_submission.py with allow/403/404 coverage for both retrofitted endpoints. * refactor(api/export): retrofit export_submissions with require_permission Apply @require_permission("Form", "write", param="form_id") to export_submissions and drop the manual frappe.has_permission block. Privilege-swap behavior, audit log, and session restoration remain unchanged. Tightens TestExportPermissions to assert http_status_code == 403 on the deny path and adds an explicit 404 case for a missing form. * test(api/user): add coverage for current user + teams endpoints api/user endpoints (get_user, get_current_user, get_user_teams) gate on session state, not DocShare, so they take no @require_permission decorator. Adds an integration test file that pins their current behavior so future changes surface regressions: - get_user returns a basic payload for an existing user, None for a missing user. - get_current_user returns the session user's profile under set_user. - get_user_teams returns a list for a real user and [] for Guest. * feat(perms): add routeData store + useRouteData composable Introduces the frontend plumbing for route-level permission resolution: - src/types/router.d.ts augments vue-router's RouteMeta with optional allowGuest and fetch fields so per-route data resolvers are typed. - src/stores/routeData.ts is the single Pinia store driving navigation: state {status, data, error}, plus resolve(route) which awaits the meta.fetch resource and normalizes Frappe errors (exc_type, HTTP status, messages) into a uniform RouteError shape. - src/composables/useRouteData.ts exposes typed, computed accessors so pages can read status/data/error without touching the store directly. No router/guard wiring yet, that lands in D2. * feat(perms): add per-route fetch meta + route data guard Wires the gated pages to the routeData store introduced in D1: - Three resource factories (formForView, formForEdit, teamForManage) point at the new whitelisted endpoints. cache keys per id keep intra-session refetches cheap. - meta.fetch attached to "Manage Team", "Manage Form" (parent), and "Edit Form". For nested Manage Form children (Overview, Submissions) vue-router merges parent meta into the matched route, so the parent resolver applies to every sub-tab. - beforeEach simplified to return-style, gains a call to useRouteData().resolve(to) after the auth check. Login redirect and allowGuest paths preserved. Public submission routes keep their existing beforeEnter and stay guest-friendly; no fetch attached. Manual error UX (RouteError component, page integration) lands in D3; until then a denied page will surface the store's error state but without a styled fallback. * feat(perms): RouteError component + page integration + nav indicator Closes the user-visible half of the permission system. - components/RouteError.vue: one component, titled per exc_type (PermissionError → Access Denied, DoesNotExistError → Not Found, AuthenticationError → Login Required, fallback → Something Went Wrong). Carries an optional first-line message and HTTP status, plus a Go to Dashboard button. - App.vue: global LoadingIndicator (top-right) bound to routeData.isNavigating so users see in-flight perm resolution. - pages/manage/ManageForm.vue, pages/EditForm.vue, pages/team/ManageTeam.vue: render <RouteError> when status === 'error', existing layout otherwise. - stores/form/manageForm.ts: dropped the primary useDoc fetch. The Form document now flows in via routeData (resolved by the guard against get_form_for_view); manageFormStore.formData / formFields / formOwner read from there. Sharing mutations and formAccessResource untouched. - pages/manage/overview/Overview.vue: removed the now-dead formResource.loading branch (loading is handled globally). * chore: refactor page loading indicator * fix(fp_team): coerce can_edit_team to bool when no DocShare exists team_members property used the raw return of frappe.db.get_value("DocShare", share_name, "write"), which is None when no DocShare row exists for a member, in turn making GetTeamMembersResponse.model_validate raise a pydantic ValidationError ("Input should be a valid boolean"). Coerce to bool() and short-circuit when share_name itself is None. False is the correct semantic when no share exists, since the user has no write permission on the team. * fix(perms): await full user init before resolving route fetch Race condition: meta.fetch for /team builds the get_team_for_manage resource using useUser().currentTeam?.name. Until userTeamsResource finishes, currentTeam is null, so the resource fires with an empty team_id and require_permission's existence check throws 404. - stores/user.ts: cache the initialize() promise so repeated calls share the same in-flight fetches instead of racing. - router.ts: after the userResource check confirms login, await useUser().initialize() inside beforeEach so userTeams + currentTeam are populated before the route resolver runs. Login redirect path is unaffected: initialize() is only awaited when isLoggedIn is true, and a thrown error still flips isLoggedIn to false via the existing catch. * test(e2e): cover route-level permission gates Two Playwright specs exercising the meta.fetch + RouteError pipeline through a real browser against the built SPA: 1. /forms/manage/<bogus-id> drives a 404 on get_form_for_view and asserts the RouteError "Not Found" heading renders. 2. The logged-in test user creates and opens their own form's /edit-form/:id, the get_form_for_edit call returns 200, and no RouteError heading appears. Read-only viewer → "Access Denied" (403) is deferred: it needs an admin-owned form shared read-only with the test user, which requires a second authenticated API context that the current fixture set does not provide. Inline comment notes this for follow-up. * fix(perms): render allowed inline tags in RouteError message Backend permission messages embed <strong> tags around the user email. The template escaped them so they showed literally. Escape all HTML then re-allow a safe set of inline tags (strong/b/em/i) and render via v-html, keeping bold formatting without an XSS vector.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…t-develop-to-version-15
- Pin Frappe to >=15.0.0,<16.0.0; Python >=3.10; Faker ~=38.2.0; ruff target py310 - Set app version to 15.0.0 - Retarget CI/linter/typecheck/ui-tests workflows to version-15 (Python 3.10, Node 20, MariaDB 10.6.24, bench init --version version-15); drop merge_group triggers - Import frappe.tests.utils.FrappeTestCase aliased as IntegrationTestCase for v15 compatibility (matches version-15 convention)
8da7879 to
36bda6f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Brings the latest
developchanges to theversion-15maintenance branch.version-15was last synced via squash backport (#86), so it had drifted ~95 commits behinddevelop(CSV/Excel export, route-level permissions, multi-column layout, heading field types, multiselect, e2e tests, dep bumps, etc.).How
develop, thengit merge -s ours upstream/version-15to recordversion-15as a parent for clean ancestry while takingdevelop's tree as the content baseline.>=15.0.0,<16.0.0;requires-python >=3.10;Faker ~=38.2.0; rufftarget-version = py310.15.0.0.version-15(Python 3.10, Node 20, MariaDB 10.6.24,bench init --version version-15); droppedmerge_grouptriggers.frappe.tests.utils.FrappeTestCasefor Frappe v15 compatibility (develop usesfrappe.tests.IntegrationTestCase, which is v16-only).Notes
ui-tests.ymlwas aligned to the version-15 runtime (py3.10/node20,bench init --version version-15) for consistency with the other workflows.🤖 Generated with Claude Code