Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
1,254 changes: 1,178 additions & 76 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"@testing-library/user-event": "^14.4.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^7.9.5",
"web-vitals": "^3.1.0"
},
"overrides": {
"@svgr/webpack": "^8.0.1",
"@adobe/css-tools": "^4.3.1",
"postcss": "^8.4.31"
"@adobe/css-tools": "^4.3.1"
},
"scripts": {
"start": "BROWSER=none WDS_SOCKET_PORT=0 vite --port 3000",
Expand All @@ -41,8 +41,12 @@
]
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.16",
"@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.21",
"jsdom": "^26.1.0",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.16",
"vite": "^6.3.6",
"vitest": "^3.0.7"
}
Expand Down
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}
54 changes: 33 additions & 21 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
import Layout from './components/Layout';
import SocialFeed from './components/SocialFeed';
import RaiseIssue from './components/RaiseIssue';
import CampaignDetail from './components/CampaignDetail';
import UserProfile from './components/UserProfile';
import Login from './components/Login';
import './App.css';

const AppContent = () => {
const location = useLocation();
const noLayoutRoutes = ['/login'];

return (
noLayoutRoutes.includes(location.pathname) ? (
<Routes>
<Route path="/login" element={<Login />} />
</Routes>
) : (
<Layout>
<Routes>
<Route path="/" element={<SocialFeed />} />
<Route path="/raise-issue" element={<RaiseIssue />} />
<Route path="/campaign" element={<CampaignDetail />} />
<Route path="/profile" element={<UserProfile />} />
</Routes>
</Layout>
)
);
};

function App() {
return (
<div className="App">
<header className="App-header">
<img src="Octocat.png" className="App-logo" alt="logo" />
<p>
GitHub Codespaces <span className="heart">♥️</span> React
</p>
<p className="small">
Edit <code>src/App.jsx</code> and save to reload.
</p>
<p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</p>
</header>
</div>
<Router>
<AppContent />
</Router>
);
}

Expand Down
19 changes: 16 additions & 3 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@ import { expect, test } from 'vitest';
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
test('renders social feed', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeDefined();
const placeholderElement = screen.getByPlaceholderText(/What's happening?/i);
expect(placeholderElement).toBeDefined();
});

test('renders navigation links', () => {
render(<App />);
const profileLink = screen.getByText(/Profile/i);
const campaignLink = screen.getByText(/Campaigns/i);
const raiseIssueLink = screen.getByText(/Raise Issue/i);
const loginLink = screen.getByText(/Login/i);

expect(profileLink).toBeDefined();
expect(campaignLink).toBeDefined();
expect(raiseIssueLink).toBeDefined();
expect(loginLink).toBeDefined();
});
94 changes: 94 additions & 0 deletions src/components/CampaignDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';

