@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 { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from "../ui/overlay/Command";
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 { useListProjectUsers, LIST_PROJECT_USERS_BASE_KEY } from "../../modules/projects/hooks/list-project-users.hook";
14
- import { useListAvailableUsers, LIST_AVAILABLE_USERS_BASE_KEY } from "../../modules/projects/hooks/list-available-users.hook";
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({ queryKey: LIST_PROJECT_USERS_BASE_KEY(id) });
52
- queryClient.invalidateQueries({ queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id) });
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(user.id, user.checked, user.removable),
207
- className: user.removable && hoveredId === user.id ? "border-gray-950" : ""
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.185",
3
+ "version": "1.1.186",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -1,27 +1,38 @@
1
- 'use client';
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?: 'top' | 'bottom' | 'left' | 'right';
24
- align?: 'start' | 'center' | 'end';
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 = projectUsers.isFetchingNextPage || availableUsers.isFetchingNextPage;
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({ queryKey: LIST_PROJECT_USERS_BASE_KEY(id) });
72
- queryClient.invalidateQueries({ queryKey: LIST_AVAILABLE_USERS_BASE_KEY(id) });
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) => addProjectUserAction({ projectId: id, users: [userId] }),
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) => removeProjectUserAction({ projectId: id, users: [userId] }),
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 !== 'owner' && u.profile !== 'admin',
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 = target.scrollTop + target.clientHeight >= target.scrollHeight - 50;
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 (availableUsers.hasNextPage && !availableUsers.isFetchingNextPage) {
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
- 'absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200',
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={() => { window.location.href = `${GAPPS_URL}/my-teams?createNew=true`; }}
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={() => { window.location.href = `${GAPPS_URL}/my-teams`; }}
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]">Visualizar todos usuários</TooltipContent>
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={() => toggleUser(user.id, user.checked, user.removable)}
205
- className={user.removable ? 'cursor-pointer' : 'cursor-default'}
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={() => toggleUser(user.id, user.checked, user.removable)}
225
- className={user.removable && hoveredId === user.id ? 'border-gray-950' : ''}
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 ? 'Remover do projeto' : 'Adicionar ao projeto'}
272
+ {user.checked
273
+ ? "Remover do projeto"
274
+ : "Adicionar ao projeto"}
230
275
  </TooltipContent>
231
276
  </Tooltip>
232
277
  </div>