diff --git a/.all-contributorsrc b/.all-contributorsrc index 06d97071c..eab6c0a6a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -867,6 +867,43 @@ "contributions": [ "code" ] + }, + { + "login": "teodorazhelyazkova", + "name": "Teodora Zhelyazkova", + "avatar_url": "https://avatars.githubusercontent.com/u/103574320?v=4", + "profile": "https://github.com/teodorazhelyazkova", + "contributions": [ + "code" + ] + }, + { + "login": "Martbul", + "name": "Martin Kovachki", + "avatar_url": "https://avatars.githubusercontent.com/u/99181339?v=4", + "profile": "https://martbul.github.io/MartinKovachki/", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "viiktorstefanov", + "name": "Viktor Stefanov", + "avatar_url": "https://avatars.githubusercontent.com/u/122864734?v=4", + "profile": "https://github.com/viiktorstefanov", + "contributions": [ + "code" + ] + }, + { + "login": "velnachev", + "name": "velnachev", + "avatar_url": "https://avatars.githubusercontent.com/u/60844919?v=4", + "profile": "https://github.com/velnachev", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 10, diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..316f05593 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/alpine +{ + "name": "NodeJS", + "image": "ghcr.io/podkrepi-bg/nodejs-devcontainer:v1.2.0", + "forwardPorts": [], // Forward ports + "containerEnv": { + "DATABASE_URL": "postgres://postgres:postgrespass@host.docker.internal:5432/postgres?schema=api" // Custom env vars + }, + "postStartCommand": "yarn", // Install dependencies + "customizations": { + "vscode": { + "extensions": [ + "ms-azuretools.vscode-docker", + "nrwl.angular-console", + "esbenp.prettier-vscode", + "firsttris.vscode-jest-runner", + "dbaeumer.vscode-eslint" + ] + } + }, + "mounts": ["source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"] +} diff --git a/.env.local.example b/.env.local.example index 05f6842a5..ed1dc968b 100644 --- a/.env.local.example +++ b/.env.local.example @@ -43,3 +43,5 @@ PAYPAL_CLIENT_ID=sb ########### GHOST_API_URL=https://blog.podkrepi.bg GHOST_CONTENT_KEY=86ec17c4b9660acd66b6034682 +PODKREPI_EMAIL=admin@podkrepi.bg +PODKREPI_PASSWORD=$ecurePa33 diff --git a/.eslintrc.js b/.eslintrc.js index c82e8eaa6..446629ed2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -45,9 +45,12 @@ module.exports = { endOfLine: 'auto', }, ], - 'no-restricted-imports': ["error", { - "name": "react-i18next", - "message": "Please use next-i18next" - }] + 'no-restricted-imports': [ + 'error', + { + name: 'react-i18next', + message: 'Please use next-i18next', + }, + ], }, } diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ff244c6d6..b80f5a889 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -38,9 +38,9 @@ jobs: restore-keys: | ${{ runner.os }}-yarn- - - name: Run db + - name: Run db and keycloak working-directory: ./api - run: docker compose up -d pg-db + run: docker compose up -d pg-db keycloak - name: Run stripe webhook working-directory: ./api @@ -71,7 +71,7 @@ jobs: - name: Install Frontend Dependencies working-directory: ./frontend run: yarn install --immutable - + - name: Install e2e Dependencies working-directory: ./frontend/e2e run: yarn install --immutable @@ -120,7 +120,7 @@ jobs: name: playwright-report path: ./frontend/e2e/test-results/ retention-days: 14 - + - uses: actions/upload-artifact@v4 if: always() with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 791dce22b..b9b32eb78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: push: true file: Dockerfile.maintenance @@ -66,7 +66,7 @@ jobs: run: echo "VERSION=master-$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 env: NODE_ENV: production SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f91aab21..de5d23ddd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 env: NODE_ENV: production SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} @@ -58,7 +58,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: push: false file: Dockerfile.maintenance diff --git a/README.md b/README.md index bc16e803a..67bd26945 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Watch releases of this repository to be notified about future updates: ## Contributors ✨ -[![All Contributors](https://img.shields.io/badge/all_contributors-80-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-84-orange.svg?style=flat-square)](#contributors-) Please check [contributors guide](https://github.com/podkrepi-bg/frontend/blob/master/CONTRIBUTING.md) for: @@ -221,6 +221,12 @@ Thanks goes to these wonderful people: iliyan90
iliyan90

💻 Georgi Parlakov
Georgi Parlakov

💻 + + Teodora Zhelyazkova
Teodora Zhelyazkova

💻 + Martin Kovachki
Martin Kovachki

💻 ⚠️ + Viktor Stefanov
Viktor Stefanov

💻 + velnachev
velnachev

💻 + diff --git a/e2e/pages/web-pages/base.page.ts b/e2e/pages/web-pages/base.page.ts index 8fe3dcef3..858cbe7d0 100644 --- a/e2e/pages/web-pages/base.page.ts +++ b/e2e/pages/web-pages/base.page.ts @@ -290,9 +290,9 @@ export class BasePage { await this.clearInputFieldBySelector(elementSelector) } if (slowTyping) { - await this.page.type(elementSelector, inputValueToFill.toString().trim(), { delay: 100 }) + await this.page.fill(elementSelector, inputValueToFill.toString().trim()) } else { - await this.page.type(elementSelector, inputValueToFill.toString().trim()) + await this.page.fill(elementSelector, inputValueToFill.toString().trim()) } if (pressEnterKey) { await this.page.press(elementSelector, 'Enter') diff --git a/e2e/pages/web-pages/external/stripe-checkout.page.ts b/e2e/pages/web-pages/external/stripe-checkout.page.ts index 8505acbea..ba0a7d7db 100644 --- a/e2e/pages/web-pages/external/stripe-checkout.page.ts +++ b/e2e/pages/web-pages/external/stripe-checkout.page.ts @@ -9,10 +9,9 @@ export class StripeCheckoutPage extends BasePage { private readonly productSummaryTotalAmount = '#ProductSummary-totalAmount span' private readonly checkoutPaymentForm = '.CheckoutPaymentForm' private readonly emailReadonlyInputField = this.checkoutPaymentForm + ' .ReadOnlyFormField-title' - private readonly cardNumberFieldSet = this.checkoutPaymentForm + ' #cardNumber-fieldset' - private readonly cardNumberInputField = this.cardNumberFieldSet + ' #cardNumber' - private readonly cardExpDateInputField = this.cardNumberFieldSet + ' #cardExpiry' - private readonly cardCvcInputField = this.cardNumberFieldSet + ' #cardCvc' + private readonly cardNumberInputField = this.checkoutPaymentForm + ' #cardNumber' + private readonly cardExpDateInputField = this.checkoutPaymentForm + ' #cardExpiry' + private readonly cardCvcInputField = this.checkoutPaymentForm + ' #cardCvc' private readonly billingNameInputField = this.checkoutPaymentForm + ' #billingName' private readonly billingCountryDropdown = this.checkoutPaymentForm + ' #billingCountry' private readonly submitPayButton = this.checkoutPaymentForm + ' button.SubmitButton' diff --git a/e2e/tests/regression/campaign-application/campaign-application-create.spec.ts b/e2e/tests/regression/campaign-application/campaign-application-create.spec.ts new file mode 100644 index 000000000..df61cb4b4 --- /dev/null +++ b/e2e/tests/regression/campaign-application/campaign-application-create.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '../../../utils/fixtures' + +test.describe('Create campaign application', () => { + test('should see list of applications', async ({ page, baseURL }) => { + await page.goto(`${baseURL}/admin/campaign-applications`) + + await expect(page.getByRole('heading')).toHaveText('Кандидат Кампании') + // await expect(page.getByRole('row')).toHaveCount(1); // just title b/c no campaign applications yet + }) +}) diff --git a/e2e/tests/regression/campaign-flow/campaign-view.spec.ts b/e2e/tests/regression/campaign-flow/campaign-view.spec.ts index 216413847..703cbfe57 100644 --- a/e2e/tests/regression/campaign-flow/campaign-view.spec.ts +++ b/e2e/tests/regression/campaign-flow/campaign-view.spec.ts @@ -36,7 +36,7 @@ test.describe.serial( }) test('Particular campaign can be opened through the Campaign page', async () => { - await headerPage.clickDonateHeaderNavButton() + await headerPage.clickDonateHeaderNavButton(LanguagesEnum.EN) await campaignsPage.clickCampaignCardByIndex(0) expect( diff --git a/e2e/utils/fixtures.ts b/e2e/utils/fixtures.ts new file mode 100644 index 000000000..4b08001f4 --- /dev/null +++ b/e2e/utils/fixtures.ts @@ -0,0 +1,30 @@ +import { test as base } from '@playwright/test' +import dotenv from 'dotenv' + +dotenv.config({ path: '../.env.local' }) +dotenv.config({ path: '../.env' }) + +const email = process.env.PODKREPI_EMAIL! +const password = process.env.PODKREPI_PASSWORD! + +export const test = base.extend({ + storageState: async ({ browser, baseURL }, use) => { + const page = await browser.newPage() + await page.goto(`${baseURL}/login`) + + await page.locator('[name=email]').fill(email) + await page.locator('[name=password]').fill(password) + + await page.locator('[type=submit]').click() + await page.waitForURL((url) => !url.pathname.includes('login')) + + const state = await page.context().storageState() + + await page.close() + + use(state) + }, +}) + +/** export the expect for consistency i.e. to be able to do `import { test, expect } from '../utils/fixtures'` */ +export { expect } from 'playwright/test' diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc..a4a7b3f5c 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/package.json b/package.json index 7bbf0915f..90b7753e9 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,15 @@ "sitemap": "next-sitemap" }, "dependencies": { - "@emotion/cache": "^11.7.1", - "@emotion/react": "^11.11.4", + "@emotion/cache": "^11.13.1", + "@emotion/react": "^11.13.3", "@emotion/server": "^11.4.0", - "@emotion/styled": "^11.11.5", + "@emotion/styled": "^11.13.0", "@mdxeditor/editor": "^0.14.2", - "@mui/icons-material": "^5.15.19", + "@mui/icons-material": "^6.1.1", "@mui/lab": "^5.0.0-alpha.170", - "@mui/material": "^5.15.19", + "@mui/material": "^6.1.1", + "@mui/material-nextjs": "^6.1.1", "@mui/styles": "^5.15.19", "@mui/x-data-grid": "^6.16.1", "@mui/x-date-pickers": "^6.16.1", @@ -45,21 +46,22 @@ "@stripe/stripe-js": "^3.3.0", "@tanstack/react-query": "^4.16.1", "@tryghost/content-api": "^1.11.4", - "axios": "^1.6.8", + "axios": "^1.7.4", "axios-hooks": "2.7.0", "chart.js": "^4.4.0", "chartjs-plugin-annotation": "^3.0.1", "chartjs-plugin-datalabels": "^2.2.0", "date-fns": "2.24.0", - "dompurify": "^3.0.3", + "dompurify": "^3.1.3", "formik": "2.2.9", "formik-persist-values": "^1.4.1", "i18next": "^23.5.1", "jwt-decode": "^3.1.2", + "latest": "^0.2.0", "lodash": "^4.17.21", "mobx": "6.3.2", "mobx-react": "7.2.0", - "next": "14.1.1", + "next": "^14.2.13", "next-auth": "^4.24.5", "next-i18next": "^14.0.3", "nookies": "^2.5.2", @@ -122,6 +124,7 @@ "jest": "^29.6.1", "jest-environment-jsdom": "^29.6.1", "lint-staged": "11.0.0", + "msw": "1", "next-sitemap": "^3.1.52", "prettier": "2.3.0", "shx": "^0.3.3", diff --git a/public/finance-reports/Podkrepi.bg_Financial_Report_062022.pdf b/public/finance-reports/Podkrepi.bg_Financial_Report_062022.pdf deleted file mode 100644 index e0b5ac297..000000000 Binary files a/public/finance-reports/Podkrepi.bg_Financial_Report_062022.pdf and /dev/null differ diff --git a/public/finance-reports/Podkrepi.bg_Financial_Report_2023.pdf b/public/finance-reports/Podkrepi.bg_Financial_Report_2023.pdf new file mode 100644 index 000000000..d447fdc79 Binary files /dev/null and b/public/finance-reports/Podkrepi.bg_Financial_Report_2023.pdf differ diff --git a/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2022.pdf b/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2022.pdf new file mode 100644 index 000000000..953d61881 Binary files /dev/null and b/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2022.pdf differ diff --git a/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2024.pdf b/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2024.pdf new file mode 100644 index 000000000..05f872e12 Binary files /dev/null and b/public/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2024.pdf differ diff --git a/public/img/ArtboardRotate.png b/public/img/ArtboardRotate.png deleted file mode 100644 index c2d2b7c22..000000000 Binary files a/public/img/ArtboardRotate.png and /dev/null differ diff --git a/public/img/ArtboardMobile.svg b/public/img/donation-graphic-mobile.svg similarity index 100% rename from public/img/ArtboardMobile.svg rename to public/img/donation-graphic-mobile.svg diff --git a/public/img/Artboard.png b/public/img/donation-graphic.png similarity index 100% rename from public/img/Artboard.png rename to public/img/donation-graphic.png diff --git a/public/img/JoinIcon.png b/public/img/join-icon.png similarity index 100% rename from public/img/JoinIcon.png rename to public/img/join-icon.png diff --git a/public/img/team-photos/AnaNikolova.png b/public/img/team-photos/AnaNikolova.png index b91e69240..252e31824 100644 Binary files a/public/img/team-photos/AnaNikolova.png and b/public/img/team-photos/AnaNikolova.png differ diff --git a/public/img/team-photos/ZhivkoNedyalkov.png b/public/img/team-photos/ZhivkoNedyalkov.png new file mode 100644 index 000000000..791f49ab3 Binary files /dev/null and b/public/img/team-photos/ZhivkoNedyalkov.png differ diff --git a/public/img/team-photos/discord-team-image.jpg b/public/img/team-photos/discord-team-image.jpg deleted file mode 100644 index 3ff7612b0..000000000 Binary files a/public/img/team-photos/discord-team-image.jpg and /dev/null differ diff --git a/public/locales/bg/about-project.json b/public/locales/bg/about-project.json index b2737320d..e9268713f 100644 --- a/public/locales/bg/about-project.json +++ b/public/locales/bg/about-project.json @@ -68,8 +68,10 @@ "finance-report-page": { "financeReport": "Финансови отчети на Сдружение Подкрепи.бг", "finance-report-2021": "Годишен финансов отчет за 2021 г.:", - "finance-report-2022-january-june": "Финансов отчет за полугодие Януари-Юни 2022 г.:", + "finance-report-2022-january-june": "Финансов отчет за полугодие януари-юни 2022 г.:", "finance-report-2022": "Годишен финансов отчет за 2022 г.:", + "finance-report-2023": "Годишен финансов отчет за 2023 г.:", + "finance-report-2024-january-june": "Финансов отчет за полугодие януари-юни 2024 г.:", "download-from-here": "Свалете от тук" } } diff --git a/public/locales/bg/about.json b/public/locales/bg/about.json index 64af93932..0aacf585d 100644 --- a/public/locales/bg/about.json +++ b/public/locales/bg/about.json @@ -1,49 +1,15 @@ { "about": { "title": "Кои сме ние?", - "management-board-members": "Членове на управителен съвет", - "supervisory-board-members": "Членове на надзорен съвет", + "management-board-members": "Членове на Управителен съвет", + "supervisory-board-members": "Членове на Надзорен съвет", "active-team-members": "Активни членове на екипа", "association-members": "Членове на сдружението", "linkedIn": "LinkedIn", "about-the-team": "За екипа", "team-description": "Ние сме група доброволци от IT средите, юристи, счетоводители, маркетолози, медици, НПО представители. Обединява ни желанието да създадем подобрена дарителска среда в България, създавайки максимално прозрачна платформа за дарения. Целта ни е платформата да се издържа от членски внос и дарения към нея, а не от процент комисиона от кампаниите, които тя обслужва.", + "avatar-alt-text": "Аватар на член на екипа", "see-less": "Вижте по-малко", "see-more": "Вижте повече" - }, - "principlesThatUniteUs": { - "title": "Принципи, които ни обединяват", - "voluntary": { - "heading": "ДОБРОВОЛНО И ПРО БОНО", - "content": "Създаваме дарителска платформа в полза на нуждаещите се и организациите, които им помагат. Не търсим финансови или други облаги от това, освен удовлетвореност от осъществените кампании и подкрепените нуждаещи се." - }, - "proactivity": { - "heading": "ПРОАКТИВНОСТ", - "content": "Не чакаме да ни се подаде задача - взимаме си директно. Стремежът в културата ни на работа е адекватна проява на свободна воля, правилен избор и извършване на задачи, без нуждата някой да ги създава и поставя." - }, - "transparency": { - "heading": "ПРОЗРАЧНОСТ", - "content": "Дарителите ни се доверяват, защото осигуряваме прозрачност чрез отворен код, отворени данни/трансакции, отворени решения. Нуждаещите ни се доверяват, защото правим кандидатстването честно, лесно и прозрачно, чрез ясни критерии. Обществото ни се доверява, защото всичко е прозрачно, в това число - и целият ни работен процес. Ние си имаме доверие, защото има ясни и прозрачни принципи на работа помежду ни." - }, - "respect": { - "heading": "УВАЖЕНИЕ", - "content": "Внимателни сме към хората около нас, уважаваме ги и ако можем, им помагаме. Мнението на всеки има значение, затова слушаме и се опитваме да разберем, какво казват другите." - }, - "expertise": { - "heading": "ЕКСПЕРТНОСТ", - "content": "Когато правим избор или вземаме трудни решения, ги базираме на мнението на експерти с опит в конкретната тема." - }, - "privacy": { - "heading": "ПОВЕРИТЕЛНОСТ", - "content": "Уважаваме и пазим личните данни на всеки нуждаещ се, даряващ или помагащ в процеса, все едно са нашите собствени. Ясно дефинираме кой има права за достъп до оперативните данни, като хората, разработващи проекта, няма да имат достъп до личните такива." - }, - "awareness": { - "heading": "ОСЪЗНАТОСТ", - "content": "Търсим най-доброто решение и когато даряваме труда си, го правим с пълното съзнание, че не всичко, което правим, ще влезе в употреба. Ако се случи така, че да не се одобри планът или предложението ти, продължаваш с усмивка към следващата задача. Ако някой друг се справя по-добре в ролята ти, то на драго сърце го препоръчваш да я оглави, а ти му помагаш с каквото можеш." - }, - "sharing": { - "heading": "СПОДЕЛЯНЕ НА ЗНАНИЯ", - "content": "Ако участник в проекта има нужда от помощ или разяснение, обясняваме или споделяме линк към документацията на проекта, даващ необходимата информация. Хората с по-малко опит имат възможност да се включат и да възприемат знания от по-опитните." - } } } diff --git a/public/locales/bg/auth.json b/public/locales/bg/auth.json index b0fb0c13e..1528caba4 100644 --- a/public/locales/bg/auth.json +++ b/public/locales/bg/auth.json @@ -19,7 +19,6 @@ }, "fields": { "email": "Email", - "email-descriptive": "Въведи email адрес", "password": "Парола", "confirm-password": "Потвърждение на парола", "first-name": "Име", diff --git a/public/locales/bg/campaign-application.json b/public/locales/bg/campaign-application.json new file mode 100644 index 000000000..1a075e329 --- /dev/null +++ b/public/locales/bg/campaign-application.json @@ -0,0 +1,90 @@ +{ + "steps": { + "organizer": { + "title": "Организатор", + "name": "Вашите име и фамилия", + "phone": "Телефон за връзка", + "email": "Email", + "transparencyTerms": "Потвърждавам, че кампанията отговаря на основните принципи за публикуване на платформата: да е законна, да е морална, да не е с бизнес насоченост." + }, + "application": { + "title": "Информация за кампанията", + "campaignTitle": "Име на кампанията", + "beneficiary": "Имена на бенефициента", + "beneficiaryRelationship": "Взаимоотношения с бенефициента", + "funds": "Необходима сума в лева", + "campaign-end": { + "title": "Желана крайна дата на кампанията:", + "options": { + "funds": "До събиране на необходимите средства", + "ongoing": "Целогодишно/безсрочно", + "date": "Посочи дата" + } + } + }, + "details": { + "title": "Разкажете ни повече за кампанията Ви", + "description": "Описание", + "current-status": { + "label": "Какво е свършено до момента по кампанията?", + "placeholder": "(набрани материални и/ или нематериални средства, какво е направено с тях, преминати прегледи/ процедури и т.н.)" + }, + "cause": "За какво са нужни средствата?", + "links": { + "label": "Ако е приложимо, прикачете:", + "fields": { + "website": "уебсайт на кампанията", + "media": "линкове към медийно отразяване", + "facebook": "линк към Facebook страница на кампанията", + "donation-platforms": "линк към други платформи за дарения, в които кампанията Ви е (била) активна" + } + }, + "photos": "Снимков/видео материал", + "documents": "Документи", + "disclaimer": "Приемат се до 10 документа. Всеки от документите може да бъде до 30МБ" + }, + "admin": { + "title": "Администраторска редакция", + "status": "Статус", + "external-url": "Външен линк", + "archived": "Архивиран", + "organizer-edit-link": "Организатор може да редактира на" + } + }, + "cta": { + "next": "Запазете и продължете", + "back": "Назад", + "submit": "Заявете кампания" + }, + "remark": { + "part-one": "*Допълнителна информация за процеса на кандидатстване и неговите етапи можете да намерите в нашите ", + "part-two": "и в секцията ", + "links": { + "terms": "Общи условия ", + "faq": "Често задавани въпроси" + } + }, + "alerts": { + "successfully-created": "Успешно създадена заявка за кампания. " + }, + "result": { + "created": "Успешно създадена заявка за кампания.", + "edited": "Успешно редактирана заявка за кампания.", + "editButton": "Редакция на заявка за кампания", + "uploadOk": "Добавени файлове", + "deleteOk": "Премахнати файлове", + "uploadFailed": "Неуспешно добавени файлове", + "deleteFailed": "Неуспешно премахнати файлове", + "uploadFailedWhat": "Какво мога да направя?", + "uploadFailedDirection": "Моля посетете страницата за редакция на заявката за кампания от бутона по-долу и опитайте отново да добавите/премахнете файловете" + }, + "status": { + "selectorLabel": "Статус", + "review": "Нова за ревю", + "requestInfo": "За още информация", + "forCommitteeReview": "За ревю от комисия", + "approved": "Одобрена", + "denied": "Отказана", + "abandoned": "Изоставена" + } +} diff --git a/public/locales/bg/campaigns.json b/public/locales/bg/campaigns.json index 3c15ca8e0..c42b73e32 100644 --- a/public/locales/bg/campaigns.json +++ b/public/locales/bg/campaigns.json @@ -123,7 +123,7 @@ }, "gallery": "Галерия", "financial-report": "Финансови отчети", - "report-campaign": "Докладвайте кампанията", + "report-campaign": "Подайте сигнал за нередност", "feedback": "Обратна връзка", "beneficiary": { "name": "Бенефициент", diff --git a/public/locales/bg/common.json b/public/locales/bg/common.json index 5d61c6cff..be2308538 100644 --- a/public/locales/bg/common.json +++ b/public/locales/bg/common.json @@ -28,7 +28,7 @@ "contribute": "Как да се включа", "project-docs": "Проектна документация", "open-source": "Отворен Код", - "open-data": "Отворени Данни" + "open-data": "Отворени данни" }, "donate": "Дарете", "donation-menu": "Кампании", @@ -130,7 +130,10 @@ "subscribe-text-nonLoggedUser-general": "Моля, продължете като гост и въведете email адреса, на който желаете да получавате известия от нас, или влезте в профила си. Вписвайки се с потребителското си име и парола, ще можете да управлявате абонамента си от своя Личен профил.", "subscribe-text-loggedUser": "Моля, изберете дали желаете да получавате новините за кампанията на email адреса, асоцииран с профила Ви, или на алтернативен адрес:", "subscribe-subtitle": "Искам да получавам новини и известия от Подкрепи.бг на този email адрес:", - "subscribe-campaign-subtTitle": "Искам да получавам новини за кампанията на този email адрес:" + "subscribe-campaign-subtTitle": "Искам да получавам новини за кампанията на този email адрес:", + "email-descriptive": "Въведи email адрес", + "agree-with-newsletter": "Съгласявам се да получавам известия.", + "agree-with-newsletter-campaign": "Съгласявам се да получавам новини за тази кампания и известия от Подкрепи.бг." }, "cookieConsent": "Подкрепи.бг не използва бисквитки, освен тези от трети страни, нужни за аналитичните компоненти Google Analytics и HotJar. Приемането на бисквитките ще ни помогне да подобрим вашето потребителско преживяване.", "cookieConsentButton": "Приемам", diff --git a/public/locales/bg/index.json b/public/locales/bg/index.json index af4b9e1fd..df25a6e15 100644 --- a/public/locales/bg/index.json +++ b/public/locales/bg/index.json @@ -4,14 +4,16 @@ "team-section": { "heading": "Кой стои зад Подкрепи.бг?", "content": "Подкрепи.бг представлява общност от специалисти в областта на програмирането, правото, маркетинга, дизайна, медицината, финансите, социалното предприемачество и др. Обединени сме от целта да създадем устойчива и прозрачна платформа за дарения, която подкрепя каузи и хора в нужда, като заедно с това популяризира и връща доверието към дарителството в България.", - "meet-our-team": "Запознайте се с екипа ни" + "meet-our-team": "Запознайте се с екипа ни", + "team-image-alt-text": "Изображение на екипа" }, "subscription-section": { "heading": "Искате да сте в час с бъдещите ни постижения?", "content": "Абонирайте се за нашия бюлетин и ние ще Ви информираме за най-важното от живота на Подкрепи.бг. Всеки месец ще получвате email от нас, в който ще Ви споделяме най-интересното за кампаниите, които поддържаме, както и за техните организатори и бенефициенти. Ще получавате новините за нашите партньори, доброволци и дарители в електронната си пощенска кутия. Ако Ви звучи добре, запишете се, като въведеш email адреса си тук:" }, "campaign": { - "see-all": "Вижте всички" + "see-all": "Вижте всички", + "see-more": "Вижте повече" }, "how-we-work": { "heading": "Как работи Подкрепи.бг?", diff --git a/public/locales/bg/marketing.json b/public/locales/bg/marketing.json new file mode 100644 index 000000000..54cfa08cd --- /dev/null +++ b/public/locales/bg/marketing.json @@ -0,0 +1,11 @@ +{ + "admin": { + "marketing": "Маркетинг", + "sendConsentEmail": "Изпращане на емайл за съгласие", + "common": { + "templateId": "Идентификатор на Sendgrid шаблон", + "listId": "Идентифицатор на Sendgrid списък с контакти", + "subject": "Тема на емайл" + } + } +} diff --git a/public/locales/bg/profile.json b/public/locales/bg/profile.json index 86b753bc2..00cee9a26 100644 --- a/public/locales/bg/profile.json +++ b/public/locales/bg/profile.json @@ -39,7 +39,6 @@ "toDate": "До", "date": "Дата", "type": "Тип", - "sort": "Вид", "cause": "Кауза", "amount": "Сума", "cancelDonation": "Откажи дарение", @@ -51,13 +50,13 @@ "lv": "лв", "status": { "header": "Статус", - "initial": "започнато", - "waiting": "чакащо", - "succeeded": "успешно", - "refund": "възстановено", - "cancelled": "отменено", - "guaranteed": "гарантирано", - "declined": "отказано" + "initial": "Започнато", + "waiting": "Чакащо", + "succeeded": "Успешно", + "refund": "Възстановено", + "cancelled": "Отменено", + "guaranteed": "Гарантирано", + "declined": "Отказано" } }, "certificates-history": { diff --git a/public/locales/bg/validation.json b/public/locales/bg/validation.json index 247b3ea72..d341eb735 100644 --- a/public/locales/bg/validation.json +++ b/public/locales/bg/validation.json @@ -13,15 +13,14 @@ "terms-of-use": "Моля, приемете oбщите условия", "terms-of-service": "Моля, приемете политиката за защита на личните данни", "agree-with": "Съгласявам се с", - "agree-with-newsletter": "Съгласявам се да получавам известия.", - "agree-with-newsletter-campaign": "Съгласявам се да получавам новини за тази кампания и известия от Подкрепи.бг *", "informed-agree-with": "Запознат съм и се съгласявам с", "terms-and-conditions": "общите условия", "gdpr": "политиката за защита на личните данни", - "newsletter": "Моля дайте своето съгласие", + "newsletter": "Моля дайте своето съгласие.", "legal-entity": "Юридическо лице", "unique-field-violation": "Полето `{1}` със тази стойност вече съществува в платформата.", "payment-reference": "Невалиден формат на кода за плащане", "eik-invalid": "Невалидно ЕИК", - "help-us-improve": "Съгласявам се да се свържете с мен за подобрение на платформата." + "help-us-improve": "Съгласявам се да се свържете с мен за подобрение на платформата.", + "positive-amount": "Сумата трябва да е положително число." } diff --git a/public/locales/en/about-project.json b/public/locales/en/about-project.json index 4ac7d2a6a..dc6456c61 100644 --- a/public/locales/en/about-project.json +++ b/public/locales/en/about-project.json @@ -3,7 +3,7 @@ "aboutPlatformTitle": "What is the platform Podkrepi.bg", "aboutPlatformDescription": "We want to create an open-source charity platform, in which the transparency removes the chances of misuse of the donated funds, both by campaign organizers and by the internal team of the donation platform.", "whatIsDoneTitle": "What has been done already", - "members": "members of the association", + "members": "members of the Association", "meetingsIcon": "meetings held", "investedHoursIcon": "hours invested", "architecture": "Assembled platform architecture", @@ -69,9 +69,11 @@ "association-name": "Association Podkrepi BG", "finance-report-page": { "financeReport": "Annual Reports", - "finance-report-2021": "Annual financial Report for 2021:", - "finance-report-2022-january-june": "Semi-annual Financial Report for January-June 2022:", - "finance-report-2022": "Annual Financial Report for 2022", + "finance-report-2021": "Annual financial report for 2021:", + "finance-report-2022-january-june": "Semiannual financial report for January-June 2022:", + "finance-report-2022": "Annual financial report for 2022:", + "finance-report-2023": "Annual financial report for 2023:", + "finance-report-2024-january-june": "Semiannual financial report for January-June 2024:", "download-from-here": "Download from here" } } diff --git a/public/locales/en/about.json b/public/locales/en/about.json index 656e6f176..670bb24b7 100644 --- a/public/locales/en/about.json +++ b/public/locales/en/about.json @@ -2,48 +2,14 @@ "about": { "title": "Who are we?", "management-board-members": "Management Board members", - "supervisory-board-members": "Supervisory Board Members", + "supervisory-board-members": "Supervisory Board members", "active-team-members": "Active team members", - "association-members": "Members of the association", + "association-members": "Members of the Association", "linkedIn": "LinkedIn", "about-the-team": "About the team", "team-description": "We are volunteers from the IT community, lawyers, accountants, marketers, medics, and NGO representatives. We are united by the desire to create an improved fundraising environment in Bulgaria by developing the most transparent platform for donations. The platform will support itself by membership fees and grants and not by charging a percentage of the funds raised for the campaigns that go through it.", + "avatar-alt-text": "Team member avatar", "see-less": "See less", "see-more": "See more" - }, - "principlesThatUniteUs": { - "title": "Our principles", - "voluntary": { - "heading": "VOLUNTARY AND PRO-BONO", - "content": "We are building a donation platform to benefit those in need and the organizations that help them. We are not seeking financial or other benefits from this initiative, except satisfaction with the implemented campaigns and the support of those in need." - }, - "proactivity": { - "heading": "PROACTIVE APPROACH", - "content": "You should not wait for a task to be given to you; you take it directly! Competent display of free will, appropriate choice and execution of assignments without the need for someone to create and set them." - }, - "transparency": { - "heading": "TRANSPARENCY", - "content": "Benefactors trust us because we provide transparency through open source, open data/transactions, open solutions. Those in need trust us because we make the application process fair, accessible and transparent, through clear criteria. Society trusts us because everything is transparent, including our work process. We trust each other because there are clear and transparent work principles established between us." - }, - "respect": { - "heading": "RESPECT", - "content": "We are kind and good to the people around us; we respect them and if we can we help them. Everyone's opinion matters, so we listen and try to understand what the other person is saying." - }, - "expertise": { - "heading": "EXPERTISE", - "content": "When we make choices or make difficult decisions, we base them on the opinion of experts with close experience in the specific topic." - }, - "privacy": { - "heading": "PRIVACY", - "content": "We respect and protect the personal data of everyone in need, donating or helping in the process, as if they were our own. We clearly define who has rights to access production data, and the people developing the project will not have access to personal data." - }, - "awareness": { - "heading": "AWARENESS", - "content": "We are looking for the best solution and when we donate our work, we do it with the full awareness that not everything we do will come into use. If it so happens that your plan or proposal is not approved, you continue with a smile to the next task. If someone else is doing better in your role, you gladly recommend them to lead it, and you help them as much as you can." - }, - "sharing": { - "heading": "SHARING KNOWLEDGE", - "content": "When a project participant needs help or explanation, you can explain or share a link to the project documentation. People with less experience get involved and absorb knowledge from more experienced team members." - } } } diff --git a/public/locales/en/auth.json b/public/locales/en/auth.json index 4515292c0..1b3e70959 100644 --- a/public/locales/en/auth.json +++ b/public/locales/en/auth.json @@ -18,7 +18,6 @@ }, "fields": { "email": "Email", - "email-descriptive": "Fill in your email", "password": "Password", "confirm-password": "Confirm Password", "first-name": "First name", diff --git a/public/locales/en/campaign-application.json b/public/locales/en/campaign-application.json new file mode 100644 index 000000000..847b7f696 --- /dev/null +++ b/public/locales/en/campaign-application.json @@ -0,0 +1,90 @@ +{ + "steps": { + "organizer": { + "title": "Organizer", + "name": "Your First and Last Name", + "phone": "Contact phone", + "email": "Email", + "transparencyTerms": "I confirm that the campaign meets the basic principles for publishing on the platform: it is legal, it is moral, and it is not business-oriented." + }, + "application": { + "title": "Campaign Application", + "campaignTitle": "Campaign title", + "beneficiary": "Beneficiary names", + "beneficiaryRelationship": "Beneficiary relationship", + "funds": "The required amount in leva", + "campaign-end": { + "title": "Desired campaign end date:", + "options": { + "funds": "Until the required funds are raised", + "ongoing": "Year-round/ongoing", + "date": "Specify date" + } + } + }, + "details": { + "title": "Tell us more about your campaign", + "description": "Description", + "current-status": { + "label": "What has been done so far in the campaign?", + "placeholder": "(collected material and/or non-material resources, what has been done with them, completed examinations/procedures, etc.)" + }, + "cause": "What are the funds needed for?", + "links": { + "label": "If applicable, attach:", + "fields": { + "website": "campaign website", + "media": "links to media coverage", + "facebook": "link to the campaign's Facebook page", + "donation-platforms": "link to other donation platforms where your campaign is (or has been) active" + } + }, + "photos": "Photo/Video material", + "documents": "Documents", + "disclaimer": "Up to 10 documents accepted. Each of the documents can be up to 30MB is size" + }, + "admin": { + "title": "Admin edit", + "status": "Status", + "external-url": "External URL", + "archived": "Archived", + "organizer-edit-link": "Organizer can edit at" + } + }, + "cta": { + "next": "Save and continue", + "back": "Back", + "submit": "Submit campaign" + }, + "remark": { + "part-one": "*Additional information about the application process and its stages can be found in our ", + "part-two": "and in the section ", + "links": { + "terms": "Terms and Conditions ", + "faq": "Frequently Asked Questions" + } + }, + "alerts": { + "successfully-created": "Campaign application successfully created." + }, + "result": { + "created": "Successfully created campaign application", + "edited": "Successfully edited campaign application", + "editButton": "Edit campaign application", + "uploadOk": "Fails added", + "deleteOk": "Files removed", + "uploadFailed": "Failed file add", + "deleteFailed": "Failed to remove files", + "uploadFailedWhat": "What can I do?", + "uploadFailedDirection": "Please visit the campaign application edit page linked below and retry adding/removing the files." + }, + "status": { + "selectorLabel": "Status", + "review": "New - for review", + "requestInfo": "Request for more info", + "forCommitteeReview": "For committee review", + "approved": "Approved", + "denied": "Denied", + "abandoned": "Abandoned" + } +} diff --git a/public/locales/en/campaigns.json b/public/locales/en/campaigns.json index 820c89e4d..a169a8573 100644 --- a/public/locales/en/campaigns.json +++ b/public/locales/en/campaigns.json @@ -123,7 +123,7 @@ }, "gallery": "Gallery", "financial-report": "Financial report", - "report-campaign": "Report the campaign", + "report-campaign": "Report an irregularity", "feedback": "Feedback", "beneficiary": { "name": "Beneficiary", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 0720d65d0..0bc4b9909 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -28,7 +28,7 @@ "contribute": "How to Contribute", "project-docs": "Project Docs", "open-source": "Open Source", - "open-data": "Open Data" + "open-data": "Open data" }, "donate": "Donate", "donation-menu": "Campaigns", @@ -130,7 +130,10 @@ "subscribe-text-nonLoggedUser-general": "Please, proceed as a guest and write down your email, on which you want to receive notifications from us or you can log in. If you log in with your and password you will be able to manage your subscription from your Personal profile", "subscribe-text-loggedUser": "Please, choose if you want to receive the news about the campaign on your profile email or on another one:", "subscribe-subtitle": "I want to receive news and notifications from Podkrepi.bg on this email:", - "subscribe-campaign-subtTitle": "I want to receive news about the campaign on this email:" + "subscribe-campaign-subtTitle": "I want to receive news about the campaign on this email:", + "email-descriptive": "Fill in your email", + "agree-with-newsletter": "I agree to receive news.", + "agree-with-newsletter-campaign": "I agree to receive news about this campaign and news by Podkrepi.bg." }, "cookieConsent": "Podkrepi.bg doesn't use cookies, except the third-party cookies required for the analytics components Google Analytics and HotJar. Accepting the cookies will help us improve your user experience.", diff --git a/public/locales/en/index.json b/public/locales/en/index.json index e5c8c96c1..9ff413cf1 100644 --- a/public/locales/en/index.json +++ b/public/locales/en/index.json @@ -4,14 +4,16 @@ "team-section": { "heading": "Who is behind Podkrepi.bg?", "content": "Podkrepi.bg is a community of specialists in the field of programming, law, marketing, design, medicine, finance, social entrepreneurship and others. We are united by the goal of creating a sustainable and transparent donation platform that supports causes and people in need, while promoting and restoring trust in donations in Bulgaria.", - "meet-our-team": "Meet our team" + "meet-our-team": "Meet our team", + "team-image-alt-text": "Team image" }, "subscription-section": { "heading": "You want to know about our future achievements?", "content": "Subscribe for our newsletter we will infrom you about the most important from the life of Podkrepi.bg. Every month you will receive email from us in which we will share with you the most important things about the campaigns that we have and their organizers and beneficiaries. You will receive news for our partners, volunteers and donors in your email box. If this sounds good for you, subscribe as you fill in your email here:" }, "campaign": { - "see-all": "See all" + "see-all": "See all", + "see-more": "See more" }, "how-we-work": { "heading": "How does Pordkprepi.bg work?", diff --git a/public/locales/en/marketing.json b/public/locales/en/marketing.json new file mode 100644 index 000000000..43bc2206b --- /dev/null +++ b/public/locales/en/marketing.json @@ -0,0 +1,11 @@ +{ + "admin": { + "marketing": "Marketing", + "sendConsentEmail": "Send newsletter consent email", + "common": { + "templateId": "ID of Sendgrid template", + "listId": "ID of Sendgrid contact list", + "subject": "Email subject" + } + } +} diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json index 113606cee..83178609b 100644 --- a/public/locales/en/profile.json +++ b/public/locales/en/profile.json @@ -39,7 +39,6 @@ "toDate": "To", "date": "Date", "type": "Type", - "sort": "Sort", "cause": "Cause", "amount": "Amount", "cancelDonation": "Cancel donation", @@ -51,13 +50,13 @@ "lv": "lv", "status": { "header": "Status", - "initial": "initial", - "waiting": "waiting", - "succeeded": "succeeded", - "refund": "refunded", - "cancelled": "cancelled", - "guaranteed": "guaranteed", - "declined": "declined" + "initial": "Initial", + "waiting": "Waiting", + "succeeded": "Succeeded", + "refund": "Refunded", + "cancelled": "Cancelled", + "guaranteed": "Guaranteed", + "declined": "Declined" } }, "certificates-history": { diff --git a/public/locales/en/validation.json b/public/locales/en/validation.json index 2e10d857d..5225bf637 100644 --- a/public/locales/en/validation.json +++ b/public/locales/en/validation.json @@ -13,15 +13,14 @@ "terms-of-use": "Please accept the Terms and Conditions", "terms-of-service": "Please accept the GDPR policy", "agree-with": "I agree to the", - "agree-with-newsletter": "I agree to receive news", - "agree-with-newsletter-campaign": "I agree to receive news about this campaign and news by Podkrepi.bg *", "informed-agree-with": "I understand and I agree to the", "terms-and-conditions": "Terms and Conditions", "gdpr": "General Data Protection Regulation (GDPR)", "legal-entity": "Legal entity", - "newsletter": "Please give your consent", + "newsletter": "Please give your consent.", "unique-field-violation": "The field `{1}` with this value already exists in the platform.", "payment-reference": "Invalid payment code format", "eik-invalid": "Invalid EIK", - "help-us-improve": "I agree to be contacted to help improve the platform." + "help-us-improve": "I agree to be contacted to help improve the platform.", + "positive-amount": "The amount must be a positive number." } diff --git a/public/scroll-to-top-icon.svg b/public/scroll-to-top-icon.svg new file mode 100644 index 000000000..fd9b29dee --- /dev/null +++ b/public/scroll-to-top-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/common/createEmotionCache.ts b/src/common/createEmotionCache.ts deleted file mode 100644 index 89fc4f66d..000000000 --- a/src/common/createEmotionCache.ts +++ /dev/null @@ -1,5 +0,0 @@ -import createCache from '@emotion/cache' - -export default function createEmotionCache() { - return createCache({ key: 'css', prepend: true }) -} diff --git a/src/common/routes.ts b/src/common/routes.ts index 4aba7c2f3..fd08d4bf3 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -91,6 +91,8 @@ export const routes = { campaigns: { index: '/campaigns', create: '/campaigns/create', + application: 'campaigns/application', + applicationEdit: (id: string) => `/campaigns/application/${id}`, viewCampaignBySlug: (slug: string) => `/campaigns/${slug}`, viewExpenses: (slug: string) => `/campaigns/${slug}/expenses`, oneTimeDonation: (slug: string) => `/campaigns/donation/${slug}`, @@ -161,6 +163,10 @@ export const routes = { viewCampaignBySlug: (slug: string) => `/admin/campaigns/${slug}`, edit: (id: string) => `/admin/campaigns/edit/${id}`, }, + campaignApplications: { + index: '/admin/campaign-applications', + edit: (id: string) => `/admin/campaign-applications/edit/${id}`, + }, news: { index: '/admin/campaign-news', create: '/admin/campaign-news/create', @@ -250,6 +256,10 @@ export const routes = { company: { create: '/admin/companies/create', }, + marketing: { + index: '/admin/marketing/', + newsLetterConsent: '/admin/marketing/newsletter-consent', + }, }, dev: { openData: '/open-data', diff --git a/src/common/theme.ts b/src/common/theme.ts index 7179b473f..de17324e4 100644 --- a/src/common/theme.ts +++ b/src/common/theme.ts @@ -37,6 +37,9 @@ const colors = { white: { main: '#ffffff', }, + red: { + error: '#ED1D1D', + }, } const borders = { @@ -68,7 +71,7 @@ export const themeOptions: ThemeOptions = { dark: darken(colors.blue.dark, 0.2), }, error: { - main: '#D32F2F', + main: colors.red.error, }, }, shape: { diff --git a/src/components/admin/affiliates/AffiliatesPage.tsx b/src/components/admin/affiliates/AffiliatesPage.tsx index 07cbd83d7..8df41bd2a 100644 --- a/src/components/admin/affiliates/AffiliatesPage.tsx +++ b/src/components/admin/affiliates/AffiliatesPage.tsx @@ -19,7 +19,7 @@ import LoopIcon from '@mui/icons-material/Loop' const addIconStyles = { color: '#4ac3ff', - background: 'transperent', + background: 'transparent', fontSize: theme.typography.pxToRem(40), cursor: 'pointer', padding: 0.7, diff --git a/src/components/admin/campaign-applications/CampaignApplicationAdminPropsEdit.tsx b/src/components/admin/campaign-applications/CampaignApplicationAdminPropsEdit.tsx new file mode 100644 index 000000000..52171a6c7 --- /dev/null +++ b/src/components/admin/campaign-applications/CampaignApplicationAdminPropsEdit.tsx @@ -0,0 +1,45 @@ +import { useTranslation } from 'next-i18next' + +import { Grid } from '@mui/material' +import { StatusSelector } from 'components/client/campaign-application/helpers/campaign-application-status' +import { + StyledFormTextField, + StyledStepHeading, +} from 'components/client/campaign-application/helpers/campaignApplication.styled' +import { CamAppDetail } from 'components/client/campaign-application/steps/CampaignApplicationSummary' +import CheckboxField from 'components/common/form/CheckboxField' +import OrganizerCanEditAt from './CampaignApplicationOrganizerCanEditAt' + +export default function CampaignApplicationAdminPropsEdit({ id }: { id: string }) { + const { t } = useTranslation('campaign-application') + return ( + + + {t('steps.admin.title')} + + + + + + + + + + + + + + + + } + /> + + + ) +} diff --git a/src/components/admin/campaign-applications/CampaignApplicationOrganizerCanEditAt.tsx b/src/components/admin/campaign-applications/CampaignApplicationOrganizerCanEditAt.tsx new file mode 100644 index 000000000..71efd59d5 --- /dev/null +++ b/src/components/admin/campaign-applications/CampaignApplicationOrganizerCanEditAt.tsx @@ -0,0 +1,23 @@ +import { routes } from 'common/routes' +import { CopyTextButton } from 'components/common/CopyTextButton' +import getConfig from 'next/config' +import Copy from '@mui/icons-material/CopyAll' +import { Typography } from '@mui/material' +export type Props = { + id: string +} +const OrganizerCanEditAt = ({ id }: Props) => { + const { publicRuntimeConfig } = getConfig() + const url = `${publicRuntimeConfig?.APP_URL}${routes.campaigns.applicationEdit(id)}` + + return ( + <> + + {url} + + } text={url} title={`Copy ${url}`} /> + + ) +} + +export default OrganizerCanEditAt diff --git a/src/components/admin/campaign-applications/CampaignApplications.tsx b/src/components/admin/campaign-applications/CampaignApplications.tsx new file mode 100644 index 000000000..b808da677 --- /dev/null +++ b/src/components/admin/campaign-applications/CampaignApplications.tsx @@ -0,0 +1,17 @@ +import { useTranslation } from 'next-i18next' + +import AdminContainer from 'components/common/navigation/AdminContainer' +import AdminLayout from 'components/common/navigation/AdminLayout' +import CampaignApplicationsGrid from './CampaignApplicationsGrid' + +export default function CampaignApplicationsPage() { + const { t } = useTranslation('campaigns') + + return ( + + + + + + ) +} diff --git a/src/components/admin/campaign-applications/CampaignApplicationsGrid.tsx b/src/components/admin/campaign-applications/CampaignApplicationsGrid.tsx new file mode 100644 index 000000000..4d230a824 --- /dev/null +++ b/src/components/admin/campaign-applications/CampaignApplicationsGrid.tsx @@ -0,0 +1,121 @@ +import { useTranslation } from 'next-i18next' + +import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid' +import { useQuery } from '@tanstack/react-query' +import { routes } from 'common/routes' +import theme from 'common/theme' +import { CampaignApplicationAdminResponse } from 'gql/campaign-applications' +import { useSession } from 'next-auth/react' +import Link from 'next/link' +import { endpoints } from 'service/apiEndpoints' +import { authQueryFnFactory } from 'service/restRequests' + +export default function CampaignApplicationsGrid() { + const { t } = useTranslation('campaign-application') + const { list } = useCampaignsList() + + const commonProps: Partial = { + align: 'left', + width: 100, + headerAlign: 'left', + } + + const columns: GridColDef[] = [ + { + field: 'state', + headerName: t('campaigns:status'), + ...commonProps, + align: 'left', + width: 220, + renderCell: (cellValues: GridRenderCellParams) => t(`status.${cellValues.row.state}`), + }, + { + field: 'campaignName', + headerName: t('campaigns:title'), + ...commonProps, + align: 'left', + width: 250, + renderCell: (cellValues: GridRenderCellParams) => ( + + {cellValues.row.campaignName} + + ), + }, + { + field: 'goal', + headerName: t('campaigns:essence'), + ...commonProps, + align: 'left', + width: 250, + }, + { + field: 'organizerName', + headerName: t('campaigns:organizer'), + ...commonProps, + align: 'left', + width: 200, + }, + { + field: 'beneficiary', + headerName: t('campaigns:beneficiary'), + ...commonProps, + align: 'left', + width: 200, + }, + { + field: 'createdAt', + headerName: t('campaigns:createDate'), + align: 'left', + width: 230, + headerAlign: 'left', + }, + { + field: 'updatedAt', + headerName: t('campaigns:updatedAt'), + align: 'left', + width: 230, + headerAlign: 'left', + }, + ] + + return ( + + ) +} + +function fetchMutation() { + const { data } = useSession() + return useQuery( + [endpoints.campaignApplication.listAllForAdmin.url], + authQueryFnFactory(data?.accessToken), + { + cacheTime: 10 * 60 * 1000, + staleTime: 10 * 60 * 1000, + }, + ) +} + +export const useCampaignsList = () => { + const { data, isLoading } = fetchMutation() + + return { + list: data?.sort((a, b) => b?.updatedAt?.localeCompare(a?.updatedAt ?? '') ?? 0), + isLoading, + } +} diff --git a/src/components/admin/campaign-applications/EditPage.tsx b/src/components/admin/campaign-applications/EditPage.tsx new file mode 100644 index 000000000..7fdc80e11 --- /dev/null +++ b/src/components/admin/campaign-applications/EditPage.tsx @@ -0,0 +1,121 @@ +import { Box, CircularProgress, Grid, Typography } from '@mui/material' +import { red } from '@mui/material/colors' +import { CampaignApplicationFormData } from 'components/client/campaign-application/helpers/campaignApplication.types' +import { ActionSubmitButton } from 'components/client/campaign-application/helpers/campaignApplicationFormActions.styled' +import CampaignApplicationBasic from 'components/client/campaign-application/steps/CampaignApplicationBasic' +import CampaignApplicationDetails from 'components/client/campaign-application/steps/CampaignApplicationDetails' +import CampaignApplicationOrganizer from 'components/client/campaign-application/steps/CampaignApplicationOrganizer' +import CampaignApplicationSummary, { + CamAppDetail, +} from 'components/client/campaign-application/steps/CampaignApplicationSummary' +import GenericForm from 'components/common/form/GenericForm' +import AdminContainer from 'components/common/navigation/AdminContainer' +import AdminLayout from 'components/common/navigation/AdminLayout' +import { CampaignApplicationExisting } from 'gql/campaign-applications' +import { useTranslation } from 'next-i18next' +import NotFoundPage from 'pages/404' +import { + mapCreateOrEditInput, + useCreateOrEditApplication, + useViewCampaignApplicationCached, +} from 'service/campaign-application' +import CampaignApplicationAdminPropsEdit from './CampaignApplicationAdminPropsEdit' +import OrganizerCanEditAt from './CampaignApplicationOrganizerCanEditAt' +import { campaignApplicationAdminValidationSchema } from 'components/client/campaign-application/helpers/validation-schema' + +export type Props = { + id: string +} + +export function EditLoadedCampaign({ campaign }: { campaign: CampaignApplicationExisting }) { + const { createOrUpdateApplication, ...c } = useCreateOrEditApplication({ + isEdit: true, + campaignApplication: campaign, + }) + const { t } = useTranslation('campaign-application') + return ( + + + {c.createOrUpdateSuccessful ? ( + + + + + Admin props / Административни подробности + + + } + /> + + + + + + } + /> + ) : ( + + onSubmit={async (v) => { + const request = mapCreateOrEditInput(v) + await createOrUpdateApplication(request) + }} + initialValues={c.initialValues} + validationSchema={campaignApplicationAdminValidationSchema.defined()}> + + + + + + {c.error && + c.error.map((e, i) => ( + + {e} + + ))} + + )} + + + + ) +} + +export default function EditPage({ id }: Props) { + const { data, isLoading, isError } = useViewCampaignApplicationCached(id, 60 * 1000) + + if (isLoading) { + return ( + + + + ) + } + + if (isError) { + return + } + + return +} diff --git a/src/components/admin/campaign-news/modals/DetailsModal.tsx b/src/components/admin/campaign-news/modals/DetailsModal.tsx index b6d0a015c..653e508b7 100644 --- a/src/components/admin/campaign-news/modals/DetailsModal.tsx +++ b/src/components/admin/campaign-news/modals/DetailsModal.tsx @@ -31,7 +31,7 @@ export default function DetailsModal({ article, onClose }: Props) { {t('Заглавие')}: {article.title} - Слъг: {article.slug} + Кратко наименование: {article.slug} Автор: {article.author} Създадена на: {getExactDateTime(article.createdAt)} diff --git a/src/components/admin/campaigns/grid/CreateForm.tsx b/src/components/admin/campaigns/grid/CreateForm.tsx index f4251a089..5432a7ae0 100644 --- a/src/components/admin/campaigns/grid/CreateForm.tsx +++ b/src/components/admin/campaigns/grid/CreateForm.tsx @@ -66,7 +66,7 @@ const validationSchema: yup.SchemaOf = yup title: yup.string().trim().min(10).max(200).required(), slug: yup.string().trim().min(10).max(200).optional(), description: yup.string().trim().min(50).max(60000).required(), - targetAmount: yup.number().integer().positive().required(), + targetAmount: yup.number().integer().positive('validation:positive-amount').required(), allowDonationOnComplete: yup.bool().optional(), campaignTypeId: yup.string().uuid().required(), beneficiaryId: yup.string().uuid().required(), diff --git a/src/components/admin/campaigns/grid/EditForm.tsx b/src/components/admin/campaigns/grid/EditForm.tsx index 82f56f33a..431325513 100644 --- a/src/components/admin/campaigns/grid/EditForm.tsx +++ b/src/components/admin/campaigns/grid/EditForm.tsx @@ -69,7 +69,7 @@ const validationSchema: yup.SchemaOf title: yup.string().trim().min(10).max(200).required(), slug: yup.string().trim().min(10).max(200).required(), description: yup.string().trim().min(50).required(), - targetAmount: yup.number().integer().positive().required(), + targetAmount: yup.number().integer().positive('validation:positive-amount').required(), allowDonationOnComplete: yup.bool().optional(), campaignTypeId: yup.string().uuid().required(), beneficiaryId: yup.string().required(), diff --git a/src/components/admin/campaigns/grid/modals/DetailsModal.tsx b/src/components/admin/campaigns/grid/modals/DetailsModal.tsx index 89bf66d7f..233daac8e 100644 --- a/src/components/admin/campaigns/grid/modals/DetailsModal.tsx +++ b/src/components/admin/campaigns/grid/modals/DetailsModal.tsx @@ -32,7 +32,7 @@ export default function DetailsModal({ campaign, onClose }: Props) { {t('Заглавие')}: {campaign.title} - Слъг: {campaign.slug} + Кратко наименование: {campaign.slug} Целева сума: {money(campaign.targetAmount, campaign.currency)} diff --git a/src/components/admin/cities/CreateForm.tsx b/src/components/admin/cities/CreateForm.tsx index 9d46544ec..4866935e8 100644 --- a/src/components/admin/cities/CreateForm.tsx +++ b/src/components/admin/cities/CreateForm.tsx @@ -9,7 +9,7 @@ import { Box, Button, Grid, Typography } from '@mui/material' import { CityFormData, CityInput, CityResponse } from 'gql/cities' import { routes } from 'common/routes' -import { ApiErrors, handleUniqueViolation } from 'service/apiErrors' +import { ApiErrors, Message, handleUniqueViolation } from 'service/apiErrors' import { useCreateCity } from 'service/city' import { AlertStore } from 'stores/AlertStore' import GenericForm from 'components/common/form/GenericForm' @@ -42,7 +42,9 @@ export default function EditForm() { const error = e.response if (error?.status === 409) { - const message = error.data.message.map((el) => handleUniqueViolation(el.constraints, t)) + const message = (error.data.message as Message[]).map((el) => + handleUniqueViolation(el.constraints, t), + ) return AlertStore.show(message.join('/n'), 'error') } diff --git a/src/components/admin/documents/Form.tsx b/src/components/admin/documents/Form.tsx index 1c77ee3e9..11bdf8596 100644 --- a/src/components/admin/documents/Form.tsx +++ b/src/components/admin/documents/Form.tsx @@ -23,7 +23,13 @@ const validationSchema = yup .object() .defined() .shape({ - type: yup.string().oneOf(validDocumentTypes).required(), + type: yup + .string() + .oneOf( + validDocumentTypes, + `Видът трябва да бъде една от следните стойности: ${validDocumentTypes.join(', ')}.`, + ) + .required(), name: yup.string().trim().min(2).max(20).required(), filename: yup.string().trim().min(2).max(20).required(), filetype: yup.string().trim().max(3), diff --git a/src/components/admin/donations/grid/Grid.tsx b/src/components/admin/donations/grid/Grid.tsx index ead4a5fe5..dd40b655d 100644 --- a/src/components/admin/donations/grid/Grid.tsx +++ b/src/components/admin/donations/grid/Grid.tsx @@ -181,7 +181,6 @@ export default observer(function Grid() { }, { field: 'paymentId', - //TODO:Ttranslate headerName: 'Плащане номер', width: 150, renderCell: (params: GridRenderCellParams) => { @@ -192,7 +191,6 @@ export default observer(function Grid() { }, { field: 'payment.status', - //TODO:Ttranslate headerName: 'Статус на плащане', renderCell(params) { return params.row.payment?.status @@ -200,7 +198,6 @@ export default observer(function Grid() { }, { field: 'payment.provider', - //TODO:Ttranslate headerName: 'Разплащателна система', renderCell(params) { return params.row.payment?.provider @@ -215,7 +212,6 @@ export default observer(function Grid() { }, { field: 'payment.billingName', - //TODO:Ttranslate headerName: 'billingName', width: 250, renderCell(params) { @@ -224,7 +220,6 @@ export default observer(function Grid() { }, { field: 'payment.billingEmail', - //TODO:Ttranslate headerName: 'billingEmail', width: 300, renderCell(params) { diff --git a/src/components/admin/expenses/Form.tsx b/src/components/admin/expenses/Form.tsx index 939ba0849..41ba887c8 100644 --- a/src/components/admin/expenses/Form.tsx +++ b/src/components/admin/expenses/Form.tsx @@ -51,7 +51,7 @@ export default function Form() { is: (value: string) => value !== undefined, then: yup .number() - .positive() + .positive('validation:positive-amount') .required() .test({ name: 'max', @@ -66,7 +66,7 @@ export default function Form() { return value < Number(currentAmount) }, }), - otherwise: yup.number().positive().integer().required(), + otherwise: yup.number().positive('validation:positive-amount').integer().required(), }), vaultId: yup.string().trim().uuid().required(), deleted: yup.boolean().required(), diff --git a/src/components/admin/irregularity/admin/grid/CreateForm.tsx b/src/components/admin/irregularity/admin/grid/CreateForm.tsx index cb20d94db..0dc7c6e33 100644 --- a/src/components/admin/irregularity/admin/grid/CreateForm.tsx +++ b/src/components/admin/irregularity/admin/grid/CreateForm.tsx @@ -5,22 +5,19 @@ import { useRouter } from 'next/router' import { useTranslation } from 'next-i18next' import * as yup from 'yup' - import { useMutation } from '@tanstack/react-query' - import { AxiosError, AxiosResponse } from 'axios' +import { Person } from 'gql/person' +import { CampaignResponse } from 'gql/campaigns' import { Button, Grid } from '@mui/material' -import { routes } from 'common/routes' - import { ApiErrors } from 'service/apiErrors' import { createIrregularity, uploadIrregularityFiles } from 'service/irregularity' - import { AlertStore } from 'stores/AlertStore' - -import { Person } from 'gql/person' -import { CampaignResponse } from 'gql/campaigns' +import { routes } from 'common/routes' +import { email, name, phone } from 'common/form/validation' +import theme from 'common/theme' import FileUpload from 'components/common/file-upload/FileUpload' import GenericForm from 'components/common/form/GenericForm' @@ -37,7 +34,6 @@ import { IrregularityStatus, UploadIrregularityFiles, } from 'components/client/irregularity/helpers/irregularity.types' -import { email, name, phone } from 'common/form/validation' import CampaignSelect from 'components/client/campaigns/CampaignSelect' import FileList from 'components/client/irregularity/helpers/FileList' @@ -139,7 +135,10 @@ export default function CreateForm({ campaigns, person }: Props) { onSubmit={onSubmit} initialValues={initialValues} validationSchema={validationSchema}> - + = yup.object().defined().shape({ + templateId: yup.string().required(), + listId: yup.string().required(), + subject: yup.string().required(), + dateThreshold: yup.string().optional(), + }) + + const mutationFn = useSendConsentEmail() + + const handleError = (e: AxiosError) => { + const error = e.response as AxiosResponse + AlertStore.show(error.data.message, 'error') + } + + const mutation = useMutation< + AxiosResponse, + AxiosError, + SendNewsLetterConsent + >({ + mutationFn, + onError: (error) => handleError(error), + onSuccess: (data) => { + const response = data.data + AlertStore.show( + t(`Съобщението беше изпратен успешно на ${response.contactCount} емайла.`), + 'success', + ) + }, + }) + + async function onSubmit( + values: SendNewsLetterConsent, + formikHelpers: FormikHelpers, + ) { + const data: SendNewsLetterConsent = { + templateId: values.templateId, + listId: values.listId, + subject: values.subject, + dateThreshold: values.dateThreshold, + } + await mutation.mutateAsync(data) + if (mutation.isSuccess && !mutation.isLoading) { + formikHelpers.resetForm({ values: initialValues }) + } + } + + return ( + + + + {t('admin.sendConsentEmail')} + + + + + + + + + + + + + + + + Премахване от списък на потребители регистрирани след: + + + + + + + + + + + + + ) +} diff --git a/src/components/admin/marketing/EmailConsent/SendEmailConsentPage.tsx b/src/components/admin/marketing/EmailConsent/SendEmailConsentPage.tsx new file mode 100644 index 000000000..db670ca40 --- /dev/null +++ b/src/components/admin/marketing/EmailConsent/SendEmailConsentPage.tsx @@ -0,0 +1,19 @@ +import AdminContainer from 'components/common/navigation/AdminContainer' +import AdminLayout from 'components/common/navigation/AdminLayout' +import React from 'react' +import SendEmailConsentForm from './SendEmailConsentForm' +import { useTranslation } from 'next-i18next' +import { Container } from '@mui/material' + +export default function SendEmailConsentPage() { + const { t } = useTranslation('marketing') + return ( + + + + + + + + ) +} diff --git a/src/components/admin/marketing/MarketingPage.tsx b/src/components/admin/marketing/MarketingPage.tsx new file mode 100644 index 000000000..394f9587d --- /dev/null +++ b/src/components/admin/marketing/MarketingPage.tsx @@ -0,0 +1,58 @@ +import { Box, Button, CardContent, Container, Grid, Typography } from '@mui/material' +import AdminContainer from 'components/common/navigation/AdminContainer' +import AdminLayout from 'components/common/navigation/AdminLayout' +import React from 'react' +import { useTranslation } from 'next-i18next' +import Link from 'next/link' +import { marketingCards } from './navigation/marketingCards' + +const colors = ['#0179a8', '#346cb0', '#5f4b8b', '#b76ba3', '#a7c796', '#00a28a', '#3686a0'] +export default function MarketingPage() { + const { t } = useTranslation('marketing') + return ( + + + + + {marketingCards.map(({ label, href, icon: Icon, disabled }, index) => ( + + + + ))} + + + + + ) +} diff --git a/src/components/admin/marketing/navigation/marketingCards.ts b/src/components/admin/marketing/navigation/marketingCards.ts new file mode 100644 index 000000000..321c9d95d --- /dev/null +++ b/src/components/admin/marketing/navigation/marketingCards.ts @@ -0,0 +1,18 @@ +import { routes } from 'common/routes' +import ThumbUpAltIcon from '@mui/icons-material/ThumbUpAlt' +import SendIcon from '@mui/icons-material/Send' + +export const marketingCards = [ + { + label: 'Изпращане на емайл за съгласие', + icon: ThumbUpAltIcon, + href: routes.admin.marketing.newsLetterConsent, + disabled: false, + }, + { + label: 'Изпращане на маркетинг емайл', + icon: SendIcon, + href: routes.admin.marketing.newsLetterConsent, + disabled: true, + }, +] diff --git a/src/components/admin/partners/helpers/guarantsData.ts b/src/components/admin/partners/helpers/guarantsData.ts index 60ab09692..b6aeaec40 100644 --- a/src/components/admin/partners/helpers/guarantsData.ts +++ b/src/components/admin/partners/helpers/guarantsData.ts @@ -38,6 +38,6 @@ export const data: TeamData[] = [ { img: '/img/partners/guarants/IvayloMarinov.png', name: 'Ивайло Маринов', - description: 'Кмет на община Аспарухово', + description: 'Кмет на район Аспарухово', }, ] diff --git a/src/components/admin/partners/helpers/mediaAboutUsData.ts b/src/components/admin/partners/helpers/mediaAboutUsData.ts index 63517cefb..65190d79e 100644 --- a/src/components/admin/partners/helpers/mediaAboutUsData.ts +++ b/src/components/admin/partners/helpers/mediaAboutUsData.ts @@ -38,7 +38,7 @@ export const articles: ArticleData[] = [ subtitle: 'Нова дигитална платформа стимулира дарителската култура в България', description: '“От началото на месец август стартира нова българска дарителска платформа, подкрепяща нуждаещите се и организациите, които им помагат. Тя се нарича Подкрепи.бг и...”', - url: 'https://darik.bg/darik.bg/nova-digitalna-platforma-stimulira-daritelskata-kultura-v-balgaria', + url: 'https://darik.bg/nova-digitalna-platforma-stimulira-daritelskata-kultura-v-balgaria', }, { img: '/img/partners/mediaAboutUs/mediapool.svg', diff --git a/src/components/admin/recurring-donation/Form.tsx b/src/components/admin/recurring-donation/Form.tsx index 30d5df074..c62eba350 100644 --- a/src/components/admin/recurring-donation/Form.tsx +++ b/src/components/admin/recurring-donation/Form.tsx @@ -38,7 +38,7 @@ const validationSchema = yup personId: yup.string().trim().uuid().required(), extSubscriptionId: yup.string().trim().required(), extCustomerId: yup.string().trim().required(), - money: yup.number().min(0).positive().required(), + money: yup.number().min(0).positive('validation:positive-amount').required(), currency: yup.string().oneOf(validCurrencies).required(), sourceVault: yup.string().trim().uuid().required(), }) @@ -74,7 +74,7 @@ export default function EditForm() { extCustomerId: data?.extCustomerId, money: fromMoney(data?.amount || 0), currency: data?.currency, - sourceVault: data?.sourceVault.id, + sourceVault: '', campaign: data?.sourceVault.campaign.id, } } diff --git a/src/components/admin/transfers/CreateForm.tsx b/src/components/admin/transfers/CreateForm.tsx index ae923ff96..763b5e79a 100644 --- a/src/components/admin/transfers/CreateForm.tsx +++ b/src/components/admin/transfers/CreateForm.tsx @@ -57,7 +57,7 @@ export default function CreateForm({ campaigns }: Props) { is: (value: string) => value !== undefined, then: yup .number() - .positive() + .positive('validation:positive-amount') .required() .test({ name: 'max', @@ -70,7 +70,7 @@ export default function CreateForm({ campaigns }: Props) { return value ? value < Number(fromMoney(currentAmount)) : false }, }), - otherwise: yup.number().positive().integer().required(), + otherwise: yup.number().positive('validation:positive-amount').integer().required(), }), reason: yup.string().trim().min(1).max(300).required(), documentId: yup.string().uuid().notRequired().nullable(), @@ -116,7 +116,7 @@ export default function CreateForm({ campaigns }: Props) { onSubmit={handleSubmit} initialValues={initialValues} validationSchema={validationSchema}> - + {t('form-heading')} diff --git a/src/components/admin/withdrawals/CreateForm.tsx b/src/components/admin/withdrawals/CreateForm.tsx index da0af9a8c..9e980042d 100644 --- a/src/components/admin/withdrawals/CreateForm.tsx +++ b/src/components/admin/withdrawals/CreateForm.tsx @@ -41,7 +41,7 @@ export default function CreateForm() { is: (value: string) => value !== undefined, then: yup .number() - .positive() + .positive('validation:positive-amount') .required() .test({ name: 'max', @@ -55,7 +55,7 @@ export default function CreateForm() { return Number(value) < Number(fromMoney(currentAmount)) }, }), - otherwise: yup.number().positive().integer().required(), + otherwise: yup.number().positive('validation:positive-amount').integer().required(), }), reason: yup.string().trim().min(1).max(300).required(), targetDate: yup.date().required(), diff --git a/src/components/admin/withdrawals/EditForm.tsx b/src/components/admin/withdrawals/EditForm.tsx index 2c1e4e06f..61e80104b 100644 --- a/src/components/admin/withdrawals/EditForm.tsx +++ b/src/components/admin/withdrawals/EditForm.tsx @@ -47,7 +47,7 @@ const validationSchema: yup.SchemaOf = yup .defined() .shape({ status: yup.string().trim().min(1).max(10).required(), - amount: yup.number().positive().required(), + amount: yup.number().positive('validation:positive-amount').required(), reason: yup.string().trim().min(1).max(300).required(), currency: yup.string().oneOf(Object.values(Currency)).required(), sourceVaultId: yup.string().uuid().required(), diff --git a/src/components/client/about-project/FinanceReportPage.tsx b/src/components/client/about-project/FinanceReportPage.tsx index 796421f63..d79da0f21 100644 --- a/src/components/client/about-project/FinanceReportPage.tsx +++ b/src/components/client/about-project/FinanceReportPage.tsx @@ -27,7 +27,7 @@ export default function FinanceReportPage() { } - href="/finance-reports/Podkrepi.bg_Financial_Report_062022.pdf"> + href="/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2022.pdf"> {t('finance-report-page.download-from-here')} @@ -40,6 +40,24 @@ export default function FinanceReportPage() { {t('finance-report-page.download-from-here')} + + {t('finance-report-page.finance-report-2023')} + } + href="/finance-reports/Podkrepi.bg_Financial_Report_2023.pdf"> + {t('finance-report-page.download-from-here')} + + + + {t('finance-report-page.finance-report-2024-january-june')} + } + href="/finance-reports/Podkrepi.bg_Financial_Semiannual_Report_2024.pdf"> + {t('finance-report-page.download-from-here')} + + ) } diff --git a/src/components/client/about/AboutPage.tsx b/src/components/client/about/AboutPage.tsx index 19cc4b072..f2d50cdab 100644 --- a/src/components/client/about/AboutPage.tsx +++ b/src/components/client/about/AboutPage.tsx @@ -17,7 +17,6 @@ export default function AboutPage() { - {/* */} ) } diff --git a/src/components/client/about/helpers/activeMembersData.tsx b/src/components/client/about/helpers/activeMembersData.tsx index 3ff10290d..b3c521ab7 100644 --- a/src/components/client/about/helpers/activeMembersData.tsx +++ b/src/components/client/about/helpers/activeMembersData.tsx @@ -66,12 +66,6 @@ export const data: TeamData[] = [ description: 'Маркетинг', linkedInProfile: 'https://www.linkedin.com/in/neli-tancheva-703193169/', }, - { - img: '/img/team-photos/SlavchoIvanov.png', - name: 'Славчо Иванов', - description: 'Софтуерна разработка', - linkedInProfile: 'https://www.linkedin.com/in/slavchoivanov/', - }, { img: '/img/team-photos/HristiyanHristov.png', name: 'Християн Христов', @@ -120,12 +114,6 @@ export const data: TeamData[] = [ description: 'Маркетинг', linkedInProfile: 'https://www.linkedin.com/in/dessislava-pencheva-emba-24369311/', }, - { - img: '/img/team-photos/ZdravkaGornachka.png', - name: 'Здравка Горначка', - description: 'Кампании', - linkedInProfile: 'https://www.linkedin.com/in/zdravka-gornachka-3a0b3510/', - }, { img: '/img/team-photos/PetyaDimitrova.png', name: 'Петя Димитрова', @@ -168,4 +156,10 @@ export const data: TeamData[] = [ description: 'Продуктов дизайн', linkedInProfile: 'https://www.linkedin.com/in/digitallymarked', }, + { + img: '/img/team-photos/ZhivkoNedyalkov.png', + name: 'Живко Недялков', + description: 'Софтуерно тестване', + linkedInProfile: 'https://www.linkedin.com/in/zhivko-nedyalkov-b44a93111/', + }, ] diff --git a/src/components/client/about/helpers/associationMembersData.tsx b/src/components/client/about/helpers/associationMembersData.tsx index f372d59c7..435d51168 100644 --- a/src/components/client/about/helpers/associationMembersData.tsx +++ b/src/components/client/about/helpers/associationMembersData.tsx @@ -52,7 +52,6 @@ export const data: TeamData[] = [ name: 'Атанас Сарафов', linkedInProfile: 'https://www.linkedin.com/in/atanas-sarafov-192a6889/', }, - { img: '/img/team-photos/PreslavGerchev.png', name: 'Преслав Герчев', diff --git a/src/components/client/about/helpers/managementBoardData.tsx b/src/components/client/about/helpers/managementBoardData.tsx index 470a90351..a7fc1de50 100644 --- a/src/components/client/about/helpers/managementBoardData.tsx +++ b/src/components/client/about/helpers/managementBoardData.tsx @@ -7,51 +7,51 @@ export type TeamData = { export const data: TeamData[] = [ { - img: '/img/team-photos/StankaCherkezova.jpg', - name: 'Станка Черкезова-Калайджиева', + img: '/img/team-photos/RadoslavBozhinov.jpg', + name: 'Радослав Божинов', description: - 'Ръководител Правен Eкип. Близо 15 години подпомагам бизнеси и отделни физически лица с консултации и процесуално представителство в сферата на търговското и гражданското право, включително в казуси с международен елемент. Имам богат юридически опит както в България, така и в чужбина, с работа като външен консултант по различни проекти, свързани с национално и европейско право.', - linkedInProfile: 'https://www.linkedin.com/in/stanka-cherkezova-b2b5845/', + 'Дизайн Лийд в British Telecom (BT), отговорен за стратегията и дизайн екипа към BT - индиректен партньорски онлайн портал. Работил съм в БНТ, маркетинг агенции, няколко английски студиа и компании преди BT. Занимавам се с дизайн от 2007 насам. Участвал съм в набирането на средства за земетресението в Турция, BBC, деца в нужда.', + linkedInProfile: 'https://www.linkedin.com/in/radoslavbozhinov/', }, { - img: '/img/team-photos/AnaNikolova.png', - name: 'Ана Николова', + img: '/img/team-photos/DianaDobreva.jpg', + name: 'Диана Добрева', description: - 'Доктор по Управление на международни проекти. 12 години опит в неправителствения сектор и развитието на младежко лидерство и предприемачество. Помагам на стартиращи и малки бизнеси със силен фокус към иновациите да развият своите идеи в бизнес начинания, чрез развитие на концепции, проектно планиране и управление и достъп до финансиране. Опитът ми в планирането и управлението на проекти ме доведе също до това да стана и външен експерт-оценител по програми на Европейската комисия и да правя обучения по тази и други теми, свързани с развитие на бизнеса.', - linkedInProfile: 'https://www.linkedin.com/in/anatnikolova/', + 'Над 20 год. опит в организация и управление на финансови потоци, вкл. развитие на 3 новостартиращи компании в БГ със съсредоточен голям финансов ресурс. В последните две години подпомагам стартиращи дружества, които получават инвестиции, за да оптимизират финансовите потоци, така че да прескочат 87%-ния риск от неуспех вследствие на недобро планиране и разпределение на ресурсите. Обичаща природата и водеща йогийски начин на живот, в който основно място заема дарителството и помощта към другите.', + linkedInProfile: 'https://www.linkedin.com/in/diana-dobreva-acca-5749b520/', }, { img: '/img/team-photos/MarianaKaroleva.jpg', name: 'Марияна Каролева', description: - 'От години се занимавам с доброволчество, като подкрепям каузи на различни организации, и в качеството си на частно лице помагам със средства, време и внимание на хора в нужда. Като ръководител на Екип Кампании участвам в процеса по структуриране и управление на кампаниите и платформата, в изграждането на различните експертни съвети и набирането на членовете им и в избора и обработката на първите кампании.', + 'От години се занимавам с доброволчество, като подкрепям каузи на различни организации, и в качеството си на частно лице помагам със средства, време и внимание на хора в нужда. Като ръководител на eкип Кампании участвам в процеса по структуриране и управление на кампаниите и платформата, в изграждането на различните експертни съвети и набирането на членовете им и в избора и обработката на първите кампании.', }, { - img: '/img/team-photos/JulianKalderon.png', - name: 'Юлиян Калдерон', + img: '/img/team-photos/GeorgiIvanov.jpg', + name: 'Георги Иванов', description: - "Над 20 години опит като предприемач. Издател на Gamers' Workshop и основател на БГСервиз, където вече 16 години развиваме бизнес софтуерния продукт nZoom. С него помагаме на фирмите да бъдат по-успешни, чрез дигитализация и ефективно използване на информационните технологии. Имам натрупан значителен опит в развитието на софтуерни продукти, който, надявам се, ще бъде от полза в организационен и чисто оперативен план за Подкрепи.бг.", - linkedInProfile: 'https://www.linkedin.com/in/jucalderon/', + 'Занимавам се с предприемачество от над 12 години. Основател на Noble Hire, Conf.ai, Club-Mate Bulgaria, а преди това и на Enhancv. По образование съм геолог. През последните близо 4 години Подкрепи.бг е неизменна част от живота ми.', + linkedInProfile: 'https://www.linkedin.com/in/joroivanoff/', }, { - img: '/img/team-photos/DianaDobreva.jpg', - name: 'Диана Добрева', + img: '/img/team-photos/ZdravkaGornachka.png', + name: 'Здравка Горначка', description: - 'Над 20 год. опит в организация и управление на финансови потоци, вкл. развитие на 3 новостартиращи в БГ компании със съсредоточен голям финансов ресурс. В последните две години подпомагам стартиращи дружества, които получават инвестиции за да оптимизират финансовите потоци така, че да прескочат 87%-ния риск от неуспех вследствие на недобро планиране и разпределение на ресурсите. Обичаща природата и водеща йогийски начин на живот, в който основно място заема дарителството и помощта към другите.', - linkedInProfile: 'https://www.linkedin.com/in/diana-dobreva-acca-5749b520/', + 'Занимавам се с обезпечаването на непрекъсваемостта на критични бизнес приложения от 2007 година, като през последните седем години съм изцяло фокусирана в сферата на прецизната и индустриална климатизация. Съосновател съм на Кулинг Пауър Сълюшънс. По образование съм PR и MBA, с голям интерес към динамиката в малките екипи, ефективното управление и скалиране на процеси. Изключително любопитна съм към новите възможности за колаборации и самоуправление, които предлага дигиталната ера. Вярвам в ненасилствената комуникация, обичам планината, запален скиор съм и доброволец в екип Кампании.', + linkedInProfile: 'https://www.linkedin.com/in/zdravka-gornachka-3a0b3510/', }, { - img: '/img/team-photos/RadoslavBozhinov.jpg', - name: 'Радослав Божинов', + img: '/img/team-photos/IvanMilchev.jpg', + name: 'Иван Милчев', description: - 'Дизайн Лийд в British Telecom (BT), отговорен за стратегията и дизайн екипа към BT индиректен партньорски онлайн портал. Работил съм в БНТ, маркетинг агенции, няколко Английски студиа и компании преди BT. Занимавам се с дизайн-а от 2007 насам. Участвал съм в набирането на средства за земетресението в Турция, BBC деца в нужда.', - linkedInProfile: 'https://www.linkedin.com/in/radoslavbozhinov/', + 'Програмист с над 10 години опит в различни софтуерни направления. По-голямата част от опита ми е придобит в Нидерландия, където съм работил за една от най-проспериращите компании в сферата на производството. От 2 години съм част от Mondoo, където разработваме софтуер за кибер сигурност. От скоро се завърнах в България и мисля да продължа развитието си тук.', + linkedInProfile: 'https://www.linkedin.com/in/ivanmilchev/', }, { - img: '/img/team-photos/IvanGoychev.jpg', - name: 'Иван Гойчев', + img: '/img/team-photos/SlavchoIvanov.png', + name: 'Славчо Иванов', description: - 'Технологичен мениджър с над 15 години опит в създаване на успешни стартъпи и реализиране на големи софтуерни продукти. Като технически директор съм водил развитието на 2 успешни стартъпа във финансовата и самолетната индустрия. Натрупах сериозен опит в огромни компании като Amazon AWS, а сега водя софтуерния отдел на Кобилдър, където дигитализираме строителната индустрия.', - linkedInProfile: 'https://www.linkedin.com/in/igoychev/', + 'Програмист, който се е борил с бъгове в много стартъпи в продължение на повече от 20 години. В момента CTO на индийска IT компания и начинаещ доброволец.', + linkedInProfile: 'https://www.linkedin.com/in/slavchoivanov/', }, ] diff --git a/src/components/client/about/helpers/principlesData.tsx b/src/components/client/about/helpers/principlesData.tsx deleted file mode 100644 index ac4ad7f27..000000000 --- a/src/components/client/about/helpers/principlesData.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { SvgIconProps } from '@mui/material' - -import VoluntaryIcon from '../icons/VoluntaryIcon' -import ProactiveIcon from '../icons/ProactiveIcon' -import ExpertiseIcon from '../icons/ExpertiseIcon' -import RespectIcon from '../icons/RespectIcon' -import TransparencyIcon from '../icons/TransparencyIcon' -import AwarenessIcon from '../icons/AwarenessIcon' -import PrivacyIcon from '../icons/PrivacyIcon' - -export type PrinciplesData = { - icon: React.ComponentType - heading: string - content: string -} - -export const principlesData: PrinciplesData[] = [ - { - icon: VoluntaryIcon, - heading: 'principlesThatUniteUs.voluntary.heading', - content: 'principlesThatUniteUs.voluntary.content', - }, - { - icon: ProactiveIcon, - heading: 'principlesThatUniteUs.proactivity.heading', - content: 'principlesThatUniteUs.proactivity.content', - }, - { - icon: ExpertiseIcon, - heading: 'principlesThatUniteUs.expertise.heading', - content: 'principlesThatUniteUs.expertise.content', - }, - { - icon: RespectIcon, - heading: 'principlesThatUniteUs.respect.heading', - content: 'principlesThatUniteUs.respect.content', - }, - { - icon: TransparencyIcon, - heading: 'principlesThatUniteUs.transparency.heading', - content: 'principlesThatUniteUs.transparency.content', - }, - { - icon: PrivacyIcon, - heading: 'principlesThatUniteUs.privacy.heading', - content: 'principlesThatUniteUs.privacy.content', - }, - { - icon: AwarenessIcon, - heading: 'principlesThatUniteUs.awareness.heading', - content: 'principlesThatUniteUs.awareness.content', - }, - { - icon: AwarenessIcon, - heading: 'principlesThatUniteUs.sharing.heading', - content: 'principlesThatUniteUs.sharing.content', - }, -] diff --git a/src/components/client/about/helpers/supervisoryBoardData.tsx b/src/components/client/about/helpers/supervisoryBoardData.tsx index 19b118066..c982d6161 100644 --- a/src/components/client/about/helpers/supervisoryBoardData.tsx +++ b/src/components/client/about/helpers/supervisoryBoardData.tsx @@ -7,24 +7,38 @@ export type TeamData = { export const data: TeamData[] = [ { - img: '/img/team-photos/GeorgiMalchev.jpg', - name: 'Георги Малчев', - linkedInProfile: 'https://www.linkedin.com/in/georgimalchev/', + img: '/img/team-photos/IvanGoychev.jpg', + name: 'Иван Гойчев', + linkedInProfile: 'https://www.linkedin.com/in/igoychev/', description: - 'Преподавател, Съдружник в агенция Xplora.bg. Член на управителния съвет на IAB Bulgaria. Маркетингът е моето призвание. Гордея се, че за почти 6 години от основаването си агенцията за интегриран дигитален маркетинг Xplora, в която съм управляващ съдружник, е сред водещите дигитални агенции в България. Имаме над 90 текущи клиенти - български и мултинационални компании, които са сред топ 3 в сектора си в областта на финансите, IT и технологичните решения, хранително-вкусовата промишленост и търговията.', + 'Технологичен мениджър с над 15 години опит в създаване на успешни стартъпи и реализиране на големи софтуерни продукти. Като технически директор съм водил развитието на 2 успешни стартъпа във финансовата и самолетната индустрия. Натрупах сериозен опит в огромни компании като Amazon AWS, а сега водя софтуерния отдел на Кобилдър, където дигитализираме строителната индустрия.', }, { - img: '/img/team-photos/MartinKovachev.jpg', - name: 'Мартин Ковачев', - linkedInProfile: 'https://www.linkedin.com/in/martin-kovachev-56984012/', + img: '/img/team-photos/AnaNikolova.png', + name: 'Ана Николова', + linkedInProfile: 'https://www.linkedin.com/in/anatnikolova/', description: - 'Кариерата си започнах пред вече далечната 2001г. От 2007г. стартирах собствена компания за софтуерни разработки и оттогава имаме опит в разнородни проекти с дългосрочни клиенти, основно от чужбина (Испания, САЩ, Германия и др). Всичко разработваме самостоятелно с inhouse екип и много рядко наемаме външни лица. Основната ни насоченост е разработване на мобилни приложения и intranet web платформи.', + 'Доктор по Управление на международни проекти. 12 години опит в неправителствения сектор и развитието на младежко лидерство и предприемачество. Помагам на стартиращи и малки бизнеси със силен фокус към иновациите да развият своите идеи в бизнес начинания чрез развитие на концепции, проектно планиране и управление и достъп до финансиране. Опитът ми в планирането и управлението на проекти ме доведе също до това да стана и външен експерт-оценител по програми на Европейската комисия и да правя обучения по тази и други теми, свързани с развитие на бизнеса.', + }, + { + img: '/img/team-photos/JulianKalderon.png', + name: 'Юлиян Калдерон', + linkedInProfile: 'https://www.linkedin.com/in/jucalderon/', + description: + "Над 20 години опит като предприемач. Издател на Gamers' Workshop и основател на БГСервиз, където вече 16 години развиваме бизнес софтуерния продукт nZoom. С него помагаме на фирмите да бъдат по-успешни, чрез дигитализация и ефективно използване на информационните технологии. Имам натрупан значителен опит в развитието на софтуерни продукти, които, надявам се, ще бъде от полза в организационен и чисто оперативен план за Подкрепи.бг.", }, { img: '/img/team-photos/IvayloIvanov.jpg', name: 'Ивайло Иванов', - linkedInProfile: 'https://www.linkedin.com/in/idivanov/', description: 'Занимавам се с дигитална трансформация на бизнес процеси от 1998г. с клиенти на международния пазар. Основател и CEO на SoftConsultGroup, създал и участвал в редица стартъп компании.', + linkedInProfile: 'https://www.linkedin.com/in/idivanov/', + }, + { + img: '/img/team-photos/StankaCherkezova.jpg', + name: 'Станка Черкезова-Калайджиева', + linkedInProfile: 'https://www.linkedin.com/in/stanka-cherkezova-b2b5845/', + description: + 'Ръководител Правен Eкип. Близо 15 години подпомагам бизнеси и отделни физически лица с консултации и процесуално представителство в сферата на търговското и гражданското право, включително в казуси с международен елемент. Имам богат юридически опит както в България, така и в чужбина, с работа като външен консултант по различни проекти, свързани с национално и европейско право.', }, ] diff --git a/src/components/client/about/icons/AwarenessIcon.tsx b/src/components/client/about/icons/AwarenessIcon.tsx deleted file mode 100644 index 7b4b12d87..000000000 --- a/src/components/client/about/icons/AwarenessIcon.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function AwarenessIcon({ ...props }: SvgIconProps) { - return ( - - - - - - ) -} diff --git a/src/components/client/about/icons/ExpertiseIcon.tsx b/src/components/client/about/icons/ExpertiseIcon.tsx deleted file mode 100644 index b48f2e629..000000000 --- a/src/components/client/about/icons/ExpertiseIcon.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function ExpertiseIcon({ ...props }: SvgIconProps) { - return ( - - - - - - - - - - - - - - - - - - - - - - - - ) -} diff --git a/src/components/client/about/icons/PrivacyIcon.tsx b/src/components/client/about/icons/PrivacyIcon.tsx deleted file mode 100644 index fa03c8a73..000000000 --- a/src/components/client/about/icons/PrivacyIcon.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function PrivacyIcon({ ...props }: SvgIconProps) { - return ( - - - - - - - - - - - - ) -} diff --git a/src/components/client/about/icons/ProactiveIcon.tsx b/src/components/client/about/icons/ProactiveIcon.tsx deleted file mode 100644 index 92899b81f..000000000 --- a/src/components/client/about/icons/ProactiveIcon.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function ProactiveIcon({ ...props }: SvgIconProps) { - return ( - - - - - - - - - - - - - - - - - ) -} diff --git a/src/components/client/about/icons/RespectIcon.tsx b/src/components/client/about/icons/RespectIcon.tsx deleted file mode 100644 index 7bba100c4..000000000 --- a/src/components/client/about/icons/RespectIcon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function RespectIcon({ ...props }: SvgIconProps) { - return ( - - - - - - - - - - - - - ) -} diff --git a/src/components/client/about/icons/VoluntaryIcon.tsx b/src/components/client/about/icons/VoluntaryIcon.tsx deleted file mode 100644 index 18d64f6b7..000000000 --- a/src/components/client/about/icons/VoluntaryIcon.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { SvgIcon, SvgIconProps } from '@mui/material' - -import theme from 'common/theme' - -export default function VoluntaryIcon({ ...props }: SvgIconProps) { - return ( - - - - - - - - - - - - - ) -} diff --git a/src/components/client/about/sections/AboutTheTeamSection/AboutTheTeamSection.tsx b/src/components/client/about/sections/AboutTheTeamSection/AboutTheTeamSection.tsx index 817539ac3..70defd19c 100644 --- a/src/components/client/about/sections/AboutTheTeamSection/AboutTheTeamSection.tsx +++ b/src/components/client/about/sections/AboutTheTeamSection/AboutTheTeamSection.tsx @@ -1,23 +1,16 @@ -import Image from 'next/image' import { useTranslation } from 'next-i18next' import { Grid, Typography } from '@mui/material' -import { DiscordTeamImage } from './AboutTheTeamSection.styled' import { AboutHeading } from 'components/client/about/AboutPage.styled' export default function AboutTheTeamSection() { const { t } = useTranslation('about') - const discordTeamImagePath = '/img/team-photos/discord-team-image.jpg' return ( {t('about.about-the-team')} {t('about.team-description')} - - {/* A11Y TODO: Translate alt text */} - Discord team image - ) } diff --git a/src/components/client/about/sections/ActiveMembersSection/ActiveMembersSection.tsx b/src/components/client/about/sections/ActiveMembersSection/ActiveMembersSection.tsx index 9a52bd736..3bf680df5 100644 --- a/src/components/client/about/sections/ActiveMembersSection/ActiveMembersSection.tsx +++ b/src/components/client/about/sections/ActiveMembersSection/ActiveMembersSection.tsx @@ -24,7 +24,12 @@ export default function ActiveMembersSection() { {data.map((teamMember) => ( <ТeamMemberWrapper key={teamMember.name}> - + {teamMember.name} {teamMember.description} diff --git a/src/components/client/about/sections/AssociationMembersSection/AssociationMembersSection.tsx b/src/components/client/about/sections/AssociationMembersSection/AssociationMembersSection.tsx index 562275ec3..b41239425 100644 --- a/src/components/client/about/sections/AssociationMembersSection/AssociationMembersSection.tsx +++ b/src/components/client/about/sections/AssociationMembersSection/AssociationMembersSection.tsx @@ -24,7 +24,12 @@ export default function AssociationMembersSection() { {data.map((teamMember) => ( - + {teamMember.name} {teamMember.linkedInProfile ? ( diff --git a/src/components/client/about/sections/ManagementBoardSection/ManagementBoardSection.tsx b/src/components/client/about/sections/ManagementBoardSection/ManagementBoardSection.tsx index 9adbd23de..fd5e93149 100644 --- a/src/components/client/about/sections/ManagementBoardSection/ManagementBoardSection.tsx +++ b/src/components/client/about/sections/ManagementBoardSection/ManagementBoardSection.tsx @@ -29,7 +29,12 @@ export default function ManagementBoardSection() { {data.map((teamMember) => ( <ТeamMemberWrapper key={teamMember.name}> - + {teamMember.name} diff --git a/src/components/client/about/sections/ManagementBoardSection/ManagementBoardsection.styled.tsx b/src/components/client/about/sections/ManagementBoardSection/ManagementBoardsection.styled.tsx index 32f6e09a4..6741e0c80 100644 --- a/src/components/client/about/sections/ManagementBoardSection/ManagementBoardsection.styled.tsx +++ b/src/components/client/about/sections/ManagementBoardSection/ManagementBoardsection.styled.tsx @@ -21,6 +21,7 @@ export const ТeamMemberWrapper = styled(Grid)(() => ({ }, [theme.breakpoints.up('md')]: { flex: '1 0 30%', + maxWidth: '30%', }, [theme.breakpoints.up('lg')]: { flex: '1 0 10%', @@ -56,6 +57,6 @@ export const ShowMoreButton = styled(Button)(() => ({ export const Description = styled(Typography)(() => ({ textAlign: 'initial', - marginBottom: theme.spacing(3), + marginBottom: theme.spacing(2), overflow: 'hidden', })) diff --git a/src/components/client/about/sections/ManagementBoardSection/TeamMemberDescription.tsx b/src/components/client/about/sections/ManagementBoardSection/TeamMemberDescription.tsx index 60dd9c9c2..0a4cedcd1 100644 --- a/src/components/client/about/sections/ManagementBoardSection/TeamMemberDescription.tsx +++ b/src/components/client/about/sections/ManagementBoardSection/TeamMemberDescription.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'next-i18next' @@ -9,19 +9,37 @@ import { ShowMoreButton, Description } from './ManagementBoardsection.styled' type Props = { description: string } + export function TeamMemberDescription({ description }: Props) { const { t } = useTranslation('about') - const [show, setShow] = useState(false) + const [isOverflowing, setIsOverflowing] = useState(false) + + const descriptionRef = useRef(null) + + // Check if the content overflows the specified height + useEffect(() => { + if (descriptionRef.current) { + const elementHeight = descriptionRef.current.scrollHeight + const maxHeight = parseFloat(theme.spacing(23).toString().replace('px', '')) + setIsOverflowing(elementHeight >= maxHeight) + } + }, [description]) return ( <> - + {description} - setShow(!show)}> - {show ? t('about.see-less') : t('about.see-more')} - + + {isOverflowing && ( + setShow(!show)}> + {show ? t('about.see-less') : t('about.see-more')} + + )} ) } diff --git a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.styled.tsx b/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.styled.tsx deleted file mode 100644 index 2b593b2a7..000000000 --- a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.styled.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { styled } from '@mui/material/styles' -import { CardContent, CardHeader, Typography } from '@mui/material' - -import theme from 'common/theme' - -export const ContentContainer = styled(CardContent)(() => ({ - margin: theme.spacing(2, 0, 5, 0), - - '&:last-child': { - paddingBottom: 0, - }, -})) - -export const StyledCardHeader = styled(CardHeader)(() => ({ - flexGrow: 1, - flex: 0, - margin: theme.spacing(0), - padding: theme.spacing(0), - alignSelf: 'center', - justifyContent: 'center', - - [theme.breakpoints.up('sm')]: { - justifyContent: 'initial', - }, -})) - -export const PrincipleHeading = styled(Typography)(() => ({ - display: 'block', - color: theme.palette.primary.main, - fontSize: theme.typography.pxToRem(18), -})) diff --git a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.tsx b/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.tsx deleted file mode 100644 index 3eff5e440..000000000 --- a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrincipleCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' - -import { Typography, SvgIconProps, Grid } from '@mui/material' - -import { ContentContainer, StyledCardHeader, PrincipleHeading } from './PrincipleCard.styled' - -type PrincipleCardProps = { - Icon: React.ComponentType - heading?: string - content: string -} - -export default function PrincipleCard({ Icon, heading, content }: PrincipleCardProps) { - return ( - - } - title={{heading}} - /> - - {content} - - - ) -} diff --git a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrinciplesThatUniteUs.tsx b/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrinciplesThatUniteUs.tsx deleted file mode 100644 index 30714dd17..000000000 --- a/src/components/client/about/sections/PrinciplesThatUniteUsSection/PrinciplesThatUniteUs.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' - -import { useTranslation } from 'next-i18next' - -import { Grid } from '@mui/material' - -import PrincipleCard from './PrincipleCard' -import { principlesData } from '../../helpers/principlesData' - -import { AboutHeading } from 'components/client/about/AboutPage.styled' - -export default function PrinciplesThatUniteUs() { - const { t } = useTranslation('about') - - return ( - - - {t('principlesThatUniteUs.title')} - - {principlesData.map((principle) => ( - - - - ))} - - ) -} diff --git a/src/components/client/about/sections/SupervisoryBoardSection/SupervisoryBoardSection.tsx b/src/components/client/about/sections/SupervisoryBoardSection/SupervisoryBoardSection.tsx index 30f2a1e2f..e34316c5f 100644 --- a/src/components/client/about/sections/SupervisoryBoardSection/SupervisoryBoardSection.tsx +++ b/src/components/client/about/sections/SupervisoryBoardSection/SupervisoryBoardSection.tsx @@ -30,7 +30,12 @@ export default function SupervisoryBoardSection() { {data.map((teamMember) => ( - + {teamMember.name}
  • {t('profile:disableModal.deactivateEmails')} - {t('profile:disableModal.link')}. + {t('profile:disableModal.link')}.
  • {t('profile:disableModal.writeUs')} - {t('profile:disableModal.link')}. + {t('profile:disableModal.link')}.

  • diff --git a/src/components/client/auth/profile/DonationTable.tsx b/src/components/client/auth/profile/DonationTable.tsx index 2d64bc286..2fd4cb35e 100644 --- a/src/components/client/auth/profile/DonationTable.tsx +++ b/src/components/client/auth/profile/DonationTable.tsx @@ -93,7 +93,7 @@ function DonationTable({ donations }: DonationTableProps) { {t('profile:donations.date')} {t('profile:donations.status.header')} - {t('profile:donations.sort')} + {t('profile:donations.type')} {t('profile:donations.cause')} {t('profile:donations.amount')} {t('profile:donations.certificate')} @@ -106,7 +106,9 @@ function DonationTable({ donations }: DonationTableProps) { {`${t( 'profile:donations.status.' + donation.payment.status, )}`} - {donation.payment.provider} + + {donation.payment.provider} + { const maxDate = new Date(new Date().setFullYear(new Date().getFullYear() - 18)) -const validationSchema: yup.SchemaOf> = yup - .object() - .defined() - .shape({ - birthday: yup - .date() - .transform(parseDateString) - .max(maxDate, 'profile:birthdateModal.ageInvalid') - .required(), - }) - function UpdateBirthdateModal({ isOpen, handleClose, @@ -76,6 +65,17 @@ function UpdateBirthdateModal({ const { t } = useTranslation() const [loading, setLoading] = useState(false) + const validationSchema: yup.SchemaOf> = yup + .object() + .defined() + .shape({ + birthday: yup + .date() + .transform(parseDateString) + .max(maxDate, t('profile:birthdateModal.ageInvalid')) + .required(), + }) + const dateBefore18Years = new Date(new Date().setFullYear(new Date().getFullYear() - 18)) const initialValues: Pick = { @@ -119,7 +119,11 @@ function UpdateBirthdateModal({ validationSchema={validationSchema}> - + diff --git a/src/components/client/blog/BlogIndexPage.tsx b/src/components/client/blog/BlogIndexPage.tsx index a0bdd10c2..236eb7a94 100644 --- a/src/components/client/blog/BlogIndexPage.tsx +++ b/src/components/client/blog/BlogIndexPage.tsx @@ -2,13 +2,7 @@ import React from 'react' import NextLink from 'next/link' import { useTranslation } from 'next-i18next' import { PostsOrPages, Pagination as GhostPagination } from '@tryghost/content-api' -import { - Container, - Stack, - Typography, - Unstable_Grid2 as Grid2, - PaginationItem, -} from '@mui/material' +import { Container, Stack, Typography, Grid2, PaginationItem } from '@mui/material' import Pagination from '@mui/material/Pagination' import theme from 'common/theme' @@ -38,9 +32,9 @@ export default function BlogIndexPage({ posts, pagination }: Props) { {posts.map((post) => ( - + - + - + ))} - + {pages > 1 && ( - + - + {page.title} - - + + - + - + - + diff --git a/src/components/client/blog/BlogPostPage.tsx b/src/components/client/blog/BlogPostPage.tsx index 25af9f864..880c1e7d2 100644 --- a/src/components/client/blog/BlogPostPage.tsx +++ b/src/components/client/blog/BlogPostPage.tsx @@ -1,6 +1,6 @@ import React from 'react' import { PostOrPage } from '@tryghost/content-api' -import { Container, Typography, Unstable_Grid2 as Grid2 } from '@mui/material' +import { Container, Typography, Grid2 } from '@mui/material' import { baseUrl, routes } from 'common/routes' import Layout from 'components/client/layout/Layout' @@ -24,19 +24,19 @@ export default function BlogPostPage({ post, referer }: Props) { ogImage={post.og_image ?? undefined}> - + - + {post.title} - - + + - + - + - + diff --git a/src/components/client/campaign-application/CampaignApplicationForm.tsx b/src/components/client/campaign-application/CampaignApplicationForm.tsx new file mode 100644 index 000000000..42e7e6c54 --- /dev/null +++ b/src/components/client/campaign-application/CampaignApplicationForm.tsx @@ -0,0 +1,213 @@ +import { Grid, StepLabel } from '@mui/material' +import { Person } from 'gql/person' +import { useCallback, useEffect, useState } from 'react' + +import { + CampaignApplicationFormData, + Step as StepType, + Steps, +} from './helpers/campaignApplication.types' + +import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos' +import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos' +import GenericForm from 'components/common/form/GenericForm' +import CampaignApplicationBasic from './steps/CampaignApplicationBasic' +import CampaignApplicationDetails from './steps/CampaignApplicationDetails' +import CampaignApplicationOrganizer from './steps/CampaignApplicationOrganizer' +import CampaignApplicationRemark from './steps/CampaignApplicationRemark' +import CampaignApplicationStepperIcon from './steps/CampaignApplicationStepperIcon' + +import { validationSchema } from './helpers/validation-schema' + +import { routes } from 'common/routes' +import { FormikHelpers } from 'formik' +import { CampaignApplicationExisting } from 'gql/campaign-applications' +import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' +import { mapCreateOrEditInput, useCreateOrEditApplication } from 'service/campaign-application' +import { AlertStore } from 'stores/AlertStore' +import { + StyledCampaignApplicationStep, + StyledCampaignApplicationStepper, + StyledStepConnector, +} from './helpers/campaignApplication.styled' +import { + ActionButton, + ActionLinkButton, + ActionSubmitButton, + Root, +} from './helpers/campaignApplicationFormActions.styled' +import CampaignApplicationSummary from './steps/CampaignApplicationSummary' + +const steps: StepType[] = [ + { + title: 'campaign-application:steps.organizer.title', + }, + { + title: 'campaign-application:steps.campaign-application.title', + }, + { + title: 'campaign-application:steps.campaign-application-details.title', + }, +] + +type Props = { + person?: Person + isEdit?: boolean + campaignApplication?: CampaignApplicationExisting +} + +export default function CampaignApplicationForm({ + person, + isEdit, + campaignApplication: existing, +}: Props) { + const { t } = useTranslation('campaign-application') + const router = useRouter() + + const { + createOrUpdateApplication, + createOrUpdateSuccessful: applicationCreated, + submitting, + uploadedFiles, + error: createCampaignError, + campaignApplicationResult: camApp, + files, + setFiles, + initialValues, + deletedFiles, + } = useCreateOrEditApplication({ + person, + isEdit, + campaignApplication: existing, + }) + + const handleSubmit = async ( + formData: CampaignApplicationFormData, + { resetForm }: FormikHelpers, + ) => { + if (activeStep === Steps.CREATED_DETAILS && camApp?.id != null) { + router.push(routes.campaigns.applicationEdit(camApp?.id)) // go to the edit page + if (isEdit) { + router.reload() // in case we are re-editing refresh the whole page to reset all the things + } + } else if (shouldSubmit) { + const createOrEdit = mapCreateOrEditInput(formData) + await createOrUpdateApplication(createOrEdit) + if (applicationCreated) { + resetForm() + AlertStore.show(t('alerts.successfully-created'), 'success') + } + } else { + setActiveStep((prevActiveStep) => prevActiveStep + 1) + } + } + + const handleBack = useCallback(() => { + setActiveStep((prevActiveStep) => prevActiveStep - 1) + }, []) + + const [activeStep, setActiveStep] = useState(Steps.ORGANIZER) + const shouldSubmit = activeStep === Steps.CAMPAIGN_DETAILS + + // move to last step after campaign application created successfully + useEffect(() => { + if (applicationCreated && camApp?.id) { + setActiveStep(Steps.CREATED_DETAILS) + } + }, [applicationCreated]) + + return ( + <> + + onSubmit={handleSubmit} + initialValues={initialValues} + validationSchema={validationSchema[activeStep]}> + }> + {steps.map((step) => ( + + + + ))} + + + + {activeStep === Steps.ORGANIZER && } + {activeStep === Steps.CAMPAIGN_BASIC && } + {activeStep === Steps.CAMPAIGN_DETAILS && ( + + )} + {activeStep === Steps.CREATED_DETAILS && ( + + )} + + + + + {activeStep === Steps.ORGANIZER ? ( + }> + {t('cta.back')} + + ) : ( + } + disabled={ + applicationCreated /**after campaign application is created disable going back and editing */ + }> + {t('cta.back')} + + )} + + + } + disabled={submitting} + /> + + + + + {(activeStep === Steps.ORGANIZER || activeStep === Steps.CAMPAIGN_BASIC) && ( + + )} + {/* campaign errors */} + {createCampaignError && ( + <> + Errors: + {createCampaignError?.map((e, i) => ( +

    {e}

    + ))} + + )} + + + ) +} diff --git a/src/components/client/campaign-application/CampaignApplicationPage.tsx b/src/components/client/campaign-application/CampaignApplicationPage.tsx new file mode 100644 index 000000000..6f8cfcefe --- /dev/null +++ b/src/components/client/campaign-application/CampaignApplicationPage.tsx @@ -0,0 +1,23 @@ +import { CircularProgress, Grid } from '@mui/material' +import { useCurrentPerson } from 'common/util/useCurrentPerson' +import Layout from '../layout/Layout' +import CampaignApplicationForm from './CampaignApplicationForm' + +export default function CampaignApplicationPage() { + const { data: userData, isLoading } = useCurrentPerson() + const person = userData?.user || undefined + + if (isLoading) { + return ( + + + + ) + } + + return ( + + + + ) +} diff --git a/src/components/client/campaign-application/EditCampaignApplicationPage.tsx b/src/components/client/campaign-application/EditCampaignApplicationPage.tsx new file mode 100644 index 000000000..2326a4845 --- /dev/null +++ b/src/components/client/campaign-application/EditCampaignApplicationPage.tsx @@ -0,0 +1,31 @@ +import { CircularProgress, Grid } from '@mui/material' +import NotFoundPage from 'pages/404' +import { useViewCampaignApplicationCached } from 'service/campaign-application' +import Layout from '../layout/Layout' +import CampaignApplicationForm from './CampaignApplicationForm' + +interface EditProps { + id: string +} + +export default function EditCampaignApplicationPage({ id }: EditProps) { + const { data, isLoading, isError } = useViewCampaignApplicationCached(id) + + if (isLoading) { + return ( + + + + ) + } + + if (isError) { + return + } + + return ( + + + + ) +} diff --git a/src/components/client/campaign-application/helpers/campaign-application-status.tsx b/src/components/client/campaign-application/helpers/campaign-application-status.tsx new file mode 100644 index 000000000..31d440d26 --- /dev/null +++ b/src/components/client/campaign-application/helpers/campaign-application-status.tsx @@ -0,0 +1,27 @@ +import { FormControl, FormControlProps, InputLabel, MenuItem, Select } from '@mui/material' +import { useField } from 'formik' +import { useTranslation } from 'next-i18next' +import { allStates } from './campaignApplication.types' + +export type Props = { name: string } & FormControlProps + +export const StatusSelector = ({ name, ...control }: Props) => { + const [field] = useField(name) + const { t } = useTranslation('campaign-application') + return ( + + {t('status.selectorLabel')} + + + ) +} diff --git a/src/components/client/campaign-application/helpers/campaignApplication.styled.tsx b/src/components/client/campaign-application/helpers/campaignApplication.styled.tsx new file mode 100644 index 000000000..40e25d7f1 --- /dev/null +++ b/src/components/client/campaign-application/helpers/campaignApplication.styled.tsx @@ -0,0 +1,66 @@ +import { styled } from '@mui/material/styles' +import { Grid, Step, StepConnector, Stepper } from '@mui/material' +import Heading from 'components/common/Heading' +import FormTextField from 'components/common/form/FormTextField' +import theme from 'common/theme' + +export const StyledCampaignApplicationStep = styled(Step)(() => ({ + padding: 0, + + '& span': { + padding: 0, + }, + + '& .Mui-active': { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.palette.primary.main, + height: '70px', + width: '70px', + borderRadius: theme.borders.round, + + '& span': { + color: theme.palette.common.white, + fontSize: theme.typography.pxToRem(48), + }, + }, +})) + +export const StyledCampaignApplicationStepper = styled(Stepper)(() => ({ + backgroundColor: 'transparent', + margin: '20px auto', + maxWidth: '530px', +})) + +export const StyledStepConnector = styled(StepConnector)(() => ({ + height: 5, + backgroundColor: theme.palette.primary.main, + + '& span': { + border: 'none', + }, +})) + +export const StyledCampaignApplicationStepperIcon = styled(Grid)(() => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: theme.palette.common.black, + height: '52.05px', + width: '52.05px', + border: `5px solid ${theme.palette.primary.main}`, + borderRadius: theme.borders.round, + zIndex: 1, + fontSize: theme.typography.pxToRem(36), +})) + +export const StyledStepHeading = styled(Heading)(() => ({ + fontWeight: 600, + paddingBlock: theme.spacing(5), +})) + +export const StyledFormTextField = styled(FormTextField)(() => ({ + borderRadius: theme.borders.round, + height: theme.spacing(5), +})) diff --git a/src/components/client/campaign-application/helpers/campaignApplication.types.ts b/src/components/client/campaign-application/helpers/campaignApplication.types.ts new file mode 100644 index 000000000..cba46e6d5 --- /dev/null +++ b/src/components/client/campaign-application/helpers/campaignApplication.types.ts @@ -0,0 +1,80 @@ +export type Step = { + title: string +} + +export enum Steps { + NONE = -1, + ORGANIZER = 0, + CAMPAIGN_BASIC = 1, + CAMPAIGN_DETAILS = 2, + CREATED_DETAILS = 3, +} + +export type CampaignApplicationOrganizer = { + name: string + phone: string + email: string + acceptTermsAndConditions: boolean + transparencyTermsAccepted: boolean + personalInformationProcessingAccepted: boolean +} + +export type CampaignApplicationBasic = { + beneficiaryNames: string + title: string + campaignType: string + funds: number + campaignEnd: string + campaignEndDate?: string +} + +export type CampaignApplicationDetails = { + cause: string + organizerBeneficiaryRelationship?: string + description?: string + currentStatus?: string +} + +// keep in sync with api repo/podkrepi.dbml -> Enum CampaignApplicationState +export type CampaignApplicationState = + | 'review' + | 'requestInfo' + | 'forCommitteeReview' + | 'approved' + | 'denied' + | 'abandoned' + +export const allStates: CampaignApplicationState[] = [ + 'review', + 'requestInfo', + 'forCommitteeReview', + 'approved', + 'denied', + 'abandoned', +] + +export type CampaignApplicationAdmin = { + state: CampaignApplicationState + ticketURL?: string + archived?: boolean +} + +export type CampaignApplicationFormData = { + organizer: CampaignApplicationOrganizer + applicationBasic: CampaignApplicationBasic + applicationDetails: CampaignApplicationDetails + admin?: CampaignApplicationAdmin +} + +export type CampaignApplicationFormDataSteps = { + [Steps.NONE]: never + [Steps.ORGANIZER]: { + organizer: CampaignApplicationOrganizer + } +} + +export enum CampaignEndTypes { + FUNDS = 'funds', + ONGOING = 'ongoing', + DATE = 'date', +} diff --git a/src/components/client/campaign-application/helpers/campaignApplicationFormActions.styled.tsx b/src/components/client/campaign-application/helpers/campaignApplicationFormActions.styled.tsx new file mode 100644 index 000000000..2da86fc7e --- /dev/null +++ b/src/components/client/campaign-application/helpers/campaignApplicationFormActions.styled.tsx @@ -0,0 +1,36 @@ +import { styled } from '@mui/material/styles' +import { Button, Grid } from '@mui/material' + +import LinkButton from 'components/common/LinkButton' +import SubmitButton from 'components/common/form/SubmitButton' + +import theme from 'common/theme' + +const commonButtonStyles = { + padding: theme.spacing(1, 5), + border: `1px solid ${theme.palette.common.black}`, + borderRadius: theme.borders.round, + color: theme.palette.common.black, + fontSize: theme.typography.pxToRem(15), + fontWeight: 800, +} + +export const Root = styled(Grid)(() => ({ + marginTop: theme.spacing(15), + textAlign: 'center', +})) + +export const ActionLinkButton = styled(LinkButton)(() => ({ + ...commonButtonStyles, + backgroundColor: theme.palette.common.white, +})) + +export const ActionButton = styled(Button)(() => ({ + ...commonButtonStyles, + backgroundColor: theme.palette.common.white, +})) + +export const ActionSubmitButton = styled(SubmitButton)(() => ({ + ...commonButtonStyles, + backgroundColor: '#62C4FB', +})) diff --git a/src/components/client/campaign-application/helpers/validation-schema.ts b/src/components/client/campaign-application/helpers/validation-schema.ts new file mode 100644 index 000000000..58f1d7598 --- /dev/null +++ b/src/components/client/campaign-application/helpers/validation-schema.ts @@ -0,0 +1,82 @@ +import * as yup from 'yup' + +import { email, name, phone } from 'common/form/validation' + +import { + CampaignApplicationAdmin, + CampaignApplicationBasic, + CampaignApplicationDetails, + CampaignApplicationFormData, + CampaignApplicationOrganizer, + CampaignApplicationState, + Steps, +} from './campaignApplication.types' + +const organizerSchema: yup.SchemaOf = yup.object().shape({ + name: name.required(), + phone: phone.required(), + email: email.required(), + acceptTermsAndConditions: yup.bool().oneOf([true], 'validation:terms-of-use').required(), + transparencyTermsAccepted: yup.bool().oneOf([true], 'validation:required').required(), + personalInformationProcessingAccepted: yup + .bool() + .oneOf([true], 'validation:terms-of-service') + .required(), +}) + +const basicSchema: yup.SchemaOf = yup.object().shape({ + beneficiaryNames: yup.string().required(), + campaignEnd: yup.string().required(), + campaignType: yup.string().required(), + funds: yup.number().required(), + title: yup.string().required(), + campaignEndDate: yup.string().optional(), +}) + +const detailsSchema: yup.SchemaOf = yup.object().shape({ + cause: yup.string().required(), + campaignGuarantee: yup.string().optional(), + currentStatus: yup.string().optional(), + description: yup.string().optional(), + documents: yup.array().optional(), + links: yup.array().optional(), + organizerBeneficiaryRelationship: yup.string().optional(), + otherFinancialSources: yup.string().optional(), +}) + +const adminPropsSchema: yup.SchemaOf = yup.object().shape({ + state: yup + .mixed() + .oneOf(['review', 'requestInfo', 'forCommitteeReview', 'approved', 'denied', 'abandoned']) + .required(), + ticketURL: yup.string().optional(), + archived: yup.bool().optional(), +}) + +export const validationSchema: { + [Steps.NONE]: undefined + [Steps.ORGANIZER]: yup.SchemaOf> + [Steps.CAMPAIGN_BASIC]: yup.SchemaOf> + [Steps.CAMPAIGN_DETAILS]: yup.SchemaOf> + [Steps.CREATED_DETAILS]: undefined +} = { + [Steps.NONE]: undefined, + [Steps.CREATED_DETAILS]: undefined, + [Steps.ORGANIZER]: yup.object().shape({ + organizer: organizerSchema.defined(), + }), + [Steps.CAMPAIGN_BASIC]: yup.object().shape({ + applicationBasic: basicSchema.defined(), + }), + [Steps.CAMPAIGN_DETAILS]: yup.object().shape({ + applicationDetails: detailsSchema.defined(), + }), +} + +export const campaignApplicationAdminValidationSchema: yup.SchemaOf = + yup.object().shape({ + organizer: organizerSchema.defined(), + applicationBasic: basicSchema.defined(), + applicationDetails: detailsSchema.defined(), + admin: adminPropsSchema.nullable().defined(), + }) diff --git a/src/components/client/campaign-application/steps/CampaignApplicationBasic.tsx b/src/components/client/campaign-application/steps/CampaignApplicationBasic.tsx new file mode 100644 index 000000000..864dcd8f9 --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationBasic.tsx @@ -0,0 +1,125 @@ +import { FormControl, Grid, Typography } from '@mui/material' +import { Field, useFormikContext } from 'formik' +import { useTranslation } from 'next-i18next' + +import CampaignTypeSelect from 'components/client/campaigns/CampaignTypeSelect' +import FormDatePicker from 'components/common/form/FormDatePicker' +import { StyledFormTextField, StyledStepHeading } from '../helpers/campaignApplication.styled' +import { CampaignApplicationFormData, CampaignEndTypes } from '../helpers/campaignApplication.types' + +import theme from 'common/theme' +import { useEffect, useState } from 'react' + +export default function CampaignApplicationBasic() { + const { t } = useTranslation('campaign-application') + const { values, setFieldValue } = useFormikContext() + // if user selects the date we'll fill in the previously selected (or new Date()) or remove that in case they chose another option + const [selectedDate, setSelectedDate] = useState( + values?.applicationBasic?.campaignEndDate ?? new Date().toString(), + ) + useEffect(() => { + const endDate = values.applicationBasic?.campaignEndDate + if (endDate != null && endDate != selectedDate) { + setSelectedDate(endDate) + } + setFieldValue( + 'applicationBasic.campaignEndDate', + values?.applicationBasic?.campaignEnd === CampaignEndTypes.DATE ? selectedDate : undefined, + false, + ) + }, [values?.applicationBasic?.campaignEnd]) + + return ( + + + {t('steps.application.title')} + + + + + + + + + + + + + + + + + + + + + {t('steps.application.campaign-end.title')} + + + + + + + + + + + + + + + + {values?.applicationBasic?.campaignEnd === CampaignEndTypes.DATE && + values?.applicationBasic?.campaignEndDate != null && ( + + + + )} + + + + + + ) +} diff --git a/src/components/client/campaign-application/steps/CampaignApplicationDetails.tsx b/src/components/client/campaign-application/steps/CampaignApplicationDetails.tsx new file mode 100644 index 000000000..6fa0945f6 --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationDetails.tsx @@ -0,0 +1,78 @@ +import { Grid, Typography } from '@mui/material' +import { useTranslation } from 'next-i18next' + +import FormTextField from 'components/common/form/FormTextField' +import { StyledStepHeading } from '../helpers/campaignApplication.styled' + +import FileList from 'components/common/file-upload/FileList' +import FileUpload from 'components/common/file-upload/FileUpload' +import { Dispatch, SetStateAction } from 'react' + +export type Props = { + files: File[] + setFiles: Dispatch> +} + +export default function CampaignApplicationDetails({ files, setFiles }: Props) { + const { t } = useTranslation('campaign-application') + + return ( + + + {t('steps.details.title')} + + + + + + + + + + + + + { + setFiles((prevFiles) => [...prevFiles, ...newFiles]) + }} + accept="text/plain,application/json,application/pdf,image/png,image/jpeg,application/xml,text/xml,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + /> + {t('steps.details.disclaimer')} + + setFiles((prevFiles) => prevFiles.filter((file) => file.name !== deletedFile.name)) + } + rolesList={{}} + onSetFileRole={() => { + // we have no roles for the campaign application - it's all a document + return undefined + }} + filesRole={[]} + /> + + + + ) +} diff --git a/src/components/client/campaign-application/steps/CampaignApplicationOrganizer.tsx b/src/components/client/campaign-application/steps/CampaignApplicationOrganizer.tsx new file mode 100644 index 000000000..a96556b3d --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationOrganizer.tsx @@ -0,0 +1,75 @@ +import { useTranslation } from 'next-i18next' + +import { FormControl, Grid, Typography } from '@mui/material' + +import CheckboxField from 'components/common/form/CheckboxField' +import AcceptTermsField from 'components/common/form/AcceptTermsField' +import AcceptPrivacyPolicyField from 'components/common/form/AcceptPrivacyPolicyField' + +import { StyledStepHeading, StyledFormTextField } from '../helpers/campaignApplication.styled' + +type Props = { + isAdmin?: boolean +} + +export default function CampaignApplicationOrganizer({ isAdmin }: Props) { + const { t } = useTranslation('campaign-application') + + return ( + + + {t('steps.organizer.title')} + + + + + + + + + + + + + + + + + + + + + {t('steps.organizer.transparencyTerms')}
    + } + disabled={isAdmin} + /> + + + + + + + + ) +} diff --git a/src/components/client/campaign-application/steps/CampaignApplicationRemark.tsx b/src/components/client/campaign-application/steps/CampaignApplicationRemark.tsx new file mode 100644 index 000000000..82f95c872 --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationRemark.tsx @@ -0,0 +1,27 @@ +import { Typography, Link, styled } from '@mui/material' +import { useTranslation } from 'next-i18next' +import { routes } from 'common/routes' + +const StyledRemark = styled(Typography)(() => ({ + textAlign: 'center', + maxWidth: '80%', + margin: '100px auto', + fontSize: '12px', +})) + +export default function CampaignApplicationRemark() { + const { t } = useTranslation('campaign-application') + + return ( + + {t('remark.part-one')} + + {t('remark.links.terms')} + + {t('remark.part-two')} + + {t('remark.links.faq')} + + + ) +} diff --git a/src/components/client/campaign-application/steps/CampaignApplicationStepperIcon.tsx b/src/components/client/campaign-application/steps/CampaignApplicationStepperIcon.tsx new file mode 100644 index 000000000..4b86a72ca --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationStepperIcon.tsx @@ -0,0 +1,16 @@ +import { StepIconProps } from '@mui/material/StepIcon' +import { StyledCampaignApplicationStepperIcon } from '../helpers/campaignApplication.styled' + +export default function CampaignApplicationStepperIcon(props: StepIconProps) { + const icons: { [index: string]: React.ReactElement } = { + 1: 1, + 2: 2, + 3: 3, + } + + return ( + + {icons[String(props.icon)]} + + ) +} diff --git a/src/components/client/campaign-application/steps/CampaignApplicationSummary.tsx b/src/components/client/campaign-application/steps/CampaignApplicationSummary.tsx new file mode 100644 index 000000000..f320c102b --- /dev/null +++ b/src/components/client/campaign-application/steps/CampaignApplicationSummary.tsx @@ -0,0 +1,145 @@ +import { Grid, Typography } from '@mui/material' +import { green, orange, red } from '@mui/material/colors' +import { CampaignApplicationResponse } from 'gql/campaign-applications' +import { useTranslation } from 'next-i18next' +import { CampaignEndTypes } from '../helpers/campaignApplication.types' + +export interface SummaryProps { + uploadedFiles: Record + camApp?: CampaignApplicationResponse + deletedFiles?: Record + isEdit?: boolean + prependChildren?: JSX.Element +} + +function FilesDetail({ + label, + files, + type, +}: { + label: string + files?: string[] + type?: 'success' | 'failure' | 'successful-delete' +}) { + return ( + Number(files?.length) > 0 && ( + <> + + {label} + + + {files?.map((f) => ( + + {f} + + ))} + + + ) + ) +} + +export function CamAppDetail({ label, value }: { label: string; value?: string | JSX.Element }) { + const normalized = + typeof value === 'string' && value.trim() != '' ? value : value != null ? value : '-' + return ( + <> + + {label} + + + {normalized} + + + ) +} + +export default function CampaignApplicationSummary({ + uploadedFiles, + camApp, + deletedFiles, + isEdit, + prependChildren, +}: SummaryProps) { + const { t } = useTranslation('campaign-application') + + return ( + <> + {t(isEdit ? 'result.edited' : 'result.created')} + + + + {prependChildren} + + + {uploadedFiles.failed.length > 0 && ( + <> + +

    {t('result.uploadFailedDirection')}

    + + )} + {deletedFiles && deletedFiles.failed.length > 0 && ( + <> + +

    {t('result.uploadFailedDirection')}

    + + )} + + + + + + + + + + + +
    +
    +
    + + ) +} diff --git a/src/components/client/campaign-news/CampaignNewsList.tsx b/src/components/client/campaign-news/CampaignNewsList.tsx index 06453c79f..a1d5da083 100644 --- a/src/components/client/campaign-news/CampaignNewsList.tsx +++ b/src/components/client/campaign-news/CampaignNewsList.tsx @@ -188,7 +188,7 @@ export default function CampaignNewsList({ articles }: Props) { expandContent(article.id) scrollToTop(article.id) }} - sx={{ background: 'transperent', width: '100%' }}> + sx={{ background: 'transparent', width: '100%' }}> {!isExpanded[article.id] ? `${t('read-more')} >` : `${t('read-less')} <`} )} diff --git a/src/components/client/campaign-news/CampaignNewsPage.tsx b/src/components/client/campaign-news/CampaignNewsPage.tsx index 4ced2d01d..2e269ec1f 100644 --- a/src/components/client/campaign-news/CampaignNewsPage.tsx +++ b/src/components/client/campaign-news/CampaignNewsPage.tsx @@ -37,11 +37,6 @@ const Root = styled(Layout)(({ theme }) => ({ letterSpacing: theme.typography.pxToRem(-1.5), marginBottom: theme.spacing(1), }, - - '.ql-video, img': { - margin: '0 auto', - display: 'block', - }, })) type Props = { diff --git a/src/components/client/campaigns/CampaignCard/CampaignCard.tsx b/src/components/client/campaigns/CampaignCard/CampaignCard.tsx index cf1ab0d60..d5c7b81ef 100644 --- a/src/components/client/campaigns/CampaignCard/CampaignCard.tsx +++ b/src/components/client/campaigns/CampaignCard/CampaignCard.tsx @@ -92,13 +92,14 @@ export default function ActiveCampaignCard({ campaign, index }: Props) { - - {t('cta.support')} - + {(campaignState === CampaignState.complete && !allowDonationOnComplete) || ( + + {t('cta.support')} + + )} ) diff --git a/src/components/client/campaigns/CampaignDetails.tsx b/src/components/client/campaigns/CampaignDetails.tsx index 1afff8217..b7f2a6d5e 100644 --- a/src/components/client/campaigns/CampaignDetails.tsx +++ b/src/components/client/campaigns/CampaignDetails.tsx @@ -2,31 +2,30 @@ import React, { useState } from 'react' import { useTranslation } from 'next-i18next' import dynamic from 'next/dynamic' - import { CampaignResponse } from 'gql/campaigns' - import 'react-quill/dist/quill.bubble.css' import { Divider, Grid, IconButton, Tooltip, Typography } from '@mui/material' -import SecurityIcon from '@mui/icons-material/Security' +import NotificationsActiveOutlinedIcon from '@mui/icons-material/NotificationsActiveOutlined' +import EditIcon from '@mui/icons-material/Edit' +import { Assessment, InfoOutlined } from '@mui/icons-material' +import EmailIcon from '@mui/icons-material/Email' import { styled } from '@mui/material/styles' -import DonationWishes from './DonationWishes' import { ImageSlider } from 'components/common/ImageSlider' -import CampaignInfo from './CampaignInfo/CampaignInfo' -import CampaignInfoGraphics from './CampaignInfoGraphics' -import CampaignInfoOperator from './CampaignInfoOperator' import LinkButton from 'components/common/LinkButton' import { campaignSliderUrls } from 'common/util/campaignImageUrls' -import CampaignPublicExpensesGrid from './CampaignPublicExpensesGrid' -import EditIcon from '@mui/icons-material/Edit' import { useCampaignApprovedExpensesList } from 'common/hooks/expenses' -import { Assessment, InfoOutlined } from '@mui/icons-material' -import { routes } from 'common/routes' import { useCanEditCampaign } from 'common/hooks/campaigns' import { moneyPublic } from 'common/util/money' +import { routes } from 'common/routes' + +import DonationWishes from './DonationWishes' +import CampaignInfo from './CampaignInfo/CampaignInfo' +import CampaignInfoGraphics from './CampaignInfoGraphics' +import CampaignInfoOperator from './CampaignInfoOperator' +import CampaignPublicExpensesGrid from './CampaignPublicExpensesGrid' import CampaignPublicExpensesChart from './CampaignPublicExpensesChart' -import EmailIcon from '@mui/icons-material/Email' import RenderCampaignSubscribeModal from '../notifications/CampaignSubscribeModal' const ReactQuill = dynamic(() => import('react-quill'), { ssr: false }) @@ -38,7 +37,7 @@ const classes = { banner: `${PREFIX}-banner`, campaignTitle: `${PREFIX}-campaignTitle`, linkButton: `${PREFIX}-linkButton`, - securityIcon: `${PREFIX}-securityIcon`, + irregularityIcon: `${PREFIX}-irregularityIcon`, subscribeLink: `${PREFIX}-subscribe`, financeSummary: `${PREFIX}-financeSummary`, } @@ -79,11 +78,11 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ }, [`& .${classes.linkButton}`]: { - fontSize: theme.typography.pxToRem(10), + fontSize: theme.typography.pxToRem(16), letterSpacing: theme.spacing(0.01), lineHeight: '150%', textDecoration: 'underline', - color: 'initial', + color: '#11356A', '&:hover': { textDecoration: 'underline', @@ -92,7 +91,7 @@ const StyledGrid = styled(Grid)(({ theme }) => ({ }, }, - [`& .${classes.securityIcon}`]: { + [`& .${classes.irregularityIcon}`]: { width: theme.spacing(2.25), height: theme.spacing(2.75), }, @@ -221,22 +220,12 @@ export default function CampaignDetails({ campaign }: Props) { - - } - href={'/contact'} - className={classes.linkButton}> - {t('campaigns:campaign.feedback')} - - - - } - href={`/campaigns/${campaign.slug}/irregularity`} - className={classes.linkButton}> - {t('campaigns:campaign.report-campaign')} - - + } + href={`/campaigns/${campaign.slug}/irregularity`} + className={classes.linkButton}> + {t('campaigns:campaign.report-campaign')} + diff --git a/src/components/client/campaigns/CampaignFilter.tsx b/src/components/client/campaigns/CampaignFilter.tsx index 820977212..90f77afdb 100644 --- a/src/components/client/campaigns/CampaignFilter.tsx +++ b/src/components/client/campaigns/CampaignFilter.tsx @@ -53,7 +53,7 @@ const Root = styled('div')(() => ({ '&:focus': { color: theme.palette.primary.light, borderBottom: `5px solid ${theme.palette.primary.light}`, - background: 'transperent', + background: 'transparent', }, '&:hover': { color: theme.palette.primary.light, diff --git a/src/components/client/campaigns/CampaignsList.tsx b/src/components/client/campaigns/CampaignsList.tsx index a15e56593..bfd9d0816 100644 --- a/src/components/client/campaigns/CampaignsList.tsx +++ b/src/components/client/campaigns/CampaignsList.tsx @@ -1,80 +1,81 @@ -import { useMemo, useState } from 'react' +import { useMemo, useState, useEffect } from 'react' + import { CampaignResponse } from 'gql/campaigns' + import Image from 'next/image' -import { useTranslation } from 'next-i18next' -import { Box, Button, Grid } from '@mui/material' +import { Box, Grid, Pagination } from '@mui/material' import useMobile from 'common/hooks/useMobile' -import theme from 'common/theme' import CampaignCard from './CampaignCard/CampaignCard' -type Props = { campaignToShow: CampaignResponse[] } +type Props = { + campaignToShow: CampaignResponse[] +} export default function CampaignsList({ campaignToShow }: Props) { - const { t } = useTranslation() const { mobile } = useMobile() - const numberOfMinimalShownCampaigns = 12 - const [all, setAll] = useState(false) + + const [currentPage, setCurrentPage] = useState(1) + + const campaignsPerPage = 20 + + const totalCampaigns = campaignToShow?.length || 0 + const totalPages = Math.ceil(totalCampaigns / campaignsPerPage) + + useEffect(() => { + setCurrentPage(1) + }, [campaignToShow]) + const campaigns = useMemo(() => { - if (all) { - return campaignToShow ?? [] - } - return campaignToShow?.slice(0, numberOfMinimalShownCampaigns) ?? [] - }, [campaignToShow, all]) + const startIndex = (currentPage - 1) * campaignsPerPage + const endIndex = startIndex + campaignsPerPage + return campaignToShow?.slice(startIndex, endIndex) ?? [] + }, [campaignToShow, currentPage]) + + const handlePageChange = (event: React.ChangeEvent, page: number) => { + setCurrentPage(page) + } return ( - ({ - width: `calc(100% + ${theme.spacing(1.5)})`, - marginLeft: `-${theme.spacing(2.75)}`, - })}> + {campaigns?.map((campaign, index) => ( - + ))} - {campaignToShow && campaignToShow?.length > numberOfMinimalShownCampaigns && ( - - + {totalCampaigns > campaignsPerPage && ( + + )} + - + {mobile ? ( Information artboard mobile ) : ( - Information artboard + Donation graphic )} diff --git a/src/components/client/campaigns/DonationWishes.tsx b/src/components/client/campaigns/DonationWishes.tsx index 8607a6124..4905b8477 100644 --- a/src/components/client/campaigns/DonationWishes.tsx +++ b/src/components/client/campaigns/DonationWishes.tsx @@ -2,15 +2,7 @@ import React, { useMemo, useRef, useState } from 'react' import { useTranslation } from 'next-i18next' -import { - Unstable_Grid2 as Grid2, - Stack, - Typography, - Grid, - Button, - TextField, - InputAdornment, -} from '@mui/material' +import { Grid2, Stack, Typography, Grid, Button, TextField, InputAdornment } from '@mui/material' import SearchIcon from '@mui/icons-material/Search' import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' @@ -156,7 +148,7 @@ export default function DonationWishes({ campaignId, pageSize = 5 }: Props) { key={id} direction="row" sx={{ p: 2, bgcolor: 'grey.100', borderRadius: theme.spacing(2) }}> - + ))} - + {data?.items?.length === 0 && searchValue !== '' && ( {t('campaign.sort.noResults')} )} - + {numOfPages > 1 && ( {options.map((option) => ( - + setValue(option.value)} style={{ diff --git a/src/components/client/faq/FaqPage.tsx b/src/components/client/faq/FaqPage.tsx index ff60fe513..790efd9b8 100644 --- a/src/components/client/faq/FaqPage.tsx +++ b/src/components/client/faq/FaqPage.tsx @@ -6,7 +6,6 @@ import React, { useMemo, useState } from 'react' import Layout from 'components/client/layout/Layout' import ContactUs from './ContactUs' -import ScrollToTop from './ScrollToTop' import VerticalTabs from './VerticalTabs' import ExpandableListItem from './ExpandableListItem' import { @@ -81,7 +80,6 @@ export default function FaqPage({ section }: Props) { - ) } diff --git a/src/components/client/faq/contents/donation.tsx b/src/components/client/faq/contents/donation.tsx index ebaf649ef..7bcce0a91 100644 --- a/src/components/client/faq/contents/donation.tsx +++ b/src/components/client/faq/contents/donation.tsx @@ -41,7 +41,7 @@ export const DONATION_QUESTIONS: ContentType[] = [ данни, които ще оповестим, са името на дарителя (физическо или юридическо лице) и размера на дарената сума. Все пак, за да дарите, е необходима предварителна регистрация на сайта, при която ще ни бъдат необходими и други Ваши данни, които са защитени по силата на{' '} - + {' Закона за защита на личните данни. '} Тези данни са необходими за връзка с Вас, в случай че настъпи необходимост да Ви върнем diff --git a/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.styled.tsx b/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.styled.tsx index 59d772da3..5c82add39 100644 --- a/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.styled.tsx +++ b/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.styled.tsx @@ -40,20 +40,24 @@ export const ActiveCampaignsWrapper = styled(Grid)(() => ({ export const SeeAllButtonWrapper = styled(Grid)(() => ({ display: 'flex', placeContent: 'center', - marginTop: theme.spacing(10), + marginTop: theme.spacing(6), })) export const SeeAllButton = styled(LinkButton)(() => ({ - fontFamily: "'Lato', sans-serif", + fontFamily: 'sans-serif', fontSize: theme.typography.pxToRem(16), fontWeight: 600, - color: theme.palette.common.black, - letterSpacing: '0.4px', - textDecoration: 'underline', - marginTop: 0, + color: '#252222', + letterSpacing: '0.46px', + backgroundColor: theme.palette.primary.light, + lineHeight: theme.spacing(3.25), + width: theme.spacing(28.5), + height: theme.spacing(6), + boxShadow: + '0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0, 0, 0, 0.12)', '&:hover': { - backgroundColor: 'transparent', - textDecoration: 'underline', + boxShadow: + '0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0, 0, 0, 0.12)', }, })) diff --git a/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.tsx b/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.tsx index 5e43ff626..71b3dfe5d 100644 --- a/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.tsx +++ b/src/components/client/index/sections/ActiveCampaignsSection/ActiveCampaignsSection.tsx @@ -1,5 +1,7 @@ import { useTranslation } from 'next-i18next' +import ArrowForwardIcon from '@mui/icons-material/ArrowForward' + import { useCampaignList } from 'common/hooks/campaigns' import { routes } from 'common/routes' import { CampaignState } from 'components/client/campaigns/helpers/campaign.enums' @@ -28,9 +30,14 @@ export default function ActiveCampaignsSection() { {activeCampaigns?.map((campaign, index) => ( ))} - {' '} + - {t('campaign.see-all')} + }> + {t('campaign.see-more')} + ) diff --git a/src/components/client/index/sections/JoinPodkrepiBgSection/JoinPodkrepiBgSection.tsx b/src/components/client/index/sections/JoinPodkrepiBgSection/JoinPodkrepiBgSection.tsx index 368afd8a7..a48ae66de 100644 --- a/src/components/client/index/sections/JoinPodkrepiBgSection/JoinPodkrepiBgSection.tsx +++ b/src/components/client/index/sections/JoinPodkrepiBgSection/JoinPodkrepiBgSection.tsx @@ -13,7 +13,7 @@ import { export default function WantToHelpPodkrepiBgSection() { const { t } = useTranslation('index') - const joinIconSource = '/img/JoinIcon.png' + const joinIconSource = '/img/join-icon.png' return ( diff --git a/src/components/client/index/sections/TeamMembersSection/TeamMembersSection.tsx b/src/components/client/index/sections/TeamMembersSection/TeamMembersSection.tsx index 420bebbc6..c0ec3722a 100644 --- a/src/components/client/index/sections/TeamMembersSection/TeamMembersSection.tsx +++ b/src/components/client/index/sections/TeamMembersSection/TeamMembersSection.tsx @@ -18,14 +18,13 @@ export default function TeamMembersSection() { {t('team-section.content')} Team image - {/* A11Y TODO: Translate alt text */} }> {t('team-section.meet-our-team')} diff --git a/src/components/client/irregularity/Actions.tsx b/src/components/client/irregularity/Actions.tsx index dc3ad9311..0ff04fdc2 100644 --- a/src/components/client/irregularity/Actions.tsx +++ b/src/components/client/irregularity/Actions.tsx @@ -8,12 +8,7 @@ import { Grid } from '@mui/material' import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos' import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos' -import { - ActionButton, - ActionLinkButton, - ActionSubmitButton, - Root, -} from './helpers/Irregularity.styled' +import { ActionButton, ActionLinkButton, ActionSubmitButton } from './helpers/Irregularity.styled' type ActionsProps = { nextLabel: string @@ -37,12 +32,12 @@ export default function Actions({ const { t } = useTranslation('irregularity') return ( - + {activeStep === 0 ? ( }> {t(backLabel)} @@ -64,6 +59,6 @@ export default function Actions({ endIcon={} /> - + ) } diff --git a/src/components/client/irregularity/helpers/Irregularity.styled.tsx b/src/components/client/irregularity/helpers/Irregularity.styled.tsx index 8eb5f1a3e..0dcae4157 100644 --- a/src/components/client/irregularity/helpers/Irregularity.styled.tsx +++ b/src/components/client/irregularity/helpers/Irregularity.styled.tsx @@ -24,61 +24,30 @@ export const ButtonsWrapper = styled(Grid)(() => ({ })) export const ActionLinkButton = styled(LinkButton)(() => ({ - backgroundColor: '#0098E3', - border: `1px solid ${theme.palette.secondary.light}`, - padding: theme.spacing(1, 5), - borderRadius: theme.borders.round, - color: theme.palette.common.white, - fontSize: theme.typography.pxToRem(18), + fontSize: theme.typography.pxToRem(14), '&:hover': { - backgroundColor: '#62C4FB', - color: theme.palette.common.black, - border: `1px solid ${theme.palette.secondary.light}`, - }, - - [theme.breakpoints.down('md')]: { - fontSize: theme.typography.pxToRem(11), - marginBottom: theme.spacing(4), + backgroundColor: 'transparent', }, })) export const ActionButton = styled(Button)(() => ({ - backgroundColor: '#0098E3', - border: `1px solid ${theme.palette.secondary.light}`, - padding: theme.spacing(1, 5), - borderRadius: theme.borders.round, - color: theme.palette.common.white, - fontSize: theme.typography.pxToRem(18), + fontSize: theme.typography.pxToRem(14), '&:hover': { - backgroundColor: '#62C4FB', color: theme.palette.common.black, - border: `1px solid ${theme.palette.secondary.light}`, - }, - - [theme.breakpoints.down('md')]: { - fontSize: theme.typography.pxToRem(11), - marginBottom: theme.spacing(4), }, })) export const ActionSubmitButton = styled(SubmitButton)(() => ({ - backgroundColor: '#0098E3', border: `1px solid ${theme.palette.secondary.light}`, padding: theme.spacing(1, 5), borderRadius: theme.borders.round, color: theme.palette.common.white, - fontSize: theme.typography.pxToRem(18), + fontSize: theme.typography.pxToRem(14), '&:hover': { - backgroundColor: '#62C4FB', color: theme.palette.common.black, border: `1px solid ${theme.palette.secondary.light}`, }, - - [theme.breakpoints.down('md')]: { - fontSize: theme.typography.pxToRem(11), - marginBottom: theme.spacing(4), - }, })) diff --git a/src/components/client/layout/Layout.tsx b/src/components/client/layout/Layout.tsx index cd5683da3..3beae5dbc 100644 --- a/src/components/client/layout/Layout.tsx +++ b/src/components/client/layout/Layout.tsx @@ -11,6 +11,7 @@ import DetailsModal from 'components/admin/modal/DetailsModal' import AppNavBar from './AppNavBar' import MobileNav from './nav/MobileNav/MobileNav' +import ScrollToTop from './ScrollToTop' const createPageTitle = (suffix: string, title?: string) => { if (title) { @@ -135,6 +136,7 @@ export default function Layout({ {!hideFooter &&