From d45c5c7c9c9001556bc42dbc4beececdffd6ee8f Mon Sep 17 00:00:00 2001 From: Cristhian A Luna Date: Fri, 3 Apr 2026 21:32:17 -0500 Subject: [PATCH 1/2] main (#1) * ci: add CI workflow with type check, build, verify and dry-run publish Runs on every push and PR across all branches. Checks: - TypeScript (server + admin) - Build - Strapi plugin verify - Version not already published on npm - Dry run publish publish.yml now requires CI to pass before publishing to npm. * fix: replace yarn run -T with npx tsc and fix implicit any type errors - Fix test:ts:front and test:ts:back scripts to use npx tsc (was yarn-only syntax) - Add explicit types for implicit any parameters caught by the type check: MouseEvent on DropZone drag handle click boolean on Modal.Root onOpenChange in EmbedModal and FormPreview string | number on SingleSelect onChange in FieldSettingsPanel and SubmissionsPage * ci: add Dependabot for npm and GitHub Actions weekly updates * fix: remove stale compiled JS files from src and ignore them in .gitignore Rollup was resolving register.js (and other compiled .js files) instead of the .ts sources, causing 'default is not exported' build failures in CI. Deleted all .js artifacts from server/src and admin/src and added them to .gitignore so they are never committed again. * fix: add --noEmit to tsc type-check scripts to prevent JS file generation Without --noEmit, tsc outputs .js files next to the .ts sources. Rollup then resolves the CJS .js files instead of the .ts sources and fails with 'default is not exported' on the CommonJS interop boundary. * ci: prevent double CI run on PR branches Push trigger now only fires on main, development and production. Feature branches are covered by the pull_request trigger alone, so CI no longer runs twice when pushing to an open PR. --- .github/dependabot.yml | 40 ++++++++++++++++ .github/workflows/ci.yml | 51 +++++++++++++++++++++ .github/workflows/publish.yml | 4 ++ .gitignore | 4 ++ admin/src/components/DropZone.tsx | 2 +- admin/src/components/EmbedModal.tsx | 2 +- admin/src/components/FieldSettingsPanel.tsx | 2 +- admin/src/components/FormPreview.tsx | 2 +- admin/src/pages/SubmissionsPage.tsx | 2 +- package.json | 4 +- 10 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ca7866c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,40 @@ +version: 2 + +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "America/Guayaquil" + open-pull-requests-limit: 10 + target-branch: "development" + groups: + strapi: + patterns: + - "@strapi/*" + dnd-kit: + patterns: + - "@dnd-kit/*" + react: + patterns: + - "react" + - "react-dom" + - "@types/react" + - "@types/react-dom" + ignore: + - dependency-name: "@strapi/strapi" + update-types: ["version-update:semver-major"] + - dependency-name: "@strapi/design-system" + update-types: ["version-update:semver-major"] + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "America/Guayaquil" + open-pull-requests-limit: 5 + target-branch: "development" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..05b04bd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + branches: + - main + - development + - production + pull_request: + workflow_call: + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Type check (server) + run: npm run test:ts:back + + - name: Type check (admin) + run: npm run test:ts:front + + - name: Build + run: npm run build + + - name: Verify plugin + run: npm run verify + + - name: Check version not already published + run: | + VERSION=$(node -p "require('./package.json').version") + if npm view strapi-plugin-form-builder-cms@$VERSION version 2>/dev/null; then + echo "❌ Version $VERSION is already published on npm. Bump the version before merging." + exit 1 + fi + echo "✅ Version $VERSION is not yet published." + + - name: Dry run publish + run: npm publish --dry-run --access public diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2c64e7f..fb9e7fc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,6 +9,7 @@ on: jobs: publish: if: github.event.pull_request.merged == true + needs: ci runs-on: ubuntu-latest steps: @@ -44,3 +45,6 @@ jobs: body: | Published from PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }} generate_release_notes: true + + ci: + uses: ./.github/workflows/ci.yml diff --git a/.gitignore b/.gitignore index 0a150cc..5d0e68b 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,10 @@ public/uploads/* dist build +# Compiled JS files inside src (TypeScript sources only) +server/src/**/*.js +admin/src/**/*.js + ############################ # Node.js diff --git a/admin/src/components/DropZone.tsx b/admin/src/components/DropZone.tsx index eb3efb0..dff53a3 100644 --- a/admin/src/components/DropZone.tsx +++ b/admin/src/components/DropZone.tsx @@ -62,7 +62,7 @@ function SortableFieldRow({ {...attributes} {...listeners} style={{ cursor: 'grab', color: 'var(--strapi-neutral-400)', padding: '0 4px' }} - onClick={(e) => e.stopPropagation()} + onClick={(e: React.MouseEvent) => e.stopPropagation()} > diff --git a/admin/src/components/EmbedModal.tsx b/admin/src/components/EmbedModal.tsx index 2619106..b7d2b87 100644 --- a/admin/src/components/EmbedModal.tsx +++ b/admin/src/components/EmbedModal.tsx @@ -24,7 +24,7 @@ export function EmbedModal({ formId, open, onClose }: Props) { }; return ( - !v && onClose()}> + !v && onClose()}> Embed this form diff --git a/admin/src/components/FieldSettingsPanel.tsx b/admin/src/components/FieldSettingsPanel.tsx index 8d2ba1a..9d96867 100644 --- a/admin/src/components/FieldSettingsPanel.tsx +++ b/admin/src/components/FieldSettingsPanel.tsx @@ -260,7 +260,7 @@ export function FieldSettingsPanel({ field, onChange }: Props) { updateValidation(i, { type: String(val), value: undefined, message: '' })} + onChange={(val: string | number) => updateValidation(i, { type: String(val), value: undefined, message: '' })} size="S" > {available.map((opt) => ( diff --git a/admin/src/components/FormPreview.tsx b/admin/src/components/FormPreview.tsx index ea84fa5..4b9c266 100644 --- a/admin/src/components/FormPreview.tsx +++ b/admin/src/components/FormPreview.tsx @@ -287,7 +287,7 @@ export function FormPreview({ title, fields, settings, open, onClose }: Props) { if (!open) return null; return ( - !v && onClose()}> + !v && onClose()}> Preview — {title} diff --git a/admin/src/pages/SubmissionsPage.tsx b/admin/src/pages/SubmissionsPage.tsx index 793bf70..4dbbb12 100644 --- a/admin/src/pages/SubmissionsPage.tsx +++ b/admin/src/pages/SubmissionsPage.tsx @@ -93,7 +93,7 @@ export function SubmissionsPage() { setStatusFilter(String(val))} + onChange={(val: string | number) => setStatusFilter(String(val))} placeholder="All statuses" size="S" > diff --git a/package.json b/package.json index 62a5760..ffa40be 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "watch": "strapi-plugin watch", "watch:link": "strapi-plugin watch:link", "verify": "strapi-plugin verify", - "test:ts:front": "run -T tsc -p admin/tsconfig.json", - "test:ts:back": "run -T tsc -p server/tsconfig.json" + "test:ts:front": "npx tsc -p admin/tsconfig.json --noEmit", + "test:ts:back": "npx tsc -p server/tsconfig.json --noEmit" }, "dependencies": { "@dnd-kit/core": "^6.3.1", From c61a918c21e9486b70b5dee9a47949f9d626e245 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:32:54 -0500 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`mai?= =?UTF-8?q?n`=20(#2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: add CI workflow with type check, build, verify and dry-run publish Runs on every push and PR across all branches. Checks: - TypeScript (server + admin) - Build - Strapi plugin verify - Version not already published on npm - Dry run publish publish.yml now requires CI to pass before publishing to npm. * fix: replace yarn run -T with npx tsc and fix implicit any type errors - Fix test:ts:front and test:ts:back scripts to use npx tsc (was yarn-only syntax) - Add explicit types for implicit any parameters caught by the type check: MouseEvent on DropZone drag handle click boolean on Modal.Root onOpenChange in EmbedModal and FormPreview string | number on SingleSelect onChange in FieldSettingsPanel and SubmissionsPage * ci: add Dependabot for npm and GitHub Actions weekly updates * fix: remove stale compiled JS files from src and ignore them in .gitignore Rollup was resolving register.js (and other compiled .js files) instead of the .ts sources, causing 'default is not exported' build failures in CI. Deleted all .js artifacts from server/src and admin/src and added them to .gitignore so they are never committed again. * fix: add --noEmit to tsc type-check scripts to prevent JS file generation Without --noEmit, tsc outputs .js files next to the .ts sources. Rollup then resolves the CJS .js files instead of the .ts sources and fails with 'default is not exported' on the CommonJS interop boundary. * ci: prevent double CI run on PR branches Push trigger now only fires on main, development and production. Feature branches are covered by the pull_request trigger alone, so CI no longer runs twice when pushing to an open PR. * 📝 Add docstrings to `main` Docstrings generation was requested by @devCluna. * https://github.com/devCluna/strapi-plugin-form-builder-cms/pull/1#issuecomment-4186035415 The following files were modified: * `admin/src/components/DropZone.tsx` * `admin/src/components/EmbedModal.tsx` * `admin/src/components/FieldSettingsPanel.tsx` * `admin/src/components/FormPreview.tsx` * `admin/src/pages/SubmissionsPage.tsx` --------- Co-authored-by: dev.cluna Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- admin/src/components/DropZone.tsx | 11 +++++++++++ admin/src/components/EmbedModal.tsx | 11 +++++++++++ admin/src/components/FieldSettingsPanel.tsx | 11 +++++++++++ admin/src/components/FormPreview.tsx | 10 ++++++++++ admin/src/pages/SubmissionsPage.tsx | 7 +++++++ 5 files changed, 50 insertions(+) diff --git a/admin/src/components/DropZone.tsx b/admin/src/components/DropZone.tsx index dff53a3..979a711 100644 --- a/admin/src/components/DropZone.tsx +++ b/admin/src/components/DropZone.tsx @@ -20,6 +20,17 @@ import { CSS } from '@dnd-kit/utilities'; import { Trash, Drag } from '@strapi/icons'; import { FormField } from '../types'; +/** + * Renders a draggable, selectable row that represents a FormField with a delete action. + * + * The row highlights when `selected`. Clicking the row calls `onSelect`; interacting with the drag handle does not toggle selection; clicking the delete button calls `onDelete`. + * + * @param field - The form field to render (label and type are displayed). + * @param selected - Whether this row is currently selected; controls visual highlight. + * @param onSelect - Callback invoked when the row (outside the drag handle and delete button) is clicked. + * @param onDelete - Callback invoked when the delete button is clicked. + * @returns The rendered sortable field row element. + */ function SortableFieldRow({ field, selected, diff --git a/admin/src/components/EmbedModal.tsx b/admin/src/components/EmbedModal.tsx index b7d2b87..4f28b43 100644 --- a/admin/src/components/EmbedModal.tsx +++ b/admin/src/components/EmbedModal.tsx @@ -8,6 +8,17 @@ interface Props { onClose: () => void; } +/** + * Renders a modal that displays an embeddable HTML snippet for a given form and provides a one-click copy action. + * + * The component returns `null` when `open` is `false`. When visible, it shows a preformatted snippet containing a container div + * with `id="sfb-form-"` and a script tag that loads the embed script from the current origin with `data-form-id` set. + * + * @param formId - The form identifier inserted into the snippet's container id and `data-form-id` attribute + * @param open - Controls whether the modal is visible + * @param onClose - Callback invoked when the modal is closed + * @returns The modal element when `open` is `true`, otherwise `null` + */ export function EmbedModal({ formId, open, onClose }: Props) { const [copied, setCopied] = useState(false); diff --git a/admin/src/components/FieldSettingsPanel.tsx b/admin/src/components/FieldSettingsPanel.tsx index 9d96867..7400ecc 100644 --- a/admin/src/components/FieldSettingsPanel.tsx +++ b/admin/src/components/FieldSettingsPanel.tsx @@ -45,6 +45,17 @@ function LabeledInput({ ); } +/** + * Render the settings panel UI for editing a single form field. + * + * Displays controls for label, name, placeholder, help text, required/width toggles, + * type-specific inputs (heading, paragraph), option management (add/update/remove), + * validation rule management (add/update/remove) with value/message editing, and CSS class. + * + * @param field - The current FormField to edit. + * @param onChange - Callback invoked with the updated FormField whenever a change is made. + * @returns A React element containing the field settings panel UI. + */ export function FieldSettingsPanel({ field, onChange }: Props) { const update = (patch: Partial) => onChange({ ...field, ...patch }); diff --git a/admin/src/components/FormPreview.tsx b/admin/src/components/FormPreview.tsx index 4b9c266..b61b424 100644 --- a/admin/src/components/FormPreview.tsx +++ b/admin/src/components/FormPreview.tsx @@ -283,6 +283,16 @@ interface Props { onClose: () => void; } +/** + * Render a modal preview of a form using the provided title, fields, and settings. + * + * @param title - The preview title displayed in the modal header + * @param fields - The array of form field definitions to render inside the preview + * @param settings - Form-level settings (used for things like the submit button text) + * @param open - Whether the preview modal is visible + * @param onClose - Callback invoked when the preview modal is closed + * @returns The modal element containing the form preview, or `null` when `open` is false + */ export function FormPreview({ title, fields, settings, open, onClose }: Props) { if (!open) return null; diff --git a/admin/src/pages/SubmissionsPage.tsx b/admin/src/pages/SubmissionsPage.tsx index 4dbbb12..3bd55c3 100644 --- a/admin/src/pages/SubmissionsPage.tsx +++ b/admin/src/pages/SubmissionsPage.tsx @@ -22,6 +22,13 @@ import { useFormsApi } from '../api'; import { Form, FormField, FormSubmission } from '../types'; import { PLUGIN_ID } from '../pluginId'; +/** + * Render the submissions management page for a specific form. + * + * Loads and displays form metadata, submission list, and statistics; provides controls to filter by status, view submission details, mark submissions as read, and delete submissions. + * + * @returns The React element for the Submissions page (table, filters, detail modal, and delete confirmation modal). + */ export function SubmissionsPage() { const { formId } = useParams<{ formId: string }>(); const navigate = useNavigate();