diff --git a/app/(landing)/projects/page.tsx b/app/(landing)/projects/page.tsx
index 7ee3a1f3a..872641336 100644
--- a/app/(landing)/projects/page.tsx
+++ b/app/(landing)/projects/page.tsx
@@ -7,7 +7,10 @@ export const metadata: Metadata = generatePageMetadata('projects');
const ProjectsPage = () => {
return (
- Projects Page
+
Projects
+
+ {/* Project cards will be rendered here */}
+
);
};
diff --git a/app/globals.css b/app/globals.css
index 76cf79fb0..f8284d924 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,6 +1,6 @@
@import 'tailwindcss';
@import 'tw-animate-css';
-
+@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
@custom-variant dark (&:is(.dark *));
/* Custom scrollbar styles for comment modal */
@@ -70,6 +70,15 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
+ --color-office-brown: var(--office-brown);
+ --color-office-brown-darker: var(--office-brown-darker);
+ --color-warning-orange: var(--warning-orange);
+ --color-warning-orange-darker: var(--warning-orange-darker);
+ --color-blue-ish: var(--blue-ish);
+ --color-blue-ish-darker: var(--blue-ish-darker);
+ --color-success-green: var(--success-green);
+ --color-success-green-darker: var(--success-green-darker);
+ --color-error-status: var(--error-status);
/* Custom breakpoints for 4K screens */
--breakpoint-4xl: 2560px;
@@ -113,6 +122,15 @@
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
+ --office-brown: #e4dbdb;
+ --office-brown-darker: #645d5d;
+ --warning-orange: #fbe2b7;
+ --warning-orange-darker: #ad6f07;
+ --blue-ish: #c6ddf7;
+ --blue-ish-darker: #034592;
+ --success-green: #b5e3c4;
+ --success-green-darker: #04802e;
+ --error-status: #034592;
}
.dark {
@@ -147,6 +165,15 @@
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
+ --office-brown: #645d5d;
+ --office-brown-darker: #e4dbdb;
+ --warning-orange: #ad6f07;
+ --warning-orange-darker: #fbe2b7;
+ --blue-ish: #034592;
+ --blue-ish-darker: #c6ddf7;
+ --success-green: #04802e;
+ --success-green-darker: #b5e3c4;
+ --error-status: #034592;
}
@layer base {
@@ -196,7 +223,9 @@ input[type='number'] {
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
-
+.font-inter {
+ font-family: 'Inter', sans-serif;
+}
/* Dropdown menu item animations */
@keyframes dropdownItemSlideIn {
from {
diff --git a/components/landing-page/project/ProjectCard.tsx b/components/landing-page/project/ProjectCard.tsx
new file mode 100644
index 000000000..fae758e60
--- /dev/null
+++ b/components/landing-page/project/ProjectCard.tsx
@@ -0,0 +1,170 @@
+import { Progress } from '@/components/ui/progress';
+import { formatNumber } from '@/lib/utils';
+
+type ProjectCardProps = {
+ creatorName: string;
+ creatorLogo: string;
+ projectImage: string;
+ projectTitle: string;
+ projectDescription: string;
+ status: 'Validation' | 'Funding' | 'Funded' | 'Completed';
+ deadlineInDays: number;
+ milestoneRejected?: boolean;
+ votes?: {
+ current: number;
+ goal: number;
+ };
+ funding?: {
+ current: number;
+ goal: number;
+ currency: string;
+ };
+ milestones?: {
+ current: number;
+ goal: number;
+ };
+};
+function ProjectCard({
+ creatorName,
+ creatorLogo,
+ projectImage,
+ projectTitle,
+ projectDescription,
+ status,
+ deadlineInDays,
+ milestoneRejected,
+ votes,
+ funding,
+ milestones,
+}: ProjectCardProps) {
+ const getStatusStyles = () => {
+ switch (status) {
+ case 'Funding':
+ return 'bg-blue-ish border-blue-ish-darker text-blue-ish-darker';
+ case 'Funded':
+ return 'bg-transparent border-primary text-primary';
+ case 'Completed':
+ return 'bg-success-green border-success-green-darker text-success-green-darker';
+ case 'Validation':
+ return 'bg-warning-orange border-warning-orange-darker text-warning-orange-darker';
+ default:
+ return '';
+ }
+ };
+
+ const getDeadlineInfo = () => {
+ if (status === 'Completed' && milestoneRejected) {
+ return {
+ text: '1 Milestone Rejected',
+ className: 'text-red-500',
+ };
+ }
+
+ if (deadlineInDays <= 3) {
+ return {
+ text: `${deadlineInDays} days to deadline`,
+ className: 'text-error-status',
+ };
+ }
+
+ if (deadlineInDays <= 15) {
+ return {
+ text: `${deadlineInDays} days to deadline`,
+ className: 'text-warning-orange-darker',
+ };
+ }
+
+ return {
+ text: `${deadlineInDays} days to deadline`,
+ className: 'text-success-green-darker',
+ };
+ };
+
+ const deadlineInfo = getDeadlineInfo();
+
+ return (
+
+
+
+
+ {creatorName}
+
+
+
+ Category
+
+
+ {status}
+
+
+
+
+
+
+
+ {projectTitle}
+
+
+
+ {projectDescription}
+
+
+
+
+
+
+ );
+}
+
+export default ProjectCard;
diff --git a/components/ui/progress.tsx b/components/ui/progress.tsx
index fb12f8137..725f49c87 100644
--- a/components/ui/progress.tsx
+++ b/components/ui/progress.tsx
@@ -21,8 +21,12 @@ function Progress({
>
);
diff --git a/lib/utils.ts b/lib/utils.ts
index 8e7abc25e..eb0c8a5a1 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -95,3 +95,13 @@ export const maskEmail = (email: string) => {
export const generateCampaignLink = async (projectId: string) => {
return `${process.env.NEXT_PUBLIC_APP_URL}/project/${projectId}`;
};
+
+export const formatNumber = (num: number): string => {
+ if (num >= 1000000) {
+ return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
+ }
+ if (num >= 1000) {
+ return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
+ }
+ return num.toString();
+};