@dataemergencia/emergency 0.5.0 → 0.6.1
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/dist/iife/emergency.iife.global.js +2 -2
- package/dist/index.cjs +639 -130
- package/dist/index.js +639 -130
- package/dist/styles.css +300 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1547,6 +1547,12 @@ var panel_module_default = {
|
|
|
1547
1547
|
"de-emergency-slide-in": "de-emergency-de-emergency-slide-in-nA5Hwa",
|
|
1548
1548
|
"de-emergency-slide-up": "de-emergency-de-emergency-slide-up-nA5Hwa",
|
|
1549
1549
|
"de-emergency-spin": "de-emergency-de-emergency-spin-nA5Hwa",
|
|
1550
|
+
"emergency-alert-row__badge": "de-emergency-emergency-alert-row__badge-nA5Hwa",
|
|
1551
|
+
"emergency-alert-row__button": "de-emergency-emergency-alert-row__button-nA5Hwa",
|
|
1552
|
+
"emergency-alert-row__stat": "de-emergency-emergency-alert-row__stat-nA5Hwa",
|
|
1553
|
+
"emergency-alert-row__stats": "de-emergency-emergency-alert-row__stats-nA5Hwa",
|
|
1554
|
+
"emergency-alert-row__time": "de-emergency-emergency-alert-row__time-nA5Hwa",
|
|
1555
|
+
"emergency-alert-row__top": "de-emergency-emergency-alert-row__top-nA5Hwa",
|
|
1550
1556
|
"emergency-availability": "de-emergency-emergency-availability-nA5Hwa",
|
|
1551
1557
|
"emergency-availability__card": "de-emergency-emergency-availability__card-nA5Hwa",
|
|
1552
1558
|
"emergency-availability__card-body": "de-emergency-emergency-availability__card-body-nA5Hwa",
|
|
@@ -1568,8 +1574,22 @@ var panel_module_default = {
|
|
|
1568
1574
|
"emergency-confirmation__icon": "de-emergency-emergency-confirmation__icon-nA5Hwa",
|
|
1569
1575
|
"emergency-confirmation__icon--danger": "de-emergency-emergency-confirmation__icon--danger-nA5Hwa",
|
|
1570
1576
|
"emergency-confirmation__icon--information": "de-emergency-emergency-confirmation__icon--information-nA5Hwa",
|
|
1577
|
+
"emergency-confirmation__spinner": "de-emergency-emergency-confirmation__spinner-nA5Hwa",
|
|
1571
1578
|
"emergency-confirmation__text": "de-emergency-emergency-confirmation__text-nA5Hwa",
|
|
1572
1579
|
"emergency-confirmation__title": "de-emergency-emergency-confirmation__title-nA5Hwa",
|
|
1580
|
+
"emergency-details": "de-emergency-emergency-details-nA5Hwa",
|
|
1581
|
+
"emergency-details__balance": "de-emergency-emergency-details__balance-nA5Hwa",
|
|
1582
|
+
"emergency-details__card": "de-emergency-emergency-details__card-nA5Hwa",
|
|
1583
|
+
"emergency-details__card-body": "de-emergency-emergency-details__card-body-nA5Hwa",
|
|
1584
|
+
"emergency-details__card-header": "de-emergency-emergency-details__card-header-nA5Hwa",
|
|
1585
|
+
"emergency-details__card-title": "de-emergency-emergency-details__card-title-nA5Hwa",
|
|
1586
|
+
"emergency-details__empty": "de-emergency-emergency-details__empty-nA5Hwa",
|
|
1587
|
+
"emergency-details__heading": "de-emergency-emergency-details__heading-nA5Hwa",
|
|
1588
|
+
"emergency-details__history-button": "de-emergency-emergency-details__history-button-nA5Hwa",
|
|
1589
|
+
"emergency-details__list": "de-emergency-emergency-details__list-nA5Hwa",
|
|
1590
|
+
"emergency-details__section": "de-emergency-emergency-details__section-nA5Hwa",
|
|
1591
|
+
"emergency-details__subtitle": "de-emergency-emergency-details__subtitle-nA5Hwa",
|
|
1592
|
+
"emergency-details__title": "de-emergency-emergency-details__title-nA5Hwa",
|
|
1573
1593
|
"emergency-error-message": "de-emergency-emergency-error-message-nA5Hwa",
|
|
1574
1594
|
"emergency-error-message__container": "de-emergency-emergency-error-message__container-nA5Hwa",
|
|
1575
1595
|
"emergency-error-message__content": "de-emergency-emergency-error-message__content-nA5Hwa",
|
|
@@ -1580,11 +1600,33 @@ var panel_module_default = {
|
|
|
1580
1600
|
"emergency-error-message__title": "de-emergency-emergency-error-message__title-nA5Hwa",
|
|
1581
1601
|
"emergency-feedback": "de-emergency-emergency-feedback-nA5Hwa",
|
|
1582
1602
|
"emergency-feedback__dismiss": "de-emergency-emergency-feedback__dismiss-nA5Hwa",
|
|
1603
|
+
"emergency-history": "de-emergency-emergency-history-nA5Hwa",
|
|
1604
|
+
"emergency-history__empty": "de-emergency-emergency-history__empty-nA5Hwa",
|
|
1605
|
+
"emergency-history__heading": "de-emergency-emergency-history__heading-nA5Hwa",
|
|
1606
|
+
"emergency-history__list": "de-emergency-emergency-history__list-nA5Hwa",
|
|
1607
|
+
"emergency-history__more": "de-emergency-emergency-history__more-nA5Hwa",
|
|
1608
|
+
"emergency-history__subtitle": "de-emergency-emergency-history__subtitle-nA5Hwa",
|
|
1609
|
+
"emergency-history__title": "de-emergency-emergency-history__title-nA5Hwa",
|
|
1610
|
+
"emergency-message-detail": "de-emergency-emergency-message-detail-nA5Hwa",
|
|
1611
|
+
"emergency-message-detail__back": "de-emergency-emergency-message-detail__back-nA5Hwa",
|
|
1612
|
+
"emergency-message-detail__card": "de-emergency-emergency-message-detail__card-nA5Hwa",
|
|
1613
|
+
"emergency-message-detail__card-header": "de-emergency-emergency-message-detail__card-header-nA5Hwa",
|
|
1614
|
+
"emergency-message-detail__card-title": "de-emergency-emergency-message-detail__card-title-nA5Hwa",
|
|
1615
|
+
"emergency-message-detail__empty": "de-emergency-emergency-message-detail__empty-nA5Hwa",
|
|
1616
|
+
"emergency-message-detail__empty-text": "de-emergency-emergency-message-detail__empty-text-nA5Hwa",
|
|
1617
|
+
"emergency-message-detail__label": "de-emergency-emergency-message-detail__label-nA5Hwa",
|
|
1618
|
+
"emergency-message-detail__list": "de-emergency-emergency-message-detail__list-nA5Hwa",
|
|
1619
|
+
"emergency-message-detail__row": "de-emergency-emergency-message-detail__row-nA5Hwa",
|
|
1620
|
+
"emergency-message-detail__value": "de-emergency-emergency-message-detail__value-nA5Hwa",
|
|
1583
1621
|
"emergency-panel": "de-emergency-emergency-panel-nA5Hwa",
|
|
1584
1622
|
"emergency-panel__availability-region": "de-emergency-emergency-panel__availability-region-nA5Hwa",
|
|
1585
1623
|
"emergency-panel__body": "de-emergency-emergency-panel__body-nA5Hwa",
|
|
1624
|
+
"emergency-panel__body--details": "de-emergency-emergency-panel__body--details-nA5Hwa",
|
|
1625
|
+
"emergency-panel__body--history": "de-emergency-emergency-panel__body--history-nA5Hwa",
|
|
1626
|
+
"emergency-panel__body--message-detail": "de-emergency-emergency-panel__body--message-detail-nA5Hwa",
|
|
1586
1627
|
"emergency-panel__body--pre-emergency": "de-emergency-emergency-panel__body--pre-emergency-nA5Hwa",
|
|
1587
1628
|
"emergency-panel__body--pricing": "de-emergency-emergency-panel__body--pricing-nA5Hwa",
|
|
1629
|
+
"emergency-panel__body--primary": "de-emergency-emergency-panel__body--primary-nA5Hwa",
|
|
1588
1630
|
"emergency-panel__body-split": "de-emergency-emergency-panel__body-split-nA5Hwa",
|
|
1589
1631
|
"emergency-panel__body-split--with-availability": "de-emergency-emergency-panel__body-split--with-availability-nA5Hwa",
|
|
1590
1632
|
"emergency-panel__body-title": "de-emergency-emergency-panel__body-title-nA5Hwa",
|
|
@@ -1593,13 +1635,14 @@ var panel_module_default = {
|
|
|
1593
1635
|
"emergency-panel__button--emergency": "de-emergency-emergency-panel__button--emergency-nA5Hwa",
|
|
1594
1636
|
"emergency-panel__button--simulation": "de-emergency-emergency-panel__button--simulation-nA5Hwa",
|
|
1595
1637
|
"emergency-panel__close-button": "de-emergency-emergency-panel__close-button-nA5Hwa",
|
|
1638
|
+
"emergency-panel__close-button--prompt": "de-emergency-emergency-panel__close-button--prompt-nA5Hwa",
|
|
1596
1639
|
"emergency-panel__close-icon": "de-emergency-emergency-panel__close-icon-nA5Hwa",
|
|
1597
|
-
"emergency-panel__divider": "de-emergency-emergency-panel__divider-nA5Hwa",
|
|
1598
1640
|
"emergency-panel__footer": "de-emergency-emergency-panel__footer-nA5Hwa",
|
|
1599
1641
|
"emergency-panel__footer--single": "de-emergency-emergency-panel__footer--single-nA5Hwa",
|
|
1642
|
+
"emergency-panel__footer--square": "de-emergency-emergency-panel__footer--square-nA5Hwa",
|
|
1600
1643
|
"emergency-panel__header": "de-emergency-emergency-panel__header-nA5Hwa",
|
|
1601
|
-
"emergency-panel__header--close-only": "de-emergency-emergency-panel__header--close-only-nA5Hwa",
|
|
1602
1644
|
"emergency-panel__prompt-region": "de-emergency-emergency-panel__prompt-region-nA5Hwa",
|
|
1645
|
+
"emergency-panel__prompt-region--idle": "de-emergency-emergency-panel__prompt-region--idle-nA5Hwa",
|
|
1603
1646
|
"emergency-panel__sr-only": "de-emergency-emergency-panel__sr-only-nA5Hwa",
|
|
1604
1647
|
"emergency-panel__title": "de-emergency-emergency-panel__title-nA5Hwa",
|
|
1605
1648
|
"emergency-panel--hidden": "de-emergency-emergency-panel--hidden-nA5Hwa",
|
|
@@ -1695,11 +1738,12 @@ function cls(...names) {
|
|
|
1695
1738
|
|
|
1696
1739
|
// src/react/components/EmergencyConfirmation/index.tsx
|
|
1697
1740
|
function EmergencyConfirmation(props) {
|
|
1698
|
-
const { abortConfirmation, simulationConfirmation, onCancelAbort, onCancelSimulation, onConfirmAbort, onConfirmSimulation } = props;
|
|
1741
|
+
const { abortConfirmation, simulationConfirmation, loading, onCancelAbort, onCancelSimulation, onConfirmAbort, onConfirmSimulation } = props;
|
|
1699
1742
|
const cardRef = useRef(null);
|
|
1700
1743
|
const titleId = useId();
|
|
1701
1744
|
const danger = abortConfirmation;
|
|
1702
1745
|
const open = abortConfirmation || simulationConfirmation;
|
|
1746
|
+
const pending = open && loading;
|
|
1703
1747
|
useEffect(() => {
|
|
1704
1748
|
if (open) cardRef.current?.querySelector("button")?.focus();
|
|
1705
1749
|
}, [open]);
|
|
@@ -1712,40 +1756,56 @@ function EmergencyConfirmation(props) {
|
|
|
1712
1756
|
role: "dialog",
|
|
1713
1757
|
"aria-modal": "true",
|
|
1714
1758
|
"aria-labelledby": titleId,
|
|
1759
|
+
"aria-busy": pending,
|
|
1715
1760
|
children: /* @__PURE__ */ jsxs("div", {
|
|
1716
1761
|
ref: cardRef,
|
|
1717
1762
|
className: cls("emergency-confirmation__card"),
|
|
1718
|
-
children: [
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
}),
|
|
1743
|
-
|
|
1744
|
-
className: cls("emergency-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1763
|
+
children: [
|
|
1764
|
+
/* @__PURE__ */ jsx("span", {
|
|
1765
|
+
className: cls("emergency-panel__sr-only"),
|
|
1766
|
+
role: "status",
|
|
1767
|
+
"aria-live": "polite",
|
|
1768
|
+
children: pending ? copy.loading : ""
|
|
1769
|
+
}),
|
|
1770
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1771
|
+
className: cls("emergency-confirmation__body"),
|
|
1772
|
+
children: [
|
|
1773
|
+
/* @__PURE__ */ jsx("div", {
|
|
1774
|
+
className: cls("emergency-confirmation__icon", `emergency-confirmation__icon--${tone}`),
|
|
1775
|
+
children: /* @__PURE__ */ jsx(Icon, { name: danger ? "alert-triangle" : "info" })
|
|
1776
|
+
}),
|
|
1777
|
+
/* @__PURE__ */ jsx("h5", {
|
|
1778
|
+
className: cls("emergency-confirmation__title"),
|
|
1779
|
+
id: titleId,
|
|
1780
|
+
children: title
|
|
1781
|
+
}),
|
|
1782
|
+
/* @__PURE__ */ jsx("p", {
|
|
1783
|
+
className: cls("emergency-confirmation__text"),
|
|
1784
|
+
children: text
|
|
1785
|
+
})
|
|
1786
|
+
]
|
|
1787
|
+
}),
|
|
1788
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1789
|
+
className: cls("emergency-confirmation__footer"),
|
|
1790
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
1791
|
+
type: "button",
|
|
1792
|
+
className: cls("emergency-confirmation__button", "emergency-confirmation__button--cancel"),
|
|
1793
|
+
disabled: pending,
|
|
1794
|
+
onClick: danger ? onCancelAbort : onCancelSimulation,
|
|
1795
|
+
children: danger ? copy.abortCancel : copy.simulateCancel
|
|
1796
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
1797
|
+
type: "button",
|
|
1798
|
+
className: cls("emergency-confirmation__button", danger ? "emergency-confirmation__button--danger" : "emergency-confirmation__button--confirm"),
|
|
1799
|
+
"aria-busy": pending,
|
|
1800
|
+
disabled: pending,
|
|
1801
|
+
onClick: danger ? onConfirmAbort : onConfirmSimulation,
|
|
1802
|
+
children: [pending ? /* @__PURE__ */ jsx("span", {
|
|
1803
|
+
className: cls("emergency-confirmation__spinner"),
|
|
1804
|
+
"aria-hidden": "true"
|
|
1805
|
+
}) : null, /* @__PURE__ */ jsx("span", { children: danger ? copy.abortConfirm : copy.simulateConfirm })]
|
|
1806
|
+
})]
|
|
1807
|
+
})
|
|
1808
|
+
]
|
|
1749
1809
|
})
|
|
1750
1810
|
});
|
|
1751
1811
|
}
|
|
@@ -1818,18 +1878,27 @@ function useFocusTrap(ref, active) {
|
|
|
1818
1878
|
}
|
|
1819
1879
|
|
|
1820
1880
|
// src/react/screens/context.tsx
|
|
1881
|
+
const EMPTY_PARAMS = {};
|
|
1821
1882
|
const EmergencyScreenContext = createContext(null);
|
|
1883
|
+
function paramsEqual(first, second) {
|
|
1884
|
+
return first.messageId === second.messageId;
|
|
1885
|
+
}
|
|
1822
1886
|
function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
1823
1887
|
const [state, setState] = useState({
|
|
1824
1888
|
screen: initialScreen,
|
|
1889
|
+
params: EMPTY_PARAMS,
|
|
1825
1890
|
history: []
|
|
1826
1891
|
});
|
|
1827
|
-
const navigate = useCallback((next) => {
|
|
1892
|
+
const navigate = useCallback((next, params = EMPTY_PARAMS) => {
|
|
1828
1893
|
setState((current) => {
|
|
1829
|
-
if (current.screen === next) return current;
|
|
1894
|
+
if (current.screen === next && paramsEqual(current.params, params)) return current;
|
|
1830
1895
|
return {
|
|
1831
1896
|
screen: next,
|
|
1832
|
-
|
|
1897
|
+
params,
|
|
1898
|
+
history: [...current.history, {
|
|
1899
|
+
screen: current.screen,
|
|
1900
|
+
params: current.params
|
|
1901
|
+
}]
|
|
1833
1902
|
};
|
|
1834
1903
|
});
|
|
1835
1904
|
}, []);
|
|
@@ -1838,31 +1907,47 @@ function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
|
1838
1907
|
const previous = current.history.at(-1);
|
|
1839
1908
|
if (!previous) return current;
|
|
1840
1909
|
return {
|
|
1841
|
-
screen: previous,
|
|
1910
|
+
screen: previous.screen,
|
|
1911
|
+
params: previous.params,
|
|
1842
1912
|
history: current.history.slice(0, -1)
|
|
1843
1913
|
};
|
|
1844
1914
|
});
|
|
1845
1915
|
}, []);
|
|
1916
|
+
const replace = useCallback((next, params = EMPTY_PARAMS) => {
|
|
1917
|
+
setState((current) => {
|
|
1918
|
+
if (current.screen === next && paramsEqual(current.params, params)) return current;
|
|
1919
|
+
return {
|
|
1920
|
+
screen: next,
|
|
1921
|
+
params,
|
|
1922
|
+
history: current.history.at(-1)?.screen === next ? current.history.slice(0, -1) : current.history
|
|
1923
|
+
};
|
|
1924
|
+
});
|
|
1925
|
+
}, []);
|
|
1846
1926
|
const reset = useCallback(() => {
|
|
1847
1927
|
setState((current) => {
|
|
1848
|
-
if (current.screen === "primary" && current.history.length === 0) return current;
|
|
1928
|
+
if (current.screen === "primary" && current.params.messageId === void 0 && current.history.length === 0) return current;
|
|
1849
1929
|
return {
|
|
1850
1930
|
screen: "primary",
|
|
1931
|
+
params: EMPTY_PARAMS,
|
|
1851
1932
|
history: []
|
|
1852
1933
|
};
|
|
1853
1934
|
});
|
|
1854
1935
|
}, []);
|
|
1855
1936
|
const value = useMemo(() => ({
|
|
1856
1937
|
screen: state.screen,
|
|
1938
|
+
params: state.params,
|
|
1857
1939
|
history: state.history,
|
|
1858
1940
|
navigate,
|
|
1941
|
+
replace,
|
|
1859
1942
|
back,
|
|
1860
1943
|
reset
|
|
1861
1944
|
}), [
|
|
1862
1945
|
back,
|
|
1863
1946
|
navigate,
|
|
1947
|
+
replace,
|
|
1864
1948
|
reset,
|
|
1865
1949
|
state.history,
|
|
1950
|
+
state.params,
|
|
1866
1951
|
state.screen
|
|
1867
1952
|
]);
|
|
1868
1953
|
return /* @__PURE__ */ jsx(EmergencyScreenContext.Provider, {
|
|
@@ -1877,12 +1962,11 @@ function useScreenNavigation() {
|
|
|
1877
1962
|
}
|
|
1878
1963
|
|
|
1879
1964
|
// src/react/components/EmergencyHeader/index.tsx
|
|
1880
|
-
function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack
|
|
1881
|
-
const closeOnly = variant === "close-only";
|
|
1965
|
+
function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack }) {
|
|
1882
1966
|
return /* @__PURE__ */ jsxs("header", {
|
|
1883
|
-
className: cls("emergency-panel__header"
|
|
1967
|
+
className: cls("emergency-panel__header"),
|
|
1884
1968
|
children: [
|
|
1885
|
-
onBack
|
|
1969
|
+
onBack ? /* @__PURE__ */ jsx("button", {
|
|
1886
1970
|
type: "button",
|
|
1887
1971
|
className: cls("emergency-panel__close-button"),
|
|
1888
1972
|
"aria-label": copy.back,
|
|
@@ -1893,7 +1977,7 @@ function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack, va
|
|
|
1893
1977
|
})
|
|
1894
1978
|
}) : null,
|
|
1895
1979
|
/* @__PURE__ */ jsx("h2", {
|
|
1896
|
-
className: cls("emergency-panel__title"
|
|
1980
|
+
className: cls("emergency-panel__title"),
|
|
1897
1981
|
id: titleId,
|
|
1898
1982
|
tabIndex: -1,
|
|
1899
1983
|
children: title
|
|
@@ -1912,6 +1996,245 @@ function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack, va
|
|
|
1912
1996
|
});
|
|
1913
1997
|
}
|
|
1914
1998
|
|
|
1999
|
+
// src/react/screens/primary/data.ts
|
|
2000
|
+
/**
|
|
2001
|
+
* Derives the remaining alert rounds from available messages and designated
|
|
2002
|
+
* responders. Invalid or non-positive input collapses to the minimum of one
|
|
2003
|
+
* round instead of producing `Infinity`, `NaN` or a negative value.
|
|
2004
|
+
*/
|
|
2005
|
+
function calculateRounds(messages, responders) {
|
|
2006
|
+
if (!Number.isFinite(messages) || !Number.isFinite(responders)) return 1;
|
|
2007
|
+
const availableMessages = Math.floor(messages);
|
|
2008
|
+
const designatedResponders = Math.floor(responders);
|
|
2009
|
+
if (availableMessages <= 0 || designatedResponders <= 0) return 1;
|
|
2010
|
+
return Math.max(1, Math.floor(availableMessages / designatedResponders));
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// src/react/screens/details/copy.ts
|
|
2014
|
+
const detailsCopy = {
|
|
2015
|
+
title: "Detalles",
|
|
2016
|
+
summaryTitle: "Alertas Disponibles",
|
|
2017
|
+
historyTitle: "Historial de alertas",
|
|
2018
|
+
historySubtitle: "Tus últimas alertas y recargas.",
|
|
2019
|
+
historyEmpty: "Todavía no tienes alertas en tu historial.",
|
|
2020
|
+
summaryEmpty: "Todavía no tienes un saldo de alertas disponible.",
|
|
2021
|
+
viewHistory: "Ver historial",
|
|
2022
|
+
messagesSingular: "mensaje de alerta",
|
|
2023
|
+
messagesPlural: "mensajes de alerta",
|
|
2024
|
+
contactsSingular: "contacto de emergencia",
|
|
2025
|
+
contactsPlural: "contactos de emergencia",
|
|
2026
|
+
roundsSingular: "ronda de avisos",
|
|
2027
|
+
roundsPlural: "rondas de avisos"
|
|
2028
|
+
};
|
|
2029
|
+
/** Returns the balance sentence split into text and emphasized number parts. */
|
|
2030
|
+
function alertBalanceParts(messages, responders) {
|
|
2031
|
+
if (!Number.isFinite(messages) || !Number.isFinite(responders) || messages < 0 || responders < 0) return [];
|
|
2032
|
+
const rounds = calculateRounds(messages, responders);
|
|
2033
|
+
const messagesUnit = messages === 1 ? detailsCopy.messagesSingular : detailsCopy.messagesPlural;
|
|
2034
|
+
const contactsUnit = responders === 1 ? detailsCopy.contactsSingular : detailsCopy.contactsPlural;
|
|
2035
|
+
const roundsUnit = rounds === 1 ? detailsCopy.roundsSingular : detailsCopy.roundsPlural;
|
|
2036
|
+
return [
|
|
2037
|
+
messages === 1 ? "Te queda " : "Te quedan ",
|
|
2038
|
+
messages,
|
|
2039
|
+
` ${messagesUnit}, dado que tienes `,
|
|
2040
|
+
responders,
|
|
2041
|
+
` ${contactsUnit}, `,
|
|
2042
|
+
rounds === 1 ? "te queda " : "te quedan ",
|
|
2043
|
+
rounds,
|
|
2044
|
+
` ${roundsUnit}.`
|
|
2045
|
+
];
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// src/react/screens/details/AlertBalanceSentence.tsx
|
|
2049
|
+
/** Renders the balance sentence with its numbers visually emphasized. */
|
|
2050
|
+
function AlertBalanceSentence({ messages, responders }) {
|
|
2051
|
+
const parts = alertBalanceParts(messages, responders);
|
|
2052
|
+
return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part, index) => typeof part === "number" ? /* @__PURE__ */ jsx("strong", { children: part }, index) : part) });
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// src/core/date.ts
|
|
2056
|
+
/**
|
|
2057
|
+
* Formats a timestamp as `h:mmam` or `h:mmpm` in local time, without a space
|
|
2058
|
+
* or leading zero.
|
|
2059
|
+
*/
|
|
2060
|
+
function formatShortTime(value, locale = "es") {
|
|
2061
|
+
if (value === null) return "";
|
|
2062
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2063
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
2064
|
+
const parts = new Intl.DateTimeFormat(locale, {
|
|
2065
|
+
hour: "numeric",
|
|
2066
|
+
minute: "2-digit",
|
|
2067
|
+
hour12: true
|
|
2068
|
+
}).formatToParts(date);
|
|
2069
|
+
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2070
|
+
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2071
|
+
const dayPeriod = parts.find((part) => part.type === "dayPeriod")?.value;
|
|
2072
|
+
if (!hour || !minute) return "";
|
|
2073
|
+
return `${hour}:${minute}${normalizePeriod(dayPeriod)}`;
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Formats a timestamp as `dd/mm/yyyy hh:mm` in local time, using 24-hour time
|
|
2077
|
+
* and zero-padded parts. Missing or invalid input collapses to an empty string.
|
|
2078
|
+
*/
|
|
2079
|
+
function formatDateTime(value, locale = "es") {
|
|
2080
|
+
if (value === null) return "";
|
|
2081
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
2082
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
2083
|
+
const parts = new Intl.DateTimeFormat(locale, {
|
|
2084
|
+
day: "2-digit",
|
|
2085
|
+
month: "2-digit",
|
|
2086
|
+
year: "numeric",
|
|
2087
|
+
hour: "2-digit",
|
|
2088
|
+
minute: "2-digit",
|
|
2089
|
+
hourCycle: "h23"
|
|
2090
|
+
}).formatToParts(date);
|
|
2091
|
+
const day = parts.find((part) => part.type === "day")?.value;
|
|
2092
|
+
const month = parts.find((part) => part.type === "month")?.value;
|
|
2093
|
+
const year = parts.find((part) => part.type === "year")?.value;
|
|
2094
|
+
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2095
|
+
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2096
|
+
if (!day || !month || !year || !hour || !minute) return "";
|
|
2097
|
+
return `${day}/${month}/${year} ${hour}:${minute}`;
|
|
2098
|
+
}
|
|
2099
|
+
function normalizePeriod(dayPeriod) {
|
|
2100
|
+
if (!dayPeriod) return "";
|
|
2101
|
+
if (/^p/i.test(dayPeriod)) return "pm";
|
|
2102
|
+
return /^a/i.test(dayPeriod) ? "am" : "";
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
// src/react/screens/history/copy.ts
|
|
2106
|
+
const historyCopy = {
|
|
2107
|
+
title: "Historial de alertas",
|
|
2108
|
+
subtitle: "Consulta tus alertas, pruebas y recargas.",
|
|
2109
|
+
empty: "Todavía no tienes alertas en tu historial.",
|
|
2110
|
+
showMore: "Ver más",
|
|
2111
|
+
kindLabels: {
|
|
2112
|
+
emergency: "Emergencia",
|
|
2113
|
+
test: "Prueba",
|
|
2114
|
+
recharge: "Recarga"
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
function formatContacts(count) {
|
|
2118
|
+
return count === 1 ? "1 contacto" : `${count} contactos`;
|
|
2119
|
+
}
|
|
2120
|
+
function formatMessages(count) {
|
|
2121
|
+
return count === 1 ? "1 mensaje" : `${count} mensajes`;
|
|
2122
|
+
}
|
|
2123
|
+
function formatHistoryRounds(count) {
|
|
2124
|
+
return count === 1 ? "1 ronda" : `${count} rondas`;
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
// src/react/screens/history/data.ts
|
|
2128
|
+
/** Local demo alert history, newest first. */
|
|
2129
|
+
const DEMO_ALERT_HISTORY = [
|
|
2130
|
+
{
|
|
2131
|
+
id: "alert-2026-10-31",
|
|
2132
|
+
kind: "emergency",
|
|
2133
|
+
occurredAt: "2026-10-31T13:50:00",
|
|
2134
|
+
responders: 3,
|
|
2135
|
+
remainingMessages: 27
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
id: "alert-2026-10-20",
|
|
2139
|
+
kind: "recharge",
|
|
2140
|
+
occurredAt: "2026-10-20T09:15:00",
|
|
2141
|
+
responders: 3,
|
|
2142
|
+
remainingMessages: 100
|
|
2143
|
+
},
|
|
2144
|
+
{
|
|
2145
|
+
id: "alert-2026-10-12",
|
|
2146
|
+
kind: "test",
|
|
2147
|
+
occurredAt: "2026-10-12T18:40:00",
|
|
2148
|
+
responders: 3,
|
|
2149
|
+
remainingMessages: 73
|
|
2150
|
+
},
|
|
2151
|
+
{
|
|
2152
|
+
id: "alert-2026-10-01",
|
|
2153
|
+
kind: "recharge",
|
|
2154
|
+
occurredAt: "2026-10-01T11:05:00",
|
|
2155
|
+
responders: 3,
|
|
2156
|
+
remainingMessages: 76
|
|
2157
|
+
}
|
|
2158
|
+
];
|
|
2159
|
+
/** Returns a newest-first copy of the history. */
|
|
2160
|
+
function sortedHistoryEntries(entries = DEMO_ALERT_HISTORY) {
|
|
2161
|
+
return [...entries].sort((first, second) => new Date(second.occurredAt).getTime() - new Date(first.occurredAt).getTime());
|
|
2162
|
+
}
|
|
2163
|
+
/** Newest entry, used as the source of the current balance. */
|
|
2164
|
+
function latestHistoryEntry(entries = DEMO_ALERT_HISTORY) {
|
|
2165
|
+
return sortedHistoryEntries(entries)[0] ?? null;
|
|
2166
|
+
}
|
|
2167
|
+
/** Resolves a single history entry by id. */
|
|
2168
|
+
function historyEntryById(id, entries = DEMO_ALERT_HISTORY) {
|
|
2169
|
+
if (!id) return null;
|
|
2170
|
+
return entries.find((entry) => entry.id === id) ?? null;
|
|
2171
|
+
}
|
|
2172
|
+
/** Rounds represented by the entry's remaining messages and responders. */
|
|
2173
|
+
function historyEntryRounds(entry) {
|
|
2174
|
+
return calculateRounds(entry.remainingMessages, entry.responders);
|
|
2175
|
+
}
|
|
2176
|
+
/** Current balance derived from the newest history entry. */
|
|
2177
|
+
function currentAlertBalance(entries = DEMO_ALERT_HISTORY) {
|
|
2178
|
+
const latest = latestHistoryEntry(entries);
|
|
2179
|
+
if (!latest) return null;
|
|
2180
|
+
return {
|
|
2181
|
+
messages: latest.remainingMessages,
|
|
2182
|
+
responders: latest.responders,
|
|
2183
|
+
rounds: historyEntryRounds(latest)
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
// src/react/screens/history/AlertHistoryRow.tsx
|
|
2188
|
+
/** Reusable history row. The whole row is a button with a full text label. */
|
|
2189
|
+
function AlertHistoryRow({ entry, locale, onOpen }) {
|
|
2190
|
+
const occurredAt = formatDateTime(entry.occurredAt, locale);
|
|
2191
|
+
const rounds = historyEntryRounds(entry);
|
|
2192
|
+
const label = [
|
|
2193
|
+
historyCopy.kindLabels[entry.kind],
|
|
2194
|
+
occurredAt,
|
|
2195
|
+
formatContacts(entry.responders),
|
|
2196
|
+
`${formatMessages(entry.remainingMessages)} restantes`,
|
|
2197
|
+
`${formatHistoryRounds(rounds)} de avisos`
|
|
2198
|
+
].join(", ");
|
|
2199
|
+
return /* @__PURE__ */ jsx("li", {
|
|
2200
|
+
className: cls("emergency-alert-row"),
|
|
2201
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
2202
|
+
type: "button",
|
|
2203
|
+
className: cls("emergency-alert-row__button"),
|
|
2204
|
+
"aria-label": label,
|
|
2205
|
+
onClick: () => onOpen(entry.id),
|
|
2206
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
2207
|
+
className: cls("emergency-alert-row__top"),
|
|
2208
|
+
children: [/* @__PURE__ */ jsx("time", {
|
|
2209
|
+
className: cls("emergency-alert-row__time"),
|
|
2210
|
+
dateTime: entry.occurredAt,
|
|
2211
|
+
children: occurredAt
|
|
2212
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2213
|
+
className: cls("emergency-alert-row__badge"),
|
|
2214
|
+
"data-kind": entry.kind,
|
|
2215
|
+
children: historyCopy.kindLabels[entry.kind]
|
|
2216
|
+
})]
|
|
2217
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
2218
|
+
className: cls("emergency-alert-row__stats"),
|
|
2219
|
+
children: [
|
|
2220
|
+
/* @__PURE__ */ jsx("span", {
|
|
2221
|
+
className: cls("emergency-alert-row__stat"),
|
|
2222
|
+
children: formatContacts(entry.responders)
|
|
2223
|
+
}),
|
|
2224
|
+
/* @__PURE__ */ jsx("span", {
|
|
2225
|
+
className: cls("emergency-alert-row__stat"),
|
|
2226
|
+
children: formatMessages(entry.remainingMessages)
|
|
2227
|
+
}),
|
|
2228
|
+
/* @__PURE__ */ jsx("span", {
|
|
2229
|
+
className: cls("emergency-alert-row__stat"),
|
|
2230
|
+
children: formatHistoryRounds(rounds)
|
|
2231
|
+
})
|
|
2232
|
+
]
|
|
2233
|
+
})]
|
|
2234
|
+
})
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
|
|
1915
2238
|
// src/react/screens/layout.tsx
|
|
1916
2239
|
/**
|
|
1917
2240
|
* Shared shell for every screen. The wrapper is `display: contents` so the
|
|
@@ -1933,46 +2256,254 @@ function EmergencyScreenLayout({ screen, titleId, header, footer, children, body
|
|
|
1933
2256
|
});
|
|
1934
2257
|
}
|
|
1935
2258
|
|
|
1936
|
-
// src/react/screens/
|
|
1937
|
-
|
|
1938
|
-
|
|
2259
|
+
// src/react/screens/details/index.tsx
|
|
2260
|
+
function DetailsScreen({ titleId, locale, onClose }) {
|
|
2261
|
+
const { back, navigate } = useScreenNavigation();
|
|
2262
|
+
const balance = currentAlertBalance();
|
|
2263
|
+
const preview = sortedHistoryEntries().slice(0, 2);
|
|
1939
2264
|
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
1940
|
-
screen,
|
|
2265
|
+
screen: "details",
|
|
1941
2266
|
titleId,
|
|
2267
|
+
bodyClassName: "emergency-panel__body--details",
|
|
1942
2268
|
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
1943
2269
|
titleId,
|
|
1944
|
-
title,
|
|
1945
|
-
onClose,
|
|
1946
|
-
onBack
|
|
2270
|
+
title: detailsCopy.title,
|
|
2271
|
+
onClose: () => onClose("close"),
|
|
2272
|
+
onBack: back
|
|
1947
2273
|
}),
|
|
1948
2274
|
footer: null,
|
|
1949
|
-
children: /* @__PURE__ */
|
|
1950
|
-
className: cls("emergency-
|
|
1951
|
-
children: /* @__PURE__ */ jsx("
|
|
1952
|
-
className: cls("emergency-
|
|
1953
|
-
|
|
1954
|
-
|
|
2275
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2276
|
+
className: cls("emergency-details"),
|
|
2277
|
+
children: [/* @__PURE__ */ jsx("section", {
|
|
2278
|
+
className: cls("emergency-details__section"),
|
|
2279
|
+
"aria-label": detailsCopy.summaryTitle,
|
|
2280
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2281
|
+
className: cls("emergency-details__card"),
|
|
2282
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2283
|
+
className: cls("emergency-details__card-header"),
|
|
2284
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
2285
|
+
className: cls("emergency-details__card-title"),
|
|
2286
|
+
children: detailsCopy.summaryTitle
|
|
2287
|
+
})
|
|
2288
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
2289
|
+
className: cls("emergency-details__card-body"),
|
|
2290
|
+
children: balance ? /* @__PURE__ */ jsx("p", {
|
|
2291
|
+
className: cls("emergency-details__balance"),
|
|
2292
|
+
children: /* @__PURE__ */ jsx(AlertBalanceSentence, {
|
|
2293
|
+
messages: balance.messages,
|
|
2294
|
+
responders: balance.responders
|
|
2295
|
+
})
|
|
2296
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
2297
|
+
className: cls("emergency-details__empty"),
|
|
2298
|
+
children: detailsCopy.summaryEmpty
|
|
2299
|
+
})
|
|
2300
|
+
})]
|
|
2301
|
+
})
|
|
2302
|
+
}), /* @__PURE__ */ jsxs("section", {
|
|
2303
|
+
className: cls("emergency-details__section"),
|
|
2304
|
+
"aria-label": detailsCopy.historyTitle,
|
|
2305
|
+
children: [
|
|
2306
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2307
|
+
className: cls("emergency-details__heading"),
|
|
2308
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
2309
|
+
className: cls("emergency-details__title"),
|
|
2310
|
+
children: detailsCopy.historyTitle
|
|
2311
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2312
|
+
className: cls("emergency-details__subtitle"),
|
|
2313
|
+
children: detailsCopy.historySubtitle
|
|
2314
|
+
})]
|
|
2315
|
+
}),
|
|
2316
|
+
preview.length > 0 ? /* @__PURE__ */ jsx("ul", {
|
|
2317
|
+
className: cls("emergency-details__list"),
|
|
2318
|
+
children: preview.map((entry) => /* @__PURE__ */ jsx(AlertHistoryRow, {
|
|
2319
|
+
entry,
|
|
2320
|
+
locale,
|
|
2321
|
+
onOpen: (messageId) => navigate("message-detail", { messageId })
|
|
2322
|
+
}, entry.id))
|
|
2323
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
2324
|
+
className: cls("emergency-details__empty"),
|
|
2325
|
+
children: detailsCopy.historyEmpty
|
|
2326
|
+
}),
|
|
2327
|
+
/* @__PURE__ */ jsx("button", {
|
|
2328
|
+
type: "button",
|
|
2329
|
+
className: cls("emergency-details__history-button"),
|
|
2330
|
+
onClick: () => navigate("history"),
|
|
2331
|
+
children: detailsCopy.viewHistory
|
|
2332
|
+
})
|
|
2333
|
+
]
|
|
2334
|
+
})]
|
|
1955
2335
|
})
|
|
1956
2336
|
});
|
|
1957
2337
|
}
|
|
1958
2338
|
|
|
1959
|
-
// src/react/screens/
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
2339
|
+
// src/react/screens/history/index.tsx
|
|
2340
|
+
function HistoryScreen({ titleId, locale, onClose }) {
|
|
2341
|
+
const { back, navigate } = useScreenNavigation();
|
|
2342
|
+
const listId = useId();
|
|
2343
|
+
const [expanded, setExpanded] = useState(false);
|
|
2344
|
+
const entries = sortedHistoryEntries();
|
|
2345
|
+
const hiddenCount = Math.max(0, entries.length - 2);
|
|
2346
|
+
const visibleEntries = expanded ? entries : entries.slice(0, 2);
|
|
2347
|
+
const showMore = hiddenCount > 0 && !expanded;
|
|
2348
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2349
|
+
screen: "history",
|
|
2350
|
+
titleId,
|
|
2351
|
+
bodyClassName: "emergency-panel__body--history",
|
|
2352
|
+
header: /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
2353
|
+
titleId,
|
|
2354
|
+
title: historyCopy.title,
|
|
2355
|
+
onClose: () => onClose("close"),
|
|
2356
|
+
onBack: back
|
|
2357
|
+
}),
|
|
2358
|
+
footer: null,
|
|
2359
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2360
|
+
className: cls("emergency-history"),
|
|
2361
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2362
|
+
className: cls("emergency-history__heading"),
|
|
2363
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
2364
|
+
className: cls("emergency-history__title"),
|
|
2365
|
+
children: historyCopy.title
|
|
2366
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2367
|
+
className: cls("emergency-history__subtitle"),
|
|
2368
|
+
children: historyCopy.subtitle
|
|
2369
|
+
})]
|
|
2370
|
+
}), entries.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("ul", {
|
|
2371
|
+
className: cls("emergency-history__list"),
|
|
2372
|
+
id: listId,
|
|
2373
|
+
children: visibleEntries.map((entry) => /* @__PURE__ */ jsx(AlertHistoryRow, {
|
|
2374
|
+
entry,
|
|
2375
|
+
locale,
|
|
2376
|
+
onOpen: (messageId) => navigate("message-detail", { messageId })
|
|
2377
|
+
}, entry.id))
|
|
2378
|
+
}), showMore ? /* @__PURE__ */ jsx("button", {
|
|
2379
|
+
type: "button",
|
|
2380
|
+
className: cls("emergency-history__more"),
|
|
2381
|
+
"aria-expanded": expanded,
|
|
2382
|
+
"aria-controls": listId,
|
|
2383
|
+
onClick: () => setExpanded(true),
|
|
2384
|
+
children: historyCopy.showMore
|
|
2385
|
+
}) : null] }) : /* @__PURE__ */ jsx("p", {
|
|
2386
|
+
className: cls("emergency-history__empty"),
|
|
2387
|
+
children: historyCopy.empty
|
|
2388
|
+
})]
|
|
2389
|
+
})
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
1964
2392
|
|
|
1965
|
-
// src/react/screens/
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2393
|
+
// src/react/screens/message-detail/index.tsx
|
|
2394
|
+
const messageDetailCopy = {
|
|
2395
|
+
title: "Detalle del mensaje",
|
|
2396
|
+
dateTime: "Fecha y hora",
|
|
2397
|
+
kind: "Tipo",
|
|
2398
|
+
responders: "Contactos de emergencia",
|
|
2399
|
+
messages: "Mensajes restantes",
|
|
2400
|
+
rounds: "Rondas de avisos",
|
|
2401
|
+
notFound: "No encontramos este mensaje. Puede que ya no esté disponible en tu historial.",
|
|
2402
|
+
backToHistory: "Volver al historial"
|
|
2403
|
+
};
|
|
2404
|
+
function MessageDetailScreen({ titleId, locale, onClose }) {
|
|
2405
|
+
const { back, params, replace } = useScreenNavigation();
|
|
2406
|
+
const entry = historyEntryById(params.messageId);
|
|
2407
|
+
const header = /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
1970
2408
|
titleId,
|
|
1971
|
-
title:
|
|
1972
|
-
description: detailsCopy.description,
|
|
2409
|
+
title: messageDetailCopy.title,
|
|
1973
2410
|
onClose: () => onClose("close"),
|
|
1974
2411
|
onBack: back
|
|
1975
2412
|
});
|
|
2413
|
+
if (!entry) return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2414
|
+
screen: "message-detail",
|
|
2415
|
+
titleId,
|
|
2416
|
+
bodyClassName: "emergency-panel__body--message-detail",
|
|
2417
|
+
header,
|
|
2418
|
+
footer: null,
|
|
2419
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2420
|
+
className: cls("emergency-message-detail__empty"),
|
|
2421
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2422
|
+
className: cls("emergency-message-detail__empty-text"),
|
|
2423
|
+
children: messageDetailCopy.notFound
|
|
2424
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2425
|
+
type: "button",
|
|
2426
|
+
className: cls("emergency-message-detail__back"),
|
|
2427
|
+
onClick: () => replace("history"),
|
|
2428
|
+
children: messageDetailCopy.backToHistory
|
|
2429
|
+
})]
|
|
2430
|
+
})
|
|
2431
|
+
});
|
|
2432
|
+
const rounds = historyEntryRounds(entry);
|
|
2433
|
+
return /* @__PURE__ */ jsx(EmergencyScreenLayout, {
|
|
2434
|
+
screen: "message-detail",
|
|
2435
|
+
titleId,
|
|
2436
|
+
bodyClassName: "emergency-panel__body--message-detail",
|
|
2437
|
+
header,
|
|
2438
|
+
footer: null,
|
|
2439
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2440
|
+
className: cls("emergency-message-detail"),
|
|
2441
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2442
|
+
className: cls("emergency-message-detail__card"),
|
|
2443
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2444
|
+
className: cls("emergency-message-detail__card-header"),
|
|
2445
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
2446
|
+
className: cls("emergency-message-detail__card-title"),
|
|
2447
|
+
children: messageDetailCopy.title
|
|
2448
|
+
})
|
|
2449
|
+
}), /* @__PURE__ */ jsxs("dl", {
|
|
2450
|
+
className: cls("emergency-message-detail__list"),
|
|
2451
|
+
children: [
|
|
2452
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2453
|
+
className: cls("emergency-message-detail__row"),
|
|
2454
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
2455
|
+
className: cls("emergency-message-detail__label"),
|
|
2456
|
+
children: messageDetailCopy.dateTime
|
|
2457
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2458
|
+
className: cls("emergency-message-detail__value"),
|
|
2459
|
+
children: formatDateTime(entry.occurredAt, locale)
|
|
2460
|
+
})]
|
|
2461
|
+
}),
|
|
2462
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2463
|
+
className: cls("emergency-message-detail__row"),
|
|
2464
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
2465
|
+
className: cls("emergency-message-detail__label"),
|
|
2466
|
+
children: messageDetailCopy.kind
|
|
2467
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2468
|
+
className: cls("emergency-message-detail__value"),
|
|
2469
|
+
children: historyCopy.kindLabels[entry.kind]
|
|
2470
|
+
})]
|
|
2471
|
+
}),
|
|
2472
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2473
|
+
className: cls("emergency-message-detail__row"),
|
|
2474
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
2475
|
+
className: cls("emergency-message-detail__label"),
|
|
2476
|
+
children: messageDetailCopy.responders
|
|
2477
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2478
|
+
className: cls("emergency-message-detail__value"),
|
|
2479
|
+
children: formatContacts(entry.responders)
|
|
2480
|
+
})]
|
|
2481
|
+
}),
|
|
2482
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2483
|
+
className: cls("emergency-message-detail__row"),
|
|
2484
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
2485
|
+
className: cls("emergency-message-detail__label"),
|
|
2486
|
+
children: messageDetailCopy.messages
|
|
2487
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2488
|
+
className: cls("emergency-message-detail__value"),
|
|
2489
|
+
children: formatMessages(entry.remainingMessages)
|
|
2490
|
+
})]
|
|
2491
|
+
}),
|
|
2492
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2493
|
+
className: cls("emergency-message-detail__row"),
|
|
2494
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
2495
|
+
className: cls("emergency-message-detail__label"),
|
|
2496
|
+
children: messageDetailCopy.rounds
|
|
2497
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
2498
|
+
className: cls("emergency-message-detail__value"),
|
|
2499
|
+
children: formatHistoryRounds(rounds)
|
|
2500
|
+
})]
|
|
2501
|
+
})
|
|
2502
|
+
]
|
|
2503
|
+
})]
|
|
2504
|
+
})
|
|
2505
|
+
})
|
|
2506
|
+
});
|
|
1976
2507
|
}
|
|
1977
2508
|
|
|
1978
2509
|
// src/core/phone.ts
|
|
@@ -1997,9 +2528,9 @@ function toVenezuelanE164(input) {
|
|
|
1997
2528
|
|
|
1998
2529
|
// src/react/components/EmergencyFooter/index.tsx
|
|
1999
2530
|
function EmergencyFooter(props) {
|
|
2000
|
-
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2531
|
+
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, square = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2001
2532
|
if (active) return /* @__PURE__ */ jsx("footer", {
|
|
2002
|
-
className: cls("emergency-panel__footer"),
|
|
2533
|
+
className: cls("emergency-panel__footer", square && "emergency-panel__footer--square"),
|
|
2003
2534
|
children: /* @__PURE__ */ jsxs("button", {
|
|
2004
2535
|
type: "button",
|
|
2005
2536
|
className: cls("emergency-status__abort-btn"),
|
|
@@ -2010,7 +2541,7 @@ function EmergencyFooter(props) {
|
|
|
2010
2541
|
})
|
|
2011
2542
|
});
|
|
2012
2543
|
return /* @__PURE__ */ jsxs("footer", {
|
|
2013
|
-
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single"),
|
|
2544
|
+
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single", square && "emergency-panel__footer--square"),
|
|
2014
2545
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
2015
2546
|
type: "button",
|
|
2016
2547
|
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
@@ -2285,9 +2816,9 @@ function EmergencyError({ error, onRetry }) {
|
|
|
2285
2816
|
|
|
2286
2817
|
// src/react/components/EmergencyFeedback/index.tsx
|
|
2287
2818
|
/** Time the feedback stays on screen before dismissing itself. */
|
|
2288
|
-
const FEEDBACK_AUTO_DISMISS_MS =
|
|
2819
|
+
const FEEDBACK_AUTO_DISMISS_MS = 12e3;
|
|
2289
2820
|
/** Time the geolocation feedback stays on screen before dismissing itself. */
|
|
2290
|
-
const GEOLOCATION_FEEDBACK_AUTO_DISMISS_MS =
|
|
2821
|
+
const GEOLOCATION_FEEDBACK_AUTO_DISMISS_MS = 12e3;
|
|
2291
2822
|
function geolocationText(error) {
|
|
2292
2823
|
switch (error.code) {
|
|
2293
2824
|
case "geolocation_denied": return copy.geolocationDenied;
|
|
@@ -2373,32 +2904,6 @@ function EmergencyLoading() {
|
|
|
2373
2904
|
});
|
|
2374
2905
|
}
|
|
2375
2906
|
|
|
2376
|
-
// src/core/date.ts
|
|
2377
|
-
/**
|
|
2378
|
-
* Formats a timestamp as `h:mmam` or `h:mmpm` in local time, without a space
|
|
2379
|
-
* or leading zero.
|
|
2380
|
-
*/
|
|
2381
|
-
function formatShortTime(value, locale = "es") {
|
|
2382
|
-
if (value === null) return "";
|
|
2383
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
2384
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
2385
|
-
const parts = new Intl.DateTimeFormat(locale, {
|
|
2386
|
-
hour: "numeric",
|
|
2387
|
-
minute: "2-digit",
|
|
2388
|
-
hour12: true
|
|
2389
|
-
}).formatToParts(date);
|
|
2390
|
-
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2391
|
-
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2392
|
-
const dayPeriod = parts.find((part) => part.type === "dayPeriod")?.value;
|
|
2393
|
-
if (!hour || !minute) return "";
|
|
2394
|
-
return `${hour}:${minute}${normalizePeriod(dayPeriod)}`;
|
|
2395
|
-
}
|
|
2396
|
-
function normalizePeriod(dayPeriod) {
|
|
2397
|
-
if (!dayPeriod) return "";
|
|
2398
|
-
if (/^p/i.test(dayPeriod)) return "pm";
|
|
2399
|
-
return /^a/i.test(dayPeriod) ? "am" : "";
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
2907
|
// src/react/components/StatusIcon/index.tsx
|
|
2403
2908
|
/**
|
|
2404
2909
|
* Explicit responder state model:
|
|
@@ -2559,23 +3064,9 @@ function formatRounds(rounds) {
|
|
|
2559
3064
|
return `${rounds} ${roundsUnit(rounds)}`;
|
|
2560
3065
|
}
|
|
2561
3066
|
|
|
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
3067
|
// src/react/screens/primary/Availability.tsx
|
|
2577
3068
|
function Availability({ onRecharge, onDetails }) {
|
|
2578
|
-
const rounds = calculateRounds(
|
|
3069
|
+
const rounds = calculateRounds(27, 3);
|
|
2579
3070
|
return /* @__PURE__ */ jsxs("section", {
|
|
2580
3071
|
className: cls("emergency-availability"),
|
|
2581
3072
|
"aria-label": primaryCopy.availabilityTitle,
|
|
@@ -2617,23 +3108,30 @@ function Availability({ onRecharge, onDetails }) {
|
|
|
2617
3108
|
|
|
2618
3109
|
// src/react/components/EmergencyBody/index.tsx
|
|
2619
3110
|
function EmergencyBody(props) {
|
|
2620
|
-
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback, onRecharge, onDetails } = props;
|
|
3111
|
+
const { active, responders, refreshing, loading, locale, geolocationError, activationError, error, abortResult, simulationResult, onRefresh, onRetry, onDismissFeedback, onRecharge, onDetails, onClose } = props;
|
|
2621
3112
|
const showingFeedback = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
2622
3113
|
const idle = !active && !loading && !showingFeedback && error === null;
|
|
2623
3114
|
return /* @__PURE__ */ jsxs("div", {
|
|
2624
3115
|
className: cls("emergency-panel__body-split", idle && "emergency-panel__body-split--with-availability"),
|
|
2625
|
-
children: [idle ? /* @__PURE__ */
|
|
3116
|
+
children: [idle ? /* @__PURE__ */ jsx("div", {
|
|
2626
3117
|
className: cls("emergency-panel__availability-region"),
|
|
2627
3118
|
children: /* @__PURE__ */ jsx(Availability, {
|
|
2628
3119
|
onRecharge,
|
|
2629
3120
|
onDetails
|
|
2630
3121
|
})
|
|
2631
|
-
}), /* @__PURE__ */
|
|
2632
|
-
className: cls("emergency-
|
|
2633
|
-
"aria-hidden": "true"
|
|
2634
|
-
})] }) : null, /* @__PURE__ */ jsxs("div", {
|
|
2635
|
-
className: cls("emergency-panel__prompt-region"),
|
|
3122
|
+
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
3123
|
+
className: cls("emergency-panel__prompt-region", idle && "emergency-panel__prompt-region--idle"),
|
|
2636
3124
|
children: [
|
|
3125
|
+
idle ? /* @__PURE__ */ jsx("button", {
|
|
3126
|
+
type: "button",
|
|
3127
|
+
className: cls("emergency-panel__close-button", "emergency-panel__close-button--prompt"),
|
|
3128
|
+
"aria-label": copy.close,
|
|
3129
|
+
onClick: onClose,
|
|
3130
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
3131
|
+
name: "close",
|
|
3132
|
+
className: cls("emergency-panel__close-icon")
|
|
3133
|
+
})
|
|
3134
|
+
}) : null,
|
|
2637
3135
|
!loading && !showingFeedback ? /* @__PURE__ */ jsx("div", {
|
|
2638
3136
|
className: cls("sos-img__container"),
|
|
2639
3137
|
id: "sos-img__container",
|
|
@@ -2677,12 +3175,12 @@ function PrimaryScreen(props) {
|
|
|
2677
3175
|
const { navigate } = useScreenNavigation();
|
|
2678
3176
|
const idle = !props.active && !props.loading && !props.feedbackVisible && props.error === null;
|
|
2679
3177
|
const needsContactPhone = props.contactPhone === null;
|
|
2680
|
-
return /* @__PURE__ */
|
|
3178
|
+
return /* @__PURE__ */ jsxs(EmergencyScreenLayout, {
|
|
2681
3179
|
screen: "primary",
|
|
2682
3180
|
titleId: props.titleId,
|
|
2683
|
-
|
|
3181
|
+
bodyClassName: idle ? "emergency-panel__body--primary" : void 0,
|
|
3182
|
+
header: idle ? null : /* @__PURE__ */ jsx(EmergencyHeader, {
|
|
2684
3183
|
titleId: props.titleId,
|
|
2685
|
-
variant: idle ? "close-only" : "default",
|
|
2686
3184
|
onClose: () => props.onClose("close")
|
|
2687
3185
|
}),
|
|
2688
3186
|
footer: /* @__PURE__ */ jsx(EmergencyFooter, {
|
|
@@ -2690,11 +3188,17 @@ function PrimaryScreen(props) {
|
|
|
2690
3188
|
busy: props.busy,
|
|
2691
3189
|
aborting: props.aborting,
|
|
2692
3190
|
feedbackVisible: props.feedbackVisible,
|
|
3191
|
+
square: idle,
|
|
2693
3192
|
onActivate: needsContactPhone ? () => navigate("pre-emergency") : props.onActivate,
|
|
2694
3193
|
onRequestAbort: props.onRequestAbort,
|
|
2695
3194
|
onRequestSimulation: props.onRequestSimulation
|
|
2696
3195
|
}),
|
|
2697
|
-
children: /* @__PURE__ */ jsx(
|
|
3196
|
+
children: [idle ? /* @__PURE__ */ jsx("h2", {
|
|
3197
|
+
className: cls("emergency-panel__sr-only"),
|
|
3198
|
+
id: props.titleId,
|
|
3199
|
+
tabIndex: -1,
|
|
3200
|
+
children: copy.panelLabel
|
|
3201
|
+
}) : null, /* @__PURE__ */ jsx(EmergencyBody, {
|
|
2698
3202
|
active: props.active,
|
|
2699
3203
|
responders: props.responders,
|
|
2700
3204
|
refreshing: props.refreshing,
|
|
@@ -2709,8 +3213,9 @@ function PrimaryScreen(props) {
|
|
|
2709
3213
|
onRetry: props.onRetry,
|
|
2710
3214
|
onDismissFeedback: props.onDismissFeedback,
|
|
2711
3215
|
onRecharge: () => navigate("pricing"),
|
|
2712
|
-
onDetails: () => navigate("details")
|
|
2713
|
-
|
|
3216
|
+
onDetails: () => navigate("details"),
|
|
3217
|
+
onClose: () => props.onClose("close")
|
|
3218
|
+
})]
|
|
2714
3219
|
});
|
|
2715
3220
|
}
|
|
2716
3221
|
|
|
@@ -2883,7 +3388,9 @@ const screenRegistry = {
|
|
|
2883
3388
|
primary: PrimaryScreen,
|
|
2884
3389
|
"pre-emergency": PreEmergencyScreen,
|
|
2885
3390
|
pricing: PricingScreen,
|
|
2886
|
-
details: DetailsScreen
|
|
3391
|
+
details: DetailsScreen,
|
|
3392
|
+
history: HistoryScreen,
|
|
3393
|
+
"message-detail": MessageDetailScreen
|
|
2887
3394
|
};
|
|
2888
3395
|
|
|
2889
3396
|
// src/react/components/EmergencyPanel/index.tsx
|
|
@@ -2912,7 +3419,7 @@ function ActiveScreen({ screenProps, open, forcePrimary }) {
|
|
|
2912
3419
|
return /* @__PURE__ */ jsx(Screen, { ...screenProps });
|
|
2913
3420
|
}
|
|
2914
3421
|
function EmergencyPanel(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;
|
|
3422
|
+
const { open, theme, active, busy, aborting, simulating, 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;
|
|
2916
3423
|
const titleId = useId();
|
|
2917
3424
|
const dialogRef = useRef(null);
|
|
2918
3425
|
const feedbackVisible = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
@@ -2964,6 +3471,7 @@ function EmergencyPanel(props) {
|
|
|
2964
3471
|
}) }), /* @__PURE__ */ jsx(EmergencyConfirmation, {
|
|
2965
3472
|
abortConfirmation,
|
|
2966
3473
|
simulationConfirmation,
|
|
3474
|
+
loading: abortConfirmation ? aborting : simulating,
|
|
2967
3475
|
onCancelAbort,
|
|
2968
3476
|
onCancelSimulation,
|
|
2969
3477
|
onConfirmAbort,
|
|
@@ -3073,6 +3581,7 @@ function EmergencyRoot() {
|
|
|
3073
3581
|
active,
|
|
3074
3582
|
busy,
|
|
3075
3583
|
aborting: phase.kind === "aborting",
|
|
3584
|
+
simulating: phase.kind === "simulating",
|
|
3076
3585
|
loading: phase.kind === "initializing" || phase.kind === "activating",
|
|
3077
3586
|
locale: config.locale ?? "es",
|
|
3078
3587
|
responders,
|