const CampaignDetail = () => {
return (
<main className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex flex-wrap gap-2 mb-6">
<a className="text-dark-gray dark:text-gray-400 text-sm font-medium hover:text-primary" href="#">Home</a>
<span className="text-dark-gray dark:text-gray-400 text-sm font-medium">/</span>
<a className="text-dark-gray dark:text-gray-400 text-sm font-medium hover:text-primary" href="#">Campaigns</a>
<span className="text-dark-gray dark:text-gray-400 text-sm font-medium">/</span>
<span className="text-text-light dark:text-text-dark text-sm font-medium">Clean the Yamuna River</span>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2 flex flex-col gap-8">
<div className="flex flex-col gap-3">
<p className="text-text-light dark:text-white text-4xl font-black leading-tight tracking-[-0.033em]">Campaign to Clean the Yamuna River</p>
<p className="text-dark-gray dark:text-gray-400 text-lg font-normal leading-normal">Join us in restoring one of India's most sacred rivers to its pristine glory for future generations.</p>
</div>
<div className="w-full bg-center bg-no-repeat bg-cover flex flex-col justify-end overflow-hidden rounded-xl min-h-96" style={{ backgroundImage: 'url("https://lh3.googleusercontent.com/aida-public/AB6AXuAtnr8Hg46rUKk7Q1rYucgecxXdNRD81by0rPofie-9_sdXPxYYUdtKAr_-B4yHtMowErxIXJAlnH7J3ShGZYO1lnI7w0-CjQWC3Ikwe5wK3PF_qH0e0v6Jh3v_16y28KODAInXN7rF8Bs41Orwf9oixXdFOBXcgQz5xQ3MOyti_6HsFfzjEArnaPFmYeBC-cXusP2ypUnKl9Y0iBdo8hsMQdUHhNjeOuUXbvPEIXJZ6cxtBpCajThWT9XmAzGzEuNqV_Rtv0MapA")' }}></div>
<div className="flex items-center gap-4 p-4 border border-light-gray dark:border-gray-800 rounded-lg bg-white dark:bg-background-dark/30">
<div className="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-12" style={{ backgroundImage: 'url("https://lh3.googleusercontent.com/aida-public/AB6AXuDnqkGmGw1Klho-w_9rOkJP31OX-3pBasKkvbsBpaTms6h6gGYU1PMEeSzUEl0hu-xrCONqrvPMMhVmIS2pknaLbfW4wZwBjBO4_soNpGbB_-9olMtZProd7k7nbV9BBF5n5oRNg3s7OFsSv3HZZub80Mc3sTxeVWXCGAguP9jeuXwdgKNP3lps5kROaCFSoPoWcOv0Mh739X49F0kEGWh5I53kgABfHB7fKnpyqMqGJifZQikDsFC3RrQtGary5mfCgUXNHgNfQQ")' }}></div>
<div className="flex-1">
<p className="text-sm text-dark-gray dark:text-gray-400">Created by</p>
<p className="font-bold text-text-light dark:text-white">Anjali Sharma</p>
</div>
<div className="flex items-center gap-2 text-secondary-accent dark:text-green-400 bg-secondary-accent/10 dark:bg-green-900/50 px-3 py-1.5 rounded-full">
<span className="material-symbols-outlined text-base">verified</span>
<span className="text-xs font-bold uppercase tracking-wider">Verified Creator</span>
</div>
</div>
<div className="border-b border-light-gray dark:border-gray-800">
<nav aria-label="Tabs" className="-mb-px flex space-x-8">
<a className="border-primary text-primary whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm" href="#">Story</a>
<a className="border-transparent text-dark-gray dark:text-gray-400 hover:text-text-light dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm" href="#">Updates</a>
<a className="border-transparent text-dark-gray dark:text-gray-400 hover:text-text-light dark:hover:text-white hover:border-gray-300 dark:hover:border-gray-600 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm" href="#">Transparency</a>
</nav>
</div>
<div className="prose prose-lg dark:prose-invert max-w-none text-dark-gray dark:text-gray-300">
<p>The Yamuna River, a lifeline for millions and a symbol of our cultural heritage, is choking. Years of pollution from industrial waste, sewage, and plastic have turned its sacred waters into a toxic stream, threatening both human health and the delicate ecosystem it supports.</p>
<p>Our mission is to organize a multi-pronged approach to clean the Yamuna. We will partner with local communities, environmental experts, and government bodies to implement large-scale clean-up drives, install water treatment solutions, and run awareness campaigns to foster a culture of respect for our rivers.</p>
<p>Your contribution, no matter how small, will directly fund:</p>
<ul>
<li>Equipment for volunteers (gloves, masks, collection tools).</li>
<li>Installation of floating barriers to trap plastic waste.</li>
<li>Educational workshops in schools and communities along the riverbank.</li>
<li>Water quality testing kits to monitor our progress transparently.</li>
</ul>
<p>Let's come together to give the Yamuna a new lease on life. Let's ensure that future generations can experience its beauty and purity. Join the movement. Be a part of the solution. Donate today.</p>
</div>
</div>
<div className="lg:col-span-1 flex flex-col gap-6 sticky top-24 self-start">
<div className="bg-white dark:bg-background-dark/30 border border-light-gray dark:border-gray-800 rounded-xl p-6 flex flex-col gap-4 shadow-sm">
<div className="flex gap-4 justify-between items-baseline">
<div>
<span className="text-3xl font-bold text-text-light dark:text-white">₹6,50,000</span>
<span className="text-dark-gray dark:text-gray-400 text-base"> raised of ₹10,00,000 goal</span>
</div>
</div>
<div className="w-full bg-light-gray dark:bg-gray-700 rounded-full h-2.5">
<div className="bg-primary h-2.5 rounded-full" style={{ width: '65%' }}></div>
</div>
<div className="flex justify-between text-sm">
<p className="text-dark-gray dark:text-gray-400"><strong className="text-text-light dark:text-white">1,204</strong> Supporters</p>
<p className="text-dark-gray dark:text-gray-400"><strong className="text-text-light dark:text-white">21</strong> Days Left</p>
</div>
</div>
<div className="bg-white dark:bg-background-dark/30 border border-light-gray dark:border-gray-800 rounded-xl p-6 flex flex-col gap-4 shadow-sm">
<h3 className="text-lg font-bold text-center text-text-light dark:text-white">Support This Cause</h3>
<div className="grid grid-cols-3 gap-3">
<button className="text-center font-bold py-3 px-2 rounded-lg bg-light-gray dark:bg-gray-800 hover:bg-primary/10 dark:hover:bg-primary/20 hover:ring-2 hover:ring-primary transition-all">₹500</button>
<button className="text-center font-bold py-3 px-2 rounded-lg bg-primary/20 dark:bg-primary/30 ring-2 ring-primary">₹1,000</button>
<button className="text-center font-bold py-3 px-2 rounded-lg bg-light-gray dark:bg-gray-800 hover:bg-primary/10 dark:hover:bg-primary/20 hover:ring-2 hover:ring-primary transition-all">₹2,500</button>
</div>
<div>
<label className="sr-only" htmlFor="custom-amount">Custom Amount</label>
<div className="relative rounded-lg">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span className="text-dark-gray dark:text-gray-400 sm:text-sm">₹</span>
</div>
<input className="block w-full rounded-lg border-light-gray dark:border-gray-700 bg-transparent py-3 pl-7 pr-12 text-text-light dark:text-white focus:ring-primary focus:border-primary" id="custom-amount" name="custom-amount" placeholder="Or enter custom amount" type="text" />
</div>
</div>
<button className="w-full flex min-w-[84px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-12 px-4 bg-secondary-accent text-white text-base font-bold leading-normal tracking-[0.015em] hover:bg-secondary-accent/90 transition-colors">
<span className="truncate">Donate Now</span>
</button>
<p className="text-xs text-center text-dark-gray dark:text-gray-500">Secure payments via UPI & Crypto</p>
</div>
</div>
</div>
</main>
);
};

