@greatapps/common 1.1.1 → 1.1.2

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.
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../../infra/utils/clsx";
4
+ function NavBarItem({ icon, label, onClick, className, labelClassName }) {
5
+ return /* @__PURE__ */ jsxs(
6
+ "div",
7
+ {
8
+ className: cn(
9
+ "rounded-lg flex items-center gap-2 px-2 py-2 h-10 cursor-pointer transition-colors bg-white hover:bg-gray-100 active:bg-gray-50",
10
+ className
11
+ ),
12
+ onClick,
13
+ children: [
14
+ icon,
15
+ /* @__PURE__ */ jsx("span", { className: cn("text-sm font-medium text-gray-950", labelClassName), children: label })
16
+ ]
17
+ }
18
+ );
19
+ }
20
+ export {
21
+ NavBarItem
22
+ };
23
+ //# sourceMappingURL=NavBarItem.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/layouts/NavBarItem.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport { cn } from '../../infra/utils/clsx';\r\n\r\ntype NavBarItemProps = {\r\n icon?: React.ReactNode;\r\n label: string;\r\n onClick: () => void;\r\n className?: string;\r\n labelClassName?: string;\r\n}\r\n\r\nfunction NavBarItem({ icon, label, onClick, className, labelClassName }: NavBarItemProps) {\r\n return (\r\n <div\r\n className={cn(\r\n 'rounded-lg flex items-center gap-2 px-2 py-2 h-10 cursor-pointer transition-colors bg-white hover:bg-gray-100 active:bg-gray-50',\r\n className\r\n )}\r\n onClick={onClick}\r\n >\r\n {icon}\r\n <span className={cn('text-sm font-medium text-gray-950', labelClassName)}>{label}</span>\r\n </div>\r\n );\r\n}\r\n\r\nexport { NavBarItem };\r\nexport type { NavBarItemProps };\r\n"],"mappings":";AAcI,SAQE,KARF;AAZJ,SAAS,UAAU;AAUnB,SAAS,WAAW,EAAE,MAAM,OAAO,SAAS,WAAW,eAAe,GAAoB;AACxF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,QACD,oBAAC,UAAK,WAAW,GAAG,qCAAqC,cAAc,GAAI,iBAAM;AAAA;AAAA;AAAA,EACnF;AAEJ;","names":[]}
@@ -0,0 +1,141 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Popover, PopoverContent, PopoverTrigger } from "../ui/overlay/Popover";
6
+ import { Button } from "../ui/buttons/Button";
7
+ import { Progress } from "../ui/feedback/Progress";
8
+ import { Separator } from "../ui/data-display/Separator";
9
+ import { NavBarItem } from "./NavBarItem";
10
+ import { UserAvatar } from "../ui/data-display/UserAvatar";
11
+ import { useAuth } from "../../providers/auth.provider";
12
+ import { cn } from "../../infra/utils/clsx";
13
+ function ProfilePopover({
14
+ side = "top",
15
+ align = "start",
16
+ contentClassName,
17
+ onEditProfile,
18
+ credits,
19
+ onCreditsClick,
20
+ menuItems = [],
21
+ logoutRedirect = "/login",
22
+ storageUrl,
23
+ logoutOverlay
24
+ }) {
25
+ const router = useRouter();
26
+ const { user, logout } = useAuth();
27
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
28
+ const handleLogout = async () => {
29
+ try {
30
+ setIsLoggingOut(true);
31
+ await logout();
32
+ router.push(logoutRedirect);
33
+ } catch (error) {
34
+ console.error("Erro ao fazer logout:", error);
35
+ } finally {
36
+ setIsLoggingOut(false);
37
+ }
38
+ };
39
+ const creditsPercentage = credits && credits.total > 0 ? credits.used / credits.total * 100 : 0;
40
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
41
+ isLoggingOut && logoutOverlay,
42
+ /* @__PURE__ */ jsxs(Popover, { children: [
43
+ /* @__PURE__ */ jsx(PopoverTrigger, { className: "cursor-pointer", children: user && /* @__PURE__ */ jsx(
44
+ UserAvatar,
45
+ {
46
+ photo: user.photo,
47
+ name: user.name,
48
+ size: 40,
49
+ storageUrl
50
+ },
51
+ user.id
52
+ ) }),
53
+ /* @__PURE__ */ jsxs(
54
+ PopoverContent,
55
+ {
56
+ className: cn(
57
+ "w-64.75 p-0 bg-white rounded-2xl shadow-md border border-gray-200 z-99",
58
+ contentClassName
59
+ ),
60
+ side,
61
+ align,
62
+ sideOffset: 8,
63
+ children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[32px_1fr_auto] items-center gap-3 p-3 w-full", children: [
65
+ user && /* @__PURE__ */ jsx(
66
+ UserAvatar,
67
+ {
68
+ photo: user.photo,
69
+ name: user.name,
70
+ size: 32,
71
+ storageUrl
72
+ },
73
+ user.id
74
+ ),
75
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
76
+ /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-gray-950 truncate", children: user?.name || "" }),
77
+ /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-gray-500 truncate", children: user?.email || "" })
78
+ ] }),
79
+ onEditProfile && /* @__PURE__ */ jsx(
80
+ Button,
81
+ {
82
+ variant: "secondary",
83
+ className: "paragraph-small-semibold h-8! whitespace-nowrap px-3 w-fit",
84
+ onClick: onEditProfile,
85
+ children: "Editar Perfil"
86
+ }
87
+ )
88
+ ] }),
89
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5", children: [
90
+ credits && /* @__PURE__ */ jsxs(
91
+ "div",
92
+ {
93
+ className: cn(
94
+ "flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors",
95
+ onCreditsClick && "cursor-pointer hover:bg-gray-900"
96
+ ),
97
+ onClick: onCreditsClick,
98
+ children: [
99
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
100
+ /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-semibold text-white truncate", children: "Cr\xE9ditos usados" }),
101
+ /* @__PURE__ */ jsxs("span", { className: "paragraph-xsmall-medium text-white/50", children: [
102
+ credits.used.toLocaleString("pt-BR"),
103
+ "/",
104
+ credits.total.toLocaleString("pt-BR")
105
+ ] })
106
+ ] }),
107
+ /* @__PURE__ */ jsx(
108
+ Progress,
109
+ {
110
+ className: "bg-white/10",
111
+ indicatorColor: "bg-white",
112
+ value: creditsPercentage
113
+ }
114
+ )
115
+ ]
116
+ }
117
+ ),
118
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
119
+ menuItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: menuItems.map((item, index) => /* @__PURE__ */ jsx(
120
+ NavBarItem,
121
+ {
122
+ icon: item.icon,
123
+ label: item.label,
124
+ onClick: item.onClick
125
+ },
126
+ index
127
+ )) }),
128
+ /* @__PURE__ */ jsx(Separator, {}),
129
+ /* @__PURE__ */ jsx(NavBarItem, { label: "Sair", onClick: handleLogout })
130
+ ] })
131
+ ] })
132
+ ]
133
+ }
134
+ )
135
+ ] })
136
+ ] });
137
+ }
138
+ export {
139
+ ProfilePopover
140
+ };
141
+ //# sourceMappingURL=ProfilePopover.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/layouts/ProfilePopover.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport { useState } from 'react';\r\nimport { useRouter } from 'next/navigation';\r\nimport { Popover, PopoverContent, PopoverTrigger } from '../ui/overlay/Popover';\r\nimport { Button } from '../ui/buttons/Button';\r\nimport { Progress } from '../ui/feedback/Progress';\r\nimport { Separator } from '../ui/data-display/Separator';\r\nimport { NavBarItem } from './NavBarItem';\r\nimport { UserAvatar } from '../ui/data-display/UserAvatar';\r\nimport { useAuth } from '../../providers/auth.provider';\r\nimport { cn } from '../../infra/utils/clsx';\r\n\r\nexport interface ProfileMenuItem {\r\n icon?: React.ReactNode;\r\n label: string;\r\n onClick: () => void;\r\n}\r\n\r\nexport interface ProfilePopoverProps {\r\n side?: 'top' | 'bottom' | 'left' | 'right';\r\n align?: 'start' | 'center' | 'end';\r\n contentClassName?: string;\r\n onEditProfile?: () => void;\r\n credits?: { used: number; total: number };\r\n onCreditsClick?: () => void;\r\n menuItems?: ProfileMenuItem[];\r\n logoutRedirect?: string;\r\n storageUrl?: string;\r\n logoutOverlay?: React.ReactNode;\r\n}\r\n\r\nfunction ProfilePopover({\r\n side = 'top',\r\n align = 'start',\r\n contentClassName,\r\n onEditProfile,\r\n credits,\r\n onCreditsClick,\r\n menuItems = [],\r\n logoutRedirect = '/login',\r\n storageUrl,\r\n logoutOverlay,\r\n}: ProfilePopoverProps) {\r\n const router = useRouter();\r\n const { user, logout } = useAuth();\r\n const [isLoggingOut, setIsLoggingOut] = useState(false);\r\n\r\n const handleLogout = async () => {\r\n try {\r\n setIsLoggingOut(true);\r\n await logout();\r\n router.push(logoutRedirect);\r\n } catch (error) {\r\n console.error('Erro ao fazer logout:', error);\r\n } finally {\r\n setIsLoggingOut(false);\r\n }\r\n };\r\n\r\n const creditsPercentage =\r\n credits && credits.total > 0\r\n ? (credits.used / credits.total) * 100\r\n : 0;\r\n\r\n return (\r\n <>\r\n {isLoggingOut && logoutOverlay}\r\n <Popover>\r\n <PopoverTrigger className=\"cursor-pointer\">\r\n {user && (\r\n <UserAvatar\r\n key={user.id}\r\n photo={user.photo}\r\n name={user.name}\r\n size={40}\r\n storageUrl={storageUrl}\r\n />\r\n )}\r\n </PopoverTrigger>\r\n\r\n <PopoverContent\r\n className={cn(\r\n 'w-64.75 p-0 bg-white rounded-2xl shadow-md border border-gray-200 z-99',\r\n contentClassName\r\n )}\r\n side={side}\r\n align={align}\r\n sideOffset={8}\r\n >\r\n <div className=\"grid grid-cols-[32px_1fr_auto] items-center gap-3 p-3 w-full\">\r\n {user && (\r\n <UserAvatar\r\n key={user.id}\r\n photo={user.photo}\r\n name={user.name}\r\n size={32}\r\n storageUrl={storageUrl}\r\n />\r\n )}\r\n\r\n <div className=\"flex flex-col gap-0.5 min-w-0\">\r\n <span className=\"paragraph-small-semibold text-gray-950 truncate\">\r\n {user?.name || ''}\r\n </span>\r\n <span className=\"paragraph-xsmall-medium text-gray-500 truncate\">\r\n {user?.email || ''}\r\n </span>\r\n </div>\r\n\r\n {onEditProfile && (\r\n <Button\r\n variant=\"secondary\"\r\n className=\"paragraph-small-semibold h-8! whitespace-nowrap px-3 w-fit\"\r\n onClick={onEditProfile}\r\n >\r\n Editar Perfil\r\n </Button>\r\n )}\r\n </div>\r\n\r\n <div className=\"flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5\">\r\n {credits && (\r\n <div\r\n className={cn(\r\n 'flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors',\r\n onCreditsClick && 'cursor-pointer hover:bg-gray-900'\r\n )}\r\n onClick={onCreditsClick}\r\n >\r\n <div className=\"flex items-center justify-between gap-2\">\r\n <span className=\"paragraph-xsmall-semibold text-white truncate\">\r\n Créditos usados\r\n </span>\r\n <span className=\"paragraph-xsmall-medium text-white/50\">\r\n {credits.used.toLocaleString('pt-BR')}/\r\n {credits.total.toLocaleString('pt-BR')}\r\n </span>\r\n </div>\r\n <Progress\r\n className=\"bg-white/10\"\r\n indicatorColor=\"bg-white\"\r\n value={creditsPercentage}\r\n />\r\n </div>\r\n )}\r\n\r\n <div className=\"flex flex-col gap-2\">\r\n {menuItems.length > 0 && (\r\n <div className=\"flex flex-col\">\r\n {menuItems.map((item, index) => (\r\n <NavBarItem\r\n key={index}\r\n icon={item.icon}\r\n label={item.label}\r\n onClick={item.onClick}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n <Separator />\r\n <NavBarItem label=\"Sair\" onClick={handleLogout} />\r\n </div>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n </>\r\n );\r\n}\r\n\r\nexport { ProfilePopover };\r\n"],"mappings":";AAkEI,mBAKQ,KA8BA,YAnCR;AAhEJ,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,UAAU;AAqBnB,SAAS,eAAe;AAAA,EACtB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY,CAAC;AAAA,EACb,iBAAiB;AAAA,EACjB;AAAA,EACA;AACF,GAAwB;AACtB,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,MAAM,OAAO,IAAI,QAAQ;AACjC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AAEtD,QAAM,eAAe,YAAY;AAC/B,QAAI;AACF,sBAAgB,IAAI;AACpB,YAAM,OAAO;AACb,aAAO,KAAK,cAAc;AAAA,IAC5B,SAAS,OAAO;AACd,cAAQ,MAAM,yBAAyB,KAAK;AAAA,IAC9C,UAAE;AACA,sBAAgB,KAAK;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,oBACJ,WAAW,QAAQ,QAAQ,IACtB,QAAQ,OAAO,QAAQ,QAAS,MACjC;AAEN,SACE,iCACG;AAAA,oBAAgB;AAAA,IACjB,qBAAC,WACC;AAAA,0BAAC,kBAAe,WAAU,kBACvB,kBACC;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK;AAAA,UACX,MAAM;AAAA,UACN;AAAA;AAAA,QAJK,KAAK;AAAA,MAKZ,GAEJ;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACA;AAAA,UACA,YAAY;AAAA,UAEZ;AAAA,iCAAC,SAAI,WAAU,gEACZ;AAAA,sBACC;AAAA,gBAAC;AAAA;AAAA,kBAEC,OAAO,KAAK;AAAA,kBACZ,MAAM,KAAK;AAAA,kBACX,MAAM;AAAA,kBACN;AAAA;AAAA,gBAJK,KAAK;AAAA,cAKZ;AAAA,cAGF,qBAAC,SAAI,WAAU,iCACb;AAAA,oCAAC,UAAK,WAAU,mDACb,gBAAM,QAAQ,IACjB;AAAA,gBACA,oBAAC,UAAK,WAAU,kDACb,gBAAM,SAAS,IAClB;AAAA,iBACF;AAAA,cAEC,iBACC;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,WAAU;AAAA,kBACV,SAAS;AAAA,kBACV;AAAA;AAAA,cAED;AAAA,eAEJ;AAAA,YAEA,qBAAC,SAAI,WAAU,kEACZ;AAAA,yBACC;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,kBAAkB;AAAA,kBACpB;AAAA,kBACA,SAAS;AAAA,kBAET;AAAA,yCAAC,SAAI,WAAU,2CACb;AAAA,0CAAC,UAAK,WAAU,iDAAgD,gCAEhE;AAAA,sBACA,qBAAC,UAAK,WAAU,yCACb;AAAA,gCAAQ,KAAK,eAAe,OAAO;AAAA,wBAAE;AAAA,wBACrC,QAAQ,MAAM,eAAe,OAAO;AAAA,yBACvC;AAAA,uBACF;AAAA,oBACA;AAAA,sBAAC;AAAA;AAAA,wBACC,WAAU;AAAA,wBACV,gBAAe;AAAA,wBACf,OAAO;AAAA;AAAA,oBACT;AAAA;AAAA;AAAA,cACF;AAAA,cAGF,qBAAC,SAAI,WAAU,uBACZ;AAAA,0BAAU,SAAS,KAClB,oBAAC,SAAI,WAAU,iBACZ,oBAAU,IAAI,CAAC,MAAM,UACpB;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAM,KAAK;AAAA,oBACX,OAAO,KAAK;AAAA,oBACZ,SAAS,KAAK;AAAA;AAAA,kBAHT;AAAA,gBAIP,CACD,GACH;AAAA,gBAEF,oBAAC,aAAU;AAAA,gBACX,oBAAC,cAAW,OAAM,QAAO,SAAS,cAAc;AAAA,iBAClD;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import Image from "next/image";
4
+ import { cn } from "../../../infra/utils/clsx";
5
+ function getInitials(name, maxLength = 2) {
6
+ return name.split(" ").map((n) => n[0]).join("").slice(0, maxLength).toUpperCase();
7
+ }
8
+ function UserAvatar({
9
+ photo,
10
+ name,
11
+ initials,
12
+ size = 32,
13
+ shape = "circle",
14
+ className,
15
+ storageUrl
16
+ }) {
17
+ const shapeClass = shape === "circle" ? "rounded-full" : "rounded-lg";
18
+ const displayInitials = initials || (name ? getInitials(name) : "?");
19
+ const photoUrl = storageUrl && photo ? `${storageUrl}/${photo}` : photo;
20
+ if (photoUrl) {
21
+ return /* @__PURE__ */ jsx(
22
+ Image,
23
+ {
24
+ alt: name || "imagem do usuario",
25
+ src: photoUrl,
26
+ width: size,
27
+ height: size,
28
+ className: cn("object-cover shrink-0", shapeClass, className),
29
+ style: { width: size, height: size }
30
+ }
31
+ );
32
+ }
33
+ return /* @__PURE__ */ jsx(
34
+ "div",
35
+ {
36
+ className: cn(
37
+ "flex items-center justify-center shrink-0 bg-gray-100",
38
+ shapeClass,
39
+ className
40
+ ),
41
+ style: { width: size, height: size },
42
+ children: /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-700", children: displayInitials })
43
+ }
44
+ );
45
+ }
46
+ export {
47
+ UserAvatar
48
+ };
49
+ //# sourceMappingURL=UserAvatar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/ui/data-display/UserAvatar.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport Image from 'next/image';\r\nimport { cn } from '../../../infra/utils/clsx';\r\n\r\nfunction getInitials(name: string, maxLength: number = 2): string {\r\n return name\r\n .split(' ')\r\n .map((n) => n[0])\r\n .join('')\r\n .slice(0, maxLength)\r\n .toUpperCase();\r\n}\r\n\r\ninterface UserAvatarProps {\r\n photo?: string | null;\r\n name?: string;\r\n initials?: string;\r\n size?: number;\r\n shape?: 'circle' | 'square';\r\n className?: string;\r\n storageUrl?: string;\r\n}\r\n\r\nfunction UserAvatar({\r\n photo,\r\n name,\r\n initials,\r\n size = 32,\r\n shape = 'circle',\r\n className,\r\n storageUrl,\r\n}: UserAvatarProps) {\r\n const shapeClass = shape === 'circle' ? 'rounded-full' : 'rounded-lg';\r\n const displayInitials = initials || (name ? getInitials(name) : '?');\r\n\r\n const photoUrl = storageUrl && photo ? `${storageUrl}/${photo}` : photo;\r\n\r\n if (photoUrl) {\r\n return (\r\n <Image\r\n alt={name || 'imagem do usuario'}\r\n src={photoUrl}\r\n width={size}\r\n height={size}\r\n className={cn('object-cover shrink-0', shapeClass, className)}\r\n style={{ width: size, height: size }}\r\n />\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n className={cn(\r\n 'flex items-center justify-center shrink-0 bg-gray-100',\r\n shapeClass,\r\n className\r\n )}\r\n style={{ width: size, height: size }}\r\n >\r\n <span className=\"text-xs font-semibold text-gray-700\">{displayInitials}</span>\r\n </div>\r\n );\r\n}\r\n\r\nexport { UserAvatar };\r\nexport type { UserAvatarProps };\r\n"],"mappings":";AAwCM;AAtCN,OAAO,WAAW;AAClB,SAAS,UAAU;AAEnB,SAAS,YAAY,MAAc,YAAoB,GAAW;AAChE,SAAO,KACJ,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EACf,KAAK,EAAE,EACP,MAAM,GAAG,SAAS,EAClB,YAAY;AACjB;AAYA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,aAAa,UAAU,WAAW,iBAAiB;AACzD,QAAM,kBAAkB,aAAa,OAAO,YAAY,IAAI,IAAI;AAEhE,QAAM,WAAW,cAAc,QAAQ,GAAG,UAAU,IAAI,KAAK,KAAK;AAElE,MAAI,UAAU;AACZ,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,QAAQ;AAAA,QACb,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,GAAG,yBAAyB,YAAY,SAAS;AAAA,QAC5D,OAAO,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA;AAAA,IACrC;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,MAEnC,8BAAC,UAAK,WAAU,uCAAuC,2BAAgB;AAAA;AAAA,EACzE;AAEJ;","names":[]}
package/dist/index.mjs CHANGED
@@ -17,6 +17,9 @@ import { NavBar } from "./components/layouts/NavBar";
17
17
  import { SideBarNavigation } from "./components/layouts/SideBarNavigation";
