@sonordev/site-kit 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +376 -0
  2. package/dist/SetupWizard-Cki06kB0.d.mts +12 -0
  3. package/dist/SetupWizard-Cki06kB0.d.ts +12 -0
  4. package/dist/analytics/index.d.mts +93 -0
  5. package/dist/analytics/index.d.ts +93 -0
  6. package/dist/analytics/index.js +89 -0
  7. package/dist/analytics/index.js.map +1 -0
  8. package/dist/analytics/index.mjs +71 -0
  9. package/dist/analytics/index.mjs.map +1 -0
  10. package/dist/api-CWtoFJCO.d.mts +137 -0
  11. package/dist/api-CWtoFJCO.d.ts +137 -0
  12. package/dist/blog/index.d.mts +305 -0
  13. package/dist/blog/index.d.ts +305 -0
  14. package/dist/blog/index.js +1578 -0
  15. package/dist/blog/index.js.map +1 -0
  16. package/dist/blog/index.mjs +1562 -0
  17. package/dist/blog/index.mjs.map +1 -0
  18. package/dist/blog/server.d.mts +229 -0
  19. package/dist/blog/server.d.ts +229 -0
  20. package/dist/blog/server.js +692 -0
  21. package/dist/blog/server.js.map +1 -0
  22. package/dist/blog/server.mjs +666 -0
  23. package/dist/blog/server.mjs.map +1 -0
  24. package/dist/chunk-24277A3Q.mjs +968 -0
  25. package/dist/chunk-24277A3Q.mjs.map +1 -0
  26. package/dist/chunk-373TK6TZ.js +321 -0
  27. package/dist/chunk-373TK6TZ.js.map +1 -0
  28. package/dist/chunk-3MYZS6PD.js +30 -0
  29. package/dist/chunk-3MYZS6PD.js.map +1 -0
  30. package/dist/chunk-43GBM4SX.js +283 -0
  31. package/dist/chunk-43GBM4SX.js.map +1 -0
  32. package/dist/chunk-4XPGGLVP.mjs +53 -0
  33. package/dist/chunk-4XPGGLVP.mjs.map +1 -0
  34. package/dist/chunk-622GAQP5.js +2008 -0
  35. package/dist/chunk-622GAQP5.js.map +1 -0
  36. package/dist/chunk-6BIPAKL4.mjs +28 -0
  37. package/dist/chunk-6BIPAKL4.mjs.map +1 -0
  38. package/dist/chunk-6ZCISNAB.mjs +343 -0
  39. package/dist/chunk-6ZCISNAB.mjs.map +1 -0
  40. package/dist/chunk-72MQFHYJ.js +1429 -0
  41. package/dist/chunk-72MQFHYJ.js.map +1 -0
  42. package/dist/chunk-7557OTHW.js +62 -0
  43. package/dist/chunk-7557OTHW.js.map +1 -0
  44. package/dist/chunk-7FUV73JZ.js +981 -0
  45. package/dist/chunk-7FUV73JZ.js.map +1 -0
  46. package/dist/chunk-7RF6PVHA.mjs +324 -0
  47. package/dist/chunk-7RF6PVHA.mjs.map +1 -0
  48. package/dist/chunk-7RYCHO6D.mjs +134 -0
  49. package/dist/chunk-7RYCHO6D.mjs.map +1 -0
  50. package/dist/chunk-7UKPRW25.mjs +1999 -0
  51. package/dist/chunk-7UKPRW25.mjs.map +1 -0
  52. package/dist/chunk-7URAOG2M.js +14864 -0
  53. package/dist/chunk-7URAOG2M.js.map +1 -0
  54. package/dist/chunk-AFAO3TGS.mjs +810 -0
  55. package/dist/chunk-AFAO3TGS.mjs.map +1 -0
  56. package/dist/chunk-BYLIU6XG.js +343 -0
  57. package/dist/chunk-BYLIU6XG.js.map +1 -0
  58. package/dist/chunk-D63MUKZ6.mjs +4423 -0
  59. package/dist/chunk-D63MUKZ6.mjs.map +1 -0
  60. package/dist/chunk-DDKW2FNA.js +390 -0
  61. package/dist/chunk-DDKW2FNA.js.map +1 -0
  62. package/dist/chunk-DQYMKR27.mjs +341 -0
  63. package/dist/chunk-DQYMKR27.mjs.map +1 -0
  64. package/dist/chunk-DW5UJKHH.js +221 -0
  65. package/dist/chunk-DW5UJKHH.js.map +1 -0
  66. package/dist/chunk-EEZCR6E6.js +50 -0
  67. package/dist/chunk-EEZCR6E6.js.map +1 -0
  68. package/dist/chunk-GCJXQ4AG.mjs +59 -0
  69. package/dist/chunk-GCJXQ4AG.mjs.map +1 -0
  70. package/dist/chunk-JGNQK2G6.mjs +14845 -0
  71. package/dist/chunk-JGNQK2G6.mjs.map +1 -0
  72. package/dist/chunk-JTLOJLWQ.mjs +563 -0
  73. package/dist/chunk-JTLOJLWQ.mjs.map +1 -0
  74. package/dist/chunk-K23A4G76.mjs +202 -0
  75. package/dist/chunk-K23A4G76.mjs.map +1 -0
  76. package/dist/chunk-KKU3K7RG.js +336 -0
  77. package/dist/chunk-KKU3K7RG.js.map +1 -0
  78. package/dist/chunk-KUGMH4ZF.js +571 -0
  79. package/dist/chunk-KUGMH4ZF.js.map +1 -0
  80. package/dist/chunk-LBVWVP72.js +110 -0
  81. package/dist/chunk-LBVWVP72.js.map +1 -0
  82. package/dist/chunk-LIVWLY2P.js +138 -0
  83. package/dist/chunk-LIVWLY2P.js.map +1 -0
  84. package/dist/chunk-M2T6R7BA.mjs +1003 -0
  85. package/dist/chunk-M2T6R7BA.mjs.map +1 -0
  86. package/dist/chunk-MV3QN7PW.mjs +47 -0
  87. package/dist/chunk-MV3QN7PW.mjs.map +1 -0
  88. package/dist/chunk-OB7E654K.js +72 -0
  89. package/dist/chunk-OB7E654K.js.map +1 -0
  90. package/dist/chunk-OIIKTGRL.mjs +380 -0
  91. package/dist/chunk-OIIKTGRL.mjs.map +1 -0
  92. package/dist/chunk-P3UWIUJS.mjs +1427 -0
  93. package/dist/chunk-P3UWIUJS.mjs.map +1 -0
  94. package/dist/chunk-PKN27UMH.mjs +136 -0
  95. package/dist/chunk-PKN27UMH.mjs.map +1 -0
  96. package/dist/chunk-QXV4667R.mjs +105 -0
  97. package/dist/chunk-QXV4667R.mjs.map +1 -0
  98. package/dist/chunk-S7FRYNSU.mjs +315 -0
  99. package/dist/chunk-S7FRYNSU.mjs.map +1 -0
  100. package/dist/chunk-TFLQX7K7.mjs +68 -0
  101. package/dist/chunk-TFLQX7K7.mjs.map +1 -0
  102. package/dist/chunk-UWE5PCYJ.mjs +279 -0
  103. package/dist/chunk-UWE5PCYJ.mjs.map +1 -0
  104. package/dist/chunk-UYFDNX2F.js +4469 -0
  105. package/dist/chunk-UYFDNX2F.js.map +1 -0
  106. package/dist/chunk-W4PALSGM.js +350 -0
  107. package/dist/chunk-W4PALSGM.js.map +1 -0
  108. package/dist/chunk-WECQ6KOB.js +1008 -0
  109. package/dist/chunk-WECQ6KOB.js.map +1 -0
  110. package/dist/chunk-XQQWI6WB.js +814 -0
  111. package/dist/chunk-XQQWI6WB.js.map +1 -0
  112. package/dist/chunk-XZJOZJB6.js +140 -0
  113. package/dist/chunk-XZJOZJB6.js.map +1 -0
  114. package/dist/chunk-ZSMWDLMK.js +63 -0
  115. package/dist/chunk-ZSMWDLMK.js.map +1 -0
  116. package/dist/cli/index.js +37243 -0
  117. package/dist/cli/index.js.map +1 -0
  118. package/dist/cli/index.mjs +37209 -0
  119. package/dist/cli/index.mjs.map +1 -0
  120. package/dist/commerce/index.d.mts +170 -0
  121. package/dist/commerce/index.d.ts +170 -0
  122. package/dist/commerce/index.js +174 -0
  123. package/dist/commerce/index.js.map +1 -0
  124. package/dist/commerce/index.mjs +5 -0
  125. package/dist/commerce/index.mjs.map +1 -0
  126. package/dist/commerce/server.d.mts +107 -0
  127. package/dist/commerce/server.d.ts +107 -0
  128. package/dist/commerce/server.js +187 -0
  129. package/dist/commerce/server.js.map +1 -0
  130. package/dist/commerce/server.mjs +177 -0
  131. package/dist/commerce/server.mjs.map +1 -0
  132. package/dist/config/index.d.mts +43 -0
  133. package/dist/config/index.d.ts +43 -0
  134. package/dist/config/index.js +66 -0
  135. package/dist/config/index.js.map +1 -0
  136. package/dist/config/index.mjs +64 -0
  137. package/dist/config/index.mjs.map +1 -0
  138. package/dist/engage/index.d.mts +33 -0
  139. package/dist/engage/index.d.ts +33 -0
  140. package/dist/engage/index.js +22 -0
  141. package/dist/engage/index.js.map +1 -0
  142. package/dist/engage/index.mjs +5 -0
  143. package/dist/engage/index.mjs.map +1 -0
  144. package/dist/forms/index.d.mts +437 -0
  145. package/dist/forms/index.d.ts +437 -0
  146. package/dist/forms/index.js +1168 -0
  147. package/dist/forms/index.js.map +1 -0
  148. package/dist/forms/index.mjs +1142 -0
  149. package/dist/forms/index.mjs.map +1 -0
  150. package/dist/generators-2XKQMPKH.mjs +4 -0
  151. package/dist/generators-2XKQMPKH.mjs.map +1 -0
  152. package/dist/generators-DTMO36DV.js +33 -0
  153. package/dist/generators-DTMO36DV.js.map +1 -0
  154. package/dist/images/index.d.mts +4 -0
  155. package/dist/images/index.d.ts +4 -0
  156. package/dist/images/index.js +46 -0
  157. package/dist/images/index.js.map +1 -0
  158. package/dist/images/index.mjs +5 -0
  159. package/dist/images/index.mjs.map +1 -0
  160. package/dist/images/server.d.mts +69 -0
  161. package/dist/images/server.d.ts +69 -0
  162. package/dist/images/server.js +21 -0
  163. package/dist/images/server.js.map +1 -0
  164. package/dist/images/server.mjs +4 -0
  165. package/dist/images/server.mjs.map +1 -0
  166. package/dist/index.d.mts +846 -0
  167. package/dist/index.d.ts +846 -0
  168. package/dist/index.js +2623 -0
  169. package/dist/index.js.map +1 -0
  170. package/dist/index.mjs +2416 -0
  171. package/dist/index.mjs.map +1 -0
  172. package/dist/layout/index.d.mts +53 -0
  173. package/dist/layout/index.d.ts +53 -0
  174. package/dist/layout/index.js +187 -0
  175. package/dist/layout/index.js.map +1 -0
  176. package/dist/layout/index.mjs +185 -0
  177. package/dist/layout/index.mjs.map +1 -0
  178. package/dist/llms/index.d.mts +448 -0
  179. package/dist/llms/index.d.ts +448 -0
  180. package/dist/llms/index.js +581 -0
  181. package/dist/llms/index.js.map +1 -0
  182. package/dist/llms/index.mjs +529 -0
  183. package/dist/llms/index.mjs.map +1 -0
  184. package/dist/manifest/index.d.mts +62 -0
  185. package/dist/manifest/index.d.ts +62 -0
  186. package/dist/manifest/index.js +85 -0
  187. package/dist/manifest/index.js.map +1 -0
  188. package/dist/manifest/index.mjs +83 -0
  189. package/dist/manifest/index.mjs.map +1 -0
  190. package/dist/middleware/index.d.mts +63 -0
  191. package/dist/middleware/index.d.ts +63 -0
  192. package/dist/middleware/index.js +54 -0
  193. package/dist/middleware/index.js.map +1 -0
  194. package/dist/middleware/index.mjs +51 -0
  195. package/dist/middleware/index.mjs.map +1 -0
  196. package/dist/migrator-2MQHOFDQ.mjs +4 -0
  197. package/dist/migrator-2MQHOFDQ.mjs.map +1 -0
  198. package/dist/migrator-THJCF6MZ.js +37 -0
  199. package/dist/migrator-THJCF6MZ.js.map +1 -0
  200. package/dist/redirects/index.d.mts +78 -0
  201. package/dist/redirects/index.d.ts +78 -0
  202. package/dist/redirects/index.js +26 -0
  203. package/dist/redirects/index.js.map +1 -0
  204. package/dist/redirects/index.mjs +5 -0
  205. package/dist/redirects/index.mjs.map +1 -0
  206. package/dist/reputation/index.d.mts +57 -0
  207. package/dist/reputation/index.d.ts +57 -0
  208. package/dist/reputation/index.js +21 -0
  209. package/dist/reputation/index.js.map +1 -0
  210. package/dist/reputation/index.mjs +4 -0
  211. package/dist/reputation/index.mjs.map +1 -0
  212. package/dist/robots/index.d.mts +38 -0
  213. package/dist/robots/index.d.ts +38 -0
  214. package/dist/robots/index.js +52 -0
  215. package/dist/robots/index.js.map +1 -0
  216. package/dist/robots/index.mjs +50 -0
  217. package/dist/robots/index.mjs.map +1 -0
  218. package/dist/routing-B5XS-6_W.d.mts +118 -0
  219. package/dist/routing-DZYzyDHw.d.ts +118 -0
  220. package/dist/scanner-GAF5PO5F.js +53 -0
  221. package/dist/scanner-GAF5PO5F.js.map +1 -0
  222. package/dist/scanner-LKJKW7IT.mjs +4 -0
  223. package/dist/scanner-LKJKW7IT.mjs.map +1 -0
  224. package/dist/securityHeaders-nwZ6nP4g.d.mts +24 -0
  225. package/dist/securityHeaders-nwZ6nP4g.d.ts +24 -0
  226. package/dist/seo/index.d.mts +600 -0
  227. package/dist/seo/index.d.ts +600 -0
  228. package/dist/seo/index.js +883 -0
  229. package/dist/seo/index.js.map +1 -0
  230. package/dist/seo/index.mjs +773 -0
  231. package/dist/seo/index.mjs.map +1 -0
  232. package/dist/seo/register-sitemap-cli.js +151 -0
  233. package/dist/seo/register-sitemap-cli.js.map +1 -0
  234. package/dist/seo/register-sitemap-cli.mjs +144 -0
  235. package/dist/seo/register-sitemap-cli.mjs.map +1 -0
  236. package/dist/seo/server.d.mts +107 -0
  237. package/dist/seo/server.d.ts +107 -0
  238. package/dist/seo/server.js +207 -0
  239. package/dist/seo/server.js.map +1 -0
  240. package/dist/seo/server.mjs +186 -0
  241. package/dist/seo/server.mjs.map +1 -0
  242. package/dist/server-api-EWXKOQZA.mjs +4 -0
  243. package/dist/server-api-EWXKOQZA.mjs.map +1 -0
  244. package/dist/server-api-GJPNRYUP.js +81 -0
  245. package/dist/server-api-GJPNRYUP.js.map +1 -0
  246. package/dist/setup/client.d.mts +60 -0
  247. package/dist/setup/client.d.ts +60 -0
  248. package/dist/setup/client.js +31 -0
  249. package/dist/setup/client.js.map +1 -0
  250. package/dist/setup/client.mjs +6 -0
  251. package/dist/setup/client.mjs.map +1 -0
  252. package/dist/setup/index.d.mts +5 -0
  253. package/dist/setup/index.d.ts +5 -0
  254. package/dist/setup/index.js +35 -0
  255. package/dist/setup/index.js.map +1 -0
  256. package/dist/setup/index.mjs +6 -0
  257. package/dist/setup/index.mjs.map +1 -0
  258. package/dist/setup/server.d.mts +14 -0
  259. package/dist/setup/server.d.ts +14 -0
  260. package/dist/setup/server.js +13 -0
  261. package/dist/setup/server.js.map +1 -0
  262. package/dist/setup/server.mjs +4 -0
  263. package/dist/setup/server.mjs.map +1 -0
  264. package/dist/site-config/index.d.mts +24 -0
  265. package/dist/site-config/index.d.ts +24 -0
  266. package/dist/site-config/index.js +17 -0
  267. package/dist/site-config/index.js.map +1 -0
  268. package/dist/site-config/index.mjs +4 -0
  269. package/dist/site-config/index.mjs.map +1 -0
  270. package/dist/sitemap/index.d.mts +96 -0
  271. package/dist/sitemap/index.d.ts +96 -0
  272. package/dist/sitemap/index.js +288 -0
  273. package/dist/sitemap/index.js.map +1 -0
  274. package/dist/sitemap/index.mjs +285 -0
  275. package/dist/sitemap/index.mjs.map +1 -0
  276. package/dist/socket-loader-J26QHHOB.js +16 -0
  277. package/dist/socket-loader-J26QHHOB.js.map +1 -0
  278. package/dist/socket-loader-R7S2YJ2J.mjs +14 -0
  279. package/dist/socket-loader-R7S2YJ2J.mjs.map +1 -0
  280. package/dist/types-0dmq3k20.d.mts +168 -0
  281. package/dist/types-0dmq3k20.d.ts +168 -0
  282. package/dist/types-Blb2QNkV.d.mts +263 -0
  283. package/dist/types-Blb2QNkV.d.ts +263 -0
  284. package/dist/types-BnCwwUX3.d.mts +250 -0
  285. package/dist/types-BnCwwUX3.d.ts +250 -0
  286. package/dist/types-CGlnp43R.d.mts +312 -0
  287. package/dist/types-CGlnp43R.d.ts +312 -0
  288. package/dist/types-D08004rU.d.mts +179 -0
  289. package/dist/types-D08004rU.d.ts +179 -0
  290. package/dist/types-DNSYU7qI.d.mts +127 -0
  291. package/dist/types-DNSYU7qI.d.ts +127 -0
  292. package/dist/types-KZP_VWZp.d.mts +266 -0
  293. package/dist/types-KZP_VWZp.d.ts +266 -0
  294. package/dist/useEventModal-BVTx69XE.d.mts +274 -0
  295. package/dist/useEventModal-Dx1dItTJ.d.ts +274 -0
  296. package/dist/web-vitals-444RLW3B.js +252 -0
  297. package/dist/web-vitals-444RLW3B.js.map +1 -0
  298. package/dist/web-vitals-KPICZIEF.mjs +241 -0
  299. package/dist/web-vitals-KPICZIEF.mjs.map +1 -0
  300. package/package.json +192 -0
