@elevasis/ui 2.74.0 → 2.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/api/index.js +3 -3
  2. package/dist/app/index.d.ts +277 -249
  3. package/dist/app/index.js +12 -11
  4. package/dist/auth/index.js +6 -7
  5. package/dist/charts/index.js +6 -7
  6. package/dist/{chunk-GO4XJ5D4.js → chunk-2GIHWU5O.js} +4 -3
  7. package/dist/chunk-2Y6AKWGU.js +42 -0
  8. package/dist/chunk-3QYRZSHP.js +854 -0
  9. package/dist/{chunk-CRA7WQZH.js → chunk-3RK2NNJX.js} +1 -1
  10. package/dist/{chunk-R6NMRXNO.js → chunk-4WLAVUPS.js} +1 -1
  11. package/dist/{chunk-GPQQGQGT.js → chunk-5R4I2PAN.js} +9 -43
  12. package/dist/{chunk-R5UGIFYU.js → chunk-5ZK7K7YP.js} +25 -1
  13. package/dist/chunk-6B4ZPBQJ.js +7 -0
  14. package/dist/{chunk-ARAFQBNI.js → chunk-73G3I5HJ.js} +160 -160
  15. package/dist/{chunk-ZWI62BQ6.js → chunk-BFLYDVEN.js} +32 -42
  16. package/dist/{chunk-W2QRBQQA.js → chunk-BG6PWDJ3.js} +381 -381
  17. package/dist/{chunk-7ARBBELS.js → chunk-BV2RNLPP.js} +1 -1
  18. package/dist/{chunk-FA7LMNQO.js → chunk-CTT4RDXE.js} +150 -151
  19. package/dist/chunk-E2LJ3FRA.js +237 -0
  20. package/dist/{chunk-E6YGA5WH.js → chunk-EFQLXRYT.js} +1 -2
  21. package/dist/{chunk-FZSG6M6W.js → chunk-EVNLJHDD.js} +3 -3
  22. package/dist/{chunk-ZYNVCWDZ.js → chunk-GDQQX5SL.js} +4 -42
  23. package/dist/{chunk-DCQKTDIG.js → chunk-GFSOQH3N.js} +1 -1
  24. package/dist/{chunk-3LOKYZJC.js → chunk-H5AEYSVX.js} +164 -3
  25. package/dist/{chunk-ZFISGKYW.js → chunk-IIEZXXDD.js} +1 -1
  26. package/dist/{chunk-3QHX4ZKV.js → chunk-INCEZAM3.js} +1 -1
  27. package/dist/{chunk-O32UQ26A.js → chunk-JZQNUQE4.js} +1 -1
  28. package/dist/{chunk-6JN2TLV2.js → chunk-K55TU6CC.js} +2023 -1700
  29. package/dist/chunk-KB5CAN5D.js +33 -0
  30. package/dist/{chunk-2MRRDF32.js → chunk-KMA4FOSV.js} +5682 -5675
  31. package/dist/{chunk-HYQOGRWG.js → chunk-KXNRTJAE.js} +1 -1
  32. package/dist/{chunk-7RJ7P6S6.js → chunk-MGAELVTZ.js} +12 -12
  33. package/dist/chunk-MKH2KOAO.js +39 -0
  34. package/dist/{chunk-4MXNHJTA.js → chunk-NAPXMYFW.js} +939 -1570
  35. package/dist/{chunk-CQPULKKL.js → chunk-OGG3QJYW.js} +14 -5
  36. package/dist/{chunk-4M2BSHVT.js → chunk-P6HHXMLJ.js} +1 -1
  37. package/dist/{chunk-WZU3MCKI.js → chunk-QAILYRC2.js} +1 -1
  38. package/dist/{chunk-RBGVNPA6.js → chunk-RXZ3XDBM.js} +1 -1
  39. package/dist/{chunk-BA3AZJUR.js → chunk-SPW6UA6B.js} +1 -1
  40. package/dist/{chunk-RFNJ2PTO.js → chunk-SVZR4GYA.js} +1 -1
  41. package/dist/{chunk-5A6KCFS5.js → chunk-UEY4AB3R.js} +789 -66
  42. package/dist/{chunk-LN4UW6S4.js → chunk-UL6MPMKY.js} +1 -1
  43. package/dist/{chunk-ZFA56VSR.js → chunk-WELN7DDV.js} +2 -2
  44. package/dist/{chunk-43LC6XAX.js → chunk-WJN2VVVJ.js} +1 -1
  45. package/dist/{chunk-BRTGCFBH.js → chunk-XODQUPFW.js} +11 -11
  46. package/dist/components/index.d.ts +181 -34
  47. package/dist/components/index.js +2249 -1804
  48. package/dist/components/navigation/index.js +7 -8
  49. package/dist/features/auth/index.d.ts +35 -3716
  50. package/dist/features/auth/index.js +9 -10
  51. package/dist/features/clients/index.js +18 -12
  52. package/dist/features/content/index.js +34 -31
  53. package/dist/features/crm/index.css +59 -59
  54. package/dist/features/crm/index.d.ts +18 -1
  55. package/dist/features/crm/index.js +13 -11
  56. package/dist/features/dashboard/index.css +59 -59
  57. package/dist/features/dashboard/index.js +8 -9
  58. package/dist/features/delivery/index.d.ts +3 -0
  59. package/dist/features/delivery/index.js +21 -20
  60. package/dist/features/knowledge/index.js +6 -7
  61. package/dist/features/lead-gen/index.css +59 -59
  62. package/dist/features/lead-gen/index.d.ts +3903 -2
  63. package/dist/features/lead-gen/index.js +15 -13
  64. package/dist/features/monitoring/index.js +10 -11
  65. package/dist/features/monitoring/requests/index.css +59 -59
  66. package/dist/features/monitoring/requests/index.js +8 -9
  67. package/dist/features/notes/index.js +4 -4
  68. package/dist/features/operations/index.css +59 -59
  69. package/dist/features/operations/index.js +13 -15
  70. package/dist/features/seo/index.css +659 -0
  71. package/dist/features/seo/index.d.ts +3962 -1
  72. package/dist/features/seo/index.js +31 -2
  73. package/dist/features/settings/index.css +59 -59
  74. package/dist/features/settings/index.d.ts +87 -3745
  75. package/dist/features/settings/index.js +10 -12
  76. package/dist/hooks/access/index.js +6 -7
  77. package/dist/hooks/delivery/index.css +59 -59
  78. package/dist/hooks/delivery/index.d.ts +13 -4
  79. package/dist/hooks/delivery/index.js +3 -3
  80. package/dist/hooks/published.d.ts +69 -27
  81. package/dist/hooks/published.js +19 -19
  82. package/dist/initialization/index.d.ts +35 -3716
  83. package/dist/knowledge/index.js +11 -11
  84. package/dist/layout/index.d.ts +55 -2
  85. package/dist/layout/index.js +10 -10
  86. package/dist/organization/index.css +59 -59
  87. package/dist/profile/index.d.ts +64 -3716
  88. package/dist/provider/index.d.ts +3 -0
  89. package/dist/provider/index.js +6 -7
  90. package/dist/provider/published.d.ts +3 -0
  91. package/dist/provider/published.js +6 -7
  92. package/dist/test-utils/index.js +3 -3
  93. package/dist/theme/index.js +3 -3
  94. package/dist/theme/presets/index.js +1 -1
  95. package/dist/types/index.d.ts +312 -251
  96. package/dist/utils/index.d.ts +1 -1
  97. package/dist/utils/index.js +1 -1
  98. package/package.json +9 -9
  99. package/src/hooks/README.md +22 -23
  100. package/dist/chunk-6ZAP3FOA.js +0 -93
  101. package/dist/chunk-AH52XQB7.js +0 -1
  102. package/dist/chunk-GDOR57D7.js +0 -165
  103. package/dist/chunk-MISHPEJX.js +0 -244
  104. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +0 -879
  105. package/dist/hooks/operations/command-view/utils/transformCommandViewData.js +0 -2
