softr-vibe-coding 2.14.4 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,9 +10,15 @@ the obvious choices:
10
10
  | shadcn `<Select>` / `<Command>` | **Portals to `document.body`, which is outside the block's shadow root**, so the styles arrive stripped. It also cannot be searched. |
11
11
  | `Combo` (below) | Local DOM, brand-styled, keyword filter, A→Z, keyboard, create-new, clip-aware drop-up. |
12
12
 
13
+ The date field has the same problem with the same answer: `<input type="date">` hands its calendar
14
+ to the browser, so a block uses the in-DOM `DatePicker` kit in [date-picker.md](date-picker.md),
15
+ which follows rule 1 of item 4 below, the Escape rule and the focus-on-open rule of this page.
16
+
13
17
  Copy the component into the block. A Vibe block is one self-contained file — there is no
14
18
  shared module to import, so each block carries its own copy. Keep one canonical copy in the
15
- project (e.g. `Assets/Softr App/Shared/combo.jsx`) and port changes from there.
19
+ project (e.g. `Assets/Softr App/Shared/combo.jsx`) and port changes from there. Pasting it
20
+ verbatim between marker lines that carry its sha makes that port mechanical:
21
+ [date-picker.md → The kit between markers](date-picker.md#the-kit-between-markers).
16
22
 
17
23
  ## The four things that will bite you
18
24
 
@@ -19,7 +19,7 @@ The official Softr MCP server (`https://mcp.softr.io/mcp`) gives an AI assistant
19
19
  - [Vibe coding block tools](#vibe-coding-block-tools) — incl. [what the server enforces on a block's data endpoints](#what-the-server-enforces-on-a-blocks-data-endpoints)
20
20
  - [Adopting Studio-AI-generated code](#adopting-studio-ai-generated-code)
21
21
  - [Vibe coding gotchas (official)](#vibe-coding-gotchas-official)
22
- - [Application management tools](#application-management-tools) — incl. [testing as any user via "Preview as"](#testing-as-any-app-user-without-logins--the-preview-as-switcher)
22
+ - [Application management tools](#application-management-tools) — incl. [condition-based user groups](#condition-based-user-groups) and [testing as any user via "Preview as"](#testing-as-any-app-user-without-logins--the-preview-as-switcher)
23
23
  - [Browsing integrations (external data sources)](#browsing-integrations-external-data-sources)
24
24
  - [Softr Database tools](#softr-database-tools)
25
25
  - [Workflows](#workflows)
@@ -536,14 +536,19 @@ verified on HubSpot on 2026-10-05, the same way.* There are two forms, and **the
536
536
  **It fails closed:** a user whose field is empty, or who has no record in the users' data source,
537
537
  gets 0 rows, and a by-id request for a record outside the condition returns 404 (on HubSpot;
538
538
  Softr Database answers HTTP 200 with an empty body).
539
+ - **Do not confuse it with the user-group syntax.** A user group's condition names the user field
540
+ as its **subject**, `USER:<field id>`: one colon, no braces (verified 2026-10-07, see
541
+ [Condition-based user groups](#condition-based-user-groups)). A Source condition names it in the
542
+ **value**, `USER:::<field id>`: three colons. The user-group form returned HTTP 400 in a Source
543
+ condition (below); the Source-condition form has not been tried in a user group.
539
544
  - **Use AND between rules.** With one rule OR and AND behave the same, but a second rule added
540
545
  under OR widens access (2026-10-05).
541
546
  - **The braced user-field spellings fail.** On 2026-09-18, on Softr Database, eleven spellings were
542
547
  tried, including `{USER:::<fieldId>}`, `{USER:<fieldId>}` and `{USER:::FIELD:<fieldId>}`. Each
543
548
  silently matched nothing. All eleven had braces, so the braceless form is untested on Softr
544
549
  Database, not disproved. A subject of `USER:<fieldId>`, the syntax user-group rules use, returned
545
- HTTP 400 "Field not found": the user field goes in the value, never the subject. No token for a
546
- user group has been found.
550
+ HTTP 400 "Field not found": the user field goes in the value, never the subject. No token that
551
+ tests whether the user is in a group has been found.
547
552
  - Studio's conditional-filter UI offers the logged-in user's Email and Email-Domain, plus every
548
553
  users-table field once users sync from a data source (documented). For a value not listed here,
549
554
  pick it in a block's Source tab, save, and read `dataSources[].condition` back with
@@ -641,6 +646,37 @@ Combined with the database tools (`database_create` / `database_create_table` /
641
646
  no zone designator and nine fractional digits (`2026-09-09T22:34:11.157881061`). They were UTC, so
642
647
  read any older logged value as UTC, never as local time.
643
648
 
649
+ ### Condition-based user groups
650
+
651
+ *Verified 2026-10-07 on Softr Database, with users synced from a Softr Database table and the user
652
+ connection's field reference key set to `id`. The HubSpot version (2026-10-05) is in
653
+ [../datasources/hubspot.md](../datasources/hubspot.md#user-sync).*
654
+
655
+ - **`application_update_user_group` sets a group's condition.** It returned the condition exactly as
656
+ sent. This one, on a "Volunteer" group, tests two single-line text fields of the users table:
657
+
658
+ ```json
659
+ {
660
+ "logicalOperator": "AND",
661
+ "expressions": [
662
+ { "subject": { "field": "USER:YB2ot", "type": "TEXT" }, "operator": "IS_NOT_EMPTY", "value": [] },
663
+ { "subject": { "field": "USER:uo0TW", "type": "TEXT" }, "operator": "IS", "value": ["Active"] }
664
+ ]
665
+ }
666
+ ```
667
+
668
+ - **The subject is `USER:<field id>`: one colon, no braces.** A block's Source condition uses a
669
+ different form, `USER:::<field id>` with three colons, and puts it in the value
670
+ ([Logged-in-user values in Source conditions](#logged-in-user-values-in-source-conditions)). Do not
671
+ copy one into the other.
672
+ - **`application_list_users` does not show condition-based membership.** Right after the update, the
673
+ one user whose record matched (login email set, status Active) still listed `userGroups: []`. The
674
+ tool shows only manual memberships, such as a user added to Administrator through `userEmails`.
675
+ Softr evaluates conditions at runtime, so an empty `userGroups` there is not evidence that a
676
+ condition fails. HubSpot behaved the same way.
677
+ - **Check membership by running the app as that user** and reading the groups the app gives them
678
+ ([how](#testing-as-any-app-user-without-logins--the-preview-as-switcher), below).
679
+
644
680
  ### Testing as any app user without logins — the "Preview as" switcher
645
681
 
646
682
  *Verified live 2026-09-18.* The `application_preview` link does not open the app directly: it opens a
@@ -666,6 +702,24 @@ passwords, no test accounts to create:
666
702
  `fetch('/studio/impersonate/<softrUserId>')` makes the preview run as that user. The id is the
667
703
  user's Softr id from `application_list_users`. It works from a fresh preview link, so it needs no
668
704
  Studio session in the browser.
705
+ - **Read a user's groups from the app itself** (verified 2026-10-07, read-only). Mint a fresh
706
+ `application_preview` link and open it, run the impersonate call above, reload the app iframe,
707
+ then read `iframe.contentWindow.__softr_current_user.userGroups`.
708
+ - The link carries `?show-toolbar=true`, so the top document is the toolbar shell and the app runs
709
+ in `document.querySelector('iframe')` (the `#preview-iframe` of
710
+ [browser-checks.md](browser-checks.md)). `window.__softr_current_user` exists only in that
711
+ iframe's `contentWindow`. The top window has no user globals at all.
712
+ - To reload, set the iframe's `src` again with a fresh `t=<timestamp>` query parameter, after the
713
+ impersonate call.
714
+ - Observed group names: a user who matched the Volunteer condition above read
715
+ `["Logged in users", "All users", "Volunteer"]`; a user with no login email read
716
+ `["Logged in users", "All users"]`.
717
+ - **`window.__softr_current_user` carries only `name`, `email`, `avatar` and `userGroups`**
718
+ (verified 2026-10-07). None of the users-table record's other fields were there, on a users table
719
+ with notes, phone and emergency-contact fields. For a privacy review: syncing a users table does
720
+ not by itself expose the record's other fields through this global. A block can still ask for
721
+ them with `useCurrentUser({ properties })`
722
+ ([../datasources/reading.md](../datasources/reading.md#current-user)).
669
723
  - **Press the preview's own `#refresh-button` after a push.** An open preview kept serving the old
670
724
  block version until it was pressed (verified 2026-10-05). Minting a fresh link does too (see the
671
725
  version note above).