Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions frontend/src/components/Routine/WeeklyGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,22 @@ function DroppableCell({ day, time, tasks, onDeleteTask }) {
}

/* ---------------- Weekly Grid ---------------- */
export default function WeeklyGrid({ scheduledTasks, onSaveDay, onDeleteTask, innerRef }) {
export default function WeeklyGrid({ scheduledTasks, onSaveDay, onDeleteTask, innerRef, highlight }) {
return (
<div className="card card-primary !pl-2.5 !pr-2.5 !py-3 animate-in" ref={innerRef}>
<h2 className="text-lg font-semibold text-main mb-4 px-6.5 pt-3">Weekly Schedule</h2>
<div
className={`card card-primary !pl-2.5 !pr-2.5 !py-3 animate-in transition-shadow duration-500 ${
highlight ? "ring-2 ring-cyan-400 shadow-[0_0_0_6px_rgba(34,211,238,0.15)]" : ""
}`}
ref={innerRef}
>
<h2 className="text-lg font-semibold text-main mb-1 px-6.5 pt-3">Weekly Schedule</h2>
{highlight ? (
<p className="text-xs text-cyan-600 dark:text-cyan-400 px-6.5 mb-3 animate-pulse">
Drag a task from your library onto the grid to start building a routine
</p>
) : (
<div className="mb-3" />
)}

<div
className="grid w-full overflow-x-auto sm:overflow-visible"
Expand Down
34 changes: 29 additions & 5 deletions frontend/src/pages/RoutineBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function RoutineBuilder() {
const [isImageExporting, setIsImageExporting] = useState(false);
const [isTemplateModalOpen, setIsTemplateModalOpen] = useState(false);
const [selectedTemplateDay, setSelectedTemplateDay] = useState("Monday");
const [highlightGrid, setHighlightGrid] = useState(false);
const gridRef = useRef(null);


Expand Down Expand Up @@ -80,6 +81,27 @@ export default function RoutineBuilder() {

const handleOpenModal = useScrollThenOpen(openModal, 0);

// Triggered by the "Saved Routines" empty state CTA.
// A routine is built by scheduling existing tasks onto the grid, so this
// should never open the Task form — that form already has its own CTA in
// the Task Library's empty state. Instead:
// - if the library already has tasks, scroll up and pulse the grid so the
// user sees where to drag tasks from
// - if the library is empty too, open the Task form, since the user needs
// at least one task before they can schedule anything
const pulseGrid = useScrollThenOpen(() => {
setHighlightGrid(true);
setTimeout(() => setHighlightGrid(false), 1600);
}, 0);

const handleStartRoutine = useCallback(() => {
if (tasks?.length > 0) {
pulseGrid();
} else {
handleOpenModal();
}
}, [tasks, pulseGrid, handleOpenModal]);

const handleSubmit = async (data) => {
try {
await addTask({ ...data, status: "Due" });
Expand Down Expand Up @@ -294,6 +316,7 @@ export default function RoutineBuilder() {
onSaveDay={openSaveRoutineModal}
onDeleteTask={removeScheduledTask}
innerRef={gridRef}
highlight={highlightGrid}
/>
</section>
</div>
Expand All @@ -308,13 +331,14 @@ export default function RoutineBuilder() {
<p className="text-sm text-muted">Loading routines…</p>
) : savedRoutines.length === 0 ? (
/*
* EmptyState is deep in the page — clicking "Create Your First
* Routine" here triggers handleOpenModal, which scrolls to the
* top first, then opens the modal once the scroll settles.
* EmptyState is deep in the page. Clicking "Create Your First
* Routine" should guide the user toward the actual routine flow
* (drag a task onto the weekly grid), not open the Task form —
* that form belongs to the Task Library's own empty state.
*/
<EmptyState
type="routines"
onAction={handleOpenModal}
onAction={handleStartRoutine}
/>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
Expand Down Expand Up @@ -434,4 +458,4 @@ export default function RoutineBuilder() {
</div>
</DndContext>
);
}
}
Loading