ad project card

This commit is contained in:
sasha-astiadi 2025-06-12 14:14:39 +02:00
parent fe3b7753f4
commit de751485d9
28 changed files with 249 additions and 101 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -130,7 +130,7 @@
/******/ /******/
/******/ /* webpack/runtime/getFullHash */ /******/ /* webpack/runtime/getFullHash */
/******/ (() => { /******/ (() => {
/******/ __webpack_require__.h = () => ("b95c8cbc77f901fb") /******/ __webpack_require__.h = () => ("d78dfb4d5bc33823")
/******/ })(); /******/ })();
/******/ /******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ /* webpack/runtime/hasOwnProperty shorthand */

View File

@ -25,7 +25,7 @@ eval(__webpack_require__.ts("Promise.resolve(/*! import() eager */).then(__webpa
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"2d0f81ac17c0\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9zdHlsZXMvdGFpbHdpbmQuY3NzIiwibWFwcGluZ3MiOiI7QUFBQSwrREFBZSxjQUFjO0FBQzdCLElBQUksSUFBVSxJQUFJLGlCQUFpQiIsInNvdXJjZXMiOlsid2VicGFjazovL19OX0UvLi9zcmMvc3R5bGVzL3RhaWx3aW5kLmNzcz83NTBkIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiMmQwZjgxYWMxN2MwXCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/styles/tailwind.css\n")); eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"c8fca2e32337\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9zdHlsZXMvdGFpbHdpbmQuY3NzIiwibWFwcGluZ3MiOiI7QUFBQSwrREFBZSxjQUFjO0FBQzdCLElBQUksSUFBVSxJQUFJLGlCQUFpQiIsInNvdXJjZXMiOlsid2VicGFjazovL19OX0UvLi9zcmMvc3R5bGVzL3RhaWx3aW5kLmNzcz83NTBkIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IFwiYzhmY2EyZTMyMzM3XCJcbmlmIChtb2R1bGUuaG90KSB7IG1vZHVsZS5ob3QuYWNjZXB0KCkgfVxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/styles/tailwind.css\n"));
/***/ }) /***/ })

View File

@ -192,7 +192,7 @@
/******/ /******/
/******/ /* webpack/runtime/getFullHash */ /******/ /* webpack/runtime/getFullHash */
/******/ !function() { /******/ !function() {
/******/ __webpack_require__.h = function() { return "6f3fe84a2c5b5a01"; } /******/ __webpack_require__.h = function() { return "0d2cb1503b82df35"; }
/******/ }(); /******/ }();
/******/ /******/
/******/ /* webpack/runtime/global */ /******/ /* webpack/runtime/global */

View File

