@greatapps/common 1.1.6 → 1.1.7
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.
- package/dist/components/layouts/ProfilePopover.mjs +16 -5
- package/dist/components/layouts/ProfilePopover.mjs.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/auth/utils/get-user-context.mjs +24 -0
- package/dist/modules/auth/utils/get-user-context.mjs.map +1 -0
- package/dist/modules/ia-credits/actions/list-ia-credits.action.mjs +9 -0
- package/dist/modules/ia-credits/actions/list-ia-credits.action.mjs.map +1 -0
- package/dist/modules/ia-credits/hooks/ia-credits.hook.mjs +75 -0
- package/dist/modules/ia-credits/hooks/ia-credits.hook.mjs.map +1 -0
- package/dist/modules/ia-credits/services/ia-credits.service.mjs +31 -0
- package/dist/modules/ia-credits/services/ia-credits.service.mjs.map +1 -0
- package/dist/modules/ia-credits/types.mjs +25 -0
- package/dist/modules/ia-credits/types.mjs.map +1 -0
- package/dist/modules/subscriptions/actions/list-subscriptions.action.mjs +9 -0
- package/dist/modules/subscriptions/actions/list-subscriptions.action.mjs.map +1 -0
- package/dist/modules/subscriptions/hooks/list-subscriptions.hook.mjs +14 -0
- package/dist/modules/subscriptions/hooks/list-subscriptions.hook.mjs.map +1 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs +32 -0
- package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -0
- package/dist/modules/subscriptions/types.mjs +12 -0
- package/dist/modules/subscriptions/types.mjs.map +1 -0
- package/dist/server.mjs +10 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/layouts/ProfilePopover.tsx +23 -7
- package/src/index.ts +4 -0
- package/src/modules/auth/utils/get-user-context.ts +29 -0
- package/src/modules/ia-credits/actions/list-ia-credits.action.ts +11 -0
- package/src/modules/ia-credits/hooks/ia-credits.hook.ts +96 -0
- package/src/modules/ia-credits/services/ia-credits.service.ts +37 -0
- package/src/modules/ia-credits/types.ts +33 -0
- package/src/modules/subscriptions/actions/list-subscriptions.action.ts +11 -0
- package/src/modules/subscriptions/hooks/list-subscriptions.hook.ts +14 -0
- package/src/modules/subscriptions/services/subscriptions.service.ts +38 -0
- package/src/modules/subscriptions/types.ts +17 -0
- package/src/server.ts +5 -0
|
@@ -11,12 +11,12 @@ import { NavBarItem } from "./NavBarItem";
|
|
|
11
11
|
import { UserAvatar } from "../ui/data-display/UserAvatar";
|
|
12
12
|
import { useAuth } from "../../providers/auth.provider";
|
|
13
13
|
import { cn } from "../../infra/utils/clsx";
|
|
14
|
+
import { useIaCredits } from "../../modules/ia-credits/hooks/ia-credits.hook";
|
|
14
15
|
function ProfilePopover({
|
|
15
16
|
side = "top",
|
|
16
17
|
align = "start",
|
|
17
18
|
contentClassName = "absolute bottom-2 left-[26px]",
|
|
18
19
|
onEditProfile,
|
|
19
|
-
credits,
|
|
20
20
|
onCreditsClick,
|
|
21
21
|
menuItems = [],
|
|
22
22
|
logoutRedirect = "/login",
|
|
@@ -25,6 +25,7 @@ function ProfilePopover({
|
|
|
25
25
|
const router = useRouter();
|
|
26
26
|
const { user, logout } = useAuth();
|
|
27
27
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
28
|
+
const { summary, subscription, isSubscriptionLoading, isCreditsLoading } = useIaCredits();
|
|
28
29
|
const handleLogout = async () => {
|
|
29
30
|
try {
|
|
30
31
|
setIsLoggingOut(true);
|
|
@@ -36,7 +37,10 @@ function ProfilePopover({
|
|
|
36
37
|
setIsLoggingOut(false);
|
|
37
38
|
}
|
|
38
39
|
};
|
|
39
|
-
const
|
|
40
|
+
const hasSubscription = !isSubscriptionLoading && subscription !== null;
|
|
41
|
+
const showCreditsSkeleton = hasSubscription && isCreditsLoading;
|
|
42
|
+
const showCredits = hasSubscription && !isCreditsLoading && summary.totalCredits > 0;
|
|
43
|
+
const creditsPercentage = showCredits && summary.totalCredits > 0 ? summary.usedCredits / summary.totalCredits * 100 : 0;
|
|
40
44
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41
45
|
isLoggingOut && logoutOverlay,
|
|
42
46
|
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
@@ -85,7 +89,14 @@ function ProfilePopover({
|
|
|
85
89
|
)
|
|
86
90
|
] }),
|
|
87
91
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5", children: [
|
|
88
|
-
|
|
92
|
+
showCreditsSkeleton && /* @__PURE__ */ jsxs("div", { className: "flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4", children: [
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
94
|
+
/* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-semibold text-white truncate", children: "Cr\xE9ditos usados" }),
|
|
95
|
+
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-16 rounded bg-white/20 animate-pulse" })
|
|
96
|
+
] }),
|
|
97
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-full rounded-full bg-white/20 animate-pulse" })
|
|
98
|
+
] }),
|
|
99
|
+
showCredits && /* @__PURE__ */ jsxs(
|
|
89
100
|
"div",
|
|
90
101
|
{
|
|
91
102
|
className: cn(
|
|
@@ -97,9 +108,9 @@ function ProfilePopover({
|
|
|
97
108
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
98
109
|
/* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-semibold text-white truncate", children: "Cr\xE9ditos usados" }),
|
|
99
110
|
/* @__PURE__ */ jsxs("span", { className: "paragraph-xsmall-medium text-white/50", children: [
|
|
100
|
-
|
|
111
|
+
summary.usedCredits.toLocaleString("pt-BR"),
|
|
101
112
|
"/",
|
|
102
|
-
|
|
113
|
+
summary.totalCredits.toLocaleString("pt-BR")
|
|
103
114
|
] })
|
|
104
115
|
] }),
|
|
105
116
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +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 * as PopoverPrimitive from '@radix-ui/react-popover';\r\nimport { Popover } 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
|
|
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 * as PopoverPrimitive from '@radix-ui/react-popover';\r\nimport { Popover } 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\nimport { useIaCredits } from '../../modules/ia-credits/hooks/ia-credits.hook';\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 onCreditsClick?: () => void;\r\n menuItems?: ProfileMenuItem[];\r\n logoutRedirect?: string;\r\n logoutOverlay?: React.ReactNode;\r\n}\r\n\r\nfunction ProfilePopover({\r\n side = 'top',\r\n align = 'start',\r\n contentClassName = 'absolute bottom-2 left-[26px]',\r\n onEditProfile,\r\n onCreditsClick,\r\n menuItems = [],\r\n logoutRedirect = '/login',\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 { summary, subscription, isSubscriptionLoading, isCreditsLoading } = useIaCredits();\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 hasSubscription = !isSubscriptionLoading && subscription !== null;\r\n const showCreditsSkeleton = hasSubscription && isCreditsLoading;\r\n const showCredits = hasSubscription && !isCreditsLoading && summary.totalCredits > 0;\r\n const creditsPercentage =\r\n showCredits && summary.totalCredits > 0\r\n ? (summary.usedCredits / summary.totalCredits) * 100\r\n : 0;\r\n\r\n return (\r\n <>\r\n {isLoggingOut && logoutOverlay}\r\n <Popover>\r\n <PopoverPrimitive.Trigger 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 />\r\n )}\r\n </PopoverPrimitive.Trigger>\r\n\r\n <PopoverPrimitive.Portal>\r\n <PopoverPrimitive.Content\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 />\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 {showCreditsSkeleton && (\r\n <div className=\"flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4\">\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 <div className=\"h-3.5 w-16 rounded bg-white/20 animate-pulse\" />\r\n </div>\r\n <div className=\"h-1.5 w-full rounded-full bg-white/20 animate-pulse\" />\r\n </div>\r\n )}\r\n\r\n {showCredits && (\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 {summary.usedCredits.toLocaleString('pt-BR')}/\r\n {summary.totalCredits.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 </PopoverPrimitive.Content>\r\n </PopoverPrimitive.Portal>\r\n </Popover>\r\n </>\r\n );\r\n}\r\n\r\nexport { ProfilePopover };\r\n"],"mappings":";AAqEI,mBAKQ,KA6BA,YAlCR;AAnEJ,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,YAAY,sBAAsB;AAClC,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAmB7B,SAAS,eAAe;AAAA,EACtB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA,YAAY,CAAC;AAAA,EACb,iBAAiB;AAAA,EACjB;AACF,GAAwB;AACtB,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,MAAM,OAAO,IAAI,QAAQ;AACjC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AAEtD,QAAM,EAAE,SAAS,cAAc,uBAAuB,iBAAiB,IAAI,aAAa;AAExF,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,kBAAkB,CAAC,yBAAyB,iBAAiB;AACnE,QAAM,sBAAsB,mBAAmB;AAC/C,QAAM,cAAc,mBAAmB,CAAC,oBAAoB,QAAQ,eAAe;AACnF,QAAM,oBACJ,eAAe,QAAQ,eAAe,IACjC,QAAQ,cAAc,QAAQ,eAAgB,MAC/C;AAEN,SACE,iCACG;AAAA,oBAAgB;AAAA,IACjB,qBAAC,WACC;AAAA,0BAAC,iBAAiB,SAAjB,EAAyB,WAAU,kBACjC,kBACC;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK;AAAA,UACX,MAAM;AAAA;AAAA,QAHD,KAAK;AAAA,MAIZ,GAEJ;AAAA,MAEA,oBAAC,iBAAiB,QAAjB,EACD;AAAA,QAAC,iBAAiB;AAAA,QAAjB;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;AAAA,gBAHD,KAAK;AAAA,cAIZ;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,qCACC,qBAAC,SAAI,WAAU,yDACb;AAAA,qCAAC,SAAI,WAAU,2CACb;AAAA,sCAAC,UAAK,WAAU,iDAAgD,gCAEhE;AAAA,kBACA,oBAAC,SAAI,WAAU,gDAA+C;AAAA,mBAChE;AAAA,gBACA,oBAAC,SAAI,WAAU,uDAAsD;AAAA,iBACvE;AAAA,cAGD,eACC;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,YAAY,eAAe,OAAO;AAAA,wBAAE;AAAA,wBAC5C,QAAQ,aAAa,eAAe,OAAO;AAAA,yBAC9C;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,GACA;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,8 @@ export * from "./providers/query.provider";
|
|
|
6
6
|
export * from "./providers/auth.provider";
|
|
7
7
|
export * from "./providers/whitelabel.provider";
|
|
8
8
|
import { useUserQuery, useUserValidateSession, useInvalidateUser, useSetUserData, USER_QUERY_KEY } from "./modules/auth/hooks/useUserQuery";
|
|
9
|
+
import { useActiveSubscription } from "./modules/subscriptions/hooks/list-subscriptions.hook";
|
|
10
|
+
import { useIaCredits } from "./modules/ia-credits/hooks/ia-credits.hook";
|
|
9
11
|
import { QueryProvider } from "./providers/query.provider";
|
|
10
12
|
import { createAuthMiddleware } from "./middlewares/create-auth-middleware";
|
|
11
13
|
import { createMiddlewareChain } from "./middlewares/chain";
|
|
@@ -179,6 +181,8 @@ export {
|
|
|
179
181
|
stopChain,
|
|
180
182
|
toastIconContainerVariants,
|
|
181
183
|
toastVariants,
|
|
184
|
+
useActiveSubscription,
|
|
185
|
+
useIaCredits,
|
|
182
186
|
useInvalidateUser,
|
|
183
187
|
useMdSidebarStore,
|
|
184
188
|
useSetUserData,
|
package/dist/index.mjs.map
CHANGED
|
@@ -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';\nexport { NotificationsPopover } from './components/layouts/NotificationsPopover';\nexport type { NotificationsPopoverProps, NotificationData } from './components/layouts/NotificationsPopover';\nexport { UsersSelectorPopover } from './components/layouts/UsersSelectorPopover';\nexport type { UsersSelectorPopoverProps, UserSelectorItem } from './components/layouts/UsersSelectorPopover';\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';\n\n// UI - Buttons\nexport { Button, buttonVariants } from './components/ui/buttons/Button';\nexport { CopyButton } from './components/ui/buttons/CopyButton';\nexport { default as CancelButton } from './components/ui/buttons/CancelButton';\n\n// UI - Data Display\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './components/ui/data-display/Accordion';\nexport { Badge, badgeVariants } from './components/ui/data-display/Badge';\nexport { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent } from './components/ui/data-display/Card';\nexport { Pagination, PaginationContent, PaginationLink, PaginationItem, PaginationPrevious, PaginationNext, PaginationEllipsis } from './components/ui/data-display/Pagination';\nexport { ScrollArea, ScrollBar } from './components/ui/data-display/ScrollArea';\nexport { Separator } from './components/ui/data-display/Separator';\nexport { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption } from './components/ui/data-display/Table';\nexport { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/data-display/Tabs';\nexport { UserAvatar } from './components/ui/data-display/UserAvatar';\nexport type { UserAvatarProps } from './components/ui/data-display/UserAvatar';\n\n// UI - Feedback\nexport { default as CircularProgress } from './components/ui/feedback/CircularProgress';\nexport { default as DefaultCircularProgress } from './components/ui/feedback/DefaultCircularProgress';\nexport { Progress } from './components/ui/feedback/Progress';\nexport { Toast, toastVariants, toastIconContainerVariants } from './components/ui/feedback/Toast';\nexport type { ToastProps } from './components/ui/feedback/Toast';\n\n// UI - Form\nexport { Calendar, CalendarDayButton } from './components/ui/form/Calendar';\nexport { Checkbox } from './components/ui/form/Checkbox';\nexport { DatePicker } from './components/ui/form/DatePicker';\nexport { DateRangePicker } from './components/ui/form/DateRangePicker';\nexport { Input } from './components/ui/form/Input';\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from './components/ui/form/InputOtp';\nexport { RadioGroup, RadioGroupItem } from './components/ui/form/RadioGroup';\nexport { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from './components/ui/form/Select';\nexport { Switch } from './components/ui/form/Switch';\nexport { Textarea } from './components/ui/form/Textarea';\n\n// UI - Overlay\nexport { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator } from './components/ui/overlay/Command';\nexport { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './components/ui/overlay/Dialog';\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } from './components/ui/overlay/Popover';\nexport { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription } from './components/ui/overlay/Sheet';\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/overlay/Tooltip';\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;
|
|
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';\nexport { useActiveSubscription } from './modules/subscriptions/hooks/list-subscriptions.hook';\nexport { useIaCredits } from './modules/ia-credits/hooks/ia-credits.hook';\nexport type { IaCreditsSummary, IaCreditOperation } from './modules/ia-credits/types';\nexport type { Subscription, FindSubscriptionsParams } from './modules/subscriptions/types';\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 { NotificationsPopover } from './components/layouts/NotificationsPopover';\nexport type { NotificationsPopoverProps, NotificationData } from './components/layouts/NotificationsPopover';\nexport { UsersSelectorPopover } from './components/layouts/UsersSelectorPopover';\nexport type { UsersSelectorPopoverProps, UserSelectorItem } from './components/layouts/UsersSelectorPopover';\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';\n\n// UI - Buttons\nexport { Button, buttonVariants } from './components/ui/buttons/Button';\nexport { CopyButton } from './components/ui/buttons/CopyButton';\nexport { default as CancelButton } from './components/ui/buttons/CancelButton';\n\n// UI - Data Display\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './components/ui/data-display/Accordion';\nexport { Badge, badgeVariants } from './components/ui/data-display/Badge';\nexport { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent } from './components/ui/data-display/Card';\nexport { Pagination, PaginationContent, PaginationLink, PaginationItem, PaginationPrevious, PaginationNext, PaginationEllipsis } from './components/ui/data-display/Pagination';\nexport { ScrollArea, ScrollBar } from './components/ui/data-display/ScrollArea';\nexport { Separator } from './components/ui/data-display/Separator';\nexport { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption } from './components/ui/data-display/Table';\nexport { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/data-display/Tabs';\nexport { UserAvatar } from './components/ui/data-display/UserAvatar';\nexport type { UserAvatarProps } from './components/ui/data-display/UserAvatar';\n\n// UI - Feedback\nexport { default as CircularProgress } from './components/ui/feedback/CircularProgress';\nexport { default as DefaultCircularProgress } from './components/ui/feedback/DefaultCircularProgress';\nexport { Progress } from './components/ui/feedback/Progress';\nexport { Toast, toastVariants, toastIconContainerVariants } from './components/ui/feedback/Toast';\nexport type { ToastProps } from './components/ui/feedback/Toast';\n\n// UI - Form\nexport { Calendar, CalendarDayButton } from './components/ui/form/Calendar';\nexport { Checkbox } from './components/ui/form/Checkbox';\nexport { DatePicker } from './components/ui/form/DatePicker';\nexport { DateRangePicker } from './components/ui/form/DateRangePicker';\nexport { Input } from './components/ui/form/Input';\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from './components/ui/form/InputOtp';\nexport { RadioGroup, RadioGroupItem } from './components/ui/form/RadioGroup';\nexport { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from './components/ui/form/Select';\nexport { Switch } from './components/ui/form/Switch';\nexport { Textarea } from './components/ui/form/Textarea';\n\n// UI - Overlay\nexport { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator } from './components/ui/overlay/Command';\nexport { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from './components/ui/overlay/Dialog';\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } from './components/ui/overlay/Popover';\nexport { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription } from './components/ui/overlay/Sheet';\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/overlay/Tooltip';\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;AACxG,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAK7B,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,4BAA4B;AAErC,SAAS,4BAA4B;AAErC,SAAS,sBAAsB;AAE/B,SAAS,kBAAkB;AAI3B,SAAS,QAAQ,sBAAsB;AACvC,SAAS,kBAAkB;AAC3B,SAAoB,WAAXA,gBAA+B;AAGxC,SAAS,WAAW,eAAe,kBAAkB,wBAAwB;AAC7E,SAAS,OAAO,qBAAqB;AACrC,SAAS,MAAM,YAAY,YAAY,WAAW,YAAY,iBAAiB,mBAAmB;AAClG,SAAS,YAAY,mBAAmB,gBAAgB,gBAAgB,oBAAoB,gBAAgB,0BAA0B;AACtI,SAAS,YAAY,iBAAiB;AACtC,SAAS,iBAAiB;AAC1B,SAAS,OAAO,aAAa,WAAW,aAAa,WAAW,UAAU,WAAW,oBAAoB;AACzG,SAAS,MAAM,UAAU,aAAa,mBAAmB;AACzD,SAAS,kBAAkB;AAI3B,SAAoB,WAAXA,gBAAmC;AAC5C,SAAoB,WAAXA,gBAA0C;AACnD,SAAS,gBAAgB;AACzB,SAAS,OAAO,eAAe,kCAAkC;AAIjE,SAAS,UAAU,yBAAyB;AAC5C,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,aAAa;AACtB,SAAS,UAAU,eAAe,cAAc,yBAAyB;AACzE,SAAS,YAAY,sBAAsB;AAC3C,SAAS,QAAQ,eAAe,aAAa,YAAY,aAAa,wBAAwB,sBAAsB,iBAAiB,eAAe,mBAAmB;AACvK,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAGzB,SAAS,SAAS,eAAe,cAAc,aAAa,cAAc,cAAc,aAAa,iBAAiB,wBAAwB;AAC9I,SAAS,QAAQ,aAAa,eAAe,mBAAmB,cAAc,cAAc,eAAe,cAAc,aAAa,qBAAqB;AAC3J,SAAS,SAAS,gBAAgB,gBAAgB,qBAAqB;AACvE,SAAS,OAAO,cAAc,YAAY,cAAc,aAAa,aAAa,YAAY,wBAAwB;AACtH,SAAS,SAAS,gBAAgB,gBAAgB,uBAAuB;AAGzE,SAAS,yBAAyB;","names":["default"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
import { ApiError } from "../../../infra/api/types";
|
|
4
|
+
const AUTH_COOKIE_NAME = "greatapps";
|
|
5
|
+
async function getUserContext() {
|
|
6
|
+
const cookieStore = await cookies();
|
|
7
|
+
const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new ApiError("Usu\xE1rio n\xE3o autenticado", "NOT_AUTHENTICATED", 401);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
13
|
+
return {
|
|
14
|
+
id_account: payload.id_account,
|
|
15
|
+
id_user: payload.id_user
|
|
16
|
+
};
|
|
17
|
+
} catch {
|
|
18
|
+
throw new ApiError("Token inv\xE1lido", "INVALID_TOKEN", 401);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
getUserContext
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=get-user-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/auth/utils/get-user-context.ts"],"sourcesContent":["import 'server-only';\r\n\r\nimport { cookies } from 'next/headers';\r\nimport { ApiError, JWTPayload } from '../../../infra/api/types';\r\n\r\nconst AUTH_COOKIE_NAME = 'greatapps';\r\n\r\nexport async function getUserContext(): Promise<{\r\n id_account: number;\r\n id_user: number;\r\n}> {\r\n const cookieStore = await cookies();\r\n const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;\r\n\r\n if (!token) {\r\n throw new ApiError('Usuário não autenticado', 'NOT_AUTHENTICATED', 401);\r\n }\r\n\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1])) as JWTPayload;\r\n\r\n return {\r\n id_account: payload.id_account,\r\n id_user: payload.id_user,\r\n };\r\n } catch {\r\n throw new ApiError('Token inválido', 'INVALID_TOKEN', 401);\r\n }\r\n}\r\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,eAAe;AACxB,SAAS,gBAA4B;AAErC,MAAM,mBAAmB;AAEzB,eAAsB,iBAGnB;AACD,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,QAAQ,YAAY,IAAI,gBAAgB,GAAG;AAEjD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,SAAS,iCAA2B,qBAAqB,GAAG;AAAA,EACxE;AAEA,MAAI;AACF,UAAM,UAAU,KAAK,MAAM,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AAEpD,WAAO;AAAA,MACL,YAAY,QAAQ;AAAA,MACpB,SAAS,QAAQ;AAAA,IACnB;AAAA,EACF,QAAQ;AACN,UAAM,IAAI,SAAS,qBAAkB,iBAAiB,GAAG;AAAA,EAC3D;AACF;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { iaCreditsService } from "../services/ia-credits.service";
|
|
3
|
+
async function listIaCreditsAction(subscriptionId) {
|
|
4
|
+
return iaCreditsService.listOperations(subscriptionId);
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
listIaCreditsAction
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=list-ia-credits.action.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/ia-credits/actions/list-ia-credits.action.ts"],"sourcesContent":["'use server';\r\n\r\nimport { PaginatedSuccessResult } from '../../../infra/api/types';\r\nimport type { IaCreditOperation } from '../types';\r\nimport { iaCreditsService } from '../services/ia-credits.service';\r\n\r\nexport async function listIaCreditsAction(\r\n subscriptionId: number | string\r\n): Promise<PaginatedSuccessResult<IaCreditOperation>> {\r\n return iaCreditsService.listOperations(subscriptionId);\r\n}\r\n"],"mappings":";AAIA,SAAS,wBAAwB;AAEjC,eAAsB,oBACpB,gBACoD;AACpD,SAAO,iBAAiB,eAAe,cAAc;AACvD;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
import { useActiveSubscription } from "../../subscriptions/hooks/list-subscriptions.hook";
|
|
5
|
+
import { listIaCreditsAction } from "../actions/list-ia-credits.action";
|
|
6
|
+
function calculateSummary(operations) {
|
|
7
|
+
const today = /* @__PURE__ */ new Date();
|
|
8
|
+
const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
9
|
+
const adds = operations.filter((op) => op.operation === "add");
|
|
10
|
+
const subtracts = operations.filter((op) => op.operation === "subtract");
|
|
11
|
+
let totalCredits = 0;
|
|
12
|
+
let usedCredits = 0;
|
|
13
|
+
let nextExpirationInDays = null;
|
|
14
|
+
let nextExpiringCredits = null;
|
|
15
|
+
for (const add of adds) {
|
|
16
|
+
const expirationRaw = add.expiration_time;
|
|
17
|
+
if (!expirationRaw) continue;
|
|
18
|
+
const expiration = new Date(expirationRaw);
|
|
19
|
+
const expirationDate = new Date(
|
|
20
|
+
expiration.getFullYear(),
|
|
21
|
+
expiration.getMonth(),
|
|
22
|
+
expiration.getDate()
|
|
23
|
+
);
|
|
24
|
+
const expiredByDate = expirationDate.getTime() < todayDate.getTime();
|
|
25
|
+
const hasExpired = add.has_expired || expiredByDate;
|
|
26
|
+
if (hasExpired) continue;
|
|
27
|
+
const addedCredits = add.operation_value;
|
|
28
|
+
const consumedForAdd = subtracts.filter((sub) => String(sub.consumed_from_add_id) === String(add.id)).reduce((sum, sub) => sum + sub.operation_value, 0);
|
|
29
|
+
const remainingForAdd = Math.max(0, addedCredits - consumedForAdd);
|
|
30
|
+
totalCredits += addedCredits;
|
|
31
|
+
usedCredits += consumedForAdd;
|
|
32
|
+
const diffMs = expirationDate.getTime() - todayDate.getTime();
|
|
33
|
+
const days = Math.max(0, Math.ceil(diffMs / (1e3 * 60 * 60 * 24)));
|
|
34
|
+
if (remainingForAdd > 0) {
|
|
35
|
+
if (nextExpirationInDays === null || days < nextExpirationInDays) {
|
|
36
|
+
nextExpirationInDays = days;
|
|
37
|
+
nextExpiringCredits = remainingForAdd;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const availableCredits = Math.max(0, totalCredits - usedCredits);
|
|
42
|
+
return {
|
|
43
|
+
totalCredits,
|
|
44
|
+
usedCredits,
|
|
45
|
+
availableCredits,
|
|
46
|
+
nextExpiringCredits,
|
|
47
|
+
nextExpirationInDays
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function useIaCredits() {
|
|
51
|
+
const { data: subscriptionData, isPending: isSubscriptionLoading } = useActiveSubscription();
|
|
52
|
+
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
53
|
+
const subscriptionId = subscription?.id;
|
|
54
|
+
const query = useQuery({
|
|
55
|
+
queryKey: ["ia-credits", subscriptionId],
|
|
56
|
+
queryFn: () => listIaCreditsAction(subscriptionId),
|
|
57
|
+
enabled: subscriptionId != null
|
|
58
|
+
});
|
|
59
|
+
const operations = query.data?.data ?? [];
|
|
60
|
+
const summary = useMemo(() => calculateSummary(operations), [operations]);
|
|
61
|
+
return {
|
|
62
|
+
subscription,
|
|
63
|
+
operations,
|
|
64
|
+
summary,
|
|
65
|
+
isLoading: isSubscriptionLoading || query.isPending,
|
|
66
|
+
isSubscriptionLoading,
|
|
67
|
+
isCreditsLoading: query.isPending,
|
|
68
|
+
isError: query.isError,
|
|
69
|
+
error: query.error
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
useIaCredits
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=ia-credits.hook.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/ia-credits/hooks/ia-credits.hook.ts"],"sourcesContent":["'use client';\r\n\r\nimport { useMemo } from 'react';\r\nimport { useQuery } from '@tanstack/react-query';\r\nimport { useActiveSubscription } from '../../subscriptions/hooks/list-subscriptions.hook';\r\nimport { listIaCreditsAction } from '../actions/list-ia-credits.action';\r\nimport type { IaCreditOperation, IaCreditsSummary } from '../types';\r\n\r\nfunction calculateSummary(operations: IaCreditOperation[]): IaCreditsSummary {\r\n const today = new Date();\r\n const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());\r\n\r\n const adds = operations.filter((op) => op.operation === 'add');\r\n const subtracts = operations.filter((op) => op.operation === 'subtract');\r\n\r\n let totalCredits = 0;\r\n let usedCredits = 0;\r\n let nextExpirationInDays: number | null = null;\r\n let nextExpiringCredits: number | null = null;\r\n\r\n for (const add of adds) {\r\n const expirationRaw = add.expiration_time;\r\n if (!expirationRaw) continue;\r\n\r\n const expiration = new Date(expirationRaw);\r\n const expirationDate = new Date(\r\n expiration.getFullYear(),\r\n expiration.getMonth(),\r\n expiration.getDate()\r\n );\r\n\r\n const expiredByDate = expirationDate.getTime() < todayDate.getTime();\r\n const hasExpired = add.has_expired || expiredByDate;\r\n\r\n if (hasExpired) continue;\r\n\r\n const addedCredits = add.operation_value;\r\n\r\n const consumedForAdd = subtracts\r\n .filter((sub) => String(sub.consumed_from_add_id) === String(add.id))\r\n .reduce((sum, sub) => sum + sub.operation_value, 0);\r\n\r\n const remainingForAdd = Math.max(0, addedCredits - consumedForAdd);\r\n\r\n totalCredits += addedCredits;\r\n usedCredits += consumedForAdd;\r\n\r\n const diffMs = expirationDate.getTime() - todayDate.getTime();\r\n const days = Math.max(0, Math.ceil(diffMs / (1000 * 60 * 60 * 24)));\r\n\r\n if (remainingForAdd > 0) {\r\n if (nextExpirationInDays === null || days < nextExpirationInDays) {\r\n nextExpirationInDays = days;\r\n nextExpiringCredits = remainingForAdd;\r\n }\r\n }\r\n }\r\n\r\n const availableCredits = Math.max(0, totalCredits - usedCredits);\r\n\r\n return {\r\n totalCredits,\r\n usedCredits,\r\n availableCredits,\r\n nextExpiringCredits,\r\n nextExpirationInDays,\r\n };\r\n}\r\n\r\nexport function useIaCredits() {\r\n const { data: subscriptionData, isPending: isSubscriptionLoading } = useActiveSubscription();\r\n const subscription = subscriptionData?.data?.[0] ?? null;\r\n const subscriptionId = subscription?.id;\r\n\r\n const query = useQuery({\r\n queryKey: ['ia-credits', subscriptionId],\r\n queryFn: () => listIaCreditsAction(subscriptionId!),\r\n enabled: subscriptionId != null,\r\n });\r\n\r\n const operations = query.data?.data ?? [];\r\n\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n const summary = useMemo(() => calculateSummary(operations), [operations]);\r\n\r\n return {\r\n subscription,\r\n operations,\r\n summary,\r\n isLoading: isSubscriptionLoading || query.isPending,\r\n isSubscriptionLoading,\r\n isCreditsLoading: query.isPending,\r\n isError: query.isError,\r\n error: query.error,\r\n };\r\n}\r\n"],"mappings":";AAEA,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AAGpC,SAAS,iBAAiB,YAAmD;AAC3E,QAAM,QAAQ,oBAAI,KAAK;AACvB,QAAM,YAAY,IAAI,KAAK,MAAM,YAAY,GAAG,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC;AAEjF,QAAM,OAAO,WAAW,OAAO,CAAC,OAAO,GAAG,cAAc,KAAK;AAC7D,QAAM,YAAY,WAAW,OAAO,CAAC,OAAO,GAAG,cAAc,UAAU;AAEvE,MAAI,eAAe;AACnB,MAAI,cAAc;AAClB,MAAI,uBAAsC;AAC1C,MAAI,sBAAqC;AAEzC,aAAW,OAAO,MAAM;AACtB,UAAM,gBAAgB,IAAI;AAC1B,QAAI,CAAC,cAAe;AAEpB,UAAM,aAAa,IAAI,KAAK,aAAa;AACzC,UAAM,iBAAiB,IAAI;AAAA,MACzB,WAAW,YAAY;AAAA,MACvB,WAAW,SAAS;AAAA,MACpB,WAAW,QAAQ;AAAA,IACrB;AAEA,UAAM,gBAAgB,eAAe,QAAQ,IAAI,UAAU,QAAQ;AACnE,UAAM,aAAa,IAAI,eAAe;AAEtC,QAAI,WAAY;AAEhB,UAAM,eAAe,IAAI;AAEzB,UAAM,iBAAiB,UACpB,OAAO,CAAC,QAAQ,OAAO,IAAI,oBAAoB,MAAM,OAAO,IAAI,EAAE,CAAC,EACnE,OAAO,CAAC,KAAK,QAAQ,MAAM,IAAI,iBAAiB,CAAC;AAEpD,UAAM,kBAAkB,KAAK,IAAI,GAAG,eAAe,cAAc;AAEjE,oBAAgB;AAChB,mBAAe;AAEf,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,QAAQ;AAC5D,UAAM,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,UAAU,MAAO,KAAK,KAAK,GAAG,CAAC;AAElE,QAAI,kBAAkB,GAAG;AACvB,UAAI,yBAAyB,QAAQ,OAAO,sBAAsB;AAChE,+BAAuB;AACvB,8BAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,mBAAmB,KAAK,IAAI,GAAG,eAAe,WAAW;AAE/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,eAAe;AAC7B,QAAM,EAAE,MAAM,kBAAkB,WAAW,sBAAsB,IAAI,sBAAsB;AAC3F,QAAM,eAAe,kBAAkB,OAAO,CAAC,KAAK;AACpD,QAAM,iBAAiB,cAAc;AAErC,QAAM,QAAQ,SAAS;AAAA,IACrB,UAAU,CAAC,cAAc,cAAc;AAAA,IACvC,SAAS,MAAM,oBAAoB,cAAe;AAAA,IAClD,SAAS,kBAAkB;AAAA,EAC7B,CAAC;AAED,QAAM,aAAa,MAAM,MAAM,QAAQ,CAAC;AAGxC,QAAM,UAAU,QAAQ,MAAM,iBAAiB,UAAU,GAAG,CAAC,UAAU,CAAC;AAExE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,yBAAyB,MAAM;AAAA,IAC1C;AAAA,IACA,kBAAkB,MAAM;AAAA,IACxB,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,EACf;AACF;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { apiClient } from "../../../infra/api/client";
|
|
3
|
+
import { ApiError } from "../../../infra/api/types";
|
|
4
|
+
import { getUserContext } from "../../auth/utils/get-user-context";
|
|
5
|
+
import { IaCreditOperationSchema } from "../types";
|
|
6
|
+
class IaCreditsService {
|
|
7
|
+
async listOperations(subscriptionId) {
|
|
8
|
+
const { id_account } = await getUserContext();
|
|
9
|
+
const endpoint = `/accounts/${id_account}/subscriptions/${subscriptionId}/items/balance/list?page=1&limit=20&sort=id:DESC`;
|
|
10
|
+
const response = await apiClient.get(endpoint);
|
|
11
|
+
if (response.status === 0) {
|
|
12
|
+
throw new ApiError(
|
|
13
|
+
response.message || "Erro ao listar cr\xE9ditos de IA",
|
|
14
|
+
"LIST_IA_CREDITS_FAILED",
|
|
15
|
+
400
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const raw = response.data ?? [];
|
|
19
|
+
const data = raw.map((item) => IaCreditOperationSchema.parse(item));
|
|
20
|
+
return {
|
|
21
|
+
data,
|
|
22
|
+
total: response.total ?? data.length,
|
|
23
|
+
success: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const iaCreditsService = new IaCreditsService();
|
|
28
|
+
export {
|
|
29
|
+
iaCreditsService
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=ia-credits.service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/ia-credits/services/ia-credits.service.ts"],"sourcesContent":["import 'server-only';\r\n\r\nimport { apiClient } from '../../../infra/api/client';\r\nimport { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';\r\nimport { getUserContext } from '../../auth/utils/get-user-context';\r\nimport { IaCreditOperation, IaCreditOperationSchema } from '../types';\r\n\r\nclass IaCreditsService {\r\n async listOperations(\r\n subscriptionId: number | string\r\n ): Promise<PaginatedSuccessResult<IaCreditOperation>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const endpoint = `/accounts/${id_account}/subscriptions/${subscriptionId}/items/balance/list?page=1&limit=20&sort=id:DESC`;\r\n\r\n const response = await apiClient.get<ApiPaginatedActionResult<IaCreditOperation>>(endpoint);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || 'Erro ao listar créditos de IA',\r\n 'LIST_IA_CREDITS_FAILED',\r\n 400\r\n );\r\n }\r\n\r\n const raw = response.data ?? [];\r\n const data = raw.map((item) => IaCreditOperationSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total ?? data.length,\r\n success: true,\r\n };\r\n }\r\n}\r\n\r\nexport const iaCreditsService = new IaCreditsService();\r\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,iBAAiB;AAC1B,SAAS,gBAAkE;AAC3E,SAAS,sBAAsB;AAC/B,SAA4B,+BAA+B;AAE3D,MAAM,iBAAiB;AAAA,EACrB,MAAM,eACJ,gBACoD;AACpD,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,WAAW,aAAa,UAAU,kBAAkB,cAAc;AAExE,UAAM,WAAW,MAAM,UAAU,IAAiD,QAAQ;AAE1F,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,SAAS,QAAQ,CAAC;AAC9B,UAAM,OAAO,IAAI,IAAI,CAAC,SAAS,wBAAwB,MAAM,IAAI,CAAC;AAElE,WAAO;AAAA,MACL;AAAA,MACA,OAAO,SAAS,SAAS,KAAK;AAAA,MAC9B,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEO,MAAM,mBAAmB,IAAI,iBAAiB;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const IaCreditOperationSchema = z.object({
|
|
3
|
+
id: z.union([z.number(), z.string()]),
|
|
4
|
+
deleted: z.number(),
|
|
5
|
+
datetime_add: z.string(),
|
|
6
|
+
id_wl: z.number(),
|
|
7
|
+
id_subscription: z.union([z.number(), z.string()]),
|
|
8
|
+
id_addon: z.number(),
|
|
9
|
+
operation: z.string(),
|
|
10
|
+
// 'add' | 'subtract'
|
|
11
|
+
operation_value: z.union([z.number(), z.string()]).transform((v) => Number(v)),
|
|
12
|
+
balance_after_operation: z.union([z.number(), z.string()]).optional(),
|
|
13
|
+
balance_before_operation: z.union([z.number(), z.string()]).optional(),
|
|
14
|
+
operation_ref: z.string().nullable().optional(),
|
|
15
|
+
operation_origin: z.string().nullable().optional(),
|
|
16
|
+
expiration_time: z.string().nullable(),
|
|
17
|
+
has_expired: z.boolean(),
|
|
18
|
+
consumed_from_add_id: z.union([z.number(), z.string()]).nullable(),
|
|
19
|
+
id_user_add: z.union([z.number(), z.string()]).nullable(),
|
|
20
|
+
user_name: z.string().nullable().optional()
|
|
21
|
+
}).passthrough();
|
|
22
|
+
export {
|
|
23
|
+
IaCreditOperationSchema
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/ia-credits/types.ts"],"sourcesContent":["import z from 'zod';\r\n\r\nexport const IaCreditOperationSchema = z\r\n .object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n datetime_add: z.string(),\r\n id_wl: z.number(),\r\n id_subscription: z.union([z.number(), z.string()]),\r\n id_addon: z.number(),\r\n operation: z.string(), // 'add' | 'subtract'\r\n operation_value: z.union([z.number(), z.string()]).transform((v) => Number(v)),\r\n balance_after_operation: z.union([z.number(), z.string()]).optional(),\r\n balance_before_operation: z.union([z.number(), z.string()]).optional(),\r\n operation_ref: z.string().nullable().optional(),\r\n operation_origin: z.string().nullable().optional(),\r\n expiration_time: z.string().nullable(),\r\n has_expired: z.boolean(),\r\n consumed_from_add_id: z.union([z.number(), z.string()]).nullable(),\r\n id_user_add: z.union([z.number(), z.string()]).nullable(),\r\n user_name: z.string().nullable().optional(),\r\n })\r\n .passthrough();\r\n\r\nexport type IaCreditOperation = z.infer<typeof IaCreditOperationSchema>;\r\n\r\nexport type IaCreditsSummary = {\r\n totalCredits: number;\r\n usedCredits: number;\r\n availableCredits: number;\r\n nextExpiringCredits: number | null;\r\n nextExpirationInDays: number | null;\r\n};\r\n"],"mappings":"AAAA,OAAO,OAAO;AAEP,MAAM,0BAA0B,EACpC,OAAO;AAAA,EACN,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACpC,SAAS,EAAE,OAAO;AAAA,EAClB,cAAc,EAAE,OAAO;AAAA,EACvB,OAAO,EAAE,OAAO;AAAA,EAChB,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACjD,UAAU,EAAE,OAAO;AAAA,EACnB,WAAW,EAAE,OAAO;AAAA;AAAA,EACpB,iBAAiB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,MAAM,OAAO,CAAC,CAAC;AAAA,EAC7E,yBAAyB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACpE,0BAA0B,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACrE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,aAAa,EAAE,QAAQ;AAAA,EACvB,sBAAsB,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACjE,aAAa,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS;AAAA,EACxD,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { subscriptionsService } from "../services/subscriptions.service";
|
|
3
|
+
async function listSubscriptionsAction(params) {
|
|
4
|
+
return subscriptionsService.listSubscriptions(params);
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
listSubscriptionsAction
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=list-subscriptions.action.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/actions/list-subscriptions.action.ts"],"sourcesContent":["'use server';\r\n\r\nimport { PaginatedSuccessResult } from '../../../infra/api/types';\r\nimport type { Subscription, FindSubscriptionsParams } from '../types';\r\nimport { subscriptionsService } from '../services/subscriptions.service';\r\n\r\nexport async function listSubscriptionsAction(\r\n params?: Partial<FindSubscriptionsParams>\r\n): Promise<PaginatedSuccessResult<Subscription>> {\r\n return subscriptionsService.listSubscriptions(params);\r\n}\r\n"],"mappings":";AAIA,SAAS,4BAA4B;AAErC,eAAsB,wBACpB,QAC+C;AAC/C,SAAO,qBAAqB,kBAAkB,MAAM;AACtD;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { listSubscriptionsAction } from "../actions/list-subscriptions.action";
|
|
4
|
+
const SUBSCRIPTIONS_QUERY_KEY = ["subscriptions"];
|
|
5
|
+
function useActiveSubscription() {
|
|
6
|
+
return useQuery({
|
|
7
|
+
queryKey: [...SUBSCRIPTIONS_QUERY_KEY, "active"],
|
|
8
|
+
queryFn: () => listSubscriptionsAction({ active: true })
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
useActiveSubscription
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=list-subscriptions.hook.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/hooks/list-subscriptions.hook.ts"],"sourcesContent":["'use client';\r\n\r\nimport { useQuery } from '@tanstack/react-query';\r\nimport { listSubscriptionsAction } from '../actions/list-subscriptions.action';\r\nimport type { FindSubscriptionsParams } from '../types';\r\n\r\nconst SUBSCRIPTIONS_QUERY_KEY = ['subscriptions'];\r\n\r\nexport function useActiveSubscription() {\r\n return useQuery({\r\n queryKey: [...SUBSCRIPTIONS_QUERY_KEY, 'active'],\r\n queryFn: () => listSubscriptionsAction({ active: true } as Partial<FindSubscriptionsParams>),\r\n });\r\n}\r\n"],"mappings":";AAEA,SAAS,gBAAgB;AACzB,SAAS,+BAA+B;AAGxC,MAAM,0BAA0B,CAAC,eAAe;AAEzC,SAAS,wBAAwB;AACtC,SAAO,SAAS;AAAA,IACd,UAAU,CAAC,GAAG,yBAAyB,QAAQ;AAAA,IAC/C,SAAS,MAAM,wBAAwB,EAAE,QAAQ,KAAK,CAAqC;AAAA,EAC7F,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { apiClient } from "../../../infra/api/client";
|
|
3
|
+
import { ApiError } from "../../../infra/api/types";
|
|
4
|
+
import { buildQueryParams } from "../../../infra/utils/params";
|
|
5
|
+
import { getUserContext } from "../../auth/utils/get-user-context";
|
|
6
|
+
import { SubscriptionSchema } from "../types";
|
|
7
|
+
class SubscriptionsService {
|
|
8
|
+
async listSubscriptions(params) {
|
|
9
|
+
const { id_account } = await getUserContext();
|
|
10
|
+
const query = buildQueryParams(params);
|
|
11
|
+
const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : ""}`;
|
|
12
|
+
const response = await apiClient.get(url);
|
|
13
|
+
if (response.status === 0) {
|
|
14
|
+
throw new ApiError(
|
|
15
|
+
response.message || "Erro ao listar assinaturas",
|
|
16
|
+
"LIST_SUBSCRIPTIONS_FAILED",
|
|
17
|
+
400
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const data = response.data.map((item) => SubscriptionSchema.parse(item));
|
|
21
|
+
return {
|
|
22
|
+
data,
|
|
23
|
+
total: response.total,
|
|
24
|
+
success: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const subscriptionsService = new SubscriptionsService();
|
|
29
|
+
export {
|
|
30
|
+
subscriptionsService
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=subscriptions.service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/subscriptions/services/subscriptions.service.ts"],"sourcesContent":["import 'server-only';\r\n\r\nimport { apiClient } from '../../../infra/api/client';\r\nimport { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';\r\nimport { buildQueryParams } from '../../../infra/utils/params';\r\nimport { getUserContext } from '../../auth/utils/get-user-context';\r\nimport { Subscription, SubscriptionSchema, FindSubscriptionsParams } from '../types';\r\n\r\nclass SubscriptionsService {\r\n async listSubscriptions(\r\n params?: Partial<FindSubscriptionsParams>\r\n ): Promise<PaginatedSuccessResult<Subscription>> {\r\n const { id_account } = await getUserContext();\r\n\r\n const query = buildQueryParams(params as Record<string, string | boolean | undefined>);\r\n const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : ''}`;\r\n\r\n const response = await apiClient.get<ApiPaginatedActionResult<Subscription>>(url);\r\n\r\n if (response.status === 0) {\r\n throw new ApiError(\r\n response.message || 'Erro ao listar assinaturas',\r\n 'LIST_SUBSCRIPTIONS_FAILED',\r\n 400\r\n );\r\n }\r\n\r\n const data = response.data.map((item) => SubscriptionSchema.parse(item));\r\n\r\n return {\r\n data,\r\n total: response.total,\r\n success: true,\r\n } satisfies PaginatedSuccessResult<Subscription>;\r\n }\r\n}\r\n\r\nexport const subscriptionsService = new SubscriptionsService();\r\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,iBAAiB;AAC1B,SAAS,gBAAkE;AAC3E,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAuB,0BAAmD;AAE1E,MAAM,qBAAqB;AAAA,EACzB,MAAM,kBACJ,QAC+C;AAC/C,UAAM,EAAE,WAAW,IAAI,MAAM,eAAe;AAE5C,UAAM,QAAQ,iBAAiB,MAAsD;AACrF,UAAM,MAAM,aAAa,UAAU,iBAAiB,QAAQ,IAAI,KAAK,KAAK,EAAE;AAE5E,UAAM,WAAW,MAAM,UAAU,IAA4C,GAAG;AAEhF,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,SAAS,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,SAAS,KAAK,IAAI,CAAC,SAAS,mBAAmB,MAAM,IAAI,CAAC;AAEvE,WAAO;AAAA,MACL;AAAA,MACA,OAAO,SAAS;AAAA,MAChB,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEO,MAAM,uBAAuB,IAAI,qBAAqB;","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
const SubscriptionSchema = z.object({
|
|
3
|
+
id: z.union([z.number(), z.string()]),
|
|
4
|
+
deleted: z.number(),
|
|
5
|
+
id_wl: z.number(),
|
|
6
|
+
id_account: z.union([z.number(), z.string()]),
|
|
7
|
+
active: z.boolean()
|
|
8
|
+
}).passthrough();
|
|
9
|
+
export {
|
|
10
|
+
SubscriptionSchema
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/subscriptions/types.ts"],"sourcesContent":["import z from 'zod';\r\n\r\nexport const SubscriptionSchema = z\r\n .object({\r\n id: z.union([z.number(), z.string()]),\r\n deleted: z.number(),\r\n id_wl: z.number(),\r\n id_account: z.union([z.number(), z.string()]),\r\n active: z.boolean(),\r\n })\r\n .passthrough();\r\n\r\nexport type Subscription = z.infer<typeof SubscriptionSchema>;\r\n\r\nexport interface FindSubscriptionsParams {\r\n active?: boolean;\r\n}\r\n"],"mappings":"AAAA,OAAO,OAAO;AAEP,MAAM,qBAAqB,EAC/B,OAAO;AAAA,EACN,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EACpC,SAAS,EAAE,OAAO;AAAA,EAClB,OAAO,EAAE,OAAO;AAAA,EAChB,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,EAC5C,QAAQ,EAAE,QAAQ;AACpB,CAAC,EACA,YAAY;","names":[]}
|
package/dist/server.mjs
CHANGED
|
@@ -2,15 +2,25 @@ import "server-only";
|
|
|
2
2
|
import { apiClient, ApiClient } from "./infra/api/client";
|
|
3
3
|
import { authService } from "./modules/auth/services/auth.service";
|
|
4
4
|
import { whitelabelService } from "./modules/whitelabel/services/whitelabel.service";
|
|
5
|
+
import { subscriptionsService } from "./modules/subscriptions/services/subscriptions.service";
|
|
6
|
+
import { iaCreditsService } from "./modules/ia-credits/services/ia-credits.service";
|
|
5
7
|
import { findWhitelabel } from "./modules/whitelabel/actions/find-whitelabel.action";
|
|
6
8
|
import { validateSessionAction } from "./modules/auth/actions/validate-session.action";
|
|
9
|
+
import { listSubscriptionsAction } from "./modules/subscriptions/actions/list-subscriptions.action";
|
|
10
|
+
import { listIaCreditsAction } from "./modules/ia-credits/actions/list-ia-credits.action";
|
|
7
11
|
import { getClientInfoFromRequest } from "./infra/utils/client-info";
|
|
12
|
+
import { getUserContext } from "./modules/auth/utils/get-user-context";
|
|
8
13
|
export {
|
|
9
14
|
ApiClient,
|
|
10
15
|
apiClient,
|
|
11
16
|
authService,
|
|
12
17
|
findWhitelabel,
|
|
13
18
|
getClientInfoFromRequest,
|
|
19
|
+
getUserContext,
|
|
20
|
+
iaCreditsService,
|
|
21
|
+
listIaCreditsAction,
|
|
22
|
+
listSubscriptionsAction,
|
|
23
|
+
subscriptionsService,
|
|
14
24
|
validateSessionAction,
|
|
15
25
|
whitelabelService
|
|
16
26
|
};
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { apiClient, ApiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,iBAAiB;AAGrC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { apiClient, ApiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,iBAAiB;AAGrC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,4BAA4B;AACrC,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,2BAA2B;AAGpC,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;","names":[]}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import { NavBarItem } from './NavBarItem';
|
|
|
11
11
|
import { UserAvatar } from '../ui/data-display/UserAvatar';
|
|
12
12
|
import { useAuth } from '../../providers/auth.provider';
|
|
13
13
|
import { cn } from '../../infra/utils/clsx';
|
|
14
|
+
import { useIaCredits } from '../../modules/ia-credits/hooks/ia-credits.hook';
|
|
14
15
|
|
|
15
16
|
export interface ProfileMenuItem {
|
|
16
17
|
icon?: React.ReactNode;
|
|
@@ -23,7 +24,6 @@ export interface ProfilePopoverProps {
|
|
|
23
24
|
align?: 'start' | 'center' | 'end';
|
|
24
25
|
contentClassName?: string;
|
|
25
26
|
onEditProfile?: () => void;
|
|
26
|
-
credits?: { used: number; total: number };
|
|
27
27
|
onCreditsClick?: () => void;
|
|
28
28
|
menuItems?: ProfileMenuItem[];
|
|
29
29
|
logoutRedirect?: string;
|
|
@@ -35,7 +35,6 @@ function ProfilePopover({
|
|
|
35
35
|
align = 'start',
|
|
36
36
|
contentClassName = 'absolute bottom-2 left-[26px]',
|
|
37
37
|
onEditProfile,
|
|
38
|
-
credits,
|
|
39
38
|
onCreditsClick,
|
|
40
39
|
menuItems = [],
|
|
41
40
|
logoutRedirect = '/login',
|
|
@@ -45,6 +44,8 @@ function ProfilePopover({
|
|
|
45
44
|
const { user, logout } = useAuth();
|
|
46
45
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
47
46
|
|
|
47
|
+
const { summary, subscription, isSubscriptionLoading, isCreditsLoading } = useIaCredits();
|
|
48
|
+
|
|
48
49
|
const handleLogout = async () => {
|
|
49
50
|
try {
|
|
50
51
|
setIsLoggingOut(true);
|
|
@@ -57,9 +58,12 @@ function ProfilePopover({
|
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
60
|
|
|
61
|
+
const hasSubscription = !isSubscriptionLoading && subscription !== null;
|
|
62
|
+
const showCreditsSkeleton = hasSubscription && isCreditsLoading;
|
|
63
|
+
const showCredits = hasSubscription && !isCreditsLoading && summary.totalCredits > 0;
|
|
60
64
|
const creditsPercentage =
|
|
61
|
-
|
|
62
|
-
? (
|
|
65
|
+
showCredits && summary.totalCredits > 0
|
|
66
|
+
? (summary.usedCredits / summary.totalCredits) * 100
|
|
63
67
|
: 0;
|
|
64
68
|
|
|
65
69
|
return (
|
|
@@ -118,7 +122,19 @@ function ProfilePopover({
|
|
|
118
122
|
</div>
|
|
119
123
|
|
|
120
124
|
<div className="flex flex-col gap-2 border-t border-gray-200 rounded-2xl p-1.5">
|
|
121
|
-
{
|
|
125
|
+
{showCreditsSkeleton && (
|
|
126
|
+
<div className="flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4">
|
|
127
|
+
<div className="flex items-center justify-between gap-2">
|
|
128
|
+
<span className="paragraph-xsmall-semibold text-white truncate">
|
|
129
|
+
Créditos usados
|
|
130
|
+
</span>
|
|
131
|
+
<div className="h-3.5 w-16 rounded bg-white/20 animate-pulse" />
|
|
132
|
+
</div>
|
|
133
|
+
<div className="h-1.5 w-full rounded-full bg-white/20 animate-pulse" />
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{showCredits && (
|
|
122
138
|
<div
|
|
123
139
|
className={cn(
|
|
124
140
|
'flex flex-col bg-gray-950 rounded-lg gap-4 w-full p-4 transition-colors',
|
|
@@ -131,8 +147,8 @@ function ProfilePopover({
|
|
|
131
147
|
Créditos usados
|
|
132
148
|
</span>
|
|
133
149
|
<span className="paragraph-xsmall-medium text-white/50">
|
|
134
|
-
{
|
|
135
|
-
{
|
|
150
|
+
{summary.usedCredits.toLocaleString('pt-BR')}/
|
|
151
|
+
{summary.totalCredits.toLocaleString('pt-BR')}
|
|
136
152
|
</span>
|
|
137
153
|
</div>
|
|
138
154
|
<Progress
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,10 @@ export * from './providers/whitelabel.provider';
|
|
|
11
11
|
|
|
12
12
|
// Hooks
|
|
13
13
|
export { useUserQuery, useUserValidateSession, useInvalidateUser, useSetUserData, USER_QUERY_KEY } from './modules/auth/hooks/useUserQuery';
|
|
14
|
+
export { useActiveSubscription } from './modules/subscriptions/hooks/list-subscriptions.hook';
|
|
15
|
+
export { useIaCredits } from './modules/ia-credits/hooks/ia-credits.hook';
|
|
16
|
+
export type { IaCreditsSummary, IaCreditOperation } from './modules/ia-credits/types';
|
|
17
|
+
export type { Subscription, FindSubscriptionsParams } from './modules/subscriptions/types';
|
|
14
18
|
|
|
15
19
|
// Providers
|
|
16
20
|
export { QueryProvider } from './providers/query.provider';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { cookies } from 'next/headers';
|
|
4
|
+
import { ApiError, JWTPayload } from '../../../infra/api/types';
|
|
5
|
+
|
|
6
|
+
const AUTH_COOKIE_NAME = 'greatapps';
|
|
7
|
+
|
|
8
|
+
export async function getUserContext(): Promise<{
|
|
9
|
+
id_account: number;
|
|
10
|
+
id_user: number;
|
|
11
|
+
}> {
|
|
12
|
+
const cookieStore = await cookies();
|
|
13
|
+
const token = cookieStore.get(AUTH_COOKIE_NAME)?.value;
|
|
14
|
+
|
|
15
|
+
if (!token) {
|
|
16
|
+
throw new ApiError('Usuário não autenticado', 'NOT_AUTHENTICATED', 401);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const payload = JSON.parse(atob(token.split('.')[1])) as JWTPayload;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
id_account: payload.id_account,
|
|
24
|
+
id_user: payload.id_user,
|
|
25
|
+
};
|
|
26
|
+
} catch {
|
|
27
|
+
throw new ApiError('Token inválido', 'INVALID_TOKEN', 401);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { PaginatedSuccessResult } from '../../../infra/api/types';
|
|
4
|
+
import type { IaCreditOperation } from '../types';
|
|
5
|
+
import { iaCreditsService } from '../services/ia-credits.service';
|
|
6
|
+
|
|
7
|
+
export async function listIaCreditsAction(
|
|
8
|
+
subscriptionId: number | string
|
|
9
|
+
): Promise<PaginatedSuccessResult<IaCreditOperation>> {
|
|
10
|
+
return iaCreditsService.listOperations(subscriptionId);
|
|
11
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { useQuery } from '@tanstack/react-query';
|
|
5
|
+
import { useActiveSubscription } from '../../subscriptions/hooks/list-subscriptions.hook';
|
|
6
|
+
import { listIaCreditsAction } from '../actions/list-ia-credits.action';
|
|
7
|
+
import type { IaCreditOperation, IaCreditsSummary } from '../types';
|
|
8
|
+
|
|
9
|
+
function calculateSummary(operations: IaCreditOperation[]): IaCreditsSummary {
|
|
10
|
+
const today = new Date();
|
|
11
|
+
const todayDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
12
|
+
|
|
13
|
+
const adds = operations.filter((op) => op.operation === 'add');
|
|
14
|
+
const subtracts = operations.filter((op) => op.operation === 'subtract');
|
|
15
|
+
|
|
16
|
+
let totalCredits = 0;
|
|
17
|
+
let usedCredits = 0;
|
|
18
|
+
let nextExpirationInDays: number | null = null;
|
|
19
|
+
let nextExpiringCredits: number | null = null;
|
|
20
|
+
|
|
21
|
+
for (const add of adds) {
|
|
22
|
+
const expirationRaw = add.expiration_time;
|
|
23
|
+
if (!expirationRaw) continue;
|
|
24
|
+
|
|
25
|
+
const expiration = new Date(expirationRaw);
|
|
26
|
+
const expirationDate = new Date(
|
|
27
|
+
expiration.getFullYear(),
|
|
28
|
+
expiration.getMonth(),
|
|
29
|
+
expiration.getDate()
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const expiredByDate = expirationDate.getTime() < todayDate.getTime();
|
|
33
|
+
const hasExpired = add.has_expired || expiredByDate;
|
|
34
|
+
|
|
35
|
+
if (hasExpired) continue;
|
|
36
|
+
|
|
37
|
+
const addedCredits = add.operation_value;
|
|
38
|
+
|
|
39
|
+
const consumedForAdd = subtracts
|
|
40
|
+
.filter((sub) => String(sub.consumed_from_add_id) === String(add.id))
|
|
41
|
+
.reduce((sum, sub) => sum + sub.operation_value, 0);
|
|
42
|
+
|
|
43
|
+
const remainingForAdd = Math.max(0, addedCredits - consumedForAdd);
|
|
44
|
+
|
|
45
|
+
totalCredits += addedCredits;
|
|
46
|
+
usedCredits += consumedForAdd;
|
|
47
|
+
|
|
48
|
+
const diffMs = expirationDate.getTime() - todayDate.getTime();
|
|
49
|
+
const days = Math.max(0, Math.ceil(diffMs / (1000 * 60 * 60 * 24)));
|
|
50
|
+
|
|
51
|
+
if (remainingForAdd > 0) {
|
|
52
|
+
if (nextExpirationInDays === null || days < nextExpirationInDays) {
|
|
53
|
+
nextExpirationInDays = days;
|
|
54
|
+
nextExpiringCredits = remainingForAdd;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const availableCredits = Math.max(0, totalCredits - usedCredits);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
totalCredits,
|
|
63
|
+
usedCredits,
|
|
64
|
+
availableCredits,
|
|
65
|
+
nextExpiringCredits,
|
|
66
|
+
nextExpirationInDays,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function useIaCredits() {
|
|
71
|
+
const { data: subscriptionData, isPending: isSubscriptionLoading } = useActiveSubscription();
|
|
72
|
+
const subscription = subscriptionData?.data?.[0] ?? null;
|
|
73
|
+
const subscriptionId = subscription?.id;
|
|
74
|
+
|
|
75
|
+
const query = useQuery({
|
|
76
|
+
queryKey: ['ia-credits', subscriptionId],
|
|
77
|
+
queryFn: () => listIaCreditsAction(subscriptionId!),
|
|
78
|
+
enabled: subscriptionId != null,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const operations = query.data?.data ?? [];
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
const summary = useMemo(() => calculateSummary(operations), [operations]);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
subscription,
|
|
88
|
+
operations,
|
|
89
|
+
summary,
|
|
90
|
+
isLoading: isSubscriptionLoading || query.isPending,
|
|
91
|
+
isSubscriptionLoading,
|
|
92
|
+
isCreditsLoading: query.isPending,
|
|
93
|
+
isError: query.isError,
|
|
94
|
+
error: query.error,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { apiClient } from '../../../infra/api/client';
|
|
4
|
+
import { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';
|
|
5
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
6
|
+
import { IaCreditOperation, IaCreditOperationSchema } from '../types';
|
|
7
|
+
|
|
8
|
+
class IaCreditsService {
|
|
9
|
+
async listOperations(
|
|
10
|
+
subscriptionId: number | string
|
|
11
|
+
): Promise<PaginatedSuccessResult<IaCreditOperation>> {
|
|
12
|
+
const { id_account } = await getUserContext();
|
|
13
|
+
|
|
14
|
+
const endpoint = `/accounts/${id_account}/subscriptions/${subscriptionId}/items/balance/list?page=1&limit=20&sort=id:DESC`;
|
|
15
|
+
|
|
16
|
+
const response = await apiClient.get<ApiPaginatedActionResult<IaCreditOperation>>(endpoint);
|
|
17
|
+
|
|
18
|
+
if (response.status === 0) {
|
|
19
|
+
throw new ApiError(
|
|
20
|
+
response.message || 'Erro ao listar créditos de IA',
|
|
21
|
+
'LIST_IA_CREDITS_FAILED',
|
|
22
|
+
400
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const raw = response.data ?? [];
|
|
27
|
+
const data = raw.map((item) => IaCreditOperationSchema.parse(item));
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
data,
|
|
31
|
+
total: response.total ?? data.length,
|
|
32
|
+
success: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const iaCreditsService = new IaCreditsService();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const IaCreditOperationSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
id: z.union([z.number(), z.string()]),
|
|
6
|
+
deleted: z.number(),
|
|
7
|
+
datetime_add: z.string(),
|
|
8
|
+
id_wl: z.number(),
|
|
9
|
+
id_subscription: z.union([z.number(), z.string()]),
|
|
10
|
+
id_addon: z.number(),
|
|
11
|
+
operation: z.string(), // 'add' | 'subtract'
|
|
12
|
+
operation_value: z.union([z.number(), z.string()]).transform((v) => Number(v)),
|
|
13
|
+
balance_after_operation: z.union([z.number(), z.string()]).optional(),
|
|
14
|
+
balance_before_operation: z.union([z.number(), z.string()]).optional(),
|
|
15
|
+
operation_ref: z.string().nullable().optional(),
|
|
16
|
+
operation_origin: z.string().nullable().optional(),
|
|
17
|
+
expiration_time: z.string().nullable(),
|
|
18
|
+
has_expired: z.boolean(),
|
|
19
|
+
consumed_from_add_id: z.union([z.number(), z.string()]).nullable(),
|
|
20
|
+
id_user_add: z.union([z.number(), z.string()]).nullable(),
|
|
21
|
+
user_name: z.string().nullable().optional(),
|
|
22
|
+
})
|
|
23
|
+
.passthrough();
|
|
24
|
+
|
|
25
|
+
export type IaCreditOperation = z.infer<typeof IaCreditOperationSchema>;
|
|
26
|
+
|
|
27
|
+
export type IaCreditsSummary = {
|
|
28
|
+
totalCredits: number;
|
|
29
|
+
usedCredits: number;
|
|
30
|
+
availableCredits: number;
|
|
31
|
+
nextExpiringCredits: number | null;
|
|
32
|
+
nextExpirationInDays: number | null;
|
|
33
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { PaginatedSuccessResult } from '../../../infra/api/types';
|
|
4
|
+
import type { Subscription, FindSubscriptionsParams } from '../types';
|
|
5
|
+
import { subscriptionsService } from '../services/subscriptions.service';
|
|
6
|
+
|
|
7
|
+
export async function listSubscriptionsAction(
|
|
8
|
+
params?: Partial<FindSubscriptionsParams>
|
|
9
|
+
): Promise<PaginatedSuccessResult<Subscription>> {
|
|
10
|
+
return subscriptionsService.listSubscriptions(params);
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useQuery } from '@tanstack/react-query';
|
|
4
|
+
import { listSubscriptionsAction } from '../actions/list-subscriptions.action';
|
|
5
|
+
import type { FindSubscriptionsParams } from '../types';
|
|
6
|
+
|
|
7
|
+
const SUBSCRIPTIONS_QUERY_KEY = ['subscriptions'];
|
|
8
|
+
|
|
9
|
+
export function useActiveSubscription() {
|
|
10
|
+
return useQuery({
|
|
11
|
+
queryKey: [...SUBSCRIPTIONS_QUERY_KEY, 'active'],
|
|
12
|
+
queryFn: () => listSubscriptionsAction({ active: true } as Partial<FindSubscriptionsParams>),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
|
|
3
|
+
import { apiClient } from '../../../infra/api/client';
|
|
4
|
+
import { ApiError, ApiPaginatedActionResult, PaginatedSuccessResult } from '../../../infra/api/types';
|
|
5
|
+
import { buildQueryParams } from '../../../infra/utils/params';
|
|
6
|
+
import { getUserContext } from '../../auth/utils/get-user-context';
|
|
7
|
+
import { Subscription, SubscriptionSchema, FindSubscriptionsParams } from '../types';
|
|
8
|
+
|
|
9
|
+
class SubscriptionsService {
|
|
10
|
+
async listSubscriptions(
|
|
11
|
+
params?: Partial<FindSubscriptionsParams>
|
|
12
|
+
): Promise<PaginatedSuccessResult<Subscription>> {
|
|
13
|
+
const { id_account } = await getUserContext();
|
|
14
|
+
|
|
15
|
+
const query = buildQueryParams(params as Record<string, string | boolean | undefined>);
|
|
16
|
+
const url = `/accounts/${id_account}/subscriptions${query ? `?${query}` : ''}`;
|
|
17
|
+
|
|
18
|
+
const response = await apiClient.get<ApiPaginatedActionResult<Subscription>>(url);
|
|
19
|
+
|
|
20
|
+
if (response.status === 0) {
|
|
21
|
+
throw new ApiError(
|
|
22
|
+
response.message || 'Erro ao listar assinaturas',
|
|
23
|
+
'LIST_SUBSCRIPTIONS_FAILED',
|
|
24
|
+
400
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const data = response.data.map((item) => SubscriptionSchema.parse(item));
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
data,
|
|
32
|
+
total: response.total,
|
|
33
|
+
success: true,
|
|
34
|
+
} satisfies PaginatedSuccessResult<Subscription>;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const subscriptionsService = new SubscriptionsService();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
3
|
+
export const SubscriptionSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
id: z.union([z.number(), z.string()]),
|
|
6
|
+
deleted: z.number(),
|
|
7
|
+
id_wl: z.number(),
|
|
8
|
+
id_account: z.union([z.number(), z.string()]),
|
|
9
|
+
active: z.boolean(),
|
|
10
|
+
})
|
|
11
|
+
.passthrough();
|
|
12
|
+
|
|
13
|
+
export type Subscription = z.infer<typeof SubscriptionSchema>;
|
|
14
|
+
|
|
15
|
+
export interface FindSubscriptionsParams {
|
|
16
|
+
active?: boolean;
|
|
17
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -6,10 +6,15 @@ export { apiClient, ApiClient } from './infra/api/client';
|
|
|
6
6
|
// Services
|
|
7
7
|
export { authService } from './modules/auth/services/auth.service';
|
|
8
8
|
export { whitelabelService } from './modules/whitelabel/services/whitelabel.service';
|
|
9
|
+
export { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';
|
|
10
|
+
export { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';
|
|
9
11
|
|
|
10
12
|
// Actions
|
|
11
13
|
export { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';
|
|
12
14
|
export { validateSessionAction } from './modules/auth/actions/validate-session.action';
|
|
15
|
+
export { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';
|
|
16
|
+
export { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';
|
|
13
17
|
|
|
14
18
|
// Server Utils
|
|
15
19
|
export { getClientInfoFromRequest } from './infra/utils/client-info';
|
|
20
|
+
export { getUserContext } from './modules/auth/utils/get-user-context';
|