Skip to content

Commit

Permalink
Merge pull request #122 from MinaFoundation/feature/start-here-v1
Browse files Browse the repository at this point in the history
Feature/start here v1
  • Loading branch information
iluxonchik authored Jan 29, 2025
2 parents 435c040 + 5130caf commit 8c59742
Show file tree
Hide file tree
Showing 5 changed files with 471 additions and 36 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pgt-web-app",
"version": "0.1.41",
"version": "0.1.42",
"private": true,
"type": "module",
"scripts": {
Expand Down
40 changes: 10 additions & 30 deletions src/app/api/funding-rounds/active/route.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,15 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { getOrCreateUserFromRequest } from "@/lib/auth";
import logger from "@/logging";
import { NextRequest } from 'next/server'
import { ApiResponse } from '@/lib/api-response'
import { prisma } from '@/lib/prisma'
import { FundingRoundService } from '@/services/FundingRoundService'

export async function GET(req: Request) {
export async function GET(req: NextRequest) {
try {
const user = await getOrCreateUserFromRequest(req);
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

// Get all funding rounds with their phases
const rounds = await prisma.fundingRound.findMany({
where: {
status: "ACTIVE",
},
include: {
submissionPhase: true,
considerationPhase: true,
deliberationPhase: true,
votingPhase: true,
topic: true,
},
});

return NextResponse.json(rounds);
const fundingRoundService = new FundingRoundService(prisma)
const activeFundingRounds = await fundingRoundService.getActiveFundingRounds()

return ApiResponse.success(activeFundingRounds)
} catch (error) {
logger.error("Failed to fetch active funding rounds:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
return ApiResponse.error(error)
}
}
15 changes: 10 additions & 5 deletions src/app/start-here/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import StartHereComponent from '@/pages/StartHere'
import { Metadata } from 'next'
import { StartHereContent } from '@/components/start-here/StartHereContent'

export const metadata = {
title: 'Start Here | MEF',
description: 'Get started with MEF proposals'
export const metadata: Metadata = {
title: 'Start Here - MINA Ecosystem Funding',
description: 'Learn about the MINA Ecosystem Funding proposal process and get started with your proposal journey.',
}

export default function StartHerePage() {
return <StartHereComponent />
return (
<div className="container max-w-6xl mx-auto py-8 px-4 min-h-[calc(100vh-4rem)]">
<StartHereContent />
</div>
)
}
214 changes: 214 additions & 0 deletions src/components/start-here/ProcessVisualization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
'use client'

import { cn } from "@/lib/utils"
import type { FundingRound } from '@prisma/client'

interface Props {
currentPhase: string
fundingRound: FundingRound & {
submissionPhase: {
startDate: Date
endDate: Date
}
considerationPhase: {
startDate: Date
endDate: Date
}
deliberationPhase: {
startDate: Date
endDate: Date
}
votingPhase: {
startDate: Date
endDate: Date
}
}
}

export function ProcessVisualization({ currentPhase, fundingRound }: Props) {
const getPhaseStyle = (phase: string) => {
const isActive = currentPhase === phase
const isPast = (() => {
const now = new Date()
switch (phase) {
case 'submission':
return now > new Date(fundingRound.submissionPhase.endDate)
case 'consideration':
return now > new Date(fundingRound.considerationPhase.endDate)
case 'deliberation':
return now > new Date(fundingRound.deliberationPhase.endDate)
case 'voting':
return now > new Date(fundingRound.votingPhase.endDate)
default:
return false
}
})()

return {
container: cn(
"border-2 p-4 rounded-lg text-center transition-all duration-200 relative",
isActive && "border-primary bg-primary/5 shadow-lg",
!isActive && isPast && "border-muted bg-muted/5 opacity-60",
!isActive && !isPast && "border-gray-300"
),
text: cn(
"text-sm",
isActive && "text-primary font-medium",
!isActive && isPast && "text-muted-foreground",
!isActive && !isPast && "text-foreground"
)
}
}

return (
<div className="relative mt-8">
{/* Timeline line */}
<div className="absolute left-24 top-0 bottom-0 w-px bg-gray-200" />

{/* Week 1-2: Submit */}
<div className="relative mb-32">
<div className="flex items-start gap-8">
<div className="w-24 pt-2">
<div className="text-sm text-gray-600">WEEK 1-2</div>
<div className="text-xs text-gray-500">SUBMIT</div>
</div>
<div className="flex-1">
<div className={getPhaseStyle('submission').container}>
<div className={getPhaseStyle('submission').text}>
PROPOSAL SUBMISSION
</div>
{/* Vertical connector from submission to consideration */}
<div className="absolute left-1/2 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
</div>
</div>
</div>
</div>

{/* Week 3-4: Consider */}
<div className="relative mb-32">
<div className="flex items-start gap-8">
<div className="w-24 pt-2">
<div className="text-sm text-gray-600">WEEK 3-4</div>
<div className="text-xs text-gray-500">CONSIDER</div>
</div>
<div className="flex-1">
<div className={getPhaseStyle('consideration').container}>
<div className={getPhaseStyle('consideration').text}>
<div>AT LEAST 5 REVIEWERS</div>
<div>OR</div>
<div>AT LEAST 10% OF PARTICIPATING</div>
<div>COMMUNITY MEMBERS</div>
<div>INDICATE INTEREST IN THE</div>
<div>PROPOSAL</div>
</div>
{/* Vertical connectors to outcomes */}
<div className="absolute left-1/4 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
<div className="absolute right-1/4 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
</div>

{/* Outcomes */}
<div className="grid grid-cols-2 gap-8 mt-32">
<div className={cn(getPhaseStyle('consideration').container, "relative")}>
<div className={getPhaseStyle('consideration').text}>
PROPOSAL DECLINED
</div>
</div>
<div className={cn(getPhaseStyle('consideration').container, "relative")}>
<div className={getPhaseStyle('consideration').text}>
PROPOSAL APPROVED
<br />
FOR DELIBERATION
</div>
{/* Connector to deliberation phase */}
<div className="absolute right-1/2 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
</div>
</div>
</div>
</div>
</div>

{/* Week 5-6: Deliberate */}
<div className="relative mb-32">
<div className="flex items-start gap-8">
<div className={cn(
"w-24 pt-2 rounded-md",
currentPhase === 'deliberation' && "bg-primary/10 p-2"
)}>
<div className="text-sm text-gray-600">WEEK 5-6</div>
<div className="text-xs text-gray-500">DELIBERATE</div>
</div>
<div className="flex-1">
<div className="grid grid-cols-2 gap-8 relative">
<div className={getPhaseStyle('deliberation').container}>
<div className={getPhaseStyle('deliberation').text}>
<div className="font-medium mb-2">10 REVIEWERS:</div>
<div>VERIFY IF THE PROPOSAL IS VALUABLE</div>
<div>AND BENEFICIAL FOR THE MINA</div>
<div>ECOSYSTEM AND PROVIDE FEEDBACK</div>
</div>
</div>
{/* Horizontal connector between boxes */}
<div className="absolute top-1/2 left-[calc(50%-2rem)] right-[calc(50%-2rem)] border-t-2 border-dashed border-gray-300" />
<div className={getPhaseStyle('deliberation').container}>
<div className={getPhaseStyle('deliberation').text}>
<div className="font-medium mb-2">COMMUNITY MEMBERS:</div>
<div>PROVIDE FEEDBACK ON THE</div>
<div>PROPOSAL</div>
</div>
</div>
</div>
{/* Connector to voting with label */}
<div className="relative">
<div className="absolute left-1/2 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
<div className="absolute left-1/2 transform -translate-x-1/2 text-sm text-gray-500 text-center mt-8">
PROPOSAL GOES INTO
<br />
VOTING WITH REVIEWERS'
<br />
RECOMMENDATION
</div>
</div>
</div>
</div>
</div>

{/* Week 7-8: Vote */}
<div className="relative mb-32">
<div className="flex items-start gap-8">
<div className="w-24 pt-2">
<div className="text-sm text-gray-600">WEEK 7-8</div>
<div className="text-xs text-gray-500">VOTE</div>
</div>
<div className="flex-1">
<div className={getPhaseStyle('voting').container}>
<div className={getPhaseStyle('voting').text}>
<div className="font-medium mb-2">ALL COMMUNITY MEMBERS:</div>
<div>VOTE ON FUNDING SELECTED</div>
<div>PROPOSALS</div>
</div>
{/* Connector to result */}
<div className="absolute left-1/2 w-px h-32 border-l-2 border-dashed border-gray-300" style={{ top: 'calc(100% + 1px)' }} />
</div>
</div>
</div>
</div>

{/* Result */}
<div className="relative">
<div className="flex items-start gap-8">
<div className="w-24 pt-2">
<div className="text-sm text-gray-600">RESULT</div>
</div>
<div className="flex-1">
<div className={cn(
"text-center text-sm",
currentPhase === 'completed' ? "text-primary font-medium" : "text-muted-foreground"
)}>
FINAL RESULT
</div>
</div>
</div>
</div>
</div>
)
}
Loading

0 comments on commit 8c59742

Please sign in to comment.