@ -541,6 +541,9 @@
.ml-0\.5 { .ml-0\.5 {
margin-left: calc(var(--spacing) * 0.5); margin-left: calc(var(--spacing) * 0.5);
} }
.ml-1 {
margin-left: calc(var(--spacing) * 1);
}
.ml-2 { .ml-2 {
margin-left: calc(var(--spacing) * 2); margin-left: calc(var(--spacing) * 2);
} }
@ -592,12 +595,18 @@
.aspect-1313\/771 { .aspect-1313\/771 {
aspect-ratio: 1313/771; aspect-ratio: 1313/771;
} }
.aspect-\[4\/5\] {
aspect-ratio: 4/5;
}
.aspect-\[1155\/678\] { .aspect-\[1155\/678\] {
aspect-ratio: 1155/678; aspect-ratio: 1155/678;
} }
.aspect-\[var\(--width\)\/var\(--height\)\] { .aspect-\[var\(--width\)\/var\(--height\)\] {
aspect-ratio: var(--width)/var(--height); aspect-ratio: var(--width)/var(--height);
} }
.aspect-auto {
aspect-ratio: auto;
}
.aspect-square { .aspect-square {
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
} }
@ -1007,6 +1016,9 @@
.gap-y-4 { .gap-y-4 {
row-gap: calc(var(--spacing) * 4); row-gap: calc(var(--spacing) * 4);
} }
.gap-y-8 {
row-gap: calc(var(--spacing) * 8);
}
.gap-y-10 { .gap-y-10 {
row-gap: calc(var(--spacing) * 10); row-gap: calc(var(--spacing) * 10);
} }
@ -1260,6 +1272,10 @@
--tw-gradient-position: to right in oklab; --tw-gradient-position: to right in oklab;
background-image: linear-gradient(var(--tw-gradient-stops)); background-image: linear-gradient(var(--tw-gradient-stops));
} }
.bg-gradient-to-t {
--tw-gradient-position: to top in oklab;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-tr { .bg-gradient-to-tr {
--tw-gradient-position: to top right in oklab; --tw-gradient-position: to top right in oklab;
background-image: linear-gradient(var(--tw-gradient-stops)); background-image: linear-gradient(var(--tw-gradient-stops));
@ -1352,6 +1368,13 @@
} }
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
} }
.from-black\/80 {
--tw-gradient-from: color-mix(in srgb, #000 80%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-from: color-mix(in oklab, var(--color-black) 80%, transparent);
}
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-gray-100 { .from-gray-100 {
--tw-gradient-from: var(--color-gray-100); --tw-gradient-from: var(--color-gray-100);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
@ -1454,6 +1477,14 @@
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position); --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-via-stops); --tw-gradient-stops: var(--tw-gradient-via-stops);
} }
.via-black\/50 {
--tw-gradient-via: color-mix(in srgb, #000 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-gradient-via: color-mix(in oklab, var(--color-black) 50%, transparent);
}
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-via-stops);
}
.via-gray-900\/40 { .via-gray-900\/40 {
--tw-gradient-via: color-mix(in srgb, oklch(21% 0.034 264.665) 40%, transparent); --tw-gradient-via: color-mix(in srgb, oklch(21% 0.034 264.665) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) { @supports (color: color-mix(in lab, red, red)) {
@ -2086,6 +2117,9 @@
.opacity-30 { .opacity-30 {
opacity: 30%; opacity: 30%;
} }
.opacity-50 {
opacity: 50%;
}
.opacity-100 { .opacity-100 {
opacity: 100%; opacity: 100%;
} }
@ -2227,6 +2261,9 @@
--tw-drop-shadow: var(--tw-drop-shadow-size); --tw-drop-shadow: var(--tw-drop-shadow-size);
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
} }
.filter {
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
.backdrop-blur-md { .backdrop-blur-md {
--tw-backdrop-blur: blur(var(--blur-md)); --tw-backdrop-blur: blur(var(--blur-md));
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
@ -2549,6 +2586,13 @@
} }
} }
} }
.hover\:bg-indigo-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-indigo-600);
}
}
}
.hover\:text-gray-600 { .hover\:text-gray-600 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@ -2556,6 +2600,13 @@
} }
} }
} }
.hover\:text-indigo-300 {
&:hover {
@media (hover: hover) {
color: var(--color-indigo-300);
}
}
}
.hover\:text-indigo-700 { .hover\:text-indigo-700 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {

File diff suppressed because one or more lines are too long

BIN
public/portfolio/rio.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

BIN
public/projects/rio.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

View File

@ -1,124 +1,186 @@
'use client' 'use client'
import { Button } from './button'
import { Heading, Subheading } from './text'
import { clsx } from 'clsx'
import { ChevronRightIcon } from '@heroicons/react/20/solid'
import { useState } from 'react' import { useState } from 'react'
import { ChevronRightIcon } from '@heroicons/react/20/solid'
import { clsx } from 'clsx'
import { Button } from './button'
const phases = ['All', 'Phase 1', 'Phase 2', 'Phase 3'] const filters = {
category: ['All', 'Education', 'Health', 'Climate', 'Infrastructure'],
country: ['All', 'South Africa', 'Brazil', 'Tanzania'],
status: ['All', 'Active', 'Funded', 'Closed'],
funding: ['All', 'Donation', 'Investment', 'Pay-it-Forward'],
}
const posts = [ const projects = [
{ {
id: 1, id: 1,
title: 'Boost your conversion rate', title: 'Zanzibar Schools',
href: '#', imageUrl: '/portfolio/zanzibar.jpg',
description: impactGoal: 'Build 10 sustainable classrooms',
'Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde..', location: 'Zanzibar, Tanzania',
imageUrl: category: 'Education',
'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', country: 'Tanzania',
status: 'Active',
fundingType: 'Donation',
fundedPercent: 68,
contributors: 128,
phase: 'Phase 1', phase: 'Phase 1',
datetime: '2020-03-16', href: '#',
}, },
{ {
id: 2, id: 2,
title: 'Boost your conversion rate', title: 'Kayamandi Health Hub',
imageUrl: '/portfolio/kayamandi.jpg',
impactGoal: 'Provide care to 500 families',
location: 'Stellenbosch, South Africa',
category: 'Health',
country: 'South Africa',
status: 'Funded',
fundingType: 'Investment',
fundedPercent: 100,
contributors: 302,
phase: 'Phase 2',
href: '#', href: '#',
description: },
'Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde..', {
imageUrl:
'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80',
phase: 'Phase 1',
datetime: '2020-03-16',
},
{
id: 3, id: 3,
title: 'Boost your conversion rate', title: 'Southern Cape Solar',
imageUrl: '/portfolio/southern.jpg',
impactGoal: 'Power 200 homes with clean energy',
location: 'Southern Cape, South Africa',
category: 'Climate',
country: 'South Africa',
status: 'Active',
fundingType: 'Investment',
fundedPercent: 54,
contributors: 91,
phase: 'Phase 3',
href: '#', href: '#',
description: },
'Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde. ', {
imageUrl: id: 4,
'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', title: 'Township WiFi Project',
phase: 'Phase 1', imageUrl: '/portfolio/township.jpg',
datetime: '2020-03-16', impactGoal: 'Free internet for 1,000 students',
}, location: 'Johannesburg, South Africa',
{ category: 'Infrastructure',
id: 1, country: 'South Africa',
title: 'Boost your conversion rate', status: 'Active',
fundingType: 'Pay-it-Forward',
fundedPercent: 32,
contributors: 45,
phase: 'Phase 2',
href: '#', href: '#',
description: },
'Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde. ', {
imageUrl: id: 5,
'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', title: 'Durban Climate Garden',
imageUrl: '/portfolio/durban.jpg',
impactGoal: 'Grow 5 community food forests',
location: 'Durban, South Africa',
category: 'Climate',
country: 'South Africa',
status: 'Funded',
fundingType: 'Donation',
fundedPercent: 100,
contributors: 212,
phase: 'Phase 3',
href: '#',
},
{
id: 6,
title: 'Rio Digital Youth Labs',
imageUrl: '/portfolio/rio.jpg',
impactGoal: 'Train 1,000 young coders',
location: 'Rio de Janeiro, Brazil',
category: 'Education',
country: 'Brazil',
status: 'Active',
fundingType: 'Pay-it-Forward',
fundedPercent: 45,
contributors: 89,
phase: 'Phase 1', phase: 'Phase 1',
datetime: '2020-03-16', href: '#',
}, },
// More posts...
] ]
export default function ProjectCard() { export default function ProjectCard() {
const [activePhase, setActivePhase] = useState('All') const [filtersState, setFiltersState] = useState({
category: 'All',
country: 'All',
status: 'All',
funding: 'All',
})
const filteredPosts = activePhase === 'All' const handleFilterChange = (type: string, value: string) => {
? posts setFiltersState(prev => ({ ...prev, [type]: value }))
: posts.filter(post => post.phase === activePhase) }
const filteredProjects = projects.filter(project => {
return (
(filtersState.category === 'All' || project.category === filtersState.category) &&
(filtersState.country === 'All' || project.country === filtersState.country) &&
(filtersState.status === 'All' || project.status === filtersState.status) &&
(filtersState.funding === 'All' || project.fundingType === filtersState.funding)
)
})
return ( return (
<div className="bg-white py-24 sm:py-32"> <div className="bg-white py-24">
<div className="mx-auto max-w-7xl px-6 lg:px-8"> <div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="flex flex-col items-center space-y-8"> <div className="text-center max-w-2xl mx-auto">
<div className="mx-auto max-w-2xl text-center"> <h2 className="text-4xl font-semibold text-gray-900 sm:text-5xl">Explore Impact Projects</h2>
<h2 className="text-4xl font-semibold tracking-tight text-balance text-gray-900 sm:text-5xl"> <p className="mt-2 text-lg text-gray-600">Back causes that shape a better future.</p>
From foundation to contribution
</h2>
<p className="mt-2 text-lg/8 text-gray-600">Deliver transformational experiences anywhere</p>
</div>
<div className="flex justify-center gap-4">
{phases.map((phase) => (
<button
key={phase}
onClick={() => setActivePhase(phase)}
className={clsx(
'px-4 py-2 rounded-full text-sm font-medium transition-colors',
activePhase === phase
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
)}
>
{phase}
</button>
))}
</div>
</div> </div>
<div className="mx-auto mt-16 grid max-w-2xl auto-rows-fr grid-cols-1 gap-4 sm:mt-20 lg:mx-0 lg:max-w-none lg:grid-cols-3">
{filteredPosts.map((post) => (
<article
key={post.id}
className="relative isolate flex flex-col justify-end overflow-hidden rounded-2xl bg-gray-900 px-8 aspect-square"
>
<img alt="" src={post.imageUrl} className="absolute inset-0 -z-10 size-full object-cover aspect-square" />
<div className="absolute inset-0 -z-10 bg-linear-to-t from-gray-900 via-gray-900/40" />
<div className="absolute inset-0 -z-10 rounded-2xl ring-1 ring-gray-900/10 ring-inset" />
<div className="flex flex-wrap items-center gap-y-1 overflow-hidden text-sm/6 text-gray-300"> {/* Filters */}
<time dateTime={post.datetime} className="mr-8"> <div className="mt-10 grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm">
{post.phase} {Object.entries(filters).map(([filterType, options]) => (
</time> <select
</div> key={filterType}
<h3 className="mt-3 text-lg/6 font-semibold text-white"> onChange={(e) => handleFilterChange(filterType, e.target.value)}
<a href={post.href}> className="w-full border border-gray-300 rounded-md p-2"
<span className="absolute inset-0" /> value={filtersState[filterType as keyof typeof filtersState]}
{post.title} >
</a> {options.map(opt => (
</h3> <option key={opt} value={opt}>{opt}</option>
<p className="mt-5 text-sm/7 leading-tight text-gray-100">{post.description}</p> ))}
<a href={post.href} className="mt-6 inline-flex items-center gap-x-2 text-sm font-semibold text-indigo-400"> </select>
Read more
<ChevronRightIcon className="h-5 w-5 flex-none" aria-hidden="true" />
</a>
</article>
))} ))}
</div> </div>
{/* Cards */}
<div className="mt-16 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProjects.map(project => (
<div key={project.id} className="relative rounded-2xl overflow-hidden bg-gray-900 text-white shadow-lg aspect-[4/5]">
<img src={project.imageUrl} alt={project.title} className="absolute inset-0 w-full h-full object-cover opacity-50" />
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/50 to-transparent" />
<div className="relative p-6 flex flex-col justify-end h-full">
<div className="text-sm text-indigo-300 font-semibold">{project.impactGoal}</div>
<h3 className="text-2xl font-bold mt-2">{project.title}</h3>
<p className="text-sm mt-1">{project.location}</p>
<div className="mt-4 text-sm">
<span className="text-gray-300">{project.fundedPercent}% funded</span> · <span className="text-gray-300">{project.contributors} backers</span>
</div>
<div className="mt-6 flex items-center justify-between gap-2">
<a href={project.href} className="text-indigo-400 hover:text-indigo-300 font-semibold flex items-center">
Learn More <ChevronRightIcon className="ml-1 h-4 w-4" />
</a>
<Button variant="secondary" className="text-sm bg-indigo-500 hover:bg-indigo-600 text-white px-3 py-1 rounded-md">
Support
</Button>
</div>
</div>
</div>
))}
</div>
{filteredProjects.length === 0 && (
<div className="text-center mt-12 text-gray-500 text-lg">No projects match your filter criteria.</div>
)}
</div> </div>
</div> </div>
) )