diff --git a/app/admin/quests/create/page.tsx b/app/admin/quests/create/page.tsx index 1173cac1..a5c4cc54 100644 --- a/app/admin/quests/create/page.tsx +++ b/app/admin/quests/create/page.tsx @@ -369,7 +369,7 @@ export default function Page() { } catch (error) { console.error("Error while creating balance task:", error); } - }else if (step.type === "CustomApi") { + } else if (step.type === "CustomApi") { try { await AdminService.createCustomApi({ quest_id: questId, @@ -383,6 +383,21 @@ export default function Page() { } catch (error) { console.error("Error while creating balance task:", error); } + + } else if (step.type === "Contract") { + try { + await AdminService.createContract({ + quest_id: questId, + name: step.data.contract_name, + desc: step.data.contract_desc, + href: step.data.contract_href, + cta: step.data.contract_cta, + calls: JSON.parse(step.data.contract_calls), + }); + } catch (error) { + console.error("Error while creating contract task:", error); + showNotification(`Error adding ${step.type} task: ${error}`, "error"); + } } }); setButtonLoading(false); @@ -480,7 +495,7 @@ export default function Page() { {}} + setShowDomainPopup={() => { }} hasRootDomain={false} rewardButtonTitle={finalQuestData.disabled ? "Enable" : "Disable"} onRewardButtonClick={async () => { diff --git a/app/admin/quests/dashboard/[questId]/page.tsx b/app/admin/quests/dashboard/[questId]/page.tsx index 79258d6e..8593af42 100644 --- a/app/admin/quests/dashboard/[questId]/page.tsx +++ b/app/admin/quests/dashboard/[questId]/page.tsx @@ -46,6 +46,7 @@ type StepMap = | { type: "Custom"; data: WithNewField } | { type: "Domain"; data: WithNewField } | { type: "Balance"; data: WithNewField } + | { type: "Contract"; data: WithNewField } | { type: "CustomApi"; data: WithNewField } | { type: "None"; data: object }; @@ -216,6 +217,18 @@ export default function Page({ params }: QuestIdProps) { balance_href: task.href, }, }; + } else if (task.task_type === "contract") { + return { + type: "Contract", + data: { + id: task.id, + contract_name: task.name, + contract_desc: task.desc, + contract_href: task.href, + contract_cta: task.cta, + contract_calls: task.calls, + }, + }; } else if(task.task_type === "custom_api"){ return { type: "CustomApi", @@ -524,6 +537,15 @@ export default function Page({ params }: QuestIdProps) { cta: step.data.balance_cta, href: step.data.balance_href, }); + } else if (step.type === "Contract") { + await AdminService.createContract({ + quest_id: questId.current, + name: step.data.contract_name, + desc: step.data.contract_desc, + href: step.data.contract_href, + cta: step.data.contract_cta, + calls: JSON.parse(step.data.contract_calls), + }); } else if(step.type === "CustomApi"){ await AdminService.createCustomApi({ @@ -537,7 +559,7 @@ export default function Page({ params }: QuestIdProps) { }) } } catch (error) { - console.error(`Error adding task of type ${step.type}:`, error); + showNotification(`Error adding ${step.type} task: ${error}`, "error"); } } }, []); @@ -633,6 +655,19 @@ export default function Page({ params }: QuestIdProps) { cta: step.data.balance_cta, href: step.data.balance_href, }); + } else if (step.type === "Contract") { + try { + await AdminService.updateContract({ + id: step.data.id, + name: step.data.contract_name, + desc: step.data.contract_desc, + href: step.data.contract_href, + cta: step.data.contract_cta, + calls: JSON.parse(step.data.contract_calls), + }); + } catch (error) { + showNotification(`Error updating ${step.type} task: ${error}`, "error"); + } } else if (step.type === "CustomApi") { await AdminService.updateCustomApi({ id: step.data.id, @@ -782,7 +817,7 @@ export default function Page({ params }: QuestIdProps) { {}} + setShowDomainPopup={() => { }} hasRootDomain={false} rewardButtonTitle={questData.disabled ? "Enable" : "Disable"} onRewardButtonClick={async () => { diff --git a/components/admin/formSteps/TaskDetailsForm.tsx b/components/admin/formSteps/TaskDetailsForm.tsx index 99f919f6..266c25ea 100644 --- a/components/admin/formSteps/TaskDetailsForm.tsx +++ b/components/admin/formSteps/TaskDetailsForm.tsx @@ -15,6 +15,7 @@ import DomainStep from "../taskSteps/domainStep"; import Typography from "@components/UI/typography/typography"; import { TEXT_TYPE } from "@constants/typography"; import BalanceStep from "../taskSteps/balanceStep"; +import ContractStep from "../taskSteps/contractStep"; import CustomApiStep from "../taskSteps/customApiStep" type TaskDetailsFormProps = { @@ -105,8 +106,16 @@ const TaskDetailsForm: FunctionComponent = ({ step={step} /> ); - } else if(step?.type === "CustomApi"){ - return( + } else if (step?.type === "Contract") { + return ( + + ); + } else if (step?.type === "CustomApi") { + return ( = ({ ] as TaskType, data: getDefaultValues( TWITTER_OPTIONS[ - category as keyof typeof TWITTER_OPTIONS + category as keyof typeof TWITTER_OPTIONS ] as TaskType ), }; @@ -220,12 +229,12 @@ const TaskDetailsForm: FunctionComponent = ({ cursor: "pointer", backgroundColor: steps[currentTask]?.type === - TWITTER_OPTIONS[category as keyof typeof TWITTER_OPTIONS] + TWITTER_OPTIONS[category as keyof typeof TWITTER_OPTIONS] ? "#ffffff" : "#29282B", color: steps[currentTask]?.type === - TWITTER_OPTIONS[category as keyof typeof TWITTER_OPTIONS] + TWITTER_OPTIONS[category as keyof typeof TWITTER_OPTIONS] ? "#29282B" : "#ffffff", }} diff --git a/components/admin/taskSteps/contractStep.tsx b/components/admin/taskSteps/contractStep.tsx new file mode 100644 index 00000000..7fc48db8 --- /dev/null +++ b/components/admin/taskSteps/contractStep.tsx @@ -0,0 +1,61 @@ +import React, { FunctionComponent } from "react"; +import TextInput from "../textInput"; + +type ContractStepProps = { + handleTasksInputChange: ( + e: React.ChangeEvent, + index: number + ) => void; + step: StepMap; + index: number; +}; + +const ContractStep: FunctionComponent = ({ + handleTasksInputChange, + step, + index, +}) => { + return ( +
+ handleTasksInputChange(e, index)} + value={step.data.contract_name || ""} + name="contract_name" + label="Name" + placeholder="Name" + /> + handleTasksInputChange(e, index)} + value={step.data.contract_desc || ""} + name="contract_desc" + label="Description" + placeholder="Description" + multiline={4} + /> + handleTasksInputChange(e, index)} + value={step.data.contract_href || ""} + name="contract_href" + label="URL" + placeholder="URL" + /> + handleTasksInputChange(e, index)} + value={step.data.contract_cta || ""} + name="contract_cta" + label="CTA" + placeholder="CTA" + /> + handleTasksInputChange(e, index)} + value={step.data.contract_calls || ""} + name="contract_calls" + label="Calls (JSON)" + placeholder='e.g.: [{ "contract": "0x...", "entry_point": "transfer", "call_data": ["0x..."], "regex": "..." }]' + multiline={4} + /> +
+ ); +}; + +export default ContractStep; diff --git a/constants/admin.ts b/constants/admin.ts index 9637e328..5539d4e6 100644 --- a/constants/admin.ts +++ b/constants/admin.ts @@ -18,6 +18,7 @@ export const TASK_OPTIONS = [ "Custom", "Domain", "Balance", + "Contract", "CustomApi" ]; @@ -116,6 +117,13 @@ export const BalanceInput = { balance_href: "", }; +export const ContractInput = { + contract_name: "", + contract_desc: "", + contract_href: "", + contract_cta: "", + contract_calls: "", +}; export const CustomApiInput = { api_name: "", api_desc: "", @@ -132,6 +140,8 @@ export const getDefaultValues = (type: TaskType) => { if (type === "Discord") return DiscordInput; if (type === "Custom") return CustomInput; if (type === "Domain") return DomainInput; + if (type === "Balance") return BalanceInput; + if (type === "Contract") return ContractInput; if (type === "CustomApi") return CustomApiInput; if (type === "None") return {}; diff --git a/services/authService.ts b/services/authService.ts index 8e4547f8..7db24d34 100644 --- a/services/authService.ts +++ b/services/authService.ts @@ -22,6 +22,8 @@ import { AddUser, CreateBalance, UpdateBalance, + CreateContract, + UpdateContract, CreateCustomApi, UpdateCustomApi, } from "../types/backTypes"; @@ -356,6 +358,38 @@ const updateBalance = async (params: UpdateBalance) => { } }; +const createContract = async (params: CreateContract) => { + try { + const response = await fetch(`${baseurl}/admin/tasks/contract/create`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + body: JSON.stringify(params), + }); + return await response.json(); + } catch (err) { + console.log("Error while creating contract task", err); + } +}; + +const updateContract = async (params: UpdateContract) => { + try { + const response = await fetch(`${baseurl}/admin/tasks/contract/update`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + body: JSON.stringify(params), + }); + return await response.json(); + } catch (err) { + console.log("Error while updating contract task", err); + } +}; + const createCustomApi = async (params: CreateCustomApi) => { try{ const response = await fetch(`${baseurl}/admin/tasks/custom_api/create`, { @@ -549,6 +583,8 @@ export const AdminService = { createCustom, createBalance, updateBalance, + createContract, + updateContract, createQuiz, createQuizQuestion, deleteTask, diff --git a/types/backTypes.d.ts b/types/backTypes.d.ts index 579b6278..f3d0faa0 100644 --- a/types/backTypes.d.ts +++ b/types/backTypes.d.ts @@ -50,6 +50,7 @@ type UserTask = { task_type: string | null; discord_guild_id: string | null; contracts: string[] | null; + calls: object | null; }; type UserDocument = { @@ -404,6 +405,24 @@ export type UpdateBalance = { href?: string; }; +export type CreateContract = { + quest_id: number; + name: string; + desc: string; + href: string; + cta: string; + calls: object; +}; + +export type UpdateContract = { + id: number; + name?: string; + desc?: string; + href?: string; + cta?: string; + calls?: object; +}; + export type UpdateCustom = { id: number; name?: string; diff --git a/types/frontTypes.d.ts b/types/frontTypes.d.ts index 54c1618e..79f4b368 100644 --- a/types/frontTypes.d.ts +++ b/types/frontTypes.d.ts @@ -316,7 +316,8 @@ type StepMap = | { type: "None"; data: object } | { type: "Domain"; data: DomainInputType } | { type: "Balance"; data: BalanceInputType } - | { type: "CustomApi"; data: CustomApiInputType }; + | { type: "Contract"; data: ContractInputType } + | { type: "CustomApi"; data: CustomApiInputType } type CustomInputType = typeof CustomInput; type DiscordInputType = typeof DiscordInput; @@ -325,6 +326,7 @@ type QuizInputType = typeof QuizDefaultInput; type TwitterFwInputType = typeof TwitterFwInput; type TwitterRwInputType = typeof TwitterRwInput; type BalanceInputType = typeof BalanceInput; +type ContractInputType = typeof ContractInput; type CustomApiInputType = typeof CustomApiInput; type TaskType = | "Quiz" @@ -334,6 +336,7 @@ type TaskType = | "TwitterRw" | "Domain" | "Balance" + | "Contract" | "CustomApi" | "None";