From 1130685512be54eada7949faabf6b7062ea2c49c Mon Sep 17 00:00:00 2001 From: JamesVictor-o Date: Sun, 29 Jun 2025 15:24:38 +0100 Subject: [PATCH 1/3] fix:fix the header dropdown on mobile --- src/components/Header.tsx | 101 +++++++++++++++++++++++++------------- src/components/Login.tsx | 95 +++++++++++++++++------------------ 2 files changed, 114 insertions(+), 82 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 1b644dc..9cce6f8 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,60 +1,95 @@ 'use client' import React, { useState } from 'react' -import { Menu, X } from 'lucide-react' -interface HeaderProps { - connectWallet: () => Promise -} const Header: React.FC = () => { const [menuOpen, setMenuOpen] = useState(false) return ( -
+

DECLEANUP NETWORK

{/* Added back button with new arrow style */}
-
{/* Dropdown Menu (Visible on Mobile) */} {menuOpen && ( -
- - DASHBOARD - - - LEADERBOARD - - - APPLY WITH CLEANUP RESULTS - +
+
+ + + +
)}

CLEAN UP, SNAP, EARN

@@ -62,4 +97,4 @@ const Header: React.FC = () => { ) } -export default Header \ No newline at end of file +export default Header diff --git a/src/components/Login.tsx b/src/components/Login.tsx index bca361c..4e16c5c 100644 --- a/src/components/Login.tsx +++ b/src/components/Login.tsx @@ -1,24 +1,22 @@ 'use client' -import { useState } from 'react' import { ConnectButton } from '@rainbow-me/rainbowkit' -import { ContractInfo } from './ContractInfo'; -import { useAccount } from 'wagmi'; +import { ContractInfo } from './ContractInfo' +import { useAccount } from 'wagmi' import Link from 'next/link' const Login: React.FC = () => { - const [isConnecting, setIsConnecting] = useState(false) - const { isConnected, } = useAccount(); + const { isConnected } = useAccount() const truncateAddress = (addr?: string) => { - if (!addr) return ""; - return `${addr.slice(0, 6)}...${addr.slice(-4)}`; - }; + if (!addr) return '' + return `${addr.slice(0, 6)}...${addr.slice(-4)}` + } const lines = ['FIRST DAPP TO SELF-TOKENIZE ENVIRONMENTAL', 'CLEANUP EFFORTS'] const linesMd = [ 'FIRST DAPP TO SELF-TOKENIZE', 'ENVIRONMENTAL CLEANUP EFFORTS', ] - const linesLg = [ + const _linesLg = [ 'FIRST DAPP TO SELF-TOKENIZE ENVIRONMENTAL', 'CLEANUP EFFORTS', ] @@ -77,48 +75,47 @@ const Login: React.FC = () => {
- -
- {isConnected ? ( - -
- GO TO DASHBOARD -
- - ) : ( -
- - {({ account, openAccountModal, openConnectModal, mounted }) => { - const connected = mounted && account; +
+ {isConnected ? ( + +
+ GO TO DASHBOARD +
+ + ) : ( +
+ + {({ account, openAccountModal, openConnectModal, mounted }) => { + const connected = mounted && account - return ( -
- {connected ? ( - - ) : ( - - )} -
- ); - }} -
+ return ( +
+ {connected ? ( + + ) : ( + + )} +
+ ) + }} + +
+ )}
- )} -
) From e1991d015f018e3ebbba41b5217243e88c045241 Mon Sep 17 00:00:00 2001 From: JamesVictor-o Date: Fri, 11 Jul 2025 04:33:05 +0100 Subject: [PATCH 2/3] fix:fixed on modal responsivness --- README.md | 260 +++++++++-- STRUCTURE.md | 266 +++++++++++ src/app/dashboard/page.tsx | 9 +- src/app/layout.tsx | 22 +- src/app/page.tsx | 2 +- .../{ => common}/SessionProvider.tsx | 0 .../{ => features}/ContractInfo.tsx | 2 +- .../{ => features}/DecleanupShareModal.tsx | 2 + src/components/{ => forms}/Login.tsx | 2 +- src/components/index.ts | 26 ++ src/components/{ => layout}/Footer.tsx | 0 src/components/{ => layout}/Header.tsx | 0 src/components/modals/ImpactProductModal.tsx | 161 ++++--- src/components/modals/PreviewModal.tsx | 214 ++++++--- src/components/modals/UploadModal.tsx | 421 ++++++++++-------- src/context/index.ts | 3 + src/hooks/index.ts | 3 + src/lib/constants/index.ts | 35 ++ src/lib/utils/format.ts | 48 ++ src/lib/utils/index.ts | 5 + src/lib/utils/storage.ts | 83 ++++ src/lib/{ => utils}/utils.ts | 0 src/lib/utils/validation.ts | 48 ++ src/lib/utils/web3.ts | 54 +++ src/{lib => services/api}/auth-req.ts | 0 src/services/api/auth.ts | 74 +++ src/services/api/contracts.ts | 59 +++ src/services/api/index.ts | 4 + src/services/api/user.ts | 50 +++ src/services/index.ts | 6 + src/{lib => services/web3}/contracts.ts | 0 .../web3}/decleanup-contracts.ts | 0 src/services/web3/index.ts | 3 + src/{app => styles/globals}/globals.css | 0 src/types/auth.ts | 18 + src/types/contracts.ts | 19 + src/types/index.ts | 5 + src/types/ui.ts | 29 ++ 38 files changed, 1582 insertions(+), 351 deletions(-) create mode 100644 STRUCTURE.md rename src/components/{ => common}/SessionProvider.tsx (100%) rename src/components/{ => features}/ContractInfo.tsx (88%) rename src/components/{ => features}/DecleanupShareModal.tsx (99%) rename src/components/{ => forms}/Login.tsx (98%) create mode 100644 src/components/index.ts rename src/components/{ => layout}/Footer.tsx (100%) rename src/components/{ => layout}/Header.tsx (100%) create mode 100644 src/context/index.ts create mode 100644 src/hooks/index.ts create mode 100644 src/lib/constants/index.ts create mode 100644 src/lib/utils/format.ts create mode 100644 src/lib/utils/index.ts create mode 100644 src/lib/utils/storage.ts rename src/lib/{ => utils}/utils.ts (100%) create mode 100644 src/lib/utils/validation.ts create mode 100644 src/lib/utils/web3.ts rename src/{lib => services/api}/auth-req.ts (100%) create mode 100644 src/services/api/auth.ts create mode 100644 src/services/api/contracts.ts create mode 100644 src/services/api/index.ts create mode 100644 src/services/api/user.ts create mode 100644 src/services/index.ts rename src/{lib => services/web3}/contracts.ts (100%) rename src/{lib => services/web3}/decleanup-contracts.ts (100%) create mode 100644 src/services/web3/index.ts rename src/{app => styles/globals}/globals.css (100%) create mode 100644 src/types/auth.ts create mode 100644 src/types/contracts.ts create mode 100644 src/types/index.ts create mode 100644 src/types/ui.ts diff --git a/README.md b/README.md index 2dcc1cb..1d39cd0 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,243 @@ -**DeCleanup Rewards** is a decentralized application (dApp) build on steller network and designed to: -a) **tokenize IRL impact created via global/local cleanup efforts** -b) **incentivize environmental actions via DeFi mechanics**. -The platform promotes environmental stewardship globally, with plans to activate global/local communities and scale across multiple blockchain ecosystems. +# DeCleanup dApp -![Screenshot of the Main Page](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmWjckBnwWkidWtTQwR17TrQWoo9j3FX5LLwRg8s3n12cN) +A decentralized application for environmental cleanup initiatives built with Next.js, TypeScript, and Web3 technologies. -## OSS data +## πŸš€ Features -**dApp GitHub repository** - πŸ”—πŸ‘‰ https://github.com/DeCleanUp-DCU/Dapp +- **Web3 Integration**: Seamless blockchain interactions +- **Modern UI**: Built with Tailwind CSS and shadcn/ui +- **Type Safety**: Full TypeScript support +- **Responsive Design**: Mobile-first approach +- **Authentication**: Secure user authentication +- **Real-time Updates**: Live data synchronization -**Smart Contract GitHub Repository** - πŸ”—πŸ‘‰ https://github.com/DeCleanUp-DCU/Smart-Contract +## πŸ“ Project Structure -**Smart Contract onchain** [Arbiscan](https://arbiscan.io/address/0xf21389b64e0eb749fd150d0c44742692e19a69c8) +This project follows a professional, scalable architecture: -## Uniqueness +``` +src/ +β”œβ”€β”€ app/ # Next.js App Router pages +β”œβ”€β”€ components/ # React components (organized by feature) +β”œβ”€β”€ context/ # React Context providers +β”œβ”€β”€ hooks/ # Custom React hooks +β”œβ”€β”€ lib/ # Utilities, constants, and helpers +β”œβ”€β”€ services/ # API and Web3 service functions +β”œβ”€β”€ styles/ # Global styles and CSS +└── types/ # TypeScript type definitions +``` -**How DeCleanup dApp works**: Participants engage in cleanups, submit proof via photos, and earn dynamic NFTs that evolve based on their contributions. More details in our [Mirror Article](https://mirror.xyz/decleanupnet.eth/ZzncKRu-Q-leEZkQ48Txm-NQRxG_hH3V3wyHaYUKfYI). +For detailed structure documentation, see [STRUCTURE.md](./STRUCTURE.md). -**User-Centric Design & Functionality**: The DeCleanup dApp offers a seamless user experience with an intuitive interface, allowing individuals and communities to easily submit proof of cleanups and earn dynamic NFTs, reflecting real-world impact on-chain. The verification system ensures quick approval for rewards, making the process simple and efficient. -More details in our [Guide](https://mirror.xyz/decleanupnet.eth/A5uzOpx9HUgXZEopCKUsbgffw9SajL4Q9eECgrguq-4). +## πŸ› οΈ Tech Stack -**Gasless Approach**: We chose Arbitrum for its low gas fees, with only the first claim requiring gas payment, while the rest of the experience is gasless. This eliminates barriers to onboarding non-tech-savvy users into Web3, enhancing real-world impact - our key KPI. Gasless transactions make Web3 more accessible to everyday people. +- **Framework**: Next.js 15 with App Router +- **Language**: TypeScript +- **Styling**: Tailwind CSS +- **UI Components**: shadcn/ui +- **Web3**: Ethers.js, Wagmi, RainbowKit +- **Authentication**: NextAuth.js +- **State Management**: React Context + TanStack Query +- **Package Manager**: npm/yarn/bun -**Contribution to Ecosystem Growth**: By integrating environmental action with blockchain technology, making it intuitive and practically gasless for the end user, DeCleanup boosts on-chain activity through introducing new wallets and increases community engagement. +## πŸš€ Getting Started -**Innovation in Application**: DeCleanup tokenizes real-world impact, converting environmental efforts into on-chain assets, which can be recognized as valuable impact products in the broader blockchain ecosystem. +### Prerequisites -**Community Onchain Activation**: Gamification elements like leaderboards and challenges via [Guild XYZ](https://guild.xyz/decleanup-network) allow users to earn extra points and compete for top spots. High-ranking users make it to the whitelist, encouraging participation and collaboration within the network. +- Node.js 18+ +- npm, yarn, or bun +- Git -## dApp and Dynamic NFT Collection +### Installation -![9 Levels NFTs](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmZELVjF8H5VvG1BxhunXK4n6LuK17RBuis5yRepEqxARk) +1. **Clone the repository** -The collection items have common traits that remain consistent with each NFT upgrade: + ```bash + git clone + cd dapp + ``` -- **Category**: Cleanup Impact NFT -- **Type**: Dynamic -- **Impact**: Environmental +2. **Install dependencies** -There are dynamic traits, which grow every three cleanups: + ```bash + npm install + # or + yarn install + # or + bun install + ``` -- **Rarity**: Common (1-3 cleanups), Rare (4-6 cleanups), Legendary (7-9 cleanups), Unique (10 cleanups) -- **Level**: Newbie (1-3 cleanups), Pro (4-6 cleanups), Hero (7-9 cleanups), Guardian (10 cleanups) +3. **Set up environment variables** -And dynamic traits which grow with every cleanup done: + ```bash + cp .env.example .env.local + ``` -- **Impact Value**: 1 (first cleanup) to 10 (last cleanup) -- **DCU Points**: 10 (first cleanup) to 100 (last cleanup) + Configure your environment variables: -While the first two categories of traits determine the general information required for this impact product’s classification on the Impact Marketplace, the last categories are mainly dynamic traits, which will determine the rewards value via Impact Marketplace DeFi utility and will contribute for user’s IRL impact rank. -Read more on this topic in the article by EcoSynthesisX on [Mirror](https://mirror.xyz/ecosynthesisx.eth/zOdeuaeFfJUFScZZKu1OGF7cWCiRgUHQSGE-14cf8fo). + ```env + NEXT_PUBLIC_API_URL=http://localhost:3000/api + NEXT_PUBLIC_DECLEANUP_CONTRACT_ADDRESS=your_contract_address + NEXTAUTH_SECRET=your_nextauth_secret + NEXTAUTH_URL=http://localhost:3000 + ``` -![Example of Traits](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmfUA1PomqfsXPZod2oo79nrMq17xT1Rxo8EdWxwFFVHxM) +4. **Run the development server** -## Partnerships and Community Activation + ```bash + npm run dev + # or + yarn dev + # or + bun dev + ``` -Initial community outreach was established by partnership proposals with the key networks such as GreenPill Network and ReFi DAO. [ReFi](https://x.com/refiphangan/status/1837797388368728419?s=61) -and GreenPill Phangan, in particular are active in the [Monthly Earth Day](https://x.com/highlyartistic/status/1837668300425429023?s=61) movement. The other ReFi node that is organizing cleanups is [ReFi Tulum](https://x.com/refitulum/status/1838290711961112606?s=61) -Additional partnerships with [Aqua Purge web3](https://x.com/aquapurgeweb3/status/1822565379593449715?s=61) have furthered our global reach. These communities organize regular cleanups and were the first we have targeted. -Recent community that joined the Network, [HEMJapan](https://x.com/hemjapan?s=21), impressed us with 12 new wallets applied with the proofs after their recent Monthly Earth Day [cleanup](https://x.com/hemjapan/status/1838096871035928649?s=61). -Moreover, we are establishing partnerships with like minded projects. As example, DeCleanup and [Cleanify](https://x.com/cleanify_vet?s=21) have a collaborative strategy to promote each other, motivating both communities to organize more cleanups, and providing additional incentives for them. +5. **Open your browser** + Navigate to [http://localhost:3000](http://localhost:3000) -## RoadMap +## πŸ“ Available Scripts -![The Roadmap](https://beige-defiant-spoonbill-537.mypinata.cloud/ipfs/QmcAgDypdKSRtUkoaBGp3puYRWEuyEjT5BauuVmXDYz7y1) +- `npm run dev` - Start development server with Turbopack +- `npm run build` - Build for production +- `npm run start` - Start production server +- `npm run lint` - Run ESLint +- `npm run format` - Format code with Prettier -The roadmap highlights the following stages for development and expansion: +## πŸ—οΈ Architecture -1. **Community Activation (22.11.2024)**: Finalize partner onboarding and establish the foundation for continued network growth. -2. **Marketing (Q4 2024)**: Engage more communities through focused marketing and participation in events such as Monthly Earth Day. -3. **Expansion (Q2 2025)**: Scale the platform to multiple blockchain networks to increase reach and global participation. -4. **DeCleanup 2.0 (Q4 2025)**: Launch full platform features, evaluate performance, and refine strategies for long-term sustainability. +### Component Organization + +Components are organized by functionality: + +- **`/components/layout/`** - Layout components (Header, Footer) +- **`/components/forms/`** - Form components (Login, Registration) +- **`/components/features/`** - Feature-specific components +- **`/components/common/`** - Reusable common components +- **`/components/ui/`** - Base UI components (shadcn/ui) + +### Service Layer + +- **`/services/api/`** - REST API service functions +- **`/services/web3/`** - Blockchain interaction services + +### Utility Functions + +- **`/lib/utils/`** - Reusable utility functions +- **`/lib/constants/`** - Application constants +- **`/lib/validators/`** - Validation functions + +## πŸ”§ Configuration + +### TypeScript + +The project uses strict TypeScript configuration with: + +- Path aliases for clean imports +- Strict type checking +- Modern ES features + +### ESLint & Prettier + +- Airbnb TypeScript ESLint configuration +- Prettier for code formatting +- Husky for pre-commit hooks + +### Tailwind CSS + +- Custom design system +- Responsive breakpoints +- Dark mode support + +## 🌐 Web3 Integration + +### Supported Networks + +- Ethereum Mainnet +- Polygon +- Arbitrum One +- Test networks (Goerli, Mumbai) + +### Contract Integration + +- DeCleanup smart contracts +- Wallet connection (RainbowKit) +- Transaction management + +## πŸ“± Responsive Design + +The application is built with a mobile-first approach and supports: + +- Mobile devices (320px+) +- Tablets (768px+) +- Desktop (1024px+) +- Large screens (1280px+) + +## πŸ§ͺ Testing + +```bash +# Run tests +npm run test + +# Run tests in watch mode +npm run test:watch + +# Run tests with coverage +npm run test:coverage +``` + +## πŸ“¦ Deployment + +### Vercel (Recommended) + +1. Connect your GitHub repository to Vercel +2. Configure environment variables +3. Deploy automatically on push to main branch + +### Manual Deployment + +```bash +# Build the application +npm run build + +# Start production server +npm run start +``` + +## 🀝 Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Code Style + +- Follow TypeScript best practices +- Use ESLint and Prettier +- Write meaningful commit messages +- Add tests for new features + +## πŸ“„ License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## πŸ†˜ Support + +If you encounter any issues or have questions: + +1. Check the [documentation](./STRUCTURE.md) +2. Search existing [issues](../../issues) +3. Create a new issue with detailed information + +## πŸ™ Acknowledgments + +- [Next.js](https://nextjs.org/) - React framework +- [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS +- [shadcn/ui](https://ui.shadcn.com/) - UI components +- [Ethers.js](https://ethers.org/) - Ethereum library +- [RainbowKit](https://rainbowkit.com/) - Wallet connection + +--- + +Built with ❀️ for a cleaner future diff --git a/STRUCTURE.md b/STRUCTURE.md new file mode 100644 index 0000000..9c3f659 --- /dev/null +++ b/STRUCTURE.md @@ -0,0 +1,266 @@ +# Project Structure Documentation + +This document outlines the professional folder structure for the DeCleanup dApp. + +## πŸ“ Root Directory + +``` +dapp/ +β”œβ”€β”€ .github/ # GitHub workflows and configurations +β”œβ”€β”€ .husky/ # Git hooks +β”œβ”€β”€ .next/ # Next.js build output +β”œβ”€β”€ node_modules/ # Dependencies +β”œβ”€β”€ out/ # Static export output +β”œβ”€β”€ public/ # Static assets +β”œβ”€β”€ src/ # Source code +β”œβ”€β”€ .gitignore # Git ignore rules +β”œβ”€β”€ .prettierrc # Prettier configuration +β”œβ”€β”€ .prettierignore # Prettier ignore rules +β”œβ”€β”€ .yarnrc.yml # Yarn configuration +β”œβ”€β”€ bun.lockb # Bun lock file +β”œβ”€β”€ components.json # UI components configuration +β”œβ”€β”€ eslint.config.mjs # ESLint configuration +β”œβ”€β”€ LICENSE # Project license +β”œβ”€β”€ next-env.d.ts # Next.js TypeScript definitions +β”œβ”€β”€ next.config.ts # Next.js configuration +β”œβ”€β”€ package.json # Project dependencies and scripts +β”œβ”€β”€ package-lock.json # NPM lock file +β”œβ”€β”€ postcss.config.mjs # PostCSS configuration +β”œβ”€β”€ README.md # Project documentation +β”œβ”€β”€ tailwind.config.ts # Tailwind CSS configuration +β”œβ”€β”€ tsconfig.json # TypeScript configuration +└── yarn.lock # Yarn lock file +``` + +## πŸ“ Source Code Structure (`src/`) + +### πŸ—οΈ App Directory (`src/app/`) + +Next.js 13+ App Router structure: + +``` +src/app/ +β”œβ”€β”€ api/ # API routes +β”‚ └── auth/ # Authentication API endpoints +β”œβ”€β”€ dashboard/ # Dashboard pages +β”‚ └── preclaim/ # Preclaim functionality +β”œβ”€β”€ leaderboard/ # Leaderboard pages +β”œβ”€β”€ favicon.ico # Site favicon +β”œβ”€β”€ globals.css # Global styles (moved to styles/globals/) +β”œβ”€β”€ index.tsx # Index page +β”œβ”€β”€ layout.tsx # Root layout +β”œβ”€β”€ page.tsx # Home page +└── provider.tsx # App providers +``` + +### 🧩 Components (`src/components/`) + +Organized by functionality: + +``` +src/components/ +β”œβ”€β”€ common/ # Reusable common components +β”‚ └── SessionProvider.tsx +β”œβ”€β”€ features/ # Feature-specific components +β”‚ β”œβ”€β”€ ContractInfo.tsx +β”‚ └── DecleanupShareModal.tsx +β”œβ”€β”€ forms/ # Form components +β”‚ └── Login.tsx +β”œβ”€β”€ layout/ # Layout components +β”‚ β”œβ”€β”€ Footer.tsx +β”‚ └── Header.tsx +β”œβ”€β”€ modals/ # Modal components +β”œβ”€β”€ ui/ # Base UI components (shadcn/ui) +β”‚ β”œβ”€β”€ button.tsx +β”‚ β”œβ”€β”€ card.tsx +β”‚ β”œβ”€β”€ collapsible.tsx +β”‚ β”œβ”€β”€ dropdown-menu.tsx +β”‚ β”œβ”€β”€ input.tsx +β”‚ β”œβ”€β”€ navigation-menu.tsx +β”‚ β”œβ”€β”€ Progress.tsx +β”‚ β”œβ”€β”€ separator.tsx +β”‚ β”œβ”€β”€ sheet.tsx +β”‚ β”œβ”€β”€ skeleton.tsx +β”‚ └── tooltip.tsx +β”œβ”€β”€ upload/ # Upload-related components +β”œβ”€β”€ imageUploader/ # Image upload components +└── landingPage/ # Landing page components +``` + +### πŸ”§ Context (`src/context/`) + +React Context providers: + +``` +src/context/ +β”œβ”€β”€ AuthContext.tsx # Authentication context +└── ContextApi.tsx # General app context +``` + +### πŸͺ Hooks (`src/hooks/`) + +Custom React hooks: + +``` +src/hooks/ +β”œβ”€β”€ useDeCleanupContracts.ts # DeCleanup contract hooks +└── use-mobile.tsx # Mobile detection hook +``` + +### πŸ“š Library (`src/lib/`) + +Utility functions and configurations: + +``` +src/lib/ +β”œβ”€β”€ constants/ # Application constants +β”‚ └── index.ts +β”œβ”€β”€ utils/ # Utility functions +β”‚ β”œβ”€β”€ format.ts # Formatting utilities +β”‚ β”œβ”€β”€ index.ts # Re-exports +β”‚ β”œβ”€β”€ storage.ts # Storage utilities +β”‚ β”œβ”€β”€ validation.ts # Validation utilities +β”‚ └── web3.ts # Web3 utilities +└── utils.ts # Legacy utils (moved to utils/) +``` + +### πŸ”Œ Services (`src/services/`) + +API and external service integrations: + +``` +src/services/ +β”œβ”€β”€ api/ # API service functions +β”‚ β”œβ”€β”€ auth.ts # Authentication API +β”‚ β”œβ”€β”€ contracts.ts # Contract API +β”‚ β”œβ”€β”€ index.ts # Re-exports +β”‚ └── user.ts # User API +└── web3/ # Web3 service functions + β”œβ”€β”€ contracts.ts # Contract interactions + └── decleanup-contracts.ts +``` + +### 🎨 Styles (`src/styles/`) + +Styling and CSS: + +``` +src/styles/ +β”œβ”€β”€ components/ # Component-specific styles +└── globals/ # Global styles + └── globals.css +``` + +### πŸ“ Types (`src/types/`) + +TypeScript type definitions: + +``` +src/types/ +β”œβ”€β”€ auth.ts # Authentication types +β”œβ”€β”€ contracts.ts # Contract types +β”œβ”€β”€ index.ts # Re-exports +β”œβ”€β”€ modal.ts # Modal types +└── ui.ts # UI component types +``` + +## πŸ—οΈ Architecture Principles + +### 1. **Separation of Concerns** + +- Each directory has a specific purpose +- Components are organized by functionality +- Business logic is separated from UI components + +### 2. **Scalability** + +- Modular structure allows easy expansion +- Clear naming conventions +- Consistent file organization + +### 3. **Maintainability** + +- Centralized types and constants +- Reusable utility functions +- Clear import/export patterns + +### 4. **Developer Experience** + +- Intuitive folder structure +- Comprehensive documentation +- Consistent coding patterns + +## πŸ“‹ File Naming Conventions + +### Components + +- **PascalCase** for component files: `Header.tsx`, `LoginForm.tsx` +- **kebab-case** for directories: `image-uploader/`, `landing-page/` + +### Utilities + +- **camelCase** for utility files: `format.ts`, `validation.ts` +- **PascalCase** for type files: `auth.ts`, `contracts.ts` + +### Constants + +- **UPPER_SNAKE_CASE** for constant values +- **camelCase** for constant objects + +## πŸ”„ Import/Export Patterns + +### Barrel Exports + +Use index files for clean imports: + +```typescript +// Instead of multiple imports +import { formatAddress } from '@/lib/utils/format' +import { isValidEmail } from '@/lib/utils/validation' + +// Use barrel exports +import { formatAddress, isValidEmail } from '@/lib/utils' +``` + +### Type Imports + +Centralize type imports: + +```typescript +// Use centralized types +import { User, AuthState } from '@/types' +``` + +## πŸš€ Best Practices + +1. **Keep components small and focused** +2. **Use TypeScript for type safety** +3. **Implement proper error handling** +4. **Follow consistent naming conventions** +5. **Document complex functions and components** +6. **Use environment variables for configuration** +7. **Implement proper testing structure** + +## πŸ“¦ Package Management + +The project supports multiple package managers: + +- **npm**: `package-lock.json` +- **yarn**: `yarn.lock` +- **bun**: `bun.lockb` + +Choose one and stick with it for consistency. + +## πŸ”§ Development Scripts + +```json +{ + "dev": "next dev --turbopack", // Development server + "build": "next build", // Production build + "start": "next start", // Production server + "lint": "next lint", // Lint code + "format": "prettier --write ." // Format code +} +``` + +This structure provides a solid foundation for a professional, scalable dApp that's easy to maintain and extend. diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index f7cb0cd..7002ca5 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -15,7 +15,7 @@ interface LongButtonProps { export default function Page() { const [isModalOpen, setIsModalOpen] = useState(false) const [isUploadModalOpen, setIsUploadModalOpen] = useState(false) - const [uploadedImages, setUploadedImages] = useState([]) + const [_uploadedImages, setUploadedImages] = useState([]) const [isShareModal, setIsShareModal] = useState(false) const handleSubmit = (images: File[]) => { @@ -157,9 +157,9 @@ export default function Page() {
-
setIsModalOpen(true)}> +
+
@@ -176,7 +176,8 @@ export default function Page() { { + onClose={() => setIsModalOpen(false)} + onUploadClick={() => { setIsModalOpen(false) setIsUploadModalOpen(true) }} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e77b9d8..3f657f2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,9 @@ -import Header from '@/components/Header' -import Footer from '@/components/Footer' +import Header from '@/components/layout/Header' +import Footer from '@/components/layout/Footer' import { ContextProvider } from '@/context/ContextApi' -import { ThirdwebProvider } from 'thirdweb/react' import { Providers } from './provider' -import '@/app/globals.css' +import '@/styles/globals/globals.css' export const metadata = { title: 'Decentralized Clean Up Network', @@ -20,16 +19,15 @@ export default function RootLayout({
- + -
-
- - {children} -
-
+
+
+ {children} +
+
- +
diff --git a/src/app/page.tsx b/src/app/page.tsx index 9644b46..a26e4b5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,4 @@ -import Login from '@/components/Login' +import { Login } from '@/components' const Page = () => { return ( diff --git a/src/components/SessionProvider.tsx b/src/components/common/SessionProvider.tsx similarity index 100% rename from src/components/SessionProvider.tsx rename to src/components/common/SessionProvider.tsx diff --git a/src/components/ContractInfo.tsx b/src/components/features/ContractInfo.tsx similarity index 88% rename from src/components/ContractInfo.tsx rename to src/components/features/ContractInfo.tsx index 686385a..da4fd28 100644 --- a/src/components/ContractInfo.tsx +++ b/src/components/features/ContractInfo.tsx @@ -1,4 +1,4 @@ -import { DCUContracts } from '../lib/decleanup-contracts' +import { DCUContracts } from "@/services" export function ContractInfo() { const contracts = new DCUContracts('ARBITRUM_SEPOLIA') diff --git a/src/components/DecleanupShareModal.tsx b/src/components/features/DecleanupShareModal.tsx similarity index 99% rename from src/components/DecleanupShareModal.tsx rename to src/components/features/DecleanupShareModal.tsx index 5b31042..a797bda 100644 --- a/src/components/DecleanupShareModal.tsx +++ b/src/components/features/DecleanupShareModal.tsx @@ -1,3 +1,5 @@ +"use client" + import React, { useEffect } from 'react' import Image from 'next/image' import { motion } from 'framer-motion' diff --git a/src/components/Login.tsx b/src/components/forms/Login.tsx similarity index 98% rename from src/components/Login.tsx rename to src/components/forms/Login.tsx index 4e16c5c..41ab377 100644 --- a/src/components/Login.tsx +++ b/src/components/forms/Login.tsx @@ -1,6 +1,6 @@ 'use client' import { ConnectButton } from '@rainbow-me/rainbowkit' -import { ContractInfo } from './ContractInfo' +import { ContractInfo } from '@/components/features/ContractInfo' import { useAccount } from 'wagmi' import Link from 'next/link' diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..f37bc3d --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,26 @@ +// Layout components +export { default as Header } from './layout/Header' +export { default as Footer } from './layout/Footer' + +// Form components +export { default as Login } from './forms/Login' + +// Feature components +export { default as ContractInfo } from './features/ContractInfo' +export { default as DecleanupShareModal } from './features/DecleanupShareModal' + +// Common components +export { default as SessionProvider } from './common/SessionProvider' + +// UI components (shadcn/ui) +export { default as Button } from './ui/button' +export { default as Card } from './ui/card' +export { default as Input } from './ui/input' +export { default as Progress } from './ui/Progress' +export { default as Separator } from './ui/separator' +export { default as Skeleton } from './ui/skeleton' +export { default as Tooltip } from './ui/tooltip' +export { default as DropdownMenu } from './ui/dropdown-menu' +export { default as NavigationMenu } from './ui/navigation-menu' +export { default as Sheet } from './ui/sheet' +export { default as Collapsible } from './ui/collapsible' diff --git a/src/components/Footer.tsx b/src/components/layout/Footer.tsx similarity index 100% rename from src/components/Footer.tsx rename to src/components/layout/Footer.tsx diff --git a/src/components/Header.tsx b/src/components/layout/Header.tsx similarity index 100% rename from src/components/Header.tsx rename to src/components/layout/Header.tsx diff --git a/src/components/modals/ImpactProductModal.tsx b/src/components/modals/ImpactProductModal.tsx index d20abc0..0e4843f 100644 --- a/src/components/modals/ImpactProductModal.tsx +++ b/src/components/modals/ImpactProductModal.tsx @@ -1,90 +1,141 @@ // components/ImpactProductModal.tsx -import { useState } from 'react' -import Image from 'next/image' -import { X, ChevronRight } from 'lucide-react' +import { X } from 'lucide-react' interface ModalProps { isOpen: boolean onClose: () => void + onUploadClick: () => void } export const ImpactProductModal: React.FC = ({ isOpen, onClose, + onUploadClick, }) => { if (!isOpen) return null return ( -
+
+ {/* Backdrop click to close */} - {/* Main modal content with the 2-column layout */} -
-
-
-
-
1
+ {/* Modal content */} +
+
+ {/* Header */} +
+

+ How to Earn SOCU Tokens +

+

+ Complete these activities to earn rewards +

+
-
- - IMPACT PRODUCT CLAIMS - - –EARN 10 SOCU PER LEVEL BY SUCCESSFULLY SUBMITTING - BEFORE-AND-AFTER CLEANUP PHOTOS, WAITING FOR THE VERIFICATION - AND CLAIMING THE LEVEL. THERE ARE 10 LEVELS AVAILABLE, WITH MORE - TO COME + {/* Content */} +
+ {/* Impact Product Claims */} +
+
+
+ 1 +
+
+

+ + IMPACT PRODUCT CLAIMS + +

+

+ Earn 10 SOCU per level by successfully + submitting before-and-after cleanup photos, waiting for + verification, and claiming the level. There are 10 levels + available, with more to come. +

+
-
-
2
-
- REFERRALS - –GET 1 SOCU FOR EACH USER WHO JOINS VIA YOUR LINK, SUBMITS - CLEANUP PHOTOS, GETS IT VERIFIED AND CLAIMS AN IMPACT PRODUCT. + {/* Referrals */} +
+
+
+ 2 +
+
+

+ + REFERRALS + +

+

+ Get 1 SOCU for each user who joins via your + link, submits cleanup photos, gets it verified and claims an + impact product. +

+
-
-
3
-
- STREAKS – - EARN 3 SOCU PER LEVEL IF YOU SUBMIT CLEANUPS AT LEAST ONCE PER - WEEK TO MAINTAIN YOUR STREAK. + {/* Streaks */} +
+
+
+ 3 +
+
+

+ + STREAKS + +

+

+ Earn 3 SOCU per level if you submit + cleanups at least once per week to maintain your streak. +

+
-
-
- + + + + + + Upload Cleanup + +
diff --git a/src/components/modals/PreviewModal.tsx b/src/components/modals/PreviewModal.tsx index 328f423..87ff919 100644 --- a/src/components/modals/PreviewModal.tsx +++ b/src/components/modals/PreviewModal.tsx @@ -1,6 +1,5 @@ import React, { useEffect } from 'react' -import { X, Send } from 'lucide-react' -import { useCleanupContext } from '@/context/ContextApi' +import { X, Clock, MessageCircle, CheckCircle } from 'lucide-react' type DecleanupShareModalProps = { isOpen: boolean @@ -11,7 +10,11 @@ const PreviewPage: React.FC = ({ isOpen, onClose, }) => { - const { cleanupPicture } = useCleanupContext() + // Mock cleanup pictures for demo + const cleanupPicture = { + before: null, // Will be populated with actual File objects + after: null + } useEffect(() => { if (isOpen) { @@ -27,67 +30,166 @@ const PreviewPage: React.FC = ({ if (!isOpen) return null return ( -
- -
+
+
+ {/* Header */} +
+
+

Cleanup Submission Preview

+ +
+
+ + {/* Status Banner */} +
+
+
+ +
+
+

Submission Under Review

+

Your cleanup photos are being reviewed by our team

+
+
+
+ {/* Content */} -
-
-
- {/* Image uploaders */} -
- {cleanupPicture.before && ( -
-

- 1 - Before -

- Before cleanup +
+
+ {/* Images Section */} +
+

Your Cleanup Journey

+ + {cleanupPicture.before || cleanupPicture.after ? ( +
+ {/* Before Image */} + {cleanupPicture.before && ( +
+
+
+ 1 +
+

Before

+
+
+ Before cleanup +
+ Before +
+
+
+ )} + + {/* After Image */} + {cleanupPicture.after && ( +
+
+
+ 2 +
+

After

+
+
+ After cleanup +
+ After +
+
+
+ )} +
+ ) : ( +
+
+
- )} +

No Images to Preview

+

Upload your before and after photos to see them here

+
+ )} +
- {cleanupPicture.after && ( -
-

- 2 - After -

- After cleanup + {/* Status Panel */} +
+
+
+

Review Status

+ + {/* Review Timeline */} +
+
+
+ +
+
+

Submitted

+

Photos uploaded successfully

+
+
+ +
+
+ +
+
+

Under Review

+

Team is verifying your cleanup

+
+
+ +
+
+ +
+
+

Approved

+

Rewards will be distributed

+
+
- )} +
- {!cleanupPicture.before && !cleanupPicture.after && ( -
-

No images to preview

+
+

What's Next?

+
+
+ ⏱️ +

Review typically takes 2-12 hours

+
+
+ πŸ† +

You'll receive your new level after approval

+
+
+ πŸ’¬ +

Contact us on Telegram for questions

+
- )} -
+
- {/* Right sidebar */} -
-
  • - After the team review the proof 
of cleanup, come back to - claim your new level. Usually the process takes from 2Β to 12 - hours. Contact us in telegram group if you have questions - orΒ forΒ troubleshooting -
  • -
    - + +
    @@ -98,4 +200,4 @@ const PreviewPage: React.FC = ({ ) } -export default PreviewPage +export default PreviewPage \ No newline at end of file diff --git a/src/components/modals/UploadModal.tsx b/src/components/modals/UploadModal.tsx index d89de90..af18e8d 100644 --- a/src/components/modals/UploadModal.tsx +++ b/src/components/modals/UploadModal.tsx @@ -1,222 +1,291 @@ import { useState } from 'react' -import { X, Send } from 'lucide-react' -import ImageUploader from '../imageUploader/ImageUploader' -import UploadInstructions from '../imageUploader/UploadInstructions' -import SocialConsentCheckbox from '../imageUploader/SocialConsentCheckbox' -import { useCleanupContext } from '@/context/ContextApi' - -interface ImageUploadModalProps { - isOpen: boolean - onClose: () => void - onSubmit: (images: File[]) => void -} +import { X, Send, Camera, Upload, CheckCircle, ArrowRight } from 'lucide-react' -const ImageUploadModal = ({ - isOpen, - onClose, - onSubmit, -}: ImageUploadModalProps) => { - const [beforeImage, setBeforeImage] = useState(null) - const [afterImage, setAfterImage] = useState(null) +const ImageUploadModal = ({ isOpen, onClose, onSubmit }) => { + const [beforeImage, setBeforeImage] = useState(null) + const [afterImage, setAfterImage] = useState(null) const [step, setStep] = useState(1) - const { checkBox, setCheckBox, setCleanupPicture } = useCleanupContext() + const [checkBox, setCheckBox] = useState(false) + const [isDragging, setIsDragging] = useState(false) if (!isOpen) return null - const handleDragOver = (e: React.DragEvent) => { + const handleDragOver = (e) => { + e.preventDefault() + setIsDragging(true) + } + + const handleDragLeave = (e) => { + e.preventDefault() + setIsDragging(false) + } + + const handleDrop = (e, type) => { e.preventDefault() + setIsDragging(false) + + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + const file = e.dataTransfer.files[0] + if (type === 'before') { + setBeforeImage(file) + } else { + setAfterImage(file) + } + } + } + + const handleFileSelect = (e, type) => { + if (e.target.files && e.target.files.length > 0) { + const file = e.target.files[0] + if (type === 'before') { + setBeforeImage(file) + } else { + setAfterImage(file) + } + } } const handleSubmit = () => { if (!checkBox) return - setStep(1) - // Store images in context - setCleanupPicture({ - before: beforeImage, - after: afterImage, - }) - - // Submit the images + const images = [] if (beforeImage) images.push(beforeImage) if (afterImage) images.push(afterImage) onSubmit(images) - - // Reset form but keep images in context for preview + + // Reset form setBeforeImage(null) setAfterImage(null) setCheckBox(false) + setStep(1) + onClose() } const handleClose = () => { setStep(1) setBeforeImage(null) setAfterImage(null) + setCheckBox(false) onClose() } - return ( -
    -
    - {/* Close button */} - + const ImageUploader = ({ image, onImageChange, label, type, stepNumber }) => ( +
    +
    +
    +
    + {stepNumber} +
    +

    {stepNumber === 1 ? 'Before' : 'After'}

    +
    +

    {label}

    +
    +
    handleDrop(e, type)} + > + {image ? ( +
    +
    + {`${type} + +
    +
    + + Image uploaded +
    +

    {image.name}

    +
    + ) : ( +
    +
    + +
    +

    + Click to upload or drag and drop +

    +

    PNG, JPG, GIF up to 10MB

    +
    + )} + + handleFileSelect(e, type)} + className="absolute inset-0 w-full h-full opacity-0 cursor-pointer" + /> +
    +
    + ) + + const ProgressBar = () => ( +
    +
    +
    = 1 ? 'bg-black text-[#FAFF00]' : 'bg-gray-200 text-gray-600' + }`}> + {beforeImage ? : '1'} +
    +
    = 2 ? 'bg-black' : 'bg-gray-200'}`} /> +
    = 2 ? 'bg-black text-[#FAFF00]' : 'bg-gray-200 text-gray-600' + }`}> + {afterImage ? : '2'} +
    +
    +
    + +
    +
    +
    + ) + + return ( +
    +
    {/* Header */} -
    -

    Upload Image

    +
    +
    +

    Share Your Cleanup Impact

    + +
    {/* Content */} -
    -
    -
    - {/* Image uploaders */} -
    - { - e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setBeforeImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - before: e.dataTransfer.files[0], - })) - } - }} - /> - - { - e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setAfterImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - after: e.dataTransfer.files[0], - })) - } - }} - /> -
    +
    + - {/* mobile flow */} -
    -
    - -
    + {/* Desktop view */} +
    +
    + + + +
    +
    - {step === 1 ? ( -
    - { - e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setBeforeImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - before: e.dataTransfer.files[0], - })) - } - }} - /> -
    - ) : ( -
    - { - e.preventDefault() - if ( - e.dataTransfer.files && - e.dataTransfer.files.length > 0 - ) { - setAfterImage(e.dataTransfer.files[0]) - setCleanupPicture(prev => ({ - ...prev, - after: e.dataTransfer.files[0], - })) - } - }} - /> -
    - )} -
    + {/* Mobile view */} +
    + {step === 1 ? ( + + ) : ( + + )} +
    - {/* Right sidebar */} -
    -
    - -
    + {/* Social consent */} +
    + +
    -
    - + {/* Action buttons */} +
    + {/* Desktop buttons */} +
    + + +
    + {/* Mobile buttons */} +
    + {step === 1 ? ( + + ) : ( +
    -
    -
    + )}
    @@ -225,4 +294,4 @@ const ImageUploadModal = ({ ) } -export default ImageUploadModal +export default ImageUploadModal \ No newline at end of file diff --git a/src/context/index.ts b/src/context/index.ts new file mode 100644 index 0000000..286b80c --- /dev/null +++ b/src/context/index.ts @@ -0,0 +1,3 @@ +// Context providers +export { default as AuthContext } from './AuthContext' +export { default as ContextProvider } from './ContextApi' diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..828e497 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,3 @@ +// Custom hooks +export { default as useDeCleanupContracts } from './useDeCleanupContracts' +export { default as useMobile } from './use-mobile' diff --git a/src/lib/constants/index.ts b/src/lib/constants/index.ts new file mode 100644 index 0000000..d274512 --- /dev/null +++ b/src/lib/constants/index.ts @@ -0,0 +1,35 @@ +// Application constants +export const APP_NAME = 'DeCleanup' +export const APP_VERSION = '1.0.0' + +// API endpoints +export const API_BASE_URL = + process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api' + +// Web3 constants +export const SUPPORTED_CHAINS = { + ETHEREUM: 1, + POLYGON: 137, + ARBITRUM: 42161, +} as const + +export const CONTRACT_ADDRESSES = { + DECLEANUP: process.env.NEXT_PUBLIC_DECLEANUP_CONTRACT_ADDRESS || '', +} as const + +// UI constants +export const BREAKPOINTS = { + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + '2xl': 1536, +} as const + +// Error messages +export const ERROR_MESSAGES = { + WALLET_CONNECTION_FAILED: 'Failed to connect wallet', + CONTRACT_INTERACTION_FAILED: 'Contract interaction failed', + NETWORK_NOT_SUPPORTED: 'Network not supported', + INSUFFICIENT_BALANCE: 'Insufficient balance', +} as const diff --git a/src/lib/utils/format.ts b/src/lib/utils/format.ts new file mode 100644 index 0000000..ed4c474 --- /dev/null +++ b/src/lib/utils/format.ts @@ -0,0 +1,48 @@ +import { clsx, type ClassValue } from 'clsx' +import { twMerge } from 'tailwind-merge' + +/** + * Utility function to merge Tailwind CSS classes + */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} + +/** + * Format wallet address for display + */ +export function formatAddress(address: string, length: number = 6): string { + if (!address) return '' + return `${address.slice(0, length)}...${address.slice(-length)}` +} + +/** + * Format number with commas + */ +export function formatNumber(num: number): string { + return new Intl.NumberFormat().format(num) +} + +/** + * Format currency + */ +export function formatCurrency( + amount: number, + currency: string = 'USD', +): string { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency, + }).format(amount) +} + +/** + * Format date + */ +export function formatDate(date: Date | string): string { + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }).format(new Date(date)) +} diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts new file mode 100644 index 0000000..f536ab7 --- /dev/null +++ b/src/lib/utils/index.ts @@ -0,0 +1,5 @@ +// Re-export all utility functions +export * from './format' +export * from './validation' +export * from './web3' +export * from './storage' diff --git a/src/lib/utils/storage.ts b/src/lib/utils/storage.ts new file mode 100644 index 0000000..657db80 --- /dev/null +++ b/src/lib/utils/storage.ts @@ -0,0 +1,83 @@ +/** + * Local storage utilities + */ +export const localStorage = { + get: (key: string): any => { + try { + const item = window.localStorage.getItem(key) + return item ? JSON.parse(item) : null + } catch { + return null + } + }, + + set: (key: string, value: any): void => { + try { + window.localStorage.setItem(key, JSON.stringify(value)) + } catch (error) { + console.error('Error saving to localStorage:', error) + } + }, + + remove: (key: string): void => { + try { + window.localStorage.removeItem(key) + } catch (error) { + console.error('Error removing from localStorage:', error) + } + }, + + clear: (): void => { + try { + window.localStorage.clear() + } catch (error) { + console.error('Error clearing localStorage:', error) + } + }, +} + +/** + * Session storage utilities + */ +export const sessionStorage = { + get: (key: string): any => { + try { + const item = window.sessionStorage.getItem(key) + return item ? JSON.parse(item) : null + } catch { + return null + } + }, + + set: (key: string, value: any): void => { + try { + window.sessionStorage.setItem(key, JSON.stringify(value)) + } catch (error) { + console.error('Error saving to sessionStorage:', error) + } + }, + + remove: (key: string): void => { + try { + window.sessionStorage.removeItem(key) + } catch (error) { + console.error('Error removing from sessionStorage:', error) + } + }, + + clear: (): void => { + try { + window.sessionStorage.clear() + } catch (error) { + console.error('Error clearing sessionStorage:', error) + } + }, +} + +// Storage keys +export const STORAGE_KEYS = { + USER_PREFERENCES: 'user_preferences', + WALLET_CONNECTION: 'wallet_connection', + THEME: 'theme', + LANGUAGE: 'language', +} as const diff --git a/src/lib/utils.ts b/src/lib/utils/utils.ts similarity index 100% rename from src/lib/utils.ts rename to src/lib/utils/utils.ts diff --git a/src/lib/utils/validation.ts b/src/lib/utils/validation.ts new file mode 100644 index 0000000..66bb132 --- /dev/null +++ b/src/lib/utils/validation.ts @@ -0,0 +1,48 @@ +/** + * Validate email address + */ +export function isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return emailRegex.test(email) +} + +/** + * Validate wallet address + */ +export function isValidWalletAddress(address: string): boolean { + const addressRegex = /^0x[a-fA-F0-9]{40}$/ + return addressRegex.test(address) +} + +/** + * Validate URL + */ +export function isValidUrl(url: string): boolean { + try { + new URL(url) + return true + } catch { + return false + } +} + +/** + * Validate required field + */ +export function isRequired(value: any): boolean { + return value !== null && value !== undefined && value !== '' +} + +/** + * Validate minimum length + */ +export function hasMinLength(value: string, minLength: number): boolean { + return value.length >= minLength +} + +/** + * Validate maximum length + */ +export function hasMaxLength(value: string, maxLength: number): boolean { + return value.length <= maxLength +} diff --git a/src/lib/utils/web3.ts b/src/lib/utils/web3.ts new file mode 100644 index 0000000..0cb1534 --- /dev/null +++ b/src/lib/utils/web3.ts @@ -0,0 +1,54 @@ +import { ethers } from 'ethers' + +/** + * Convert wei to ether + */ +export function weiToEther(wei: string | number): string { + return ethers.formatEther(wei.toString()) +} + +/** + * Convert ether to wei + */ +export function etherToWei(ether: string | number): string { + return ethers.parseEther(ether.toString()).toString() +} + +/** + * Get network name by chain ID + */ +export function getNetworkName(chainId: number): string { + const networks: { [key: number]: string } = { + 1: 'Ethereum Mainnet', + 137: 'Polygon', + 42161: 'Arbitrum One', + 5: 'Goerli Testnet', + 80001: 'Mumbai Testnet', + } + return networks[chainId] || 'Unknown Network' +} + +/** + * Check if address is a contract + */ +export async function isContract( + address: string, + provider: ethers.Provider, +): Promise { + try { + const code = await provider.getCode(address) + return code !== '0x' + } catch { + return false + } +} + +/** + * Get transaction status + */ +export function getTransactionStatus( + receipt: ethers.TransactionReceipt | null, +): string { + if (!receipt) return 'Pending' + return receipt.status === 1 ? 'Success' : 'Failed' +} diff --git a/src/lib/auth-req.ts b/src/services/api/auth-req.ts similarity index 100% rename from src/lib/auth-req.ts rename to src/services/api/auth-req.ts diff --git a/src/services/api/auth.ts b/src/services/api/auth.ts new file mode 100644 index 0000000..0324bf1 --- /dev/null +++ b/src/services/api/auth.ts @@ -0,0 +1,74 @@ +import { API_BASE_URL } from '@/lib/constants' +import { LoginCredentials, User } from '@/types' + +/** + * Login user + */ +export async function loginUser( + credentials: LoginCredentials, +): Promise<{ user: User; token: string }> { + const response = await fetch(`${API_BASE_URL}/auth/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(credentials), + }) + + if (!response.ok) { + throw new Error('Login failed') + } + + return response.json() +} + +/** + * Logout user + */ +export async function logoutUser(): Promise { + const response = await fetch(`${API_BASE_URL}/auth/logout`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }) + + if (!response.ok) { + throw new Error('Logout failed') + } +} + +/** + * Get current user session + */ +export async function getCurrentUser(): Promise { + try { + const response = await fetch(`${API_BASE_URL}/auth/me`, { + credentials: 'include', + }) + + if (!response.ok) { + return null + } + + return response.json() + } catch { + return null + } +} + +/** + * Refresh authentication token + */ +export async function refreshToken(): Promise<{ token: string }> { + const response = await fetch(`${API_BASE_URL}/auth/refresh`, { + method: 'POST', + credentials: 'include', + }) + + if (!response.ok) { + throw new Error('Token refresh failed') + } + + return response.json() +} diff --git a/src/services/api/contracts.ts b/src/services/api/contracts.ts new file mode 100644 index 0000000..cfdf293 --- /dev/null +++ b/src/services/api/contracts.ts @@ -0,0 +1,59 @@ +import { API_BASE_URL } from '@/lib/constants' + +/** + * Get contract metadata + */ +export async function getContractMetadata( + contractAddress: string, +): Promise { + const response = await fetch( + `${API_BASE_URL}/contracts/${contractAddress}/metadata`, + ) + + if (!response.ok) { + throw new Error('Failed to fetch contract metadata') + } + + return response.json() +} + +/** + * Get contract events + */ +export async function getContractEvents( + contractAddress: string, + fromBlock?: number, +): Promise { + const params = new URLSearchParams() + if (fromBlock) { + params.append('fromBlock', fromBlock.toString()) + } + + const response = await fetch( + `${API_BASE_URL}/contracts/${contractAddress}/events?${params}`, + ) + + if (!response.ok) { + throw new Error('Failed to fetch contract events') + } + + return response.json() +} + +/** + * Get contract balance + */ +export async function getContractBalance( + contractAddress: string, +): Promise { + const response = await fetch( + `${API_BASE_URL}/contracts/${contractAddress}/balance`, + ) + + if (!response.ok) { + throw new Error('Failed to fetch contract balance') + } + + const data = await response.json() + return data.balance +} diff --git a/src/services/api/index.ts b/src/services/api/index.ts new file mode 100644 index 0000000..d8bd130 --- /dev/null +++ b/src/services/api/index.ts @@ -0,0 +1,4 @@ +// Re-export all API services +export * from './auth' +export * from './user' +export * from './contracts' diff --git a/src/services/api/user.ts b/src/services/api/user.ts new file mode 100644 index 0000000..bb0e6f2 --- /dev/null +++ b/src/services/api/user.ts @@ -0,0 +1,50 @@ +import { API_BASE_URL } from '@/lib/constants' +import { User } from '@/types' + +/** + * Get user profile + */ +export async function getUserProfile(userId: string): Promise { + const response = await fetch(`${API_BASE_URL}/users/${userId}`) + + if (!response.ok) { + throw new Error('Failed to fetch user profile') + } + + return response.json() +} + +/** + * Update user profile + */ +export async function updateUserProfile( + userId: string, + data: Partial, +): Promise { + const response = await fetch(`${API_BASE_URL}/users/${userId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + + if (!response.ok) { + throw new Error('Failed to update user profile') + } + + return response.json() +} + +/** + * Get user statistics + */ +export async function getUserStats(userId: string): Promise { + const response = await fetch(`${API_BASE_URL}/users/${userId}/stats`) + + if (!response.ok) { + throw new Error('Failed to fetch user statistics') + } + + return response.json() +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..24a5cd5 --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,6 @@ +// API services +export * from './api' + +// Web3 services +export * from './web3/contracts' +export * from './web3/decleanup-contracts' diff --git a/src/lib/contracts.ts b/src/services/web3/contracts.ts similarity index 100% rename from src/lib/contracts.ts rename to src/services/web3/contracts.ts diff --git a/src/lib/decleanup-contracts.ts b/src/services/web3/decleanup-contracts.ts similarity index 100% rename from src/lib/decleanup-contracts.ts rename to src/services/web3/decleanup-contracts.ts diff --git a/src/services/web3/index.ts b/src/services/web3/index.ts new file mode 100644 index 0000000..493ef5e --- /dev/null +++ b/src/services/web3/index.ts @@ -0,0 +1,3 @@ +// Web3 service exports +export * from './contracts' +export * from './decleanup-contracts' diff --git a/src/app/globals.css b/src/styles/globals/globals.css similarity index 100% rename from src/app/globals.css rename to src/styles/globals/globals.css diff --git a/src/types/auth.ts b/src/types/auth.ts new file mode 100644 index 0000000..c019083 --- /dev/null +++ b/src/types/auth.ts @@ -0,0 +1,18 @@ +export interface User { + id: string + email: string + name?: string + image?: string + walletAddress?: string +} + +export interface AuthState { + user: User | null + isAuthenticated: boolean + isLoading: boolean +} + +export interface LoginCredentials { + email: string + password: string +} diff --git a/src/types/contracts.ts b/src/types/contracts.ts new file mode 100644 index 0000000..3669299 --- /dev/null +++ b/src/types/contracts.ts @@ -0,0 +1,19 @@ +export interface ContractConfig { + address: string + abi: any[] + chainId: number +} + +export interface DeCleanupContract { + address: string + abi: any[] + functions: { + [key: string]: any + } +} + +export interface ContractState { + isConnected: boolean + isConnecting: boolean + error: string | null +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..86b3e08 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,5 @@ +// Re-export all types from individual files +export * from './modal' +export * from './auth' +export * from './contracts' +export * from './ui' diff --git a/src/types/ui.ts b/src/types/ui.ts new file mode 100644 index 0000000..a153745 --- /dev/null +++ b/src/types/ui.ts @@ -0,0 +1,29 @@ +export interface ButtonProps { + variant?: + | 'default' + | 'destructive' + | 'outline' + | 'secondary' + | 'ghost' + | 'link' + size?: 'default' | 'sm' | 'lg' | 'icon' + disabled?: boolean + children: React.ReactNode + onClick?: () => void + className?: string +} + +export interface CardProps { + title?: string + description?: string + children: React.ReactNode + className?: string +} + +export interface ModalProps { + isOpen: boolean + onClose: () => void + title?: string + children: React.ReactNode + size?: 'sm' | 'md' | 'lg' | 'xl' +} From 9f09d4127d803491529af5dcfc05ed43d7cd56dc Mon Sep 17 00:00:00 2001 From: JamesVictor-o Date: Fri, 11 Jul 2025 04:59:44 +0100 Subject: [PATCH 3/3] fit dashboad interface --- src/app/dashboard/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 7002ca5..acb9542 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -24,7 +24,7 @@ export default function Page() { console.log('Uploaded images:', images) } return ( -
    +
    {/* 24 WEEKS STREAK*/} @@ -203,8 +203,8 @@ function LongButton({ text, isNotBlack }: LongButtonProps) {