@greatapps/common 1.1.230 → 1.1.232

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.
Files changed (237) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +1 -1
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
  3. package/dist/components/account/ConfirmDeleteAccountModal.mjs.map +1 -1
  4. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -1
  5. package/dist/components/account/DeleteAccountModal.mjs.map +1 -1
  6. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
  7. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs.map +1 -1
  8. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
  9. package/dist/components/account/hooks/useChangePhoneForm.mjs.map +1 -1
  10. package/dist/components/account/hooks/useTwoFactorAuthForm.mjs.map +1 -1
  11. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
  12. package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -1
  13. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
  14. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  15. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
  16. package/dist/components/account/sections/SecuritySection.mjs.map +1 -1
  17. package/dist/components/embeds/CrispEmbed.mjs.map +1 -1
  18. package/dist/components/embeds/FrillEmbed.mjs.map +1 -1
  19. package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
  20. package/dist/components/layouts/AppNavBar.mjs.map +1 -1
  21. package/dist/components/layouts/MdSideBarNavigation.mjs.map +1 -1
  22. package/dist/components/layouts/NavBar.mjs +1 -1
  23. package/dist/components/layouts/NavBar.mjs.map +1 -1
  24. package/dist/components/layouts/NotificationsPopover.mjs.map +1 -1
  25. package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
  26. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  27. package/dist/components/navigation/CancelledSubscriptionBanner.mjs.map +1 -1
  28. package/dist/components/navigation/NavItemsContext.mjs.map +1 -1
  29. package/dist/components/navigation/ProjectSelectorPopover.mjs +4 -1
  30. package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -1
  31. package/dist/components/navigation/shells/TabsMobileShell.mjs.map +1 -1
  32. package/dist/components/navigation/subcomponents/ProjectSelector.mjs +5 -1
  33. package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -1
  34. package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
  35. package/dist/components/ui/buttons/Button.mjs.map +1 -1
  36. package/dist/components/ui/buttons/CancelButton.mjs.map +1 -1
  37. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -1
  38. package/dist/components/ui/data-display/Accordion.mjs.map +1 -1
  39. package/dist/components/ui/data-display/Badge.mjs.map +1 -1
  40. package/dist/components/ui/data-display/Card.mjs.map +1 -1
  41. package/dist/components/ui/data-display/Pagination.mjs.map +1 -1
  42. package/dist/components/ui/data-display/ScrollArea.mjs.map +1 -1
  43. package/dist/components/ui/data-display/Separator.mjs.map +1 -1
  44. package/dist/components/ui/data-display/Table.mjs.map +1 -1
  45. package/dist/components/ui/data-display/Tabs.mjs.map +1 -1
  46. package/dist/components/ui/feedback/CircularProgress.mjs.map +1 -1
  47. package/dist/components/ui/feedback/DefaultCircularProgress.mjs.map +1 -1
  48. package/dist/components/ui/feedback/LoadingOverlay.mjs.map +1 -1
  49. package/dist/components/ui/feedback/Progress.mjs.map +1 -1
  50. package/dist/components/ui/feedback/Toast.mjs.map +1 -1
  51. package/dist/components/ui/form/Calendar.mjs.map +1 -1
  52. package/dist/components/ui/form/Checkbox.mjs.map +1 -1
  53. package/dist/components/ui/form/ComboboxField.mjs.map +1 -1
  54. package/dist/components/ui/form/DatePicker.mjs.map +1 -1
  55. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -1
  56. package/dist/components/ui/form/FormField.mjs.map +1 -1
  57. package/dist/components/ui/form/Input.mjs.map +1 -1
  58. package/dist/components/ui/form/InputOtp.mjs.map +1 -1
  59. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  60. package/dist/components/ui/form/RadioGroup.mjs.map +1 -1
  61. package/dist/components/ui/form/Select.mjs +1 -1
  62. package/dist/components/ui/form/Select.mjs.map +1 -1
  63. package/dist/components/ui/form/Switch.mjs.map +1 -1
  64. package/dist/components/ui/form/Textarea.mjs.map +1 -1
  65. package/dist/components/ui/overlay/Command.mjs.map +1 -1
  66. package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
  67. package/dist/components/ui/overlay/Popover.mjs.map +1 -1
  68. package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
  69. package/dist/components/ui/overlay/Tooltip.mjs.map +1 -1
  70. package/dist/hooks/copy-to-clipboard.hook.mjs.map +1 -1
  71. package/dist/index.mjs +7 -0
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/middleware.mjs.map +1 -1
  74. package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -1
  75. package/dist/modules/accounts/actions/find-current-account.action.mjs.map +1 -1
  76. package/dist/modules/accounts/actions/list-currencies.action.mjs.map +1 -1
  77. package/dist/modules/accounts/actions/list-timezones.action.mjs.map +1 -1
  78. package/dist/modules/accounts/hooks/current-account.hook.mjs.map +1 -1
  79. package/dist/modules/accounts/hooks/useAccountManagement.mjs.map +1 -1
  80. package/dist/modules/accounts/hooks/useCurrencies.mjs.map +1 -1
  81. package/dist/modules/accounts/hooks/useTimezones.mjs.map +1 -1
  82. package/dist/modules/accounts/services/account.service.mjs.map +1 -1
  83. package/dist/modules/auth/hooks/login.hook.mjs.map +1 -1
  84. package/dist/modules/auth/hooks/logout.hook.mjs.map +1 -1
  85. package/dist/modules/auth/hooks/register.hook.mjs.map +1 -1
  86. package/dist/modules/ia-credits/actions/list-ia-credits.action.mjs.map +1 -1
  87. package/dist/modules/projects/actions/add-project-user.action.mjs.map +1 -1
  88. package/dist/modules/projects/actions/list-all-account-users.action.mjs.map +1 -1
  89. package/dist/modules/projects/actions/list-available-users.action.mjs.map +1 -1
  90. package/dist/modules/projects/actions/list-project-users.action.mjs.map +1 -1
  91. package/dist/modules/projects/actions/remove-project-user.action.mjs.map +1 -1
  92. package/dist/modules/projects/hooks/add-project-user.hook.mjs.map +1 -1
  93. package/dist/modules/projects/hooks/list-all-account-users.hook.mjs.map +1 -1
  94. package/dist/modules/projects/hooks/list-available-users.hook.mjs.map +1 -1
  95. package/dist/modules/projects/hooks/list-project-users.hook.mjs.map +1 -1
  96. package/dist/modules/projects/hooks/remove-project-user.hook.mjs.map +1 -1
  97. package/dist/modules/subscriptions/actions/find-plan-by-id.action.mjs +10 -0
  98. package/dist/modules/subscriptions/actions/find-plan-by-id.action.mjs.map +1 -0
  99. package/dist/modules/subscriptions/actions/list-subscriptions.action.mjs.map +1 -1
  100. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
  101. package/dist/modules/subscriptions/hooks/list-subscriptions.hook.mjs.map +1 -1
  102. package/dist/modules/subscriptions/hooks/use-has-plan-addon.hook.mjs +15 -0
  103. package/dist/modules/subscriptions/hooks/use-has-plan-addon.hook.mjs.map +1 -0
  104. package/dist/modules/subscriptions/hooks/use-plan-by-id.hook.mjs +15 -0
  105. package/dist/modules/subscriptions/hooks/use-plan-by-id.hook.mjs.map +1 -0
  106. package/dist/modules/subscriptions/services/plans.service.mjs +34 -0
  107. package/dist/modules/subscriptions/services/plans.service.mjs.map +1 -0
  108. package/dist/modules/subscriptions/types/plan.type.mjs +20 -0
  109. package/dist/modules/subscriptions/types/plan.type.mjs.map +1 -0
  110. package/dist/modules/users/action/find-user-by-id.action.mjs.map +1 -1
  111. package/dist/modules/users/action/list-messages.action.mjs.map +1 -1
  112. package/dist/modules/users/action/mark-all-messages-as-read.action.mjs.map +1 -1
  113. package/dist/modules/users/action/mark-message-as-read.action.mjs.map +1 -1
  114. package/dist/modules/users/hooks/messages.hook.mjs.map +1 -1
  115. package/dist/modules/whitelabel/actions/find-whitelabel-styles.action.mjs.map +1 -1
  116. package/dist/modules/whitelabel/actions/find-whitelabel.action.mjs.map +1 -1
  117. package/dist/modules/whitelabel/services/whitelabel-styles.service.mjs.map +1 -1
  118. package/dist/providers/whitelabel.provider.mjs.map +1 -1
  119. package/dist/server.mjs +4 -0
  120. package/dist/server.mjs.map +1 -1
  121. package/dist/utils/countries.mjs.map +1 -1
  122. package/dist/utils/safeServerAction.mjs.map +1 -1
  123. package/dist/utils/withAction.mjs.map +1 -1
  124. package/package.json +89 -89
  125. package/src/components/account/ConfigurationsMyAccountModal.tsx +150 -150
  126. package/src/components/account/ConfirmDeleteAccountModal.tsx +55 -55
  127. package/src/components/account/ConfirmGlobalPreferencesModal.tsx +66 -66
  128. package/src/components/account/DeleteAccountModal.tsx +130 -130
  129. package/src/components/account/DisableTwoFactorAuthModal.tsx +86 -86
  130. package/src/components/account/IsntPossibleDeleteAccountModal.tsx +49 -49
  131. package/src/components/account/TwoFactorAuthModal.tsx +223 -223
  132. package/src/components/account/hooks/useChangePhoneForm.tsx +81 -81
  133. package/src/components/account/hooks/useTwoFactorAuthForm.tsx +101 -101
  134. package/src/components/account/sections/ChangeEmailModal.tsx +225 -225
  135. package/src/components/account/sections/ChangePasswordSection.tsx +64 -64
  136. package/src/components/account/sections/ChangePhoneModal.tsx +231 -231
  137. package/src/components/account/sections/MyProfileSection.tsx +327 -327
  138. package/src/components/account/sections/PreferencesSection.tsx +273 -273
  139. package/src/components/account/sections/SecuritySection.tsx +160 -160
  140. package/src/components/embeds/CrispEmbed.tsx +101 -101
  141. package/src/components/embeds/FrillEmbed.tsx +119 -119
  142. package/src/components/layouts/AppMobileNavBar.tsx +110 -110
  143. package/src/components/layouts/AppNavBar.tsx +57 -57
  144. package/src/components/layouts/MdSideBarNavigation.tsx +56 -56
  145. package/src/components/layouts/NavBar.tsx +120 -120
  146. package/src/components/layouts/NotificationsPopover.tsx +115 -115
  147. package/src/components/layouts/ProfilePopover.tsx +167 -167
  148. package/src/components/layouts/UsersSelectorPopover.tsx +238 -238
  149. package/src/components/navigation/CancelledSubscriptionBanner.tsx +69 -69
  150. package/src/components/navigation/NavItemsContext.ts +20 -20
  151. package/src/components/navigation/ProjectSelectorPopover.tsx +157 -151
  152. package/src/components/navigation/shells/TabsMobileShell.tsx +72 -72
  153. package/src/components/navigation/subcomponents/ProjectSelector.tsx +155 -149
  154. package/src/components/pages/notifications/Notifications.tsx +160 -160
  155. package/src/components/ui/buttons/Button.tsx +57 -57
  156. package/src/components/ui/buttons/CancelButton.tsx +14 -14
  157. package/src/components/ui/buttons/CopyButton.tsx +54 -54
  158. package/src/components/ui/data-display/Accordion.tsx +81 -81
  159. package/src/components/ui/data-display/Badge.tsx +47 -47
  160. package/src/components/ui/data-display/Card.tsx +93 -93
  161. package/src/components/ui/data-display/Pagination.tsx +126 -126
  162. package/src/components/ui/data-display/ScrollArea.tsx +59 -59
  163. package/src/components/ui/data-display/Separator.tsx +29 -29
  164. package/src/components/ui/data-display/Table.tsx +117 -117
  165. package/src/components/ui/data-display/Tabs.tsx +67 -67
  166. package/src/components/ui/feedback/CircularProgress.tsx +75 -75
  167. package/src/components/ui/feedback/DefaultCircularProgress.tsx +61 -61
  168. package/src/components/ui/feedback/LoadingOverlay.tsx +31 -31
  169. package/src/components/ui/feedback/Progress.tsx +40 -40
  170. package/src/components/ui/feedback/Toast.tsx +101 -101
  171. package/src/components/ui/form/Calendar.tsx +228 -228
  172. package/src/components/ui/form/Checkbox.tsx +31 -31
  173. package/src/components/ui/form/ComboboxField.tsx +154 -154
  174. package/src/components/ui/form/DatePicker.tsx +97 -97
  175. package/src/components/ui/form/DateRangePicker.tsx +108 -108
  176. package/src/components/ui/form/FormField.tsx +131 -131
  177. package/src/components/ui/form/Input.tsx +20 -20
  178. package/src/components/ui/form/InputOtp.tsx +80 -80
  179. package/src/components/ui/form/PhoneInput.tsx +205 -205
  180. package/src/components/ui/form/RadioGroup.tsx +45 -45
  181. package/src/components/ui/form/Select.tsx +200 -200
  182. package/src/components/ui/form/Switch.tsx +32 -32
  183. package/src/components/ui/form/Textarea.tsx +19 -19
  184. package/src/components/ui/overlay/Command.tsx +185 -185
  185. package/src/components/ui/overlay/Dialog.tsx +151 -151
  186. package/src/components/ui/overlay/Popover.tsx +49 -49
  187. package/src/components/ui/overlay/Sheet.tsx +166 -166
  188. package/src/components/ui/overlay/Tooltip.tsx +62 -62
  189. package/src/hooks/copy-to-clipboard.hook.tsx +67 -67
  190. package/src/index.ts +359 -355
  191. package/src/middleware.ts +4 -4
  192. package/src/modules/accounts/actions/account-management.action.ts +90 -90
  193. package/src/modules/accounts/actions/find-current-account.action.ts +9 -9
  194. package/src/modules/accounts/actions/list-currencies.action.ts +8 -8
  195. package/src/modules/accounts/actions/list-timezones.action.ts +8 -8
  196. package/src/modules/accounts/hooks/current-account.hook.tsx +22 -22
  197. package/src/modules/accounts/hooks/useAccountManagement.ts +85 -85
  198. package/src/modules/accounts/hooks/useCurrencies.ts +15 -15
  199. package/src/modules/accounts/hooks/useTimezones.ts +15 -15
  200. package/src/modules/accounts/services/account.service.ts +236 -236
  201. package/src/modules/accounts/types.ts +214 -214
  202. package/src/modules/auth/hooks/login.hook.tsx +13 -13
  203. package/src/modules/auth/hooks/logout.hook.tsx +11 -11
  204. package/src/modules/auth/hooks/register.hook.tsx +11 -11
  205. package/src/modules/ia-credits/actions/list-ia-credits.action.ts +8 -8
  206. package/src/modules/projects/actions/add-project-user.action.ts +9 -9
  207. package/src/modules/projects/actions/list-all-account-users.action.ts +9 -9
  208. package/src/modules/projects/actions/list-available-users.action.ts +9 -9
  209. package/src/modules/projects/actions/list-project-users.action.ts +9 -9
  210. package/src/modules/projects/actions/remove-project-user.action.ts +9 -9
  211. package/src/modules/projects/hooks/add-project-user.hook.ts +69 -69
  212. package/src/modules/projects/hooks/list-all-account-users.hook.ts +34 -34
  213. package/src/modules/projects/hooks/list-available-users.hook.ts +33 -33
  214. package/src/modules/projects/hooks/list-project-users.hook.ts +33 -33
  215. package/src/modules/projects/hooks/remove-project-user.hook.ts +69 -69
  216. package/src/modules/subscriptions/actions/find-plan-by-id.action.ts +8 -0
  217. package/src/modules/subscriptions/actions/list-subscriptions.action.ts +9 -9
  218. package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +19 -19
  219. package/src/modules/subscriptions/hooks/list-subscriptions.hook.ts +17 -17
  220. package/src/modules/subscriptions/hooks/use-has-plan-addon.hook.ts +17 -0
  221. package/src/modules/subscriptions/hooks/use-plan-by-id.hook.ts +13 -0
  222. package/src/modules/subscriptions/services/plans.service.ts +36 -0
  223. package/src/modules/subscriptions/types/plan.type.ts +21 -0
  224. package/src/modules/users/action/find-user-by-id.action.ts +8 -8
  225. package/src/modules/users/action/list-messages.action.ts +12 -12
  226. package/src/modules/users/action/mark-all-messages-as-read.action.ts +8 -8
  227. package/src/modules/users/action/mark-message-as-read.action.ts +8 -8
  228. package/src/modules/users/hooks/messages.hook.ts +60 -60
  229. package/src/modules/whitelabel/actions/find-whitelabel-styles.action.ts +14 -14
  230. package/src/modules/whitelabel/actions/find-whitelabel.action.ts +23 -23
  231. package/src/modules/whitelabel/services/whitelabel-styles.service.ts +98 -98
  232. package/src/modules/whitelabel/styles-schema.ts +37 -37
  233. package/src/providers/whitelabel.provider.tsx +46 -46
  234. package/src/server.ts +2 -0
  235. package/src/utils/countries.ts +224 -224
  236. package/src/utils/safeServerAction.ts +21 -21
  237. package/src/utils/withAction.ts +13 -13