export default CampaignDetail;
40 changes: 40 additions & 0 deletions src/components/Composer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';

const Composer = () => {
return (
<div className="p-4 border-b border-gray-200">
<div className="flex">
<img
src="https://lh3.googleusercontent.com/aida-public/AB6AXuB1miwEl0pkxz8KHYFc-VWzae0a2BkSrI6Aa4GtFano86mVGWgpfpAv_18ikkcaDG8cElxfQ5X5NZbsuRYv1VhyAlAIPkcY8tEdsCGFV5H9vYKrP67I81rTnqfWCwVd9nQOFXqGA79emOMAhS9k71G3se7RqhPHJIilG8Yi9hBC3yF-HPBxAicxsh8sxYRoAN3UhzWKBtakjDj-7tfWTpTMlP3Gh_gkh00rAyr5sCU7_y85mtVr5tfRhT7pVR6jpepizcbV6SyDuA"
alt="User avatar"
className="w-10 h-10 rounded-full mr-2"
/>
<textarea
className="w-full p-2 border-none focus:ring-0"
placeholder="What's happening?"
/>
</div>
<div className="flex justify-between items-center mt-2">
<div className="flex space-x-2 text-primary">
<button className="hover:bg-primary/20 p-1 rounded-full">
<span className="material-symbols-outlined">image</span>
</button>
<button className="hover:bg-primary/20 p-1 rounded-full">
<span className="material-symbols-outlined">videocam</span>
</button>
<button className="hover:bg-primary/20 p-1 rounded-full">
<span className="material-symbols-outlined">location_on</span>
</button>
<button className="hover:bg-primary/20 p-1 rounded-full">
<span className="material-symbols-outlined">ballot</span>
</button>
</div>
<button className="bg-primary text-white px-4 py-1 rounded-full font-bold">
Post
</button>
</div>
</div>
);
};

