@greatapps/common 1.1.441 → 1.1.443
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/embeds/CrispEmbed.mjs +1 -1
- package/dist/components/embeds/CrispEmbed.mjs.map +1 -1
- package/dist/components/pages/notifications/Notifications.mjs +27 -17
- package/dist/components/pages/notifications/Notifications.mjs.map +1 -1
- package/dist/modules/plans/actions/list-plans.action.mjs +1 -10
- package/dist/modules/plans/actions/list-plans.action.mjs.map +1 -1
- package/dist/modules/plans/hooks/list-plans.hook.mjs.map +1 -1
- package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs +6 -3
- package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/embeds/CrispEmbed.tsx +1 -1
- package/src/components/pages/notifications/Notifications.tsx +33 -16
- package/src/modules/plans/actions/list-plans.action.ts +2 -13
- package/src/modules/plans/hooks/list-plans.hook.ts +2 -2
- package/src/modules/plans/hooks/use-plan-by-id.hook.ts +9 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/embeds/CrispEmbed.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useWhitelabel } from '../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\n\nconst V4_WHITELABEL_ID = 367568;\n\nconst PROFILE_LABELS: Record<string, string> = {\n owner: 'Proprietário',\n admin: 'Administrador',\n editor: 'Colaborador',\n viewer: 'Visualizador',\n};\n\nconst CRISP_WEBSITE_ID = '3339a3ec-b4c6-4aac-8b86-45668fb7655c';\nconst CRISP_SCRIPT_URL = 'https://client.crisp.chat/l.js';\nconst CRISP_LOAD_DELAY = 800;\n\n// ── Crisp helpers ──────────────────────────────────────────────\n\nfunction safeCrispPush(args: unknown[]) {\n try {\n const $crisp = (window as any)?.$crisp;\n if ($crisp) $crisp.push(args);\n } catch {}\n}\n\nfunction crispDo(action: string) {\n safeCrispPush(['do', action]);\n}\n\nfunction crispSegment(segment: string) {\n safeCrispPush(['set', 'session:segments', [[segment]]]);\n}\n\nfunction crispSessionData(key: string, value: string) {\n safeCrispPush(['set', 'session:data', [[[key, value]]]]);\n}\n\n// ── Public API ─────────────────────────────────────────────────\n\nexport function openCrispHelpdesk() {\n crispDo('helpdesk:search');\n}\n\nexport function hideCrisp() {\n crispDo('chat:hide');\n}\n\nexport function showCrisp() {\n crispDo('chat:show');\n}\n\nexport function updateCrispUser(data: {\n email?: string;\n name?: string;\n lastName?: string;\n company?: string;\n ddi?: string;\n phone?: string;\n photo?: string;\n}) {\n try {\n if (data.email) safeCrispPush(['set', 'user:email', data.email]);\n\n const fullName = `${data.name ?? ''} ${data.lastName ?? ''}`.trim();\n if (fullName) safeCrispPush(['set', 'user:nickname', fullName]);\n\n if (data.company) safeCrispPush(['set', 'user:company', data.company]);\n\n const phone = `${data.ddi ?? ''}${data.phone ?? ''}`.trim();\n if (phone) safeCrispPush(['set', 'user:phone', phone]);\n\n if (data.photo?.startsWith('http')) {\n safeCrispPush(['set', 'user:avatar', data.photo]);\n }\n } catch {}\n}\n\n// ── Component ──────────────────────────────────────────────────\n\nexport function CrispEmbed() {\n const { user, account } = useAuth();\n const { whitelabel } = useWhitelabel();\n const { data: subscriptionData } = useActiveSubscription();\n const scriptLoadedRef = useRef(false);\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const isV4 = whitelabel?.id === V4_WHITELABEL_ID;\n\n // Load Crisp script (once, with 800ms delay)\n useEffect(() => {\n if (!user || scriptLoadedRef.current) return;\n\n try {\n const win = window as any;\n\n if (!win.$crisp) {\n win.$crisp = [];\n win.CRISP_WEBSITE_ID = CRISP_WEBSITE_ID;\n win.$crisp.push(['config', 'container:index', [100]]);\n }\n\n if (document.querySelector(`script[src=\"${CRISP_SCRIPT_URL}\"]`)) {\n scriptLoadedRef.current = true;\n return;\n }\n\n const timeout = setTimeout(() => {\n try {\n if (document.querySelector(`script[src=\"${CRISP_SCRIPT_URL}\"]`)) return;\n const s = document.createElement('script');\n s.src = CRISP_SCRIPT_URL;\n s.async = true;\n document.head.appendChild(s);\n scriptLoadedRef.current = true;\n } catch {}\n }, CRISP_LOAD_DELAY);\n\n return () => clearTimeout(timeout);\n } catch {}\n }, [user]);\n\n // Identify user\n useEffect(() => {\n try {\n if (!user || !(window as any)?.$crisp) return;\n\n updateCrispUser({\n email: user.email,\n name: user.name,\n lastName: user.last_name,\n company: account?.name,\n ddi: user.ddi,\n phone: user.phone,\n photo: user.photo,\n });\n\n if (isV4) {\n crispSegment('company_v4');\n crispSessionData('Company', 'V4');\n } else {\n const planName = subscription?.plan_name ?? '';\n const profileLabel = PROFILE_LABELS[user.profile ?? ''] ?? user.profile ?? '';\n if (planName) crispSegment(`plano_${planName}`);\n if (profileLabel) crispSegment(`usuario_${profileLabel}`);\n if (planName) crispSessionData('Plano', planName);\n if (profileLabel) crispSessionData('Usuario', profileLabel);\n }\n } catch {}\n }, [user, account, isV4, subscription?.plan_name]);\n\n return null;\n}\n"],"mappings":";AAEA,SAAS,WAAW,cAAc;AAClC,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AAEtC,MAAM,mBAAmB;AAEzB,MAAM,iBAAyC;AAAA,EAC7C,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACV;AAEA,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AAIzB,SAAS,cAAc,MAAiB;AACtC,MAAI;AACF,UAAM,SAAU,QAAgB;AAChC,QAAI,OAAQ,QAAO,KAAK,IAAI;AAAA,EAC9B,QAAQ;AAAA,EAAC;AACX;AAEA,SAAS,QAAQ,QAAgB;AAC/B,gBAAc,CAAC,MAAM,MAAM,CAAC;AAC9B;AAEA,SAAS,aAAa,SAAiB;AACrC,gBAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AACxD;AAEA,SAAS,iBAAiB,KAAa,OAAe;AACpD,gBAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC;AACzD;AAIO,SAAS,oBAAoB;AAClC,UAAQ,iBAAiB;AAC3B;AAEO,SAAS,YAAY;AAC1B,UAAQ,WAAW;AACrB;AAEO,SAAS,YAAY;AAC1B,UAAQ,WAAW;AACrB;AAEO,SAAS,gBAAgB,MAQ7B;AACD,MAAI;AACF,QAAI,KAAK,MAAO,eAAc,CAAC,OAAO,cAAc,KAAK,KAAK,CAAC;AAE/D,UAAM,WAAW,GAAG,KAAK,QAAQ,EAAE,IAAI,KAAK,YAAY,EAAE,GAAG,KAAK;AAClE,QAAI,SAAU,eAAc,CAAC,OAAO,iBAAiB,QAAQ,CAAC;AAE9D,QAAI,KAAK,QAAS,eAAc,CAAC,OAAO,gBAAgB,KAAK,OAAO,CAAC;AAErE,UAAM,QAAQ,GAAG,KAAK,OAAO,EAAE,GAAG,KAAK,SAAS,EAAE,GAAG,KAAK;AAC1D,QAAI,MAAO,eAAc,CAAC,OAAO,cAAc,KAAK,CAAC;AAErD,QAAI,KAAK,OAAO,WAAW,MAAM,GAAG;AAClC,oBAAc,CAAC,OAAO,eAAe,KAAK,KAAK,CAAC;AAAA,IAClD;AAAA,EACF,QAAQ;AAAA,EAAC;AACX;AAIO,SAAS,aAAa;AAC3B,QAAM,EAAE,MAAM,QAAQ,IAAI,QAAQ;AAClC,QAAM,EAAE,WAAW,IAAI,cAAc;AACrC,QAAM,EAAE,MAAM,iBAAiB,IAAI,sBAAsB;AACzD,QAAM,kBAAkB,OAAO,KAAK;AAEpC,QAAM,eAAe,kBAAkB,OAAO,CAAC,KAAK;AACpD,QAAM,OAAO,YAAY,OAAO;AAGhC,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,gBAAgB,QAAS;AAEtC,QAAI;AACF,YAAM,MAAM;AAEZ,UAAI,CAAC,IAAI,QAAQ;AACf,YAAI,SAAS,CAAC;AACd,YAAI,mBAAmB;AACvB,YAAI,OAAO,KAAK,CAAC,UAAU,mBAAmB,CAAC,GAAG,CAAC,CAAC;AAAA,MACtD;AAEA,UAAI,SAAS,cAAc,eAAe,gBAAgB,IAAI,GAAG;AAC/D,wBAAgB,UAAU;AAC1B;AAAA,MACF;AAEA,YAAM,UAAU,WAAW,MAAM;AAC/B,YAAI;AACF,cAAI,SAAS,cAAc,eAAe,gBAAgB,IAAI,EAAG;AACjE,gBAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,YAAE,MAAM;AACR,YAAE,QAAQ;AACV,mBAAS,KAAK,YAAY,CAAC;AAC3B,0BAAgB,UAAU;AAAA,QAC5B,QAAQ;AAAA,QAAC;AAAA,MACX,GAAG,gBAAgB;AAEnB,aAAO,MAAM,aAAa,OAAO;AAAA,IACnC,QAAQ;AAAA,IAAC;AAAA,EACX,GAAG,CAAC,IAAI,CAAC;AAGT,YAAU,MAAM;AACd,QAAI;AACF,UAAI,CAAC,QAAQ,CAAE,QAAgB,OAAQ;AAEvC,sBAAgB;AAAA,QACd,OAAO,KAAK;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,SAAS,SAAS;AAAA,QAClB,KAAK,KAAK;AAAA,QACV,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../../src/components/embeds/CrispEmbed.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useWhitelabel } from '../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../modules/subscriptions/hooks/find-active-subscription.hook';\n\nconst V4_WHITELABEL_ID = 367568;\n\nconst PROFILE_LABELS: Record<string, string> = {\n owner: 'Proprietário',\n admin: 'Administrador',\n editor: 'Colaborador',\n viewer: 'Visualizador',\n};\n\nconst CRISP_WEBSITE_ID = '3339a3ec-b4c6-4aac-8b86-45668fb7655c';\nconst CRISP_SCRIPT_URL = 'https://client.crisp.chat/l.js';\nconst CRISP_LOAD_DELAY = 800;\n\n// ── Crisp helpers ──────────────────────────────────────────────\n\nfunction safeCrispPush(args: unknown[]) {\n try {\n const $crisp = (window as any)?.$crisp;\n if ($crisp) $crisp.push(args);\n } catch {}\n}\n\nfunction crispDo(action: string) {\n safeCrispPush(['do', action]);\n}\n\nfunction crispSegment(segment: string) {\n safeCrispPush(['set', 'session:segments', [[segment]]]);\n}\n\nfunction crispSessionData(key: string, value: string) {\n safeCrispPush(['set', 'session:data', [[[key, value]]]]);\n}\n\n// ── Public API ─────────────────────────────────────────────────\n\nexport function openCrispHelpdesk() {\n crispDo('helpdesk:search');\n}\n\nexport function hideCrisp() {\n crispDo('chat:hide');\n}\n\nexport function showCrisp() {\n crispDo('chat:show');\n}\n\nexport function updateCrispUser(data: {\n email?: string;\n name?: string;\n lastName?: string;\n company?: string;\n ddi?: string;\n phone?: string;\n photo?: string;\n}) {\n try {\n if (data.email) safeCrispPush(['set', 'user:email', data.email]);\n\n const fullName = `${data.name ?? ''} ${data.lastName ?? ''}`.trim();\n if (fullName) safeCrispPush(['set', 'user:nickname', fullName]);\n\n if (data.company) safeCrispPush(['set', 'user:company', data.company]);\n\n const phone = `${data.ddi ?? ''}${data.phone ?? ''}`.trim();\n if (phone) safeCrispPush(['set', 'user:phone', phone]);\n\n if (data.photo?.startsWith('http')) {\n safeCrispPush(['set', 'user:avatar', data.photo]);\n }\n } catch {}\n}\n\n// ── Component ──────────────────────────────────────────────────\n\nexport function CrispEmbed() {\n const { user, account } = useAuth();\n const { whitelabel } = useWhitelabel();\n const { data: subscriptionData } = useActiveSubscription();\n const scriptLoadedRef = useRef(false);\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const isV4 = whitelabel?.id === V4_WHITELABEL_ID;\n\n // Load Crisp script (once, with 800ms delay)\n useEffect(() => {\n if (!user || scriptLoadedRef.current) return;\n\n try {\n const win = window as any;\n\n if (!win.$crisp) {\n win.$crisp = [];\n win.CRISP_WEBSITE_ID = CRISP_WEBSITE_ID;\n win.$crisp.push(['config', 'container:index', [100]]);\n }\n\n if (document.querySelector(`script[src=\"${CRISP_SCRIPT_URL}\"]`)) {\n scriptLoadedRef.current = true;\n return;\n }\n\n const timeout = setTimeout(() => {\n try {\n if (document.querySelector(`script[src=\"${CRISP_SCRIPT_URL}\"]`)) return;\n const s = document.createElement('script');\n s.src = CRISP_SCRIPT_URL;\n s.async = true;\n document.head.appendChild(s);\n scriptLoadedRef.current = true;\n } catch {}\n }, CRISP_LOAD_DELAY);\n\n return () => clearTimeout(timeout);\n } catch {}\n }, [user]);\n\n // Identify user\n useEffect(() => {\n try {\n if (!user || !(window as any)?.$crisp) return;\n\n updateCrispUser({\n email: user.email,\n name: user.name,\n lastName: user.last_name,\n company: account?.name,\n ddi: user.ddi,\n phone: user.phone,\n photo: user.photo ?? undefined,\n });\n\n if (isV4) {\n crispSegment('company_v4');\n crispSessionData('Company', 'V4');\n } else {\n const planName = subscription?.plan_name ?? '';\n const profileLabel = PROFILE_LABELS[user.profile ?? ''] ?? user.profile ?? '';\n if (planName) crispSegment(`plano_${planName}`);\n if (profileLabel) crispSegment(`usuario_${profileLabel}`);\n if (planName) crispSessionData('Plano', planName);\n if (profileLabel) crispSessionData('Usuario', profileLabel);\n }\n } catch {}\n }, [user, account, isV4, subscription?.plan_name]);\n\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -14,6 +14,12 @@ import { Toast } from "../../ui/feedback/Toast";
|
|
|
14
14
|
import { formatDistanceToNow } from "date-fns";
|
|
15
15
|
import { ptBR } from "date-fns/locale";
|
|
16
16
|
import { toast } from "sonner";
|
|
17
|
+
import Link from "next/link";
|
|
18
|
+
function parseNotificationUrl(raw) {
|
|
19
|
+
if (!raw) return null;
|
|
20
|
+
const match = raw.match(/href="([^"]+)"/);
|
|
21
|
+
return match ? match[1] : raw.startsWith("/") ? raw : null;
|
|
22
|
+
}
|
|
17
23
|
function NotificationsList({
|
|
18
24
|
pages,
|
|
19
25
|
isFetchingNextPage,
|
|
@@ -38,23 +44,27 @@ function NotificationsList({
|
|
|
38
44
|
className: "flex flex-col border border-gray-200 rounded-lg w-full bg-white max-h-[70vh] overflow-y-auto custom-scrollbar",
|
|
39
45
|
onScroll: handleScroll,
|
|
40
46
|
children: [
|
|
41
|
-
notifications.map((notification, index) =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
notifications.map((notification, index) => {
|
|
48
|
+
const detailsUrl = parseNotificationUrl(notification.url);
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
NotificationCard,
|
|
51
|
+
{
|
|
52
|
+
icon: /* @__PURE__ */ jsx(IconBell, { size: 20, className: "text-pages-600" }),
|
|
53
|
+
iconBgColor: "bg-zinc-50",
|
|
54
|
+
title: notification.subject,
|
|
55
|
+
description: notification.message,
|
|
56
|
+
gapPx: 8,
|
|
57
|
+
time: formatDistanceToNow(new Date(notification.datetime_add), {
|
|
58
|
+
addSuffix: true,
|
|
59
|
+
locale: ptBR
|
|
60
|
+
}),
|
|
61
|
+
isUnread: notification.status === 1,
|
|
62
|
+
showBorder: index < notifications.length - 1,
|
|
63
|
+
actions: detailsUrl ? /* @__PURE__ */ jsx(Button, { asChild: true, variant: "secondary", className: "h-8! px-3! paragraph-small-semibold", children: /* @__PURE__ */ jsx(Link, { href: detailsUrl, children: "Detalhes" }) }) : void 0
|
|
64
|
+
},
|
|
65
|
+
notification.id
|
|
66
|
+
);
|
|
67
|
+
}),
|
|
58
68
|
isFetchingNextPage && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ jsx(IconLoader2, { size: 20, className: "animate-spin text-gray-400" }) })
|
|
59
69
|
]
|
|
60
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/pages/notifications/Notifications.tsx"],"sourcesContent":["'use client';\n\nimport { IconBell, IconCheck, IconLoader2 } from '@tabler/icons-react';\nimport { Button } from \"../../ui/buttons/Button\";\nimport {\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"../../ui/data-display/Tabs\";\nimport { useInfiniteMessages, useMarkAllMessagesAsRead } from \"../../../modules/users/hooks/messages.hook\";\nimport NotificationCard from \"../../widgets/notifications/NotificationCard\";\nimport { Toast } from \"../../ui/feedback/Toast\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { ptBR } from \"date-fns/locale\";\nimport { toast } from \"sonner\";\nimport React from \"react\";\n\nfunction NotificationsList({\n pages,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n}: {\n pages: ReturnType<typeof useInfiniteMessages>[\"data\"];\n isFetchingNextPage: boolean;\n hasNextPage: boolean;\n fetchNextPage: () => void;\n}) {\n const notifications = pages?.pages.flatMap((p) => p.data) ?? [];\n\n const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {\n const target = e.currentTarget;\n const nearBottom =\n target.scrollTop + target.clientHeight >= target.scrollHeight - 50;\n if (!nearBottom) return;\n if (hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n };\n\n if (notifications.length === 0 && !isFetchingNextPage) {\n return (\n <div className=\"rounded-lg border border-zinc-200 bg-white\">\n <div className=\"flex flex-col items-center justify-center py-16 gap-2\">\n <span className=\"paragraph-medium-regular text-zinc-500\">Nenhuma notificação encontrada.</span>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className=\"flex flex-col border border-gray-200 rounded-lg w-full bg-white max-h-[70vh] overflow-y-auto custom-scrollbar\"\n onScroll={handleScroll}\n >\n {notifications.map((notification, index) => (\n <NotificationCard\n key={notification.id}\n icon={<IconBell size={20} className=\"text-pages-600\" />}\n iconBgColor=\"bg-zinc-50\"\n title={notification.subject}\n description={notification.message}\n gapPx={8}\n time={formatDistanceToNow(new Date(notification.datetime_add), {\n addSuffix: true,\n locale: ptBR,\n })}\n isUnread={notification.status === 1}\n showBorder={index < notifications.length - 1}\n />\n ))}\n {isFetchingNextPage && (\n <div className=\"flex items-center justify-center py-4\">\n <IconLoader2 size={20} className=\"animate-spin text-gray-400\" />\n </div>\n )}\n </div>\n );\n}\n\nexport function NotificationPageContent() {\n const unreadNotifications = useInfiniteMessages({\n readed: false,\n type: \"notification\",\n order: \"desc\",\n });\n const allNotifications = useInfiniteMessages({\n type: \"notification\",\n order: \"desc\",\n });\n\n const totalAll = allNotifications.data?.pages[0]?.total ?? 0;\n const totalUnread = unreadNotifications.data?.pages[0]?.total ?? 0;\n\n const { mutate: markAllAsRead, isPending: isMarkingAll } = useMarkAllMessagesAsRead();\n\n const handleMarkAllAsRead = () => {\n markAllAsRead(undefined, {\n onSuccess: () => {\n toast.custom((t) => (\n <Toast variant=\"success\" message=\"Todas as notificações foram marcadas como lidas\" toastId={t} />\n ));\n },\n onError: () => {\n toast.custom((t) => (\n <Toast variant=\"error\" message=\"Erro ao marcar notificações como lidas\" toastId={t} />\n ));\n },\n });\n };\n\n return (\n <div className=\"flex flex-col gap-6 lg:gap-8\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex flex-col gap-2\">\n <h1 className=\"title-large-semibold text-gray-950\">\n Notificações\n </h1>\n <p className=\"paragraph-small-regular text-gray-600 hidden lg:block\">\n Tenha controle total sobre páginas, experiências e conversões.\n </p>\n </div>\n <Button\n variant={\"secondary\"}\n className=\"h-10! lg:h-12! shrink-0\"\n onClick={handleMarkAllAsRead}\n disabled={isMarkingAll || totalUnread === 0}\n >\n {isMarkingAll ? <IconLoader2 size={20} className=\"animate-spin\" /> : <IconCheck size={20} />}\n <span className=\"lg:block hidden\">Marcar todos como lidos</span>\n <span className=\"lg:hidden\">Marcar como lidos</span>\n </Button>\n </div>\n <Tabs\n defaultValue=\"all\"\n className=\"flex flex-col w-full bg-transparent gap-6 lg:gap-8\"\n >\n <TabsList className=\"h-auto pb-0 bg-transparent\">\n <TabsTrigger className=\"gap-2\" value=\"all\">\n Todas as notificações{\" \"}\n <span className=\"text-gray-500\">({totalAll})</span>\n </TabsTrigger>\n <TabsTrigger className=\"gap-2\" value=\"unreads\" disabled={totalUnread === 0}>\n Não lidas{\" \"}\n <span className=\"text-gray-500\">({totalUnread})</span>\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"all\">\n <NotificationsList\n pages={allNotifications.data}\n isFetchingNextPage={allNotifications.isFetchingNextPage}\n hasNextPage={allNotifications.hasNextPage}\n fetchNextPage={allNotifications.fetchNextPage}\n />\n </TabsContent>\n\n <TabsContent value=\"unreads\">\n <NotificationsList\n pages={unreadNotifications.data}\n isFetchingNextPage={unreadNotifications.isFetchingNextPage}\n hasNextPage={unreadNotifications.hasNextPage}\n fetchNextPage={unreadNotifications.fetchNextPage}\n />\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":";AA6CU,cAON,YAPM;AA3CV,SAAS,UAAU,WAAW,mBAAmB;AACjD,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,qBAAqB,gCAAgC;AAC9D,OAAO,sBAAsB;AAC7B,SAAS,aAAa;AACtB,SAAS,2BAA2B;AACpC,SAAS,YAAY;AACrB,SAAS,aAAa;AAGtB,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,gBAAgB,OAAO,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC;AAE9D,QAAM,eAAe,CAAC,MAAqC;AACzD,UAAM,SAAS,EAAE;AACjB,UAAM,aACJ,OAAO,YAAY,OAAO,gBAAgB,OAAO,eAAe;AAClE,QAAI,CAAC,WAAY;AACjB,QAAI,eAAe,CAAC,oBAAoB;AACtC,oBAAc;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,cAAc,WAAW,KAAK,CAAC,oBAAoB;AACrD,WACE,oBAAC,SAAI,WAAU,8CACb,8BAAC,SAAI,WAAU,yDACb,8BAAC,UAAK,WAAU,0CAAyC,mDAA+B,GAC1F,GACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,UAAU;AAAA,MAET;AAAA,sBAAc,IAAI,CAAC,cAAc,UAChC;AAAA,UAAC;AAAA;AAAA,YAEC,MAAM,oBAAC,YAAS,MAAM,IAAI,WAAU,kBAAiB;AAAA,YACrD,aAAY;AAAA,YACZ,OAAO,aAAa;AAAA,YACpB,aAAa,aAAa;AAAA,YAC1B,OAAO;AAAA,YACP,MAAM,oBAAoB,IAAI,KAAK,aAAa,YAAY,GAAG;AAAA,cAC7D,WAAW;AAAA,cACX,QAAQ;AAAA,YACV,CAAC;AAAA,YACD,UAAU,aAAa,WAAW;AAAA,YAClC,YAAY,QAAQ,cAAc,SAAS;AAAA;AAAA,UAXtC,aAAa;AAAA,QAYpB,CACD;AAAA,QACA,sBACC,oBAAC,SAAI,WAAU,yCACb,8BAAC,eAAY,MAAM,IAAI,WAAU,8BAA6B,GAChE;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEO,SAAS,0BAA0B;AACxC,QAAM,sBAAsB,oBAAoB;AAAA,IAC9C,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC;AACD,QAAM,mBAAmB,oBAAoB;AAAA,IAC3C,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC;AAED,QAAM,WAAW,iBAAiB,MAAM,MAAM,CAAC,GAAG,SAAS;AAC3D,QAAM,cAAc,oBAAoB,MAAM,MAAM,CAAC,GAAG,SAAS;AAEjE,QAAM,EAAE,QAAQ,eAAe,WAAW,aAAa,IAAI,yBAAyB;AAEpF,QAAM,sBAAsB,MAAM;AAChC,kBAAc,QAAW;AAAA,MACvB,WAAW,MAAM;AACf,cAAM,OAAO,CAAC,MACZ,oBAAC,SAAM,SAAQ,WAAU,SAAQ,yDAAkD,SAAS,GAAG,CAChG;AAAA,MACH;AAAA,MACA,SAAS,MAAM;AACb,cAAM,OAAO,CAAC,MACZ,oBAAC,SAAM,SAAQ,SAAQ,SAAQ,gDAAyC,SAAS,GAAG,CACrF;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,SAAI,WAAU,gCACb;AAAA,yBAAC,SAAI,WAAU,2CACb;AAAA,2BAAC,SAAI,WAAU,uBACb;AAAA,4BAAC,QAAG,WAAU,sCAAqC,gCAEnD;AAAA,QACA,oBAAC,OAAE,WAAU,yDAAwD,qFAErE;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UACV,SAAS;AAAA,UACT,UAAU,gBAAgB,gBAAgB;AAAA,UAEzC;AAAA,2BAAe,oBAAC,eAAY,MAAM,IAAI,WAAU,gBAAe,IAAK,oBAAC,aAAU,MAAM,IAAI;AAAA,YAC1F,oBAAC,UAAK,WAAU,mBAAkB,qCAAuB;AAAA,YACzD,oBAAC,UAAK,WAAU,aAAY,+BAAiB;AAAA;AAAA;AAAA,MAC/C;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,cAAa;AAAA,QACb,WAAU;AAAA,QAEV;AAAA,+BAAC,YAAS,WAAU,8BAClB;AAAA,iCAAC,eAAY,WAAU,SAAQ,OAAM,OAAM;AAAA;AAAA,cACnB;AAAA,cACtB,qBAAC,UAAK,WAAU,iBAAgB;AAAA;AAAA,gBAAE;AAAA,gBAAS;AAAA,iBAAC;AAAA,eAC9C;AAAA,YACA,qBAAC,eAAY,WAAU,SAAQ,OAAM,WAAU,UAAU,gBAAgB,GAAG;AAAA;AAAA,cAChE;AAAA,cACV,qBAAC,UAAK,WAAU,iBAAgB;AAAA;AAAA,gBAAE;AAAA,gBAAY;AAAA,iBAAC;AAAA,eACjD;AAAA,aACF;AAAA,UAEA,oBAAC,eAAY,OAAM,OACjB;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,iBAAiB;AAAA,cACxB,oBAAoB,iBAAiB;AAAA,cACrC,aAAa,iBAAiB;AAAA,cAC9B,eAAe,iBAAiB;AAAA;AAAA,UAClC,GACF;AAAA,UAEA,oBAAC,eAAY,OAAM,WACjB;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,oBAAoB;AAAA,cAC3B,oBAAoB,oBAAoB;AAAA,cACxC,aAAa,oBAAoB;AAAA,cACjC,eAAe,oBAAoB;AAAA;AAAA,UACrC,GACF;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/pages/notifications/Notifications.tsx"],"sourcesContent":["'use client';\n\nimport { IconBell, IconCheck, IconLoader2 } from '@tabler/icons-react';\nimport { Button } from \"../../ui/buttons/Button\";\nimport {\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"../../ui/data-display/Tabs\";\nimport { useInfiniteMessages, useMarkAllMessagesAsRead } from \"../../../modules/users/hooks/messages.hook\";\nimport NotificationCard from \"../../widgets/notifications/NotificationCard\";\nimport { Toast } from \"../../ui/feedback/Toast\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { ptBR } from \"date-fns/locale\";\nimport { toast } from \"sonner\";\nimport React from \"react\";\nimport Link from \"next/link\";\n\nfunction parseNotificationUrl(raw: string): string | null {\n if (!raw) return null;\n const match = raw.match(/href=\"([^\"]+)\"/);\n return match ? match[1] : raw.startsWith('/') ? raw : null;\n}\n\nfunction NotificationsList({\n pages,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n}: {\n pages: ReturnType<typeof useInfiniteMessages>[\"data\"];\n isFetchingNextPage: boolean;\n hasNextPage: boolean;\n fetchNextPage: () => void;\n}) {\n const notifications = pages?.pages.flatMap((p) => p.data) ?? [];\n\n const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {\n const target = e.currentTarget;\n const nearBottom =\n target.scrollTop + target.clientHeight >= target.scrollHeight - 50;\n if (!nearBottom) return;\n if (hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n };\n\n if (notifications.length === 0 && !isFetchingNextPage) {\n return (\n <div className=\"rounded-lg border border-zinc-200 bg-white\">\n <div className=\"flex flex-col items-center justify-center py-16 gap-2\">\n <span className=\"paragraph-medium-regular text-zinc-500\">Nenhuma notificação encontrada.</span>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className=\"flex flex-col border border-gray-200 rounded-lg w-full bg-white max-h-[70vh] overflow-y-auto custom-scrollbar\"\n onScroll={handleScroll}\n >\n {notifications.map((notification, index) => {\n const detailsUrl = parseNotificationUrl(notification.url);\n return (\n <NotificationCard\n key={notification.id}\n icon={<IconBell size={20} className=\"text-pages-600\" />}\n iconBgColor=\"bg-zinc-50\"\n title={notification.subject}\n description={notification.message}\n gapPx={8}\n time={formatDistanceToNow(new Date(notification.datetime_add), {\n addSuffix: true,\n locale: ptBR,\n })}\n isUnread={notification.status === 1}\n showBorder={index < notifications.length - 1}\n actions={\n detailsUrl ? (\n <Button asChild variant=\"secondary\" className=\"h-8! px-3! paragraph-small-semibold\">\n <Link href={detailsUrl}>Detalhes</Link>\n </Button>\n ) : undefined\n }\n />\n );\n })}\n {isFetchingNextPage && (\n <div className=\"flex items-center justify-center py-4\">\n <IconLoader2 size={20} className=\"animate-spin text-gray-400\" />\n </div>\n )}\n </div>\n );\n}\n\nexport function NotificationPageContent() {\n const unreadNotifications = useInfiniteMessages({\n readed: false,\n type: \"notification\",\n order: \"desc\",\n });\n const allNotifications = useInfiniteMessages({\n type: \"notification\",\n order: \"desc\",\n });\n\n const totalAll = allNotifications.data?.pages[0]?.total ?? 0;\n const totalUnread = unreadNotifications.data?.pages[0]?.total ?? 0;\n\n const { mutate: markAllAsRead, isPending: isMarkingAll } = useMarkAllMessagesAsRead();\n\n const handleMarkAllAsRead = () => {\n markAllAsRead(undefined, {\n onSuccess: () => {\n toast.custom((t) => (\n <Toast variant=\"success\" message=\"Todas as notificações foram marcadas como lidas\" toastId={t} />\n ));\n },\n onError: () => {\n toast.custom((t) => (\n <Toast variant=\"error\" message=\"Erro ao marcar notificações como lidas\" toastId={t} />\n ));\n },\n });\n };\n\n return (\n <div className=\"flex flex-col gap-6 lg:gap-8\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex flex-col gap-2\">\n <h1 className=\"title-large-semibold text-gray-950\">\n Notificações\n </h1>\n <p className=\"paragraph-small-regular text-gray-600 hidden lg:block\">\n Tenha controle total sobre páginas, experiências e conversões.\n </p>\n </div>\n <Button\n variant={\"secondary\"}\n className=\"h-10! lg:h-12! shrink-0\"\n onClick={handleMarkAllAsRead}\n disabled={isMarkingAll || totalUnread === 0}\n >\n {isMarkingAll ? <IconLoader2 size={20} className=\"animate-spin\" /> : <IconCheck size={20} />}\n <span className=\"lg:block hidden\">Marcar todos como lidos</span>\n <span className=\"lg:hidden\">Marcar como lidos</span>\n </Button>\n </div>\n <Tabs\n defaultValue=\"all\"\n className=\"flex flex-col w-full bg-transparent gap-6 lg:gap-8\"\n >\n <TabsList className=\"h-auto pb-0 bg-transparent\">\n <TabsTrigger className=\"gap-2\" value=\"all\">\n Todas as notificações{\" \"}\n <span className=\"text-gray-500\">({totalAll})</span>\n </TabsTrigger>\n <TabsTrigger className=\"gap-2\" value=\"unreads\" disabled={totalUnread === 0}>\n Não lidas{\" \"}\n <span className=\"text-gray-500\">({totalUnread})</span>\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"all\">\n <NotificationsList\n pages={allNotifications.data}\n isFetchingNextPage={allNotifications.isFetchingNextPage}\n hasNextPage={allNotifications.hasNextPage}\n fetchNextPage={allNotifications.fetchNextPage}\n />\n </TabsContent>\n\n <TabsContent value=\"unreads\">\n <NotificationsList\n pages={unreadNotifications.data}\n isFetchingNextPage={unreadNotifications.isFetchingNextPage}\n hasNextPage={unreadNotifications.hasNextPage}\n fetchNextPage={unreadNotifications.fetchNextPage}\n />\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import { plansService, safeServerAction } from "@greatapps/common/server";
|
|
3
|
-
import { mapApiPlanToUiPlan } from "../utils/map-api-plan-to-ui";
|
|
4
3
|
async function listPlansAction() {
|
|
5
4
|
return safeServerAction(async () => {
|
|
6
5
|
const { data: apiPlans } = await plansService.listPlans({
|
|
@@ -9,15 +8,7 @@ async function listPlansAction() {
|
|
|
9
8
|
sort: "id:ASC"
|
|
10
9
|
});
|
|
11
10
|
if (!apiPlans?.length) return [];
|
|
12
|
-
|
|
13
|
-
return sorted.map((plan) => {
|
|
14
|
-
const popular = plan.id === 2 || plan.id_plan === 2;
|
|
15
|
-
return mapApiPlanToUiPlan(plan, {
|
|
16
|
-
planId: plan.id_plan ?? plan.id,
|
|
17
|
-
isPopular: popular,
|
|
18
|
-
buttonVariant: popular ? "brand" : void 0
|
|
19
|
-
});
|
|
20
|
-
});
|
|
11
|
+
return [...apiPlans].sort((a, b) => a.value - b.value);
|
|
21
12
|
});
|
|
22
13
|
}
|
|
23
14
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/plans/actions/list-plans.action.ts"],"sourcesContent":["'use server';\n\nimport { plansService, safeServerAction } from '@greatapps/common/server';\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/plans/actions/list-plans.action.ts"],"sourcesContent":["'use server';\n\nimport { plansService, safeServerAction } from '@greatapps/common/server';\n\nexport async function listPlansAction() {\n return safeServerAction(async () => {\n const { data: apiPlans } = await plansService.listPlans({\n active: true,\n search: 'client',\n sort: 'id:ASC',\n });\n\n if (!apiPlans?.length) return [];\n\n return [...apiPlans].sort((a, b) => a.value - b.value);\n });\n}\n"],"mappings":";AAEA,SAAS,cAAc,wBAAwB;AAE/C,eAAsB,kBAAkB;AACtC,SAAO,iBAAiB,YAAY;AAClC,UAAM,EAAE,MAAM,SAAS,IAAI,MAAM,aAAa,UAAU;AAAA,MACtD,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AAED,QAAI,CAAC,UAAU,OAAQ,QAAO,CAAC;AAE/B,WAAO,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAAA,EACvD,CAAC;AACH;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/plans/hooks/list-plans.hook.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { withAction } from '@greatapps/common';\nimport { listPlansAction } from '../actions/list-plans.action';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/plans/hooks/list-plans.hook.ts"],"sourcesContent":["'use client';\n\nimport { useQuery } from '@tanstack/react-query';\nimport { withAction } from '@greatapps/common';\nimport { listPlansAction } from '../actions/list-plans.action';\nimport type { Plan } from '../types/plan.type';\n\nexport const PLANS_QUERY_KEY = ['plans'];\n\nexport function usePlans() {\n return useQuery<Plan[]>({\n queryKey: [...PLANS_QUERY_KEY],\n queryFn: withAction(listPlansAction),\n staleTime: Infinity,\n gcTime: Infinity,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n retry: 1,\n });\n}\n"],"mappings":";AAEA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAGzB,MAAM,kBAAkB,CAAC,OAAO;AAEhC,SAAS,WAAW;AACzB,SAAO,SAAiB;AAAA,IACtB,UAAU,CAAC,GAAG,eAAe;AAAA,IAC7B,SAAS,WAAW,eAAe;AAAA,IACnC,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT,CAAC;AACH;","names":[]}
|
|
@@ -13,10 +13,13 @@ function usePlanById(idPlan) {
|
|
|
13
13
|
queryKey: [...PLANS_QUERY_KEY]
|
|
14
14
|
});
|
|
15
15
|
for (const [, data] of queries) {
|
|
16
|
-
if (!data) continue;
|
|
17
|
-
const
|
|
16
|
+
if (!data || !Array.isArray(data)) continue;
|
|
17
|
+
const numericId = Number(idPlan);
|
|
18
|
+
const plan = data.find(
|
|
19
|
+
(p) => p.id === numericId || p.id_plan === numericId
|
|
20
|
+
);
|
|
18
21
|
if (plan) {
|
|
19
|
-
return plan;
|
|
22
|
+
return { success: true, data: plan };
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/plans/hooks/use-plan-by-id.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { withAction } from \"../../../utils/withAction\";\nimport { findPlanByIdAction } from \"../actions/find-plan-by-id.action\";\nimport { Plan } from \"../types/plan.type\";\nimport { PLANS_QUERY_KEY } from \"./list-plans.hook\";\
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/plans/hooks/use-plan-by-id.hook.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { withAction } from \"../../../utils/withAction\";\nimport { findPlanByIdAction } from \"../actions/find-plan-by-id.action\";\nimport type { Plan } from \"../types/plan.type\";\nimport { PLANS_QUERY_KEY } from \"./list-plans.hook\";\n\nexport function usePlanById(idPlan: number | string | undefined) {\n const queryClient = useQueryClient();\n\n return useQuery({\n queryKey: [...PLANS_QUERY_KEY, idPlan],\n queryFn: withAction(() => findPlanByIdAction(idPlan!)),\n initialData: () => {\n const queries = queryClient.getQueriesData<Plan[]>({\n queryKey: [...PLANS_QUERY_KEY],\n });\n\n for (const [, data] of queries) {\n if (!data || !Array.isArray(data)) continue;\n const numericId = Number(idPlan);\n const plan = data.find(\n (p) => p.id === numericId || p.id_plan === numericId\n );\n if (plan) {\n return { success: true as const, data: plan };\n }\n }\n\n return undefined;\n },\n initialDataUpdatedAt: 0,\n enabled: !!idPlan,\n });\n}\n"],"mappings":";AAEA,SAAS,UAAU,sBAAsB;AACzC,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AAEnC,SAAS,uBAAuB;AAEzB,SAAS,YAAY,QAAqC;AAC/D,QAAM,cAAc,eAAe;AAEnC,SAAO,SAAS;AAAA,IACd,UAAU,CAAC,GAAG,iBAAiB,MAAM;AAAA,IACrC,SAAS,WAAW,MAAM,mBAAmB,MAAO,CAAC;AAAA,IACrD,aAAa,MAAM;AACjB,YAAM,UAAU,YAAY,eAAuB;AAAA,QACjD,UAAU,CAAC,GAAG,eAAe;AAAA,MAC/B,CAAC;AAED,iBAAW,CAAC,EAAE,IAAI,KAAK,SAAS;AAC9B,YAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,IAAI,EAAG;AACnC,cAAM,YAAY,OAAO,MAAM;AAC/B,cAAM,OAAO,KAAK;AAAA,UAChB,CAAC,MAAM,EAAE,OAAO,aAAa,EAAE,YAAY;AAAA,QAC7C;AACA,YAAI,MAAM;AACR,iBAAO,EAAE,SAAS,MAAe,MAAM,KAAK;AAAA,QAC9C;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,sBAAsB;AAAA,IACtB,SAAS,CAAC,CAAC;AAAA,EACb,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -15,6 +15,13 @@ import { formatDistanceToNow } from "date-fns";
|
|
|
15
15
|
import { ptBR } from "date-fns/locale";
|
|
16
16
|
import { toast } from "sonner";
|
|
17
17
|
import React from "react";
|
|
18
|
+
import Link from "next/link";
|
|
19
|
+
|
|
20
|
+
function parseNotificationUrl(raw: string): string | null {
|
|
21
|
+
if (!raw) return null;
|
|
22
|
+
const match = raw.match(/href="([^"]+)"/);
|
|
23
|
+
return match ? match[1] : raw.startsWith('/') ? raw : null;
|
|
24
|
+
}
|
|
18
25
|
|
|
19
26
|
function NotificationsList({
|
|
20
27
|
pages,
|
|
@@ -54,22 +61,32 @@ function NotificationsList({
|
|
|
54
61
|
className="flex flex-col border border-gray-200 rounded-lg w-full bg-white max-h-[70vh] overflow-y-auto custom-scrollbar"
|
|
55
62
|
onScroll={handleScroll}
|
|
56
63
|
>
|
|
57
|
-
{notifications.map((notification, index) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
{notifications.map((notification, index) => {
|
|
65
|
+
const detailsUrl = parseNotificationUrl(notification.url);
|
|
66
|
+
return (
|
|
67
|
+
<NotificationCard
|
|
68
|
+
key={notification.id}
|
|
69
|
+
icon={<IconBell size={20} className="text-pages-600" />}
|
|
70
|
+
iconBgColor="bg-zinc-50"
|
|
71
|
+
title={notification.subject}
|
|
72
|
+
description={notification.message}
|
|
73
|
+
gapPx={8}
|
|
74
|
+
time={formatDistanceToNow(new Date(notification.datetime_add), {
|
|
75
|
+
addSuffix: true,
|
|
76
|
+
locale: ptBR,
|
|
77
|
+
})}
|
|
78
|
+
isUnread={notification.status === 1}
|
|
79
|
+
showBorder={index < notifications.length - 1}
|
|
80
|
+
actions={
|
|
81
|
+
detailsUrl ? (
|
|
82
|
+
<Button asChild variant="secondary" className="h-8! px-3! paragraph-small-semibold">
|
|
83
|
+
<Link href={detailsUrl}>Detalhes</Link>
|
|
84
|
+
</Button>
|
|
85
|
+
) : undefined
|
|
86
|
+
}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
})}
|
|
73
90
|
{isFetchingNextPage && (
|
|
74
91
|
<div className="flex items-center justify-center py-4">
|
|
75
92
|
<IconLoader2 size={20} className="animate-spin text-gray-400" />
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
|
|
3
3
|
import { plansService, safeServerAction } from '@greatapps/common/server';
|
|
4
|
-
import type { UiPlan } from '../types/plan.type';
|
|
5
|
-
import { mapApiPlanToUiPlan } from '../utils/map-api-plan-to-ui';
|
|
6
4
|
|
|
7
5
|
export async function listPlansAction() {
|
|
8
6
|
return safeServerAction(async () => {
|
|
@@ -12,17 +10,8 @@ export async function listPlansAction() {
|
|
|
12
10
|
sort: 'id:ASC',
|
|
13
11
|
});
|
|
14
12
|
|
|
15
|
-
if (!apiPlans?.length) return []
|
|
13
|
+
if (!apiPlans?.length) return [];
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return sorted.map((plan) => {
|
|
20
|
-
const popular = plan.id === 2 || plan.id_plan === 2;
|
|
21
|
-
return mapApiPlanToUiPlan(plan, {
|
|
22
|
-
planId: plan.id_plan ?? plan.id,
|
|
23
|
-
isPopular: popular,
|
|
24
|
-
buttonVariant: popular ? 'brand' : undefined,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
15
|
+
return [...apiPlans].sort((a, b) => a.value - b.value);
|
|
27
16
|
});
|
|
28
17
|
}
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import { useQuery } from '@tanstack/react-query';
|
|
4
4
|
import { withAction } from '@greatapps/common';
|
|
5
5
|
import { listPlansAction } from '../actions/list-plans.action';
|
|
6
|
-
import type {
|
|
6
|
+
import type { Plan } from '../types/plan.type';
|
|
7
7
|
|
|
8
8
|
export const PLANS_QUERY_KEY = ['plans'];
|
|
9
9
|
|
|
10
10
|
export function usePlans() {
|
|
11
|
-
return useQuery<
|
|
11
|
+
return useQuery<Plan[]>({
|
|
12
12
|
queryKey: [...PLANS_QUERY_KEY],
|
|
13
13
|
queryFn: withAction(listPlansAction),
|
|
14
14
|
staleTime: Infinity,
|
|
@@ -3,26 +3,28 @@
|
|
|
3
3
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { withAction } from "../../../utils/withAction";
|
|
5
5
|
import { findPlanByIdAction } from "../actions/find-plan-by-id.action";
|
|
6
|
-
import { Plan } from "../types/plan.type";
|
|
6
|
+
import type { Plan } from "../types/plan.type";
|
|
7
7
|
import { PLANS_QUERY_KEY } from "./list-plans.hook";
|
|
8
|
-
import { PaginatedSuccessResult } from "../../../infra/api/types";
|
|
9
8
|
|
|
10
9
|
export function usePlanById(idPlan: number | string | undefined) {
|
|
11
10
|
const queryClient = useQueryClient();
|
|
12
|
-
|
|
11
|
+
|
|
13
12
|
return useQuery({
|
|
14
13
|
queryKey: [...PLANS_QUERY_KEY, idPlan],
|
|
15
14
|
queryFn: withAction(() => findPlanByIdAction(idPlan!)),
|
|
16
15
|
initialData: () => {
|
|
17
|
-
const queries = queryClient.getQueriesData<
|
|
16
|
+
const queries = queryClient.getQueriesData<Plan[]>({
|
|
18
17
|
queryKey: [...PLANS_QUERY_KEY],
|
|
19
18
|
});
|
|
20
19
|
|
|
21
20
|
for (const [, data] of queries) {
|
|
22
|
-
if (!data) continue;
|
|
23
|
-
const
|
|
21
|
+
if (!data || !Array.isArray(data)) continue;
|
|
22
|
+
const numericId = Number(idPlan);
|
|
23
|
+
const plan = data.find(
|
|
24
|
+
(p) => p.id === numericId || p.id_plan === numericId
|
|
25
|
+
);
|
|
24
26
|
if (plan) {
|
|
25
|
-
return plan;
|
|
27
|
+
return { success: true as const, data: plan };
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|