Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
ebece30
feat: add frappe dependency for benchmarking
harshtandiya Feb 14, 2026
e221ba9
fix: route handling for /form
harshtandiya Feb 15, 2026
cdbf020
feat: child table support v1 (#42)
harshtandiya Feb 18, 2026
ac65fb3
fix: update frappe dependency version in pyproject.toml
harshtandiya Feb 18, 2026
3f906ca
fix: child table fields fetch
harshtandiya Feb 18, 2026
717294b
refactor: update TextEditor component bindings
harshtandiya Feb 18, 2026
6c908ad
feat: team management pages (#46)
harshtandiya Mar 7, 2026
1203874
fix: team avatar uploader (#47)
harshtandiya Mar 7, 2026
b4edd43
feat: enhance TeamSwitcher with search functionality
harshtandiya Mar 7, 2026
35f8301
refactor: update CreateTeamDialog to use ImageUploader for logo uploads
harshtandiya Mar 7, 2026
53338e7
refactor: update BaseLayout styling and icon usage
harshtandiya Mar 8, 2026
75b26c6
feat: add Breadcrumbs component to Dashboard
harshtandiya Mar 8, 2026
13fbb6a
feat: submission list pages (#59)
harshtandiya Apr 7, 2026
8237bfd
fix: form sharing methods (#58)
harshtandiya Apr 7, 2026
9ab5744
chore: add CLAUDE.md and release skill
harshtandiya Apr 7, 2026
2063b8b
chore: hygine (#60)
harshtandiya Apr 7, 2026
9d1b761
chore: update dependabot configuration to use npm instead of yarn
harshtandiya Apr 7, 2026
ef91dd0
chore(deps): bump actions/cache from 4 to 5 (#61)
dependabot[bot] Apr 7, 2026
be85a0d
chore(deps): bump actions/setup-node from 3 to 6 (#62)
dependabot[bot] Apr 7, 2026
5d3e7f9
chore(deps): bump actions/setup-python from 4 to 6 (#63)
dependabot[bot] Apr 7, 2026
04ac036
chore(deps): bump pre-commit/action from 3.0.0 to 3.0.1 (#65)
dependabot[bot] Apr 7, 2026
acb4966
chore(deps): bump actions/checkout from 3 to 6 (#66)
dependabot[bot] Apr 7, 2026
150affb
chore(deps-dev): bump jsdom from 25.0.1 to 29.0.2 in /frontend (#67)
dependabot[bot] Apr 7, 2026
3f0e758
chore(deps): bump zod from 4.1.12 to 4.3.6 in /frontend (#69)
dependabot[bot] Apr 7, 2026
9430da6
chore(deps): bump vue from 3.5.21 to 3.5.32 in /frontend (#68)
dependabot[bot] Apr 7, 2026
b43d1fd
chore(deps): bump socket.io-client from 4.8.1 to 4.8.3 in /frontend (…
dependabot[bot] Apr 7, 2026
30a42ed
chore(deps-dev): bump postcss from 8.5.6 to 8.5.8 in /frontend (#71)
dependabot[bot] Apr 7, 2026
686dda1
refactor: move vulnerable dependency check to a separate workflow
harshtandiya Apr 8, 2026
b7eccac
chore(deps): update frappe-ui to 0.1.272
harshtandiya Apr 8, 2026
6b43d21
chore(deps): update vite to version 5.4.21 and adjust auto-imports an…
harshtandiya Apr 9, 2026
6d2cd50
refactor(tests): streamline email handling in TestTeamInvitations
harshtandiya Apr 9, 2026
985f14e
refactor(tests): enhance email handling in TestTeamInvitations
harshtandiya Apr 9, 2026
46b31b0
chore(deps): update faker requirement from ~=38.2.0 to ~=40.13.0 (#64)
dependabot[bot] Apr 9, 2026
ad39f4d
chore(deps): update devDependencies for Vite and Vitest (#73)
harshtandiya Apr 10, 2026
db798f0
feat: test framework with test factory (#72)
harshtandiya Apr 10, 2026
dea2f7c
chore!: refactor code for better maintainability (#75)
harshtandiya Apr 12, 2026
afa04bc
feat(e2e): Playwright E2E test suite (#57) (#79)
harshtandiya Apr 15, 2026
376e932
feat(multiselect): add Multiselect field with registry-driven builder…
harshtandiya Apr 19, 2026
128a821
chore: update .gitignore to include Playwright report and skills-lock…
harshtandiya Apr 19, 2026
79e0ce3
chore(deps): bump @vueuse/core from 13.9.0 to 14.2.1 in /frontend (#80)
dependabot[bot] Apr 19, 2026
96a0f94
fix(multiselect): reset option input and error message on startAdding…
harshtandiya Apr 20, 2026
55c4fc0
chore: better ui to remove options in multiselect (#87)
harshtandiya Apr 20, 2026
7a3aa8c
chore(deps-dev): bump postcss from 8.5.8 to 8.5.10 in /frontend (#95)
dependabot[bot] Apr 22, 2026
222c73d
chore(deps): bump dayjs from 1.11.19 to 1.11.20 in /frontend (#84)
dependabot[bot] Apr 22, 2026
9795bfa
chore(deps): update faker requirement from ~=40.13.0 to >=40.13,<40.1…
dependabot[bot] Apr 22, 2026
092d2e8
chore(deps): bump @lottiefiles/dotlottie-vue in /frontend (#93)
dependabot[bot] Apr 22, 2026
9aa693f
chore(deps): bump actions/setup-node from 4 to 6 (#88)
dependabot[bot] Apr 22, 2026
d147e0d
chore(deps): bump actions/checkout from 4 to 6 (#90)
dependabot[bot] Apr 22, 2026
00a219c
chore(deps): bump actions/setup-python from 5 to 6 (#91)
dependabot[bot] Apr 22, 2026
e95b934
chore(deps): bump actions/upload-artifact from 4 to 7 (#89)
dependabot[bot] Apr 22, 2026
0cf6ef1
chore(deps): bump lucide-vue-next from 0.575.0 to 1.0.0 in /frontend …
dependabot[bot] Apr 22, 2026
cd63fa7
ci: add merge_group trigger to enable GitHub merge queue (#97)
harshtandiya Apr 22, 2026
26e2a74
fix(e2e): auto-fill form title in FormBuilderPage.goto() to prevent M…
harshtandiya Apr 22, 2026
d5cde23
fix: resolve semgrep findings across codebase (#98)
harshtandiya Apr 22, 2026
a6f6b2a
chore: update gitignore to ignore semgrep folder
harshtandiya Apr 22, 2026
0ff85f1
ci: add backport workflow for version-15 branch (#99)
harshtandiya Apr 22, 2026
17bb3c4
chore: migrate from Biome to oxlint (#100)
harshtandiya Apr 22, 2026
16989b1
feat: add Heading 1/2/3 field types (#103)
harshtandiya Apr 26, 2026
4e9b9d1
refactor: redesign the form builder layout (#105)
harshtandiya Apr 26, 2026
6fcb904
fix(ci): use exact label name in backport workflow condition
harshtandiya Apr 26, 2026
96bc2f7
fix(ci): add label_pattern to match backport/branch-name format
harshtandiya Apr 26, 2026
3343548
fix(ci): add checkout step to backport workflow
harshtandiya Apr 26, 2026
66e04de
enhance(FieldActions): add tooltips for field actions buttons
harshtandiya Apr 26, 2026
72f63f8
fix(Form): correct initial route generation string (#106)
harshtandiya Apr 26, 2026
9e7849d
fix: prevent duplicate fieldnames on form save (#107)
harshtandiya Apr 27, 2026
47ea08e
chore(deps): bump korthout/backport-action from 3 to 4 (#108)
dependabot[bot] Apr 28, 2026
fe19483
chore(deps): bump @lucide/vue from 1.8.0 to 1.11.0 in /frontend (#112)
dependabot[bot] Apr 28, 2026
0c4c6bc
chore(deps-dev): bump oxlint from 1.61.0 to 1.62.0 in /frontend (#113)
dependabot[bot] Apr 28, 2026
0ffdad3
chore(deps): bump vue from 3.5.32 to 3.5.33 in /frontend (#110)
dependabot[bot] Apr 28, 2026
9035fdb
chore(deps-dev): bump @vitejs/plugin-vue in /frontend (#109)
dependabot[bot] Apr 28, 2026
afdf6c8
feat(FormBuilder): add ghost class for draggable fields (#115)
harshtandiya May 1, 2026
638777b
feat(FormBuilder): implement keyboard shortcut for saving forms (#117)
harshtandiya May 1, 2026
fd7acea
feat(FormBuilder): morph action button label with TextMorph (#118)
harshtandiya May 1, 2026
fb81ac3
fix: button text on mount
harshtandiya May 1, 2026
95c3f79
chore(deps): bump pinia from 3.0.3 to 3.0.4 in /frontend (#123)
dependabot[bot] May 8, 2026
ae9cad9
chore(deps-dev): bump postcss from 8.5.10 to 8.5.14 in /frontend (#122)
dependabot[bot] May 8, 2026
93680f7
chore(deps-dev): bump autoprefixer from 10.4.21 to 10.5.0 in /fronten…
dependabot[bot] May 8, 2026
aee5b46
chore(deps): bump @lottiefiles/dotlottie-vue in /frontend (#121)
dependabot[bot] May 9, 2026
2c18e31
feat: multi-column form layout with column stacking (#119)
harshtandiya May 10, 2026
3c72b20
fix(builder): adjust drop zone dimensions for improved layout
harshtandiya May 10, 2026
7654caf
fix(e2e): make drag helper robust to row drop zone size changes
harshtandiya May 10, 2026
7681e77
feat(tests): introduce writing-tests skill for backend test fixtures
harshtandiya May 12, 2026
9861044
fix(e2e): detect cross-row drag via data-row-index (#132)
harshtandiya May 12, 2026
1c86cee
docs(skill): expand writing-tests with gotchas and conventions (#134)
harshtandiya May 12, 2026
26bc77f
feat: export submissions as CSV/Excel (#133)
harshtandiya May 12, 2026
0ca1e7c
chore(deps-dev): bump vue-tsc from 3.2.4 to 3.2.8 in /frontend (#130)
dependabot[bot] May 13, 2026
f15b95f
chore(deps-dev): bump oxlint from 1.62.0 to 1.64.0 in /frontend (#129)
dependabot[bot] May 13, 2026
ebb73e6
chore(deps-dev): bump @types/node from 25.6.0 to 25.7.0 in /frontend …
dependabot[bot] May 13, 2026
8d43df9
fix(builder): replace live field previews in Add Fields palette (#135)
harshtandiya May 16, 2026
dd565e7
fix(rating): retain user-selected value (#24) (#136)
harshtandiya May 16, 2026
a339ea3
chore(deps-dev): bump @playwright/test in /frontend (#140)
dependabot[bot] May 19, 2026
19bb5bc
chore(deps): update faker requirement (#138)
dependabot[bot] May 20, 2026
76799f1
chore(deps): bump @vueuse/core from 14.2.1 to 14.3.0 in /frontend (#139)
dependabot[bot] May 20, 2026
02be0d8
chore(deps): bump vue-router from 4.5.1 to 4.6.4 in /frontend (#141)
dependabot[bot] May 20, 2026
17b2ace
feat(perms): route-level permissions (backend gates + frontend wiring…
harshtandiya May 24, 2026
fe1cac7
Merge remote-tracking branch 'upstream/version-15' into chore/backpor…
harshtandiya May 24, 2026
36bda6f
chore(version-15): apply deployment layer over develop merge
harshtandiya May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: release
description: Draft a new GitHub release for buildwithhussain/forms_pro by inspecting existing releases and merged PRs since the last release. Use when the user wants to cut, draft, or create a release.
argument-hint: [version]
---

You are preparing a new GitHub release for the repo `buildwithhussain/forms_pro`.

## Steps

1. **Fetch current releases**
Run: `gh release list --repo buildwithhussain/forms_pro`

2. **Inspect the latest release** to get its tag and published date:
Run: `gh release view <latest-tag> --repo buildwithhussain/forms_pro`

3. **List all merged PRs** since the last release (sorted by merge date):
Run: `gh pr list --repo buildwithhussain/forms_pro --state merged --limit 100 --json number,title,author,mergedAt | jq 'sort_by(.mergedAt)'`
Filter to only PRs merged **after** the last release's published date.

4. **Determine the next version**
- If `$ARGUMENTS` is provided, use it as the tag (e.g. `v0.1.3-beta`).
- Otherwise, infer the next patch version from the latest tag (e.g. `v0.1.1-beta` → `v0.1.2-beta`).

5. **Categorize PRs** into:
- **Features** — titles starting with `feat:`
- **Fixes** — titles starting with `fix:`
- **Chores / CI** — titles starting with `chore:`, `refactor:`, `ci:`, `docs:` — **omit these from release notes**

6. **Show the user a plan**: list the proposed tag, target branch (`main`), and the drafted release notes. Ask for confirmation before creating anything.

7. **After confirmation**, create a draft release:
```
gh release create <tag> \
--repo buildwithhussain/forms_pro \
--title "<tag>" \
--draft \
--target main \
--notes "<release notes>"
```

8. Return the draft release URL so the user can review and publish it.

## Release Notes Format

Follow the same format as previous releases exactly:

```markdown
## What's Changed

### Features
* feat: <title> by @<author> in https://github.com/BuildWithHussain/forms_pro/pull/<number>

### Fixes
* fix: <title> by @<author> in https://github.com/BuildWithHussain/forms_pro/pull/<number>

**Full Changelog**: https://github.com/BuildWithHussain/forms_pro/compare/<previous-tag>...<new-tag>
```

Omit a section entirely if there are no PRs in that category.
149 changes: 149 additions & 0 deletions .claude/skills/writing-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
name: writing-tests
description: Use when writing or modifying backend tests in `forms_pro/tests/`. Forms Pro test suites must build test documents via `frappe_factory_bot` factories rather than `frappe.new_doc` / `frappe.get_doc`. This skill explains where factories live, how to author one, and how to consume them in tests.
---

# Writing Tests (frappe_factory_bot)

The dev bench has `frappe_factory_bot` installed alongside Forms Pro (`apps/frappe_factory_bot`, repo: `harshtandiya/frappe_factory_bot`). All Forms Pro tests build their fixtures through factories defined in `forms_pro/tests/factories/`.

## Rules

1. **Never** call `frappe.new_doc(...)` or `frappe.get_doc({...}).insert()` directly in a test to spin up a fixture. Use a factory.
2. One factory per doctype. File name: `<snake_case_doctype>_factory.py` inside `forms_pro/tests/factories/`. **Naming exception**: when a doctype is used in a narrow domain context (e.g. the generic `"DocType"` doctype acting as a placeholder for a Form's linked DocType), name the factory after the domain — `LinkedFormDoctypeFactory`, not `DoctypeFactory`.
3. Factory class name: `<PascalCaseDocType>Factory`, subclassing `BaseFactory[<DocClass>]` (parameterise the generic with the actual `Document` subclass for type hints).
4. `default_attributes` must only set the minimum fields required for `.insert()` to succeed. Use `Faker` (`_fake = Faker()`) with `_fake.unique.*` when a field must be unique.
5. Reuse the factory bot's primitives instead of reinventing them:
- **Traits**: extra `@property` methods returning attribute dicts. Apply with `Factory.create("trait_name")`.
- **Overrides**: kwargs to `create()` / `build()`. Take precedence over traits and defaults.
- **Relationships (in traits)**: in a trait that fabricates a related doc, *always* honour `self.overrides.get("<fk_field>")` before creating a new dependent record (prevents orphans).
- **Relationships (in defaults)**: when a required FK has no sensible literal default, use the same `self.overrides.get(...) or RelatedFactory.create().name` short-circuit inside `default_attributes` so callers can pass an existing record without spawning an orphan.
6. Factory bot owns teardown via `__del__` hooks. Do **not** add manual cleanup of factory-built docs in tearDown.
7. **Do not override `create()`** to wrap heavy side effects. If a doctype needs more than `frappe.get_doc({...}).insert()` to be usable (parent DocType, DocShare, etc.), build a separate factory for the dependency and reference it from `default_attributes` (see rule 5). Overriding `create()` breaks `build_list` / `create_list` and diverges from the convention.
8. If a doctype you need has no factory yet, write one first, then write the test. Keep the factory commit / change separate-ish from test logic when reasonable.

## Authoring a factory

Template — replace placeholders. Note the generic `BaseFactory[FooBar]` typing.

```python
from typing import Any

from faker import Faker
from frappe_factory_bot.frappe_factory_bot.base_factory import BaseFactory

from forms_pro.forms_pro.doctype.foo_bar.foo_bar import FooBar

_fake = Faker()


class FooBarFactory(BaseFactory[FooBar]):
doctype = "Foo Bar"

@property
def default_attributes(self) -> dict[str, Any]:
from forms_pro.tests.factories.fp_team_factory import FPTeamFactory

return {
"title": _fake.unique.sentence(nb_words=3),
"is_active": 1,
# Lazy-create the FK only if the caller did not pass one.
"owner_team": self.overrides.get("owner_team") or FPTeamFactory.create().name,
}

@property
def with_owner_team(self) -> dict[str, Any]:
# Same honour-override pattern, applicable to optional FKs surfaced via traits.
from forms_pro.tests.factories.fp_team_factory import FPTeamFactory

return {
"owner_team": self.overrides.get("owner_team") or FPTeamFactory.create().name,
}
```

Reference existing factories:
- `forms_pro/tests/factories/user_factory.py` — `Faker.unique`, `with_forms_pro_role` trait that seeds a child table.
- `forms_pro/tests/factories/fp_team_factory.py` — minimal default attributes.
- `forms_pro/tests/factories/user_invitation_factory.py` — overrides-aware defaults + `to_forms_pro_app` trait.
- `forms_pro/tests/factories/form_factory.py` + `linked_form_doctype_factory.py` — pattern for a doctype with non-trivial dependencies; defaults lazy-create the placeholder DocType and team.

## Gotchas

### `__del_override__` fires on garbage collection

`BaseFactory._attach_del` swaps the returned doc's class so its `__del__` runs cleanup. Python invokes `__del__` when the doc is GC'd. If you chain `Factory.create().name` and discard the doc, the cleanup may run **before** the FK is used downstream, deleting the doc you just relied on.

```python
# Risky — DocType may be GC'd before the Form insert reads its name.
fk = MyDocTypeFactory.create().name

# Safe — binding keeps the doc alive for the test's lifetime.
parent = MyDocTypeFactory.create()
fk = parent.name
```

For factories that do not need destructive cleanup (e.g. DocTypes whose unique random names provide isolation), it is acceptable to skip the `__del_override__` override entirely.

### `before_insert` / `validate` can clobber overrides

Some doctypes hard-set fields during `before_insert` (e.g. `Form.before_insert` forces `is_published = False`). Passing `is_published=1` as a factory override is silently overwritten — the override is merged into the dict that becomes the new doc, but `before_insert` runs after that and overwrites the attribute.

Patch post-insert when this matters:

```python
form = FormFactory.create(...)
form.is_published = 1
form.save(ignore_permissions=True)
```

Rule of thumb: if the doctype hard-sets a field in `before_insert`, `validate`, or `before_validate`, factory overrides cannot reach it — touch the field after `.create()` and save again.

## Consuming factories in tests

```python
import frappe
from frappe.tests import IntegrationTestCase

from forms_pro.tests.factories.user_factory import UserFactory
from forms_pro.tests.factories.fp_team_factory import FPTeamFactory


class TestSomething(IntegrationTestCase):
def test_invites_existing_member(self) -> None:
user = UserFactory.create("with_forms_pro_role")
team = FPTeamFactory.create(owner=user.name)
# IDE knows `user: User` and `team: FPTeam` thanks to BaseFactory[T].
frappe.set_user(user.name)
...
```

Method cheat sheet (all class methods on the factory):

| Call | Returns | Saved? |
| --- | --- | --- |
| `Factory.build(*traits, **overrides)` | `T` | no |
| `Factory.create(*traits, **overrides)` | `T` | yes |
| `Factory.build_list(n, *traits, **overrides)` | `list[T]` | no |
| `Factory.create_list(n, *traits, **overrides)` | `list[T]` | yes |

Precedence: overrides > traits > defaults. Passing a trait that does not exist raises `TypeError`.

## Test runner

```bash
bench --site forms.dev run-tests --module forms_pro.tests.test_<module>
```

The command above will run tests inside the module.

If you want to run all tests, you can use the following command:

```bash
bench --site forms.dev run-tests --app forms_pro
```

If you want to run a specific test, you can use the following command:

```bash
bench --site forms.dev run-tests --module *path_to_test_file* --test *test_method_name*
```
17 changes: 17 additions & 0 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Backport

on:
pull_request_target:
types: [closed, labeled]

jobs:
backport:
if: github.event.pull_request.merged && contains(join(github.event.pull_request.labels.*.name, ','), 'backport/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: korthout/backport-action@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pull_title: "[backport] ${pull_title}"
label_pattern: "^backport/([^ ]+)$"
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: CI

# Deployment branch for Frappe version-15 (Python 3.10, Node 18, MariaDB 10.6.24)
# Deployment branch for Frappe version-15 (Python 3.10, Node 20, MariaDB 10.6.24)
on:
push:
branches:
Expand All @@ -12,7 +12,6 @@ on:
- version-15
paths-ignore:
- ".github/**"
merge_group:

concurrency:
group: version-15-forms_pro-${{ github.event.number }}
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:
- version-15
paths-ignore:
- ".github/**"
merge_group:
workflow_dispatch:

permissions:
Expand All @@ -21,7 +20,7 @@ jobs:
linter:
name: 'Frappe Linter'
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
if: github.event_name == 'pull_request'

steps:
- uses: actions/checkout@v6
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Frontend TypeScript

# Frappe version-15 deployment branch: Node 18
# Frappe version-15 deployment branch: Node 20
on:
push:
branches:
Expand All @@ -14,7 +14,6 @@ on:
paths:
- "frontend/**"
- ".github/workflows/typecheck.yml"
merge_group:

concurrency:
group: typecheck-version-15-forms_pro-${{ github.event.number || github.sha }}
Expand Down
14 changes: 8 additions & 6 deletions .github/workflows/ui-tests.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
name: UI Tests

# Frappe version-15 deployment branch (Python 3.10, Node 20)
on:
push:
branches:
- develop
- version-15
pull_request:
merge_group:
branches:
- version-15
workflow_dispatch:

concurrency:
Expand All @@ -28,7 +30,7 @@ jobs:
ports:
- 11000:6379
mariadb:
image: mariadb:10.6
image: mariadb:10.6.24
env:
MYSQL_ROOT_PASSWORD: root
ports:
Expand All @@ -42,12 +44,12 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
python-version: "3.10"

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
node-version: 20
check-latest: true

- name: Add to Hosts
Expand Down Expand Up @@ -90,7 +92,7 @@ jobs:
- name: Setup Frappe bench
run: |
pip install frappe-bench
bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench
bench init ~/frappe-bench --version version-15 --skip-redis-config-generation --skip-assets --python "$(which python)"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"

Expand Down
19 changes: 4 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,11 @@ repos:
.*boilerplate.*
)$

- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.44.0
- repo: https://github.com/oxc-project/mirrors-oxlint
rev: v1.60.0
hooks:
- id: eslint
types_or: [javascript, ts]
args: ["--quiet"]
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
frontend/components.d.ts|
frontend/auto-imports.d.ts|
forms_pro/public/frontend/.*|
cypress/.*|
.*node_modules.*|
.*boilerplate.*
)$
- id: oxlint
types_or: [javascript, ts, vue]

ci:
autoupdate_schedule: weekly
Expand Down
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ npm run build
# TypeScript type check
cd frontend && yarn typecheck

# Lint & format (BiomeJS)
# Lint (oxlint) & format (Prettier via pre-commit)
cd frontend && yarn lint
```

Expand Down Expand Up @@ -107,6 +107,7 @@ When a form is saved, its fields are synced to the linked DocType as `CustomFiel
| `/release [version]` | Draft a new GitHub release. Inspects merged PRs since the last release, categorizes them, and creates a draft on GitHub for review. |
| `/add-field <FieldtypeName>` | Add a new field type end-to-end: backend doctype + mapping, submission serialization, frontend component, fieldTypes registry, options resolution, and submission display. |
| `/userinterface-wiki` | Review UI/UX against best practices — animations, CSS, typography, UX patterns, prefetching, icons. Outputs file:line findings. |
| `/writing-tests` | Use `frappe_factory_bot` factories under `forms_pro/tests/factories/` for backend test fixtures (never `frappe.new_doc` / `frappe.get_doc`). Covers authoring a typed factory, traits, overrides, and consumption patterns. Auto-applies when writing/modifying tests. |

> **Adding skills via `npx skills`:** Always use `--copy` and target `.claude/skills/` explicitly so Claude Code can discover them:
> ```bash
Expand All @@ -116,8 +117,9 @@ When a form is saved, its fields are synced to the linked DocType as `CustomFiel

## Key Conventions

- **Python target**: 3.10+; formatted with `ruff`
- **Python target**: 3.14+; formatted with `ruff`
- **TypeScript**: strict mode; `vue-tsc` enforced in CI
- **Frappe patterns**: use `frappe.get_doc`, `frappe.get_all`, `frappe.db.*`; avoid raw SQL unless necessary
- **PR titles**: validated by CI workflow (conventional commit format expected)
- **Tests**: use Frappe's `IntegrationTestCase`; test infrastructure set up in `install.py:before_tests()`
- **Test fixtures**: build documents via factories in `forms_pro/tests/factories/` (powered by `frappe_factory_bot`, repo `harshtandiya/frappe_factory_bot`). Do **not** use `frappe.new_doc` / `frappe.get_doc(...).insert()` in tests. See the `/writing-tests` skill.
3 changes: 3 additions & 0 deletions forms_pro/api/export/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .endpoints import export_submissions

__all__ = ["export_submissions"]
Loading
Loading