Only this pageAll pages
Powered by GitBook
1 of 29

latest

Loading...

Dev Guidelines

Loading...

Loading...

Loading...

Loading...

Loading...

Architecture

Backend

Frontend

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Infrastructure

Loading...

REST API

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

GRAPHQL

Loading...

Branching strategy

The general approach to branching used within the organisation.

Inherits the process from https://nvie.com/posts/a-successful-git-branching-model/​‌

​​🌞 Good branch names:‌

  • nice-kebab-cased-titles

  • fixes-footer-links

  • 4411290-setup-state-management-integration

  • feature/new-design

  • hotfix/db-connection

  • release-1.2.3

​​⛈ Bad branch names:‌

  • patch-1 - not enough context

  • camelCasedBranchNames - camelcase

  • PascalCasedBranchNames - pascal case

long-titles-above-80-chars-{.....} - too long

  • #58/something - shell understands it as comment

  • Branching model

    Merges

    ​​​

    ​​​

    Introduction

    Dev docs for the Podkrepi.bg organisation.

    Who is this documentation oriented for?

    Our documentation is publicly accessible for everyone to read and contribute to. All current and future contributors are encouraged to read the Dev Guidelines section as a starting point.

    Weekly dev meetings

    Our weekly development meeting takes place every Saturday between 6pm - 7:30pm EEST (UTC+3) in the discord server of the organisation. All issues regarding development can be discussed during these meetings and everyone is welcome to join!

    Healthcheck

    Health check

    GET https://api.podkrepi.localhost/api/v1/healthcheck

    {
      "status": 200
    }
    {
      "status": 500
    }

    Endpoints

    Schema

    Environments

    Guidelines

    Recognising Contributions

    We're integrated with https://allcontributors.org/ bot

    Comment on this issue, asking @all-contributors bot to add a contributor:

    @all-contributors please add @<username> for <contributions>

    <contribution>: See the Emoji Key (Contribution Types Reference) for a list of valid contribution types.

    .env

    ## General ##	
    #############	
    
    COMPOSE_PROJECT_NAME=podkrepi	
    NODE_ENV=development # development, production	
    TARGET_ENV=development # development, production	
    
    ## API ##	
    #########	
    
    API_URL=https://api.podkrepi.localhost/	
    
    ## APP ##	
    #########	
    
    APP_URL=http://localhost:3040	
    APP_PORT=3040	
    
    ## Next Auth ##	
    #############	
    
    NEXTAUTH_URL=https://http://localhost:3040	
    JWT_SECRET=!Change__Me!	
    
    ## Discord ##	
    #############	
    
    DISCORD_CLIENT_ID=	
    DISCORD_CLIENT_SECRET=

    Production

    Production environment

    Build frontend

    yarn build

    Build Docker image

    Local Development

    Local development

    Setup local dev environment

    Starting a task

    Authentication

    docker build . \
        --file ./Dockerfile \
        --target production \
        --build-arg NODE_ENV=production
    Start development server

    Visit http://localhost:3040/

    Start dev server via Docker Compose

    Install the binary via https://docs.docker.com/compose/install/

    Start the container in foreground

    Start the container in background

    Stop the docker container with docker-compose down

    Linting

    git clone [email protected]:daritelska-platforma/frontend.git
    cd frontend
    
    # Symlink dev environment
    ln -s .env.example .env
    
    # Install dependencies
    yarn
    yarn dev
    docker-compose up
    docker-compose up -d
    docker-compose logs -f
    yarn lint
    yarn lint:styles
    yarn format
    yarn type-check

    Campaigns

    Get Campaigns

    GET https://api.podkrepi.bg/v1/campaigns

    This endpoint allows you to get campaigns.

    Headers

    Name
    Type
    Description

    Get Campaign by ID

    GET https://api.podkrepi.bg/v1/campaigns/:id

    This endpoint allows you to get campaigns.

    Path Parameters

    Name
    Type
    Description

    Headers

    Name
    Type
    Description

    Definition of done

    An unwritten contract signed by everyone in the organisation.

    We use the definition of done to asses whether or not an enhancement can be considered to be complete (shippable to production).

    Do not mix DoD with acceptance criteria.

    DoD is our general framework to derive and end-to-end process which ensures every common piece of work meets the organisation's quality standards. Whereas, AC is a set of strictly defined targets for each individual (functional) piece of work that must be met before a task is considered to be complete.

    Why is DoD important to us?

    It is vital that every contributor in our organisation is aware of and understands our DoD. This helps us:

    • Complete all tasks without introducing any gaps in the process.

    • Follow with clarity where we are on our roadmap and what has been completed.

    • Know what is expected from every contributor.

    • Meet a certain quality of our work which leads to:

      • Lowering the amount of time spend for reworks.

      • Delivering on our promise to create a platform which makes a difference in our society.

    Framework

    The following is a general approach towards DoD, each team is encouraged to derive their own process suitable for the team's functionality in the organisation.

    Development

    1. Code and unit tests for functionality completed.

    2. Assumption of meeting Acceptance Criteria.

    3. Premerge [if applicable] tests and code reviews passed.

    4. Postmerge [if applicable] tests passed.

    5. Project builds with no failures.

    6. Project deployed to dev environment.

    7. Initial QA performed by the developer. [if this step fails, go back to step 1]

    8. Feature documentation [if applicable] and tests documentation [if applicable] written.

    9. Feature QA'ed by a QA engineer against the acceptance criteria. [if this step fails, go back to step 1]

    10. Official documentation updated.

    11. Feature marked as completed.

    Release

    1. All features included in the release are marked as completed. Where a feature is not yet completed, the feature is postponed for the next release.

    2. Update changelog and release documentation where applicable.

    3. Current environment is "green" - all unit, functional, integration, E2E and other possible tests are passing.

    4. Release acceptance criteria is met. [If this step fails, go back to step 1]

    5. QA functionality of the platform. [If this step fails, go back to step 1]

    6. QA if any experimental features are exposed to the end user. [If this step fails, go back to step 1]

    7. Promote the release to the next environment in the pipeline.

    8. Release successfully deployed to new environment. [If this step fails, go back to step 1]

    9. Release marked as completed.

    Authentication

    string

    Authentication token to track down who is emptying our stocks.

    id

    string

    ID of the cake to get, for free of course.

    Authentication

    string

    Authentication token to track down who is emptying our stocks.

    [
        {
            "id": "5572f770-a434-4ed8-9a91-d94474d26c8e"
            "name": "Campaign name"
        },
        {
            "id": "97692474-2482-460f-86ec-2b84954f989b"
            "name": "Campaign name"
        }
    ]
    {
        "error": "Not Found"
    }
    {
        "id": "5572f770-a434-4ed8-9a91-d94474d26c8e"
        "name": "Campaign name"
    }
    {
        "error": "Not Found"
    }

    Pull requests guidelines

    Wondering how to write a good PR up to the organisation's standards?

    Pull requests are official record of all changes we make. As an open source organisation PRs might be read from multiple people and not only the assigned reviewers. Future contributors are likely to search and come back to past PRs for a variety of reasons, therefore, it is vital that the important changes are not only in the code but also included in the description. Always think about the reviewers and potential future readers!

    Think twice about the title and description before submitting for review

    The title is what usually appears in the version control. Thus, it must be a full, clear and imperative sentence.

    Bad title example:

    Translations (i18n)

    Translation namespaces

    Default namespace is called common and contains translations used on all pages (Layout, Nav, etc) and is stored at frontend/public/locales/{locale}/common.json

    Namespaces (scopes, domains) are stored in separate json files at frontend/public/locales/{locale}/{namespace}.json One namespace can combine the translations keys from several pages with common reusable strings ex. auth

  • Deleting API endpoint and adding new one- not specific enough, not imperative, bad wording.

  • Good title example:

    • Delete the /foo/bar REST endpoint and replace it with /foo/baz

    The latter example describes the specific change made, improves the searchability and gives enough information to the reader which allows them to skim through the changes rather than spending more time going through the rest of the description.

    Keep the body as informative as possible. Start with outlining the problem you are trying to solve/the feature you want to add.

    All readers must get a perfect impression of what the change is and reason/motivation behind it. Think about any attachments that might be useful. Add screenshots if the changeset leads to visual difference in the frontend, add any external link/references to any design docs that might help the reader to better understand context of/changes in the PR. However, try to keep those to a minimal and outline as much as possible in the written description as any external resources can easily be deleted in the future or problems with access permissions might occur.

    Do not skip any important information. Outline with clarity the alternative solutions and the reasons for avoiding those. Similarly, it is vital to explain any shortcomings of the approach that you took to solving the problem, what can possibly go wrong in the future and how we can possibly overcome those issues if you can think of any.

    Keep the changeset as small as possible

    10 lines of code = 10 issues, 500 lines of code = LGTM!... code reviews in a nutshell...

    Your PR changeset size should be just right, i.e. one self-contained change per PR! Furthermore, you should strive to limit your changeset to 250-300 lines of code where possible (source, source). This is beneficial for both the reviewers and authors. As a reviewer you will be able to go through the changes much quicker and provide a better feedback as the quantity of changes you are reviewing is less and the amount of context needed is proportional to the size. Vice-versa, as an author you will get good feedback fast and hoping for less potentially introduced bugs. Moreover, changes you will be required to make upon receiving the feedback would be less and easier to do, i.e. you will have to do less work if something is rejected. Small PRs are also easier to design and the mergeability and simplicity of rolling back the changes increase substantially.

    How to introduce smaller PRs?

    Break down your issues!

    For example: you want to add a new feature for creating a product, your changes will include a new public API endpoint and its backend implementation. Instead of dumping all of your changes in a massive PR, you can break down your issue similarly to:

    • A differential with any Database schema changes required.

    • A differential with any protobuf changes required.

    • Initial commit with stubs for the backend.

    • A series of differentials containing the backend implementation and respective unit tests.

    • A differential with any changes required to the public API.

    Stacking PRs greatly improves the efficiency of the above process, you can see more examples how to this here.

    Tests for new functionality must go in the same PR as the functional changes, do not try to reduce the changeset size by splitting unit tests from functional changes.

    Do not mix changes - refactoring, bug fixing and enhancements must not go in the same differential.

    Large PRs

    It is inevitable that sometimes larger PRs will be needed - when refactoring code, code added by automation, deleting files etc. This is always fine as long as you have considered all options how to avoid the massive change but nothing seems to help.

    Do not break the builds!

    The system's functionality must always be intact after merging the changes. We understand it is impossible for this rule not to be broken ever. But, introducing PRs that intentionally add a breaking change (even for a short period of time until your subsequent PR is merged) is unacceptable! There are a number of ways to avoid those and if unsure, ask your friendly community members for how to approach the problem.

    Sources

    The (written) unwritten guide to pull requests

    Optimal pull request size

    Best Practices for Code Review

    The anatomy of a perfect pull request

    Google's Engineering Practices documentation

    scope collects keys for
    login
    and
    register
    pages.

    Translation keys

    It is preferred to use kebab-case for translation keys and extract another level of nesting when the common prefix of the keys is above 3 or makes sense to be separated as new keys might be added in the future.

    • Namespace is separated with :

    • Translation nesting levels are separated with .

    • Words in a translation key are separated with -

    domain:pages.nested-level.another-nested-level.translation-key

    Usage in React

    Usage of translation hook useTranslation is preferred over usage of <Trans /> component, whenever possible.

    Usage in components

    SSR preloading i18n in pages

    {
      "cta": {
        "login": "Log In",
        "register": "Register",
        "send": "Send",
        "reset": "Reset"
      },
      "fields": {
        "email": "Email",
        "password": "Password",
        "confirm-password": "Confirm Password",
        "first-name": "First name",
        "last-name": "Last name"
      },
      "pages": {
        "forgotten-password": {
          "instructions": "To reset your password, please type your email address below.",
          "greeting": "Hello {{name}}!"
        }
      }
    }
    import { useTranslation } from 'next-i18next'
    
    export default function CustomComponent() {
      const { t } = uxseTranslation()
    
      return (
        <div>
          <h1>{t('nav.custom-page')}</h1>
          <h2>{t('auth:pages.forgotten-password.greeting', { name: 'Interpolation' })}</h2>
          <p>{t('auth:pages.forgotten-password.instructions')}</p>
        </div>
      )
    }
    import { GetStaticProps } from 'next'
    import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
    
    import Page from 'components/forgottenPassword/ForgottenPasswordPage'
    
    export const getStaticProps: GetStaticProps = async ({ locale }) => ({
      props: {
        ...(await serverSideTranslations(locale ?? 'bg', ['common', 'auth'])), // List used namespaces
      },
    })
    
    export default Page

    Forms

    Form definition

    Form usage

    Validation

    Default translations

    Simple strings are mapped directly to their respective translation

    Default translations with interpolation

    Complex translation keys are being evaluated upon translation

    Custom translations in validation schema

    Commonly used translations with the same translation key

    Inline translations in validation schema

    Custom translations with keys defined right next to the form

    Deployment to Kubernetes

    Prerequisites

    • Install kubectl https://kubernetes.io/docs/tasks/tools/

    • Install kustomize

    Frontend

    Staging

    You may trigger staging release at any point of time from the master branch by:

    This will update the deployment using ghcr.io/podkrepi-bg/frontend:master image

    Production

    Create new release

    The following command will:

    • Bump the version in package.json

    • Tag the latest master branch

    • [postversion] Push local tags to the remote origin

    Apply manifests

    Once the image has been built by the GitHub Actions and is present in the Docker image repository you may trigger the actual deployment to the cluster.

    Backend

    Staging

    You may trigger staging release at any point of time from the master branch by:

    This will update the deployment using ghcr.io/podkrepi-bg/api:master image

    Production

    Create new release

    The following command will:

    • Bump the version in package.json

    • Tag the latest master branch

    • [postversion] Push local tags to the remote origin

    Apply manifests

    Once the image has been built by the GitHub Actions and is present in the Docker image repository you may trigger the actual deployment to the cluster.

    Manual deployment

    If you want to set a specific version for the deployment image you can do that by editing backend/manifests/overlays/production/kustomization.yaml

    import React from 'react'
    import * as yup from 'yup'
    import { useTranslation } from 'next-i18next'
    import { Grid, TextField, Button } from '@material-ui/core'
    
    import { AlertStore } from 'stores/AlertStore'
    import useForm, { translateError, customValidators } from 'common/form/useForm'
    
    export type FormData = {
      email: string
    }
    
    const validationSchema: yup.SchemaOf<FormData> = yup.object().defined().shape({
      email: yup.string().email().required(),
    })
    
    const defaults: FormData = {
      email: '',
    }
    
    export type MyFormProps = { initialValues?: FormData }
    
    export default function MyForm({ initialValues = defaults }: MyFormProps) {
      const { t } = useTranslation()
    
      const { formik } = useForm({
        initialValues,
        validationSchema,
        onSubmit: (values) => {
          console.log(values)
        },
      })
    
      return (
        <form onSubmit={formik.handleSubmit}>
          <Grid container spacing={3}>
            <Grid item xs={12}>
              <TextField
                type="text"
                fullWidth
                label={t('auth:fields.email')}
                name="email"
                size="small"
                variant="outlined"
                autoFocus
                error={Boolean(formik.errors.email)}
                helperText={translateError(formik.errors.email)}
                value={formik.values.email}
                onBlur={formik.handleBlur}
                onChange={formik.handleChange}
              />
            </Grid>
            <Grid item xs={12}>
              <Button fullWidth type="submit" color="primary" variant="contained">
                {t('auth:cta.login')}
              </Button>
            </Grid>
          </Grid>
        </form>
      )
    }
    <MyForm />
    
    <MyForm initailValues={{email: '[email protected]'}} />

    Update the image newTag version in frontend/manifests/overlays/production/kustomization.yaml

  • Commit and push the manifest update

  • Update the image newTag version in backend/manifests/overlays/production/kustomization.yaml

  • Commit and push the manifest update

  • https://kubectl.docs.kubernetes.io/installation/kustomize/
    https://github.com/orgs/podkrepi-bg/packages/container/package/frontend
    https://github.com/orgs/podkrepi-bg/packages/container/package/api
    {
      "invalid": "Field is invalid",
      "required": "Required field"
    }
    setLocale({
      mixed: {
        default: 'validation:invalid',
        required: 'validation:required',
      },
      string: {
        email: 'validation:email',
      },
    })
    {
      "field-too-short": "Field should be at least {{min}} symbols",
      "field-too-long": "Field should be maximum {{max}} symbols"
    }
    setLocale({
      string: {
        min: ({ min }: { min: number }) => ({
          key: 'validation:field-too-short',
          values: { min },
        }),
        max: ({ max }: { max: number }) => ({
          key: 'validation:field-too-long',
          values: { max },
        }),
      },
    })
    yup.string().min(6 customValidators.passwordMin)
    const validationSchema: yup.SchemaOf<FormData> = yup
      .object()
      .defined()
      .shape({
        password: yup.string().min(6, ({ min }) => ({
          key: 'validation:password-min',
          values: { min },
        })),
      })
    cd podkrepi.bg/frontend
    kubectl apply -k manifests/overlays/development
    cd podkrepi.bg/frontend
    kubectl apply -k manifests/overlays/production
    cd podkrepi.bg/frontend
    kubectl apply -k manifests/overlays/production
    cd podkrepi.bg/backend
    kubectl apply -k manifests/overlays/development
    cd podkrepi.bg/backend
    kubectl apply -k manifests/overlays/production
    cd podkrepi.bg/backend
    kubectl apply -k manifests/overlays/production
    images:
    - name: ghcr.io/podkrepi-bg/api/migrations
      newTag: v0.3.3
    - name: ghcr.io/podkrepi-bg/api
      newTag: v0.3.3

    Contact Request

    Create contact request

    POST https://api.podkrepi.localhost/api/v1/contact

    Request Body

    Name
    Type
    Description

    Fetch contact requests

    GET https://api.podkrepi.localhost/api/v1/contact

    This endpoint allows you to get free cakes.

    Headers

    Name
    Type
    Description

    Fetch specific contact request

    GET https://api.podkrepi.localhost/api/v1/contact/:id

    Path Parameters

    Name
    Type
    Description

    Headers

    Name
    Type
    Description

    Delete contact request

    DELETE https://api.podkrepi.localhost/api/v1/contact/:id

    Path Parameters

    Name
    Type
    Description

    Headers

    Name
    Type
    Description

    firstName

    string

    lastName

    string

    email

    string

    phone

    string

    company

    string

    message

    string

    Authentication

    string

    Authentication token to track down who is emptying our stocks.

    ID

    string

    Authentication

    string

    id

    string

    Authentication

    string

    {
      "id": "112cb853-b0b9-482e-a944-cf4fca5566b7",
      "firstName": "John",
      "lastName": "Doe",
      "email": "[email protected]",
      "company": "Camplight",
      "phone": "+359888888888",
      "message": "I wanna help",
      "createdAt": "2021-03-19T00:36:50.076597738Z",
      "updatedAt": "2021-03-19T00:36:50.076597738Z",
      "deletedAt": null
    }
    {
      "statusCode": 400,
      "error": "email: non zero value required;firstName: non zero value required;lastName: non zero value required;message: non zero value required;phone: non zero value required",
      "validation": [
        {
          "field": "firstName",
          "message": "firstName: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "lastName",
          "message": "lastName: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "email",
          "message": "email: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "phone",
          "message": "phone: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "message",
          "message": "message: non zero value required",
          "validator": "required",
          "customMessage": false
        }
      ]
    }
    [
      {
        "id": "0d6ff16e-ae4c-48e4-868c-05095d923053",
        "firstName": "John",
        "lastName": "Doe",
        "email": "[email protected]",
        "company": "Camplight",
        "phone": "",
        "message": "I wanna help",
        "createdAt": "2021-03-19T00:11:21.031966Z",
        "updatedAt": "2021-03-19T00:11:21.031966Z",
        "deletedAt": null
      },
      {
        "id": "76e6e2f7-b3f2-4df4-9185-cc44185e7c86",
        "firstName": "John",
        "lastName": "Doe",
        "email": "[email protected]",
        "company": "ACME",
        "phone": "+359888888888",
        "message": "I wanna help",
        "createdAt": "2021-03-19T00:20:31.355736Z",
        "updatedAt": "2021-03-19T00:20:31.355736Z",
        "deletedAt": null
      }
    ]
    {
      "id": "112cb853-b0b9-482e-a944-cf4fca5566b7",
      "firstName": "John",
      "lastName": "Doe",
      "email": "[email protected]",
      "company": "Camplight",
      "phone": "+359888888888",
      "message": "I wanna help",
      "createdAt": "2021-03-19T00:36:50.076597738Z",
      "updatedAt": "2021-03-19T00:36:50.076597738Z",
      "deletedAt": null
    }
    {
      "error": "No contact found",
      "status": 404
    }
    {
      "status": 200
    }
    {
      "status": 404
    }

    React Guidelines

    Imports

    A common way to sort the imports in the file is by their source: external, absolute, relative separated by an empty line. Each of those groups can be sorted by line length, but that's not super important.

    File structure

    Inherits AirBnb naming convention

    Use PascalCase for React components and camelCase for their instances

    Naming convention react components

    Pascal cased file names src/components/GenericForm.tsx

    Filename and default component of the file should have the same name.

    Naming convention non react components

    Camel cased file names src/utils/hooks/useUser.ts

    Naming convention folders

    Lowercase kebab cased folders src/components/common/password-reset/ResetForm.tsx

    Naming convention pages

    Lowercase kebab cased files located in src/pages/sample-page.tsx which correspond to /sample-page url.

    Types

    The common convention is that the main type of the component's props is called after the component itself with suffix -Props. Prop types of AdvancedForm becomes AdvancedFormProps.

    Components

    Preferred export style 🌞

    • Nice IDE support and readability

    Alternative export styles

    • Named function

      ⛅ Allows attaching static props to the function

    • Const arrow function

      🌞 Nice for locally defined components

      ⛅ Okay for default exports, but not preferred

    Styles

    There are three common ways to style a component:

    Styles using the

    Single component that inherits all sizing props from MUI

    🌞 Nice for quick layouts that should follow the theme

    ⛅Not the best for custom scenarios with more than _six props passed to it. Use hooks instead

    ⛅ Not nice when the children have clear nesting structure of more than _three levels. Use hooks or scss instead

    Styles using useStyles() hook

    🌞 Nice for very specific styling that leverages theme methods and props

    ⛅ Too verbose for simple use cases, if it contains less than 2 css rules. Use Box instead

    ⛅ Not the best when dealing with styling of deep nested structures within the same component. Use scss instead

    Styles using SCSS files

    Next.js supports out of the box. Read more at

    File convention is based on a suffix .module.scss (ex. about.module.scss)

    🌞 Nice when dealing with complex nested structures that are scoped in a single component. When dealing with sub-components we're not sure if some of the rules will be left unused.

    ⛅ Too verbose for simple use cases, if it contains less than 2 css rules in a dedicated file. Use Box instead

    ⛈️ Cannot use theme support or theme variables Use hook instead

    import React, { useState } from 'react'
    import { useTranslation } from 'next-i18next'
    
    import Nav from 'components/layout/Nav'
    import Layout from 'components/layout/Layout'
    
    import SimpleForm from './SimpleForm'
    import styles from './advanced.module.scss'

    Unnamed arrow function ⛈️

    Discouraged

    https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md

  • Class components ⛈️

    Discouraged as hooks cannot be used inside the class components

  • https://github.com/airbnb/javascript/tree/master/react#naming
    <Box /> component
    https://material-ui.com/system/basics/#all-inclusive
    SCSS
    https://nextjs.org/docs/basic-features/built-in-css-support#sass-support
    export default () => <div>page</div>
    class Page extends React.Component {
      render() {
        return <div>page</div>
      }
    }
    export default function GenericForm() {}
    type AdvancedFormProps = React.PropsWithChildren({
      title?: string
      age?: number
    })
    
    export default function AdvancedForm({ title = 'Nice', children, age }: AdvancedFormProps) {
      return (
        <div title={title} data-age={age}>
          {children}
        </div>
      )
    }
    export default function RegisterPage() {
      return <div>page</div>
    }
    function RegisterPage() {
      return <div>page</div>
    }
    
    Register.getInitialProps = async (ctx) => {
      return { stars: 128 }
    }
    
    export default RegisterPage
    const RegisterForm = () => <form>page</form>
    
    export default function RegisterPage() {
      return <RegisterForm />
    }
    const RegisterPage = () => <form>page</form>
    
    export default RegisterPage
    <Box component="nav" px={5} mt={2}>
      <a>{t('nav.forgottenPassword')}</p>
    </Box>
    <Box component="nav" px={5} pb={12} mt={2} mb={4} lineHeight={2} letterSpacing={none} fontSize={20}>
      <Box component="span" px={5} pb={12} mt={2} mb={4} lineHeight={2} letterSpacing={none} fontSize={17}>
        <a>{t('nav.forgottenPassword')}</p>
      </Box>
      <Box component="span" px={5} pb={12} mt={2} mb={4} lineHeight={2} letterSpacing={none} fontSize={13}>
        <a>{t('nav.forgottenPassword')}</p>
      </Box>
    </Box>
    const useStyles = makeStyles((theme) =>
      createStyles({
        pageTitle: {
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          padding: theme.spacing(4),
          margin: theme.spacing(5, 3, 4),
          color: theme.palette.secondary.main,
          backgroundColor: theme.palette.primary.main,
          '&:hover': {
            color: theme.palette.secondary.dark,
          },
        },
        // ...
      }),
    )
    
    export default function SomeBox() {
      const classes = useStyles()
      return (
        <Box className={classes.pageTitle}>
          <p>{t('nav.forgottenPassword')}</p>
        </Box>
      )
    }
    @import 'styles/variables';
    
    .page {
      color: $text-color;
    
      .nav {
        background-color: $nav-color;
    
        a {
          text-decoration: none;
          text-transform: uppercase;
        }
      }
    }
    import styles from './about.module.scss'
    
    <Box className={styles.page}>
      <p>{t('nav.forgottenPassword')}</p>
    </Box>
    @import 'styles/variables';
    
    a {
      text-decoration: none;
    }

    Support Request

    Create support request

    POST https://api.podkrepi.localhost/api/v1/support-request

    Request Body
    Name
    Type
    Description

    person

    object

    support_data

    object

    {
      "id": "d1451c92-0e86-463a-bf1c-2e4554a77f30",
      "person": {
        "email": "[email protected]",
        "name": "John Doe",
        "phone": "+3598777777777",
    
    

    Fetch support requests

    GET https://api.podkrepi.localhost/api/v1/support-request

    This endpoint allows you to get free cakes.

    Headers

    Name
    Type
    Description

    Authentication

    string

    Authentication token to track down who is emptying our stocks.

    Fetch specific support request

    GET https://api.podkrepi.localhost/api/v1/support-request/:id

    Path Parameters

    Name
    Type
    Description

    ID

    string

    Headers

    Name
    Type
    Description

    Authentication

    string

    Delete support request

    DELETE https://api.podkrepi.localhost/api/v1/support-request/:id

    Path Parameters

    Name
    Type
    Description

    id

    string

    Headers

    Name
    Type
    Description

    Authentication

    string

    {
      "statusCode": 400,
      "error": "Person.email: non zero value required;Person.name: non zero value required;Person.phone: non zero value required",
      "validation": [
        {
          "field": "Person.email",
          "message": "Person.email: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "Person.name",
          "message": "Person.name: non zero value required",
          "validator": "required",
          "customMessage": false
        },
        {
          "field": "Person.phone",
          "message": "Person.phone: non zero value required",
          "validator": "required",
          "customMessage": false
        }
      ]
    }
    [
      {
        "id": "17222da9-b550-4e82-a6c6-88100bf54675",
        "person": {
          "email": "",
          "name": "",
          "phone": "",
          "address": "",
          "terms": false,
          "newsletter": false
        },
        "support_data": {
          "roles": {
            "benefactor": false,
            "partner": false,
            "associationMember": false,
            "promoter": false,
            "volunteer": false
          },
          "benefactor": {
            "campaignBenefactor": false,
            "platformBenefactor": false
          },
          "partner": {
            "npo": false,
            "bussiness": false,
            "other": false,
            "otherText": ""
          },
          "volunteer": {
            "backend": false,
            "frontend": false,
            "marketing": false,
            "designer": false,
            "projectManager": false,
            "devOps": false,
            "financesAndAccounts": false,
            "lawyer": false,
            "qa": false
          },
          "associationMember": {
            "isMember": false
          },
          "promoter": {
            "mediaPartner": false,
            "ambassador": false,
            "other": false,
            "otherText": ""
          }
        },
        "createdAt": "2021-04-07T09:16:28.026936Z",
        "updatedAt": "2021-04-07T09:16:28.026936Z",
        "deletedAt": null
      }
    ]
    {
      "id": "17222da9-b550-4e82-a6c6-88100bf54675",
      "person": {
        "email": "",
        "name": "",
        "phone": "",
        "address": "",
        "terms": false,
        "newsletter": false
      },
      "support_data": {
        "roles": {
          "benefactor": false,
          "partner": false,
          "associationMember": false,
          "promoter": false,
          "volunteer": false
        },
        "benefactor": {
          "campaignBenefactor": false,
          "platformBenefactor": false
        },
        "partner": {
          "npo": false,
          "bussiness": false,
          "other": false,
          "otherText": ""
        },
        "volunteer": {
          "backend": false,
          "frontend": false,
          "marketing": false,
          "designer": false,
          "projectManager": false,
          "devOps": false,
          "financesAndAccounts": false,
          "lawyer": false,
          "qa": false
        },
        "associationMember": {
          "isMember": false
        },
        "promoter": {
          "mediaPartner": false,
          "ambassador": false,
          "other": false,
          "otherText": ""
        }
      },
      "createdAt": "2021-04-07T09:16:28.026936Z",
      "updatedAt": "2021-04-07T09:16:28.026936Z",
      "deletedAt": null
    }
    {
      "error": "No support request found",
      "status": 404
    }
    {
      "status": 200
    }
    {
      "status": 404
    }
    "address": "6 John Doe Str",
    "terms": true,
    "newsletter": true
    },
    "support_data": {
    "roles": {
    "benefactor": true,
    "partner": true,
    "associationMember": false,
    "promoter": false,
    "volunteer": true
    },
    "benefactor": {
    "campaignBenefactor": true,
    "platformBenefactor": false
    },
    "partner": {
    "npo": false,
    "bussiness": true,
    "other": false,
    "otherText": "aaaaa"
    },
    "volunteer": {
    "backend": false,
    "frontend": true,
    "marketing": false,
    "designer": true,
    "projectManager": false,
    "devOps": true,
    "financesAndAccounts": true,
    "lawyer": false,
    "qa": false
    },
    "associationMember": {
    "isMember": true
    },
    "promoter": {
    "mediaPartner": false,
    "ambassador": false,
    "other": true,
    "otherText": "bbbbb"
    }
    },
    "createdAt": "2021-04-07T12:57:09.42898025Z",
    "updatedAt": "2021-04-07T12:57:09.42898025Z",
    "deletedAt": null
    }