diff --git a/apps/web/components/memories-grid.tsx b/apps/web/components/memories-grid.tsx index 795056cf..ce947181 100644 --- a/apps/web/components/memories-grid.tsx +++ b/apps/web/components/memories-grid.tsx @@ -6,6 +6,7 @@ import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api" import { useInfiniteQuery, useQuery } from "@tanstack/react-query" import { useCallback, memo, useMemo, useState, useRef, useEffect } from "react" import { useQueryState } from "nuqs" +import { AnimatePresence } from "motion/react" import type { z } from "zod" import { Masonry, useInfiniteLoader } from "masonic" import { dmSansClassName } from "@/lib/fonts" @@ -55,10 +56,13 @@ import { LayoutGrid, Loader, Trash2Icon, + UserRound, XIcon, } from "lucide-react" import { useProcessingDocuments } from "@/hooks/use-processing-documents" import { TimelineView } from "./timeline-view" +import { SpaceProfilePanel } from "@/components/space-profile-panel" +import { SpaceProfileModal } from "@/components/space-profile-modal" // Document category type type DocumentCategory = @@ -249,6 +253,7 @@ export function MemoriesGrid({ emptyStateProps, }: MemoriesGridProps) { const [showBulkDeleteConfirm, setShowBulkDeleteConfirm] = useState(false) + const [profileOpen, setProfileOpen] = useState(false) const [localViewMode, setLocalViewMode] = useState<"grid" | "timeline">( () => { if (typeof window === "undefined") return "grid" @@ -259,7 +264,7 @@ export function MemoriesGrid({ }, ) const { user, isSessionPending } = useAuth() - const { effectiveContainerTags } = useProject() + const { effectiveContainerTags, selectedProject } = useProject() const processingStatusMap = useProcessingDocuments() const isMobile = useIsMobile() const [selectedCategories, setSelectedCategories] = useQueryState( @@ -347,6 +352,14 @@ export function MemoriesGrid({ localStorage.setItem("memories-view-mode", mode) }, []) + const handleToggleProfile = useCallback(() => { + if (isMobile) { + setProfileOpen(true) + return + } + setProfileOpen((open) => !open) + }, [isMobile]) + const handleCategoryToggle = useCallback( (category: DocumentCategory) => { setSelectedCategories((prev) => { @@ -541,7 +554,7 @@ export function MemoriesGrid({ documents.every((d) => d.id && selectedDocumentIds.has(d.id)) return ( -
+
{!isEmpty && !isSelectionMode && (
Timeline +
{onEnterSelectionMode && ( + ) +} + +export function OrgContext() { + const { data: settings, isLoading, isError } = useOrgSettings() + const updateSettings = useUpdateOrgSettings() + const [confirmDialog, setConfirmDialog] = useState< + "enable" | "disable" | null + >(null) + const [isManaging, setIsManaging] = useState(false) + const [prompt, setPrompt] = useState("") + + const enabled = settings?.shouldLLMFilter ?? false + const savedPrompt = settings?.filterPrompt ?? "" + const dirty = prompt.trim() !== savedPrompt.trim() + const settingsReady = !isLoading && !isError + + useEffect(() => { + setPrompt(settings?.filterPrompt ?? "") + }, [settings?.filterPrompt]) + + const selectedTemplateId = useMemo(() => { + const normalized = prompt.trim() + return CONTEXT_TEMPLATES.find( + (template) => template.prompt.trim() === normalized, + )?.id + }, [prompt]) + + const handleConfirmToggle = () => { + const newEnabled = confirmDialog === "enable" + updateSettings.mutate( + newEnabled + ? { shouldLLMFilter: true } + : { shouldLLMFilter: false, filterPrompt: null }, + { + onSuccess: () => { + setConfirmDialog(null) + if (!newEnabled) { + setIsManaging(false) + setPrompt("") + } + }, + }, + ) + } + + const handleSave = () => { + updateSettings.mutate( + { + shouldLLMFilter: true, + filterPrompt: prompt.trim() ? prompt.trim() : null, + }, + { + onSuccess: () => { + setIsManaging(false) + }, + }, + ) + } + + const handleCancel = () => { + setPrompt(savedPrompt) + setIsManaging(false) + } + + return ( +
+
+ Organization Context +

+ Guide how Nova processes and remembers your content. +

+
+ +
+
+
+
+

+ {enabled ? "Context is enabled" : "Context is disabled"} +

+

+ {enabled + ? "New content will use your guidance when Nova creates memories." + : "Turn this on to tell Nova what matters most when it learns."} +

+
+
+
+ setConfirmDialog(enabled ? "disable" : "enable")} + disabled={!settingsReady || updateSettings.isPending} + variant={enabled ? "danger" : "primary"} + > + {enabled ? "DISABLE" : "ENABLE"} + + {enabled && ( + setIsManaging(true)} + disabled={updateSettings.isPending} + > + + MANAGE + + )} +
+
+ + {enabled && !isManaging && savedPrompt && ( +
+

+ {savedPrompt} +

+
+ )} + + {enabled && !isManaging && !savedPrompt && ( +

+ No organization context configured.{" "} + +

+ )} + + {enabled && isManaging && ( +
+