Skip to content
Merged
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
61 changes: 32 additions & 29 deletions apps/web/components/add-document/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,40 +380,42 @@ export function AddDocument({

<div
className={cn(
"overflow-auto flex flex-col justify-between px-1 scrollbar-thin flex-1",
"flex flex-col flex-1 min-h-0 px-1",
isMobile ? "w-full" : "w-2/3",
)}
>
{activeTab === "note" && (
<NoteContent
onSubmit={handleNoteSubmit}
onContentChange={handleNoteContentChange}
isSubmitting={noteMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "link" && (
<LinkContent
onSubmit={handleLinkSubmit}
onDataChange={handleLinkDataChange}
isSubmitting={linkMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "file" && (
<FileContent
onSubmit={handleFileSubmit}
onDataChange={handleFileDataChange}
isSubmitting={fileMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "connect" && (
<ConnectContent selectedProject={localSelectedProject} />
)}
<div className="overflow-auto flex-1 min-h-0 scrollbar-thin">
{activeTab === "note" && (
<NoteContent
onSubmit={handleNoteSubmit}
onContentChange={handleNoteContentChange}
isSubmitting={noteMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "link" && (
<LinkContent
onSubmit={handleLinkSubmit}
onDataChange={handleLinkDataChange}
isSubmitting={linkMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "file" && (
<FileContent
onSubmit={handleFileSubmit}
onDataChange={handleFileDataChange}
isSubmitting={fileMutation.isPending}
isOpen={isOpen}
/>
)}
{activeTab === "connect" && (
<ConnectContent selectedProject={localSelectedProject} />
)}
</div>
<div
className={cn(
"flex gap-2 pt-3",
"flex gap-2 pt-3 shrink-0",
isMobile ? "flex-col" : "justify-between",
)}
>
Expand All @@ -424,6 +426,7 @@ export function AddDocument({
setLocalSelectedProject(projects[0] ?? localSelectedProject)
}
variant="insideOut"
singleSelect
/>
)}
<div
Expand Down
7 changes: 6 additions & 1 deletion apps/web/components/add-space-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,11 @@ const EMOJI_LIST = [
export function AddSpaceModal({
isOpen,
onClose,
onCreated,
}: {
isOpen: boolean
onClose: () => void
onCreated?: (containerTag: string) => void
}) {
const [spaceName, setSpaceName] = useState("")
const [emoji, setEmoji] = useState("📁")
Expand All @@ -87,8 +89,11 @@ export function AddSpaceModal({
createProjectMutation.mutate(
{ name: trimmedName, emoji: emoji || undefined },
{
onSuccess: () => {
onSuccess: (data) => {
analytics.spaceCreated()
if (data?.containerTag) {
onCreated?.(data.containerTag)
}
handleClose()
},
},
Expand Down
55 changes: 40 additions & 15 deletions apps/web/components/select-spaces-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface SelectSpacesModalProps {
selectedProjects: string[]
onApply: (selected: string[]) => void
projects: ContainerTagListType[]
singleSelect?: boolean
}

export function SelectSpacesModal({
Expand All @@ -24,6 +25,7 @@ export function SelectSpacesModal({
selectedProjects,
onApply,
projects,
singleSelect = false,
}: SelectSpacesModalProps) {
const [searchQuery, setSearchQuery] = useState("")
const [localSelection, setLocalSelection] =
Expand All @@ -44,6 +46,10 @@ export function SelectSpacesModal({
}

const handleToggle = (containerTag: string) => {
if (singleSelect) {
setLocalSelection([containerTag])
return
}
setLocalSelection((prev) => {
if (prev.includes(containerTag)) {
return prev.filter((tag) => tag !== containerTag)
Expand Down Expand Up @@ -117,10 +123,12 @@ export function SelectSpacesModal({
dmSans125ClassName(),
)}
>
Select Spaces
Select Space{!singleSelect && "s"}
</p>
<p className="text-[#737373] font-medium text-[16px] leading-[1.35]">
Choose one or more spaces to filter your memories
{singleSelect
? "Choose a space for your memory"
: "Choose one or more spaces to filter your memories"}
</p>
</div>
<DialogPrimitive.Close
Expand Down Expand Up @@ -173,16 +181,29 @@ export function SelectSpacesModal({
: "bg-transparent border border-transparent hover:bg-[#14161A]/50",
)}
>
<div
className={cn(
"w-5 h-5 rounded-md border-2 flex items-center justify-center shrink-0 transition-colors",
isSelected
? "bg-blue-500 border-blue-500"
: "border-[#737373]",
)}
>
{isSelected && <Check className="size-3 text-white" />}
</div>
{singleSelect ? (
<div
className={cn(
"w-5 h-5 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors",
isSelected ? "border-blue-500" : "border-[#737373]",
)}
>
{isSelected && (
<div className="w-2.5 h-2.5 rounded-full bg-blue-500" />
)}
</div>
) : (
<div
className={cn(
"w-5 h-5 rounded-md border-2 flex items-center justify-center shrink-0 transition-colors",
isSelected
? "bg-blue-500 border-blue-500"
: "border-[#737373]",
)}
>
{isSelected && <Check className="size-3 text-white" />}
</div>
)}
<span className="text-lg">{project.emoji || "📁"}</span>
<span className="text-[#fafafa] text-sm font-medium truncate flex-1">
{project.name ?? project.containerTag}
Expand All @@ -195,9 +216,13 @@ export function SelectSpacesModal({

<div className="flex items-center justify-between">
<p className="text-[#737373] text-sm">
{localSelection.length === 0
? "No spaces selected (showing all)"
: `${localSelection.length} space${localSelection.length > 1 ? "s" : ""} selected`}
{singleSelect
? localSelection.length === 0
? "No space selected"
: "1 space selected"
: localSelection.length === 0
? "No spaces selected (showing all)"
: `${localSelection.length} space${localSelection.length > 1 ? "s" : ""} selected`}
</p>
<div className="flex items-center gap-[22px]">
<button
Expand Down
36 changes: 22 additions & 14 deletions apps/web/components/space-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface SpaceSelectorProps {
showNewSpace?: boolean
enableDelete?: boolean
compact?: boolean
singleSelect?: boolean
}

const triggerVariants = {
Expand All @@ -70,6 +71,7 @@ export function SpaceSelector({
showNewSpace = true,
enableDelete = false,
compact = false,
singleSelect = false,
}: SpaceSelectorProps) {
const [isOpen, setIsOpen] = useState(false)
const [showCreateDialog, setShowCreateDialog] = useState(false)
Expand Down Expand Up @@ -272,20 +274,24 @@ export function SpaceSelector({
>
<div className="flex flex-col gap-2">
<div className="flex flex-col">
<DropdownMenuItem
onClick={handleSelectNovaSpaces}
className={cn(
"flex items-center gap-2 px-3 py-2.5 rounded-md cursor-pointer text-white text-sm font-medium",
isNovaSpaces
? "bg-[#293952]/40"
: "opacity-60 hover:opacity-100 hover:bg-[#293952]/40",
)}
>
<Globe className="size-4" />
<span className="flex-1">Nova Spaces</span>
</DropdownMenuItem>
{!singleSelect && (
<>
<DropdownMenuItem
onClick={handleSelectNovaSpaces}
className={cn(
"flex items-center gap-2 px-3 py-2.5 rounded-md cursor-pointer text-white text-sm font-medium",
isNovaSpaces
? "bg-[#293952]/40"
: "opacity-60 hover:opacity-100 hover:bg-[#293952]/40",
)}
>
<Globe className="size-4" />
<span className="flex-1">Nova Spaces</span>
</DropdownMenuItem>

<DropdownMenuSeparator className="bg-[#2E3033] my-1" />
<DropdownMenuSeparator className="bg-[#2E3033] my-1" />
</>
)}

<div className="px-3 py-1">
<span className="text-[10px] uppercase tracking-wider text-[#737373] font-medium">
Expand Down Expand Up @@ -362,7 +368,7 @@ export function SpaceSelector({
}}
>
<Layers className="size-4" />
<span>Select Spaces</span>
<span>Select Space{!singleSelect && "s"}</span>
</button>

{showNewSpace && (
Expand All @@ -386,6 +392,7 @@ export function SpaceSelector({
<AddSpaceModal
isOpen={showCreateDialog}
onClose={() => setShowCreateDialog(false)}
onCreated={(containerTag) => onValueChange([containerTag])}
/>

<SelectSpacesModal
Expand All @@ -394,6 +401,7 @@ export function SpaceSelector({
selectedProjects={selectedProjects}
onApply={handleSelectSpacesApply}
projects={allProjects}
singleSelect={singleSelect}
/>

<Dialog
Expand Down
18 changes: 10 additions & 8 deletions apps/web/hooks/use-project-mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { $fetch } from "@lib/api"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { toast } from "sonner"
import { useProject } from "@/stores"
import type { Project } from "@repo/lib/types"
import type { ContainerTagListType } from "@repo/lib/types"

export function useProjectMutations() {
const queryClient = useQueryClient()
Expand All @@ -29,6 +29,7 @@ export function useProjectMutations() {
onSuccess: (data) => {
toast.success("Project created successfully!")
queryClient.invalidateQueries({ queryKey: ["projects"] })
queryClient.invalidateQueries({ queryKey: ["container-tags"] })

if (data?.containerTag) {
setSelectedProjects([data.containerTag])
Expand Down Expand Up @@ -63,14 +64,12 @@ export function useProjectMutations() {
},
onSuccess: (_, variables) => {
toast.success("Project deleted successfully")
queryClient.invalidateQueries({ queryKey: ["projects"] })
queryClient.invalidateQueries({ queryKey: ["container-tags"] })

const allProjects =
queryClient.getQueryData<Project[]>(["projects"]) || []
const deletedProject = allProjects.find(
(p) => p.id === variables.projectId,
)
const allTags =
queryClient.getQueryData<ContainerTagListType[]>(["container-tags"]) ||
[]
const deletedProject = allTags.find((p) => p.id === variables.projectId)

if (
deletedProject?.containerTag &&
selectedProjects.includes(deletedProject.containerTag)
Expand All @@ -79,6 +78,9 @@ export function useProjectMutations() {
selectedProjects.filter((tag) => tag !== deletedProject.containerTag),
)
}

queryClient.invalidateQueries({ queryKey: ["projects"] })
queryClient.invalidateQueries({ queryKey: ["container-tags"] })
},
onError: (error) => {
toast.error("Failed to delete project", {
Expand Down
Loading