@squaredr/fieldcraft-pro 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,13 +1,283 @@
1
- export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, cleanPreset, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, squaredrDarkPreset, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo } from './chunk-BYT2P3I6.mjs';
2
- export { ResponseViewer } from './chunk-BK4SMEW4.mjs';
1
+ export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo } from './chunk-ANBMT5AW.mjs';
2
+ export { ResponseViewer } from './chunk-5LKGCZAW.mjs';
3
3
  export { cn } from './chunk-QQ4JZGTD.mjs';
4
- export { PREVIEW_SCHEMA, ThemeEditor, ThemeEditorInner } from './chunk-22T5PY6L.mjs';
4
+ export { PRESET_FAMILIES, PREVIEW_SCHEMA, ThemeEditor, ThemeEditorInner, ThemeEditorThemeProvider, resolveThemeFromDOM, themeEditorDarkPreset, themeEditorLightPreset, useEditorTheme } from './chunk-IKGS2ZXV.mjs';
5
5
  export { FieldCraftProProvider, UnlicensedOverlay, isProductionEnvironment, requireLicense, useLicense, validateLicense } from './chunk-VECQKSWS.mjs';
6
+ import { useState, useEffect, Suspense } from 'react';
7
+ import { FieldWrapper } from '@squaredr/fieldcraft-react';
8
+ import { jsx, jsxs } from 'react/jsx-runtime';
9
+
10
+ function formatCurrency(amount, currency) {
11
+ if (amount == null) return "";
12
+ try {
13
+ return new Intl.NumberFormat(void 0, {
14
+ style: "currency",
15
+ currency: currency ?? "USD"
16
+ }).format(amount);
17
+ } catch {
18
+ return `${currency ?? "USD"} ${amount.toFixed(2)}`;
19
+ }
20
+ }
21
+ function ProPaymentField(props) {
22
+ const { field, value, error, touched, disabled, readonly, onChange, onBlur, customProps } = props;
23
+ const config = field.config;
24
+ const current = value ?? { status: "pending" };
25
+ const provider = config?.provider ?? "stripe";
26
+ const publicKey = config?.publicKey;
27
+ const amount = customProps?.amount ?? config?.amount;
28
+ const currency = config?.currency ?? "USD";
29
+ const directSecret = customProps?.clientSecret;
30
+ const onCreateIntent = customProps?.onCreatePaymentIntent;
31
+ const onPaymentComplete = customProps?.onPaymentComplete;
32
+ const serverUrl = config?.serverUrl;
33
+ const [clientSecret, setClientSecret] = useState(directSecret);
34
+ const [intentLoading, setIntentLoading] = useState(false);
35
+ const [intentError, setIntentError] = useState(null);
36
+ const mode = directSecret ? "direct" : onCreateIntent ? "callback" : serverUrl ? "url" : "setup";
37
+ useEffect(() => {
38
+ if (directSecret) {
39
+ setClientSecret(directSecret);
40
+ return;
41
+ }
42
+ if (mode === "setup" || !amount || !publicKey) return;
43
+ if (clientSecret) return;
44
+ const fetchIntent = async () => {
45
+ setIntentLoading(true);
46
+ setIntentError(null);
47
+ try {
48
+ if (mode === "callback" && onCreateIntent) {
49
+ const result = await onCreateIntent({ amount, currency, provider, metadata: { fieldId: field.id } });
50
+ setClientSecret(result.clientSecret);
51
+ } else if (mode === "url" && serverUrl) {
52
+ const res = await fetch(serverUrl, {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ body: JSON.stringify({ amount, currency, provider, metadata: { fieldId: field.id } })
56
+ });
57
+ if (!res.ok) throw new Error(`Server error (${res.status})`);
58
+ const data = await res.json();
59
+ setClientSecret(data.clientSecret);
60
+ }
61
+ } catch (err) {
62
+ setIntentError(err instanceof Error ? err.message : "Failed to create payment intent");
63
+ } finally {
64
+ setIntentLoading(false);
65
+ }
66
+ };
67
+ fetchIntent();
68
+ }, [mode, amount, currency, provider, publicKey, directSecret, clientSecret, onCreateIntent, serverUrl, field.id]);
69
+ const handleSuccess = (chargeId) => {
70
+ const result = { status: "succeeded", chargeId };
71
+ onChange(result);
72
+ onPaymentComplete?.(result);
73
+ onBlur();
74
+ };
75
+ const handleError = (message) => {
76
+ const result = { status: "failed", error: message };
77
+ onChange(result);
78
+ onPaymentComplete?.(result);
79
+ onBlur();
80
+ };
81
+ if (!publicKey) {
82
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border-2 border-dashed border-input p-4 text-center", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
83
+ "Payment field \u2014 configure a ",
84
+ /* @__PURE__ */ jsx("code", { className: "bg-muted px-1 rounded text-xs", children: "publicKey" }),
85
+ " in the field properties panel."
86
+ ] }) }) });
87
+ }
88
+ if (provider !== "stripe") {
89
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-input bg-muted/50 p-4", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
90
+ 'Provider "',
91
+ provider,
92
+ '" \u2014 coming soon.'
93
+ ] }) }) });
94
+ }
95
+ if (current.status === "succeeded") {
96
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-input p-4", children: [
97
+ amount != null && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mb-2", children: [
98
+ formatCurrency(amount, currency),
99
+ config?.description && /* @__PURE__ */ jsxs("span", { children: [
100
+ " \u2014 ",
101
+ config.description
102
+ ] })
103
+ ] }),
104
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", style: { color: "var(--success, #22c55e)" }, children: [
105
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, width: 16, height: 16, children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) }),
106
+ /* @__PURE__ */ jsxs("span", { children: [
107
+ "Payment successful",
108
+ current.chargeId ? ` (${current.chargeId})` : ""
109
+ ] })
110
+ ] })
111
+ ] }) });
112
+ }
113
+ if (current.status === "failed") {
114
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/50 p-4", children: [
115
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-destructive font-medium", children: [
116
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, width: 16, height: 16, children: [
117
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
118
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
119
+ ] }),
120
+ /* @__PURE__ */ jsxs("span", { children: [
121
+ "Payment failed",
122
+ current.error ? `: ${current.error}` : ""
123
+ ] })
124
+ ] }),
125
+ /* @__PURE__ */ jsx(
126
+ "button",
127
+ {
128
+ type: "button",
129
+ className: "mt-2 text-xs text-primary hover:underline",
130
+ onClick: () => {
131
+ onChange({ status: "pending" });
132
+ setClientSecret(void 0);
133
+ },
134
+ children: "Retry"
135
+ }
136
+ )
137
+ ] }) });
138
+ }
139
+ if (intentLoading || mode !== "setup" && mode !== "direct" && !clientSecret) {
140
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-input p-4", children: [
141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
142
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Payment" }),
143
+ /* @__PURE__ */ jsx("span", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: provider })
144
+ ] }),
145
+ amount != null && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: [
146
+ "Amount: ",
147
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatCurrency(amount, currency) }),
148
+ config?.description && /* @__PURE__ */ jsxs("span", { children: [
149
+ " \u2014 ",
150
+ config.description
151
+ ] })
152
+ ] }),
153
+ intentError ? /* @__PURE__ */ jsxs("div", { children: [
154
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: intentError }),
155
+ /* @__PURE__ */ jsx(
156
+ "button",
157
+ {
158
+ type: "button",
159
+ className: "mt-2 text-xs text-primary hover:underline",
160
+ onClick: () => {
161
+ setIntentError(null);
162
+ setClientSecret(void 0);
163
+ },
164
+ children: "Retry"
165
+ }
166
+ )
167
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
168
+ /* @__PURE__ */ jsx("svg", { className: "animate-spin size-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeDasharray: "60", strokeDashoffset: "20" }) }),
169
+ /* @__PURE__ */ jsx("span", { children: "Preparing payment..." })
170
+ ] })
171
+ ] }) });
172
+ }
173
+ if (!clientSecret) {
174
+ const waitingForAmount = config?.amountField && !amount;
175
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-input p-4", children: [
176
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
177
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Payment" }),
178
+ /* @__PURE__ */ jsx("span", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: provider })
179
+ ] }),
180
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: waitingForAmount ? "Select an option above to proceed with payment." : "Provide a clientSecret via customProps, an onCreatePaymentIntent callback, or a serverUrl." })
181
+ ] }) });
182
+ }
183
+ return /* @__PURE__ */ jsx(FieldWrapper, { field, error, touched, children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-input p-4", children: [
184
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
185
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Payment" }),
186
+ /* @__PURE__ */ jsx("span", { className: "text-xs bg-muted px-2 py-0.5 rounded", children: provider })
187
+ ] }),
188
+ amount != null && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: [
189
+ "Amount: ",
190
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: formatCurrency(amount, currency) }),
191
+ config?.description && /* @__PURE__ */ jsxs("span", { children: [
192
+ " \u2014 ",
193
+ config.description
194
+ ] })
195
+ ] }),
196
+ /* @__PURE__ */ jsx(
197
+ Suspense,
198
+ {
199
+ fallback: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground py-4", children: [
200
+ /* @__PURE__ */ jsx("svg", { className: "animate-spin size-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeDasharray: "60", strokeDashoffset: "20" }) }),
201
+ "Loading payment form..."
202
+ ] }),
203
+ children: /* @__PURE__ */ jsx(
204
+ PayKitCheckout,
205
+ {
206
+ publicKey,
207
+ clientSecret,
208
+ amount,
209
+ currency,
210
+ disabled: disabled || readonly,
211
+ onSuccess: handleSuccess,
212
+ onError: handleError
213
+ }
214
+ )
215
+ }
216
+ )
217
+ ] }) });
218
+ }
219
+ function PayKitCheckout({
220
+ publicKey,
221
+ clientSecret,
222
+ amount,
223
+ currency,
224
+ disabled,
225
+ onSuccess,
226
+ onError
227
+ }) {
228
+ const [PayKit, setPayKit] = useState(null);
229
+ const [loadError, setLoadError] = useState(null);
230
+ useEffect(() => {
231
+ let cancelled = false;
232
+ Promise.all([
233
+ // @ts-expect-error — optional peer dep, resolved at runtime
234
+ import('@squaredr/paykit-react'),
235
+ // @ts-expect-error — optional peer dep, resolved at runtime
236
+ import('@squaredr/paykit-stripe/client')
237
+ ]).then(([paykit, stripe]) => {
238
+ if (cancelled) return;
239
+ setPayKit({
240
+ Provider: paykit.PayKitProvider,
241
+ Form: paykit.CheckoutForm,
242
+ adapter: new stripe.StripeClientAdapter(publicKey)
243
+ });
244
+ }).catch((err) => {
245
+ if (cancelled) return;
246
+ setLoadError(
247
+ `Failed to load payment SDK. Ensure @squaredr/paykit-react and @squaredr/paykit-stripe are installed. (${err.message})`
248
+ );
249
+ });
250
+ return () => {
251
+ cancelled = true;
252
+ };
253
+ }, [publicKey]);
254
+ if (loadError) {
255
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: loadError });
256
+ }
257
+ if (!PayKit) {
258
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground py-2", children: [
259
+ /* @__PURE__ */ jsx("svg", { className: "animate-spin size-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeDasharray: "60", strokeDashoffset: "20" }) }),
260
+ "Loading Stripe..."
261
+ ] });
262
+ }
263
+ return /* @__PURE__ */ jsx(PayKit.Provider, { clientAdapter: PayKit.adapter, children: /* @__PURE__ */ jsx(
264
+ PayKit.Form,
265
+ {
266
+ clientSecret,
267
+ submitLabel: disabled ? "Payment disabled" : `Pay ${formatCurrency(amount, currency)}`,
268
+ onSuccess: (result) => onSuccess(result.chargeId),
269
+ onError: (err) => onError(err.message)
270
+ }
271
+ ) });
272
+ }
273
+ var PRO_FIELD_OVERRIDES = {
274
+ payment: ProPaymentField
275
+ };
6
276
 
