Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement user dashboard and fixes linting errors #110

Merged
merged 3 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 113 additions & 112 deletions apps/web/app/api/escrow/initialize/route.ts
Original file line number Diff line number Diff line change
@@ -1,128 +1,129 @@
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { createClient } from "@supabase/supabase-js";
import { validateEscrowInitialization } from "~/lib/validators/escrow";
import { initializeEscrowContract } from "~/lib/stellar/escrow";
import type { EscrowInitialization } from "~/lib/types/escrow";
import { AppError } from "~/lib/errors";
import { createClient } from '@supabase/supabase-js'
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { AppError } from '~/lib/errors'
import { initializeEscrowContract } from '~/lib/stellar/escrow'
import type { EscrowInitialization } from '~/lib/types/escrow'
import { validateEscrowInitialization } from '~/lib/validators/escrow'

const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY

if (!supabaseUrl || !supabaseServiceKey) {
throw new Error('Missing Supabase environment variables')
}

const supabase = createClient(supabaseUrl, supabaseServiceKey)

export async function POST(req: NextRequest) {
try {
const initializationData: EscrowInitialization = await req.json();
const validationResult =
validateEscrowInitialization(initializationData);
try {
const initializationData: EscrowInitialization = await req.json()
const validationResult = validateEscrowInitialization(initializationData)

if (!validationResult.success) {
return NextResponse.json(
{
error: "Invalid escrow initialization",
details: validationResult.errors
},
{ status: 400 }
);
}
if (!validationResult.success) {
return NextResponse.json(
{
error: 'Invalid escrow initialization',
details: validationResult.errors,
},
{ status: 400 },
)
}

const contractResult = await initializeEscrowContract(
initializationData.contractParams,
initializationData.contractParams.parties.payerSecretKey
);
const contractResult = await initializeEscrowContract(
initializationData.contractParams,
initializationData.contractParams.parties.payerSecretKey,
)

if (!contractResult.success) {
return NextResponse.json(
{
error: "Failed to initialize escrow contract",
details: contractResult.error
},
{ status: 500 }
);
}
if (!contractResult.success) {
return NextResponse.json(
{
error: 'Failed to initialize escrow contract',
details: contractResult.error,
},
{ status: 500 },
)
}

const { data: dbResult, error: dbError } = await supabase
.from("escrow_contracts")
.insert({
engagement_id: contractResult.engagementId,
contract_id: contractResult.contractAddress,
project_id: initializationData.metadata.projectId,
contribution_id: contractResult.contributionId,
payer_address: initializationData.contractParams.parties.payer,
receiver_address:
initializationData.contractParams.parties.receiver,
amount: contractResult.totalAmount,
platform_fee: initializationData.contractParams.platformFee,
current_state: "PENDING",
metadata: initializationData.metadata
})
.select("id")
.single();
const { data: dbResult, error: dbError } = await supabase
.from('escrow_contracts')
.insert({
engagement_id: contractResult.engagementId,
contract_id: contractResult.contractAddress,
project_id: initializationData.metadata.projectId,
contribution_id: contractResult.contributionId,
payer_address: initializationData.contractParams.parties.payer,
receiver_address: initializationData.contractParams.parties.receiver,
amount: contractResult.totalAmount,
platform_fee: initializationData.contractParams.platformFee,
current_state: 'PENDING',
metadata: initializationData.metadata,
})
.select('id')
.single()

if (dbError) {
return NextResponse.json(
{
error: "Failed to track escrow contract",
details: dbError
},
{ status: 500 }
);
}
if (dbError) {
return NextResponse.json(
{
error: 'Failed to track escrow contract',
details: dbError,
},
{ status: 500 },
)
}

// If successful, update the state to INITIALIZED
await supabase
.from("escrow_contracts")
.update({ current_state: "INITIALIZED" })
.eq("id", dbResult.id);
// If successful, update the state to INITIALIZED
await supabase
.from('escrow_contracts')
.update({ current_state: 'INITIALIZED' })
.eq('id', dbResult.id)

return NextResponse.json(
{
escrowId: dbResult.id,
contractAddress: contractResult.contractAddress,
status: "INITIALIZED"
},
{ status: 201 }
);
} catch (error) {
if (error instanceof AppError) {
console.error("Escrow initialization error:", error);
return NextResponse.json(
{
error: error.message,
details: error.details // Include additional details for troubleshooting
},
{ status: error.statusCode }
);
}
return NextResponse.json(
{
escrowId: dbResult.id,
contractAddress: contractResult.contractAddress,
status: 'INITIALIZED',
},
{ status: 201 },
)
} catch (error) {
if (error instanceof AppError) {
console.error('Escrow initialization error:', error)
return NextResponse.json(
{
error: error.message,
details: error.details, // Include additional details for troubleshooting
},
{ status: error.statusCode },
)
}

console.error("Internal server error during escrow initialization:", error);
return NextResponse.json(
{
error: "Internal server error during escrow initialization"
},
{ status: 500 }

);
}
console.error('Internal server error during escrow initialization:', error)
return NextResponse.json(
{
error: 'Internal server error during escrow initialization',
},
{ status: 500 },
)
}
}

