From e5ca2683fff0a95d6092b8bfc8c0e67e0bc251d3 Mon Sep 17 00:00:00 2001 From: Angel Di Pinto Date: Mon, 31 Aug 2026 23:15:49 +0200 Subject: [PATCH 1/4] fix: normalizer banner start/end dates --- .../admin/comm/banners/BannersForm.svelte | 21 ++++++++++++++----- src/utils/dates.ts | 21 +++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/admin/comm/banners/BannersForm.svelte b/src/components/admin/comm/banners/BannersForm.svelte index be60e5cd..d2000761 100644 --- a/src/components/admin/comm/banners/BannersForm.svelte +++ b/src/components/admin/comm/banners/BannersForm.svelte @@ -2,7 +2,7 @@ import { actions } from "astro:actions"; import { locale, t } from "../../../../i18n/store"; - import { formatDate } from "../../../../utils/dates"; + import { endOfDay, formatDate, startOfDay } from "../../../../utils/dates"; import ActionableButton from "../../../library/buttons/ActionableButton.svelte"; import DateInput from "../../../library/inputs/DateInput.svelte"; import TextArea from "../../../library/inputs/TextArea.svelte"; @@ -23,9 +23,20 @@ let fieldErrors: FieldErrors = $state({}); - function handleDateSelect(date: Date) { + function normalizeDate(field: "startsAt" | "endsAt", date: Date) { const now = new Date(); - date.setHours(now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds()); + const isToday = + date.getFullYear() === now.getFullYear() && + date.getMonth() === now.getMonth() && + date.getDate() === now.getDate(); + + // Ends always run to the end of their day. Start dates run from their + // midnight, except when the day is today the current time is used so it + // passes the "no earlier than now" validation. DateInput binds the same + // object, so the normalized time is applied in place. + const normalized = field === "endsAt" ? endOfDay(date) : isToday ? now : startOfDay(date); + + date.setTime(normalized.getTime()); } async function submit() { @@ -110,7 +121,7 @@ placeholder={$t("pages.admin.comm.banners.fields.startDatePlaceholder")} error={fieldErrors.startsAt && $t(fieldErrors.startsAt, { date: formatDate(new Date(), $locale) })} - onInput={handleDateSelect} + onInput={(date) => normalizeDate("startsAt", date)} /> normalizeDate("endsAt", date)} /> diff --git a/src/utils/dates.ts b/src/utils/dates.ts index 7e17e017..b290015e 100644 --- a/src/utils/dates.ts +++ b/src/utils/dates.ts @@ -10,6 +10,27 @@ export function formatDate(date: Date, locale: Locale) { return date.toLocaleDateString(locale, options); } +/** + * The first instant of the day for the given date (midnight, 00:00). + * @param date The date to normalize + * @returns A new Date set to the start of the given day + */ +export function startOfDay(date: Date): Date { + return new Date(date.getFullYear(), date.getMonth(), date.getDate()); +} + +/** + * The last instant of the day for the given date (23:59:59.999). + * Derived from the start of the following day rather than hardcoding the end-of-day time. + * @param date The date to normalize + * @returns A new Date set to the end of the given day + */ +export function endOfDay(date: Date): Date { + // Start of the next day, minus a single millisecond + const nextDay = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1); + return new Date(nextDay.getTime() - 1); +} + /** * Get a date `n` ms from now * @param offset Milliseconds since now until future date From 916c81fefeea8cb59233b5e5543db0038abab304 Mon Sep 17 00:00:00 2001 From: Angel Di Pinto Date: Mon, 31 Aug 2026 23:23:04 +0200 Subject: [PATCH 2/4] fix: date greaterThan key --- src/i18n/locales/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 9556aaba..dc85c962 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -27,7 +27,7 @@ "expectedUrl": "No es una URL" }, "date": { - "greaterThan": "La fecha debe ser igual o mayor que el {{ date }}" + "greaterThan": "La fecha debe ser mayor que el {{ date }}" }, "file": { "maxAllowedSize": "Tamaño máximo permitido {{ size }}", From 4a4054d0af5daea55936717de72c5e49292b13c3 Mon Sep 17 00:00:00 2001 From: Angel Di Pinto Date: Mon, 31 Aug 2026 23:40:03 +0200 Subject: [PATCH 3/4] extract HomeBanners --- src/components/home/HomeBanners.astro | 28 +++++++++++++++++++++++++++ src/pages/[...locale]/index.astro | 19 ++---------------- 2 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 src/components/home/HomeBanners.astro diff --git a/src/components/home/HomeBanners.astro b/src/components/home/HomeBanners.astro new file mode 100644 index 00000000..68eedb80 --- /dev/null +++ b/src/components/home/HomeBanners.astro @@ -0,0 +1,28 @@ +--- +import HomeBanner from "./HomeBanner.svelte"; +import type { BannerRecord } from "../../repositories/banners"; + +interface Props { + banners: BannerRecord[]; +} + +const { banners } = Astro.props; +const { t } = Astro.locals; +--- + +{ + banners.length > 0 && ( +
+ {banners.map((banner) => ( + + ))} +
+ ) +} diff --git a/src/pages/[...locale]/index.astro b/src/pages/[...locale]/index.astro index ab862fe4..85ee71c9 100644 --- a/src/pages/[...locale]/index.astro +++ b/src/pages/[...locale]/index.astro @@ -8,7 +8,7 @@ import Stats from "../../components/home/Stats.astro"; import FeaturedPromoters from "../../components/home/FeaturedPromoters.astro"; import Blog from "../../components/home/Blog.astro"; import Matchfunding from "../../components/home/Matchfunding.astro"; -import HomeBanner from "../../components/home/HomeBanner.svelte"; +import Banners from "../../components/home/HomeBanners.astro"; // Disabled sections, re-enable the import along with the component below: // import Stories from "../../components/home/Stories.svelte"; // import Resources from "../../components/home/Resources.astro"; @@ -29,22 +29,7 @@ const activeBanners = await bannerRepository.getActive(); - { - activeBanners.length > 0 && ( -
- {activeBanners.map((banner) => ( - - ))} -
- ) - } + From 1cb3bc408f0cb1ff9f2cd9dd3766a4dfe6d8eea9 Mon Sep 17 00:00:00 2001 From: Angel Di Pinto Date: Tue, 1 Sep 2026 13:12:38 +0200 Subject: [PATCH 4/4] refactor: convert HomeBanners to svelte --- src/components/home/HomeBanners.astro | 28 -------------------------- src/components/home/HomeBanners.svelte | 26 ++++++++++++++++++++++++ src/pages/[...locale]/index.astro | 4 ++-- 3 files changed, 28 insertions(+), 30 deletions(-) delete mode 100644 src/components/home/HomeBanners.astro create mode 100644 src/components/home/HomeBanners.svelte diff --git a/src/components/home/HomeBanners.astro b/src/components/home/HomeBanners.astro deleted file mode 100644 index 68eedb80..00000000 --- a/src/components/home/HomeBanners.astro +++ /dev/null @@ -1,28 +0,0 @@ ---- -import HomeBanner from "./HomeBanner.svelte"; -import type { BannerRecord } from "../../repositories/banners"; - -interface Props { - banners: BannerRecord[]; -} - -const { banners } = Astro.props; -const { t } = Astro.locals; ---- - -{ - banners.length > 0 && ( -
- {banners.map((banner) => ( - - ))} -
- ) -} diff --git a/src/components/home/HomeBanners.svelte b/src/components/home/HomeBanners.svelte new file mode 100644 index 00000000..6376e3a1 --- /dev/null +++ b/src/components/home/HomeBanners.svelte @@ -0,0 +1,26 @@ + + +{#if banners.length > 0} +
+ {#each banners as banner} + + {/each} +
+{/if} diff --git a/src/pages/[...locale]/index.astro b/src/pages/[...locale]/index.astro index 85ee71c9..2d91b411 100644 --- a/src/pages/[...locale]/index.astro +++ b/src/pages/[...locale]/index.astro @@ -8,7 +8,7 @@ import Stats from "../../components/home/Stats.astro"; import FeaturedPromoters from "../../components/home/FeaturedPromoters.astro"; import Blog from "../../components/home/Blog.astro"; import Matchfunding from "../../components/home/Matchfunding.astro"; -import Banners from "../../components/home/HomeBanners.astro"; +import Banners from "../../components/home/HomeBanners.svelte"; // Disabled sections, re-enable the import along with the component below: // import Stories from "../../components/home/Stories.svelte"; // import Resources from "../../components/home/Resources.astro"; @@ -29,7 +29,7 @@ const activeBanners = await bannerRepository.getActive(); - +