pr360-questionnaire 2.1.6 → 2.1.10
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/build/questionnaire-test.js +13351 -10804
- package/css/app.scss +3 -0
- package/css/base/_variables.scss +1 -0
- package/css/components/_event-details.scss +92 -0
- package/css/components/_modal.scss +1 -0
- package/css/components/_prospect-details.scss +203 -0
- package/css/components/_site-rules.scss +189 -0
- package/css/components/_tables.scss +199 -455
- package/dist/index.js +2644 -913
- package/js/app.js +296 -5
- package/js/calendar.ts +84 -0
- package/js/questionnaire.ts +21 -48
- package/package.json +9 -2
- package/vendor/ShowHistory.js +0 -15
package/js/app.js
CHANGED
|
@@ -24,8 +24,9 @@ import topbar from "../vendor/topbar"
|
|
|
24
24
|
import Prism from './prism';
|
|
25
25
|
import 'mermaid-chart';
|
|
26
26
|
import Sortable from "../vendor/Sortable.js"
|
|
27
|
-
import { ShowHistory } from "../vendor/ShowHistory.js";
|
|
28
27
|
import "./tooltip_position.js";
|
|
28
|
+
import PhoenixCustomEvent from 'phoenix-custom-event-hook';
|
|
29
|
+
import live_select from "live_select";
|
|
29
30
|
|
|
30
31
|
const PrismHook = {
|
|
31
32
|
mounted() { this.highlight() },
|
|
@@ -42,15 +43,304 @@ const SortableHook = {
|
|
|
42
43
|
ghostClass: "drag-ghost",
|
|
43
44
|
forceFallback: true,
|
|
44
45
|
onEnd: e => {
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// Custom logic for rules list
|
|
47
|
+
if (this.el.id === "rules-sortable-list") {
|
|
48
|
+
// Collect the new order of rule positions (or ids)
|
|
49
|
+
const newOrder = Array.from(this.el.querySelectorAll(".row-container")).map(div => div.dataset.id);
|
|
50
|
+
this.pushEventTo(this.el, "reorder_rules", {order: newOrder});
|
|
51
|
+
} else {
|
|
52
|
+
// Default behavior for other sortable lists
|
|
53
|
+
let params = {old: e.oldIndex, new: e.newIndex, ...e.item.dataset}
|
|
54
|
+
this.pushEventTo(this.el, "reposition", params)
|
|
55
|
+
}
|
|
47
56
|
}
|
|
48
57
|
})
|
|
49
58
|
}
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
const TableTooltipHook = {
|
|
62
|
+
mounted() {
|
|
63
|
+
this.tooltip = null;
|
|
64
|
+
this.tooltipTimeout = null;
|
|
65
|
+
|
|
66
|
+
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
|
67
|
+
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
68
|
+
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
69
|
+
|
|
70
|
+
this.el.addEventListener('mouseenter', this.handleMouseEnter);
|
|
71
|
+
this.el.addEventListener('mousemove', this.handleMouseMove);
|
|
72
|
+
this.el.addEventListener('mouseleave', this.handleMouseLeave);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
destroyed() {
|
|
76
|
+
this.el.removeEventListener('mouseenter', this.handleMouseEnter);
|
|
77
|
+
this.el.removeEventListener('mousemove', this.handleMouseMove);
|
|
78
|
+
this.el.removeEventListener('mouseleave', this.handleMouseLeave);
|
|
79
|
+
this.removeTooltip();
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
handleMouseEnter(event) {
|
|
83
|
+
const content = this.el.textContent.trim();
|
|
84
|
+
const title = this.el.getAttribute('title');
|
|
85
|
+
const displayText = title || content;
|
|
86
|
+
|
|
87
|
+
// Only show tooltip if content is actually truncated
|
|
88
|
+
if (this.el.scrollWidth > this.el.clientWidth || displayText !== content) {
|
|
89
|
+
this.showTooltip(event, displayText);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
handleMouseMove(event) {
|
|
94
|
+
if (this.tooltip) {
|
|
95
|
+
this.positionTooltip(event);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
handleMouseLeave() {
|
|
100
|
+
this.removeTooltip();
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
showTooltip(event, text) {
|
|
104
|
+
this.removeTooltip();
|
|
105
|
+
|
|
106
|
+
this.tooltip = document.createElement('div');
|
|
107
|
+
this.tooltip.className = 'table-tooltip';
|
|
108
|
+
this.tooltip.textContent = text;
|
|
109
|
+
this.tooltip.style.cssText = `
|
|
110
|
+
position: fixed;
|
|
111
|
+
background-color: #007bff;
|
|
112
|
+
color: #fff;
|
|
113
|
+
padding: 12px;
|
|
114
|
+
border-radius: 6px;
|
|
115
|
+
font-size: 13px;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
z-index: 1060;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
max-width: 300px;
|
|
120
|
+
word-wrap: break-word;
|
|
121
|
+
white-space: normal;
|
|
122
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
123
|
+
text-align: left;
|
|
124
|
+
line-height: 1.4;
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
// Add arrow element
|
|
128
|
+
this.arrow = document.createElement('div');
|
|
129
|
+
this.arrow.style.cssText = `
|
|
130
|
+
position: absolute;
|
|
131
|
+
left: 20px;
|
|
132
|
+
bottom: -16px;
|
|
133
|
+
border-width: 8px;
|
|
134
|
+
border-style: solid;
|
|
135
|
+
border-color: #007bff transparent transparent transparent;
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
this.tooltip.appendChild(this.arrow);
|
|
139
|
+
document.body.appendChild(this.tooltip);
|
|
140
|
+
this.positionTooltip(event);
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
positionTooltip(event) {
|
|
144
|
+
if (!this.tooltip) return;
|
|
145
|
+
|
|
146
|
+
const rect = this.tooltip.getBoundingClientRect();
|
|
147
|
+
const viewportWidth = window.innerWidth;
|
|
148
|
+
const viewportHeight = window.innerHeight;
|
|
149
|
+
|
|
150
|
+
// Position tooltip above the cursor by default (like status history)
|
|
151
|
+
let left = event.clientX - 20; // Align arrow with cursor
|
|
152
|
+
let top = event.clientY - rect.height - 24; // Position above with margin for arrow
|
|
153
|
+
|
|
154
|
+
// Adjust if tooltip would go off screen
|
|
155
|
+
if (left + rect.width > viewportWidth - 20) {
|
|
156
|
+
left = viewportWidth - rect.width - 20;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (left < 20) {
|
|
160
|
+
left = 20;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// If tooltip would go off screen at the top, position it below
|
|
164
|
+
if (top < 20) {
|
|
165
|
+
top = event.clientY + 20;
|
|
166
|
+
// Update arrow to point upward
|
|
167
|
+
if (this.arrow) {
|
|
168
|
+
this.arrow.style.cssText = `
|
|
169
|
+
position: absolute;
|
|
170
|
+
left: 20px;
|
|
171
|
+
top: -16px;
|
|
172
|
+
border-width: 8px;
|
|
173
|
+
border-style: solid;
|
|
174
|
+
border-color: transparent transparent #007bff transparent;
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// Arrow points downward (default)
|
|
179
|
+
if (this.arrow) {
|
|
180
|
+
this.arrow.style.cssText = `
|
|
181
|
+
position: absolute;
|
|
182
|
+
left: 20px;
|
|
183
|
+
bottom: -16px;
|
|
184
|
+
border-width: 8px;
|
|
185
|
+
border-style: solid;
|
|
186
|
+
border-color: #007bff transparent transparent transparent;
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.tooltip.style.left = `${left}px`;
|
|
192
|
+
this.tooltip.style.top = `${top}px`;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
removeTooltip() {
|
|
196
|
+
if (this.tooltip) {
|
|
197
|
+
document.body.removeChild(this.tooltip);
|
|
198
|
+
this.tooltip = null;
|
|
199
|
+
this.arrow = null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ProspectsTableSyncHook = {
|
|
205
|
+
mounted() {
|
|
206
|
+
this.mainTable = document.getElementById('prospects-main');
|
|
207
|
+
this.stickyTable = document.getElementById('prospects-sticky');
|
|
208
|
+
|
|
209
|
+
if (!this.mainTable || !this.stickyTable) return;
|
|
210
|
+
|
|
211
|
+
this.handleMainTableHover = this.handleMainTableHover.bind(this);
|
|
212
|
+
this.handleStickyTableHover = this.handleStickyTableHover.bind(this);
|
|
213
|
+
this.syncRowHeights = this.syncRowHeights.bind(this);
|
|
214
|
+
this.handleInputOrResize = this.handleInputOrResize.bind(this);
|
|
215
|
+
|
|
216
|
+
// Add event listeners to main table rows
|
|
217
|
+
this.mainTable.querySelectorAll('tbody tr').forEach(row => {
|
|
218
|
+
row.addEventListener('mouseenter', this.handleMainTableHover);
|
|
219
|
+
row.addEventListener('mouseleave', this.handleMainTableHover);
|
|
220
|
+
});
|
|
221
|
+
// Add event listeners to sticky table rows
|
|
222
|
+
this.stickyTable.querySelectorAll('tbody tr').forEach(row => {
|
|
223
|
+
row.addEventListener('mouseenter', this.handleStickyTableHover);
|
|
224
|
+
row.addEventListener('mouseleave', this.handleStickyTableHover);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Sync row heights initially and on resize
|
|
228
|
+
this.syncRowHeights();
|
|
229
|
+
window.addEventListener('resize', this.syncRowHeights);
|
|
230
|
+
|
|
231
|
+
// Listen for input/resize events in main table (for expanding textareas)
|
|
232
|
+
this.mainTable.querySelectorAll('input, textarea').forEach(el => {
|
|
233
|
+
el.addEventListener('input', this.handleInputOrResize);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// MutationObserver to watch for modal open/close
|
|
237
|
+
this.modalObserver = new MutationObserver(() => {
|
|
238
|
+
// Defer to next frame to allow DOM/layout to settle
|
|
239
|
+
requestAnimationFrame(this.syncRowHeights);
|
|
240
|
+
});
|
|
241
|
+
// Watch for changes to the modal-blur-overlay (added/removed)
|
|
242
|
+
this.modalObserver.observe(document.body, {
|
|
243
|
+
childList: true,
|
|
244
|
+
subtree: true
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
destroyed() {
|
|
249
|
+
if (this.mainTable) {
|
|
250
|
+
this.mainTable.querySelectorAll('tbody tr').forEach(row => {
|
|
251
|
+
row.removeEventListener('mouseenter', this.handleMainTableHover);
|
|
252
|
+
row.removeEventListener('mouseleave', this.handleMainTableHover);
|
|
253
|
+
});
|
|
254
|
+
this.mainTable.querySelectorAll('input, textarea').forEach(el => {
|
|
255
|
+
el.removeEventListener('input', this.handleInputOrResize);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (this.stickyTable) {
|
|
259
|
+
this.stickyTable.querySelectorAll('tbody tr').forEach(row => {
|
|
260
|
+
row.removeEventListener('mouseenter', this.handleStickyTableHover);
|
|
261
|
+
row.removeEventListener('mouseleave', this.handleStickyTableHover);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
window.removeEventListener('resize', this.syncRowHeights);
|
|
265
|
+
if (this.modalObserver) {
|
|
266
|
+
this.modalObserver.disconnect();
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
syncRowHeights() {
|
|
271
|
+
// Sync header row height
|
|
272
|
+
const mainHeader = this.mainTable.querySelector('thead tr');
|
|
273
|
+
const stickyHeader = this.stickyTable.querySelector('thead tr');
|
|
274
|
+
if (mainHeader && stickyHeader) {
|
|
275
|
+
const mainHeaderHeight = mainHeader.offsetHeight;
|
|
276
|
+
stickyHeader.style.height = mainHeaderHeight + 'px';
|
|
277
|
+
stickyHeader.style.minHeight = mainHeaderHeight + 'px';
|
|
278
|
+
stickyHeader.style.maxHeight = mainHeaderHeight + 'px';
|
|
279
|
+
}
|
|
280
|
+
// Sync body row heights
|
|
281
|
+
const mainRows = this.mainTable.querySelectorAll('tbody tr');
|
|
282
|
+
const stickyRows = this.stickyTable.querySelectorAll('tbody tr');
|
|
283
|
+
for (let i = 0; i < mainRows.length; i++) {
|
|
284
|
+
const mainHeight = mainRows[i].offsetHeight;
|
|
285
|
+
stickyRows[i].style.height = mainHeight + 'px';
|
|
286
|
+
stickyRows[i].style.minHeight = mainHeight + 'px';
|
|
287
|
+
stickyRows[i].style.maxHeight = mainHeight + 'px';
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
handleInputOrResize() {
|
|
292
|
+
// Defer to next frame to allow textarea/input to resize first
|
|
293
|
+
requestAnimationFrame(this.syncRowHeights);
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
handleMainTableHover(event) {
|
|
297
|
+
const prospectId = event.target.closest('tr').dataset.prospectId;
|
|
298
|
+
if (!prospectId) return;
|
|
299
|
+
const stickyRow = this.stickyTable.querySelector(`tr[data-prospect-id="${prospectId}"]`);
|
|
300
|
+
if (stickyRow) {
|
|
301
|
+
if (event.type === 'mouseenter') {
|
|
302
|
+
stickyRow.style.backgroundColor = '#f0f4ff';
|
|
303
|
+
} else {
|
|
304
|
+
stickyRow.style.backgroundColor = '';
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
handleStickyTableHover(event) {
|
|
310
|
+
const prospectId = event.target.closest('tr').dataset.prospectId;
|
|
311
|
+
if (!prospectId) return;
|
|
312
|
+
const mainRow = this.mainTable.querySelector(`tr[data-prospect-id="${prospectId}"]`);
|
|
313
|
+
if (mainRow) {
|
|
314
|
+
if (event.type === 'mouseenter') {
|
|
315
|
+
mainRow.style.backgroundColor = '#f0f4ff';
|
|
316
|
+
} else {
|
|
317
|
+
mainRow.style.backgroundColor = '';
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const ScrollToSelectedProspectHook = {
|
|
324
|
+
mounted() {
|
|
325
|
+
this.scrollToSelected();
|
|
326
|
+
},
|
|
327
|
+
updated() {
|
|
328
|
+
this.scrollToSelected();
|
|
329
|
+
},
|
|
330
|
+
scrollToSelected() {
|
|
331
|
+
const selectedId = this.el.getAttribute('data-selected-prospect-id');
|
|
332
|
+
if (!selectedId) return;
|
|
333
|
+
const row = this.el.querySelector(`tr[data-prospect-id=\"${selectedId}\"]`);
|
|
334
|
+
if (row) {
|
|
335
|
+
row.scrollIntoView({ behavior: 'auto', block: 'center' });
|
|
336
|
+
row.classList.add('prospect-row-focused');
|
|
337
|
+
setTimeout(() => row.classList.remove('prospect-row-focused'), 700);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
52
342
|
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
|
53
|
-
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: {Prism: PrismHook, Sortable: SortableHook,
|
|
343
|
+
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: {Prism: PrismHook, Sortable: SortableHook, PhoenixCustomEvent, TableTooltip: TableTooltipHook, ProspectsTableSync: ProspectsTableSyncHook, ScrollToSelectedProspect: ScrollToSelectedProspectHook, ...live_select}})
|
|
54
344
|
|
|
55
345
|
// Show progress bar on live navigation and form submits
|
|
56
346
|
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
|
@@ -66,4 +356,5 @@ liveSocket.connect()
|
|
|
66
356
|
// >> liveSocket.disableLatencySim()
|
|
67
357
|
window.liveSocket = liveSocket
|
|
68
358
|
|
|
69
|
-
import './questionnaire.ts';
|
|
359
|
+
import './questionnaire.ts';
|
|
360
|
+
import './calendar.js';
|
package/js/calendar.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { html, LitElement } from "lit";
|
|
2
|
+
import { property } from 'lit/decorators.js'
|
|
3
|
+
import { Calendar } from "@fullcalendar/core";
|
|
4
|
+
import dayGridPlugin from "@fullcalendar/daygrid";
|
|
5
|
+
import resourceTimelinePlugin from "@fullcalendar/resource-timeline";
|
|
6
|
+
import momentPlugin from "@fullcalendar/moment";
|
|
7
|
+
|
|
8
|
+
export type CalendarEvent = {
|
|
9
|
+
id: string;
|
|
10
|
+
type: "Event";
|
|
11
|
+
date: string;
|
|
12
|
+
startTime: string;
|
|
13
|
+
endTime: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type Date = {
|
|
17
|
+
type: "Date";
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Organization = {
|
|
22
|
+
id: string;
|
|
23
|
+
type: "Organization";
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class CalendarElement extends LitElement {
|
|
28
|
+
@property()
|
|
29
|
+
|
|
30
|
+
@property({ attribute: "date" })
|
|
31
|
+
date: string;
|
|
32
|
+
|
|
33
|
+
@property({ attribute: "data-events" })
|
|
34
|
+
events: string;
|
|
35
|
+
|
|
36
|
+
@property({ attribute: "calendar" })
|
|
37
|
+
calendar: Calendar;
|
|
38
|
+
|
|
39
|
+
firstUpdated() {
|
|
40
|
+
super.firstUpdated(null);
|
|
41
|
+
let calendarEl: HTMLElement = document.getElementById('full-calendar')!;
|
|
42
|
+
this.calendar = new Calendar(calendarEl, {
|
|
43
|
+
schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
|
|
44
|
+
plugins: [resourceTimelinePlugin, momentPlugin, dayGridPlugin],
|
|
45
|
+
slotMinWidth: 70,
|
|
46
|
+
slotMinTime: "08:00",
|
|
47
|
+
slotMaxTime: "20:00",
|
|
48
|
+
stickyHeaderDates: true,
|
|
49
|
+
initialView: 'dayGridWeek',
|
|
50
|
+
headerToolbar: {
|
|
51
|
+
left: 'prev,next today',
|
|
52
|
+
center: "title",
|
|
53
|
+
right: 'dayGridMonth, dayGridWeek, dayGridDay'
|
|
54
|
+
},
|
|
55
|
+
titleFormat: "dddd, MMM D, YYYY",
|
|
56
|
+
events: JSON.parse(this.events),
|
|
57
|
+
eventClick: function (info) {
|
|
58
|
+
info.el.dispatchEvent(
|
|
59
|
+
new CustomEvent("show_event", {
|
|
60
|
+
bubbles: true,
|
|
61
|
+
detail: { id: info.event.id },
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
this.calendar.render();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
render() {
|
|
70
|
+
return html`<div id="full-calendar"></div>`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
createRenderRoot() {
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
updated() {
|
|
78
|
+
this.calendar.gotoDate(this.date);
|
|
79
|
+
this.calendar.getEventSources()[0].remove();
|
|
80
|
+
this.calendar.addEventSource({ events: JSON.parse(this.events) });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
customElements.define("pr360-calendar", CalendarElement);
|
package/js/questionnaire.ts
CHANGED
|
@@ -150,7 +150,8 @@ export class QuestionnaireElement extends LitElement {
|
|
|
150
150
|
<div class="questionnaire-illustration"><img src=${this.contactInfoImageUrl()}> </div>
|
|
151
151
|
<div class="questionnaire--question"><h2 class="u-padding--bt">${(this.currentStep as Video).text}</h2></div>
|
|
152
152
|
<vimeo-video controls src=${(this.currentStep as Video).url} class="questionnaire--video"></vimeo-video>
|
|
153
|
-
<div data-test-id="
|
|
153
|
+
<div data-test-id="questionnaire-info"><h4>Thank you for completing the assessment. We'll be calling you within 24 hours with more information.</h4></div>
|
|
154
|
+
<div data-test-id="site-phone-number"><h4>If you'd like to speak with us sooner, </br> please feel free to</h4><a href="tel:${this.phoneNumber}"><h1>Call Us</h1></a></div>
|
|
154
155
|
</div>
|
|
155
156
|
</div>
|
|
156
157
|
</div>
|
|
@@ -178,9 +179,7 @@ export class QuestionnaireElement extends LitElement {
|
|
|
178
179
|
</div>
|
|
179
180
|
<div>
|
|
180
181
|
<label for="email">Email</label>
|
|
181
|
-
<input type="email" id="email" name="email" required
|
|
182
|
-
pattern="^[^@\s]+@[^@\s]+\\.[^@\s]+$"
|
|
183
|
-
title="Please enter a valid email address (e.g. user@example.com)"/>
|
|
182
|
+
<input type="email" id="email" name="email" required />
|
|
184
183
|
</div>
|
|
185
184
|
<div>
|
|
186
185
|
<label for="phone">Phone</label>
|
|
@@ -192,7 +191,7 @@ export class QuestionnaireElement extends LitElement {
|
|
|
192
191
|
<label for="zip_code">Zip Code</label>
|
|
193
192
|
<input type="text" id="zip_code" name="zip_code" required
|
|
194
193
|
pattern="^[0-9]{5,6}$"
|
|
195
|
-
title="Please enter a valid
|
|
194
|
+
title="Please enter a valid zip code (5 or 6 digits)"/>
|
|
196
195
|
</div>
|
|
197
196
|
<div>
|
|
198
197
|
<label for="insurance_provider">Insurance Provider</label>
|
|
@@ -290,57 +289,31 @@ export class QuestionnaireElement extends LitElement {
|
|
|
290
289
|
|
|
291
290
|
if (this.contactInfoForm?.checkValidity()) {
|
|
292
291
|
const email = this.emailInput?.value;
|
|
293
|
-
const phone = this.phoneInput?.value;
|
|
294
|
-
const zipCode = this.zipCodeInput?.value;
|
|
295
|
-
|
|
296
|
-
// Email validation
|
|
297
|
-
const emailRegex = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
298
|
-
if (!emailRegex.test(email)) {
|
|
299
|
-
alert('Please enter a valid email address');
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Phone validation
|
|
304
|
-
const phoneRegex = /^\d{6,}$/;
|
|
305
|
-
if (!phoneRegex.test(phone)) {
|
|
306
|
-
alert('Phone number must contain only numbers and be at least 6 digits long');
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Zip code validation
|
|
311
|
-
const zipCodeRegex = /^\d{5,6}$/;
|
|
312
|
-
if (!zipCodeRegex.test(zipCode)) {
|
|
313
|
-
alert('Zip code must be 5 or 6 digits');
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
292
|
|
|
317
293
|
const hsq = window['_hsq'] = window['_hsq'] || [];
|
|
318
294
|
hsq.push(['identify', { email: email }]);
|
|
319
295
|
hsq.push(['setPath', '/submit-contact-info']);
|
|
320
296
|
hsq.push(['trackPageView']);
|
|
321
297
|
|
|
298
|
+
const formData = {
|
|
299
|
+
nodeId: this.currentStep?.id,
|
|
300
|
+
first_name: this.firstNameInput.value,
|
|
301
|
+
last_name: this.lastNameInput.value,
|
|
302
|
+
email: email,
|
|
303
|
+
zip_code: this.zipCodeInput?.value,
|
|
304
|
+
phone_number: this.phoneInput?.value,
|
|
305
|
+
insurance_provider: this.insuranceProviderSelect.value
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
window['dataLayer'] = window['dataLayer'] || [];
|
|
309
|
+
window['dataLayer'].push({
|
|
310
|
+
'event': 'questionnaireSubmission',
|
|
311
|
+
'formData': formData
|
|
312
|
+
});
|
|
313
|
+
|
|
322
314
|
this.dispatchEvent(new CustomEvent('submitContactInfo', {
|
|
323
|
-
detail:
|
|
324
|
-
nodeId: this.currentStep?.id,
|
|
325
|
-
first_name: this.firstNameInput.value,
|
|
326
|
-
last_name: this.lastNameInput.value,
|
|
327
|
-
email: email,
|
|
328
|
-
zip_code: zipCode,
|
|
329
|
-
phone_number: phone,
|
|
330
|
-
insurance_provider: this.insuranceProviderSelect.value
|
|
331
|
-
}
|
|
315
|
+
detail: formData
|
|
332
316
|
}));
|
|
333
317
|
}
|
|
334
318
|
}
|
|
335
|
-
|
|
336
|
-
formatPhoneNumber(phoneNumber: string) {
|
|
337
|
-
switch (phoneNumber.length) {
|
|
338
|
-
case 10:
|
|
339
|
-
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`
|
|
340
|
-
case 11:
|
|
341
|
-
return `${phoneNumber.slice(0, 1)}-${phoneNumber.slice(1, 4)}-${phoneNumber.slice(4, 7)}-${phoneNumber.slice(7)}`
|
|
342
|
-
default:
|
|
343
|
-
return phoneNumber
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
319
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr360-questionnaire",
|
|
3
3
|
"description": "An element to render a questionnaire for PatientReach 360.",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.10",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": {
|
|
7
7
|
"email": "chris@launchscout.com",
|
|
@@ -25,9 +25,16 @@
|
|
|
25
25
|
"phoenix": "file:../deps/phoenix",
|
|
26
26
|
"phoenix_html": "file:../deps/phoenix_html",
|
|
27
27
|
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
|
28
|
+
"live_select": "file:../deps/live_select",
|
|
29
|
+
"phoenix-custom-event-hook": "^0.0.6",
|
|
28
30
|
"phx-live-state": "^0.9.2",
|
|
29
31
|
"vimeo-video-element": "^1.0.1",
|
|
30
|
-
"mermaid-chart": "launchscout/mermaid-chart"
|
|
32
|
+
"mermaid-chart": "launchscout/mermaid-chart",
|
|
33
|
+
"@fullcalendar/core": "^6.1.8",
|
|
34
|
+
"@fullcalendar/daygrid": "^6.1.8",
|
|
35
|
+
"@fullcalendar/moment": "^6.1.11",
|
|
36
|
+
"@fullcalendar/resource": "^6.1.8",
|
|
37
|
+
"@fullcalendar/resource-timeline": "^6.1.8"
|
|
31
38
|
},
|
|
32
39
|
"devDependencies": {
|
|
33
40
|
"@open-wc/testing": "^3.2.0",
|
package/vendor/ShowHistory.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const ShowHistory = {
|
|
2
|
-
mounted() {
|
|
3
|
-
const id = this.el.dataset.id
|
|
4
|
-
const target = this.el.dataset.target
|
|
5
|
-
|
|
6
|
-
this.el.addEventListener("mouseenter", () => {
|
|
7
|
-
this.pushEventTo(target, "show_history_tooltip", { id })
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
this.el.addEventListener("mouseleave", () => {
|
|
11
|
-
this.pushEventTo(target, "hide_history_tooltip", { })
|
|
12
|
-
})
|
|
13
|
-
},
|
|
14
|
-
}
|
|
15
|
-
|