@dataemergencia/emergency 0.4.0 → 0.6.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 +3 -3
- package/dist/index.cjs +819 -149
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +819 -149
- package/dist/styles.css +540 -71
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -763,6 +763,7 @@ function createEmergencyStore(config) {
|
|
|
763
763
|
lastError: null,
|
|
764
764
|
geolocation: null,
|
|
765
765
|
geolocationError: null,
|
|
766
|
+
contactPhone: null,
|
|
766
767
|
realtimeStatus: "idle",
|
|
767
768
|
abortResult: null,
|
|
768
769
|
simulationResult: null,
|
|
@@ -1205,7 +1206,8 @@ function createEmergencyController(deps) {
|
|
|
1205
1206
|
patch({
|
|
1206
1207
|
panelOpen: false,
|
|
1207
1208
|
abortConfirmation: false,
|
|
1208
|
-
simulationConfirmation: false
|
|
1209
|
+
simulationConfirmation: false,
|
|
1210
|
+
contactPhone: null
|
|
1209
1211
|
});
|
|
1210
1212
|
events.emit("panel:closed", { reason });
|
|
1211
1213
|
}
|
|
@@ -1254,6 +1256,7 @@ function createEmergencyController(deps) {
|
|
|
1254
1256
|
lastError: null,
|
|
1255
1257
|
geolocation: null,
|
|
1256
1258
|
geolocationError: null,
|
|
1259
|
+
contactPhone: null,
|
|
1257
1260
|
realtimeStatus: "idle",
|
|
1258
1261
|
abortResult: null,
|
|
1259
1262
|
simulationResult: null,
|
|
@@ -1295,7 +1298,8 @@ function createEmergencyController(deps) {
|
|
|
1295
1298
|
geolocationError: null,
|
|
1296
1299
|
...state.geolocationError !== null || state.abortResult?.ok === false || state.simulationResult?.ok === false || state.phase.kind === "ready" && state.lastError !== null ? { lastError: null } : {}
|
|
1297
1300
|
});
|
|
1298
|
-
}
|
|
1301
|
+
},
|
|
1302
|
+
setContactPhone: (phone) => patch({ contactPhone: phone })
|
|
1299
1303
|
},
|
|
1300
1304
|
store,
|
|
1301
1305
|
events
|
|
@@ -1526,6 +1530,11 @@ const paths = {
|
|
|
1526
1530
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 7h.01" })
|
|
1527
1531
|
] }),
|
|
1528
1532
|
"arrow-left": /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M19 12H5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m12 19-7-7 7-7" })] }),
|
|
1533
|
+
"map-pin": /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 22s7-6.2 7-12a7 7 0 1 0-14 0c0 5.8 7 12 7 12z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1534
|
+
cx: "12",
|
|
1535
|
+
cy: "10",
|
|
1536
|
+
r: "2.5"
|
|
1537
|
+
})] }),
|
|
1529
1538
|
"credit-card-plus": /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1530
1539
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5" }),
|
|
1531
1540
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M18 12h.01" }),
|
|
@@ -1567,6 +1576,12 @@ var panel_module_default = {
|
|
|
1567
1576
|
"de-emergency-slide-in": "de-emergency-de-emergency-slide-in-nA5Hwa",
|
|
1568
1577
|
"de-emergency-slide-up": "de-emergency-de-emergency-slide-up-nA5Hwa",
|
|
1569
1578
|
"de-emergency-spin": "de-emergency-de-emergency-spin-nA5Hwa",
|
|
1579
|
+
"emergency-alert-row__badge": "de-emergency-emergency-alert-row__badge-nA5Hwa",
|
|
1580
|
+
"emergency-alert-row__button": "de-emergency-emergency-alert-row__button-nA5Hwa",
|
|
1581
|
+
"emergency-alert-row__stat": "de-emergency-emergency-alert-row__stat-nA5Hwa",
|
|
1582
|
+
"emergency-alert-row__stats": "de-emergency-emergency-alert-row__stats-nA5Hwa",
|
|
1583
|
+
"emergency-alert-row__time": "de-emergency-emergency-alert-row__time-nA5Hwa",
|
|
1584
|
+
"emergency-alert-row__top": "de-emergency-emergency-alert-row__top-nA5Hwa",
|
|
1570
1585
|
"emergency-availability": "de-emergency-emergency-availability-nA5Hwa",
|
|
1571
1586
|
"emergency-availability__card": "de-emergency-emergency-availability__card-nA5Hwa",
|
|
1572
1587
|
"emergency-availability__card-body": "de-emergency-emergency-availability__card-body-nA5Hwa",
|
|
@@ -1590,6 +1605,19 @@ var panel_module_default = {
|
|
|
1590
1605
|
"emergency-confirmation__icon--information": "de-emergency-emergency-confirmation__icon--information-nA5Hwa",
|
|
1591
1606
|
"emergency-confirmation__text": "de-emergency-emergency-confirmation__text-nA5Hwa",
|
|
1592
1607
|
"emergency-confirmation__title": "de-emergency-emergency-confirmation__title-nA5Hwa",
|
|
1608
|
+
"emergency-details": "de-emergency-emergency-details-nA5Hwa",
|
|
1609
|
+
"emergency-details__balance": "de-emergency-emergency-details__balance-nA5Hwa",
|
|
1610
|
+
"emergency-details__card": "de-emergency-emergency-details__card-nA5Hwa",
|
|
1611
|
+
"emergency-details__card-body": "de-emergency-emergency-details__card-body-nA5Hwa",
|
|
1612
|
+
"emergency-details__card-header": "de-emergency-emergency-details__card-header-nA5Hwa",
|
|
1613
|
+
"emergency-details__card-title": "de-emergency-emergency-details__card-title-nA5Hwa",
|
|
1614
|
+
"emergency-details__empty": "de-emergency-emergency-details__empty-nA5Hwa",
|
|
1615
|
+
"emergency-details__heading": "de-emergency-emergency-details__heading-nA5Hwa",
|
|
1616
|
+
"emergency-details__history-button": "de-emergency-emergency-details__history-button-nA5Hwa",
|
|
1617
|
+
"emergency-details__list": "de-emergency-emergency-details__list-nA5Hwa",
|
|
1618
|
+
"emergency-details__section": "de-emergency-emergency-details__section-nA5Hwa",
|
|
1619
|
+
"emergency-details__subtitle": "de-emergency-emergency-details__subtitle-nA5Hwa",
|
|
1620
|
+
"emergency-details__title": "de-emergency-emergency-details__title-nA5Hwa",
|
|
1593
1621
|
"emergency-error-message": "de-emergency-emergency-error-message-nA5Hwa",
|
|
1594
1622
|
"emergency-error-message__container": "de-emergency-emergency-error-message__container-nA5Hwa",
|
|
1595
1623
|
"emergency-error-message__content": "de-emergency-emergency-error-message__content-nA5Hwa",
|
|
@@ -1600,10 +1628,33 @@ var panel_module_default = {
|
|
|
1600
1628
|
"emergency-error-message__title": "de-emergency-emergency-error-message__title-nA5Hwa",
|
|
1601
1629
|
"emergency-feedback": "de-emergency-emergency-feedback-nA5Hwa",
|
|
1602
1630
|
"emergency-feedback__dismiss": "de-emergency-emergency-feedback__dismiss-nA5Hwa",
|
|
1631
|
+
"emergency-history": "de-emergency-emergency-history-nA5Hwa",
|
|
1632
|
+
"emergency-history__empty": "de-emergency-emergency-history__empty-nA5Hwa",
|
|
1633
|
+
"emergency-history__heading": "de-emergency-emergency-history__heading-nA5Hwa",
|
|
1634
|
+
"emergency-history__list": "de-emergency-emergency-history__list-nA5Hwa",
|
|
1635
|
+
"emergency-history__more": "de-emergency-emergency-history__more-nA5Hwa",
|
|
1636
|
+
"emergency-history__subtitle": "de-emergency-emergency-history__subtitle-nA5Hwa",
|
|
1637
|
+
"emergency-history__title": "de-emergency-emergency-history__title-nA5Hwa",
|
|
1638
|
+
"emergency-message-detail": "de-emergency-emergency-message-detail-nA5Hwa",
|
|
1639
|
+
"emergency-message-detail__back": "de-emergency-emergency-message-detail__back-nA5Hwa",
|
|
1640
|
+
"emergency-message-detail__card": "de-emergency-emergency-message-detail__card-nA5Hwa",
|
|
1641
|
+
"emergency-message-detail__card-header": "de-emergency-emergency-message-detail__card-header-nA5Hwa",
|
|
1642
|
+
"emergency-message-detail__card-title": "de-emergency-emergency-message-detail__card-title-nA5Hwa",
|
|
1643
|
+
"emergency-message-detail__empty": "de-emergency-emergency-message-detail__empty-nA5Hwa",
|
|
1644
|
+
"emergency-message-detail__empty-text": "de-emergency-emergency-message-detail__empty-text-nA5Hwa",
|
|
1645
|
+
"emergency-message-detail__label": "de-emergency-emergency-message-detail__label-nA5Hwa",
|
|
1646
|
+
"emergency-message-detail__list": "de-emergency-emergency-message-detail__list-nA5Hwa",
|
|
1647
|
+
"emergency-message-detail__row": "de-emergency-emergency-message-detail__row-nA5Hwa",
|
|
1648
|
+
"emergency-message-detail__value": "de-emergency-emergency-message-detail__value-nA5Hwa",
|
|
1603
1649
|
"emergency-panel": "de-emergency-emergency-panel-nA5Hwa",
|
|
1604
1650
|
"emergency-panel__availability-region": "de-emergency-emergency-panel__availability-region-nA5Hwa",
|
|
1605
1651
|
"emergency-panel__body": "de-emergency-emergency-panel__body-nA5Hwa",
|
|
1652
|
+
"emergency-panel__body--details": "de-emergency-emergency-panel__body--details-nA5Hwa",
|
|
1653
|
+
"emergency-panel__body--history": "de-emergency-emergency-panel__body--history-nA5Hwa",
|
|
1654
|
+
"emergency-panel__body--message-detail": "de-emergency-emergency-panel__body--message-detail-nA5Hwa",
|
|
1655
|
+
"emergency-panel__body--pre-emergency": "de-emergency-emergency-panel__body--pre-emergency-nA5Hwa",
|
|
1606
1656
|
"emergency-panel__body--pricing": "de-emergency-emergency-panel__body--pricing-nA5Hwa",
|
|
1657
|
+
"emergency-panel__body--primary": "de-emergency-emergency-panel__body--primary-nA5Hwa",
|
|
1607
1658
|
"emergency-panel__body-split": "de-emergency-emergency-panel__body-split-nA5Hwa",
|
|
1608
1659
|
"emergency-panel__body-split--with-availability": "de-emergency-emergency-panel__body-split--with-availability-nA5Hwa",
|
|
1609
1660
|
"emergency-panel__body-title": "de-emergency-emergency-panel__body-title-nA5Hwa",
|
|
@@ -1612,19 +1663,37 @@ var panel_module_default = {
|
|
|
1612
1663
|
"emergency-panel__button--emergency": "de-emergency-emergency-panel__button--emergency-nA5Hwa",
|
|
1613
1664
|
"emergency-panel__button--simulation": "de-emergency-emergency-panel__button--simulation-nA5Hwa",
|
|
1614
1665
|
"emergency-panel__close-button": "de-emergency-emergency-panel__close-button-nA5Hwa",
|
|
1666
|
+
"emergency-panel__close-button--prompt": "de-emergency-emergency-panel__close-button--prompt-nA5Hwa",
|
|
1615
1667
|
"emergency-panel__close-icon": "de-emergency-emergency-panel__close-icon-nA5Hwa",
|
|
1616
1668
|
"emergency-panel__footer": "de-emergency-emergency-panel__footer-nA5Hwa",
|
|
1669
|
+
"emergency-panel__footer--single": "de-emergency-emergency-panel__footer--single-nA5Hwa",
|
|
1670
|
+
"emergency-panel__footer--square": "de-emergency-emergency-panel__footer--square-nA5Hwa",
|
|
1617
1671
|
"emergency-panel__header": "de-emergency-emergency-panel__header-nA5Hwa",
|
|
1618
1672
|
"emergency-panel__prompt-region": "de-emergency-emergency-panel__prompt-region-nA5Hwa",
|
|
1673
|
+
"emergency-panel__prompt-region--idle": "de-emergency-emergency-panel__prompt-region--idle-nA5Hwa",
|
|
1674
|
+
"emergency-panel__sr-only": "de-emergency-emergency-panel__sr-only-nA5Hwa",
|
|
1619
1675
|
"emergency-panel__title": "de-emergency-emergency-panel__title-nA5Hwa",
|
|
1620
1676
|
"emergency-panel--hidden": "de-emergency-emergency-panel--hidden-nA5Hwa",
|
|
1677
|
+
"emergency-pre-emergency": "de-emergency-emergency-pre-emergency-nA5Hwa",
|
|
1678
|
+
"emergency-pre-emergency__error": "de-emergency-emergency-pre-emergency__error-nA5Hwa",
|
|
1679
|
+
"emergency-pre-emergency__form": "de-emergency-emergency-pre-emergency__form-nA5Hwa",
|
|
1680
|
+
"emergency-pre-emergency__input": "de-emergency-emergency-pre-emergency__input-nA5Hwa",
|
|
1681
|
+
"emergency-pre-emergency__input--area-code": "de-emergency-emergency-pre-emergency__input--area-code-nA5Hwa",
|
|
1682
|
+
"emergency-pre-emergency__input--invalid": "de-emergency-emergency-pre-emergency__input--invalid-nA5Hwa",
|
|
1683
|
+
"emergency-pre-emergency__input--number": "de-emergency-emergency-pre-emergency__input--number-nA5Hwa",
|
|
1684
|
+
"emergency-pre-emergency__input--valid": "de-emergency-emergency-pre-emergency__input--valid-nA5Hwa",
|
|
1685
|
+
"emergency-pre-emergency__label": "de-emergency-emergency-pre-emergency__label-nA5Hwa",
|
|
1686
|
+
"emergency-pre-emergency__notice": "de-emergency-emergency-pre-emergency__notice-nA5Hwa",
|
|
1687
|
+
"emergency-pre-emergency__notice-icon": "de-emergency-emergency-pre-emergency__notice-icon-nA5Hwa",
|
|
1688
|
+
"emergency-pre-emergency__notice-text": "de-emergency-emergency-pre-emergency__notice-text-nA5Hwa",
|
|
1689
|
+
"emergency-pre-emergency__phone-row": "de-emergency-emergency-pre-emergency__phone-row-nA5Hwa",
|
|
1621
1690
|
"emergency-pricing": "de-emergency-emergency-pricing-nA5Hwa",
|
|
1622
1691
|
"emergency-pricing__badge": "de-emergency-emergency-pricing__badge-nA5Hwa",
|
|
1623
|
-
"emergency-pricing__footnote": "de-emergency-emergency-pricing__footnote-nA5Hwa",
|
|
1624
1692
|
"emergency-pricing__heading": "de-emergency-emergency-pricing__heading-nA5Hwa",
|
|
1625
1693
|
"emergency-pricing__package": "de-emergency-emergency-pricing__package-nA5Hwa",
|
|
1626
1694
|
"emergency-pricing__package--highlighted": "de-emergency-emergency-pricing__package--highlighted-nA5Hwa",
|
|
1627
1695
|
"emergency-pricing__package-caption": "de-emergency-emergency-pricing__package-caption-nA5Hwa",
|
|
1696
|
+
"emergency-pricing__package-footnote": "de-emergency-emergency-pricing__package-footnote-nA5Hwa",
|
|
1628
1697
|
"emergency-pricing__package-header": "de-emergency-emergency-pricing__package-header-nA5Hwa",
|
|
1629
1698
|
"emergency-pricing__package-label": "de-emergency-emergency-pricing__package-label-nA5Hwa",
|
|
1630
1699
|
"emergency-pricing__package-name": "de-emergency-emergency-pricing__package-name-nA5Hwa",
|
|
@@ -1820,18 +1889,27 @@ function useFocusTrap(ref, active) {
|
|
|
1820
1889
|
}
|
|
1821
1890
|
|
|
1822
1891
|
// src/react/screens/context.tsx
|
|
1892
|
+
const EMPTY_PARAMS = {};
|
|
1823
1893
|
const EmergencyScreenContext = (0, react.createContext)(null);
|
|
1894
|
+
function paramsEqual(first, second) {
|
|
1895
|
+
return first.messageId === second.messageId;
|
|
1896
|
+
}
|
|
1824
1897
|
function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
1825
1898
|
const [state, setState] = (0, react.useState)({
|
|
1826
1899
|
screen: initialScreen,
|
|
1900
|
+
params: EMPTY_PARAMS,
|
|
1827
1901
|
history: []
|
|
1828
1902
|
});
|
|
1829
|
-
const navigate = (0, react.useCallback)((next) => {
|
|
1903
|
+
const navigate = (0, react.useCallback)((next, params = EMPTY_PARAMS) => {
|
|
1830
1904
|
setState((current) => {
|
|
1831
|
-
if (current.screen === next) return current;
|
|
1905
|
+
if (current.screen === next && paramsEqual(current.params, params)) return current;
|
|
1832
1906
|
return {
|
|
1833
1907
|
screen: next,
|
|
1834
|
-
|
|
1908
|
+
params,
|
|
1909
|
+
history: [...current.history, {
|
|
1910
|
+
screen: current.screen,
|
|
1911
|
+
params: current.params
|
|
1912
|
+
}]
|
|
1835
1913
|
};
|
|
1836
1914
|
});
|
|
1837
1915
|
}, []);
|
|
@@ -1840,31 +1918,47 @@ function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
|
1840
1918
|
const previous = current.history.at(-1);
|
|
1841
1919
|
if (!previous) return current;
|
|
1842
1920
|
return {
|
|
1843
|
-
screen: previous,
|
|
1921
|
+
screen: previous.screen,
|
|
1922
|
+
params: previous.params,
|
|
1844
1923
|
history: current.history.slice(0, -1)
|
|
1845
1924
|
};
|
|
1846
1925
|
});
|
|
1847
1926
|
}, []);
|
|
1927
|
+
const replace = (0, react.useCallback)((next, params = EMPTY_PARAMS) => {
|
|
1928
|
+
setState((current) => {
|
|
1929
|
+
if (current.screen === next && paramsEqual(current.params, params)) return current;
|
|
1930
|
+
return {
|
|
1931
|
+
screen: next,
|
|
1932
|
+
params,
|
|
1933
|
+
history: current.history.at(-1)?.screen === next ? current.history.slice(0, -1) : current.history
|
|
1934
|
+
};
|
|
1935
|
+
});
|
|
1936
|
+
}, []);
|
|
1848
1937
|
const reset = (0, react.useCallback)(() => {
|
|
1849
1938
|
setState((current) => {
|
|
1850
|
-
if (current.screen === "primary" && current.history.length === 0) return current;
|
|
1939
|
+
if (current.screen === "primary" && current.params.messageId === void 0 && current.history.length === 0) return current;
|
|
1851
1940
|
return {
|
|
1852
1941
|
screen: "primary",
|
|
1942
|
+
params: EMPTY_PARAMS,
|
|
1853
1943
|
history: []
|
|
1854
1944
|
};
|
|
1855
1945
|
});
|
|
1856
1946
|
}, []);
|
|
1857
1947
|
const value = (0, react.useMemo)(() => ({
|
|
1858
1948
|
screen: state.screen,
|
|
1949
|
+
params: state.params,
|
|
1859
1950
|
history: state.history,
|
|
1860
1951
|
navigate,
|
|
1952
|
+
replace,
|
|
1861
1953
|
back,
|
|
1862
1954
|
reset
|
|
1863
1955
|
}), [
|
|
1864
1956
|
back,
|
|
1865
1957
|
navigate,
|
|
1958
|
+
replace,
|
|
1866
1959
|
reset,
|
|
1867
1960
|
state.history,
|
|
1961
|
+
state.params,
|
|
1868
1962
|
state.screen
|
|
1869
1963
|
]);
|
|
1870
1964
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenContext.Provider, {
|
|
@@ -1913,6 +2007,245 @@ function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack })
|
|
|
1913
2007
|
});
|
|
1914
2008
|
}
|
|
1915
2009
|
|
|
2010
|
+
// src/react/screens/primary/data.ts
|
|
2011
|
+
/**
|
|
2012
|
+
* Derives the remaining alert rounds from available messages and designated
|
|
2013
|
+
* responders. Invalid or non-positive input collapses to the minimum of one
|
|
2014
|
+
* round instead of producing `Infinity`, `NaN` or a negative value.
|
|
2015
|
+
*/
|
|
2016
|
+
function calculateRounds(messages, responders) {
|
|
2017
|
+
if (!Number.isFinite(messages) || !Number.isFinite(responders)) return 1;
|
|
2018
|
+
const availableMessages = Math.floor(messages);
|
|
2019
|
+
const designatedResponders = Math.floor(responders);
|
|
2020
|
+
if (availableMessages <= 0 || designatedResponders <= 0) return 1;
|
|
2021
|
+
return Math.max(1, Math.floor(availableMessages / designatedResponders));
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// src/react/screens/details/copy.ts
|
|
2025
|
+
const detailsCopy = {
|
|
2026
|
+
title: "Detalles",
|
|
2027
|
+
summaryTitle: "Alertas Disponibles",
|
|
2028
|
+
historyTitle: "Historial de alertas",
|
|
2029
|
+
historySubtitle: "Tus últimas alertas y recargas.",
|
|
2030
|
+
historyEmpty: "Todavía no tienes alertas en tu historial.",
|
|
2031
|
+
summaryEmpty: "Todavía no tienes un saldo de alertas disponible.",
|
|
2032
|
+
viewHistory: "Ver historial",
|
|
2033
|
+
messagesSingular: "mensaje de alerta",
|
|
2034
|
+
messagesPlural: "mensajes de alerta",
|
|
2035
|
+
contactsSingular: "contacto de emergencia",
|
|
2036
|
+
contactsPlural: "contactos de emergencia",
|
|
2037
|
+
roundsSingular: "ronda de avisos",
|
|
2038
|
+
roundsPlural: "rondas de avisos"
|
|
2039
|
+
};
|
|
2040
|
+
/** Returns the balance sentence split into text and emphasized number parts. */
|
|
2041
|
+
function alertBalanceParts(messages, responders) {
|
|
2042
|
+
if (!Number.isFinite(messages) || !Number.isFinite(responders) || messages < 0 || responders < 0) return [];
|
|
2043
|
+
const rounds = calculateRounds(messages, responders);
|
|
2044
|
+
const messagesUnit = messages === 1 ? detailsCopy.messagesSingular : detailsCopy.messagesPlural;
|
|
2045
|
+
const contactsUnit = responders === 1 ? detailsCopy.contactsSingular : detailsCopy.contactsPlural;
|
|
2046
|
+
const roundsUnit = rounds === 1 ? detailsCopy.roundsSingular : detailsCopy.roundsPlural;
|
|
2047
|
+
return [
|
|
2048
|
+
messages === 1 ? "Te queda " : "Te quedan ",
|
|
2049
|
+
messages,
|
|
2050
|
+
` ${messagesUnit}, dado que tienes `,
|
|
2051
|
+
responders,
|
|
2052
|
+
` ${contactsUnit}, `,
|
|
2053
|
+
rounds === 1 ? "te queda " : "te quedan ",
|
|
2054
|
+
rounds,
|
|
2055
|
+
` ${roundsUnit}.`
|
|
2056
|
+
];
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
// src/react/screens/details/AlertBalanceSentence.tsx
|
|
2060
|
+
/** Renders the balance sentence with its numbers visually emphasized. */
|
|
2061
|
+
function AlertBalanceSentence({ messages, responders }) {
|
|
2062
|
+
const parts = alertBalanceParts(messages, responders);
|
|
2063
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: parts.map((part, index) => typeof part === "number" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: part }, index) : part) });
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
// src/core/date.ts
|
|
2067
|
+
/**
|
|
2068
|
+
* Formats a timestamp as `h:mmam` or `h:mmpm` in local time, without a space
|
|
2069
|
+
* or leading zero.
|
|
2070
|
+
*/
|
|
2071
|
+
function formatShortTime(value, locale = "es") {
|
|
2072
|
+
if (value === null) return "";
|
|
2073
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2074
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
2075
|
+
const parts = new Intl.DateTimeFormat(locale, {
|
|
2076
|
+
hour: "numeric",
|
|
2077
|
+
minute: "2-digit",
|
|
2078
|
+
hour12: true
|
|
2079
|
+
}).formatToParts(date);
|
|
2080
|
+
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2081
|
+
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2082
|
+
const dayPeriod = parts.find((part) => part.type === "dayPeriod")?.value;
|
|
2083
|
+
if (!hour || !minute) return "";
|
|
2084
|
+
return `${hour}:${minute}${normalizePeriod(dayPeriod)}`;
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Formats a timestamp as `dd/mm/yyyy hh:mm` in local time, using 24-hour time
|
|
2088
|
+
* and zero-padded parts. Missing or invalid input collapses to an empty string.
|
|
2089
|
+
*/
|
|
2090
|
+
function formatDateTime(value, locale = "es") {
|
|
2091
|
+
if (value === null) return "";
|
|
2092
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2093
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
2094
|
+
const parts = new Intl.DateTimeFormat(locale, {
|
|
2095
|
+
day: "2-digit",
|
|
2096
|
+
month: "2-digit",
|
|
2097
|
+
year: "numeric",
|
|
2098
|
+
hour: "2-digit",
|
|
2099
|
+
minute: "2-digit",
|
|
2100
|
+
hourCycle: "h23"
|
|
2101
|
+
}).formatToParts(date);
|
|
2102
|
+
const day = parts.find((part) => part.type === "day")?.value;
|
|
2103
|
+
const month = parts.find((part) => part.type === "month")?.value;
|
|
2104
|
+
const year = parts.find((part) => part.type === "year")?.value;
|
|
2105
|
+
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2106
|
+
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2107
|
+
if (!day || !month || !year || !hour || !minute) return "";
|
|
2108
|
+
return `${day}/${month}/${year} ${hour}:${minute}`;
|
|
2109
|
+
}
|
|
2110
|
+
function normalizePeriod(dayPeriod) {
|
|
2111
|
+
if (!dayPeriod) return "";
|
|
2112
|
+
if (/^p/i.test(dayPeriod)) return "pm";
|
|
2113
|
+
return /^a/i.test(dayPeriod) ? "am" : "";
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// src/react/screens/history/copy.ts
|
|
2117
|
+
const historyCopy = {
|
|
2118
|
+
title: "Historial de alertas",
|
|
2119
|
+
subtitle: "Consulta tus alertas, pruebas y recargas.",
|
|
2120
|
+
empty: "Todavía no tienes alertas en tu historial.",
|
|
2121
|
+
showMore: "Ver más",
|
|
2122
|
+
kindLabels: {
|
|
2123
|
+
emergency: "Emergencia",
|
|
2124
|
+
test: "Prueba",
|
|
2125
|
+
recharge: "Recarga"
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
function formatContacts(count) {
|
|
2129
|
+
return count === 1 ? "1 contacto" : `${count} contactos`;
|
|
2130
|
+
}
|
|
2131
|
+
function formatMessages(count) {
|
|
2132
|
+
return count === 1 ? "1 mensaje" : `${count} mensajes`;
|
|
2133
|
+
}
|
|
2134
|
+
function formatHistoryRounds(count) {
|
|
2135
|
+
return count === 1 ? "1 ronda" : `${count} rondas`;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/react/screens/history/data.ts
|
|
2139
|
+
/** Local demo alert history, newest first. */
|
|
2140
|
+
const DEMO_ALERT_HISTORY = [
|
|
2141
|
+
{
|
|
2142
|
+
id: "alert-2026-10-31",
|
|
2143
|
+
kind: "emergency",
|
|
2144
|
+
occurredAt: "2026-10-31T13:50:00",
|
|
2145
|
+
responders: 3,
|
|
2146
|
+
remainingMessages: 27
|
|
2147
|
+
},
|
|
2148
|
+
{
|
|
2149
|
+
id: "alert-2026-10-20",
|
|
2150
|
+
kind: "recharge",
|
|
2151
|
+
occurredAt: "2026-10-20T09:15:00",
|
|
2152
|
+
responders: 3,
|
|
2153
|
+
remainingMessages: 100
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
id: "alert-2026-10-12",
|
|
2157
|
+
kind: "test",
|
|
2158
|
+
occurredAt: "2026-10-12T18:40:00",
|
|
2159
|
+
responders: 3,
|
|
2160
|
+
remainingMessages: 73
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
id: "alert-2026-10-01",
|
|
2164
|
+
kind: "recharge",
|
|
2165
|
+
occurredAt: "2026-10-01T11:05:00",
|
|
2166
|
+
responders: 3,
|
|
2167
|
+
remainingMessages: 76
|
|
2168
|
+
}
|
|
2169
|
+
];
|
|
2170
|
+
/** Returns a newest-first copy of the history. */
|
|
2171
|
+
function sortedHistoryEntries(entries = DEMO_ALERT_HISTORY) {
|
|
2172
|
+
return [...entries].sort((first, second) => new Date(second.occurredAt).getTime() - new Date(first.occurredAt).getTime());
|
|
2173
|
+
}
|
|
2174
|
+
/** Newest entry, used as the source of the current balance. */
|
|
2175
|
+
function latestHistoryEntry(entries = DEMO_ALERT_HISTORY) {
|
|
2176
|
+
return sortedHistoryEntries(entries)[0] ?? null;
|
|
2177
|
+
}
|
|
2178
|
+
/** Resolves a single history entry by id. */
|
|
2179
|
+
function historyEntryById(id, entries = DEMO_ALERT_HISTORY) {
|
|
2180
|
+
if (!id) return null;
|
|
2181
|
+
return entries.find((entry) => entry.id === id) ?? null;
|
|
2182
|
+
}
|
|
2183
|
+
/** Rounds represented by the entry's remaining messages and responders. */
|
|
2184
|
+
function historyEntryRounds(entry) {
|
|
2185
|
+
return calculateRounds(entry.remainingMessages, entry.responders);
|
|
2186
|
+
}
|
|
2187
|
+
/** Current balance derived from the newest history entry. */
|
|
2188
|
+
function currentAlertBalance(entries = DEMO_ALERT_HISTORY) {
|
|
2189
|
+
const latest = latestHistoryEntry(entries);
|
|
2190
|
+
if (!latest) return null;
|
|
2191
|
+
return {
|
|
2192
|
+
messages: latest.remainingMessages,
|
|
2193
|
+
responders: latest.responders,
|
|
2194
|
+
rounds: historyEntryRounds(latest)
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// src/react/screens/history/AlertHistoryRow.tsx
|
|
2199
|
+
/** Reusable history row. The whole row is a button with a full text label. */
|
|
2200
|
+
function AlertHistoryRow({ entry, locale, onOpen }) {
|
|
2201
|
+
const occurredAt = formatDateTime(entry.occurredAt, locale);
|
|
2202
|
+
const rounds = historyEntryRounds(entry);
|
|
2203
|
+
const label = [
|
|
2204
|
+
historyCopy.kindLabels[entry.kind],
|
|
2205
|
+
occurredAt,
|
|
2206
|
+
formatContacts(entry.responders),
|
|
2207
|
+
`${formatMessages(entry.remainingMessages)} restantes`,
|
|
2208
|
+
`${formatHistoryRounds(rounds)} de avisos`
|
|
2209
|
+
].join(", ");
|
|
2210
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
2211
|
+
className: cls("emergency-alert-row"),
|
|
2212
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2213
|
+
type: "button",
|
|
2214
|
+
className: cls("emergency-alert-row__button"),
|
|
2215
|
+
"aria-label": label,
|
|
2216
|
+
onClick: () => onOpen(entry.id),
|
|
2217
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2218
|
+
className: cls("emergency-alert-row__top"),
|
|
2219
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
2220
|
+
className: cls("emergency-alert-row__time"),
|
|
2221
|
+
dateTime: entry.occurredAt,
|
|
2222
|
+
children: occurredAt
|
|
2223
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2224
|
+
className: cls("emergency-alert-row__badge"),
|
|
2225
|
+
"data-kind": entry.kind,
|
|
2226
|
+
children: historyCopy.kindLabels[entry.kind]
|
|
2227
|
+
})]
|
|
2228
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2229
|
+
className: cls("emergency-alert-row__stats"),
|
|
2230
|
+
children: [
|
|
2231
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2232
|
+
className: cls("emergency-alert-row__stat"),
|
|
2233
|
+
children: formatContacts(entry.responders)
|
|
2234
|
+
}),
|
|
2235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2236
|
+
className: cls("emergency-alert-row__stat"),
|
|
2237
|
+
children: formatMessages(entry.remainingMessages)
|
|
2238
|
+
}),
|
|
2239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2240
|
+
className: cls("emergency-alert-row__stat"),
|
|
2241
|
+
children: formatHistoryRounds(rounds)
|
|
2242
|
+
})
|
|
2243
|
+
]
|
|
2244
|
+
})]
|
|
2245
|
+
})
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
|
|
1916
2249
|
// src/react/screens/layout.tsx
|
|
1917
2250
|
/**
|
|
1918
2251
|
* Shared shell for every screen. The wrapper is `display: contents` so the
|
|
@@ -1934,45 +2267,429 @@ function EmergencyScreenLayout({ screen, titleId, header, footer, children, body
|
|
|
1934
2267
|
});
|
|
1935
2268
|
}
|
|
1936
2269
|
|
|
1937
|
-
// src/react/screens/
|
|
1938
|
-
|
|
1939
|
-
|
|
2270
|
+
// src/react/screens/details/index.tsx
|
|
2271
|
+
function DetailsScreen({ titleId, locale, onClose }) {
|
|
2272
|
+
const { back, navigate } = useScreenNavigation();
|
|
2273
|
+
const balance = currentAlertBalance();
|
|
2274
|
+
const preview = sortedHistoryEntries().slice(0, 2);
|
|
1940
2275
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
1941
|
-
screen,
|
|
2276
|
+
screen: "details",
|
|
1942
2277
|
titleId,
|
|
2278
|
+
bodyClassName: "emergency-panel__body--details",
|
|
1943
2279
|
header: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
1944
2280
|
titleId,
|
|
1945
|
-
title,
|
|
1946
|
-
onClose,
|
|
1947
|
-
onBack
|
|
2281
|
+
title: detailsCopy.title,
|
|
2282
|
+
onClose: () => onClose("close"),
|
|
2283
|
+
onBack: back
|
|
1948
2284
|
}),
|
|
1949
2285
|
footer: null,
|
|
2286
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2287
|
+
className: cls("emergency-details"),
|
|
2288
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
2289
|
+
className: cls("emergency-details__section"),
|
|
2290
|
+
"aria-label": detailsCopy.summaryTitle,
|
|
2291
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2292
|
+
className: cls("emergency-details__card"),
|
|
2293
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2294
|
+
className: cls("emergency-details__card-header"),
|
|
2295
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2296
|
+
className: cls("emergency-details__card-title"),
|
|
2297
|
+
children: detailsCopy.summaryTitle
|
|
2298
|
+
})
|
|
2299
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2300
|
+
className: cls("emergency-details__card-body"),
|
|
2301
|
+
children: balance ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2302
|
+
className: cls("emergency-details__balance"),
|
|
2303
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertBalanceSentence, {
|
|
2304
|
+
messages: balance.messages,
|
|
2305
|
+
responders: balance.responders
|
|
2306
|
+
})
|
|
2307
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2308
|
+
className: cls("emergency-details__empty"),
|
|
2309
|
+
children: detailsCopy.summaryEmpty
|
|
2310
|
+
})
|
|
2311
|
+
})]
|
|
2312
|
+
})
|
|
2313
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2314
|
+
className: cls("emergency-details__section"),
|
|
2315
|
+
"aria-label": detailsCopy.historyTitle,
|
|
2316
|
+
children: [
|
|
2317
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2318
|
+
className: cls("emergency-details__heading"),
|
|
2319
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2320
|
+
className: cls("emergency-details__title"),
|
|
2321
|
+
children: detailsCopy.historyTitle
|
|
2322
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2323
|
+
className: cls("emergency-details__subtitle"),
|
|
2324
|
+
children: detailsCopy.historySubtitle
|
|
2325
|
+
})]
|
|
2326
|
+
}),
|
|
2327
|
+
preview.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2328
|
+
className: cls("emergency-details__list"),
|
|
2329
|
+
children: preview.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertHistoryRow, {
|
|
2330
|
+
entry,
|
|
2331
|
+
locale,
|
|
2332
|
+
onOpen: (messageId) => navigate("message-detail", { messageId })
|
|
2333
|
+
}, entry.id))
|
|
2334
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2335
|
+
className: cls("emergency-details__empty"),
|
|
2336
|
+
children: detailsCopy.historyEmpty
|
|
2337
|
+
}),
|
|
2338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2339
|
+
type: "button",
|
|
2340
|
+
className: cls("emergency-details__history-button"),
|
|
2341
|
+
onClick: () => navigate("history"),
|
|
2342
|
+
children: detailsCopy.viewHistory
|
|
2343
|
+
})
|
|
2344
|
+
]
|
|
2345
|
+
})]
|
|
2346
|
+
})
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
// src/react/screens/history/index.tsx
|
|
2351
|
+
function HistoryScreen({ titleId, locale, onClose }) {
|
|
2352
|
+
const { back, navigate } = useScreenNavigation();
|
|
2353
|
+
const listId = (0, react.useId)();
|
|
2354
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
2355
|
+
const entries = sortedHistoryEntries();
|
|
2356
|
+
const hiddenCount = Math.max(0, entries.length - 2);
|
|
2357
|
+
const visibleEntries = expanded ? entries : entries.slice(0, 2);
|
|
2358
|
+
const showMore = hiddenCount > 0 && !expanded;
|
|
2359
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
2360
|
+
screen: "history",
|
|
2361
|
+
titleId,
|
|
2362
|
+
bodyClassName: "emergency-panel__body--history",
|
|
2363
|
+
header: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
2364
|
+
titleId,
|
|
2365
|
+
title: historyCopy.title,
|
|
2366
|
+
onClose: () => onClose("close"),
|
|
2367
|
+
onBack: back
|
|
2368
|
+
}),
|
|
2369
|
+
footer: null,
|
|
2370
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2371
|
+
className: cls("emergency-history"),
|
|
2372
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2373
|
+
className: cls("emergency-history__heading"),
|
|
2374
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2375
|
+
className: cls("emergency-history__title"),
|
|
2376
|
+
children: historyCopy.title
|
|
2377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2378
|
+
className: cls("emergency-history__subtitle"),
|
|
2379
|
+
children: historyCopy.subtitle
|
|
2380
|
+
})]
|
|
2381
|
+
}), entries.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2382
|
+
className: cls("emergency-history__list"),
|
|
2383
|
+
id: listId,
|
|
2384
|
+
children: visibleEntries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertHistoryRow, {
|
|
2385
|
+
entry,
|
|
2386
|
+
locale,
|
|
2387
|
+
onOpen: (messageId) => navigate("message-detail", { messageId })
|
|
2388
|
+
}, entry.id))
|
|
2389
|
+
}), showMore ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2390
|
+
type: "button",
|
|
2391
|
+
className: cls("emergency-history__more"),
|
|
2392
|
+
"aria-expanded": expanded,
|
|
2393
|
+
"aria-controls": listId,
|
|
2394
|
+
onClick: () => setExpanded(true),
|
|
2395
|
+
children: historyCopy.showMore
|
|
2396
|
+
}) : null] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2397
|
+
className: cls("emergency-history__empty"),
|
|
2398
|
+
children: historyCopy.empty
|
|
2399
|
+
})]
|
|
2400
|
+
})
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
// src/react/screens/message-detail/index.tsx
|
|
2405
|
+
const messageDetailCopy = {
|
|
2406
|
+
title: "Detalle del mensaje",
|
|
2407
|
+
dateTime: "Fecha y hora",
|
|
2408
|
+
kind: "Tipo",
|
|
2409
|
+
responders: "Contactos de emergencia",
|
|
2410
|
+
messages: "Mensajes restantes",
|
|
2411
|
+
rounds: "Rondas de avisos",
|
|
2412
|
+
notFound: "No encontramos este mensaje. Puede que ya no esté disponible en tu historial.",
|
|
2413
|
+
backToHistory: "Volver al historial"
|
|
2414
|
+
};
|
|
2415
|
+
function MessageDetailScreen({ titleId, locale, onClose }) {
|
|
2416
|
+
const { back, params, replace } = useScreenNavigation();
|
|
2417
|
+
const entry = historyEntryById(params.messageId);
|
|
2418
|
+
const header = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
2419
|
+
titleId,
|
|
2420
|
+
title: messageDetailCopy.title,
|
|
2421
|
+
onClose: () => onClose("close"),
|
|
2422
|
+
onBack: back
|
|
2423
|
+
});
|
|
2424
|
+
if (!entry) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
2425
|
+
screen: "message-detail",
|
|
2426
|
+
titleId,
|
|
2427
|
+
bodyClassName: "emergency-panel__body--message-detail",
|
|
2428
|
+
header,
|
|
2429
|
+
footer: null,
|
|
2430
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2431
|
+
className: cls("emergency-message-detail__empty"),
|
|
2432
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2433
|
+
className: cls("emergency-message-detail__empty-text"),
|
|
2434
|
+
children: messageDetailCopy.notFound
|
|
2435
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2436
|
+
type: "button",
|
|
2437
|
+
className: cls("emergency-message-detail__back"),
|
|
2438
|
+
onClick: () => replace("history"),
|
|
2439
|
+
children: messageDetailCopy.backToHistory
|
|
2440
|
+
})]
|
|
2441
|
+
})
|
|
2442
|
+
});
|
|
2443
|
+
const rounds = historyEntryRounds(entry);
|
|
2444
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
2445
|
+
screen: "message-detail",
|
|
2446
|
+
titleId,
|
|
2447
|
+
bodyClassName: "emergency-panel__body--message-detail",
|
|
2448
|
+
header,
|
|
2449
|
+
footer: null,
|
|
1950
2450
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1951
|
-
className: cls("emergency-
|
|
1952
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1953
|
-
className: cls("emergency-
|
|
1954
|
-
children:
|
|
2451
|
+
className: cls("emergency-message-detail"),
|
|
2452
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2453
|
+
className: cls("emergency-message-detail__card"),
|
|
2454
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2455
|
+
className: cls("emergency-message-detail__card-header"),
|
|
2456
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2457
|
+
className: cls("emergency-message-detail__card-title"),
|
|
2458
|
+
children: messageDetailCopy.title
|
|
2459
|
+
})
|
|
2460
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
2461
|
+
className: cls("emergency-message-detail__list"),
|
|
2462
|
+
children: [
|
|
2463
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2464
|
+
className: cls("emergency-message-detail__row"),
|
|
2465
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
|
|
2466
|
+
className: cls("emergency-message-detail__label"),
|
|
2467
|
+
children: messageDetailCopy.dateTime
|
|
2468
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
2469
|
+
className: cls("emergency-message-detail__value"),
|
|
2470
|
+
children: formatDateTime(entry.occurredAt, locale)
|
|
2471
|
+
})]
|
|
2472
|
+
}),
|
|
2473
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2474
|
+
className: cls("emergency-message-detail__row"),
|
|
2475
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
|
|
2476
|
+
className: cls("emergency-message-detail__label"),
|
|
2477
|
+
children: messageDetailCopy.kind
|
|
2478
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
2479
|
+
className: cls("emergency-message-detail__value"),
|
|
2480
|
+
children: historyCopy.kindLabels[entry.kind]
|
|
2481
|
+
})]
|
|
2482
|
+
}),
|
|
2483
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2484
|
+
className: cls("emergency-message-detail__row"),
|
|
2485
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
|
|
2486
|
+
className: cls("emergency-message-detail__label"),
|
|
2487
|
+
children: messageDetailCopy.responders
|
|
2488
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
2489
|
+
className: cls("emergency-message-detail__value"),
|
|
2490
|
+
children: formatContacts(entry.responders)
|
|
2491
|
+
})]
|
|
2492
|
+
}),
|
|
2493
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2494
|
+
className: cls("emergency-message-detail__row"),
|
|
2495
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
|
|
2496
|
+
className: cls("emergency-message-detail__label"),
|
|
2497
|
+
children: messageDetailCopy.messages
|
|
2498
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
2499
|
+
className: cls("emergency-message-detail__value"),
|
|
2500
|
+
children: formatMessages(entry.remainingMessages)
|
|
2501
|
+
})]
|
|
2502
|
+
}),
|
|
2503
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2504
|
+
className: cls("emergency-message-detail__row"),
|
|
2505
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
|
|
2506
|
+
className: cls("emergency-message-detail__label"),
|
|
2507
|
+
children: messageDetailCopy.rounds
|
|
2508
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
2509
|
+
className: cls("emergency-message-detail__value"),
|
|
2510
|
+
children: formatHistoryRounds(rounds)
|
|
2511
|
+
})]
|
|
2512
|
+
})
|
|
2513
|
+
]
|
|
2514
|
+
})]
|
|
1955
2515
|
})
|
|
1956
2516
|
})
|
|
1957
2517
|
});
|
|
1958
2518
|
}
|
|
1959
2519
|
|
|
1960
|
-
// src/
|
|
1961
|
-
const
|
|
1962
|
-
|
|
1963
|
-
|
|
2520
|
+
// src/core/phone.ts
|
|
2521
|
+
const VENEZUELA_DIALLING_CODE = "+58";
|
|
2522
|
+
const VENEZUELAN_MOBILE_PATTERN = /^4\d{9}$/;
|
|
2523
|
+
function normalizeVenezuelanPhone(input) {
|
|
2524
|
+
return input.replace(/\D/g, "").replace(/^0+/, "");
|
|
2525
|
+
}
|
|
2526
|
+
function formatVenezuelanPhone(input) {
|
|
2527
|
+
const digits = normalizeVenezuelanPhone(input).slice(0, 10);
|
|
2528
|
+
if (digits.length <= 3) return digits;
|
|
2529
|
+
return `${digits.slice(0, 3)} ${digits.slice(3)}`;
|
|
2530
|
+
}
|
|
2531
|
+
function isValidVenezuelanPhone(input) {
|
|
2532
|
+
return VENEZUELAN_MOBILE_PATTERN.test(normalizeVenezuelanPhone(input));
|
|
2533
|
+
}
|
|
2534
|
+
function toVenezuelanE164(input) {
|
|
2535
|
+
const digits = normalizeVenezuelanPhone(input);
|
|
2536
|
+
if (!VENEZUELAN_MOBILE_PATTERN.test(digits)) return null;
|
|
2537
|
+
return `${"+58"}${digits}`;
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
// src/react/components/EmergencyFooter/index.tsx
|
|
2541
|
+
function EmergencyFooter(props) {
|
|
2542
|
+
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, square = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2543
|
+
if (active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
2544
|
+
className: cls("emergency-panel__footer", square && "emergency-panel__footer--square"),
|
|
2545
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2546
|
+
type: "button",
|
|
2547
|
+
className: cls("emergency-status__abort-btn"),
|
|
2548
|
+
"aria-label": copy.abort,
|
|
2549
|
+
disabled: aborting || feedbackVisible,
|
|
2550
|
+
onClick: onRequestAbort,
|
|
2551
|
+
children: [copy.abort, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "alert-triangle" })]
|
|
2552
|
+
})
|
|
2553
|
+
});
|
|
2554
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
2555
|
+
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single", square && "emergency-panel__footer--square"),
|
|
2556
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2557
|
+
type: "button",
|
|
2558
|
+
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
2559
|
+
"aria-label": copy.emergencyAria,
|
|
2560
|
+
disabled: busy || feedbackVisible || emergencyDisabled,
|
|
2561
|
+
onClick: onActivate,
|
|
2562
|
+
children: [copy.emergency, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "emergency" })]
|
|
2563
|
+
}), showSimulation ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2564
|
+
type: "button",
|
|
2565
|
+
className: cls("emergency-panel__button", "emergency-panel__button--simulation"),
|
|
2566
|
+
"aria-label": copy.testAria,
|
|
2567
|
+
disabled: busy || feedbackVisible,
|
|
2568
|
+
onClick: onRequestSimulation,
|
|
2569
|
+
children: [copy.test, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "list-check" })]
|
|
2570
|
+
}) : null]
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
// src/react/screens/pre-emergency/copy.ts
|
|
2575
|
+
const preEmergencyCopy = {
|
|
2576
|
+
title: "Pre-emergencia",
|
|
2577
|
+
permissionNotice: "Debes autorizar todos los permisos de ubicación",
|
|
2578
|
+
phoneLabel: "Indique su número de teléfono",
|
|
2579
|
+
phonePlaceholder: "Ej. 0412 000 0000",
|
|
2580
|
+
areaCodeLabel: "Código de país",
|
|
2581
|
+
areaCode: "+58",
|
|
2582
|
+
invalidPhone: "Ingresa un número móvil venezolano válido. Ej. 412 1301234"
|
|
1964
2583
|
};
|
|
1965
2584
|
|
|
1966
|
-
// src/react/screens/
|
|
1967
|
-
function
|
|
2585
|
+
// src/react/screens/pre-emergency/index.tsx
|
|
2586
|
+
function PreEmergencyScreen(props) {
|
|
1968
2587
|
const { back } = useScreenNavigation();
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2588
|
+
const phoneId = (0, react.useId)();
|
|
2589
|
+
const errorId = `${phoneId}-error`;
|
|
2590
|
+
const permissionId = `${phoneId}-permission`;
|
|
2591
|
+
const [digits, setDigits] = (0, react.useState)("");
|
|
2592
|
+
const [touched, setTouched] = (0, react.useState)(false);
|
|
2593
|
+
const valid = isValidVenezuelanPhone(digits);
|
|
2594
|
+
const invalid = touched && digits.length > 0 && !valid;
|
|
2595
|
+
const handleChange = (event) => {
|
|
2596
|
+
const next = normalizeVenezuelanPhone(event.target.value).slice(0, 10);
|
|
2597
|
+
setDigits(next);
|
|
2598
|
+
};
|
|
2599
|
+
const handleSubmit = () => {
|
|
2600
|
+
setTouched(true);
|
|
2601
|
+
if (!valid) return;
|
|
2602
|
+
const phone = toVenezuelanE164(digits);
|
|
2603
|
+
if (phone === null) return;
|
|
2604
|
+
props.onSetContactPhone(phone);
|
|
2605
|
+
props.onActivate();
|
|
2606
|
+
};
|
|
2607
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
2608
|
+
screen: "pre-emergency",
|
|
2609
|
+
titleId: props.titleId,
|
|
2610
|
+
bodyClassName: "emergency-panel__body--pre-emergency",
|
|
2611
|
+
header: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
2612
|
+
titleId: props.titleId,
|
|
2613
|
+
title: preEmergencyCopy.title,
|
|
2614
|
+
onClose: () => props.onClose("close"),
|
|
2615
|
+
onBack: back
|
|
2616
|
+
}),
|
|
2617
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyFooter, {
|
|
2618
|
+
active: props.active,
|
|
2619
|
+
busy: props.busy,
|
|
2620
|
+
aborting: props.aborting,
|
|
2621
|
+
feedbackVisible: props.feedbackVisible,
|
|
2622
|
+
emergencyDisabled: !valid,
|
|
2623
|
+
showSimulation: false,
|
|
2624
|
+
onActivate: handleSubmit,
|
|
2625
|
+
onRequestAbort: props.onRequestAbort,
|
|
2626
|
+
onRequestSimulation: props.onRequestSimulation
|
|
2627
|
+
}),
|
|
2628
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2629
|
+
className: cls("emergency-pre-emergency"),
|
|
2630
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2631
|
+
role: "note",
|
|
2632
|
+
"aria-labelledby": permissionId,
|
|
2633
|
+
className: cls("emergency-pre-emergency__notice"),
|
|
2634
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
2635
|
+
name: "map-pin",
|
|
2636
|
+
className: cls("emergency-pre-emergency__notice-icon")
|
|
2637
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2638
|
+
id: permissionId,
|
|
2639
|
+
className: cls("emergency-pre-emergency__notice-text"),
|
|
2640
|
+
children: preEmergencyCopy.permissionNotice
|
|
2641
|
+
})]
|
|
2642
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
2643
|
+
className: cls("emergency-pre-emergency__form"),
|
|
2644
|
+
noValidate: true,
|
|
2645
|
+
onSubmit: (event) => {
|
|
2646
|
+
event.preventDefault();
|
|
2647
|
+
handleSubmit();
|
|
2648
|
+
},
|
|
2649
|
+
onKeyDown: (event) => {
|
|
2650
|
+
if (event.key === "Enter") {
|
|
2651
|
+
event.preventDefault();
|
|
2652
|
+
handleSubmit();
|
|
2653
|
+
}
|
|
2654
|
+
},
|
|
2655
|
+
children: [
|
|
2656
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2657
|
+
htmlFor: phoneId,
|
|
2658
|
+
className: cls("emergency-pre-emergency__label"),
|
|
2659
|
+
children: preEmergencyCopy.phoneLabel
|
|
2660
|
+
}),
|
|
2661
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2662
|
+
className: cls("emergency-pre-emergency__phone-row"),
|
|
2663
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2664
|
+
type: "tel",
|
|
2665
|
+
"aria-label": preEmergencyCopy.areaCodeLabel,
|
|
2666
|
+
className: cls("emergency-pre-emergency__input", "emergency-pre-emergency__input--area-code", valid && "emergency-pre-emergency__input--valid"),
|
|
2667
|
+
value: preEmergencyCopy.areaCode,
|
|
2668
|
+
disabled: true,
|
|
2669
|
+
readOnly: true
|
|
2670
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2671
|
+
id: phoneId,
|
|
2672
|
+
type: "tel",
|
|
2673
|
+
inputMode: "tel",
|
|
2674
|
+
autoComplete: "tel-national",
|
|
2675
|
+
placeholder: preEmergencyCopy.phonePlaceholder,
|
|
2676
|
+
className: cls("emergency-pre-emergency__input", "emergency-pre-emergency__input--number", valid && "emergency-pre-emergency__input--valid", invalid && "emergency-pre-emergency__input--invalid"),
|
|
2677
|
+
value: formatVenezuelanPhone(digits),
|
|
2678
|
+
"aria-invalid": invalid,
|
|
2679
|
+
"aria-describedby": invalid ? errorId : void 0,
|
|
2680
|
+
onChange: handleChange,
|
|
2681
|
+
onBlur: () => setTouched(true)
|
|
2682
|
+
})]
|
|
2683
|
+
}),
|
|
2684
|
+
invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2685
|
+
id: errorId,
|
|
2686
|
+
role: "alert",
|
|
2687
|
+
className: cls("emergency-pre-emergency__error"),
|
|
2688
|
+
children: preEmergencyCopy.invalidPhone
|
|
2689
|
+
}) : null
|
|
2690
|
+
]
|
|
2691
|
+
})]
|
|
2692
|
+
})
|
|
1976
2693
|
});
|
|
1977
2694
|
}
|
|
1978
2695
|
|
|
@@ -2198,32 +2915,6 @@ function EmergencyLoading() {
|
|
|
2198
2915
|
});
|
|
2199
2916
|
}
|
|
2200
2917
|
|
|
2201
|
-
// src/core/date.ts
|
|
2202
|
-
/**
|
|
2203
|
-
* Formats a timestamp as `h:mmam` or `h:mmpm` in local time, without a space
|
|
2204
|
-
* or leading zero.
|
|
2205
|
-
*/
|
|
2206
|
-
function formatShortTime(value, locale = "es") {
|
|
2207
|
-
if (value === null) return "";
|
|
2208
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
2209
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
2210
|
-
const parts = new Intl.DateTimeFormat(locale, {
|
|
2211
|
-
hour: "numeric",
|
|
2212
|
-
minute: "2-digit",
|
|
2213
|
-
hour12: true
|
|
2214
|
-
}).formatToParts(date);
|
|
2215
|
-
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2216
|
-
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2217
|
-
const dayPeriod = parts.find((part) => part.type === "dayPeriod")?.value;
|
|
2218
|
-
if (!hour || !minute) return "";
|
|
2219
|
-
return `${hour}:${minute}${normalizePeriod(dayPeriod)}`;
|
|
2220
|
-
}
|
|
2221
|
-
function normalizePeriod(dayPeriod) {
|
|
2222
|
-
if (!dayPeriod) return "";
|
|
2223
|
-
if (/^p/i.test(dayPeriod)) return "pm";
|
|
2224
|
-
return /^a/i.test(dayPeriod) ? "am" : "";
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
2918
|
// src/react/components/StatusIcon/index.tsx
|
|
2228
2919
|
/**
|
|
2229
2920
|
* Explicit responder state model:
|
|
@@ -2384,23 +3075,9 @@ function formatRounds(rounds) {
|
|
|
2384
3075
|
return `${rounds} ${roundsUnit(rounds)}`;
|
|
2385
3076
|
}
|
|
2386
3077
|
|
|
2387
|
-
// src/react/screens/primary/data.ts
|
|
2388
|
-
/**
|
|
2389
|
-
* Derives the remaining alert rounds from available messages and designated
|
|
2390
|
-
* responders. Invalid or non-positive input collapses to the minimum of one
|
|
2391
|
-
* round instead of producing `Infinity`, `NaN` or a negative value.
|
|
2392
|
-
*/
|
|
2393
|
-
function calculateRounds(messages, responders) {
|
|
2394
|
-
if (!Number.isFinite(messages) || !Number.isFinite(responders)) return 1;
|
|
2395
|
-
const availableMessages = Math.floor(messages);
|
|
2396
|
-
const designatedResponders = Math.floor(responders);
|
|
2397
|
-
if (availableMessages <= 0 || designatedResponders <= 0) return 1;
|
|
2398
|
-
return Math.max(1, Math.floor(availableMessages / designatedResponders));
|
|
2399
|
-
}
|
|
2400
|
-
|
|
2401
3078
|
// src/react/screens/primary/Availability.tsx
|
|
2402
3079
|
function Availability({ onRecharge, onDetails }) {
|
|
2403
|
-
const rounds = calculateRounds(
|
|
3080
|
+
const rounds = calculateRounds(27, 3);
|
|
2404
3081
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2405
3082
|
className: cls("emergency-availability"),
|
|
2406
3083
|
"aria-label": primaryCopy.availabilityTitle,
|
|
@@ -2442,7 +3119,7 @@ function Availability({ onRecharge, onDetails }) {
|
|
|
2442
3119
|
|
|
2443
3120
|
// src/react/components/EmergencyBody/index.tsx
|
|
2444
3121
|
function EmergencyBody(props) {
|
|
2445
|
-
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback, onRecharge, onDetails } = props;
|
|
3122
|
+
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback, onRecharge, onDetails, onClose } = props;
|
|
2446
3123
|
const showingFeedback = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
2447
3124
|
const idle = !active && !loading && !showingFeedback && error === null;
|
|
2448
3125
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2454,8 +3131,18 @@ function EmergencyBody(props) {
|
|
|
2454
3131
|
onDetails
|
|
2455
3132
|
})
|
|
2456
3133
|
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2457
|
-
className: cls("emergency-panel__prompt-region"),
|
|
3134
|
+
className: cls("emergency-panel__prompt-region", idle && "emergency-panel__prompt-region--idle"),
|
|
2458
3135
|
children: [
|
|
3136
|
+
idle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3137
|
+
type: "button",
|
|
3138
|
+
className: cls("emergency-panel__close-button", "emergency-panel__close-button--prompt"),
|
|
3139
|
+
"aria-label": copy.close,
|
|
3140
|
+
onClick: onClose,
|
|
3141
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3142
|
+
name: "close",
|
|
3143
|
+
className: cls("emergency-panel__close-icon")
|
|
3144
|
+
})
|
|
3145
|
+
}) : null,
|
|
2459
3146
|
!loading && !showingFeedback ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2460
3147
|
className: cls("sos-img__container"),
|
|
2461
3148
|
id: "sos-img__container",
|
|
@@ -2494,47 +3181,16 @@ function EmergencyBody(props) {
|
|
|
2494
3181
|
});
|
|
2495
3182
|
}
|
|
2496
3183
|
|
|
2497
|
-
// src/react/components/EmergencyFooter/index.tsx
|
|
2498
|
-
function EmergencyFooter(props) {
|
|
2499
|
-
const { active, busy, aborting, feedbackVisible, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2500
|
-
if (active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
2501
|
-
className: cls("emergency-panel__footer"),
|
|
2502
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2503
|
-
type: "button",
|
|
2504
|
-
className: cls("emergency-status__abort-btn"),
|
|
2505
|
-
"aria-label": copy.abort,
|
|
2506
|
-
disabled: aborting || feedbackVisible,
|
|
2507
|
-
onClick: onRequestAbort,
|
|
2508
|
-
children: [copy.abort, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "alert-triangle" })]
|
|
2509
|
-
})
|
|
2510
|
-
});
|
|
2511
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
2512
|
-
className: cls("emergency-panel__footer"),
|
|
2513
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2514
|
-
type: "button",
|
|
2515
|
-
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
2516
|
-
"aria-label": copy.emergencyAria,
|
|
2517
|
-
disabled: busy || feedbackVisible,
|
|
2518
|
-
onClick: onActivate,
|
|
2519
|
-
children: [copy.emergency, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "emergency" })]
|
|
2520
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2521
|
-
type: "button",
|
|
2522
|
-
className: cls("emergency-panel__button", "emergency-panel__button--simulation"),
|
|
2523
|
-
"aria-label": copy.testAria,
|
|
2524
|
-
disabled: busy || feedbackVisible,
|
|
2525
|
-
onClick: onRequestSimulation,
|
|
2526
|
-
children: [copy.test, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { name: "list-check" })]
|
|
2527
|
-
})]
|
|
2528
|
-
});
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
3184
|
// src/react/screens/primary/index.tsx
|
|
2532
3185
|
function PrimaryScreen(props) {
|
|
2533
3186
|
const { navigate } = useScreenNavigation();
|
|
2534
|
-
|
|
3187
|
+
const idle = !props.active && !props.loading && !props.feedbackVisible && props.error === null;
|
|
3188
|
+
const needsContactPhone = props.contactPhone === null;
|
|
3189
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(EmergencyScreenLayout, {
|
|
2535
3190
|
screen: "primary",
|
|
2536
3191
|
titleId: props.titleId,
|
|
2537
|
-
|
|
3192
|
+
bodyClassName: idle ? "emergency-panel__body--primary" : void 0,
|
|
3193
|
+
header: idle ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
2538
3194
|
titleId: props.titleId,
|
|
2539
3195
|
onClose: () => props.onClose("close")
|
|
2540
3196
|
}),
|
|
@@ -2543,11 +3199,17 @@ function PrimaryScreen(props) {
|
|
|
2543
3199
|
busy: props.busy,
|
|
2544
3200
|
aborting: props.aborting,
|
|
2545
3201
|
feedbackVisible: props.feedbackVisible,
|
|
2546
|
-
|
|
3202
|
+
square: idle,
|
|
3203
|
+
onActivate: needsContactPhone ? () => navigate("pre-emergency") : props.onActivate,
|
|
2547
3204
|
onRequestAbort: props.onRequestAbort,
|
|
2548
3205
|
onRequestSimulation: props.onRequestSimulation
|
|
2549
3206
|
}),
|
|
2550
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3207
|
+
children: [idle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
3208
|
+
className: cls("emergency-panel__sr-only"),
|
|
3209
|
+
id: props.titleId,
|
|
3210
|
+
tabIndex: -1,
|
|
3211
|
+
children: copy.panelLabel
|
|
3212
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyBody, {
|
|
2551
3213
|
active: props.active,
|
|
2552
3214
|
responders: props.responders,
|
|
2553
3215
|
refreshing: props.refreshing,
|
|
@@ -2562,8 +3224,9 @@ function PrimaryScreen(props) {
|
|
|
2562
3224
|
onRetry: props.onRetry,
|
|
2563
3225
|
onDismissFeedback: props.onDismissFeedback,
|
|
2564
3226
|
onRecharge: () => navigate("pricing"),
|
|
2565
|
-
onDetails: () => navigate("details")
|
|
2566
|
-
|
|
3227
|
+
onDetails: () => navigate("details"),
|
|
3228
|
+
onClose: () => props.onClose("close")
|
|
3229
|
+
})]
|
|
2567
3230
|
});
|
|
2568
3231
|
}
|
|
2569
3232
|
|
|
@@ -2577,7 +3240,7 @@ const pricingCopy = {
|
|
|
2577
3240
|
chosen: "Seleccionado",
|
|
2578
3241
|
whatsapp: "de WhatsApp",
|
|
2579
3242
|
rounds: "rondas de alerta",
|
|
2580
|
-
perMessage: "
|
|
3243
|
+
perMessage: "IVA Incluido",
|
|
2581
3244
|
footnote: "Rondas calculadas con 3 contactos de emergencia. Válidas para pruebas y emergencias."
|
|
2582
3245
|
};
|
|
2583
3246
|
|
|
@@ -2591,17 +3254,16 @@ const PRICING_PACKAGES = [{
|
|
|
2591
3254
|
label: "PAQUETE 1",
|
|
2592
3255
|
messages: 100,
|
|
2593
3256
|
priceLabel: "$7",
|
|
2594
|
-
priceCaption: "IVA
|
|
2595
|
-
perMessageLabel: "$0.
|
|
3257
|
+
priceCaption: "IVA incluído",
|
|
3258
|
+
perMessageLabel: "$0.07",
|
|
2596
3259
|
highlighted: false
|
|
2597
3260
|
}, {
|
|
2598
3261
|
id: "package-2",
|
|
2599
3262
|
label: "PAQUETE 2",
|
|
2600
3263
|
messages: 200,
|
|
2601
3264
|
priceLabel: "12$",
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
perMessageLabel: "$0.05",
|
|
3265
|
+
priceCaption: "IVA incluído",
|
|
3266
|
+
perMessageLabel: "$0.06",
|
|
2605
3267
|
highlighted: true,
|
|
2606
3268
|
badge: "MEJOR VALOR"
|
|
2607
3269
|
}];
|
|
@@ -2678,6 +3340,10 @@ function PackageCard({ pkg, selected, onSelect }) {
|
|
|
2678
3340
|
})
|
|
2679
3341
|
]
|
|
2680
3342
|
}),
|
|
3343
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3344
|
+
className: cls("emergency-pricing__package-footnote"),
|
|
3345
|
+
children: pricingCopy.footnote
|
|
3346
|
+
}),
|
|
2681
3347
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2682
3348
|
type: "button",
|
|
2683
3349
|
className: cls("emergency-pricing__select"),
|
|
@@ -2706,30 +3372,23 @@ function PricingScreen(props) {
|
|
|
2706
3372
|
footer: null,
|
|
2707
3373
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2708
3374
|
className: cls("emergency-pricing"),
|
|
2709
|
-
children: [
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
})]
|
|
2721
|
-
})
|
|
2722
|
-
}),
|
|
2723
|
-
PRICING_PACKAGES.map((pkg) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PackageCard, {
|
|
2724
|
-
pkg,
|
|
2725
|
-
selected: selected === pkg.id,
|
|
2726
|
-
onSelect: setSelected
|
|
2727
|
-
}, pkg.id)),
|
|
2728
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2729
|
-
className: cls("emergency-pricing__footnote"),
|
|
2730
|
-
children: pricingCopy.footnote
|
|
3375
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3376
|
+
className: cls("emergency-pricing__heading"),
|
|
3377
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3378
|
+
className: cls("emergency-pricing__titles"),
|
|
3379
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3380
|
+
className: cls("emergency-pricing__title"),
|
|
3381
|
+
children: pricingCopy.heading
|
|
3382
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3383
|
+
className: cls("emergency-pricing__subtitle"),
|
|
3384
|
+
children: pricingCopy.subtitle
|
|
3385
|
+
})]
|
|
2731
3386
|
})
|
|
2732
|
-
|
|
3387
|
+
}), PRICING_PACKAGES.map((pkg) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PackageCard, {
|
|
3388
|
+
pkg,
|
|
3389
|
+
selected: selected === pkg.id,
|
|
3390
|
+
onSelect: setSelected
|
|
3391
|
+
}, pkg.id))]
|
|
2733
3392
|
})
|
|
2734
3393
|
});
|
|
2735
3394
|
}
|
|
@@ -2738,8 +3397,11 @@ function PricingScreen(props) {
|
|
|
2738
3397
|
/** Every screen the panel can render. Add one entry to add a screen. */
|
|
2739
3398
|
const screenRegistry = {
|
|
2740
3399
|
primary: PrimaryScreen,
|
|
3400
|
+
"pre-emergency": PreEmergencyScreen,
|
|
2741
3401
|
pricing: PricingScreen,
|
|
2742
|
-
details: DetailsScreen
|
|
3402
|
+
details: DetailsScreen,
|
|
3403
|
+
history: HistoryScreen,
|
|
3404
|
+
"message-detail": MessageDetailScreen
|
|
2743
3405
|
};
|
|
2744
3406
|
|
|
2745
3407
|
// src/react/components/EmergencyPanel/index.tsx
|
|
@@ -2768,7 +3430,7 @@ function ActiveScreen({ screenProps, open, forcePrimary }) {
|
|
|
2768
3430
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Screen, { ...screenProps });
|
|
2769
3431
|
}
|
|
2770
3432
|
function EmergencyPanel(props) {
|
|
2771
|
-
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;
|
|
3433
|
+
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;
|
|
2772
3434
|
const titleId = (0, react.useId)();
|
|
2773
3435
|
const dialogRef = (0, react.useRef)(null);
|
|
2774
3436
|
const feedbackVisible = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
@@ -2793,13 +3455,15 @@ function EmergencyPanel(props) {
|
|
|
2793
3455
|
abortResult,
|
|
2794
3456
|
simulationResult,
|
|
2795
3457
|
feedbackVisible,
|
|
3458
|
+
contactPhone,
|
|
2796
3459
|
onClose,
|
|
2797
3460
|
onActivate,
|
|
2798
3461
|
onRefresh,
|
|
2799
3462
|
onRetry,
|
|
2800
3463
|
onDismissFeedback,
|
|
2801
3464
|
onRequestAbort,
|
|
2802
|
-
onRequestSimulation
|
|
3465
|
+
onRequestSimulation,
|
|
3466
|
+
onSetContactPhone
|
|
2803
3467
|
};
|
|
2804
3468
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2805
3469
|
ref: dialogRef,
|
|
@@ -2906,6 +3570,7 @@ function EmergencyRoot() {
|
|
|
2906
3570
|
const responders = useEmergencyStore((state) => state.responders);
|
|
2907
3571
|
const resumeAvailable = useEmergencyStore((state) => state.resumeAvailable);
|
|
2908
3572
|
const geolocationError = useEmergencyStore((state) => state.geolocationError);
|
|
3573
|
+
const contactPhone = useEmergencyStore((state) => state.contactPhone);
|
|
2909
3574
|
const lastError = useEmergencyStore((state) => state.lastError);
|
|
2910
3575
|
const abortResult = useEmergencyStore((state) => state.abortResult);
|
|
2911
3576
|
const simulationResult = useEmergencyStore((state) => state.simulationResult);
|
|
@@ -2931,6 +3596,7 @@ function EmergencyRoot() {
|
|
|
2931
3596
|
responders,
|
|
2932
3597
|
refreshing: phase.kind === "refreshing",
|
|
2933
3598
|
geolocationError,
|
|
3599
|
+
contactPhone,
|
|
2934
3600
|
activationError,
|
|
2935
3601
|
error: showError ? error : null,
|
|
2936
3602
|
abortResult,
|
|
@@ -2957,7 +3623,8 @@ function EmergencyRoot() {
|
|
|
2957
3623
|
},
|
|
2958
3624
|
onConfirmSimulation: () => {
|
|
2959
3625
|
client.simulate();
|
|
2960
|
-
}
|
|
3626
|
+
},
|
|
3627
|
+
onSetContactPhone: (phone) => client.setContactPhone(phone)
|
|
2961
3628
|
})] });
|
|
2962
3629
|
}
|
|
2963
3630
|
|
|
@@ -2989,6 +3656,7 @@ function createFailedClient(error) {
|
|
|
2989
3656
|
lastError: error,
|
|
2990
3657
|
geolocation: null,
|
|
2991
3658
|
geolocationError: null,
|
|
3659
|
+
contactPhone: null,
|
|
2992
3660
|
realtimeStatus: "idle",
|
|
2993
3661
|
abortResult: null,
|
|
2994
3662
|
simulationResult: null,
|
|
@@ -3017,7 +3685,8 @@ function createFailedClient(error) {
|
|
|
3017
3685
|
cancelAbortConfirmation: noop,
|
|
3018
3686
|
requestSimulationConfirmation: noop,
|
|
3019
3687
|
cancelSimulationConfirmation: noop,
|
|
3020
|
-
dismissFeedback: noop
|
|
3688
|
+
dismissFeedback: noop,
|
|
3689
|
+
setContactPhone: noop
|
|
3021
3690
|
};
|
|
3022
3691
|
}
|
|
3023
3692
|
function attachTrigger(trigger, client) {
|
|
@@ -3106,6 +3775,7 @@ function mount(options) {
|
|
|
3106
3775
|
requestSimulationConfirmation: () => client.requestSimulationConfirmation(),
|
|
3107
3776
|
cancelSimulationConfirmation: () => client.cancelSimulationConfirmation(),
|
|
3108
3777
|
dismissFeedback: () => client.dismissFeedback(),
|
|
3778
|
+
setContactPhone: (phone) => client.setContactPhone(phone),
|
|
3109
3779
|
destroy
|
|
3110
3780
|
};
|
|
3111
3781
|
}
|