date-fns#formatISO JavaScript Examples
The following examples show how to use
date-fns#formatISO.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: monitor.js From lnft with GNU Affero General Public License v3.0 | 6 votes |
checkTransactions = async () => {
try {
let { transactions } = await q(getUnconfirmed);
for (let i = 0; i < transactions.length; i++) {
let tx = transactions[i];
let { block_time, confirmed } = await electrs
.url(`/tx/${tx.hash}/status`)
.get()
.json();
if (confirmed) {
let {
update_transactions_by_pk: { artwork_id, type, bid },
} = await q(setConfirmed, {
id: tx.id,
});
if (["deposit", "withdrawal"].includes(type))
await q(setTransactionTime, {
id: tx.id,
created_at: formatISO(new Date(1000 * block_time)),
});
if (type === "accept")
await q(setOwner, { id: artwork_id, owner_id: bid.user_id });
}
}
} catch (e) {
console.log("problem checking transactions", e);
}
setTimeout(checkTransactions, 5000);
}
Example #2
Source File: commonFunctions.js From covid19india-react with MIT License | 5 votes |
getIndiaDateISO = () => {
return formatISO(getIndiaDate(), {representation: 'date'});
}
Example #3
Source File: commonFunctions.js From covid19india-react with MIT License | 5 votes |
getIndiaDateYesterdayISO = () => {
return formatISO(getIndiaDateYesterday(), {representation: 'date'});
}
Example #4
Source File: Calendar.js From covid19india-react with MIT License | 4 votes |
function Calendar({date, dates, slider}) {
const [view, setView] = useState('month');
const [activeStartDate, setActiveStartDate] = useState(parseIndiaDate(date));
const minDate = parseIndiaDate(dates[0]);
const maxDate = parseIndiaDate(dates[dates.length - 1]);
const isDateDisabled = ({date, view}) => {
return (
view === 'month' &&
!dates.includes(formatISO(date, {representation: 'date'}))
);
};
const handleCalendarClick = (value) => {
const clickedDate = formatISO(value, {representation: 'date'});
slider.moveToSlide(dates.indexOf(clickedDate));
};
const handleViewButton = ({view}) => {
setView(view);
};
const handleNavigationButton = ({activeStartDate}) => {
setActiveStartDate(activeStartDate);
};
const handleNavigation = (direction) => {
const newDate = add(
activeStartDate,
view === 'month' ? {months: direction} : {years: direction}
);
const lower =
view === 'month' ? startOfMonth(minDate) : startOfYear(minDate);
const upper = view === 'month' ? endOfMonth(maxDate) : endOfYear(maxDate);
if (lower <= newDate && newDate <= upper) {
setActiveStartDate(newDate);
}
};
const swipeHandlers = useSwipeable({
onSwipedRight: handleNavigation.bind(this, -1),
onSwipedLeft: handleNavigation.bind(this, 1),
});
const handleWheel = (event) => {
if (event.deltaX !== 0) {
handleNavigation(Math.sign(event.deltaX));
}
};
return (
<div className="Calendar" onWheel={handleWheel} {...swipeHandlers}>
<ReactCalendar
value={parseIndiaDate(date)}
tileDisabled={isDateDisabled}
{...{minDate, maxDate, activeStartDate, view}}
onActiveStartDateChange={handleNavigationButton}
onViewChange={handleViewButton}
minDetail="year"
showFixedNumberOfWeeks
onChange={handleCalendarClick}
prevLabel={
<div>
<ChevronLeft size={18} />
</div>
}
nextLabel={
<div>
<ChevronRight size={18} />
</div>
}
prev2Label={
<div>
<ChevronsLeft size={18} />
</div>
}
next2Label={
<div>
<ChevronsRight size={18} />
</div>
}
/>
</div>
);
}
Example #5
Source File: Home.js From covid19india-react with MIT License | 4 votes |
function Home() {
const [regionHighlighted, setRegionHighlighted] = useState({
stateCode: 'TT',
districtName: null,
});
const [anchor, setAnchor] = useLocalStorage('anchor', null);
const [expandTable, setExpandTable] = useLocalStorage('expandTable', false);
const [mapStatistic, setMapStatistic] = useSessionStorage(
'mapStatistic',
'active'
);
const [mapView, setMapView] = useLocalStorage('mapView', MAP_VIEWS.DISTRICTS);
const [date, setDate] = useState('');
const location = useLocation();
const {data: timeseries} = useStickySWR(
`${DATA_API_ROOT}/timeseries.min.json`,
fetcher,
{
revalidateOnMount: true,
refreshInterval: API_REFRESH_INTERVAL,
}
);
const {data} = useStickySWR(
`${DATA_API_ROOT}/data${date ? `-${date}` : ''}.min.json`,
fetcher,
{
revalidateOnMount: true,
refreshInterval: API_REFRESH_INTERVAL,
}
);
const homeRightElement = useRef();
const isVisible = useIsVisible(homeRightElement);
const {width} = useWindowSize();
const hideDistrictData = date !== '' && date < DISTRICT_START_DATE;
const hideDistrictTestData =
date === '' ||
date >
formatISO(
addDays(parseIndiaDate(DISTRICT_TEST_END_DATE), TESTED_EXPIRING_DAYS),
{representation: 'date'}
);
const hideVaccinated =
getStatistic(data?.['TT'], 'total', 'vaccinated') === 0;
const lastDataDate = useMemo(() => {
const updatedDates = [
data?.['TT']?.meta?.date,
data?.['TT']?.meta?.tested?.date,
data?.['TT']?.meta?.vaccinated?.date,
].filter((date) => date);
return updatedDates.length > 0
? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
representation: 'date',
})
: null;
}, [data]);
const lastUpdatedDate = useMemo(() => {
const updatedDates = Object.keys(data || {})
.map((stateCode) => data?.[stateCode]?.meta?.['last_updated'])
.filter((datetime) => datetime);
return updatedDates.length > 0
? formatDateObjIndia(
max(updatedDates.map((datetime) => parseIndiaDate(datetime)))
)
: null;
}, [data]);
const noDistrictDataStates = useMemo(
() =>
// Heuristic: All cases are in Unknown
Object.entries(data || {}).reduce((res, [stateCode, stateData]) => {
res[stateCode] = !!(
stateData?.districts &&
stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
PRIMARY_STATISTICS.every(
(statistic) =>
getStatistic(stateData, 'total', statistic) ===
getStatistic(
stateData.districts[UNKNOWN_DISTRICT_KEY],
'total',
statistic
)
)
);
return res;
}, {}),
[data]
);
const noRegionHighlightedDistrictData =
regionHighlighted?.stateCode &&
regionHighlighted?.districtName &&
regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
noDistrictDataStates[regionHighlighted.stateCode];
return (
<>
<Helmet>
<title>Coronavirus Outbreak in India - covid19india.org</title>
<meta
name="title"
content="Coronavirus Outbreak in India: Latest Map and Case Count"
/>
</Helmet>
<div className="Home">
<div className={classnames('home-left', {expanded: expandTable})}>
<div className="header">
<Suspense fallback={<div />}>
<Search />
</Suspense>
{!data && !timeseries && <div style={{height: '60rem'}} />}
<>
{!timeseries && <div style={{minHeight: '61px'}} />}
{timeseries && (
<Suspense fallback={<div style={{minHeight: '61px'}} />}>
<Actions
{...{
date,
setDate,
dates: Object.keys(timeseries['TT']?.dates),
lastUpdatedDate,
}}
/>
</Suspense>
)}
</>
</div>
<div style={{position: 'relative', marginTop: '1rem'}}>
{data && (
<Suspense fallback={<div style={{height: '50rem'}} />}>
{width >= 769 && !expandTable && (
<MapSwitcher {...{mapStatistic, setMapStatistic}} />
)}
<Level data={data['TT']} />
</Suspense>
)}
<>
{!timeseries && <div style={{height: '123px'}} />}
{timeseries && (
<Suspense fallback={<div style={{height: '123px'}} />}>
<Minigraphs
timeseries={timeseries['TT']?.dates}
{...{date}}
/>
</Suspense>
)}
</>
</div>
{!hideVaccinated && <VaccinationHeader data={data['TT']} />}
{data && (
<Suspense fallback={<TableLoader />}>
<Table
{...{
data,
regionHighlighted,
setRegionHighlighted,
expandTable,
setExpandTable,
hideDistrictData,
hideDistrictTestData,
hideVaccinated,
lastDataDate,
noDistrictDataStates,
}}
/>
</Suspense>
)}
</div>
<div
className={classnames('home-right', {expanded: expandTable})}
ref={homeRightElement}
style={{minHeight: '4rem'}}
>
{(isVisible || location.hash) && (
<>
{data && (
<div
className={classnames('map-container', {
expanded: expandTable,
stickied:
anchor === 'mapexplorer' || (expandTable && width >= 769),
})}
>
<Suspense fallback={<div style={{height: '50rem'}} />}>
<StateHeader data={data['TT']} stateCode={'TT'} />
<MapExplorer
{...{
stateCode: 'TT',
data,
mapStatistic,
setMapStatistic,
mapView,
setMapView,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable,
lastDataDate,
hideDistrictData,
hideDistrictTestData,
hideVaccinated,
noRegionHighlightedDistrictData,
}}
/>
</Suspense>
</div>
)}
{timeseries && (
<Suspense fallback={<div style={{height: '50rem'}} />}>
<TimeseriesExplorer
stateCode="TT"
{...{
timeseries,
date,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable,
hideVaccinated,
noRegionHighlightedDistrictData,
}}
/>
</Suspense>
)}
</>
)}
</div>
</div>
{isVisible && (
<Suspense fallback={<div />}>
<Footer />
</Suspense>
)}
</>
);
}
Example #6
Source File: Minigraphs.js From covid19india-react with MIT License | 4 votes |
function Minigraphs({timeseries, date: timelineDate}) {
const refs = useRef([]);
const endDate = timelineDate || getIndiaDateYesterdayISO();
let [wrapperRef, {width}] = useMeasure();
width = Math.min(width, maxWidth);
const dates = useMemo(() => {
const pastDates = Object.keys(timeseries || {}).filter(
(date) => date <= endDate
);
const lastDate = pastDates[pastDates.length - 1];
const cutOffDateLower = formatISO(
subDays(parseIndiaDate(lastDate), MINIGRAPH_LOOKBACK_DAYS),
{representation: 'date'}
);
return pastDates.filter((date) => date >= cutOffDateLower);
}, [endDate, timeseries]);
const getMinigraphStatistic = useCallback(
(date, statistic) => {
return getStatistic(timeseries?.[date], 'delta', statistic);
},
[timeseries]
);
useEffect(() => {
if (!width) return;
const T = dates.length;
const chartRight = width - margin.right;
const chartBottom = height - margin.bottom;
const xScale = scaleTime()
.clamp(true)
.domain([
parseIndiaDate(dates[0] || endDate),
parseIndiaDate(dates[T - 1]) || endDate,
])
.range([margin.left, chartRight]);
refs.current.forEach((ref, index) => {
const svg = select(ref);
const statistic = LEVEL_STATISTICS[index];
const color = STATISTIC_CONFIGS[statistic].color;
const dailyMaxAbs = max(dates, (date) =>
Math.abs(getMinigraphStatistic(date, statistic))
);
const yScale = scaleLinear()
.clamp(true)
.domain([-dailyMaxAbs, dailyMaxAbs])
.range([chartBottom, margin.top]);
const linePath = line()
.curve(curveMonotoneX)
.x((date) => xScale(parseIndiaDate(date)))
.y((date) => yScale(getMinigraphStatistic(date, statistic)));
let pathLength;
svg
.selectAll('path')
.data(T ? [dates] : [])
.join(
(enter) =>
enter
.append('path')
.attr('fill', 'none')
.attr('stroke', color + '99')
.attr('stroke-width', 2.5)
.attr('d', linePath)
.attr('stroke-dasharray', function () {
return (pathLength = this.getTotalLength());
})
.call((enter) =>
enter
.attr('stroke-dashoffset', pathLength)
.transition()
.delay(100)
.duration(2500)
.attr('stroke-dashoffset', 0)
),
(update) =>
update
.attr('stroke-dasharray', null)
.transition()
.duration(500)
.attrTween('d', function (date) {
const previous = select(this).attr('d');
const current = linePath(date);
return interpolatePath(previous, current);
})
.selection()
);
svg
.selectAll('circle')
.data(T ? [dates[T - 1]] : [])
.join(
(enter) =>
enter
.append('circle')
.attr('fill', color)
.attr('r', 2.5)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) =>
yScale(getMinigraphStatistic(date, statistic))
)
.style('opacity', 0)
.call((enter) =>
enter
.transition()
.delay(2100)
.duration(500)
.style('opacity', 1)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) =>
yScale(getMinigraphStatistic(date, statistic))
)
),
(update) =>
update
.transition()
.duration(500)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) =>
yScale(getMinigraphStatistic(date, statistic))
)
.style('opacity', 1)
.selection()
);
});
}, [endDate, dates, width, getMinigraphStatistic]);
return (
<div className="Minigraph">
{LEVEL_STATISTICS.map((statistic, index) => (
<div
key={statistic}
className={classnames('svg-parent')}
ref={index === 0 ? wrapperRef : null}
style={{width: `calc(${100 / LEVEL_STATISTICS.length}%)`}}
>
<svg
ref={(el) => {
refs.current[index] = el;
}}
preserveAspectRatio="xMidYMid meet"
width={width}
height={height}
/>
</div>
))}
</div>
);
}
Example #7
Source File: State.js From covid19india-react with MIT License | 4 votes |
function State() {
const {t} = useTranslation();
const stateCode = useParams().stateCode.toUpperCase();
const [mapStatistic, setMapStatistic] = useSessionStorage(
'mapStatistic',
'active'
);
const [showAllDistricts, setShowAllDistricts] = useState(false);
const [regionHighlighted, setRegionHighlighted] = useState({
stateCode: stateCode,
districtName: null,
});
const [delta7Mode, setDelta7Mode] = useState(false);
useEffect(() => {
if (regionHighlighted.stateCode !== stateCode) {
setRegionHighlighted({
stateCode: stateCode,
districtName: null,
});
setShowAllDistricts(false);
}
}, [regionHighlighted.stateCode, stateCode]);
const {data: timeseries, error: timeseriesResponseError} = useSWR(
`${DATA_API_ROOT}/timeseries-${stateCode}.min.json`,
fetcher,
{
revalidateOnMount: true,
refreshInterval: 100000,
}
);
const {data} = useSWR(`${DATA_API_ROOT}/data.min.json`, fetcher, {
revalidateOnMount: true,
refreshInterval: 100000,
});
const stateData = data?.[stateCode];
const toggleShowAllDistricts = () => {
setShowAllDistricts(!showAllDistricts);
};
const handleSort = (districtNameA, districtNameB) => {
const districtA = stateData.districts[districtNameA];
const districtB = stateData.districts[districtNameB];
return (
getStatistic(districtB, 'total', mapStatistic) -
getStatistic(districtA, 'total', mapStatistic)
);
};
const gridRowCount = useMemo(() => {
if (!stateData) return;
const gridColumnCount = window.innerWidth >= 540 ? 3 : 2;
const districtCount = stateData?.districts
? Object.keys(stateData.districts).filter(
(districtName) => districtName !== 'Unknown'
).length
: 0;
const gridRowCount = Math.ceil(districtCount / gridColumnCount);
return gridRowCount;
}, [stateData]);
const stateMetaElement = useRef();
const isStateMetaVisible = useIsVisible(stateMetaElement);
const trail = useMemo(() => {
const styles = [];
[0, 0, 0, 0].map((element, index) => {
styles.push({
animationDelay: `${index * 250}ms`,
});
return null;
});
return styles;
}, []);
const lookback = showAllDistricts ? (window.innerWidth >= 540 ? 10 : 8) : 6;
const lastDataDate = useMemo(() => {
const updatedDates = [
stateData?.meta?.date,
stateData?.meta?.tested?.date,
stateData?.meta?.vaccinated?.date,
].filter((date) => date);
return updatedDates.length > 0
? formatISO(max(updatedDates.map((date) => parseIndiaDate(date))), {
representation: 'date',
})
: null;
}, [stateData]);
const primaryStatistic = MAP_STATISTICS.includes(mapStatistic)
? mapStatistic
: 'confirmed';
const noDistrictData = useMemo(() => {
// Heuristic: All cases are in Unknown
return !!(
stateData?.districts &&
stateData.districts?.[UNKNOWN_DISTRICT_KEY] &&
PRIMARY_STATISTICS.every(
(statistic) =>
getStatistic(stateData, 'total', statistic) ===
getStatistic(
stateData.districts[UNKNOWN_DISTRICT_KEY],
'total',
statistic
)
)
);
}, [stateData]);
const statisticConfig = STATISTIC_CONFIGS[primaryStatistic];
const noRegionHighlightedDistrictData =
regionHighlighted?.districtName &&
regionHighlighted.districtName !== UNKNOWN_DISTRICT_KEY &&
noDistrictData;
const districts = Object.keys(
((!noDistrictData || !statisticConfig.hasPrimary) &&
stateData?.districts) ||
{}
);
return (
<>
<Helmet>
<title>
Coronavirus Outbreak in {STATE_NAMES[stateCode]} - covid19india.org
</title>
<meta
name="title"
content={`Coronavirus Outbreak in ${STATE_NAMES[stateCode]}: Latest Map and Case Count`}
/>
</Helmet>
<div className="State">
<div className="state-left">
<StateHeader data={stateData} stateCode={stateCode} />
<div style={{position: 'relative'}}>
<MapSwitcher {...{mapStatistic, setMapStatistic}} />
<Level data={stateData} />
<Minigraphs
timeseries={timeseries?.[stateCode]?.dates}
{...{stateCode}}
forceRender={!!timeseriesResponseError}
/>
</div>
{stateData?.total?.vaccinated1 && (
<VaccinationHeader data={stateData} />
)}
{data && (
<Suspense fallback={<div style={{minHeight: '50rem'}} />}>
<MapExplorer
{...{
stateCode,
data,
regionHighlighted,
setRegionHighlighted,
mapStatistic,
setMapStatistic,
lastDataDate,
delta7Mode,
setDelta7Mode,
noRegionHighlightedDistrictData,
noDistrictData,
}}
></MapExplorer>
</Suspense>
)}
<span ref={stateMetaElement} />
{isStateMetaVisible && data && (
<Suspense fallback={<div />}>
<StateMeta
{...{
stateCode,
data,
}}
timeseries={timeseries?.[stateCode]?.dates}
/>
</Suspense>
)}
</div>
<div className="state-right">
<>
<div className="district-bar">
<div
className={classnames('district-bar-top', {
expanded: showAllDistricts,
})}
>
<div className="district-bar-left">
<h2
className={classnames(primaryStatistic, 'fadeInUp')}
style={trail[0]}
>
{t('Top districts')}
</h2>
<div
className={`districts fadeInUp ${
showAllDistricts ? 'is-grid' : ''
}`}
style={
showAllDistricts
? {
gridTemplateRows: `repeat(${gridRowCount}, 2rem)`,
...trail[1],
}
: trail[1]
}
>
{districts
.filter((districtName) => districtName !== 'Unknown')
.sort((a, b) => handleSort(a, b))
.slice(0, showAllDistricts ? undefined : 5)
.map((districtName) => {
const total = getStatistic(
stateData.districts[districtName],
'total',
primaryStatistic
);
const delta = getStatistic(
stateData.districts[districtName],
'delta',
primaryStatistic
);
return (
<div key={districtName} className="district">
<h2>{formatNumber(total)}</h2>
<h5>{t(districtName)}</h5>
{primaryStatistic !== 'active' && (
<div className="delta">
<h6 className={primaryStatistic}>
{delta > 0
? '\u2191' + formatNumber(delta)
: ''}
</h6>
</div>
)}
</div>
);
})}
</div>
</div>
<div className="district-bar-right fadeInUp" style={trail[2]}>
{timeseries &&
(primaryStatistic === 'confirmed' ||
primaryStatistic === 'deceased') && (
<div className="happy-sign">
{Object.keys(timeseries[stateCode]?.dates || {})
.slice(-lookback)
.every(
(date) =>
getStatistic(
timeseries[stateCode].dates[date],
'delta',
primaryStatistic
) === 0
) && (
<div
className={`alert ${
primaryStatistic === 'confirmed' ? 'is-green' : ''
}`}
>
<SmileyIcon />
<div className="alert-right">
No new {primaryStatistic} cases in the past five
days
</div>
</div>
)}
</div>
)}
<DeltaBarGraph
timeseries={timeseries?.[stateCode]?.dates}
statistic={primaryStatistic}
{...{stateCode, lookback}}
forceRender={!!timeseriesResponseError}
/>
</div>
</div>
<div className="district-bar-bottom">
{districts.length > 5 ? (
<button
className="button fadeInUp"
onClick={toggleShowAllDistricts}
style={trail[3]}
>
<span>
{t(showAllDistricts ? 'View less' : 'View all')}
</span>
</button>
) : (
<div style={{height: '3.75rem', flexBasis: '15%'}} />
)}
</div>
</div>
<Suspense fallback={<div />}>
<TimeseriesExplorer
{...{
stateCode,
timeseries,
regionHighlighted,
setRegionHighlighted,
noRegionHighlightedDistrictData,
}}
forceRender={!!timeseriesResponseError}
/>
</Suspense>
</>
</div>
</div>
<Footer />
</>
);
}
Example #8
Source File: StateMeta.js From covid19india-react with MIT License | 4 votes |
function StateMeta({stateCode, data, timeseries}) {
const {t} = useTranslation();
const confirmedPerLakh = getStatistic(data[stateCode], 'total', 'confirmed', {
normalizedByPopulationPer: 'lakh',
});
const testPerLakh = getStatistic(data[stateCode], 'total', 'tested', {
normalizedByPopulationPer: 'lakh',
});
const totalConfirmedPerLakh = getStatistic(data['TT'], 'total', 'confirmed', {
normalizedByPopulationPer: 'lakh',
});
const activePercent = getStatistic(data[stateCode], 'total', 'activeRatio');
const recoveryPercent = getStatistic(
data[stateCode],
'total',
'recoveryRatio'
);
const deathPercent = getStatistic(data[stateCode], 'total', 'cfr');
// Show TPR for week preceeding last updated date
const pastDates = Object.keys(timeseries || {}).filter(
(date) => date <= getIndiaDateYesterdayISO()
);
const lastDate = pastDates[pastDates.length - 1];
const prevWeekDate = formatISO(subDays(parseIndiaDate(lastDate), 6));
const tprWeek = getStatistic(timeseries?.[lastDate], 'delta', 'tpr', {
movingAverage: true,
});
return (
<>
<div className="StateMeta population">
<div className="meta-item population">
<h3>{t('Population')}</h3>
<h1>{formatNumber(data[stateCode]?.meta?.population)}</h1>
</div>
<div className="alert">
<Compass />
<div className="alert-right">
{t('Based on 2019 population projection by NCP')}
<a
href="https://nhm.gov.in/New_Updates_2018/Report_Population_Projection_2019.pdf"
target="_noblank"
>
report
</a>
</div>
</div>
</div>
<div className="StateMeta">
<StateMetaCard
className="confirmed"
title={t('Confirmed Per Lakh')}
statistic={formatNumber(confirmedPerLakh)}
total={formatNumber(totalConfirmedPerLakh)}
formula={
<>
{`${1e5} x `}
<Fraction
numerator={t('Total confirmed cases')}
denominator={t('Total population')}
/>
</>
}
description={`
~${formatNumber(confirmedPerLakh, 'long')} ${t(
'out of every lakh people in'
)} ${STATE_NAMES[stateCode]} ${t(
'have tested positive for the virus.'
)}
`}
/>
<StateMetaCard
className="active"
title={t('Active Ratio')}
statistic={`${formatNumber(activePercent, '%')}`}
formula={
<>
{'100 x '}
<Fraction
numerator={t('Total active cases right now')}
denominator={t('Total confirmed cases')}
/>
</>
}
description={
activePercent > 0
? `${t('For every 100 confirmed cases')}, ~${formatNumber(
activePercent,
'long'
)} ${t('are currently infected.')}`
: t('Currently, there are no active cases in this state.')
}
/>
<StateMetaCard
className="recovery"
title={t('Recovery Ratio')}
statistic={`${formatNumber(recoveryPercent, '%')}`}
formula={
<>
{'100 x '}
<Fraction
numerator={t('Total recovered cases')}
denominator={t('Total confirmed cases')}
/>
</>
}
description={
recoveryPercent > 0
? `${t('For every 100 confirmed cases')}, ~${formatNumber(
recoveryPercent,
'long'
)} ${t('have recovered from the virus.')}`
: t('Unfortunately, there are no recoveries in this state yet.')
}
/>
<StateMetaCard
className="mortality"
title={t('Case Fatality Ratio')}
statistic={`${formatNumber(deathPercent, '%')}`}
formula={
<>
{'100 x '}
<Fraction
numerator={t('Total deaths')}
denominator={t('Total confirmed cases')}
/>
</>
}
description={
deathPercent > 0
? `${t('For every 100 confirmed cases')}, ~${formatNumber(
deathPercent,
'long'
)} ${t('have unfortunately passed away from the virus.')}`
: t(
'Fortunately, no one has passed away from the virus in this state.'
)
}
/>
<StateMetaCard
className="tpr"
title={t('Test Positivity Ratio')}
statistic={tprWeek > 0 ? `${formatNumber(tprWeek, '%')}` : '-'}
formula={
<>
{'100 x '}
<Fraction
numerator={t('Confirmed cases last week')}
denominator={t('Samples tested last week')}
/>
</>
}
date={`${formatDate(prevWeekDate, 'dd MMM')} - ${formatDate(
lastDate,
'dd MMM'
)}`}
description={
tprWeek > 0
? `${t('In the last one week,')} ${formatNumber(tprWeek, '%')}
${t('of samples tested came back positive.')}`
: t('No tested sample came back positive in last one week.')
}
/>
<StateMetaCard
className="tpl"
title={t('Tests Per Lakh')}
statistic={`${formatNumber(testPerLakh)}`}
formula={
<>
{`${1e5} x `}
<Fraction
numerator={t('Total samples tested')}
denominator={t('Total population')}
/>
</>
}
date={
testPerLakh && data[stateCode]?.meta?.tested?.date
? `${t('As of')} ${formatLastUpdated(
data[stateCode].meta.tested.date
)} ${t('ago')}`
: ''
}
description={
testPerLakh > 0
? `${t('For every lakh people in')} ${STATE_NAMES[stateCode]},
~${formatNumber(testPerLakh, 'long')} ${t(
'samples were tested.'
)}`
: t('No tests have been conducted in this state yet.')
}
/>
</div>
</>
);
}
Example #9
Source File: TimeseriesBrush.js From covid19india-react with MIT License | 4 votes |
function TimeseriesBrush({
timeseries,
dates,
currentBrushSelection,
endDate,
lookback,
setBrushSelectionEnd,
setLookback,
animationIndex,
}) {
const chartRef = useRef();
const [wrapperRef, {width, height}] = useMeasure();
const endDateMin =
lookback !== null
? min([
formatISO(addDays(parseIndiaDate(dates[0]), lookback), {
representation: 'date',
}),
endDate,
])
: endDate;
const xScale = useMemo(() => {
const T = dates.length;
// Chart extremes
const chartRight = width - margin.right;
return scaleTime()
.clamp(true)
.domain([
parseIndiaDate(dates[0] || endDate),
parseIndiaDate(dates[T - 1] || endDate),
])
.range([margin.left, chartRight]);
}, [width, endDate, dates]);
useEffect(() => {
if (!width || !height) return;
// Chart extremes
const chartBottom = height - margin.bottom;
const xAxis = (g) =>
g
.attr('class', 'x-axis')
.call(axisBottom(xScale).ticks(numTicksX(width)));
// Switched to daily confirmed instead of cumulative ARD
const timeseriesStacked = stack()
.keys(BRUSH_STATISTICS)
.value((date, statistic) =>
Math.max(0, getStatistic(timeseries[date], 'delta', statistic))
)(dates);
const yScale = scaleLinear()
.clamp(true)
.domain([
0,
max(
timeseriesStacked[timeseriesStacked.length - 1],
([, y1]) => yBufferTop * y1
),
])
.range([chartBottom, margin.top]);
const svg = select(chartRef.current);
const t = svg.transition().duration(D3_TRANSITION_DURATION);
svg
.select('.x-axis')
.attr('pointer-events', 'none')
.style('transform', `translate3d(0, ${chartBottom}px, 0)`)
.transition(t)
.call(xAxis);
const areaPath = area()
.curve(curveMonotoneX)
.x((d) => xScale(parseIndiaDate(d.data)))
.y0((d) => yScale(d[0]))
.y1((d) => yScale(d[1]));
svg
.select('.trend-areas')
.selectAll('.trend-area')
.data(timeseriesStacked)
.join(
(enter) =>
enter
.append('path')
.attr('class', 'trend-area')
.attr('fill', ({key}) => STATISTIC_CONFIGS[key].color)
.attr('fill-opacity', 0.4)
.attr('stroke', ({key}) => STATISTIC_CONFIGS[key].color)
.attr('d', areaPath)
.attr('pointer-events', 'none'),
(update) =>
update
.transition(t)
.attrTween('d', function (date) {
const previous = select(this).attr('d');
const current = areaPath(date);
return interpolatePath(previous, current);
})
.selection()
);
}, [dates, width, height, xScale, timeseries]);
const defaultSelection = currentBrushSelection.map((date) =>
xScale(parseIndiaDate(date))
);
const brush = useMemo(() => {
if (!width || !height) return;
// Chart extremes
const chartRight = width - margin.right;
const chartBottom = height - margin.bottom;
const brush = brushX()
.extent([
[margin.left, margin.top],
[chartRight, chartBottom],
])
.handleSize(20);
return brush;
}, [width, height]);
const brushed = useCallback(
({sourceEvent, selection}) => {
if (!sourceEvent) return;
const [brushStartDate, brushEndDate] = selection.map(xScale.invert);
ReactDOM.unstable_batchedUpdates(() => {
setBrushSelectionEnd(formatISO(brushEndDate, {representation: 'date'}));
setLookback(differenceInDays(brushEndDate, brushStartDate));
});
},
[xScale, setBrushSelectionEnd, setLookback]
);
const beforebrushstarted = useCallback(
(event) => {
const svg = select(chartRef.current);
const selection = brushSelection(svg.select('.brush').node());
if (!selection) return;
const dx = selection[1] - selection[0];
const [[cx]] = pointers(event);
const [x0, x1] = [cx - dx / 2, cx + dx / 2];
const [X0, X1] = xScale.range();
svg
.select('.brush')
.call(
brush.move,
x1 > X1 ? [X1 - dx, X1] : x0 < X0 ? [X0, X0 + dx] : [x0, x1]
);
},
[brush, xScale]
);
const brushended = useCallback(
({sourceEvent, selection}) => {
if (!sourceEvent || !selection) return;
const domain = selection
.map(xScale.invert)
.map((date) => formatISO(date, {representation: 'date'}));
const svg = select(chartRef.current);
svg
.select('.brush')
.call(
brush.move,
domain.map((date) => xScale(parseIndiaDate(date)))
)
.call((g) => g.select('.overlay').attr('cursor', 'pointer'));
},
[brush, xScale]
);
useEffect(() => {
if (!brush) return;
brush.on('start brush', brushed).on('end', brushended);
const svg = select(chartRef.current);
svg
.select('.brush')
.call(brush)
.call((g) =>
g
.select('.overlay')
.attr('cursor', 'pointer')
.datum({type: 'selection'})
.on('mousedown touchstart', beforebrushstarted)
);
}, [brush, brushed, brushended, beforebrushstarted]);
useEffect(() => {
if (!brush) return;
const svg = select(chartRef.current);
svg.select('.brush').call(brush.move, defaultSelection);
}, [brush, defaultSelection]);
const handleWheel = (event) => {
if (event.deltaX) {
setBrushSelectionEnd(
max([
endDateMin,
dates[
Math.max(
0,
Math.min(
dates.length - 1,
dates.indexOf(currentBrushSelection[1]) +
Math.sign(event.deltaX) * brushWheelDelta
)
)
],
])
);
}
};
return (
<div className="Timeseries">
<div
className={classnames('svg-parent is-brush fadeInUp')}
ref={wrapperRef}
onWheel={handleWheel}
style={{animationDelay: `${animationIndex * 250}ms`}}
>
<svg ref={chartRef} preserveAspectRatio="xMidYMid meet">
<defs>
<clipPath id="clipPath">
<rect
x={0}
y={`${margin.top}`}
width={width}
height={`${Math.max(0, height - margin.bottom)}`}
/>
</clipPath>
<mask id="mask">
<rect
x={0}
y={`${margin.top}`}
width={width}
height={`${Math.max(0, height - margin.bottom)}`}
fill="hsl(0, 0%, 40%)"
/>
<use href="#selection" fill="white" />
</mask>
</defs>
<g className="brush" clipPath="url(#clipPath)">
<g mask="url(#mask)">
<rect className="overlay" />
<g className="trend-areas" />
<rect className="selection" id="selection" />
</g>
</g>
<g className="x-axis" />
</svg>
</div>
</div>
);
}
Example #10
Source File: TimeseriesExplorer.js From covid19india-react with MIT License | 4 votes |
function TimeseriesExplorer({
stateCode,
timeseries,
date: timelineDate,
regionHighlighted,
setRegionHighlighted,
anchor,
setAnchor,
expandTable = false,
hideVaccinated = false,
noRegionHighlightedDistrictData,
}) {
const {t} = useTranslation();
const [lookback, setLookback] = useLocalStorage('timeseriesLookbackDays', 90);
const [chartType, setChartType] = useLocalStorage('chartType', 'delta');
const [isUniform, setIsUniform] = useLocalStorage('isUniform', false);
const [isLog, setIsLog] = useLocalStorage('isLog', false);
const [isMovingAverage, setIsMovingAverage] = useLocalStorage(
'isMovingAverage',
false
);
const stateCodeDateRange = Object.keys(timeseries?.[stateCode]?.dates || {});
const beginningDate =
stateCodeDateRange[0] || timelineDate || getIndiaDateYesterdayISO();
const endDate = min([
stateCodeDateRange[stateCodeDateRange.length - 1],
timelineDate || getIndiaDateYesterdayISO(),
]);
const [brushSelectionEnd, setBrushSelectionEnd] = useState(endDate);
useEffect(() => {
setBrushSelectionEnd(endDate);
}, [endDate]);
const brushSelectionStart =
lookback !== null
? formatISO(subDays(parseIndiaDate(brushSelectionEnd), lookback), {
representation: 'date',
})
: beginningDate;
const explorerElement = useRef();
const isVisible = useIsVisible(explorerElement, {once: true});
const {width} = useWindowSize();
const selectedRegion = useMemo(() => {
if (timeseries?.[regionHighlighted.stateCode]?.districts) {
return {
stateCode: regionHighlighted.stateCode,
districtName: regionHighlighted.districtName,
};
} else {
return {
stateCode: regionHighlighted.stateCode,
districtName: null,
};
}
}, [timeseries, regionHighlighted.stateCode, regionHighlighted.districtName]);
const selectedTimeseries = useMemo(() => {
if (selectedRegion.districtName) {
return timeseries?.[selectedRegion.stateCode]?.districts?.[
selectedRegion.districtName
]?.dates;
} else {
return timeseries?.[selectedRegion.stateCode]?.dates;
}
}, [timeseries, selectedRegion.stateCode, selectedRegion.districtName]);
const regions = useMemo(() => {
const states = Object.keys(timeseries || {})
.filter((code) => code !== stateCode)
.sort((code1, code2) =>
STATE_NAMES[code1].localeCompare(STATE_NAMES[code2])
)
.map((code) => {
return {
stateCode: code,
districtName: null,
};
});
const districts = Object.keys(timeseries || {}).reduce((acc1, code) => {
return [
...acc1,
...Object.keys(timeseries?.[code]?.districts || {}).reduce(
(acc2, districtName) => {
return [
...acc2,
{
stateCode: code,
districtName: districtName,
},
];
},
[]
),
];
}, []);
return [
{
stateCode: stateCode,
districtName: null,
},
...states,
...districts,
];
}, [timeseries, stateCode]);
const dropdownRegions = useMemo(() => {
if (
regions.find(
(region) =>
region.stateCode === regionHighlighted.stateCode &&
region.districtName === regionHighlighted.districtName
)
)
return regions;
return [
...regions,
{
stateCode: regionHighlighted.stateCode,
districtName: regionHighlighted.districtName,
},
];
}, [regionHighlighted.stateCode, regionHighlighted.districtName, regions]);
const dates = useMemo(
() =>
Object.keys(selectedTimeseries || {}).filter((date) => date <= endDate),
[selectedTimeseries, endDate]
);
const brushSelectionDates = useMemo(
() =>
dates.filter(
(date) => brushSelectionStart <= date && date <= brushSelectionEnd
),
[dates, brushSelectionStart, brushSelectionEnd]
);
const handleChange = useCallback(
({target}) => {
setRegionHighlighted(JSON.parse(target.value));
},
[setRegionHighlighted]
);
const resetDropdown = useCallback(() => {
setRegionHighlighted({
stateCode: stateCode,
districtName: null,
});
}, [stateCode, setRegionHighlighted]);
const statistics = useMemo(
() =>
TIMESERIES_STATISTICS.filter(
(statistic) =>
(!(STATISTIC_CONFIGS[statistic]?.category === 'vaccinated') ||
!hideVaccinated) &&
// (chartType === 'total' || statistic !== 'active') &&
(chartType === 'delta' || statistic !== 'tpr')
),
[chartType, hideVaccinated]
);
return (
<div
className={classnames(
'TimeseriesExplorer fadeInUp',
{
stickied: anchor === 'timeseries',
},
{expanded: expandTable}
)}
style={{
display:
anchor && anchor !== 'timeseries' && (!expandTable || width < 769)
? 'none'
: '',
}}
ref={explorerElement}
>
<div className="timeseries-header">
<div
className={classnames('anchor', 'fadeInUp', {
stickied: anchor === 'timeseries',
})}
style={{
display: expandTable && width >= 769 ? 'none' : '',
}}
onClick={
setAnchor &&
setAnchor.bind(this, anchor === 'timeseries' ? null : 'timeseries')
}
>
<PinIcon />
</div>
<h1>{t('Spread Trends')}</h1>
<div className="tabs">
{Object.entries(TIMESERIES_CHART_TYPES).map(
([ctype, value], index) => (
<div
className={`tab ${chartType === ctype ? 'focused' : ''}`}
key={ctype}
onClick={setChartType.bind(this, ctype)}
>
<h4>{t(value)}</h4>
</div>
)
)}
</div>
<div className="timeseries-options">
<div className="scale-modes">
<label className="main">{`${t('Scale Modes')}:`}</label>
<div className="timeseries-mode">
<label htmlFor="timeseries-mode">{t('Uniform')}</label>
<input
id="timeseries-mode"
type="checkbox"
className="switch"
checked={isUniform}
aria-label={t('Checked by default to scale uniformly.')}
onChange={setIsUniform.bind(this, !isUniform)}
/>
</div>
<div
className={`timeseries-mode ${
chartType !== 'total' ? 'disabled' : ''
}`}
>
<label htmlFor="timeseries-logmode">{t('Logarithmic')}</label>
<input
id="timeseries-logmode"
type="checkbox"
checked={chartType === 'total' && isLog}
className="switch"
disabled={chartType !== 'total'}
onChange={setIsLog.bind(this, !isLog)}
/>
</div>
</div>
<div
className={`timeseries-mode ${
chartType === 'total' ? 'disabled' : ''
} moving-average`}
>
<label htmlFor="timeseries-moving-average">
{t('7 day Moving Average')}
</label>
<input
id="timeseries-moving-average"
type="checkbox"
checked={chartType === 'delta' && isMovingAverage}
className="switch"
disabled={chartType !== 'delta'}
onChange={setIsMovingAverage.bind(this, !isMovingAverage)}
/>
</div>
</div>
</div>
{dropdownRegions && (
<div className="state-selection">
<div className="dropdown">
<select
value={JSON.stringify(selectedRegion)}
onChange={handleChange}
>
{dropdownRegions
.filter(
(region) =>
STATE_NAMES[region.stateCode] !== region.districtName
)
.map((region) => {
return (
<option
value={JSON.stringify(region)}
key={`${region.stateCode}-${region.districtName}`}
>
{region.districtName
? t(region.districtName)
: t(STATE_NAMES[region.stateCode])}
</option>
);
})}
</select>
</div>
<div className="reset-icon" onClick={resetDropdown}>
<ReplyIcon />
</div>
</div>
)}
{isVisible && (
<Suspense fallback={<TimeseriesLoader />}>
<Timeseries
timeseries={selectedTimeseries}
regionHighlighted={selectedRegion}
dates={brushSelectionDates}
{...{
statistics,
endDate,
chartType,
isUniform,
isLog,
isMovingAverage,
noRegionHighlightedDistrictData,
}}
/>
<TimeseriesBrush
timeseries={selectedTimeseries}
regionHighlighted={selectedRegion}
currentBrushSelection={[brushSelectionStart, brushSelectionEnd]}
animationIndex={statistics.length}
{...{dates, endDate, lookback, setBrushSelectionEnd, setLookback}}
/>
</Suspense>
)}
{!isVisible && <div style={{height: '50rem'}} />}
<div
className="pills fadeInUp"
style={{animationDelay: `${(1 + statistics.length) * 250}ms`}}
>
{TIMESERIES_LOOKBACK_DAYS.map((numDays) => (
<button
key={numDays}
type="button"
className={classnames({
selected: numDays === lookback,
})}
onClick={setLookback.bind(this, numDays)}
>
{numDays !== null ? `${numDays} ${t('days')}` : t('Beginning')}
</button>
))}
</div>
</div>
);
}
Example #11
Source File: Job.jsx From bull-master with MIT License | 4 votes |
function Job({ match, history }) {
const classes = useStyles();
const [job, getJob] = useResource(({ jobId, queueName }) => ({
url: `/queues/${queueName}/jobs/${jobId}`,
method: 'GET',
}));
const [, createRemoveRequest] = useRequest(({ jobId, queueName }) => ({
url: `/queues/${queueName}/removes`,
method: 'POST',
data: {
jobs: [jobId],
},
}));
const [, createRetryJobRequest] = useRequest(({ jobId, queueName }) => ({
url: `/queues/${queueName}/retries`,
method: 'POST',
data: {
jobs: [jobId],
},
}));
const [, createPromoteJobRequest] = useRequest(({ jobId, queueName }) => ({
url: `/queues/${queueName}/promotes`,
method: 'POST',
data: {
jobs: [jobId],
},
}));
const { jobId, queueName } = match.params;
useInterval(() => {
getJob({ jobId, queueName });
}, 4000);
const stacktrace = job.data?.stacktrace || [];
const logs = job.data?.logs || [];
const status = job.data?.status || 'active';
const handleRemove = () => {
createRemoveRequest({ jobId, queueName })
.ready()
.then(() => history.push(`/queues/${queueName}?status=${status}`));
};
const handlePromote = () => {
createPromoteJobRequest({ jobId, queueName })
.ready()
.then(() => getJob({ jobId, queueName }));
};
const handleRetry = () => {
createRetryJobRequest({ jobId, queueName })
.ready()
.then(() => getJob({ jobId, queueName }));
};
return (
<Grid container spacing={3}>
<Grid item xs={12}>
<Breadcrumbs aria-label="breadcrumb">
<Link component={RouterLink} color="inherit" to="/">
Dashboard
</Link>
<Link
component={RouterLink}
color="inherit"
to={`/queues/${queueName}`}
>
{queueName}
</Link>
<Typography color="textPrimary">#{jobId}</Typography>
</Breadcrumbs>
</Grid>
<Grid item xs={12} md={8}>
<Section>
<Grid container alignItems="center" style={{ marginBottom: 16 }}>
<Grid item xs={6} container alignItems="center">
<Title style={{ marginRight: 16 }}>#{jobId}</Title>
<Status style={{ marginRight: 16 }} status={job.data?.status} />
<CircularProgress
style={{ display: 'inline-block' }}
value={job.data?.progress}
/>
</Grid>
<Grid
item
container
xs={6}
className={classes.actions}
justify="flex-end"
>
{['failed'].includes(status) && (
<Button
variant="contained"
color="primary"
className={clsx(classes.retry, classes.button)}
onClick={handleRetry}
>
Retry
</Button>
)}
{['delayed', 'failed', 'completed', 'waiting', 'paused'].includes(
status,
) && (
<Button
className={clsx(classes.remove, classes.button)}
variant="contained"
onClick={handleRemove}
>
Remove
</Button>
)}
{status === 'delayed' && (
<Button
variant="contained"
color="primary"
className={clsx(classes.promote, classes.button)}
onClick={handlePromote}
>
Promote
</Button>
)}
</Grid>
</Grid>
<Grid container style={{ marginBottom: 16 }} alignItems="center">
<Grid item xs={4}>
<Typography variant="body1" component="span">
Retries Attempted
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="body1">{job.data?.attemptsMade}</Typography>
</Grid>
</Grid>
<Grid container style={{ marginBottom: 16 }} alignItems="center">
<Grid item xs={4}>
<Typography variant="body1" component="span">
Total Configured Retries
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="body1">{job.data?.attemptsTotal}</Typography>
</Grid>
</Grid>
<Grid container style={{ marginBottom: 16 }} alignItems="center">
<Grid item xs={4}>
<Typography variant="body1" component="span">
Job Name
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="body1">{job.data?.name}</Typography>
</Grid>
</Grid>
<Grid container style={{ marginBottom: 16 }} alignItems="center">
<Grid item xs={4}>
<Typography variant="body1" component="span">
Delayed To
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="body1">
{job.data?.delayedTo ? formatISO(job.data?.delayedTo) : '-'}
</Typography>
</Grid>
</Grid>
<Grid container style={{ marginBottom: 16 }} alignItems="center">
<Grid item xs={4}>
<Typography variant="body1" component="span">
Failed Reason
</Typography>
</Grid>
<Grid item xs={4}>
<Typography variant="body1">{job.data?.failedReason}</Typography>
</Grid>
</Grid>
</Section>
</Grid>
<Grid item xs={12} md={4}>
<Section>
<Grid container alignItems="center">
<Grid item xs={8}>
<Title>Timeline</Title>
<List>
<ListItem style={{ paddingLeft: 0 }}>
<ListItemText
secondary={
job.data?.timestamp ? formatISO(job.data?.timestamp) : '-'
}
>
Started At
</ListItemText>
</ListItem>
<ListItem style={{ paddingLeft: 0 }}>
<ListItemText
secondary={
job.data?.processedOn
? formatISO(job.data?.processedOn)
: '-'
}
>
Processed At
</ListItemText>
</ListItem>
<ListItem style={{ paddingLeft: 0 }}>
<ListItemText
secondary={
job.data?.finishedOn
? formatISO(job.data?.finishedOn)
: '-'
}
>
Finished At
</ListItemText>
</ListItem>
</List>
</Grid>
</Grid>
</Section>
</Grid>
<Grid item xs={12}>
<Section>
<Title>Data</Title>
<pre>{JSON.stringify(job.data?.data, null, 2)}</pre>
</Section>
</Grid>
{stacktrace.length > 0 && (
<Grid item xs={12}>
<Section>
<Typography variant="h6">Error Stack</Typography>
<List>
{stacktrace.reverse().map((trace, index) => (
/* eslint-disable-next-line */
<ListItem key={`${trace}-${index}`}>
<pre>{trace}</pre>
</ListItem>
))}
</List>
</Section>
</Grid>
)}
{logs.length > 0 && (
<Grid item xs={12}>
<Section>
<Typography variant="h6">Logs</Typography>
<List>
{logs.reverse().map((log, index) => (
/* eslint-disable-next-line */
<ListItem key={`${log}-${index}`}>
<pre style={{ margin: 0 }}>{log}</pre>
</ListItem>
))}
</List>
</Section>
</Grid>
)}
<Grid item xs={12}>
<Section>
<Title>Raw Data</Title>
<pre>{JSON.stringify(job?.data, null, 2)}</pre>
</Section>
</Grid>
</Grid>
);
}
Example #12
Source File: Queue.jsx From bull-master with MIT License | 4 votes |
function Queue({ match, history, location }) {
const { pathname } = location || {};
const [queue, getQueue] = useResource(() => ({
url: `/queues/${match.params.queueName}`,
method: 'GET',
}));
useInterval(getQueue, 4000);
const [jobs, getJobs] = useResource(({ page, pageSize, status }) => ({
url: `/queues/${match.params.queueName}/jobs?status=${status}&pageSize=${pageSize}&page=${page}`,
method: 'GET',
}));
const [, retryAll] = useResource(() => ({
url: `/queues/${match.params.queueName}/retries`,
method: 'POST',
data: {
status: 'failed',
},
}));
const [, cleanQueue] = useResource(() => ({
url: `/queues/${match.params.queueName}/clean`,
method: 'POST',
}));
const [, pauseQueue] = useResource(() => ({
url: `/queues/${match.params.queueName}/pause`,
method: 'POST',
}));
const [, resumeQueue] = useResource(() => ({
url: `/queues/${match.params.queueName}/resume`,
method: 'POST',
}));
const [, promoteAll] = useResource(() => ({
url: `/queues/${match.params.queueName}/promotes`,
method: 'POST',
data: {
status: 'delayed',
},
}));
const [selected, setSelected] = useState([]);
const query = new URLSearchParams(location.search);
const status = query.get('status') || 'active';
const page = parseInt(query.get('page') || 0, 10);
const pageSize = parseInt(query.get('pageSize') || 5, 10);
const refreshTable = () => {
const newQuery = new URLSearchParams(location.search);
const newStatus = newQuery.get('status') || 'active';
const newPage = parseInt(newQuery.get('page') || 0, 10);
const newPageSize = parseInt(newQuery.get('pageSize') || 5, 10);
getJobs({
page: newPage,
pageSize: newPageSize,
status: newStatus,
});
};
useInterval(refreshTable, 4000);
const handleStatusChange = (event, newValue) => {
const newQuery = new URLSearchParams(location.search);
newQuery.set('status', newValue);
newQuery.set('page', 0);
setSelected([]);
getJobs({
page,
pageSize,
status: newValue,
});
history.push(`${pathname}?${newQuery.toString()}`);
};
const handleChangePage = (e, newPage) => {
const newQuery = new URLSearchParams(location.search);
newQuery.set('page', newPage);
getJobs({
page: newPage,
pageSize,
status,
});
history.push(`${pathname}?${newQuery.toString()}`);
};
const handleChangeRowsPerPage = (e) => {
const newQuery = new URLSearchParams(location.search);
newQuery.set('pageSize', e.target.value);
getJobs({
page,
pageSize: e.target.value,
status,
});
history.push(`${pathname}?${newQuery.toString()}`);
};
const data = jobs.data?.data || [];
const handleSelectAllClick = (event) => {
if (event.target.checked) {
const newSelecteds = data.map((n) => n.id);
setSelected(newSelecteds);
return;
}
setSelected([]);
};
const handleCellClick = (event, name) => {
const selectedIndex = selected.indexOf(name);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1),
);
}
setSelected(newSelected);
};
const handleBulkRetry = () => {
client({
url: `/queues/${match.params.queueName}/retries`,
method: 'POST',
data: {
jobs: selected,
},
})
.then(() => setSelected([]))
.then(refreshTable);
};
const handleBulkPromote = () => {
client({
url: `/queues/${match.params.queueName}/promotes`,
method: 'POST',
data: {
jobs: selected,
},
})
.then(() => setSelected([]))
.then(refreshTable);
};
const handleBulkRemove = () => {
client({
url: `/queues/${match.params.queueName}/removes`,
method: 'POST',
data: {
jobs: selected,
},
})
.then(() => setSelected([]))
.then(refreshTable);
};
const { name, counts } = queue.data || {};
return (
<Grid container spacing={3}>
<Grid container item xs={12} justify="space-between">
<Breadcrumbs aria-label="breadcrumb">
<Link component={RouterLink} color="inherit" to="/">
Dashboard
</Link>
<Typography color="textPrimary">{name}</Typography>
</Breadcrumbs>
<div>
<Button variant="outlined" onClick={pauseQueue}>
Pause Queue
</Button>
<Button onClick={resumeQueue}>Resume Queue</Button>
<Button onClick={cleanQueue}>Clean Queue</Button>
</div>
</Grid>
<Grid item xs={12}>
<StatusTabs
value={status}
counts={counts}
onChange={handleStatusChange}
/>
</Grid>
<Grid item xs={12}>
<Table
title={name}
page={page}
selected={selected}
onChangePage={handleChangePage}
onCellClick={handleCellClick}
onSelectAllClick={handleSelectAllClick}
columns={[
{ title: 'ID', field: 'id' },
{ title: 'Job Name', field: 'name' },
{
title: 'Created At',
field: 'timestamp',
render: (value) =>
value && (
<Tooltip
placement="top"
title={`${formatDistanceStrict(value, Date.now())} ago`}
>
<span>{formatISO(value)}</span>
</Tooltip>
),
},
{
title: 'Started At',
field: 'processedOn',
render: (value) =>
value && (
<Tooltip placement="top" title={formatISO(value)}>
<span>{formatDistanceStrict(value, Date.now())} ago</span>
</Tooltip>
),
},
{
title: 'Completed At',
field: 'finishedOn',
render: (value) =>
value ? (
<Tooltip placement="top" title={formatISO(value)}>
<span>{formatDistanceStrict(value, Date.now())} ago</span>
</Tooltip>
) : (
'Not completed'
),
},
{
title: 'Delayed To',
field: 'delayedTo',
render: (value) =>
value && (
<Tooltip placement="top" title={formatISO(value)}>
<span>{formatDistanceStrict(value, Date.now())} later</span>
</Tooltip>
),
},
{
title: 'Progress',
field: 'progress',
render: (value) => <CircularProgress size={48} value={value} />,
},
{
title: 'Attempts',
field: 'attempts',
render: (val, field) =>
`${field.attemptsMade}/${field.attemptsTotal}`,
},
{
title: 'Actions',
field: 'actions',
render: (val, field) => (
<Button
size="small"
component={RouterLink}
startIcon={<VisibilityIcon />}
to={`${pathname}/${field.id}`}
>
Details
</Button>
),
},
].filter((column) => FIELDS[status].includes(column.field))}
rowsPerPage={pageSize}
pageSizeOptions={[5, 20, 50, 100]}
onChangeRowsPerPage={handleChangeRowsPerPage}
totalCount={jobs.data?.totalCount}
bulkActions={
<div>
{status === 'delayed' && (
<Fragment>
<Button onClick={handleBulkPromote}>Promote</Button>
<Button onClick={handleBulkRemove}>Remove</Button>
</Fragment>
)}
{status === 'failed' && (
<Button onClick={handleBulkRetry}>Retry</Button>
)}
</div>
}
actions={
<div>
{status === 'delayed' && (
<Button onClick={promoteAll}>Promote All</Button>
)}
{status === 'failed' && (
<Button onClick={retryAll}>Retry All</Button>
)}
</div>
}
data={data}
/>
</Grid>
</Grid>
);
}
Example #13
Source File: auctions.js From lnft with GNU Affero General Public License v3.0 | 4 votes |
setInterval(async () => {
try {
let { artworks } = await q(getFinishedAuctions, {
now: formatISO(new Date()),
});
for (let i = 0; i < artworks.length; i++) {
let artwork = artworks[i];
let { bid } = artwork;
await q(closeAuction, {
id: artwork.id,
artwork: {
auction_start: null,
auction_end: null,
},
});
console.log("finalizing auction for", artwork.slug);
console.log("reserve price", artwork.reserve_price);
try {
if (
!(bid && bid.psbt) ||
compareAsc(parseISO(bid.created_at), parseISO(artwork.auction_end)) >
0 ||
bid.amount < artwork.reserve_price
)
throw new Error("no bid");
let combined = combine(artwork.auction_tx, bid.psbt);
await check(combined);
let psbt = await sign(combined);
await broadcast(psbt);
await q(releaseToken, {
id: artwork.id,
owner_id: bid.user.id,
amount: bid.amount,
hash: psbt.extractTransaction().getId(),
psbt: psbt.toBase64(),
asset: artwork.asking_asset,
bid_id: bid.id,
type: "release",
});
console.log("released to high bidder");
} catch (e) {
console.log("couldn't release to bidder,", e.message);
await q(cancelBids, {
id: artwork.id,
start: artwork.auction_start,
end: artwork.auction_end,
});
if (artwork.has_royalty) continue;
try {
let psbt = await sign(artwork.auction_release_tx);
await broadcast(psbt);
console.log("released to current owner");
await q(releaseToken, {
id: artwork.id,
owner_id: artwork.owner.id,
amount: 0,
hash: psbt.extractTransaction().getId(),
psbt: psbt.toBase64(),
asset: artwork.asking_asset,
type: "return",
});
} catch (e) {
console.log("problem releasing", e);
}
}
}
} catch (e) {
console.log(e);
}
}, 2000);
Example #14
Source File: monitor.js From lnft with GNU Affero General Public License v3.0 | 4 votes |
updateTransactions = async (address, user_id) => {
await wait(() => !updating[address]);
updating[address] = true;
let { transactions } = await q(getLastTransactionsForAddress, { address });
let txns = (
await getTxns(
address,
transactions.map((tx) => tx.hash)
)
).reverse();
if (txns.length)
console.log(`updating ${txns.length} transactions for ${address}`);
for (let i = 0; i < txns.length; i++) {
let { txid, vin, vout, status } = txns[i];
let hex;
try {
hex =
hexcache[txid] || (await electrs.url(`/tx/${txid}/hex`).get().text());
hexcache[txid] = hex;
} catch (e) {
await sleep(3000);
hex =
hexcache[txid] || (await electrs.url(`/tx/${txid}/hex`).get().text());
hexcache[txid] = hex;
}
let total = {};
for (let j = 0; j < vin.length; j++) {
let { txid: prev, vout } = vin[j];
try {
let tx =
txcache[prev] || (await electrs.url(`/tx/${prev}`).get().json());
txcache[prev] = tx;
let { asset, value, scriptpubkey_address: a } = tx.vout[vout];
if (address === a) total[asset] = (total[asset] || 0) - parseInt(value);
} catch (e) {
console.log("problem finding input", prev, e);
}
}
for (let k = 0; k < vout.length; k++) {
let { asset, value, scriptpubkey_address: a } = vout[k];
if (address === a) total[asset] = (total[asset] || 0) + parseInt(value);
}
let assets = Object.keys(total);
for (let l = 0; l < assets.length; l++) {
let asset = assets[l];
let type = total[asset] < 0 ? "withdrawal" : "deposit";
if (
transactions.find(
(tx) =>
tx.user_id === user_id &&
tx.hash === txid &&
tx.asset === asset &&
tx.type === type
)
)
continue;
let transaction = {
address,
user_id,
asset,
type,
amount: total[asset],
hash: txid,
confirmed: status.confirmed,
hex,
json: JSON.stringify(txns[i]),
};
if (status.block_time)
transaction.created_at = formatISO(new Date(1000 * status.block_time));
try {
let {
insert_transactions_one: { id },
} = await q(createTransaction, { transaction });
console.log("inserting transaction", type, txid);
transactions.push(transaction);
} catch (e) {
console.log(e, type, txid, asset, user_id);
continue;
}
}
}
if (txns.length) console.log("done updating", address);
delete updating[address];
return txns;
}