merge: fix/ux-leaf-f-polish into integration/ux-28-gaps (resolve OnboardingFlow+DispatchPanel in layout)
This commit is contained in:
@@ -6,7 +6,7 @@ import { useDashboardStore } from '@/lib/stores/use-dashboard-store';
|
||||
import { WidgetErrorBoundary } from '@/components/widget-error-boundary';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Settings2, LayoutGrid } from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
// Lazy load react-grid-layout (client-only, ~45KB)
|
||||
const ResponsiveGridLayout = dynamic(
|
||||
@@ -68,12 +68,14 @@ const widgetLabels: Record<string, string> = {
|
||||
'quick-capture': 'Quick Capture',
|
||||
};
|
||||
|
||||
export default function DashboardPage() {
|
||||
function DashboardPage() {
|
||||
const { widgets, setWidgets, addWidget, removeWidget } = useDashboardStore();
|
||||
const [layoutAnnouncement, setLayoutAnnouncement] = React.useState('');
|
||||
const [editMode, setEditMode] = React.useState(false);
|
||||
const [showConfig, setShowConfig] = React.useState(false);
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const domainFilter = searchParams.get('domain');
|
||||
|
||||
const layout = widgets.map((w) => ({
|
||||
i: w.id,
|
||||
@@ -113,7 +115,7 @@ export default function DashboardPage() {
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Dashboard</h1>
|
||||
<p className="mt-1 text-muted-foreground">Your day, at a glance.</p>
|
||||
<p className="mt-1 text-muted-foreground">Your day, at a glance.{domainFilter ? " (Filtered: " + domainFilter + ")" : ""}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
@@ -201,3 +203,10 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default function DashboardPageWrapper() {
|
||||
return (
|
||||
<Suspense fallback={<div className="py-12 text-center text-muted-foreground">Loading dashboard...</div>}>
|
||||
<DashboardPage />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { HabitCreateDialog } from "@/components/habits/habit-create-dialog";
|
||||
import { HabitEditDialog } from "@/components/habits/habit-edit-dialog";
|
||||
import { HabitCompletionDialog } from "@/components/habits/habit-completion-dialog";
|
||||
import { HabitCalendarHeatmap } from "@/components/habits/habit-calendar-heatmap";
|
||||
import { HabitAnalytics } from "@/components/habits/habit-analytics";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Habit {
|
||||
@@ -247,6 +248,11 @@ export default function HabitsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Analytics */}
|
||||
<div className="mt-4">
|
||||
<HabitAnalytics domainId={domainId || ""} habits={habits} />
|
||||
</div>
|
||||
|
||||
<HabitCreateDialog
|
||||
open={createOpen}
|
||||
onOpenChange={setCreateOpen}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { KeyboardShortcutsProvider } from '@/components/keyboard-shortcuts-provi
|
||||
import { WebVitalsTracker } from '@/components/web-vitals-tracker';
|
||||
import { MobileBottomNav } from '@/components/mobile-bottom-nav';
|
||||
import { DispatchPanel } from '@/components/agents/dispatch-panel';
|
||||
import { OnboardingFlow } from '@/components/onboarding/onboarding-flow';
|
||||
|
||||
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -18,7 +19,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
<Sidebar />
|
||||
<div className="flex flex-1 flex-col pb-16 md:pb-0">
|
||||
<TopBar />
|
||||
<main id="main-content" className="flex-1 overflow-auto p-6" tabIndex={-1}>
|
||||
<main id="main-content" className="flex-1 overflow-auto p-4 md:p-6" tabIndex={-1}>
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
@@ -32,6 +33,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
triggerClassName="h-12 w-12 rounded-full shadow-lg md:h-auto md:w-auto md:rounded-md md:px-4 md:py-2"
|
||||
/>
|
||||
</div>
|
||||
<OnboardingFlow />
|
||||
<div className="sr-only" aria-live="polite" aria-atomic="true" id="a11y-announcer" />
|
||||
</KeyboardShortcutsProvider>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Plus, FileText, Link2, GitBranch, Trash2, Pin, Archive, Search, PinOff,
|
||||
import dynamic from 'next/dynamic';
|
||||
import { toast } from 'sonner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { NoteTemplates } from '@/components/notes/note-templates';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -162,6 +163,28 @@ export default function NotesPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function createNoteWithContent(content: string) {
|
||||
if (!domainId) return;
|
||||
try {
|
||||
const response = await fetch("/api/domains/" + domainId + "/notes", {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
title: 'Untitled note',
|
||||
content,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) throw new Error('Unable to create note.');
|
||||
const newNote = await response.json();
|
||||
setNotes((current) => [newNote, ...current]);
|
||||
setSelectedNote(newNote);
|
||||
toast.success('Note created from template');
|
||||
} catch (error) {
|
||||
console.error('Failed to create note:', error);
|
||||
toast.error('Unable to create note');
|
||||
}
|
||||
}
|
||||
|
||||
async function createNote() {
|
||||
if (!domainId) return;
|
||||
try {
|
||||
@@ -339,6 +362,9 @@ export default function NotesPage() {
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<NoteTemplates onCreateFromTemplate={(content) => {
|
||||
createNoteWithContent(content);
|
||||
}} />
|
||||
<Button onClick={createNote}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
New note
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { SectionDialog } from "@/components/projects/section-dialog";
|
||||
import { ProjectTimeline } from "@/components/projects/project-timeline";
|
||||
import Link from "next/link";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@@ -331,6 +332,9 @@ export default function ProjectDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<ProjectTimeline sections={project.sections} projectTargetDate={project.targetDate} />
|
||||
|
||||
<SectionDialog
|
||||
open={sectionDialogOpen}
|
||||
onOpenChange={setSectionDialogOpen}
|
||||
|
||||
Reference in New Issue
Block a user