@@ -0,0 +1,571 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/images/ManagedImage.tsx
8
+ var isDevMode = () => {
9
+ if (typeof window === "undefined") return false;
10
+ return process.env.NODE_ENV === "development" || window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.search.includes("uptrade_dev=true");
11
+ };
12
+ function ManagedImage({
13
+ apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,
14
+ apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
15
+ slotId,
16
+ pagePath,
17
+ alt,
18
+ className = "",
19
+ width,
20
+ height,
21
+ objectFit = "cover",
22
+ fallback,
23
+ placeholder,
24
+ onLoad,
25
+ onError,
26
+ priority,
27
+ style,
28
+ forceDevMode
29
+ }) {
30
+ const [imageData, setImageData] = react.useState(null);
31
+ const [loading, setLoading] = react.useState(true);
32
+ const [error, setError] = react.useState(null);
33
+ const [showPicker, setShowPicker] = react.useState(false);
34
+ const [devMode] = react.useState(() => forceDevMode || isDevMode());
35
+ const currentPath = pagePath ?? (typeof window !== "undefined" ? window.location.pathname : "/");
36
+ const fetchImage = react.useCallback(async () => {
37
+ if (!apiKey || !apiUrl) {
38
+ setLoading(false);
39
+ return;
40
+ }
41
+ try {
42
+ const params = new URLSearchParams({ page_path: currentPath });
43
+ const res = await fetch(
44
+ `${apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
45
+ {
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ "x-api-key": apiKey
49
+ }
50
+ }
51
+ );
52
+ if (!res.ok) {
53
+ throw new Error(`Failed to fetch image: ${res.status}`);
54
+ }
55
+ const data = await res.json();
56
+ setImageData(data.image);
57
+ setError(null);
58
+ } catch (err) {
59
+ console.error("[ManagedImage] Fetch error:", err);
60
+ setError(err instanceof Error ? err : new Error(String(err)));
61
+ onError?.(err instanceof Error ? err : new Error(String(err)));
62
+ } finally {
63
+ setLoading(false);
64
+ }
65
+ }, [apiKey, apiUrl, slotId, currentPath, onError]);
66
+ react.useEffect(() => {
67
+ fetchImage();
68
+ }, [fetchImage]);
69
+ const objectPosition = imageData ? `${imageData.focal_point_x}% ${imageData.focal_point_y}%` : "50% 50%";
70
+ const imageUrl = imageData?.public_url || imageData?.external_url || fallback;
71
+ const handleClick = (e) => {
72
+ if (devMode) {
73
+ e.preventDefault();
74
+ e.stopPropagation();
75
+ setShowPicker(true);
76
+ }
77
+ };
78
+ if (loading) {
79
+ return /* @__PURE__ */ jsxRuntime.jsx(
80
+ "div",
81
+ {
82
+ className: `bg-gray-200 animate-pulse ${className}`,
83
+ style: {
84
+ width: width ?? "100%",
85
+ height: height ?? 200,
86
+ ...style
87
+ }
88
+ }
89
+ );
90
+ }
91
+ if (!imageUrl) {
92
+ if (placeholder) {
93
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: placeholder });
94
+ }
95
+ return /* @__PURE__ */ jsxRuntime.jsxs(
96
+ "div",
97
+ {
98
+ onClick: handleClick,
99
+ className: `
100
+ bg-gray-100 border-2 border-dashed border-gray-300
101
+ flex items-center justify-center text-gray-400
102
+ ${devMode ? "cursor-pointer hover:border-blue-400 hover:text-blue-500 hover:bg-blue-50" : ""}
103
+ ${className}
104
+ `,
105
+ style: {
106
+ width: width ?? "100%",
107
+ height: height ?? 200,
108
+ ...style
109
+ },
110
+ title: devMode ? `Click to add image for slot: ${slotId}` : void 0,
111
+ children: [
112
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-4", children: [
113
+ /* @__PURE__ */ jsxRuntime.jsx(
114
+ "svg",
115
+ {
116
+ className: "w-12 h-12 mx-auto mb-2",
117
+ fill: "none",
118
+ stroke: "currentColor",
119
+ viewBox: "0 0 24 24",
120
+ children: /* @__PURE__ */ jsxRuntime.jsx(
121
+ "path",
122
+ {
123
+ strokeLinecap: "round",
124
+ strokeLinejoin: "round",
125
+ strokeWidth: 1.5,
126
+ d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
127
+ }
128
+ )
129
+ }
130
+ ),
131
+ devMode && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: "Click to add image" }),
132
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1", children: slotId })
133
+ ] }),
134
+ showPicker && /* @__PURE__ */ jsxRuntime.jsx(
135
+ ImagePickerModal,
136
+ {
137
+ slotId,
138
+ pagePath: currentPath,
139
+ config: { apiKey, apiUrl },
140
+ onClose: () => setShowPicker(false),
141
+ onSelect: () => {
142
+ setShowPicker(false);
143
+ fetchImage();
144
+ }
145
+ }
146
+ )
147
+ ]
148
+ }
149
+ );
150
+ }
151
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", style: { width, height }, children: [
152
+ /* @__PURE__ */ jsxRuntime.jsx(
153
+ "img",
154
+ {
155
+ src: imageUrl,
156
+ alt: imageData?.alt_text || alt || "",
157
+ title: imageData?.title || void 0,
158
+ "data-managed-image": "true",
159
+ "data-slot-id": slotId,
160
+ "data-page-path": currentPath,
161
+ className,
162
+ style: {
163
+ objectFit,
164
+ objectPosition,
165
+ width: width ?? "100%",
166
+ height: height ?? "auto",
167
+ ...style
168
+ },
169
+ onLoad,
170
+ onError: () => onError?.(new Error("Image failed to load")),
171
+ loading: priority ? "eager" : "lazy",
172
+ onClick: handleClick
173
+ }
174
+ ),
175
+ devMode && /* @__PURE__ */ jsxRuntime.jsx(
176
+ "div",
177
+ {
178
+ className: "absolute inset-0 bg-black/0 hover:bg-black/30 transition-colors cursor-pointer flex items-center justify-center opacity-0 hover:opacity-100",
179
+ onClick: handleClick,
180
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white/90 px-3 py-1.5 rounded-lg shadow-lg text-sm font-medium text-gray-700", children: "Edit Image" })
181
+ }
182
+ ),
183
+ showPicker && /* @__PURE__ */ jsxRuntime.jsx(
184
+ ImagePickerModal,
185
+ {
186
+ slotId,
187
+ pagePath: currentPath,
188
+ config: { apiKey, apiUrl },
189
+ currentImage: imageData,
190
+ onClose: () => setShowPicker(false),
191
+ onSelect: () => {
192
+ setShowPicker(false);
193
+ fetchImage();
194
+ }
195
+ }
196
+ )
197
+ ] });
198
+ }
199
+ function ImagePickerModal({
200
+ slotId,
201
+ pagePath,
202
+ config,
203
+ currentImage,
204
+ onClose,
205
+ onSelect
206
+ }) {
207
+ const [files, setFiles] = react.useState([]);
208
+ const [folders, setFolders] = react.useState([]);
209
+ const [currentFolder, setCurrentFolder] = react.useState("");
210
+ const [search, setSearch] = react.useState("");
211
+ const [loading, setLoading] = react.useState(true);
212
+ const [uploading, setUploading] = react.useState(false);
213
+ const [altText, setAltText] = react.useState(currentImage?.alt_text || "");
214
+ const fetchFiles = react.useCallback(async () => {
215
+ if (!config?.apiKey || !config?.apiUrl) return;
216
+ setLoading(true);
217
+ try {
218
+ const params = new URLSearchParams();
219
+ if (currentFolder) params.set("folder", currentFolder);
220
+ if (search) params.set("search", search);
221
+ const res = await fetch(
222
+ `${config.apiUrl}/public/images/files?${params}`,
223
+ {
224
+ headers: {
225
+ "Content-Type": "application/json",
226
+ "x-api-key": config.apiKey
227
+ }
228
+ }
229
+ );
230
+ if (res.ok) {
231
+ const data = await res.json();
232
+ setFiles(data.files || []);
233
+ setFolders(data.folders || []);
234
+ }
235
+ } catch (err) {
236
+ console.error("[ImagePicker] Fetch error:", err);
237
+ } finally {
238
+ setLoading(false);
239
+ }
240
+ }, [config?.apiKey, config?.apiUrl, currentFolder, search]);
241
+ react.useEffect(() => {
242
+ fetchFiles();
243
+ }, [fetchFiles]);
244
+ const handleSelectFile = async (file) => {
245
+ if (!config?.apiKey || !config?.apiUrl) return;
246
+ try {
247
+ const res = await fetch(
248
+ `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,
249
+ {
250
+ method: "POST",
251
+ headers: {
252
+ "Content-Type": "application/json",
253
+ "x-api-key": config.apiKey
254
+ },
255
+ body: JSON.stringify({
256
+ page_path: pagePath,
257
+ file_id: file.id,
258
+ alt_text: altText || file.filename
259
+ })
260
+ }
261
+ );
262
+ if (res.ok) {
263
+ onSelect();
264
+ }
265
+ } catch (err) {
266
+ console.error("[ImagePicker] Select error:", err);
267
+ }
268
+ };
269
+ const handleUpload = async (e) => {
270
+ const file = e.target.files?.[0];
271
+ if (!file || !config?.apiKey || !config?.apiUrl) return;
272
+ setUploading(true);
273
+ try {
274
+ const formData = new FormData();
275
+ formData.append("file", file);
276
+ formData.append("slot_id", slotId);
277
+ formData.append("page_path", pagePath);
278
+ formData.append("folder", currentFolder || "Website/Images");
279
+ if (altText) formData.append("alt_text", altText);
280
+ const res = await fetch(`${config.apiUrl}/public/images/upload`, {
281
+ method: "POST",
282
+ headers: {
283
+ "x-api-key": config.apiKey
284
+ },
285
+ body: formData
286
+ });
287
+ if (res.ok) {
288
+ onSelect();
289
+ }
290
+ } catch (err) {
291
+ console.error("[ImagePicker] Upload error:", err);
292
+ } finally {
293
+ setUploading(false);
294
+ }
295
+ };
296
+ const handleClear = async () => {
297
+ if (!config?.apiKey || !config?.apiUrl) return;
298
+ try {
299
+ const params = new URLSearchParams({ page_path: pagePath });
300
+ await fetch(
301
+ `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
302
+ {
303
+ method: "DELETE",
304
+ headers: {
305
+ "x-api-key": config.apiKey
306
+ }
307
+ }
308
+ );
309
+ onSelect();
310
+ } catch (err) {
311
+ console.error("[ImagePicker] Clear error:", err);
312
+ }
313
+ };
314
+ return /* @__PURE__ */ jsxRuntime.jsx(
315
+ "div",
316
+ {
317
+ className: "fixed inset-0 z-[99999] bg-black/50 flex items-center justify-center p-4",
318
+ onClick: onClose,
319
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
320
+ "div",
321
+ {
322
+ className: "bg-white rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden",
323
+ onClick: (e) => e.stopPropagation(),
324
+ children: [
325
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-4 border-b", children: [
326
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: "Select Image" }),
328
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500", children: [
329
+ "Slot: ",
330
+ slotId
331
+ ] })
332
+ ] }),
333
+ /* @__PURE__ */ jsxRuntime.jsx(
334
+ "button",
335
+ {
336
+ onClick: onClose,
337
+ className: "text-gray-400 hover:text-gray-600 p-2",
338
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
339
+ }
340
+ )
341
+ ] }),
342
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 overflow-y-auto", style: { maxHeight: "calc(90vh - 180px)" }, children: [
343
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 mb-4", children: [
344
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
345
+ "input",
346
+ {
347
+ type: "text",
348
+ placeholder: "Search images...",
349
+ value: search,
350
+ onChange: (e) => setSearch(e.target.value),
351
+ className: "w-full px-3 py-2 border rounded-lg text-sm"
352
+ }
353
+ ) }),
354
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "px-4 py-2 bg-blue-600 text-white rounded-lg cursor-pointer hover:bg-blue-700 text-sm font-medium flex items-center gap-2", children: [
355
+ uploading ? "Uploading..." : "Upload New",
356
+ /* @__PURE__ */ jsxRuntime.jsx(
357
+ "input",
358
+ {
359
+ type: "file",
360
+ accept: "image/*",
361
+ onChange: handleUpload,
362
+ disabled: uploading,
363
+ className: "hidden"
364
+ }
365
+ )
366
+ ] }),
367
+ currentImage?.file_id && /* @__PURE__ */ jsxRuntime.jsx(
368
+ "button",
369
+ {
370
+ onClick: handleClear,
371
+ className: "px-4 py-2 border border-red-200 text-red-600 rounded-lg hover:bg-red-50 text-sm",
372
+ children: "Remove"
373
+ }
374
+ )
375
+ ] }),
376
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(
377
+ "input",
378
+ {
379
+ type: "text",
380
+ placeholder: "Alt text for image...",
381
+ value: altText,
382
+ onChange: (e) => setAltText(e.target.value),
383
+ className: "w-full px-3 py-2 border rounded-lg text-sm"
384
+ }
385
+ ) }),
386
+ folders.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 mb-4 flex-wrap", children: [
387
+ /* @__PURE__ */ jsxRuntime.jsx(
388
+ "button",
389
+ {
390
+ onClick: () => setCurrentFolder(""),
391
+ className: `px-3 py-1 text-sm rounded-full ${!currentFolder ? "bg-blue-100 text-blue-700" : "bg-gray-100 hover:bg-gray-200"}`,
392
+ children: "All"
393
+ }
394
+ ),
395
+ folders.map((folder) => /* @__PURE__ */ jsxRuntime.jsx(
396
+ "button",
397
+ {
398
+ onClick: () => setCurrentFolder(folder),
399
+ className: `px-3 py-1 text-sm rounded-full ${currentFolder === folder ? "bg-blue-100 text-blue-700" : "bg-gray-100 hover:bg-gray-200"}`,
400
+ children: folder
401
+ },
402
+ folder
403
+ ))
404
+ ] }),
405
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-4", children: [...Array(8)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square bg-gray-200 rounded-lg animate-pulse" }, i)) }) : files.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12 text-gray-500", children: [
406
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No images found" }),
407
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: "Upload a new image to get started" })
408
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-4", children: files.map((file) => /* @__PURE__ */ jsxRuntime.jsx(
409
+ "button",
410
+ {
411
+ onClick: () => handleSelectFile(file),
412
+ className: `
413
+ aspect-square rounded-lg overflow-hidden border-2 transition-all
414
+ hover:border-blue-400 hover:shadow-lg
415
+ ${currentImage?.file_id === file.id ? "border-blue-500 ring-2 ring-blue-200" : "border-gray-200"}
416
+ `,
417
+ children: /* @__PURE__ */ jsxRuntime.jsx(
418
+ "img",
419
+ {
420
+ src: file.public_url,
421
+ alt: file.filename,
422
+ className: "w-full h-full object-cover"
423
+ }
424
+ )
425
+ },
426
+ file.id
427
+ )) })
428
+ ] }),
429
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border-t bg-gray-50 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
430
+ "button",
431
+ {
432
+ onClick: onClose,
433
+ className: "px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg text-sm",
434
+ children: "Cancel"
435
+ }
436
+ ) })
437
+ ]
438
+ }
439
+ )
440
+ }
441
+ );
442
+ }
443
+
444
+ // src/images/api.ts
445
+ async function fetchManagedImage(config, slotId, pagePath) {
446
+ const params = new URLSearchParams();
447
+ if (pagePath) params.set("page_path", pagePath);
448
+ const res = await fetch(
449
+ `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
450
+ {
451
+ headers: {
452
+ "Content-Type": "application/json",
453
+ "x-api-key": config.apiKey
454
+ }
455
+ }
456
+ );
457
+ if (!res.ok) {
458
+ throw new Error(`Failed to fetch image: ${res.status}`);
459
+ }
460
+ return res.json();
461
+ }
462
+ async function fetchManagedImages(config, options) {
463
+ const params = new URLSearchParams();
464
+ if (options?.pagePath) params.set("page_path", options.pagePath);
465
+ if (options?.category) params.set("category", options.category);
466
+ if (options?.includePlaceholders) params.set("include_placeholders", "true");
467
+ const res = await fetch(
468
+ `${config.apiUrl}/public/images?${params}`,
469
+ {
470
+ headers: {
471
+ "Content-Type": "application/json",
472
+ "x-api-key": config.apiKey
473
+ }
474
+ }
475
+ );
476
+ if (!res.ok) {
477
+ throw new Error(`Failed to fetch images: ${res.status}`);
478
+ }
479
+ return res.json();
480
+ }
481
+ async function listImageFiles(config, options) {
482
+ const params = new URLSearchParams();
483
+ if (options?.folder) params.set("folder", options.folder);
484
+ if (options?.search) params.set("search", options.search);
485
+ const res = await fetch(
486
+ `${config.apiUrl}/public/images/files?${params}`,
487
+ {
488
+ headers: {
489
+ "Content-Type": "application/json",
490
+ "x-api-key": config.apiKey
491
+ }
492
+ }
493
+ );
494
+ if (!res.ok) {
495
+ throw new Error(`Failed to list files: ${res.status}`);
496
+ }
497
+ return res.json();
498
+ }
499
+ async function uploadImage(config, file, options) {
500
+ const formData = new FormData();
501
+ formData.append("file", file);
502
+ if (options?.slotId) formData.append("slot_id", options.slotId);
503
+ if (options?.pagePath) formData.append("page_path", options.pagePath);
504
+ if (options?.folder) formData.append("folder", options.folder);
505
+ if (options?.altText) formData.append("alt_text", options.altText);
506
+ const res = await fetch(`${config.apiUrl}/public/images/upload`, {
507
+ method: "POST",
508
+ headers: {
509
+ "x-api-key": config.apiKey
510
+ },
511
+ body: formData
512
+ });
513
+ if (!res.ok) {
514
+ throw new Error(`Failed to upload image: ${res.status}`);
515
+ }
516
+ return res.json();
517
+ }
518
+ async function assignImageToSlot(config, slotId, options) {
519
+ const res = await fetch(
520
+ `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,
521
+ {
522
+ method: "POST",
523
+ headers: {
524
+ "Content-Type": "application/json",
525
+ "x-api-key": config.apiKey
526
+ },
527
+ body: JSON.stringify({
528
+ page_path: options.pagePath,
529
+ file_id: options.fileId,
530
+ external_url: options.externalUrl,
531
+ alt_text: options.altText,
532
+ title: options.title,
533
+ caption: options.caption,
534
+ focal_point_x: options.focalPointX,
535
+ focal_point_y: options.focalPointY,
536
+ aspect_ratio: options.aspectRatio
537
+ })
538
+ }
539
+ );
540
+ if (!res.ok) {
541
+ throw new Error(`Failed to assign image: ${res.status}`);
542
+ }
543
+ return res.json();
544
+ }
545
+ async function clearImageSlot(config, slotId, pagePath) {
546
+ const params = new URLSearchParams();
547
+ if (pagePath) params.set("page_path", pagePath);
548
+ const res = await fetch(
549
+ `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
550
+ {
551
+ method: "DELETE",
552
+ headers: {
553
+ "x-api-key": config.apiKey
554
+ }
555
+ }
556
+ );
557
+ if (!res.ok) {
558
+ throw new Error(`Failed to clear slot: ${res.status}`);
559
+ }
560
+ return res.json();
561
+ }
562
+
563
+ exports.ManagedImage = ManagedImage;
564
+ exports.assignImageToSlot = assignImageToSlot;
565
+ exports.clearImageSlot = clearImageSlot;
566
+ exports.fetchManagedImage = fetchManagedImage;
567
+ exports.fetchManagedImages = fetchManagedImages;
568
+ exports.listImageFiles = listImageFiles;
569
+ exports.uploadImage = uploadImage;
570
+ //# sourceMappingURL=chunk-KUGMH4ZF.js.map
571
+ //# sourceMappingURL=chunk-KUGMH4ZF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/images/ManagedImage.tsx","../src/images/api.ts"],"names":["useState","useCallback","useEffect","jsx","jsxs"],"mappings":"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","file":"chunk-KUGMH4ZF.js","sourcesContent":["/**\n * ManagedImage - Portal-managed image component\n * \n * Features:\n * - Fetches images from Portal API via API key (never direct Supabase)\n * - Dev mode: Click to open image picker modal\n * - Supports responsive variants\n * - Automatic focal point handling\n * - Placeholder state for empty slots\n * \n * @example\n * ```tsx\n * <ManagedImage \n * slotId=\"hero-background\"\n * alt=\"Hero background image\"\n * className=\"w-full h-96 object-cover\"\n * />\n * ```\n */\n\n'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\n\nexport interface ManagedImageData {\n id: string\n slot_id: string\n page_path: string | null\n file_id: string | null\n external_url: string | null\n alt_text: string | null\n title: string | null\n caption: string | null\n focal_point_x: number\n focal_point_y: number\n aspect_ratio: string | null\n public_url?: string\n is_placeholder: boolean\n}\n\nexport interface ImageFile {\n id: string\n filename: string\n storage_path: string\n mime_type: string\n file_size: number\n folder_path: string | null\n public_url?: string\n}\n\nexport interface ManagedImageProps {\n /** API key for Portal API */\n apiKey?: string\n /** API URL (defaults to https://api.uptrademedia.com) */\n apiUrl?: string\n /** Unique slot identifier (e.g., 'hero-background', 'about-team-1') */\n slotId: string\n /** Page path for page-specific slots (defaults to current path) */\n pagePath?: string\n /** Fallback alt text if not set in Portal */\n alt?: string\n /** CSS class names */\n className?: string\n /** Image width */\n width?: number | string\n /** Image height */\n height?: number | string\n /** CSS object-fit property */\n objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'\n /** Fallback image URL when no image assigned */\n fallback?: string\n /** Custom placeholder component */\n placeholder?: React.ReactNode\n /** Called when image loads */\n onLoad?: () => void\n /** Called on error */\n onError?: (error: Error) => void\n /** Priority loading (Next.js Image optimization) */\n priority?: boolean\n /** Additional styles */\n style?: React.CSSProperties\n /** Enable dev picker even outside dev mode */\n forceDevMode?: boolean\n}\n\n// Check if we're in dev mode\nconst isDevMode = (): boolean => {\n if (typeof window === 'undefined') return false\n return (\n process.env.NODE_ENV === 'development' ||\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.search.includes('uptrade_dev=true')\n )\n}\n\nexport function ManagedImage({\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n slotId,\n pagePath,\n alt,\n className = '',\n width,\n height,\n objectFit = 'cover',\n fallback,\n placeholder,\n onLoad,\n onError,\n priority,\n style,\n forceDevMode,\n}: ManagedImageProps) {\n const [imageData, setImageData] = useState<ManagedImageData | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n const [showPicker, setShowPicker] = useState(false)\n const [devMode] = useState(() => forceDevMode || isDevMode())\n\n // Get current page path if not provided\n const currentPath = pagePath ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n\n // Fetch image data from Portal API\n const fetchImage = useCallback(async () => {\n if (!apiKey || !apiUrl) {\n setLoading(false)\n return\n }\n\n try {\n const params = new URLSearchParams({ page_path: currentPath })\n const res = await fetch(\n `${apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,\n {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to fetch image: ${res.status}`)\n }\n\n const data = await res.json()\n setImageData(data.image)\n setError(null)\n } catch (err) {\n console.error('[ManagedImage] Fetch error:', err)\n setError(err instanceof Error ? err : new Error(String(err)))\n onError?.(err instanceof Error ? err : new Error(String(err)))\n } finally {\n setLoading(false)\n }\n }, [apiKey, apiUrl, slotId, currentPath, onError])\n\n useEffect(() => {\n fetchImage()\n }, [fetchImage])\n\n // Calculate object-position from focal point\n const objectPosition = imageData\n ? `${imageData.focal_point_x}% ${imageData.focal_point_y}%`\n : '50% 50%'\n\n // Get the image URL\n const imageUrl = imageData?.public_url || imageData?.external_url || fallback\n\n // Handle click in dev mode\n const handleClick = (e: React.MouseEvent) => {\n if (devMode) {\n e.preventDefault()\n e.stopPropagation()\n setShowPicker(true)\n }\n }\n\n // Render placeholder state\n if (loading) {\n return (\n <div\n className={`bg-gray-200 animate-pulse ${className}`}\n style={{\n width: width ?? '100%',\n height: height ?? 200,\n ...style,\n }}\n />\n )\n }\n\n // No image assigned - show placeholder or dev picker hint\n if (!imageUrl) {\n if (placeholder) {\n return <>{placeholder}</>\n }\n\n return (\n <div\n onClick={handleClick}\n className={`\n bg-gray-100 border-2 border-dashed border-gray-300 \n flex items-center justify-center text-gray-400\n ${devMode ? 'cursor-pointer hover:border-blue-400 hover:text-blue-500 hover:bg-blue-50' : ''}\n ${className}\n `}\n style={{\n width: width ?? '100%',\n height: height ?? 200,\n ...style,\n }}\n title={devMode ? `Click to add image for slot: ${slotId}` : undefined}\n >\n <div className=\"text-center p-4\">\n <svg\n className=\"w-12 h-12 mx-auto mb-2\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n {devMode && (\n <p className=\"text-sm font-medium\">Click to add image</p>\n )}\n <p className=\"text-xs mt-1\">{slotId}</p>\n </div>\n\n {showPicker && (\n <ImagePickerModal\n slotId={slotId}\n pagePath={currentPath}\n config={{ apiKey, apiUrl }}\n onClose={() => setShowPicker(false)}\n onSelect={() => {\n setShowPicker(false)\n fetchImage()\n }}\n />\n )}\n </div>\n )\n }\n\n // Render the image\n return (\n <div className=\"relative\" style={{ width, height }}>\n <img\n src={imageUrl}\n alt={imageData?.alt_text || alt || ''}\n title={imageData?.title || undefined}\n data-managed-image=\"true\"\n data-slot-id={slotId}\n data-page-path={currentPath}\n className={className}\n style={{\n objectFit,\n objectPosition,\n width: width ?? '100%',\n height: height ?? 'auto',\n ...style,\n }}\n onLoad={onLoad}\n onError={() => onError?.(new Error('Image failed to load'))}\n loading={priority ? 'eager' : 'lazy'}\n onClick={handleClick}\n />\n\n {/* Dev mode overlay */}\n {devMode && (\n <div\n className=\"absolute inset-0 bg-black/0 hover:bg-black/30 transition-colors cursor-pointer flex items-center justify-center opacity-0 hover:opacity-100\"\n onClick={handleClick}\n >\n <div className=\"bg-white/90 px-3 py-1.5 rounded-lg shadow-lg text-sm font-medium text-gray-700\">\n Edit Image\n </div>\n </div>\n )}\n\n {showPicker && (\n <ImagePickerModal\n slotId={slotId}\n pagePath={currentPath}\n config={{ apiKey, apiUrl }}\n currentImage={imageData}\n onClose={() => setShowPicker(false)}\n onSelect={() => {\n setShowPicker(false)\n fetchImage()\n }}\n />\n )}\n </div>\n )\n}\n\n// ============================================================================\n// IMAGE PICKER MODAL\n// ============================================================================\n\ninterface ImagePickerModalProps {\n slotId: string\n pagePath: string\n config: any\n currentImage?: ManagedImageData | null\n onClose: () => void\n onSelect: () => void\n}\n\nfunction ImagePickerModal({\n slotId,\n pagePath,\n config,\n currentImage,\n onClose,\n onSelect,\n}: ImagePickerModalProps) {\n const [files, setFiles] = useState<ImageFile[]>([])\n const [folders, setFolders] = useState<string[]>([])\n const [currentFolder, setCurrentFolder] = useState<string>('')\n const [search, setSearch] = useState('')\n const [loading, setLoading] = useState(true)\n const [uploading, setUploading] = useState(false)\n const [altText, setAltText] = useState(currentImage?.alt_text || '')\n\n // Fetch available files\n const fetchFiles = useCallback(async () => {\n if (!config?.apiKey || !config?.apiUrl) return\n\n setLoading(true)\n try {\n const params = new URLSearchParams()\n if (currentFolder) params.set('folder', currentFolder)\n if (search) params.set('search', search)\n\n const res = await fetch(\n `${config.apiUrl}/public/images/files?${params}`,\n {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n }\n )\n\n if (res.ok) {\n const data = await res.json()\n setFiles(data.files || [])\n setFolders(data.folders || [])\n }\n } catch (err) {\n console.error('[ImagePicker] Fetch error:', err)\n } finally {\n setLoading(false)\n }\n }, [config?.apiKey, config?.apiUrl, currentFolder, search])\n\n useEffect(() => {\n fetchFiles()\n }, [fetchFiles])\n\n // Select an existing file\n const handleSelectFile = async (file: ImageFile) => {\n if (!config?.apiKey || !config?.apiUrl) return\n\n try {\n const res = await fetch(\n `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n body: JSON.stringify({\n page_path: pagePath,\n file_id: file.id,\n alt_text: altText || file.filename,\n }),\n }\n )\n\n if (res.ok) {\n onSelect()\n }\n } catch (err) {\n console.error('[ImagePicker] Select error:', err)\n }\n }\n\n // Upload new file\n const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file || !config?.apiKey || !config?.apiUrl) return\n\n setUploading(true)\n try {\n const formData = new FormData()\n formData.append('file', file)\n formData.append('slot_id', slotId)\n formData.append('page_path', pagePath)\n formData.append('folder', currentFolder || 'Website/Images')\n if (altText) formData.append('alt_text', altText)\n\n const res = await fetch(`${config.apiUrl}/public/images/upload`, {\n method: 'POST',\n headers: {\n 'x-api-key': config.apiKey,\n },\n body: formData,\n })\n\n if (res.ok) {\n onSelect()\n }\n } catch (err) {\n console.error('[ImagePicker] Upload error:', err)\n } finally {\n setUploading(false)\n }\n }\n\n // Clear the slot\n const handleClear = async () => {\n if (!config?.apiKey || !config?.apiUrl) return\n\n try {\n const params = new URLSearchParams({ page_path: pagePath })\n await fetch(\n `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,\n {\n method: 'DELETE',\n headers: {\n 'x-api-key': config.apiKey,\n },\n }\n )\n onSelect()\n } catch (err) {\n console.error('[ImagePicker] Clear error:', err)\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-[99999] bg-black/50 flex items-center justify-center p-4\"\n onClick={onClose}\n >\n <div\n className=\"bg-white rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden\"\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-4 border-b\">\n <div>\n <h2 className=\"text-lg font-semibold\">Select Image</h2>\n <p className=\"text-sm text-gray-500\">Slot: {slotId}</p>\n </div>\n <button\n onClick={onClose}\n className=\"text-gray-400 hover:text-gray-600 p-2\"\n >\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n {/* Body */}\n <div className=\"p-4 overflow-y-auto\" style={{ maxHeight: 'calc(90vh - 180px)' }}>\n {/* Upload & Search */}\n <div className=\"flex gap-4 mb-4\">\n <div className=\"flex-1\">\n <input\n type=\"text\"\n placeholder=\"Search images...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"w-full px-3 py-2 border rounded-lg text-sm\"\n />\n </div>\n <label className=\"px-4 py-2 bg-blue-600 text-white rounded-lg cursor-pointer hover:bg-blue-700 text-sm font-medium flex items-center gap-2\">\n {uploading ? 'Uploading...' : 'Upload New'}\n <input\n type=\"file\"\n accept=\"image/*\"\n onChange={handleUpload}\n disabled={uploading}\n className=\"hidden\"\n />\n </label>\n {currentImage?.file_id && (\n <button\n onClick={handleClear}\n className=\"px-4 py-2 border border-red-200 text-red-600 rounded-lg hover:bg-red-50 text-sm\"\n >\n Remove\n </button>\n )}\n </div>\n\n {/* Alt text input */}\n <div className=\"mb-4\">\n <input\n type=\"text\"\n placeholder=\"Alt text for image...\"\n value={altText}\n onChange={(e) => setAltText(e.target.value)}\n className=\"w-full px-3 py-2 border rounded-lg text-sm\"\n />\n </div>\n\n {/* Folder navigation */}\n {folders.length > 0 && (\n <div className=\"flex gap-2 mb-4 flex-wrap\">\n <button\n onClick={() => setCurrentFolder('')}\n className={`px-3 py-1 text-sm rounded-full ${\n !currentFolder ? 'bg-blue-100 text-blue-700' : 'bg-gray-100 hover:bg-gray-200'\n }`}\n >\n All\n </button>\n {folders.map((folder) => (\n <button\n key={folder}\n onClick={() => setCurrentFolder(folder)}\n className={`px-3 py-1 text-sm rounded-full ${\n currentFolder === folder ? 'bg-blue-100 text-blue-700' : 'bg-gray-100 hover:bg-gray-200'\n }`}\n >\n {folder}\n </button>\n ))}\n </div>\n )}\n\n {/* File grid */}\n {loading ? (\n <div className=\"grid grid-cols-4 gap-4\">\n {[...Array(8)].map((_, i) => (\n <div key={i} className=\"aspect-square bg-gray-200 rounded-lg animate-pulse\" />\n ))}\n </div>\n ) : files.length === 0 ? (\n <div className=\"text-center py-12 text-gray-500\">\n <p>No images found</p>\n <p className=\"text-sm mt-1\">Upload a new image to get started</p>\n </div>\n ) : (\n <div className=\"grid grid-cols-4 gap-4\">\n {files.map((file) => (\n <button\n key={file.id}\n onClick={() => handleSelectFile(file)}\n className={`\n aspect-square rounded-lg overflow-hidden border-2 transition-all\n hover:border-blue-400 hover:shadow-lg\n ${currentImage?.file_id === file.id ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-200'}\n `}\n >\n <img\n src={file.public_url}\n alt={file.filename}\n className=\"w-full h-full object-cover\"\n />\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-4 border-t bg-gray-50 flex justify-end\">\n <button\n onClick={onClose}\n className=\"px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg text-sm\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nexport default ManagedImage\n","/**\n * Images API functions\n * \n * All functions use Portal API with API key authentication.\n * Never makes direct Supabase calls.\n */\n\nimport type { ManagedImageData, ImageFile } from './ManagedImage'\n\nexport interface ImageApiConfig {\n apiUrl: string\n apiKey: string\n}\n\n/**\n * Fetch a managed image for a specific slot\n */\nexport async function fetchManagedImage(\n config: ImageApiConfig,\n slotId: string,\n pagePath?: string,\n): Promise<{ image: ManagedImageData | null; is_placeholder: boolean }> {\n const params = new URLSearchParams()\n if (pagePath) params.set('page_path', pagePath)\n\n const res = await fetch(\n `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,\n {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to fetch image: ${res.status}`)\n }\n\n return res.json()\n}\n\n/**\n * Fetch all managed images for the project\n */\nexport async function fetchManagedImages(\n config: ImageApiConfig,\n options?: {\n pagePath?: string\n category?: string\n includePlaceholders?: boolean\n },\n): Promise<{ images: ManagedImageData[] }> {\n const params = new URLSearchParams()\n if (options?.pagePath) params.set('page_path', options.pagePath)\n if (options?.category) params.set('category', options.category)\n if (options?.includePlaceholders) params.set('include_placeholders', 'true')\n\n const res = await fetch(\n `${config.apiUrl}/public/images?${params}`,\n {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to fetch images: ${res.status}`)\n }\n\n return res.json()\n}\n\n/**\n * List available image files in the project\n */\nexport async function listImageFiles(\n config: ImageApiConfig,\n options?: {\n folder?: string\n search?: string\n },\n): Promise<{ files: ImageFile[]; folders: string[] }> {\n const params = new URLSearchParams()\n if (options?.folder) params.set('folder', options.folder)\n if (options?.search) params.set('search', options.search)\n\n const res = await fetch(\n `${config.apiUrl}/public/images/files?${params}`,\n {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to list files: ${res.status}`)\n }\n\n return res.json()\n}\n\n/**\n * Upload a new image\n */\nexport async function uploadImage(\n config: ImageApiConfig,\n file: File,\n options?: {\n slotId?: string\n pagePath?: string\n folder?: string\n altText?: string\n },\n): Promise<{ file: ImageFile; image?: ManagedImageData }> {\n const formData = new FormData()\n formData.append('file', file)\n if (options?.slotId) formData.append('slot_id', options.slotId)\n if (options?.pagePath) formData.append('page_path', options.pagePath)\n if (options?.folder) formData.append('folder', options.folder)\n if (options?.altText) formData.append('alt_text', options.altText)\n\n const res = await fetch(`${config.apiUrl}/public/images/upload`, {\n method: 'POST',\n headers: {\n 'x-api-key': config.apiKey,\n },\n body: formData,\n })\n\n if (!res.ok) {\n throw new Error(`Failed to upload image: ${res.status}`)\n }\n\n return res.json()\n}\n\n/**\n * Assign an existing file to an image slot\n */\nexport async function assignImageToSlot(\n config: ImageApiConfig,\n slotId: string,\n options: {\n fileId?: string\n externalUrl?: string\n pagePath?: string\n altText?: string\n title?: string\n caption?: string\n focalPointX?: number\n focalPointY?: number\n aspectRatio?: string\n },\n): Promise<{ image: ManagedImageData }> {\n const res = await fetch(\n `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n },\n body: JSON.stringify({\n page_path: options.pagePath,\n file_id: options.fileId,\n external_url: options.externalUrl,\n alt_text: options.altText,\n title: options.title,\n caption: options.caption,\n focal_point_x: options.focalPointX,\n focal_point_y: options.focalPointY,\n aspect_ratio: options.aspectRatio,\n }),\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to assign image: ${res.status}`)\n }\n\n return res.json()\n}\n\n/**\n * Clear an image from a slot (keeps the file)\n */\nexport async function clearImageSlot(\n config: ImageApiConfig,\n slotId: string,\n pagePath?: string,\n): Promise<{ success: boolean }> {\n const params = new URLSearchParams()\n if (pagePath) params.set('page_path', pagePath)\n\n const res = await fetch(\n `${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,\n {\n method: 'DELETE',\n headers: {\n 'x-api-key': config.apiKey,\n },\n }\n )\n\n if (!res.ok) {\n throw new Error(`Failed to clear slot: ${res.status}`)\n }\n\n return res.json()\n}\n"]}