@greatapps/common 1.1.185 → 1.1.186
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,12 +6,24 @@ import { UserAvatar } from "../ui/data-display/UserAvatar";
|
|
|
6
6
|
import { useQueryClient, useMutation } from "@tanstack/react-query";
|
|
7
7
|
import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
|
|
8
8
|
import { Button } from "../ui/buttons/Button";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Command,
|
|
11
|
+
CommandEmpty,
|
|
12
|
+
CommandInput,
|
|
13
|
+
CommandItem,
|
|
14
|
+
CommandList
|
|
15
|
+
} from "../ui/overlay/Command";
|
|
10
16
|
import { Checkbox } from "../ui/form/Checkbox";
|
|
11
17
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
|
|
12
18
|
import { cn } from "../../infra/utils/clsx";
|
|
13
|
-
import {
|
|
14
|
-
|
|
19
|
+
import {
|
|
20
|
+
useListProjectUsers,
|
|
21
|
+
LIST_PROJECT_USERS_BASE_KEY
|
|
22
|
+
} from "../../modules/projects/hooks/list-project-users.hook";
|
|
23
|
+
import {
|
|
24
|
+
useListAvailableUsers,
|
|
25
|
+
LIST_AVAILABLE_USERS_BASE_KEY
|
|
26
|
+
} from "../../modules/projects/hooks/list-available-users.hook";
|
|
15
27
|
import { addProjectUserAction } from "../../modules/projects/actions/add-project-user.action";
|
|
16
28
|
import { removeProjectUserAction } from "../../modules/projects/actions/remove-project-user.action";
|
|
17
29
|
function UserItemSkeleton() {
|
|
@@ -48,8 +60,12 @@ function UsersSelectorPopover({
|
|
|
48
60
|
const isFetchingMore = projectUsers.isFetchingNextPage || availableUsers.isFetchingNextPage;
|
|
49
61
|
const totalInProject = projectUsers.data?.pages[0]?.total ?? inProject.length;
|
|
50
62
|
const invalidate = () => {
|
|
51
|
-
queryClient.invalidateQueries({
|
|
52
|
-
|
|
63
|
+
queryClient.invalidateQueries({
|
|
64
|
+
queryKey: LIST_PROJECT_USERS_BASE_KEY(id)
|
|
65
|
+
});
|
|
66
|
+
queryClient.invalidateQueries({
|
|
67
|
+
queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id)
|
|
68
|
+
});
|
|
53
69
|
};
|
|
54
70
|
const addMutation = useMutation({
|
|
55
71
|
mutationFn: (userId) => addProjectUserAction({ projectId: id, users: [userId] }),
|
|
@@ -203,8 +219,15 @@ function UsersSelectorPopover({
|
|
|
203
219
|
{
|
|
204
220
|
checked: user.checked,
|
|
205
221
|
disabled: !user.removable,
|
|
206
|
-
onCheckedChange: () => toggleUser(
|
|
207
|
-
|
|
222
|
+
onCheckedChange: () => toggleUser(
|
|
223
|
+
user.id,
|
|
224
|
+
user.checked,
|
|
225
|
+
user.removable
|
|
226
|
+
),
|
|
227
|
+
className: cn(
|
|
228
|
+
user.removable && hoveredId === user.id ? "border-gray-950" : "",
|
|
229
|
+
"disabled:opacity-20"
|
|
230
|
+
)
|
|
208
231
|
}
|
|
209
232
|
)
|
|
210
233
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/layouts/UsersSelectorPopover.tsx"],"sourcesContent":["'use client';\r\n\r\n\"use client\"\r\n\r\nimport { useEffect, useState } from 'react';\r\nimport { Plus, Settings, User, Users } from 'lucide-react';\r\nimport { UserAvatar } from '../ui/data-display/UserAvatar';\r\nimport { useQueryClient, useMutation } from '@tanstack/react-query';\r\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/overlay/Popover';\r\nimport { Button } from '../ui/buttons/Button';\r\nimport { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '../ui/overlay/Command';\r\nimport { Checkbox } from '../ui/form/Checkbox';\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../ui/overlay/Tooltip';\r\nimport { cn } from '../../infra/utils/clsx';\r\nimport { useListProjectUsers, LIST_PROJECT_USERS_BASE_KEY } from '../../modules/projects/hooks/list-project-users.hook';\r\nimport { useListAvailableUsers, LIST_AVAILABLE_USERS_BASE_KEY } from '../../modules/projects/hooks/list-available-users.hook';\r\nimport { addProjectUserAction } from '../../modules/projects/actions/add-project-user.action';\r\nimport { removeProjectUserAction } from '../../modules/projects/actions/remove-project-user.action';\r\n\r\n\r\nexport interface UsersSelectorPopoverProps {\r\n projectId?: number;\r\n side?: 'top' | 'bottom' | 'left' | 'right';\r\n align?: 'start' | 'center' | 'end';\r\n contentClassName?: string;\r\n}\r\n\r\nfunction UserItemSkeleton() {\r\n return (\r\n <div className=\"flex items-center justify-between px-2 py-1.5 gap-2\">\r\n <div className=\"flex items-center gap-2 flex-1\">\r\n <div className=\"skeleton size-8 rounded-full shrink-0\" />\r\n <div className=\"skeleton h-3.5 w-28 rounded\" />\r\n </div>\r\n <div className=\"skeleton size-4 rounded shrink-0\" />\r\n </div>\r\n );\r\n}\r\n\r\nconst GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ?? '';\r\n\r\nexport function UsersSelectorPopover({\r\n projectId,\r\n side,\r\n align,\r\n contentClassName,\r\n}: UsersSelectorPopoverProps) {\r\n const queryClient = useQueryClient();\r\n const [open, setOpen] = useState(false);\r\n const [hoveredId, setHoveredId] = useState<number | null>(null);\r\n const [searchInput, setSearchInput] = useState('');\r\n const [search, setSearch] = useState('');\r\n\r\n const id = projectId ?? 0;\r\n\r\n useEffect(() => {\r\n const timer = setTimeout(() => setSearch(searchInput), 300);\r\n return () => clearTimeout(timer);\r\n }, [searchInput]);\r\n\r\n const projectUsers = useListProjectUsers(id, search);\r\n const availableUsers = useListAvailableUsers(id, search);\r\n\r\n const inProject = projectUsers.data?.pages.flatMap((p) => p.data) ?? [];\r\n const notInProject = availableUsers.data?.pages.flatMap((p) => p.data) ?? [];\r\n const isLoading = projectUsers.isLoading || availableUsers.isLoading;\r\n const isFetchingMore = projectUsers.isFetchingNextPage || availableUsers.isFetchingNextPage;\r\n const totalInProject = projectUsers.data?.pages[0]?.total ?? inProject.length;\r\n\r\n const invalidate = () => {\r\n queryClient.invalidateQueries({ queryKey: LIST_PROJECT_USERS_BASE_KEY(id) });\r\n queryClient.invalidateQueries({ queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id) });\r\n };\r\n\r\n const addMutation = useMutation({\r\n mutationFn: (userId: number) => addProjectUserAction({ projectId: id, users: [userId] }),\r\n onSuccess: invalidate,\r\n });\r\n\r\n const removeMutation = useMutation({\r\n mutationFn: (userId: number) => removeProjectUserAction({ projectId: id, users: [userId] }),\r\n onSuccess: invalidate,\r\n });\r\n\r\n const allUsers = [\r\n ...inProject.map((u) => ({\r\n id: u.id_user,\r\n name: u.name,\r\n photo: u.photo ?? null,\r\n checked: true as const,\r\n removable: u.profile !== 'owner' && u.profile !== 'admin',\r\n })),\r\n ...notInProject.map((u) => ({\r\n id: u.id,\r\n name: u.name,\r\n photo: u.photo ?? null,\r\n checked: false as const,\r\n removable: true,\r\n })),\r\n ];\r\n\r\n const toggleUser = (id: number, checked: boolean, removable: boolean) => {\r\n if (checked) {\r\n if (!removable) return;\r\n removeMutation.mutate(id);\r\n } else {\r\n addMutation.mutate(id);\r\n }\r\n };\r\n\r\n const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {\r\n const target = e.currentTarget;\r\n const nearBottom = target.scrollTop + target.clientHeight >= target.scrollHeight - 50;\r\n if (!nearBottom) return;\r\n if (projectUsers.hasNextPage && !projectUsers.isFetchingNextPage) {\r\n projectUsers.fetchNextPage();\r\n } else if (availableUsers.hasNextPage && !availableUsers.isFetchingNextPage) {\r\n availableUsers.fetchNextPage();\r\n }\r\n };\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger className=\"cursor-pointer\">\r\n <div className=\"flex items-center gap-2 text-gray-950 px-3 py-2 border border-gray-200 rounded-full hover:border-gray-950\">\r\n <span className=\"paragraph-small-semibold\">{totalInProject}</span>\r\n <User size={16} />\r\n </div>\r\n </PopoverTrigger>\r\n\r\n <PopoverContent\r\n className={cn(\r\n 'absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200',\r\n contentClassName\r\n )}\r\n side={side}\r\n align={align}\r\n sideOffset={8}\r\n >\r\n <div className=\"flex flex-col p-3 gap-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"paragraph-small-semibold\">Usuários</span>\r\n <div className=\"flex items-center gap-1.5\">\r\n <Button\r\n variant=\"secondary\"\r\n className=\"h-8 paragraph-small-semibold text-gray-950\"\r\n onClick={() => { window.location.href = `${GAPPS_URL}/my-teams?createNew=true`; }}\r\n >\r\n <Plus size={16} />\r\n Adicionar\r\n </Button>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"secondary\"\r\n className=\"p-0 size-8\"\r\n onClick={() => { window.location.href = `${GAPPS_URL}/my-teams`; }}\r\n >\r\n <Settings size={18} />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent className=\"z-[1002]\">Visualizar todos usuários</TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </div>\r\n\r\n <Command shouldFilter={false} className=\"gap-2\" value=\"\">\r\n <CommandInput\r\n placeholder=\"Busque aqui\"\r\n value={searchInput}\r\n onValueChange={setSearchInput}\r\n />\r\n <CommandList\r\n className=\"custom-scrollbar paragraph-small-medium text-gray-600 h-[162px]\"\r\n onScroll={handleScroll}\r\n >\r\n {isLoading ? (\r\n <>\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n </>\r\n ) : allUsers.length === 0 ? (\r\n <div className=\"flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4\">\r\n <Users size={28} className=\"text-gray-300\" />\r\n <span className=\"paragraph-small-medium text-center text-gray-500\">\r\n Nenhum usuário neste projeto.\r\n <br />\r\n Adicione um usuário para começar.\r\n </span>\r\n </div>\r\n ) : (\r\n <>\r\n <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>\r\n {allUsers.map((user) => (\r\n <CommandItem\r\n key={user.id}\r\n value={user.name || String(user.id)}\r\n autoFocus={false}\r\n onPointerEnter={() => setHoveredId(user.id)}\r\n onPointerLeave={() => setHoveredId(null)}\r\n onFocus={() => setHoveredId(user.id)}\r\n onBlur={() => setHoveredId(null)}\r\n onSelect={() => toggleUser(user.id, user.checked, user.removable)}\r\n className={user.removable ? 'cursor-pointer' : 'cursor-default'}\r\n >\r\n <div className=\"flex items-center justify-between w-full\">\r\n <div className=\"flex items-center gap-2 min-w-0\">\r\n <UserAvatar\r\n photo={user.photo}\r\n name={user.name ?? undefined}\r\n size={32}\r\n />\r\n <span className=\"truncate\">{user.name}</span>\r\n </div>\r\n <Tooltip open={user.removable && hoveredId === user.id}>\r\n <TooltipTrigger\r\n onClick={(e) => e.stopPropagation()}\r\n onPointerDown={(e) => e.stopPropagation()}\r\n >\r\n <Checkbox\r\n checked={user.checked}\r\n disabled={!user.removable}\r\n onCheckedChange={() => toggleUser(user.id, user.checked, user.removable)}\r\n className={user.removable && hoveredId === user.id ? 'border-gray-950' : ''}\r\n />\r\n </TooltipTrigger>\r\n <TooltipContent className=\"z-[1002]\">\r\n {user.checked ? 'Remover do projeto' : 'Adicionar ao projeto'}\r\n </TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </CommandItem>\r\n ))}\r\n {isFetchingMore && (\r\n <>\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n </>\r\n )}\r\n </>\r\n )}\r\n </CommandList>\r\n </Command>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n}\r\n"],"mappings":"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","names":["id"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/layouts/UsersSelectorPopover.tsx"],"sourcesContent":["\"use client\";\r\n\r\n\"use client\";\r\n\r\nimport { useEffect, useState } from \"react\";\r\nimport { Plus, Settings, User, Users } from \"lucide-react\";\r\nimport { UserAvatar } from \"../ui/data-display/UserAvatar\";\r\nimport { useQueryClient, useMutation } from \"@tanstack/react-query\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../ui/overlay/Popover\";\r\nimport { Button } from \"../ui/buttons/Button\";\r\nimport {\r\n Command,\r\n CommandEmpty,\r\n CommandInput,\r\n CommandItem,\r\n CommandList,\r\n} from \"../ui/overlay/Command\";\r\nimport { Checkbox } from \"../ui/form/Checkbox\";\r\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"../ui/overlay/Tooltip\";\r\nimport { cn } from \"../../infra/utils/clsx\";\r\nimport {\r\n useListProjectUsers,\r\n LIST_PROJECT_USERS_BASE_KEY,\r\n} from \"../../modules/projects/hooks/list-project-users.hook\";\r\nimport {\r\n useListAvailableUsers,\r\n LIST_AVAILABLE_USERS_BASE_KEY,\r\n} from \"../../modules/projects/hooks/list-available-users.hook\";\r\nimport { addProjectUserAction } from \"../../modules/projects/actions/add-project-user.action\";\r\nimport { removeProjectUserAction } from \"../../modules/projects/actions/remove-project-user.action\";\r\n\r\nexport interface UsersSelectorPopoverProps {\r\n projectId?: number;\r\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\r\n align?: \"start\" | \"center\" | \"end\";\r\n contentClassName?: string;\r\n}\r\n\r\nfunction UserItemSkeleton() {\r\n return (\r\n <div className=\"flex items-center justify-between px-2 py-1.5 gap-2\">\r\n <div className=\"flex items-center gap-2 flex-1\">\r\n <div className=\"skeleton size-8 rounded-full shrink-0\" />\r\n <div className=\"skeleton h-3.5 w-28 rounded\" />\r\n </div>\r\n <div className=\"skeleton size-4 rounded shrink-0\" />\r\n </div>\r\n );\r\n}\r\n\r\nconst GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ?? \"\";\r\n\r\nexport function UsersSelectorPopover({\r\n projectId,\r\n side,\r\n align,\r\n contentClassName,\r\n}: UsersSelectorPopoverProps) {\r\n const queryClient = useQueryClient();\r\n const [open, setOpen] = useState(false);\r\n const [hoveredId, setHoveredId] = useState<number | null>(null);\r\n const [searchInput, setSearchInput] = useState(\"\");\r\n const [search, setSearch] = useState(\"\");\r\n\r\n const id = projectId ?? 0;\r\n\r\n useEffect(() => {\r\n const timer = setTimeout(() => setSearch(searchInput), 300);\r\n return () => clearTimeout(timer);\r\n }, [searchInput]);\r\n\r\n const projectUsers = useListProjectUsers(id, search);\r\n const availableUsers = useListAvailableUsers(id, search);\r\n\r\n const inProject = projectUsers.data?.pages.flatMap((p) => p.data) ?? [];\r\n const notInProject = availableUsers.data?.pages.flatMap((p) => p.data) ?? [];\r\n const isLoading = projectUsers.isLoading || availableUsers.isLoading;\r\n const isFetchingMore =\r\n projectUsers.isFetchingNextPage || availableUsers.isFetchingNextPage;\r\n const totalInProject = projectUsers.data?.pages[0]?.total ?? inProject.length;\r\n\r\n const invalidate = () => {\r\n queryClient.invalidateQueries({\r\n queryKey: LIST_PROJECT_USERS_BASE_KEY(id),\r\n });\r\n queryClient.invalidateQueries({\r\n queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id),\r\n });\r\n };\r\n\r\n const addMutation = useMutation({\r\n mutationFn: (userId: number) =>\r\n addProjectUserAction({ projectId: id, users: [userId] }),\r\n onSuccess: invalidate,\r\n });\r\n\r\n const removeMutation = useMutation({\r\n mutationFn: (userId: number) =>\r\n removeProjectUserAction({ projectId: id, users: [userId] }),\r\n onSuccess: invalidate,\r\n });\r\n\r\n const allUsers = [\r\n ...inProject.map((u) => ({\r\n id: u.id_user,\r\n name: u.name,\r\n photo: u.photo ?? null,\r\n checked: true as const,\r\n removable: u.profile !== \"owner\" && u.profile !== \"admin\",\r\n })),\r\n ...notInProject.map((u) => ({\r\n id: u.id,\r\n name: u.name,\r\n photo: u.photo ?? null,\r\n checked: false as const,\r\n removable: true,\r\n })),\r\n ];\r\n\r\n const toggleUser = (id: number, checked: boolean, removable: boolean) => {\r\n if (checked) {\r\n if (!removable) return;\r\n removeMutation.mutate(id);\r\n } else {\r\n addMutation.mutate(id);\r\n }\r\n };\r\n\r\n const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {\r\n const target = e.currentTarget;\r\n const nearBottom =\r\n target.scrollTop + target.clientHeight >= target.scrollHeight - 50;\r\n if (!nearBottom) return;\r\n if (projectUsers.hasNextPage && !projectUsers.isFetchingNextPage) {\r\n projectUsers.fetchNextPage();\r\n } else if (\r\n availableUsers.hasNextPage &&\r\n !availableUsers.isFetchingNextPage\r\n ) {\r\n availableUsers.fetchNextPage();\r\n }\r\n };\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger className=\"cursor-pointer\">\r\n <div className=\"flex items-center gap-2 text-gray-950 px-3 py-2 border border-gray-200 rounded-full hover:border-gray-950\">\r\n <span className=\"paragraph-small-semibold\">{totalInProject}</span>\r\n <User size={16} />\r\n </div>\r\n </PopoverTrigger>\r\n\r\n <PopoverContent\r\n className={cn(\r\n \"absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200\",\r\n contentClassName,\r\n )}\r\n side={side}\r\n align={align}\r\n sideOffset={8}\r\n >\r\n <div className=\"flex flex-col p-3 gap-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"paragraph-small-semibold\">Usuários</span>\r\n <div className=\"flex items-center gap-1.5\">\r\n <Button\r\n variant=\"secondary\"\r\n className=\"h-8 paragraph-small-semibold text-gray-950\"\r\n onClick={() => {\r\n window.location.href = `${GAPPS_URL}/my-teams?createNew=true`;\r\n }}\r\n >\r\n <Plus size={16} />\r\n Adicionar\r\n </Button>\r\n <Tooltip>\r\n <TooltipTrigger asChild>\r\n <Button\r\n variant=\"secondary\"\r\n className=\"p-0 size-8\"\r\n onClick={() => {\r\n window.location.href = `${GAPPS_URL}/my-teams`;\r\n }}\r\n >\r\n <Settings size={18} />\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent className=\"z-[1002]\">\r\n Visualizar todos usuários\r\n </TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </div>\r\n\r\n <Command shouldFilter={false} className=\"gap-2\" value=\"\">\r\n <CommandInput\r\n placeholder=\"Busque aqui\"\r\n value={searchInput}\r\n onValueChange={setSearchInput}\r\n />\r\n <CommandList\r\n className=\"custom-scrollbar paragraph-small-medium text-gray-600 h-[162px]\"\r\n onScroll={handleScroll}\r\n >\r\n {isLoading ? (\r\n <>\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n </>\r\n ) : allUsers.length === 0 ? (\r\n <div className=\"flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4\">\r\n <Users size={28} className=\"text-gray-300\" />\r\n <span className=\"paragraph-small-medium text-center text-gray-500\">\r\n Nenhum usuário neste projeto.\r\n <br />\r\n Adicione um usuário para começar.\r\n </span>\r\n </div>\r\n ) : (\r\n <>\r\n <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>\r\n {allUsers.map((user) => (\r\n <CommandItem\r\n key={user.id}\r\n value={user.name || String(user.id)}\r\n autoFocus={false}\r\n onPointerEnter={() => setHoveredId(user.id)}\r\n onPointerLeave={() => setHoveredId(null)}\r\n onFocus={() => setHoveredId(user.id)}\r\n onBlur={() => setHoveredId(null)}\r\n onSelect={() =>\r\n toggleUser(user.id, user.checked, user.removable)\r\n }\r\n className={\r\n user.removable ? \"cursor-pointer\" : \"cursor-default\"\r\n }\r\n >\r\n <div className=\"flex items-center justify-between w-full\">\r\n <div className=\"flex items-center gap-2 min-w-0\">\r\n <UserAvatar\r\n photo={user.photo}\r\n name={user.name ?? undefined}\r\n size={32}\r\n />\r\n <span className=\"truncate\">{user.name}</span>\r\n </div>\r\n <Tooltip open={user.removable && hoveredId === user.id}>\r\n <TooltipTrigger\r\n onClick={(e) => e.stopPropagation()}\r\n onPointerDown={(e) => e.stopPropagation()}\r\n >\r\n <Checkbox\r\n checked={user.checked}\r\n disabled={!user.removable}\r\n onCheckedChange={() =>\r\n toggleUser(\r\n user.id,\r\n user.checked,\r\n user.removable,\r\n )\r\n }\r\n className={cn(\r\n user.removable && hoveredId === user.id\r\n ? \"border-gray-950\"\r\n : \"\",\r\n \"disabled:opacity-20\",\r\n )}\r\n />\r\n </TooltipTrigger>\r\n <TooltipContent className=\"z-[1002]\">\r\n {user.checked\r\n ? \"Remover do projeto\"\r\n : \"Adicionar ao projeto\"}\r\n </TooltipContent>\r\n </Tooltip>\r\n </div>\r\n </CommandItem>\r\n ))}\r\n {isFetchingMore && (\r\n <>\r\n <UserItemSkeleton />\r\n <UserItemSkeleton />\r\n </>\r\n )}\r\n </>\r\n )}\r\n </CommandList>\r\n </Command>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n}\r\n"],"mappings":"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","names":["id"]}
|
package/package.json
CHANGED
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"use client"
|
|
4
|
-
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
6
|
-
import { Plus, Settings, User, Users } from 'lucide-react';
|
|
7
|
-
import { UserAvatar } from '../ui/data-display/UserAvatar';
|
|
8
|
-
import { useQueryClient, useMutation } from '@tanstack/react-query';
|
|
9
|
-
import { Popover, PopoverContent, PopoverTrigger } from '../ui/overlay/Popover';
|
|
10
|
-
import { Button } from '../ui/buttons/Button';
|
|
11
|
-
import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '../ui/overlay/Command';
|
|
12
|
-
import { Checkbox } from '../ui/form/Checkbox';
|
|
13
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/overlay/Tooltip';
|
|
14
|
-
import { cn } from '../../infra/utils/clsx';
|
|
15
|
-
import { useListProjectUsers, LIST_PROJECT_USERS_BASE_KEY } from '../../modules/projects/hooks/list-project-users.hook';
|
|
16
|
-
import { useListAvailableUsers, LIST_AVAILABLE_USERS_BASE_KEY } from '../../modules/projects/hooks/list-available-users.hook';
|
|
17
|
-
import { addProjectUserAction } from '../../modules/projects/actions/add-project-user.action';
|
|
18
|
-
import { removeProjectUserAction } from '../../modules/projects/actions/remove-project-user.action';
|
|
1
|
+
"use client";
|
|
19
2
|
|
|
3
|
+
"use client";
|
|
4
|
+
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { Plus, Settings, User, Users } from "lucide-react";
|
|
7
|
+
import { UserAvatar } from "../ui/data-display/UserAvatar";
|
|
8
|
+
import { useQueryClient, useMutation } from "@tanstack/react-query";
|
|
9
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
|
|
10
|
+
import { Button } from "../ui/buttons/Button";
|
|
11
|
+
import {
|
|
12
|
+
Command,
|
|
13
|
+
CommandEmpty,
|
|
14
|
+
CommandInput,
|
|
15
|
+
CommandItem,
|
|
16
|
+
CommandList,
|
|
17
|
+
} from "../ui/overlay/Command";
|
|
18
|
+
import { Checkbox } from "../ui/form/Checkbox";
|
|
19
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
|
|
20
|
+
import { cn } from "../../infra/utils/clsx";
|
|
21
|
+
import {
|
|
22
|
+
useListProjectUsers,
|
|
23
|
+
LIST_PROJECT_USERS_BASE_KEY,
|
|
24
|
+
} from "../../modules/projects/hooks/list-project-users.hook";
|
|
25
|
+
import {
|
|
26
|
+
useListAvailableUsers,
|
|
27
|
+
LIST_AVAILABLE_USERS_BASE_KEY,
|
|
28
|
+
} from "../../modules/projects/hooks/list-available-users.hook";
|
|
29
|
+
import { addProjectUserAction } from "../../modules/projects/actions/add-project-user.action";
|
|
30
|
+
import { removeProjectUserAction } from "../../modules/projects/actions/remove-project-user.action";
|
|
20
31
|
|
|
21
32
|
export interface UsersSelectorPopoverProps {
|
|
22
33
|
projectId?: number;
|
|
23
|
-
side?:
|
|
24
|
-
align?:
|
|
34
|
+
side?: "top" | "bottom" | "left" | "right";
|
|
35
|
+
align?: "start" | "center" | "end";
|
|
25
36
|
contentClassName?: string;
|
|
26
37
|
}
|
|
27
38
|
|
|
@@ -37,7 +48,7 @@ function UserItemSkeleton() {
|
|
|
37
48
|
);
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
const GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ??
|
|
51
|
+
const GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ?? "";
|
|
41
52
|
|
|
42
53
|
export function UsersSelectorPopover({
|
|
43
54
|
projectId,
|
|
@@ -48,8 +59,8 @@ export function UsersSelectorPopover({
|
|
|
48
59
|
const queryClient = useQueryClient();
|
|
49
60
|
const [open, setOpen] = useState(false);
|
|
50
61
|
const [hoveredId, setHoveredId] = useState<number | null>(null);
|
|
51
|
-
const [searchInput, setSearchInput] = useState(
|
|
52
|
-
const [search, setSearch] = useState(
|
|
62
|
+
const [searchInput, setSearchInput] = useState("");
|
|
63
|
+
const [search, setSearch] = useState("");
|
|
53
64
|
|
|
54
65
|
const id = projectId ?? 0;
|
|
55
66
|
|
|
@@ -64,21 +75,28 @@ export function UsersSelectorPopover({
|
|
|
64
75
|
const inProject = projectUsers.data?.pages.flatMap((p) => p.data) ?? [];
|
|
65
76
|
const notInProject = availableUsers.data?.pages.flatMap((p) => p.data) ?? [];
|
|
66
77
|
const isLoading = projectUsers.isLoading || availableUsers.isLoading;
|
|
67
|
-
const isFetchingMore =
|
|
78
|
+
const isFetchingMore =
|
|
79
|
+
projectUsers.isFetchingNextPage || availableUsers.isFetchingNextPage;
|
|
68
80
|
const totalInProject = projectUsers.data?.pages[0]?.total ?? inProject.length;
|
|
69
81
|
|
|
70
82
|
const invalidate = () => {
|
|
71
|
-
queryClient.invalidateQueries({
|
|
72
|
-
|
|
83
|
+
queryClient.invalidateQueries({
|
|
84
|
+
queryKey: LIST_PROJECT_USERS_BASE_KEY(id),
|
|
85
|
+
});
|
|
86
|
+
queryClient.invalidateQueries({
|
|
87
|
+
queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id),
|
|
88
|
+
});
|
|
73
89
|
};
|
|
74
90
|
|
|
75
91
|
const addMutation = useMutation({
|
|
76
|
-
mutationFn: (userId: number) =>
|
|
92
|
+
mutationFn: (userId: number) =>
|
|
93
|
+
addProjectUserAction({ projectId: id, users: [userId] }),
|
|
77
94
|
onSuccess: invalidate,
|
|
78
95
|
});
|
|
79
96
|
|
|
80
97
|
const removeMutation = useMutation({
|
|
81
|
-
mutationFn: (userId: number) =>
|
|
98
|
+
mutationFn: (userId: number) =>
|
|
99
|
+
removeProjectUserAction({ projectId: id, users: [userId] }),
|
|
82
100
|
onSuccess: invalidate,
|
|
83
101
|
});
|
|
84
102
|
|
|
@@ -88,7 +106,7 @@ export function UsersSelectorPopover({
|
|
|
88
106
|
name: u.name,
|
|
89
107
|
photo: u.photo ?? null,
|
|
90
108
|
checked: true as const,
|
|
91
|
-
removable: u.profile !==
|
|
109
|
+
removable: u.profile !== "owner" && u.profile !== "admin",
|
|
92
110
|
})),
|
|
93
111
|
...notInProject.map((u) => ({
|
|
94
112
|
id: u.id,
|
|
@@ -110,11 +128,15 @@ export function UsersSelectorPopover({
|
|
|
110
128
|
|
|
111
129
|
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
|
112
130
|
const target = e.currentTarget;
|
|
113
|
-
const nearBottom =
|
|
131
|
+
const nearBottom =
|
|
132
|
+
target.scrollTop + target.clientHeight >= target.scrollHeight - 50;
|
|
114
133
|
if (!nearBottom) return;
|
|
115
134
|
if (projectUsers.hasNextPage && !projectUsers.isFetchingNextPage) {
|
|
116
135
|
projectUsers.fetchNextPage();
|
|
117
|
-
} else if (
|
|
136
|
+
} else if (
|
|
137
|
+
availableUsers.hasNextPage &&
|
|
138
|
+
!availableUsers.isFetchingNextPage
|
|
139
|
+
) {
|
|
118
140
|
availableUsers.fetchNextPage();
|
|
119
141
|
}
|
|
120
142
|
};
|
|
@@ -130,8 +152,8 @@ export function UsersSelectorPopover({
|
|
|
130
152
|
|
|
131
153
|
<PopoverContent
|
|
132
154
|
className={cn(
|
|
133
|
-
|
|
134
|
-
contentClassName
|
|
155
|
+
"absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200",
|
|
156
|
+
contentClassName,
|
|
135
157
|
)}
|
|
136
158
|
side={side}
|
|
137
159
|
align={align}
|
|
@@ -144,7 +166,9 @@ export function UsersSelectorPopover({
|
|
|
144
166
|
<Button
|
|
145
167
|
variant="secondary"
|
|
146
168
|
className="h-8 paragraph-small-semibold text-gray-950"
|
|
147
|
-
onClick={() => {
|
|
169
|
+
onClick={() => {
|
|
170
|
+
window.location.href = `${GAPPS_URL}/my-teams?createNew=true`;
|
|
171
|
+
}}
|
|
148
172
|
>
|
|
149
173
|
<Plus size={16} />
|
|
150
174
|
Adicionar
|
|
@@ -154,12 +178,16 @@ export function UsersSelectorPopover({
|
|
|
154
178
|
<Button
|
|
155
179
|
variant="secondary"
|
|
156
180
|
className="p-0 size-8"
|
|
157
|
-
onClick={() => {
|
|
181
|
+
onClick={() => {
|
|
182
|
+
window.location.href = `${GAPPS_URL}/my-teams`;
|
|
183
|
+
}}
|
|
158
184
|
>
|
|
159
185
|
<Settings size={18} />
|
|
160
186
|
</Button>
|
|
161
187
|
</TooltipTrigger>
|
|
162
|
-
<TooltipContent className="z-[1002]">
|
|
188
|
+
<TooltipContent className="z-[1002]">
|
|
189
|
+
Visualizar todos usuários
|
|
190
|
+
</TooltipContent>
|
|
163
191
|
</Tooltip>
|
|
164
192
|
</div>
|
|
165
193
|
</div>
|
|
@@ -201,8 +229,12 @@ export function UsersSelectorPopover({
|
|
|
201
229
|
onPointerLeave={() => setHoveredId(null)}
|
|
202
230
|
onFocus={() => setHoveredId(user.id)}
|
|
203
231
|
onBlur={() => setHoveredId(null)}
|
|
204
|
-
onSelect={() =>
|
|
205
|
-
|
|
232
|
+
onSelect={() =>
|
|
233
|
+
toggleUser(user.id, user.checked, user.removable)
|
|
234
|
+
}
|
|
235
|
+
className={
|
|
236
|
+
user.removable ? "cursor-pointer" : "cursor-default"
|
|
237
|
+
}
|
|
206
238
|
>
|
|
207
239
|
<div className="flex items-center justify-between w-full">
|
|
208
240
|
<div className="flex items-center gap-2 min-w-0">
|
|
@@ -221,12 +253,25 @@ export function UsersSelectorPopover({
|
|
|
221
253
|
<Checkbox
|
|
222
254
|
checked={user.checked}
|
|
223
255
|
disabled={!user.removable}
|
|
224
|
-
onCheckedChange={() =>
|
|
225
|
-
|
|
256
|
+
onCheckedChange={() =>
|
|
257
|
+
toggleUser(
|
|
258
|
+
user.id,
|
|
259
|
+
user.checked,
|
|
260
|
+
user.removable,
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
className={cn(
|
|
264
|
+
user.removable && hoveredId === user.id
|
|
265
|
+
? "border-gray-950"
|
|
266
|
+
: "",
|
|
267
|
+
"disabled:opacity-20",
|
|
268
|
+
)}
|
|
226
269
|
/>
|
|
227
270
|
</TooltipTrigger>
|
|
228
271
|
<TooltipContent className="z-[1002]">
|
|
229
|
-
{user.checked
|
|
272
|
+
{user.checked
|
|
273
|
+
? "Remover do projeto"
|
|
274
|
+
: "Adicionar ao projeto"}
|
|
230
275
|
</TooltipContent>
|
|
231
276
|
</Tooltip>
|
|
232
277
|
</div>
|