Replace the custom polling loop in the Slack sender with the
go.gearno.de/kit/worker framework, matching the email sending worker.
Add a processing_started_at column to slack_messages so claims are safe
under the worker's claim/process model, with stale recovery for crashed
in-flight sends. Rename sender.go to worker.go to match the kit worker
file naming convention.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
probod-bootstrap could only resolve aws:// Secrets Manager refs.
Add awsps:// for SSM Parameter Store (GetParameter with decryption)
and awssm:// as an explicit Secrets Manager prefix. Keep aws:// for
backward compatibility. Values are cached per run per backend.
Update Helm, .env.example, and probod-bootstrap changelog.
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
Address PR review feedback identified by cubic:
- Re-key CardInset off the Card's resolved padding (shared via context)
instead of size, so insets line up even when padding is decoupled from
size via the padding prop; padding="none" becomes a no-op.
- Give ProboLogo a default aria-label so its role="img" SVG has an
accessible name when used standalone; callers can still override it.
- Make the ProboLogo story render spread args so Storybook controls work.
Signed-off-by: Émile Ré <emile@probo.com>
Add the frameworks (Compliance), security commitments, trusted-by, and
recent updates sections to the home page, plus a site-wide "Powered by
Probo" footer. Frameworks, references, and updates are relay-driven; the
security commitments section uses placeholder POJO data until a backend
exists.
Keep generic primitives in the v2 kit (ProboLogo, a faint Text tone, and
a soft Card variant with padding="none") and place the product-specific
cards under the app's components folder, composing the base Card. Resolve
each framework's themed logo in the graph via Framework.themedLogoUrl,
mirroring the trust center logo resolver.
Signed-off-by: Émile Ré <emile@probo.com>
Fix the valid issues raised in the scaffold review.
UI kit: the Button loading state now replaces only the leading icon
instead of dropping the label, Button consumes the `active` variant so
it no longer leaks onto the DOM, and every v2 skeleton sets aria-hidden
after the prop spread so a consumer cannot override it.
@probo/relay: guard the caller-supplied onCompleted/onError callbacks so
a throwing callback still settles the awaitable mutation promise instead
of leaving it pending.
compliance-portal: normalize external website hrefs and read hostname
via URL.hostname, add a localized catch-all not-found route, and widen
the .gitattributes glob so colocated __generated__ artifacts at any depth
are marked generated.
Docs: correct the forms guide (Base UI passes plain values, Zod v3
flatten API), spread the child fragment in the permissions example, and
drop references to v2 components that do not exist in the ui guide.
Signed-off-by: Émile Ré <emile@probo.com>
The popup stacked a CSS border on top of shadow-5, whose first layer is
already a 0 0 0 1px sand-a3 hairline ring, so the edge rendered as two
1px lines. Drop the redundant border and let the shadow provide the
single hairline.
Also transition the scale property instead of transform: Tailwind v4
maps scale-95 to the CSS scale property, so the old transform,opacity
list never animated the scale and the close snapped. Animate scale and
opacity with an explicit 150ms ease-out so open and close are smooth.
Signed-off-by: Émile Ré <emile@probo.com>
Soften the popup border to the translucent sand-a2 token and inset the
separator (mx-3) so it lines up with item text instead of bleeding to
the panel edges, lightening it to sand-a3. Both now track the Figma
alpha hairlines and keep their dark-mode values via the Radix alpha
scale.
Also modernize the existing arbitrary-variant classes in this file to
Tailwind's shorthand syntax (data-[x]: -> data-x:, origin-[var(...)] ->
origin-(...)).
Signed-off-by: Émile Ré <emile@probo.com>
Add a TrustCenter.themedLogoUrl Relay live resolver that reads the
light and dark logos from the graph and subscribes to the system
prefers-color-scheme media query, returning the dark logo (falling
back to the light one) when the OS prefers dark.
The TopBar now selects this single field instead of pulling both logo
URLs and branching on useSystemTheme, keeping the theme-to-URL logic in
the graph where any component can reuse it.
Signed-off-by: Émile Ré <emile@probo.com>
DropdownGroupLabel renders Base UI's Menu.GroupLabel, which requires a
Menu.Group ancestor. The user menu rendered the label directly in the
popup, so opening it threw a missing MenuGroupContext error. Wrap the
email label in a DropdownGroup.
Signed-off-by: Émile Ré <emile@probo.com>
The dark-mode script set the .dark class once at load. Subscribe to the
prefers-color-scheme media query so the theme also updates while the page
is open when the OS appearance changes.
Signed-off-by: Émile Ré <emile@probo.com>
TextSkeleton and HeadingSkeleton baked w-full into their shared base.
Under tailwind-variants/lite (no tailwind-merge) that collided with the
caller's width class and won by stylesheet order, so every skeleton
ignored its width: bars stretched full in column contexts and collapsed
in flex rows.
Drop w-full from the base so the single caller width applies, matching
ButtonSkeleton/AvatarSkeleton. Size the compliance-portal TopBar and
Hero skeleton bars to roughly match their English copy, and add a rule
so skeleton widths are revisited when en-US strings change.
Signed-off-by: Émile Ré <emile@probo.com>
The solid color rules in the Button, IconButton, and Badge variant
matrices had no highContrast condition, so a high-contrast solid element
matched both the base rule and the high-contrast rule. With
tailwind-variants/lite (no tailwind-merge) both classes are emitted, and
a static text-white from the base wins over the theme-aware text step.
That looked fine in light mode but left illegible white text on the
light step-12 background once dark mode was enabled.
Gate the base solid rules on highContrast:false so the self-contained
high-contrast rules are authoritative. The tinted variants are left as
is: their step-11/step-12 text both flip with the theme, so they are
never illegible.
Signed-off-by: Émile Ré <emile@probo.com>
Add a synchronous head script to the compliance portal that sets the
.dark class on <html> when the OS prefers a dark scheme, so the v2 Radix
theme resolves to dark values before first paint with no flash. Declare
a color-scheme meta so native UI matches the active scheme.
Signed-off-by: Émile Ré <emile@probo.com>
Replace the remaining hard-coded UI copy in the top bar, user menu, home
hero, and the documents/subprocessors/updates/requests pages with i18next
keys, and supply the en-US and fr-FR catalog entries for them. The home
hero title uses value interpolation rather than string concatenation so
word order stays correct across locales.
Signed-off-by: Émile Ré <emile@probo.com>
Wire i18next into the compliance portal with a custom backend built on
import.meta.glob, so each _locales/*.json becomes its own lazily loaded
chunk keyed by a namespace derived from the folder path. The active
language is resolved from the browser, collapsing any fr*/en* tag to
fr-FR/en-US with en-US as the ultimate fallback; fallbackLng then only
covers individual missing keys.
Add an app-level default namespace catalog and switch the documents
page title to a translation key to exercise the lazy-loading path.
Signed-off-by: Émile Ré <emile@probo.com>
On a hard reload the root route briefly showed a generic centered pulse
(PageSkeleton) while the layout chunk downloaded, then snapped to the
topbar skeleton. Point the root Fallback at MainLayoutSkeleton so the
chunk-load fallback matches the loader's own skeleton and the transition
is seamless. PageSkeleton is now unused and removed.
Signed-off-by: Émile Ré <emile@probo.com>
Build the Trust Center landing hero and the nav page headers on a shared
HeaderBand shell. The home page loads its own query and feeds the Hero
(org headline, description, and contact info) via a colocated fragment;
the Documents, Subprocessors, Updates, and Data Requests pages use a
PageHeader (size-7 title, optional count, inline actions). Switch the
layout body to the grey sand-2 canvas so the white header bands read as
surfaces on top.
Split the header UI into focused components rather than one
over-configurable Hero: HeaderBand owns the band, Hero the landing
content, PageHeader the nav-page content. Add a hostname helper under
lib/url and standardize the 1024px container on the max-w-5xl token.
The Documents/Subprocessors filter/search/tabs toolbars and item counts
are deferred until the matching v2 components and queries exist.
Signed-off-by: Émile Ré <emile@probo.com>
Capture two conventions surfaced while building the top bar: use the
Relay @required directive to make expected-present nullable fields
non-null for consistent typing, and reserve default exports for the
component that lazy() imports as a bundle entry while everything else
uses named exports.
Signed-off-by: Émile Ré <emile@probo.com>
Build the Trust Center top navigation in a Relay-wired layout route. The
portal now mounts a Relay environment and provider, and the root layout
loads a query whose fragment feeds the TopBar: brand, ghost-pill nav with
an active state from the router, and a guest "Get Access" button versus an
authenticated user menu. Placeholder section routes keep the nav links and
active state functional until real pages land.
Wire the Relay tagged-template transform via @rolldown/plugin-babel and
set the router basename to the /trust/{slug} path prefix so the app
resolves under its served path.
Signed-off-by: Émile Ré <emile@probo.com>
Round out the button family with the two navigational siblings described
in the UI guide: Anchor renders a styled <a> for external links and Link
renders a react-router link for in-app navigation, both sharing Button's
tv styles. Add an `active` look variant so nav items can carry a
persistent selected state without forking structure.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Card component from the Probo Radix UI Figma: a <div>
container with size 1-5 (padding + radius), the surface/classic/ghost
variants, and an interactive hover/active affordance. Add a CardInset
part that bleeds content (e.g. a cover image) to the card edges,
reading the card size from context so it negates the matching
padding. Includes a paired CardSkeleton. Styling uses
tailwind-variants/lite.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Dropdown Menu family from the Probo Radix UI Figma on
top of Base UI's Menu primitive, exported as flat Dropdown* parts:
Dropdown (root), DropdownTrigger, DropdownPopup, DropdownItem (with
shortcut and accent/error color), DropdownCheckboxItem,
DropdownRadioGroup/RadioItem, DropdownGroup/GroupLabel,
DropdownSeparator, and DropdownSubmenu/SubmenuTrigger.
The popup carries size (1-2), variant (solid/soft highlight), and
highContrast to its items through a small context. Styling uses
tailwind-variants/lite with data-[highlighted] selectors; item
shortcuts inherit the contrast color on highlight.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Callout component from the Probo Radix UI Figma: a
short contextual message with a leading icon (defaults to an info
icon), with size 1-3, the soft/surface/outline variants across the
sand/gold/red/green/amber/sky scales, and highContrast. Includes a
paired CalloutSkeleton. Styling uses tailwind-variants/lite slots
(root/icon/text).
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Badge component from the Probo Radix UI Figma: a
non-interactive <span> with size 1-3, the solid/soft/surface/outline
variants across the sand/gold/red/green/amber/sky scales with
highContrast, and an optional iconStart. Corner radius is bound to
size. Includes a paired BadgeSkeleton. Styling uses
tailwind-variants/lite.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the IconButton component from the Probo Radix UI Figma: a
square, icon-only <button> sharing Button's size 1-4 and
variant/color/highContrast matrix, with a loading spinner state. The
single icon is passed as children; callers provide an aria-label.
Corner radius is bound to size. Includes a paired IconButtonSkeleton.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Button component from the Probo Radix UI Figma: a
<button> with size 1-4, the classic/solid/soft/surface/outline/ghost
variants across the sand/gold/red/green/amber/sky scales with
highContrast, iconStart/iconEnd slots, and a loading state that
shows only a spinner. Corner radius is bound to size per the design
(radius is theme-global, not a per-instance prop). Includes a paired
ButtonSkeleton. Styling uses tailwind-variants/lite.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Avatar component from the Probo Radix UI Figma on top
of Base UI's avatar primitive (image load + fallback). It supports
size 1-9, solid/soft variants across the sand/gold/red/green/amber/
sky scales with highContrast, a radius scale, and a fallback node
for initials or an icon, plus a paired AvatarSkeleton. Styling uses
tailwind-variants/lite slots (root/image/fallback).
Add @base-ui/react to @probo/ui — the first v2 component built on a
Base UI primitive.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the Heading primitive from the Probo Radix UI Figma: it
renders the semantic h1–h6 chosen by a numeric level prop (driving
the document outline) while size controls the visual scale
independently, defaulting to size 6 / bold weight per the design.
Add a paired HeadingSkeleton and a story.
Factor the shared size/weight/align/color variants and the color ×
highContrast compound variants out of the Text tv so Text and
Heading differ only in their defaults.
Signed-off-by: Émile Ré <emile@probo.com>
The numbered v2 token scales (text-1…9, rounded-1…6, shadow-1…6)
share a prefix with the color/utility namespaces, and tailwind-merge
(which tailwind-variants runs by default) cannot tell a numeric
text-3 from a color step, so it silently dropped the size whenever a
text-<color> was present. Switch the v2 kit to the merge-free
tailwind-variants/lite entrypoint (added in v3): upgrade
tailwind-variants to ^3.2.2, import tv/VariantProps from
tailwind-variants/lite in the typography primitives, and drop the
per-call twMerge:false.
The legacy v1 kit stays on tailwind-variants (merge intact). Document
the rationale in ui.md and add a packages/ui/src/v2 ESLint guard that
forbids tailwind-variants, tailwind-merge, and clsx.
Signed-off-by: Émile Ré <emile@probo.com>
Implement the first v2 typography component from the Probo Radix UI
Figma: a Text primitive rendering a single span with size, weight,
align, color, and highContrast variants (color x highContrast
resolves to the sand/gold/red/green/amber/sky text step 11 or 12),
plus a paired TextSkeleton. Styling is tailwind-variants only, with
the scale shared through a variants module per the kit guides.
Stand up a theme-isolated v2 Storybook (.storybook-v2) loading the
v2 theme with a light/dark toggle, since v1 and v2 cannot share one
Tailwind build; the v1 build now excludes src/v2.
Signed-off-by: Émile Ré <emile@probo.com>
The @probo/eslint-plugin-relay-types shim only types an
`eslint-plugin-relay` import, which no app makes anymore now that
linting runs through the single root eslint.config.mjs. The apps'
tsconfig.node.json even pulled it in for a per-app eslint.config.mjs
that no longer exists.
Remove the devDependency and the `types` entry from each app's
node tsconfig, and drop the stale eslint.config.mjs include. The
shim stays for packages/eslint-config, its real consumer.
Signed-off-by: Émile Ré <emile@probo.com>
Apply small follow-up edits to the frontend documentation: the
AGENTS index, the forms, react-components, and ui guides.
Signed-off-by: Émile Ré <emile@probo.com>
Extract the awaitable useMutation into @probo/relay as a
createUseMutation factory that delegates feedback to an injected
MutationNotifier, keeping the package free of UI and i18n
dependencies. compliance-portal binds it to its Base UI toast +
i18next + formatError stack and imports it by explicit path
(#/lib/relay/useMutation), dropping the lone intra-app barrel; a
compliance-portal-scoped no-restricted-imports rule forbids
react-relay's useMutation.
Bring packages/relay and packages/routes into the shared ESLint
scope and fix the violations that surfaced, and deprecate the
legacy withQueryRef / loaderFromQueryLoader helpers. Document the
shared-hook pattern and the "index.ts for package entrypoints only"
rule in the relay, hooks, and app-arborescence guides.
Signed-off-by: Émile Ré <emile@probo.com>
Relay 21 made the filename-prefix requirement opt-in for non-Haste
projects, and the relay/graphql-naming lint rule only covers
operations and legacy fragment containers, leaving hooks-based
fragment names unguarded. Re-enable compiler enforcement via
enforce_module_name_prefix_for_non_haste so fragments keep the
collision-free, navigable <ModuleName>_<localName> convention, and
document the rationale in the Relay guide. The compiler accepts the
current sources unchanged, so existing names already comply.
Signed-off-by: Émile Ré <emile@probo.com>
The v2 token guide endorsed raw <h1>/<p> with hand-applied step
classes, which invites token drift across app code. Add a UI guide
section establishing that content text flows through kit typography
components (Text, Heading, Code) and that raw elements are reserved
for kit internals and structural, non-textual nodes. Cross-link it
from the typography token section so the two guides stay aligned.
Signed-off-by: Émile Ré <emile@probo.com>
Add the frontend guides the v2 UI kit and compliance-portal need but
that the first rework left uncovered: forms, routing, client state, and
permission-gated UI.
forms.md documents a tiered approach on Base UI Field/Form -- native
constraints, then a validate function, then zod parsed in onSubmit, and
react-hook-form only for large or dynamic forms -- and drops the custom
useFormWithSchema wrapper. routing.md covers @probo/routes, navigation,
typed params, URL-as-state, redirects, auth/protected routes, and the
folded-in no-outlet-context rule. state-management.md gives a decision
order across Relay, URL, local state, context, and zustand.
permissions.md gates UI on the canUpdate/canDelete permission(action:)
fields without re-encoding authorization in the client.
Rename v2-colors.md to v2-tokens.md and add the typography, radius,
shadow, and native-spacing scales alongside color. Extend ui.md with
user feedback, empty-state, and accessibility sections; standardize
toasts on Base UI's Toast (Toast.useToastManager) and retire the legacy
useToast across ui.md, forms.md, error-handling.md, and relay.md. Add an
Intl formatting section to i18n.md and a non-Relay HTTP / file
upload-download section to ts-style.md. Update the AGENTS.md index and
the v2-color-scale cursor rule for the new and renamed guides.
Signed-off-by: Émile Ré <emile@probo.com>
Make contrib/claude the single source of truth for v2 frontend work
on the compliance-portal app and packages/ui/src/v2, treating console
and the legacy @probo/ui tree as non-compliant code to migrate rather
than precedent.
Rewrite ui.md around the v2 kit: flat folders, Base UI as the headless
layer styled thinly (controlled open/onOpenChange, no imperative ref or
cloneElement plumbing), tailwind-variants only, separate components over
structure-changing variants, and bundle-safe skeletons that never drag
Base UI into the loading path.
Add a naming/suffix taxonomy to react-components.md, replacing the
Table/Row and connection-item Card suffixes with List/ListItem, and add
an error/fallback props convention. Document _lib and _locales special
folders plus routes.ts placement in app-arborescence.md, with at most
one _locales per routes.ts.
Add error-handling.md (reusable ErrorBoundary usable at any level plus
async try/catch) and i18n.md (i18next key-based catalogs). Update the
relay file-organization and fragment examples, the connection-item
cursor rule, and the AGENTS.md index to match.
Signed-off-by: Émile Ré <emile@probo.com>
Wire up react-router with a root layout route and an index home page,
following the single-arborescence conventions: pages live under pages/,
routes are built from AppRoute via routeFromAppRoute, and route bundles
load lazily behind a Suspense fallback.
Add the @probo/routes and @probo/react-lazy workspace dependencies that
the route setup relies on.
Signed-off-by: Émile Ré <emile@probo.com>
Introduce a lib/http module with buildEndpoint and getPathPrefix,
extracted from the trust app so the compliance portal can resolve its
GraphQL endpoint with the same path-prefix handling.
Signed-off-by: Émile Ré <emile@probo.com>
The compliance-portal scaffold had an index.html pointing at a
src/main.tsx that did not exist, and carried stale config copied
from another app, so the bundle could not build.
Add the React entrypoint (main.tsx, App.tsx), a Tailwind index.css
wired to the standalone v2 theme, and vite-env.d.ts for the CSS
side-effect import and env typing. Scope the Tailwind @source scan
to packages/ui/src/v2 since this is a v2-only build, and use v2
color and type scales in index.html and App.
Drop the deprecated baseUrl from tsconfig.app.json and remove the
invalid react() babel relay option, which is unsupported by
@vitejs/plugin-react 6 and unneeded until Relay queries exist.
Signed-off-by: Émile Ré <emile@probo.com>
The compliance-portal app still carried the per-workspace ESLint
scaffold that the rest of the monorepo dropped when linting was
centralized into the root eslint.config.mjs. It defined its own
local config, lint script, and lint-only devDeps, and was not
covered by the root lint script, so it was never linted.
Remove the local eslint.config.mjs, the lint script, and the
eslint/@probo/eslint-config devDeps, then add the app to the root
appDirs so the shared ts, react, and relay rule sets apply to it.
Replace the explicit per-directory list in the root lint script
with a bare `eslint .`, which the root config was already designed
for via its globalIgnores. Ignore pkg/** so the vendored minified
JS under the Go tree is not linted.
Signed-off-by: Émile Ré <emile@probo.com>
Add the root configuration files for apps/compliance-portal, the
refactor of the legacy trust center SPA. The scaffold mirrors the
existing trust app (Vite, Tailwind, Relay, TypeScript project
references, ESLint) and targets the trust v1 GraphQL schema, which
the compliancepage backend already serves under /api/trust/v1.
Wire the app into the root relay.config.json with a dedicated
complianceportal project so the Relay compiler emits artifacts for
its sources. External dependencies are pinned to current latest
versions rather than matching the older majors used by the other
apps.
Signed-off-by: Émile Ré <emile@probo.com>
Convert the hand-rolled webhook Sender loop into a kit worker.Handler so
the sender gets the standard worker recovery, metrics and tracing
instead. Claim loads the next unprocessed webhook data and Process
performs the deliveries; behaviour is preserved with a single-concurrency
worker on the existing poll interval.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
Allow install scripts for the packages we actually need (esbuild for
Vite builds, fsevents for dev file watching, unrs-resolver for n8n
linting) and deny the rest (core-js, isolated-vm,
eslint-plugin-n8n-nodes-base) so npm 11's install-script warning is
covered.
Signed-off-by: Sacha Al Himdani <sacha@probo.com>