fix: 'Pause sending by' is off by default everywhere
The optional 'Pause sending by' deadline was defaulting to 18 (= 6 PM)
in three places:
- reminders.delivery_window_end_hour schema default (NOT NULL DEFAULT 18)
- createReminderAction / editScheduleAction fallback when the field
is missing on the input
- the Zod refine validator's secondary fallback
Net effect: any reminder created before this change has 18 in the DB,
so the edit form's checkbox flips ON automatically (the wizard treats
'value !== undefined && value !== 24' as 'opted in'). The wizard's
own create flow always sends 24 explicitly when the box is unchecked
— but legacy / direct API payloads + the schema default for older rows
don't carry that intent through.
Switch every default to 24 (the off-sentinel the wizard already uses)
so the optional toggle stays off until the operator ticks it. New
migration 0012 also backfills existing rows from 18 → 24 so editing
old reminders no longer auto-checks 'Pause sending by'.
Tests in when-form-deadline.test.tsx already lock in the UI contract
(off when initialDeliveryEndHour is undefined or 24, on for any other
value). No assertion changes needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5c48e0e85f
commit
e800882d15
@ -271,7 +271,7 @@ const createReminderSchema = z
|
|||||||
path: ["messages"],
|
path: ["messages"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.refine((d) => (d.deliveryWindowStartHour ?? 6) < (d.deliveryWindowEndHour ?? 18), {
|
.refine((d) => (d.deliveryWindowStartHour ?? 6) < (d.deliveryWindowEndHour ?? 24), {
|
||||||
message: "Delivery window start must be earlier than end",
|
message: "Delivery window start must be earlier than end",
|
||||||
path: ["deliveryWindowStartHour"],
|
path: ["deliveryWindowStartHour"],
|
||||||
});
|
});
|
||||||
@ -328,7 +328,11 @@ export async function createReminderAction(
|
|||||||
timezone,
|
timezone,
|
||||||
} = parsed.data;
|
} = parsed.data;
|
||||||
const deliveryWindowStartHour = parsed.data.deliveryWindowStartHour ?? 6;
|
const deliveryWindowStartHour = parsed.data.deliveryWindowStartHour ?? 6;
|
||||||
const deliveryWindowEndHour = parsed.data.deliveryWindowEndHour ?? 18;
|
// 24 = "no deadline" (off). The wizard sends 24 explicitly when the
|
||||||
|
// operator hasn't ticked the optional "Pause sending by" checkbox;
|
||||||
|
// fall back to 24 here so legacy payloads / direct API calls don't
|
||||||
|
// accidentally enable the deadline at 6pm.
|
||||||
|
const deliveryWindowEndHour = parsed.data.deliveryWindowEndHour ?? 24;
|
||||||
const parts = resolveMessageParts(parsed.data);
|
const parts = resolveMessageParts(parsed.data);
|
||||||
|
|
||||||
const op = await getSeededOperator();
|
const op = await getSeededOperator();
|
||||||
@ -442,7 +446,11 @@ export async function updateReminderAction(
|
|||||||
timezone,
|
timezone,
|
||||||
} = parsed.data;
|
} = parsed.data;
|
||||||
const deliveryWindowStartHour = parsed.data.deliveryWindowStartHour ?? 6;
|
const deliveryWindowStartHour = parsed.data.deliveryWindowStartHour ?? 6;
|
||||||
const deliveryWindowEndHour = parsed.data.deliveryWindowEndHour ?? 18;
|
// 24 = "no deadline" (off). The wizard sends 24 explicitly when the
|
||||||
|
// operator hasn't ticked the optional "Pause sending by" checkbox;
|
||||||
|
// fall back to 24 here so legacy payloads / direct API calls don't
|
||||||
|
// accidentally enable the deadline at 6pm.
|
||||||
|
const deliveryWindowEndHour = parsed.data.deliveryWindowEndHour ?? 24;
|
||||||
const parts = resolveMessageParts(parsed.data);
|
const parts = resolveMessageParts(parsed.data);
|
||||||
|
|
||||||
const op = await getSeededOperator();
|
const op = await getSeededOperator();
|
||||||
|
|||||||
10
packages/db/migrations/0012_lucky_masked_marvel.sql
Normal file
10
packages/db/migrations/0012_lucky_masked_marvel.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- Switch the default to 24 ("no deadline" sentinel) so newly-created
|
||||||
|
-- reminders are off-by-default for the optional "Pause sending by"
|
||||||
|
-- toggle, matching the wizard's UX contract.
|
||||||
|
ALTER TABLE "reminders" ALTER COLUMN "delivery_window_end_hour" SET DEFAULT 24;
|
||||||
|
-- Existing rows still hold the old default (18). Treat those as
|
||||||
|
-- "schema-default, never opted in by the operator" and clear them to
|
||||||
|
-- 24 so editing an old reminder doesn't auto-check the deadline box.
|
||||||
|
-- Operators who actually wanted a 6pm deadline can re-enable it from
|
||||||
|
-- the edit form.
|
||||||
|
UPDATE "reminders" SET "delivery_window_end_hour" = 24 WHERE "delivery_window_end_hour" = 18;
|
||||||
1050
packages/db/migrations/meta/0012_snapshot.json
Normal file
1050
packages/db/migrations/meta/0012_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,90 +1,97 @@
|
|||||||
{
|
{
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778311164225,
|
"when": 1778311164225,
|
||||||
"tag": "0000_conscious_tarantula",
|
"tag": "0000_conscious_tarantula",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778320434707,
|
"when": 1778320434707,
|
||||||
"tag": "0001_smart_vertigo",
|
"tag": "0001_smart_vertigo",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778338808600,
|
"when": 1778338808600,
|
||||||
"tag": "0002_left_jimmy_woo",
|
"tag": "0002_left_jimmy_woo",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 3,
|
"idx": 3,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778343712901,
|
"when": 1778343712901,
|
||||||
"tag": "0003_messy_bruce_banner",
|
"tag": "0003_messy_bruce_banner",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 4,
|
"idx": 4,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778345543406,
|
"when": 1778345543406,
|
||||||
"tag": "0004_next_prowler",
|
"tag": "0004_next_prowler",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 5,
|
"idx": 5,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778347437350,
|
"when": 1778347437350,
|
||||||
"tag": "0005_flippant_joystick",
|
"tag": "0005_flippant_joystick",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 6,
|
"idx": 6,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778385559051,
|
"when": 1778385559051,
|
||||||
"tag": "0006_adorable_nehzno",
|
"tag": "0006_adorable_nehzno",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 7,
|
"idx": 7,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778386591494,
|
"when": 1778386591494,
|
||||||
"tag": "0007_overconfident_menace",
|
"tag": "0007_overconfident_menace",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 8,
|
"idx": 8,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778395584234,
|
"when": 1778395584234,
|
||||||
"tag": "0008_greedy_matthew_murdock",
|
"tag": "0008_greedy_matthew_murdock",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 9,
|
"idx": 9,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778464000000,
|
"when": 1778464000000,
|
||||||
"tag": "0009_rename_ended_to_inactive",
|
"tag": "0009_rename_ended_to_inactive",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 10,
|
"idx": 10,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778464001000,
|
"when": 1778464001000,
|
||||||
"tag": "0010_fancy_wolf_cub",
|
"tag": "0010_fancy_wolf_cub",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 11,
|
"idx": 11,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1778464002000,
|
"when": 1778464002000,
|
||||||
"tag": "0011_premium_grandmaster",
|
"tag": "0011_premium_grandmaster",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
"idx": 12,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1778412502601,
|
||||||
|
"tag": "0012_lucky_masked_marvel",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@ -92,8 +92,11 @@ export const reminders = pgTable("reminders", {
|
|||||||
// Delivery window (operator timezone). End hour is enforced at runtime
|
// Delivery window (operator timezone). End hour is enforced at runtime
|
||||||
// by fire-reminder when window enforcement lands; start hour is documented
|
// by fire-reminder when window enforcement lands; start hour is documented
|
||||||
// here but not gated in v1.
|
// here but not gated in v1.
|
||||||
|
// 24 is the "no deadline" sentinel — it's the off-by-default state so a
|
||||||
|
// reminder created without the operator explicitly opting into "Pause
|
||||||
|
// sending by" stays unbounded.
|
||||||
deliveryWindowStartHour: integer("delivery_window_start_hour").notNull().default(6),
|
deliveryWindowStartHour: integer("delivery_window_start_hour").notNull().default(6),
|
||||||
deliveryWindowEndHour: integer("delivery_window_end_hour").notNull().default(18),
|
deliveryWindowEndHour: integer("delivery_window_end_hour").notNull().default(24),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const reminderTargets = pgTable(
|
export const reminderTargets = pgTable(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user