const initializeEscrow = async (data: EscrowInitialization) => {
const response = await fetch("/api/escrow/initialize", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
const response = await fetch('/api/escrow/initialize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})

if (!response.ok) {
const error = await response.json();
throw {
error: error.error || "Failed to initialize escrow",
details: error.details || null
};
}
if (!response.ok) {
const error = await response.json()
throw {
error: error.error || 'Failed to initialize escrow',
details: error.details || null,
}
}

return response.json();
};
return response.json()
}
11 changes: 7 additions & 4 deletions apps/web/components/sections/user/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
mockImpactMetrics,
mockProjects,
} from '~/lib/mock-data/mock-user-dashboard'
import type { ImpactMetric, Project } from '~/lib/types/userdashboard'
import type {
ImpactMetricItem,
ProjectDetails,
} from '~/lib/types/user-dashboard'

export function UserDashboard() {
return (
Expand Down Expand Up @@ -62,7 +65,7 @@ export function UserDashboard() {
</div>

<ul className="grid grid-cols-1 md:grid-cols-3 gap-4">
{mockImpactMetrics.map((metric: ImpactMetric) => (
{mockImpactMetrics.map((metric: ImpactMetricItem) => (
<li
key={metric.label}
className="bg-muted rounded-lg p-6 text-center"
Expand Down Expand Up @@ -111,7 +114,7 @@ export function UserDashboard() {
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{mockProjects.map((project: Project) => (
{mockProjects.map((project: ProjectDetails) => (
<ProjectCard key={project.id} {...project} />
))}
</div>
Expand All @@ -128,7 +131,7 @@ export function UserDashboard() {
</Button>
</div>
<div className="space-y-4">
{mockProjects.map((project: Project) => {
{mockProjects.map((project: ProjectDetails) => {
const formattedDate = new Date().toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
Expand Down
24 changes: 12 additions & 12 deletions apps/web/lib/error.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
export class AppError extends Error {
public statusCode: number;
public details?: T;
export class AppError<T = unknown> extends Error {
public statusCode: number
public details?: T

constructor(message: string, statusCode: number, details?: any) {
super(message);
this.statusCode = statusCode;
this.details = details as T;
this.name = "AppError";
}
constructor(message: string, statusCode: number, details?: T) {
super(message)
this.statusCode = statusCode
this.details = details
this.name = 'AppError'
}
}

export interface AppErrorResponse<T = unknown> {
error: string; // A brief error message
details?: T; // Optional detailed information about the error
statusCode?: number; // Optional HTTP status code
error: string // A brief error message
details?: T // Optional detailed information about the error
statusCode?: number // Optional HTTP status code
}
9 changes: 6 additions & 3 deletions apps/web/lib/mock-data/mock-user-dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { BarChart2, Users, Wallet } from 'lucide-react'
import type { ImpactMetric, Project } from '~/lib/types/userdashboard'
import type {
ImpactMetricItem,
ProjectDetails,
} from '~/lib/types/user-dashboard'

export const mockProjects: Project[] = [
export const mockProjects: ProjectDetails[] = [
{
id: 'healthy-kids-id',
image: '/images/kids.webp',
Expand Down Expand Up @@ -40,7 +43,7 @@ export const mockProjects: Project[] = [
},
]

export const mockImpactMetrics: ImpactMetric[] = [
export const mockImpactMetrics: ImpactMetricItem[] = [
{
label: 'Total Impact',
value: '$12,890.50',
Expand Down
Loading
Loading