@mohasinac/appkit 2.7.3 → 2.7.5

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 (74) hide show
  1. package/README.md +18 -0
  2. package/dist/_internal/server/features/seo/index.d.ts +8 -0
  3. package/dist/_internal/server/features/seo/index.js +4 -0
  4. package/dist/_internal/server/features/seo/manifest.d.ts +7 -0
  5. package/dist/_internal/server/features/seo/manifest.js +48 -0
  6. package/dist/_internal/server/features/seo/og.d.ts +11 -0
  7. package/dist/_internal/server/features/seo/og.js +52 -0
  8. package/dist/_internal/server/features/seo/robots.d.ts +5 -0
  9. package/dist/_internal/server/features/seo/robots.js +30 -0
  10. package/dist/_internal/server/features/seo/sitemap.d.ts +5 -0
  11. package/dist/_internal/server/features/seo/sitemap.js +254 -0
  12. package/dist/configs/next.d.ts +0 -21
  13. package/dist/configs/next.js +68 -0
  14. package/dist/features/account/components/AddressFilters.js +1 -0
  15. package/dist/features/admin/components/AdminAllEventEntriesView.js +1 -1
  16. package/dist/features/admin/components/AdminBidsView.js +1 -1
  17. package/dist/features/admin/components/AdminBlogView.js +1 -1
  18. package/dist/features/admin/components/AdminBrandsView.js +1 -1
  19. package/dist/features/admin/components/AdminCartsView.js +1 -1
  20. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  21. package/dist/features/admin/components/AdminContactView.js +1 -1
  22. package/dist/features/admin/components/AdminCouponsView.js +1 -1
  23. package/dist/features/admin/components/AdminFaqsView.js +1 -1
  24. package/dist/features/admin/components/AdminHistoryView.js +1 -1
  25. package/dist/features/admin/components/AdminNewsletterView.js +1 -1
  26. package/dist/features/admin/components/AdminNotificationsView.js +1 -1
  27. package/dist/features/admin/components/AdminOrdersView.js +1 -1
  28. package/dist/features/admin/components/AdminPayoutsView.js +1 -1
  29. package/dist/features/admin/components/AdminProductsView.js +1 -1
  30. package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
  31. package/dist/features/admin/components/AdminReviewsView.js +1 -1
  32. package/dist/features/admin/components/AdminSessionsView.js +1 -1
  33. package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
  34. package/dist/features/admin/components/AdminStoresView.js +1 -1
  35. package/dist/features/admin/components/AdminUsersView.js +1 -1
  36. package/dist/features/admin/components/AdminWishlistsView.js +1 -1
  37. package/dist/features/auctions/components/AuctionFilters.js +1 -0
  38. package/dist/features/blog/components/BlogFilters.js +1 -0
  39. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  40. package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
  41. package/dist/features/categories/components/CategoryFilters.js +1 -0
  42. package/dist/features/categories/components/CategoryProductsListing.js +1 -1
  43. package/dist/features/categories/hooks/useCategories.js +1 -0
  44. package/dist/features/events/components/AdminEventsView.js +1 -1
  45. package/dist/features/events/components/EventFilters.js +1 -0
  46. package/dist/features/events/components/EventsIndexListing.js +1 -1
  47. package/dist/features/orders/components/OrderFilters.js +1 -0
  48. package/dist/features/pre-orders/components/PreOrderFilters.js +1 -0
  49. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +1 -1
  50. package/dist/features/products/components/AuctionsIndexListing.js +1 -1
  51. package/dist/features/products/components/ProductFilters.js +1 -0
  52. package/dist/features/products/components/ProductsIndexListing.js +1 -1
  53. package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
  54. package/dist/features/reviews/components/ReviewFilters.js +1 -0
  55. package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
  56. package/dist/features/seller/components/SellerAuctionsView.js +1 -1
  57. package/dist/features/seller/components/SellerBidsView.js +1 -1
  58. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  59. package/dist/features/seller/components/SellerOffersView.js +1 -1
  60. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  61. package/dist/features/seller/components/SellerPayoutsView.js +1 -1
  62. package/dist/features/seller/components/SellerPreOrdersView.js +1 -1
  63. package/dist/features/seller/components/SellerPrizeDrawsView.js +1 -1
  64. package/dist/features/seller/components/SellerProductsView.js +1 -1
  65. package/dist/features/stores/components/StoreAuctionsListing.js +1 -1
  66. package/dist/features/stores/components/StoreFilters.js +1 -0
  67. package/dist/features/stores/components/StorePreOrdersListing.js +1 -1
  68. package/dist/features/stores/components/StoreProductsListing.js +1 -1
  69. package/dist/features/stores/components/StoresIndexListing.js +1 -1
  70. package/dist/index.d.ts +0 -2
  71. package/dist/index.js +0 -6
  72. package/dist/server.d.ts +2 -0
  73. package/dist/server.js +2 -0
  74. package/package.json +2 -2
package/README.md CHANGED
@@ -56,6 +56,24 @@ Only publish when explicitly requested. See `CLAUDE.md § Appkit Publish & Deplo
56
56
 
57
57
  ## Version History
58
58
 
59
+ ### v2.7.3 — 2026-05-14
60
+
61
+ **Barrel cleanup — RSC views moved to server-entry; routeHandler readonly roles fix**
62
+
63
+ - `server-entry.ts`: RSC page views (bundles, categories, events, stores, products, etc.) moved here from main index to prevent firebase-admin leaking into client bundles
64
+ - `src/features/categories/components/index.ts`: `BundlesListView` + `BundleDetailView` removed from client barrel (now server-only)
65
+ - `src/next/api/routeHandler.ts`: `roles` option widened to `readonly string[]` — fixes TS error when passing `ROLES_TRUST_SAFETY` (readonly tuple) to `createRouteHandler`
66
+
67
+ ### v2.7.2 — 2026-05-14
68
+
69
+ **routeHandler readonly roles type fix**
70
+
71
+ - `src/next/api/routeHandler.ts`: `roles?: string[]` → `roles?: readonly string[]`
72
+
73
+ ### v2.7.1 — 2026-05-14
74
+
75
+ **Patch publish to clear npm "already published" state**
76
+
59
77
  ### v2.7.0 — 2026-05-14
60
78
 
61
79
  **Support Tickets seed data + Firestore indices + seed pipeline wiring (BAN9/SCAM9 followup)**
@@ -0,0 +1,8 @@
1
+ export { buildRobots } from "./robots";
2
+ export type { RobotsOptions } from "./robots";
3
+ export { buildManifest } from "./manifest";
4
+ export type { ManifestOptions } from "./manifest";
5
+ export { buildSitemap } from "./sitemap";
6
+ export type { SitemapOptions } from "./sitemap";
7
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
8
+ export type { DefaultOgOptions } from "./og";
@@ -0,0 +1,4 @@
1
+ export { buildRobots } from "./robots";
2
+ export { buildManifest } from "./manifest";
3
+ export { buildSitemap } from "./sitemap";
4
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
@@ -0,0 +1,7 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface ManifestOptions {
3
+ name: string;
4
+ shortName: string;
5
+ description: string;
6
+ }
7
+ export declare function buildManifest({ name, shortName, description }: ManifestOptions): MetadataRoute.Manifest;
@@ -0,0 +1,48 @@
1
+ import { ROUTES } from "../../../../next/routing/route-map";
2
+ export function buildManifest({ name, shortName, description }) {
3
+ return {
4
+ name,
5
+ short_name: shortName,
6
+ description,
7
+ start_url: "/",
8
+ display: "standalone",
9
+ orientation: "portrait",
10
+ background_color: "#ffffff",
11
+ theme_color: "#3570fc",
12
+ categories: ["shopping", "marketplace", "business"],
13
+ lang: "en",
14
+ dir: "ltr",
15
+ prefer_related_applications: false,
16
+ icons: [
17
+ {
18
+ src: "/favicon.svg",
19
+ sizes: "any",
20
+ type: "image/svg+xml",
21
+ purpose: "any maskable",
22
+ },
23
+ ],
24
+ screenshots: [],
25
+ shortcuts: [
26
+ {
27
+ name: "Browse Products",
28
+ url: String(ROUTES.PUBLIC.PRODUCTS),
29
+ description: "Browse the latest products",
30
+ },
31
+ {
32
+ name: "Live Auctions",
33
+ url: String(ROUTES.PUBLIC.AUCTIONS),
34
+ description: "Join live auction bidding",
35
+ },
36
+ {
37
+ name: "Categories",
38
+ url: String(ROUTES.PUBLIC.CATEGORIES),
39
+ description: "Explore product categories",
40
+ },
41
+ {
42
+ name: "Stores",
43
+ url: String(ROUTES.PUBLIC.STORES),
44
+ description: "Browse seller storefronts",
45
+ },
46
+ ],
47
+ };
48
+ }
@@ -0,0 +1,11 @@
1
+ import { ImageResponse } from "next/og";
2
+ export declare const DEFAULT_OG_SIZE: {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ export interface DefaultOgOptions {
7
+ siteName: string;
8
+ tagline?: string;
9
+ domain?: string;
10
+ }
11
+ export declare function buildDefaultOgImage({ siteName, tagline, domain }: DefaultOgOptions): ImageResponse;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ImageResponse } from "next/og";
3
+ export const DEFAULT_OG_SIZE = { width: 1200, height: 630 };
4
+ export function buildDefaultOgImage({ siteName, tagline, domain }) {
5
+ const subtitle = tagline ?? "Shop, Bid & Sell — India's Multi-Seller Marketplace";
6
+ return new ImageResponse(_jsxs("div", { style: {
7
+ width: "100%",
8
+ height: "100%",
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ alignItems: "center",
12
+ justifyContent: "center",
13
+ background: "linear-gradient(135deg, #3570fc 0%, #65c408 50%, #e91e8c 100%)",
14
+ fontFamily: "sans-serif",
15
+ position: "relative",
16
+ overflow: "hidden",
17
+ }, children: [_jsx("div", { style: {
18
+ position: "absolute",
19
+ inset: 0,
20
+ opacity: 0.07,
21
+ backgroundImage: "radial-gradient(circle at 25% 25%, white 1px, transparent 0), radial-gradient(circle at 75% 75%, white 1px, transparent 0)",
22
+ backgroundSize: "48px 48px",
23
+ } }), _jsx("div", { style: {
24
+ display: "flex",
25
+ alignItems: "center",
26
+ justifyContent: "center",
27
+ width: 120,
28
+ height: 120,
29
+ borderRadius: 24,
30
+ background: "rgba(255,255,255,0.15)",
31
+ marginBottom: 32,
32
+ fontSize: 64,
33
+ }, children: "\uD83D\uDECD\uFE0F" }), _jsx("div", { style: {
34
+ fontSize: 80,
35
+ fontWeight: 800,
36
+ color: "white",
37
+ letterSpacing: "-2px",
38
+ marginBottom: 16,
39
+ textShadow: "0 4px 24px rgba(0,0,0,0.3)",
40
+ }, children: siteName }), _jsx("div", { style: {
41
+ fontSize: 32,
42
+ color: "rgba(255,255,255,0.85)",
43
+ fontWeight: 400,
44
+ letterSpacing: "0.5px",
45
+ }, children: subtitle }), domain && (_jsx("div", { style: {
46
+ position: "absolute",
47
+ bottom: 36,
48
+ fontSize: 22,
49
+ color: "rgba(255,255,255,0.6)",
50
+ letterSpacing: "1px",
51
+ }, children: domain }))] }), { ...DEFAULT_OG_SIZE });
52
+ }
@@ -0,0 +1,5 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface RobotsOptions {
3
+ siteUrl: string;
4
+ }
5
+ export declare function buildRobots({ siteUrl }: RobotsOptions): MetadataRoute.Robots;
@@ -0,0 +1,30 @@
1
+ export function buildRobots({ siteUrl }) {
2
+ return {
3
+ rules: [
4
+ {
5
+ userAgent: "*",
6
+ allow: "/",
7
+ disallow: [
8
+ "/admin/",
9
+ "/api/",
10
+ "/seller/",
11
+ "/user/",
12
+ "/auth/",
13
+ "/checkout/",
14
+ "/cart/",
15
+ "/demo/",
16
+ "/track/",
17
+ "/unauthorized/",
18
+ "/_next/",
19
+ "/profile/*/edit",
20
+ ],
21
+ },
22
+ {
23
+ userAgent: ["GPTBot", "ChatGPT-User", "Google-Extended", "CCBot"],
24
+ disallow: "/",
25
+ },
26
+ ],
27
+ sitemap: `${siteUrl}/sitemap.xml`,
28
+ host: siteUrl,
29
+ };
30
+ }
@@ -0,0 +1,5 @@
1
+ import type { MetadataRoute } from "next";
2
+ export interface SitemapOptions {
3
+ baseUrl: string;
4
+ }
5
+ export declare function buildSitemap({ baseUrl }: SitemapOptions): Promise<MetadataRoute.Sitemap>;
@@ -0,0 +1,254 @@
1
+ import { getAdminDb } from "../../../../providers/db-firebase";
2
+ import { ROUTES } from "../../../../next/routing/route-map";
3
+ import { PRODUCT_COLLECTION } from "../../../..";
4
+ import { EVENTS_COLLECTION, EVENT_FIELDS } from "../../../../features/events";
5
+ import { BLOG_POSTS_COLLECTION, BLOG_POST_FIELDS } from "../../../../features/blog";
6
+ import { CATEGORIES_COLLECTION, CATEGORY_FIELDS } from "../../../../features/categories";
7
+ import { STORE_COLLECTION, STORE_FIELDS } from "../../../../features/stores";
8
+ import { SCAMMER_COLLECTION } from "../../../../features/scams/schemas/firestore";
9
+ import { serverLogger } from "../../../../monitoring/server-logger";
10
+ // Product field strings — matches consumer field-names.ts
11
+ const PRODUCT_STATUS = "status";
12
+ const PRODUCT_STATUS_PUBLISHED = "published";
13
+ const PRODUCT_SLUG = "slug";
14
+ const PRODUCT_UPDATED_AT = "updatedAt";
15
+ function staticPages(baseUrl) {
16
+ const page = (path, changeFreq, priority) => ({ url: `${baseUrl}${path}`, lastModified: new Date(), changeFrequency: changeFreq, priority });
17
+ return [
18
+ page(String(ROUTES.HOME), "daily", 1.0),
19
+ page(String(ROUTES.PUBLIC.PRODUCTS), "hourly", 0.9),
20
+ page(String(ROUTES.PUBLIC.AUCTIONS), "hourly", 0.9),
21
+ page(String(ROUTES.PUBLIC.CATEGORIES), "weekly", 0.8),
22
+ page(String(ROUTES.PUBLIC.BLOG), "daily", 0.7),
23
+ page(String(ROUTES.PUBLIC.EVENTS), "daily", 0.7),
24
+ page(String(ROUTES.PUBLIC.SELLERS), "weekly", 0.6),
25
+ page(String(ROUTES.PUBLIC.ABOUT), "monthly", 0.5),
26
+ page(String(ROUTES.PUBLIC.CONTACT), "monthly", 0.5),
27
+ page(String(ROUTES.PUBLIC.FAQS), "monthly", 0.4),
28
+ page(String(ROUTES.PUBLIC.TERMS), "yearly", 0.3),
29
+ page(String(ROUTES.PUBLIC.PRIVACY), "yearly", 0.3),
30
+ page(String(ROUTES.PUBLIC.SECURITY), "yearly", 0.4),
31
+ page(String(ROUTES.PUBLIC.HELP), "monthly", 0.4),
32
+ page(String(ROUTES.PUBLIC.STORES), "weekly", 0.7),
33
+ page(String(ROUTES.PUBLIC.PROMOTIONS), "daily", 0.6),
34
+ page(String(ROUTES.PUBLIC.REVIEWS), "daily", 0.5),
35
+ page(String(ROUTES.PUBLIC.SELLER_GUIDE), "monthly", 0.5),
36
+ page(String(ROUTES.PUBLIC.COOKIE_POLICY), "yearly", 0.2),
37
+ page(String(ROUTES.PUBLIC.REFUND_POLICY), "yearly", 0.3),
38
+ page(String(ROUTES.PUBLIC.SHIPPING_POLICY), "yearly", 0.3),
39
+ page(String(ROUTES.PUBLIC.PRE_ORDERS), "daily", 0.7),
40
+ page(String(ROUTES.PUBLIC.FEES), "monthly", 0.4),
41
+ page(String(ROUTES.PUBLIC.HOW_AUCTIONS_WORK), "monthly", 0.4),
42
+ page(String(ROUTES.PUBLIC.HOW_PRE_ORDERS_WORK), "monthly", 0.4),
43
+ page(String(ROUTES.PUBLIC.HOW_OFFERS_WORK), "monthly", 0.4),
44
+ page(String(ROUTES.PUBLIC.HOW_CHECKOUT_WORKS), "monthly", 0.4),
45
+ page(String(ROUTES.PUBLIC.HOW_ORDERS_WORK), "monthly", 0.4),
46
+ page(String(ROUTES.PUBLIC.HOW_REVIEWS_WORK), "monthly", 0.4),
47
+ page(String(ROUTES.PUBLIC.HOW_PAYOUTS_WORK), "monthly", 0.4),
48
+ page(String(ROUTES.PUBLIC.SCAMS), "daily", 0.8),
49
+ page(String(ROUTES.PUBLIC.SCAM_TYPES), "monthly", 0.7),
50
+ page(String(ROUTES.PUBLIC.SCAM_REPORT), "monthly", 0.6),
51
+ page(String(ROUTES.PUBLIC.SCAM_FAQS), "weekly", 0.7),
52
+ ];
53
+ }
54
+ async function fetchProductUrls(baseUrl) {
55
+ try {
56
+ const db = getAdminDb();
57
+ const snap = await db
58
+ .collection(PRODUCT_COLLECTION)
59
+ .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
60
+ .where("listingType", "==", "standard")
61
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
62
+ .limit(5000)
63
+ .get();
64
+ return snap.docs.map((doc) => {
65
+ const data = doc.data();
66
+ const slug = data[PRODUCT_SLUG] ?? doc.id;
67
+ return {
68
+ url: `${baseUrl}${ROUTES.PUBLIC.PRODUCT_DETAIL(slug)}`,
69
+ lastModified: data[PRODUCT_UPDATED_AT]?.toDate?.() ?? new Date(),
70
+ changeFrequency: "daily",
71
+ priority: 0.8,
72
+ };
73
+ });
74
+ }
75
+ catch (err) {
76
+ serverLogger.warn("sitemap: failed to fetch product URLs", { error: err });
77
+ return [];
78
+ }
79
+ }
80
+ async function fetchAuctionUrls(baseUrl) {
81
+ try {
82
+ const db = getAdminDb();
83
+ const snap = await db
84
+ .collection(PRODUCT_COLLECTION)
85
+ .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
86
+ .where("listingType", "==", "auction")
87
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
88
+ .limit(2000)
89
+ .get();
90
+ return snap.docs.map((doc) => {
91
+ const data = doc.data();
92
+ const slug = data[PRODUCT_SLUG] ?? doc.id;
93
+ return {
94
+ url: `${baseUrl}${ROUTES.PUBLIC.AUCTION_DETAIL(slug)}`,
95
+ lastModified: data[PRODUCT_UPDATED_AT]?.toDate?.() ?? new Date(),
96
+ changeFrequency: "daily",
97
+ priority: 0.8,
98
+ };
99
+ });
100
+ }
101
+ catch (err) {
102
+ serverLogger.warn("sitemap: failed to fetch auction URLs", { error: err });
103
+ return [];
104
+ }
105
+ }
106
+ async function fetchEventUrls(baseUrl) {
107
+ try {
108
+ const db = getAdminDb();
109
+ const snap = await db
110
+ .collection(EVENTS_COLLECTION)
111
+ .where(EVENT_FIELDS.STATUS, "==", EVENT_FIELDS.STATUS_VALUES.ACTIVE)
112
+ .select(EVENT_FIELDS.UPDATED_AT)
113
+ .limit(500)
114
+ .get();
115
+ return snap.docs.map((doc) => {
116
+ const data = doc.data();
117
+ return {
118
+ url: `${baseUrl}${ROUTES.PUBLIC.EVENT_DETAIL(doc.id)}`,
119
+ lastModified: data[EVENT_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
120
+ changeFrequency: "daily",
121
+ priority: 0.7,
122
+ };
123
+ });
124
+ }
125
+ catch (err) {
126
+ serverLogger.warn("sitemap: failed to fetch event URLs", { error: err });
127
+ return [];
128
+ }
129
+ }
130
+ async function fetchCategoryUrls(baseUrl) {
131
+ try {
132
+ const db = getAdminDb();
133
+ const snap = await db
134
+ .collection(CATEGORIES_COLLECTION)
135
+ .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
136
+ .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT)
137
+ .limit(500)
138
+ .get();
139
+ return snap.docs.map((doc) => {
140
+ const data = doc.data();
141
+ const slug = data[CATEGORY_FIELDS.SLUG] ?? doc.id;
142
+ return {
143
+ url: `${baseUrl}/categories/${slug}`,
144
+ lastModified: data[CATEGORY_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
145
+ changeFrequency: "weekly",
146
+ priority: 0.7,
147
+ };
148
+ });
149
+ }
150
+ catch (err) {
151
+ serverLogger.warn("sitemap: failed to fetch category URLs", { error: err });
152
+ return [];
153
+ }
154
+ }
155
+ async function fetchBlogPostUrls(baseUrl) {
156
+ try {
157
+ const db = getAdminDb();
158
+ const snap = await db
159
+ .collection(BLOG_POSTS_COLLECTION)
160
+ .where(BLOG_POST_FIELDS.STATUS, "==", BLOG_POST_FIELDS.STATUS_VALUES.PUBLISHED)
161
+ .select(BLOG_POST_FIELDS.SLUG, BLOG_POST_FIELDS.PUBLISHED_AT, BLOG_POST_FIELDS.UPDATED_AT)
162
+ .limit(1000)
163
+ .get();
164
+ return snap.docs.map((doc) => {
165
+ const data = doc.data();
166
+ const slug = data[BLOG_POST_FIELDS.SLUG] ?? doc.id;
167
+ const lastModified = data[BLOG_POST_FIELDS.UPDATED_AT]?.toDate?.() ??
168
+ data[BLOG_POST_FIELDS.PUBLISHED_AT]?.toDate?.() ??
169
+ new Date();
170
+ return {
171
+ url: `${baseUrl}${ROUTES.BLOG.ARTICLE(slug)}`,
172
+ lastModified,
173
+ changeFrequency: "weekly",
174
+ priority: 0.7,
175
+ };
176
+ });
177
+ }
178
+ catch (err) {
179
+ serverLogger.warn("sitemap: failed to fetch blog post URLs", { error: err });
180
+ return [];
181
+ }
182
+ }
183
+ async function fetchStoreUrls(baseUrl) {
184
+ try {
185
+ const db = getAdminDb();
186
+ const snap = await db
187
+ .collection(STORE_COLLECTION)
188
+ .where(STORE_FIELDS.STATUS, "==", STORE_FIELDS.STATUS_VALUES.ACTIVE)
189
+ .where(STORE_FIELDS.IS_PUBLIC, "==", true)
190
+ .select(STORE_FIELDS.STORE_SLUG, STORE_FIELDS.UPDATED_AT)
191
+ .limit(1000)
192
+ .get();
193
+ return snap.docs.map((doc) => {
194
+ const data = doc.data();
195
+ const slug = data[STORE_FIELDS.STORE_SLUG] ?? doc.id;
196
+ return {
197
+ url: `${baseUrl}${ROUTES.PUBLIC.STORE_DETAIL(slug)}`,
198
+ lastModified: data[STORE_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
199
+ changeFrequency: "weekly",
200
+ priority: 0.6,
201
+ };
202
+ });
203
+ }
204
+ catch (err) {
205
+ serverLogger.warn("sitemap: failed to fetch store URLs", { error: err });
206
+ return [];
207
+ }
208
+ }
209
+ async function fetchScammerUrls(baseUrl) {
210
+ try {
211
+ const db = getAdminDb();
212
+ const snap = await db
213
+ .collection(SCAMMER_COLLECTION)
214
+ .where("status", "==", "verified")
215
+ .select("seo", "updatedAt")
216
+ .limit(2000)
217
+ .get();
218
+ return snap.docs.map((doc) => {
219
+ const data = doc.data();
220
+ const slug = data.seo?.slug ?? doc.id;
221
+ return {
222
+ url: `${baseUrl}${ROUTES.PUBLIC.SCAM_DETAIL(slug)}`,
223
+ lastModified: data.updatedAt?.toDate?.() ?? new Date(),
224
+ changeFrequency: "weekly",
225
+ priority: 0.7,
226
+ };
227
+ });
228
+ }
229
+ catch (err) {
230
+ serverLogger.warn("sitemap: failed to fetch scammer URLs", { error: err });
231
+ return [];
232
+ }
233
+ }
234
+ export async function buildSitemap({ baseUrl }) {
235
+ const [productUrls, categoryUrls, eventUrls, blogUrls, auctionUrls, storeUrls, scammerUrls] = await Promise.all([
236
+ fetchProductUrls(baseUrl),
237
+ fetchCategoryUrls(baseUrl),
238
+ fetchEventUrls(baseUrl),
239
+ fetchBlogPostUrls(baseUrl),
240
+ fetchAuctionUrls(baseUrl),
241
+ fetchStoreUrls(baseUrl),
242
+ fetchScammerUrls(baseUrl),
243
+ ]);
244
+ return [
245
+ ...staticPages(baseUrl),
246
+ ...categoryUrls,
247
+ ...blogUrls,
248
+ ...productUrls,
249
+ ...auctionUrls,
250
+ ...eventUrls,
251
+ ...storeUrls,
252
+ ...scammerUrls,
253
+ ];
254
+ }
@@ -1,24 +1,3 @@
1
- /**
2
- * defineNextConfig — appkit-aware Next.js config factory.
3
- *
4
- * Provides sensible defaults for projects consuming @mohasinac/appkit.
5
- * Consumer overrides deep-merge with the defaults (arrays concatenate).
6
- *
7
- * Notable defaults:
8
- * - `serverExternalPackages` includes firebase-admin + GCP deps
9
- * - `outputFileTracingIncludes` uses broad org-level globs (**) so every
10
- * current and future @google-cloud/* subpackage (incl. build/protos/**) is
11
- * included in Vercel Lambda bundles without needing per-package entries.
12
- * - Consumer additions to the same route key are MERGED (union), not replaced.
13
- * - `images.remotePatterns` restricts to Firebase Storage + localhost; consumer
14
- * can append additional patterns via `images.remotePatterns`.
15
- * - `experimental.serverActions.bodySizeLimit` set to "4mb"
16
- * - Optional `IgnorePlugin` for optional native deps (request, fast-crc32c)
17
- *
18
- * When the _internal/ dual-entry migration completes in S7, the firebase-admin
19
- * workarounds are removed from this helper because the client entry will never
20
- * import firebase-admin. Until then they remain for backward compatibility.
21
- */
22
1
  export interface NextConfigOverride {
23
2
  serverExternalPackages?: string[];
24
3
  experimental?: Record<string, unknown>;
@@ -1,3 +1,4 @@
1
+ import path from "path";
1
2
  /**
2
3
  * defineNextConfig — appkit-aware Next.js config factory.
3
4
  *
@@ -107,6 +108,11 @@ export function defineNextConfig(override = {}) {
107
108
  "./node_modules/duplexify/**",
108
109
  "./node_modules/uuid/**",
109
110
  "./node_modules/lodash.camelcase/**",
111
+ // gaxios transitive deps missing from Vercel tracer (dynamic require, not statically analysed)
112
+ "./node_modules/is-stream/**",
113
+ "./node_modules/extend/**",
114
+ "./node_modules/https-proxy-agent/**",
115
+ "./node_modules/agent-base/**",
110
116
  ],
111
117
  };
112
118
  const mergedOutputFileTracingIncludes = {
@@ -122,6 +128,19 @@ export function defineNextConfig(override = {}) {
122
128
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
129
  function mergedWebpack(config, ctx) {
124
130
  const { isServer, webpack } = ctx;
131
+ // Deduplicate firebase client SDK across the monorepo.
132
+ // appkit ships its own node_modules/firebase (from standalone dev). Without
133
+ // this alias, webpack may resolve firebase/app to two separate module
134
+ // instances — one from appkit/node_modules and one from root node_modules —
135
+ // producing two separate Firebase app registries. getApps()/getAuth() then
136
+ // disagree on which apps exist, causing "No Firebase App '[DEFAULT]'" errors.
137
+ // Pinning every firebase/* import to the root copy ensures the singleton
138
+ // registry is shared regardless of which file imports firebase.
139
+ const _firebaseRoot = path.resolve(process.cwd(), "node_modules", "firebase");
140
+ config.resolve.alias = {
141
+ ...(config.resolve.alias ?? {}),
142
+ "firebase": _firebaseRoot,
143
+ };
125
144
  if (isServer) {
126
145
  const externalFn = ({ request }, callback) => {
127
146
  if (request &&
@@ -138,6 +157,55 @@ export function defineNextConfig(override = {}) {
138
157
  // firebase-admin wraps in try/catch and degrades gracefully without.
139
158
  config.plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /^(request|fast-crc32c)$/ }));
140
159
  }
160
+ else {
161
+ // ── Client bundle ──────────────────────────────────────────────────────
162
+ // client-entry.ts re-exports ALL of index.ts (including repositories,
163
+ // email providers, and payment SDKs) via `export * from "./index"`.
164
+ // sideEffects:false should tree-shake unused server-only exports, but
165
+ // webpack must first PARSE the full module graph to enumerate all exports.
166
+ // That parse phase hits Node.js-only requires deep in firebase-admin,
167
+ // resend, razorpay, etc. and throws "Module not found" errors.
168
+ //
169
+ // Fix: alias every server-only package to `false` so webpack replaces them
170
+ // with empty stubs during the parse phase. Tree-shaking then eliminates the
171
+ // dead code paths that would have called them. This mirrors what the server
172
+ // branch does with `externals`, but for client bundles.
173
+ // Strip the "node:" URI scheme prefix — webpack 5 browser target doesn't
174
+ // handle it by default, causing UnhandledSchemeError.
175
+ config.plugins.push(new webpack.NormalModuleReplacementPlugin(/^node:/, (resource) => {
176
+ resource.request = resource.request.replace(/^node:/, "");
177
+ }));
178
+ // Stub out server-only packages: reuse FIREBASE_EXTERNAL_PACKAGES plus
179
+ // additional email/payment SDKs that also import Node.js built-ins.
180
+ const CLIENT_STUB_PACKAGES = [
181
+ ...FIREBASE_EXTERNAL_PACKAGES,
182
+ "resend",
183
+ "@react-email/render",
184
+ "razorpay",
185
+ "firebase-functions",
186
+ ];
187
+ const clientStubAliases = Object.fromEntries(CLIENT_STUB_PACKAGES.flatMap((pkg) => [
188
+ [pkg, false],
189
+ // Also stub sub-paths so `import ... from 'pkg/sub'` is covered.
190
+ // Webpack resolves these via the alias before following sub-paths,
191
+ // so the top-level alias alone handles most cases, but explicit
192
+ // sub-path aliases cover edge cases like `firebase-admin/app`.
193
+ ]));
194
+ config.resolve.alias = {
195
+ ...(config.resolve.alias ?? {}),
196
+ ...clientStubAliases,
197
+ };
198
+ // Fallbacks for Node.js built-ins that have no browser polyfill and are
199
+ // reached before the alias can intercept (e.g. inline require("fs")).
200
+ // DO NOT stub path/os/url/stream — Next.js provides browser polyfills.
201
+ config.resolve.fallback = {
202
+ ...(config.resolve.fallback ?? {}),
203
+ child_process: false,
204
+ fs: false,
205
+ net: false,
206
+ tls: false,
207
+ };
208
+ }
141
209
  return consumerWebpack ? consumerWebpack(config, ctx) : config;
142
210
  }
143
211
  // Default remotePatterns: Firebase Storage + localhost only.
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useTranslations } from "next-intl";
3
4
  import { FilterFacetSection } from "../../filters/FilterFacetSection";
@@ -92,7 +92,7 @@ export function AdminAllEventEntriesView({ children, ...props }) {
92
92
  if (hasChildren) {
93
93
  return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
94
94
  }
95
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user name or event ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); table.setPage(1); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No entries found", renderRowActions: (row) => {
95
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user name or event ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No entries found", renderRowActions: (row) => {
96
96
  const er = row;
97
97
  return (_jsx(RowActionMenu, { actions: [
98
98
  { label: "Confirm", onClick: () => updateMutation.mutate({ id: er.id, status: "CONFIRMED" }) },
@@ -97,7 +97,7 @@ export function AdminBidsView({ children, ...props }) {
97
97
  if (hasChildren) {
98
98
  return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
99
99
  }
100
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bids, products, or bidder IDs", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); table.setPage(1); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No bids found", renderRowActions: (row) => {
100
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bids, products, or bidder IDs", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No bids found", renderRowActions: (row) => {
101
101
  const bidRow = row;
102
102
  const isCancelled = bidRow.status === "cancelled" || bidRow.status === "voided";
103
103
  return (_jsx(RowActionMenu, { actions: [
@@ -95,5 +95,5 @@ export function AdminBlogView({ children, getRowHref, ...props }) {
95
95
  if (hasChildren) {
96
96
  return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
97
97
  }
98
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search articles, authors, or tags", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); table.setPage(1); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Post"] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No blog posts found", onRowClick: (row) => openEditPanel(row.id) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Featured", tabs: FEATURED_TABS, value: pendingFilters.isFeatured ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, isFeatured: id })), allId: "" })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "New Post" : "Edit Post", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBlogEditorView, { postId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
98
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search articles, authors, or tags", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Post"] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No blog posts found", onRowClick: (row) => openEditPanel(row.id) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Featured", tabs: FEATURED_TABS, value: pendingFilters.isFeatured ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, isFeatured: id })), allId: "" })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "New Post" : "Edit Post", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBlogEditorView, { postId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
99
99
  }