@greatapps/common 1.1.218 → 1.1.221

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 (117) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +6 -6
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
  3. package/dist/components/account/ConfirmDeleteAccountModal.mjs +2 -2
  4. package/dist/components/account/ConfirmDeleteAccountModal.mjs.map +1 -1
  5. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs +2 -2
  6. package/dist/components/account/ConfirmGlobalPreferencesModal.mjs.map +1 -1
  7. package/dist/components/account/DeleteAccountModal.mjs +3 -3
  8. package/dist/components/account/DeleteAccountModal.mjs.map +1 -1
  9. package/dist/components/account/DisableTwoFactorAuthModal.mjs +2 -2
  10. package/dist/components/account/DisableTwoFactorAuthModal.mjs.map +1 -1
  11. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs +2 -2
  12. package/dist/components/account/IsntPossibleDeleteAccountModal.mjs.map +1 -1
  13. package/dist/components/account/TwoFactorAuthModal.mjs +6 -6
  14. package/dist/components/account/TwoFactorAuthModal.mjs.map +1 -1
  15. package/dist/components/account/sections/ChangeEmailModal.mjs +6 -6
  16. package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
  17. package/dist/components/account/sections/ChangePasswordSection.mjs +4 -4
  18. package/dist/components/account/sections/ChangePasswordSection.mjs.map +1 -1
  19. package/dist/components/account/sections/ChangePhoneModal.mjs +5 -5
  20. package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
  21. package/dist/components/account/sections/MyProfileSection.mjs +3 -3
  22. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  23. package/dist/components/account/sections/PreferencesSection.mjs +6 -6
  24. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
  25. package/dist/components/account/sections/SecuritySection.mjs +3 -3
  26. package/dist/components/account/sections/SecuritySection.mjs.map +1 -1
  27. package/dist/components/layouts/AppMobileNavBar.mjs +0 -1
  28. package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
  29. package/dist/components/layouts/MdSideBarNavigation.mjs +2 -2
  30. package/dist/components/layouts/MdSideBarNavigation.mjs.map +1 -1
  31. package/dist/components/layouts/NavBar.mjs +2 -2
  32. package/dist/components/layouts/NavBar.mjs.map +1 -1
  33. package/dist/components/layouts/NotificationsPopover.mjs +4 -4
  34. package/dist/components/layouts/NotificationsPopover.mjs.map +1 -1
  35. package/dist/components/layouts/ProfilePopover.mjs +7 -10
  36. package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
  37. package/dist/components/layouts/UsersSelectorPopover.mjs +5 -5
  38. package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
  39. package/dist/components/navigation/ProjectSelectorPopover.mjs +6 -6
  40. package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -1
  41. package/dist/components/navigation/subcomponents/ProjectSelector.mjs +5 -5
  42. package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -1
  43. package/dist/components/pages/notifications/Notifications.mjs +4 -4
  44. package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
  45. package/dist/components/ui/buttons/CopyButton.mjs +2 -2
  46. package/dist/components/ui/buttons/CopyButton.mjs.map +1 -1
  47. package/dist/components/ui/data-display/Accordion.mjs +2 -2
  48. package/dist/components/ui/data-display/Accordion.mjs.map +1 -1
  49. package/dist/components/ui/data-display/Pagination.mjs +7 -7
  50. package/dist/components/ui/data-display/Pagination.mjs.map +1 -1
  51. package/dist/components/ui/feedback/Toast.mjs +7 -7
  52. package/dist/components/ui/feedback/Toast.mjs.map +1 -1
  53. package/dist/components/ui/form/Calendar.mjs +7 -7
  54. package/dist/components/ui/form/Calendar.mjs.map +1 -1
  55. package/dist/components/ui/form/Checkbox.mjs +2 -2
  56. package/dist/components/ui/form/Checkbox.mjs.map +1 -1
  57. package/dist/components/ui/form/ComboboxField.mjs +4 -4
  58. package/dist/components/ui/form/ComboboxField.mjs.map +1 -1
  59. package/dist/components/ui/form/DateRangePicker.mjs +2 -2
  60. package/dist/components/ui/form/DateRangePicker.mjs.map +1 -1
  61. package/dist/components/ui/form/FormField.mjs +3 -3
  62. package/dist/components/ui/form/FormField.mjs.map +1 -1
  63. package/dist/components/ui/form/InputOtp.mjs +2 -2
  64. package/dist/components/ui/form/InputOtp.mjs.map +1 -1
  65. package/dist/components/ui/form/RadioGroup.mjs +2 -2
  66. package/dist/components/ui/form/RadioGroup.mjs.map +1 -1
  67. package/dist/components/ui/form/Select.mjs +5 -5
  68. package/dist/components/ui/form/Select.mjs.map +1 -1
  69. package/dist/components/ui/form/SwitchOptionFieldWithIcon.mjs.map +1 -1
  70. package/dist/components/ui/overlay/Command.mjs +2 -2
  71. package/dist/components/ui/overlay/Command.mjs.map +1 -1
  72. package/dist/components/ui/overlay/Dialog.mjs +2 -2
  73. package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
  74. package/dist/components/ui/overlay/Sheet.mjs +2 -2
  75. package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
  76. package/dist/components/widgets/notifications/NotificationCard.mjs +2 -2
  77. package/dist/components/widgets/notifications/NotificationCard.mjs.map +1 -1
  78. package/package.json +1 -2
  79. package/src/components/account/ConfigurationsMyAccountModal.tsx +150 -150
  80. package/src/components/account/ConfirmDeleteAccountModal.tsx +2 -2
  81. package/src/components/account/ConfirmGlobalPreferencesModal.tsx +66 -66
  82. package/src/components/account/DeleteAccountModal.tsx +3 -3
  83. package/src/components/account/DisableTwoFactorAuthModal.tsx +86 -86
  84. package/src/components/account/IsntPossibleDeleteAccountModal.tsx +2 -2
  85. package/src/components/account/TwoFactorAuthModal.tsx +223 -223
  86. package/src/components/account/sections/ChangeEmailModal.tsx +225 -225
  87. package/src/components/account/sections/ChangePasswordSection.tsx +64 -64
  88. package/src/components/account/sections/ChangePhoneModal.tsx +5 -5
  89. package/src/components/account/sections/MyProfileSection.tsx +327 -327
  90. package/src/components/account/sections/PreferencesSection.tsx +273 -273
  91. package/src/components/account/sections/SecuritySection.tsx +160 -160
  92. package/src/components/layouts/AppMobileNavBar.tsx +0 -1
  93. package/src/components/layouts/MdSideBarNavigation.tsx +54 -54
  94. package/src/components/layouts/NavBar.tsx +2 -2
  95. package/src/components/layouts/NotificationsPopover.tsx +115 -115
  96. package/src/components/layouts/ProfilePopover.tsx +179 -184
  97. package/src/components/layouts/UsersSelectorPopover.tsx +5 -5
  98. package/src/components/navigation/ProjectSelectorPopover.tsx +151 -151
  99. package/src/components/navigation/subcomponents/ProjectSelector.tsx +149 -149
  100. package/src/components/pages/notifications/Notifications.tsx +4 -4
  101. package/src/components/ui/buttons/CopyButton.tsx +2 -2
  102. package/src/components/ui/data-display/Accordion.tsx +2 -2
  103. package/src/components/ui/data-display/Pagination.tsx +7 -7
  104. package/src/components/ui/feedback/Toast.tsx +7 -7
  105. package/src/components/ui/form/Calendar.tsx +7 -7
  106. package/src/components/ui/form/Checkbox.tsx +2 -2
  107. package/src/components/ui/form/ComboboxField.tsx +154 -154
  108. package/src/components/ui/form/DateRangePicker.tsx +2 -2
  109. package/src/components/ui/form/FormField.tsx +131 -130
  110. package/src/components/ui/form/InputOtp.tsx +2 -2
  111. package/src/components/ui/form/RadioGroup.tsx +2 -2
  112. package/src/components/ui/form/Select.tsx +5 -5
  113. package/src/components/ui/form/SwitchOptionFieldWithIcon.tsx +2 -2
  114. package/src/components/ui/overlay/Command.tsx +2 -2
  115. package/src/components/ui/overlay/Dialog.tsx +2 -2
  116. package/src/components/ui/overlay/Sheet.tsx +2 -2
  117. package/src/components/widgets/notifications/NotificationCard.tsx +68 -68
