Commit Graph
608 Commits
Author SHA1 Message Date
Matthew Meszaros 3601147c7c feat: address review on the avatar persistence fix: avatar object keys carry a random nonce next to the millisecond epoch so two uploads in the same millisecond cannot share an immutably cached URL, the org avatar hooks capture the target workspace id when the mutation starts and patch the org pointer, list and current-org caches only for that id so a mid-flight workspace switch cannot stamp the avatar onto the wrong org, and the API docs describe the stored file deletion on remove as best effort 2026-09-03 01:15:10 -07:00
Matthew Meszaros f579497c81 feat: make profile and workspace avatar changes survive a refresh: user avatar upload and remove go through the user service so the cached /auth/me copy is dropped instead of serving the old avatar for 30 minutes, the org list mapper carries avatar_url into the persisted workspace pointer it previously discarded, the avatar mutation hooks patch the cached user, org pointer and org queries with the server's answer before invalidating, all four handlers delete the replaced or removed object under our own avatars/ prefix with millisecond keys so quick replacements cannot collide on an immutable URL, the org avatar removal is audited, the uploader copy stops promising WebP and GIF, and the API docs note that remove deletes the stored file (fixes #295) 2026-09-03 01:06:36 -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 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 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 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 773a3ecaf2 Merge remote-tracking branch 'origin/main' into fix/account-reverification-button-missing 2026-08-31 06:26:40 -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 6918ee06b0 Merge remote-tracking branch 'origin/main' into fix/issue-273 2026-08-31 05:49:38 -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 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 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 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 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 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 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
Matthew Meszaros 3bd9325bee feat: address review on segments: gate the add-to-campaign and from-segment actions behind the manage-campaigns permission on the segment page, the segments list and the campaign Leads tab so the dashboard never offers an enrolment the API would refuse, trim the segment package and migration comments to the one invariant they carry, and drop the em dash from the editor comment 2026-08-30 00:11:19 -07:00
Matthew Meszaros 52916ab60d feat: verification evidence engine and Greptile fixes for #264: contact_verification_evidence ledger (migration 000111) fed by clean deliveries, human opens, clicks, replies and recipient-naming bounces from the send, tracking, reply and bounce paths, a decaying score that lets real mail outrank a probe and a newer bounce outrank older engagement, verification_confidence on every contact with a reasons list and animated Deliverability card in the contact drawer, per-organization probe breakers, undeliverable counts that ignore finished leads, tighter wiring comments, and docs 2026-08-29 23:49:33 -07:00
Matthew Meszaros 15dface385 feat: add the Add to segment and Remove from segment action nodes to the campaign sequence canvas with a segment picker 2026-08-29 23:45:06 -07:00
Matthew Meszaros b638ac6b24 feat: show every segment with membership and pin in / pin out / back-to-automatic controls in the contact drawer overview 2026-08-29 23:45:06 -07:00
Matthew Meszaros 716186d503 feat: add a Segments picker to the contact filters drawer and count a segment scope as an active filter for exports 2026-08-29 23:45:06 -07:00
Matthew Meszaros ad40be9276 feat: scope the contacts table by segment, add the Segment and Remove-from-segment selection bar actions, let the add-from-contacts dialog target a segment, and add the add-to-campaign and from-segment lead dialogs 2026-08-29 23:45:05 -07:00
Matthew Meszaros c8b901b204 feat: add the Segments list and detail pages with duplicate, delete, add-to-campaign and a pinned-contacts panel, register the routes, the sidebar entry and the breadcrumb labels 2026-08-29 23:45:05 -07:00
Matthew Meszaros 3bfc36c71a feat: add the segment condition builder drawer with a live match count and the campaign, segment and enum multi-pickers it uses 2026-08-29 23:45:05 -07:00
Matthew Meszaros d831aba034 feat: add segment_ids to the dashboard contact search model and route segment audit events and contact changes to the segments queries on the realtime spine 2026-08-29 23:45:05 -07:00
Matthew Meszaros 960f42b846 feat: add the dashboard segment model, API client and react-query hooks for segments, fields, preview, overrides, member modes, contact-side membership and campaign enrolment 2026-08-29 23:45:05 -07:00
Matthew Meszaros a02ff7c936 feat: address verification overhaul for #264: MillionVerifier as a pay-as-you-go integration plugin with org-sealed key and automatic fallback to the built-in check, built-in prober gains domain cache, Microsoft/Yahoo fingerprinting, MX fallback, disposable/role sub-statuses and a self-check breaker, imports and POST /contacts accept verification results in any known provider vocabulary with auto-detected columns, verdict provenance and expiry columns (migration 000110), campaigns park at paused_undeliverable with re-verify/send-anyway instead of finishing, POST/GET /contacts/verification bulk actions and overview, launch gate override via acknowledge_list_risk, animated verification marks, banner and settings card in the dashboard, and docs 2026-08-29 23:11:22 -07:00
Matthew Meszaros 44b2c18906 feat: address review on cloud-managed mailboxes: the consumer now asks the cloud to vouch for a warmup token in a mailbox it warms (GET /pool-link/instance/mailboxes/:id/warmup-tokens/:token) and files anything unverified as ordinary mail instead of dropping on a sender-controlled header, disconnect keeps local mirrors and the link until the cloud confirms the instance is released so managed mailboxes cannot be stranded, and long narrative comments are cut to one line 2026-08-29 10:07:36 -07:00
Matthew Meszaros 1e47648208 feat: show the 'warmup cannot run with one mailbox' pool-size notice only on self-hosted instances, since a hosted workspace warms against the shared pool and one mailbox already has hundreds of partners there 2026-08-29 09:52:19 -07:00
Matthew Meszaros 2a831e9783 feat: let a linked self-hosted instance sign Google and Microsoft mailboxes in through Warmbly Cloud's own OAuth apps and send with cloud-brokered access tokens: the cloud runs the consent (pool_link_mailboxes.managed, brokered state in Redis, the existing /addresses/*/callback completes it and redirects to the instance's /cloud-oauth/done), keeps the refresh grant, mints short-lived tokens at /pool-link/instance/mailboxes/:id/token and refuses them for revoked links, removed, inactive or blocked mailboxes; the instance mirrors such mailboxes without a credential (cloud_link_mailboxes.managed), ships them to the worker as brokered so goog/msgraph init on a token source that pulls from /api/v1/internal/cloud-link/token/:id, lets the consumer ignore cloud warmup tokens for enrolled mailboxes, and can adopt mailboxes connected directly on the workspace; Add account shows the cloud path and the adoptable list, and the Warmbly Cloud guide documents the model 2026-08-29 09:50:52 -07:00
Matthew Meszaros febe5bb0d7 feat: let the /connect code input accept a pasted ABCD-EFGH code by stripping the dash and lowercase through input-otp's pasteTransformer before its alphanumeric pattern check, which was rejecting the whole paste 2026-08-29 09:19:31 -07:00
Matthew Meszaros 41d52723b6 feat: rebuild the Warmbly Cloud /connect approval page on the auth screen's sky as a three step card (code, review, linked) with the OTP slot input for the ABCD-EFGH code, the requesting instance shown as an identity card with a live instance-to-cloud diagram, workspace choice as selectable rows instead of a native select, inline unknown-code recovery, and a way back to the instance once linked 2026-08-29 09:13:46 -07:00
Matthew Meszaros 315509d0da feat: replace the purple lock card with a full-screen plan chooser on the auth screen's sky (warm 10 mailboxes free, self-host and link, or choose a plan), give the hosted Accounts page a two-path panel (connect a mailbox here, or self-host for free and link the instance) that collapses to a usage strip once mailboxes exist, drop indigo from the access dialog, describe the free plan honestly in the catalog, and default the transactional email footer to the real company details (Mindroot Ltd, 16543299, 71-75 Shelton Street) 2026-08-29 08:48:13 -07:00
Matthew Meszaros d621d9b79b feat: sign a new account in as part of registration: createAccount returns the user, both the verification-free and the code-confirm paths mint a session through finishLoginAs (2FA challenge included), /auth/register/confirm answers with that session instead of 204, and the dashboard drops into the workspace right after signing up instead of bouncing to the sign-in form 2026-08-29 08:41:34 -07:00