Skip to content

Commit

Permalink
Merge branch 'main' into feat/home
Browse files Browse the repository at this point in the history
  • Loading branch information
RodrigoNet0 authored Jul 20, 2024
2 parents d819849 + 1aa914d commit e54afab
Show file tree
Hide file tree
Showing 17 changed files with 670 additions and 255 deletions.
3 changes: 2 additions & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const config: StorybookConfig = {
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
"@storybook/addon-themes"
"@storybook/addon-themes",
"@storybook/addon-remix-react-router",
],
framework: {
name: "@storybook/react-vite",
Expand Down
640 changes: 390 additions & 250 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"lucide-react": "^0.408.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-query": "^3.39.3",
"react-router": "^6.24.1",
"react-router-dom": "^6.24.1",
"react-query": "^3.39.3",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7"
},
Expand Down Expand Up @@ -63,6 +64,7 @@
"plop": "^4.0.1",
"postcss": "^8",
"storybook": "^8.2.2",
"storybook-addon-remix-react-router": "^3.0.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2",
"vite": "^5.3.1",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProfileCard/ProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const ProfileCard = ({ name, role, imageUrl, imageFallback }: ProfileCard
return (
<div className="flex items-center gap-2">
<Avatar className="w-10 h-10 rounded-full flex justify-center border border-gray-50 bg-white ">
<AvatarImage alt={name} src={imageUrl} />
<AvatarImage className="object-cover" alt={name} src={imageUrl} />
<AvatarFallback className="font-semibold text-lg justify-center items-center flex" >{imageFallback}</AvatarFallback>
</Avatar>
<div className="flex flex-col">
Expand Down
20 changes: 20 additions & 0 deletions src/components/ReturnButton/ReturnButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Meta } from '@storybook/react';
import { reactRouterParameters, withRouter } from 'storybook-addon-remix-react-router';
import { ReturnButton } from './ReturnButton';

export default {
title: 'Components/ReturnButton',
component: ReturnButton,
decorators: [withRouter]
} as Meta;

export const Default = {
parameters: {
reactRouter: reactRouterParameters({
routing: {
path: '/',
handle: 'Home Page',
}
})
}
}
13 changes: 13 additions & 0 deletions src/components/ReturnButton/ReturnButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import ReturnButton from './ReturnButton';

test('renders ReturnButton', () => {
const { getByRole } = render(
<BrowserRouter>
<ReturnButton />
</BrowserRouter>
);
const linkElement = getByRole('link');
expect(linkElement).toBeInTheDocument();
});
15 changes: 15 additions & 0 deletions src/components/ReturnButton/ReturnButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// src/components/ReturnButton/ReturnButton.tsx
import { Link } from 'react-router-dom';
import { ChevronLeft } from 'lucide-react';

export const ReturnButton = () => {
return (
<>
<Link to="/" className="self-start">
<ChevronLeft size={28} className="text-[#A855F7] self-start absolute" />
</Link>
</>
);
}

export default ReturnButton;
3 changes: 3 additions & 0 deletions src/components/ReturnButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ReturnButton } from "./ReturnButton"

export { ReturnButton }
29 changes: 29 additions & 0 deletions src/hooks/useSavedTalks.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { renderHook, act } from "@testing-library/react";
import { useSavedTalks } from "./useSavedTalks";

describe("useSavedTalks", () => {
beforeEach(() => {
localStorage.clear();
});

it("should retrieve saved talks from local storage", () => {
localStorage.setItem("savedTalks", JSON.stringify([1, 2, 3]));
const { result } = renderHook(() => useSavedTalks());
expect(result.current.savedCardIds).toEqual([1, 2, 3]);
});

it("should save a new talk when toggleSaveCard is called", () => {
const { result } = renderHook(() => useSavedTalks());
act(() => { result.current.toggleSaveCard(1) });
expect(result.current.savedCardIds).toEqual([1]);
expect(JSON.parse(localStorage.getItem("savedTalks")!)).toEqual([1]);
});

it("should remove a saved talk when toggleSaveCard is called", () => {
localStorage.setItem("savedTalks", JSON.stringify([1]));
const { result } = renderHook(() => useSavedTalks());
act(() => { result.current.toggleSaveCard(1) });
expect(result.current.savedCardIds).toEqual([]);
expect(JSON.parse(localStorage.getItem("savedTalks")!)).toEqual([]);
});
});
25 changes: 25 additions & 0 deletions src/hooks/useSavedTalks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, useEffect, useCallback } from 'react';

const LOCAL_STORAGE_KEY = 'savedTalks';

export const useSavedTalks = () => {
const [savedCardIds, setSavedCardIds] = useState<number[]>([]);

useEffect(() => {
const storedIds = localStorage.getItem(LOCAL_STORAGE_KEY);
if (storedIds) {
setSavedCardIds(JSON.parse(storedIds));
}
}, []);

const toggleSaveCard = useCallback((cardId: number) => {
setSavedCardIds((prevIds) => {
const isSaved = prevIds.includes(cardId);
const newIds = isSaved ? prevIds.filter((id) => id !== cardId) : [...prevIds, cardId];
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newIds));
return newIds;
});
}, []);

return { savedCardIds, toggleSaveCard };
};
6 changes: 6 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,9 @@
}
}



.self-start {
align-self: self-start;
}
main
28 changes: 28 additions & 0 deletions src/pages/LivePage/LivePage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/react";
import { LivePage } from ".";
import { QueryClient, QueryClientProvider } from "react-query";
import { BrowserRouter } from "react-router-dom";
import { Story } from "@storybook/blocks";

export type Story = StoryObj<typeof LivePage>;

const queryClient = new QueryClient();

const meta: Meta<typeof LivePage> = {
title: "Pages/Live Page",
tags: ["autodocs"],
component: LivePage,
decorators: [
(Story) => (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Story />
</BrowserRouter>
</QueryClientProvider>
),
],
};

export default meta;

export const Default: Story = {};
17 changes: 17 additions & 0 deletions src/pages/LivePage/LivePage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { render } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { BrowserRouter } from 'react-router-dom';
import { LivePage } from './LivePage';

test('renders LivePage', () => {
const queryClient = new QueryClient();
const { getByText } = render(
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<LivePage />
</BrowserRouter>
</QueryClientProvider>
);
const element = getByText(/Acontecendo agora:/i);
expect(element).toBeInTheDocument();
});
89 changes: 89 additions & 0 deletions src/pages/LivePage/LivePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Header } from "@/components/Header";
import { ReturnButton } from "@/components/ReturnButton";
import { SpeakerSection } from "@/components/SpeakerSection";
import { Separator } from "@/components/ui/separator";
import { useAgenda } from "@/hooks/useAgenda";
import { useSavedTalks } from "@/hooks/useSavedTalks";
import { getLiveTalk, getNextTalk } from "./utils/talks";

export const LivePage = () => {
const { data } = useAgenda();
const { savedCardIds, toggleSaveCard } = useSavedTalks();
const now = new Date();

const liveFrontendTalk = getLiveTalk(data?.Frontend || [], now);
const liveConvidadosTalk = getLiveTalk(data?.Convida || [], now);
const liveComunidadesTalk = getLiveTalk(data?.Comunidades || [], now);

const nextFrontendTalk = getNextTalk(data?.Frontend || [], now);
const nextComunidadesTalk = getNextTalk(data?.Comunidades || [], now);
const nextConvidadosTalk = getNextTalk(data?.Convida || [], now);

return (
<section className="container mt-12 flex flex-col items-center">
<ReturnButton/>
<Header label="Agenda do Evento" />
<h2 className="text-white text-center mt-8 text-xl font-semibold">
Acontecendo agora:
</h2>
{liveFrontendTalk || liveConvidadosTalk || liveComunidadesTalk ? (
<>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={liveFrontendTalk}
savedCardIds={savedCardIds}
sectionTitle="Front-End CE"
/>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={liveConvidadosTalk}
savedCardIds={savedCardIds}
sectionTitle="Convida"
/>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={liveComunidadesTalk}
savedCardIds={savedCardIds}
sectionTitle="Comunidades"
/>
</>
) : (
<p className="text-white text-center mt-4">
Se avexe não, já já tem mais! 🙂 🥳
</p>
)}
<Separator className="w-20 mt-8 h-1 rounded-2xl self-center bg-[#7c3aed]" />
<section className="mb-12 mt-8">
<h3 className="text-xl font-semibold text-center text-white">
Próximas Talks
</h3>
{nextFrontendTalk || nextConvidadosTalk || nextComunidadesTalk ? (
<>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={nextFrontendTalk}
savedCardIds={savedCardIds}
sectionTitle="Front-End CE"
/>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={nextConvidadosTalk}
savedCardIds={savedCardIds}
sectionTitle="Convida"
/>
<SpeakerSection
handleCardModeChange={toggleSaveCard}
liveTalk={nextComunidadesTalk}
savedCardIds={savedCardIds}
sectionTitle="Comunidades"
/>
</>
) : (
<p className="text-white text-center mt-4">
Fique ligado, as próximas talks estão chegando!
</p>
)}
</section>
</section>
);
};
3 changes: 3 additions & 0 deletions src/pages/LivePage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LivePage } from "./LivePage";

export { LivePage };
23 changes: 23 additions & 0 deletions src/pages/LivePage/utils/talks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Palestra } from "@/api/types";
import { format, isAfter, isBefore } from "date-fns";

export const getLiveTalk = (talks: Palestra[], now: Date) => {
return talks.find((talk) => {
const startTime = new Date(`${format(now, "yyyy-MM-dd")}T${talk.hour}`);
const endTime = new Date(startTime.getTime() + 60 * 20 * 1000);
return isAfter(now, startTime) && isBefore(now, endTime);
});
};

export const getNextTalk = (talks: Palestra[], now: Date) => {
const sortedTalks = [...talks].sort(
(a, b) =>
new Date(`${format(now, "yyyy-MM-dd")}T${a.hour}`).getTime() -
new Date(`${format(now, "yyyy-MM-dd")}T${b.hour}`).getTime()
);

return sortedTalks.find((talk) => {
const startTime = new Date(`${format(now, "yyyy-MM-dd")}T${talk.hour}`);
return isAfter(startTime, now);
});
};
5 changes: 3 additions & 2 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { HomePage } from "@/pages/HomePage";

import { HomePage } from "@/pages/HomePage";
import { LivePage } from "@/pages/LivePage";
import { Routes, Route } from "react-router-dom";

export const AppRoutes = () => {
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/agenda" element={<h1>Minha Agenda</h1>} />
<Route path="/lives" element={<h1>Live Talks</h1>} />
<Route path="/lives" element={<LivePage />} />
<Route path="*" element={<h1>404</h1>} />
</Routes>
);
Expand Down

0 comments on commit e54afab

Please sign in to comment.