@@ -1,115 +1,115 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { Bell } from "lucide-react";
5
- import * as PopoverPrimitive from "@radix-ui/react-popover";
6
- import { Popover } from "../ui/overlay/Popover";
7
- import { Button } from "../ui/buttons/Button";
8
- import NotificationCard from "../widgets/notifications/NotificationCard";
9
- import { cn } from "../../infra/utils/clsx";
10
- import { useMessages, useMarkAllMessagesAsRead } from "../../modules/users/hooks/messages.hook";
11
- import { formatDistanceToNow } from "date-fns";
12
- import { ptBR } from "date-fns/locale";
13
-
14
- export interface NotificationsPopoverProps {
15
- onViewAll?: () => void;
16
- side?: "top" | "bottom" | "left" | "right";
17
- align?: "start" | "center" | "end";
18
- contentClassName?: string;
19
- badgeClassName?: string;
20
- }
21
-
22
- function NotificationsPopover({
23
- onViewAll,
24
- side,
25
- align,
26
- contentClassName,
27
- badgeClassName = "bg-pages-400",
28
- }: NotificationsPopoverProps) {
29
- const [open, setOpen] = useState(false);
30
- const { data: messagesData } = useMessages({ type: 'notification', readed: true, limit: 5 });
31
- const { mutate: markAllAsRead } = useMarkAllMessagesAsRead();
32
-
33
- const messages = messagesData?.data ?? [];
34
- const totalUnread = messagesData?.totalUnread ?? 0;
35
-
36
- const handleOpenChange = (isOpen: boolean) => {
37
- setOpen(isOpen);
38
- if (isOpen && totalUnread > 0) {
39
- markAllAsRead();
40
- }
41
- };
42
-
43
- return (
44
- <Popover open={open} onOpenChange={handleOpenChange}>
45
- <PopoverPrimitive.Trigger className="cursor-pointer">
46
- <div
47
- className={`relative rounded-full p-2.5 ${open ? "bg-gray-100" : "hover:bg-gray-100"}`}
48
- >
49
- <Bell size={20} className="text-gray-500" />
50
- {totalUnread > 0 && (
51
- <span
52
- className={cn(
53
- "absolute top-2 right-2 size-2.5 rounded-full",
54
- badgeClassName,
55
- )}
56
- />
57
- )}
58
- </div>
59
- </PopoverPrimitive.Trigger>
60
- <PopoverPrimitive.Portal>
61
- <PopoverPrimitive.Content
62
- className={cn(
63
- "absolute bottom-2 left-[26px] z-[9999] w-[308px] p-0 pt-4 bg-white rounded-2xl shadow-md border border-gray-200",
64
- contentClassName,
65
- )}
66
- side={side}
67
- align={align}
68
- sideOffset={8}
69
- >
70
- <span className="paragraph-small-semibold text-gray-950 px-4">
71
- Últimas notificações
72
- </span>
73
-
74
- {messages.length > 0 ? (
75
- messages.map((message, index) => (
76
- <NotificationCard
77
- key={message.id}
78
- icon={<Bell size={16} />}
79
- iconBgColor="bg-gray-100"
80
- title={message.subject}
81
- time={formatDistanceToNow(new Date(message.datetime_add), {
82
- addSuffix: true,
83
- locale: ptBR,
84
- })}
85
- gapPx={6}
86
- showBorder={index < messages.length - 1}
87
- />
88
- ))
89
- ) : (
90
- <div className="flex flex-col items-center gap-4 pt-[41.5px] pb-[33.5px]">
91
- <div className="bg-gray-50 rounded-md p-2.5">
92
- <Bell size={20} color="var(--color-gray-950)" />
93
- </div>
94
- <span className="paragraph-small-medium text-gray-950">
95
- Sem notificações por enquanto
96
- </span>
97
- </div>
98
- )}
99
-
100
- {onViewAll && (
101
- <Button
102
- variant="secondary"
103
- className="h-8! mt-2 mb-4 mx-4 paragraph-small-semibold"
104
- onClick={onViewAll}
105
- >
106
- Todas as notificações
107
- </Button>
108
- )}
109
- </PopoverPrimitive.Content>
110
- </PopoverPrimitive.Portal>
111
- </Popover>
112
- );
113
- }
114
-
115
- export { NotificationsPopover };
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { IconBell } from '@tabler/icons-react';
5
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
6
+ import { Popover } from "../ui/overlay/Popover";
7
+ import { Button } from "../ui/buttons/Button";
8
+ import NotificationCard from "../widgets/notifications/NotificationCard";
9
+ import { cn } from "../../infra/utils/clsx";
10
+ import { useMessages, useMarkAllMessagesAsRead } from "../../modules/users/hooks/messages.hook";
11
+ import { formatDistanceToNow } from "date-fns";
12
+ import { ptBR } from "date-fns/locale";
13
+
14
+ export interface NotificationsPopoverProps {
15
+ onViewAll?: () => void;
16
+ side?: "top" | "bottom" | "left" | "right";
17
+ align?: "start" | "center" | "end";
18
+ contentClassName?: string;
19
+ badgeClassName?: string;
20
+ }
21
+
22
+ function NotificationsPopover({
23
+ onViewAll,
24
+ side,
25
+ align,
26
+ contentClassName,
27
+ badgeClassName = "bg-pages-400",
28
+ }: NotificationsPopoverProps) {
29
+ const [open, setOpen] = useState(false);
30
+ const { data: messagesData } = useMessages({ type: 'notification', readed: true, limit: 5 });
31
+ const { mutate: markAllAsRead } = useMarkAllMessagesAsRead();
32
+
33
+ const messages = messagesData?.data ?? [];
34
+ const totalUnread = messagesData?.totalUnread ?? 0;
35
+
36
+ const handleOpenChange = (isOpen: boolean) => {
37
+ setOpen(isOpen);
38
+ if (isOpen && totalUnread > 0) {
39
+ markAllAsRead();
40
+ }
41
+ };
42
+
43
+ return (
44
+ <Popover open={open} onOpenChange={handleOpenChange}>
45
+ <PopoverPrimitive.Trigger className="cursor-pointer">
46
+ <div
47
+ className={`relative rounded-full p-2.5 ${open ? "bg-gray-100" : "hover:bg-gray-100"}`}
48
+ >
49
+ <IconBell size={20} className="text-gray-500" />
50
+ {totalUnread > 0 && (
51
+ <span
52
+ className={cn(
53
+ "absolute top-2 right-2 size-2.5 rounded-full",
54
+ badgeClassName,
55
+ )}
56
+ />
57
+ )}
58
+ </div>
59
+ </PopoverPrimitive.Trigger>
60
+ <PopoverPrimitive.Portal>
61
+ <PopoverPrimitive.Content
62
+ className={cn(
63
+ "absolute bottom-2 left-[26px] z-[9999] w-[308px] p-0 pt-4 bg-white rounded-2xl shadow-md border border-gray-200",
64
+ contentClassName,
65
+ )}
66
+ side={side}
67
+ align={align}
68
+ sideOffset={8}
69
+ >
70
+ <span className="paragraph-small-semibold text-gray-950 px-4">
71
+ Últimas notificações
72
+ </span>
73
+
74
+ {messages.length > 0 ? (
75
+ messages.map((message, index) => (
76
+ <NotificationCard
77
+ key={message.id}
78
+ icon={<IconBell size={16} />}
79
+ iconBgColor="bg-gray-100"
80
+ title={message.subject}
81
+ time={formatDistanceToNow(new Date(message.datetime_add), {
82
+ addSuffix: true,
83
+ locale: ptBR,
84
+ })}
85
+ gapPx={6}
86
+ showBorder={index < messages.length - 1}
87
+ />
88
+ ))
89
+ ) : (
90
+ <div className="flex flex-col items-center gap-4 pt-[41.5px] pb-[33.5px]">
91
+ <div className="bg-gray-50 rounded-md p-2.5">
92
+ <IconBell size={20} color="var(--color-gray-950)" />
93
+ </div>
94
+ <span className="paragraph-small-medium text-gray-950">
95
+ Sem notificações por enquanto
96
+ </span>
97
+ </div>
98
+ )}
99
+
100
+ {onViewAll && (
101
+ <Button
102
+ variant="secondary"
103
+ className="h-8! mt-2 mb-4 mx-4 paragraph-small-semibold"
104
+ onClick={onViewAll}
105
+ >
106
+ Todas as notificações
107
+ </Button>
108
+ )}
109
+ </PopoverPrimitive.Content>
110
+ </PopoverPrimitive.Portal>
111
+ </Popover>
112
+ );
113
+ }
114
+
115
+ export { NotificationsPopover };
@@ -1,184 +1,179 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { useRouter } from "next/navigation";
5
- import * as PopoverPrimitive from "@radix-ui/react-popover";
6
- import { Popover } from "../ui/overlay/Popover";
7
- import { Progress } from "../ui/feedback/Progress";
8
- import { Separator } from "../ui/data-display/Separator";
9
- import { Settings } from "lucide-react";
10
- import { NavBarItem } from "./NavBarItem";
11
- import { UserAvatar } from "../ui/data-display/UserAvatar";
12
- import { LoadingOverlay } from "../ui/feedback/LoadingOverlay";
13
- import { useAuth } from "../../providers/auth.provider";
14
- import { cn } from "../../infra/utils/clsx";
15
- import { useIaCredits } from "../../modules/ia-credits/hooks/ia-credits.hook";
16
-
17
- export interface ProfileMenuItem {
18
- icon?: React.ReactNode;
19
- label: string;
20
- onClick: () => void;
21
- }
22
-
23
- export interface ProfilePopoverProps {
24
- side?: "top" | "bottom" | "left" | "right";
25
- align?: "start" | "center" | "end";
26
- contentClassName?: string;
27
- onEditProfile?: () => void;
28
- onCreditsClick?: () => void;
29
- menuItems?: ProfileMenuItem[];
30
- logoutRedirect?: string;
31
- }
32
-
33
- function ProfilePopover({
34
- side = "top",
35
- align = "start",
36
- contentClassName = "absolute bottom-2 left-[26px]",
37
- onEditProfile,
38
- onCreditsClick,
39
- menuItems = [],
40
- logoutRedirect = "/login",
41
- }: ProfilePopoverProps) {
42
- const router = useRouter();
43
- const { user, logout } = useAuth();
44
- const [isLoggingOut, setIsLoggingOut] = useState(false);
45
-
46
- const { summary, subscription, isSubscriptionLoading, isCreditsLoading } =
47
- useIaCredits();
48
-
49
- const handleLogout = async () => {
50
- try {
51
- setIsLoggingOut(true);
52
- await logout();
53
- } finally {
54
- setIsLoggingOut(false);
55
- if (logoutRedirect !== "/login") {
56
- router.push(logoutRedirect);
57
- }
58
- }
59
- };
60
-
61
- const hasSubscription = !isSubscriptionLoading && subscription !== null;
62
- const showCreditsSkeleton = hasSubscription && isCreditsLoading;
63
- const showCredits =
64
- hasSubscription && !isCreditsLoading;
65
- const creditsPercentage =
66
- showCredits && summary.totalCredits > 0
67
- ? (summary.usedCredits / summary.totalCredits) * 100
68
- : 0;
69
-
70
- return (
71
- <>
72
- <LoadingOverlay isLoading={isLoggingOut} message="Saindo da sua conta..." />
73
- <Popover>
74
- <PopoverPrimitive.Trigger className="cursor-pointer">
75
- {user && (
76
- <UserAvatar
77
- key={user.id}
78
- photo={user.photo}
79
- name={user.name}
80
- size={40}
81
- />
82
- )}
83
- </PopoverPrimitive.Trigger>
84
-
85
- <PopoverPrimitive.Portal>
86
- <PopoverPrimitive.Content
87
- className={cn(
88
- "w-64.75 p-0 bg-white rounded-2xl shadow-md border border-gray-200 z-99",
89
- contentClassName,
90
- )}
91
- side={side}
92
- align={align}
93
- sideOffset={8}
94
- >
95
- <div className="grid grid-cols-[32px_1fr] items-center gap-3 p-3 w-full">
96
- {user && (
97
- <UserAvatar
98
- key={user.id}
99
- photo={user.photo}
100
- name={user.name}
101
- size={32}
102
- />
103
- )}
104
-
105
- <div className="flex flex-col gap-0.5 min-w-0">
106
- <span className="paragraph-small-semibold text-gray-950 truncate">
107
- {user?.name || ""}
108
- </span>
109
- <span className="paragraph-xsmall-medium text-gray-500 truncate">
110
- {user?.email || ""}
111
- </span>
112
- </div>
113
- </div>
114
-
115
- <div className="flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5">
116
- {showCreditsSkeleton && (
117
- <div className="flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4">
118
- <div className="flex items-center justify-between gap-2">
119
- <span className="paragraph-xsmall-semibold text-white truncate">
120
- Créditos usados
121
- </span>
122
- <div className="skeleton h-3.5 w-16 rounded" />
123
- </div>
124
- <div className="skeleton h-1.5 w-full rounded-full" />
125
- </div>
126
- )}
127
-
128
- {showCredits && (
129
- <div
130
- className={cn(
131
- "flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors",
132
- onCreditsClick && "cursor-pointer hover:bg-gray-900",
133
- )}
134
- onClick={onCreditsClick}
135
- >
136
- <div className="flex items-center justify-between gap-2">
137
- <span className="paragraph-xsmall-semibold text-white truncate">
138
- Créditos usados
139
- </span>
140
- <span className="paragraph-xsmall-medium text-white/50">
141
- {summary.usedCredits.toLocaleString("pt-BR")}/
142
- {summary.totalCredits.toLocaleString("pt-BR")}
143
- </span>
144
- </div>
145
- <Progress
146
- className="bg-white/10"
147
- indicatorColor="bg-white"
148
- value={creditsPercentage}
149
- />
150
- </div>
151
- )}
152
-
153
- <div className="flex flex-col gap-2">
154
- {menuItems.length > 0 && (
155
- <div className="flex flex-col">
156
- {menuItems.map((item, index) => (
157
- <NavBarItem
158
- key={index}
159
- icon={item.icon}
160
- label={item.label}
161
- onClick={item.onClick}
162
- />
163
- ))}
164
- </div>
165
- )}
166
- {onEditProfile && (
167
- <NavBarItem
168
- icon={<Settings size={16} />}
169
- label="Configurações"
170
- onClick={onEditProfile}
171
- />
172
- )}
173
- <Separator />
174
- <NavBarItem label="Sair" onClick={handleLogout} />
175
- </div>
176
- </div>
177
- </PopoverPrimitive.Content>
178
- </PopoverPrimitive.Portal>
179
- </Popover>
180
- </>
181
- );
182
- }
183
-
184
- export { ProfilePopover };
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
6
+ import { Popover } from "../ui/overlay/Popover";
7
+ import { Progress } from "../ui/feedback/Progress";
8
+ import { Separator } from "../ui/data-display/Separator";
9
+ import { IconSettings } from "@tabler/icons-react";
10
+ import { NavBarItem } from "./NavBarItem";
11
+ import { UserAvatar } from "../ui/data-display/UserAvatar";
12
+ import { LoadingOverlay } from "../ui/feedback/LoadingOverlay";
13
+ import { useAuth } from "../../providers/auth.provider";
14
+ import { cn } from "../../infra/utils/clsx";
15
+ import { useIaCredits } from "../../modules/ia-credits/hooks/ia-credits.hook";
16
+
17
+ export interface ProfileMenuItem {
18
+ icon?: React.ReactNode;
19
+ label: string;
20
+ onClick: () => void;
21
+ }
22
+
23
+ export interface ProfilePopoverProps {
24
+ side?: "top" | "bottom" | "left" | "right";
25
+ align?: "start" | "center" | "end";
26
+ contentClassName?: string;
27
+ onEditProfile?: () => void;
28
+ onCreditsClick?: () => void;
29
+ menuItems?: ProfileMenuItem[];
30
+ logoutRedirect?: string;
31
+ }
32
+
33
+ function ProfilePopover({
34
+ side = "top",
35
+ align = "start",
36
+ contentClassName = "absolute bottom-2 left-[26px]",
37
+ onEditProfile,
38
+ onCreditsClick,
39
+ menuItems = [],
40
+ logoutRedirect = "/login",
41
+ }: ProfilePopoverProps) {
42
+ const router = useRouter();
43
+ const { user, logout } = useAuth();
44
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
45
+
46
+ const { summary, subscription, isSubscriptionLoading, isCreditsLoading } =
47
+ useIaCredits();
48
+
49
+ const handleLogout = async () => {
50
+ try {
51
+ setIsLoggingOut(true);
52
+ await logout();
53
+ } finally {
54
+ setIsLoggingOut(false);
55
+ if (logoutRedirect !== "/login") {
56
+ router.push(logoutRedirect);
57
+ }
58
+ }
59
+ };
60
+
61
+ const hasSubscription = !isSubscriptionLoading && subscription !== null;
62
+ const showCreditsSkeleton = hasSubscription && isCreditsLoading;
63
+ const showCredits = hasSubscription && !isCreditsLoading;
64
+ const creditsPercentage =
65
+ showCredits && summary.totalCredits > 0
66
+ ? (summary.usedCredits / summary.totalCredits) * 100
67
+ : 0;
68
+
69
+ return (
70
+ <>
71
+ <LoadingOverlay
72
+ isLoading={isLoggingOut}
73
+ message="Saindo da sua conta..."
74
+ />
75
+ <Popover>
76
+ <PopoverPrimitive.Trigger className="cursor-pointer">
77
+ {user && (
78
+ <UserAvatar
79
+ key={user.id}
80
+ photo={user.photo}
81
+ name={user.name}
82
+ size={40}
83
+ />
84
+ )}
85
+ </PopoverPrimitive.Trigger>
86
+
87
+ <PopoverPrimitive.Portal>
88
+ <PopoverPrimitive.Content
89
+ className={cn(
90
+ "w-64.75 p-0 bg-white rounded-2xl shadow-md border border-gray-200 z-99",
91
+ contentClassName,
92
+ )}
93
+ side={side}
94
+ align={align}
95
+ sideOffset={8}
96
+ >
97
+ <div className="grid grid-cols-[32px_1fr] items-center gap-3 p-3 w-full">
98
+ {user && (
99
+ <UserAvatar
100
+ key={user.id}
101
+ photo={user.photo}
102
+ name={user.name}
103
+ size={32}
104
+ />
105
+ )}
106
+
107
+ <div className="flex flex-col gap-0.5 min-w-0">
108
+ <span className="paragraph-small-semibold text-gray-950 truncate">
109
+ {user?.name || ""}
110
+ </span>
111
+ <span className="paragraph-xsmall-medium text-gray-500 truncate">
112
+ {user?.email || ""}
113
+ </span>
114
+ </div>
115
+ </div>
116
+
117
+ <div className="flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5">
118
+ {showCreditsSkeleton && (
119
+ <div className="flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4">
120
+ <div className="flex items-center justify-between gap-2">
121
+ <span className="paragraph-xsmall-semibold text-white truncate">
122
+ Créditos usados
123
+ </span>
124
+ <div className="skeleton h-3.5 w-16 rounded" />
125
+ </div>
126
+ <div className="skeleton h-1.5 w-full rounded-full" />
127
+ </div>
128
+ )}
129
+
130
+ {showCredits && (
131
+ <div
132
+ className={cn(
133
+ "flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors",
134
+ onCreditsClick && "cursor-pointer hover:bg-gray-900",
135
+ )}
136
+ onClick={onCreditsClick}
137
+ >
138
+ <div className="flex items-center justify-between gap-2">
139
+ <span className="paragraph-xsmall-semibold text-white truncate">
140
+ Créditos usados
141
+ </span>
142
+ <span className="paragraph-xsmall-medium text-white/50">
143
+ {summary.usedCredits.toLocaleString("pt-BR")}/
144
+ {summary.totalCredits.toLocaleString("pt-BR")}
145
+ </span>
146
+ </div>
147
+ <Progress
148
+ className="bg-white/10"
149
+ indicatorColor="bg-white"
150
+ value={creditsPercentage}
151
+ />
152
+ </div>
153
+ )}
154
+
155
+ <div className="flex flex-col gap-2">
156
+ {menuItems.length > 0 && (
157
+ <div className="flex flex-col">
158
+ {menuItems.map((item, index) => (
159
+ <NavBarItem
160
+ key={index}
161
+ icon={item.icon}
162
+ label={item.label}
163
+ onClick={item.onClick}
164
+ />
165
+ ))}
166
+ </div>
167
+ )}
168
+ <Separator />
169
+ <NavBarItem label="Sair" onClick={handleLogout} />
170
+ </div>
171
+ </div>
172
+ </PopoverPrimitive.Content>
173
+ </PopoverPrimitive.Portal>
174
+ </Popover>
175
+ </>
176
+ );
177
+ }
178
+
179
+ export { ProfilePopover };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect, useState } from "react";
4
- import { Plus, Settings, User, Users } from "lucide-react";
4
+ import { IconPlus, IconSettings, IconUser, IconUsers } from '@tabler/icons-react';
5
5
  import { UserAvatar } from "../ui/data-display/UserAvatar";
