|
2 | 2 | import type { SlDrawer } from '@shoelace-style/shoelace';
|
3 | 3 |
|
4 | 4 | import { getCommonStore } from '$store/common.svelte.ts';
|
| 5 | + import { getLoginStore } from '$store/login.svelte.ts'; |
| 6 | +
|
| 7 | + const CUSTOM_UPLOAD_ELEMENT_ID = '95c6807c-82b4-4208-b5df-5a3e795227b0'; |
5 | 8 |
|
6 | 9 | const API_URL = import.meta.env.VITE_API_URL;
|
7 | 10 | type Props = {
|
8 | 11 | display: Boolean;
|
9 | 12 | entityId: string | undefined;
|
10 | 13 | projectId: number | undefined;
|
| 14 | + taskId: number | undefined; |
11 | 15 | webFormsRef: HTMLElement | undefined;
|
12 | 16 | };
|
13 | 17 | const commonStore = getCommonStore();
|
14 |
| - let { display = $bindable(false), entityId, webFormsRef = $bindable(undefined), projectId }: Props = $props(); |
| 18 | + const loginStore = getLoginStore(); |
| 19 | +
|
| 20 | + let { display = $bindable(false), entityId, webFormsRef = $bindable(undefined), projectId, taskId }: Props = $props(); |
15 | 21 | let drawerRef: SlDrawer;
|
16 |
| - let iframeRef: HTMLIFrameElement; |
| 22 | + let iframeRef: HTMLIFrameElement | undefined; |
17 | 23 | let odkForm: any;
|
18 |
| - // to-do: hide drawer upon successful submission |
| 24 | + let startDate: string | undefined; |
| 25 | +
|
| 26 | + let pic: any; |
| 27 | +
|
| 28 | + function handleMutation() { |
| 29 | + if (!iframeRef) return; |
| 30 | + if (!iframeRef.contentDocument) return; |
| 31 | + if (!odkForm) return; |
| 32 | + if (!webFormsRef) return; |
| 33 | +
|
| 34 | + // check if we've already added the custom upload input |
| 35 | + if (iframeRef.contentDocument.getElementById(CUSTOM_UPLOAD_ELEMENT_ID)) return; |
| 36 | + console.log('over-writing control node'); |
| 37 | +
|
| 38 | + const uploadControl = odkForm.getChildren().find((n: any) => n.constructor.name === 'UploadControl'); |
| 39 | + const uploadControlNodeId = uploadControl.nodeId; |
| 40 | + const uploadControlNodeSelector = `[id='${uploadControlNodeId}_container']`; |
| 41 | + const uploadControlNode = webFormsRef.querySelector(uploadControlNodeSelector); |
| 42 | + if (!uploadControlNode) return; |
| 43 | +
|
| 44 | + const controlText = iframeRef.contentDocument.createElement('div'); |
| 45 | + controlText.id = CUSTOM_UPLOAD_ELEMENT_ID; |
| 46 | + controlText.className = 'control-text'; |
| 47 | + controlText.style.marginBottom = '.75rem'; |
| 48 | +
|
| 49 | + const label = iframeRef.contentDocument.createElement('label'); |
| 50 | + label.style.fontWeight = '400'; |
| 51 | + label.style.lineHeight = '1.8rem'; |
| 52 | + const span = iframeRef.contentDocument.createElement('span'); |
| 53 | + span.textContent = uploadControl.engineState.label.chunks.map((chunk: any) => chunk.asString).join(' '); |
| 54 | + label.appendChild(span); |
| 55 | + controlText.appendChild(label); |
| 56 | + const inputWrapper = iframeRef.contentDocument.createElement('div'); |
| 57 | + const input = iframeRef.contentDocument.createElement('input'); |
| 58 | + input.id = CUSTOM_UPLOAD_ELEMENT_ID; |
| 59 | + input.addEventListener('change', () => { |
| 60 | + pic = input.files?.[0]; |
| 61 | + }); |
| 62 | + input.type = 'file'; |
| 63 | + inputWrapper.appendChild(input); |
| 64 | +
|
| 65 | + uploadControlNode.innerHTML = ''; |
| 66 | + uploadControlNode.appendChild(controlText); |
| 67 | + uploadControlNode.appendChild(inputWrapper); |
| 68 | + } |
19 | 69 | function handleSubmit(payload: any) {
|
20 | 70 | (async () => {
|
21 | 71 | if (!payload.detail) return;
|
| 72 | +
|
| 73 | + let submission_xml = await payload.detail[0].data.instanceFile.text(); |
| 74 | +
|
| 75 | + submission_xml = submission_xml.replace('<start/>', `<start>${startDate}</start>`); |
| 76 | + submission_xml = submission_xml.replace('<end/>', `<end>${new Date().toISOString()}</end>`); |
| 77 | +
|
| 78 | + const authDetails = loginStore?.getAuthDetails; |
| 79 | + if (authDetails?.username) { |
| 80 | + submission_xml = submission_xml.replace('<username/>', `<username>${authDetails?.username}</username>`); |
| 81 | + } |
| 82 | +
|
| 83 | + if (authDetails?.email_address) { |
| 84 | + submission_xml = submission_xml.replace('<email/>', `<email>${authDetails?.email_address}</email>`); |
| 85 | + } |
| 86 | +
|
| 87 | + if (pic && pic?.name) { |
| 88 | + submission_xml = submission_xml.replace('<image/>', `<image>${pic?.name}</image>`); |
| 89 | + } |
| 90 | +
|
22 | 91 | const url = `${API_URL}/submission?project_id=${projectId}`;
|
23 | 92 | var data = new FormData();
|
24 |
| - data.append('submission_xml', await payload.detail[0].data.instanceFile.text()); |
| 93 | + data.append('submission_xml', submission_xml); |
| 94 | + data.append('submission_files', pic); |
25 | 95 | await fetch(url, {
|
26 | 96 | method: 'POST',
|
27 | 97 | body: data,
|
|
35 | 105 | console.log('set odkForm', odkForm);
|
36 | 106 | // over-write default language
|
37 | 107 | setFormLanguage(commonStore.locale);
|
| 108 | +
|
| 109 | + const nodes = odkForm.getChildren(); |
| 110 | +
|
38 | 111 | // select feature that you clicked on map
|
39 |
| - odkForm |
40 |
| - .getChildren() |
41 |
| - .find((it: any) => it.definition.nodeset === '/data/feature') |
42 |
| - ?.setValueState([entityId]); |
| 112 | + nodes.find((it: any) => it.definition.nodeset === '/data/feature')?.setValueState([entityId]); |
| 113 | + nodes.find((it: any) => it.definition.nodeset === '/data/task_id')?.setValueState(`${taskId}`); |
43 | 114 | }
|
44 | 115 | }
|
45 | 116 | const setFormLanguage = (newLocale: string) => {
|
|
51 | 122 | }
|
52 | 123 | }
|
53 | 124 | };
|
| 125 | + $effect(() => { |
| 126 | + // making sure we re-run whenever one of the following reactive variables changes |
| 127 | + display; |
| 128 | + projectId; |
| 129 | + entityId; |
| 130 | + if (display) { |
| 131 | + startDate = new Date().toISOString(); |
| 132 | + } |
| 133 | + }); |
54 | 134 | $effect(() => {
|
55 | 135 | if (commonStore.locale) {
|
56 | 136 | setFormLanguage(commonStore.locale);
|
|
63 | 143 | projectId;
|
64 | 144 | entityId;
|
65 | 145 | if (iframeRef) {
|
| 146 | + const observer = new MutationObserver(handleMutation); |
66 | 147 | const intervalId = setInterval(() => {
|
67 | 148 | webFormsRef = iframeRef?.contentDocument?.querySelector('odk-web-form') || undefined;
|
68 | 149 | if (webFormsRef) {
|
69 | 150 | clearInterval(intervalId);
|
70 | 151 | webFormsRef.addEventListener('submit', handleSubmit);
|
71 | 152 | webFormsRef.addEventListener('odkForm', handleOdkForm);
|
| 153 | + observer.observe(webFormsRef, { attributes: true, childList: true, subtree: true }); |
72 | 154 | }
|
73 | 155 | }, 10);
|
| 156 | +
|
74 | 157 | // clear interval when re-run
|
75 | 158 | return () => {
|
76 | 159 | clearInterval(intervalId);
|
| 160 | + observer.disconnect(); |
77 | 161 | };
|
78 | 162 | }
|
79 | 163 | });
|
|
104 | 188 | bind:this={iframeRef}
|
105 | 189 | title="odk-web-forms-wrapper"
|
106 | 190 | src={`./web-forms.html?projectId=${projectId}&entityId=${entityId}&api_url=${API_URL}&language=${commonStore.locale}`}
|
107 |
| - style="height: {window.outerHeight}px; width: 100%; z-index: 11;" |
| 191 | + style="height: 100%; width: 100%; z-index: 11;" |
108 | 192 | >
|
109 | 193 | </iframe>
|
110 | 194 | {/key}
|
|
0 commit comments