From cb0eed2847fe5f6a109860257475551a0057bd8d Mon Sep 17 00:00:00 2001 From: Julien Rousseau Date: Sun, 16 Feb 2025 17:40:45 -0500 Subject: [PATCH] added schedule page title and updated prettier config --- packages/web/.prettierrc.json | 3 - packages/web/astro.config.mjs | 6 +- packages/web/package.json | 17 +- packages/web/src/components/Calendar.astro | 622 ++++++++++--------- packages/web/src/components/Footer.astro | 159 ++--- packages/web/src/components/Header.astro | 184 +++--- packages/web/src/components/HeaderLink.astro | 64 +- packages/web/src/components/Title.astro | 24 + packages/web/src/components/TopArrow.astro | 40 +- packages/web/src/layouts/Layout.astro | 163 +++-- packages/web/src/layouts/UpdateTitle.astro | 18 + packages/web/src/lib/constants.ts | 10 +- packages/web/src/lib/formatters.ts | 4 +- packages/web/src/lib/maths.test.ts | 36 +- packages/web/src/lib/maths.ts | 30 +- packages/web/src/lib/utils.ts | 24 +- packages/web/src/pages/404.astro | 31 +- packages/web/src/pages/horaire.astro | 336 +++++----- packages/web/src/pages/robots.txt.ts | 4 +- packages/web/src/routes/router.ts | 37 +- packages/web/src/routes/routes.ts | 102 +-- packages/web/tailwind.config.mjs | 38 +- packages/web/tsconfig.json | 6 +- packages/web/vitest.config.ts | 2 +- 24 files changed, 1055 insertions(+), 905 deletions(-) delete mode 100644 packages/web/.prettierrc.json create mode 100644 packages/web/src/components/Title.astro create mode 100644 packages/web/src/layouts/UpdateTitle.astro diff --git a/packages/web/.prettierrc.json b/packages/web/.prettierrc.json deleted file mode 100644 index b3b052f..0000000 --- a/packages/web/.prettierrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "tabWidth": 2 -} diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs index 47fdd0d..a243995 100644 --- a/packages/web/astro.config.mjs +++ b/packages/web/astro.config.mjs @@ -7,6 +7,6 @@ import tailwind from "@astrojs/tailwind"; // https://astro.build/config export default defineConfig({ - site: "https://trampolineintercite.com", - integrations: [sitemap(), tailwind()], -}); \ No newline at end of file + site: "https://trampolineintercite.com", + integrations: [sitemap(), tailwind()], +}); diff --git a/packages/web/package.json b/packages/web/package.json index 7bdceee..6b98f21 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -6,7 +6,8 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "lint": "astro check", + "lint": "tsc --noEmit && prettier -c . && astro check", + "lint:fix": "prettier -w .", "astro": "astro", "test": "vitest" }, @@ -24,5 +25,19 @@ "prettier-plugin-astro": "^0.14.1", "tailwind-merge": "^3.0.1", "vitest": "^3.0.4" + }, + "prettier": { + "tabWidth": 4, + "plugins": [ + "prettier-plugin-astro" + ], + "overrides": [ + { + "files": "*.astro", + "options": { + "parser": "astro" + } + } + ] } } diff --git a/packages/web/src/components/Calendar.astro b/packages/web/src/components/Calendar.astro index cfabddd..44a9907 100644 --- a/packages/web/src/components/Calendar.astro +++ b/packages/web/src/components/Calendar.astro @@ -6,7 +6,7 @@ import { dateFormatter } from "../lib/formatters"; import { weekdays } from "../lib/constants"; interface Props { - activities: Activity[][]; + activities: Activity[][]; } const timeMin = 8; @@ -14,57 +14,60 @@ const timeMax = 22; const minutesPerTick = 60; type ActivityRender = Activity & { - inset: number; - adjacent?: { position: number; count: number }; + inset: number; + adjacent?: { position: number; count: number }; }; const activities = Astro.props.activities.map((w) => { - const weekday: ActivityRender[] = w - .sort((a, b) => decimal(a.time.start) - decimal(b.time.start)) - .map((a) => ({ ...a, inset: 0 })); - - const adjacents = new Set(); - for (let i = 1; i < weekday.length; i++) { - const adjacentCount = adjacents.size; - const current = weekday[i]; - const previous = weekday[i - 1]; - - if (overlap(previous, current)) { - if (decimal(current.time.start) - decimal(previous.time.start) < 0.5) { - adjacents.add(previous); - adjacents.add(current); - } else { - current.inset = previous.inset + 1; - } - } + const weekday: ActivityRender[] = w + .sort((a, b) => decimal(a.time.start) - decimal(b.time.start)) + .map((a) => ({ ...a, inset: 0 })); + + const adjacents = new Set(); + for (let i = 1; i < weekday.length; i++) { + const adjacentCount = adjacents.size; + const current = weekday[i]; + const previous = weekday[i - 1]; + + if (overlap(previous, current)) { + if ( + decimal(current.time.start) - decimal(previous.time.start) < + 0.5 + ) { + adjacents.add(previous); + adjacents.add(current); + } else { + current.inset = previous.inset + 1; + } + } - if ( - adjacents.size === adjacentCount || - (adjacents.size > 0 && i === weekday.length - 1) - ) { - let position = 0; - for (const activity of adjacents) { - activity.adjacent = { position, count: adjacents.size }; - position++; - } - adjacents.clear(); + if ( + adjacents.size === adjacentCount || + (adjacents.size > 0 && i === weekday.length - 1) + ) { + let position = 0; + for (const activity of adjacents) { + activity.adjacent = { position, count: adjacents.size }; + position++; + } + adjacents.clear(); + } } - } - return weekday; + return weekday; }); // NOTE: CSS grid does not allow to have fractional spans. // Therefore, it is needed to scale it by the smallest time interval in every activity. const minutes = [ - ...activities.flat(2).reduce( - (set, activity) => { - set.add(activity.time.start.minute); - set.add(activity.time.end.minute); - return set; - }, - new Set([minutesPerTick]), - ), + ...activities.flat(2).reduce( + (set, activity) => { + set.add(activity.time.start.minute); + set.add(activity.time.end.minute); + return set; + }, + new Set([minutesPerTick]), + ), ].filter((min) => min !== 0); const minuteScale = gcd(minutes); const gridScale = minutesPerTick / minuteScale; @@ -72,302 +75,319 @@ const timeStep = minutesPerTick / 60; const cellScale = gridScale / timeStep; function decimal(time: Time): number { - return time.hour + time.minute / 60; + return time.hour + time.minute / 60; } function overlap(a: Activity, b: Activity): boolean { - return ( - decimal(a.time.start) <= decimal(b.time.start) && - decimal(b.time.start) < decimal(a.time.end) - ); + return ( + decimal(a.time.start) <= decimal(b.time.start) && + decimal(b.time.start) < decimal(a.time.end) + ); } function formatTime(time: number | Time): string { - const hour = typeof time === "number" ? Math.floor(time) : time.hour; - const minutes = - typeof time === "number" ? Math.round((time - hour) * 60) : time.minute; - return `${hour.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`; + const hour = typeof time === "number" ? Math.floor(time) : time.hour; + const minutes = + typeof time === "number" ? Math.round((time - hour) * 60) : time.minute; + return `${hour.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`; } /*** * Changes from a 'sunday to saturday' week index to a 'monday to sunday' week index. */ function shiftWeekday(weekday: number) { - return (weekday + 6) % 7; + return (weekday + 6) % 7; } ---
-
- { - // --- Weekday column headers --- - weekdays.map((weekday, i) => ( -
- {weekday[0]} -
- )) - } - { - // --- Vertical grid lines --- - Array.from({ length: 8 }).map((_, col) => ( -
- )) - } - { - // --- Horizontal grid lines --- - Array.from({ length: (timeMax - timeMin) / timeStep + 1 }).map( - (_, row) => ( -
- ), - ) - } - { - // --- Time labels on side of grid --- - Array.from({ length: (timeMax - timeMin) / timeStep + 1 }).map((_, i) => ( -
- - {formatTime(timeMin + i * timeStep)} - -
- )) - } +
+ { + // --- Weekday column headers --- + weekdays.map((weekday, i) => ( +
+ {weekday[0]} +
+ )) + } + { + // --- Vertical grid lines --- + Array.from({ length: 8 }).map((_, col) => ( +
+ )) + } + { + // --- Horizontal grid lines --- + Array.from({ length: (timeMax - timeMin) / timeStep + 1 }).map( + (_, row) => ( +
+ ), + ) + } + { + // --- Time labels on side of grid --- + Array.from({ length: (timeMax - timeMin) / timeStep + 1 }).map( + (_, i) => ( +
+ + {formatTime(timeMin + i * timeStep)} + +
+ ), + ) + } - { - // --- Activity squares --- - activities.map((list, weekday) => - list.map((activity) => { - const width = - (activity.adjacent - ? 100 / activity.adjacent.count - : 100 - 10 * activity.inset) + "%"; - const marginLeft = activity.adjacent - ? (activity.adjacent.position / activity.adjacent.count) * 100 + "%" - : undefined; - - return ( -
0 && - "border-l-[1px] border-t-[1px] border-white", - ])} - data-title={activity.title} - data-subtitle={activity.subtitle} - data-start-time={formatTime(activity.time.start)} - data-end-time={formatTime(activity.time.end)} - data-first-lesson={dateFormatter.format(activity.lessons.first)} - data-last-lesson={dateFormatter.format(activity.lessons.last)} - data-amount={activity.price.toFixed(2)} - data-lessons={activity.lessons.count} - data-weekday={weekdays[weekday]} - > - - - - {activity.title} - - {activity.subtitle?.length && ( - - {activity.subtitle} - - )} - -
- ); - }), - ) - } -
+ { + // --- Activity squares --- + activities.map((list, weekday) => + list.map((activity) => { + const width = + (activity.adjacent + ? 100 / activity.adjacent.count + : 100 - 10 * activity.inset) + "%"; + const marginLeft = activity.adjacent + ? (activity.adjacent.position / + activity.adjacent.count) * + 100 + + "%" + : undefined; + + return ( +
0 && + "border-l-[1px] border-t-[1px] border-white", + ])} + data-title={activity.title} + data-subtitle={activity.subtitle} + data-start-time={formatTime(activity.time.start)} + data-end-time={formatTime(activity.time.end)} + data-first-lesson={dateFormatter.format( + activity.lessons.first, + )} + data-last-lesson={dateFormatter.format( + activity.lessons.last, + )} + data-amount={activity.price.toFixed(2)} + data-lessons={activity.lessons.count} + data-weekday={weekdays[weekday]} + > + + + + {activity.title} + + {activity.subtitle?.length && ( + + {activity.subtitle} + + )} + +
+ ); + }), + ) + } +
+ diff --git a/packages/web/src/components/HeaderLink.astro b/packages/web/src/components/HeaderLink.astro index 345411d..d50d8aa 100644 --- a/packages/web/src/components/HeaderLink.astro +++ b/packages/web/src/components/HeaderLink.astro @@ -7,38 +7,38 @@ const route = Astro.props; ---
- - {route.label} - + + {route.label} + - { - "subroutes" in route && ( - diff --git a/packages/web/src/components/Title.astro b/packages/web/src/components/Title.astro new file mode 100644 index 0000000..780ce2b --- /dev/null +++ b/packages/web/src/components/Title.astro @@ -0,0 +1,24 @@ +--- +interface Props { + title: string; + subtitle?: string; +} + +const { title, subtitle = "" } = Astro.props; +--- + +

+ + {title} + + { + subtitle.length > 0 && ( + <> + · + {subtitle} + + ) + } +

+ +
diff --git a/packages/web/src/components/TopArrow.astro b/packages/web/src/components/TopArrow.astro index 2cb6041..bbc44f9 100644 --- a/packages/web/src/components/TopArrow.astro +++ b/packages/web/src/components/TopArrow.astro @@ -1,29 +1,29 @@ diff --git a/packages/web/src/layouts/Layout.astro b/packages/web/src/layouts/Layout.astro index 1db77c3..43bec85 100644 --- a/packages/web/src/layouts/Layout.astro +++ b/packages/web/src/layouts/Layout.astro @@ -5,17 +5,18 @@ import TopArrow from "../components/TopArrow.astro"; import type { Routes } from "../routes/router"; import { routerLabels } from "../routes/router"; import { asArray } from "../lib/utils"; +import UpdateTitle from "./UpdateTitle.astro"; type Path = Routes<["footer-only", "disabled"]>; interface Props { - path: Path | Path[]; + path: Path | Path[]; } const paths = asArray(Astro.props.path); const labels = paths.reduce( - (acc, path) => ({ ...acc, [path]: routerLabels[path] }), - {} as Record, + (acc, path) => ({ ...acc, [path]: routerLabels[path] }), + {} as Record, ); const label = labels[paths[0]]; @@ -24,94 +25,84 @@ const title = "Trampoline Intercité" + (label ? ` | ${label}` : ""); - - - - - - - - - - - - - - {title} - - - -
-
- -
-