Award shortlist UX improvements + configurable invite link expiry
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m30s

Award shortlist:
- Expandable reasoning text (click to toggle, hover hint)
- Bulk select/deselect all checkbox in header
- Top N projects highlighted with amber background
- New bulkToggleShortlisted backend mutation

Invite link expiry:
- New "Invitation Link Expiry (hours)" field in Security Settings
- Reads from systemSettings `invite_link_expiry_hours` (default 72h / 3 days)
- Email template dynamically shows "X hours" or "X days" based on setting
- All 3 invite paths (bulk create, single invite, bulk resend) use setting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 22:05:58 +01:00
parent 8a7da0fd93
commit d02b0b91b9
6 changed files with 156 additions and 24 deletions

View File

@@ -21,6 +21,7 @@ import {
const formSchema = z.object({
session_duration_hours: z.string().regex(/^\d+$/, 'Must be a number'),
magic_link_expiry_minutes: z.string().regex(/^\d+$/, 'Must be a number'),
invite_link_expiry_hours: z.string().regex(/^\d+$/, 'Must be a number'),
rate_limit_requests_per_minute: z.string().regex(/^\d+$/, 'Must be a number'),
})
@@ -30,6 +31,7 @@ interface SecuritySettingsFormProps {
settings: {
session_duration_hours?: string
magic_link_expiry_minutes?: string
invite_link_expiry_hours?: string
rate_limit_requests_per_minute?: string
}
}
@@ -42,6 +44,7 @@ export function SecuritySettingsForm({ settings }: SecuritySettingsFormProps) {
defaultValues: {
session_duration_hours: settings.session_duration_hours || '24',
magic_link_expiry_minutes: settings.magic_link_expiry_minutes || '15',
invite_link_expiry_hours: settings.invite_link_expiry_hours || '72',
rate_limit_requests_per_minute: settings.rate_limit_requests_per_minute || '60',
},
})
@@ -61,6 +64,7 @@ export function SecuritySettingsForm({ settings }: SecuritySettingsFormProps) {
settings: [
{ key: 'session_duration_hours', value: data.session_duration_hours },
{ key: 'magic_link_expiry_minutes', value: data.magic_link_expiry_minutes },
{ key: 'invite_link_expiry_hours', value: data.invite_link_expiry_hours },
{ key: 'rate_limit_requests_per_minute', value: data.rate_limit_requests_per_minute },
],
})
@@ -105,6 +109,24 @@ export function SecuritySettingsForm({ settings }: SecuritySettingsFormProps) {
)}
/>
<FormField
control={form.control}
name="invite_link_expiry_hours"
render={({ field }) => (
<FormItem>
<FormLabel>Invitation Link Expiry (hours)</FormLabel>
<FormControl>
<Input type="number" min="1" max="720" placeholder="72" {...field} />
</FormControl>
<FormDescription>
How long invitation links sent to new users remain valid.
Default: 72 hours (3 days). Maximum: 720 hours (30 days).
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="rate_limit_requests_per_minute"

View File

@@ -115,6 +115,7 @@ export function SettingsContent({ initialSettings, isSuperAdmin = true }: Settin
const securitySettings = getSettingsByKeys([
'session_duration_hours',
'magic_link_expiry_minutes',
'invite_link_expiry_hours',
'rate_limit_requests_per_minute',
])