Partial grants completed without feedback, leaving Reconnect
required unexplained. Keep the token and toast the backend
missing-scopes error after the OAuth callback redirect.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Visitors have no Identity.locale, so the banner now falls
back to a supported navigator language match and omits the
persist action. Unsupported browser languages stay silent
instead of pretending English is preferred.
Signed-off-by: Émile Ré <emile@probo.com>
Repo-wide /releases/latest pointed at the wrong
assets when other tracks published more recently.
Point users at probo-agent/v* releases and split
download vs enroll into separate copy blocks with
placeholder URLs they fill in.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Rename button-styled Link/Anchor to ButtonLink/
ButtonAnchor and add underlined text Link/Anchor
so names match look and element. Hero meta uses
plain Anchors for contact and custom links.
Signed-off-by: Émile Ré <emile@probo.com>
Prefer the saved locale as the primary CTA, soften secondary
and NDA actions, tint dismiss hovers to the banner palette,
and center the leading icon with the message.
Signed-off-by: Émile Ré <emile@probo.com>
The button is on the review page itself, so Review and sign
is redundant. Console keeps its own reviewAndSign key.
Signed-off-by: Émile Ré <emile@probo.com>
Locale and NDA notices shared layout chrome but were not kit
Callouts. Pull the full-bleed band into Banner and rename the
feature wrappers so the names match the role.
Signed-off-by: Émile Ré <emile@probo.com>
When both banners stack they read as one band if they share
gold. Use sky (info) for locale and amber (warning) for NDA,
and keep content inside the same max-w-5xl column as TopBar.
Signed-off-by: Émile Ré <emile@probo.com>
Match the unsigned-NDA banner layout: keep layout classes in
tv slots so the callout shell stays free of raw utilities.
Signed-off-by: Émile Ré <emile@probo.com>
Signed-in users with an incomplete portal NDA need a clear
path to review and sign without waiting for a private
document export to fail the gate.
Signed-off-by: Émile Ré <emile@probo.com>
Match the document viewer header so users can leave without
signing, returning to the list instead of the continue URL
that would re-trigger the gate.
Signed-off-by: Émile Ré <emile@probo.com>
Mutations never reach route error boundaries, so each call
site reimplemented sign-in / full-name / NDA redirects.
Consume those gates in the shared useMutation notifier and
drop the duplicated handlers.
Signed-off-by: Émile Ré <emile@probo.com>
Document export is a mutation, so NDA_SIGNATURE_REQUIRED never
reaches the route boundary. Catch it in the export hook, stop
re-firing on mutate identity churn, and match gate errors by
name when instanceof fails across package copies.
Signed-off-by: Émile Ré <emile@probo.com>
Admins could only revoke devices, so never-enrolled and revoked
inventory rows piled up with no way to remove them. Soft-delete
is limited to REVOKED devices (revoke first), and ITAM GC now
hard-deletes PENDING/REVOKED orphans with no API key, postures,
or valid enrollment token—including user tombstones without
history.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
JSONL was awkward in spreadsheets and SIEM imports. Write
tab-separated-friendly CSV with organization name on every row,
resolve audit actors to email or API key name, and enrich SCIM rows
with profile email and display name when available.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Replace the cramped status checkboxes with a multi-select
dropdown (new DropdownCheckboxItem), and update the Relay
store to PENDING when an activation email is sent so the
row reflects the server-side state change immediately.
Signed-off-by: Émile Ré <emile@probo.com>
A debounced search could fire after status, role, kind, or sort
changed and refetch with the previous filter values, briefly
reverting the list. Cancel the pending callback before those
immediate refetches so the latest filters stick.
Signed-off-by: Émile Ré <emile@probo.com>
Address PR review feedback on the profile-state split: SAML sign-in now
activates a pending profile, deactivation counts owners against the
profile's own organization to close a last-owner bypass, the migration
leaves historical activated_at/deactivated_at NULL rather than
fabricating timestamps, and pending members are no longer rendered with
the deactivated (faded) styling.
Drop the single-value state filter in favor of the multi-value states
across the profile and signatory surfaces. Remove ProfileFilter.state
(only states[] remains) and convert the signatures profileState filter
to profileStates. Turn the console people filter, the CLI
"user list --state" flag, and the n8n listUsers and getAllSignatures
state inputs into multi-select controls, where an empty selection means
all states.
Signed-off-by: Émile Ré <emile@probo.com>
"Archive" was misleading for users: the action sets a profile to
DEACTIVATED while keeping the person in the organization. Rename it to
"deactivate" across the API, CLI, MCP, n8n, and console UI.
Consolidate the two overlapping operations into a single deactivateUser
backed by the fuller, guarded logic (SCIM guard, last-active-owner
guard, invitation expiry, signature cancellation, membership update,
webhook) and authorized via iam:membership-profile:deactivate. Remove
the archiveUser surface and the thin state-only deactivate path.
Signed-off-by: Émile Ré <emile@probo.com>
The inline owner picker on the assets table queried people without a
state filter, so deactivated profiles remained selectable as owners.
Restrict it to ACTIVE and PENDING to match the other owner pickers,
keeping pending members assignable while dropping deactivated ones.
Signed-off-by: Émile Ré <emile@probo.com>
Replace the binary profile ACTIVE/INACTIVE model with PENDING, ACTIVE,
and DEACTIVATED so invited-but-not-yet-activated members remain
assignable to assets, data, and risks instead of being treated like
deactivated users.
Add activated_at/deactivated_at timestamps and Mark* lifecycle helpers,
and update every transition (create, invite/re-invite, activation,
archive, SCIM, SAML, sessions, compliance-portal grant) to the new
states. Expose a multi-state states[] filter across coredata, GraphQL,
MCP, and the console owner pickers, which now request ACTIVE and
PENDING members.
A migration renames the membership_state enum, classifies existing
inactive profiles as PENDING from recent invitation activity, and
backfills the new timestamp columns.
Signed-off-by: Émile Ré <emile@probo.com>
Route audit-log and SCIM-event exports through export_jobs with typed
arguments, an iam BuildAndUploadExport/SendExportEmail implementation,
and a concurrent export-job worker with stale recovery. Stream JSONL via
page.WalkAll into S3, and expose the request flow on console, connect,
MCP, and CLI.
Co-authored-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
List rows and the viewer toolbar used the same clipboard
toast path; Ukrainian moreRegions needs one/few/many forms.
Signed-off-by: Émile Ré <emile@probo.com>
The swipe Popup keeps a CSS transform that re-roots
position:fixed, so Base UI's modal inert cutout missed
the locale trigger and blocked hover and clicks.
Signed-off-by: Émile Ré <emile@probo.com>
Keeps DocumentViewer focused on layout and preview state, and
gives the page/zoom/copy/download chrome its own home.
Signed-off-by: Émile Ré <emile@probo.com>
Unauthorized visitors still need the document identity and a
way back to the list; only the preview toolbar is withheld,
with the access CTA replacing the file body.
Signed-off-by: Émile Ré <emile@probo.com>
Sharing only copies the viewer URL, so rename the action and
expose the same control on each documents list row.
Signed-off-by: Émile Ré <emile@probo.com>
AWS-scale region lists blew out subprocessor cards. Cap the
visible labels and open the rest from a +N trigger using a
new Base UI Popover in the v2 kit.
Signed-off-by: Émile Ré <emile@probo.com>
When a reference name wraps to two lines, sibling cards in the
same grid row stayed shorter because MediaTile only sized to its
content. Fill the grid cell and grow the caption so peers match.
Signed-off-by: Émile Ré <emile@probo.com>
Vite only forwarded /graphql, so /initiate?continue=… fell through
to the SPA and the locale loader rewrote it to /en/initiate.
Match the host-root API routes (including query strings) so local
OAuth hits the Go backend.
Signed-off-by: Émile Ré <emile@probo.com>
Let signed-in visitors override the OS color scheme from
the account menu without persisting across reloads, and
keep themed logos in sync with the effective mode.
Signed-off-by: Émile Ré <emile@probo.com>
Long nav copy was squeezing the entity name into an
ellipsis inside max-w-5xl. Stack name over the tagline,
hide brand text below lg, and shorten the longest
topBar.nav strings so the brand stays readable.
Signed-off-by: Émile Ré <emile@probo.com>
Static lang=\"en\" in index.html left French UI declared as English
for assistive tech. Mirror the compliance portal by updating
document.documentElement.lang on init and when the locale changes.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Reject deletion while a campaign is fetching sources so workers are
not racing a removed record. All other statuses remain deletable.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Drop the backend status gate on campaign delete and show delete in
the console whenever the user has delete permission, regardless of
whether the campaign is draft, in progress, or completed.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Completed campaigns were immutable because delete only accepted draft
or cancelled status. Extend deletion to completed records and surface
delete in the console for that status.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Pass/fail was the main device UI signal, but operators need
the agent's observed value. Expose a formatted value per
check, show current postures on the device page, and replace
the Postures tab with paginated report history grouped by
agent push time. Status stays in the model for later rulesets.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
ISO audits often span a window distinct from certificate validity.
Store optional audit_start_date and audit_end_date on the audit
record and expose them through GraphQL, MCP, CLI, n8n, and console.
Signed-off-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
Add a per-address confirmation-email cooldown and disable the
resend/forgot-password submit buttons while the mutation is in
flight so callers cannot flood the mail queue or double-submit.
Signed-off-by: Émile Ré <emile@probo.com>
Unverified password identities were able to open sessions after
signing out. Reject sign-in with EMAIL_NOT_VERIFIED and add a
resend-confirmation flow so users can complete verification.
Signed-off-by: Émile Ré <emile@probo.com>
Intl.DisplayNames rejects the GLOBAL pseudo-region, so label
resolution now handles it explicitly before rendering the picker.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
The bar read the raw selection size for its count, so after a request
succeeded and Relay flipped the rows to pending the left count stayed
stale while the action count dropped to zero. Resolve the selection
against the current rows instead, so requested rows leave the count at
once and the bar closes without depending on a clear that may not run.
Signed-off-by: Émile Ré <emile@probo.com>
Drop the request-all shortcut so callers always name the
documents, reports, and files to request. TopBar Get Access
now only signs in; bulk selection is the multi-resource path.
Signed-off-by: Émile Ré <emile@probo.com>
The bulk request skips any id that already has an access record, so
rejected or revoked rows reported success while creating nothing. Treat a
row as requestable only when the viewer is unauthorized and no access
record exists yet, keeping the selection CTA count honest.
Signed-off-by: Émile Ré <emile@probo.com>
The bottom selection action bar carried its layout as inline class
strings on the container divs. Move them into a slotted tv in the
documents variants module, matching the documentsLayout pattern, so the
component consumes named slots instead of ad hoc className literals.
Signed-off-by: Émile Ré <emile@probo.com>
Public / already-authorized and already-requested rows could be ticked
even though they never contribute to "Request Access (N)", making the
selection count look inconsistent with the CTA. Restrict selection to
locked rows: their checkbox is disabled otherwise, and "Select all" now
picks only the lockable rows.
Signed-off-by: Émile Ré <emile@probo.com>
Visitors could only request access to one document, report, or file at
a time. Add row checkboxes and a bottom selection toolbar to the
compliance portal documents page so a visitor can select several rows
and request access to all still-locked ones in a single round-trip.
Expose a selection-scoped requestAccesses mutation that forwards the
chosen id lists to the existing RequestPortalAccess service (one
transaction, one NDA/auth gate). The resolver loads and tenant-checks
every target before requesting so a foreign id is rejected before any
access row is written, and echoes the affected nodes so the client
flips each row to pending in place.
Add a styled Base UI Checkbox to the v2 kit, a local selection context
shared by the independent row fragments, and mirror the new selection
strings across all locales.
Signed-off-by: Émile Ré <emile@probo.com>
Makes it practical to find people in larger orgs across GraphQL, MCP, CLI, and n8n, with page size raised to 100.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>