-
-
Notifications
You must be signed in to change notification settings - Fork 481
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(web): Manage tags in bulk actions
- Loading branch information
1 parent
6d4d1a6
commit 67729c1
Showing
6 changed files
with
214 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
apps/web/components/dashboard/bookmarks/BookmarkTagsEditor.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { toast } from "@/components/ui/use-toast"; | ||
|
||
import type { ZBookmark } from "@hoarder/shared/types/bookmarks"; | ||
import { useUpdateBookmarkTags } from "@hoarder/shared-react/hooks/bookmarks"; | ||
|
||
import { TagsEditor } from "./TagsEditor"; | ||
|
||
export function BookmarkTagsEditor({ bookmark }: { bookmark: ZBookmark }) { | ||
const { mutate } = useUpdateBookmarkTags({ | ||
onSuccess: () => { | ||
toast({ | ||
description: "Tags has been updated!", | ||
}); | ||
}, | ||
onError: () => { | ||
toast({ | ||
variant: "destructive", | ||
title: "Something went wrong", | ||
description: "There was a problem with your request.", | ||
}); | ||
}, | ||
}); | ||
|
||
return ( | ||
<TagsEditor | ||
tags={bookmark.tags} | ||
onAttach={({ tagName, tagId }) => { | ||
mutate({ | ||
bookmarkId: bookmark.id, | ||
attach: [ | ||
{ | ||
tagName, | ||
tagId, | ||
}, | ||
], | ||
detach: [], | ||
}); | ||
}} | ||
onDetach={({ tagId }) => { | ||
mutate({ | ||
bookmarkId: bookmark.id, | ||
attach: [], | ||
detach: [{ tagId }], | ||
}); | ||
}} | ||
/> | ||
); | ||
} |
126 changes: 126 additions & 0 deletions
126
apps/web/components/dashboard/bookmarks/BulkTagModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { Button } from "@/components/ui/button"; | ||
import { | ||
Dialog, | ||
DialogClose, | ||
DialogContent, | ||
DialogFooter, | ||
DialogHeader, | ||
DialogTitle, | ||
} from "@/components/ui/dialog"; | ||
import { toast } from "@/components/ui/use-toast"; | ||
|
||
import { useUpdateBookmarkTags } from "@hoarder/shared-react/hooks/bookmarks"; | ||
import { api } from "@hoarder/shared-react/trpc"; | ||
import { ZBookmark } from "@hoarder/shared/types/bookmarks"; | ||
|
||
import { TagsEditor } from "./TagsEditor"; | ||
|
||
export default function BulkTagModal({ | ||
bookmarkIds, | ||
open, | ||
setOpen, | ||
}: { | ||
bookmarkIds: string[]; | ||
open: boolean; | ||
setOpen: (open: boolean) => void; | ||
}) { | ||
const results = api.useQueries((t) => | ||
bookmarkIds.map((id) => t.bookmarks.getBookmark({ bookmarkId: id })), | ||
); | ||
|
||
const bookmarks = results | ||
.map((r) => r.data) | ||
.filter((b): b is ZBookmark => !!b); | ||
|
||
const { mutateAsync } = useUpdateBookmarkTags({ | ||
onError: (err) => { | ||
if (err.data?.code == "BAD_REQUEST") { | ||
if (err.data.zodError) { | ||
toast({ | ||
variant: "destructive", | ||
description: Object.values(err.data.zodError.fieldErrors) | ||
.flat() | ||
.join("\n"), | ||
}); | ||
} else { | ||
toast({ | ||
variant: "destructive", | ||
description: err.message, | ||
}); | ||
} | ||
} else { | ||
toast({ | ||
variant: "destructive", | ||
title: "Something went wrong", | ||
}); | ||
} | ||
}, | ||
}); | ||
|
||
const onAttach = async (tag: { tagName: string; tagId?: string }) => { | ||
const results = await Promise.allSettled( | ||
bookmarkIds.map((id) => | ||
mutateAsync({ | ||
bookmarkId: id, | ||
attach: [tag], | ||
detach: [], | ||
}), | ||
), | ||
); | ||
const successes = results.filter((r) => r.status == "fulfilled").length; | ||
toast({ | ||
description: `Tag "${tag.tagName}" has been added to ${successes} bookmarks!`, | ||
}); | ||
}; | ||
|
||
const onDetach = async ({ | ||
tagId, | ||
tagName, | ||
}: { | ||
tagId: string; | ||
tagName: string; | ||
}) => { | ||
const results = await Promise.allSettled( | ||
bookmarkIds.map((id) => | ||
mutateAsync({ | ||
bookmarkId: id, | ||
attach: [], | ||
detach: [{ tagId }], | ||
}), | ||
), | ||
); | ||
const successes = results.filter((r) => r.status == "fulfilled").length; | ||
toast({ | ||
description: `Tag "${tagName}" has been removed from ${successes} bookmarks!`, | ||
}); | ||
}; | ||
|
||
// Get all the tags that are attached to all the bookmarks | ||
let tags = bookmarks | ||
.flatMap((b) => b.tags) | ||
.filter((tag) => | ||
bookmarks.every((b) => b.tags.some((t) => tag.id == t.id)), | ||
); | ||
// Filter duplicates | ||
tags = tags.filter( | ||
(tag, index, self) => index === self.findIndex((t) => t.id == tag.id), | ||
); | ||
|
||
return ( | ||
<Dialog open={open} onOpenChange={setOpen}> | ||
<DialogContent> | ||
<DialogHeader> | ||
<DialogTitle>Edit Tags of {bookmarks.length} Bookmarks</DialogTitle> | ||
</DialogHeader> | ||
<TagsEditor tags={tags} onAttach={onAttach} onDetach={onDetach} /> | ||
<DialogFooter className="sm:justify-end"> | ||
<DialogClose asChild> | ||
<Button type="button" variant="secondary"> | ||
Close | ||
</Button> | ||
</DialogClose> | ||
</DialogFooter> | ||
</DialogContent> | ||
</Dialog> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters