From e29482de204da3baaabb098dc5d7d71dae5f91ef Mon Sep 17 00:00:00 2001 From: andres Date: Sun, 21 Apr 2024 23:25:11 +0200 Subject: [PATCH] chore: refactor, move things around for better maintainability fix: add semantic tags to account for screen readers --- src/components/podcast-episodes-list.tsx | 77 ------------------- .../podcast/podcast-episodes-list.tsx | 22 ++++++ .../podcast/podcast-episodes-table.tsx | 52 +++++++++++++ .../{ => podcast}/podcast-info-card.tsx | 6 +- .../{ => podcast}/podcast-preview-card.tsx | 0 src/components/ui/input/input.tsx | 16 ++++ src/components/{ => ui/layout}/layout.tsx | 2 +- .../{ => ui/spinner}/spinner.module.css | 0 src/components/{ => ui/spinner}/spinner.tsx | 0 src/components/ui/table/table.tsx | 2 +- src/components/{ => utils}/wrap.tsx | 0 src/pages/episode.tsx | 18 ++--- src/pages/home.tsx | 2 +- src/pages/podcast.tsx | 2 +- src/router.tsx | 4 +- src/utils/datetime.ts | 16 ++++ src/utils/index.ts | 1 + 17 files changed, 125 insertions(+), 95 deletions(-) delete mode 100644 src/components/podcast-episodes-list.tsx create mode 100644 src/components/podcast/podcast-episodes-list.tsx create mode 100644 src/components/podcast/podcast-episodes-table.tsx rename src/components/{ => podcast}/podcast-info-card.tsx (90%) rename src/components/{ => podcast}/podcast-preview-card.tsx (100%) create mode 100644 src/components/ui/input/input.tsx rename src/components/{ => ui/layout}/layout.tsx (94%) rename src/components/{ => ui/spinner}/spinner.module.css (100%) rename src/components/{ => ui/spinner}/spinner.tsx (100%) rename src/components/{ => utils}/wrap.tsx (100%) create mode 100644 src/utils/datetime.ts create mode 100644 src/utils/index.ts diff --git a/src/components/podcast-episodes-list.tsx b/src/components/podcast-episodes-list.tsx deleted file mode 100644 index ab9da62..0000000 --- a/src/components/podcast-episodes-list.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Link, useParams } from "react-router-dom"; -import { usePodcastQuery } from "../services/podcasts/podcast.hooks"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeadCell, - TableRow, -} from "./ui/table/table"; -import { useTitle } from "../hooks/use-title"; - -export function PodcastEpisodesList() { - const { podcastId } = useParams<{ podcastId: string }>(); - const { data: podcast } = usePodcastQuery(podcastId); - useTitle(podcast?.title ?? "Podcast"); - - return ( -
-
- Episodes: {podcast?.trackCount} -
-
- - - - Title - Date - Duration - - - - {podcast?.episodes?.map((episode) => { - const formattedDate = formatDate(episode.releaseDate); - const formattedDuration = formatDuration(episode.durationSeconds); - const url = `/podcast/${podcastId}/episode/${episode.id}`; - - return ( - - - - {episode.title} - - - {formattedDate} - - {formattedDuration} - - - ); - })} - -
-
-
- ); -} - -function formatDuration(duration?: number) { - if (!duration) { - return "N/A"; - } - const minutes = Math.floor(duration / 60); - const seconds = Math.floor(duration % 60); - return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; -} - -function formatDate(date: string) { - return new Date(date).toLocaleDateString("es-ES", { - month: "2-digit", - day: "2-digit", - year: "numeric", - }); -} diff --git a/src/components/podcast/podcast-episodes-list.tsx b/src/components/podcast/podcast-episodes-list.tsx new file mode 100644 index 0000000..5d9fc65 --- /dev/null +++ b/src/components/podcast/podcast-episodes-list.tsx @@ -0,0 +1,22 @@ +import { useParams } from "react-router-dom"; +import { usePodcastQuery } from "../../services/podcasts/podcast.hooks"; +import { useTitle } from "../../hooks/use-title"; +import { PodcastEpisodesTable } from "./podcast-episodes-table"; + +export function PodcastEpisodesList() { + const { podcastId } = useParams<{ podcastId: string }>(); + const { data: podcast } = usePodcastQuery(podcastId); + + useTitle(podcast?.title ?? "Podcast"); + + return ( +
+
+ Episodes: {podcast?.trackCount} +
+
+ +
+
+ ); +} diff --git a/src/components/podcast/podcast-episodes-table.tsx b/src/components/podcast/podcast-episodes-table.tsx new file mode 100644 index 0000000..cb4ae4a --- /dev/null +++ b/src/components/podcast/podcast-episodes-table.tsx @@ -0,0 +1,52 @@ +import { memo } from "react"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeadCell, + TableRow, +} from "../ui/table/table"; +import { formatDate, formatDuration } from "../../utils"; +import { Link } from "react-router-dom"; +import { Episode } from "../../services/rss-parser"; + +type Props = { + episodes: Episode[] | undefined; +}; + +export const PodcastEpisodesTable = memo(({ episodes }: Props) => { + return ( + + + + Title + Date + Duration + + + + {episodes?.map((episode) => { + const formattedDate = formatDate(episode.releaseDate); + const dateTime = new Date(episode.releaseDate).toISOString(); + const formattedDuration = formatDuration(episode.durationSeconds); + const url = `episode/${episode.id}`; + + return ( + + + + {episode.title} + + + + + + {formattedDuration} + + ); + })} + +
+ ); +}); diff --git a/src/components/podcast-info-card.tsx b/src/components/podcast/podcast-info-card.tsx similarity index 90% rename from src/components/podcast-info-card.tsx rename to src/components/podcast/podcast-info-card.tsx index 11ba3bd..5958dc5 100644 --- a/src/components/podcast-info-card.tsx +++ b/src/components/podcast/podcast-info-card.tsx @@ -1,5 +1,5 @@ import { Link, useParams } from "react-router-dom"; -import { Wrap } from "./wrap"; +import { Wrap } from "../utils/wrap"; type Props = { title: string; @@ -39,10 +39,10 @@ export function PodcastInfoCard({ by {author} -
+
Description:
diff --git a/src/components/podcast-preview-card.tsx b/src/components/podcast/podcast-preview-card.tsx similarity index 100% rename from src/components/podcast-preview-card.tsx rename to src/components/podcast/podcast-preview-card.tsx diff --git a/src/components/ui/input/input.tsx b/src/components/ui/input/input.tsx new file mode 100644 index 0000000..a3f696c --- /dev/null +++ b/src/components/ui/input/input.tsx @@ -0,0 +1,16 @@ +import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react"; +import { clsx } from "clsx"; + +type InputProps = ComponentPropsWithoutRef<"input">; +type InputRef = ElementRef<"input">; + +export const Input = forwardRef( + ({ className, ...props }, ref) => { + const classes = clsx( + "w-1/3 rounded-md border border-gray-300 p-2", + className, + ); + + return ; + }, +); diff --git a/src/components/layout.tsx b/src/components/ui/layout/layout.tsx similarity index 94% rename from src/components/layout.tsx rename to src/components/ui/layout/layout.tsx index 10e388f..8f629e1 100644 --- a/src/components/layout.tsx +++ b/src/components/ui/layout/layout.tsx @@ -1,5 +1,5 @@ import { Link, Outlet } from "react-router-dom"; -import { Spinner } from "./spinner"; +import { Spinner } from "../spinner/spinner"; import { useIsFetching } from "@tanstack/react-query"; export function Layout() { diff --git a/src/components/spinner.module.css b/src/components/ui/spinner/spinner.module.css similarity index 100% rename from src/components/spinner.module.css rename to src/components/ui/spinner/spinner.module.css diff --git a/src/components/spinner.tsx b/src/components/ui/spinner/spinner.tsx similarity index 100% rename from src/components/spinner.tsx rename to src/components/ui/spinner/spinner.tsx diff --git a/src/components/ui/table/table.tsx b/src/components/ui/table/table.tsx index 5006cb7..f7af7b2 100644 --- a/src/components/ui/table/table.tsx +++ b/src/components/ui/table/table.tsx @@ -35,7 +35,7 @@ export const TableHeadCell = forwardRef< ElementRef<"th">, ComponentPropsWithoutRef<"th"> >(({ children, className, ...rest }, ref) => { - const classes = clsx(className, "py-3 px-4"); + const classes = clsx(className, "py-3 px-4 text-start"); return ( diff --git a/src/components/wrap.tsx b/src/components/utils/wrap.tsx similarity index 100% rename from src/components/wrap.tsx rename to src/components/utils/wrap.tsx diff --git a/src/pages/episode.tsx b/src/pages/episode.tsx index 631aa12..366cef3 100644 --- a/src/pages/episode.tsx +++ b/src/pages/episode.tsx @@ -17,17 +17,17 @@ export function Episode() { useTitle(episode?.title ?? "Episode"); return ( -
-

{episode?.title}

-
+
+ {episode?.title} +
+
-
- -
-
+ + ); } diff --git a/src/pages/home.tsx b/src/pages/home.tsx index b455689..7da88a7 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { PodcastPreviewCard } from "../components/podcast-preview-card"; +import { PodcastPreviewCard } from "../components/podcast/podcast-preview-card"; import { useTopPodcastsQuery } from "../services/podcasts/podcast.hooks"; export function Home() { diff --git a/src/pages/podcast.tsx b/src/pages/podcast.tsx index 5ba8f3a..5c33843 100644 --- a/src/pages/podcast.tsx +++ b/src/pages/podcast.tsx @@ -1,6 +1,6 @@ import { Outlet, useParams } from "react-router-dom"; import { usePodcastQuery } from "../services/podcasts/podcast.hooks"; -import { PodcastInfoCard } from "../components/podcast-info-card"; +import { PodcastInfoCard } from "../components/podcast/podcast-info-card"; export function Podcast() { const { podcastId } = useParams<{ podcastId: string }>(); diff --git a/src/router.tsx b/src/router.tsx index 0aa7a31..dcbe5e5 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -2,8 +2,8 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { Home } from "./pages/home"; import { Podcast } from "./pages/podcast"; import { Episode } from "./pages/episode"; -import { PodcastEpisodesList } from "./components/podcast-episodes-list"; -import { Layout } from "./components/layout"; +import { PodcastEpisodesList } from "./components/podcast/podcast-episodes-list"; +import { Layout } from "./components/ui/layout/layout"; const router = createBrowserRouter([ { diff --git a/src/utils/datetime.ts b/src/utils/datetime.ts new file mode 100644 index 0000000..e3dcc20 --- /dev/null +++ b/src/utils/datetime.ts @@ -0,0 +1,16 @@ +export function formatDate(date: string) { + return new Date(date).toLocaleDateString("es-ES", { + month: "2-digit", + day: "2-digit", + year: "numeric", + }); +} + +export function formatDuration(duration?: number) { + if (!duration) { + return "N/A"; + } + const minutes = Math.floor(duration / 60); + const seconds = Math.floor(duration % 60); + return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..34541dd --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export * from "./datetime";