export default Composer;
69 changes: 69 additions & 0 deletions src/components/Form.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';

export const Input = ({ label, placeholder, type = 'text' }) => (
<label className="flex flex-col w-full">
<p className="text-base font-medium pb-2">{label}</p>
<input
className="form-input flex w-full min-w-0 flex-1 resize-none overflow-hidden rounded-lg text-text-light dark:text-text-dark focus:outline-0 focus:ring-2 focus:ring-primary/50 border border-border-light dark:border-border-dark bg-background-light dark:bg-white/5 focus:border-primary h-14 placeholder:text-muted-light p-[15px] text-base font-normal leading-normal"
placeholder={placeholder}
type={type}
/>
</label>
);

export const Textarea = ({ label, placeholder }) => (
<div className="flex flex-col w-full">
<p className="text-base font-medium pb-2">{label}</p>
<div className="rounded-lg border border-border-light dark:border-border-dark focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/50 overflow-hidden">
<textarea
className="form-input w-full min-h-48 resize-y border-0 bg-background-light dark:bg-white/5 p-4 focus:ring-0 text-text-light dark:text-text-dark placeholder:text-muted-light"
placeholder={placeholder}
/>
<div className="flex items-center gap-1 border-t border-border-light dark:border-border-dark bg-white dark:bg-background-dark p-2">
<button className="flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-white/10 text-muted-light dark:text-muted-dark">
<span className="material-symbols-outlined">format_bold</span>
</button>
<button className="flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-white/10 text-muted-light dark:text-muted-dark">
<span className="material-symbols-outlined">format_italic</span>
</button>
<button className="flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-white/10 text-muted-light dark:text-muted-dark">
<span className="material-symbols-outlined">format_list_bulleted</span>
</button>
<button className="flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-white/10 text-muted-light dark:text-muted-dark">
<span className="material-symbols-outlined">format_list_numbered</span>
</button>
</div>
</div>
</div>
);

export const Select = ({ label, children }) => (
<label className="flex flex-col w-full">
<p className="text-base font-medium pb-2">{label}</p>
<select className="form-select flex w-full min-w-0 flex-1 resize-none overflow-hidden rounded-lg text-text-light dark:text-text-dark focus:outline-0 focus:ring-2 focus:ring-primary/50 border border-border-light dark:border-border-dark bg-background-light dark:bg-white/5 focus:border-primary h-14 p-[15px] text-base font-normal">
{children}
</select>
</label>
);

export const FileInput = () => (
<div className="relative flex flex-col items-center justify-center w-full p-8 border-2 border-dashed border-border-light dark:border-border-dark rounded-xl bg-background-light dark:bg-white/5 hover:border-primary transition-colors">
<span className="material-symbols-outlined text-4xl text-muted-light dark:text-muted-dark">cloud_upload</span>
<p className="mt-2 text-sm text-muted-light dark:text-muted-dark"><span className="font-semibold text-primary">Click to upload</span> or drag and drop</p>
<p className="text-xs text-muted-light dark:text-muted-dark/80">SVG, PNG, JPG, MP4 or PDF (MAX. 25MB)</p>
<input className="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" multiple="" type="file"/>
</div>
);


export const Button = ({ children, primary = false }) => (
<button
className={`w-full sm:w-auto flex items-center justify-center rounded-lg h-12 px-6 text-base font-bold transition-colors ${
primary
? 'bg-primary text-white hover:bg-opacity-90'
: 'bg-gray-200 dark:bg-white/10 text-text-light dark:text-text-dark hover:bg-gray-300 dark:hover:bg-white/20'
}`}
>
{children}
</button>
);
19 changes: 19 additions & 0 deletions src/components/Layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import LeftSidebar from './LeftSidebar';
import RightSidebar from './RightSidebar';

const Layout = ({ children }) => {
return (
<div className="flex-grow w-full max-w-7xl mx-auto">
<div className="grid grid-cols-10 gap-x-6">
<LeftSidebar />
<main className="col-span-5 border-x border-border-light dark:border-border-dark min-h-screen">
{children}
</main>
<RightSidebar />
</div>
</div>
);
};

export default Layout;
Loading