diff --git a/public/images/events/event-placeholder.png b/public/images/events/event-placeholder.png new file mode 100644 index 00000000000..4a15165452a Binary files /dev/null and b/public/images/events/event-placeholder.png differ diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 5dbca466c14..e4da86a39b6 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -1,8 +1,11 @@ import React from "react" -import { Box, Heading } from "@chakra-ui/react" +import { useRouter } from "next/router" +import { useTranslation } from "react-i18next" +import { BsCalendar3 } from "react-icons/bs" +import { Box, Flex, Heading, Icon } from "@chakra-ui/react" +import { Image } from "@chakra-ui/react" import { ButtonLink } from "./Buttons" -import Emoji from "./Emoji" import Text from "./OldText" const clearStyles = { @@ -16,87 +19,113 @@ export type EventCardProps = { title: string href: string date: string + startDate: string + endDate: string description: string className?: string location: string - isEven: boolean + imageUrl?: string } const EventCard = ({ title, href, - date, description, className, location, - isEven, -}: EventCardProps) => ( - - + imageUrl, + endDate, + startDate, +}: EventCardProps) => { + const { locale } = useRouter() + const { t } = useTranslation("page-community") + const formatedDate = new Intl.DateTimeFormat(locale, { + day: "2-digit", + month: "short", + }).formatRange( + // .replace(/-/g, "/") ==> Fixes Safari Invalid date + new Date(startDate?.replace(/-/g, "/")), + new Date(endDate?.replace(/-/g, "/")) + ) + + return ( - - {date} - - - - - {location} - - - - - {title} - - {description} - View Event + + + + + + + {formatedDate} + + + + {/* TODO : add image hostname to next config or add event image to public dir */} + + {title} + + + + + {title} + + + {location} + + + + {description} + + + + + + {t("page-community-upcoming-events-view-event")} + + + - -) + ) +} export default EventCard diff --git a/src/components/MeetupList.tsx b/src/components/MeetupList.tsx index 6b4c9ed3472..7e98f285706 100644 --- a/src/components/MeetupList.tsx +++ b/src/components/MeetupList.tsx @@ -1,8 +1,10 @@ import { useState } from "react" import sortBy from "lodash/sortBy" +import { FaChevronRight } from "react-icons/fa6" import { Box, Flex, + Icon, LinkBox, LinkOverlay, List, @@ -23,8 +25,6 @@ import { trackCustomEvent } from "@/lib/utils/matomo" import meetups from "@/data/community-meetups.json" -import { useRtlFlip } from "@/hooks/useRtlFlip" - export interface Meetup { title: string emoji: string @@ -49,13 +49,7 @@ const sortedMeetups: Array = sortBy(meetups, ["emoji", "location"]) // TODO prop if ordered list or unordered const MeetupList = () => { const [searchField, setSearchField] = useState("") - const { flipForRtl } = useRtlFlip() const filteredMeetups = filterMeetups(searchField) - const listBoxShadow = useColorModeValue("tableBox.light", "tableBox.dark") - const listItemBoxShadow = useColorModeValue( - "tableItemBox.light", - "tableItemBox.dark" - ) const handleSearch = (event: React.ChangeEvent): void => { setSearchField(event.target.value) @@ -81,14 +75,18 @@ const MeetupList = () => { results update as you type - + {filteredMeetups.map((meetup, idx) => ( { boxShadow: `0 0 1px ${primaryBaseColor}`, bg: "tableBackgroundHover", }} + borderBottom={"2px solid"} + borderColor={"offBackground"} > @@ -133,16 +133,14 @@ const MeetupList = () => { {meetup.location} - + + + ))} diff --git a/src/components/UpcomingEventsList.tsx b/src/components/UpcomingEventsList.tsx index 51bedc6e34e..1bc062cc221 100644 --- a/src/components/UpcomingEventsList.tsx +++ b/src/components/UpcomingEventsList.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from "react" +import _ from "lodash" import { useRouter } from "next/router" import { useTranslation } from "next-i18next" -import { Box } from "@chakra-ui/react" +import { Box, Grid, Heading } from "@chakra-ui/react" import type { CommunityConference, Lang } from "@/lib/types" @@ -15,19 +16,14 @@ import { getLocaleTimestamp } from "@/lib/utils/time" import communityEvents from "@/data/community-events.json" -type OrderedUpcomingEvent = CommunityConference & { - date: string - formattedDetails: string -} - const UpcomingEventsList = () => { const { locale } = useRouter() const { t } = useTranslation("page-community") - const eventsPerLoad = 10 - const [orderedUpcomingEvents, setOrderedUpcomingEvents] = useState< - OrderedUpcomingEvent[] - >([]) - const [maxRange, setMaxRange] = useState(eventsPerLoad) + const monthsPerLoad = 2 + + const [monthGroupedEvents, setMonthGroupedEvents] = useState({}) + + const [maxRange, setMaxRange] = useState(monthsPerLoad) // Create Date object from each YYYY-MM-DD JSON date string const dateParse = (dateString: string): Date => { @@ -73,13 +69,27 @@ const UpcomingEventsList = () => { formattedDetails: details, } }) + const groupedEvents = _.groupBy(formattedEvents, ({ startDate }) => { + // .replace(/-/g, "/") ==> Fixes Safari Invalid date + const start = new Date(startDate.replace(/-/g, "/")) + const formatYearMonth = new Intl.DateTimeFormat(locale, { + month: "short", + year: "numeric", + }).format(start) + return `${formatYearMonth}` + }) + + setMonthGroupedEvents(groupedEvents) - setOrderedUpcomingEvents(formattedEvents) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const loadMoreEvents = () => { - setMaxRange((counter) => counter + eventsPerLoad) + setMaxRange((counter) => { + if (counter + monthsPerLoad > Object.keys(monthGroupedEvents)?.length) + return Object.keys(monthGroupedEvents)?.length + return counter + monthsPerLoad + }) trackCustomEvent({ eventCategory: "more events button", eventAction: "click", @@ -87,7 +97,7 @@ const UpcomingEventsList = () => { }) } - if (orderedUpcomingEvents.length === 0) { + if (Object.keys(monthGroupedEvents)?.length === 0) { return ( {t("page-community-upcoming-events-no-events")}{" "} @@ -100,56 +110,87 @@ const UpcomingEventsList = () => { return ( <> + {Object.keys(monthGroupedEvents) + ?.slice(0, maxRange) + ?.map((month) => { + const events = monthGroupedEvents[month] + return ( + + + {month} + + + {events.map( + ( + { + title, + to, + href, + formattedDetails, + date, + location, + imageUrl, + startDate, + endDate, + }, + idx + ) => ( + + ) + )} + + + ) + })} + - {orderedUpcomingEvents - ?.slice(0, maxRange) - .map(({ title, href, formattedDetails, date, location }, idx) => { - return ( - - ) - })} - - - {maxRange <= orderedUpcomingEvents.length && ( + > + {Object.keys(monthGroupedEvents)?.length !== maxRange && ( + - )} - + + )} ) } diff --git a/src/intl/en/page-community.json b/src/intl/en/page-community.json index 6e2435fd642..488aacc7dbc 100644 --- a/src/intl/en/page-community.json +++ b/src/intl/en/page-community.json @@ -44,6 +44,7 @@ "page-community-try-ethereum": "Try Ethereum for yourself", "page-community-upcoming-events-no-events": "We're not aware of any upcoming events. Know of one?", "page-community-upcoming-events-load-more": "Load more", + "page-community-upcoming-events-view-event": "View event", "page-community-why-get-involved-title": "Why get involved?", "page-community-why-get-involved-card-1-title": "Find your tribe", "page-community-why-get-involved-card-1-description": "There is a tribe for everyone. Find and connect with like minded individuals to discuss, ponder, and celebrate Ethereum together.",