@@ -0,0 +1,854 @@
1
+ import { eqOrgId, sanitizePostgrestValue, useBatchDeleteRows } from './chunk-KB5CAN5D.js';
2
+ import { useSupabase } from './chunk-MKH2KOAO.js';
3
+ import { useTableSort, sortData } from './chunk-2Y6AKWGU.js';
4
+ import { useTableSelection } from './chunk-WDBNW44Z.js';
5
+ import { SubshellPageLayout } from './chunk-6B4ZPBQJ.js';
6
+ import { TableSelectionToolbar, SortableHeader } from './chunk-TUMSNGTX.js';
7
+ import { FilterBar } from './chunk-PDHTXPSF.js';
8
+ import { usePaginationState, PageTitleCaption, StatCard, EmptyState, CustomModal, SubshellContentContainer } from './chunk-K55TU6CC.js';
9
+ import { showApiErrorNotification } from './chunk-KXNRTJAE.js';
10
+ import { useOrganization } from './chunk-DD3CCMCZ.js';
11
+ import { PAGE_SIZE_DEFAULT } from './chunk-5ZK7K7YP.js';
12
+ import { Stack, SimpleGrid, Paper, Select, TextInput, Group, Button, Center, Loader, Table, Checkbox, Text, Badge, Pagination, Title, Anchor, Card, Divider } from '@mantine/core';
13
+ import { IconFileText, IconCircleCheck, IconPencil, IconAlertTriangle, IconEye, IconClick, IconTarget, IconBrain, IconSearch, IconFilterOff, IconExternalLink, IconArrowLeft, IconPercentage, IconChartLine, IconForms } from '@tabler/icons-react';
14
+ import { useState, useMemo } from 'react';
15
+ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
16
+ import { jsxs, jsx } from 'react/jsx-runtime';
17
+
18
+ // ../core/src/business/seo/types.ts
19
+ var SEO_PAGE_TYPES = ["location", "use-case", "comparison", "pillar", "cluster"];
20
+ var SEO_PAGE_STATUSES = ["draft", "review", "published", "stale"];
21
+ var SEO_VERTICALS = [
22
+ "veterinary-clinics",
23
+ "auto-repair",
24
+ "home-services",
25
+ "hvac-contractors",
26
+ "pest-control",
27
+ "commercial-cleaning",
28
+ "med-spas",
29
+ "hoa-property-management",
30
+ "security-companies"
31
+ ];
32
+ var seoStatsKeys = {
33
+ all: ["seo-stats"],
34
+ stats: (orgId) => [...seoStatsKeys.all, "overview", orgId]
35
+ };
36
+ function useSeoStats() {
37
+ const supabase = useSupabase();
38
+ const { currentSupabaseOrganizationId } = useOrganization();
39
+ return useQuery({
40
+ queryKey: seoStatsKeys.stats(currentSupabaseOrganizationId),
41
+ queryFn: async () => {
42
+ if (!currentSupabaseOrganizationId) {
43
+ return {
44
+ totalPages: 0,
45
+ byStatus: { draft: 0, review: 0, published: 0, stale: 0 },
46
+ byType: { location: 0, pillar: 0, cluster: 0, "use-case": 0, comparison: 0 },
47
+ byVertical: {}
48
+ };
49
+ }
50
+ const { data: pages, error } = await eqOrgId(
51
+ supabase.from("acq_seo_pages").select("status, page_type, vertical"),
52
+ currentSupabaseOrganizationId
53
+ );
54
+ if (error) throw error;
55
+ const items = pages || [];
56
+ const byStatus = { draft: 0, review: 0, published: 0, stale: 0 };
57
+ const byType = { location: 0, pillar: 0, cluster: 0, "use-case": 0, comparison: 0 };
58
+ const byVertical = {};
59
+ for (const p of items) {
60
+ if (p.status in byStatus) byStatus[p.status]++;
61
+ if (p.page_type in byType) byType[p.page_type]++;
62
+ byVertical[p.vertical] = (byVertical[p.vertical] || 0) + 1;
63
+ }
64
+ return {
65
+ totalPages: items.length,
66
+ byStatus,
67
+ byType,
68
+ byVertical
69
+ };
70
+ },
71
+ enabled: !!currentSupabaseOrganizationId
72
+ });
73
+ }
74
+ var seoPageKeys = {
75
+ all: ["seo-pages"],
76
+ list: (orgId, filters) => [...seoPageKeys.all, "list", orgId, filters],
77
+ detail: (id) => [...seoPageKeys.all, "detail", id]
78
+ };
79
+ function useSeoPages(filters = {}) {
80
+ const supabase = useSupabase();
81
+ const { currentSupabaseOrganizationId } = useOrganization();
82
+ return useQuery({
83
+ queryKey: seoPageKeys.list(currentSupabaseOrganizationId, filters),
84
+ queryFn: async () => {
85
+ if (!currentSupabaseOrganizationId) return [];
86
+ let query = eqOrgId(supabase.from("acq_seo_pages").select("*"), currentSupabaseOrganizationId);
87
+ if (filters.pageType) query = query.eq("page_type", filters.pageType);
88
+ if (filters.vertical) query = query.eq("vertical", filters.vertical);
89
+ if (filters.status) query = query.eq("status", filters.status);
90
+ if (filters.search) {
91
+ const search = sanitizePostgrestValue(filters.search);
92
+ query = query.or(`title.ilike.%${search}%,slug.ilike.%${search}%`);
93
+ }
94
+ query = query.order("created_at", { ascending: false });
95
+ const { data, error } = await query;
96
+ if (error) throw error;
97
+ return data || [];
98
+ },
99
+ enabled: !!currentSupabaseOrganizationId
100
+ });
101
+ }
102
+ function useSeoPage(id) {
103
+ const supabase = useSupabase();
104
+ const { currentSupabaseOrganizationId } = useOrganization();
105
+ return useQuery({
106
+ queryKey: [...seoPageKeys.detail(id), currentSupabaseOrganizationId],
107
+ queryFn: async () => {
108
+ if (!currentSupabaseOrganizationId || !id) return null;
109
+ const { data, error } = await eqOrgId(
110
+ supabase.from("acq_seo_pages").select("*").eq("id", id),
111
+ currentSupabaseOrganizationId
112
+ ).single();
113
+ if (error) {
114
+ if (error.code === "PGRST116") return null;
115
+ throw error;
116
+ }
117
+ return data;
118
+ },
119
+ enabled: !!currentSupabaseOrganizationId && !!id
120
+ });
121
+ }
122
+ function useUpdateSeoPage() {
123
+ const supabase = useSupabase();
124
+ const queryClient = useQueryClient();
125
+ return useMutation({
126
+ mutationFn: async (params) => {
127
+ const { data, error } = await supabase.from("acq_seo_pages").update({ ...params.updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", params.id).select().single();
128
+ if (error) throw error;
129
+ return data;
130
+ },
131
+ onSuccess: () => {
132
+ queryClient.invalidateQueries({ queryKey: seoPageKeys.all });
133
+ queryClient.invalidateQueries({ queryKey: seoStatsKeys.all });
134
+ },
135
+ onError: (error) => {
136
+ showApiErrorNotification(error);
137
+ }
138
+ });
139
+ }
140
+ function useDeleteSeoPage() {
141
+ const supabase = useSupabase();
142
+ const queryClient = useQueryClient();
143
+ return useMutation({
144
+ mutationFn: async (id) => {
145
+ const { error } = await supabase.from("acq_seo_pages").delete().eq("id", id);
146
+ if (error) throw error;
147
+ },
148
+ onSuccess: () => {
149
+ queryClient.invalidateQueries({ queryKey: seoPageKeys.all });
150
+ queryClient.invalidateQueries({ queryKey: seoStatsKeys.all });
151
+ },
152
+ onError: (error) => {
153
+ showApiErrorNotification(error);
154
+ }
155
+ });
156
+ }
157
+ var seoMetricsKeys = {
158
+ all: ["seo-metrics"],
159
+ forPage: (orgId, pageId) => [...seoMetricsKeys.all, "page", orgId, pageId],
160
+ aggregate: (orgId, days) => [...seoMetricsKeys.all, "aggregate", orgId, days]
161
+ };
162
+ function useSeoPageMetrics(pageId, days = 30) {
163
+ const supabase = useSupabase();
164
+ const { currentSupabaseOrganizationId } = useOrganization();
165
+ return useQuery({
166
+ queryKey: [...seoMetricsKeys.forPage(currentSupabaseOrganizationId, pageId), days],
167
+ queryFn: async () => {
168
+ if (!currentSupabaseOrganizationId || !pageId) return [];
169
+ const since = /* @__PURE__ */ new Date();
170
+ since.setDate(since.getDate() - days);
171
+ const { data, error } = await eqOrgId(supabase.from("acq_seo_metrics").select("*"), currentSupabaseOrganizationId).eq("seo_page_id", pageId).gte("period", since.toISOString().split("T")[0]).order("period", { ascending: true });
172
+ if (error) throw error;
173
+ return data || [];
174
+ },
175
+ enabled: !!currentSupabaseOrganizationId && !!pageId
176
+ });
177
+ }
178
+ function useSeoAggregateMetrics(days = 30) {
179
+ const supabase = useSupabase();
180
+ const { currentSupabaseOrganizationId } = useOrganization();
181
+ return useQuery({
182
+ queryKey: seoMetricsKeys.aggregate(currentSupabaseOrganizationId, days),
183
+ queryFn: async () => {
184
+ const empty = {
185
+ totalImpressions: 0,
186
+ totalClicks: 0,
187
+ avgCtr: 0,
188
+ avgPosition: 0,
189
+ totalCtaClicks: 0,
190
+ totalFormSubmissions: 0,
191
+ totalAiCitations: 0
192
+ };
193
+ if (!currentSupabaseOrganizationId) return empty;
194
+ const since = /* @__PURE__ */ new Date();
195
+ since.setDate(since.getDate() - days);
196
+ const { data, error } = await eqOrgId(
197
+ supabase.from("acq_seo_metrics").select("impressions, clicks, ctr, avg_position, cta_clicks, form_submissions, ai_citations"),
198
+ currentSupabaseOrganizationId
199
+ ).gte("period", since.toISOString().split("T")[0]);
200
+ if (error) throw error;
201
+ if (!data?.length) return empty;
202
+ let totalImpressions = 0;
203
+ let totalClicks = 0;
204
+ let totalCtaClicks = 0;
205
+ let totalFormSubmissions = 0;
206
+ let totalAiCitations = 0;
207
+ let ctrSum = 0;
208
+ let posSum = 0;
209
+ let ctrCount = 0;
210
+ let posCount = 0;
211
+ for (const row of data) {
212
+ totalImpressions += row.impressions || 0;
213
+ totalClicks += row.clicks || 0;
214
+ totalCtaClicks += row.cta_clicks || 0;
215
+ totalFormSubmissions += row.form_submissions || 0;
216
+ if (row.ctr != null) {
217
+ ctrSum += Number(row.ctr);
218
+ ctrCount++;
219
+ }
220
+ if (row.avg_position != null) {
221
+ posSum += Number(row.avg_position);
222
+ posCount++;
223
+ }
224
+ if (row.ai_citations) {
225
+ const c = row.ai_citations;
226
+ for (const v of Object.values(c)) totalAiCitations += v || 0;
227
+ }
228
+ }
229
+ return {
230
+ totalImpressions,
231
+ totalClicks,
232
+ avgCtr: ctrCount ? ctrSum / ctrCount : 0,
233
+ avgPosition: posCount ? posSum / posCount : 0,
234
+ totalCtaClicks,
235
+ totalFormSubmissions,
236
+ totalAiCitations
237
+ };
238
+ },
239
+ enabled: !!currentSupabaseOrganizationId
240
+ });
241
+ }
242
+
243
+ // src/features/seo/formatters.ts
244
+ function formatDate(dateString) {
245
+ if (!dateString) return "-";
246
+ return new Date(dateString).toLocaleDateString("en-US", {
247
+ month: "short",
248
+ day: "numeric",
249
+ year: "numeric"
250
+ });
251
+ }
252
+ function getStatusColor(status) {
253
+ switch (status) {
254
+ case "published":
255
+ return "green";
256
+ case "review":
257
+ return "blue";
258
+ case "draft":
259
+ return "gray";
260
+ case "stale":
261
+ return "orange";
262
+ default:
263
+ return "gray";
264
+ }
265
+ }
266
+ function getPageTypeColor(pageType) {
267
+ switch (pageType) {
268
+ case "pillar":
269
+ return "violet";
270
+ case "cluster":
271
+ return "cyan";
272
+ case "location":
273
+ return "teal";
274
+ case "use-case":
275
+ return "indigo";
276
+ case "comparison":
277
+ return "pink";
278
+ default:
279
+ return "gray";
280
+ }
281
+ }
282
+ function formatVertical(vertical) {
283
+ return vertical.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
284
+ }
285
+ function SeoPagesPage({ websiteBaseUrl, onPageClick }) {
286
+ const { data: stats, isLoading: statsLoading } = useSeoStats();
287
+ const { data: metrics, isLoading: metricsLoading } = useSeoAggregateMetrics();
288
+ const [typeFilter, setTypeFilter] = useState(null);
289
+ const [verticalFilter, setVerticalFilter] = useState(null);
290
+ const [statusFilter, setStatusFilter] = useState(null);
291
+ const [searchQuery, setSearchQuery] = useState("");
292
+ const filters = {
293
+ pageType: typeFilter || void 0,
294
+ vertical: verticalFilter || void 0,
295
+ status: statusFilter || void 0,
296
+ search: searchQuery || void 0
297
+ };
298
+ const hasActiveFilters = !!typeFilter || !!verticalFilter || !!statusFilter || !!searchQuery;
299
+ const resetFilters = () => {
300
+ setTypeFilter(null);
301
+ setVerticalFilter(null);
302
+ setStatusFilter(null);
303
+ setSearchQuery("");
304
+ };
305
+ const { data: pages, isLoading: pagesLoading } = useSeoPages(filters);
306
+ const { sort, toggleSort } = useTableSort("updated");
307
+ const sortAccessors = useMemo(
308
+ () => ({
309
+ title: (p) => p.title,
310
+ type: (p) => p.page_type,
311
+ vertical: (p) => p.vertical,
312
+ status: (p) => p.status,
313
+ updated: (p) => p.updated_at || ""
314
+ }),
315
+ []
316
+ );
317
+ const sortedPages = useMemo(() => sortData(pages || [], sort, sortAccessors), [pages, sort, sortAccessors]);
318
+ const pagination = usePaginationState(
319
+ PAGE_SIZE_DEFAULT,
320
+ [typeFilter, verticalFilter, statusFilter, searchQuery],
321
+ sortedPages.length
322
+ );
323
+ const paginatedPages = useMemo(() => {
324
+ return sortedPages.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT);
325
+ }, [sortedPages, pagination.offset]);
326
+ const selection = useTableSelection(paginatedPages, sortedPages);
327
+ const batchDeleteMutation = useBatchDeleteRows("acq_seo_pages", [seoPageKeys.all]);
328
+ const [showBatchDelete, setShowBatchDelete] = useState(false);
329
+ return /* @__PURE__ */ jsxs(SubshellPageLayout, { children: [
330
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "SEO Pages", caption: "Search engine optimization tracking and insights" }) }),
331
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
332
+ /* @__PURE__ */ jsx(
333
+ StatCard,
334
+ {
335
+ variant: "hero",
336
+ icon: IconFileText,
337
+ value: stats?.totalPages || 0,
338
+ label: "Total Pages",
339
+ isLoading: statsLoading
340
+ }
341
+ ),
342
+ /* @__PURE__ */ jsx(
343
+ StatCard,
344
+ {
345
+ variant: "hero",
346
+ icon: IconCircleCheck,
347
+ value: stats?.byStatus.published || 0,
348
+ label: "Published",
349
+ isLoading: statsLoading
350
+ }
351
+ ),
352
+ /* @__PURE__ */ jsx(
353
+ StatCard,
354
+ {
355
+ variant: "hero",
356
+ icon: IconPencil,
357
+ value: stats?.byStatus.draft || 0,
358
+ label: "Drafts",
359
+ isLoading: statsLoading
360
+ }
361
+ ),
362
+ /* @__PURE__ */ jsx(
363
+ StatCard,
364
+ {
365
+ variant: "hero",
366
+ icon: IconAlertTriangle,
367
+ value: stats?.byStatus.stale || 0,
368
+ label: "Stale",
369
+ isLoading: statsLoading
370
+ }
371
+ )
372
+ ] }),
373
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
374
+ /* @__PURE__ */ jsx(
375
+ StatCard,
376
+ {
377
+ variant: "hero",
378
+ icon: IconEye,
379
+ value: metrics?.totalImpressions?.toLocaleString() || "0",
380
+ label: "Impressions (30d)",
381
+ isLoading: metricsLoading
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx(
385
+ StatCard,
386
+ {
387
+ variant: "hero",
388
+ icon: IconClick,
389
+ value: metrics?.totalClicks?.toLocaleString() || "0",
390
+ label: "Clicks (30d)",
391
+ isLoading: metricsLoading
392
+ }
393
+ ),
394
+ /* @__PURE__ */ jsx(
395
+ StatCard,
396
+ {
397
+ variant: "hero",
398
+ icon: IconTarget,
399
+ value: metrics?.totalCtaClicks?.toLocaleString() || "0",
400
+ label: "CTA Clicks (30d)",
401
+ isLoading: metricsLoading
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(
405
+ StatCard,
406
+ {
407
+ variant: "hero",
408
+ icon: IconBrain,
409
+ value: metrics?.totalAiCitations || 0,
410
+ label: "AI Citations (30d)",
411
+ isLoading: metricsLoading
412
+ }
413
+ )
414
+ ] }),
415
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
416
+ /* @__PURE__ */ jsxs(
417
+ FilterBar,
418
+ {
419
+ actions: /* @__PURE__ */ jsxs(Group, { gap: "md", children: [
420
+ hasActiveFilters && /* @__PURE__ */ jsx(
421
+ Button,
422
+ {
423
+ variant: "subtle",
424
+ color: "gray",
425
+ leftSection: /* @__PURE__ */ jsx(IconFilterOff, { size: 16 }),
426
+ onClick: resetFilters,
427
+ children: "Reset"
428
+ }
429
+ ),
430
+ /* @__PURE__ */ jsx(
431
+ TableSelectionToolbar,
432
+ {
433
+ selectedCount: selection.selectedCount,
434
+ onDelete: () => setShowBatchDelete(true),
435
+ isDeleting: batchDeleteMutation.isPending
436
+ }
437
+ )
438
+ ] }),
439
+ children: [
440
+ /* @__PURE__ */ jsx(
441
+ Select,
442
+ {
443
+ placeholder: "All Types",
444
+ data: SEO_PAGE_TYPES.map((t) => ({ value: t, label: t })),
445
+ style: { minWidth: 160 },
446
+ size: "sm",
447
+ clearable: true,
448
+ value: typeFilter,
449
+ onChange: setTypeFilter
450
+ }
451
+ ),
452
+ /* @__PURE__ */ jsx(
453
+ Select,
454
+ {
455
+ placeholder: "All Verticals",
456
+ data: SEO_VERTICALS.map((v) => ({ value: v, label: formatVertical(v) })),
457
+ style: { minWidth: 200 },
458
+ size: "sm",
459
+ clearable: true,
460
+ value: verticalFilter,
461
+ onChange: setVerticalFilter
462
+ }
463
+ ),
464
+ /* @__PURE__ */ jsx(
465
+ Select,
466
+ {
467
+ placeholder: "All Statuses",
468
+ data: SEO_PAGE_STATUSES.map((s) => ({ value: s, label: s })),
469
+ style: { minWidth: 160 },
470
+ size: "sm",
471
+ clearable: true,
472
+ value: statusFilter,
473
+ onChange: setStatusFilter
474
+ }
475
+ ),
476
+ /* @__PURE__ */ jsx(
477
+ TextInput,
478
+ {
479
+ placeholder: "Search title or slug...",
480
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
481
+ style: { minWidth: 250 },
482
+ size: "sm",
483
+ value: searchQuery,
484
+ onChange: (e) => setSearchQuery(e.target.value)
485
+ }
486
+ )
487
+ ]
488
+ }
489
+ ),
490
+ pagesLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !pages?.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconFileText, title: "No pages found" }) : /* @__PURE__ */ jsxs(Table, { children: [
491
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
492
+ /* @__PURE__ */ jsx(Table.Th, { w: 40, children: /* @__PURE__ */ jsx(
493
+ Checkbox,
494
+ {
495
+ checked: selection.isPageAllSelected,
496
+ indeterminate: selection.isPagePartiallySelected,
497
+ onChange: selection.togglePage
498
+ }
499
+ ) }),
500
+ /* @__PURE__ */ jsx(SortableHeader, { column: "title", sort, onToggle: toggleSort, children: "Title" }),
501
+ /* @__PURE__ */ jsx(SortableHeader, { column: "type", sort, onToggle: toggleSort, children: "Type" }),
502
+ /* @__PURE__ */ jsx(SortableHeader, { column: "vertical", sort, onToggle: toggleSort, children: "Vertical" }),
503
+ /* @__PURE__ */ jsx(SortableHeader, { column: "status", sort, onToggle: toggleSort, children: "Status" }),
504
+ /* @__PURE__ */ jsx(SortableHeader, { column: "updated", sort, onToggle: toggleSort, children: "Updated" }),
505
+ /* @__PURE__ */ jsx(Table.Th, {})
506
+ ] }) }),
507
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedPages.map((page) => /* @__PURE__ */ jsxs(Table.Tr, { style: { cursor: "pointer" }, onClick: () => onPageClick(page.id), children: [
508
+ /* @__PURE__ */ jsx(Table.Td, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(Checkbox, { checked: selection.isSelected(page.id), onChange: () => selection.toggle(page.id) }) }),
509
+ /* @__PURE__ */ jsxs(Table.Td, { children: [
510
+ /* @__PURE__ */ jsx(Text, { fw: 500, size: "sm", children: page.title }),
511
+ /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
512
+ "/",
513
+ page.slug
514
+ ] })
515
+ ] }),
516
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", size: "sm", color: getPageTypeColor(page.page_type), children: page.page_type }) }),
517
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: formatVertical(page.vertical) }) }),
518
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Badge, { variant: "light", size: "sm", color: getStatusColor(page.status), children: page.status }) }),
519
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(page.updated_at) }) }),
520
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
521
+ "a",
522
+ {
523
+ href: `${websiteBaseUrl}/${page.slug}`,
524
+ target: "_blank",
525
+ rel: "noopener noreferrer",
526
+ style: { color: "var(--mantine-color-blue-5)", display: "inline-flex" },
527
+ onClick: (e) => e.stopPropagation(),
528
+ children: /* @__PURE__ */ jsx(IconExternalLink, { size: 16 })
529
+ }
530
+ ) })
531
+ ] }, page.id)) })
532
+ ] }),
533
+ sortedPages.length > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
534
+ Pagination,
535
+ {
536
+ value: pagination.page,
537
+ onChange: pagination.setPage,
538
+ total: pagination.totalPages(sortedPages.length),
539
+ size: "sm"
540
+ }
541
+ ) })
542
+ ] }) }),
543
+ /* @__PURE__ */ jsx(
544
+ CustomModal,
545
+ {
546
+ opened: showBatchDelete,
547
+ onClose: () => !batchDeleteMutation.isPending && setShowBatchDelete(false),
548
+ size: "sm",
549
+ loading: batchDeleteMutation.isPending,
550
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "md", children: [
551
+ /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
552
+ /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
553
+ /* @__PURE__ */ jsxs(Title, { order: 4, children: [
554
+ "Delete ",
555
+ selection.selectedCount,
556
+ " Pages"
557
+ ] })
558
+ ] }),
559
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
560
+ "Are you sure you want to delete",
561
+ " ",
562
+ /* @__PURE__ */ jsx(Text, { span: true, fw: 600, children: selection.selectedCount }),
563
+ " ",
564
+ "selected pages?"
565
+ ] }),
566
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone." }),
567
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", mt: "md", children: [
568
+ /* @__PURE__ */ jsx(Button, { variant: "light", onClick: () => setShowBatchDelete(false), disabled: batchDeleteMutation.isPending, children: "Cancel" }),
569
+ /* @__PURE__ */ jsx(
570
+ Button,
571
+ {
572
+ color: "red",
573
+ loading: batchDeleteMutation.isPending,
574
+ onClick: () => {
575
+ batchDeleteMutation.mutate([...selection.selectedIds], {
576
+ onSuccess: () => {
577
+ setShowBatchDelete(false);
578
+ selection.clear();
579
+ }
580
+ });
581
+ },
582
+ children: "Delete"
583
+ }
584
+ )
585
+ ] })
586
+ ] })
587
+ }
588
+ )
589
+ ] });
590
+ }
591
+ function SeoPageDetailPage({ pageId, websiteBaseUrl, onBack, backLabel = "SEO Pages" }) {
592
+ const { data: page, isLoading } = useSeoPage(pageId);
593
+ const { data: metricsData, isLoading: metricsLoading } = useSeoPageMetrics(pageId);
594
+ const metrics = useMemo(() => {
595
+ if (!metricsData?.length)
596
+ return {
597
+ totalImpressions: 0,
598
+ totalClicks: 0,
599
+ avgCtr: 0,
600
+ avgPosition: 0,
601
+ totalCtaClicks: 0,
602
+ totalFormSubmissions: 0,
603
+ totalAiCitations: 0
604
+ };
605
+ let totalImpressions = 0;
606
+ let totalClicks = 0;
607
+ let totalCtaClicks = 0;
608
+ let totalFormSubmissions = 0;
609
+ let totalAiCitations = 0;
610
+ let ctrSum = 0;
611
+ let posSum = 0;
612
+ let ctrCount = 0;
613
+ let posCount = 0;
614
+ for (const row of metricsData) {
615
+ totalImpressions += row.impressions || 0;
616
+ totalClicks += row.clicks || 0;
617
+ totalCtaClicks += row.cta_clicks || 0;
618
+ totalFormSubmissions += row.form_submissions || 0;
619
+ if (row.ctr != null) {
620
+ ctrSum += Number(row.ctr);
621
+ ctrCount++;
622
+ }
623
+ if (row.avg_position != null) {
624
+ posSum += Number(row.avg_position);
625
+ posCount++;
626
+ }
627
+ if (row.ai_citations) {
628
+ const c = row.ai_citations;
629
+ for (const v of Object.values(c)) totalAiCitations += v || 0;
630
+ }
631
+ }
632
+ return {
633
+ totalImpressions,
634
+ totalClicks,
635
+ avgCtr: ctrCount ? ctrSum / ctrCount : 0,
636
+ avgPosition: posCount ? posSum / posCount : 0,
637
+ totalCtaClicks,
638
+ totalFormSubmissions,
639
+ totalAiCitations
640
+ };
641
+ }, [metricsData]);
642
+ if (isLoading) {
643
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) });
644
+ }
645
+ if (!page) {
646
+ return /* @__PURE__ */ jsx(SubshellContentContainer, { children: /* @__PURE__ */ jsx(EmptyState, { icon: IconFileText, title: "Page not found" }) });
647
+ }
648
+ return /* @__PURE__ */ jsxs(SubshellPageLayout, { children: [
649
+ /* @__PURE__ */ jsxs(Stack, { children: [
650
+ /* @__PURE__ */ jsx(
651
+ PageTitleCaption,
652
+ {
653
+ title: page.title,
654
+ caption: `${formatVertical(page.vertical)} \xB7 ${page.page_type} \xB7 ${page.status}`,
655
+ rightSection: /* @__PURE__ */ jsx(Button, { variant: "light", leftSection: /* @__PURE__ */ jsx(IconArrowLeft, { size: 16 }), onClick: onBack, size: "sm", children: backLabel })
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
659
+ /* @__PURE__ */ jsx(Badge, { size: "sm", color: getPageTypeColor(page.page_type), children: page.page_type }),
660
+ /* @__PURE__ */ jsx(Badge, { size: "sm", color: getStatusColor(page.status), children: page.status }),
661
+ /* @__PURE__ */ jsx(Anchor, { href: `${websiteBaseUrl}/${page.slug}`, target: "_blank", rel: "noopener noreferrer", size: "sm", children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
662
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 14 }),
663
+ "View on website"
664
+ ] }) })
665
+ ] })
666
+ ] }),
667
+ /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
668
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Details" }),
669
+ /* @__PURE__ */ jsx(Divider, {}),
670
+ /* @__PURE__ */ jsxs(Group, { children: [
671
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Slug" }),
672
+ /* @__PURE__ */ jsxs(Text, { size: "sm", children: [
673
+ "/",
674
+ page.slug
675
+ ] })
676
+ ] }),
677
+ /* @__PURE__ */ jsxs(Group, { children: [
678
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Vertical" }),
679
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatVertical(page.vertical) })
680
+ ] }),
681
+ page.city && /* @__PURE__ */ jsxs(Group, { children: [
682
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "City" }),
683
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: page.city })
684
+ ] }),
685
+ page.meta_description && /* @__PURE__ */ jsxs(Group, { align: "flex-start", children: [
686
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Meta Description" }),
687
+ /* @__PURE__ */ jsx(Text, { size: "sm", style: { flex: 1 }, children: page.meta_description })
688
+ ] }),
689
+ /* @__PURE__ */ jsxs(Group, { children: [
690
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Created" }),
691
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(page.created_at) })
692
+ ] }),
693
+ /* @__PURE__ */ jsxs(Group, { children: [
694
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Updated" }),
695
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(page.updated_at) })
696
+ ] }),
697
+ page.published_at && /* @__PURE__ */ jsxs(Group, { children: [
698
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Published" }),
699
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(page.published_at) })
700
+ ] }),
701
+ page.refreshed_at && /* @__PURE__ */ jsxs(Group, { children: [
702
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 120, children: "Last Refreshed" }),
703
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: formatDate(page.refreshed_at) })
704
+ ] })
705
+ ] }) }),
706
+ /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
707
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Performance (30 days)" }),
708
+ /* @__PURE__ */ jsx(Divider, {}),
709
+ metricsLoading ? /* @__PURE__ */ jsx(Center, { p: "md", children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) }) : /* @__PURE__ */ jsxs(SimpleGrid, { cols: 3, children: [
710
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
711
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Impressions" }),
712
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.totalImpressions.toLocaleString() })
713
+ ] }),
714
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
715
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Clicks" }),
716
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.totalClicks.toLocaleString() })
717
+ ] }),
718
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
719
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Avg CTR" }),
720
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.avgCtr ? `${(metrics.avgCtr * 100).toFixed(1)}%` : "0%" })
721
+ ] }),
722
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
723
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Avg Position" }),
724
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.avgPosition ? metrics.avgPosition.toFixed(1) : "-" })
725
+ ] }),
726
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
727
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "CTA Clicks" }),
728
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.totalCtaClicks.toLocaleString() })
729
+ ] }),
730
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
731
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "Form Submissions" }),
732
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.totalFormSubmissions.toLocaleString() })
733
+ ] }),
734
+ /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
735
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "AI Citations" }),
736
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: metrics.totalAiCitations })
737
+ ] })
738
+ ] })
739
+ ] }) })
740
+ ] });
741
+ }
742
+ function SeoMetricsPage() {
743
+ const { data: metrics, isLoading: metricsLoading } = useSeoAggregateMetrics();
744
+ const { data: pages, isLoading: pagesLoading } = useSeoPages({ status: "published" });
745
+ const pagination = usePaginationState(PAGE_SIZE_DEFAULT);
746
+ const paginatedPages = useMemo(() => {
747
+ if (!pages) return [];
748
+ return pages.slice(pagination.offset, pagination.offset + PAGE_SIZE_DEFAULT);
749
+ }, [pages, pagination.offset]);
750
+ return /* @__PURE__ */ jsxs(SubshellPageLayout, { children: [
751
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(PageTitleCaption, { title: "SEO Metrics", caption: "Search performance and conversion tracking" }) }),
752
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 4 }, children: [
753
+ /* @__PURE__ */ jsx(
754
+ StatCard,
755
+ {
756
+ variant: "hero",
757
+ icon: IconEye,
758
+ value: metrics?.totalImpressions?.toLocaleString() || "0",
759
+ label: "Total Impressions",
760
+ isLoading: metricsLoading
761
+ }
762
+ ),
763
+ /* @__PURE__ */ jsx(
764
+ StatCard,
765
+ {
766
+ variant: "hero",
767
+ icon: IconClick,
768
+ value: metrics?.totalClicks?.toLocaleString() || "0",
769
+ label: "Total Clicks",
770
+ isLoading: metricsLoading
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx(
774
+ StatCard,
775
+ {
776
+ variant: "hero",
777
+ icon: IconPercentage,
778
+ value: metrics?.avgCtr ? `${(metrics.avgCtr * 100).toFixed(1)}%` : "0%",
779
+ label: "Avg CTR",
780
+ isLoading: metricsLoading
781
+ }
782
+ ),
783
+ /* @__PURE__ */ jsx(
784
+ StatCard,
785
+ {
786
+ variant: "hero",
787
+ icon: IconChartLine,
788
+ value: metrics?.avgPosition ? metrics.avgPosition.toFixed(1) : "-",
789
+ label: "Avg Position",
790
+ isLoading: metricsLoading
791
+ }
792
+ )
793
+ ] }),
794
+ /* @__PURE__ */ jsxs(SimpleGrid, { cols: { base: 2, sm: 3 }, children: [
795
+ /* @__PURE__ */ jsx(
796
+ StatCard,
797
+ {
798
+ variant: "hero",
799
+ icon: IconTarget,
800
+ value: metrics?.totalCtaClicks?.toLocaleString() || "0",
801
+ label: "CTA Clicks",
802
+ isLoading: metricsLoading
803
+ }
804
+ ),
805
+ /* @__PURE__ */ jsx(
806
+ StatCard,
807
+ {
808
+ variant: "hero",
809
+ icon: IconForms,
810
+ value: metrics?.totalFormSubmissions?.toLocaleString() || "0",
811
+ label: "Form Submissions",
812
+ isLoading: metricsLoading
813
+ }
814
+ ),
815
+ /* @__PURE__ */ jsx(
816
+ StatCard,
817
+ {
818
+ variant: "hero",
819
+ icon: IconBrain,
820
+ value: metrics?.totalAiCitations || 0,
821
+ label: "AI Citations",
822
+ isLoading: metricsLoading
823
+ }
824
+ )
825
+ ] }),
826
+ /* @__PURE__ */ jsx(Paper, { children: /* @__PURE__ */ jsxs(Stack, { children: [
827
+ pagesLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : !pages?.length ? /* @__PURE__ */ jsx(EmptyState, { icon: IconFileText, title: "No published pages yet" }) : /* @__PURE__ */ jsxs(Table, { children: [
828
+ /* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
829
+ /* @__PURE__ */ jsx(Table.Th, { children: "Title" }),
830
+ /* @__PURE__ */ jsx(Table.Th, { children: "Vertical" }),
831
+ /* @__PURE__ */ jsx(Table.Th, { children: "Published" }),
832
+ /* @__PURE__ */ jsx(Table.Th, { children: "Refreshed" })
833
+ ] }) }),
834
+ /* @__PURE__ */ jsx(Table.Tbody, { children: paginatedPages.map((page) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
835
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { fw: 500, size: "sm", children: page.title }) }),
836
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", children: formatVertical(page.vertical) }) }),
837
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(page.published_at) }) }),
838
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDate(page.refreshed_at) }) })
839
+ ] }, page.id)) })
840
+ ] }),
841
+ pages && pages.length > PAGE_SIZE_DEFAULT && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
842
+ Pagination,
843
+ {
844
+ value: pagination.page,
845
+ onChange: pagination.setPage,
846
+ total: pagination.totalPages(pages.length),
847
+ size: "sm"
848
+ }
849
+ ) })
850
+ ] }) })
851
+ ] });
852
+ }
853
+
854
+ export { SEO_PAGE_STATUSES, SEO_PAGE_TYPES, SEO_VERTICALS, SeoMetricsPage, SeoPageDetailPage, SeoPagesPage, formatDate, formatVertical, getPageTypeColor, getStatusColor, seoMetricsKeys, seoPageKeys, seoStatsKeys, useDeleteSeoPage, useSeoAggregateMetrics, useSeoPage, useSeoPageMetrics, useSeoPages, useSeoStats, useUpdateSeoPage };