18
18
  import { MdSideBarNavigation } from "./components/layouts/MdSideBarNavigation";
19
19
  import { NotificationItem } from "./components/layouts/NotificationItem";
20
+ import { ProfilePopover } from "./components/layouts/ProfilePopover";
21
+ import { NavBarItem } from "./components/layouts/NavBarItem";
22
+ import { UserAvatar } from "./components/ui/data-display/UserAvatar";
20
23
  import { useMdSidebarStore } from "./store/useMdSidebarStore";
21
24
  export {
22
25
  MainLayout,
@@ -25,10 +28,13 @@ export {
25
28
  MainLayoutSpacer,
26
29
  MdSideBarNavigation,
27
30
  NavBar,
31
+ NavBarItem,
28
32
  NotificationItem,
33
+ ProfilePopover,
29
34
  QueryProvider,
30
35
  SideBarNavigation,
31
36
  USER_QUERY_KEY,
37
+ UserAvatar,
32
38
  buildQueryParams,
33
39
  cn,
34
40
  continueChain,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Types\nexport * from './modules/auth/schema';\nexport * from './modules/users/schema';\nexport * from './modules/whitelabel/schema';\nexport * from './infra/api/types';\n\n// Contexts\nexport * from './providers/query.provider';\nexport * from './providers/auth.provider';\nexport * from './providers/whitelabel.provider';\n\n// Hooks\nexport { useUserQuery, useUserValidateSession, useInvalidateUser, useSetUserData, USER_QUERY_KEY } from './modules/auth/hooks/useUserQuery';\n\n// Providers\nexport { QueryProvider } from './providers/query.provider';\n\n// Middleware\nexport { createAuthMiddleware } from './middlewares/create-auth-middleware';\nexport { createMiddlewareChain } from './middlewares/chain';\nexport { continueChain, stopChain } from './middlewares/types';\nexport type { MiddlewareFunction, MiddlewareConfig, MiddlewareResult } from './middlewares/types';\n\n// Utils\nexport { cn } from './infra/utils/clsx';\nexport { buildQueryParams } from './infra/utils/params';\n\n// Layout\nexport { MainLayout, MainLayoutContent, MainLayoutSpacer, MainLayoutMain } from './components/layouts/MainLayout';\nexport { NavBar } from './components/layouts/NavBar';\nexport type { NavBarProps } from './components/layouts/NavBar';\nexport { SideBarNavigation } from './components/layouts/SideBarNavigation';\nexport { MdSideBarNavigation } from './components/layouts/MdSideBarNavigation';\nexport { NotificationItem } from './components/layouts/NotificationItem';\nexport type { NotificationItemProps } from './components/layouts/NotificationItem';\n\n// Store\nexport { useMdSidebarStore } from './store/useMdSidebarStore';\n"],"mappings":"AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,cAAc,wBAAwB,mBAAmB,gBAAgB,sBAAsB;AAGxG,SAAS,qBAAqB;AAG9B,SAAS,4BAA4B;AACrC,SAAS,6BAA6B;AACtC,SAAS,eAAe,iBAAiB;AAIzC,SAAS,UAAU;AACnB,SAAS,wBAAwB;AAGjC,SAAS,YAAY,mBAAmB,kBAAkB,sBAAsB;AAChF,SAAS,cAAc;AAEvB,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAIjC,SAAS,yBAAyB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Types\nexport * from './modules/auth/schema';\nexport * from './modules/users/schema';\nexport * from './modules/whitelabel/schema';\nexport * from './infra/api/types';\n\n// Contexts\nexport * from './providers/query.provider';\nexport * from './providers/auth.provider';\nexport * from './providers/whitelabel.provider';\n\n// Hooks\nexport { useUserQuery, useUserValidateSession, useInvalidateUser, useSetUserData, USER_QUERY_KEY } from './modules/auth/hooks/useUserQuery';\n\n// Providers\nexport { QueryProvider } from './providers/query.provider';\n\n// Middleware\nexport { createAuthMiddleware } from './middlewares/create-auth-middleware';\nexport { createMiddlewareChain } from './middlewares/chain';\nexport { continueChain, stopChain } from './middlewares/types';\nexport type { MiddlewareFunction, MiddlewareConfig, MiddlewareResult } from './middlewares/types';\n\n// Utils\nexport { cn } from './infra/utils/clsx';\nexport { buildQueryParams } from './infra/utils/params';\n\n// Layout\nexport { MainLayout, MainLayoutContent, MainLayoutSpacer, MainLayoutMain } from './components/layouts/MainLayout';\nexport { NavBar } from './components/layouts/NavBar';\nexport type { NavBarProps } from './components/layouts/NavBar';\nexport { SideBarNavigation } from './components/layouts/SideBarNavigation';\nexport { MdSideBarNavigation } from './components/layouts/MdSideBarNavigation';\nexport { NotificationItem } from './components/layouts/NotificationItem';\nexport type { NotificationItemProps } from './components/layouts/NotificationItem';\nexport { ProfilePopover } from './components/layouts/ProfilePopover';\nexport type { ProfilePopoverProps, ProfileMenuItem } from './components/layouts/ProfilePopover';\nexport { NavBarItem } from './components/layouts/NavBarItem';\nexport type { NavBarItemProps } from './components/layouts/NavBarItem';\nexport { UserAvatar } from './components/ui/data-display/UserAvatar';\nexport type { UserAvatarProps } from './components/ui/data-display/UserAvatar';\n\n// Store\nexport { useMdSidebarStore } from './store/useMdSidebarStore';\n"],"mappings":"AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,cAAc,wBAAwB,mBAAmB,gBAAgB,sBAAsB;AAGxG,SAAS,qBAAqB;AAG9B,SAAS,4BAA4B;AACrC,SAAS,6BAA6B;AACtC,SAAS,eAAe,iBAAiB;AAIzC,SAAS,UAAU;AACnB,SAAS,wBAAwB;AAGjC,SAAS,YAAY,mBAAmB,kBAAkB,sBAAsB;AAChF,SAAS,cAAc;AAEvB,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAEjC,SAAS,sBAAsB;AAE/B,SAAS,kBAAkB;AAE3B,SAAS,kBAAkB;AAI3B,SAAS,yBAAyB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,29 @@
1
+ "use client"
2
+
3
+ import { cn } from '../../infra/utils/clsx';
4
+
5
+ type NavBarItemProps = {
6
+ icon?: React.ReactNode;
7
+ label: string;
8
+ onClick: () => void;
9
+ className?: string;
10
+ labelClassName?: string;
11
+ }
12
+
13
+ function NavBarItem({ icon, label, onClick, className, labelClassName }: NavBarItemProps) {
14
+ return (
15
+ <div
16
+ className={cn(
17
+ 'rounded-lg flex items-center gap-2 px-2 py-2 h-10 cursor-pointer transition-colors bg-white hover:bg-gray-100 active:bg-gray-50',
18
+ className
19
+ )}
20
+ onClick={onClick}
21
+ >
22
+ {icon}
23
+ <span className={cn('text-sm font-medium text-gray-950', labelClassName)}>{label}</span>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export { NavBarItem };
29
+ export type { NavBarItemProps };
@@ -0,0 +1,171 @@
1
+ "use client"
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { Popover, PopoverContent, PopoverTrigger } from '../ui/overlay/Popover';
6
+ import { Button } from '../ui/buttons/Button';
7
+ import { Progress } from '../ui/feedback/Progress';
8
+ import { Separator } from '../ui/data-display/Separator';
9
+ import { NavBarItem } from './NavBarItem';
10
+ import { UserAvatar } from '../ui/data-display/UserAvatar';
11
+ import { useAuth } from '../../providers/auth.provider';
12
+ import { cn } from '../../infra/utils/clsx';
13
+
14
+ export interface ProfileMenuItem {
15
+ icon?: React.ReactNode;
16
+ label: string;
17
+ onClick: () => void;
18
+ }
19
+
20
+ export interface ProfilePopoverProps {
21
+ side?: 'top' | 'bottom' | 'left' | 'right';
22
+ align?: 'start' | 'center' | 'end';
23
+ contentClassName?: string;
24
+ onEditProfile?: () => void;
25
+ credits?: { used: number; total: number };
26
+ onCreditsClick?: () => void;
27
+ menuItems?: ProfileMenuItem[];
28
+ logoutRedirect?: string;
29
+ storageUrl?: string;
30
+ logoutOverlay?: React.ReactNode;
31
+ }
32
+
33
+ function ProfilePopover({
34
+ side = 'top',
35
+ align = 'start',
36
+ contentClassName,
37
+ onEditProfile,
38
+ credits,
39
+ onCreditsClick,
40
+ menuItems = [],
41
+ logoutRedirect = '/login',
42
+ storageUrl,
43
+ logoutOverlay,
44
+ }: ProfilePopoverProps) {
45
+ const router = useRouter();
46
+ const { user, logout } = useAuth();
47
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
48
+
49
+ const handleLogout = async () => {
50
+ try {
51
+ setIsLoggingOut(true);
52
+ await logout();
53
+ router.push(logoutRedirect);
54
+ } catch (error) {
55
+ console.error('Erro ao fazer logout:', error);
56
+ } finally {
57
+ setIsLoggingOut(false);
58
+ }
59
+ };
60
+
61
+ const creditsPercentage =
62
+ credits && credits.total > 0
63
+ ? (credits.used / credits.total) * 100
64
+ : 0;
65
+
66
+ return (
67
+ <>
68
+ {isLoggingOut && logoutOverlay}
69
+ <Popover>
70
+ <PopoverTrigger className="cursor-pointer">
71
+ {user && (
72
+ <UserAvatar
73
+ key={user.id}
74
+ photo={user.photo}
75
+ name={user.name}
76
+ size={40}
77
+ storageUrl={storageUrl}
78
+ />
79
+ )}
80
+ </PopoverTrigger>
81
+
82
+ <PopoverContent
83
+ className={cn(
84
+ 'w-64.75 p-0 bg-white rounded-2xl shadow-md border border-gray-200 z-99',
85
+ contentClassName
86
+ )}
87
+ side={side}
88
+ align={align}
89
+ sideOffset={8}
90
+ >
91
+ <div className="grid grid-cols-[32px_1fr_auto] items-center gap-3 p-3 w-full">
92
+ {user && (
93
+ <UserAvatar
94
+ key={user.id}
95
+ photo={user.photo}
96
+ name={user.name}
97
+ size={32}
98
+ storageUrl={storageUrl}
99
+ />
100
+ )}
101
+
102
+ <div className="flex flex-col gap-0.5 min-w-0">
103
+ <span className="paragraph-small-semibold text-gray-950 truncate">
104
+ {user?.name || ''}
105
+ </span>
106
+ <span className="paragraph-xsmall-medium text-gray-500 truncate">
107
+ {user?.email || ''}
108
+ </span>
109
+ </div>
110
+
111
+ {onEditProfile && (
112
+ <Button
113
+ variant="secondary"
114
+ className="paragraph-small-semibold h-8! whitespace-nowrap px-3 w-fit"
115
+ onClick={onEditProfile}
116
+ >
117
+ Editar Perfil
118
+ </Button>
119
+ )}
120
+ </div>
121
+
122
+ <div className="flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5">
123
+ {credits && (
124
+ <div
125
+ className={cn(
126
+ 'flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors',
127
+ onCreditsClick && 'cursor-pointer hover:bg-gray-900'
128
+ )}
129
+ onClick={onCreditsClick}
130
+ >
131
+ <div className="flex items-center justify-between gap-2">
132
+ <span className="paragraph-xsmall-semibold text-white truncate">
133
+ Créditos usados
134
+ </span>
135
+ <span className="paragraph-xsmall-medium text-white/50">
136
+ {credits.used.toLocaleString('pt-BR')}/
137
+ {credits.total.toLocaleString('pt-BR')}
138
+ </span>
139
+ </div>
140
+ <Progress
141
+ className="bg-white/10"
142
+ indicatorColor="bg-white"
143
+ value={creditsPercentage}
144
+ />
145
+ </div>
146
+ )}
147
+
148
+ <div className="flex flex-col gap-2">
149
+ {menuItems.length > 0 && (
150
+ <div className="flex flex-col">
151
+ {menuItems.map((item, index) => (
152
+ <NavBarItem
153
+ key={index}
154
+ icon={item.icon}
155
+ label={item.label}
156
+ onClick={item.onClick}
157
+ />
158
+ ))}
159
+ </div>
160
+ )}
161
+ <Separator />
162
+ <NavBarItem label="Sair" onClick={handleLogout} />
163
+ </div>
164
+ </div>
165
+ </PopoverContent>
166
+ </Popover>
167
+ </>
168
+ );
169
+ }
170
+
171
+ export { ProfilePopover };
@@ -0,0 +1,67 @@
1
+ "use client"
2
+
3
+ import Image from 'next/image';
4
+ import { cn } from '../../../infra/utils/clsx';
5
+
6
+ function getInitials(name: string, maxLength: number = 2): string {
7
+ return name
8
+ .split(' ')
9
+ .map((n) => n[0])
10
+ .join('')
11
+ .slice(0, maxLength)
12
+ .toUpperCase();
13
+ }
14
+
15
+ interface UserAvatarProps {
16
+ photo?: string | null;
17
+ name?: string;
18
+ initials?: string;
19
+ size?: number;
20
+ shape?: 'circle' | 'square';
21
+ className?: string;
22
+ storageUrl?: string;
23
+ }
24
+
25
+ function UserAvatar({
26
+ photo,
27
+ name,
28
+ initials,
29
+ size = 32,
30
+ shape = 'circle',
31
+ className,
32
+ storageUrl,
33
+ }: UserAvatarProps) {
34
+ const shapeClass = shape === 'circle' ? 'rounded-full' : 'rounded-lg';
35
+ const displayInitials = initials || (name ? getInitials(name) : '?');
36
+
37
+ const photoUrl = storageUrl && photo ? `${storageUrl}/${photo}` : photo;
38
+
39
+ if (photoUrl) {
40
+ return (
41
+ <Image
42
+ alt={name || 'imagem do usuario'}
43
+ src={photoUrl}
44
+ width={size}
45
+ height={size}
46
+ className={cn('object-cover shrink-0', shapeClass, className)}
47
+ style={{ width: size, height: size }}
48
+ />
49
+ );
50
+ }
51
+
52
+ return (
53
+ <div
54
+ className={cn(
55
+ 'flex items-center justify-center shrink-0 bg-gray-100',
56
+ shapeClass,
57
+ className
58
+ )}
59
+ style={{ width: size, height: size }}
60
+ >
61
+ <span className="text-xs font-semibold text-gray-700">{displayInitials}</span>
62
+ </div>
63
+ );
64
+ }
65
+
66
+ export { UserAvatar };
67
+ export type { UserAvatarProps };
package/src/index.ts CHANGED
@@ -33,6 +33,12 @@ export { SideBarNavigation } from './components/layouts/SideBarNavigation';
33
33
  export { MdSideBarNavigation } from './components/layouts/MdSideBarNavigation';
34
34
  export { NotificationItem } from './components/layouts/NotificationItem';
35
35
  export type { NotificationItemProps } from './components/layouts/NotificationItem';
36
+ export { ProfilePopover } from './components/layouts/ProfilePopover';
37
+ export type { ProfilePopoverProps, ProfileMenuItem } from './components/layouts/ProfilePopover';
38
+ export { NavBarItem } from './components/layouts/NavBarItem';
39
+ export type { NavBarItemProps } from './components/layouts/NavBarItem';
40
+ export { UserAvatar } from './components/ui/data-display/UserAvatar';
41
+ export type { UserAvatarProps } from './components/ui/data-display/UserAvatar';
36
42
 
37
43
  // Store
38
44
  export { useMdSidebarStore } from './store/useMdSidebarStore';