@flamingo-stack/openframe-frontend-core 0.0.671 → 0.0.672-snapshot.20260928032919
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/chat-protocol/card-marker.d.ts +12 -0
- package/dist/chat-protocol/card-marker.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +17 -0
- package/dist/chat-protocol/index.cjs.map +1 -1
- package/dist/chat-protocol/index.d.ts +1 -0
- package/dist/chat-protocol/index.d.ts.map +1 -1
- package/dist/chat-protocol/index.js +15 -1
- package/dist/chat-protocol/index.js.map +1 -1
- package/dist/{chunk-4PVY4JJN.js → chunk-2TQ64TG3.js} +3 -3
- package/dist/{chunk-FAJUQKWQ.cjs → chunk-3EIB5PNM.cjs} +76 -75
- package/dist/chunk-3EIB5PNM.cjs.map +1 -0
- package/dist/{chunk-FUXUJSPP.cjs → chunk-3UDBK2YG.cjs} +1 -1
- package/dist/chunk-3UDBK2YG.cjs.map +1 -0
- package/dist/{chunk-G64BNLDO.js → chunk-3X6WFYSV.js} +2 -2
- package/dist/{chunk-DUZEFACP.js → chunk-4UAIOXYC.js} +3 -3
- package/dist/{chunk-SNNE34OP.js → chunk-5KD5SSP6.js} +5 -5
- package/dist/{chunk-RKUCK6MN.js → chunk-6NGIPAQ6.js} +2 -2
- package/dist/{chunk-OG2BNJ2Q.cjs → chunk-6PPEG2N4.cjs} +389 -375
- package/dist/chunk-6PPEG2N4.cjs.map +1 -0
- package/dist/{chunk-NIYIK32F.js → chunk-6ZX6TFSS.js} +18 -4
- package/dist/{chunk-NIYIK32F.js.map → chunk-6ZX6TFSS.js.map} +1 -1
- package/dist/{chunk-6C6UUOH4.js → chunk-A77HP3N6.js} +8 -7
- package/dist/{chunk-6C6UUOH4.js.map → chunk-A77HP3N6.js.map} +1 -1
- package/dist/{chunk-YNGAJGYB.cjs → chunk-AXMNWMAU.cjs} +340 -6
- package/dist/chunk-AXMNWMAU.cjs.map +1 -0
- package/dist/{chunk-PLG6LP7E.cjs → chunk-BAEHCDPI.cjs} +49 -49
- package/dist/{chunk-PLG6LP7E.cjs.map → chunk-BAEHCDPI.cjs.map} +1 -1
- package/dist/{chunk-R22IA7MP.js → chunk-BBDEIE56.js} +4 -4
- package/dist/{chunk-PQ7YC5Y6.js → chunk-D5HO3RNF.js} +3 -3
- package/dist/{chunk-II66XWU5.cjs → chunk-D6IOMPAH.cjs} +3 -3
- package/dist/{chunk-II66XWU5.cjs.map → chunk-D6IOMPAH.cjs.map} +1 -1
- package/dist/{chunk-DAGKBFZN.cjs → chunk-EQIBWG5U.cjs} +33 -33
- package/dist/{chunk-DAGKBFZN.cjs.map → chunk-EQIBWG5U.cjs.map} +1 -1
- package/dist/{chunk-3PVOF3IB.js → chunk-EVB3EO5S.js} +337 -3
- package/dist/{chunk-3PVOF3IB.js.map → chunk-EVB3EO5S.js.map} +1 -1
- package/dist/{chunk-SJZPKIW6.cjs → chunk-G64BDYU5.cjs} +12 -12
- package/dist/{chunk-SJZPKIW6.cjs.map → chunk-G64BDYU5.cjs.map} +1 -1
- package/dist/{chunk-BP542DGQ.cjs → chunk-GLPGYU6H.cjs} +55 -28
- package/dist/chunk-GLPGYU6H.cjs.map +1 -0
- package/dist/{chunk-TLLTL6LB.js → chunk-JD4U7QMG.js} +1 -1
- package/dist/chunk-JD4U7QMG.js.map +1 -0
- package/dist/{chunk-ZRIVCLPR.js → chunk-JD5TKRCU.js} +1 -1
- package/dist/chunk-JD5TKRCU.js.map +1 -0
- package/dist/{chunk-YJWR7D6O.cjs → chunk-JKIM7U7N.cjs} +3 -3
- package/dist/{chunk-YJWR7D6O.cjs.map → chunk-JKIM7U7N.cjs.map} +1 -1
- package/dist/{chunk-K3O4D7LW.js → chunk-KUASHD7Z.js} +4 -4
- package/dist/{chunk-GF5TSQ6C.cjs → chunk-KXL534EV.cjs} +10 -10
- package/dist/{chunk-GF5TSQ6C.cjs.map → chunk-KXL534EV.cjs.map} +1 -1
- package/dist/{chunk-FHTNCWIP.js → chunk-KYVFU7VQ.js} +5 -5
- package/dist/{chunk-OYMOG544.js → chunk-KZAT6VWE.js} +5 -5
- package/dist/{chunk-D55IENZJ.cjs → chunk-LCMT2GZK.cjs} +1 -1
- package/dist/chunk-LCMT2GZK.cjs.map +1 -0
- package/dist/{chunk-IJIIL6WU.js → chunk-MVU7HF5A.js} +6 -6
- package/dist/{chunk-XGXHHSDU.cjs → chunk-NYDFVUMH.cjs} +22 -22
- package/dist/{chunk-XGXHHSDU.cjs.map → chunk-NYDFVUMH.cjs.map} +1 -1
- package/dist/{chunk-STLBB6PF.js → chunk-O75Y5O3U.js} +35 -8
- package/dist/chunk-O75Y5O3U.js.map +1 -0
- package/dist/{chunk-DAWYS25M.cjs → chunk-PDNGWDJF.cjs} +17 -17
- package/dist/{chunk-DAWYS25M.cjs.map → chunk-PDNGWDJF.cjs.map} +1 -1
- package/dist/{chunk-T3ACDOVD.cjs → chunk-VRZZCL4R.cjs} +7 -7
- package/dist/{chunk-T3ACDOVD.cjs.map → chunk-VRZZCL4R.cjs.map} +1 -1
- package/dist/{chunk-XH4MRTZM.cjs → chunk-WQFT7GOC.cjs} +5 -5
- package/dist/{chunk-XH4MRTZM.cjs.map → chunk-WQFT7GOC.cjs.map} +1 -1
- package/dist/{chunk-YIDNSRPL.cjs → chunk-XGBFQ3YV.cjs} +31 -31
- package/dist/{chunk-YIDNSRPL.cjs.map → chunk-XGBFQ3YV.cjs.map} +1 -1
- package/dist/{chunk-UH3BL2OT.js → chunk-Y7TZHPS2.js} +2 -2
- package/dist/{chunk-EIAVE6SE.cjs → chunk-YKKW67A3.cjs} +10 -10
- package/dist/{chunk-EIAVE6SE.cjs.map → chunk-YKKW67A3.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +18 -17
- package/dist/components/case-studies/index.cjs.map +1 -1
- package/dist/components/case-studies/index.js +7 -6
- package/dist/components/case-studies/index.js.map +1 -1
- package/dist/components/case-studies/share-experience-section.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/contact-form.d.ts +6 -1
- package/dist/components/contact/contact-form.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/docs/index.cjs +9 -9
- package/dist/components/docs/index.js +8 -8
- package/dist/components/embeds/index.cjs +6 -6
- package/dist/components/embeds/index.js +5 -5
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/features/waitlist-form.d.ts +3 -1
- package/dist/components/features/waitlist-form.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +77 -76
- package/dist/components/help-center-pages/index.cjs.map +1 -1
- package/dist/components/help-center-pages/index.js +17 -16
- package/dist/components/help-center-pages/index.js.map +1 -1
- package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
- package/dist/components/index.cjs +43 -43
- package/dist/components/index.js +15 -15
- package/dist/components/meeting-scheduler/booking-form.d.ts +4 -1
- package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +73 -39
- package/dist/components/meeting-scheduler/index.cjs.map +1 -1
- package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.js +44 -10
- package/dist/components/meeting-scheduler/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +8 -8
- package/dist/components/navigation/index.js +7 -7
- package/dist/components/onboarding-guides/index.cjs +8 -8
- package/dist/components/onboarding-guides/index.js +7 -7
- package/dist/components/tickets/help-center-create-form.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +9 -9
- package/dist/components/tickets/index.js +8 -8
- package/dist/components/ui/index.cjs +5 -5
- package/dist/components/ui/index.js +4 -4
- package/dist/contexts/endpoints-runtime-context.d.ts +5 -0
- package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +2 -2
- package/dist/contexts/index.js +1 -1
- package/dist/hooks/index.cjs +5 -3
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -2
- package/dist/hooks/use-form-rescue.d.ts +37 -0
- package/dist/hooks/use-form-rescue.d.ts.map +1 -0
- package/dist/index.cjs +51 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +50 -8
- package/dist/index.js.map +1 -1
- package/dist/utils/form-rescue.cjs +117 -0
- package/dist/utils/form-rescue.cjs.map +1 -0
- package/dist/utils/form-rescue.d.ts +98 -0
- package/dist/utils/form-rescue.d.ts.map +1 -0
- package/dist/utils/form-rescue.js +96 -0
- package/dist/utils/form-rescue.js.map +1 -0
- package/dist/utils/index.cjs +113 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +94 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +7 -1
- package/src/chat-protocol/__tests__/card-marker.test.ts +27 -1
- package/src/chat-protocol/card-marker.ts +24 -0
- package/src/chat-protocol/index.ts +3 -0
- package/src/components/case-studies/share-experience-section.tsx +1 -0
- package/src/components/contact/contact-form.tsx +38 -2
- package/src/components/features/waitlist-form.tsx +23 -2
- package/src/components/help-center-pages/trust-center-sections.tsx +1 -0
- package/src/components/meeting-scheduler/booking-form.tsx +14 -0
- package/src/components/meeting-scheduler/index.tsx +33 -3
- package/src/components/tickets/help-center-create-form.tsx +2 -0
- package/src/contexts/endpoints-runtime-context.tsx +5 -0
- package/src/hooks/__tests__/use-form-rescue.test.tsx +182 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/use-form-rescue.ts +315 -0
- package/src/utils/__tests__/form-rescue.test.ts +106 -0
- package/src/utils/form-rescue.ts +190 -0
- package/src/utils/index.ts +5 -0
- package/dist/chunk-BP542DGQ.cjs.map +0 -1
- package/dist/chunk-D55IENZJ.cjs.map +0 -1
- package/dist/chunk-FAJUQKWQ.cjs.map +0 -1
- package/dist/chunk-FUXUJSPP.cjs.map +0 -1
- package/dist/chunk-OG2BNJ2Q.cjs.map +0 -1
- package/dist/chunk-STLBB6PF.js.map +0 -1
- package/dist/chunk-TLLTL6LB.js.map +0 -1
- package/dist/chunk-YNGAJGYB.cjs.map +0 -1
- package/dist/chunk-ZRIVCLPR.js.map +0 -1
- /package/dist/{chunk-4PVY4JJN.js.map → chunk-2TQ64TG3.js.map} +0 -0
- /package/dist/{chunk-G64BNLDO.js.map → chunk-3X6WFYSV.js.map} +0 -0
- /package/dist/{chunk-DUZEFACP.js.map → chunk-4UAIOXYC.js.map} +0 -0
- /package/dist/{chunk-SNNE34OP.js.map → chunk-5KD5SSP6.js.map} +0 -0
- /package/dist/{chunk-RKUCK6MN.js.map → chunk-6NGIPAQ6.js.map} +0 -0
- /package/dist/{chunk-R22IA7MP.js.map → chunk-BBDEIE56.js.map} +0 -0
- /package/dist/{chunk-PQ7YC5Y6.js.map → chunk-D5HO3RNF.js.map} +0 -0
- /package/dist/{chunk-K3O4D7LW.js.map → chunk-KUASHD7Z.js.map} +0 -0
- /package/dist/{chunk-FHTNCWIP.js.map → chunk-KYVFU7VQ.js.map} +0 -0
- /package/dist/{chunk-OYMOG544.js.map → chunk-KZAT6VWE.js.map} +0 -0
- /package/dist/{chunk-IJIIL6WU.js.map → chunk-MVU7HF5A.js.map} +0 -0
- /package/dist/{chunk-UH3BL2OT.js.map → chunk-Y7TZHPS2.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
useEndpointsRuntime,
|
|
4
4
|
useRequiredEndpointsRuntime
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JD5TKRCU.js";
|
|
6
6
|
import {
|
|
7
7
|
contentFetch,
|
|
8
8
|
createSearchParams,
|
|
@@ -3059,6 +3059,319 @@ function useHumanitySignals() {
|
|
|
3059
3059
|
return { honeypotInputProps: { ref, name: HONEYPOT_FIELD }, getSignals, resetSignals };
|
|
3060
3060
|
}
|
|
3061
3061
|
|
|
3062
|
+
// src/hooks/use-form-rescue.ts
|
|
3063
|
+
import { useCallback as useCallback14, useEffect as useEffect20, useMemo as useMemo7, useRef as useRef14 } from "react";
|
|
3064
|
+
|
|
3065
|
+
// src/utils/form-rescue.ts
|
|
3066
|
+
var FORM_RESCUE_FORM_IDS = [
|
|
3067
|
+
"contact",
|
|
3068
|
+
"case_study_pitch",
|
|
3069
|
+
"data_room_request",
|
|
3070
|
+
"trust_center_request",
|
|
3071
|
+
"tmcg_join",
|
|
3072
|
+
"meeting_booking",
|
|
3073
|
+
"waitlist"
|
|
3074
|
+
];
|
|
3075
|
+
function isFormRescueFormId(value) {
|
|
3076
|
+
return typeof value === "string" && FORM_RESCUE_FORM_IDS.includes(value);
|
|
3077
|
+
}
|
|
3078
|
+
var FORM_RESCUE_VALUE_FIELDS = [
|
|
3079
|
+
"email",
|
|
3080
|
+
"name",
|
|
3081
|
+
"firstName",
|
|
3082
|
+
"lastName",
|
|
3083
|
+
"company",
|
|
3084
|
+
"jobtitle",
|
|
3085
|
+
"companySize",
|
|
3086
|
+
"linkedin_url"
|
|
3087
|
+
];
|
|
3088
|
+
var EXCLUDED_FIELD_PATTERN = /pass(word|code)?|card|cvv|cvc|ssn|social.?security|iban|bank|routing|token|secret|otp|pin$/i;
|
|
3089
|
+
function isExcludedRescueField(name) {
|
|
3090
|
+
return EXCLUDED_FIELD_PATTERN.test(name);
|
|
3091
|
+
}
|
|
3092
|
+
var FORM_RESCUE_MAX_VALUE_CHARS = 200;
|
|
3093
|
+
var FORM_RESCUE_DEBOUNCE_MS = 1500;
|
|
3094
|
+
var FORM_RESCUE_MAX_FIELDS = 40;
|
|
3095
|
+
var FORM_RESCUE_ATTEMPT_FIELD = "form_attempt_id";
|
|
3096
|
+
var FORM_RESCUE_RESUME_FIELD = "form_resume_token";
|
|
3097
|
+
var FORM_RESCUE_SUBMIT_KEYS = [FORM_RESCUE_ATTEMPT_FIELD, FORM_RESCUE_RESUME_FIELD];
|
|
3098
|
+
var FORM_RESCUE_RESUME_PARAM = "resume";
|
|
3099
|
+
var FORM_DRAFT_STATUSES = ["started", "rescuable", "rescued", "submitted"];
|
|
3100
|
+
var EMAIL_PATTERN = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
|
|
3101
|
+
function isRescueEmail(value) {
|
|
3102
|
+
return typeof value === "string" && value.length <= FORM_RESCUE_MAX_VALUE_CHARS && EMAIL_PATTERN.test(value.trim());
|
|
3103
|
+
}
|
|
3104
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3105
|
+
function isFormAttemptId(value) {
|
|
3106
|
+
return typeof value === "string" && UUID_PATTERN.test(value);
|
|
3107
|
+
}
|
|
3108
|
+
function isFilled(value) {
|
|
3109
|
+
if (value == null) return false;
|
|
3110
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
3111
|
+
if (typeof value === "boolean") return value;
|
|
3112
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
3113
|
+
if (typeof value === "object") return Object.values(value).some(isFilled);
|
|
3114
|
+
return true;
|
|
3115
|
+
}
|
|
3116
|
+
function sanitizeRescueValues(values) {
|
|
3117
|
+
const out = {};
|
|
3118
|
+
if (!values || typeof values !== "object") return out;
|
|
3119
|
+
for (const key of FORM_RESCUE_VALUE_FIELDS) {
|
|
3120
|
+
if (isExcludedRescueField(key)) continue;
|
|
3121
|
+
const raw = values[key];
|
|
3122
|
+
if (typeof raw !== "string") continue;
|
|
3123
|
+
const trimmed = raw.trim().slice(0, FORM_RESCUE_MAX_VALUE_CHARS);
|
|
3124
|
+
if (trimmed) out[key] = trimmed;
|
|
3125
|
+
}
|
|
3126
|
+
return out;
|
|
3127
|
+
}
|
|
3128
|
+
function filledRescueFields(values, fieldNames) {
|
|
3129
|
+
return fieldNames.filter((name) => !isExcludedRescueField(name) && isFilled(values[name])).slice(0, FORM_RESCUE_MAX_FIELDS);
|
|
3130
|
+
}
|
|
3131
|
+
function rescueCompletionPct(filledCount, totalCount) {
|
|
3132
|
+
if (totalCount <= 0) return 0;
|
|
3133
|
+
return Math.max(0, Math.min(100, Math.round(filledCount / totalCount * 100)));
|
|
3134
|
+
}
|
|
3135
|
+
function sanitizeRescueFieldName(value) {
|
|
3136
|
+
if (typeof value !== "string") return null;
|
|
3137
|
+
const trimmed = value.trim();
|
|
3138
|
+
return /^[A-Za-z0-9_.-]{1,64}$/.test(trimmed) && !isExcludedRescueField(trimmed) ? trimmed : null;
|
|
3139
|
+
}
|
|
3140
|
+
var FORM_RESCUE_EVENTS = {
|
|
3141
|
+
started: "form_started",
|
|
3142
|
+
progress: "form_progress",
|
|
3143
|
+
leadCaptured: "form_lead_captured",
|
|
3144
|
+
abandoned: "form_abandoned",
|
|
3145
|
+
submitted: "form_submitted",
|
|
3146
|
+
rescued: "form_rescued",
|
|
3147
|
+
resumed: "form_resumed"
|
|
3148
|
+
};
|
|
3149
|
+
function captureFormRescueEvent(event, properties) {
|
|
3150
|
+
try {
|
|
3151
|
+
if (typeof window === "undefined") return;
|
|
3152
|
+
const ph = window.posthog;
|
|
3153
|
+
if (typeof ph?.capture === "function") ph.capture(event, properties);
|
|
3154
|
+
} catch {
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// src/hooks/use-form-rescue.ts
|
|
3159
|
+
var LOCAL_DRAFT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3160
|
+
var STORAGE_PREFIX = "form-rescue:v1:";
|
|
3161
|
+
var UTM_KEYS = ["source", "medium", "campaign", "content", "term"];
|
|
3162
|
+
function newAttemptId() {
|
|
3163
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
3164
|
+
const bytes = new Uint8Array(16);
|
|
3165
|
+
crypto.getRandomValues(bytes);
|
|
3166
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
3167
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
3168
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3169
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3170
|
+
}
|
|
3171
|
+
function readLocalDraft(formId) {
|
|
3172
|
+
try {
|
|
3173
|
+
const raw = window.localStorage.getItem(STORAGE_PREFIX + formId);
|
|
3174
|
+
if (!raw) return null;
|
|
3175
|
+
const parsed = JSON.parse(raw);
|
|
3176
|
+
if (!isFormAttemptId(parsed.attemptId) || typeof parsed.savedAt !== "number") return null;
|
|
3177
|
+
if (Date.now() - parsed.savedAt > LOCAL_DRAFT_TTL_MS) return null;
|
|
3178
|
+
return { attemptId: parsed.attemptId, values: sanitizeRescueValues(parsed.values), savedAt: parsed.savedAt };
|
|
3179
|
+
} catch {
|
|
3180
|
+
return null;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
function writeLocalDraft(formId, draft) {
|
|
3184
|
+
try {
|
|
3185
|
+
window.localStorage.setItem(STORAGE_PREFIX + formId, JSON.stringify(draft));
|
|
3186
|
+
} catch {
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
function clearLocalDraft(formId) {
|
|
3190
|
+
try {
|
|
3191
|
+
window.localStorage.removeItem(STORAGE_PREFIX + formId);
|
|
3192
|
+
} catch {
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
function readUtm() {
|
|
3196
|
+
try {
|
|
3197
|
+
const params = new URLSearchParams(window.location.search);
|
|
3198
|
+
const utm = {};
|
|
3199
|
+
for (const key of UTM_KEYS) {
|
|
3200
|
+
const value = params.get(`utm_${key}`);
|
|
3201
|
+
if (value) utm[key] = value.slice(0, 200);
|
|
3202
|
+
}
|
|
3203
|
+
return Object.keys(utm).length ? utm : void 0;
|
|
3204
|
+
} catch {
|
|
3205
|
+
return void 0;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
function useFormRescue({ formId, fieldNames, onRestore, getSignals }) {
|
|
3209
|
+
const draftsUrl = useEndpointsRuntime()?.formDraftsUrl;
|
|
3210
|
+
const attemptIdRef = useRef14(null);
|
|
3211
|
+
const resumeTokenRef = useRef14(null);
|
|
3212
|
+
const latestRef = useRef14(null);
|
|
3213
|
+
const dirtyRef = useRef14(false);
|
|
3214
|
+
const startedRef = useRef14(false);
|
|
3215
|
+
const leadCapturedRef = useRef14(false);
|
|
3216
|
+
const completedRef = useRef14(false);
|
|
3217
|
+
const timerRef = useRef14(null);
|
|
3218
|
+
const fieldNamesRef = useRef14(fieldNames);
|
|
3219
|
+
const onRestoreRef = useRef14(onRestore);
|
|
3220
|
+
const getSignalsRef = useRef14(getSignals);
|
|
3221
|
+
useEffect20(() => {
|
|
3222
|
+
fieldNamesRef.current = fieldNames;
|
|
3223
|
+
onRestoreRef.current = onRestore;
|
|
3224
|
+
getSignalsRef.current = getSignals;
|
|
3225
|
+
});
|
|
3226
|
+
const attemptId = useCallback14(() => {
|
|
3227
|
+
if (!attemptIdRef.current) attemptIdRef.current = newAttemptId();
|
|
3228
|
+
return attemptIdRef.current;
|
|
3229
|
+
}, []);
|
|
3230
|
+
const send = useCallback14(
|
|
3231
|
+
(keepalive) => {
|
|
3232
|
+
if (!formId || !draftsUrl || !dirtyRef.current || completedRef.current || !latestRef.current) return;
|
|
3233
|
+
dirtyRef.current = false;
|
|
3234
|
+
const body = {
|
|
3235
|
+
attempt_id: attemptId(),
|
|
3236
|
+
form_id: formId,
|
|
3237
|
+
...latestRef.current,
|
|
3238
|
+
source_path: typeof window !== "undefined" ? window.location.pathname : "/",
|
|
3239
|
+
utm: readUtm()
|
|
3240
|
+
};
|
|
3241
|
+
try {
|
|
3242
|
+
void contentFetch(draftsUrl, {
|
|
3243
|
+
method: "POST",
|
|
3244
|
+
headers: { "Content-Type": "application/json" },
|
|
3245
|
+
body: JSON.stringify({ ...body, ...getSignalsRef.current?.() ?? {} }),
|
|
3246
|
+
keepalive
|
|
3247
|
+
}).catch(() => void 0);
|
|
3248
|
+
} catch {
|
|
3249
|
+
}
|
|
3250
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.progress, {
|
|
3251
|
+
form_id: formId,
|
|
3252
|
+
attempt_id: attemptId(),
|
|
3253
|
+
fields_filled: body.fields_filled,
|
|
3254
|
+
last_field: body.last_field,
|
|
3255
|
+
completion_pct: body.completion_pct
|
|
3256
|
+
});
|
|
3257
|
+
},
|
|
3258
|
+
[attemptId, draftsUrl, formId]
|
|
3259
|
+
);
|
|
3260
|
+
useEffect20(() => {
|
|
3261
|
+
if (!formId || typeof window === "undefined") return void 0;
|
|
3262
|
+
let cancelled = false;
|
|
3263
|
+
const token = new URLSearchParams(window.location.search).get(FORM_RESCUE_RESUME_PARAM);
|
|
3264
|
+
const restoreLocal = () => {
|
|
3265
|
+
const local = readLocalDraft(formId);
|
|
3266
|
+
if (!local) return;
|
|
3267
|
+
attemptIdRef.current = local.attemptId;
|
|
3268
|
+
if (Object.keys(local.values).length) onRestoreRef.current?.(local.values);
|
|
3269
|
+
};
|
|
3270
|
+
if (token && draftsUrl && /^[A-Za-z0-9_-]{16,128}$/.test(token)) {
|
|
3271
|
+
contentFetch(`${draftsUrl}/resume/${encodeURIComponent(token)}`).then(async (res) => res.ok ? await res.json() : null).then((data) => {
|
|
3272
|
+
if (cancelled) return;
|
|
3273
|
+
if (!data || data.form_id !== formId) {
|
|
3274
|
+
restoreLocal();
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
resumeTokenRef.current = token;
|
|
3278
|
+
onRestoreRef.current?.(sanitizeRescueValues(data.values));
|
|
3279
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.resumed, { form_id: formId, attempt_id: attemptId() });
|
|
3280
|
+
}).catch(() => {
|
|
3281
|
+
if (!cancelled) restoreLocal();
|
|
3282
|
+
});
|
|
3283
|
+
} else {
|
|
3284
|
+
restoreLocal();
|
|
3285
|
+
}
|
|
3286
|
+
return () => {
|
|
3287
|
+
cancelled = true;
|
|
3288
|
+
};
|
|
3289
|
+
}, [attemptId, draftsUrl, formId]);
|
|
3290
|
+
useEffect20(() => {
|
|
3291
|
+
if (!formId || typeof document === "undefined") return void 0;
|
|
3292
|
+
const onHide = () => {
|
|
3293
|
+
if (completedRef.current || !startedRef.current) return;
|
|
3294
|
+
if (timerRef.current) {
|
|
3295
|
+
clearTimeout(timerRef.current);
|
|
3296
|
+
timerRef.current = null;
|
|
3297
|
+
}
|
|
3298
|
+
send(true);
|
|
3299
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.abandoned, {
|
|
3300
|
+
form_id: formId,
|
|
3301
|
+
attempt_id: attemptId(),
|
|
3302
|
+
last_field: latestRef.current?.last_field ?? null,
|
|
3303
|
+
completion_pct: latestRef.current?.completion_pct ?? 0
|
|
3304
|
+
});
|
|
3305
|
+
};
|
|
3306
|
+
const onVisibility = () => {
|
|
3307
|
+
if (document.visibilityState === "hidden") onHide();
|
|
3308
|
+
};
|
|
3309
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
3310
|
+
window.addEventListener("pagehide", onHide);
|
|
3311
|
+
return () => {
|
|
3312
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
3313
|
+
window.removeEventListener("pagehide", onHide);
|
|
3314
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3315
|
+
};
|
|
3316
|
+
}, [attemptId, formId, send]);
|
|
3317
|
+
const track = useCallback14(
|
|
3318
|
+
(values, lastField) => {
|
|
3319
|
+
if (!formId || completedRef.current) return;
|
|
3320
|
+
const names = fieldNamesRef.current;
|
|
3321
|
+
const visible = {};
|
|
3322
|
+
for (const name of names) visible[name] = values[name];
|
|
3323
|
+
const filled = filledRescueFields(visible, names);
|
|
3324
|
+
if (!filled.length) return;
|
|
3325
|
+
if (!startedRef.current) {
|
|
3326
|
+
startedRef.current = true;
|
|
3327
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.started, { form_id: formId, attempt_id: attemptId() });
|
|
3328
|
+
}
|
|
3329
|
+
const clean = sanitizeRescueValues(visible);
|
|
3330
|
+
if (!leadCapturedRef.current && isRescueEmail(clean.email)) {
|
|
3331
|
+
leadCapturedRef.current = true;
|
|
3332
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.leadCaptured, { form_id: formId, attempt_id: attemptId() });
|
|
3333
|
+
}
|
|
3334
|
+
latestRef.current = {
|
|
3335
|
+
values: clean,
|
|
3336
|
+
fields_filled: filled,
|
|
3337
|
+
last_field: sanitizeRescueFieldName(lastField),
|
|
3338
|
+
completion_pct: rescueCompletionPct(filled.length, names.length)
|
|
3339
|
+
};
|
|
3340
|
+
dirtyRef.current = true;
|
|
3341
|
+
writeLocalDraft(formId, { attemptId: attemptId(), values: clean, savedAt: Date.now() });
|
|
3342
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3343
|
+
timerRef.current = setTimeout(() => {
|
|
3344
|
+
timerRef.current = null;
|
|
3345
|
+
send(false);
|
|
3346
|
+
}, FORM_RESCUE_DEBOUNCE_MS);
|
|
3347
|
+
},
|
|
3348
|
+
[attemptId, formId, send]
|
|
3349
|
+
);
|
|
3350
|
+
const submitFields = useCallback14(() => {
|
|
3351
|
+
if (!formId) return {};
|
|
3352
|
+
return {
|
|
3353
|
+
[FORM_RESCUE_ATTEMPT_FIELD]: attemptId(),
|
|
3354
|
+
...resumeTokenRef.current ? { [FORM_RESCUE_RESUME_FIELD]: resumeTokenRef.current } : {}
|
|
3355
|
+
};
|
|
3356
|
+
}, [attemptId, formId]);
|
|
3357
|
+
const complete = useCallback14(() => {
|
|
3358
|
+
if (!formId) return;
|
|
3359
|
+
if (timerRef.current) {
|
|
3360
|
+
clearTimeout(timerRef.current);
|
|
3361
|
+
timerRef.current = null;
|
|
3362
|
+
}
|
|
3363
|
+
captureFormRescueEvent(FORM_RESCUE_EVENTS.submitted, { form_id: formId, attempt_id: attemptId() });
|
|
3364
|
+
clearLocalDraft(formId);
|
|
3365
|
+
attemptIdRef.current = null;
|
|
3366
|
+
resumeTokenRef.current = null;
|
|
3367
|
+
latestRef.current = null;
|
|
3368
|
+
dirtyRef.current = false;
|
|
3369
|
+
startedRef.current = false;
|
|
3370
|
+
leadCapturedRef.current = false;
|
|
3371
|
+
}, [attemptId, formId]);
|
|
3372
|
+
return useMemo7(() => ({ track, submitFields, complete }), [track, submitFields, complete]);
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3062
3375
|
export {
|
|
3063
3376
|
useAutoLimitTags,
|
|
3064
3377
|
useKeyboardCollisionPadding,
|
|
@@ -3144,6 +3457,27 @@ export {
|
|
|
3144
3457
|
findHoneypotCopySource,
|
|
3145
3458
|
evaluateHumanitySignals,
|
|
3146
3459
|
splitCsvEnv,
|
|
3147
|
-
useHumanitySignals
|
|
3460
|
+
useHumanitySignals,
|
|
3461
|
+
FORM_RESCUE_FORM_IDS,
|
|
3462
|
+
isFormRescueFormId,
|
|
3463
|
+
FORM_RESCUE_VALUE_FIELDS,
|
|
3464
|
+
isExcludedRescueField,
|
|
3465
|
+
FORM_RESCUE_MAX_VALUE_CHARS,
|
|
3466
|
+
FORM_RESCUE_DEBOUNCE_MS,
|
|
3467
|
+
FORM_RESCUE_MAX_FIELDS,
|
|
3468
|
+
FORM_RESCUE_ATTEMPT_FIELD,
|
|
3469
|
+
FORM_RESCUE_RESUME_FIELD,
|
|
3470
|
+
FORM_RESCUE_SUBMIT_KEYS,
|
|
3471
|
+
FORM_RESCUE_RESUME_PARAM,
|
|
3472
|
+
FORM_DRAFT_STATUSES,
|
|
3473
|
+
isRescueEmail,
|
|
3474
|
+
isFormAttemptId,
|
|
3475
|
+
sanitizeRescueValues,
|
|
3476
|
+
filledRescueFields,
|
|
3477
|
+
rescueCompletionPct,
|
|
3478
|
+
sanitizeRescueFieldName,
|
|
3479
|
+
FORM_RESCUE_EVENTS,
|
|
3480
|
+
captureFormRescueEvent,
|
|
3481
|
+
useFormRescue
|
|
3148
3482
|
};
|
|
3149
|
-
//# sourceMappingURL=chunk-
|
|
3483
|
+
//# sourceMappingURL=chunk-EVB3EO5S.js.map
|