@@ -1,238 +1,238 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
- import { IconPlus, IconSettings, IconUser, IconUsers } from '@tabler/icons-react';
5
- import { UserAvatar } from "../ui/data-display/UserAvatar";
6
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
7
- import { Button } from "../ui/buttons/Button";
8
- import {
9
- Command,
10
- CommandEmpty,
11
- CommandInput,
12
- CommandItem,
13
- CommandList,
14
- } from "../ui/overlay/Command";
15
- import { Checkbox } from "../ui/form/Checkbox";
16
- import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
17
- import { cn } from "../../infra/utils/clsx";
18
- import { useListProjectUsers } from "../../modules/projects/hooks/list-project-users.hook";
19
- import { useListAllAccountUsers } from "../../modules/projects/hooks/list-all-account-users.hook";
20
- import { useAddProjectUser } from "../../modules/projects/hooks/add-project-user.hook";
21
- import { useRemoveProjectUser } from "../../modules/projects/hooks/remove-project-user.hook";
22
-
23
- export interface UsersSelectorPopoverProps {
24
- projectId?: number;
25
- side?: "top" | "bottom" | "left" | "right";
26
- align?: "start" | "center" | "end";
27
- contentClassName?: string;
28
- }
29
-
30
- function UserItemSkeleton() {
31
- return (
32
- <div className="flex items-center justify-between px-2 py-1.5 gap-2">
33
- <div className="flex items-center gap-2 flex-1">
34
- <div className="skeleton size-8 rounded-full shrink-0" />
35
- <div className="skeleton h-3.5 w-28 rounded" />
36
- </div>
37
- <div className="skeleton size-4 rounded shrink-0" />
38
- </div>
39
- );
40
- }
41
-
42
- const GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ?? "";
43
-
44
- export function UsersSelectorPopover({
45
- projectId,
46
- side,
47
- align,
48
- contentClassName,
49
- }: UsersSelectorPopoverProps) {
50
- const [open, setOpen] = useState(false);
51
- const [hoveredId, setHoveredId] = useState<number | null>(null);
52
- const [searchInput, setSearchInput] = useState("");
53
- const [search, setSearch] = useState("");
54
-
55
- const id = projectId ?? 0;
56
-
57
- useEffect(() => {
58
- const timer = setTimeout(() => setSearch(searchInput), 300);
59
- return () => clearTimeout(timer);
60
- }, [searchInput]);
61
-
62
- const accountUsers = useListAllAccountUsers(search, id || undefined);
63
- const projectUsers = useListProjectUsers(id, "");
64
- const addMutation = useAddProjectUser(id);
65
- const removeMutation = useRemoveProjectUser(id);
66
-
67
- const allUsers = accountUsers.data?.pages.flatMap((p) => p.data) ?? [];
68
- const isLoading = accountUsers.isLoading;
69
- const isFetchingMore = accountUsers.isFetchingNextPage;
70
- const totalInProject = projectUsers.data?.pages[0]?.total ?? 0;
71
-
72
- const toggleUser = (userId: number, inProject: boolean, removable: boolean) => {
73
- if (inProject) {
74
- if (!removable) return;
75
- removeMutation.mutate(userId);
76
- } else {
77
- addMutation.mutate(userId);
78
- }
79
- };
80
-
81
- const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
82
- const target = e.currentTarget;
83
- const nearBottom =
84
- target.scrollTop + target.clientHeight >= target.scrollHeight - 50;
85
- if (!nearBottom) return;
86
- if (accountUsers.hasNextPage && !accountUsers.isFetchingNextPage) {
87
- accountUsers.fetchNextPage();
88
- }
89
- };
90
-
91
- return (
92
- <Popover open={open} onOpenChange={setOpen}>
93
- <PopoverTrigger className="cursor-pointer">
94
- <div className="flex items-center gap-2 text-gray-950 px-3 py-2 border border-gray-200 rounded-full hover:border-gray-950">
95
- <span className="paragraph-small-semibold">{totalInProject}</span>
96
- <IconUser size={16} />
97
- </div>
98
- </PopoverTrigger>
99
-
100
- <PopoverContent
101
- className={cn(
102
- "absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200",
103
- contentClassName,
104
- )}
105
- side={side}
106
- align={align}
107
- sideOffset={8}
108
- >
109
- <div className="flex flex-col p-3 gap-2">
110
- <div className="flex items-center justify-between">
111
- <span className="paragraph-small-semibold">Usuários</span>
112
- <div className="flex items-center gap-1.5">
113
- <Button
114
- variant="secondary"
115
- className="h-8 paragraph-small-semibold text-gray-950"
116
- onClick={() => {
117
- window.location.href = `${GAPPS_URL}/my-teams?createNew=true`;
118
- }}
119
- >
120
- <IconPlus size={16} />
121
- Adicionar
122
- </Button>
123
- <Tooltip>
124
- <TooltipTrigger asChild>
125
- <Button
126
- variant="secondary"
127
- className="p-0 size-8"
128
- onClick={() => {
129
- window.location.href = `${GAPPS_URL}/my-teams`;
130
- }}
131
- >
132
- <IconSettings size={18} />
133
- </Button>
134
- </TooltipTrigger>
135
- <TooltipContent className="z-[1002]">
136
- Visualizar todos usuários
137
- </TooltipContent>
138
- </Tooltip>
139
- </div>
140
- </div>
141
-
142
- <Command shouldFilter={false} className="gap-2" value="">
143
- <CommandInput
144
- placeholder="Busque aqui"
145
- value={searchInput}
146
- onValueChange={setSearchInput}
147
- />
148
- <CommandList
149
- className="custom-scrollbar paragraph-small-medium text-gray-600 h-[162px]"
150
- onScroll={handleScroll}
151
- >
152
- {isLoading ? (
153
- <>
154
- <UserItemSkeleton />
155
- <UserItemSkeleton />
156
- <UserItemSkeleton />
157
- </>
158
- ) : allUsers.length === 0 ? (
159
- <div className="flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4">
160
- <IconUsers size={28} className="text-gray-300" />
161
- <span className="paragraph-small-medium text-center text-gray-500">
162
- Nenhum usuário encontrado.
163
- </span>
164
- </div>
165
- ) : (
166
- <>
167
- <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
168
- {allUsers.map((user) => {
169
- const inProject = user.in_project ?? false;
170
- const removable =
171
- inProject &&
172
- user.profile !== "owner" &&
173
- user.profile !== "admin";
174
- const interactive = !inProject || removable;
175
-
176
- return (
177
- <CommandItem
178
- key={user.id}
179
- value={user.name || String(user.id)}
180
- autoFocus={false}
181
- onPointerEnter={() => setHoveredId(user.id)}
182
- onPointerLeave={() => setHoveredId(null)}
183
- onFocus={() => setHoveredId(user.id)}
184
- onBlur={() => setHoveredId(null)}
185
- onSelect={() => toggleUser(user.id, inProject, removable)}
186
- className={interactive ? "cursor-pointer" : "cursor-default"}
187
- >
188
- <div className="flex items-center justify-between w-full">
189
- <div className="flex items-center gap-2 min-w-0">
190
- <UserAvatar
191
- photo={user.photo}
192
- name={user.name ?? undefined}
193
- size={32}
194
- />
195
- <span className="truncate">{user.name}</span>
196
- </div>
197
- <Tooltip open={interactive && hoveredId === user.id}>
198
- <TooltipTrigger
199
- onClick={(e) => e.stopPropagation()}
200
- onPointerDown={(e) => e.stopPropagation()}
201
- >
202
- <Checkbox
203
- checked={inProject}
204
- disabled={!interactive}
205
- onCheckedChange={() =>
206
- toggleUser(user.id, inProject, removable)
207
- }
208
- className={cn(
209
- interactive && hoveredId === user.id
210
- ? "border-gray-950"
211
- : "",
212
- "disabled:opacity-10",
213
- )}
214
- />
215
- </TooltipTrigger>
216
- <TooltipContent className="z-[1002]">
217
- {inProject ? "Remover do projeto" : "Adicionar ao projeto"}
218
- </TooltipContent>
219
- </Tooltip>
220
- </div>
221
- </CommandItem>
222
- );
223
- })}
224
- {isFetchingMore && (
225
- <>
226
- <UserItemSkeleton />
227
- <UserItemSkeleton />
228
- </>
229
- )}
230
- </>
231
- )}
232
- </CommandList>
233
- </Command>
234
- </div>
235
- </PopoverContent>
236
- </Popover>
237
- );
238
- }
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { IconPlus, IconSettings, IconUser, IconUsers } from '@tabler/icons-react';
5
+ import { UserAvatar } from "../ui/data-display/UserAvatar";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
7
+ import { Button } from "../ui/buttons/Button";
8
+ import {
9
+ Command,
10
+ CommandEmpty,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList,
14
+ } from "../ui/overlay/Command";
15
+ import { Checkbox } from "../ui/form/Checkbox";
16
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
17
+ import { cn } from "../../infra/utils/clsx";
18
+ import { useListProjectUsers } from "../../modules/projects/hooks/list-project-users.hook";
19
+ import { useListAllAccountUsers } from "../../modules/projects/hooks/list-all-account-users.hook";
20
+ import { useAddProjectUser } from "../../modules/projects/hooks/add-project-user.hook";
21
+ import { useRemoveProjectUser } from "../../modules/projects/hooks/remove-project-user.hook";
22
+
23
+ export interface UsersSelectorPopoverProps {
24
+ projectId?: number;
25
+ side?: "top" | "bottom" | "left" | "right";
26
+ align?: "start" | "center" | "end";
27
+ contentClassName?: string;
28
+ }
29
+
30
+ function UserItemSkeleton() {
31
+ return (
32
+ <div className="flex items-center justify-between px-2 py-1.5 gap-2">
33
+ <div className="flex items-center gap-2 flex-1">
34
+ <div className="skeleton size-8 rounded-full shrink-0" />
35
+ <div className="skeleton h-3.5 w-28 rounded" />
36
+ </div>
37
+ <div className="skeleton size-4 rounded shrink-0" />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ const GAPPS_URL = process.env.NEXT_PUBLIC_GREAT_APPS ?? "";
43
+
44
+ export function UsersSelectorPopover({
45
+ projectId,
46
+ side,
47
+ align,
48
+ contentClassName,
49
+ }: UsersSelectorPopoverProps) {
50
+ const [open, setOpen] = useState(false);
51
+ const [hoveredId, setHoveredId] = useState<number | null>(null);
52
+ const [searchInput, setSearchInput] = useState("");
53
+ const [search, setSearch] = useState("");
54
+
55
+ const id = projectId ?? 0;
56
+
57
+ useEffect(() => {
58
+ const timer = setTimeout(() => setSearch(searchInput), 300);
59
+ return () => clearTimeout(timer);
60
+ }, [searchInput]);
61
+
62
+ const accountUsers = useListAllAccountUsers(search, id || undefined);
63
+ const projectUsers = useListProjectUsers(id, "");
64
+ const addMutation = useAddProjectUser(id);
65
+ const removeMutation = useRemoveProjectUser(id);
66
+
67
+ const allUsers = accountUsers.data?.pages.flatMap((p) => p.data) ?? [];
68
+ const isLoading = accountUsers.isLoading;
69
+ const isFetchingMore = accountUsers.isFetchingNextPage;
70
+ const totalInProject = projectUsers.data?.pages[0]?.total ?? 0;
71
+
72
+ const toggleUser = (userId: number, inProject: boolean, removable: boolean) => {
73
+ if (inProject) {
74
+ if (!removable) return;
75
+ removeMutation.mutate(userId);
76
+ } else {
77
+ addMutation.mutate(userId);
78
+ }
79
+ };
80
+
81
+ const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
82
+ const target = e.currentTarget;
83
+ const nearBottom =
84
+ target.scrollTop + target.clientHeight >= target.scrollHeight - 50;
85
+ if (!nearBottom) return;
86
+ if (accountUsers.hasNextPage && !accountUsers.isFetchingNextPage) {
87
+ accountUsers.fetchNextPage();
88
+ }
89
+ };
90
+
91
+ return (
92
+ <Popover open={open} onOpenChange={setOpen}>
93
+ <PopoverTrigger className="cursor-pointer">
94
+ <div className="flex items-center gap-2 text-gray-950 px-3 py-2 border border-gray-200 rounded-full hover:border-gray-950">
95
+ <span className="paragraph-small-semibold">{totalInProject}</span>
96
+ <IconUser size={16} />
97
+ </div>
98
+ </PopoverTrigger>
99
+
100
+ <PopoverContent
101
+ className={cn(
102
+ "absolute right-[-88px] w-[273px] p-0 bg-white rounded-2xl shadow-md border border-gray-200",
103
+ contentClassName,
104
+ )}
105
+ side={side}
106
+ align={align}
107
+ sideOffset={8}
108
+ >
109
+ <div className="flex flex-col p-3 gap-2">
110
+ <div className="flex items-center justify-between">
111
+ <span className="paragraph-small-semibold">Usuários</span>
112
+ <div className="flex items-center gap-1.5">
113
+ <Button
114
+ variant="secondary"
115
+ className="h-8 paragraph-small-semibold text-gray-950"
116
+ onClick={() => {
117
+ window.location.href = `${GAPPS_URL}/my-teams?createNew=true`;
118
+ }}
119
+ >
120
+ <IconPlus size={16} />
121
+ Adicionar
122
+ </Button>
123
+ <Tooltip>
124
+ <TooltipTrigger asChild>
125
+ <Button
126
+ variant="secondary"
127
+ className="p-0 size-8"
128
+ onClick={() => {
129
+ window.location.href = `${GAPPS_URL}/my-teams`;
130
+ }}
131
+ >
132
+ <IconSettings size={18} />
133
+ </Button>
134
+ </TooltipTrigger>
135
+ <TooltipContent className="z-[1002]">
136
+ Visualizar todos usuários
137
+ </TooltipContent>
138
+ </Tooltip>
139
+ </div>
140
+ </div>
141
+
142
+ <Command shouldFilter={false} className="gap-2" value="">
143
+ <CommandInput
144
+ placeholder="Busque aqui"
145
+ value={searchInput}
146
+ onValueChange={setSearchInput}
147
+ />
148
+ <CommandList
149
+ className="custom-scrollbar paragraph-small-medium text-gray-600 h-[162px]"
150
+ onScroll={handleScroll}
151
+ >
152
+ {isLoading ? (
153
+ <>
154
+ <UserItemSkeleton />
155
+ <UserItemSkeleton />
156
+ <UserItemSkeleton />
157
+ </>
158
+ ) : allUsers.length === 0 ? (
159
+ <div className="flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4">
160
+ <IconUsers size={28} className="text-gray-300" />
161
+ <span className="paragraph-small-medium text-center text-gray-500">
162
+ Nenhum usuário encontrado.
163
+ </span>
164
+ </div>
165
+ ) : (
166
+ <>
167
+ <CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
168
+ {allUsers.map((user) => {
169
+ const inProject = user.in_project ?? false;
170
+ const removable =
171
+ inProject &&
172
+ user.profile !== "owner" &&
173
+ user.profile !== "admin";
174
+ const interactive = !inProject || removable;
175
+
176
+ return (
177
+ <CommandItem
178
+ key={user.id}
179
+ value={user.name || String(user.id)}
180
+ autoFocus={false}
181
+ onPointerEnter={() => setHoveredId(user.id)}
182
+ onPointerLeave={() => setHoveredId(null)}
183
+ onFocus={() => setHoveredId(user.id)}
184
+ onBlur={() => setHoveredId(null)}
185
+ onSelect={() => toggleUser(user.id, inProject, removable)}
186
+ className={interactive ? "cursor-pointer" : "cursor-default"}
187
+ >
188
+ <div className="flex items-center justify-between w-full">
189
+ <div className="flex items-center gap-2 min-w-0">
190
+ <UserAvatar
191
+ photo={user.photo}
192
+ name={user.name ?? undefined}
193
+ size={32}
194
+ />
195
+ <span className="truncate">{user.name}</span>
196
+ </div>
197
+ <Tooltip open={interactive && hoveredId === user.id}>
198
+ <TooltipTrigger
199
+ onClick={(e) => e.stopPropagation()}
200
+ onPointerDown={(e) => e.stopPropagation()}
201
+ >
202
+ <Checkbox
203
+ checked={inProject}
204
+ disabled={!interactive}
205
+ onCheckedChange={() =>
206
+ toggleUser(user.id, inProject, removable)
207
+ }
208
+ className={cn(
209
+ interactive && hoveredId === user.id
210
+ ? "border-gray-950"
211
+ : "",
212
+ "disabled:opacity-10",
213
+ )}
214
+ />
215
+ </TooltipTrigger>
216
+ <TooltipContent className="z-[1002]">
217
+ {inProject ? "Remover do projeto" : "Adicionar ao projeto"}
218
+ </TooltipContent>
219
+ </Tooltip>
220
+ </div>
221
+ </CommandItem>
222
+ );
223
+ })}
224
+ {isFetchingMore && (
225
+ <>
226
+ <UserItemSkeleton />
227
+ <UserItemSkeleton />
228
+ </>
229
+ )}
230
+ </>
231
+ )}
232
+ </CommandList>
233
+ </Command>
234
+ </div>
235
+ </PopoverContent>
236
+ </Popover>
237
+ );
238
+ }
@@ -1,69 +1,69 @@
1
- 'use client';
2
-
3
- import { useState, useEffect, useCallback } from 'react';
4
- import { IconAlertOctagon, IconX } from '@tabler/icons-react';
5
- import { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';
6
-
7
- function daysBetween(a: Date, b: Date): number {
8
- const msPerDay = 24 * 60 * 60 * 1000;
9
- const da = new Date(a.getFullYear(), a.getMonth(), a.getDate());
10
- const db = new Date(b.getFullYear(), b.getMonth(), b.getDate());
11
- return Math.round((db.getTime() - da.getTime()) / msPerDay);
12
- }
13
-
14
- interface CancelledSubscriptionBannerProps {
15
- onReactivate?: () => void;
16
- }
17
-
18
- export function CancelledSubscriptionBanner({ onReactivate }: CancelledSubscriptionBannerProps) {
19
- const { data: { data: [subscription] = [] } = {} } = useActiveSubscription();
20
- const [visible, setVisible] = useState(true);
21
- const [animated, setAnimated] = useState(false);
22
-
23
- useEffect(() => {
24
- const timer = setTimeout(() => setAnimated(true), 50);
25
- return () => clearTimeout(timer);
26
- }, []);
27
-
28
- const dismiss = useCallback(() => setVisible(false), []);
29
-
30
- const cancellationDate = subscription?.date_cancellation;
31
- if (!cancellationDate || !visible) return null;
32
-
33
- const today = new Date();
34
- const daysSinceCancellation = daysBetween(new Date(cancellationDate), today);
35
- if (daysSinceCancellation < 0 || daysSinceCancellation > 5) return null;
36
-
37
- const daysRemaining = Math.max(0, 5 - daysSinceCancellation);
38
-
39
- return (
40
- <div className="fixed top-[80px] md:top-0 lg:left-[273px] left-0 right-0 z-[60] flex justify-center pointer-events-none px-4 lg:px-0">
41
- <div
42
- className={`flex flex-col sm:flex-row items-start sm:items-center justify-between bg-red-500 rounded-lg px-3 py-2 sm:py-1 w-full lg:w-[805px] max-w-full pointer-events-auto transition-transform duration-500 ease-out gap-2 sm:gap-0 ${animated ? 'translate-y-0 md:translate-y-9' : '-translate-y-full'}`}
43
- >
44
- {daysRemaining > 0 && (
45
- <div className="flex items-center gap-2 flex-1 w-full sm:w-auto">
46
- <IconAlertOctagon size={16} className="text-white shrink-0" />
47
- <span className="paragraph-xsmall-semibold text-white">
48
- Assinatura cancelada! Em {daysRemaining} dias suas páginas serão tiradas do ar!
49
- </span>
50
- </div>
51
- )}
52
- <div className="flex items-center gap-1.5 w-full sm:w-auto justify-between sm:justify-start">
53
- <button
54
- type="button"
55
- className="flex items-center justify-center p-1.5 cursor-pointer"
56
- onClick={onReactivate}
57
- >
58
- <span className="paragraph-xsmall-semibold text-white underline">
59
- Reativar assinatura
60
- </span>
61
- </button>
62
- <button type="button" className="cursor-pointer shrink-0" onClick={dismiss}>
63
- <IconX size={18} className="text-red-200" />
64
- </button>
65
- </div>
66
- </div>
67
- </div>
68
- );
69
- }
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ import { IconAlertOctagon, IconX } from '@tabler/icons-react';
5
+ import { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';
6
+
7
+ function daysBetween(a: Date, b: Date): number {
8
+ const msPerDay = 24 * 60 * 60 * 1000;
9
+ const da = new Date(a.getFullYear(), a.getMonth(), a.getDate());
10
+ const db = new Date(b.getFullYear(), b.getMonth(), b.getDate());
11
+ return Math.round((db.getTime() - da.getTime()) / msPerDay);
12
+ }
13
+
14
+ interface CancelledSubscriptionBannerProps {
15
+ onReactivate?: () => void;
16
+ }
17
+
18
+ export function CancelledSubscriptionBanner({ onReactivate }: CancelledSubscriptionBannerProps) {
19
+ const { data: { data: [subscription] = [] } = {} } = useActiveSubscription();
20
+ const [visible, setVisible] = useState(true);
21
+ const [animated, setAnimated] = useState(false);
22
+
23
+ useEffect(() => {
24
+ const timer = setTimeout(() => setAnimated(true), 50);
25
+ return () => clearTimeout(timer);
26
+ }, []);
27
+
28
+ const dismiss = useCallback(() => setVisible(false), []);
29
+
30
+ const cancellationDate = subscription?.date_cancellation;
31
+ if (!cancellationDate || !visible) return null;
32
+
33
+ const today = new Date();
34
+ const daysSinceCancellation = daysBetween(new Date(cancellationDate), today);
35
+ if (daysSinceCancellation < 0 || daysSinceCancellation > 5) return null;
36
+
37
+ const daysRemaining = Math.max(0, 5 - daysSinceCancellation);
38
+
39
+ return (
40
+ <div className="fixed top-[80px] md:top-0 lg:left-[273px] left-0 right-0 z-[60] flex justify-center pointer-events-none px-4 lg:px-0">
41
+ <div
42
+ className={`flex flex-col sm:flex-row items-start sm:items-center justify-between bg-red-500 rounded-lg px-3 py-2 sm:py-1 w-full lg:w-[805px] max-w-full pointer-events-auto transition-transform duration-500 ease-out gap-2 sm:gap-0 ${animated ? 'translate-y-0 md:translate-y-9' : '-translate-y-full'}`}
43
+ >
44
+ {daysRemaining > 0 && (
45
+ <div className="flex items-center gap-2 flex-1 w-full sm:w-auto">
46
+ <IconAlertOctagon size={16} className="text-white shrink-0" />
47
+ <span className="paragraph-xsmall-semibold text-white">
48
+ Assinatura cancelada! Em {daysRemaining} dias suas páginas serão tiradas do ar!
49
+ </span>
50
+ </div>
51
+ )}
52
+ <div className="flex items-center gap-1.5 w-full sm:w-auto justify-between sm:justify-start">
53
+ <button
54
+ type="button"
55
+ className="flex items-center justify-center p-1.5 cursor-pointer"
56
+ onClick={onReactivate}
57
+ >
58
+ <span className="paragraph-xsmall-semibold text-white underline">
59
+ Reativar assinatura
60
+ </span>
61
+ </button>
62
+ <button type="button" className="cursor-pointer shrink-0" onClick={dismiss}>
63
+ <IconX size={18} className="text-red-200" />
64
+ </button>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ );
69
+ }
@@ -1,20 +1,20 @@
1
- 'use client';
2
-
3
- import { createContext, useContext } from 'react';
4
- import type { NavItemConfig } from './subcomponents/NavItems';
5
-
6
- interface NavItemsContextValue {
7
- navItems: NavItemConfig[];
8
- setNavItems: (items: NavItemConfig[]) => void;
9
- mobileVariant: 'sheet' | 'tabs';
10
- }
11
-
12
- export const NavItemsContext = createContext<NavItemsContextValue>({
13
- navItems: [],
14
- setNavItems: () => {},
15
- mobileVariant: 'sheet',
16
- });
17
-
18
- export function useNavItemsContext(): NavItemsContextValue {
19
- return useContext(NavItemsContext);
20
- }
1
+ 'use client';
2
+
3
+ import { createContext, useContext } from 'react';
4
+ import type { NavItemConfig } from './subcomponents/NavItems';
5
+
6
+ interface NavItemsContextValue {
7
+ navItems: NavItemConfig[];
8
+ setNavItems: (items: NavItemConfig[]) => void;
9
+ mobileVariant: 'sheet' | 'tabs';
10
+ }
11
+
12
+ export const NavItemsContext = createContext<NavItemsContextValue>({
13
+ navItems: [],
14
+ setNavItems: () => {},
15
+ mobileVariant: 'sheet',
16
+ });
17
+
18
+ export function useNavItemsContext(): NavItemsContextValue {
19
+ return useContext(NavItemsContext);
20
+ }