diff --git a/.env b/.env new file mode 100644 index 00000000..b61883eb --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL="postgresql://ppt:devpassword@localhost:5432/elections_db?schema=public" diff --git a/components/about-us/contributors.tsx b/app/components/about-us/contributors.tsx similarity index 100% rename from components/about-us/contributors.tsx rename to app/components/about-us/contributors.tsx diff --git a/components/about-us/footer.tsx b/app/components/about-us/footer.tsx similarity index 88% rename from components/about-us/footer.tsx rename to app/components/about-us/footer.tsx index 4ffea8a3..ea03cb74 100644 --- a/components/about-us/footer.tsx +++ b/app/components/about-us/footer.tsx @@ -2,7 +2,7 @@ import { Layout } from "antd"; import Link from "next/link"; import { socialMediaOptions } from "../../src/utils/online-platform"; import { FooterLogo } from "../global/logos"; -import SocialSharing from "../global/social-sharing"; +import SocialSharing from "../social/SocialSharing"; const { Footer } = Layout; diff --git a/components/about-us/join-us.tsx b/app/components/about-us/join-us.tsx similarity index 100% rename from components/about-us/join-us.tsx rename to app/components/about-us/join-us.tsx diff --git a/components/about-us/motivation.tsx b/app/components/about-us/motivation.tsx similarity index 100% rename from components/about-us/motivation.tsx rename to app/components/about-us/motivation.tsx diff --git a/app/components/footer/ContributorsList.tsx b/app/components/footer/ContributorsList.tsx new file mode 100644 index 00000000..70f56d47 --- /dev/null +++ b/app/components/footer/ContributorsList.tsx @@ -0,0 +1,17 @@ +import { shuffleList } from "../../utils/contributors"; + +const ContributorsList = () => + // const [hasMounted, setHasMounted] = useState(false); + + // useEffect(() => { + // setHasMounted(true); + // }, []); + + // if (!hasMounted) { + // return null; + // } + <> + {shuffleList().join(", ")}. + + +export default ContributorsList; diff --git a/app/components/footer/LayoutFooter.tsx b/app/components/footer/LayoutFooter.tsx new file mode 100644 index 00000000..b1f231c0 --- /dev/null +++ b/app/components/footer/LayoutFooter.tsx @@ -0,0 +1,37 @@ +import { Col, Row } from "antd"; +import { Footer } from "antd/es/layout/layout"; +import Link from "next/link"; +import { FooterLogo } from "../global/logos"; +import SocialSharing from "../social/SocialSharing"; +import ContributorsList from "./ContributorsList"; + +const LayoutFooter = () => + + +export default LayoutFooter; diff --git a/components/global/icons.tsx b/app/components/global/icons.tsx similarity index 100% rename from components/global/icons.tsx rename to app/components/global/icons.tsx diff --git a/components/global/layout-header.tsx b/app/components/global/layout-header.tsx similarity index 98% rename from components/global/layout-header.tsx rename to app/components/global/layout-header.tsx index 9c542511..b1cc9b15 100644 --- a/components/global/layout-header.tsx +++ b/app/components/global/layout-header.tsx @@ -5,8 +5,8 @@ import Link from "next/link"; import { Fragment, memo, useState } from "react"; import { LOOMIO_PPT_URL } from "../../src/utils/constants"; import { socialMediaOptions } from "../../src/utils/online-platform"; +import SocialSharing from "../social/SocialSharing"; import { HeaderLogo } from "./logos"; -import SocialSharing from "./social-sharing"; const LayoutHeader = () => { const [headerState, setState] = useState({ diff --git a/app/components/global/logos.tsx b/app/components/global/logos.tsx new file mode 100644 index 00000000..478b1aec --- /dev/null +++ b/app/components/global/logos.tsx @@ -0,0 +1,127 @@ +import Image from "next/image"; +import header_logo from "../../../public/horizontal_logo.svg"; +import infographic_image from "../../../public/infographic.svg"; +// import rows_people_image from "../../public/rows-people.svg"; +import footer_logo from "../../../public/vertical_logo.jpg"; +import voting_image from '../../../public/voting.svg'; + +import ADNLogo from '../../../public/party-logos/adn_logo.png'; +import AliancaLogo from '../../../public/party-logos/Alianca.png'; +import AliancaDemocraticaLogo from '../../../public/party-logos/alianca_democratica.png'; +import BlocoEsquerdaLogo from '../../../public/party-logos/blocodeesquerda.jpg'; +import CDSLogo from '../../../public/party-logos/CDS.png'; +import CDULogo from '../../../public/party-logos/cdu.jpg'; +import ChegaLogo from '../../../public/party-logos/chega.png'; +import ErgueTeLogo from '../../../public/party-logos/ergue-te_logo.png'; +import IniciativaLiberalLogo from '../../../public/party-logos/Iniciativa_Liberal_logo.gif'; +import JPPLogo from '../../../public/party-logos/jpp_logo.png'; +import LivreLogo from '../../../public/party-logos/livre-logo.png'; +import MadeiraPrimeiroLogo from '../../../public/party-logos/madeira_primeiro.png'; +import MASLogo from '../../../public/party-logos/mas-logo.jpg'; +import NosCidadaosLogo from '../../../public/party-logos/Nos_Cidadaos_logo.jpg'; +import PPMLogo from '../../../public/party-logos/Partido_Popular_Monarquico_logo.png'; +import PTLogo from '../../../public/party-logos/partido_terra_logo.jpg'; +import PTPLogo from '../../../public/party-logos/Partido_Trabalhista_Portugues_logo.jpg'; +import PCTPMRPPLogo from '../../../public/party-logos/PCTPMRPP_LOGO.png'; +import PANLogo from '../../../public/party-logos/Pessoas-Animais-Natureza_logo.png'; +import PSLogo from '../../../public/party-logos/PS_logo.png'; +import PSDLogo from '../../../public/party-logos/psd-logo.jpg'; +import RIRLogo from '../../../public/party-logos/RIR_logo.jpg'; +import VoltLogo from '../../../public/party-logos/volt.png'; + +export const PPTLogo = () => + PPT Header Logo + +export const FooterLogo = () => + PPT Footer Logo + +export const VotingImage = () => + voting + +export const InfographicImage = () => + infographic + +// export const AboutUsHeaderImage = () => +// people + +export const renderPartyLogo = (fileName: string): string => { + switch (fileName) { + case 'adn_logo.png': + return ADNLogo.src; + case 'alianca_democratica.png': + return AliancaDemocraticaLogo.src; + case 'Alianca.png': + return AliancaLogo.src; + case 'blocodeesquerda.jpg': + return BlocoEsquerdaLogo.src; + case 'CDS.png': + return CDSLogo.src; + case 'cdu.jpg': + return CDULogo.src; + case 'chega.png': + return ChegaLogo.src; + case 'ergue-te_logo.png': + return ErgueTeLogo.src; + case 'Iniciativa_Liberal_logo.gif': + return IniciativaLiberalLogo.src; + case 'jpp_logo.png': + return JPPLogo.src; + case 'livre-logo.png': + return LivreLogo.src; + case 'madeira_primeiro.png': + return MadeiraPrimeiroLogo.src; + case 'mas-logo.jpg': + return MASLogo.src; + case 'Nos_Cidadaos_logo.jpg': + return NosCidadaosLogo.src; + case 'Partido_Popular_Monarquico_logo.png': + return PPMLogo.src; + case 'partido_terra_logo.jpg': + return PTLogo.src; + case 'Partido_Trabalhista_Portugues_logo.jpg': + return PTPLogo.src; + case 'PCTPMRPP_LOGO.png': + return PCTPMRPPLogo.src; + case 'Pessoas-Animais-Natureza_logo.png': + return PANLogo.src; + case 'PS_logo.png': + return PSLogo.src; + case 'psd-logo.jpg': + return PSDLogo.src; + case 'RIR_logo.jpg': + return RIRLogo.src; + case 'volt.png': + return VoltLogo.src; + default: + throw Error(`${fileName} does not exist.`); + } +} diff --git a/components/global/meta-tags.tsx b/app/components/global/meta-tags.tsx similarity index 100% rename from components/global/meta-tags.tsx rename to app/components/global/meta-tags.tsx diff --git a/app/components/header/LayoutHeader.tsx b/app/components/header/LayoutHeader.tsx new file mode 100644 index 00000000..32e29568 --- /dev/null +++ b/app/components/header/LayoutHeader.tsx @@ -0,0 +1,35 @@ +import Col from "antd/es/col"; +import { Header } from "antd/es/layout/layout"; +import Row from "antd/es/row"; +import Link from "next/link"; +import { PPTLogo } from "../global/logos"; +import SocialSharing from "../social/SocialSharing"; +import MenuItems from "./MenuItems"; + +const LayoutHeader = () => + <> +
+ + + + + + + + +
+ + +export default LayoutHeader; diff --git a/app/components/header/MenuItems.tsx b/app/components/header/MenuItems.tsx new file mode 100644 index 00000000..97c36462 --- /dev/null +++ b/app/components/header/MenuItems.tsx @@ -0,0 +1,38 @@ +import { Menu } from 'antd'; +import Link from 'next/link'; + +const MenuItems = () => { + const legislativesRoute = '/legislativas'; + return ( + Home) + }, + { + key: '1', + label: (Legislativas), + children: [ + { + key: '1-1', + label: (Debates) + } + ] + }, + { + key: '2', + label: (Autarquicas) + }, + { + key: '3', + label: (Presidenciais) + } + ]} + > + + ) +} + +export default MenuItems; diff --git a/components/home/media.tsx b/app/components/home/LegislativeVideoSection.tsx similarity index 94% rename from components/home/media.tsx rename to app/components/home/LegislativeVideoSection.tsx index e587d595..a4e68245 100644 --- a/components/home/media.tsx +++ b/app/components/home/LegislativeVideoSection.tsx @@ -1,7 +1,9 @@ +'use client'; + import { Button, Col, Row } from "antd"; import { useEffect, useState } from "react"; import ReactPlayer from "react-player/youtube"; -import { YOUTUBE_VIDEO_EP1, YOUTUBE_VIDEO_EP2, YOUTUBE_VIDEO_EP3, YOUTUBE_VIDEO_EP4 } from "../../src/utils/constants"; +import { YOUTUBE_VIDEO_EP1, YOUTUBE_VIDEO_EP2, YOUTUBE_VIDEO_EP3, YOUTUBE_VIDEO_EP4 } from "../../utils/constants"; interface Video { url: string; @@ -31,7 +33,7 @@ const videos: Video[] = [ }, ]; -const HomeMedia = () => { +const LegislativeVideoSection = () => { const [currentEpisode, setCurrentEpisode] = useState(0); const [isPlayerHidden, setIsPlayerHidden] = useState(true); const { url, caption } = videos[currentEpisode]; @@ -87,4 +89,4 @@ const HomeMedia = () => { ); }; -export default HomeMedia; +export default LegislativeVideoSection; diff --git a/components/home/avatar-list.tsx b/app/components/home/avatar-list.tsx similarity index 100% rename from components/home/avatar-list.tsx rename to app/components/home/avatar-list.tsx diff --git a/components/home/countdown-renderer.tsx b/app/components/home/countdown-renderer.tsx similarity index 100% rename from components/home/countdown-renderer.tsx rename to app/components/home/countdown-renderer.tsx diff --git a/components/home/countdown-unit.tsx b/app/components/home/countdown-unit.tsx similarity index 100% rename from components/home/countdown-unit.tsx rename to app/components/home/countdown-unit.tsx diff --git a/app/components/home/countdown.tsx b/app/components/home/countdown.tsx new file mode 100644 index 00000000..0817c901 --- /dev/null +++ b/app/components/home/countdown.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { Button, Col, Row } from "antd"; +import Link from "next/link"; +import Countdown from "react-countdown-now"; +import { VotingImage } from "../global/logos"; +import CountdownRenderer from "./countdown-renderer"; + +const LegislativeElectionCountdown = () => { + const legislativeElectionDate = new Date(2024, 2, 10); + + return ( +
+ + + + + + + + + +
+ ) +} + +export default LegislativeElectionCountdown; diff --git a/components/home/infographic.tsx b/app/components/home/infographic.tsx similarity index 100% rename from components/home/infographic.tsx rename to app/components/home/infographic.tsx diff --git a/components/home/mission.tsx b/app/components/home/mission.tsx similarity index 95% rename from components/home/mission.tsx rename to app/components/home/mission.tsx index a88a5dde..33e23c65 100644 --- a/components/home/mission.tsx +++ b/app/components/home/mission.tsx @@ -1,7 +1,7 @@ import { Col, Row } from "antd"; import Infographic from "./infographic"; -export const HomeMissionInfographic = () => +export const LegislativeMission = () =>
-export const HomeMission = () => { +export const Mission = () => { const legislativeElectionDate = new Date(2024, 2, 10); const electionDay = legislativeElectionDate.getDate(); const electionMonthName = legislativeElectionDate.toLocaleString('pt-pt', { month: 'long' }); diff --git a/components/home/parties.tsx b/app/components/home/parties.tsx similarity index 100% rename from components/home/parties.tsx rename to app/components/home/parties.tsx diff --git a/components/home/round-avatar.tsx b/app/components/home/round-avatar.tsx similarity index 89% rename from components/home/round-avatar.tsx rename to app/components/home/round-avatar.tsx index f34105d1..ef287c69 100644 --- a/components/home/round-avatar.tsx +++ b/app/components/home/round-avatar.tsx @@ -1,11 +1,8 @@ -import dynamic from "next/dynamic"; import Link from "next/link"; import { HomePageParty } from "../../src/retriever/dtos/party-dto"; import { acronymConversion, Conversion } from "../../src/utils/manipuation"; import { renderPartyLogo } from "../global/logos"; -const Avatar = dynamic(import("antd/es/avatar"), { ssr: false }); - interface RoundAvatarProps { party: HomePageParty } diff --git a/app/components/party/PartiesGrid.tsx b/app/components/party/PartiesGrid.tsx new file mode 100644 index 00000000..9433027f --- /dev/null +++ b/app/components/party/PartiesGrid.tsx @@ -0,0 +1,120 @@ +'use client'; + +import { electoralCircleDropdown } from "@/retriever/dtos/electoral-circle-dto"; +import { Col, Row, Select, Switch } from "antd"; +import { useState } from "react"; +import { shuffleParties } from "../../utils/manipuation"; +import { LegislativeHomeParty } from "./PartiesSection"; +import PartyRoundAvatar from "./PartyRoundAvatar"; + +interface PartiesGridProps { + parties: LegislativeHomeParty[], + theme: string +} + +enum ElectoralCircleFilter { + TODOS = "Todos", + BRAGANCa = "Bragança", + LISBOA = "Lisboa", + PORTO = 'Porto' +} + +const PartiesGrid = ({ parties, theme }: PartiesGridProps) => { + let sortedParties: LegislativeHomeParty[]; + console.log(parties); + + const [state, setState] = useState({ + isAlphabeticalOrder: false, + districtFilter: ElectoralCircleFilter.TODOS, + }); + + const onChange = () => { + setState({ + isAlphabeticalOrder: !state.isAlphabeticalOrder, + districtFilter: state.districtFilter, + }); + }; + + const filterDisctrict = (district: string) => { + setState({ + isAlphabeticalOrder: state.isAlphabeticalOrder, + districtFilter: district as ElectoralCircleFilter, + }); + }; + + if (state.districtFilter !== ElectoralCircleFilter.TODOS) { + sortedParties = parties.filter((party: LegislativeHomeParty) => [...party.electoralCircles].filter(district => district === state.districtFilter)); + } + + if (state.isAlphabeticalOrder) { + // sortedParties = parties.sort((partyA, partyB) => partyA.acronym.toLowerCase().localeCompare(partyB.acronym.toLowerCase())); + const partiesToSort = [...parties]; + sortedParties = partiesToSort.sort((party1, party2) => { + const partyName1 = party1.acronym.toLowerCase(); + const partyName2 = party2.acronym.toLowerCase(); + + if (partyName1 > partyName2) { + return 1; + } + if (partyName1 < partyName2) { + return -1; + } + return 0; + }); + } else { + sortedParties = shuffleParties(parties); + } + + return ( + <> + + +

+ A ordem dos partidos a seguir é feita de forma aleatória. +
+ Sempre que voltares a carregar esta página a ordem será diferente. +

+ + +

Lista de Partidos

+ + +
+ + Ordenar alfabeticamente +
+ +
+ + + + + +
+ {parties.map(party => ( + + ))} +
+ +
+ + ); +}; + +export default PartiesGrid; diff --git a/app/components/party/PartiesSection.tsx b/app/components/party/PartiesSection.tsx new file mode 100644 index 00000000..d93004a0 --- /dev/null +++ b/app/components/party/PartiesSection.tsx @@ -0,0 +1,69 @@ +import { PrismaClient } from "@prisma/client"; +import PartiesGrid from "./PartiesGrid"; + +export interface LegislativeHomeParty { + id: number, + acronym: string, + name: string, + logoFileName: string, + electoralCircles: Set +} + +const PartiesSection = async () => { + let currentParties; + const mockedParties = [{ + id: 'test', + acronym: 'PSD', + name: 'Partido Social Democrata', + logoFileName: 'psd-logo.jpg', + candidares: [] + }] + const parties = await getAllLegislativeParties(); + + if (parties.length < 1) { + currentParties = mockedParties; + } + else { + currentParties = parties; + } + + return ( +
+ +
+ ); +}; + +export const getAllLegislativeParties = async (): Promise => { + const prisma = new PrismaClient(); + const queryResult = await prisma.party.findMany({ + select: { + id: true, + name: true, + acronym: true, + photoFileName: true, + Candidates: { + select: { + ElectoralCircle: { + select: { + name: true + } + } + } + } + } + }); + + return queryResult.map(result => ({ + id: result.id, + acronym: result.acronym, + name: result.name, + logoFileName: result.photoFileName, + electoralCircles: new Set(result.Candidates.map(district => district.ElectoralCircle.name)) + })) +} + +export default PartiesSection; diff --git a/app/components/party/PartyRoundAvatar.tsx b/app/components/party/PartyRoundAvatar.tsx new file mode 100644 index 00000000..28ae9b11 --- /dev/null +++ b/app/components/party/PartyRoundAvatar.tsx @@ -0,0 +1,25 @@ +import { Avatar } from "antd"; +import Link from "next/link"; +import { acronymConversion, Conversion } from "../../utils/manipuation"; +import { renderPartyLogo } from "../global/logos"; +import { Party } from "./PartiesGrid"; + +interface PartyRoundAvatar { + party: Party +} + +const PartyRoundAvatar = ({ party }: PartyRoundAvatar) => { + const { name, acronym, logoFileName } = party; + + return ( + +
+ +

{acronym}

+

{name}

+
+ + ) +} + +export default PartyRoundAvatar; diff --git a/components/party/candidate-list.tsx b/app/components/party/candidate-list.tsx similarity index 100% rename from components/party/candidate-list.tsx rename to app/components/party/candidate-list.tsx diff --git a/components/party/header.tsx b/app/components/party/header.tsx similarity index 97% rename from components/party/header.tsx rename to app/components/party/header.tsx index c9198529..a00967c2 100644 --- a/components/party/header.tsx +++ b/app/components/party/header.tsx @@ -4,7 +4,7 @@ import { Fragment } from "react"; import { OnlinePlatform, OnlinePlatformType, PartyPage } from "../../src/retriever/dtos/party-dto"; import { Conversion, acronymConversion } from "../../src/utils/manipuation"; import { renderPartyLogo } from "../global/logos"; -import SocialSharing from "../global/social-sharing"; +import SocialSharing from "../social/SocialSharing"; interface PartyHeaderProps { party: PartyPage; diff --git a/components/party/intro.tsx b/app/components/party/intro.tsx similarity index 100% rename from components/party/intro.tsx rename to app/components/party/intro.tsx diff --git a/components/party/manifesto/section.tsx b/app/components/party/manifesto/section.tsx similarity index 100% rename from components/party/manifesto/section.tsx rename to app/components/party/manifesto/section.tsx diff --git a/components/party/manifesto/sider.tsx b/app/components/party/manifesto/sider.tsx similarity index 100% rename from components/party/manifesto/sider.tsx rename to app/components/party/manifesto/sider.tsx diff --git a/components/party/party-candidate-table.tsx b/app/components/party/party-candidate-table.tsx similarity index 100% rename from components/party/party-candidate-table.tsx rename to app/components/party/party-candidate-table.tsx diff --git a/components/global/social-icon.tsx b/app/components/social/SocialIcon.tsx similarity index 85% rename from components/global/social-icon.tsx rename to app/components/social/SocialIcon.tsx index 725c9ead..76371969 100644 --- a/components/global/social-icon.tsx +++ b/app/components/social/SocialIcon.tsx @@ -1,5 +1,5 @@ -import { OnlinePlatformType } from "../../src/retriever/dtos/party-dto"; -import { EmailSvgIcon, FacebookSvgIcon, GithubSvgIcon, InstagramSvgIcon, MediumSvgIcon, TwitterSvgIcon } from "./icons"; +import { OnlinePlatformType } from "../../utils/online-platform"; +import { EmailSvgIcon, FacebookSvgIcon, GithubSvgIcon, InstagramSvgIcon, MediumSvgIcon, TwitterSvgIcon } from "../global/icons"; interface SocialIconProps { icon: OnlinePlatformType; diff --git a/components/global/social-sharing.tsx b/app/components/social/SocialSharing.tsx similarity index 61% rename from components/global/social-sharing.tsx rename to app/components/social/SocialSharing.tsx index dab89b1c..de0e39d9 100644 --- a/components/global/social-sharing.tsx +++ b/app/components/social/SocialSharing.tsx @@ -1,14 +1,13 @@ -import { OnlinePlatform, OnlinePlatformType } from "../../src/retriever/dtos/party-dto"; -import SocialIcon from "./social-icon"; +import { OnlinePlatform, OnlinePlatformType, SOCIAL_MEDIA_OPTIONS } from "../../utils/online-platform"; +import SocialIcon from "./SocialIcon"; interface SocialSharingProps { - onlinePlatforms: OnlinePlatform[]; theme?: string; } -const SocialSharing = ({ onlinePlatforms, theme }: SocialSharingProps) => +const SocialSharing = ({ theme }: SocialSharingProps) =>