feat(recurrence): Daily tab is "every day at <time>" — drop weekday choice
The Daily tab had two radios: "Every day" vs "Every weekday (Mon–Fri)".
That's confusing — Mon-Fri-only is a weekly pattern, not a daily one,
and it overlapped exactly with what the Weekly tab can already do
(select Mon, Tue, Wed, Thu, Fri).
So Daily now means literally every day. The tab body is just the time
picker plus a one-liner ("Fires once a day at the time below.").
Legacy reminders that stored "MM HH * * 1-5" still load fine — the
parser maps any DOW list (including the 1-5 range) onto a Weekly draft
with Mon-Fri pre-selected. So a saved "weekday" daily reminder shows
up as Weekly with the right days checked, no data loss.
RadioRow component went unused after this — removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08435988c2
commit
657fa71bf9
@ -34,7 +34,6 @@ type RuleType = "daily" | "weekly" | "monthly" | "yearly";
|
|||||||
|
|
||||||
interface Draft {
|
interface Draft {
|
||||||
type: RuleType;
|
type: RuleType;
|
||||||
dailyMode: "every_day" | "weekdays";
|
|
||||||
/** Cron weekday list (0=Sun..6=Sat). */
|
/** Cron weekday list (0=Sun..6=Sat). */
|
||||||
weekdays: number[];
|
weekdays: number[];
|
||||||
monthDay: number;
|
monthDay: number;
|
||||||
@ -50,7 +49,6 @@ const MAX_RULES = 8;
|
|||||||
function defaultDraft(firstFire: DateTime): Draft {
|
function defaultDraft(firstFire: DateTime): Draft {
|
||||||
return {
|
return {
|
||||||
type: "daily",
|
type: "daily",
|
||||||
dailyMode: "every_day",
|
|
||||||
weekdays: [isoWeekdayToCron(firstFire.weekday)],
|
weekdays: [isoWeekdayToCron(firstFire.weekday)],
|
||||||
monthDay: firstFire.day,
|
monthDay: firstFire.day,
|
||||||
month: firstFire.month,
|
month: firstFire.month,
|
||||||
@ -87,7 +85,7 @@ function draftToCron(d: Draft): string | null {
|
|||||||
const h = clamp(d.hour, 0, 23);
|
const h = clamp(d.hour, 0, 23);
|
||||||
switch (d.type) {
|
switch (d.type) {
|
||||||
case "daily":
|
case "daily":
|
||||||
return d.dailyMode === "weekdays" ? `${m} ${h} * * 1-5` : `${m} ${h} * * *`;
|
return `${m} ${h} * * *`;
|
||||||
case "weekly":
|
case "weekly":
|
||||||
if (!d.weekdays.length) return null;
|
if (!d.weekdays.length) return null;
|
||||||
return `${m} ${h} * * ${d.weekdays.slice().sort((a, b) => a - b).join(",")}`;
|
return `${m} ${h} * * ${d.weekdays.slice().sort((a, b) => a - b).join(",")}`;
|
||||||
@ -102,9 +100,7 @@ function describeDraft(d: Draft): string {
|
|||||||
const t = `${pad2(clamp(d.hour, 0, 23))}:${pad2(clamp(d.minute, 0, 59))}`;
|
const t = `${pad2(clamp(d.hour, 0, 23))}:${pad2(clamp(d.minute, 0, 59))}`;
|
||||||
switch (d.type) {
|
switch (d.type) {
|
||||||
case "daily":
|
case "daily":
|
||||||
return d.dailyMode === "weekdays"
|
return `Every day at ${t}`;
|
||||||
? `Every weekday at ${t}`
|
|
||||||
: `Every day at ${t}`;
|
|
||||||
case "weekly": {
|
case "weekly": {
|
||||||
if (!d.weekdays.length) return "Pick at least one weekday";
|
if (!d.weekdays.length) return "Pick at least one weekday";
|
||||||
const labels = d.weekdays
|
const labels = d.weekdays
|
||||||
@ -137,12 +133,11 @@ function draftFromCronExpr(expr: string, firstFire: DateTime): Draft {
|
|||||||
const rest = head[3]!.trim();
|
const rest = head[3]!.trim();
|
||||||
|
|
||||||
let m: RegExpMatchArray | null;
|
let m: RegExpMatchArray | null;
|
||||||
if (rest === "* * 1-5") {
|
|
||||||
return { ...base, type: "daily", dailyMode: "weekdays", hour, minute };
|
|
||||||
}
|
|
||||||
if (rest === "* * *") {
|
if (rest === "* * *") {
|
||||||
return { ...base, type: "daily", dailyMode: "every_day", hour, minute };
|
return { ...base, type: "daily", hour, minute };
|
||||||
}
|
}
|
||||||
|
// Any DOW list (including the legacy "1-5" weekday-only daily rule)
|
||||||
|
// round-trips as a Weekly draft.
|
||||||
if ((m = rest.match(/^\* \* ([0-9,\-]+)$/))) {
|
if ((m = rest.match(/^\* \* ([0-9,\-]+)$/))) {
|
||||||
const days = m[1]!
|
const days = m[1]!
|
||||||
.split(",")
|
.split(",")
|
||||||
@ -357,18 +352,9 @@ function RuleEditor({ draft, onChange }: RuleEditorProps) {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="daily" className="space-y-3 pt-3">
|
<TabsContent value="daily" className="space-y-3 pt-3">
|
||||||
<RadioRow
|
<p className="text-xs text-muted-foreground">
|
||||||
name={`daily-${draft.type}`}
|
Fires once a day at the time below.
|
||||||
checked={draft.dailyMode === "every_day"}
|
</p>
|
||||||
onChange={() => onChange({ dailyMode: "every_day" })}
|
|
||||||
label="Every day"
|
|
||||||
/>
|
|
||||||
<RadioRow
|
|
||||||
name={`daily-${draft.type}`}
|
|
||||||
checked={draft.dailyMode === "weekdays"}
|
|
||||||
onChange={() => onChange({ dailyMode: "weekdays" })}
|
|
||||||
label="Every weekday (Mon – Fri)"
|
|
||||||
/>
|
|
||||||
<TimeField draft={draft} onChange={onChange} />
|
<TimeField draft={draft} onChange={onChange} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
@ -485,24 +471,3 @@ function TimeField({ draft, onChange }: TimeFieldProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RadioRowProps {
|
|
||||||
name: string;
|
|
||||||
checked: boolean;
|
|
||||||
onChange: () => void;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function RadioRow({ name, checked, onChange, label }: RadioRowProps) {
|
|
||||||
return (
|
|
||||||
<label className="flex items-center gap-2 cursor-pointer">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name={name}
|
|
||||||
checked={checked}
|
|
||||||
onChange={onChange}
|
|
||||||
className="size-4 accent-primary"
|
|
||||||
/>
|
|
||||||
<span className="text-sm">{label}</span>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user