Commit Graph
1951 Commits
Author SHA1 Message Date
Matthew Meszaros f200c8cf0a Merge pull request #287 from warmbly/feature/public-forms
feat: forms v2 with themes, layouts, pages, personalized links, funnel analytics, campaign integration and a custom forms domain
2026-09-01 09:55:24 -07:00
Matthew Meszaros 53b932292b fix: address review findings on forms v2: the list aggregates query passed a Go int into a text concatenation so pgx could not encode it and starts, identified and the trend sparkline were always empty behind a silent degrade; the forms tables sat above campaigns in the org transfer order despite carrying a campaign_id foreign key, which would fail a full import; an imported forms domain inherited the destination workspace's stale verified flag; a form whose creator was offboarded stopped creating contacts because created_by is ON DELETE SET NULL, now falling back to the workspace owner; the forms mirror check never ran when only the web-side copy changed; and the bare metal guide asked for five DNS names while certbot requested a sixth 2026-09-01 09:49:06 -07:00
Matthew Meszaros 290f1f00aa Merge remote-tracking branch 'origin/main' into feature/public-forms
# Conflicts:
#	cmd/backend/main.go
#	docs/content/docs/guides/workspace-export-import.mdx
2026-09-01 09:29:53 -07:00
Matthew Meszaros 6fb5c3ca08 feat: connect forms to campaigns end to end: a toolbar button and {{ menu that insert a per-recipient {{form_link:id}} chip resolved at send time, a Forms panel on the campaign overview reporting links sent, opened, started, submitted and the resulting rate per form, GET /campaigns/:id/forms behind the campaign read gates, and campaign attribution carried through every form submission, funnel event and analytics breakdown 2026-09-01 09:27:33 -07:00
Matthew Meszaros 05d74dbe9f feat: forms v2, a full redesign of the hosted form and its builder: eight one-click themes, card/wide/split layouts with a brand cover panel, classic paged and Typeform-style focus modes split on a new page_break block, logo/cover/background uploads with size, fit and veil controls, an optional header bar that can span the page or sit with the form, a real HSV colour picker and font preview, per-contact personalized links that prefill and attribute without email verification, a render-token gate so the form JSON cannot be scraped without loading the page, funnel analytics with per-page drop-off and identified visitors, a leads-style forms list and responses table, and an organization custom forms domain verified by CNAME with hourly re-checks 2026-09-01 09:27:28 -07:00
Matthew Meszaros 84815381a9 fix: route the orphaned website tracking settings page, which the settings nav linked to but main.tsx never registered, and give every unmapped route a document title so forms, segments, categories, oauth apps and six settings pages stop rendering the literal Page not found in the browser tab 2026-09-01 09:26:40 -07:00
Matthew Meszaros 62e8f26d10 Merge pull request #284 from warmbly/fix/issue-277
feat: live segment-to-campaign linking, lead removal, and import targeting (issue #277)
2026-09-01 08:19:43 -07:00
Matthew Meszaros 271b4b1625 feat: coalesce org segment syncs instead of dropping them, so an import's pinned contacts reach their linked campaigns in the same request: SyncOrgLinkedCampaigns now records a follow-up when a request lands while a pass is running (ImportCommit writes segment membership after its chunked Add already started one, so that pass read the old membership and left the contacts to the 2-minute sweep), the per-org state moves from a sync.Map to a mutex-guarded map so running, queued and released are one atomic transition, and two hermetic tests cover the mid-pass coalesce and the idle re-run 2026-09-01 04:19:59 -07:00
Matthew Meszaros 4385f6ec02 feat: merge main and renumber the segment migrations after PR #283 took 000114 and 000115: campaign_segments becomes 000116 and campaign_lead_removals becomes 000117, since unibox_folders and mailbox_security_mode are already recorded in deployments' schema_migrations and ours are not, restoring a clean 000001-000117 sequence that check-migrations and golang-migrate both accept 2026-09-01 04:06:13 -07:00
Matthew Meszaros 128e314103 Merge remote-tracking branch 'origin/main' into fix/issue-277 2026-09-01 04:03:37 -07:00
Matthew Meszaros 33011468fb feat: rebuild the contacts bulk edit panel: a Will apply strip above the footer lists every queued change as a chip you can take back one at a time so a bulk write is never applied blind, the header names the segment or campaign the selection came from, add and remove pickers sit side by side under one heading behind small tinted glyphs instead of shouting green and red labels, the locally duplicated campaign picker is replaced by the shared CampaignMultiPicker so chips match the rest of the app, custom-field rows put key and value on one line with the type hint inline and mark a half-filled operation as skipped instead of sending it, and Escape now closes an open picker or the confirm before the drawer (with data-floating added to CategoryPicker) 2026-09-01 03:55:10 -07:00
Matthew Meszaros d6ddf1f170 feat: fix implicit-TLS SMTP on 465 and IMAP STARTTLS on 143 behind a stored per-mailbox security mode that accepts any port, stop worker ID churn orphaning mailbox assignments via flock-claimed persistent worker ids, give the unibox a standard mail-folder sidebar (inbox/sent/drafts/archive/spam/trash) backed by a provider-derived folder column, and expose the AI tool registry over REST for non-MCP function-calling agents (#283) 2026-09-01 03:53:19 -07:00
Matthew Meszaros c2b0f93583 feat: close the last silent-success window on the segment pin: the in-transaction segment_members insert now compares RowsAffected against the deduplicated segment list and rolls the create back with a 400 when a segment was deleted between the service's existence check and the write, plus a live repository test that a contact created with a segment is pinned as an include override while a sibling naming none stays out, and that a create naming a segment the organization does not have fails and leaves no contact behind 2026-09-01 03:15:08 -07:00
Matthew Meszaros 103145e970 feat: address the CodeRabbit review on the segment pin and campaign selection bar: the include override for a contact created inside a segment is now written inside the contact repository's own transaction (parsed and deduped alongside campaigns and categories, org-scoped, ON CONFLICT upsert) so a failed override write rolls the contact back instead of answering 200 with a membership that never happened, the service keeps only the pre-write existence check that turns an unknown segment into a 400, and the leads selection bar drops its bulk Delete inside a campaign so the destructive bulk action matches the row action and cannot delete workspace contacts from a Leads tab 2026-09-01 03:07:39 -07:00
Matthew Meszaros 55307b0ad9 feat: fix the segment dialog UI and make a contact created inside a segment join it (issue #285): the add-to-campaign picker now shows a humanised, colour-toned campaign status instead of the raw PAUSED_NO_ACCOUNTS enum, its footer wraps instead of clipping the hint mid-sentence and the Add leads button no longer breaks across two lines, campaign status labels move to a shared components/app/campaigns/status module, the Segments, Categories and Segment pages plus every unmapped settings and onboarding route get a document title so they stop reading Page not found, and POST /contacts takes a segments array that pins the new contacts in as include overrides (validated before the write, best-effort after it) which the New contact dialog sends when opened from a segment page 2026-09-01 02:57:30 -07:00
Matthew Meszaros 7b6830daa5 feat: say segments out loud in the API scope catalog: the READ_CONTACTS and WRITE_CONTACTS descriptions served to the API key dialog (and the docs permission table) now name segments, since segment CRUD, previews, member overrides and enrolment ride on the contact scopes and the picker never said so 2026-09-01 01:32:56 -07:00
Matthew Meszaros 5e87b1bbbf Merge remote-tracking branch 'origin/main' into feature/public-forms 2026-09-01 01:17:54 -07:00
Matthew Meszaros 60c9e316d9 feat: hosted lead-capture forms end to end: drag-and-drop builder with field settings, design panel, embed/share and submissions tabs in the dashboard, a public TanStack form app (forms/) served by the new standalone forms service (cmd/forms + internal/formserver) on FORMS_DOMAIN with per-form frame-ancestors CSP, honeypot/fill-time/Turnstile/per-IP submit protection and a same-origin JSON API proxying the backend internal API, form submissions creating contacts with categories and campaign enrollment plus realtime, audit, webhook and org-transfer coverage, migration 000114, seed forms, CI jobs, Dockerfile, systemd/nginx/compose manifests and docs 2026-09-01 01:17:51 -07:00
Matthew Meszaros 8db032a656 feat: address review on segment-campaign linking: a campaign_lead_removals table (migration 000115, orgtransfer-registered) records hand-removed leads so the automatic segment sync never re-adds them while a manual add or the one-shot enrol clears the record, PUT /campaigns/:id/segments rejects an omitted segment_ids so {} cannot detach everything, rows.Err checks on the four new link queries so a truncated read cannot commit as success, write-path enrolment syncs detached from the request goroutine with a per-org in-flight dedupe, per-campaign 30s deadlines in the sweep instead of one shared budget, and an error-with-retry state in the linked-segments dialog 2026-09-01 00:33:42 -07:00
Matthew Meszaros 66a105fb9f feat: make segments a live campaign audience and round out lead management (issue #277): a campaign_segments link table with GET/PUT /campaigns/:id/segments (max 20, replace semantics), immediate enrolment of linked-segment members as leads plus targeted re-syncs on contact writes, segment edits, pin-ins and imports and a 2-minute backend sweep for drift, waking active campaigns and restarting completed ones through the full launch checks; a Segments manager dialog and toolbar button on the campaign Leads tab; Remove from campaign as the row action and a selection-bar bulk action so leads can leave a campaign without deleting the contact; campaign and segment pickers in the CSV import wizard backed by a new segment_ids import option that pins imported rows as include overrides; a delete guard naming linked campaigns; orgtransfer registration, live repo tests and docs for all of it 2026-08-31 08:45:27 -07:00
Matthew Meszaros ddb0e349ad Merge pull request #281 from warmbly/fix/account-reverification-button-missing
Add the missing mailbox re-verification flow
2026-08-31 06:34:45 -07:00
Matthew Meszaros 773a3ecaf2 Merge remote-tracking branch 'origin/main' into fix/account-reverification-button-missing 2026-08-31 06:26:40 -07:00
Matthew Meszaros c2f5cc4e9e feat: address review on the reconnect flow: a reauth whose repeat consent omits the refresh token now refuses (typed error) when the stored one cannot be read instead of sealing an empty string over it, and credential errors resolve only after the reactivation Update succeeds so a failed reactivation keeps the banner and its reconnect button 2026-08-31 06:26:39 -07:00
Matthew Meszaros acec534b6b feat: give the mailbox drawer's ramp-hold, lifecycle and cold-ramp notice cards top padding (px-5 py-4 like every other Overview section) so they no longer touch the section divider above them 2026-08-31 06:16:15 -07:00
Matthew Meszaros 2d0db3ef63 Merge pull request #282 from warmbly/fix/issue-273
Fix missing line breaks in imported messages: read unibox bodies from the key the worker writes
2026-08-31 06:13:57 -07:00
Matthew Meszaros 70ca90a3e0 feat: fix the reconnect reactivation 500 found in live testing: reconnectAccount now loads the row via GetByID itself because the org-scoped Get selects no user_id and the owner-scoped Update was handed an empty uuid; UpdateSMTPIMAPCredentials switches to GetByID with an explicit tenancy check, and the test stub's Get now mimics the real partial row 2026-08-31 05:53:12 -07:00
Matthew Meszaros 6918ee06b0 Merge remote-tracking branch 'origin/main' into fix/issue-273 2026-08-31 05:49:38 -07:00
Matthew Meszaros 846d502284 Merge pull request #280 from warmbly/fix/cold-email-sending-defaults
feat: make the per-mailbox daily campaign cap configurable up to 5,000/day
2026-08-31 05:43:40 -07:00
Matthew Meszaros 6ea9ecbc82 Merge remote-tracking branch 'origin/main' into fix/issue-273 2026-08-31 05:40:29 -07:00
Matthew Meszaros 9d9c126b76 feat: hyphenate the 24-hour window compound in the mailboxes guide gap-throughput note 2026-08-31 05:39:53 -07:00
Matthew Meszaros a657da3a1d Merge remote-tracking branch 'origin/main' into fix/cold-email-sending-defaults 2026-08-31 05:39:46 -07:00
Matthew Meszaros 5abfb69882 feat: address Greptile review on #282: re-measure useFlipPlacement/useFlipAlignment on window resize so an open popover keeps a valid side when the toolbar reflows, and trim the unibox body-key and hook comments down to their one-line invariants 2026-08-31 05:36:24 -07:00
Matthew Meszaros c28179e61f feat: raise the campaigns ramp_start/ramp_ceiling DB CHECK constraints to 5000 (migration 000113) so raised API bounds cannot 500 on the constraint, validate the effective ramp pair against stored values on partial updates, and fix the campaign/mailbox guides on the effective cap and the 600s gap throughput bound 2026-08-31 05:31:13 -07:00
Matthew Meszaros 3a8015b12e Merge pull request #278 from warmbly/feature/live-panel-redesign
Redesign the sidebar LivePanel: visuals over text
2026-08-31 05:29:51 -07:00
Matthew Meszaros 896fc60f4d feat: add the missing mailbox re-verification flow (issue #274): POST /emails/onboarding/oauth/reauth/:id re-runs the provider consent for an existing Gmail/Outlook mailbox (login_hint preselect, same-address check, token rewrite keeping the stored refresh token) and PUT /emails/onboarding/smtp-imap/:id replaces SMTP/IMAP credentials after live validation; both resolve the credential-class error rows and reactivate the mailbox onto its worker, the drawer's Needs attention banner gains Re-authorize and Update credentials buttons, and the mailboxes guide documents reconnecting 2026-08-31 05:29:35 -07:00
Matthew Meszaros dfca54959f Merge branch 'main' into feature/live-panel-redesign 2026-08-31 05:27:56 -07:00
Matthew Meszaros 1a991a5d7a Merge pull request #279 from warmbly/feature/segment-api-crud
feat: surface the segment ID in the dashboard and docs for segment API integrators
2026-08-31 05:20:20 -07:00
Matthew Meszaros f35d7e5037 feat: address CodeRabbit review on the segment ID chip: narrow the API reference sentence to endpoints that operate on an existing segment, add an aria-label and a polite sr-only status to the copy button so the copied state reaches assistive technology 2026-08-31 04:19:20 -07:00
Matthew Meszaros 1194f819d0 Merge remote-tracking branch 'origin/main' into feature/segment-api-crud 2026-08-31 04:18:44 -07:00
Matthew Meszaros 760cb5651c feat: fix issue 273: read unibox message bodies from the key the worker actually writes (users/<owner>/emails/<mailbox>/<message>.emsg via config.StorageEndpointEmailBody) instead of the never-written emails/<owner>/<id> key, so opened messages render the full stored plain+HTML body with its line breaks instead of degrading to the collapsed one-line snippet with the truncated-message notice; thread the mailbox account id through GetBody, GetByID and the body-text search backfill, drop the dead PutBody/GetEmailKey, and in the contact activity detail stop content escaping the drawer: measured left/right alignment for the date-range popover (new useFlipAlignment hook) and wrap-anywhere on expanded detail values so long URLs cannot widen the grid past the card 2026-08-31 03:54:59 -07:00
Matthew Meszaros d936f04973 Merge branch 'main' into feature/live-panel-redesign 2026-08-31 03:53:06 -07:00
Matthew Meszaros ec1ac7080a feat: derive the LivePanel capacity denominator from the sum of each mailbox's configured campaign_limit (falling back to the 50/day default) instead of a flat mailbox count times 50, so tuned per-mailbox limits move the meter 2026-08-31 03:52:38 -07:00
Matthew Meszaros d7a17a0149 feat: make the per-mailbox daily campaign cap configurable up to 5000 (issue #276): raise campaign_limit, campaign daily_limit and ramp start/ceiling validation to config.LimitMax, warn in the dashboard above 100/day, and update aitools, zapier and docs copy to match 2026-08-31 03:50:36 -07:00
Matthew Meszaros 55156a8964 feat: surface the segment ID for API integrators: a click-to-copy ID chip in the segment page header, a Copy segment ID entry in the segments list row menu, a Segments in the API section in the segments guide pointing at the CRUD, members and enrolment endpoints, and a note in the API reference on where the dashboard shows the ID 2026-08-31 03:47:12 -07:00
Matthew Meszaros 3fc4fd0bb0 feat: redesign the sidebar LivePanel around visuals instead of text rows: a hero sent-today number that scrubs day-by-day when hovering the new smooth 14-day area sparkline (replacing the bar strip), a sky capacity meter for today vs the derived daily cap, icon glance chips for mailboxes, active senders and unread inbox, a zero-filled continuous trend axis so quiet days are not squeezed out, and no LIVE status row since realtime updates already read as live 2026-08-31 03:41:27 -07:00
Matthew Meszaros f11115b642 Add greptile config.json to stop automatic pr reviews 2026-08-31 12:12:14 +02:00
Matthew Meszaros ecf6699c24 Merge pull request #270 from warmbly/feature/contact-segments
feat: contact segments (saved audiences) with live SQL evaluation, manual overrides and one-step campaign enrolment
2026-08-30 02:36:29 -07:00
Matthew Meszaros 7a7c6051bc Merge remote-tracking branch 'origin/main' into feature/contact-segments 2026-08-30 02:07:56 -07:00
Matthew Meszaros b99e77c389 feat: replace the contact filter side sheet with an inline interactive filter bar: always-visible Category, Segment, Status and Campaign pills that apply on the spot, an Add filter menu for custom fields, date added, last updated, campaign count and (on Leads) lead status and engagement, editable pills with remove, a live matching count, Clear and Save as segment, half-filled custom-field pills kept out of the request, and docs for filtering the list 2026-08-30 01:57:38 -07:00
Matthew Meszaros 7dc6a9a26c feat: fold segments into the contacts area instead of a separate nav item: a Contacts layout with All contacts, Segments and Categories tabs, segment pages moved to /app/contacts/segments, a new Categories tab with live counts, rename, color, create and delete, a Save as segment button in the contact filter panel that opens the editor with the filters mapped to conditions, and ?category= pre-filtering of the contact list 2026-08-30 01:50:57 -07:00