Skip to content

Commit acd279c

Browse files
committed
feat: intergrate the comment api
1 parent 3f4e06d commit acd279c

1 file changed

Lines changed: 169 additions & 26 deletions

File tree

  • components/landing-page/project/CreateProjectModal

components/landing-page/project/CreateProjectModal/index.tsx

Lines changed: 169 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,18 @@ import Team, { TeamFormData } from './Team';
99
import Contact, { ContactFormData } from './Contact';
1010
import LoadingScreen from './LoadingScreen';
1111
import SuccessScreen from './SuccessScreen';
12+
import TransactionSigningScreen from './TransactionSigningScreen';
1213
import { z } from 'zod';
13-
import { createCrowdfundingProject } from '@/lib/api/project';
14-
import { CreateCrowdfundingProjectRequest } from '@/lib/api/types';
14+
import {
15+
prepareCrowdfundingProject,
16+
confirmCrowdfundingProject,
17+
} from '@/lib/api/project';
18+
import {
19+
CreateCrowdfundingProjectRequest,
20+
PrepareCrowdfundingProjectResponse,
21+
} from '@/lib/api/types';
22+
import { useWalletSigning } from '@/hooks/use-wallet';
23+
import { useWalletProtection } from '@/hooks/use-wallet-protection';
1524

1625
type StepHandle = { validate: () => boolean; markSubmitted?: () => void };
1726

@@ -34,6 +43,18 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
3443
const [submitErrors, setSubmitErrors] = useState<string[]>([]);
3544
const [showSuccess, setShowSuccess] = useState(false);
3645
const [isLoading, setIsLoading] = useState(false);
46+
const [unsignedTransaction, setUnsignedTransaction] = useState<string | null>(
47+
null
48+
);
49+
const [isSigningTransaction, setIsSigningTransaction] = useState(false);
50+
51+
// New state for two-step flow
52+
const [preparedData, setPreparedData] = useState<
53+
PrepareCrowdfundingProjectResponse['data'] | null
54+
>(null);
55+
const [flowStep, setFlowStep] = useState<
56+
'form' | 'preparing' | 'signing' | 'confirming' | 'success'
57+
>('form');
3758

