@evaro/consultation-sdk 0.1.0 → 0.2.0-next.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/README.md +2 -2
- package/dist/index.mjs +1309 -152
- package/dist/types/Consultation.d.ts +17 -2
- package/dist/types/api.d.ts +5 -0
- package/dist/types/draftQueue.d.ts +22 -0
- package/dist/types/flow/form.d.ts +67 -0
- package/dist/types/flow/path.d.ts +7 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/targets/diagnosisRequest.d.ts +1 -1
- package/dist/types/targets/index.d.ts +1 -1
- package/dist/types/types.d.ts +4 -0
- package/dist/types/ui/ErrorNotice.d.ts +4 -0
- package/dist/types/ui/FormFlow.d.ts +9 -0
- package/dist/types/ui/form/Blocks.d.ts +6 -0
- package/dist/types/ui/form/FormField.d.ts +38 -0
- package/dist/types/ui/form/inputs.d.ts +28 -0
- package/dist/types/useTargetSave.d.ts +30 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -20,6 +20,21 @@ var host = (hosts, environment) => {
|
|
|
20
20
|
};
|
|
21
21
|
var apiHost = (environment) => host(API_HOSTS, environment);
|
|
22
22
|
var nodeApiHost = (environment) => host(NODE_API_HOSTS, environment);
|
|
23
|
+
var isLocalHost = (hostname) => hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "[::1]" || /^127\./.test(hostname);
|
|
24
|
+
var parseUrl = (value) => {
|
|
25
|
+
try {
|
|
26
|
+
return new URL(value);
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error(`nodeApiUrl is not a valid URL: ${value}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** The NodeAPI base for target calls: `nodeApiUrl` when the host sets one, otherwise the environment's host. */
|
|
32
|
+
function nodeApiBase(environment, nodeApiUrl) {
|
|
33
|
+
if (!nodeApiUrl) return nodeApiHost(environment);
|
|
34
|
+
const url = parseUrl(nodeApiUrl);
|
|
35
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && isLocalHost(url.hostname))) throw new Error(`nodeApiUrl must use https, except for localhost: ${nodeApiUrl}`);
|
|
36
|
+
return `${url.origin}${url.pathname}`.replace(/\/+$/, "");
|
|
37
|
+
}
|
|
23
38
|
var RequestError = class extends Error {
|
|
24
39
|
status;
|
|
25
40
|
constructor(message, status) {
|
|
@@ -28,11 +43,26 @@ var RequestError = class extends Error {
|
|
|
28
43
|
this.name = "RequestError";
|
|
29
44
|
}
|
|
30
45
|
};
|
|
46
|
+
/** The server refused a write because the answers changed in another place. */
|
|
47
|
+
var isConflict = (error) => error instanceof RequestError && error.status === 409;
|
|
48
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
31
49
|
async function requestJson(url, init = {}) {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
...init,
|
|
55
|
+
signal: controller.signal
|
|
56
|
+
});
|
|
57
|
+
const body = await response.json().catch(() => null);
|
|
58
|
+
if (!response.ok) throw new RequestError(body?.message || `Request failed with status ${response.status}`, response.status);
|
|
59
|
+
return body;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (controller.signal.aborted) throw new RequestError("The request timed out", 0);
|
|
62
|
+
throw error;
|
|
63
|
+
} finally {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
36
66
|
}
|
|
37
67
|
/** The consultation as Connect loads it. `preview` reads the unpublished version, which needs no token. */
|
|
38
68
|
async function fetchVendorConsultation(environment, hash, vendor, preview) {
|
|
@@ -49,6 +79,50 @@ function fetchVendorTheme(environment, vendor) {
|
|
|
49
79
|
}
|
|
50
80
|
return themes.get(key);
|
|
51
81
|
}
|
|
82
|
+
// sdk/src/draftQueue.ts
|
|
83
|
+
/**
|
|
84
|
+
* Runs one target's drafts and submits one after another, so an older write never lands after a newer one, and
|
|
85
|
+
* a reload can wait for the writes that are still open.
|
|
86
|
+
*/
|
|
87
|
+
var DraftQueue = class {
|
|
88
|
+
onIdle;
|
|
89
|
+
tail = Promise.resolve();
|
|
90
|
+
open = 0;
|
|
91
|
+
constructor(onIdle = () => {}) {
|
|
92
|
+
this.onIdle = onIdle;
|
|
93
|
+
}
|
|
94
|
+
/** Runs the task after the open ones. A failed task does not stop the queue. */
|
|
95
|
+
run(task) {
|
|
96
|
+
const result = this.tail.then(task);
|
|
97
|
+
this.open++;
|
|
98
|
+
this.tail = result.catch(() => void 0).finally(() => {
|
|
99
|
+
this.open--;
|
|
100
|
+
if (this.open === 0) this.onIdle();
|
|
101
|
+
});
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
/** Settles when every task queued so far has settled. */
|
|
105
|
+
idle() {
|
|
106
|
+
return this.tail;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var queues = /* @__PURE__ */ new Map();
|
|
110
|
+
/** Runs a write on the target's queue. Only a write creates a queue, and the queue goes once it is idle. */
|
|
111
|
+
function runOnDraftQueue(key, task) {
|
|
112
|
+
let queue = queues.get(key);
|
|
113
|
+
if (!queue) {
|
|
114
|
+
const created = new DraftQueue(() => {
|
|
115
|
+
if (queues.get(key) === created) queues.delete(key);
|
|
116
|
+
});
|
|
117
|
+
queue = created;
|
|
118
|
+
queues.set(key, queue);
|
|
119
|
+
}
|
|
120
|
+
return queue.run(task);
|
|
121
|
+
}
|
|
122
|
+
/** Settles when the target's open writes have settled. A target with no open writes settles at once. */
|
|
123
|
+
function draftQueueIdle(key) {
|
|
124
|
+
return queues.get(key)?.idle() ?? Promise.resolve();
|
|
125
|
+
}
|
|
52
126
|
// sdk/src/flow/validations.ts
|
|
53
127
|
var questionValidations = { blood_pressure: {
|
|
54
128
|
validate: (value) => /^\d{1,3}\/\d{1,3}$/.test(value),
|
|
@@ -199,7 +273,7 @@ function isFreeTextTooShort(type, answer) {
|
|
|
199
273
|
return FREE_TEXT_TYPES.includes(type) && (answer ?? "").trim().length < 3;
|
|
200
274
|
}
|
|
201
275
|
/** Whether a question holds an answer the patient could move on from. */
|
|
202
|
-
function isAnswered(question, answer) {
|
|
276
|
+
function isAnswered$1(question, answer) {
|
|
203
277
|
if (!question) return false;
|
|
204
278
|
if (Array.isArray(answer)) return answer.length > 0;
|
|
205
279
|
if (!answer) return false;
|
|
@@ -217,7 +291,7 @@ function areFollowUpsAnswered(followUpHashes, questions, answers, optionHashes,
|
|
|
217
291
|
seen.add(hash);
|
|
218
292
|
const question = questions?.find((q) => q.hash === hash);
|
|
219
293
|
const answer = answers?.[hash];
|
|
220
|
-
if (!isAnswered(question, answer)) return false;
|
|
294
|
+
if (!isAnswered$1(question, answer)) return false;
|
|
221
295
|
return areFollowUpsAnswered(getFollowUpHashes(getMatchedOptionLogics(answer, optionHashes?.[hash], question.options)), questions, answers, optionHashes, seen);
|
|
222
296
|
});
|
|
223
297
|
}
|
|
@@ -442,6 +516,13 @@ function resumeHistory(walk) {
|
|
|
442
516
|
return walkFrom(visibleQuestions[0]?.hash, walk, (hash) => areFollowUpsAnswered([hash], allQuestions, answers, optionHashes));
|
|
443
517
|
}
|
|
444
518
|
/**
|
|
519
|
+
* The one-page form's main questions: the flow from the first question, where only an answer takes a jump, so an
|
|
520
|
+
* unanswered question goes on to its default next one. It stops at an answer with an exit.
|
|
521
|
+
*/
|
|
522
|
+
function formPath(walk) {
|
|
523
|
+
return walkFrom(walk.visibleQuestions[0]?.hash, walk, () => true);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
445
526
|
* The answers a draft keeps: the path behind the user, and ahead of them the questions the current answers
|
|
446
527
|
* still lead to. After Back, answers further on survive while the user edits, even when an edit leaves the
|
|
447
528
|
* current question unfinished. Answers of a branch the user left are dropped.
|
|
@@ -711,15 +792,21 @@ function useConsultationFlow({ consultation, consultationType, initialAnswers, i
|
|
|
711
792
|
};
|
|
712
793
|
}
|
|
713
794
|
// sdk/src/targets/diagnosisRequest.ts
|
|
795
|
+
var versionOf = (body) => {
|
|
796
|
+
if (typeof body?.version !== "number") throw new Error("NodeAPI sent the assessment without a version");
|
|
797
|
+
return body.version;
|
|
798
|
+
};
|
|
714
799
|
var toSaved = (body) => ({
|
|
715
800
|
answers: body?.answers ?? {},
|
|
716
801
|
status: body?.status ?? "empty",
|
|
717
802
|
consultationVersion: body?.consultation_version ?? null,
|
|
718
|
-
submittedAt: body?.submitted_at ?? null
|
|
803
|
+
submittedAt: body?.submitted_at ?? null,
|
|
804
|
+
version: versionOf(body)
|
|
719
805
|
});
|
|
720
806
|
/** Assessment answers stored on a diagnosis request, through NodeAPI with the Prescriber App user's token. */
|
|
721
|
-
function diagnosisRequestTarget(environment, id, getToken) {
|
|
722
|
-
const
|
|
807
|
+
function diagnosisRequestTarget(environment, id, getToken, nodeApiUrl) {
|
|
808
|
+
const host = () => nodeApiBase(environment, nodeApiUrl);
|
|
809
|
+
const base = () => `${host()}/prescriber/diagnosis-requests/${encodeURIComponent(id)}/assessment`;
|
|
723
810
|
const headers = async () => {
|
|
724
811
|
const token = await getToken?.();
|
|
725
812
|
if (!token) throw new Error("No token: the user must be signed in to load or save answers");
|
|
@@ -728,32 +815,235 @@ function diagnosisRequestTarget(environment, id, getToken) {
|
|
|
728
815
|
"Content-Type": "application/json"
|
|
729
816
|
};
|
|
730
817
|
};
|
|
818
|
+
let version = null;
|
|
819
|
+
let loads = 0;
|
|
731
820
|
return {
|
|
732
|
-
|
|
821
|
+
get version() {
|
|
822
|
+
return version;
|
|
823
|
+
},
|
|
824
|
+
load: async () => {
|
|
825
|
+
const load = ++loads;
|
|
826
|
+
const saved = toSaved(await requestJson(base(), { headers: await headers() }));
|
|
827
|
+
if (load === loads) version = saved.version;
|
|
828
|
+
return saved;
|
|
829
|
+
},
|
|
733
830
|
saveDraft: async (answers) => {
|
|
734
|
-
await requestJson(`${base()}/draft`, {
|
|
831
|
+
version = versionOf(await requestJson(`${base()}/draft`, {
|
|
735
832
|
method: "PUT",
|
|
736
833
|
headers: await headers(),
|
|
737
|
-
body: JSON.stringify({
|
|
738
|
-
|
|
834
|
+
body: JSON.stringify({
|
|
835
|
+
answers,
|
|
836
|
+
version
|
|
837
|
+
})
|
|
838
|
+
}));
|
|
739
839
|
},
|
|
740
840
|
submit: async (answers) => {
|
|
741
|
-
await requestJson(`${base()}/submit`, {
|
|
841
|
+
version = versionOf(await requestJson(`${base()}/submit`, {
|
|
742
842
|
method: "POST",
|
|
743
843
|
headers: await headers(),
|
|
744
|
-
body: JSON.stringify({
|
|
745
|
-
|
|
844
|
+
body: JSON.stringify({
|
|
845
|
+
answers,
|
|
846
|
+
version
|
|
847
|
+
})
|
|
848
|
+
}));
|
|
746
849
|
}
|
|
747
850
|
};
|
|
748
851
|
}
|
|
749
852
|
// sdk/src/targets/index.ts
|
|
750
853
|
/** One adapter for each target type. To save answers in a new place, add a type here, not code in the host. */
|
|
751
|
-
function createTargetAdapter(target, environment, getToken) {
|
|
854
|
+
function createTargetAdapter(target, environment, getToken, nodeApiUrl) {
|
|
752
855
|
switch (target.type) {
|
|
753
|
-
case "diagnosis-request": return diagnosisRequestTarget(environment, target.id, getToken);
|
|
856
|
+
case "diagnosis-request": return diagnosisRequestTarget(environment, target.id, getToken, nodeApiUrl);
|
|
754
857
|
default: throw new Error(`Unknown target type: ${target.type}`);
|
|
755
858
|
}
|
|
756
859
|
}
|
|
860
|
+
var errorMessage = (error) => error instanceof Error ? error.message : "Something went wrong";
|
|
861
|
+
function useTargetSave({ adapter, queueKey, revision, latestDraft, onDone, onSubmitted, onConflict, onSaved }) {
|
|
862
|
+
const [saveState, setSaveState] = useState("idle");
|
|
863
|
+
const [submitState, setSubmitState] = useState("idle");
|
|
864
|
+
const [submitError, setSubmitError] = useState(null);
|
|
865
|
+
const latestDraftRef = useRef(latestDraft);
|
|
866
|
+
latestDraftRef.current = latestDraft;
|
|
867
|
+
const onDoneRef = useRef(onDone);
|
|
868
|
+
onDoneRef.current = onDone;
|
|
869
|
+
const onSubmittedRef = useRef(onSubmitted);
|
|
870
|
+
onSubmittedRef.current = onSubmitted;
|
|
871
|
+
const onConflictRef = useRef(onConflict);
|
|
872
|
+
onConflictRef.current = onConflict;
|
|
873
|
+
const onSavedRef = useRef(onSaved);
|
|
874
|
+
onSavedRef.current = onSaved;
|
|
875
|
+
const draftTimer = useRef(null);
|
|
876
|
+
const latestRevision = useRef(revision);
|
|
877
|
+
latestRevision.current = revision;
|
|
878
|
+
const savedRevision = useRef(revision);
|
|
879
|
+
const mounted = useRef(true);
|
|
880
|
+
const closed = useRef(false);
|
|
881
|
+
const draftSeq = useRef(0);
|
|
882
|
+
const conflicted = useRef(false);
|
|
883
|
+
const stopForConflict = useCallback(() => {
|
|
884
|
+
if (conflicted.current) return;
|
|
885
|
+
conflicted.current = true;
|
|
886
|
+
closed.current = true;
|
|
887
|
+
savedRevision.current = latestRevision.current;
|
|
888
|
+
clearTimeout(draftTimer.current);
|
|
889
|
+
onConflictRef.current();
|
|
890
|
+
}, []);
|
|
891
|
+
const saveDraft = useCallback(() => {
|
|
892
|
+
if (!adapter) return;
|
|
893
|
+
clearTimeout(draftTimer.current);
|
|
894
|
+
const seq = ++draftSeq.current;
|
|
895
|
+
const isLatest = () => seq === draftSeq.current;
|
|
896
|
+
setSaveState("saving");
|
|
897
|
+
runOnDraftQueue(queueKey, async () => {
|
|
898
|
+
if (closed.current) return false;
|
|
899
|
+
const revision = latestRevision.current;
|
|
900
|
+
await adapter.saveDraft(latestDraftRef.current());
|
|
901
|
+
savedRevision.current = Math.max(savedRevision.current, revision);
|
|
902
|
+
return true;
|
|
903
|
+
}).then((sent) => {
|
|
904
|
+
if (sent && mounted.current) onSavedRef.current();
|
|
905
|
+
if (!isLatest() || !mounted.current) return;
|
|
906
|
+
setSaveState((state) => sent ? "saved" : state === "saving" ? "idle" : state);
|
|
907
|
+
}, (error) => {
|
|
908
|
+
if (isConflict(error)) {
|
|
909
|
+
if (mounted.current) stopForConflict();
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
if (isLatest() && mounted.current) setSaveState("error");
|
|
913
|
+
});
|
|
914
|
+
}, [
|
|
915
|
+
adapter,
|
|
916
|
+
queueKey,
|
|
917
|
+
stopForConflict
|
|
918
|
+
]);
|
|
919
|
+
useEffect(() => {
|
|
920
|
+
if (!adapter || revision === 0 || closed.current) return;
|
|
921
|
+
draftTimer.current = setTimeout(saveDraft, 600);
|
|
922
|
+
return () => clearTimeout(draftTimer.current);
|
|
923
|
+
}, [
|
|
924
|
+
adapter,
|
|
925
|
+
revision,
|
|
926
|
+
saveDraft
|
|
927
|
+
]);
|
|
928
|
+
useEffect(() => {
|
|
929
|
+
mounted.current = true;
|
|
930
|
+
return () => {
|
|
931
|
+
mounted.current = false;
|
|
932
|
+
if (!adapter || latestRevision.current <= savedRevision.current || closed.current) return;
|
|
933
|
+
const answers = latestDraftRef.current();
|
|
934
|
+
runOnDraftQueue(queueKey, () => adapter.saveDraft(answers)).catch((error) => {
|
|
935
|
+
if (isConflict(error)) onConflictRef.current();
|
|
936
|
+
});
|
|
937
|
+
};
|
|
938
|
+
}, []);
|
|
939
|
+
/** Shows why the answers cannot be submitted yet. */
|
|
940
|
+
const rejectSubmit = (message) => {
|
|
941
|
+
setSubmitState("error");
|
|
942
|
+
setSubmitError(message);
|
|
943
|
+
};
|
|
944
|
+
const submit = (answers) => {
|
|
945
|
+
if (!adapter) return;
|
|
946
|
+
closed.current = true;
|
|
947
|
+
clearTimeout(draftTimer.current);
|
|
948
|
+
setSubmitState("submitting");
|
|
949
|
+
setSubmitError(null);
|
|
950
|
+
runOnDraftQueue(queueKey, async () => {
|
|
951
|
+
if (conflicted.current) return {
|
|
952
|
+
submitted: false,
|
|
953
|
+
conflict: true,
|
|
954
|
+
draftSaved: false,
|
|
955
|
+
error: null
|
|
956
|
+
};
|
|
957
|
+
try {
|
|
958
|
+
await adapter.submit(answers);
|
|
959
|
+
return {
|
|
960
|
+
submitted: true,
|
|
961
|
+
conflict: false,
|
|
962
|
+
draftSaved: true,
|
|
963
|
+
error: null
|
|
964
|
+
};
|
|
965
|
+
} catch (error) {
|
|
966
|
+
const versionBefore = adapter.version;
|
|
967
|
+
const saved = await adapter.load().catch(() => null);
|
|
968
|
+
if (saved?.status === "submitted") return {
|
|
969
|
+
submitted: true,
|
|
970
|
+
conflict: false,
|
|
971
|
+
draftSaved: true,
|
|
972
|
+
error: null
|
|
973
|
+
};
|
|
974
|
+
if (saved && saved.version !== versionBefore) return {
|
|
975
|
+
submitted: false,
|
|
976
|
+
conflict: true,
|
|
977
|
+
draftSaved: false,
|
|
978
|
+
error
|
|
979
|
+
};
|
|
980
|
+
const revision = latestRevision.current;
|
|
981
|
+
let conflict = false;
|
|
982
|
+
const draftSaved = await adapter.saveDraft(latestDraftRef.current()).then(() => true, (draftError) => {
|
|
983
|
+
conflict = isConflict(draftError);
|
|
984
|
+
return false;
|
|
985
|
+
});
|
|
986
|
+
if (conflict) {
|
|
987
|
+
if ((await adapter.load().catch(() => null))?.status === "submitted") return {
|
|
988
|
+
submitted: true,
|
|
989
|
+
conflict: false,
|
|
990
|
+
draftSaved: true,
|
|
991
|
+
error: null
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
if (draftSaved) savedRevision.current = Math.max(savedRevision.current, revision);
|
|
995
|
+
return {
|
|
996
|
+
submitted: false,
|
|
997
|
+
conflict,
|
|
998
|
+
draftSaved,
|
|
999
|
+
error
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}).then(({ submitted, conflict, draftSaved, error }) => {
|
|
1003
|
+
if (submitted) {
|
|
1004
|
+
if (mounted.current) {
|
|
1005
|
+
setSubmitState("submitted");
|
|
1006
|
+
setSaveState("idle");
|
|
1007
|
+
}
|
|
1008
|
+
onSavedRef.current();
|
|
1009
|
+
onSubmittedRef.current?.();
|
|
1010
|
+
onDoneRef.current?.();
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if (!mounted.current) return;
|
|
1014
|
+
if (conflict) {
|
|
1015
|
+
stopForConflict();
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
closed.current = false;
|
|
1019
|
+
setSubmitState("error");
|
|
1020
|
+
setSubmitError(`The answers could not be submitted. ${errorMessage(error)}`);
|
|
1021
|
+
setSaveState(draftSaved ? "saved" : "error");
|
|
1022
|
+
});
|
|
1023
|
+
};
|
|
1024
|
+
return {
|
|
1025
|
+
saveState,
|
|
1026
|
+
submitState,
|
|
1027
|
+
submitError,
|
|
1028
|
+
saveDraft,
|
|
1029
|
+
submit,
|
|
1030
|
+
rejectSubmit
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
// node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
1034
|
+
function r(e) {
|
|
1035
|
+
var t, f, n = "";
|
|
1036
|
+
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
1037
|
+
else if ("object" == typeof e) if (Array.isArray(e)) {
|
|
1038
|
+
var o = e.length;
|
|
1039
|
+
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
|
1040
|
+
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
|
1041
|
+
return n;
|
|
1042
|
+
}
|
|
1043
|
+
function clsx() {
|
|
1044
|
+
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
|
1045
|
+
return n;
|
|
1046
|
+
}
|
|
757
1047
|
// node_modules/.pnpm/dompurify@3.4.16/node_modules/dompurify/dist/purify.es.mjs
|
|
758
1048
|
/*! @license DOMPurify 3.4.16 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.16/LICENSE */
|
|
759
1049
|
function _OverloadYield(e, d) {
|
|
@@ -3275,20 +3565,6 @@ var sanitize$1 = (html) => {
|
|
|
3275
3565
|
ALLOWED_ATTR
|
|
3276
3566
|
});
|
|
3277
3567
|
};
|
|
3278
|
-
// node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
3279
|
-
function r(e) {
|
|
3280
|
-
var t, f, n = "";
|
|
3281
|
-
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
3282
|
-
else if ("object" == typeof e) if (Array.isArray(e)) {
|
|
3283
|
-
var o = e.length;
|
|
3284
|
-
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
|
3285
|
-
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
|
3286
|
-
return n;
|
|
3287
|
-
}
|
|
3288
|
-
function clsx() {
|
|
3289
|
-
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
|
3290
|
-
return n;
|
|
3291
|
-
}
|
|
3292
3568
|
// sdk/src/ui/primitives.tsx
|
|
3293
3569
|
/**
|
|
3294
3570
|
* Connect's UI primitives (`UI/Button`, `UI/TextField`, `UI/DatePicker`, `UI/Accordion`, `UI/CircularProgress`)
|
|
@@ -3521,32 +3797,194 @@ var Accordion = ({ title, description }) => {
|
|
|
3521
3797
|
})]
|
|
3522
3798
|
});
|
|
3523
3799
|
};
|
|
3524
|
-
// sdk/src/ui/
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
className: "flex flex-col
|
|
3528
|
-
children: [
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
dangerouslySetInnerHTML: { __html: sanitize$1(content ?? "You are not eligible for this treatment. Please exit the consultation or review your answers.") }
|
|
3536
|
-
}),
|
|
3537
|
-
/* @__PURE__ */ jsx(Button, {
|
|
3538
|
-
className: "w-full mb-4 text-md font-bold normal-case bg-primary-700 rounded-3xl",
|
|
3539
|
-
onClick: onReview,
|
|
3540
|
-
children: "Review answer"
|
|
3541
|
-
}),
|
|
3542
|
-
onClose && /* @__PURE__ */ jsx(Button, {
|
|
3543
|
-
variant: "text",
|
|
3544
|
-
className: "w-full text-sm normal-case font-bold text-gray-900 rounded-3xl",
|
|
3545
|
-
onClick: onClose,
|
|
3546
|
-
children: "Exit Consultation"
|
|
3547
|
-
})
|
|
3548
|
-
]
|
|
3800
|
+
// sdk/src/ui/ErrorNotice.tsx
|
|
3801
|
+
var ErrorNotice = ({ message, onRetry }) => /* @__PURE__ */ jsxs("div", {
|
|
3802
|
+
role: "alert",
|
|
3803
|
+
className: "my-4 flex flex-col gap-3 rounded-lg border border-error-300 bg-error-100 p-4 text-sm text-gray-900",
|
|
3804
|
+
children: [/* @__PURE__ */ jsx("p", { children: message }), /* @__PURE__ */ jsx(Button, {
|
|
3805
|
+
variant: "outlined",
|
|
3806
|
+
fullWidth: false,
|
|
3807
|
+
className: "self-start !py-2",
|
|
3808
|
+
onClick: onRetry,
|
|
3809
|
+
children: "Try again"
|
|
3810
|
+
})]
|
|
3549
3811
|
});
|
|
3812
|
+
// sdk/src/flow/form.ts
|
|
3813
|
+
var plain = (html) => (html ?? "").replace(/<[^>]*>/g, "").trim().toLowerCase();
|
|
3814
|
+
/** A radio question whose only options are Yes and No. The form draws it as a Yes / No pair. */
|
|
3815
|
+
function isYesNo(question) {
|
|
3816
|
+
if (question?.type !== "radio" || question.options?.length !== 2) return false;
|
|
3817
|
+
const labels = question.options.map((option) => plain(option.label)).sort();
|
|
3818
|
+
return labels[0] === "no" && labels[1] === "yes";
|
|
3819
|
+
}
|
|
3820
|
+
var shownFrom = (hashes, walk) => {
|
|
3821
|
+
const shown = /* @__PURE__ */ new Set();
|
|
3822
|
+
for (const hash of hashes) withFollowUps(hash, walk.allQuestions, walk.answers, walk.optionHashes, shown);
|
|
3823
|
+
return shown;
|
|
3824
|
+
};
|
|
3825
|
+
/** The first answer with an exit, on a main question or one of its open follow-ups. The page stops there. */
|
|
3826
|
+
function pathAndExit(walk) {
|
|
3827
|
+
const path = formPath(walk);
|
|
3828
|
+
for (let index = 0; index < path.length; index++) for (const hash of shownFrom([path[index]], walk)) {
|
|
3829
|
+
const question = walk.allQuestions.find((q) => q.hash === hash);
|
|
3830
|
+
const logic = getMatchedOptionLogics(walk.answers[hash], walk.optionHashes[hash], question?.options ?? []).find(hasExitLogic);
|
|
3831
|
+
if (logic) return {
|
|
3832
|
+
path: path.slice(0, index + 1),
|
|
3833
|
+
exit: {
|
|
3834
|
+
questionHash: hash,
|
|
3835
|
+
title: logic.exit_title ?? null,
|
|
3836
|
+
content: logic.exit ?? null
|
|
3837
|
+
}
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
return {
|
|
3841
|
+
path,
|
|
3842
|
+
exit: null
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
/**
|
|
3846
|
+
* A table section reads its main questions row by row, in consultation order: one question for each heading
|
|
3847
|
+
* after the first. A row a jump or an exit hides as a whole is left out. When the questions do not fill whole
|
|
3848
|
+
* rows, or an answer hides part of a row, the section shows as a list, so a cell can never move under the wrong
|
|
3849
|
+
* row label.
|
|
3850
|
+
*/
|
|
3851
|
+
function sectionTable(section, questions, shown) {
|
|
3852
|
+
const columns = section.columns ?? [];
|
|
3853
|
+
const width = columns.length - 1;
|
|
3854
|
+
if (!isTableLayout(section) || !questions.length || questions.length % width) return null;
|
|
3855
|
+
const rows = [];
|
|
3856
|
+
for (let start = 0; start < questions.length; start += width) {
|
|
3857
|
+
const row = questions.slice(start, start + width);
|
|
3858
|
+
const showing = row.filter((hash) => shown.has(hash)).length;
|
|
3859
|
+
if (showing === row.length) rows.push(row);
|
|
3860
|
+
else if (showing) return null;
|
|
3861
|
+
}
|
|
3862
|
+
return rows.length ? {
|
|
3863
|
+
columns,
|
|
3864
|
+
rows
|
|
3865
|
+
} : null;
|
|
3866
|
+
}
|
|
3867
|
+
/** A section set to table with a heading for the row labels and at least one more. */
|
|
3868
|
+
var isTableLayout = (section) => section.layout === "table" && (section.columns?.length ?? 0) > 1;
|
|
3869
|
+
/** Main questions in flow order: a run with no section, or a section's questions at the place of its first one. */
|
|
3870
|
+
function draftBlocks(path, sectionOf) {
|
|
3871
|
+
const drafts = [];
|
|
3872
|
+
const bySection = /* @__PURE__ */ new Map();
|
|
3873
|
+
for (const hash of path) {
|
|
3874
|
+
const section = sectionOf(hash);
|
|
3875
|
+
const last = drafts[drafts.length - 1];
|
|
3876
|
+
if (!section) {
|
|
3877
|
+
if (last?.kind === "questions") last.questions.push(hash);
|
|
3878
|
+
else drafts.push({
|
|
3879
|
+
kind: "questions",
|
|
3880
|
+
questions: [hash]
|
|
3881
|
+
});
|
|
3882
|
+
} else if (bySection.has(section.id)) bySection.get(section.id)?.questions.push(hash);
|
|
3883
|
+
else {
|
|
3884
|
+
const created = {
|
|
3885
|
+
kind: "section",
|
|
3886
|
+
source: section,
|
|
3887
|
+
questions: [hash]
|
|
3888
|
+
};
|
|
3889
|
+
bySection.set(section.id, created);
|
|
3890
|
+
drafts.push(created);
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
return drafts;
|
|
3894
|
+
}
|
|
3895
|
+
/**
|
|
3896
|
+
* What the page shows: the flow from the first question. A section's questions show together, where its first
|
|
3897
|
+
* question comes in the flow, and questions with no section show where they come. Sections next to each other
|
|
3898
|
+
* with the same group show as tabs. Follow-ups show under their parent, so only main questions are grouped.
|
|
3899
|
+
*/
|
|
3900
|
+
function buildForm(walk, consultation) {
|
|
3901
|
+
const { path, exit } = pathAndExit(walk);
|
|
3902
|
+
const sections = new Map((consultation.sections ?? []).map((section) => [section.id, section]));
|
|
3903
|
+
const byHash = new Map(walk.allQuestions.map((question) => [question.hash, question]));
|
|
3904
|
+
const sectionOf = (hash) => sections.get(byHash.get(hash)?.consultation_section?.[consultation.hash] ?? "") ?? null;
|
|
3905
|
+
const onPath = new Set(path);
|
|
3906
|
+
const sectionQuestionsOf = /* @__PURE__ */ new Map();
|
|
3907
|
+
for (const question of walk.visibleQuestions) {
|
|
3908
|
+
const id = sectionOf(question.hash)?.id;
|
|
3909
|
+
if (id) sectionQuestionsOf.set(id, [...sectionQuestionsOf.get(id) ?? [], question.hash]);
|
|
3910
|
+
}
|
|
3911
|
+
const optional = /* @__PURE__ */ new Set();
|
|
3912
|
+
const blocks = [];
|
|
3913
|
+
for (const draft of draftBlocks(path, sectionOf)) {
|
|
3914
|
+
if (draft.kind === "questions") {
|
|
3915
|
+
blocks.push(draft);
|
|
3916
|
+
continue;
|
|
3917
|
+
}
|
|
3918
|
+
const { source } = draft;
|
|
3919
|
+
const table = sectionTable(source, sectionQuestionsOf.get(source.id) ?? [], onPath);
|
|
3920
|
+
if (isTableLayout(source)) {
|
|
3921
|
+
for (const hash of draft.questions) if (!isYesNo(byHash.get(hash))) optional.add(hash);
|
|
3922
|
+
}
|
|
3923
|
+
const section = {
|
|
3924
|
+
id: source.id,
|
|
3925
|
+
name: source.name,
|
|
3926
|
+
description: source.description ?? "",
|
|
3927
|
+
table,
|
|
3928
|
+
questions: table ? table.rows.flat() : draft.questions
|
|
3929
|
+
};
|
|
3930
|
+
const group = source.group?.trim();
|
|
3931
|
+
const previous = blocks[blocks.length - 1];
|
|
3932
|
+
if (group && previous?.kind === "tabs" && previous.name === group) previous.sections.push(section);
|
|
3933
|
+
else if (group) blocks.push({
|
|
3934
|
+
kind: "tabs",
|
|
3935
|
+
name: group,
|
|
3936
|
+
sections: [section]
|
|
3937
|
+
});
|
|
3938
|
+
else blocks.push({
|
|
3939
|
+
kind: "section",
|
|
3940
|
+
section
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
const shown = [...shownFrom(pageOrder(blocks), walk)];
|
|
3944
|
+
for (const hash of shown) {
|
|
3945
|
+
const question = byHash.get(hash);
|
|
3946
|
+
if (question?.type === "checkbox" && question.options?.length === 1) optional.add(hash);
|
|
3947
|
+
}
|
|
3948
|
+
return {
|
|
3949
|
+
path,
|
|
3950
|
+
shown,
|
|
3951
|
+
blocks,
|
|
3952
|
+
optional,
|
|
3953
|
+
exit
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
/** The main questions in the order the page draws them. */
|
|
3957
|
+
function pageOrder(blocks) {
|
|
3958
|
+
return blocks.flatMap((block) => {
|
|
3959
|
+
if (block.kind === "questions") return block.questions;
|
|
3960
|
+
if (block.kind === "section") return block.section.questions;
|
|
3961
|
+
return block.sections.flatMap((section) => section.questions);
|
|
3962
|
+
});
|
|
3963
|
+
}
|
|
3964
|
+
/** The copied rule for one answer, without its follow-ups: those are checked as questions of their own. */
|
|
3965
|
+
var isAnswered = (question, answers) => areFollowUpsAnswered([question.hash], [{
|
|
3966
|
+
...question,
|
|
3967
|
+
options: question.options?.map((option) => option.logic?.follow_ups ? {
|
|
3968
|
+
...option,
|
|
3969
|
+
logic: {
|
|
3970
|
+
...option.logic,
|
|
3971
|
+
follow_ups: []
|
|
3972
|
+
}
|
|
3973
|
+
} : option)
|
|
3974
|
+
}], answers, {});
|
|
3975
|
+
/** Questions on the page that still need an answer, in page order. Optional questions are never missing. */
|
|
3976
|
+
function missingAnswers(form, walk) {
|
|
3977
|
+
const byHash = new Map(walk.allQuestions.map((question) => [question.hash, question]));
|
|
3978
|
+
return form.shown.filter((hash) => {
|
|
3979
|
+
if (form.optional.has(hash)) return false;
|
|
3980
|
+
const question = byHash.get(hash);
|
|
3981
|
+
return !question || !isAnswered(question, walk.answers);
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
/** The answers on the page: what a draft saves and a submit sends. Answers of a branch a jump leaves are dropped. */
|
|
3985
|
+
function formAnswers(form, walk) {
|
|
3986
|
+
return answersOnPath(form.path, walk.allQuestions, walk.answers, walk.optionHashes);
|
|
3987
|
+
}
|
|
3550
3988
|
// node_modules/.pnpm/framer-motion@14.9.1_react-dom@19.3.0_react@19.3.0__react@19.3.0/node_modules/framer-motion/dist/es/context/LayoutGroupContext.mjs
|
|
3551
3989
|
var LayoutGroupContext = /* @__PURE__ */ createContext({});
|
|
3552
3990
|
// node_modules/.pnpm/framer-motion@14.9.1_react-dom@19.3.0_react@19.3.0__react@19.3.0/node_modules/framer-motion/dist/es/utils/use-constant.mjs
|
|
@@ -19504,6 +19942,725 @@ var Question = ({ hash, consultationHash, questions, answers, optionHashes, onAn
|
|
|
19504
19942
|
}, hash)
|
|
19505
19943
|
});
|
|
19506
19944
|
};
|
|
19945
|
+
// sdk/src/ui/form/inputs.tsx
|
|
19946
|
+
/** Inputs of the one-page form, in the Prescriber App style: 14px text, 16px rounded fields, a Yes / No pair. */
|
|
19947
|
+
var fieldClass = "w-full rounded-2xl border border-gray-300 bg-white px-4 text-sm font-normal text-gray-900 placeholder:text-gray-400 focus:border-primary-700 focus:ring-primary-700 disabled:opacity-60";
|
|
19948
|
+
var plainText = (html) => An(html ?? "").result.trim();
|
|
19949
|
+
/** Options as a joined pair of buttons, such as Yes / No. They are radio inputs, so the keyboard works as usual. */
|
|
19950
|
+
var Segmented = ({ question, answers, optionHashes, onAnswer, disabled, label }) => /* @__PURE__ */ jsx("fieldset", {
|
|
19951
|
+
"aria-label": label ?? plainText(question.title),
|
|
19952
|
+
className: "inline-flex shrink-0",
|
|
19953
|
+
children: question.options.map((option, index) => {
|
|
19954
|
+
const selected = isOptionSelected(option, answers[question.hash], optionHashes[question.hash]);
|
|
19955
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
19956
|
+
className: clsx("flex h-[39px] min-w-[51px] cursor-pointer items-center justify-center border px-3.5 text-sm font-normal transition-colors has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary-300", index === 0 ? "rounded-l-xl" : "-ml-px", index === question.options.length - 1 && "rounded-r-xl", disabled && "pointer-events-none opacity-60", selected ? "relative z-10 border-primary-700 bg-primary-700 text-white" : "border-gray-300 bg-white text-gray-900 hover:bg-gray-50"),
|
|
19957
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
19958
|
+
type: "radio",
|
|
19959
|
+
className: "sr-only",
|
|
19960
|
+
name: `${question.hash}-choice`,
|
|
19961
|
+
checked: selected,
|
|
19962
|
+
disabled,
|
|
19963
|
+
onChange: () => onAnswer(question, option.label, option.logic)
|
|
19964
|
+
}), plainText(option.label)]
|
|
19965
|
+
}, `${option.label}-${index}`);
|
|
19966
|
+
})
|
|
19967
|
+
});
|
|
19968
|
+
/** A radio question as a dropdown. The native list opens outside the shadow root's styles but works everywhere. */
|
|
19969
|
+
var Select = ({ question, answers, optionHashes, onAnswer, disabled, label }) => {
|
|
19970
|
+
const selected = question.options.findIndex((option) => isOptionSelected(option, answers[question.hash], optionHashes[question.hash]));
|
|
19971
|
+
return /* @__PURE__ */ jsxs("select", {
|
|
19972
|
+
"aria-label": label ?? plainText(question.title),
|
|
19973
|
+
disabled,
|
|
19974
|
+
value: selected < 0 ? "" : String(selected),
|
|
19975
|
+
onChange: (event) => {
|
|
19976
|
+
const option = question.options[Number(event.target.value)];
|
|
19977
|
+
if (option) onAnswer(question, option.label, option.logic);
|
|
19978
|
+
},
|
|
19979
|
+
className: clsx(fieldClass, "h-12 pr-10", selected < 0 && "text-gray-400"),
|
|
19980
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
19981
|
+
value: "",
|
|
19982
|
+
disabled: true,
|
|
19983
|
+
children: "Select an option"
|
|
19984
|
+
}), question.options.map((option, index) => /* @__PURE__ */ jsx("option", {
|
|
19985
|
+
value: String(index),
|
|
19986
|
+
className: "text-gray-900",
|
|
19987
|
+
children: plainText(option.label)
|
|
19988
|
+
}, `${option.label}-${index}`))]
|
|
19989
|
+
});
|
|
19990
|
+
};
|
|
19991
|
+
/** Free text, a phone number or a date. `multiline` draws the tall box used for notes. */
|
|
19992
|
+
var TextInput = ({ question, answers, onAnswer, disabled, label, multiline, placeholder }) => {
|
|
19993
|
+
const value = answers[question.hash] ?? "";
|
|
19994
|
+
const validation = question.validation ? questionValidations[question.validation] : null;
|
|
19995
|
+
const invalid = !!validation && !!value && !validation.validate(value);
|
|
19996
|
+
const aria = {
|
|
19997
|
+
"aria-label": label ?? plainText(question.title),
|
|
19998
|
+
"aria-invalid": invalid || void 0
|
|
19999
|
+
};
|
|
20000
|
+
const change = (text) => onAnswer(question, question.type === "mobile" ? text.replace(/[^0-9]/g, "") : text, void 0);
|
|
20001
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20002
|
+
className: "flex w-full flex-col gap-1",
|
|
20003
|
+
children: [question.type === "date" ? /* @__PURE__ */ jsx(DatePicker, {
|
|
20004
|
+
...aria,
|
|
20005
|
+
placeholder: "DD-MM-YYYY",
|
|
20006
|
+
value,
|
|
20007
|
+
onChange: change,
|
|
20008
|
+
disabled,
|
|
20009
|
+
className: clsx(fieldClass, "h-12")
|
|
20010
|
+
}) : multiline ? /* @__PURE__ */ jsx("textarea", {
|
|
20011
|
+
...aria,
|
|
20012
|
+
value,
|
|
20013
|
+
disabled,
|
|
20014
|
+
placeholder: validation?.placeholder ?? placeholder,
|
|
20015
|
+
onChange: (event) => change(event.target.value),
|
|
20016
|
+
className: clsx(fieldClass, "min-h-[121px] resize-y py-4 leading-5")
|
|
20017
|
+
}) : /* @__PURE__ */ jsx("input", {
|
|
20018
|
+
...aria,
|
|
20019
|
+
type: question.type === "mobile" ? "tel" : "text",
|
|
20020
|
+
value,
|
|
20021
|
+
disabled,
|
|
20022
|
+
placeholder: validation?.placeholder ?? placeholder,
|
|
20023
|
+
onChange: (event) => change(event.target.value),
|
|
20024
|
+
className: clsx(fieldClass, "h-12")
|
|
20025
|
+
}), invalid && /* @__PURE__ */ jsx("p", {
|
|
20026
|
+
className: "text-xs text-error-900",
|
|
20027
|
+
children: validation.errorMessage
|
|
20028
|
+
})]
|
|
20029
|
+
});
|
|
20030
|
+
};
|
|
20031
|
+
/** Checkbox options as small ticks. A question with one option draws as one tick with its label. */
|
|
20032
|
+
var Checks = ({ question, answers, optionHashes, onAnswer, disabled }) => /* @__PURE__ */ jsx("div", {
|
|
20033
|
+
className: "flex flex-col gap-2",
|
|
20034
|
+
children: question.options.map((option, index) => /* @__PURE__ */ jsxs("label", {
|
|
20035
|
+
className: "flex cursor-pointer items-center gap-3",
|
|
20036
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
20037
|
+
type: "checkbox",
|
|
20038
|
+
className: "h-[18px] w-[18px] cursor-pointer rounded border-gray-300 text-[#1a2f91] focus:ring-primary-700",
|
|
20039
|
+
checked: isOptionSelected(option, answers[question.hash], optionHashes[question.hash]),
|
|
20040
|
+
disabled,
|
|
20041
|
+
onChange: () => onAnswer(question, option.label, option.logic)
|
|
20042
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
20043
|
+
className: "text-sm font-normal text-gray-700",
|
|
20044
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(option.label) }
|
|
20045
|
+
})]
|
|
20046
|
+
}, `${option.label}-${index}`))
|
|
20047
|
+
});
|
|
20048
|
+
/** Radio options that are not Yes / No, as a list of round buttons. */
|
|
20049
|
+
var Radios = ({ question, answers, optionHashes, onAnswer, disabled }) => /* @__PURE__ */ jsx("div", {
|
|
20050
|
+
role: "radiogroup",
|
|
20051
|
+
"aria-label": plainText(question.title),
|
|
20052
|
+
className: "flex flex-col gap-2",
|
|
20053
|
+
children: question.options.map((option, index) => {
|
|
20054
|
+
const selected = isOptionSelected(option, answers[question.hash], optionHashes[question.hash]);
|
|
20055
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
20056
|
+
className: "flex cursor-pointer items-center gap-3",
|
|
20057
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
20058
|
+
type: "radio",
|
|
20059
|
+
name: question.hash,
|
|
20060
|
+
className: "h-[18px] w-[18px] cursor-pointer border-gray-300 text-primary-700 focus:ring-primary-700",
|
|
20061
|
+
checked: selected,
|
|
20062
|
+
disabled,
|
|
20063
|
+
onChange: () => onAnswer(question, option.label, option.logic)
|
|
20064
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
20065
|
+
className: "flex flex-col",
|
|
20066
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
20067
|
+
className: "text-sm font-normal text-gray-900",
|
|
20068
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(option.label) }
|
|
20069
|
+
}), option.helper && /* @__PURE__ */ jsx("span", {
|
|
20070
|
+
className: "text-xs font-normal text-gray-700",
|
|
20071
|
+
children: option.helper
|
|
20072
|
+
})]
|
|
20073
|
+
})]
|
|
20074
|
+
}, `${option.label}-${index}`);
|
|
20075
|
+
})
|
|
20076
|
+
});
|
|
20077
|
+
// sdk/src/ui/form/FormField.tsx
|
|
20078
|
+
/** One question of the one-page form with its open follow-ups, in the Prescriber App style. */
|
|
20079
|
+
var MissingNote = ({ show }) => show ? /* @__PURE__ */ jsx("p", {
|
|
20080
|
+
role: "alert",
|
|
20081
|
+
className: "text-xs text-error-900",
|
|
20082
|
+
children: "Answer this question."
|
|
20083
|
+
}) : null;
|
|
20084
|
+
/** The question's description as the grey hint under its label. */
|
|
20085
|
+
var Hint = ({ html }) => plainText(html) ? /* @__PURE__ */ jsx("div", {
|
|
20086
|
+
className: "text-xs font-normal tracking-[-0.072px] text-gray-700 [&_a]:underline [&_p]:m-0",
|
|
20087
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(html) }
|
|
20088
|
+
}) : null;
|
|
20089
|
+
var getOpenFollowUps = (hash, context) => {
|
|
20090
|
+
const question = context.questions.find((q) => q.hash === hash);
|
|
20091
|
+
return getFollowUpHashes(getMatchedOptionLogics(context.answers[hash], context.optionHashes[hash], question?.options ?? []));
|
|
20092
|
+
};
|
|
20093
|
+
/** The open follow-ups of a question, indented under it. */
|
|
20094
|
+
var FollowUps = ({ hash, context }) => {
|
|
20095
|
+
const followUps = getOpenFollowUps(hash, context);
|
|
20096
|
+
if (!followUps.length) return null;
|
|
20097
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20098
|
+
className: "mt-3 flex flex-col gap-4 border-l-2 border-primary-100 pl-4",
|
|
20099
|
+
children: followUps.map((followUp) => /* @__PURE__ */ jsx(FormField, {
|
|
20100
|
+
hash: followUp,
|
|
20101
|
+
context
|
|
20102
|
+
}, followUp))
|
|
20103
|
+
});
|
|
20104
|
+
};
|
|
20105
|
+
/** `spaced` adds the gap a field needs inside a section, where Yes / No rows sit with no gap. */
|
|
20106
|
+
var FormField = ({ hash, context, spaced }) => {
|
|
20107
|
+
const question = context.questions.find((q) => q.hash === hash);
|
|
20108
|
+
if (!question) return null;
|
|
20109
|
+
const input = {
|
|
20110
|
+
question,
|
|
20111
|
+
answers: context.answers,
|
|
20112
|
+
optionHashes: context.optionHashes,
|
|
20113
|
+
onAnswer: context.onAnswer
|
|
20114
|
+
};
|
|
20115
|
+
const disabled = context.disabled;
|
|
20116
|
+
const missing = context.missing.has(hash);
|
|
20117
|
+
if (isYesNo(question)) return /* @__PURE__ */ jsxs("div", {
|
|
20118
|
+
id: hash,
|
|
20119
|
+
className: "scroll-m-9 border-b border-gray-300 px-4 py-2",
|
|
20120
|
+
children: [
|
|
20121
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20122
|
+
className: "flex items-center gap-6",
|
|
20123
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
20124
|
+
className: "flex min-w-0 flex-1 flex-col",
|
|
20125
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
20126
|
+
className: "text-sm font-normal tracking-[-0.084px] text-gray-900",
|
|
20127
|
+
children: question.title
|
|
20128
|
+
}), /* @__PURE__ */ jsx(Hint, { html: question.description })]
|
|
20129
|
+
}), /* @__PURE__ */ jsx(Segmented, {
|
|
20130
|
+
...input,
|
|
20131
|
+
disabled
|
|
20132
|
+
})]
|
|
20133
|
+
}),
|
|
20134
|
+
/* @__PURE__ */ jsx(MissingNote, { show: missing }),
|
|
20135
|
+
/* @__PURE__ */ jsx(FollowUps, {
|
|
20136
|
+
hash,
|
|
20137
|
+
context
|
|
20138
|
+
})
|
|
20139
|
+
]
|
|
20140
|
+
});
|
|
20141
|
+
const supported = FILL_TYPES.includes(question.type);
|
|
20142
|
+
const singleCheck = question.type === "checkbox" && question.options.length === 1;
|
|
20143
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20144
|
+
id: hash,
|
|
20145
|
+
className: clsx("flex scroll-m-9 flex-col gap-1", spaced && "py-3"),
|
|
20146
|
+
children: [
|
|
20147
|
+
!singleCheck && /* @__PURE__ */ jsxs("div", {
|
|
20148
|
+
className: clsx("flex flex-col gap-px", [
|
|
20149
|
+
"radio",
|
|
20150
|
+
"checkbox",
|
|
20151
|
+
"scale"
|
|
20152
|
+
].includes(question.type) && "mb-1"),
|
|
20153
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
20154
|
+
className: "text-sm font-bold leading-5 text-gray-900",
|
|
20155
|
+
children: question.title
|
|
20156
|
+
}), /* @__PURE__ */ jsx(Hint, { html: question.description })]
|
|
20157
|
+
}),
|
|
20158
|
+
!supported && /* @__PURE__ */ jsxs("p", {
|
|
20159
|
+
className: "rounded-2xl border border-gray-300 bg-gray-50 p-4 text-sm font-normal text-gray-700",
|
|
20160
|
+
children: [
|
|
20161
|
+
"This question type (",
|
|
20162
|
+
question.type,
|
|
20163
|
+
") is shown in Connect only."
|
|
20164
|
+
]
|
|
20165
|
+
}),
|
|
20166
|
+
[
|
|
20167
|
+
"text",
|
|
20168
|
+
"mobile",
|
|
20169
|
+
"date"
|
|
20170
|
+
].includes(question.type) && /* @__PURE__ */ jsx(TextInput, {
|
|
20171
|
+
...input,
|
|
20172
|
+
disabled
|
|
20173
|
+
}),
|
|
20174
|
+
question.type === "textarea" && /* @__PURE__ */ jsx(TextInput, {
|
|
20175
|
+
...input,
|
|
20176
|
+
disabled,
|
|
20177
|
+
multiline: true
|
|
20178
|
+
}),
|
|
20179
|
+
question.type === "checkbox" && /* @__PURE__ */ jsx(Checks, {
|
|
20180
|
+
...input,
|
|
20181
|
+
disabled
|
|
20182
|
+
}),
|
|
20183
|
+
question.type === "scale" && /* @__PURE__ */ jsx(Segmented, {
|
|
20184
|
+
...input,
|
|
20185
|
+
disabled
|
|
20186
|
+
}),
|
|
20187
|
+
["radio", "radio_image"].includes(question.type) && /* @__PURE__ */ jsx(Radios, {
|
|
20188
|
+
...input,
|
|
20189
|
+
disabled
|
|
20190
|
+
}),
|
|
20191
|
+
/* @__PURE__ */ jsx(MissingNote, { show: missing }),
|
|
20192
|
+
/* @__PURE__ */ jsx(FollowUps, {
|
|
20193
|
+
hash,
|
|
20194
|
+
context
|
|
20195
|
+
})
|
|
20196
|
+
]
|
|
20197
|
+
});
|
|
20198
|
+
};
|
|
20199
|
+
/** A question as a table cell, with no label of its own. */
|
|
20200
|
+
var Cell = ({ hash, column, context }) => {
|
|
20201
|
+
const question = context.questions.find((q) => q.hash === hash);
|
|
20202
|
+
if (!question) return null;
|
|
20203
|
+
const input = {
|
|
20204
|
+
question,
|
|
20205
|
+
answers: context.answers,
|
|
20206
|
+
optionHashes: context.optionHashes,
|
|
20207
|
+
onAnswer: context.onAnswer,
|
|
20208
|
+
disabled: context.disabled,
|
|
20209
|
+
label: `${plainText(question.title)}: ${column}`
|
|
20210
|
+
};
|
|
20211
|
+
const placeholder = `Write ${column.toLowerCase()}`;
|
|
20212
|
+
let control = /* @__PURE__ */ jsx("span", {
|
|
20213
|
+
className: "text-sm text-gray-400",
|
|
20214
|
+
children: question.type
|
|
20215
|
+
});
|
|
20216
|
+
if (isYesNo(question) || question.type === "scale") control = /* @__PURE__ */ jsx(Segmented, { ...input });
|
|
20217
|
+
else if (["radio", "radio_image"].includes(question.type)) control = /* @__PURE__ */ jsx(Select, { ...input });
|
|
20218
|
+
else if ([
|
|
20219
|
+
"text",
|
|
20220
|
+
"textarea",
|
|
20221
|
+
"mobile",
|
|
20222
|
+
"date"
|
|
20223
|
+
].includes(question.type)) control = /* @__PURE__ */ jsx(TextInput, {
|
|
20224
|
+
...input,
|
|
20225
|
+
placeholder
|
|
20226
|
+
});
|
|
20227
|
+
else if (question.type === "checkbox") control = /* @__PURE__ */ jsx(Checks, { ...input });
|
|
20228
|
+
const missing = context.missing.has(hash);
|
|
20229
|
+
const pair = isYesNo(question) || question.type === "scale";
|
|
20230
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20231
|
+
id: hash,
|
|
20232
|
+
className: clsx("scroll-m-9 rounded-xl", pair ? "inline-flex" : "flex w-full", missing && "ring-2 ring-error-900 ring-offset-1"),
|
|
20233
|
+
children: [control, missing && /* @__PURE__ */ jsx("span", {
|
|
20234
|
+
role: "alert",
|
|
20235
|
+
className: "sr-only",
|
|
20236
|
+
children: "Answer this question."
|
|
20237
|
+
})]
|
|
20238
|
+
});
|
|
20239
|
+
};
|
|
20240
|
+
// sdk/src/ui/form/Blocks.tsx
|
|
20241
|
+
/** Sections, tables and tabs of the one-page form, drawn as in the Prescriber App Assessment design. */
|
|
20242
|
+
var Heading = ({ name, description }) => /* @__PURE__ */ jsxs("div", {
|
|
20243
|
+
className: "flex flex-col gap-px",
|
|
20244
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
20245
|
+
className: "text-sm font-bold leading-5 tracking-[-0.084px] text-gray-900",
|
|
20246
|
+
children: name
|
|
20247
|
+
}), plainText(description) && /* @__PURE__ */ jsx("div", {
|
|
20248
|
+
className: "text-xs font-normal tracking-[-0.072px] text-gray-700 [&_p]:m-0",
|
|
20249
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(description) }
|
|
20250
|
+
})]
|
|
20251
|
+
});
|
|
20252
|
+
/** "A1 Attention to detail" draws its code in bold, as the design does. */
|
|
20253
|
+
var RowLabel = ({ title }) => {
|
|
20254
|
+
const match = /^([A-Z]{1,3}\d+[a-z]?)\s+(.+)$/.exec(title.trim());
|
|
20255
|
+
return /* @__PURE__ */ jsx("p", {
|
|
20256
|
+
className: "text-sm font-normal leading-5 tracking-[-0.084px] text-gray-900",
|
|
20257
|
+
children: match ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
20258
|
+
/* @__PURE__ */ jsx("span", {
|
|
20259
|
+
className: "font-extrabold",
|
|
20260
|
+
children: match[1]
|
|
20261
|
+
}),
|
|
20262
|
+
" ",
|
|
20263
|
+
match[2]
|
|
20264
|
+
] }) : title
|
|
20265
|
+
});
|
|
20266
|
+
};
|
|
20267
|
+
/** A Yes / No pair sits in the middle of its column, under a centred heading. */
|
|
20268
|
+
var isPair = (hash, questions) => {
|
|
20269
|
+
const question = questions.find((q) => q.hash === hash);
|
|
20270
|
+
return isYesNo(question) || question?.type === "scale";
|
|
20271
|
+
};
|
|
20272
|
+
/** Column widths from the first row: a pair takes its own width, a dropdown a fixed one, text the space left. */
|
|
20273
|
+
var columnClass = (hash, questions) => {
|
|
20274
|
+
const question = questions.find((q) => q.hash === hash);
|
|
20275
|
+
if (isPair(hash, questions)) return "w-px whitespace-nowrap text-center";
|
|
20276
|
+
if (question?.type === "radio" || question?.type === "radio_image") return "w-[248px] min-w-[220px]";
|
|
20277
|
+
return "min-w-[240px]";
|
|
20278
|
+
};
|
|
20279
|
+
var Table = ({ table, context }) => {
|
|
20280
|
+
const cell = "py-2 pr-6 last:pr-0 lg:pr-12";
|
|
20281
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20282
|
+
className: "w-full overflow-x-auto rounded-3xl border border-gray-200 bg-gray-50 p-4",
|
|
20283
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
20284
|
+
className: "w-full min-w-[1000px] border-collapse text-left",
|
|
20285
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", {
|
|
20286
|
+
className: "border-b border-gray-300",
|
|
20287
|
+
children: table.columns.map((column, index) => /* @__PURE__ */ jsx("th", {
|
|
20288
|
+
scope: "col",
|
|
20289
|
+
className: clsx("py-4 pr-6 text-base font-bold leading-5 tracking-[-0.15px] text-[#364153] last:pr-0 lg:pr-12", index === 0 ? "w-[300px] min-w-[220px]" : columnClass(table.rows[0][index - 1], context.questions)),
|
|
20290
|
+
children: column
|
|
20291
|
+
}, `${column}-${index}`))
|
|
20292
|
+
}) }), /* @__PURE__ */ jsx("tbody", { children: table.rows.map((cells) => {
|
|
20293
|
+
const label = context.questions.find((q) => q.hash === cells[0])?.title ?? "";
|
|
20294
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
|
|
20295
|
+
className: "border-b border-gray-300",
|
|
20296
|
+
children: [/* @__PURE__ */ jsx("th", {
|
|
20297
|
+
scope: "row",
|
|
20298
|
+
className: clsx(cell, "font-normal"),
|
|
20299
|
+
children: /* @__PURE__ */ jsx(RowLabel, { title: label })
|
|
20300
|
+
}), cells.map((hash, index) => /* @__PURE__ */ jsx("td", {
|
|
20301
|
+
className: clsx(cell, "align-middle"),
|
|
20302
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
20303
|
+
className: clsx(isPair(hash, context.questions) && "flex justify-center"),
|
|
20304
|
+
children: /* @__PURE__ */ jsx(Cell, {
|
|
20305
|
+
hash,
|
|
20306
|
+
column: table.columns[index + 1],
|
|
20307
|
+
context
|
|
20308
|
+
})
|
|
20309
|
+
})
|
|
20310
|
+
}, hash))]
|
|
20311
|
+
}), cells.some((hash) => getOpenFollowUps(hash, context).length) && /* @__PURE__ */ jsx("tr", {
|
|
20312
|
+
className: "border-b border-gray-300",
|
|
20313
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
20314
|
+
colSpan: table.columns.length,
|
|
20315
|
+
className: "pb-3",
|
|
20316
|
+
children: cells.map((hash) => /* @__PURE__ */ jsx(FollowUps, {
|
|
20317
|
+
hash,
|
|
20318
|
+
context
|
|
20319
|
+
}, hash))
|
|
20320
|
+
})
|
|
20321
|
+
})] }, cells[0]);
|
|
20322
|
+
}) })]
|
|
20323
|
+
})
|
|
20324
|
+
});
|
|
20325
|
+
};
|
|
20326
|
+
var SectionBody = ({ section, context }) => section.table ? /* @__PURE__ */ jsx(Table, {
|
|
20327
|
+
table: section.table,
|
|
20328
|
+
context
|
|
20329
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
20330
|
+
className: "flex flex-col",
|
|
20331
|
+
children: section.questions.map((hash) => /* @__PURE__ */ jsx(FormField, {
|
|
20332
|
+
hash,
|
|
20333
|
+
context,
|
|
20334
|
+
spaced: true
|
|
20335
|
+
}, hash))
|
|
20336
|
+
});
|
|
20337
|
+
/** A section's questions with their open follow-ups, walking nested ones. */
|
|
20338
|
+
var sectionHashes = (section, context) => {
|
|
20339
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
20340
|
+
for (const hash of section.questions) withFollowUps(hash, context.questions, context.answers, context.optionHashes, hashes);
|
|
20341
|
+
return hashes;
|
|
20342
|
+
};
|
|
20343
|
+
var Tabs = ({ name, sections, context }) => {
|
|
20344
|
+
const [active, setActive] = useState(sections[0].id);
|
|
20345
|
+
const current = sections.find((section) => section.id === active) ?? sections[0];
|
|
20346
|
+
const needsAnswer = (section) => [...sectionHashes(section, context)].some((hash) => context.missing.has(hash));
|
|
20347
|
+
const focus = context.focus;
|
|
20348
|
+
const handledTick = useRef(focus?.tick ?? 0);
|
|
20349
|
+
useEffect(() => {
|
|
20350
|
+
if (!focus || focus.tick === handledTick.current) return;
|
|
20351
|
+
handledTick.current = focus.tick;
|
|
20352
|
+
const holder = sections.find((section) => sectionHashes(section, context).has(focus.hash));
|
|
20353
|
+
if (holder) setActive(holder.id);
|
|
20354
|
+
}, [focus?.tick]);
|
|
20355
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20356
|
+
className: "flex flex-col gap-6 rounded-3xl border border-gray-200 p-4",
|
|
20357
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
20358
|
+
className: "flex flex-col gap-4",
|
|
20359
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
20360
|
+
className: "py-4",
|
|
20361
|
+
children: /* @__PURE__ */ jsx(Heading, {
|
|
20362
|
+
name,
|
|
20363
|
+
description: current.description
|
|
20364
|
+
})
|
|
20365
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20366
|
+
role: "tablist",
|
|
20367
|
+
"aria-label": name,
|
|
20368
|
+
className: "flex gap-7 overflow-x-auto border-b border-gray-200",
|
|
20369
|
+
children: sections.map((section) => {
|
|
20370
|
+
const count = section.table?.rows.length ?? section.questions.length;
|
|
20371
|
+
const selected = section === current;
|
|
20372
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
20373
|
+
type: "button",
|
|
20374
|
+
role: "tab",
|
|
20375
|
+
"aria-selected": selected,
|
|
20376
|
+
onClick: () => setActive(section.id),
|
|
20377
|
+
className: clsx("relative h-12 whitespace-nowrap px-2 text-base font-normal tracking-[-0.31px]", selected ? "text-primary-700" : "text-gray-500 hover:text-gray-900"),
|
|
20378
|
+
children: [
|
|
20379
|
+
section.name,
|
|
20380
|
+
" (",
|
|
20381
|
+
count,
|
|
20382
|
+
")",
|
|
20383
|
+
needsAnswer(section) && /* @__PURE__ */ jsx("span", {
|
|
20384
|
+
className: "ml-1 text-error-900",
|
|
20385
|
+
children: "•"
|
|
20386
|
+
}),
|
|
20387
|
+
selected && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-primary-700" })
|
|
20388
|
+
]
|
|
20389
|
+
}, section.id);
|
|
20390
|
+
})
|
|
20391
|
+
})]
|
|
20392
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20393
|
+
role: "tabpanel",
|
|
20394
|
+
children: /* @__PURE__ */ jsx(SectionBody, {
|
|
20395
|
+
section: current,
|
|
20396
|
+
context
|
|
20397
|
+
})
|
|
20398
|
+
})]
|
|
20399
|
+
});
|
|
20400
|
+
};
|
|
20401
|
+
var Block = ({ block, context }) => {
|
|
20402
|
+
if (block.kind === "questions") return /* @__PURE__ */ jsx(Fragment$1, { children: block.questions.map((hash) => /* @__PURE__ */ jsx(FormField, {
|
|
20403
|
+
hash,
|
|
20404
|
+
context
|
|
20405
|
+
}, hash)) });
|
|
20406
|
+
if (block.kind === "tabs") return /* @__PURE__ */ jsx(Tabs, {
|
|
20407
|
+
name: block.name,
|
|
20408
|
+
sections: block.sections,
|
|
20409
|
+
context
|
|
20410
|
+
});
|
|
20411
|
+
const { section } = block;
|
|
20412
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20413
|
+
className: "flex flex-col gap-1 rounded-[32px] border border-gray-200 bg-white p-6",
|
|
20414
|
+
children: [/* @__PURE__ */ jsx(Heading, {
|
|
20415
|
+
name: section.name,
|
|
20416
|
+
description: section.description
|
|
20417
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20418
|
+
className: "mt-2",
|
|
20419
|
+
children: /* @__PURE__ */ jsx(SectionBody, {
|
|
20420
|
+
section,
|
|
20421
|
+
context
|
|
20422
|
+
})
|
|
20423
|
+
})]
|
|
20424
|
+
});
|
|
20425
|
+
};
|
|
20426
|
+
// sdk/src/ui/FormFlow.tsx
|
|
20427
|
+
/** The one-page form layout: every question the flow reaches, in the Prescriber App Assessment design. */
|
|
20428
|
+
/**
|
|
20429
|
+
* Keys that stay with a block when answers add or hide blocks or sections. A tab group keeps its key when its
|
|
20430
|
+
* first section changes, so the selected tab does not reset; a second group with the same name gets its own.
|
|
20431
|
+
*/
|
|
20432
|
+
var blockKeys = (blocks) => {
|
|
20433
|
+
const groups = /* @__PURE__ */ new Map();
|
|
20434
|
+
return blocks.map((block) => {
|
|
20435
|
+
if (block.kind === "questions") return `questions-${block.questions[0]}`;
|
|
20436
|
+
if (block.kind === "section") return `section-${block.section.id}`;
|
|
20437
|
+
const seen = groups.get(block.name) ?? 0;
|
|
20438
|
+
groups.set(block.name, seen + 1);
|
|
20439
|
+
return `tabs-${block.name}-${seen}`;
|
|
20440
|
+
});
|
|
20441
|
+
};
|
|
20442
|
+
var FormFlow = ({ loaded, adapter, consultationType, submitLabel = "Submit", onDone, onEvent, queueKey, onConflict, onSaved }) => {
|
|
20443
|
+
const { consultation, saved } = loaded;
|
|
20444
|
+
const allQuestions = useMemo(() => withReachableJumps(consultation.questions ?? []), [consultation.questions]);
|
|
20445
|
+
const visibleQuestions = useMemo(() => filterVisibleQuestions(allQuestions, {}), [allQuestions]);
|
|
20446
|
+
const [answers, setAnswers] = useState(saved.answers ?? {});
|
|
20447
|
+
const [optionHashes, setOptionHashes] = useState({});
|
|
20448
|
+
const [revision, setRevision] = useState(0);
|
|
20449
|
+
const [checked, setChecked] = useState(false);
|
|
20450
|
+
const [focus, setFocus] = useState(null);
|
|
20451
|
+
const container = useRef(null);
|
|
20452
|
+
const walk = {
|
|
20453
|
+
allQuestions,
|
|
20454
|
+
visibleQuestions,
|
|
20455
|
+
consultationHash: consultation.hash,
|
|
20456
|
+
answers,
|
|
20457
|
+
optionHashes
|
|
20458
|
+
};
|
|
20459
|
+
const form = useMemo(() => buildForm(walk, consultation), [
|
|
20460
|
+
allQuestions,
|
|
20461
|
+
visibleQuestions,
|
|
20462
|
+
consultation,
|
|
20463
|
+
answers,
|
|
20464
|
+
optionHashes
|
|
20465
|
+
]);
|
|
20466
|
+
const missing = useMemo(() => new Set(checked ? missingAnswers(form, walk) : []), [checked, form]);
|
|
20467
|
+
const events = useMemo(() => consultationEvents({
|
|
20468
|
+
consultationHash: consultation.hash,
|
|
20469
|
+
experimentOf: consultation.experiment_of,
|
|
20470
|
+
consultationType: consultationType ?? consultation.type ?? "treatment"
|
|
20471
|
+
}), [
|
|
20472
|
+
consultation.hash,
|
|
20473
|
+
consultation.experiment_of,
|
|
20474
|
+
consultation.type,
|
|
20475
|
+
consultationType
|
|
20476
|
+
]);
|
|
20477
|
+
const emitRef = useRef(onEvent);
|
|
20478
|
+
emitRef.current = onEvent;
|
|
20479
|
+
const submittedShown = useRef([]);
|
|
20480
|
+
const save = useTargetSave({
|
|
20481
|
+
adapter,
|
|
20482
|
+
queueKey,
|
|
20483
|
+
revision,
|
|
20484
|
+
latestDraft: () => formAnswers(form, walk),
|
|
20485
|
+
onDone,
|
|
20486
|
+
onConflict,
|
|
20487
|
+
onSaved,
|
|
20488
|
+
onSubmitted: () => {
|
|
20489
|
+
for (const hash of submittedShown.current) emitRef.current?.(events.questionAnswered(hash));
|
|
20490
|
+
emitRef.current?.(events.completed());
|
|
20491
|
+
}
|
|
20492
|
+
});
|
|
20493
|
+
const busy = save.submitState === "submitting" || save.submitState === "submitted";
|
|
20494
|
+
const started = useRef(false);
|
|
20495
|
+
useEffect(() => {
|
|
20496
|
+
if (started.current || Object.keys(saved.answers ?? {}).length || !visibleQuestions.length) return;
|
|
20497
|
+
started.current = true;
|
|
20498
|
+
emitRef.current?.(events.started());
|
|
20499
|
+
}, []);
|
|
20500
|
+
const viewed = useRef(/* @__PURE__ */ new Set());
|
|
20501
|
+
useEffect(() => {
|
|
20502
|
+
for (const hash of form.shown) {
|
|
20503
|
+
if (viewed.current.has(hash)) continue;
|
|
20504
|
+
viewed.current.add(hash);
|
|
20505
|
+
emitRef.current?.(events.questionViewed(hash));
|
|
20506
|
+
}
|
|
20507
|
+
}, [form.shown, events]);
|
|
20508
|
+
useEffect(() => {
|
|
20509
|
+
if (!focus) return;
|
|
20510
|
+
const frame = requestAnimationFrame(() => container.current?.querySelector(`[id="${CSS.escape(focus.hash)}"]`)?.scrollIntoView?.({ behavior: "smooth" }));
|
|
20511
|
+
return () => cancelAnimationFrame(frame);
|
|
20512
|
+
}, [focus]);
|
|
20513
|
+
const exitHash = form.exit?.questionHash ?? null;
|
|
20514
|
+
useEffect(() => {
|
|
20515
|
+
if (!exitHash) return;
|
|
20516
|
+
emitRef.current?.(events.questionAnswered(exitHash));
|
|
20517
|
+
emitRef.current?.(events.exited());
|
|
20518
|
+
}, [exitHash, events]);
|
|
20519
|
+
const answer = (question, value, logic) => {
|
|
20520
|
+
if (busy) return;
|
|
20521
|
+
const result = applyAnswer({
|
|
20522
|
+
question,
|
|
20523
|
+
state: {
|
|
20524
|
+
answers,
|
|
20525
|
+
optionHashes
|
|
20526
|
+
},
|
|
20527
|
+
value,
|
|
20528
|
+
logic,
|
|
20529
|
+
ownsProgression: false
|
|
20530
|
+
});
|
|
20531
|
+
if (!result) return;
|
|
20532
|
+
setAnswers(result.answers);
|
|
20533
|
+
setOptionHashes(result.optionHashes);
|
|
20534
|
+
setRevision((value) => value + 1);
|
|
20535
|
+
};
|
|
20536
|
+
const submit = () => {
|
|
20537
|
+
if (!adapter) return;
|
|
20538
|
+
setChecked(true);
|
|
20539
|
+
if (form.exit) {
|
|
20540
|
+
save.rejectSubmit("One answer ends the consultation. Change it before you submit.");
|
|
20541
|
+
return;
|
|
20542
|
+
}
|
|
20543
|
+
const open = missingAnswers(form, walk);
|
|
20544
|
+
if (open.length) {
|
|
20545
|
+
save.rejectSubmit(`${open.length} ${open.length === 1 ? "question has" : "questions have"} no answer. Answer them before you submit.`);
|
|
20546
|
+
setFocus((previous) => ({
|
|
20547
|
+
hash: open[0],
|
|
20548
|
+
tick: (previous?.tick ?? 0) + 1
|
|
20549
|
+
}));
|
|
20550
|
+
return;
|
|
20551
|
+
}
|
|
20552
|
+
submittedShown.current = form.shown;
|
|
20553
|
+
save.submit(formAnswers(form, walk));
|
|
20554
|
+
};
|
|
20555
|
+
if (!visibleQuestions.length) return /* @__PURE__ */ jsx("p", {
|
|
20556
|
+
className: "py-6 text-sm text-gray-600",
|
|
20557
|
+
children: "This consultation has no questions."
|
|
20558
|
+
});
|
|
20559
|
+
const context = {
|
|
20560
|
+
consultationHash: consultation.hash,
|
|
20561
|
+
questions: allQuestions,
|
|
20562
|
+
answers,
|
|
20563
|
+
optionHashes,
|
|
20564
|
+
onAnswer: answer,
|
|
20565
|
+
disabled: busy,
|
|
20566
|
+
missing,
|
|
20567
|
+
focus
|
|
20568
|
+
};
|
|
20569
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20570
|
+
ref: container,
|
|
20571
|
+
className: "flex flex-col gap-[26px] rounded-[32px] border border-gray-200 bg-white p-4 font-normal text-gray-900 sm:p-6",
|
|
20572
|
+
children: [
|
|
20573
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20574
|
+
className: "flex flex-wrap items-center justify-between gap-3",
|
|
20575
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
20576
|
+
className: "text-xl font-bold leading-[30px] text-[#111]",
|
|
20577
|
+
children: consultation.title
|
|
20578
|
+
}), adapter && save.submitState !== "submitted" && /* @__PURE__ */ jsxs("div", {
|
|
20579
|
+
className: "flex items-center gap-2",
|
|
20580
|
+
children: [
|
|
20581
|
+
/* @__PURE__ */ jsx("span", {
|
|
20582
|
+
className: "text-xs text-gray-500",
|
|
20583
|
+
"aria-live": "polite",
|
|
20584
|
+
children: save.saveState === "saving" ? "Saving…" : save.saveState === "saved" ? "Draft saved" : ""
|
|
20585
|
+
}),
|
|
20586
|
+
/* @__PURE__ */ jsx(Button, {
|
|
20587
|
+
variant: "outlined",
|
|
20588
|
+
fullWidth: false,
|
|
20589
|
+
disabled: busy,
|
|
20590
|
+
onClick: save.saveDraft,
|
|
20591
|
+
className: "!h-10 !rounded-2xl !border-primary-700 !px-4 !py-0 text-sm !font-bold normal-case",
|
|
20592
|
+
children: "Save draft"
|
|
20593
|
+
}),
|
|
20594
|
+
/* @__PURE__ */ jsx(Button, {
|
|
20595
|
+
fullWidth: false,
|
|
20596
|
+
loading: save.submitState === "submitting",
|
|
20597
|
+
disabled: busy,
|
|
20598
|
+
onClick: submit,
|
|
20599
|
+
className: "!h-10 !rounded-2xl !px-4 !py-0 text-sm !font-bold normal-case !shadow-none",
|
|
20600
|
+
children: submitLabel
|
|
20601
|
+
})
|
|
20602
|
+
]
|
|
20603
|
+
})]
|
|
20604
|
+
}),
|
|
20605
|
+
save.submitState === "submitted" && /* @__PURE__ */ jsx("p", {
|
|
20606
|
+
className: "text-base font-bold",
|
|
20607
|
+
children: "The answers are submitted."
|
|
20608
|
+
}),
|
|
20609
|
+
save.submitError && /* @__PURE__ */ jsx("p", {
|
|
20610
|
+
className: "text-sm text-error-900",
|
|
20611
|
+
children: save.submitError
|
|
20612
|
+
}),
|
|
20613
|
+
save.saveState === "error" && /* @__PURE__ */ jsx(ErrorNotice, {
|
|
20614
|
+
message: "Your last answer could not be saved.",
|
|
20615
|
+
onRetry: save.saveDraft
|
|
20616
|
+
}),
|
|
20617
|
+
(() => {
|
|
20618
|
+
const keys = blockKeys(form.blocks);
|
|
20619
|
+
return form.blocks.map((block, index) => /* @__PURE__ */ jsx(Block, {
|
|
20620
|
+
block,
|
|
20621
|
+
context
|
|
20622
|
+
}, keys[index]));
|
|
20623
|
+
})(),
|
|
20624
|
+
form.exit && /* @__PURE__ */ jsxs("div", {
|
|
20625
|
+
role: "alert",
|
|
20626
|
+
className: "rounded-2xl border border-warning-300 bg-warning-100 p-4",
|
|
20627
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
20628
|
+
className: "mb-1 text-sm font-bold",
|
|
20629
|
+
children: form.exit.title || "No suitable treatments available"
|
|
20630
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20631
|
+
className: "text-sm [&_a]:underline [&_p]:mb-2 [&_ul]:ml-4 [&_ul]:list-disc",
|
|
20632
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(form.exit.content ?? "This answer ends the consultation. Change it to go on.") }
|
|
20633
|
+
})]
|
|
20634
|
+
})
|
|
20635
|
+
]
|
|
20636
|
+
});
|
|
20637
|
+
};
|
|
20638
|
+
// sdk/src/ui/Knockout.tsx
|
|
20639
|
+
/** Connect's exit page (`ExitConsultation.tsx`) as a screen inside the component. */
|
|
20640
|
+
var Knockout = ({ title, content, onReview, onClose }) => /* @__PURE__ */ jsxs("div", {
|
|
20641
|
+
className: "flex flex-col items-start py-8 max-w-2xl",
|
|
20642
|
+
children: [
|
|
20643
|
+
/* @__PURE__ */ jsx("h1", {
|
|
20644
|
+
className: "text-xl md:text-3xl font-bold mb-4",
|
|
20645
|
+
children: title || "No suitable treatments available"
|
|
20646
|
+
}),
|
|
20647
|
+
/* @__PURE__ */ jsx("div", {
|
|
20648
|
+
className: "text-sm sm:text-base mb-8 [&_a]:underline [&_ul]:list-disc [&_ul]:ml-4 [&_li]:ml-4 [&_b]:font-bold [&_strong]:font-bold [&_p]:mb-3",
|
|
20649
|
+
dangerouslySetInnerHTML: { __html: sanitize$1(content ?? "You are not eligible for this treatment. Please exit the consultation or review your answers.") }
|
|
20650
|
+
}),
|
|
20651
|
+
/* @__PURE__ */ jsx(Button, {
|
|
20652
|
+
className: "w-full mb-4 text-md font-bold normal-case bg-primary-700 rounded-3xl",
|
|
20653
|
+
onClick: onReview,
|
|
20654
|
+
children: "Review answer"
|
|
20655
|
+
}),
|
|
20656
|
+
onClose && /* @__PURE__ */ jsx(Button, {
|
|
20657
|
+
variant: "text",
|
|
20658
|
+
className: "w-full text-sm normal-case font-bold text-gray-900 rounded-3xl",
|
|
20659
|
+
onClick: onClose,
|
|
20660
|
+
children: "Exit Consultation"
|
|
20661
|
+
})
|
|
20662
|
+
]
|
|
20663
|
+
});
|
|
19507
20664
|
// sdk/src/ui/ReadOnlyAnswers.tsx
|
|
19508
20665
|
var isHttpsUrl = (value) => {
|
|
19509
20666
|
try {
|
|
@@ -19571,7 +20728,7 @@ var ReadOnlyAnswers = ({ questions, answers, submittedAt }) => {
|
|
|
19571
20728
|
});
|
|
19572
20729
|
};
|
|
19573
20730
|
// sdk/src/styles.css?inline
|
|
19574
|
-
var styles_default = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #E9EAED; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n\ninput:where([type='text']),input:where(:not([type])),input:where([type='email']),input:where([type='url']),input:where([type='password']),input:where([type='number']),input:where([type='date']),input:where([type='datetime-local']),input:where([type='month']),input:where([type='search']),input:where([type='tel']),input:where([type='time']),input:where([type='week']),select:where([multiple]),textarea,select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n border-radius: 0px;\n padding-top: 0.5rem;\n padding-right: 0.75rem;\n padding-bottom: 0.5rem;\n padding-left: 0.75rem;\n font-size: 1rem;\n line-height: 1.5rem;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='text']):focus, input:where(:not([type])):focus, input:where([type='email']):focus, input:where([type='url']):focus, input:where([type='password']):focus, input:where([type='number']):focus, input:where([type='date']):focus, input:where([type='datetime-local']):focus, input:where([type='month']):focus, input:where([type='search']):focus, input:where([type='tel']):focus, input:where([type='time']):focus, input:where([type='week']):focus, select:where([multiple]):focus, textarea:focus, select:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n border-color: #2563eb;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\ninput::placeholder,textarea::placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n\n::-webkit-date-and-time-value {\n min-height: 1.5em;\n text-align: inherit;\n}\n\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n\n::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {\n padding-top: 0;\n padding-bottom: 0;\n}\n\nselect {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236D7684' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e\");\n background-position: right 0.5rem center;\n background-repeat: no-repeat;\n background-size: 1.5em 1.5em;\n padding-right: 2.5rem;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n}\n\nselect:where([multiple]),select:where([size]:not([size=\"1\"])) {\n background-image: initial;\n background-position: initial;\n background-repeat: unset;\n background-size: initial;\n padding-right: 0.75rem;\n -webkit-print-color-adjust: unset;\n print-color-adjust: unset;\n}\n\ninput:where([type='checkbox']),input:where([type='radio']) {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding: 0;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n display: inline-block;\n vertical-align: middle;\n background-origin: border-box;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n flex-shrink: 0;\n height: 1rem;\n width: 1rem;\n color: #2563eb;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='checkbox']) {\n border-radius: 0px;\n}\n\ninput:where([type='radio']) {\n border-radius: 100%;\n}\n\ninput:where([type='checkbox']):focus,input:where([type='radio']):focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 2px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n\ninput:where([type='checkbox']):checked,input:where([type='radio']):checked {\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\ninput:where([type='checkbox']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='radio']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='radio']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):checked:hover,input:where([type='checkbox']):checked:focus,input:where([type='radio']):checked:hover,input:where([type='radio']):checked:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='checkbox']):indeterminate {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e\");\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):indeterminate {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):indeterminate:hover,input:where([type='checkbox']):indeterminate:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='file']) {\n background: unset;\n border-color: inherit;\n border-width: 0;\n border-radius: 0;\n padding: 0;\n font-size: unset;\n line-height: inherit;\n}\n\ninput:where([type='file']):focus {\n outline: 1px solid ButtonText;\n outline: 1px auto -webkit-focus-ring-color;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 600px) {\n\n .container {\n max-width: 600px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 900px) {\n\n .container {\n max-width: 900px;\n }\n}\n@media (min-width: 1000px) {\n\n .container {\n max-width: 1000px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.m-\\[1px\\] {\n margin: 1px;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-3 {\n margin-top: 0.75rem;\n margin-bottom: 0.75rem;\n}\n.my-4 {\n margin-top: 1rem;\n margin-bottom: 1rem;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-10 {\n margin-bottom: 2.5rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mr-auto {\n margin-right: auto;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-6 {\n margin-top: 1.5rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.mt-px {\n margin-top: 1px;\n}\n.block {\n display: block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.table {\n display: table;\n}\n.hidden {\n display: none;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-full {\n height: 100%;\n}\n.min-h-6 {\n min-height: 1.5rem;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-\\[calc\\(50\\%-8px\\)\\] {\n width: calc(50% - 8px);\n}\n.w-full {\n width: 100%;\n}\n.min-w-6 {\n min-width: 1.5rem;\n}\n.max-w-2xl {\n max-width: 42rem;\n}\n.max-w-\\[600px\\] {\n max-width: 600px;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes spin {\n\n to {\n transform: rotate(360deg);\n }\n}\n.animate-spin {\n animation: spin 1s linear infinite;\n}\n.cursor-not-allowed {\n cursor: not-allowed;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize {\n resize: both;\n}\n.scroll-m-9 {\n scroll-margin: 2.25rem;\n}\n.flex-col {\n flex-direction: column;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-start {\n align-items: flex-start;\n}\n.\\!items-end {\n align-items: flex-end !important;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.gap-5 {\n gap: 1.25rem;\n}\n.self-start {\n align-self: flex-start;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.break-words {\n overflow-wrap: break-word;\n}\n.rounded-3xl {\n border-radius: 1.5rem;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: 0.5rem;\n}\n.rounded-md {\n border-radius: 0.375rem;\n}\n.border {\n border-width: 1px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-solid {\n border-style: solid;\n}\n.border-none {\n border-style: none;\n}\n.\\!border-gray-100 {\n --tw-border-opacity: 1 !important;\n border-color: rgb(240 241 243 / var(--tw-border-opacity, 1)) !important;\n}\n.border-error-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 196 178 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(233 234 237 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(210 213 217 / var(--tw-border-opacity, 1));\n}\n.border-primary {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-900 {\n border-color: var(--color-primary-900, #022DB7);\n}\n.border-success {\n --tw-border-opacity: 1;\n border-color: rgb(73 183 21 / var(--tw-border-opacity, 1));\n}\n.\\!bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA) !important;\n}\n.bg-error-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 224 214 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 241 243 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.bg-primary-700 {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-transparent {\n background-color: transparent;\n}\n.object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.\\!py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-\\[10px\\] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.py-16 {\n padding-top: 4rem;\n padding-bottom: 4rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3\\.5 {\n padding-top: 0.875rem;\n padding-bottom: 0.875rem;\n}\n.py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n.py-6 {\n padding-top: 1.5rem;\n padding-bottom: 1.5rem;\n}\n.py-8 {\n padding-top: 2rem;\n padding-bottom: 2rem;\n}\n.py-\\[10px\\] {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.pb-4 {\n padding-bottom: 1rem;\n}\n.pb-\\[calc\\(50\\%-8px\\)\\] {\n padding-bottom: calc(50% - 8px);\n}\n.pr-2 {\n padding-right: 0.5rem;\n}\n.text-left {\n text-align: left;\n}\n.font-primary {\n font-family: var(--font-primary), serif;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xl {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 500;\n}\n.font-extrabold {\n font-weight: 600;\n}\n.font-medium {\n font-weight: 300;\n}\n.font-normal {\n font-weight: 400;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.normal-case {\n text-transform: none;\n}\n.tracking-\\[-0\\.176px\\] {\n letter-spacing: -0.176px;\n}\n.tracking-\\[-0\\.27px\\] {\n letter-spacing: -0.27px;\n}\n.\\!text-error {\n --tw-text-opacity: 1 !important;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1)) !important;\n}\n.\\!text-primary-300 {\n color: var(--color-primary-300, #ABBBED) !important;\n}\n.text-black {\n --tw-text-opacity: 1;\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\n}\n.text-error {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-error-900 {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(109 118 132 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(98 106 119 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(78 84 94 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(57 62 69 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-700 {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-900 {\n color: var(--color-primary-900, #022DB7);\n}\n.text-secondary {\n color: var(--color-secondary-700, #080F30);\n}\n.text-success {\n --tw-text-opacity: 1;\n color: rgb(73 183 21 / var(--tw-text-opacity, 1));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline {\n text-decoration-line: underline;\n}\n.no-underline {\n text-decoration-line: none;\n}\n.opacity-25 {\n opacity: 0.25;\n}\n.opacity-60 {\n opacity: 0.6;\n}\n.opacity-75 {\n opacity: 0.75;\n}\n.\\!shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.\\!shadow-none {\n --tw-shadow: 0 0 #0000 !important;\n --tw-shadow-colored: 0 0 #0000 !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.outline {\n outline-style: solid;\n}\n.outline-1 {\n outline-width: 1px;\n}\n.outline-offset-0 {\n outline-offset: 0px;\n}\n.\\!outline-error {\n outline-color: #FF4D1A !important;\n}\n.outline-gray-300 {\n outline-color: #D2D5D9;\n}\n.\\!ring-0 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n\n/* Preflight styles html and body, which a shadow root does not have, so the base type goes on the host. */\n:host {\n display: block;\n --font-primary:\n \"Poppins\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial,\n sans-serif;\n --font-heading: \"Fraunces\", Georgia, serif;\n font-family: var(--font-primary);\n line-height: 1.5;\n text-align: left;\n color: #24272b;\n -webkit-font-smoothing: antialiased;\n}\n.placeholder\\:text-gray-400::-moz-placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.placeholder\\:text-gray-400::placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.focus-within\\:outline-2:focus-within {\n outline-width: 2px;\n}\n.focus-within\\:outline-primary:focus-within {\n outline-color: var(--color-primary-700, #153CB7);\n}\n.hover\\:border-primary-900:hover {\n border-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary-100:hover {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.hover\\:bg-primary-900:hover {\n background-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:text-primary-900:hover {\n color: var(--color-primary-900, #022DB7);\n}\n.hover\\:outline-gray-700:hover {\n outline-color: #4E545E;\n}\n@media (min-width: 600px) {\n\n .sm\\:mt-14 {\n margin-top: 3.5rem;\n }\n\n .sm\\:text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n }\n\n .sm\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .sm\\:text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n }\n\n .sm\\:text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n }\n}\n@media (min-width: 768px) {\n\n .md\\:mb-10 {\n margin-bottom: 2.5rem;\n }\n\n .md\\:mt-10 {\n margin-top: 2.5rem;\n }\n\n .md\\:mt-12 {\n margin-top: 3rem;\n }\n\n .md\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .md\\:text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n\n .md\\:leading-\\[22px\\] {\n line-height: 22px;\n }\n}\n.\\[\\&_a\\]\\:underline a {\n text-decoration-line: underline;\n}\n.\\[\\&_b\\]\\:font-bold b {\n font-weight: 500;\n}\n.\\[\\&_h3\\]\\:mb-2 h3 {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_h3\\]\\:mt-4 h3 {\n margin-top: 1rem;\n}\n.\\[\\&_h3\\]\\:text-base h3 {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.\\[\\&_h3\\]\\:font-bold h3 {\n font-weight: 500;\n}\n@media (min-width: 600px) {\n\n .sm\\:\\[\\&_h3\\]\\:text-lg h3 {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n}\n.\\[\\&_h4\\]\\:mb-1 h4 {\n margin-bottom: 0.25rem;\n}\n.\\[\\&_h4\\]\\:mt-4 h4 {\n margin-top: 1rem;\n}\n.\\[\\&_h4\\]\\:font-bold h4 {\n font-weight: 500;\n}\n.\\[\\&_img\\]\\:mx-auto img {\n margin-left: auto;\n margin-right: auto;\n}\n.\\[\\&_img\\]\\:my-1 img {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.\\[\\&_img\\]\\:max-w-full img {\n max-width: 100%;\n}\n.\\[\\&_li\\]\\:ml-4 li {\n margin-left: 1rem;\n}\n.\\[\\&_p\\:not\\(\\:last-child\\)\\]\\:mb-2 p:not(:last-child) {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:mb-3 p {\n margin-bottom: 0.75rem;\n}\n.\\[\\&_strong\\]\\:font-bold strong {\n font-weight: 500;\n}\n.\\[\\&_ul\\]\\:mb-2 ul {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_ul\\]\\:ml-4 ul {\n margin-left: 1rem;\n}\n.\\[\\&_ul\\]\\:list-disc ul {\n list-style-type: disc;\n}\n";
|
|
20731
|
+
var styles_default = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #E9EAED; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #A0A3A6; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n\ninput:where([type='text']),input:where(:not([type])),input:where([type='email']),input:where([type='url']),input:where([type='password']),input:where([type='number']),input:where([type='date']),input:where([type='datetime-local']),input:where([type='month']),input:where([type='search']),input:where([type='tel']),input:where([type='time']),input:where([type='week']),select:where([multiple]),textarea,select {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n border-radius: 0px;\n padding-top: 0.5rem;\n padding-right: 0.75rem;\n padding-bottom: 0.5rem;\n padding-left: 0.75rem;\n font-size: 1rem;\n line-height: 1.5rem;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='text']):focus, input:where(:not([type])):focus, input:where([type='email']):focus, input:where([type='url']):focus, input:where([type='password']):focus, input:where([type='number']):focus, input:where([type='date']):focus, input:where([type='datetime-local']):focus, input:where([type='month']):focus, input:where([type='search']):focus, input:where([type='tel']):focus, input:where([type='time']):focus, input:where([type='week']):focus, select:where([multiple]):focus, textarea:focus, select:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n border-color: #2563eb;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\ninput::placeholder,textarea::placeholder {\n color: #6D7684;\n opacity: 1;\n}\n\n::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n}\n\n::-webkit-date-and-time-value {\n min-height: 1.5em;\n text-align: inherit;\n}\n\n::-webkit-datetime-edit {\n display: inline-flex;\n}\n\n::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {\n padding-top: 0;\n padding-bottom: 0;\n}\n\nselect {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236D7684' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e\");\n background-position: right 0.5rem center;\n background-repeat: no-repeat;\n background-size: 1.5em 1.5em;\n padding-right: 2.5rem;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n}\n\nselect:where([multiple]),select:where([size]:not([size=\"1\"])) {\n background-image: initial;\n background-position: initial;\n background-repeat: unset;\n background-size: initial;\n padding-right: 0.75rem;\n -webkit-print-color-adjust: unset;\n print-color-adjust: unset;\n}\n\ninput:where([type='checkbox']),input:where([type='radio']) {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n padding: 0;\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n display: inline-block;\n vertical-align: middle;\n background-origin: border-box;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n flex-shrink: 0;\n height: 1rem;\n width: 1rem;\n color: #2563eb;\n background-color: #fff;\n border-color: #6D7684;\n border-width: 1px;\n --tw-shadow: 0 0 #0000;\n}\n\ninput:where([type='checkbox']) {\n border-radius: 0px;\n}\n\ninput:where([type='radio']) {\n border-radius: 100%;\n}\n\ninput:where([type='checkbox']):focus,input:where([type='radio']):focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);\n --tw-ring-offset-width: 2px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: #2563eb;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n}\n\ninput:where([type='checkbox']):checked,input:where([type='radio']):checked {\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\ninput:where([type='checkbox']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='radio']):checked {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e\");\n}\n\n@media (forced-colors: active) {\n\n input:where([type='radio']):checked {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):checked:hover,input:where([type='checkbox']):checked:focus,input:where([type='radio']):checked:hover,input:where([type='radio']):checked:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='checkbox']):indeterminate {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e\");\n border-color: transparent;\n background-color: currentColor;\n background-size: 100% 100%;\n background-position: center;\n background-repeat: no-repeat;\n}\n\n@media (forced-colors: active) {\n\n input:where([type='checkbox']):indeterminate {\n -webkit-appearance: auto;\n -moz-appearance: auto;\n appearance: auto;\n }\n}\n\ninput:where([type='checkbox']):indeterminate:hover,input:where([type='checkbox']):indeterminate:focus {\n border-color: transparent;\n background-color: currentColor;\n}\n\ninput:where([type='file']) {\n background: unset;\n border-color: inherit;\n border-width: 0;\n border-radius: 0;\n padding: 0;\n font-size: unset;\n line-height: inherit;\n}\n\ninput:where([type='file']):focus {\n outline: 1px solid ButtonText;\n outline: 1px auto -webkit-focus-ring-color;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 600px) {\n\n .container {\n max-width: 600px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 900px) {\n\n .container {\n max-width: 900px;\n }\n}\n@media (min-width: 1000px) {\n\n .container {\n max-width: 1000px;\n }\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.inset-x-0 {\n left: 0px;\n right: 0px;\n}\n.bottom-0 {\n bottom: 0px;\n}\n.z-10 {\n z-index: 10;\n}\n.m-\\[1px\\] {\n margin: 1px;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-3 {\n margin-top: 0.75rem;\n margin-bottom: 0.75rem;\n}\n.my-4 {\n margin-top: 1rem;\n margin-bottom: 1rem;\n}\n.-ml-px {\n margin-left: -1px;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-10 {\n margin-bottom: 2.5rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-1 {\n margin-left: 0.25rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mr-auto {\n margin-right: auto;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-2 {\n margin-top: 0.5rem;\n}\n.mt-3 {\n margin-top: 0.75rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-6 {\n margin-top: 1.5rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.mt-px {\n margin-top: 1px;\n}\n.block {\n display: block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.table {\n display: table;\n}\n.hidden {\n display: none;\n}\n.\\!h-10 {\n height: 2.5rem !important;\n}\n.h-0\\.5 {\n height: 0.125rem;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-12 {\n height: 3rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-\\[18px\\] {\n height: 18px;\n}\n.h-\\[39px\\] {\n height: 39px;\n}\n.h-full {\n height: 100%;\n}\n.min-h-6 {\n min-height: 1.5rem;\n}\n.min-h-\\[121px\\] {\n min-height: 121px;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-\\[18px\\] {\n width: 18px;\n}\n.w-\\[248px\\] {\n width: 248px;\n}\n.w-\\[300px\\] {\n width: 300px;\n}\n.w-\\[calc\\(50\\%-8px\\)\\] {\n width: calc(50% - 8px);\n}\n.w-full {\n width: 100%;\n}\n.w-px {\n width: 1px;\n}\n.min-w-0 {\n min-width: 0px;\n}\n.min-w-6 {\n min-width: 1.5rem;\n}\n.min-w-\\[1000px\\] {\n min-width: 1000px;\n}\n.min-w-\\[220px\\] {\n min-width: 220px;\n}\n.min-w-\\[240px\\] {\n min-width: 240px;\n}\n.min-w-\\[51px\\] {\n min-width: 51px;\n}\n.max-w-2xl {\n max-width: 42rem;\n}\n.max-w-\\[600px\\] {\n max-width: 600px;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n.shrink-0 {\n flex-shrink: 0;\n}\n.grow {\n flex-grow: 1;\n}\n.border-collapse {\n border-collapse: collapse;\n}\n.rotate-180 {\n --tw-rotate: 180deg;\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n@keyframes spin {\n\n to {\n transform: rotate(360deg);\n }\n}\n.animate-spin {\n animation: spin 1s linear infinite;\n}\n.cursor-not-allowed {\n cursor: not-allowed;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize-y {\n resize: vertical;\n}\n.resize {\n resize: both;\n}\n.scroll-m-9 {\n scroll-margin: 2.25rem;\n}\n.flex-col {\n flex-direction: column;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-start {\n align-items: flex-start;\n}\n.\\!items-end {\n align-items: flex-end !important;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.justify-between {\n justify-content: space-between;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.gap-5 {\n gap: 1.25rem;\n}\n.gap-6 {\n gap: 1.5rem;\n}\n.gap-7 {\n gap: 1.75rem;\n}\n.gap-\\[26px\\] {\n gap: 26px;\n}\n.gap-px {\n gap: 1px;\n}\n.self-start {\n align-self: flex-start;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-x-auto {\n overflow-x: auto;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.whitespace-pre-wrap {\n white-space: pre-wrap;\n}\n.break-words {\n overflow-wrap: break-word;\n}\n.\\!rounded-2xl {\n border-radius: 1rem !important;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.rounded-2xl {\n border-radius: 1rem;\n}\n.rounded-3xl {\n border-radius: 1.5rem;\n}\n.rounded-\\[32px\\] {\n border-radius: 32px;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: 0.5rem;\n}\n.rounded-md {\n border-radius: 0.375rem;\n}\n.rounded-xl {\n border-radius: 0.75rem;\n}\n.rounded-l-xl {\n border-top-left-radius: 0.75rem;\n border-bottom-left-radius: 0.75rem;\n}\n.rounded-r-xl {\n border-top-right-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem;\n}\n.border {\n border-width: 1px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-l-2 {\n border-left-width: 2px;\n}\n.border-solid {\n border-style: solid;\n}\n.border-none {\n border-style: none;\n}\n.\\!border-gray-100 {\n --tw-border-opacity: 1 !important;\n border-color: rgb(240 241 243 / var(--tw-border-opacity, 1)) !important;\n}\n.\\!border-primary-700 {\n border-color: var(--color-primary-700, #153CB7) !important;\n}\n.border-error-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 196 178 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(233 234 237 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(210 213 217 / var(--tw-border-opacity, 1));\n}\n.border-primary {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-100 {\n border-color: var(--color-primary-100, #EBEFFA);\n}\n.border-primary-700 {\n border-color: var(--color-primary-700, #153CB7);\n}\n.border-primary-900 {\n border-color: var(--color-primary-900, #022DB7);\n}\n.border-success {\n --tw-border-opacity: 1;\n border-color: rgb(73 183 21 / var(--tw-border-opacity, 1));\n}\n.border-warning-300 {\n --tw-border-opacity: 1;\n border-color: rgb(255 221 153 / var(--tw-border-opacity, 1));\n}\n.\\!bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA) !important;\n}\n.bg-error-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 224 214 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 241 243 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-primary-100 {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.bg-primary-700 {\n background-color: var(--color-primary-700, #153CB7);\n}\n.bg-transparent {\n background-color: transparent;\n}\n.bg-warning-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(255 241 214 / var(--tw-bg-opacity, 1));\n}\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n.object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.p-6 {\n padding: 1.5rem;\n}\n.\\!px-4 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n}\n.\\!py-0 {\n padding-top: 0px !important;\n padding-bottom: 0px !important;\n}\n.\\!py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n.px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.px-3\\.5 {\n padding-left: 0.875rem;\n padding-right: 0.875rem;\n}\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-\\[10px\\] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.py-16 {\n padding-top: 4rem;\n padding-bottom: 4rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.py-3\\.5 {\n padding-top: 0.875rem;\n padding-bottom: 0.875rem;\n}\n.py-4 {\n padding-top: 1rem;\n padding-bottom: 1rem;\n}\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n.py-6 {\n padding-top: 1.5rem;\n padding-bottom: 1.5rem;\n}\n.py-8 {\n padding-top: 2rem;\n padding-bottom: 2rem;\n}\n.py-\\[10px\\] {\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.pb-3 {\n padding-bottom: 0.75rem;\n}\n.pb-4 {\n padding-bottom: 1rem;\n}\n.pb-\\[calc\\(50\\%-8px\\)\\] {\n padding-bottom: calc(50% - 8px);\n}\n.pl-4 {\n padding-left: 1rem;\n}\n.pr-10 {\n padding-right: 2.5rem;\n}\n.pr-2 {\n padding-right: 0.5rem;\n}\n.pr-6 {\n padding-right: 1.5rem;\n}\n.text-left {\n text-align: left;\n}\n.text-center {\n text-align: center;\n}\n.align-middle {\n vertical-align: middle;\n}\n.font-primary {\n font-family: var(--font-primary), serif;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xl {\n font-size: 1.25rem;\n line-height: 1.75rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.\\!font-bold {\n font-weight: 500 !important;\n}\n.font-bold {\n font-weight: 500;\n}\n.font-extrabold {\n font-weight: 600;\n}\n.font-medium {\n font-weight: 300;\n}\n.font-normal {\n font-weight: 400;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.normal-case {\n text-transform: none;\n}\n.leading-5 {\n line-height: 1.25rem;\n}\n.leading-\\[30px\\] {\n line-height: 30px;\n}\n.tracking-\\[-0\\.072px\\] {\n letter-spacing: -0.072px;\n}\n.tracking-\\[-0\\.084px\\] {\n letter-spacing: -0.084px;\n}\n.tracking-\\[-0\\.15px\\] {\n letter-spacing: -0.15px;\n}\n.tracking-\\[-0\\.176px\\] {\n letter-spacing: -0.176px;\n}\n.tracking-\\[-0\\.27px\\] {\n letter-spacing: -0.27px;\n}\n.tracking-\\[-0\\.31px\\] {\n letter-spacing: -0.31px;\n}\n.\\!text-error {\n --tw-text-opacity: 1 !important;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1)) !important;\n}\n.\\!text-primary-300 {\n color: var(--color-primary-300, #ABBBED) !important;\n}\n.text-\\[\\#111\\] {\n --tw-text-opacity: 1;\n color: rgb(17 17 17 / var(--tw-text-opacity, 1));\n}\n.text-\\[\\#1a2f91\\] {\n --tw-text-opacity: 1;\n color: rgb(26 47 145 / var(--tw-text-opacity, 1));\n}\n.text-\\[\\#364153\\] {\n --tw-text-opacity: 1;\n color: rgb(54 65 83 / var(--tw-text-opacity, 1));\n}\n.text-black {\n --tw-text-opacity: 1;\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\n}\n.text-error {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-error-900 {\n --tw-text-opacity: 1;\n color: rgb(255 77 26 / var(--tw-text-opacity, 1));\n}\n.text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(109 118 132 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(98 106 119 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(78 84 94 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(57 62 69 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-700 {\n color: var(--color-primary-700, #153CB7);\n}\n.text-primary-900 {\n color: var(--color-primary-900, #022DB7);\n}\n.text-secondary {\n color: var(--color-secondary-700, #080F30);\n}\n.text-success {\n --tw-text-opacity: 1;\n color: rgb(73 183 21 / var(--tw-text-opacity, 1));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline {\n text-decoration-line: underline;\n}\n.no-underline {\n text-decoration-line: none;\n}\n.opacity-25 {\n opacity: 0.25;\n}\n.opacity-60 {\n opacity: 0.6;\n}\n.opacity-75 {\n opacity: 0.75;\n}\n.\\!shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.\\!shadow-none {\n --tw-shadow: 0 0 #0000 !important;\n --tw-shadow-colored: 0 0 #0000 !important;\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline-none {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.outline {\n outline-style: solid;\n}\n.outline-1 {\n outline-width: 1px;\n}\n.outline-offset-0 {\n outline-offset: 0px;\n}\n.\\!outline-error {\n outline-color: #FF4D1A !important;\n}\n.outline-gray-300 {\n outline-color: #D2D5D9;\n}\n.\\!ring-0 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important;\n}\n.ring-2 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\n.ring-error-900 {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(255 77 26 / var(--tw-ring-opacity, 1));\n}\n.ring-offset-1 {\n --tw-ring-offset-width: 1px;\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-colors {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n\n/* Preflight styles html and body, which a shadow root does not have, so the base type goes on the host. */\n:host {\n display: block;\n --font-primary:\n \"Poppins\", system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial,\n sans-serif;\n --font-heading: \"Fraunces\", Georgia, serif;\n font-family: var(--font-primary);\n line-height: 1.5;\n text-align: left;\n color: #24272b;\n -webkit-font-smoothing: antialiased;\n}\n.placeholder\\:text-gray-400::-moz-placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.placeholder\\:text-gray-400::placeholder {\n --tw-text-opacity: 1;\n color: rgb(160 163 166 / var(--tw-text-opacity, 1));\n}\n.last\\:pr-0:last-child {\n padding-right: 0px;\n}\n.focus-within\\:outline-2:focus-within {\n outline-width: 2px;\n}\n.focus-within\\:outline-primary:focus-within {\n outline-color: var(--color-primary-700, #153CB7);\n}\n.hover\\:border-primary-900:hover {\n border-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(233 234 237 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(250 250 251 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary-100:hover {\n background-color: var(--color-primary-100, #EBEFFA);\n}\n.hover\\:bg-primary-900:hover {\n background-color: var(--color-primary-900, #022DB7);\n}\n.hover\\:text-gray-900:hover {\n --tw-text-opacity: 1;\n color: rgb(36 39 43 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-primary-900:hover {\n color: var(--color-primary-900, #022DB7);\n}\n.hover\\:outline-gray-700:hover {\n outline-color: #4E545E;\n}\n.focus\\:border-primary-700:focus {\n border-color: var(--color-primary-700, #153CB7);\n}\n.focus\\:ring-primary-700:focus {\n --tw-ring-color: var(--color-primary-700, #153CB7);\n}\n.disabled\\:opacity-60:disabled {\n opacity: 0.6;\n}\n.has-\\[\\:focus-visible\\]\\:ring-2:has(:focus-visible) {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\n.has-\\[\\:focus-visible\\]\\:ring-primary-300:has(:focus-visible) {\n --tw-ring-color: var(--color-primary-300, #ABBBED);\n}\n@media (min-width: 600px) {\n\n .sm\\:mt-14 {\n margin-top: 3.5rem;\n }\n\n .sm\\:p-6 {\n padding: 1.5rem;\n }\n\n .sm\\:text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n }\n\n .sm\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .sm\\:text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n }\n\n .sm\\:text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n }\n}\n@media (min-width: 768px) {\n\n .md\\:mb-10 {\n margin-bottom: 2.5rem;\n }\n\n .md\\:mt-10 {\n margin-top: 2.5rem;\n }\n\n .md\\:mt-12 {\n margin-top: 3rem;\n }\n\n .md\\:text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n }\n\n .md\\:text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n\n .md\\:leading-\\[22px\\] {\n line-height: 22px;\n }\n}\n@media (min-width: 900px) {\n\n .lg\\:pr-12 {\n padding-right: 3rem;\n }\n}\n.\\[\\&_a\\]\\:underline a {\n text-decoration-line: underline;\n}\n.\\[\\&_b\\]\\:font-bold b {\n font-weight: 500;\n}\n.\\[\\&_h3\\]\\:mb-2 h3 {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_h3\\]\\:mt-4 h3 {\n margin-top: 1rem;\n}\n.\\[\\&_h3\\]\\:text-base h3 {\n font-size: 1rem;\n line-height: 1.5rem;\n}\n.\\[\\&_h3\\]\\:font-bold h3 {\n font-weight: 500;\n}\n@media (min-width: 600px) {\n\n .sm\\:\\[\\&_h3\\]\\:text-lg h3 {\n font-size: 1.125rem;\n line-height: 1.75rem;\n }\n}\n.\\[\\&_h4\\]\\:mb-1 h4 {\n margin-bottom: 0.25rem;\n}\n.\\[\\&_h4\\]\\:mt-4 h4 {\n margin-top: 1rem;\n}\n.\\[\\&_h4\\]\\:font-bold h4 {\n font-weight: 500;\n}\n.\\[\\&_img\\]\\:mx-auto img {\n margin-left: auto;\n margin-right: auto;\n}\n.\\[\\&_img\\]\\:my-1 img {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.\\[\\&_img\\]\\:max-w-full img {\n max-width: 100%;\n}\n.\\[\\&_li\\]\\:ml-4 li {\n margin-left: 1rem;\n}\n.\\[\\&_p\\:not\\(\\:last-child\\)\\]\\:mb-2 p:not(:last-child) {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:m-0 p {\n margin: 0px;\n}\n.\\[\\&_p\\]\\:mb-2 p {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_p\\]\\:mb-3 p {\n margin-bottom: 0.75rem;\n}\n.\\[\\&_strong\\]\\:font-bold strong {\n font-weight: 500;\n}\n.\\[\\&_ul\\]\\:mb-2 ul {\n margin-bottom: 0.5rem;\n}\n.\\[\\&_ul\\]\\:ml-4 ul {\n margin-left: 1rem;\n}\n.\\[\\&_ul\\]\\:list-disc ul {\n list-style-type: disc;\n}\n";
|
|
19575
20732
|
// sdk/src/ui/ShadowRoot.tsx
|
|
19576
20733
|
var FONT_LINK_ID = "evaro-consultation-fonts";
|
|
19577
20734
|
var FONT_URL = "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600&family=Poppins:wght@300;400;500;600&display=swap";
|
|
@@ -19626,7 +20783,8 @@ var EMPTY_SAVED = {
|
|
|
19626
20783
|
answers: {},
|
|
19627
20784
|
status: "empty",
|
|
19628
20785
|
consultationVersion: null,
|
|
19629
|
-
submittedAt: null
|
|
20786
|
+
submittedAt: null,
|
|
20787
|
+
version: null
|
|
19630
20788
|
};
|
|
19631
20789
|
var themeStyle = (theme) => {
|
|
19632
20790
|
const style = {};
|
|
@@ -19636,41 +20794,72 @@ var themeStyle = (theme) => {
|
|
|
19636
20794
|
}
|
|
19637
20795
|
return style;
|
|
19638
20796
|
};
|
|
19639
|
-
var
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
|
|
19644
|
-
|
|
19645
|
-
|
|
19646
|
-
className: "self-start !py-2",
|
|
19647
|
-
onClick: onRetry,
|
|
19648
|
-
children: "Try again"
|
|
19649
|
-
})]
|
|
19650
|
-
});
|
|
20797
|
+
var targetApiKey = (environment, nodeApiUrl) => {
|
|
20798
|
+
try {
|
|
20799
|
+
return nodeApiBase(environment, nodeApiUrl);
|
|
20800
|
+
} catch {
|
|
20801
|
+
return `invalid:${environment}:${nodeApiUrl ?? ""}`;
|
|
20802
|
+
}
|
|
20803
|
+
};
|
|
19651
20804
|
function Consultation(props) {
|
|
19652
|
-
const { consultation: hash, vendor, environment, target, getToken, readOnly, preview } = props;
|
|
20805
|
+
const { consultation: hash, vendor, environment, target, getToken, nodeApiUrl, readOnly, preview, layout = "steps" } = props;
|
|
19653
20806
|
const [loaded, setLoaded] = useState(null);
|
|
19654
20807
|
const [loadError, setLoadError] = useState(null);
|
|
19655
20808
|
const [attempt, setAttempt] = useState(0);
|
|
19656
20809
|
const getTokenRef = useRef(getToken);
|
|
19657
20810
|
getTokenRef.current = getToken;
|
|
19658
20811
|
const targetKey = target ? `${target.type}:${target.id}` : "";
|
|
19659
|
-
const
|
|
20812
|
+
const targetApi = target ? targetApiKey(environment, nodeApiUrl) : "";
|
|
20813
|
+
const adapter = useMemo(() => target ? createTargetAdapter(target, environment, () => getTokenRef.current?.() ?? null, nodeApiUrl) : null, [targetKey, targetApi]);
|
|
20814
|
+
const previewFlag = !!preview;
|
|
20815
|
+
const readOnlyFlag = !!readOnly;
|
|
20816
|
+
const loadKey = JSON.stringify([
|
|
20817
|
+
hash,
|
|
20818
|
+
vendor,
|
|
20819
|
+
environment,
|
|
20820
|
+
previewFlag,
|
|
20821
|
+
targetKey,
|
|
20822
|
+
targetApi,
|
|
20823
|
+
readOnlyFlag,
|
|
20824
|
+
layout
|
|
20825
|
+
]);
|
|
20826
|
+
const queueKey = JSON.stringify([targetApi, targetKey]);
|
|
20827
|
+
const shownQueueKey = useRef(queueKey);
|
|
20828
|
+
shownQueueKey.current = queueKey;
|
|
20829
|
+
const [conflict, setConflict] = useState(false);
|
|
20830
|
+
const savedOnly = useRef(false);
|
|
20831
|
+
const lastLoaded = useRef(null);
|
|
20832
|
+
const onConflict = useCallback(() => {
|
|
20833
|
+
if (shownQueueKey.current !== queueKey) return;
|
|
20834
|
+
savedOnly.current = true;
|
|
20835
|
+
setConflict(true);
|
|
20836
|
+
setAttempt((value) => value + 1);
|
|
20837
|
+
}, [queueKey]);
|
|
20838
|
+
const onSaved = useCallback(() => {
|
|
20839
|
+
if (shownQueueKey.current === queueKey) setConflict(false);
|
|
20840
|
+
}, [queueKey]);
|
|
20841
|
+
useEffect(() => setConflict(false), [loadKey]);
|
|
19660
20842
|
useEffect(() => {
|
|
19661
20843
|
let cancelled = false;
|
|
19662
20844
|
setLoaded(null);
|
|
19663
20845
|
setLoadError(null);
|
|
20846
|
+
const afterDrafts = () => draftQueueIdle(queueKey);
|
|
20847
|
+
const reuse = savedOnly.current && lastLoaded.current?.key === loadKey ? lastLoaded.current : null;
|
|
20848
|
+
savedOnly.current = false;
|
|
19664
20849
|
Promise.resolve().then(() => Promise.all([
|
|
19665
|
-
fetchVendorConsultation(environment, hash, vendor,
|
|
19666
|
-
fetchVendorTheme(environment, vendor).catch(() => null),
|
|
19667
|
-
adapter ? adapter.load() : Promise.resolve(EMPTY_SAVED)
|
|
20850
|
+
reuse?.consultation ?? fetchVendorConsultation(environment, hash, vendor, previewFlag),
|
|
20851
|
+
reuse ? reuse.theme : fetchVendorTheme(environment, vendor).catch(() => null),
|
|
20852
|
+
adapter ? afterDrafts().then(() => adapter.load()) : Promise.resolve(EMPTY_SAVED)
|
|
19668
20853
|
])).then(([consultation, theme, saved]) => {
|
|
19669
|
-
if (
|
|
20854
|
+
if (cancelled) return;
|
|
20855
|
+
lastLoaded.current = {
|
|
20856
|
+
key: loadKey,
|
|
20857
|
+
attempt,
|
|
19670
20858
|
consultation,
|
|
19671
20859
|
theme,
|
|
19672
20860
|
saved
|
|
19673
|
-
}
|
|
20861
|
+
};
|
|
20862
|
+
setLoaded(lastLoaded.current);
|
|
19674
20863
|
}).catch((error) => {
|
|
19675
20864
|
if (!cancelled) setLoadError(errorMessage(error));
|
|
19676
20865
|
});
|
|
@@ -19681,8 +20870,11 @@ function Consultation(props) {
|
|
|
19681
20870
|
hash,
|
|
19682
20871
|
vendor,
|
|
19683
20872
|
environment,
|
|
19684
|
-
|
|
20873
|
+
previewFlag,
|
|
19685
20874
|
adapter,
|
|
20875
|
+
queueKey,
|
|
20876
|
+
readOnlyFlag,
|
|
20877
|
+
layout,
|
|
19686
20878
|
attempt
|
|
19687
20879
|
]);
|
|
19688
20880
|
return /* @__PURE__ */ jsx(ShadowRoot, {
|
|
@@ -19694,25 +20886,40 @@ function Consultation(props) {
|
|
|
19694
20886
|
message: `The consultation could not be loaded. ${loadError}`,
|
|
19695
20887
|
onRetry: () => setAttempt((value) => value + 1)
|
|
19696
20888
|
}),
|
|
19697
|
-
|
|
20889
|
+
loaded?.key !== loadKey && !loadError && /* @__PURE__ */ jsx("div", {
|
|
19698
20890
|
className: "flex justify-center py-16 text-primary",
|
|
19699
20891
|
"aria-busy": "true",
|
|
19700
20892
|
children: /* @__PURE__ */ jsx(CircularProgress, { size: 48 })
|
|
19701
20893
|
}),
|
|
19702
|
-
|
|
20894
|
+
conflict && loaded?.key === loadKey && /* @__PURE__ */ jsx("p", {
|
|
20895
|
+
role: "status",
|
|
20896
|
+
className: "my-4 rounded-lg border border-warning-300 bg-warning-100 p-4 text-sm text-gray-900",
|
|
20897
|
+
children: "The answers changed in another place. The saved answers are shown again."
|
|
20898
|
+
}),
|
|
20899
|
+
loaded?.key === loadKey && (readOnly || loaded.saved.status === "submitted" ? /* @__PURE__ */ jsx(ReadOnlyAnswers, {
|
|
19703
20900
|
questions: loaded.consultation.questions ?? [],
|
|
19704
20901
|
answers: loaded.saved.answers,
|
|
19705
20902
|
submittedAt: loaded.saved.submittedAt
|
|
19706
|
-
}) : /* @__PURE__ */ jsx(
|
|
20903
|
+
}) : layout === "form" ? /* @__PURE__ */ jsx(FormFlow, {
|
|
19707
20904
|
...props,
|
|
19708
20905
|
loaded,
|
|
19709
|
-
adapter
|
|
19710
|
-
|
|
20906
|
+
adapter,
|
|
20907
|
+
queueKey,
|
|
20908
|
+
onConflict,
|
|
20909
|
+
onSaved
|
|
20910
|
+
}, `${loadKey}:${loaded.attempt}`) : /* @__PURE__ */ jsx(Flow, {
|
|
20911
|
+
...props,
|
|
20912
|
+
loaded,
|
|
20913
|
+
adapter,
|
|
20914
|
+
queueKey,
|
|
20915
|
+
onConflict,
|
|
20916
|
+
onSaved
|
|
20917
|
+
}, `${loadKey}:${loaded.attempt}`))
|
|
19711
20918
|
]
|
|
19712
20919
|
})
|
|
19713
20920
|
});
|
|
19714
20921
|
}
|
|
19715
|
-
var Flow = ({ loaded, adapter, consultationType, onDone, onEvent }) => {
|
|
20922
|
+
var Flow = ({ loaded, adapter, consultationType, onDone, onEvent, queueKey, onConflict, onSaved }) => {
|
|
19716
20923
|
const { consultation, saved } = loaded;
|
|
19717
20924
|
const emit = useCallback((event) => onEvent?.(event), [onEvent]);
|
|
19718
20925
|
const flow = useConsultationFlow({
|
|
@@ -19722,78 +20929,28 @@ var Flow = ({ loaded, adapter, consultationType, onDone, onEvent }) => {
|
|
|
19722
20929
|
isNewStart: !Object.keys(saved.answers ?? {}).length,
|
|
19723
20930
|
emit
|
|
19724
20931
|
});
|
|
19725
|
-
const
|
|
19726
|
-
const [submitState, setSubmitState] = useState("idle");
|
|
19727
|
-
const [submitError, setSubmitError] = useState(null);
|
|
19728
|
-
const latestDraft = useRef(null);
|
|
19729
|
-
latestDraft.current = () => draftAnswers(flow.history, {
|
|
19730
|
-
allQuestions: flow.allQuestions,
|
|
19731
|
-
visibleQuestions: flow.questions,
|
|
19732
|
-
consultationHash: consultation.hash,
|
|
19733
|
-
answers: flow.answers,
|
|
19734
|
-
optionHashes: flow.optionHashes
|
|
19735
|
-
});
|
|
19736
|
-
const draftTimer = useRef(null);
|
|
19737
|
-
const draftQueue = useRef(Promise.resolve());
|
|
19738
|
-
const closed = useRef(false);
|
|
19739
|
-
const draftSeq = useRef(0);
|
|
19740
|
-
const saveDraft = useCallback(() => {
|
|
19741
|
-
if (!adapter) return;
|
|
19742
|
-
const seq = ++draftSeq.current;
|
|
19743
|
-
const isLatest = () => seq === draftSeq.current;
|
|
19744
|
-
setSaveState("saving");
|
|
19745
|
-
const request = draftQueue.current.then(async () => {
|
|
19746
|
-
if (closed.current) return false;
|
|
19747
|
-
await adapter.saveDraft(latestDraft.current());
|
|
19748
|
-
return true;
|
|
19749
|
-
});
|
|
19750
|
-
draftQueue.current = request.catch(() => void 0);
|
|
19751
|
-
request.then((sent) => {
|
|
19752
|
-
if (!isLatest()) return;
|
|
19753
|
-
setSaveState((state) => sent ? "saved" : state === "saving" ? "idle" : state);
|
|
19754
|
-
}, () => {
|
|
19755
|
-
if (isLatest()) setSaveState("error");
|
|
19756
|
-
});
|
|
19757
|
-
}, [adapter]);
|
|
19758
|
-
useEffect(() => {
|
|
19759
|
-
if (!adapter || flow.revision === 0 || closed.current) return;
|
|
19760
|
-
draftTimer.current = setTimeout(saveDraft, 600);
|
|
19761
|
-
return () => clearTimeout(draftTimer.current);
|
|
19762
|
-
}, [
|
|
20932
|
+
const { saveState, submitState, submitError, saveDraft, ...save } = useTargetSave({
|
|
19763
20933
|
adapter,
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
|
|
19767
|
-
|
|
20934
|
+
queueKey,
|
|
20935
|
+
revision: flow.revision,
|
|
20936
|
+
latestDraft: () => draftAnswers(flow.history, {
|
|
20937
|
+
allQuestions: flow.allQuestions,
|
|
20938
|
+
visibleQuestions: flow.questions,
|
|
20939
|
+
consultationHash: consultation.hash,
|
|
20940
|
+
answers: flow.answers,
|
|
20941
|
+
optionHashes: flow.optionHashes
|
|
20942
|
+
}),
|
|
20943
|
+
onDone,
|
|
20944
|
+
onConflict,
|
|
20945
|
+
onSaved
|
|
20946
|
+
});
|
|
20947
|
+
const submit = () => {
|
|
19768
20948
|
if (!adapter) return;
|
|
19769
20949
|
if (!isPathComplete(flow.history, flow.allQuestions, flow.answers, flow.optionHashes)) {
|
|
19770
|
-
|
|
19771
|
-
setSubmitError("Some questions have no answer. Go back and answer them before you submit.");
|
|
20950
|
+
save.rejectSubmit("Some questions have no answer. Go back and answer them before you submit.");
|
|
19772
20951
|
return;
|
|
19773
20952
|
}
|
|
19774
|
-
|
|
19775
|
-
clearTimeout(draftTimer.current);
|
|
19776
|
-
setSubmitState("submitting");
|
|
19777
|
-
setSubmitError(null);
|
|
19778
|
-
const submitted = () => {
|
|
19779
|
-
setSubmitState("submitted");
|
|
19780
|
-
setSaveState("idle");
|
|
19781
|
-
onDone?.();
|
|
19782
|
-
};
|
|
19783
|
-
try {
|
|
19784
|
-
await draftQueue.current;
|
|
19785
|
-
await adapter.submit(answersOnPath(flow.history, flow.allQuestions, flow.answers, flow.optionHashes));
|
|
19786
|
-
submitted();
|
|
19787
|
-
} catch (error) {
|
|
19788
|
-
if (await adapter.load().then((saved) => saved.status, () => null) === "submitted") {
|
|
19789
|
-
submitted();
|
|
19790
|
-
return;
|
|
19791
|
-
}
|
|
19792
|
-
setSubmitState("error");
|
|
19793
|
-
setSubmitError(`The answers could not be submitted. ${errorMessage(error)}`);
|
|
19794
|
-
closed.current = false;
|
|
19795
|
-
saveDraft();
|
|
19796
|
-
}
|
|
20953
|
+
save.submit(answersOnPath(flow.history, flow.allQuestions, flow.answers, flow.optionHashes));
|
|
19797
20954
|
};
|
|
19798
20955
|
const progress = flow.phase === "complete" ? 1 : flow.questions.length ? (flow.questionIndex + 1) / flow.questions.length : 0;
|
|
19799
20956
|
const busy = submitState === "submitting";
|