7
277
  // src/index.ts
8
278
  if (typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV !== "production") {
9
279
  const _fc_banner = `
10
- %c FieldCraft Pro %c v1.4.0
280
+ %c FieldCraft Pro %c v1.6.0
11
281
 
12
282
  %cForm Builder \xB7 Response Viewer \xB7 Theme Editor
13
283
 
@@ -24,3 +294,5 @@ Need a license? \u2192 https://squaredr.tech/products/fieldcraft/admin-pro#prici
24
294
  "color:#6b7280"
25
295
  );
26
296
  }
297
+
298
+ export { PRO_FIELD_OVERRIDES, ProPaymentField };
@@ -2073,6 +2073,7 @@ function ResponseViewerInner({
2073
2073
  return /* @__PURE__ */ jsxRuntime.jsxs(
2074
2074
  "div",
2075
2075
  {
2076
+ "data-fcrv-root": "",
2076
2077
  className: "flex flex-col border border-border rounded-lg overflow-hidden bg-background text-foreground",
2077
2078
  style: { height: heightValue, width: widthValue },
2078
2079
  children: [
@@ -1,3 +1,3 @@
1
- export { ResponseViewer } from '../chunk-BK4SMEW4.mjs';
1
+ export { ResponseViewer } from '../chunk-5LKGCZAW.mjs';
2
2
  import '../chunk-QQ4JZGTD.mjs';
3
3
  import '../chunk-VECQKSWS.mjs';