-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contextual move #558
Contextual move #558
Changes from 7 commits
2f33993
3be6c36
e5d9f77
ccd2615
9d2419d
143f92c
2cb2b3d
b11b6ec
7949e98
ab19615
9503ac6
943295b
13c73e0
38d476d
53a845a
ae9c9b0
13527eb
44a1895
5d8b358
9b3e294
2a9be52
cb44eb2
173b8a1
0fe8d1d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,102 @@ | ||||||||||||||
"use client" | ||||||||||||||
|
||||||||||||||
import React, { useEffect, useRef, useState } from "react" | ||||||||||||||
|
||||||||||||||
import { DialogHeader, DialogTitle } from "../ui/dialog" | ||||||||||||||
import { Input } from "../ui/input" | ||||||||||||||
import { useAuth } from "@/src/contexts/AuthContext" | ||||||||||||||
import { useModal } from "@/src/contexts/ModalProvider" | ||||||||||||||
import { useToast } from "@/src/hooks/use-toast" | ||||||||||||||
import { useCycleItemStore } from "@/src/lib/store/cycle.store" | ||||||||||||||
import useSpaceStore from "@/src/lib/store/space.store" | ||||||||||||||
|
||||||||||||||
type Props = { | ||||||||||||||
inboxItemId: string | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const MoveInboxItem = ({ inboxItemId }: Props) => { | ||||||||||||||
const { session } = useAuth() | ||||||||||||||
const { hideModal } = useModal() | ||||||||||||||
const inputRef = useRef<HTMLInputElement>(null) | ||||||||||||||
const [searchTerm, setSearchTerm] = useState("") | ||||||||||||||
const { spaces, fetchSpaces } = useSpaceStore() | ||||||||||||||
const { updateItem, fetchInbox } = useCycleItemStore() | ||||||||||||||
const { toast } = useToast() | ||||||||||||||
|
||||||||||||||
// Fetch spaces if they don't exist | ||||||||||||||
useEffect(() => { | ||||||||||||||
if (!spaces) { | ||||||||||||||
void fetchSpaces(session) | ||||||||||||||
} | ||||||||||||||
}, [fetchSpaces, session, spaces]) | ||||||||||||||
|
||||||||||||||
// Focus the input field when the modal opens | ||||||||||||||
useEffect(() => { | ||||||||||||||
if (inputRef.current) { | ||||||||||||||
inputRef.current.focus() | ||||||||||||||
} | ||||||||||||||
}, []) | ||||||||||||||
|
||||||||||||||
const handleSpaceClick = async (spaceId: string) => { | ||||||||||||||
try { | ||||||||||||||
await updateItem(session, { spaces: [spaceId] }, inboxItemId) | ||||||||||||||
await fetchInbox(session) | ||||||||||||||
toast({ title: "🚀 Moved successfully!" }) | ||||||||||||||
hideModal() | ||||||||||||||
} catch (error) { | ||||||||||||||
toast({title: "Oops something seems wrong!", variant: "destructive"}) | ||||||||||||||
console.error("Failed to move item:", error) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance error handling and user feedback. The space movement handler needs improvements in error handling and user feedback: const handleSpaceClick = async (spaceId: string) => {
+ setIsLoading(true)
try {
await updateItem(session, { spaces: [spaceId] }, inboxItemId)
await fetchInbox(session)
- toast({ title: "🚀 Moved successfully!" })
+ const space = spaces.find(s => s._id === spaceId)
+ toast({ title: `🚀 Moved to ${space?.name || 'space'} successfully!` })
hideModal()
} catch (error) {
- toast({title: "Oops something seems wrong!", variant: "destructive"})
- console.error("Failed to move item:", error)
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
+ toast({
+ title: "Failed to move item",
+ description: errorMessage,
+ variant: "destructive"
+ })
+ console.error("Failed to move item:", errorMessage)
} finally {
+ setIsLoading(false)
}
}
|
||||||||||||||
|
||||||||||||||
// Filter spaces based on the search term | ||||||||||||||
const filteredSpaces = spaces.filter((space) => | ||||||||||||||
space.name.toLowerCase().includes(searchTerm.toLowerCase()) | ||||||||||||||
) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle undefined 'spaces' to prevent runtime errors If Apply this diff to guard against undefined - const filteredSpaces = spaces.filter((space) =>
+ const filteredSpaces = (spaces || []).filter((space) =>
space.name.toLowerCase().includes(searchTerm.toLowerCase())
) 📝 Committable suggestion
Suggested change
|
||||||||||||||
|
||||||||||||||
return ( | ||||||||||||||
<> | ||||||||||||||
<DialogHeader className="hidden h-0 p-0"> | ||||||||||||||
<DialogTitle className="hidden p-0"></DialogTitle> | ||||||||||||||
</DialogHeader> | ||||||||||||||
<div className="flex justify-between gap-2 px-4 pt-1 text-xs text-secondary-foreground"> | ||||||||||||||
<span className="flex-1 truncate text-primary-foreground"> | ||||||||||||||
<Input | ||||||||||||||
ref={inputRef} | ||||||||||||||
className="w-full border-none px-0 text-primary-foreground outline-none placeholder:text-secondary-foreground" | ||||||||||||||
placeholder="Specify the target item: todo, note, event etc" | ||||||||||||||
value={searchTerm} | ||||||||||||||
onChange={(e) => setSearchTerm(e.target.value)} | ||||||||||||||
/> | ||||||||||||||
</span> | ||||||||||||||
</div> | ||||||||||||||
<div className="flex items-center gap-5 bg-transparent text-secondary-foreground"> | ||||||||||||||
<div className="flex h-fit min-w-[350px] flex-col gap-5 overflow-hidden rounded-lg bg-background p-5 text-sm"> | ||||||||||||||
<div className="flex max-h-96 flex-col gap-1.5 overflow-y-auto"> | ||||||||||||||
{filteredSpaces.length > 0 ? ( | ||||||||||||||
filteredSpaces.map((space) => ( | ||||||||||||||
<button | ||||||||||||||
key={space._id} | ||||||||||||||
className="cursor-pointer text-left hover:text-primary-foreground" | ||||||||||||||
onClick={() => handleSpaceClick(space._id)} | ||||||||||||||
onKeyDown={(e) => { | ||||||||||||||
if (e.key === "Enter") { | ||||||||||||||
handleSpaceClick(space._id) | ||||||||||||||
} | ||||||||||||||
}} | ||||||||||||||
type="button" | ||||||||||||||
> | ||||||||||||||
{space.name} | ||||||||||||||
</button> | ||||||||||||||
)) | ||||||||||||||
) : ( | ||||||||||||||
<div>No matching results found!</div> | ||||||||||||||
)} | ||||||||||||||
</div> | ||||||||||||||
</div> | ||||||||||||||
</div> | ||||||||||||||
</> | ||||||||||||||
) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance accessibility and loading states. The UI implementation needs improvements in accessibility and loading feedback: - <DialogHeader className="hidden h-0 p-0">
- <DialogTitle className="hidden p-0"></DialogTitle>
+ <DialogHeader className="sr-only">
+ <DialogTitle>Move Item to Space</DialogTitle>
</DialogHeader>
<div className="flex justify-between gap-2 px-4 pt-1 text-xs text-secondary-foreground">
<span className="flex-1 truncate text-primary-foreground">
<Input
ref={inputRef}
+ aria-label="Search spaces"
className="w-full border-none px-0 text-primary-foreground outline-none placeholder:text-secondary-foreground"
placeholder="Specify the target item: todo, note, event etc"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
+ disabled={isLoading}
/>
</span>
</div>
<div className="flex items-center gap-5 bg-transparent text-secondary-foreground">
<div className="flex h-fit min-w-[350px] flex-col gap-5 overflow-hidden rounded-lg bg-background p-5 text-sm">
<div className="flex max-h-96 flex-col gap-1.5 overflow-y-auto">
+ {isLoading && <div>Loading spaces...</div>}
{filteredSpaces.length > 0 ? (
filteredSpaces.map((space) => (
<button
key={space._id}
className="cursor-pointer text-left hover:text-primary-foreground"
onClick={() => handleSpaceClick(space._id)}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleSpaceClick(space._id)
}
}}
type="button"
+ disabled={isLoading}
+ aria-label={`Move to ${space.name}`}
>
{space.name}
</button>
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
export default MoveInboxItem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add cleanup and handle potential race conditions.
The effects need some improvements for better reliability: