Skip to content

Commit

Permalink
Merge pull request #17 from Okulon/feat/nillion-bounty-integration
Browse files Browse the repository at this point in the history
Feat/nillion bounty integration
  • Loading branch information
Okulon authored Feb 8, 2025
2 parents d5637c8 + 909bf90 commit 852f679
Show file tree
Hide file tree
Showing 28 changed files with 3,920 additions and 82 deletions.
10 changes: 10 additions & 0 deletions frontend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Feature Flags
NEXT_PUBLIC_ENABLE_NILLION=true

# Nillion Configuration (using demo credentials)
NEXT_PUBLIC_NILLION_SECRET_KEY=a786abe58f933e190d01d05b467838abb1e391007a674d8a3aef106e15a0bf5a
NEXT_PUBLIC_NILLION_ORG_DID=did:nil:testnet:nillion1vn49zpzgpagey80lp4xzzefaz09kufr5e6zq8c

# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_NETWORK=sepolia
7 changes: 7 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Feature Flags
NEXT_PUBLIC_ENABLE_NILLION=true

# Nillion Configuration
NEXT_PUBLIC_NILLION_SECRET_KEY=your_secret_key_here
NEXT_PUBLIC_NILLION_ORG_DID=your_org_did_here

# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_NETWORK=sepolia
Expand Down
6 changes: 6 additions & 0 deletions frontend/.env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Feature Flags
NEXT_PUBLIC_ENABLE_NILLION=true

# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_NETWORK=sepolia
46 changes: 46 additions & 0 deletions frontend/.github/PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# API Integration Plan and Base Infrastructure

## Overview
This PR sets up the foundation for integrating our frontend with the backend API. It includes comprehensive documentation and base infrastructure for API integration.

## Changes
- Added detailed API integration plan (`docs/API_INTEGRATION.md`)
- Created base API client with error handling (`src/api/client.ts`)
- Set up directory structure for API integration

## API Integration Plan Highlights
1. **Phased Approach**
- Phase 1: API Contract Definition
- Phase 2: Environment Setup
- Phase 3: API Integration
- Phase 4: Testing & Validation
- Phase 5: Monitoring & Maintenance

2. **Documentation**
- Defined all required endpoints
- Documented data schemas
- Outlined integration testing strategy
- Added environment configuration guide

3. **Infrastructure**
- Base API client with axios
- Error handling with custom APIError class
- Authentication interceptors
- TypeScript types and interfaces

## Next Steps
- [ ] Review API contract with backend team
- [ ] Set up staging environment
- [ ] Begin endpoint implementation
- [ ] Create integration test suite

## Testing
- Base API client is set up with proper error handling
- Documentation has been reviewed for completeness
- Directory structure follows best practices

## Screenshots
N/A - Infrastructure and documentation changes only

## Additional Notes
This PR serves as a foundation for our API integration. It will help the team understand what endpoints are needed and how to implement them consistently.
39 changes: 36 additions & 3 deletions frontend/app/bounties/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,46 @@ import { useParams, useRouter } from 'next/navigation';
import { IconBrandGithub, IconClock, IconCalendar, IconTrophy, IconArrowLeft } from '@tabler/icons-react';
import Link from 'next/link';
import { FeatureGate } from '@/hooks/useFeature';
import { mockBounties } from '@/mocks/bounties';
import { Bounty } from '@/types/bounty';
import { useBounty } from '@/hooks/useBounty';
import BountySkeleton from '@/components/bounties/BountySkeleton';
import BountyError from '@/components/bounties/BountyError';

export default function BountyDetail() {
const { id } = useParams();
const bounty = mockBounties.find(b => b.id === id) as Bounty;
const {
data: bounty,
isLoading,
error,
refetch
} = useBounty(id as string);

// Show loading state
if (isLoading) {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white pt-24">
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<BountySkeleton />
</div>
</div>
</div>
);
}

// Show error state
if (error) {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white pt-24">
<div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto">
<BountyError onRetry={() => refetch()} />
</div>
</div>
</div>
);
}

// Show not found state
if (!bounty) {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white flex items-center justify-center pt-24">
Expand Down
35 changes: 33 additions & 2 deletions frontend/app/bounties/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@

import { useState } from 'react';
import Link from 'next/link';
import { Bounty } from '@/types/bounty';
import { useBounties } from '@/hooks/useBounties';
import { useFeature } from '@/hooks/useFeature';
import { mockBounties } from '@/mocks/bounties';
import CreateBountyModal from '@/components/bounties/CreateBountyModal';
import BountySkeleton from '@/components/bounties/BountySkeleton';
import BountyError from '@/components/bounties/BountyError';

export default function BountyBoard() {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const isNillionEnabled = useFeature('nillion.enabled');

const {
data: bountyData,
isLoading,
error,
refetch
} = useBounties();

// Use Nillion data if enabled, otherwise fall back to mock data
const bounties = isNillionEnabled ? bountyData?.items : mockBounties;
return (
<main className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white">
{/* Header Section */}
Expand Down Expand Up @@ -42,7 +56,24 @@ export default function BountyBoard() {
<section className="py-12">
<div className="container mx-auto px-4">
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{mockBounties.map((bounty) => (
{/* Loading State */}
{isLoading && (
<>
{[1, 2, 3, 4, 5, 6].map((i) => (
<BountySkeleton key={i} />
))}
</>
)}

{/* Error State */}
{error && (
<div className="col-span-full">
<BountyError onRetry={() => refetch()} />
</div>
)}

{/* Data State */}
{!isLoading && !error && bounties?.map((bounty) => (
<Link
key={bounty.id}
href={`/bounties/${bounty.id}`}
Expand Down
7 changes: 5 additions & 2 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next'
import { Space_Grotesk } from 'next/font/google'
import './globals.css'
import Navbar from '@/components/Navbar'
import QueryProvider from '@/providers/QueryProvider'

const spaceGrotesk = Space_Grotesk({
subsets: ['latin'],
Expand Down Expand Up @@ -32,8 +33,10 @@ export default function RootLayout({
return (
<html lang="en" className={spaceGrotesk.variable}>
<body className={`${spaceGrotesk.className} antialiased`}>
<Navbar />
{children}
<QueryProvider>
<Navbar />
{children}
</QueryProvider>
</body>
</html>
)
Expand Down
22 changes: 22 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const nextJest = require('next/jest');

const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
});

// Add any custom config to be passed to Jest
const customJestConfig = {
testTimeout: 10000,
testEnvironmentOptions: {
customExportConditions: [''],
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jest-environment-jsdom',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
14 changes: 14 additions & 0 deletions frontend/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '@testing-library/jest-dom';

// Mock next/navigation
jest.mock('next/navigation', () => ({
useRouter: () => ({
push: jest.fn(),
replace: jest.fn(),
prefetch: jest.fn(),
}),
useParams: () => ({}),
useSearchParams: () => ({
get: jest.fn(),
}),
}));
27 changes: 27 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,35 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"dependencies": {
"@fontsource/space-grotesk": "^5.1.1",
"@next/font": "^14.2.15",
"@tabler/icons-react": "^3.29.0",
"@tanstack/react-query": "^5.66.0",
"next": "14.1.0",
"nillion-sv-wrappers": "^1.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"zod": "^3.24.1"
},
"devDependencies": {
"@tanstack/react-query-devtools": "^5.66.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/jest": "^29.5.14",
"@types/node": "^20.11.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "14.1.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
Expand Down
Loading

0 comments on commit 852f679

Please sign in to comment.