diff --git a/src/components/admin/shared/registry-driven-form.tsx b/src/components/admin/shared/registry-driven-form.tsx
index 64bad49d..58c0cdae 100644
--- a/src/components/admin/shared/registry-driven-form.tsx
+++ b/src/components/admin/shared/registry-driven-form.tsx
@@ -28,6 +28,7 @@ type SettingType =
| 'number'
| 'boolean'
| 'select'
+ | 'radio'
| 'url'
| 'email'
| 'textarea'
@@ -526,6 +527,36 @@ function FieldInput({
/>
);
}
+ if (entry.type === 'radio' && entry.options) {
+ // Inline radio group — clearer than a dropdown for 2–3 mutually-exclusive
+ // options where each choice has materially different consequences (e.g.
+ // auto-vs-manual signing dispatch). Falls through to plain Select when
+ // the option count grows.
+ const current = value == null ? null : String(value);
+ return (
+
+ {entry.options.map((o) => {
+ const checked = current === o.value;
+ return (
+
+ );
+ })}
+
+ );
+ }
if (entry.type === 'select' && entry.options) {
// Radix Select rejects an empty-string `value` because that's its internal
// sentinel for "cleared". Pass `undefined` instead so the placeholder
diff --git a/src/lib/settings/registry.ts b/src/lib/settings/registry.ts
index aba52898..d5e7f1f4 100644
--- a/src/lib/settings/registry.ts
+++ b/src/lib/settings/registry.ts
@@ -210,10 +210,10 @@ export const REGISTRY: SettingEntry[] = [
label: 'Initial signing-invitation email behaviour',
description:
'Auto = the system sends the branded "please sign" email immediately when an EOI/contract/reservation is generated. Manual = the document is generated and the signing URL appears in the UI; a rep clicks "Send invitation" to dispatch. Applies to all document types, not just EOI.',
- type: 'select',
+ type: 'radio',
options: [
- { value: 'manual', label: 'Manual (rep clicks Send after generation)' },
- { value: 'auto', label: 'Auto (send branded email on generate)' },
+ { value: 'manual', label: 'Manual — rep clicks Send after generation' },
+ { value: 'auto', label: 'Auto — send branded email on generate' },
],
scope: 'port',
defaultValue: 'manual',
@@ -241,11 +241,11 @@ export const REGISTRY: SettingEntry[] = [
section: 'documenso.behavior',
label: 'Signing order',
description:
- 'PARALLEL = all recipients can sign at once. SEQUENTIAL = each waits for the previous (v2 only - v1 always parallel).',
- type: 'select',
+ 'PARALLEL = all recipients can sign at once. SEQUENTIAL = each waits for the previous (v2 only — v1 always parallel).',
+ type: 'radio',
options: [
- { value: 'PARALLEL', label: 'Parallel - all recipients sign concurrently' },
- { value: 'SEQUENTIAL', label: 'Sequential - order matters (v2 only)' },
+ { value: 'PARALLEL', label: 'Parallel — all recipients sign concurrently' },
+ { value: 'SEQUENTIAL', label: 'Sequential — order matters (v2 only)' },
],
scope: 'port',
defaultValue: 'PARALLEL',
diff --git a/src/lib/settings/resolver.ts b/src/lib/settings/resolver.ts
index eaf05de4..964ac3b7 100644
--- a/src/lib/settings/resolver.ts
+++ b/src/lib/settings/resolver.ts
@@ -51,6 +51,7 @@ function defaultValidator(entry: SettingEntry): z.ZodTypeAny {
case 'boolean':
return z.coerce.boolean();
case 'select':
+ case 'radio':
if (entry.options) {
return z.enum(entry.options.map((o) => o.value) as [string, ...string[]]);
}
diff --git a/src/lib/settings/types.ts b/src/lib/settings/types.ts
index c0d1a463..91c0f7b8 100644
--- a/src/lib/settings/types.ts
+++ b/src/lib/settings/types.ts
@@ -6,6 +6,7 @@ export type SettingType =
| 'number'
| 'boolean'
| 'select'
+ | 'radio'
| 'url'
| 'email'
| 'textarea'