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_require__.h = () => ("b95c8cbc77f901fb")
/******/ __webpack_require__.h = () => ("d78dfb4d5bc33823")
/******/ })();
/******/
/******/ /* 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__) {
"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 */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "6f3fe84a2c5b5a01"; }
/******/ __webpack_require__.h = function() { return "0d2cb1503b82df35"; }
/******/ }();
/******/
/******/ /* webpack/runtime/global */

View File

@ -541,6 +541,9 @@
.ml-0\.5 {
margin-left: calc(var(--spacing) * 0.5);
}
.ml-1 {
margin-left: calc(var(--spacing) * 1);
}
.ml-2 {
margin-left: calc(var(--spacing) * 2);
}
@ -592,12 +595,18 @@
.aspect-1313\/771 {
aspect-ratio: 1313/771;
}
.aspect-\[4\/5\] {
aspect-ratio: 4/5;
}
.aspect-\[1155\/678\] {
aspect-ratio: 1155/678;
}
.aspect-\[var\(--width\)\/var\(--height\)\] {
aspect-ratio: var(--width)/var(--height);
}
.aspect-auto {
aspect-ratio: auto;
}
.aspect-square {
aspect-ratio: 1 / 1;
}
@ -1007,6 +1016,9 @@
.gap-y-4 {
row-gap: calc(var(--spacing) * 4);
}
.gap-y-8 {
row-gap: calc(var(--spacing) * 8);
}
.gap-y-10 {
row-gap: calc(var(--spacing) * 10);
}
@ -1260,6 +1272,10 @@
--tw-gradient-position: to right in oklab;
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 {
--tw-gradient-position: to top right in oklab;
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));
}
.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 {
--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));
@ -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-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 {
--tw-gradient-via: color-mix(in srgb, oklch(21% 0.034 264.665) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@ -2086,6 +2117,9 @@
.opacity-30 {
opacity: 30%;
}
.opacity-50 {
opacity: 50%;
}
.opacity-100 {
opacity: 100%;
}
@ -2227,6 +2261,9 @@
--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 {
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 {
--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,);
@ -2549,6 +2586,13 @@
}
}
}
.hover\:bg-indigo-600 {
&:hover {
@media (hover: hover) {
background-color: var(--color-indigo-600);
}
}
}
.hover\:text-gray-600 {
&: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 {
@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'
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 { 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,
title: 'Boost your conversion rate',
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',
title: 'Zanzibar Schools',
imageUrl: '/portfolio/zanzibar.jpg',
impactGoal: 'Build 10 sustainable classrooms',
location: 'Zanzibar, Tanzania',
category: 'Education',
country: 'Tanzania',
status: 'Active',
fundingType: 'Donation',
fundedPercent: 68,
contributors: 128,
phase: 'Phase 1',
datetime: '2020-03-16',
},
{
href: '#',
},
{
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: '#',
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,
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: '#',
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: 1,
title: 'Boost your conversion rate',
},
{
id: 4,
title: 'Township WiFi Project',
imageUrl: '/portfolio/township.jpg',
impactGoal: 'Free internet for 1,000 students',
location: 'Johannesburg, South Africa',
category: 'Infrastructure',
country: 'South Africa',
status: 'Active',
fundingType: 'Pay-it-Forward',
fundedPercent: 32,
contributors: 45,
phase: 'Phase 2',
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',
},
{
id: 5,
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',
datetime: '2020-03-16',
},
// More posts...
href: '#',
},
]
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'
? posts
: posts.filter(post => post.phase === activePhase)
const handleFilterChange = (type: string, value: string) => {
setFiltersState(prev => ({ ...prev, [type]: value }))
}
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 (
<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="flex flex-col items-center space-y-8">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-4xl font-semibold tracking-tight text-balance text-gray-900 sm:text-5xl">
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 className="text-center max-w-2xl mx-auto">
<h2 className="text-4xl font-semibold text-gray-900 sm:text-5xl">Explore Impact Projects</h2>
<p className="mt-2 text-lg text-gray-600">Back causes that shape a better future.</p>
</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">
<time dateTime={post.datetime} className="mr-8">
{post.phase}
</time>
</div>
<h3 className="mt-3 text-lg/6 font-semibold text-white">
<a href={post.href}>
<span className="absolute inset-0" />
{post.title}
</a>
</h3>
<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">
Read more
<ChevronRightIcon className="h-5 w-5 flex-none" aria-hidden="true" />
</a>
</article>
{/* Filters */}
<div className="mt-10 grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm">
{Object.entries(filters).map(([filterType, options]) => (
<select
key={filterType}
onChange={(e) => handleFilterChange(filterType, e.target.value)}
className="w-full border border-gray-300 rounded-md p-2"
value={filtersState[filterType as keyof typeof filtersState]}
>
{options.map(opt => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
))}
</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>
)