diff --git a/config/example.env b/config/example.env index 34b06bf8d..6af9e0776 100644 --- a/config/example.env +++ b/config/example.env @@ -271,3 +271,10 @@ ZKEVM_MAINNET_NODE_HTTP_URL= ZKEVM_CARDONA_NODE_HTTP_URL= ENDAOMENT_ADMIN_WALLET_ADDRESS=0xfE3524e04E4e564F9935D34bB5e80c5CaB07F5b4 + +ABC_LAUNCH_API_SECRET= +ABC_LAUNCH_API_URL= +ABC_LAUNCH_DATA_SOURCE= + +QACC_NETWORK_ID= +ABC_LAUNCHER_ADAPTER= \ No newline at end of file diff --git a/config/test.env b/config/test.env index 0f23bc9af..f54f85253 100644 --- a/config/test.env +++ b/config/test.env @@ -220,3 +220,7 @@ ZKEVM_MAINNET_NODE_HTTP_URL=https://polygon-zkevm.drpc.org ZKEVM_CARDONA_NODE_HTTP_URL=https://rpc.cardona.zkevm-rpc.com ENDAOMENT_ADMIN_WALLET_ADDRESS=0xfE3524e04E4e564F9935D34bB5e80c5CaB07F5b4 + +ABC_LAUNCH_API_SECRET= +ABC_LAUNCH_API_URL= +ABC_LAUNCH_DATA_SOURCE= \ No newline at end of file diff --git a/src/adapters/abcLauncher/AbcLauncherAdapter.ts b/src/adapters/abcLauncher/AbcLauncherAdapter.ts new file mode 100644 index 000000000..98a107b7d --- /dev/null +++ b/src/adapters/abcLauncher/AbcLauncherAdapter.ts @@ -0,0 +1,67 @@ +// a method the get objects from mongodb api read from config DONATION_SAVE_BACKUP_API_URL with sercret read from DONATION_SAVE_BACKUP_API_SECRET, +// it must filter objects by those doesn't have `imported` field with true value +// also must support pagination + +import axios from 'axios'; +import { logger } from '../../utils/logger'; +import config from '../../config'; +import { IAbcLauncher } from './AbcLauncherInterface'; +import { Abc } from '../../entities/project'; + +const ABC_LAUNCH_API_URL = config.get('ABC_LAUNCH_API_URL') as string; +const ABC_LAUNCH_API_SECRET = config.get('ABC_LAUNCH_API_SECRET') as string; +const ABC_LAUNCH_DATA_SOURCE = config.get('ABC_LAUNCH_DATA_SOURCE') as string; +const ABC_LAUNCH_COLLECTION = config.get('ABC_LAUNCH_COLLECTION') || 'project'; +const ABC_LAUNCH_DATABASE = config.get('ABC_LAUNCH_DATABASE') || 'abc-launcher'; + +// add '/' if doesn't exist at the +const baseUrl = ABC_LAUNCH_API_URL.endsWith('/') + ? ABC_LAUNCH_API_URL + : `${ABC_LAUNCH_API_URL}/`; + +export class AbcLauncherAdapter implements IAbcLauncher { + async getProjectAbcLaunchData( + projectAddress: string, + ): Promise { + try { + const result = await axios.post( + `${baseUrl}find`, + { + collection: ABC_LAUNCH_COLLECTION, + database: ABC_LAUNCH_DATABASE, + dataSource: ABC_LAUNCH_DATA_SOURCE, + filter: { + projectAddress: projectAddress.toLocaleLowerCase(), + }, + }, + { + headers: { + 'api-key': ABC_LAUNCH_API_SECRET, + 'Content-Type': 'application/json', + 'Access-Control-Request-Headers': '*', + }, + }, + ); + + if (result.status !== 200) { + logger.error('getNotImportedDonationsFromBackup error', result.data); + throw new Error( + 'getNotImportedDonationsFromBackup error, status: ' + result.status, + ); + } + const data = result.data.documents[0]; + if (!data) return undefined; + return { + tokenTicker: data.tokenTicker, + tokenName: data.tokenName, + icon: data.iconHash, + orchestratorAddress: data.orchestratorAddress, + issuanceTokenAddress: data.issuanceTokenAddress, + projectAddress: data.projectAddress, + }; + } catch (e) { + logger.error('getNotImportedDonationsFromBackup error', e); + throw e; + } + } +} diff --git a/src/adapters/abcLauncher/AbcLauncherAdapterMock.ts b/src/adapters/abcLauncher/AbcLauncherAdapterMock.ts new file mode 100644 index 000000000..b97c0a314 --- /dev/null +++ b/src/adapters/abcLauncher/AbcLauncherAdapterMock.ts @@ -0,0 +1,34 @@ +import { Abc } from '../../entities/project'; +import { IAbcLauncher } from './AbcLauncherInterface'; + +export class AbcLauncherAdapterMock implements IAbcLauncher { + private _nextData: Abc; + + getDefaultData(): Abc { + return { + tokenTicker: 'MOCK', + tokenName: 'Mock Token Name', + icon: 'moch_icon_hash', + orchestratorAddress: 'mock_address', + issuanceTokenAddress: 'mock_issue_address', + projectAddress: 'mock_project_address', + }; + } + + setNextData(data: Abc) { + this._nextData = data; + } + + constructor() { + this._nextData = this.getDefaultData(); + } + + async getProjectAbcLaunchData(projectAddress: string) { + const data = this._nextData; + this._nextData = this.getDefaultData(); + return { + ...data, + projectAddress, + }; + } +} diff --git a/src/adapters/abcLauncher/AbcLauncherInterface.ts b/src/adapters/abcLauncher/AbcLauncherInterface.ts new file mode 100644 index 000000000..20ab325e2 --- /dev/null +++ b/src/adapters/abcLauncher/AbcLauncherInterface.ts @@ -0,0 +1,5 @@ +import { Abc } from '../../entities/project'; + +export interface IAbcLauncher { + getProjectAbcLaunchData(projectAddress: string): Promise; +} diff --git a/src/adapters/adaptersFactory.ts b/src/adapters/adaptersFactory.ts index c93bc1ea2..ead37fc1b 100644 --- a/src/adapters/adaptersFactory.ts +++ b/src/adapters/adaptersFactory.ts @@ -17,6 +17,8 @@ import { DonationSaveBackupMockAdapter } from './donationSaveBackup/DonationSave import { SuperFluidAdapter } from './superFluid/superFluidAdapter'; import { SuperFluidMockAdapter } from './superFluid/superFluidMockAdapter'; import { SuperFluidAdapterInterface } from './superFluid/superFluidAdapterInterface'; +import { AbcLauncherAdapter } from './abcLauncher/AbcLauncherAdapter'; +import { AbcLauncherAdapterMock } from './abcLauncher/AbcLauncherAdapterMock'; const discordAdapter = new DiscordAdapter(); const googleAdapter = new GoogleAdapter(); @@ -111,3 +113,17 @@ export const getSuperFluidAdapter = (): SuperFluidAdapterInterface => { return superFluidMockAdapter; } }; + +const abcLauncherAdapter = new AbcLauncherAdapter(); +const abcLauncherMockAdapter = new AbcLauncherAdapterMock(); + +export const getAbcLauncherAdapter = () => { + switch (process.env.ABC_LAUNCHER_ADAPTER) { + case 'abcLauncher': + return abcLauncherAdapter; + case 'mock': + return abcLauncherMockAdapter; + default: + return abcLauncherMockAdapter; + } +}; diff --git a/src/entities/project.ts b/src/entities/project.ts index 1230025d8..f5d7dab48 100644 --- a/src/entities/project.ts +++ b/src/entities/project.ts @@ -117,6 +117,39 @@ export enum ReviewStatus { Listed = 'Listed', NotListed = 'Not Listed', } +@ObjectType() +class ProjectTeamMember { + @Field() + name: string; + + @Field({ nullable: true }) + image?: string; + + @Field({ nullable: true }) + twitter?: string; + + @Field({ nullable: true }) + linkedin?: string; + + @Field({ nullable: true }) + farcaster?: string; +} + +@ObjectType() +export class Abc { + @Field() + tokenName: string; + @Field() + tokenTicker: string; + @Field() + issuanceTokenAddress: string; + @Field() + icon: string; + @Field() + orchestratorAddress: string; + @Field() + projectAddress: string; +} @Entity() @ObjectType() @@ -198,6 +231,18 @@ export class Project extends BaseEntity { @Column({ nullable: true }) image?: string; + @Field({ nullable: true }) + @Column({ nullable: true }) + teaser?: string; + + @Field(_ => [ProjectTeamMember], { nullable: true }) + @Column('jsonb', { nullable: true }) + teamMembers: ProjectTeamMember[]; + + @Field(_ => Abc, { nullable: true }) + @Column('jsonb', { nullable: true }) + abc: Abc; + @Index('trgm_idx_project_impact_location', { synchronize: false }) @Field({ nullable: true }) @Column({ nullable: true }) diff --git a/src/provider.ts b/src/provider.ts index aa8062928..ad333a0f5 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -34,6 +34,10 @@ export const NETWORK_IDS = { SOLANA_DEVNET: 103, }; +export const QACC_NETWORK_ID = config.get('QACC_NETWORK_ID') + ? +config.get('QACC_NETWORK_ID') + : NETWORK_IDS.ZKEVM_CARDONA; + export const superTokensToToken = { ETHx: 'ETH', USDCx: 'USDC', diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts new file mode 100644 index 000000000..0ecfe1c2b --- /dev/null +++ b/src/resolvers/projectResolver.test.ts @@ -0,0 +1,85 @@ +import axios from 'axios'; +import { assert, expect } from 'chai'; +import { + generateRandomEtheriumAddress, + generateTestAccessToken, + graphqlUrl, + saveUserDirectlyToDb, +} from '../../test/testUtils'; +import { User } from '../entities/user'; +import { createProjectQuery } from '../../test/graphqlQueries'; +import { + CreateProjectInput, + ProjectTeamMemberInput, +} from './types/project-input'; +import { getAbcLauncherAdapter } from '../adapters/adaptersFactory'; + +describe('ProjectCreate test', createProjectTestCases); + +function createProjectTestCases() { + let user: User; + let accessToken: string; + + beforeEach(async () => { + user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); + accessToken = await generateTestAccessToken(user.id); + }); + + it('should create project with team members successfully', async () => { + assert.isOk(user); + assert.isOk(accessToken); + + const teamMembers: ProjectTeamMemberInput[] = [ + { + name: 'John Doe', + image: 'https://example.com/john-doe.jpg', + twitter: 'https://twitter.com/johndoe', + linkedin: 'https://linkedin.com/johndoe', + farcaster: 'https://farcaster.com/johndoe', + }, + { + name: 'Jane Doe', + image: 'https://example.com/jane-doe.jpg', + twitter: 'https://twitter.com/janedoe', + linkedin: 'https://linkedin.com/janedoe', + farcaster: 'https://farcaster.com/janedoe', + }, + ]; + + const projectAddress = generateRandomEtheriumAddress(); + const createProjectInput: CreateProjectInput = { + title: 'Test Create Project 1', + adminUserId: user.id, + description: 'Test Project Description', + categories: [], + image: 'https://example.com/test-project.jpg', + teaser: 'https://example.com/test-project-teaser.jpg', + impactLocation: 'Test Impact Location', + isDraft: false, + teamMembers, + address: projectAddress, + }; + + const result = await axios.post( + graphqlUrl, + { + query: createProjectQuery, + variables: { + project: createProjectInput, + }, + }, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); + + const project = result.data.data.createProject; + assert.isOk(project); + expect(project.teamMembers).to.deep.equal(teamMembers); + const expectedAbc = + await getAbcLauncherAdapter().getProjectAbcLaunchData(projectAddress); + expect(project.abc).to.deep.equal(expectedAbc); + }); +} diff --git a/src/resolvers/projectResolver.ts b/src/resolvers/projectResolver.ts index 0722d9e3a..a7291f01a 100644 --- a/src/resolvers/projectResolver.ts +++ b/src/resolvers/projectResolver.ts @@ -52,7 +52,6 @@ import { } from '../utils/errorMessages'; import { canUserVisitProject, - validateProjectRelatedAddresses, validateProjectTitle, validateProjectTitleForEdit, validateProjectWalletAddress, @@ -73,9 +72,7 @@ import { findProjectRecipientAddressByProjectId, getPurpleListAddresses, isWalletAddressInPurpleList, - removeRecipientAddressOfProject, } from '../repositories/projectAddressRepository'; -import { RelatedAddressInputType } from './types/ProjectVerificationUpdateInput'; import { FilterProjectQueryInputParams, filterProjectsQuery, @@ -85,8 +82,11 @@ import { totalProjectsPerDateByMonthAndYear, } from '../repositories/projectRepository'; import { sortTokensByOrderAndAlphabets } from '../utils/tokenUtils'; -import { getNotificationAdapter } from '../adapters/adaptersFactory'; -import { NETWORK_IDS } from '../provider'; +import { + getAbcLauncherAdapter, + getNotificationAdapter, +} from '../adapters/adaptersFactory'; +import { NETWORK_IDS, QACC_NETWORK_ID } from '../provider'; import { getVerificationFormStatusByProjectId } from '../repositories/projectVerificationRepository'; import { resourcePerDateReportValidator, @@ -95,7 +95,7 @@ import { import { ResourcePerDateRange } from './donationResolver'; import { findUserReactionsByProjectIds } from '../repositories/reactionRepository'; import { AppDataSource } from '../orm'; -import { creteSlugFromProject, isSocialMediaEqual } from '../utils/utils'; +import { creteSlugFromProject } from '../utils/utils'; import { findCampaignBySlug } from '../repositories/campaignRepository'; import { Campaign } from '../entities/campaign'; import { FeaturedUpdate } from '../entities/featuredUpdate'; @@ -105,10 +105,7 @@ import { ChainType } from '../types/network'; import { findActiveQfRound } from '../repositories/qfRoundRepository'; import { getAllProjectsRelatedToActiveCampaigns } from '../services/campaignService'; import { getAppropriateNetworkId } from '../services/chains'; -import { - addBulkProjectSocialMedia, - removeProjectSocialMedia, -} from '../repositories/projectSocialMediaRepository'; +import { addBulkProjectSocialMedia } from '../repositories/projectSocialMediaRepository'; const projectUpdatsCacheDuration = 1000 * 60 * 60; @@ -1009,163 +1006,167 @@ export class ProjectResolver { @Mutation(_returns => Project) async updateProject( + // eslint-disable-next-line @typescript-eslint/no-unused-vars @Arg('projectId') projectId: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars @Arg('newProjectData') newProjectData: UpdateProjectInput, + // eslint-disable-next-line @typescript-eslint/no-unused-vars @Ctx() { req: { user } }: ApolloContext, ) { - if (!user) - throw new Error( - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - const { image } = newProjectData; - - // const project = await Project.findOne({ id: projectId }); - const project = await findProjectById(projectId); - - if (!project) - throw new Error(i18n.__(translationErrorMessagesKeys.PROJECT_NOT_FOUND)); - - logger.debug(`project.adminUserId ---> : ${project.adminUserId}`); - logger.debug(`user.userId ---> : ${user.userId}`); - logger.debug(`updateProject, inputData :`, newProjectData); - if (project.adminUserId !== user.userId) - throw new Error( - i18n.__(translationErrorMessagesKeys.YOU_ARE_NOT_THE_OWNER_OF_PROJECT), - ); - - for (const field in newProjectData) { - if (field === 'addresses' || field === 'socialMedia') { - // We will take care of addresses and relations manually - continue; - } - project[field] = newProjectData[field]; - } - - if (!newProjectData.categories) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, - ), - ); - } - - const categoriesPromise = newProjectData.categories.map(async category => { - const [c] = await this.categoryRepository.find({ - where: { - name: category, - isActive: true, - canUseOnFrontend: true, - }, - }); - if (!c) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, - ), - ); - } - return c; - }); - - const categories = await Promise.all(categoriesPromise); - if (categories.length > 5) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE, - ), - ); - } - project.categories = categories; - - const heartCount = await Reaction.count({ where: { projectId } }); - - const qualityScore = getQualityScore( - project.description, - Boolean(image), - heartCount, - ); - if (newProjectData.title) { - await validateProjectTitleForEdit(newProjectData.title, projectId); - } - - if (newProjectData.addresses) { - await validateProjectRelatedAddresses( - newProjectData.addresses, - projectId, - ); - } - const slugBase = creteSlugFromProject(newProjectData.title); - if (!slugBase) { - throw new Error( - i18n.__(translationErrorMessagesKeys.INVALID_PROJECT_TITLE), - ); - } - const newSlug = await getAppropriateSlug(slugBase, projectId); - if (project.slug !== newSlug && !project.slugHistory?.includes(newSlug)) { - // it's just needed for editProject, we dont add current slug in slugHistory so it's not needed to do this in addProject - project.slugHistory?.push(project.slug as string); - } - if (image !== undefined) { - project.image = image; - } - project.slug = newSlug; - project.qualityScore = qualityScore; - project.updatedAt = new Date(); - project.listed = null; - project.reviewStatus = ReviewStatus.NotReviewed; - - await project.save(); - await project.reload(); - - if (!isSocialMediaEqual(project.socialMedia, newProjectData.socialMedia)) { - await removeProjectSocialMedia(projectId); - if (newProjectData.socialMedia && newProjectData.socialMedia.length > 0) { - const socialMediaEntities = newProjectData.socialMedia.map( - socialMediaInput => { - return { - type: socialMediaInput.type, - link: socialMediaInput.link, - projectId, - userId: user.userId, - }; - }, - ); - await addBulkProjectSocialMedia(socialMediaEntities); - } - } - - const adminUser = (await findUserById(project.adminUserId)) as User; - if (newProjectData.addresses) { - await removeRecipientAddressOfProject({ project }); - await addBulkNewProjectAddress( - newProjectData?.addresses.map(relatedAddress => { - return { - project, - user: adminUser, - address: relatedAddress.address, - chainType: relatedAddress.chainType, - - // Frontend doesn't send networkId for solana addresses so we set it to default solana chain id - networkId: getAppropriateNetworkId({ - networkId: relatedAddress.networkId, - chainType: relatedAddress.chainType, - }), - - isRecipient: true, - }; - }), - ); - } + throw new Error(i18n.__(translationErrorMessagesKeys.NOT_IMPLEMENTED)); + // if (!user) + // throw new Error( + // i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), + // ); + // const { image } = newProjectData; + + // // const project = await Project.findOne({ id: projectId }); + // const project = await findProjectById(projectId); + + // if (!project) + // throw new Error(i18n.__(translationErrorMessagesKeys.PROJECT_NOT_FOUND)); + + // logger.debug(`project.adminUserId ---> : ${project.adminUserId}`); + // logger.debug(`user.userId ---> : ${user.userId}`); + // logger.debug(`updateProject, inputData :`, newProjectData); + // if (project.adminUserId !== user.userId) + // throw new Error( + // i18n.__(translationErrorMessagesKeys.YOU_ARE_NOT_THE_OWNER_OF_PROJECT), + // ); + + // for (const field in newProjectData) { + // if (field === 'addresses' || field === 'socialMedia') { + // // We will take care of addresses and relations manually + // continue; + // } + // project[field] = newProjectData[field]; + // } + + // if (!newProjectData.categories) { + // throw new Error( + // i18n.__( + // translationErrorMessagesKeys.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, + // ), + // ); + // } + + // const categoriesPromise = newProjectData.categories.map(async category => { + // const [c] = await this.categoryRepository.find({ + // where: { + // name: category, + // isActive: true, + // canUseOnFrontend: true, + // }, + // }); + // if (!c) { + // throw new Error( + // i18n.__( + // translationErrorMessagesKeys.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, + // ), + // ); + // } + // return c; + // }); - project.adminUser = adminUser; - project.addresses = await findProjectRecipientAddressByProjectId({ - projectId, - }); + // const categories = await Promise.all(categoriesPromise); + // if (categories.length > 5) { + // throw new Error( + // i18n.__( + // translationErrorMessagesKeys.CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE, + // ), + // ); + // } + // project.categories = categories; + + // const heartCount = await Reaction.count({ where: { projectId } }); + + // const qualityScore = getQualityScore( + // project.description, + // Boolean(image), + // heartCount, + // ); + // if (newProjectData.title) { + // await validateProjectTitleForEdit(newProjectData.title, projectId); + // } + + // if (newProjectData.addresses) { + // await validateProjectRelatedAddresses( + // newProjectData.addresses, + // projectId, + // ); + // } + // const slugBase = creteSlugFromProject(newProjectData.title); + // if (!slugBase) { + // throw new Error( + // i18n.__(translationErrorMessagesKeys.INVALID_PROJECT_TITLE), + // ); + // } + // const newSlug = await getAppropriateSlug(slugBase, projectId); + // if (project.slug !== newSlug && !project.slugHistory?.includes(newSlug)) { + // // it's just needed for editProject, we dont add current slug in slugHistory so it's not needed to do this in addProject + // project.slugHistory?.push(project.slug as string); + // } + // if (image !== undefined) { + // project.image = image; + // } + // project.slug = newSlug; + // project.qualityScore = qualityScore; + // project.updatedAt = new Date(); + // project.listed = null; + // project.reviewStatus = ReviewStatus.NotReviewed; + + // await project.save(); + // await project.reload(); + + // if (!isSocialMediaEqual(project.socialMedia, newProjectData.socialMedia)) { + // await removeProjectSocialMedia(projectId); + // if (newProjectData.socialMedia && newProjectData.socialMedia.length > 0) { + // const socialMediaEntities = newProjectData.socialMedia.map( + // socialMediaInput => { + // return { + // type: socialMediaInput.type, + // link: socialMediaInput.link, + // projectId, + // userId: user.userId, + // }; + // }, + // ); + // await addBulkProjectSocialMedia(socialMediaEntities); + // } + // } + + // const adminUser = (await findUserById(project.adminUserId)) as User; + // if (newProjectData.addresses) { + // await removeRecipientAddressOfProject({ project }); + // await addBulkNewProjectAddress( + // newProjectData?.addresses.map(relatedAddress => { + // return { + // project, + // user: adminUser, + // address: relatedAddress.address, + // chainType: relatedAddress.chainType, + + // // Frontend doesn't send networkId for solana addresses so we set it to default solana chain id + // networkId: getAppropriateNetworkId({ + // networkId: relatedAddress.networkId, + // chainType: relatedAddress.chainType, + // }), + + // isRecipient: true, + // }; + // }), + // ); + // } + + // project.adminUser = adminUser; + // project.addresses = await findProjectRecipientAddressByProjectId({ + // projectId, + // }); - // Edit emails - await getNotificationAdapter().projectEdited({ project }); + // // Edit emails + // await getNotificationAdapter().projectEdited({ project }); - return project; + // return project; } @Mutation(_returns => Project) @@ -1337,9 +1338,13 @@ export class ProjectResolver { ); } - await validateProjectRelatedAddresses( - projectInput.addresses as RelatedAddressInputType[], + const abcLauncherAdapter = getAbcLauncherAdapter(); + const abc = await abcLauncherAdapter.getProjectAbcLaunchData( + projectInput.address, ); + if (!abc) { + throw new Error(i18n.__(translationErrorMessagesKeys.ABC_NOT_FOUND)); + } await validateProjectTitle(projectInput.title); const slugBase = creteSlugFromProject(projectInput.title); if (!slugBase) { @@ -1385,6 +1390,7 @@ export class ProjectResolver { const project = Project.create({ ...projectInput, + abc, categories: categories as Category[], organization: organization as Organization, image, @@ -1423,8 +1429,9 @@ export class ProjectResolver { // const adminUser = (await findUserById(Number(newProject.admin))) as User; // newProject.adminUser = adminUser; await addBulkNewProjectAddress( - projectInput?.addresses.map(relatedAddress => { - const { networkId, address, chainType } = relatedAddress; + [projectInput?.address].map(address => { + const networkId = QACC_NETWORK_ID; + const chainType = ChainType.EVM; return { project, user, diff --git a/src/resolvers/types/project-input.ts b/src/resolvers/types/project-input.ts index 251405c37..473234f51 100644 --- a/src/resolvers/types/project-input.ts +++ b/src/resolvers/types/project-input.ts @@ -2,10 +2,7 @@ import { Field, InputType } from 'type-graphql'; import { FileUpload } from 'graphql-upload/Upload.js'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.js'; import { MaxLength } from 'class-validator'; -import { - ProjectSocialMediaInput, - RelatedAddressInputType, -} from './ProjectVerificationUpdateInput'; +import { ProjectSocialMediaInput } from './ProjectVerificationUpdateInput'; import { IMAGE_LINK_MAX_SIZE, @@ -14,7 +11,6 @@ import { PROJECT_TITLE_MAX_LENGTH, } from '../../constants/validators'; import { errorMessages } from '../../utils/errorMessages'; - @InputType() export class ImageUpload { // Client uploads image file @@ -26,7 +22,25 @@ export class ImageUpload { } @InputType() -class ProjectInput { +export class ProjectTeamMemberInput { + @Field() + name: string; + + @Field({ nullable: true }) + image?: string; + + @Field({ nullable: true }) + twitter?: string; + + @Field({ nullable: true }) + linkedin?: string; + + @Field({ nullable: true }) + farcaster?: string; +} + +@InputType() +export class ProjectInput { @Field() @MaxLength(PROJECT_TITLE_MAX_LENGTH) title: string; @@ -47,6 +61,10 @@ class ProjectInput { @MaxLength(IMAGE_LINK_MAX_SIZE) image?: string; + @Field({ nullable: true }) + @MaxLength(IMAGE_LINK_MAX_SIZE) + teaser?: string; + @Field({ nullable: true }) @MaxLength(IMPACT_LOCATION_MAX_SIZE) impactLocation?: string; @@ -59,16 +77,19 @@ class ProjectInput { @Field(() => [ProjectSocialMediaInput], { nullable: true }) socialMedia?: ProjectSocialMediaInput[]; + + @Field(() => [ProjectTeamMemberInput], { nullable: true }) + teamMembers?: ProjectTeamMemberInput[]; } @InputType() export class CreateProjectInput extends ProjectInput { - @Field(() => [RelatedAddressInputType], { nullable: true }) - addresses: RelatedAddressInputType[]; + @Field({ nullable: true }) + address: string; } @InputType() export class UpdateProjectInput extends ProjectInput { - @Field(() => [RelatedAddressInputType], { nullable: true }) - addresses?: RelatedAddressInputType[]; + @Field({ nullable: true }) + address: string; } diff --git a/src/utils/errorMessages.ts b/src/utils/errorMessages.ts index 1197a4b93..6f82d18b6 100644 --- a/src/utils/errorMessages.ts +++ b/src/utils/errorMessages.ts @@ -321,7 +321,7 @@ export const translationErrorMessagesKeys = { 'PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED', DRAFT_DONATION_DISABLED: 'DRAFT_DONATION_DISABLED', EVM_SUPPORT_ONLY: 'EVM_SUPPORT_ONLY', - + ABC_NOT_FOUND: 'ABC_NOT_FOUND', NO_EMAIL_PROVIDED: 'NO_EMAIL_PROVIDED', INCORRECT_CODE: 'INCORRECT_CODE', }; diff --git a/src/utils/locales/en.json b/src/utils/locales/en.json index 3b947d3d6..f6ef11b45 100644 --- a/src/utils/locales/en.json +++ b/src/utils/locales/en.json @@ -1,107 +1,107 @@ { - "GITCOIN_ERROR_FETCHING_DATA": "Unable to fetch gitcoin data", - "CHAINVINE_CLICK_EVENT_ERROR": "Unable to register click event or link donor", - "CHAINVINE_REGISTRATION_ERROR": "Chainvine ID failed to be generated for the user", - "FIAT_DONATION_ALREADY_EXISTS": "Fiat donation already exists", - "UPLOAD_FAILED": "Upload file failed", - "CHANGE_API_INVALID_TITLE_OR_EIN": "ChangeAPI title or EIN not found or invalid", - "INVALID_SOCIAL_NETWORK": "Invalid social network", - "RECIPIENT_ADDRESSES_CANT_BE_EMPTY": "Recipient addresses can't be empty", - "NOT_IMPLEMENTED": "Not implemented", - "SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID": "Should send at least on of userId or projectId", - "YOU_JUST_CAN_VERIFY_REJECTED_AND_SUBMITTED_FORMS": "You just can verify rejected and submitted forms", - "YOU_JUST_CAN_MAKE_DRAFT_REJECTED_AND_SUBMITTED_FORMS": "You just can make draft rejected and submitted forms", - "YOU_JUST_CAN_REJECT_SUBMITTED_FORMS": "You just can reject submitted forms", - "INVALID_TRACK_ID_FOR_OAUTH2_LOGIN": "Invalid trackId for oauth2 login", - "SOCIAL_NETWORK_IS_DIFFERENT_WITH_CLAIMED_ONE": "Social network is different with claimed one", - "SOCIAL_PROFILE_NOT_FOUND": "Social profile not gound", - "CHANGE_API_TITLE_OR_EIN_NOT_PRECISE": "Please query the exact project title or EIN ID from the ChangeAPI site", - "YOU_ARE_NOT_OWNER_OF_THIS_DONATION": "You are not owner of this donation", - "NOT_SUPPORTED_THIRD_PARTY_API": "Third Party API not supported", - "IPFS_IMAGE_UPLOAD_FAILED": "Image upload failed", - "YOU_SHOULD_FILL_EMAIL_PERSONAL_INFO_BEFORE_CONFIRMING_EMAIL": "You should fill email in personal info step before confirming it", - "YOU_ALREADY_VERIFIED_THIS_EMAIL": "You already verified this email", - "INVALID_FROM_DATE": "Invalid fromDate", - "INVALID_TO_DATE": "Invalid toDate", - "VERIFIED_USERNAME_IS_DIFFERENT_WITH_CLAIMED_ONE": "Username is not the claimed one", - "INVALID_AUTHORIZATION_VERSION": "Authorization version is not valid", - "INVALID_STEP": "Invalid step", - "DONOR_REPORTED_IT_AS_FAILED": "Donor reported it as failed", - "INVALID_DATE_FORMAT": "Date format should be YYYYMMDD HH:mm:ss", - "INTERNAL_SERVER_ERROR": "Internal server error", - "ERROR_CONNECTING_DB": "Error in connecting DB", - "YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT": "You dont have access to view this project", - "JUST_ACTIVE_PROJECTS_ACCEPT_DONATION": "Just active projects accept donation", - "CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE": "Please select no more than 5 categories", - "CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION": "This category is not valid", - "INVALID_TX_HASH": "Invalid txHash", - "INVALID_TRANSACTION_ID": "Invalid transactionId", - "DUPLICATE_TX_HASH": "There is a donation with this txHash in our DB", - "YOU_ARE_NOT_THE_OWNER_OF_PROJECT": "You are not the owner of this project.", - "YOU_ARE_NOT_THE_OWNER_OF_PROJECT_VERIFICATION_FORM": "You are not the owner of this project verification form.", - "YOU_ARE_NOT_THE_OWNER_OF_SOCIAL_PROFILE": "You are not the owner of this social profile project verification form.", - "PROJECT_VERIFICATION_FORM_IS_NOT_DRAFT_SO_YOU_CANT_MODIFY_SOCIAL_PROFILES": "project verification form is not draft, so you cant modify social profiles", - "YOU_ALREADY_ADDED_THIS_SOCIAL_PROFILE_FOR_THIS_VERIFICATION_FORM": "You already have added this social profile for this verification form", - "PROJECT_VERIFICATION_FORM_NOT_FOUND": "Project verification form not found", - "PROJECT_IS_ALREADY_VERIFIED": "Project is already verified.", - "YOU_JUST_CAN_EDIT_DRAFT_REQUESTS": "Project is already verified.", - "EMAIL_CONFIRMATION_CANNOT_BE_SENT_IN_THIS_STEP": "Email confirmation cannot be sent in this step", - "THERE_IS_AN_ONGOING_VERIFICATION_REQUEST_FOR_THIS_PROJECT": "There is an ongoing project verification request for this project", - "THERE_IS_NOT_ANY_ONGOING_PROJECT_VERIFICATION_FORM_FOR_THIS_PROJECT": "There is not any project verification form for this project", - "PROJECT_STATUS_NOT_FOUND": "No project status found, this should be impossible", - "YOU_DONT_HAVE_ACCESS_TO_DEACTIVATE_THIS_PROJECT": "You dont have access to deactivate this project", - "PROJECT_NOT_FOUND": "Project not found.", - "PROJECT_IS_NOT_ACTIVE": "Project is not active.", - "INVALID_FUNCTION": "Invalid function name of transaction", - "PROJECT_UPDATE_NOT_FOUND": "Project update not found.", - "DONATION_NOT_FOUND": "donation not found", - "THIS_PROJECT_IS_CANCELLED_OR_DEACTIVATED_ALREADY": "This project has been cancelled by an Admin for inappropriate content or a violation of the Terms of Use", - "DONATION_VIEWING_LOGIN_REQUIRED": "You must be signed-in in order to register project donations", - "TRANSACTION_NOT_FOUND": "Transaction not found.", - "TRANSACTION_FROM_ADDRESS_IS_DIFFERENT_FROM_SENT_FROM_ADDRESS": "FromAddress of Transaction is different from sent fromAddress", - "TRANSACTION_STATUS_IS_FAILED_IN_NETWORK": "Transaction status is failed in network", - "INVALID_VERIFICATION_REVOKE_STATUS": "Invalid revoke status updated", - "TRANSACTION_NOT_FOUND_AND_NONCE_IS_USED": "Transaction not found and nonce is used", - "TRANSACTION_AMOUNT_IS_DIFFERENT_WITH_SENT_AMOUNT": "Transaction amount is different with sent amount", - "TRANSACTION_CANT_BE_OLDER_THAN_DONATION": "Transaction can not be older than donation", - "TRANSACTION_TO_ADDRESS_IS_DIFFERENT_FROM_SENT_TO_ADDRESS": "ToAddress of Transaction is different to sent toAddress", - "TRANSACTION_SMART_CONTRACT_CONFLICTS_WITH_CURRENCY": "Smart contract address is not equal to transaction.to", - "USER_NOT_FOUND": "User not found.", - "INVALID_NETWORK_ID": "Network Id is invalid", - "INVALID_TOKEN_SYMBOL": "Token symbol is invalid", - "TOKEN_SYMBOL_IS_REQUIRED": "Token symbol is required", - "TOKEN_NOT_FOUND": "Token Not found", - "TRANSACTION_NOT_FOUNT_IN_USER_HISTORY": "TRANSACTION_NOT_FOUNT_IN_USER_HISTORY", - "TRANSACTION_WITH_THIS_NONCE_IS_NOT_MINED_ALREADY": "Transaction with this nonce is not mined already", - "TO_ADDRESS_OF_DONATION_SHOULD_BE_PROJECT_WALLET_ADDRESS": "toAddress of donation should be equal to project wallet address", - "INVALID_WALLET_ADDRESS": "Address not valid", - "INVALID_EMAIL": "Email not valid", - "UN_AUTHORIZED": "unAuthorized", - "BOTH_FIRST_NAME_AND_LAST_NAME_CANT_BE_EMPTY": "Both firstName and lastName cant be empty", - "FIRSTNAME_CANT_BE_EMPTY_STRING": "firstName cant be empty string", - "LASTNAME_CANT_BE_EMPTY_STRING": "lastName cant be empty string", - "PROJECT_WITH_THIS_TITLE_EXISTS": "There is a project with this title, please use another title", - "INVALID_PROJECT_TITLE": "Your project name isnt valid, please only use letters and numbers", - "ACCESS_DENIED": "Access denied", - "AUTHENTICATION_REQUIRED": "Authentication required.", - "SOMETHING_WENT_WRONG": "Something went wrong.", - "PROJECT_DOES_NOT_SUPPORT_THIS_TOKEN": "Project doesnt support this token", - "THERE_IS_NO_RECIPIENT_ADDRESS_FOR_THIS_NETWORK_ID_AND_PROJECT": "There is no recipient address for this project and networkId", - "AMOUNT_IS_INVALID": "Amount is not valid", - "CURRENCY_IS_INVALID": "Currency is not valid", - "SHOULD_HAVE_AT_LEAST_ONE_CONNECTED_SOCIAL_NETWORK_BEFORE_SUBMIT": "Should have one connected social network before submit", - "SOCIAL_PROFILE_IS_ALREADY_VERIFIED": "Social profile is already verified", - "YOU_ARE_NOT_THE_OWNER_OF_THIS_SOCIAL_PROFILE": "You are not the owner of social profile", - "ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE": "Error in getting accessToken by authorization code", - "REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST": "There is not any category with name registered-non-profits, probably you forgot to run migrations", - "PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED": "Content length exceeded", - "INVALID_TOKEN_ADDRESS": "Invalid tokenAddress", - "Project doesnt have recipient address on this network": "Project doesnt have recipient address on this network", - "DRAFT_DONATION_DISABLED": "Draft donation is disabled", - "EVM_SUPPORT_ONLY": "Only EVM support", - "INVALID_PROJECT_ID": "INVALID_PROJECT_ID", - "TX_NOT_FOUND": "TX_NOT_FOUND", - - "NO_EMAIL_PROVIDED": "No email address provided.", - "INCORRECT_CODE": "The verification code you entered is incorrect." -} \ No newline at end of file + "GITCOIN_ERROR_FETCHING_DATA": "Unable to fetch gitcoin data", + "CHAINVINE_CLICK_EVENT_ERROR": "Unable to register click event or link donor", + "CHAINVINE_REGISTRATION_ERROR": "Chainvine ID failed to be generated for the user", + "FIAT_DONATION_ALREADY_EXISTS": "Fiat donation already exists", + "UPLOAD_FAILED": "Upload file failed", + "CHANGE_API_INVALID_TITLE_OR_EIN": "ChangeAPI title or EIN not found or invalid", + "INVALID_SOCIAL_NETWORK": "Invalid social network", + "RECIPIENT_ADDRESSES_CANT_BE_EMPTY": "Recipient addresses can't be empty", + "NOT_IMPLEMENTED": "Not implemented", + "SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID": "Should send at least on of userId or projectId", + "YOU_JUST_CAN_VERIFY_REJECTED_AND_SUBMITTED_FORMS": "You just can verify rejected and submitted forms", + "YOU_JUST_CAN_MAKE_DRAFT_REJECTED_AND_SUBMITTED_FORMS": "You just can make draft rejected and submitted forms", + "YOU_JUST_CAN_REJECT_SUBMITTED_FORMS": "You just can reject submitted forms", + "INVALID_TRACK_ID_FOR_OAUTH2_LOGIN": "Invalid trackId for oauth2 login", + "SOCIAL_NETWORK_IS_DIFFERENT_WITH_CLAIMED_ONE": "Social network is different with claimed one", + "SOCIAL_PROFILE_NOT_FOUND": "Social profile not gound", + "CHANGE_API_TITLE_OR_EIN_NOT_PRECISE": "Please query the exact project title or EIN ID from the ChangeAPI site", + "YOU_ARE_NOT_OWNER_OF_THIS_DONATION": "You are not owner of this donation", + "NOT_SUPPORTED_THIRD_PARTY_API": "Third Party API not supported", + "IPFS_IMAGE_UPLOAD_FAILED": "Image upload failed", + "YOU_SHOULD_FILL_EMAIL_PERSONAL_INFO_BEFORE_CONFIRMING_EMAIL": "You should fill email in personal info step before confirming it", + "YOU_ALREADY_VERIFIED_THIS_EMAIL": "You already verified this email", + "INVALID_FROM_DATE": "Invalid fromDate", + "INVALID_TO_DATE": "Invalid toDate", + "VERIFIED_USERNAME_IS_DIFFERENT_WITH_CLAIMED_ONE": "Username is not the claimed one", + "INVALID_AUTHORIZATION_VERSION": "Authorization version is not valid", + "INVALID_STEP": "Invalid step", + "DONOR_REPORTED_IT_AS_FAILED": "Donor reported it as failed", + "INVALID_DATE_FORMAT": "Date format should be YYYYMMDD HH:mm:ss", + "INTERNAL_SERVER_ERROR": "Internal server error", + "ERROR_CONNECTING_DB": "Error in connecting DB", + "YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT": "You dont have access to view this project", + "JUST_ACTIVE_PROJECTS_ACCEPT_DONATION": "Just active projects accept donation", + "CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE": "Please select no more than 5 categories", + "CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION": "This category is not valid", + "INVALID_TX_HASH": "Invalid txHash", + "INVALID_TRANSACTION_ID": "Invalid transactionId", + "DUPLICATE_TX_HASH": "There is a donation with this txHash in our DB", + "YOU_ARE_NOT_THE_OWNER_OF_PROJECT": "You are not the owner of this project.", + "YOU_ARE_NOT_THE_OWNER_OF_PROJECT_VERIFICATION_FORM": "You are not the owner of this project verification form.", + "YOU_ARE_NOT_THE_OWNER_OF_SOCIAL_PROFILE": "You are not the owner of this social profile project verification form.", + "PROJECT_VERIFICATION_FORM_IS_NOT_DRAFT_SO_YOU_CANT_MODIFY_SOCIAL_PROFILES": "project verification form is not draft, so you cant modify social profiles", + "YOU_ALREADY_ADDED_THIS_SOCIAL_PROFILE_FOR_THIS_VERIFICATION_FORM": "You already have added this social profile for this verification form", + "PROJECT_VERIFICATION_FORM_NOT_FOUND": "Project verification form not found", + "PROJECT_IS_ALREADY_VERIFIED": "Project is already verified.", + "YOU_JUST_CAN_EDIT_DRAFT_REQUESTS": "Project is already verified.", + "EMAIL_CONFIRMATION_CANNOT_BE_SENT_IN_THIS_STEP": "Email confirmation cannot be sent in this step", + "THERE_IS_AN_ONGOING_VERIFICATION_REQUEST_FOR_THIS_PROJECT": "There is an ongoing project verification request for this project", + "THERE_IS_NOT_ANY_ONGOING_PROJECT_VERIFICATION_FORM_FOR_THIS_PROJECT": "There is not any project verification form for this project", + "PROJECT_STATUS_NOT_FOUND": "No project status found, this should be impossible", + "YOU_DONT_HAVE_ACCESS_TO_DEACTIVATE_THIS_PROJECT": "You dont have access to deactivate this project", + "PROJECT_NOT_FOUND": "Project not found.", + "PROJECT_IS_NOT_ACTIVE": "Project is not active.", + "INVALID_FUNCTION": "Invalid function name of transaction", + "PROJECT_UPDATE_NOT_FOUND": "Project update not found.", + "DONATION_NOT_FOUND": "donation not found", + "THIS_PROJECT_IS_CANCELLED_OR_DEACTIVATED_ALREADY": "This project has been cancelled by an Admin for inappropriate content or a violation of the Terms of Use", + "DONATION_VIEWING_LOGIN_REQUIRED": "You must be signed-in in order to register project donations", + "TRANSACTION_NOT_FOUND": "Transaction not found.", + "TRANSACTION_FROM_ADDRESS_IS_DIFFERENT_FROM_SENT_FROM_ADDRESS": "FromAddress of Transaction is different from sent fromAddress", + "TRANSACTION_STATUS_IS_FAILED_IN_NETWORK": "Transaction status is failed in network", + "INVALID_VERIFICATION_REVOKE_STATUS": "Invalid revoke status updated", + "TRANSACTION_NOT_FOUND_AND_NONCE_IS_USED": "Transaction not found and nonce is used", + "TRANSACTION_AMOUNT_IS_DIFFERENT_WITH_SENT_AMOUNT": "Transaction amount is different with sent amount", + "TRANSACTION_CANT_BE_OLDER_THAN_DONATION": "Transaction can not be older than donation", + "TRANSACTION_TO_ADDRESS_IS_DIFFERENT_FROM_SENT_TO_ADDRESS": "ToAddress of Transaction is different to sent toAddress", + "TRANSACTION_SMART_CONTRACT_CONFLICTS_WITH_CURRENCY": "Smart contract address is not equal to transaction.to", + "USER_NOT_FOUND": "User not found.", + "INVALID_NETWORK_ID": "Network Id is invalid", + "INVALID_TOKEN_SYMBOL": "Token symbol is invalid", + "TOKEN_SYMBOL_IS_REQUIRED": "Token symbol is required", + "TOKEN_NOT_FOUND": "Token Not found", + "TRANSACTION_NOT_FOUNT_IN_USER_HISTORY": "TRANSACTION_NOT_FOUNT_IN_USER_HISTORY", + "TRANSACTION_WITH_THIS_NONCE_IS_NOT_MINED_ALREADY": "Transaction with this nonce is not mined already", + "TO_ADDRESS_OF_DONATION_SHOULD_BE_PROJECT_WALLET_ADDRESS": "toAddress of donation should be equal to project wallet address", + "INVALID_WALLET_ADDRESS": "Address not valid", + "INVALID_EMAIL": "Email not valid", + "UN_AUTHORIZED": "unAuthorized", + "BOTH_FIRST_NAME_AND_LAST_NAME_CANT_BE_EMPTY": "Both firstName and lastName cant be empty", + "FIRSTNAME_CANT_BE_EMPTY_STRING": "firstName cant be empty string", + "LASTNAME_CANT_BE_EMPTY_STRING": "lastName cant be empty string", + "PROJECT_WITH_THIS_TITLE_EXISTS": "There is a project with this title, please use another title", + "INVALID_PROJECT_TITLE": "Your project name isnt valid, please only use letters and numbers", + "ACCESS_DENIED": "Access denied", + "AUTHENTICATION_REQUIRED": "Authentication required.", + "SOMETHING_WENT_WRONG": "Something went wrong.", + "PROJECT_DOES_NOT_SUPPORT_THIS_TOKEN": "Project doesnt support this token", + "THERE_IS_NO_RECIPIENT_ADDRESS_FOR_THIS_NETWORK_ID_AND_PROJECT": "There is no recipient address for this project and networkId", + "AMOUNT_IS_INVALID": "Amount is not valid", + "CURRENCY_IS_INVALID": "Currency is not valid", + "SHOULD_HAVE_AT_LEAST_ONE_CONNECTED_SOCIAL_NETWORK_BEFORE_SUBMIT": "Should have one connected social network before submit", + "SOCIAL_PROFILE_IS_ALREADY_VERIFIED": "Social profile is already verified", + "YOU_ARE_NOT_THE_OWNER_OF_THIS_SOCIAL_PROFILE": "You are not the owner of social profile", + "ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE": "Error in getting accessToken by authorization code", + "REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST": "There is not any category with name registered-non-profits, probably you forgot to run migrations", + "PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED": "Content length exceeded", + "INVALID_TOKEN_ADDRESS": "Invalid tokenAddress", + "Project doesnt have recipient address on this network": "Project doesnt have recipient address on this network", + "DRAFT_DONATION_DISABLED": "Draft donation is disabled", + "EVM_SUPPORT_ONLY": "Only EVM support", + "INVALID_PROJECT_ID": "INVALID_PROJECT_ID", + "TX_NOT_FOUND": "TX_NOT_FOUND", + "ABC_NOT_FOUND": "Abc not found", + "NO_EMAIL_PROVIDED": "No email address provided.", + "INCORRECT_CODE": "The verification code you entered is incorrect." +} diff --git a/src/utils/locales/es.json b/src/utils/locales/es.json index d00214f3d..9d44eec2e 100644 --- a/src/utils/locales/es.json +++ b/src/utils/locales/es.json @@ -98,7 +98,7 @@ "PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED": "El contenido es demasiado largo", "DRAFT_DONATION_DISABLED": "El borrador de donación está deshabilitado", "EVM_SUPPORT_ONLY": "Solo se admite EVM", - - "NO_EMAIL_PROVIDED": "No se ha proporcionado una dirección de correo electrónico.", + "ABC_NOT_FOUND": "ABC no encontrado", + "NO_EMAIL_PROVIDED": "No se ha proporcionado una dirección de correo electrónico." "INCORRECT_CODE": "El código de verificación que ingresaste es incorrecto." } diff --git a/test/graphqlQueries.ts b/test/graphqlQueries.ts index cbaf802c3..89826c1cc 100644 --- a/test/graphqlQueries.ts +++ b/test/graphqlQueries.ts @@ -123,6 +123,21 @@ export const createProjectQuery = ` email walletAddress } + teamMembers { + name + image + twitter + linkedin + farcaster + } + abc { + tokenName + tokenTicker + issuanceTokenAddress + icon + orchestratorAddress + projectAddress + } } } `;