feat: journal dashboard widgets, agent polish, and edit-mode live previews
CI / checks (push) Failing after 2m7s
CI / build (push) Successful in 4m36s

Journal dashboard widgets and quick-add; rich-text quick-add dialogs.

Dashboard draft sync for live edit previews; assistant bubble + API tools.

Journal UX: stress slider, mood grid, query cap fix.
This commit is contained in:
ginnoir
2026-07-04 22:03:45 -05:00
parent 4a924a4107
commit a09747c314
76 changed files with 4594 additions and 611 deletions
@@ -8,7 +8,9 @@ import listPlugin from "@fullcalendar/list";
import type { DateSelectArg, EventClickArg, EventDropArg } from "@fullcalendar/core";
import type { EventResizeDoneArg } from "@fullcalendar/interaction";
import { CalendarPlus, Check, Eye, EyeOff, Plus, Trash2, X } from "lucide-react";
import dynamic from "next/dynamic";
import { useMemo, useState, useEffect, useTransition } from "react";
import { richTextToPlainText } from "@/components/rich-text";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -34,6 +36,18 @@ import {
updateEvent,
} from "../server/actions";
const RichTextEditor = dynamic(
() => import("@/components/rich-text/rich-text-editor").then((mod) => mod.RichTextEditor),
{
ssr: false,
loading: () => (
<div className="min-h-24 rounded-lg border border-input px-3 py-2 text-sm text-muted-foreground animate-pulse">
Loading editor
</div>
),
},
);
type EventDraft = {
id?: string;
calendarId: string;
@@ -180,7 +194,7 @@ export function CalendarShell({
endAt: new Date(selectedEvent.endAt),
allDay: selectedEvent.allDay,
location: selectedEvent.location || null,
notes: selectedEvent.notes || null,
notes: richTextToPlainText(selectedEvent.notes) ? selectedEvent.notes : null,
};
startTransition(async () => {
@@ -513,13 +527,17 @@ export function CalendarShell({
value={selectedEvent.location}
onChange={(event) => setSelectedEvent({ ...selectedEvent, location: event.target.value })}
/>
<Label htmlFor="event-notes">Notes</Label>
<textarea
id="event-notes"
className="min-h-20 rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
value={selectedEvent.notes}
onChange={(event) => setSelectedEvent({ ...selectedEvent, notes: event.target.value })}
/>
<div className="space-y-1.5 min-w-0">
<Label htmlFor="event-notes">Notes</Label>
<RichTextEditor
id="event-notes"
aria-label="Notes"
value={selectedEvent.notes}
onChange={(notes) => setSelectedEvent({ ...selectedEvent, notes })}
disabled={isPending}
placeholder="Add details…"
/>
</div>
{!selectedEvent.id && (
<label className="flex items-center gap-2 text-sm">
<input
@@ -696,7 +714,7 @@ export function CalendarShell({
style={{
borderRadius: isMobile ? "var(--r-xl) var(--r-xl) 0 0" : "var(--r-lg)",
width: isMobile ? "100%" : undefined,
maxWidth: isMobile ? undefined : "512px",
maxWidth: isMobile ? undefined : "672px",
maxHeight: "90dvh",
padding: "1rem",
paddingBottom: isMobile ? "calc(1rem + env(safe-area-inset-bottom))" : "1rem",
@@ -1,4 +1,5 @@
import { Calendar as CalendarIcon, MapPin } from "lucide-react";
import { RichTextContent } from "@/components/rich-text";
import type { CalendarShareData, EventShareData } from "../server/share-queries";
import { ShareEyebrow } from "@/components/share/share-eyebrow";
import { MiniDayCard } from "@/components/share/mini-day-card";
@@ -92,7 +93,7 @@ export function EventSharedView({ data }: { data: EventShareData }) {
</ShareDetailCard>
{data.notes && (
<p
<div
className="serif"
style={{
fontSize: 16,
@@ -103,11 +104,10 @@ export function EventSharedView({ data }: { data: EventShareData }) {
padding: "18px 22px",
margin: "14px 0 24px",
textWrap: "pretty",
whiteSpace: "pre-wrap",
}}
>
{data.notes}
</p>
<RichTextContent html={data.notes} />
</div>
)}
</>
);