diff --git a/src/modules/project/forms/ProjectRegion.tsx b/src/modules/project/forms/ProjectRegion.tsx
index 6a0b072f5..0baee39c6 100644
--- a/src/modules/project/forms/ProjectRegion.tsx
+++ b/src/modules/project/forms/ProjectRegion.tsx
@@ -1,5 +1,6 @@
import { useQuery } from '@apollo/client'
import { HStack, IconButton, StackProps, useDisclosure, VStack } from '@chakra-ui/react'
+import { useAtom } from 'jotai'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PiX } from 'react-icons/pi'
@@ -15,6 +16,7 @@ import { FieldContainer } from '../../../shared/components/form/FieldContainer'
import { SkeletonLayout } from '../../../shared/components/layouts'
import { Country, Location, Maybe, Project, ProjectCountriesGetResult, ProjectRegionsGetResult } from '../../../types'
import { ProjectState } from '../state/projectAtom'
+import { projectFormErrorAtom } from '../state/projectFormAtom'
const useStyles = createUseStyles(({ colors }: AppTheme) => ({
container: {
@@ -51,6 +53,8 @@ export const ProjectRegion = ({ location, updateProject, ...rest }: ProjectRegio
const { t } = useTranslation()
const classes = useStyles()
+ const [projectFormError, setProjectFormError] = useAtom(projectFormErrorAtom)
+
const [inputValue, setInputValue] = useState('')
const { isOpen, onOpen, onClose } = useDisclosure()
@@ -98,7 +102,12 @@ export const ProjectRegion = ({ location, updateProject, ...rest }: ProjectRegio
}
}
+ const clearLocationError = () => {
+ setProjectFormError((prev) => ({ ...prev, location: undefined }))
+ }
+
const removeRegion = () => {
+ clearLocationError()
updateProject({
location: { region: '', country: { code: '', name: '' } },
})
@@ -114,7 +123,7 @@ export const ProjectRegion = ({ location, updateProject, ...rest }: ProjectRegio
return (
{t('Get found more easily by putting your project on the map. Select a country or region')}
}
@@ -139,8 +148,11 @@ export const ProjectRegion = ({ location, updateProject, ...rest }: ProjectRegio
inputValue={inputValue}
onMenuOpen={onOpen}
onMenuClose={onClose}
+ isInvalid={Boolean(projectFormError.location)}
+ onFocus={clearLocationError}
/>
)}
+
{displayLocation && (
@@ -160,6 +172,11 @@ export const ProjectRegion = ({ location, updateProject, ...rest }: ProjectRegio
)}
+ {projectFormError.location && (
+
+ {projectFormError.location}
+
+ )}
)
}
diff --git a/src/modules/project/pages1/projectCreation/constants.ts b/src/modules/project/pages1/projectCreation/constants.ts
index cbde28d3e..ece74d6e5 100644
--- a/src/modules/project/pages1/projectCreation/constants.ts
+++ b/src/modules/project/pages1/projectCreation/constants.ts
@@ -327,3 +327,5 @@ export const tagToRewardCategoryMapping: { [key: string]: RewardCategory[] } = {
homeschooling: [RewardCategory.Shoutout, RewardCategory.Course, RewardCategory.Experience],
minecraft: [RewardCategory.Game],
}
+
+export const ProjectCountryCodesThatAreRestricted = ['UA', 'PSE', 'YE', 'RU']
diff --git a/src/modules/project/pages1/projectCreation/hooks/useLocationMandatoryRedirect.tsx b/src/modules/project/pages1/projectCreation/hooks/useLocationMandatoryRedirect.tsx
new file mode 100644
index 000000000..5c79791df
--- /dev/null
+++ b/src/modules/project/pages1/projectCreation/hooks/useLocationMandatoryRedirect.tsx
@@ -0,0 +1,16 @@
+import { useEffect } from 'react'
+import { useNavigate } from 'react-router'
+
+import { useProjectAtom } from '@/modules/project/hooks/useProjectAtom'
+import { getPath } from '@/shared/constants'
+
+export const useLocationMandatoryRedirect = () => {
+ const navigate = useNavigate()
+ const { project, loading } = useProjectAtom()
+
+ useEffect(() => {
+ if (project && !loading && !project.location?.country?.code) {
+ navigate(getPath('launchProjectDetails', project?.id))
+ }
+ }, [loading, project, navigate])
+}
diff --git a/src/modules/project/pages1/projectCreation/views/ProjectCreateStory.tsx b/src/modules/project/pages1/projectCreation/views/ProjectCreateStory.tsx
index 8d5d01ae8..38657e701 100644
--- a/src/modules/project/pages1/projectCreation/views/ProjectCreateStory.tsx
+++ b/src/modules/project/pages1/projectCreation/views/ProjectCreateStory.tsx
@@ -10,6 +10,7 @@ import { dimensions, getPath } from '../../../../../shared/constants'
import { ProjectStoryForm } from '../../../forms/ProjectStoryForm'
import { FormContinueButton } from '../components/FormContinueButton'
import { ProjectCreateLayout } from '../components/ProjectCreateLayout'
+import { useLocationMandatoryRedirect } from '../hooks/useLocationMandatoryRedirect'
import { useProjectStoryForm } from '../hooks/useProjectStoryForm'
export const ProjectCreateStory = () => {
@@ -25,6 +26,8 @@ export const ProjectCreateStory = () => {
const form = useProjectStoryForm({ project })
+ useLocationMandatoryRedirect()
+
const onLeave = () => {
if (!project) {
return navigate(-1)
diff --git a/src/modules/project/pages1/projectCreation/views/ProjectCreationWalletConnectionPage.tsx b/src/modules/project/pages1/projectCreation/views/ProjectCreationWalletConnectionPage.tsx
index 35a02add1..5d1e1f45c 100644
--- a/src/modules/project/pages1/projectCreation/views/ProjectCreationWalletConnectionPage.tsx
+++ b/src/modules/project/pages1/projectCreation/views/ProjectCreationWalletConnectionPage.tsx
@@ -14,6 +14,7 @@ import { useNotification } from '../../../../../utils'
import { ProjectCreationWalletConnectionForm } from '..'
import { FormContinueButton } from '../components/FormContinueButton'
import { ProjectCreateLayout } from '../components/ProjectCreateLayout'
+import { useLocationMandatoryRedirect } from '../hooks/useLocationMandatoryRedirect'
import { ConnectionOption, useWalletForm } from '../hooks/useWalletForm'
import { ProjectCreateCompletionPage } from './ProjectCreateCompletionPage'
@@ -27,6 +28,8 @@ export const ProjectCreationWalletConnectionPage = () => {
const { project, loading } = useProjectAtom()
const { createWallet } = useProjectWalletAPI()
+ useLocationMandatoryRedirect()
+
const [isReadyForLaunch, setReadyForLaunch] = useState(false)
const handleNext = (createWalletInput: CreateWalletInput | null) => {
diff --git a/src/modules/project/pages1/projectCreation/views/ProjectDetails.tsx b/src/modules/project/pages1/projectCreation/views/ProjectDetails.tsx
index 9648157a3..cf3b1466e 100644
--- a/src/modules/project/pages1/projectCreation/views/ProjectDetails.tsx
+++ b/src/modules/project/pages1/projectCreation/views/ProjectDetails.tsx
@@ -1,8 +1,10 @@
import { VStack } from '@chakra-ui/react'
+import { useSetAtom } from 'jotai'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import { useProjectDetailsAPI } from '@/modules/project/API/useProjectDetailsAPI'
+import { projectFormErrorAtom } from '@/modules/project/state/projectFormAtom'
import TitleWithProgressBar from '../../../../../components/molecules/TitleWithProgressBar'
import { getPath } from '../../../../../shared/constants'
@@ -12,27 +14,47 @@ import { ProjectRegion } from '../../../forms/ProjectRegion'
import { ProjectTagsCreateEdit } from '../../../forms/ProjectTagsCreateEdit'
import { FormContinueButton } from '../components/FormContinueButton'
import { ProjectCreateLayout } from '../components/ProjectCreateLayout'
+import { ProjectCountryCodesThatAreRestricted } from '../constants'
import { useProjectDetailsForm } from '../hooks/useProjectDetailsForm'
export const ProjectDetails = () => {
const { t } = useTranslation()
const navigate = useNavigate()
- const { toast, unexpected } = useNotification()
+ const toast = useNotification()
const { queryProjectDetails } = useProjectDetailsAPI(true)
- const { updateProject, saveProject, saveTags, setLinks, setTags, project, tags, linkError, tagsLoading, isDirty } =
+ const { updateProject, saveProject, saveTags, setLinks, setTags, project, tags, linkError, tagsLoading } =
useProjectDetailsForm()
+ const setProjectFormError = useSetAtom(projectFormErrorAtom)
+
const onSubmit = async () => {
if (!project) {
return
}
+ if (!project.location || !project.location.country || !project.location.country.code) {
+ toast.error({
+ title: 'Please select a region',
+ description: 'Project region is required to proceed',
+ })
+ setProjectFormError((prev) => ({ ...prev, location: 'Project region is required' }))
+ return
+ }
+
+ if (ProjectCountryCodesThatAreRestricted.includes(project.location.country.code)) {
+ toast.error({
+ title: 'Country not supported',
+ description: 'We are not able to support projects from this country',
+ })
+ setProjectFormError((prev) => ({ ...prev, location: 'Country not supported' }))
+ return
+ }
+
if (linkError.includes(true)) {
- toast({
- status: 'warning',
+ toast.warning({
title: 'failed to update project',
description: 'please enter a valid url for project links',
})
@@ -45,7 +67,7 @@ export const ProjectDetails = () => {
navigate(getPath('launchProjectStory', project.id))
} catch (e) {
- unexpected()
+ toast.unexpected()
}
}
@@ -66,7 +88,7 @@ export const ProjectDetails = () => {
return (
<>
}
+ continueButton={}
onBackClick={onBackClick}
title={}
>
diff --git a/src/modules/project/pages1/projectCreation/views/rewards/ProjectCreateRewards.tsx b/src/modules/project/pages1/projectCreation/views/rewards/ProjectCreateRewards.tsx
index adb83a2dc..da1fd2e97 100644
--- a/src/modules/project/pages1/projectCreation/views/rewards/ProjectCreateRewards.tsx
+++ b/src/modules/project/pages1/projectCreation/views/rewards/ProjectCreateRewards.tsx
@@ -8,6 +8,7 @@ import TitleWithProgressBar from '../../../../../../components/molecules/TitleWi
import { getPath } from '../../../../../../shared/constants'
import { FormContinueButton } from '../../components/FormContinueButton'
import { ProjectCreateLayout } from '../../components/ProjectCreateLayout'
+import { useLocationMandatoryRedirect } from '../../hooks/useLocationMandatoryRedirect'
export const ProjectCreateRewards = () => {
const { t } = useTranslation()
@@ -18,6 +19,8 @@ export const ProjectCreateRewards = () => {
const { project } = useProjectAtom()
const { rewards } = useRewardsAtom()
+ useLocationMandatoryRedirect()
+
const isNew = useMatch(getPath('launchProjectRewardsCreate', project?.id))
const isEdit = useMatch(getPath('launchProjectRewardsEdit', project?.id, ':rewardId'))
const isCreatingOrEditing = isNew || isEdit
diff --git a/src/modules/project/pages1/projectDashboard/views/ProjectDashboardDetails.tsx b/src/modules/project/pages1/projectDashboard/views/ProjectDashboardDetails.tsx
index e9301a10e..c7c224668 100644
--- a/src/modules/project/pages1/projectDashboard/views/ProjectDashboardDetails.tsx
+++ b/src/modules/project/pages1/projectDashboard/views/ProjectDashboardDetails.tsx
@@ -1,12 +1,15 @@
import { Button, ButtonProps, VStack } from '@chakra-ui/react'
+import { useSetAtom } from 'jotai'
import { useTranslation } from 'react-i18next'
import { useProjectDetailsAPI } from '@/modules/project/API/useProjectDetailsAPI'
import { ProjectLinks } from '@/modules/project/forms/ProjectLinks'
import { ProjectRegion } from '@/modules/project/forms/ProjectRegion'
import { ProjectTagsCreateEdit } from '@/modules/project/forms/ProjectTagsCreateEdit'
+import { projectFormErrorAtom } from '@/modules/project/state/projectFormAtom'
import { useNotification } from '@/utils'
+import { ProjectCountryCodesThatAreRestricted } from '../../projectCreation/constants'
import { useProjectDetailsForm } from '../../projectCreation/hooks/useProjectDetailsForm'
import { DashboardLayout } from '../common'
import { ProjectUnsavedModal, useProjectUnsavedModal } from '../common/ProjectUnsavedModal'
@@ -19,12 +22,31 @@ export const ProjectDashboardDetails = () => {
const { project, isDirty, linkError, saveProject, saving, saveTags, setLinks, setTags, tags, updateProject } =
useProjectDetailsForm()
+ const setProjectFormError = useSetAtom(projectFormErrorAtom)
const unsavedModal = useProjectUnsavedModal({
hasUnsaved: isDirty,
})
const onSubmit = async () => {
+ if (!project.location || !project.location.country || !project.location.country.code) {
+ toast.error({
+ title: 'Please select a region',
+ description: 'Project region is required to proceed',
+ })
+ setProjectFormError((prev) => ({ ...prev, location: 'Project region is required' }))
+ return
+ }
+
+ if (ProjectCountryCodesThatAreRestricted.includes(project.location.country.code)) {
+ toast.error({
+ title: 'Country not supported',
+ description: 'We are not able to support projects from this country',
+ })
+ setProjectFormError((prev) => ({ ...prev, location: 'Country not supported' }))
+ return
+ }
+
if (linkError.includes(true)) {
toast.warning({
title: 'failed to update project',
diff --git a/src/modules/project/state/projectFormAtom.ts b/src/modules/project/state/projectFormAtom.ts
index 2010e1e5b..9a87a8fc5 100644
--- a/src/modules/project/state/projectFormAtom.ts
+++ b/src/modules/project/state/projectFormAtom.ts
@@ -38,7 +38,12 @@ export const diffProjectAtom = atom((get) => {
])
})
+type ProjectFormError = { [key in keyof ProjectState]: string }
+/** Project form error state */
+export const projectFormErrorAtom = atom({} as ProjectFormError)
+
/** Reset all real-atoms in this file to it's initial State */
export const projectFormAtomReset = atom(null, (get, set) => {
set(formProjectAtom, {} as ProjectState)
+ set(projectFormErrorAtom, {} as ProjectFormError)
})