3859
// Form data state
3960
const [formData, setFormData] = useState<ProjectFormData>({
@@ -56,6 +77,12 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
5677
// Ref for the scrollable content container
5778
const contentRef = useRef<HTMLDivElement>(null);
5879

80+
// Wallet signing hooks
81+
const { signTransaction } = useWalletSigning();
82+
const { requireWallet } = useWalletProtection({
83+
actionName: 'sign project creation transaction',
84+
});
85+
5986
// Reset scroll position when step changes with smooth transition
6087
useEffect(() => {
6188
if (currentStep === 1) return; // Skip for initial load
@@ -209,15 +236,83 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
209236
backup: contact.backupContact || '',
210237
},
211238
socialLinks: apiSocialLinks,
239+
signer: 'GD4NCQMLAU5Z7HIMNGMKLSFLCA46AILVIB6FJZ7QEJXANFNGBHFR24H6',
212240
};
213241
};
214242

243+
const handleRetry = () => {
244+
setSubmitErrors([]);
245+
setFlowStep('signing');
246+
};
247+
248+
const handleSignTransaction = async () => {
249+
if (!unsignedTransaction || !preparedData) {
250+
setSubmitErrors(['No transaction to sign or prepared data missing']);
251+
return;
252+
}
253+
254+
requireWallet(async () => {
255+
setIsSigningTransaction(true);
256+
setFlowStep('confirming');
257+
258+
try {
259+
// Sign the transaction using the wallet
260+
const signedXdr = await signTransaction(unsignedTransaction);
261+
262+
// Submit the signed transaction to the backend (Step 2)
263+
const confirmResponse = await confirmCrowdfundingProject({
264+
signedXdr,
265+
escrowAddress: preparedData.escrowAddress,
266+
projectData: preparedData.projectData,
267+
mappedMilestones: preparedData.mappedMilestones,
268+
mappedTeam: preparedData.mappedTeam,
269+
});
270+
271+
if (confirmResponse.success) {
272+
// Project created successfully
273+
setFlowStep('success');
274+
setShowSuccess(true);
275+
setIsSigningTransaction(false);
276+
setIsSubmitting(false);
277+
} else {
278+
throw new Error(
279+
confirmResponse.message || 'Failed to create project'
280+
);
281+
}
282+
} catch (error) {
283+
let errorMessage = 'Failed to sign transaction. Please try again.';
284+
285+
if (error instanceof Error) {
286+
if (error.message.includes('User rejected')) {
287+
errorMessage =
288+
'Transaction signing was cancelled. Please try again.';
289+
} else if (error.message.includes('Invalid transaction')) {
290+
errorMessage =
291+
'Invalid transaction format. Please contact support.';
292+
} else if (error.message.includes('Network')) {
293+
errorMessage =
294+
'Network error. Please check your connection and try again.';
295+
} else if (error.message.includes('Wallet not connected')) {
296+
errorMessage =
297+
'Wallet is not connected. Please reconnect your wallet.';
298+
} else {
299+
errorMessage = error.message;
300+
}
301+
}
302+
303+
setSubmitErrors([errorMessage]);
304+
setIsSigningTransaction(false);
305+
setFlowStep('signing');
306+
}
307+
});
308+
};
309+
215310
const handleSubmit = async () => {
216311
setIsSubmitting(true);
217312
setIsLoading(true);
313+
setFlowStep('preparing');
218314

219315
try {
220-
// Validate full payload with master schema before submitting
221316
const milestoneSchema = z
222317
.object({
223318
id: z.string().optional(),
@@ -301,6 +396,7 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
301396
parsed.error.issues.map(i => `${i.path.join('.')} - ${i.message}`)
302397
);
303398
setIsSubmitting(false);
399+
setFlowStep('form');
304400
return;
305401
}
306402

@@ -309,25 +405,42 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
309405
// Map form data to API request format
310406
const apiRequest = mapFormDataToApiRequest(payload);
311407

312-
// Call the API
313-
const response = await createCrowdfundingProject(apiRequest);
408+
// Step 1: Prepare project and get unsigned transaction
409+
const prepareResponse = await prepareCrowdfundingProject(apiRequest);
314410

315-
if (response.success) {
316-
// Show success modal
411+
if (prepareResponse.success) {
412+
// Store prepared data for step 2
413+
setPreparedData(prepareResponse.data);
414+
setUnsignedTransaction(prepareResponse.data.unsignedXdr);
415+
setFlowStep('signing');
317416
setIsLoading(false);
318-
setShowSuccess(true);
319-
setIsSubmitting(false);
320417
} else {
321-
throw new Error(response.message || 'Failed to create project');
418+
throw new Error(prepareResponse.message || 'Failed to prepare project');
322419
}
323420
} catch (error) {
324-
setSubmitErrors([
325-
error instanceof Error
326-
? error.message
327-
: 'Error submitting project. Please try again.',
328-
]);
421+
let errorMessage = 'Error preparing project. Please try again.';
422+
423+
if (error instanceof Error) {
424+
if (error.message.includes('Network')) {
425+
errorMessage =
426+
'Network error. Please check your connection and try again.';
427+
} else if (error.message.includes('Validation')) {
428+
errorMessage =
429+
'Project validation failed. Please check your project details.';
430+
} else if (error.message.includes('Unauthorized')) {
431+
errorMessage =
432+
'Authentication required. Please log in and try again.';
433+
} else if (error.message.includes('Server')) {
434+
errorMessage = 'Server error. Please try again in a few moments.';
435+
} else {
436+
errorMessage = error.message;
437+
}
438+
}
439+
440+
setSubmitErrors([errorMessage]);
329441
setIsLoading(false);
330442
setIsSubmitting(false);
443+
setFlowStep('form');
331444
}
332445
};
333446

@@ -377,6 +490,11 @@ const CreateProjectModal = ({ open, setOpen }: CreateProjectModalProps) => {
377490
setCurrentStep(1);
378491
setShowSuccess(false);
379492
setIsLoading(false);
493+
setUnsignedTransaction(null);
494+
setIsSigningTransaction(false);
495+
setSubmitErrors([]);
496+
setPreparedData(null);
497+
setFlowStep('form');
380498
setOpen(false);
381499
};
382500

@@ -429,24 +547,24 @@ Our development is structured in clear phases with measurable milestones and com
429547
title: 'Smart Contract Development',
430548
description:
431549
'Develop and audit core smart contracts for yield farming and automated market making. This includes the main protocol contracts, token contracts, and governance mechanisms.',
432-
startDate: '2024-02-01',
433-
endDate: '2024-04-30',
550+
startDate: '2026-02-01',
551+
endDate: '2026-04-30',
434552
},
435553
{
436554
id: 'milestone-2',
437555
title: 'Frontend Development',
438556
description:
439557
'Build a user-friendly web interface for interacting with the protocol. This includes dashboard, yield farming interface, and portfolio management tools.',
440-
startDate: '2024-03-01',
441-
endDate: '2024-05-31',
558+
startDate: '2026-05-01',
559+
endDate: '2026-06-30',
442560
},
443561
{
444562
id: 'milestone-3',
445563
title: 'Security Audit & Testing',
446564
description:
447565
'Conduct comprehensive security audits, penetration testing, and bug bounty programs to ensure the protocol is secure and ready for mainnet launch.',
448-
startDate: '2024-05-01',
449-
endDate: '2024-07-31',
566+
startDate: '2026-07-01',
567+
endDate: '2026-08-31',
450568
},
451569
],
452570
},
@@ -482,12 +600,37 @@ Our development is structured in clear phases with measurable milestones and com
482600
};
483601

484602
const renderStepContent = () => {
485-
if (isLoading) {
603+
// Handle the new two-step flow states
604+
if (flowStep === 'preparing' || isLoading) {
486605
return <LoadingScreen />;
487606
}
488-
if (showSuccess) {
607+
if (flowStep === 'success' || showSuccess) {
489608
return <SuccessScreen onContinue={handleReset} />;
490609
}
610+
if (
611+
flowStep === 'signing' &&
612+
unsignedTransaction &&
613+
!isSigningTransaction
614+
) {
615+
return (
616+
<TransactionSigningScreen
617+
onSign={handleSignTransaction}
618+
flowStep='signing'
619+
onRetry={handleRetry}
620+
hasError={submitErrors.length > 0}
621+
errorMessage={submitErrors[0]}
622+
/>
623+
);
624+
}
625+
if (flowStep === 'confirming' || isSigningTransaction) {
626+
return (
627+
<TransactionSigningScreen
628+
onSign={handleSignTransaction}
629+
isSigning={true}
630+
flowStep='confirming'
631+
/>
632+
);
633+
}
491634

492635
switch (currentStep) {
493636
case 1:
@@ -547,7 +690,7 @@ Our development is structured in clear phases with measurable milestones and com
547690
open={open}
548691
setOpen={setOpen}
549692
>
550-
{!(showSuccess || isLoading) && (
693+
{flowStep === 'form' && (
551694
<Header
552695
currentStep={currentStep}
553696
onBack={handleBack}
@@ -558,7 +701,7 @@ Our development is structured in clear phases with measurable milestones and com
558701
ref={contentRef}
559702
className={`min-h-[calc(55vh)] px-4 transition-opacity duration-100 md:px-[50px] lg:px-[75px] xl:px-[150px]`}
560703
>
561-
{showSuccess || isLoading ? (
704+
{flowStep !== 'form' ? (
562705
<div className='flex h-full items-center justify-center'>
563706
{renderStepContent()}
564707
</div>
@@ -582,7 +725,7 @@ Our development is structured in clear phases with measurable milestones and com
582725
</>
583726
)}
584727
</div>
585-
{!(showSuccess || isLoading) && (
728+
{flowStep === 'form' && (
586729
<Footer
587730
currentStep={currentStep}
588731
onContinue={handleContinue}

0 commit comments

Comments
 (0)