diff --git a/public/locales/bg/campaigns.json b/public/locales/bg/campaigns.json index 5bc6b2e03..644ce18db 100644 --- a/public/locales/bg/campaigns.json +++ b/public/locales/bg/campaigns.json @@ -97,7 +97,7 @@ "classic": "Предишен" }, "amount": "Необходима сума", - "type": "Тип кампания", + "type": "Тип кампания:", "state": "Статус", "from": "от", "noCommissionInfo": "Подкрепи.бг работи с 0% комисиона. Заплащат се единствено банкови такси, които изрично се упоменават преди да направите дарението си.", @@ -120,6 +120,7 @@ "financial-report": "Финансови отчети", "report-campaign": "Докладвайте кампанията", "feedback": "Обратна връзка", + "altImageText": "Снимка на", "images": { "add": "Добави снимки", "add-more": "Добави още", @@ -172,7 +173,17 @@ "end-date": "Крайна дата:", "indefinite": "Безсрочна", "tag": "Таг:", - "date": "Дата:" + "date": "Дата:", + "chipLabels": { + "categories": { + "aboutCampaign": "За кампанията", + "transparency": "Прозрачност" + }, + "news": "Новини", + "reports": "Отчети", + "experts": "Експертен съвет", + "guarantor": "Гарант" + } }, "info-graphics": { "donation-title": "100% от дарението отива при нуждаещите се", diff --git a/public/locales/en/campaigns.json b/public/locales/en/campaigns.json index 82941ee9e..ffb288f86 100644 --- a/public/locales/en/campaigns.json +++ b/public/locales/en/campaigns.json @@ -17,7 +17,7 @@ "coordinator": "Coordinator", "organizer": "Organizer", "beneficiary": "Beneficiary", - "campaignType": "Campaign type", + "campaignType": "Campaign type:", "description": "Description", "targetAmount": "Target amount", "donationsAmount": "Donations amount", @@ -103,6 +103,8 @@ "financial-report": "Financial report", "report-campaign": "Report the campaign", "feedback": "Feedback", + "altImageText": "Image of", + "images": { "add": "Add image", "add-more": "Add more", @@ -158,7 +160,17 @@ "start-date": "Start date:", "end-date": "End date:", "tag": "Tag:", - "date": "Date:" + "date": "Date:", + "chipLabels": { + "categories": { + "aboutCampaign": "About campaign", + "transparency": "Transparency" + }, + "news": "News", + "reports": "Reports", + "experts": "Our expert members", + "guarantor": "Guarantor" + } }, "info-graphics": { "donation-title": "100% of the donation goes to those in need", diff --git a/src/components/client/campaigns/CampaignDetails.tsx b/src/components/client/campaigns/CampaignDetails.tsx index 4a2fa50b4..faea75129 100644 --- a/src/components/client/campaigns/CampaignDetails.tsx +++ b/src/components/client/campaigns/CampaignDetails.tsx @@ -2,6 +2,7 @@ import React from 'react' import { useTranslation } from 'next-i18next' import dynamic from 'next/dynamic' +import { useRouter } from 'next/router' import { CampaignResponse } from 'gql/campaigns' @@ -26,6 +27,7 @@ import { routes } from 'common/routes' import { useCanEditCampaign } from 'common/hooks/campaigns' import { moneyPublic } from 'common/util/money' import ReceiptLongIcon from '@mui/icons-material/ReceiptLong' +import CampaignDetailsChip from './CampaignDetailsChip' const ReactQuill = dynamic(() => import('react-quill'), { ssr: false }) const CampaignNewsSection = dynamic(() => import('./CampaignNewsSection'), { ssr: false }) @@ -101,6 +103,27 @@ export default function CampaignDetails({ campaign }: Props) { const canEditCampaign = useCanEditCampaign(campaign.slug) const { data: expensesList } = useCampaignApprovedExpensesList(campaign.slug) const totalExpenses = expensesList?.reduce((acc, expense) => acc + expense.amount, 0) + const router = useRouter() + + const chipLabelsCampaign = { + experts: t('campaigns:campaign.chipLabels.experts'), + guarantor: t('campaigns:campaign.chipLabels.guarantor'), + } + const chipLabelsTransparency = { + news: t('campaigns:campaign.chipLabels.news'), + reports: t('campaigns:campaign.chipLabels.reports'), + } + + const scrollToSection = (sectionId: string) => { + const target = document.getElementById(sectionId) + if (target) { + target.scrollIntoView({ behavior: 'smooth' }) + } + } + + const redirectToExperts = () => { + router.push('https://podkrepi.bg/blog/ekspertni-saveti') + } return ( @@ -112,8 +135,30 @@ export default function CampaignDetails({ campaign }: Props) { showExpensesLink={(expensesList && expensesList?.length > 0) || canEditCampaign} /> - - + + + + {t('campaigns:campaign.chipLabels.categories.aboutCampaign')} + + {Object.entries(chipLabelsCampaign).map(([id, label]) => ( + scrollToSection(id)} + /> + ))} + + + + {t('campaigns:campaign.chipLabels.categories.transparency')} + + {Object.entries(chipLabelsTransparency).map(([id, label]) => ( + scrollToSection(id)} /> + ))} + + + + @@ -121,12 +166,13 @@ export default function CampaignDetails({ campaign }: Props) { + {expensesList?.length || canEditCampaign ? ( - + {t('campaigns:campaign.financial-report')} diff --git a/src/components/client/campaigns/CampaignDetailsChip.tsx b/src/components/client/campaigns/CampaignDetailsChip.tsx new file mode 100644 index 000000000..f488d2bb6 --- /dev/null +++ b/src/components/client/campaigns/CampaignDetailsChip.tsx @@ -0,0 +1,13 @@ +import { Chip } from '@mui/material' + +interface CampaingDetailsChipProps { + chip: string + onClick: () => void +} + +const CampaignDetailsChip: React.FC = ({ chip, onClick }) => { + return ( + + ) +} +export default CampaignDetailsChip diff --git a/src/components/client/campaigns/CampaignInfo.tsx b/src/components/client/campaigns/CampaignInfo.tsx new file mode 100644 index 000000000..902196d61 --- /dev/null +++ b/src/components/client/campaigns/CampaignInfo.tsx @@ -0,0 +1,128 @@ +import React from 'react' + +import { useTranslation } from 'next-i18next' + +import { CampaignResponse } from 'gql/campaigns' + +import { bg, enUS } from 'date-fns/locale' + +import { Button, Grid, Typography, Divider } from '@mui/material' +import { styled } from '@mui/material/styles' + +import { getExactDate } from 'common/util/date' +import CampaignInfoOrganizer from './CampaignInfoOrganizer' +import { Assessment } from '@mui/icons-material' +import CampaignInfoBeneficiary from './CampaignInfoBeneficiary' + +const PREFIX = 'CampaignInfo' + +const classes = { + personWrapper: `${PREFIX}-personWrapper`, + infoBlockWrapper: `${PREFIX}-infoBlockWrapper`, + infoDetailsWrapper: `${PREFIX}-infoDetailsWrapper`, + campaignTextWithIcon: `${PREFIX}-campaignTextWithIcon`, + divider: `${PREFIX}-divider`, +} + +const StyledGrid = styled(Grid)(({ theme }) => ({ + [`& .${classes.infoBlockWrapper}`]: { + flexWrap: 'wrap', + flexDirection: 'column', + rowSpacing: theme.spacing(5), + gap: theme.spacing(2), + + [theme.breakpoints.up('lg')]: { + display: 'flex', + flexDirection: 'row', + flexWrap: 'initial', + gap: 0, + }, + }, + + [`& .${classes.divider}`]: { + borderRightWidth: 0, + borderBottomWidth: 0, + margin: 0, + + [theme.breakpoints.up('lg')]: { + borderRightWidth: 1, + margin: theme.spacing(0, 5, 0, 0), + }, + }, + + [`& .${classes.infoDetailsWrapper}`]: { + flexDirection: 'column', + }, + + [`& .${classes.campaignTextWithIcon}`]: { + flexWrap: 'wrap', + fontSize: theme.typography.pxToRem(11), + lineHeight: '150%', + fontWeight: 700, + }, +})) + +type Props = { + campaign: CampaignResponse + showExpensesLink: boolean +} + +export default function CampaignInfo({ campaign, showExpensesLink }: Props) { + const { t, i18n } = useTranslation() + const locale = i18n.language == 'bg' ? bg : enUS + + return ( + + + + + {t('campaigns:campaign.type')} {campaign.campaignType.name} + + + + + {t('campaigns:campaign.start-date')} {getExactDate(campaign.startDate, locale)} + + + + + {t('campaigns:campaign.end-date')}{' '} + {campaign.endDate + ? getExactDate(campaign.endDate, locale) + : t('campaigns:campaign.indefinite')} + + + + + {t('campaigns:campaign.status')} {t(`campaigns:campaign-status.${campaign.state}`)} + + + + + + + + + {/* {showExpensesLink && ( + + + + )} */} + + + + + + + + ) +} diff --git a/src/components/client/campaigns/CampaignInfoBeneficiary.tsx b/src/components/client/campaigns/CampaignInfoBeneficiary.tsx new file mode 100644 index 000000000..983f9dfe4 --- /dev/null +++ b/src/components/client/campaigns/CampaignInfoBeneficiary.tsx @@ -0,0 +1,96 @@ +import React from 'react' + +import { useTranslation } from 'next-i18next' +import Image from 'next/image' + +import { CampaignResponse } from 'gql/campaigns' + +import { Button, Grid, Typography } from '@mui/material' +import EmailIcon from '@mui/icons-material/Email' +import { styled } from '@mui/material/styles' + +import { organizerCampaignPictureUrl } from 'common/util/campaignImageUrls' + +const PREFIX = 'CampaignInfoOrganizer' + +const classes = { + infoButtonIcon: `${PREFIX}-infoButtonIcon`, + personAvatar: `${PREFIX}-personAvatar`, + avatarWrapper: `${PREFIX}-avatarWrapper`, + trustedButton: `${PREFIX}-trustedButton`, + organizer: `${PREFIX}-organizer`, +} + +const StyledGrid = styled(Grid)(({ theme }) => ({ + [`& .${classes.avatarWrapper}`]: { + paddingLeft: theme.spacing(2.5), + + [theme.breakpoints.up('lg')]: { + paddingLeft: theme.spacing(0), + }, + }, + + [`& .${classes.personAvatar}`]: { + borderRadius: '50%', + objectFit: 'cover', + }, + + [`& .${classes.trustedButton}`]: { + color: theme.palette.primary.main, + textDecoration: 'underline', + fontSize: theme.spacing(1.75), + padding: 0, + paddingLeft: 2, + + '&:hover': { + backgroundColor: 'unset', + textDecoration: 'underline', + }, + }, + + [`& .${classes.infoButtonIcon}`]: { + marginRight: theme.spacing(1), + }, + + [`& .${classes.organizer}`]: { + fontSize: theme.typography.pxToRem(14), + fontWight: 700, + }, +})) + +type Props = { + campaign: CampaignResponse +} + +export default function CampaignInfoBeneficiary({ campaign }: Props) { + const { t } = useTranslation() + const organizerAvatarSource = organizerCampaignPictureUrl(campaign) + + return ( + + + + + + + {t('campaigns:campaign.beneficiary.name')} + + + {campaign?.beneficiary?.person + ? campaign?.beneficiary?.person?.firstName + campaign?.beneficiary?.person?.lastName + : campaign?.beneficiary?.company?.companyName} + + + + ) +} diff --git a/src/components/client/campaigns/CampaignInfoOrganizer.tsx b/src/components/client/campaigns/CampaignInfoOrganizer.tsx new file mode 100644 index 000000000..5c8cde81f --- /dev/null +++ b/src/components/client/campaigns/CampaignInfoOrganizer.tsx @@ -0,0 +1,97 @@ +import React from 'react' + +import { useTranslation } from 'next-i18next' +import Image from 'next/image' + +import { CampaignResponse } from 'gql/campaigns' + +import { Button, Grid, Typography } from '@mui/material' +import EmailIcon from '@mui/icons-material/Email' +import { styled } from '@mui/material/styles' + +import { organizerCampaignPictureUrl } from 'common/util/campaignImageUrls' + +const PREFIX = 'CampaignInfoOrganizer' + +const classes = { + infoButtonIcon: `${PREFIX}-infoButtonIcon`, + personAvatar: `${PREFIX}-personAvatar`, + avatarWrapper: `${PREFIX}-avatarWrapper`, + trustedButton: `${PREFIX}-trustedButton`, + organizer: `${PREFIX}-organizer`, +} + +const StyledGrid = styled(Grid)(({ theme }) => ({ + [`& .${classes.avatarWrapper}`]: { + paddingLeft: theme.spacing(2.5), + + [theme.breakpoints.up('lg')]: { + paddingLeft: theme.spacing(0), + }, + }, + + [`& .${classes.personAvatar}`]: { + borderRadius: '50%', + objectFit: 'cover', + }, + + [`& .${classes.trustedButton}`]: { + color: theme.palette.primary.main, + textDecoration: 'underline', + fontSize: theme.spacing(1.75), + padding: 0, + paddingLeft: 2, + + '&:hover': { + backgroundColor: 'unset', + textDecoration: 'underline', + }, + }, + + [`& .${classes.infoButtonIcon}`]: { + marginRight: theme.spacing(1), + }, + + [`& .${classes.organizer}`]: { + fontSize: theme.typography.pxToRem(14), + fontWight: 700, + }, +})) + +type Props = { + campaign: CampaignResponse +} + +export default function CampaignInfoOrganizer({ campaign }: Props) { + const { t } = useTranslation() + const organizerAvatarSource = organizerCampaignPictureUrl(campaign) + + return ( + + + + + + + {t('campaigns:campaign.organizer.name')} + + + {campaign.organizer?.person.firstName || ''} {campaign.organizer?.person.lastName || ''} + + {/* //TODO add functionality to the button,discuss with team */} + + + + ) +} diff --git a/src/components/client/campaigns/CampaignNewsSection.tsx b/src/components/client/campaigns/CampaignNewsSection.tsx index 9895fa25b..9c69a1f15 100644 --- a/src/components/client/campaigns/CampaignNewsSection.tsx +++ b/src/components/client/campaigns/CampaignNewsSection.tsx @@ -160,7 +160,7 @@ export default function CampaignNewsSection({ campaign, canCreateArticle }: Prop const [isExpanded, expandContent] = useShowMoreContent() return ( - + {t('news')}