@dataemergencia/emergency 0.3.0 → 0.5.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 +1 -1
- package/dist/iife/emergency.iife.global.js +24 -24
- package/dist/index.cjs +1000 -312
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1001 -313
- package/dist/styles.css +680 -52
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRoot } from "react-dom/client";
|
|
|
2
2
|
import axios, { isAxiosError } from "axios";
|
|
3
3
|
import Pusher from "pusher-js";
|
|
4
4
|
import { createStore } from "zustand/vanilla";
|
|
5
|
-
import { createContext, useCallback, useContext, useEffect, useId, useRef } from "react";
|
|
5
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { useStore } from "zustand";
|
|
8
8
|
|
|
@@ -362,6 +362,7 @@ function createBffClient(options) {
|
|
|
362
362
|
const copy = {
|
|
363
363
|
panelLabel: "Panel de emergencia",
|
|
364
364
|
close: "Cerrar panel de emergencia",
|
|
365
|
+
back: "Volver",
|
|
365
366
|
question: "¿Tiene usted una emergencia?",
|
|
366
367
|
illustrationAlt: "Ícono paramédico",
|
|
367
368
|
emergency: "Emergencia",
|
|
@@ -733,6 +734,7 @@ function createEmergencyStore(config) {
|
|
|
733
734
|
lastError: null,
|
|
734
735
|
geolocation: null,
|
|
735
736
|
geolocationError: null,
|
|
737
|
+
contactPhone: null,
|
|
736
738
|
realtimeStatus: "idle",
|
|
737
739
|
abortResult: null,
|
|
738
740
|
simulationResult: null,
|
|
@@ -1175,7 +1177,8 @@ function createEmergencyController(deps) {
|
|
|
1175
1177
|
patch({
|
|
1176
1178
|
panelOpen: false,
|
|
1177
1179
|
abortConfirmation: false,
|
|
1178
|
-
simulationConfirmation: false
|
|
1180
|
+
simulationConfirmation: false,
|
|
1181
|
+
contactPhone: null
|
|
1179
1182
|
});
|
|
1180
1183
|
events.emit("panel:closed", { reason });
|
|
1181
1184
|
}
|
|
@@ -1224,6 +1227,7 @@ function createEmergencyController(deps) {
|
|
|
1224
1227
|
lastError: null,
|
|
1225
1228
|
geolocation: null,
|
|
1226
1229
|
geolocationError: null,
|
|
1230
|
+
contactPhone: null,
|
|
1227
1231
|
realtimeStatus: "idle",
|
|
1228
1232
|
abortResult: null,
|
|
1229
1233
|
simulationResult: null,
|
|
@@ -1265,7 +1269,8 @@ function createEmergencyController(deps) {
|
|
|
1265
1269
|
geolocationError: null,
|
|
1266
1270
|
...state.geolocationError !== null || state.abortResult?.ok === false || state.simulationResult?.ok === false || state.phase.kind === "ready" && state.lastError !== null ? { lastError: null } : {}
|
|
1267
1271
|
});
|
|
1268
|
-
}
|
|
1272
|
+
},
|
|
1273
|
+
setContactPhone: (phone) => patch({ contactPhone: phone })
|
|
1269
1274
|
},
|
|
1270
1275
|
store,
|
|
1271
1276
|
events
|
|
@@ -1440,74 +1445,6 @@ function validateConfig(config) {
|
|
|
1440
1445
|
};
|
|
1441
1446
|
}
|
|
1442
1447
|
|
|
1443
|
-
// src/react/components/icons/paramedical-mark.tsx
|
|
1444
|
-
/**
|
|
1445
|
-
* Neutral inline illustration used by the idle panel.
|
|
1446
|
-
*/
|
|
1447
|
-
function ParamedicalMark({ className, title }) {
|
|
1448
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
1449
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1450
|
-
version: "1.0",
|
|
1451
|
-
viewBox: "0 0 192 192",
|
|
1452
|
-
width: "192",
|
|
1453
|
-
height: "192",
|
|
1454
|
-
id: "Star of life",
|
|
1455
|
-
className,
|
|
1456
|
-
"aria-label": title,
|
|
1457
|
-
children: [
|
|
1458
|
-
/* @__PURE__ */ jsx("defs", { id: "defs2886" }),
|
|
1459
|
-
/* @__PURE__ */ jsxs("g", {
|
|
1460
|
-
id: "cross",
|
|
1461
|
-
children: [/* @__PURE__ */ jsx("path", {
|
|
1462
|
-
d: "m 74,133 0,52 44,0 0,-52 44,26 22,-38 L 140,96 184,70 162,32 117,58 117,7 74,7 74,58 30,32 8,70 52,96 8,121 30,159 z",
|
|
1463
|
-
id: "outline",
|
|
1464
|
-
style: {
|
|
1465
|
-
fill: "none",
|
|
1466
|
-
stroke: "#3333bb",
|
|
1467
|
-
strokeWidth: 6
|
|
1468
|
-
}
|
|
1469
|
-
}), /* @__PURE__ */ jsx("path", {
|
|
1470
|
-
d: "m 74,133 0,52 44,0 0,-52 44,26 22,-38 L 140,96 184,70 162,32 117,58 117,7 74,7 74,58 30,32 8,70 52,96 8,121 30,159 z",
|
|
1471
|
-
id: "inner",
|
|
1472
|
-
style: {
|
|
1473
|
-
fill: "#0000bb",
|
|
1474
|
-
fillRule: "evenodd",
|
|
1475
|
-
stroke: "#ffffff",
|
|
1476
|
-
strokeWidth: 2
|
|
1477
|
-
}
|
|
1478
|
-
})]
|
|
1479
|
-
}),
|
|
1480
|
-
/* @__PURE__ */ jsxs("g", {
|
|
1481
|
-
id: "symbols",
|
|
1482
|
-
style: {
|
|
1483
|
-
fill: "#ffffff",
|
|
1484
|
-
fillRule: "evenodd",
|
|
1485
|
-
stroke: "#3333bb",
|
|
1486
|
-
strokeWidth: 2
|
|
1487
|
-
},
|
|
1488
|
-
children: [/* @__PURE__ */ jsx("path", {
|
|
1489
|
-
d: "m 93,166 c 0,7 6,7 6,0 l 3,-138 c 0,-8 -12,-8 -12,0 l 3,135 0,3 z",
|
|
1490
|
-
id: "rod"
|
|
1491
|
-
}), /* @__PURE__ */ jsxs("g", {
|
|
1492
|
-
id: "snake",
|
|
1493
|
-
children: [/* @__PURE__ */ jsx("path", {
|
|
1494
|
-
d: "m 100,153 c 7,4 3,15 -3,15 2,-5 3,-9 3,-15 z m 1,-40 c 10,9 4,22 -3,26 -6,3 -6,7 -6,15 -9,-10 -4,-19 4,-24 5,-4 5,-10 5,-17 z m 1,-30 c 0,11 -17,14 -17,22 0,3 1,9 6,12 0,-11 9,-12 15,-18 7,-7 7,-19 -4,-28 0,4 0,8 0,12 z M 90,77 C 80,69 76,55 80,46 c 3,-10 20,-11 20,-3 0,8 -20,3 -13,16 l 3,5 0,13 z",
|
|
1495
|
-
id: "body"
|
|
1496
|
-
}), /* @__PURE__ */ jsx("path", {
|
|
1497
|
-
d: "m 95,43 c 7,-3 -7,-3 0,0 z",
|
|
1498
|
-
id: "eye",
|
|
1499
|
-
style: {
|
|
1500
|
-
fill: "#3333bb",
|
|
1501
|
-
fillRule: "evenodd",
|
|
1502
|
-
stroke: "none"
|
|
1503
|
-
}
|
|
1504
|
-
})]
|
|
1505
|
-
})]
|
|
1506
|
-
})
|
|
1507
|
-
]
|
|
1508
|
-
});
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
1448
|
// src/react/components/Icon/index.tsx
|
|
1512
1449
|
/** Icons whose artwork does not fit the default 24x24 grid need their own canvas. */
|
|
1513
1450
|
const viewBoxes = { emergency: "0 0 640 640" };
|
|
@@ -1564,6 +1501,23 @@ const paths = {
|
|
|
1564
1501
|
/* @__PURE__ */ jsx("path", { d: "M12 7h.01" })
|
|
1565
1502
|
] }),
|
|
1566
1503
|
"arrow-left": /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M19 12H5" }), /* @__PURE__ */ jsx("path", { d: "m12 19-7-7 7-7" })] }),
|
|
1504
|
+
"map-pin": /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M12 22s7-6.2 7-12a7 7 0 1 0-14 0c0 5.8 7 12 7 12z" }), /* @__PURE__ */ jsx("circle", {
|
|
1505
|
+
cx: "12",
|
|
1506
|
+
cy: "10",
|
|
1507
|
+
r: "2.5"
|
|
1508
|
+
})] }),
|
|
1509
|
+
"credit-card-plus": /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1510
|
+
/* @__PURE__ */ jsx("path", { d: "M12 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5" }),
|
|
1511
|
+
/* @__PURE__ */ jsx("path", { d: "M18 12h.01" }),
|
|
1512
|
+
/* @__PURE__ */ jsx("path", { d: "M19 22v-6" }),
|
|
1513
|
+
/* @__PURE__ */ jsx("path", { d: "m22 19-3-3-3 3" }),
|
|
1514
|
+
/* @__PURE__ */ jsx("path", { d: "M6 12h.01" }),
|
|
1515
|
+
/* @__PURE__ */ jsx("circle", {
|
|
1516
|
+
cx: "12",
|
|
1517
|
+
cy: "12",
|
|
1518
|
+
r: "2"
|
|
1519
|
+
})
|
|
1520
|
+
] }),
|
|
1567
1521
|
emergency: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("path", { d: "M272.5 96C272.5 78.3 286.8 64 304.5 64L336.5 64C354.2 64 368.5 78.3 368.5 96L368.5 236.9L490.5 166.5C505.8 157.7 525.4 162.9 534.2 178.2L550.2 205.9C559 221.2 553.8 240.8 538.5 249.6L416.5 320L538.5 390.4C553.8 399.2 559.1 418.8 550.2 434.1L534.2 461.8C525.4 477.1 505.8 482.4 490.5 473.5L368.5 403.1L368.5 544C368.5 561.7 354.2 576 336.5 576L304.5 576C286.8 576 272.5 561.7 272.5 544L272.5 403.1L150.5 473.5C135.2 482.3 115.6 477.1 106.8 461.8L90.8 434.1C82 418.8 87.2 399.2 102.5 390.4L224.5 320L102.5 249.6C87.2 240.8 82 221.2 90.8 205.9L106.8 178.2C115.6 162.9 135.2 157.7 150.5 166.5L272.5 236.9L272.5 96z" }) })
|
|
1568
1522
|
};
|
|
1569
1523
|
function Icon({ name, className, title }) {
|
|
@@ -1593,6 +1547,16 @@ var panel_module_default = {
|
|
|
1593
1547
|
"de-emergency-slide-in": "de-emergency-de-emergency-slide-in-nA5Hwa",
|
|
1594
1548
|
"de-emergency-slide-up": "de-emergency-de-emergency-slide-up-nA5Hwa",
|
|
1595
1549
|
"de-emergency-spin": "de-emergency-de-emergency-spin-nA5Hwa",
|
|
1550
|
+
"emergency-availability": "de-emergency-emergency-availability-nA5Hwa",
|
|
1551
|
+
"emergency-availability__card": "de-emergency-emergency-availability__card-nA5Hwa",
|
|
1552
|
+
"emergency-availability__card-body": "de-emergency-emergency-availability__card-body-nA5Hwa",
|
|
1553
|
+
"emergency-availability__card-header": "de-emergency-emergency-availability__card-header-nA5Hwa",
|
|
1554
|
+
"emergency-availability__card-title": "de-emergency-emergency-availability__card-title-nA5Hwa",
|
|
1555
|
+
"emergency-availability__details": "de-emergency-emergency-availability__details-nA5Hwa",
|
|
1556
|
+
"emergency-availability__label": "de-emergency-emergency-availability__label-nA5Hwa",
|
|
1557
|
+
"emergency-availability__recharge": "de-emergency-emergency-availability__recharge-nA5Hwa",
|
|
1558
|
+
"emergency-availability__rounds": "de-emergency-emergency-availability__rounds-nA5Hwa",
|
|
1559
|
+
"emergency-availability__rounds-group": "de-emergency-emergency-availability__rounds-group-nA5Hwa",
|
|
1596
1560
|
"emergency-confirmation": "de-emergency-emergency-confirmation-nA5Hwa",
|
|
1597
1561
|
"emergency-confirmation__body": "de-emergency-emergency-confirmation__body-nA5Hwa",
|
|
1598
1562
|
"emergency-confirmation__button": "de-emergency-emergency-confirmation__button-nA5Hwa",
|
|
@@ -1617,7 +1581,12 @@ var panel_module_default = {
|
|
|
1617
1581
|
"emergency-feedback": "de-emergency-emergency-feedback-nA5Hwa",
|
|
1618
1582
|
"emergency-feedback__dismiss": "de-emergency-emergency-feedback__dismiss-nA5Hwa",
|
|
1619
1583
|
"emergency-panel": "de-emergency-emergency-panel-nA5Hwa",
|
|
1584
|
+
"emergency-panel__availability-region": "de-emergency-emergency-panel__availability-region-nA5Hwa",
|
|
1620
1585
|
"emergency-panel__body": "de-emergency-emergency-panel__body-nA5Hwa",
|
|
1586
|
+
"emergency-panel__body--pre-emergency": "de-emergency-emergency-panel__body--pre-emergency-nA5Hwa",
|
|
1587
|
+
"emergency-panel__body--pricing": "de-emergency-emergency-panel__body--pricing-nA5Hwa",
|
|
1588
|
+
"emergency-panel__body-split": "de-emergency-emergency-panel__body-split-nA5Hwa",
|
|
1589
|
+
"emergency-panel__body-split--with-availability": "de-emergency-emergency-panel__body-split--with-availability-nA5Hwa",
|
|
1621
1590
|
"emergency-panel__body-title": "de-emergency-emergency-panel__body-title-nA5Hwa",
|
|
1622
1591
|
"emergency-panel__body-title-container": "de-emergency-emergency-panel__body-title-container-nA5Hwa",
|
|
1623
1592
|
"emergency-panel__button": "de-emergency-emergency-panel__button-nA5Hwa",
|
|
@@ -1625,16 +1594,60 @@ var panel_module_default = {
|
|
|
1625
1594
|
"emergency-panel__button--simulation": "de-emergency-emergency-panel__button--simulation-nA5Hwa",
|
|
1626
1595
|
"emergency-panel__close-button": "de-emergency-emergency-panel__close-button-nA5Hwa",
|
|
1627
1596
|
"emergency-panel__close-icon": "de-emergency-emergency-panel__close-icon-nA5Hwa",
|
|
1597
|
+
"emergency-panel__divider": "de-emergency-emergency-panel__divider-nA5Hwa",
|
|
1628
1598
|
"emergency-panel__footer": "de-emergency-emergency-panel__footer-nA5Hwa",
|
|
1599
|
+
"emergency-panel__footer--single": "de-emergency-emergency-panel__footer--single-nA5Hwa",
|
|
1629
1600
|
"emergency-panel__header": "de-emergency-emergency-panel__header-nA5Hwa",
|
|
1601
|
+
"emergency-panel__header--close-only": "de-emergency-emergency-panel__header--close-only-nA5Hwa",
|
|
1602
|
+
"emergency-panel__prompt-region": "de-emergency-emergency-panel__prompt-region-nA5Hwa",
|
|
1603
|
+
"emergency-panel__sr-only": "de-emergency-emergency-panel__sr-only-nA5Hwa",
|
|
1630
1604
|
"emergency-panel__title": "de-emergency-emergency-panel__title-nA5Hwa",
|
|
1631
1605
|
"emergency-panel--hidden": "de-emergency-emergency-panel--hidden-nA5Hwa",
|
|
1606
|
+
"emergency-pre-emergency": "de-emergency-emergency-pre-emergency-nA5Hwa",
|
|
1607
|
+
"emergency-pre-emergency__error": "de-emergency-emergency-pre-emergency__error-nA5Hwa",
|
|
1608
|
+
"emergency-pre-emergency__form": "de-emergency-emergency-pre-emergency__form-nA5Hwa",
|
|
1609
|
+
"emergency-pre-emergency__input": "de-emergency-emergency-pre-emergency__input-nA5Hwa",
|
|
1610
|
+
"emergency-pre-emergency__input--area-code": "de-emergency-emergency-pre-emergency__input--area-code-nA5Hwa",
|
|
1611
|
+
"emergency-pre-emergency__input--invalid": "de-emergency-emergency-pre-emergency__input--invalid-nA5Hwa",
|
|
1612
|
+
"emergency-pre-emergency__input--number": "de-emergency-emergency-pre-emergency__input--number-nA5Hwa",
|
|
1613
|
+
"emergency-pre-emergency__input--valid": "de-emergency-emergency-pre-emergency__input--valid-nA5Hwa",
|
|
1614
|
+
"emergency-pre-emergency__label": "de-emergency-emergency-pre-emergency__label-nA5Hwa",
|
|
1615
|
+
"emergency-pre-emergency__notice": "de-emergency-emergency-pre-emergency__notice-nA5Hwa",
|
|
1616
|
+
"emergency-pre-emergency__notice-icon": "de-emergency-emergency-pre-emergency__notice-icon-nA5Hwa",
|
|
1617
|
+
"emergency-pre-emergency__notice-text": "de-emergency-emergency-pre-emergency__notice-text-nA5Hwa",
|
|
1618
|
+
"emergency-pre-emergency__phone-row": "de-emergency-emergency-pre-emergency__phone-row-nA5Hwa",
|
|
1619
|
+
"emergency-pricing": "de-emergency-emergency-pricing-nA5Hwa",
|
|
1620
|
+
"emergency-pricing__badge": "de-emergency-emergency-pricing__badge-nA5Hwa",
|
|
1621
|
+
"emergency-pricing__heading": "de-emergency-emergency-pricing__heading-nA5Hwa",
|
|
1622
|
+
"emergency-pricing__package": "de-emergency-emergency-pricing__package-nA5Hwa",
|
|
1623
|
+
"emergency-pricing__package--highlighted": "de-emergency-emergency-pricing__package--highlighted-nA5Hwa",
|
|
1624
|
+
"emergency-pricing__package-caption": "de-emergency-emergency-pricing__package-caption-nA5Hwa",
|
|
1625
|
+
"emergency-pricing__package-footnote": "de-emergency-emergency-pricing__package-footnote-nA5Hwa",
|
|
1626
|
+
"emergency-pricing__package-header": "de-emergency-emergency-pricing__package-header-nA5Hwa",
|
|
1627
|
+
"emergency-pricing__package-label": "de-emergency-emergency-pricing__package-label-nA5Hwa",
|
|
1628
|
+
"emergency-pricing__package-name": "de-emergency-emergency-pricing__package-name-nA5Hwa",
|
|
1629
|
+
"emergency-pricing__price": "de-emergency-emergency-pricing__price-nA5Hwa",
|
|
1630
|
+
"emergency-pricing__price-meta": "de-emergency-emergency-pricing__price-meta-nA5Hwa",
|
|
1631
|
+
"emergency-pricing__price-note": "de-emergency-emergency-pricing__price-note-nA5Hwa",
|
|
1632
|
+
"emergency-pricing__price-row": "de-emergency-emergency-pricing__price-row-nA5Hwa",
|
|
1633
|
+
"emergency-pricing__select": "de-emergency-emergency-pricing__select-nA5Hwa",
|
|
1634
|
+
"emergency-pricing__stat": "de-emergency-emergency-pricing__stat-nA5Hwa",
|
|
1635
|
+
"emergency-pricing__stat-divider": "de-emergency-emergency-pricing__stat-divider-nA5Hwa",
|
|
1636
|
+
"emergency-pricing__stat-label": "de-emergency-emergency-pricing__stat-label-nA5Hwa",
|
|
1637
|
+
"emergency-pricing__stat-value": "de-emergency-emergency-pricing__stat-value-nA5Hwa",
|
|
1638
|
+
"emergency-pricing__stats": "de-emergency-emergency-pricing__stats-nA5Hwa",
|
|
1639
|
+
"emergency-pricing__subtitle": "de-emergency-emergency-pricing__subtitle-nA5Hwa",
|
|
1640
|
+
"emergency-pricing__title": "de-emergency-emergency-pricing__title-nA5Hwa",
|
|
1641
|
+
"emergency-pricing__titles": "de-emergency-emergency-pricing__titles-nA5Hwa",
|
|
1632
1642
|
"emergency-resume-notification-button": "de-emergency-emergency-resume-notification-button-nA5Hwa",
|
|
1633
1643
|
"emergency-resume-notification-card": "de-emergency-emergency-resume-notification-card-nA5Hwa",
|
|
1634
1644
|
"emergency-resume-notification-icon": "de-emergency-emergency-resume-notification-icon-nA5Hwa",
|
|
1635
1645
|
"emergency-resume-notification-message": "de-emergency-emergency-resume-notification-message-nA5Hwa",
|
|
1636
1646
|
"emergency-resume-notification-overlay": "de-emergency-emergency-resume-notification-overlay-nA5Hwa",
|
|
1637
1647
|
"emergency-resume-notification-title": "de-emergency-emergency-resume-notification-title-nA5Hwa",
|
|
1648
|
+
"emergency-screen": "de-emergency-emergency-screen-nA5Hwa",
|
|
1649
|
+
"emergency-screen__placeholder": "de-emergency-emergency-screen__placeholder-nA5Hwa",
|
|
1650
|
+
"emergency-screen__placeholder-text": "de-emergency-emergency-screen__placeholder-text-nA5Hwa",
|
|
1638
1651
|
"emergency-status__abort-btn": "de-emergency-emergency-status__abort-btn-nA5Hwa",
|
|
1639
1652
|
"emergency-status__body": "de-emergency-emergency-status__body-nA5Hwa",
|
|
1640
1653
|
"emergency-status__cell": "de-emergency-emergency-status__cell-nA5Hwa",
|
|
@@ -1680,6 +1693,532 @@ function cls(...names) {
|
|
|
1680
1693
|
return names.filter((name) => Boolean(name)).map((name) => panel_module_default[name] ?? "").filter(Boolean).join(" ");
|
|
1681
1694
|
}
|
|
1682
1695
|
|
|
1696
|
+
// src/react/components/EmergencyConfirmation/index.tsx
|
|
1697
|
+
function EmergencyConfirmation(props) {
|
|
1698
|
+
const { abortConfirmation, simulationConfirmation, onCancelAbort, onCancelSimulation, onConfirmAbort, onConfirmSimulation } = props;
|
|
1699
|
+
const cardRef = useRef(null);
|
|
1700
|
+
const titleId = useId();
|
|
1701
|
+
const danger = abortConfirmation;
|
|
1702
|
+
const open = abortConfirmation || simulationConfirmation;
|
|
1703
|
+
useEffect(() => {
|
|
1704
|
+
if (open) cardRef.current?.querySelector("button")?.focus();
|
|
1705
|
+
}, [open]);
|
|
1706
|
+
if (!open) return null;
|
|
1707
|
+
const title = danger ? copy.abortConfirmTitle : copy.simulateConfirmTitle;
|
|
1708
|
+
const text = danger ? copy.abortConfirmText : copy.simulateConfirmText;
|
|
1709
|
+
const tone = danger ? "danger" : "information";
|
|
1710
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1711
|
+
className: cls("emergency-confirmation"),
|
|
1712
|
+
role: "dialog",
|
|
1713
|
+
"aria-modal": "true",
|
|
1714
|
+
"aria-labelledby": titleId,
|
|
1715
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1716
|
+
ref: cardRef,
|
|
1717
|
+
className: cls("emergency-confirmation__card"),
|
|
1718
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1719
|
+
className: cls("emergency-confirmation__body"),
|
|
1720
|
+
children: [
|
|
1721
|
+
/* @__PURE__ */ jsx("div", {
|
|
1722
|
+
className: cls("emergency-confirmation__icon", `emergency-confirmation__icon--${tone}`),
|
|
1723
|
+
children: /* @__PURE__ */ jsx(Icon, { name: danger ? "alert-triangle" : "info" })
|
|
1724
|
+
}),
|
|
1725
|
+
/* @__PURE__ */ jsx("h5", {
|
|
1726
|
+
className: cls("emergency-confirmation__title"),
|
|
1727
|
+
id: titleId,
|
|
1728
|
+
children: title
|
|
1729
|
+
}),
|
|
1730
|
+
/* @__PURE__ */ jsx("p", {
|
|
1731
|
+
className: cls("emergency-confirmation__text"),
|
|
1732
|
+
children: text
|
|
1733
|
+
})
|
|
1734
|
+
]
|
|
1735
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1736
|
+
className: cls("emergency-confirmation__footer"),
|
|
1737
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
1738
|
+
type: "button",
|
|
1739
|
+
className: cls("emergency-confirmation__button", "emergency-confirmation__button--cancel"),
|
|
1740
|
+
onClick: danger ? onCancelAbort : onCancelSimulation,
|
|
1741
|
+
children: danger ? copy.abortCancel : copy.simulateCancel
|
|
1742
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
1743
|
+
type: "button",
|
|
1744
|
+
className: cls("emergency-confirmation__button", danger ? "emergency-confirmation__button--danger" : "emergency-confirmation__button--confirm"),
|
|
1745
|
+
onClick: danger ? onConfirmAbort : onConfirmSimulation,
|
|
1746
|
+
children: danger ? copy.abortConfirm : copy.simulateConfirm
|
|
1747
|
+
})]
|
|
1748
|
+
})]
|
|
1749
|
+
})
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// src/react/hooks/useEscapeKey.ts
|
|
1754
|
+
function useEscapeKey(active, onEscape) {
|
|
1755
|
+
useEffect(() => {
|
|
1756
|
+
if (!active || typeof document === "undefined") return;
|
|
1757
|
+
const handleKeyDown = (event) => {
|
|
1758
|
+
if (event.key === "Escape") {
|
|
1759
|
+
event.stopPropagation();
|
|
1760
|
+
onEscape();
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1764
|
+
return () => {
|
|
1765
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1766
|
+
};
|
|
1767
|
+
}, [active, onEscape]);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// src/react/hooks/useFocusTrap.ts
|
|
1771
|
+
const FOCUSABLE_SELECTOR = [
|
|
1772
|
+
"a[href]",
|
|
1773
|
+
"button:not([disabled])",
|
|
1774
|
+
"input:not([disabled])",
|
|
1775
|
+
"select:not([disabled])",
|
|
1776
|
+
"textarea:not([disabled])",
|
|
1777
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
1778
|
+
].join(",");
|
|
1779
|
+
/**
|
|
1780
|
+
* Keeps keyboard focus inside the dialog while it is open and restores focus to
|
|
1781
|
+
* the element that opened it when it closes.
|
|
1782
|
+
*/
|
|
1783
|
+
function useFocusTrap(ref, active) {
|
|
1784
|
+
useEffect(() => {
|
|
1785
|
+
if (!active || typeof document === "undefined") return;
|
|
1786
|
+
const container = ref.current;
|
|
1787
|
+
if (!container) return;
|
|
1788
|
+
const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
1789
|
+
const focusable = () => Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter((element) => !element.hasAttribute("hidden") && element.getAttribute("aria-hidden") !== "true");
|
|
1790
|
+
(focusable()[0] ?? container).focus({ preventScroll: true });
|
|
1791
|
+
const handleKeyDown = (event) => {
|
|
1792
|
+
if (event.key !== "Tab") return;
|
|
1793
|
+
const items = focusable();
|
|
1794
|
+
if (items.length === 0) {
|
|
1795
|
+
event.preventDefault();
|
|
1796
|
+
container.focus({ preventScroll: true });
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const first = items[0];
|
|
1800
|
+
const last = items[items.length - 1];
|
|
1801
|
+
const current = document.activeElement;
|
|
1802
|
+
if (event.shiftKey && (current === first || !container.contains(current))) {
|
|
1803
|
+
event.preventDefault();
|
|
1804
|
+
if (last) last.focus({ preventScroll: true });
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (!event.shiftKey && (current === last || !container.contains(current)) && first) {
|
|
1808
|
+
event.preventDefault();
|
|
1809
|
+
first.focus({ preventScroll: true });
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
document.addEventListener("keydown", handleKeyDown, true);
|
|
1813
|
+
return () => {
|
|
1814
|
+
document.removeEventListener("keydown", handleKeyDown, true);
|
|
1815
|
+
if (previouslyFocused?.isConnected) previouslyFocused.focus({ preventScroll: true });
|
|
1816
|
+
};
|
|
1817
|
+
}, [active, ref]);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/react/screens/context.tsx
|
|
1821
|
+
const EmergencyScreenContext = createContext(null);
|
|
1822
|
+
function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
1823
|
+
const [state, setState] = useState({
|
|
1824
|
+
screen: initialScreen,
|
|
1825
|
+
history: []
|
|
1826
|
+
});
|
|
1827
|
+
const navigate = useCallback((next) => {
|
|
1828
|
+
setState((current) => {
|
|
1829
|
+
if (current.screen === next) return current;
|
|
1830
|
+
return {
|
|
1831
|
+
screen: next,
|
|
1832
|
+
history: [...current.history, current.screen]
|
|
1833
|
+
};
|
|
1834
|
+
});
|
|
1835
|
+
}, []);
|
|
1836
|
+
const back = useCallback(() => {
|
|
1837
|
+
setState((current) => {
|
|
1838
|
+
const previous = current.history.at(-1);
|
|
1839
|
+
if (!previous) return current;
|
|
1840
|
+
return {
|
|
1841
|
+
screen: previous,
|
|
1842
|
+
history: current.history.slice(0, -1)
|
|
1843
|
+
};
|
|
1844
|
+
});
|
|
1845
|
+
}, []);
|
|
1846
|
+
const reset = useCallback(() => {
|
|
1847
|
+
setState((current) => {
|
|
1848
|
+
if (current.screen === "primary" && current.history.length === 0) return current;
|
|
1849
|
+
return {
|
|
1850
|
+
screen: "primary",
|
|
1851
|
+
history: []
|
|
1852
|
+
};
|
|
1853
|
+
});
|
|
1854
|
+
}, []);
|
|
1855
|
+
const value = useMemo(() => ({
|
|
1856
|
+
screen: state.screen,
|
|
1857
|
+
history: state.history,
|
|
1858
|
+
navigate,
|
|
1859
|
+
back,
|
|
1860
|
+
reset
|
|
1861
|
+
}), [
|
|
1862
|
+
back,
|
|
1863
|
+
navigate,
|
|
1864
|
+
reset,
|
|
1865
|
+
state.history,
|
|
1866
|
+
state.screen
|
|
1867
|
+
]);
|
|
1868
|
+
return /* @__PURE__ */ jsx(EmergencyScreenContext.Provider, {
|
|
1869
|
+
value,
|
|
1870
|
+
children
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
function useScreenNavigation() {
|
|
1874
|
+
const value = useContext(EmergencyScreenContext);
|
|
1875
|
+
if (!value) throw new Error("useScreenNavigation must be used inside EmergencyScreenProvider.");
|
|
1876
|
+
return value;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// src/react/components/EmergencyHeader/index.tsx
|
|
1880
|
+
function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack, variant = "default" }) {
|
|
1881
|
+
const closeOnly = variant === "close-only";
|
|
1882
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
1883
|
+
className: cls("emergency-panel__header", closeOnly && "emergency-panel__header--close-only"),
|
|
1884
|
+
children: [
|
|
1885
|
+
onBack && !closeOnly ? /* @__PURE__ */ jsx("button", {
|
|
1886
|
+
type: "button",
|
|
1887
|
+
className: cls("emergency-panel__close-button"),
|
|
1888
|
+
"aria-label": copy.back,
|
|
1889
|
+
onClick: onBack,
|
|
1890
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1891
|
+
name: "arrow-left",
|
|
1892
|
+
className: cls("emergency-panel__close-icon")
|
|
1893
|
+
})
|
|
1894
|
+
}) : null,
|
|
1895
|
+
/* @__PURE__ */ jsx("h2", {
|
|
1896
|
+
className: cls("emergency-panel__title", closeOnly && "emergency-panel__sr-only"),
|
|
1897
|
+
id: titleId,
|
|
1898
|
+
tabIndex: -1,
|
|
1899
|
+
children: title
|
|
1900
|
+
}),
|
|
1901
|
+
/* @__PURE__ */ jsx("button", {
|
|
1902
|
+
type: "button",
|
|
1903
|
+
className: cls("emergency-panel__close-button"),
|
|
1904
|
+
"aria-label": copy.close,
|
|
1905
|
+
onClick: onClose,
|
|
1906
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1907
|
+
name: "close",
|
|
1908
|
+
className: cls("emergency-panel__close-icon")
|
|
1909
|
+
})
|
|
1910
|
+
})
|
|
1911
|
+
]
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/react/screens/layout.tsx
|
|
1916
|
+
/**
|
|
1917
|
+
* Shared shell for every screen. The wrapper is `display: contents` so the
|
|
1918
|
+
* header, body and footer stay direct grid rows of the emergency panel.
|
|
1919
|
+
*/
|
|
1920
|
+
function EmergencyScreenLayout({ screen, titleId, header, footer, children, bodyClassName }) {
|
|
1921
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1922
|
+
className: cls("emergency-screen"),
|
|
1923
|
+
"data-screen": screen,
|
|
1924
|
+
children: [
|
|
1925
|
+
header,
|
|
1926
|
+
/* @__PURE__ */ jsx("section", {
|
|
1927
|
+
className: cls("emergency-panel__body", bodyClassName ?? null),
|
|
1928
|
+
"aria-labelledby": titleId,
|
|
1929
|
+
children
|
|
1930
|
+
}),
|
|
1931
|
+
footer
|
|
1932
|
+
]
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// src/react/screens/PlaceholderScreen.tsx
|
|
1937
|
+
/** Minimal in-design screen used to prove navigation before real designs land. */
|
|
1938
|
+
function PlaceholderScreen({ screen, titleId, title, description, onClose, onBack }) {
|
|
1939
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
1940
|
+
screen,
|
|
1941
|
+
titleId,
|
|
1942
|
+
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
1943
|
+
titleId,
|
|
1944
|
+
title,
|
|
1945
|
+
onClose,
|
|
1946
|
+
onBack
|
|
1947
|
+
}),
|
|
1948
|
+
footer: null,
|
|
1949
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1950
|
+
className: cls("emergency-screen__placeholder"),
|
|
1951
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
1952
|
+
className: cls("emergency-screen__placeholder-text"),
|
|
1953
|
+
children: description
|
|
1954
|
+
})
|
|
1955
|
+
})
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// src/react/screens/details/copy.ts
|
|
1960
|
+
const detailsCopy = {
|
|
1961
|
+
title: "Detalles",
|
|
1962
|
+
description: "Aquí podrás consultar el detalle de tus alertas disponibles. Esta pantalla estará disponible próximamente."
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1965
|
+
// src/react/screens/details/index.tsx
|
|
1966
|
+
function DetailsScreen({ titleId, onClose }) {
|
|
1967
|
+
const { back } = useScreenNavigation();
|
|
1968
|
+
return /* @__PURE__ */ jsx(PlaceholderScreen, {
|
|
1969
|
+
screen: "details",
|
|
1970
|
+
titleId,
|
|
1971
|
+
title: detailsCopy.title,
|
|
1972
|
+
description: detailsCopy.description,
|
|
1973
|
+
onClose: () => onClose("close"),
|
|
1974
|
+
onBack: back
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
// src/core/phone.ts
|
|
1979
|
+
const VENEZUELA_DIALLING_CODE = "+58";
|
|
1980
|
+
const VENEZUELAN_MOBILE_PATTERN = /^4\d{9}$/;
|
|
1981
|
+
function normalizeVenezuelanPhone(input) {
|
|
1982
|
+
return input.replace(/\D/g, "").replace(/^0+/, "");
|
|
1983
|
+
}
|
|
1984
|
+
function formatVenezuelanPhone(input) {
|
|
1985
|
+
const digits = normalizeVenezuelanPhone(input).slice(0, 10);
|
|
1986
|
+
if (digits.length <= 3) return digits;
|
|
1987
|
+
return `${digits.slice(0, 3)} ${digits.slice(3)}`;
|
|
1988
|
+
}
|
|
1989
|
+
function isValidVenezuelanPhone(input) {
|
|
1990
|
+
return VENEZUELAN_MOBILE_PATTERN.test(normalizeVenezuelanPhone(input));
|
|
1991
|
+
}
|
|
1992
|
+
function toVenezuelanE164(input) {
|
|
1993
|
+
const digits = normalizeVenezuelanPhone(input);
|
|
1994
|
+
if (!VENEZUELAN_MOBILE_PATTERN.test(digits)) return null;
|
|
1995
|
+
return `${"+58"}${digits}`;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// src/react/components/EmergencyFooter/index.tsx
|
|
1999
|
+
function EmergencyFooter(props) {
|
|
2000
|
+
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2001
|
+
if (active) return /* @__PURE__ */ jsx("footer", {
|
|
2002
|
+
className: cls("emergency-panel__footer"),
|
|
2003
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
2004
|
+
type: "button",
|
|
2005
|
+
className: cls("emergency-status__abort-btn"),
|
|
2006
|
+
"aria-label": copy.abort,
|
|
2007
|
+
disabled: aborting || feedbackVisible,
|
|
2008
|
+
onClick: onRequestAbort,
|
|
2009
|
+
children: [copy.abort, /* @__PURE__ */ jsx(Icon, { name: "alert-triangle" })]
|
|
2010
|
+
})
|
|
2011
|
+
});
|
|
2012
|
+
return /* @__PURE__ */ jsxs("footer", {
|
|
2013
|
+
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single"),
|
|
2014
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
2015
|
+
type: "button",
|
|
2016
|
+
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
2017
|
+
"aria-label": copy.emergencyAria,
|
|
2018
|
+
disabled: busy || feedbackVisible || emergencyDisabled,
|
|
2019
|
+
onClick: onActivate,
|
|
2020
|
+
children: [copy.emergency, /* @__PURE__ */ jsx(Icon, { name: "emergency" })]
|
|
2021
|
+
}), showSimulation ? /* @__PURE__ */ jsxs("button", {
|
|
2022
|
+
type: "button",
|
|
2023
|
+
className: cls("emergency-panel__button", "emergency-panel__button--simulation"),
|
|
2024
|
+
"aria-label": copy.testAria,
|
|
2025
|
+
disabled: busy || feedbackVisible,
|
|
2026
|
+
onClick: onRequestSimulation,
|
|
2027
|
+
children: [copy.test, /* @__PURE__ */ jsx(Icon, { name: "list-check" })]
|
|
2028
|
+
}) : null]
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// src/react/screens/pre-emergency/copy.ts
|
|
2033
|
+
const preEmergencyCopy = {
|
|
2034
|
+
title: "Pre-emergencia",
|
|
2035
|
+
permissionNotice: "Debes autorizar todos los permisos de ubicación",
|
|
2036
|
+
phoneLabel: "Indique su número de teléfono",
|
|
2037
|
+
phonePlaceholder: "Ej. 0412 000 0000",
|
|
2038
|
+
areaCodeLabel: "Código de país",
|
|
2039
|
+
areaCode: "+58",
|
|
2040
|
+
invalidPhone: "Ingresa un número móvil venezolano válido. Ej. 412 1301234"
|
|
2041
|
+
};
|
|
2042
|
+
|
|
2043
|
+
// src/react/screens/pre-emergency/index.tsx
|
|
2044
|
+
function PreEmergencyScreen(props) {
|
|
2045
|
+
const { back } = useScreenNavigation();
|
|
2046
|
+
const phoneId = useId();
|
|
2047
|
+
const errorId = `${phoneId}-error`;
|
|
2048
|
+
const permissionId = `${phoneId}-permission`;
|
|
2049
|
+
const [digits, setDigits] = useState("");
|
|
2050
|
+
const [touched, setTouched] = useState(false);
|
|
2051
|
+
const valid = isValidVenezuelanPhone(digits);
|
|
2052
|
+
const invalid = touched && digits.length > 0 && !valid;
|
|
2053
|
+
const handleChange = (event) => {
|
|
2054
|
+
const next = normalizeVenezuelanPhone(event.target.value).slice(0, 10);
|
|
2055
|
+
setDigits(next);
|
|
2056
|
+
};
|
|
2057
|
+
const handleSubmit = () => {
|
|
2058
|
+
setTouched(true);
|
|
2059
|
+
if (!valid) return;
|
|
2060
|
+
const phone = toVenezuelanE164(digits);
|
|
2061
|
+
if (phone === null) return;
|
|
2062
|
+
props.onSetContactPhone(phone);
|
|
2063
|
+
props.onActivate();
|
|
2064
|
+
};
|
|
2065
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2066
|
+
screen: "pre-emergency",
|
|
2067
|
+
titleId: props.titleId,
|
|
2068
|
+
bodyClassName: "emergency-panel__body--pre-emergency",
|
|
2069
|
+
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
2070
|
+
titleId: props.titleId,
|
|
2071
|
+
title: preEmergencyCopy.title,
|
|
2072
|
+
onClose: () => props.onClose("close"),
|
|
2073
|
+
onBack: back
|
|
2074
|
+
}),
|
|
2075
|
+
footer: /* @__PURE__ */ jsx(EmergencyFooter, {
|
|
2076
|
+
active: props.active,
|
|
2077
|
+
busy: props.busy,
|
|
2078
|
+
aborting: props.aborting,
|
|
2079
|
+
feedbackVisible: props.feedbackVisible,
|
|
2080
|
+
emergencyDisabled: !valid,
|
|
2081
|
+
showSimulation: false,
|
|
2082
|
+
onActivate: handleSubmit,
|
|
2083
|
+
onRequestAbort: props.onRequestAbort,
|
|
2084
|
+
onRequestSimulation: props.onRequestSimulation
|
|
2085
|
+
}),
|
|
2086
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2087
|
+
className: cls("emergency-pre-emergency"),
|
|
2088
|
+
children: [/* @__PURE__ */ jsxs("section", {
|
|
2089
|
+
role: "note",
|
|
2090
|
+
"aria-labelledby": permissionId,
|
|
2091
|
+
className: cls("emergency-pre-emergency__notice"),
|
|
2092
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
2093
|
+
name: "map-pin",
|
|
2094
|
+
className: cls("emergency-pre-emergency__notice-icon")
|
|
2095
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2096
|
+
id: permissionId,
|
|
2097
|
+
className: cls("emergency-pre-emergency__notice-text"),
|
|
2098
|
+
children: preEmergencyCopy.permissionNotice
|
|
2099
|
+
})]
|
|
2100
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
2101
|
+
className: cls("emergency-pre-emergency__form"),
|
|
2102
|
+
noValidate: true,
|
|
2103
|
+
onSubmit: (event) => {
|
|
2104
|
+
event.preventDefault();
|
|
2105
|
+
handleSubmit();
|
|
2106
|
+
},
|
|
2107
|
+
onKeyDown: (event) => {
|
|
2108
|
+
if (event.key === "Enter") {
|
|
2109
|
+
event.preventDefault();
|
|
2110
|
+
handleSubmit();
|
|
2111
|
+
}
|
|
2112
|
+
},
|
|
2113
|
+
children: [
|
|
2114
|
+
/* @__PURE__ */ jsx("label", {
|
|
2115
|
+
htmlFor: phoneId,
|
|
2116
|
+
className: cls("emergency-pre-emergency__label"),
|
|
2117
|
+
children: preEmergencyCopy.phoneLabel
|
|
2118
|
+
}),
|
|
2119
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2120
|
+
className: cls("emergency-pre-emergency__phone-row"),
|
|
2121
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
2122
|
+
type: "tel",
|
|
2123
|
+
"aria-label": preEmergencyCopy.areaCodeLabel,
|
|
2124
|
+
className: cls("emergency-pre-emergency__input", "emergency-pre-emergency__input--area-code", valid && "emergency-pre-emergency__input--valid"),
|
|
2125
|
+
value: preEmergencyCopy.areaCode,
|
|
2126
|
+
disabled: true,
|
|
2127
|
+
readOnly: true
|
|
2128
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
2129
|
+
id: phoneId,
|
|
2130
|
+
type: "tel",
|
|
2131
|
+
inputMode: "tel",
|
|
2132
|
+
autoComplete: "tel-national",
|
|
2133
|
+
placeholder: preEmergencyCopy.phonePlaceholder,
|
|
2134
|
+
className: cls("emergency-pre-emergency__input", "emergency-pre-emergency__input--number", valid && "emergency-pre-emergency__input--valid", invalid && "emergency-pre-emergency__input--invalid"),
|
|
2135
|
+
value: formatVenezuelanPhone(digits),
|
|
2136
|
+
"aria-invalid": invalid,
|
|
2137
|
+
"aria-describedby": invalid ? errorId : void 0,
|
|
2138
|
+
onChange: handleChange,
|
|
2139
|
+
onBlur: () => setTouched(true)
|
|
2140
|
+
})]
|
|
2141
|
+
}),
|
|
2142
|
+
invalid ? /* @__PURE__ */ jsx("p", {
|
|
2143
|
+
id: errorId,
|
|
2144
|
+
role: "alert",
|
|
2145
|
+
className: cls("emergency-pre-emergency__error"),
|
|
2146
|
+
children: preEmergencyCopy.invalidPhone
|
|
2147
|
+
}) : null
|
|
2148
|
+
]
|
|
2149
|
+
})]
|
|
2150
|
+
})
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// src/react/components/icons/paramedical-mark.tsx
|
|
2155
|
+
/**
|
|
2156
|
+
* Neutral inline illustration used by the idle panel.
|
|
2157
|
+
*/
|
|
2158
|
+
function ParamedicalMark({ className, title }) {
|
|
2159
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
2160
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2161
|
+
version: "1.0",
|
|
2162
|
+
viewBox: "0 0 192 192",
|
|
2163
|
+
width: "192",
|
|
2164
|
+
height: "192",
|
|
2165
|
+
id: "Star of life",
|
|
2166
|
+
className,
|
|
2167
|
+
"aria-label": title,
|
|
2168
|
+
children: [
|
|
2169
|
+
/* @__PURE__ */ jsx("defs", { id: "defs2886" }),
|
|
2170
|
+
/* @__PURE__ */ jsxs("g", {
|
|
2171
|
+
id: "cross",
|
|
2172
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
2173
|
+
d: "m 74,133 0,52 44,0 0,-52 44,26 22,-38 L 140,96 184,70 162,32 117,58 117,7 74,7 74,58 30,32 8,70 52,96 8,121 30,159 z",
|
|
2174
|
+
id: "outline",
|
|
2175
|
+
style: {
|
|
2176
|
+
fill: "none",
|
|
2177
|
+
stroke: "#3333bb",
|
|
2178
|
+
strokeWidth: 6
|
|
2179
|
+
}
|
|
2180
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
2181
|
+
d: "m 74,133 0,52 44,0 0,-52 44,26 22,-38 L 140,96 184,70 162,32 117,58 117,7 74,7 74,58 30,32 8,70 52,96 8,121 30,159 z",
|
|
2182
|
+
id: "inner",
|
|
2183
|
+
style: {
|
|
2184
|
+
fill: "#0000bb",
|
|
2185
|
+
fillRule: "evenodd",
|
|
2186
|
+
stroke: "#ffffff",
|
|
2187
|
+
strokeWidth: 2
|
|
2188
|
+
}
|
|
2189
|
+
})]
|
|
2190
|
+
}),
|
|
2191
|
+
/* @__PURE__ */ jsxs("g", {
|
|
2192
|
+
id: "symbols",
|
|
2193
|
+
style: {
|
|
2194
|
+
fill: "#ffffff",
|
|
2195
|
+
fillRule: "evenodd",
|
|
2196
|
+
stroke: "#3333bb",
|
|
2197
|
+
strokeWidth: 2
|
|
2198
|
+
},
|
|
2199
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
2200
|
+
d: "m 93,166 c 0,7 6,7 6,0 l 3,-138 c 0,-8 -12,-8 -12,0 l 3,135 0,3 z",
|
|
2201
|
+
id: "rod"
|
|
2202
|
+
}), /* @__PURE__ */ jsxs("g", {
|
|
2203
|
+
id: "snake",
|
|
2204
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
2205
|
+
d: "m 100,153 c 7,4 3,15 -3,15 2,-5 3,-9 3,-15 z m 1,-40 c 10,9 4,22 -3,26 -6,3 -6,7 -6,15 -9,-10 -4,-19 4,-24 5,-4 5,-10 5,-17 z m 1,-30 c 0,11 -17,14 -17,22 0,3 1,9 6,12 0,-11 9,-12 15,-18 7,-7 7,-19 -4,-28 0,4 0,8 0,12 z M 90,77 C 80,69 76,55 80,46 c 3,-10 20,-11 20,-3 0,8 -20,3 -13,16 l 3,5 0,13 z",
|
|
2206
|
+
id: "body"
|
|
2207
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
2208
|
+
d: "m 95,43 c 7,-3 -7,-3 0,0 z",
|
|
2209
|
+
id: "eye",
|
|
2210
|
+
style: {
|
|
2211
|
+
fill: "#3333bb",
|
|
2212
|
+
fillRule: "evenodd",
|
|
2213
|
+
stroke: "none"
|
|
2214
|
+
}
|
|
2215
|
+
})]
|
|
2216
|
+
})]
|
|
2217
|
+
})
|
|
2218
|
+
]
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
|
|
1683
2222
|
// src/react/components/MessageSurface/index.tsx
|
|
1684
2223
|
function MessageSurface(props) {
|
|
1685
2224
|
const { variant, icon, title, text, details, className, children } = props;
|
|
@@ -2004,237 +2543,410 @@ function EmergencyStatusPanel({ responders, refreshing, locale, onRefresh }) {
|
|
|
2004
2543
|
});
|
|
2005
2544
|
}
|
|
2006
2545
|
|
|
2546
|
+
// src/react/screens/primary/copy.ts
|
|
2547
|
+
const primaryCopy = {
|
|
2548
|
+
availabilityTitle: "Alertas Disponibles",
|
|
2549
|
+
recharge: "Recargar",
|
|
2550
|
+
details: "Detalles",
|
|
2551
|
+
roundSingular: "ronda disponible",
|
|
2552
|
+
roundPlural: "rondas disponibles"
|
|
2553
|
+
};
|
|
2554
|
+
/** Singular/plural label for the remaining rounds. */
|
|
2555
|
+
function roundsUnit(rounds) {
|
|
2556
|
+
return rounds === 1 ? primaryCopy.roundSingular : primaryCopy.roundPlural;
|
|
2557
|
+
}
|
|
2558
|
+
function formatRounds(rounds) {
|
|
2559
|
+
return `${rounds} ${roundsUnit(rounds)}`;
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// src/react/screens/primary/data.ts
|
|
2563
|
+
/**
|
|
2564
|
+
* Derives the remaining alert rounds from available messages and designated
|
|
2565
|
+
* responders. Invalid or non-positive input collapses to the minimum of one
|
|
2566
|
+
* round instead of producing `Infinity`, `NaN` or a negative value.
|
|
2567
|
+
*/
|
|
2568
|
+
function calculateRounds(messages, responders) {
|
|
2569
|
+
if (!Number.isFinite(messages) || !Number.isFinite(responders)) return 1;
|
|
2570
|
+
const availableMessages = Math.floor(messages);
|
|
2571
|
+
const designatedResponders = Math.floor(responders);
|
|
2572
|
+
if (availableMessages <= 0 || designatedResponders <= 0) return 1;
|
|
2573
|
+
return Math.max(1, Math.floor(availableMessages / designatedResponders));
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// src/react/screens/primary/Availability.tsx
|
|
2577
|
+
function Availability({ onRecharge, onDetails }) {
|
|
2578
|
+
const rounds = calculateRounds(100, 3);
|
|
2579
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
2580
|
+
className: cls("emergency-availability"),
|
|
2581
|
+
"aria-label": primaryCopy.availabilityTitle,
|
|
2582
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
2583
|
+
type: "button",
|
|
2584
|
+
className: cls("emergency-availability__recharge"),
|
|
2585
|
+
onClick: onRecharge,
|
|
2586
|
+
children: [/* @__PURE__ */ jsx(Icon, { name: "credit-card-plus" }), primaryCopy.recharge]
|
|
2587
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2588
|
+
className: cls("emergency-availability__card"),
|
|
2589
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2590
|
+
className: cls("emergency-availability__card-header"),
|
|
2591
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
2592
|
+
className: cls("emergency-availability__card-title"),
|
|
2593
|
+
children: primaryCopy.availabilityTitle
|
|
2594
|
+
})
|
|
2595
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2596
|
+
className: cls("emergency-availability__card-body"),
|
|
2597
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2598
|
+
className: cls("emergency-availability__rounds-group"),
|
|
2599
|
+
"aria-label": formatRounds(rounds),
|
|
2600
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2601
|
+
className: cls("emergency-availability__rounds"),
|
|
2602
|
+
children: rounds
|
|
2603
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2604
|
+
className: cls("emergency-availability__label"),
|
|
2605
|
+
children: roundsUnit(rounds)
|
|
2606
|
+
})]
|
|
2607
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2608
|
+
type: "button",
|
|
2609
|
+
className: cls("emergency-availability__details"),
|
|
2610
|
+
onClick: onDetails,
|
|
2611
|
+
children: primaryCopy.details
|
|
2612
|
+
})]
|
|
2613
|
+
})]
|
|
2614
|
+
})]
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2007
2618
|
// src/react/components/EmergencyBody/index.tsx
|
|
2008
2619
|
function EmergencyBody(props) {
|
|
2009
|
-
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback } = props;
|
|
2620
|
+
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback, onRecharge, onDetails } = props;
|
|
2010
2621
|
const showingFeedback = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
}) : null,
|
|
2020
|
-
active || loading || showingFeedback ? null : /* @__PURE__ */ jsx("div", {
|
|
2021
|
-
className: cls("emergency-panel__body-title-container"),
|
|
2022
|
-
children: /* @__PURE__ */ jsx("h4", {
|
|
2023
|
-
className: cls("emergency-panel__body-title"),
|
|
2024
|
-
children: copy.question
|
|
2622
|
+
const idle = !active && !loading && !showingFeedback && error === null;
|
|
2623
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2624
|
+
className: cls("emergency-panel__body-split", idle && "emergency-panel__body-split--with-availability"),
|
|
2625
|
+
children: [idle ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
2626
|
+
className: cls("emergency-panel__availability-region"),
|
|
2627
|
+
children: /* @__PURE__ */ jsx(Availability, {
|
|
2628
|
+
onRecharge,
|
|
2629
|
+
onDetails
|
|
2025
2630
|
})
|
|
2631
|
+
}), /* @__PURE__ */ jsx("hr", {
|
|
2632
|
+
className: cls("emergency-panel__divider"),
|
|
2633
|
+
"aria-hidden": "true"
|
|
2634
|
+
})] }) : null, /* @__PURE__ */ jsxs("div", {
|
|
2635
|
+
className: cls("emergency-panel__prompt-region"),
|
|
2636
|
+
children: [
|
|
2637
|
+
!loading && !showingFeedback ? /* @__PURE__ */ jsx("div", {
|
|
2638
|
+
className: cls("sos-img__container"),
|
|
2639
|
+
id: "sos-img__container",
|
|
2640
|
+
children: /* @__PURE__ */ jsx(ParamedicalMark, {
|
|
2641
|
+
className: cls("sos-img"),
|
|
2642
|
+
title: copy.illustrationAlt
|
|
2643
|
+
})
|
|
2644
|
+
}) : null,
|
|
2645
|
+
active || loading || showingFeedback ? null : /* @__PURE__ */ jsx("div", {
|
|
2646
|
+
className: cls("emergency-panel__body-title-container"),
|
|
2647
|
+
children: /* @__PURE__ */ jsx("h4", {
|
|
2648
|
+
className: cls("emergency-panel__body-title"),
|
|
2649
|
+
children: copy.question
|
|
2650
|
+
})
|
|
2651
|
+
}),
|
|
2652
|
+
active && !showingFeedback ? /* @__PURE__ */ jsx(EmergencyStatusPanel, {
|
|
2653
|
+
responders,
|
|
2654
|
+
refreshing,
|
|
2655
|
+
locale,
|
|
2656
|
+
onRefresh
|
|
2657
|
+
}) : null,
|
|
2658
|
+
loading ? /* @__PURE__ */ jsx(EmergencyLoading, {}) : null,
|
|
2659
|
+
error ? /* @__PURE__ */ jsx(EmergencyError, {
|
|
2660
|
+
error,
|
|
2661
|
+
onRetry
|
|
2662
|
+
}) : null,
|
|
2663
|
+
/* @__PURE__ */ jsx(EmergencyFeedback, {
|
|
2664
|
+
abortResult,
|
|
2665
|
+
simulationResult,
|
|
2666
|
+
geolocationError,
|
|
2667
|
+
activationError,
|
|
2668
|
+
onDismiss: onDismissFeedback
|
|
2669
|
+
})
|
|
2670
|
+
]
|
|
2671
|
+
})]
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// src/react/screens/primary/index.tsx
|
|
2676
|
+
function PrimaryScreen(props) {
|
|
2677
|
+
const { navigate } = useScreenNavigation();
|
|
2678
|
+
const idle = !props.active && !props.loading && !props.feedbackVisible && props.error === null;
|
|
2679
|
+
const needsContactPhone = props.contactPhone === null;
|
|
2680
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2681
|
+
screen: "primary",
|
|
2682
|
+
titleId: props.titleId,
|
|
2683
|
+
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
2684
|
+
titleId: props.titleId,
|
|
2685
|
+
variant: idle ? "close-only" : "default",
|
|
2686
|
+
onClose: () => props.onClose("close")
|
|
2026
2687
|
}),
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2688
|
+
footer: /* @__PURE__ */ jsx(EmergencyFooter, {
|
|
2689
|
+
active: props.active,
|
|
2690
|
+
busy: props.busy,
|
|
2691
|
+
aborting: props.aborting,
|
|
2692
|
+
feedbackVisible: props.feedbackVisible,
|
|
2693
|
+
onActivate: needsContactPhone ? () => navigate("pre-emergency") : props.onActivate,
|
|
2694
|
+
onRequestAbort: props.onRequestAbort,
|
|
2695
|
+
onRequestSimulation: props.onRequestSimulation
|
|
2696
|
+
}),
|
|
2697
|
+
children: /* @__PURE__ */ jsx(EmergencyBody, {
|
|
2698
|
+
active: props.active,
|
|
2699
|
+
responders: props.responders,
|
|
2700
|
+
refreshing: props.refreshing,
|
|
2701
|
+
loading: props.loading,
|
|
2702
|
+
locale: props.locale,
|
|
2703
|
+
geolocationError: props.geolocationError,
|
|
2704
|
+
activationError: props.activationError,
|
|
2705
|
+
error: props.error,
|
|
2706
|
+
abortResult: props.abortResult,
|
|
2707
|
+
simulationResult: props.simulationResult,
|
|
2708
|
+
onRefresh: props.onRefresh,
|
|
2709
|
+
onRetry: props.onRetry,
|
|
2710
|
+
onDismissFeedback: props.onDismissFeedback,
|
|
2711
|
+
onRecharge: () => navigate("pricing"),
|
|
2712
|
+
onDetails: () => navigate("details")
|
|
2044
2713
|
})
|
|
2045
|
-
|
|
2714
|
+
});
|
|
2046
2715
|
}
|
|
2047
2716
|
|
|
2048
|
-
// src/react/
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2717
|
+
// src/react/screens/pricing/copy.ts
|
|
2718
|
+
const pricingCopy = {
|
|
2719
|
+
title: "Recargar",
|
|
2720
|
+
heading: "Elige tu paquete",
|
|
2721
|
+
subtitle: "Alertas de WhatsApp para tus contactos",
|
|
2722
|
+
bestValue: "MEJOR VALOR",
|
|
2723
|
+
choose: "Seleccionar",
|
|
2724
|
+
chosen: "Seleccionado",
|
|
2725
|
+
whatsapp: "de WhatsApp",
|
|
2726
|
+
rounds: "rondas de alerta",
|
|
2727
|
+
perMessage: "IVA Incluido",
|
|
2728
|
+
footnote: "Rondas calculadas con 3 contactos de emergencia. Válidas para pruebas y emergencias."
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
// src/react/screens/pricing/data.ts
|
|
2732
|
+
/**
|
|
2733
|
+
* Local demo catalog. Prices and per-message labels are literal values from the
|
|
2734
|
+
* mock; they are not derived from the package totals.
|
|
2735
|
+
*/
|
|
2736
|
+
const PRICING_PACKAGES = [{
|
|
2737
|
+
id: "package-1",
|
|
2738
|
+
label: "PAQUETE 1",
|
|
2739
|
+
messages: 100,
|
|
2740
|
+
priceLabel: "$7",
|
|
2741
|
+
priceCaption: "IVA incluído",
|
|
2742
|
+
perMessageLabel: "$0.07",
|
|
2743
|
+
highlighted: false
|
|
2744
|
+
}, {
|
|
2745
|
+
id: "package-2",
|
|
2746
|
+
label: "PAQUETE 2",
|
|
2747
|
+
messages: 200,
|
|
2748
|
+
priceLabel: "12$",
|
|
2749
|
+
priceCaption: "IVA incluído",
|
|
2750
|
+
perMessageLabel: "$0.06",
|
|
2751
|
+
highlighted: true,
|
|
2752
|
+
badge: "MEJOR VALOR"
|
|
2753
|
+
}];
|
|
2754
|
+
/** Derives the alert rounds a package provides for the demo responder count. */
|
|
2755
|
+
function packageRounds(pkg) {
|
|
2756
|
+
return calculateRounds(pkg.messages, 3);
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
// src/react/screens/pricing/PackageCard.tsx
|
|
2760
|
+
function PackageCard({ pkg, selected, onSelect }) {
|
|
2761
|
+
const labelId = useId();
|
|
2762
|
+
const rounds = packageRounds(pkg);
|
|
2763
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
2764
|
+
className: cls("emergency-pricing__package", pkg.highlighted && "emergency-pricing__package--highlighted"),
|
|
2765
|
+
"aria-labelledby": labelId,
|
|
2766
|
+
"data-selected": selected ? "true" : void 0,
|
|
2767
|
+
children: [
|
|
2768
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2769
|
+
className: cls("emergency-pricing__package-header"),
|
|
2770
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2771
|
+
className: cls("emergency-pricing__package-label"),
|
|
2772
|
+
id: labelId,
|
|
2773
|
+
children: pkg.label
|
|
2774
|
+
}), pkg.badge ? /* @__PURE__ */ jsx("span", {
|
|
2775
|
+
className: cls("emergency-pricing__badge"),
|
|
2776
|
+
children: pkg.badge
|
|
2777
|
+
}) : null]
|
|
2778
|
+
}),
|
|
2779
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2780
|
+
className: cls("emergency-pricing__price-row"),
|
|
2781
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("h4", {
|
|
2782
|
+
className: cls("emergency-pricing__package-name"),
|
|
2783
|
+
children: [pkg.messages, " alertas"]
|
|
2784
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2785
|
+
className: cls("emergency-pricing__package-caption"),
|
|
2786
|
+
children: pricingCopy.whatsapp
|
|
2787
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
2788
|
+
className: cls("emergency-pricing__price-meta"),
|
|
2789
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2790
|
+
className: cls("emergency-pricing__price"),
|
|
2791
|
+
children: pkg.priceLabel
|
|
2792
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
2793
|
+
className: cls("emergency-pricing__price-note"),
|
|
2794
|
+
children: [pkg.priceCaption, pkg.priceNote ? ` ${pkg.priceNote}` : null]
|
|
2795
|
+
})]
|
|
2796
|
+
})]
|
|
2797
|
+
}),
|
|
2798
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2799
|
+
className: cls("emergency-pricing__stats"),
|
|
2072
2800
|
children: [
|
|
2073
|
-
/* @__PURE__ */
|
|
2074
|
-
className: cls("emergency-
|
|
2075
|
-
children: /* @__PURE__ */ jsx(
|
|
2801
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2802
|
+
className: cls("emergency-pricing__stat"),
|
|
2803
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2804
|
+
className: cls("emergency-pricing__stat-value"),
|
|
2805
|
+
children: rounds
|
|
2806
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2807
|
+
className: cls("emergency-pricing__stat-label"),
|
|
2808
|
+
children: pricingCopy.rounds
|
|
2809
|
+
})]
|
|
2076
2810
|
}),
|
|
2077
|
-
/* @__PURE__ */ jsx("
|
|
2078
|
-
className: cls("emergency-
|
|
2079
|
-
|
|
2080
|
-
children: title
|
|
2811
|
+
/* @__PURE__ */ jsx("div", {
|
|
2812
|
+
className: cls("emergency-pricing__stat-divider"),
|
|
2813
|
+
"aria-hidden": "true"
|
|
2081
2814
|
}),
|
|
2082
|
-
/* @__PURE__ */
|
|
2083
|
-
className: cls("emergency-
|
|
2084
|
-
children:
|
|
2815
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2816
|
+
className: cls("emergency-pricing__stat"),
|
|
2817
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2818
|
+
className: cls("emergency-pricing__stat-value"),
|
|
2819
|
+
children: pkg.perMessageLabel
|
|
2820
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2821
|
+
className: cls("emergency-pricing__stat-label"),
|
|
2822
|
+
children: pricingCopy.perMessage
|
|
2823
|
+
})]
|
|
2085
2824
|
})
|
|
2086
2825
|
]
|
|
2087
|
-
}),
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
})]
|
|
2101
|
-
})
|
|
2826
|
+
}),
|
|
2827
|
+
/* @__PURE__ */ jsx("p", {
|
|
2828
|
+
className: cls("emergency-pricing__package-footnote"),
|
|
2829
|
+
children: pricingCopy.footnote
|
|
2830
|
+
}),
|
|
2831
|
+
/* @__PURE__ */ jsx("button", {
|
|
2832
|
+
type: "button",
|
|
2833
|
+
className: cls("emergency-pricing__select"),
|
|
2834
|
+
"aria-pressed": selected,
|
|
2835
|
+
onClick: () => onSelect(pkg.id),
|
|
2836
|
+
children: selected ? pricingCopy.chosen : pricingCopy.choose
|
|
2837
|
+
})
|
|
2838
|
+
]
|
|
2102
2839
|
});
|
|
2103
2840
|
}
|
|
2104
2841
|
|
|
2105
|
-
// src/react/
|
|
2106
|
-
function
|
|
2107
|
-
const {
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2842
|
+
// src/react/screens/pricing/index.tsx
|
|
2843
|
+
function PricingScreen(props) {
|
|
2844
|
+
const { back } = useScreenNavigation();
|
|
2845
|
+
const [selected, setSelected] = useState(null);
|
|
2846
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2847
|
+
screen: "pricing",
|
|
2848
|
+
titleId: props.titleId,
|
|
2849
|
+
bodyClassName: "emergency-panel__body--pricing",
|
|
2850
|
+
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
2851
|
+
titleId: props.titleId,
|
|
2852
|
+
title: pricingCopy.title,
|
|
2853
|
+
onClose: () => props.onClose("close"),
|
|
2854
|
+
onBack: back
|
|
2855
|
+
}),
|
|
2856
|
+
footer: null,
|
|
2857
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2858
|
+
className: cls("emergency-pricing"),
|
|
2859
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2860
|
+
className: cls("emergency-pricing__heading"),
|
|
2861
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2862
|
+
className: cls("emergency-pricing__titles"),
|
|
2863
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
2864
|
+
className: cls("emergency-pricing__title"),
|
|
2865
|
+
children: pricingCopy.heading
|
|
2866
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2867
|
+
className: cls("emergency-pricing__subtitle"),
|
|
2868
|
+
children: pricingCopy.subtitle
|
|
2869
|
+
})]
|
|
2870
|
+
})
|
|
2871
|
+
}), PRICING_PACKAGES.map((pkg) => /* @__PURE__ */ jsx(PackageCard, {
|
|
2872
|
+
pkg,
|
|
2873
|
+
selected: selected === pkg.id,
|
|
2874
|
+
onSelect: setSelected
|
|
2875
|
+
}, pkg.id))]
|
|
2117
2876
|
})
|
|
2118
2877
|
});
|
|
2119
|
-
return /* @__PURE__ */ jsxs("footer", {
|
|
2120
|
-
className: cls("emergency-panel__footer"),
|
|
2121
|
-
children: [/* @__PURE__ */ jsxs("button", {
|
|
2122
|
-
type: "button",
|
|
2123
|
-
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
2124
|
-
"aria-label": copy.emergencyAria,
|
|
2125
|
-
disabled: busy || feedbackVisible,
|
|
2126
|
-
onClick: onActivate,
|
|
2127
|
-
children: [copy.emergency, /* @__PURE__ */ jsx(Icon, { name: "emergency" })]
|
|
2128
|
-
}), /* @__PURE__ */ jsxs("button", {
|
|
2129
|
-
type: "button",
|
|
2130
|
-
className: cls("emergency-panel__button", "emergency-panel__button--simulation"),
|
|
2131
|
-
"aria-label": copy.testAria,
|
|
2132
|
-
disabled: busy || feedbackVisible,
|
|
2133
|
-
onClick: onRequestSimulation,
|
|
2134
|
-
children: [copy.test, /* @__PURE__ */ jsx(Icon, { name: "list-check" })]
|
|
2135
|
-
})]
|
|
2136
|
-
});
|
|
2137
2878
|
}
|
|
2138
2879
|
|
|
2139
|
-
// src/react/
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
2148
|
-
type: "button",
|
|
2149
|
-
className: cls("emergency-panel__close-button"),
|
|
2150
|
-
"aria-label": copy.close,
|
|
2151
|
-
onClick: onClose,
|
|
2152
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
2153
|
-
name: "close",
|
|
2154
|
-
className: cls("emergency-panel__close-icon")
|
|
2155
|
-
})
|
|
2156
|
-
})]
|
|
2157
|
-
});
|
|
2158
|
-
}
|
|
2880
|
+
// src/react/screens/registry.ts
|
|
2881
|
+
/** Every screen the panel can render. Add one entry to add a screen. */
|
|
2882
|
+
const screenRegistry = {
|
|
2883
|
+
primary: PrimaryScreen,
|
|
2884
|
+
"pre-emergency": PreEmergencyScreen,
|
|
2885
|
+
pricing: PricingScreen,
|
|
2886
|
+
details: DetailsScreen
|
|
2887
|
+
};
|
|
2159
2888
|
|
|
2160
|
-
// src/react/
|
|
2161
|
-
function
|
|
2889
|
+
// src/react/components/EmergencyPanel/index.tsx
|
|
2890
|
+
function ActiveScreen({ screenProps, open, forcePrimary }) {
|
|
2891
|
+
const { screen, reset } = useScreenNavigation();
|
|
2892
|
+
const shouldReset = !open || forcePrimary;
|
|
2893
|
+
const effectiveScreen = shouldReset ? "primary" : screen;
|
|
2894
|
+
const previousOpen = useRef(open);
|
|
2895
|
+
const previousScreen = useRef(effectiveScreen);
|
|
2162
2896
|
useEffect(() => {
|
|
2163
|
-
if (
|
|
2164
|
-
|
|
2165
|
-
if (event.key === "Escape") {
|
|
2166
|
-
event.stopPropagation();
|
|
2167
|
-
onEscape();
|
|
2168
|
-
}
|
|
2169
|
-
};
|
|
2170
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
2171
|
-
return () => {
|
|
2172
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
2173
|
-
};
|
|
2174
|
-
}, [active, onEscape]);
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
// src/react/hooks/useFocusTrap.ts
|
|
2178
|
-
const FOCUSABLE_SELECTOR = [
|
|
2179
|
-
"a[href]",
|
|
2180
|
-
"button:not([disabled])",
|
|
2181
|
-
"input:not([disabled])",
|
|
2182
|
-
"select:not([disabled])",
|
|
2183
|
-
"textarea:not([disabled])",
|
|
2184
|
-
"[tabindex]:not([tabindex=\"-1\"])"
|
|
2185
|
-
].join(",");
|
|
2186
|
-
/**
|
|
2187
|
-
* Keeps keyboard focus inside the dialog while it is open and restores focus to
|
|
2188
|
-
* the element that opened it when it closes.
|
|
2189
|
-
*/
|
|
2190
|
-
function useFocusTrap(ref, active) {
|
|
2897
|
+
if (shouldReset) reset();
|
|
2898
|
+
}, [reset, shouldReset]);
|
|
2191
2899
|
useEffect(() => {
|
|
2192
|
-
|
|
2193
|
-
const
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
(
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
}
|
|
2206
|
-
const first = items[0];
|
|
2207
|
-
const last = items[items.length - 1];
|
|
2208
|
-
const current = document.activeElement;
|
|
2209
|
-
if (event.shiftKey && (current === first || !container.contains(current))) {
|
|
2210
|
-
event.preventDefault();
|
|
2211
|
-
if (last) last.focus({ preventScroll: true });
|
|
2212
|
-
return;
|
|
2213
|
-
}
|
|
2214
|
-
if (!event.shiftKey && current === last && first) {
|
|
2215
|
-
event.preventDefault();
|
|
2216
|
-
first.focus({ preventScroll: true });
|
|
2217
|
-
}
|
|
2218
|
-
};
|
|
2219
|
-
document.addEventListener("keydown", handleKeyDown, true);
|
|
2220
|
-
return () => {
|
|
2221
|
-
document.removeEventListener("keydown", handleKeyDown, true);
|
|
2222
|
-
if (previouslyFocused?.isConnected) previouslyFocused.focus({ preventScroll: true });
|
|
2223
|
-
};
|
|
2224
|
-
}, [active, ref]);
|
|
2900
|
+
const wasOpen = previousOpen.current;
|
|
2901
|
+
const previousEffectiveScreen = previousScreen.current;
|
|
2902
|
+
previousOpen.current = open;
|
|
2903
|
+
previousScreen.current = effectiveScreen;
|
|
2904
|
+
if (!open || !wasOpen || previousEffectiveScreen === effectiveScreen) return;
|
|
2905
|
+
document.getElementById(screenProps.titleId)?.focus({ preventScroll: true });
|
|
2906
|
+
}, [
|
|
2907
|
+
effectiveScreen,
|
|
2908
|
+
open,
|
|
2909
|
+
screenProps.titleId
|
|
2910
|
+
]);
|
|
2911
|
+
const Screen = screenRegistry[effectiveScreen];
|
|
2912
|
+
return /* @__PURE__ */ jsx(Screen, { ...screenProps });
|
|
2225
2913
|
}
|
|
2226
|
-
|
|
2227
|
-
// src/react/components/EmergencyPanel/index.tsx
|
|
2228
2914
|
function EmergencyPanel(props) {
|
|
2229
|
-
const { open, theme, active, busy, aborting, loading, locale, responders, refreshing, geolocationError, activationError, error, abortResult, simulationResult, abortConfirmation, simulationConfirmation, onClose, onActivate, onRefresh, onRetry, onDismissFeedback, onRequestAbort, onRequestSimulation, onCancelAbort, onCancelSimulation, onConfirmAbort, onConfirmSimulation } = props;
|
|
2915
|
+
const { open, theme, active, busy, aborting, loading, locale, responders, refreshing, geolocationError, contactPhone, activationError, error, abortResult, simulationResult, abortConfirmation, simulationConfirmation, onClose, onActivate, onRefresh, onRetry, onDismissFeedback, onRequestAbort, onRequestSimulation, onCancelAbort, onCancelSimulation, onConfirmAbort, onConfirmSimulation, onSetContactPhone } = props;
|
|
2230
2916
|
const titleId = useId();
|
|
2231
2917
|
const dialogRef = useRef(null);
|
|
2232
2918
|
const feedbackVisible = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
2919
|
+
const forcePrimary = active || loading || error !== null || feedbackVisible;
|
|
2233
2920
|
const closeOnEscape = useCallback(() => {
|
|
2234
2921
|
onClose("escape");
|
|
2235
2922
|
}, [onClose]);
|
|
2236
2923
|
useFocusTrap(dialogRef, open);
|
|
2237
2924
|
useEscapeKey(open, closeOnEscape);
|
|
2925
|
+
const screenProps = {
|
|
2926
|
+
titleId,
|
|
2927
|
+
active,
|
|
2928
|
+
busy,
|
|
2929
|
+
aborting,
|
|
2930
|
+
loading,
|
|
2931
|
+
locale,
|
|
2932
|
+
responders,
|
|
2933
|
+
refreshing,
|
|
2934
|
+
geolocationError,
|
|
2935
|
+
activationError,
|
|
2936
|
+
error,
|
|
2937
|
+
abortResult,
|
|
2938
|
+
simulationResult,
|
|
2939
|
+
feedbackVisible,
|
|
2940
|
+
contactPhone,
|
|
2941
|
+
onClose,
|
|
2942
|
+
onActivate,
|
|
2943
|
+
onRefresh,
|
|
2944
|
+
onRetry,
|
|
2945
|
+
onDismissFeedback,
|
|
2946
|
+
onRequestAbort,
|
|
2947
|
+
onRequestSimulation,
|
|
2948
|
+
onSetContactPhone
|
|
2949
|
+
};
|
|
2238
2950
|
return /* @__PURE__ */ jsxs("div", {
|
|
2239
2951
|
ref: dialogRef,
|
|
2240
2952
|
className: cls("emergency-panel", !open && "emergency-panel--hidden"),
|
|
@@ -2245,48 +2957,18 @@ function EmergencyPanel(props) {
|
|
|
2245
2957
|
"aria-hidden": !open,
|
|
2246
2958
|
inert: !open,
|
|
2247
2959
|
tabIndex: -1,
|
|
2248
|
-
children: [
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
loading,
|
|
2261
|
-
locale,
|
|
2262
|
-
geolocationError,
|
|
2263
|
-
activationError,
|
|
2264
|
-
error,
|
|
2265
|
-
abortResult,
|
|
2266
|
-
simulationResult,
|
|
2267
|
-
onRefresh,
|
|
2268
|
-
onRetry,
|
|
2269
|
-
onDismissFeedback
|
|
2270
|
-
})
|
|
2271
|
-
}),
|
|
2272
|
-
/* @__PURE__ */ jsx(EmergencyFooter, {
|
|
2273
|
-
active,
|
|
2274
|
-
busy,
|
|
2275
|
-
aborting,
|
|
2276
|
-
feedbackVisible,
|
|
2277
|
-
onActivate,
|
|
2278
|
-
onRequestAbort,
|
|
2279
|
-
onRequestSimulation
|
|
2280
|
-
}),
|
|
2281
|
-
/* @__PURE__ */ jsx(EmergencyConfirmation, {
|
|
2282
|
-
abortConfirmation,
|
|
2283
|
-
simulationConfirmation,
|
|
2284
|
-
onCancelAbort,
|
|
2285
|
-
onCancelSimulation,
|
|
2286
|
-
onConfirmAbort,
|
|
2287
|
-
onConfirmSimulation
|
|
2288
|
-
})
|
|
2289
|
-
]
|
|
2960
|
+
children: [/* @__PURE__ */ jsx(EmergencyScreenProvider, { children: /* @__PURE__ */ jsx(ActiveScreen, {
|
|
2961
|
+
screenProps,
|
|
2962
|
+
open,
|
|
2963
|
+
forcePrimary
|
|
2964
|
+
}) }), /* @__PURE__ */ jsx(EmergencyConfirmation, {
|
|
2965
|
+
abortConfirmation,
|
|
2966
|
+
simulationConfirmation,
|
|
2967
|
+
onCancelAbort,
|
|
2968
|
+
onCancelSimulation,
|
|
2969
|
+
onConfirmAbort,
|
|
2970
|
+
onConfirmSimulation
|
|
2971
|
+
})]
|
|
2290
2972
|
});
|
|
2291
2973
|
}
|
|
2292
2974
|
|
|
@@ -2370,6 +3052,7 @@ function EmergencyRoot() {
|
|
|
2370
3052
|
const responders = useEmergencyStore((state) => state.responders);
|
|
2371
3053
|
const resumeAvailable = useEmergencyStore((state) => state.resumeAvailable);
|
|
2372
3054
|
const geolocationError = useEmergencyStore((state) => state.geolocationError);
|
|
3055
|
+
const contactPhone = useEmergencyStore((state) => state.contactPhone);
|
|
2373
3056
|
const lastError = useEmergencyStore((state) => state.lastError);
|
|
2374
3057
|
const abortResult = useEmergencyStore((state) => state.abortResult);
|
|
2375
3058
|
const simulationResult = useEmergencyStore((state) => state.simulationResult);
|
|
@@ -2395,6 +3078,7 @@ function EmergencyRoot() {
|
|
|
2395
3078
|
responders,
|
|
2396
3079
|
refreshing: phase.kind === "refreshing",
|
|
2397
3080
|
geolocationError,
|
|
3081
|
+
contactPhone,
|
|
2398
3082
|
activationError,
|
|
2399
3083
|
error: showError ? error : null,
|
|
2400
3084
|
abortResult,
|
|
@@ -2421,7 +3105,8 @@ function EmergencyRoot() {
|
|
|
2421
3105
|
},
|
|
2422
3106
|
onConfirmSimulation: () => {
|
|
2423
3107
|
client.simulate();
|
|
2424
|
-
}
|
|
3108
|
+
},
|
|
3109
|
+
onSetContactPhone: (phone) => client.setContactPhone(phone)
|
|
2425
3110
|
})] });
|
|
2426
3111
|
}
|
|
2427
3112
|
|
|
@@ -2453,6 +3138,7 @@ function createFailedClient(error) {
|
|
|
2453
3138
|
lastError: error,
|
|
2454
3139
|
geolocation: null,
|
|
2455
3140
|
geolocationError: null,
|
|
3141
|
+
contactPhone: null,
|
|
2456
3142
|
realtimeStatus: "idle",
|
|
2457
3143
|
abortResult: null,
|
|
2458
3144
|
simulationResult: null,
|
|
@@ -2481,7 +3167,8 @@ function createFailedClient(error) {
|
|
|
2481
3167
|
cancelAbortConfirmation: noop,
|
|
2482
3168
|
requestSimulationConfirmation: noop,
|
|
2483
3169
|
cancelSimulationConfirmation: noop,
|
|
2484
|
-
dismissFeedback: noop
|
|
3170
|
+
dismissFeedback: noop,
|
|
3171
|
+
setContactPhone: noop
|
|
2485
3172
|
};
|
|
2486
3173
|
}
|
|
2487
3174
|
function attachTrigger(trigger, client) {
|
|
@@ -2570,6 +3257,7 @@ function mount(options) {
|
|
|
2570
3257
|
requestSimulationConfirmation: () => client.requestSimulationConfirmation(),
|
|
2571
3258
|
cancelSimulationConfirmation: () => client.cancelSimulationConfirmation(),
|
|
2572
3259
|
dismissFeedback: () => client.dismissFeedback(),
|
|
3260
|
+
setContactPhone: (phone) => client.setContactPhone(phone),
|
|
2573
3261
|
destroy
|
|
2574
3262
|
};
|
|
2575
3263
|
}
|