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
2 changes: 1 addition & 1 deletion app/(protected)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Separator } from "@/components/ui/separator"
import { HabitIcon } from "@/components/habit-icon"
import { useAuth } from "@/components/auth-provider"
import { useAppState } from "@/hooks/use-store"
import { addHabit, removeHabit, setNotificationTime } from "@/lib/store"
import { addHabit, removeHabit, setNotificationTime } from "@/lib/store/actions"
import { LIMITS } from "@/lib/domain/validation"
import {
useNotificationPermission,
Expand Down
2 changes: 1 addition & 1 deletion components/evening/step-habits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { useAppState } from "@/hooks/use-store"
import { Check } from "lucide-react"

Expand Down
2 changes: 1 addition & 1 deletion components/evening/step-journal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { useAppState } from "@/hooks/use-store"
import { getTodayKey } from "@/lib/time/today"
import { LIMITS, countWords } from "@/lib/domain/validation"
Expand Down
8 changes: 5 additions & 3 deletions components/evening/step-sleep-target.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Slider } from "@/components/ui/slider"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { AnchorMotif } from "@/components/anchor-motif"

interface StepSleepTargetProps {
Expand All @@ -14,7 +14,9 @@ interface StepSleepTargetProps {
const BEDTIME_OPTIONS = ["21:00", "21:30", "22:00", "22:30", "23:00", "23:30", "00:00", "00:30", "01:00"]

function formatTime(t: string) {
const [h, m] = t.split(":").map(Number)
const parts = t.split(":").map(Number)
const h = parts[0] ?? 0
const m = parts[1] ?? 0
const period = h < 12 ? "AM" : "PM"
const hour = h === 0 ? 12 : h > 12 ? h - 12 : h
return `${hour}:${String(m).padStart(2, "0")} ${period}`
Expand Down Expand Up @@ -73,7 +75,7 @@ export function StepSleepTarget({ onNext, onBack }: StepSleepTargetProps) {
max={10}
step={0.5}
value={[hours]}
onValueChange={([v]) => setHours(v)}
onValueChange={([v]) => setHours(v ?? 8)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Route slider updates through store actions instead of component state setters.

This still mutates component state directly in a .tsx component. Move this through lib/store/actions per project rule.

As per coding guidelines, "**/*.{tsx,jsx}: Mutate state only via lib/store/actions — never use setState directly in components".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/evening/step-sleep-target.tsx` at line 78, The onValueChange
handler currently calls the component state setter setHours directly; replace
that direct state mutation by dispatching the appropriate store action from
lib/store/actions instead: create or use an action (e.g., setSleepHours or
updateHours) and import it into this component, then change onValueChange={([v])
=> setHours(v ?? 8)} to call the action dispatcher with the new value (v ?? 8)
so all state updates flow through the store actions rather than setHours in the
TSX component.

Source: Coding guidelines

/>
<div className="flex justify-between text-xs text-muted-foreground">
<span>5h</span>
Expand Down
2 changes: 1 addition & 1 deletion components/morning/step-intention.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { LIMITS } from "@/lib/domain/validation"

const PROMPTS = [
Expand Down
2 changes: 1 addition & 1 deletion components/morning/step-meditation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from "react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { motion } from "framer-motion"
import { Pause, Play } from "lucide-react"

Expand Down
8 changes: 5 additions & 3 deletions components/morning/step-mood.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState, useRef } from "react"
import { Button } from "@/components/ui/button"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { type MoodPoint } from "@/lib/domain/entry"

interface StepMoodProps {
Expand All @@ -29,8 +29,10 @@ export function StepMood({ onNext, onBack, isMorning = true }: StepMoodProps) {
let clientX: number, clientY: number

if ("touches" in e) {
clientX = e.touches[0].clientX
clientY = e.touches[0].clientY
const touch = e.touches[0]
if (!touch) return
clientX = touch.clientX
clientY = touch.clientY
} else {
clientX = e.clientX
clientY = e.clientY
Expand Down
4 changes: 2 additions & 2 deletions components/morning/step-sleep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Slider } from "@/components/ui/slider"
import { cn } from "@/lib/utils"
import { updateTodayEntry } from "@/lib/store"
import { updateTodayEntry } from "@/lib/store/actions"
import { type SleepQuality } from "@/lib/domain/entry"

const SLEEP_OPTIONS: { value: SleepQuality; label: string; glyph: string }[] = [
Expand Down Expand Up @@ -73,7 +73,7 @@ export function StepSleep({ onNext, onBack }: StepSleepProps) {
max={12}
step={0.5}
value={[hours]}
onValueChange={([v]) => setHours(v)}
onValueChange={([v]) => setHours(v ?? 8)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Use lib/store/actions for this update instead of direct state setter.

The handler still updates local component state directly in .tsx. Please route this through store actions to match repository rules.

As per coding guidelines, "**/*.{tsx,jsx}: Mutate state only via lib/store/actions — never use setState directly in components".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/morning/step-sleep.tsx` at line 75, The onValueChange handler is
directly calling the local setter setHours; replace this with the appropriate
action from lib/store/actions: import the store action (e.g., the action that
updates sleep hours) and call that action with (v ?? 8) instead of calling
setHours. Update the onValueChange callback in components/morning/step-sleep.tsx
to dispatch/use the action from lib/store/actions (referencing the existing
setHours usage to locate the spot) and remove the direct setHours call so the
component mutates state only via lib/store/actions.

Source: Coding guidelines

className="py-1"
/>
<div className="flex justify-between text-xs text-muted-foreground">
Expand Down
4 changes: 2 additions & 2 deletions lib/auth/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export type FieldError = string | null

export function validateEmail(value: string): FieldError {
const result = EmailSchema.safeParse(value)
return result.success ? null : result.error.issues[0].message
return result.success ? null : result.error.issues[0]?.message ?? null
}

export function validatePassword(value: string): FieldError {
const result = PasswordSchema.safeParse(value)
return result.success ? null : result.error.issues[0].message
return result.success ? null : result.error.issues[0]?.message ?? null
}
13 changes: 8 additions & 5 deletions lib/store/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach } from "vitest"
import { getSnapshot, setState } from "./store"
import { addHabit, removeHabit, updateTodayEntry, setNotificationTime } from "./actions"
import { INITIAL_STATE } from "./state"
import { getTodayKey } from "@/lib/time/today"

beforeEach(() => {
setState(() => INITIAL_STATE)
Expand All @@ -11,17 +12,19 @@ describe("updateTodayEntry", () => {
it("creates today's entry from patch", () => {
updateTodayEntry({ intention: "Focus" })
const s = getSnapshot()
const key = Object.keys(s.entries)[0]
expect(s.entries[key].intention).toBe("Focus")
const entry = s.entries[getTodayKey()]
expect(entry).toBeDefined()
expect(entry?.intention).toBe("Focus")
})

it("merges into existing entry", () => {
updateTodayEntry({ intention: "A" })
updateTodayEntry({ journal: "Went well" })
const s = getSnapshot()
const key = Object.keys(s.entries)[0]
expect(s.entries[key].intention).toBe("A")
expect(s.entries[key].journal).toBe("Went well")
const entry = s.entries[getTodayKey()]
expect(entry).toBeDefined()
expect(entry?.intention).toBe("A")
expect(entry?.journal).toBe("Went well")
})
})

Expand Down
Loading
Loading