@dataemergencia/emergency 0.5.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/dist/iife/emergency.iife.global.js +2 -2
- package/dist/index.cjs +584 -95
- package/dist/index.js +584 -95
- package/dist/styles.css +272 -16
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1576,6 +1576,12 @@ var panel_module_default = {
|
|
|
1576
1576
|
"de-emergency-slide-in": "de-emergency-de-emergency-slide-in-nA5Hwa",
|
|
1577
1577
|
"de-emergency-slide-up": "de-emergency-de-emergency-slide-up-nA5Hwa",
|
|
1578
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",
|
|
1579
1585
|
"emergency-availability": "de-emergency-emergency-availability-nA5Hwa",
|
|
1580
1586
|
"emergency-availability__card": "de-emergency-emergency-availability__card-nA5Hwa",
|
|
1581
1587
|
"emergency-availability__card-body": "de-emergency-emergency-availability__card-body-nA5Hwa",
|
|
@@ -1599,6 +1605,19 @@ var panel_module_default = {
|
|
|
1599
1605
|
"emergency-confirmation__icon--information": "de-emergency-emergency-confirmation__icon--information-nA5Hwa",
|
|
1600
1606
|
"emergency-confirmation__text": "de-emergency-emergency-confirmation__text-nA5Hwa",
|
|
1601
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",
|
|
1602
1621
|
"emergency-error-message": "de-emergency-emergency-error-message-nA5Hwa",
|
|
1603
1622
|
"emergency-error-message__container": "de-emergency-emergency-error-message__container-nA5Hwa",
|
|
1604
1623
|
"emergency-error-message__content": "de-emergency-emergency-error-message__content-nA5Hwa",
|
|
@@ -1609,11 +1628,33 @@ var panel_module_default = {
|
|
|
1609
1628
|
"emergency-error-message__title": "de-emergency-emergency-error-message__title-nA5Hwa",
|
|
1610
1629
|
"emergency-feedback": "de-emergency-emergency-feedback-nA5Hwa",
|
|
1611
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",
|
|
1612
1649
|
"emergency-panel": "de-emergency-emergency-panel-nA5Hwa",
|
|
1613
1650
|
"emergency-panel__availability-region": "de-emergency-emergency-panel__availability-region-nA5Hwa",
|
|
1614
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",
|
|
1615
1655
|
"emergency-panel__body--pre-emergency": "de-emergency-emergency-panel__body--pre-emergency-nA5Hwa",
|
|
1616
1656
|
"emergency-panel__body--pricing": "de-emergency-emergency-panel__body--pricing-nA5Hwa",
|
|
1657
|
+
"emergency-panel__body--primary": "de-emergency-emergency-panel__body--primary-nA5Hwa",
|
|
1617
1658
|
"emergency-panel__body-split": "de-emergency-emergency-panel__body-split-nA5Hwa",
|
|
1618
1659
|
"emergency-panel__body-split--with-availability": "de-emergency-emergency-panel__body-split--with-availability-nA5Hwa",
|
|
1619
1660
|
"emergency-panel__body-title": "de-emergency-emergency-panel__body-title-nA5Hwa",
|
|
@@ -1622,13 +1663,14 @@ var panel_module_default = {
|
|
|
1622
1663
|
"emergency-panel__button--emergency": "de-emergency-emergency-panel__button--emergency-nA5Hwa",
|
|
1623
1664
|
"emergency-panel__button--simulation": "de-emergency-emergency-panel__button--simulation-nA5Hwa",
|
|
1624
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",
|
|
1625
1667
|
"emergency-panel__close-icon": "de-emergency-emergency-panel__close-icon-nA5Hwa",
|
|
1626
|
-
"emergency-panel__divider": "de-emergency-emergency-panel__divider-nA5Hwa",
|
|
1627
1668
|
"emergency-panel__footer": "de-emergency-emergency-panel__footer-nA5Hwa",
|
|
1628
1669
|
"emergency-panel__footer--single": "de-emergency-emergency-panel__footer--single-nA5Hwa",
|
|
1670
|
+
"emergency-panel__footer--square": "de-emergency-emergency-panel__footer--square-nA5Hwa",
|
|
1629
1671
|
"emergency-panel__header": "de-emergency-emergency-panel__header-nA5Hwa",
|
|
1630
|
-
"emergency-panel__header--close-only": "de-emergency-emergency-panel__header--close-only-nA5Hwa",
|
|
1631
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",
|
|
1632
1674
|
"emergency-panel__sr-only": "de-emergency-emergency-panel__sr-only-nA5Hwa",
|
|
1633
1675
|
"emergency-panel__title": "de-emergency-emergency-panel__title-nA5Hwa",
|
|
1634
1676
|
"emergency-panel--hidden": "de-emergency-emergency-panel--hidden-nA5Hwa",
|
|
@@ -1847,18 +1889,27 @@ function useFocusTrap(ref, active) {
|
|
|
1847
1889
|
}
|
|
1848
1890
|
|
|
1849
1891
|
// src/react/screens/context.tsx
|
|
1892
|
+
const EMPTY_PARAMS = {};
|
|
1850
1893
|
const EmergencyScreenContext = (0, react.createContext)(null);
|
|
1894
|
+
function paramsEqual(first, second) {
|
|
1895
|
+
return first.messageId === second.messageId;
|
|
1896
|
+
}
|
|
1851
1897
|
function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
1852
1898
|
const [state, setState] = (0, react.useState)({
|
|
1853
1899
|
screen: initialScreen,
|
|
1900
|
+
params: EMPTY_PARAMS,
|
|
1854
1901
|
history: []
|
|
1855
1902
|
});
|
|
1856
|
-
const navigate = (0, react.useCallback)((next) => {
|
|
1903
|
+
const navigate = (0, react.useCallback)((next, params = EMPTY_PARAMS) => {
|
|
1857
1904
|
setState((current) => {
|
|
1858
|
-
if (current.screen === next) return current;
|
|
1905
|
+
if (current.screen === next && paramsEqual(current.params, params)) return current;
|
|
1859
1906
|
return {
|
|
1860
1907
|
screen: next,
|
|
1861
|
-
|
|
1908
|
+
params,
|
|
1909
|
+
history: [...current.history, {
|
|
1910
|
+
screen: current.screen,
|
|
1911
|
+
params: current.params
|
|
1912
|
+
}]
|
|
1862
1913
|
};
|
|
1863
1914
|
});
|
|
1864
1915
|
}, []);
|
|
@@ -1867,31 +1918,47 @@ function EmergencyScreenProvider({ children, initialScreen = "primary" }) {
|
|
|
1867
1918
|
const previous = current.history.at(-1);
|
|
1868
1919
|
if (!previous) return current;
|
|
1869
1920
|
return {
|
|
1870
|
-
screen: previous,
|
|
1921
|
+
screen: previous.screen,
|
|
1922
|
+
params: previous.params,
|
|
1871
1923
|
history: current.history.slice(0, -1)
|
|
1872
1924
|
};
|
|
1873
1925
|
});
|
|
1874
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
|
+
}, []);
|
|
1875
1937
|
const reset = (0, react.useCallback)(() => {
|
|
1876
1938
|
setState((current) => {
|
|
1877
|
-
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;
|
|
1878
1940
|
return {
|
|
1879
1941
|
screen: "primary",
|
|
1942
|
+
params: EMPTY_PARAMS,
|
|
1880
1943
|
history: []
|
|
1881
1944
|
};
|
|
1882
1945
|
});
|
|
1883
1946
|
}, []);
|
|
1884
1947
|
const value = (0, react.useMemo)(() => ({
|
|
1885
1948
|
screen: state.screen,
|
|
1949
|
+
params: state.params,
|
|
1886
1950
|
history: state.history,
|
|
1887
1951
|
navigate,
|
|
1952
|
+
replace,
|
|
1888
1953
|
back,
|
|
1889
1954
|
reset
|
|
1890
1955
|
}), [
|
|
1891
1956
|
back,
|
|
1892
1957
|
navigate,
|
|
1958
|
+
replace,
|
|
1893
1959
|
reset,
|
|
1894
1960
|
state.history,
|
|
1961
|
+
state.params,
|
|
1895
1962
|
state.screen
|
|
1896
1963
|
]);
|
|
1897
1964
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenContext.Provider, {
|
|
@@ -1906,12 +1973,11 @@ function useScreenNavigation() {
|
|
|
1906
1973
|
}
|
|
1907
1974
|
|
|
1908
1975
|
// src/react/components/EmergencyHeader/index.tsx
|
|
1909
|
-
function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack
|
|
1910
|
-
const closeOnly = variant === "close-only";
|
|
1976
|
+
function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack }) {
|
|
1911
1977
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
1912
|
-
className: cls("emergency-panel__header"
|
|
1978
|
+
className: cls("emergency-panel__header"),
|
|
1913
1979
|
children: [
|
|
1914
|
-
onBack
|
|
1980
|
+
onBack ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1915
1981
|
type: "button",
|
|
1916
1982
|
className: cls("emergency-panel__close-button"),
|
|
1917
1983
|
"aria-label": copy.back,
|
|
@@ -1922,7 +1988,7 @@ function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack, va
|
|
|
1922
1988
|
})
|
|
1923
1989
|
}) : null,
|
|
1924
1990
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1925
|
-
className: cls("emergency-panel__title"
|
|
1991
|
+
className: cls("emergency-panel__title"),
|
|
1926
1992
|
id: titleId,
|
|
1927
1993
|
tabIndex: -1,
|
|
1928
1994
|
children: title
|
|
@@ -1941,6 +2007,245 @@ function EmergencyHeader({ titleId, onClose, title = copy.panelLabel, onBack, va
|
|
|
1941
2007
|
});
|
|
1942
2008
|
}
|
|
1943
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
|
+
|
|
1944
2249
|
// src/react/screens/layout.tsx
|
|
1945
2250
|
/**
|
|
1946
2251
|
* Shared shell for every screen. The wrapper is `display: contents` so the
|
|
@@ -1962,46 +2267,254 @@ function EmergencyScreenLayout({ screen, titleId, header, footer, children, body
|
|
|
1962
2267
|
});
|
|
1963
2268
|
}
|
|
1964
2269
|
|
|
1965
|
-
// src/react/screens/
|
|
1966
|
-
|
|
1967
|
-
|
|
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);
|
|
1968
2275
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyScreenLayout, {
|
|
1969
|
-
screen,
|
|
2276
|
+
screen: "details",
|
|
1970
2277
|
titleId,
|
|
2278
|
+
bodyClassName: "emergency-panel__body--details",
|
|
1971
2279
|
header: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
1972
2280
|
titleId,
|
|
1973
|
-
title,
|
|
1974
|
-
onClose,
|
|
1975
|
-
onBack
|
|
2281
|
+
title: detailsCopy.title,
|
|
2282
|
+
onClose: () => onClose("close"),
|
|
2283
|
+
onBack: back
|
|
1976
2284
|
}),
|
|
1977
2285
|
footer: null,
|
|
1978
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1979
|
-
className: cls("emergency-
|
|
1980
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1981
|
-
className: cls("emergency-
|
|
1982
|
-
|
|
1983
|
-
|
|
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
|
+
})]
|
|
1984
2346
|
})
|
|
1985
2347
|
});
|
|
1986
2348
|
}
|
|
1987
2349
|
|
|
1988
|
-
// src/react/screens/
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
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
|
+
}
|
|
1993
2403
|
|
|
1994
|
-
// src/react/screens/
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
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, {
|
|
1999
2419
|
titleId,
|
|
2000
|
-
title:
|
|
2001
|
-
description: detailsCopy.description,
|
|
2420
|
+
title: messageDetailCopy.title,
|
|
2002
2421
|
onClose: () => onClose("close"),
|
|
2003
2422
|
onBack: back
|
|
2004
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,
|
|
2450
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
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
|
+
})]
|
|
2515
|
+
})
|
|
2516
|
+
})
|
|
2517
|
+
});
|
|
2005
2518
|
}
|
|
2006
2519
|
|
|
2007
2520
|
// src/core/phone.ts
|
|
@@ -2026,9 +2539,9 @@ function toVenezuelanE164(input) {
|
|
|
2026
2539
|
|
|
2027
2540
|
// src/react/components/EmergencyFooter/index.tsx
|
|
2028
2541
|
function EmergencyFooter(props) {
|
|
2029
|
-
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2542
|
+
const { active, busy, aborting, feedbackVisible, showSimulation = true, emergencyDisabled = false, square = false, onActivate, onRequestAbort, onRequestSimulation } = props;
|
|
2030
2543
|
if (active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
2031
|
-
className: cls("emergency-panel__footer"),
|
|
2544
|
+
className: cls("emergency-panel__footer", square && "emergency-panel__footer--square"),
|
|
2032
2545
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2033
2546
|
type: "button",
|
|
2034
2547
|
className: cls("emergency-status__abort-btn"),
|
|
@@ -2039,7 +2552,7 @@ function EmergencyFooter(props) {
|
|
|
2039
2552
|
})
|
|
2040
2553
|
});
|
|
2041
2554
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
2042
|
-
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single"),
|
|
2555
|
+
className: cls("emergency-panel__footer", !showSimulation && "emergency-panel__footer--single", square && "emergency-panel__footer--square"),
|
|
2043
2556
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2044
2557
|
type: "button",
|
|
2045
2558
|
className: cls("emergency-panel__button", "emergency-panel__button--emergency"),
|
|
@@ -2402,32 +2915,6 @@ function EmergencyLoading() {
|
|
|
2402
2915
|
});
|
|
2403
2916
|
}
|
|
2404
2917
|
|
|
2405
|
-
// src/core/date.ts
|
|
2406
|
-
/**
|
|
2407
|
-
* Formats a timestamp as `h:mmam` or `h:mmpm` in local time, without a space
|
|
2408
|
-
* or leading zero.
|
|
2409
|
-
*/
|
|
2410
|
-
function formatShortTime(value, locale = "es") {
|
|
2411
|
-
if (value === null) return "";
|
|
2412
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
2413
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
2414
|
-
const parts = new Intl.DateTimeFormat(locale, {
|
|
2415
|
-
hour: "numeric",
|
|
2416
|
-
minute: "2-digit",
|
|
2417
|
-
hour12: true
|
|
2418
|
-
}).formatToParts(date);
|
|
2419
|
-
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
2420
|
-
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
2421
|
-
const dayPeriod = parts.find((part) => part.type === "dayPeriod")?.value;
|
|
2422
|
-
if (!hour || !minute) return "";
|
|
2423
|
-
return `${hour}:${minute}${normalizePeriod(dayPeriod)}`;
|
|
2424
|
-
}
|
|
2425
|
-
function normalizePeriod(dayPeriod) {
|
|
2426
|
-
if (!dayPeriod) return "";
|
|
2427
|
-
if (/^p/i.test(dayPeriod)) return "pm";
|
|
2428
|
-
return /^a/i.test(dayPeriod) ? "am" : "";
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
2918
|
// src/react/components/StatusIcon/index.tsx
|
|
2432
2919
|
/**
|
|
2433
2920
|
* Explicit responder state model:
|
|
@@ -2588,23 +3075,9 @@ function formatRounds(rounds) {
|
|
|
2588
3075
|
return `${rounds} ${roundsUnit(rounds)}`;
|
|
2589
3076
|
}
|
|
2590
3077
|
|
|
2591
|
-
// src/react/screens/primary/data.ts
|
|
2592
|
-
/**
|
|
2593
|
-
* Derives the remaining alert rounds from available messages and designated
|
|
2594
|
-
* responders. Invalid or non-positive input collapses to the minimum of one
|
|
2595
|
-
* round instead of producing `Infinity`, `NaN` or a negative value.
|
|
2596
|
-
*/
|
|
2597
|
-
function calculateRounds(messages, responders) {
|
|
2598
|
-
if (!Number.isFinite(messages) || !Number.isFinite(responders)) return 1;
|
|
2599
|
-
const availableMessages = Math.floor(messages);
|
|
2600
|
-
const designatedResponders = Math.floor(responders);
|
|
2601
|
-
if (availableMessages <= 0 || designatedResponders <= 0) return 1;
|
|
2602
|
-
return Math.max(1, Math.floor(availableMessages / designatedResponders));
|
|
2603
|
-
}
|
|
2604
|
-
|
|
2605
3078
|
// src/react/screens/primary/Availability.tsx
|
|
2606
3079
|
function Availability({ onRecharge, onDetails }) {
|
|
2607
|
-
const rounds = calculateRounds(
|
|
3080
|
+
const rounds = calculateRounds(27, 3);
|
|
2608
3081
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2609
3082
|
className: cls("emergency-availability"),
|
|
2610
3083
|
"aria-label": primaryCopy.availabilityTitle,
|
|
@@ -2646,23 +3119,30 @@ function Availability({ onRecharge, onDetails }) {
|
|
|
2646
3119
|
|
|
2647
3120
|
// src/react/components/EmergencyBody/index.tsx
|
|
2648
3121
|
function EmergencyBody(props) {
|
|
2649
|
-
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;
|
|
2650
3123
|
const showingFeedback = geolocationError !== null || Boolean(activationError) || abortResult !== null || simulationResult !== null;
|
|
2651
3124
|
const idle = !active && !loading && !showingFeedback && error === null;
|
|
2652
3125
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2653
3126
|
className: cls("emergency-panel__body-split", idle && "emergency-panel__body-split--with-availability"),
|
|
2654
|
-
children: [idle ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3127
|
+
children: [idle ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2655
3128
|
className: cls("emergency-panel__availability-region"),
|
|
2656
3129
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Availability, {
|
|
2657
3130
|
onRecharge,
|
|
2658
3131
|
onDetails
|
|
2659
3132
|
})
|
|
2660
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2661
|
-
className: cls("emergency-
|
|
2662
|
-
"aria-hidden": "true"
|
|
2663
|
-
})] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2664
|
-
className: cls("emergency-panel__prompt-region"),
|
|
3133
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3134
|
+
className: cls("emergency-panel__prompt-region", idle && "emergency-panel__prompt-region--idle"),
|
|
2665
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,
|
|
2666
3146
|
!loading && !showingFeedback ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2667
3147
|
className: cls("sos-img__container"),
|
|
2668
3148
|
id: "sos-img__container",
|
|
@@ -2706,12 +3186,12 @@ function PrimaryScreen(props) {
|
|
|
2706
3186
|
const { navigate } = useScreenNavigation();
|
|
2707
3187
|
const idle = !props.active && !props.loading && !props.feedbackVisible && props.error === null;
|
|
2708
3188
|
const needsContactPhone = props.contactPhone === null;
|
|
2709
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3189
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(EmergencyScreenLayout, {
|
|
2710
3190
|
screen: "primary",
|
|
2711
3191
|
titleId: props.titleId,
|
|
2712
|
-
|
|
3192
|
+
bodyClassName: idle ? "emergency-panel__body--primary" : void 0,
|
|
3193
|
+
header: idle ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyHeader, {
|
|
2713
3194
|
titleId: props.titleId,
|
|
2714
|
-
variant: idle ? "close-only" : "default",
|
|
2715
3195
|
onClose: () => props.onClose("close")
|
|
2716
3196
|
}),
|
|
2717
3197
|
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmergencyFooter, {
|
|
@@ -2719,11 +3199,17 @@ function PrimaryScreen(props) {
|
|
|
2719
3199
|
busy: props.busy,
|
|
2720
3200
|
aborting: props.aborting,
|
|
2721
3201
|
feedbackVisible: props.feedbackVisible,
|
|
3202
|
+
square: idle,
|
|
2722
3203
|
onActivate: needsContactPhone ? () => navigate("pre-emergency") : props.onActivate,
|
|
2723
3204
|
onRequestAbort: props.onRequestAbort,
|
|
2724
3205
|
onRequestSimulation: props.onRequestSimulation
|
|
2725
3206
|
}),
|
|
2726
|
-
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, {
|
|
2727
3213
|
active: props.active,
|
|
2728
3214
|
responders: props.responders,
|
|
2729
3215
|
refreshing: props.refreshing,
|
|
@@ -2738,8 +3224,9 @@ function PrimaryScreen(props) {
|
|
|
2738
3224
|
onRetry: props.onRetry,
|
|
2739
3225
|
onDismissFeedback: props.onDismissFeedback,
|
|
2740
3226
|
onRecharge: () => navigate("pricing"),
|
|
2741
|
-
onDetails: () => navigate("details")
|
|
2742
|
-
|
|
3227
|
+
onDetails: () => navigate("details"),
|
|
3228
|
+
onClose: () => props.onClose("close")
|
|
3229
|
+
})]
|
|
2743
3230
|
});
|
|
2744
3231
|
}
|
|
2745
3232
|
|
|
@@ -2912,7 +3399,9 @@ const screenRegistry = {
|
|
|
2912
3399
|
primary: PrimaryScreen,
|
|
2913
3400
|
"pre-emergency": PreEmergencyScreen,
|
|
2914
3401
|
pricing: PricingScreen,
|
|
2915
|
-
details: DetailsScreen
|
|
3402
|
+
details: DetailsScreen,
|
|
3403
|
+
history: HistoryScreen,
|
|
3404
|
+
"message-detail": MessageDetailScreen
|
|
2916
3405
|
};
|
|
2917
3406
|
|
|
2918
3407
|
// src/react/components/EmergencyPanel/index.tsx
|