diff --git a/app/webpacker/components/CompetitionsOverview/ListViewSection.js b/app/webpacker/components/CompetitionsOverview/ListViewSection.js index 2aaf6f47cb..fdd6fdecc7 100644 --- a/app/webpacker/components/CompetitionsOverview/ListViewSection.js +++ b/app/webpacker/components/CompetitionsOverview/ListViewSection.js @@ -4,6 +4,7 @@ import { } from 'semantic-ui-react'; import { BarLoader } from 'react-spinners'; +import { DateTime } from 'luxon'; import I18n from '../../lib/i18n'; import { computeAnnouncementStatus, @@ -21,7 +22,7 @@ import { } from '../../lib/utils/competition-table'; import { countries } from '../../lib/wca-data.js.erb'; import { adminCompetitionUrl, competitionUrl } from '../../lib/requests/routes.js.erb'; -import { dateRange } from '../../lib/utils/dates'; +import { dateRange, toRelativeOptions } from '../../lib/utils/dates'; function ListViewSection({ competitions, @@ -450,22 +451,42 @@ function RegistrationStatus({ comp, isLoading }) { return ( } - content={I18n.t('competitions.index.tooltips.registration.opens_in', { duration: comp.time_until_registration })} + content={ + I18n.t( + 'competitions.index.tooltips.registration.opens_in', + { + relativeDate: DateTime.fromISO(comp.registration_open).toRelative( + toRelativeOptions.default, + ), + }, + ) + } position="top center" size="tiny" /> ); } + if (comp.registration_status === 'past') { return ( } - content={I18n.t('competitions.index.tooltips.registration.closed', { days: I18n.t('common.days', { count: dayDifferenceFromToday(comp.start_date) }) })} + content={ + I18n.t( + 'competitions.index.tooltips.registration.closed', + { + relativeDate: DateTime.fromISO(comp.start_date).toRelative( + toRelativeOptions.roundUpAndAtBestDayPrecision, + ), + }, + ) + } position="top center" size="tiny" /> ); } + if (comp.registration_status === 'full') { return ( ); } + if (comp.registration_status === 'open') { return ( { switch (competingStatus) { @@ -24,10 +25,10 @@ const competingStatusIcon = (competingStatus) => { const registrationStatusIconText = (competition) => { if (competition.registration_status === 'not_yet_opened') { - return I18n.t('competitions.index.tooltips.registration.opens_in', { duration: DateTime.fromISO(competition.registration_open).toRelative({ locale: window.I18n.locale }) }); + return I18n.t('competitions.index.tooltips.registration.opens_in', { relativeDate: DateTime.fromISO(competition.registration_open).toRelative(toRelativeOptions.default) }); } if (competition.registration_status === 'past') { - return I18n.t('competitions.index.tooltips.registration.closed', { days: DateTime.fromISO(competition.start_date).toRelative({ locale: window.I18n.locale }) }); + return I18n.t('competitions.index.tooltips.registration.closed', { relativeDate: DateTime.fromISO(competition.start_date).toRelative(toRelativeOptions.roundUpAndAtBestDayPrecision) }); } if (competition.registration_status === 'full') { return I18n.t('competitions.index.tooltips.registration.full'); diff --git a/app/webpacker/lib/utils/dates.js b/app/webpacker/lib/utils/dates.js index 5523488962..83420c4871 100644 --- a/app/webpacker/lib/utils/dates.js +++ b/app/webpacker/lib/utils/dates.js @@ -1,9 +1,23 @@ import { DateTime, Interval } from 'luxon'; + // parameter name conventions: // - `luxonDate` for luxon DateTime objects // - `date` for date-only ISO strings (no time) // - `dateTime` for date-and-time ISO strings +export const toRelativeOptions = { + default: { + locale: window.I18n.locale, + }, + roundUpAndAtBestDayPrecision: { + locale: window.I18n.locale, + // don't be more precise than "days" (i.e. no hours/minutes/seconds) + unit: ['years', 'months', 'weeks', 'days'], + // round up, e.g. in 8 hours -> pads to 1 day 8 hours -> rounds to "in 1 day" + padding: 24 * 60 * 60 * 1000, + }, +}; + /// / luxon parameters export const areOnSameDate = ( diff --git a/config/locales/en.yml b/config/locales/en.yml index 3e2cc0faaf..bcba370284 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1575,10 +1575,10 @@ en: recent: "Last %{count} days" #context: the registration status icon registration: - opens_in: "Registration will open in %{duration}" - closed: "Registration has closed. Competition starts in %{days}" + opens_in: "Registration will open %{relativeDate}." + closed: "Registration has closed. Competition will start %{relativeDate}." open: "Registration is open!" - full: "Competitor limit reached, new registrations will go to the waiting list" + full: "Competitor limit reached, new registrations will go to the waiting list." no_more_comps: "No more competitions." no_comp_found: "No competitions found." no_comp_match: "We didn't find any competitions with %{events}! Try searching for fewer events."