From 8d7fe4f1d757f39dc349356784c384ba33f90f9b Mon Sep 17 00:00:00 2001 From: Jean Livino Date: Mon, 22 Jul 2024 20:09:16 -0300 Subject: [PATCH] refactor: improve performance and layout of agenda pages (#72) * refactor: improve performance and layout of agenda pages * ci: add prettier * feat: mid day split in home * feat: Add keynote and room labels to speaker card (#73) * feat: Add keynote and room labels to speaker card * style: improve spacing of button group --- prettier.config.js | 15 +++ src/api/types.ts | 39 ++++--- src/components/ButtonGroup/ButtonGroup.tsx | 34 ++++-- src/components/LinkAgenda/LinkAgenda.tsx | 23 ++-- .../SpeakerCard/SpeakerCard.stories.tsx | 6 + src/components/SpeakerCard/SpeakerCard.tsx | 20 +++- src/components/SpeakerCard/types.ts | 3 + .../SpeakerSection/SpeakerSection.tsx | 11 +- src/hooks/useAgenda.ts | 12 +- src/lib/talks.ts | 18 +++ src/pages/HomePage/HomePage.tsx | 48 ++++++-- src/pages/MyAgenda/MyAgenda.tsx | 104 +++++++++++------- 12 files changed, 237 insertions(+), 96 deletions(-) create mode 100644 prettier.config.js create mode 100644 src/lib/talks.ts diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..e4b4a92 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,15 @@ +export default { + semi: true, + trailingComma: "es5", + singleQuote: false, + printWidth: 100, + tabWidth: 2, + useTabs: false, + bracketSpacing: true, + arrowParens: "always", + endOfLine: "lf", + quoteProps: "as-needed", + jsxSingleQuote: false, + htmlWhitespaceSensitivity: "strict", + vueIndentScriptAndStyle: false, +}; diff --git a/src/api/types.ts b/src/api/types.ts index e4b76e1..5759898 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,26 +1,29 @@ -export type TalkCategory = 'Convida' | 'Frontend' | 'Comunidades'; +export type TalkCategory = "Convida" | "Frontend" | "Comunidades"; -export interface Agenda extends Record{ - [type: string]: Palestra[] +export interface Agenda extends Record { + [type: string]: Palestra[]; } export interface Palestra { - speaker: Speaker - keynote?: boolean - room: string - hour: string - id: number - title: string - tags: string[] + speaker: Speaker; + keynote?: boolean; + room: string; + hour: string; + id: number; + title: string; + tags: string[]; } -export interface Speaker { - role: string - company: string - bio: string - social_link: string - id: number - title: string - image: string +export interface PalestraWithRoom extends Palestra { + room: string; } +export interface Speaker { + role: string; + company: string; + bio: string; + social_link: string; + id: number; + title: string; + image: string; +} diff --git a/src/components/ButtonGroup/ButtonGroup.tsx b/src/components/ButtonGroup/ButtonGroup.tsx index 45e4b20..157f8cb 100644 --- a/src/components/ButtonGroup/ButtonGroup.tsx +++ b/src/components/ButtonGroup/ButtonGroup.tsx @@ -9,13 +9,33 @@ export const ButtonGroup = (props: Props) => { if (props.onChange) { props.onChange(newMode); } - } + }; return ( -
- - - - +
+ + + +
); -}; \ No newline at end of file +}; diff --git a/src/components/LinkAgenda/LinkAgenda.tsx b/src/components/LinkAgenda/LinkAgenda.tsx index 85019d9..ccb476d 100644 --- a/src/components/LinkAgenda/LinkAgenda.tsx +++ b/src/components/LinkAgenda/LinkAgenda.tsx @@ -1,14 +1,23 @@ +import { cn } from "@/lib/utils"; import { Link } from "react-router-dom"; -export const LinkAgenda = () => { +const linkCN = cn("text-white underline text-nowrap"); + +export const LinkAgenda = ({ isMyAgenda }: { isMyAgenda?: boolean }) => { return ( -
- - Acontecendo agora +
+ {/* + Acontecendo agora + */} + {isMyAgenda ? ( + + Agenda completa - - Ver minha agenda > + ) : ( + + Ver minha agenda > -
+ )} +
); }; diff --git a/src/components/SpeakerCard/SpeakerCard.stories.tsx b/src/components/SpeakerCard/SpeakerCard.stories.tsx index 54c2e3e..a14f776 100644 --- a/src/components/SpeakerCard/SpeakerCard.stories.tsx +++ b/src/components/SpeakerCard/SpeakerCard.stories.tsx @@ -16,6 +16,9 @@ const meta: Meta = { role: { control: "text" }, onChangeMode: { action: "mode changed" }, hour: { control: "text" }, + keynote: { control: "boolean" }, + showRoom: { control: "boolean" }, + room: { control: "text" }, isSaved: { control: "boolean" }, }, } satisfies Meta; @@ -31,6 +34,9 @@ export const Default: Story = { name: "Abraão", role: "Developer", hour: "10:00", + keynote: false, + showRoom: false, + room: "Test Room", isSaved: true, }, }; diff --git a/src/components/SpeakerCard/SpeakerCard.tsx b/src/components/SpeakerCard/SpeakerCard.tsx index 85db8c5..1dfc8d9 100644 --- a/src/components/SpeakerCard/SpeakerCard.tsx +++ b/src/components/SpeakerCard/SpeakerCard.tsx @@ -16,9 +16,12 @@ export const SpeakerCard = ({ role, hour, isSaved, + keynote, + showRoom, + room, onChangeMode, }: SpeakerCardProps) => { - /* const [isPast, setIsPast] = useState(false); + /* const [isPast, setIsPast] = useState(false); useEffect(() => { const now = new Date(); @@ -35,10 +38,23 @@ export const SpeakerCard = ({ return ( + {showRoom && room && ( + + Trilha: {room} + + )}
{hour}

{label} + {keynote && ( + + Keynote + + )}

@@ -61,7 +77,7 @@ export const SpeakerCard = ({ imageUrl={imageUrl} imageFallback={imageFallback} /> - {/* {isPast && } */} + {/* {isPast && } */}
); diff --git a/src/components/SpeakerCard/types.ts b/src/components/SpeakerCard/types.ts index 465e43d..0382ebe 100644 --- a/src/components/SpeakerCard/types.ts +++ b/src/components/SpeakerCard/types.ts @@ -5,5 +5,8 @@ export interface SpeakerCardProps extends ProfileCardProps { tags: string[]; hour: string; isSaved: boolean; + keynote?: boolean; + showRoom?: boolean; + room?: string; onChangeMode: (mode: boolean) => void; } diff --git a/src/components/SpeakerSection/SpeakerSection.tsx b/src/components/SpeakerSection/SpeakerSection.tsx index c92889e..1d10902 100644 --- a/src/components/SpeakerSection/SpeakerSection.tsx +++ b/src/components/SpeakerSection/SpeakerSection.tsx @@ -2,8 +2,12 @@ import { SpeakerCard } from "@/components/SpeakerCard"; import { Button } from "@/components/ui/button"; import { SpeakerSectionProps } from "./types"; -export const SpeakerSection = ({ sectionTitle, liveTalk, savedCardIds, handleCardModeChange }: SpeakerSectionProps) => { - +export const SpeakerSection = ({ + sectionTitle, + liveTalk, + savedCardIds, + handleCardModeChange, +}: SpeakerSectionProps) => { return (
{liveTalk && ( @@ -23,10 +27,11 @@ export const SpeakerSection = ({ sectionTitle, liveTalk, savedCardIds, handleCar imageFallback={liveTalk.speaker.title[0]} name={liveTalk.speaker.title} role={liveTalk.speaker.role} + keynote={liveTalk.keynote} /> )}
); -}; \ No newline at end of file +}; diff --git a/src/hooks/useAgenda.ts b/src/hooks/useAgenda.ts index acf766c..46bf29a 100644 --- a/src/hooks/useAgenda.ts +++ b/src/hooks/useAgenda.ts @@ -1,9 +1,5 @@ -import { getAgenda } from '@/api'; -import {useQuery} from 'react-query'; +import { getAgenda } from "@/api"; +import { useQuery } from "react-query"; - -export const useAgenda = () =>{ - const data = useQuery(['agenda'], {queryFn: getAgenda, staleTime: Infinity}); - - return data; -} \ No newline at end of file +export const useAgenda = () => + useQuery(["agenda"], { queryFn: getAgenda, staleTime: Infinity }); diff --git a/src/lib/talks.ts b/src/lib/talks.ts new file mode 100644 index 0000000..d57ada7 --- /dev/null +++ b/src/lib/talks.ts @@ -0,0 +1,18 @@ +import { Agenda, Palestra, PalestraWithRoom } from "@/api/types"; + +export const splitTalksToMidDay = (talks: PalestraWithRoom[] | Palestra[]) => { + const talksBeforeMidDay = talks.filter( + (item) => item.hour.split(":")[0] < "12", + ); + const talksAfterMidDay = talks.filter( + (item) => item.hour.split(":")[0] >= "12", + ); + + return { talksBeforeMidDay, talksAfterMidDay }; +}; + +export const agendaResponseToTalks = (data: Agenda): PalestraWithRoom[] => { + return Object.entries(data).reduce((acc, [key, value]) => { + return acc.concat(value.map((talk) => ({ ...talk, room: key }))); + }, [] as PalestraWithRoom[]); +}; diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx index 0051c9f..f58b6b4 100644 --- a/src/pages/HomePage/HomePage.tsx +++ b/src/pages/HomePage/HomePage.tsx @@ -9,20 +9,28 @@ import { SpeakerCard } from "@/components/SpeakerCard"; import { useAgenda } from "@/hooks/useAgenda"; import { useSavedTalks } from "@/hooks/useSavedTalks"; import { Mode } from "@/components/ButtonGroup/types"; +import { splitTalksToMidDay } from "@/lib/talks"; export const HomePage = () => { const { data } = useAgenda(); const { savedCardIds, toggleSaveCard } = useSavedTalks(); - - const [currentMode, setCurrentMode] = useState("Frontend"); + + const [currentMode, setCurrentMode] = useState(undefined); const keys = ["Frontend", "Convida", "FireBanking", "Comunidades"]; - const allTalks = keys .reduce((acc: Palestra[], key) => { + const allTalks = keys + .reduce((acc: Palestra[], key) => { return acc.concat(data?.[key] || []); - }, []).sort((a, b) => a.hour.localeCompare(b.hour)); + }, []) + .sort((a, b) => a.hour.localeCompare(b.hour)); + + const filteredTalks = allTalks.filter( + (talk) => !currentMode || talk.room === currentMode.toLowerCase(), + ); - const filteredTalks = allTalks.filter((talk) => talk.room === currentMode.toLowerCase()); + const { talksBeforeMidDay, talksAfterMidDay } = + splitTalksToMidDay(filteredTalks); return (
@@ -34,9 +42,28 @@ export const HomePage = () => {
- -
- {filteredTalks.map((talk) => ( + +
+ + {talksBeforeMidDay.map((talk) => ( + toggleSaveCard(talk.id)} + /> + ))} + + {talksAfterMidDay.map((talk) => ( { imageFallback={talk.speaker.title[0]} name={talk.speaker.title} role={talk.speaker.role} + room={talk.room} + keynote={talk.keynote} + showRoom={!currentMode} isSaved={savedCardIds.includes(talk.id)} onChangeMode={() => toggleSaveCard(talk.id)} /> ))} +
-
); }; diff --git a/src/pages/MyAgenda/MyAgenda.tsx b/src/pages/MyAgenda/MyAgenda.tsx index afcf833..9da9b52 100644 --- a/src/pages/MyAgenda/MyAgenda.tsx +++ b/src/pages/MyAgenda/MyAgenda.tsx @@ -1,4 +1,3 @@ -import { Palestra } from "@/api/types"; import { DeadComponent } from "@/components/DeadComponent"; import { Header } from "@/components/Header"; import { LinkAgenda } from "@/components/LinkAgenda"; @@ -6,7 +5,9 @@ import { ReturnButton } from "@/components/ReturnButton"; import { SpeakerCard } from "@/components/SpeakerCard"; import { useAgenda } from "@/hooks/useAgenda"; import { useSavedTalks } from "@/hooks/useSavedTalks"; +import { agendaResponseToTalks, splitTalksToMidDay } from "@/lib/talks"; import { useMemo } from "react"; +import { Link } from "react-router-dom"; export const MyAgenda = () => { const { data } = useAgenda(); @@ -14,62 +15,81 @@ export const MyAgenda = () => { const allTalks = useMemo(() => { if (!data) return []; - const sections = Object.values(data).flat(); - return sections.flat(); + return agendaResponseToTalks(data); }, [data]); const savedTalks = useMemo(() => { - return allTalks.filter((item: Palestra) => savedCardIds.includes(item.id)); + return allTalks + .filter((item) => savedCardIds.includes(item.id)) + .sort((a, b) => a.hour.localeCompare(b.hour)); }, [allTalks, savedCardIds]); - const talksByHour = useMemo(() => { - const grouped: Record = {}; - savedTalks.forEach((talk) => { - if (!grouped[talk.hour]) { - grouped[talk.hour] = []; - } - grouped[talk.hour].push(talk); - }); - return grouped; - }, [savedTalks]); + const { talksBeforeMidDay, talksAfterMidDay } = + splitTalksToMidDay(savedTalks); + + const hasSavedTalks = useMemo(() => savedTalks.length > 0, [savedTalks]); return (
- -
-
- +
- {Object.keys(talksByHour).length > 0 ? ( - Object.keys(talksByHour).map((hour) => ( -
-
- {talksByHour[hour].map((talk) => ( - toggleSaveCard(talk.id)} - /> - ))} -
-
- )) - ) : ( -

Nenhuma palestra salva para exibir.

+ {!hasSavedTalks && ( +

+ Nenhuma palestra salva para exibir.
+ + Ver todas as palestras. + +

)} - + {hasSavedTalks && ( +
+ + + {talksBeforeMidDay.map((talk) => ( + toggleSaveCard(talk.id)} + /> + ))} + + + {talksAfterMidDay.map((talk) => ( + toggleSaveCard(talk.id)} + /> + ))} + + +
+ )}
); };