6
6
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
7
7
  import { Button } from "../ui/buttons/Button";
@@ -93,7 +93,7 @@ export function UsersSelectorPopover({
93
93
  <PopoverTrigger className="cursor-pointer">
94
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
95
  <span className="paragraph-small-semibold">{totalInProject}</span>
96
- <User size={16} />
96
+ <IconUser size={16} />
97
97
  </div>
98
98
  </PopoverTrigger>
99
99
 
@@ -117,7 +117,7 @@ export function UsersSelectorPopover({
117
117
  window.location.href = `${GAPPS_URL}/my-teams?createNew=true`;
118
118
  }}
119
119
  >
120
- <Plus size={16} />
120
+ <IconPlus size={16} />
121
121
  Adicionar
122
122
  </Button>
123
123
  <Tooltip>
@@ -129,7 +129,7 @@ export function UsersSelectorPopover({
129
129
  window.location.href = `${GAPPS_URL}/my-teams`;
130
130
  }}
131
131
  >
132
- <Settings size={18} />
132
+ <IconSettings size={18} />
133
133
  </Button>
134
134
  </TooltipTrigger>
135
135
  <TooltipContent className="z-[1002]">
@@ -157,7 +157,7 @@ export function UsersSelectorPopover({
157
157
  </>
158
158
  ) : allUsers.length === 0 ? (
159
159
  <div className="flex flex-col items-center justify-center gap-2 h-full text-gray-400 py-4">
160
- <Users size={28} className="text-gray-300" />
160
+ <IconUsers size={28} className="text-gray-300" />
161
161
  <span className="paragraph-small-medium text-center text-gray-500">
162
162
  Nenhum usuário encontrado.
163
163
  </span>