Skip to content

Commit

Permalink
feat: project-create pages implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
maricdiranovic committed Feb 11, 2025
1 parent ba6fe1c commit 255355c
Show file tree
Hide file tree
Showing 36 changed files with 1,385 additions and 13 deletions.
12 changes: 12 additions & 0 deletions client/src/constants/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ export const NAV = [
label: 'Dashboards',
href: '/dashboards',
},
{
label: "My Details",
href: '/my-details'
},
{
label: "My Projects",
href: "/my-projects"
},
{
label: "My Funding",
href: "/my-funding"
},
{
label: 'About',
href: 'https://forainitiative.org/about/',
Expand Down
25 changes: 23 additions & 2 deletions client/src/containers/cards/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,37 @@ import { Funder } from 'types/funder';
import { Project } from 'types/project';

import Card from './card';
import Icon from 'components/icon';
import ADD_ICON_SVG from 'svgs/icons/add.svg?sprite';
import Link from 'next/link';

export interface CardsProps {
data: (Funder | Project)[] | Partial<Funder>[] | Partial<Project>[];
theme?: 'green' | 'grey';
theme?: 'green' | 'grey' | 'bg-green-0';
pathname: string;
project?: boolean;
}

const Cards = ({ data = [], theme = 'grey', pathname }: CardsProps) => {
const Cards = ({ data = [], theme = 'grey', pathname, project = false }: CardsProps) => {
return (
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{project && (
<div
className={
'flex flex-col items-center justify-center p-8 bg-green-0 text-[24px] text-center space-x-2'
}
>
<p className="font-[400]">
{' '}
Add new <br /> Project{' '}
</p>
<div className="mt-5">
<Link href="/my-projects/new">
<Icon icon={ADD_ICON_SVG} className="w-10 h-10 font-medium" />
</Link>
</div>
</div>
)}
{data.map((item) => (
<Card theme={theme} key={item.id} href={`${pathname}/${item.id}`} {...item} />
))}
Expand Down
35 changes: 35 additions & 0 deletions client/src/containers/funding/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ProjectCard from 'containers/projects/ui/Card';
import React from 'react';
import Icon from 'components/icon';
import eye from 'svgs/icons/eye.svg?sprite';
import upload from 'svgs/icons/upload.svg?sprite';
import CHEVRON_LEFT from 'svgs/icons/arrow-left.svg?sprite';

const FundingHome = () => {
return (
<ProjectCard title="">
<div className="flex flex-col items-center justify-center font-display h-[50vh]">
<h2 className="md:text-2xl">You have no funding</h2>
<h2 className="md:text-2xl">Reported for this project</h2>
<div className="md:w-[406px] text-center font-sans my-5">
<p>
Lorem ipsum dolor sit amet consectetur. Convallis fusce neque odio nunc elementum
habitant sit sagittis.
</p>
</div>
<button className="bg-green-10 text-black rounded-lg p-2"> Report Funding</button>
</div>

<div className="flex items-center justify-end">
<button className="flex items-center p-2 mt-6 text-black transition bg-transparent border rounded-lg hover:bg-blue-700">
<span className="pr-2">
<Icon icon={CHEVRON_LEFT} className="w-3 h-3" />
</span>
Focus Area{' '}
</button>
</div>
</ProjectCard>
);
};

export default FundingHome;
79 changes: 69 additions & 10 deletions client/src/containers/header/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,36 @@ const Header = () => {
const { pathname } = useRouter();
const { data: session } = useSession();
const isAuthPath = useMemo(() => pathname.includes('/auth'), [pathname]);

const NAV_ITEMS = useMemo(() => {
return NAV.filter((n) => !n.footer && !(session && n.auth));
return NAV.filter(
(n) =>
!n.footer && !(session && n.auth) && (!n.label.includes('My') || n.label === 'My Projects')
);
}, [session]);
const isMyPage = useMemo(() => pathname.startsWith('/my-') || pathname.includes('/projects/new'), [pathname]);

const MY_NAV_ITEMS = useMemo(() => {
return NAV.filter((n) => n.label.includes('My'));
}, [])


const isActiveNavItem = useCallback(
(href: string) => {
(href: string, isMyNav = false) => {
if (isAuthPath) return false;

return pathname.includes(href) && pathname !== '/';
const isActive = pathname.includes(href) && pathname !== '/';
return isActive ? (isMyNav ? 'border-b-2 border-green-0 px-2' : 'rounded-lg bg-green-0') : '';
},
[pathname, isAuthPath]
);


// Filter only "My" related items if we are on a "My" page
// const filteredNavItems = isMyPage
// ? NAV_ITEMS.filter((item) => item.label.includes('My'))
// : NAV_ITEMS;

return (
<header
className={cx({
Expand All @@ -58,34 +75,35 @@ const Header = () => {
{/* NAV */}
<nav className="flex items-center justify-between">
<ul className="flex items-center justify-between space-x-3">
{NAV_ITEMS.map((item) => {
{!isMyPage && NAV_ITEMS.map((item) => {
const { href, label, filled, target, rel, className } = item;

return (
<li key={href}>
{target === '_blank' && (
{target === '_blank' ? (
<a
href={href}
target={target}
rel={rel}
className={cx({
className={cx(
isActiveNavItem(href),
{
'text-base font-semibold py-2 px-7': true,
'hover:rounded-lg hover:bg-grey-60/75': pathname !== href,
'rounded-lg bg-green-0': isActiveNavItem(href),

'text-grey-0 hover:underline': !filled,
})}
>
{label}
</a>
)}
{!target && (
) : (
<Link
href={href}
className={cx(
'text-base font-semibold py-2 px-7',
isActiveNavItem(href),
{
'hover:rounded-lg hover:bg-grey-60/75': !pathname.includes(href),
'rounded-lg bg-green-0': isActiveNavItem(href),
'pointer-events-none select-none':
pathname.includes(href) && pathname !== '/',
},
Expand All @@ -98,6 +116,47 @@ const Header = () => {
</li>
);
})}

{/* "MY NAVIGATION: SHOW ONLY MY ITEMS " */}
{isMyPage &&
MY_NAV_ITEMS.map(({ href, label, filled, target, rel, className }) => (
<li key={href}>
{target === '_blank' ? (
<a
href={href}
target={target}
rel={rel}
className={cx(
'text-base font-semibold py-2 px-7',
isActiveNavItem(href, true),
{
'hover:rounded-lg hover:bg-grey-60/75': pathname !== href,
'text-grey-0 hover:underline': !filled,
},
className
)}
>
{label}
</a>
) : (
<Link
href={href}
className={cx(
'text-base font-semibold py-2 px-7',
isActiveNavItem(href, true),
{
'hover:rounded-lg hover:bg-grey-60/75': !pathname.includes(href),
'pointer-events-none select-none':
pathname.includes(href) && pathname !== '/',
},
className
)}
>
{label}
</Link>
)}
</li>
))}
{session && (
<Button type="button" theme="transparent">
<CircleUserIcon />
Expand Down
12 changes: 12 additions & 0 deletions client/src/containers/my-projects/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import MyProjectList from './list'

const MyProject = () => {
return (
<>
<MyProjectList />
</>
)
}

export default MyProject
1 change: 1 addition & 0 deletions client/src/containers/my-projects/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './component';
148 changes: 148 additions & 0 deletions client/src/containers/my-projects/list/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import cx from 'classnames';
import { Menu } from '@headlessui/react';
import MyProjectsSentence from 'containers/sentence/my-projects';
import Wrapper from 'containers/wrapper';
import { setSort } from 'store/myProjects';
import { useFetchMemberProjects, useMyInfinityProjects } from 'hooks/my-projects';
import { useCallback } from 'react';
import { useAppDispatch, useAppSelector } from 'store/hooks';
import Button from 'components/button';
import Icon from 'components/icon';
import Loading from 'components/loading';
import CHEVRON_DOWN_SVG from 'svgs/ui/chevron-down.svg?sprite';
import Cards from 'containers/cards';
import Link from 'next/link';

const MyProjectList = () => {
const { sort } = useAppSelector((state) => state['/myProjects']);

const dispatch = useAppDispatch();
const {
data: myProjects,
fetchNextPage: fetchNextPageProjects,
hasNextPage: hasNextProjectsPage,
isFetchingNextPage: isFetchingNextProjectsPage,
isFetching: isFetchingMyProjects,
isFetched: isFetchedMyProjects,
} = useMyInfinityProjects({
sort,
perPage: 12,
});

const LOADING = isFetchingMyProjects && !isFetchingMyProjects;

const handleSortMyProjects = useCallback(
(value) => {
dispatch(setSort({ field: 'name', order: value }));
},
[dispatch]
);

const handleOnShowMore = useCallback(() => {
fetchNextPageProjects();
}, [fetchNextPageProjects]);

return (
<>
<Wrapper>
<div className="py-8">
{!!myProjects.length && (
<>
<MyProjectsSentence type="projects" />
<div className="flex justify-between mt-10">
<Menu as="div" className="relative">
<Menu.Button className="flex items-center space-x-2">
<p className="font-semibold">Sort by</p>
<Icon
icon={CHEVRON_DOWN_SVG}
className={cx({
'w-3 h-3': true,
})}
/>
</Menu.Button>
<Menu.Items className="absolute flex flex-col py-2 bg-white rounded-md shadow-lg focus:outline-none">
<Menu.Item>
{({ active }) => (
<button
className={cx({
'px-4 py-3': true,
'bg-grey-20/20': active,
})}
type="button"
onClick={() => handleSortMyProjects('asc')}
>
A - Z
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
className={cx({
'px-4 py-3': true,
'bg-grey-20/20': active,
})}
type="button"
onClick={() => handleSortMyProjects('desc')}
>
Z - A
</button>
)}
</Menu.Item>
</Menu.Items>
</Menu>
</div>
</>
)}
</div>

{!myProjects.length && !LOADING && (
<div className="flex items-center justify-center px-5 grow ">
<div className="flex flex-col items-center py-12 pb-20 space-y-4">
<p className="md:text-[40px] font-display font-medium">You have no projects added.</p>
<p className="max-w-sm text-center text-grey-20">
Sorry, we have searched in our entire database but we couldn&apos;t find any results
fitting your search criteria.
</p>

<button className="px-4 py-2 text-black rounded-lg bg-green-10">
<Link href="/my-projects/new">Add Product</Link>
</button>
</div>
</div>
)}

{!!myProjects.length && (
<div className="pb-10">
<Cards
pathname="/members/projects"
theme="bg-green-0"
data={myProjects}
project={true}
/>
</div>
)}

{hasNextProjectsPage && (
<div className="flex justify-center py-10">
<Button
type="button"
theme="black-alt"
size="xl"
className="overflow-hidden"
onClick={handleOnShowMore}
>
Show more
<Loading
visible={isFetchingNextProjectsPage}
className="absolute flex items-center justify-center w-full h-full bg-white"
/>
</Button>
</div>
)}
</Wrapper>
</>
);
};

export default MyProjectList;
1 change: 1 addition & 0 deletions client/src/containers/my-projects/list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './component';
Loading

0 comments on commit 255355c

Please sign in to comment.