diff --git a/app/actions.tsx b/app/actions.tsx index 7871933a..50e985bf 100644 --- a/app/actions.tsx +++ b/app/actions.tsx @@ -7,7 +7,7 @@ import { getMutableAIState } from 'ai/rsc' import { CoreMessage, ToolResultPart } from 'ai' -import { nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' import type { FeatureCollection } from 'geojson' import { Spinner } from '@/components/ui/spinner' import { Section } from '@/components/section' diff --git a/app/page.tsx b/app/page.tsx index 051e54bb..e098417e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,5 @@ import { Chat } from '@/components/chat' -import {nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' import { AI } from './actions' export const maxDuration = 60 diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 012db1bd..ca2fbc6f 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -8,7 +8,7 @@ import { UserMessage } from './user-message' import { Button } from './ui/button' import { ArrowRight, Plus, Paperclip, X, Sprout } from 'lucide-react' import Textarea from 'react-textarea-autosize' -import { nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' import { useSettingsStore } from '@/lib/store/settings' import { PartialRelated } from '@/lib/schema/related' import { getSuggestions } from '@/lib/actions/suggest' diff --git a/components/header-search-button.tsx b/components/header-search-button.tsx index 1923e596..04896457 100644 --- a/components/header-search-button.tsx +++ b/components/header-search-button.tsx @@ -7,7 +7,7 @@ import { Search } from 'lucide-react' import { useMap } from './map/map-context' import { useActions, useUIState } from 'ai/rsc' import { AI } from '@/app/actions' -import { nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' import { UserMessage } from './user-message' import { toast } from 'sonner' import { useSettingsStore } from '@/lib/store/settings' diff --git a/components/history-list.tsx b/components/history-list.tsx index 5713bd2e..5b67c538 100644 --- a/components/history-list.tsx +++ b/components/history-list.tsx @@ -2,31 +2,13 @@ import React, { cache } from 'react'; import HistoryItem from './history-item'; import { ClearHistory } from './clear-history'; import { getChats } from '@/lib/actions/chat'; - -// Define the type for the chat data returned by getChats -type ChatData = { - userId: string; - id: string; - title: string; - createdAt: Date; - visibility: string | null; -}; - -// Define the Chat type expected by HistoryItem -type Chat = { - userId: string; - id: string; - title: string; - createdAt: Date; - visibility: string | null; - path: string; -}; +import type { Chat as DrizzleChat } from '@/lib/actions/chat-db'; type HistoryListProps = { userId?: string; }; -const loadChats = cache(async (userId?: string): Promise => { +const loadChats = cache(async (userId?: string): Promise => { return await getChats(userId); }); @@ -52,12 +34,12 @@ export async function HistoryList({ userId }: HistoryListProps) { No search history ) : ( - chats.map((chat: ChatData) => ( + chats.map((chat: DrizzleChat) => ( )) diff --git a/components/resolution-carousel.tsx b/components/resolution-carousel.tsx index e6fa46c8..1b28cbf7 100644 --- a/components/resolution-carousel.tsx +++ b/components/resolution-carousel.tsx @@ -12,7 +12,7 @@ import { ResolutionImage } from './resolution-image' import { Button } from './ui/button' import { useActions, useUIState } from 'ai/rsc' import { AI } from '@/app/actions' -import { nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' import { UserMessage } from './user-message' import { toast } from 'sonner' import { CompareSlider } from './compare-slider' diff --git a/components/search-related.tsx b/components/search-related.tsx index a03901c5..3dbb49c8 100644 --- a/components/search-related.tsx +++ b/components/search-related.tsx @@ -12,7 +12,7 @@ import { import { AI } from '@/app/actions' import { UserMessage } from './user-message' import { PartialRelated } from '@/lib/schema/related' -import { nanoid } from 'nanoid' +import { nanoid } from '@/lib/utils' export interface SearchRelatedProps { relatedQueries: StreamableValue diff --git a/components/settings/components/settings.tsx b/components/settings/components/settings.tsx index 0d201916..7a526e18 100644 --- a/components/settings/components/settings.tsx +++ b/components/settings/components/settings.tsx @@ -1,19 +1,18 @@ -"use client" - -import { useState, useEffect } from "react" -import { useRouter } from "next/navigation" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { z } from "zod" -import * as Tabs from "@radix-ui/react-tabs"; -import { Button } from "@/components/ui/button" -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" -import { FormProvider, UseFormReturn } from "react-hook-form"; import React from "react"; -import { Loader2, Save, RotateCcw } from "lucide-react" -import { motion, AnimatePresence } from "framer-motion" -// Or, if the file does not exist, create it as shown below. -import { SystemPromptForm } from "./system-prompt-form" -import { ModelSelectionForm } from "./model-selection-form" +'use client' + +import React, { useState, useEffect } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import * as Tabs from '@radix-ui/react-tabs' +import { useRouter } from 'next/navigation' +import { zodResolver } from '@hookform/resolvers/zod' +import { useForm } from 'react-hook-form' +import * as z from 'zod' +import { Loader2, Save, RotateCcw } from 'lucide-react' + +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' +import { SystemPromptForm } from './system-prompt-form' +import { ModelSelectionForm } from './model-selection-form' import { UserManagementForm } from './user-management-form'; import { Form } from "@/components/ui/form" import { useSettingsStore, MapProvider } from "@/lib/store/settings"; @@ -22,8 +21,10 @@ import { Label } from "@/components/ui/label"; import { useToast } from "@/components/ui/hooks/use-toast" import { getSystemPrompt, saveSystemPrompt } from "../../../lib/actions/chat" import { getSelectedModel, saveSelectedModel } from "../../../lib/actions/users" +import { useCurrentUser } from "@/lib/auth/use-current-user" +import { SettingsSkeleton } from './settings-skeleton' -// Define the form schema +// Define the form schema with enum validation for roles const settingsFormSchema = z.object({ systemPrompt: z .string() @@ -43,7 +44,7 @@ const settingsFormSchema = z.object({ role: z.enum(["admin", "editor", "viewer"]), }), ), - newUserEmail: z.string().email().optional(), + newUserEmail: z.string().email().optional().or(z.literal('')), newUserRole: z.enum(["admin", "editor", "viewer"]).optional(), }) @@ -54,10 +55,7 @@ const defaultValues: Partial = { systemPrompt: "You are a planetary copilot, an AI assistant designed to help users with information about planets, space exploration, and astronomy. Provide accurate, educational, and engaging responses about our solar system and beyond.", selectedModel: "Grok 4.2", - users: [ - { id: "1", email: "admin@example.com", role: "admin" }, - { id: "2", email: "user@example.com", role: "editor" }, - ], + users: [], } interface SettingsProps { @@ -67,16 +65,16 @@ interface SettingsProps { export function Settings({ initialTab = "system-prompt" }: SettingsProps) { const { toast } = useToast() const router = useRouter() - const [isLoading, setIsLoading] = useState(false) + const [isSaving, setIsSaving] = useState(false) const [currentTab, setCurrentTab] = useState(initialTab); const { mapProvider, setMapProvider } = useSettingsStore(); + const { user, loading: authLoading } = useCurrentUser(); useEffect(() => { setCurrentTab(initialTab); }, [initialTab]); - // TODO: Replace 'anonymous' with actual user ID from session/auth context - const userId = 'anonymous'; + const userId = user?.id; const form = useForm({ resolver: zodResolver(settingsFormSchema), @@ -85,6 +83,8 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { useEffect(() => { async function fetchData() { + if (!userId || authLoading) return; + const [existingPrompt, selectedModel] = await Promise.all([ getSystemPrompt(userId), getSelectedModel(), @@ -98,10 +98,23 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { } } fetchData(); - }, [form, userId]); + }, [form, userId, authLoading]); + + if (authLoading) { + return ; + } async function onSubmit(data: SettingsFormValues) { - setIsLoading(true) + if (!userId) { + toast({ + title: "Error", + description: "You must be logged in to save settings.", + variant: "destructive", + }); + return; + } + + setIsSaving(true) try { // Save the system prompt and selected model @@ -124,9 +137,6 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { title: "Settings updated", description: "Your settings have been saved successfully.", }) - - // Refresh the page to reflect changes - // router.refresh(); // Consider if refresh is needed or if optimistic update is enough } catch (error: any) { // Error notification toast({ @@ -135,7 +145,7 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { variant: "destructive", }) } finally { - setIsLoading(false) + setIsSaving(false) } } @@ -223,12 +233,12 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { - -