(null);
@@ -167,7 +184,32 @@ function CreateInviteModal({ onClose, onCreated }: CreateInviteModalProps) {
}
maxUsesNum = parsed;
}
- const expiresAt = expiryPreset === null ? null : Date.now() + expiryPreset;
+
+ // Resolve expiresAt from the preset selection. Custom requires a non-empty,
+ // strictly-future datetime; everything else is derived from the preset's ms offset.
+ let expiresAt: number | null;
+ if (expiryId === 'never') {
+ expiresAt = null;
+ } else if (expiryId === 'custom') {
+ if (customDateTime === '') {
+ addToast('Pick a future date & time', 'warning');
+ return;
+ }
+ const ts = new Date(customDateTime).getTime();
+ if (!Number.isFinite(ts) || ts <= Date.now()) {
+ addToast('Pick a future date & time', 'warning');
+ return;
+ }
+ expiresAt = ts;
+ } else {
+ const preset = EXPIRY_PRESETS.find((p) => p.id === expiryId);
+ if (!preset || preset.ms === null) {
+ // Unreachable: only 'never'/'custom' have null ms, and both are handled above.
+ addToast('Invalid expiry selection', 'warning');
+ return;
+ }
+ expiresAt = Date.now() + preset.ms;
+ }
setSubmitting(true);
try {
@@ -271,12 +313,12 @@ function CreateInviteModal({ onClose, onCreated }: CreateInviteModalProps) {
{EXPIRY_PRESETS.map((p) => (
))}
+ {expiryId === 'custom' && (
+ setCustomDateTime(e.target.value)}
+ min={toDatetimeLocalValue(Date.now() + 60_000)}
+ disabled={submitting}
+ className="input-standard w-full px-3 py-2 text-sm mt-2"
+ />
+ )}
{/* Actions */}