mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-02-11 09:34:24 +01:00
update: unify category filter, update projects sorting, update nav logo
This commit is contained in:
BIN
public/assets/images/logo-light.png
Normal file
BIN
public/assets/images/logo-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -41,8 +41,9 @@ export default function NavDesktop() {
|
||||
<div className="content-container">
|
||||
<div className="flex items-center">
|
||||
<Link to="/">
|
||||
<h2 className="text-h5 font-bold mr-40 lg:mr-64">
|
||||
<img className='h-40' src={ASSETS.Logo} alt="Bolt fun logo" />
|
||||
<h2 className="text-body5 md:text-h5 font-bold mr-24 md:mr-40 lg:mr-64 flex items-center gap-16">
|
||||
<img className='h-40' src={ASSETS.LogoLight} alt="Lightning Landscape Logo" />
|
||||
<span className="font-extrabold">Lightning Landscape</span>
|
||||
</h2>
|
||||
</Link>
|
||||
<Button color="primary" size="sm" variant="text" className="ml-auto">Submit project <BiRocket /></Button>
|
||||
|
||||
BIN
src/assets/images/logo-light.png
Normal file
BIN
src/assets/images/logo-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -3,7 +3,8 @@ import Images_ExploreHeader2 from './images/shock-the-web.jpg'
|
||||
import Category_BG from './images/category-bg.jpg'
|
||||
import Image_Hottest_Header from './images/hottest.jfif'
|
||||
import Logo from './images/logo.png'
|
||||
import LogoLight from './images/logo-light.png'
|
||||
|
||||
const ASSETS = { Images_ExploreHeader1, Images_ExploreHeader2, Image_Hottest_Header, Logo, Category_BG }
|
||||
const ASSETS = { Images_ExploreHeader1, Images_ExploreHeader2, Image_Hottest_Header, Logo, LogoLight, Category_BG }
|
||||
|
||||
export default ASSETS;
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { CategoryList, useAllCategoriesQuery } from 'src/graphql';
|
||||
import { CategoryList, useAllCategoriesQuery, useGetFiltersQuery } from 'src/graphql';
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import { useCarousel } from 'src/utils/hooks';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
@@ -31,7 +31,7 @@ export default function Categories(props: Props) {
|
||||
align: 'start', slidesToScroll: 2,
|
||||
containScroll: "trimSnaps",
|
||||
})
|
||||
const { data, loading } = useAllCategoriesQuery();
|
||||
const { data, loading } = useGetFiltersQuery();
|
||||
|
||||
|
||||
if (loading || !data)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useExplorePageQuery } from 'src/graphql';
|
||||
import ProjectsGrid from './ProjectsGrid/ProjectsGrid';
|
||||
import { Helmet } from "react-helmet";
|
||||
import Categories, { Category } from '../../Components/Categories/Categories';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import Header from './Header/Header';
|
||||
import Button from 'src/Components/Button/Button';
|
||||
import { useAppDispatch } from 'src/utils/hooks';
|
||||
@@ -15,7 +15,7 @@ import { NetworkStatus } from '@apollo/client';
|
||||
import { FiSliders } from 'react-icons/fi';
|
||||
import { HiOutlineChevronDoubleDown } from 'react-icons/hi'
|
||||
import { ProjectsFilters } from './Filters/FiltersModal';
|
||||
import { getFiltersFromUrl, useUpdateUrlWithFilters } from './helpers';
|
||||
import { useUpdateUrlWithFilters } from './helpers';
|
||||
import { withBasicProvider } from 'src/utils/helperFunctions';
|
||||
import { ProjectsFiltersProvider, useProjectsFilters } from './filters-context';
|
||||
|
||||
@@ -35,12 +35,9 @@ const PAGE_SIZE = 28;
|
||||
function ExplorePage() {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { filters, updateFilters } = useProjectsFilters();
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useState<Category | null>(null)
|
||||
const projectsLength = useRef<number>(0);
|
||||
const [showDeadProjects, setShowDeadProjects] = useState(false)
|
||||
const [canFetchMore, setCanFetchMore] = useState(true);
|
||||
|
||||
useUpdateUrlWithFilters(filters)
|
||||
@@ -50,13 +47,12 @@ function ExplorePage() {
|
||||
let filter: QueryFilter = {}
|
||||
let hasSearchFilters = false;
|
||||
|
||||
const defaultFilters = {} as const;
|
||||
|
||||
if (filters?.categories) {
|
||||
filter.categoryId = filters?.categories.map(c => c.id);
|
||||
filter.categoryId = filters?.categories.map(c => c.id!);
|
||||
hasSearchFilters = true;
|
||||
}
|
||||
if (selectedCategory?.id) filter.categoryId = [selectedCategory?.id]
|
||||
|
||||
|
||||
if (filters?.tags) {
|
||||
filter.tags = filters?.tags.map(t => t.id)
|
||||
@@ -83,7 +79,7 @@ function ExplorePage() {
|
||||
return { queryFilters: null, hasSearchFilters }
|
||||
|
||||
return { queryFilters: filter, hasSearchFilters };
|
||||
}, [filters, selectedCategory?.id])
|
||||
}, [filters])
|
||||
|
||||
const { data, networkStatus, error, fetchMore } = useExplorePageQuery({
|
||||
variables: {
|
||||
@@ -102,7 +98,6 @@ function ExplorePage() {
|
||||
|
||||
|
||||
const onFiltersUpdated = useCallback(({ payload }: typeof UPDATE_FILTERS_ACTION) => {
|
||||
setSelectedCategory(null)
|
||||
setCanFetchMore(true);
|
||||
updateFilters(payload)
|
||||
}, [updateFilters])
|
||||
@@ -129,11 +124,7 @@ function ExplorePage() {
|
||||
}
|
||||
|
||||
const selectCategoryTab = (category: Category | null) => {
|
||||
if (filters) {
|
||||
const { categories, ...filtersWithoutCategory } = filters
|
||||
updateFilters(filtersWithoutCategory)
|
||||
}
|
||||
setSelectedCategory(category);
|
||||
updateFilters({ ...(filters ?? {}), categories: category ? [category] : undefined })
|
||||
setCanFetchMore(true);
|
||||
}
|
||||
|
||||
@@ -150,9 +141,6 @@ function ExplorePage() {
|
||||
</div>
|
||||
}
|
||||
|
||||
const deadProjectsCount = data?.projects?.filter(p => p?.dead).length;
|
||||
const hasDeadProjectsFilter = filters?.projectStatus === 'dead';
|
||||
|
||||
const isLoading = networkStatus === NetworkStatus.loading || networkStatus === NetworkStatus.refetch || networkStatus === NetworkStatus.setVariables;
|
||||
const isLoadingMore = networkStatus === NetworkStatus.fetchMore;
|
||||
const canLoadMore = !isLoading && !isLoadingMore && data?.projects && data.projects.length > 0 && canFetchMore;
|
||||
@@ -165,11 +153,11 @@ function ExplorePage() {
|
||||
<meta property="og:title" content={`Lightning Landscape`} />
|
||||
</Helmet>
|
||||
<Header
|
||||
selectedCategry={selectedCategory}
|
||||
selectedCategry={filters?.categories?.[0] ?? null}
|
||||
/>
|
||||
<div className="page-container">
|
||||
<div className="grid grid-cols-1 md:grid-cols-[1fr_auto] items-center gap-x-32 gap-y-16 mb-36">
|
||||
<div className="min-w-0 max-md:row-start-2"><Categories filtersActive={hasSearchFilters} value={selectedCategory} onChange={v => selectCategoryTab(v)} /></div>
|
||||
<div className="min-w-0 max-md:row-start-2"><Categories filtersActive={hasSearchFilters} value={filters?.categories?.[0] ?? null} onChange={v => selectCategoryTab(v)} /></div>
|
||||
<Button
|
||||
className={`self-center ${hasSearchFilters ? "!font-bold !bg-primary-50 !text-primary-600 !border-2 !border-primary-400" : "!text-gray-600"}`}
|
||||
variant='outline'
|
||||
|
||||
@@ -6,7 +6,7 @@ import Button from 'src/Components/Button/Button'
|
||||
import { useAppDispatch, useMediaQuery } from 'src/utils/hooks'
|
||||
import { PayloadAction } from '@reduxjs/toolkit'
|
||||
import IconButton from 'src/Components/IconButton/IconButton'
|
||||
import { useGetFiltersQuery } from 'src/graphql'
|
||||
import { GetFiltersQuery, useGetFiltersQuery } from 'src/graphql'
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { random } from 'src/utils/helperFunctions';
|
||||
import { MEDIA_QUERIES } from 'src/utils/theme'
|
||||
@@ -17,14 +17,15 @@ interface Props extends ModalCard {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface IFormInputs {
|
||||
text: string,
|
||||
href: string,
|
||||
}
|
||||
|
||||
type FilterCategory = NonNullable<NonNullable<GetFiltersQuery['categoryList']>[number]>
|
||||
|
||||
export type ProjectsFilters = {
|
||||
categories: { id: string, label: string }[]
|
||||
categories: FilterCategory[]
|
||||
tags: { id: string, label: string }[]
|
||||
yearFounded: typeof yearsFoundedOptions[number]['value'],
|
||||
projectStatus: typeof projectStatusOptions[number]['value']
|
||||
@@ -43,7 +44,7 @@ export default function FiltersModal({ onClose, direction, initFilters, callback
|
||||
const [projectStatusFilter, setProjectStatusFilter] = useState(initFilters?.projectStatus ?? "any");
|
||||
const [projectLicenseFilter, setProjectLicenseFilter] = useState(initFilters?.projectLicense ?? "any");
|
||||
|
||||
const clickCategory = (value: { id: string, label: string }) => {
|
||||
const clickCategory = (value: FilterCategory) => {
|
||||
if (categoriesFilter.some(v => v.id === value.id))
|
||||
setCategoriesFilter([]);
|
||||
else
|
||||
@@ -122,8 +123,8 @@ export default function FiltersModal({ onClose, direction, initFilters, callback
|
||||
active:scale-95 transition-transform
|
||||
${!categoriesFilter.some(f => f.id === category?.id!) ? "bg-gray-100 hover:bg-gray-200 border-gray-200" : "bg-primary-100 text-primary-600 border-primary-200"}
|
||||
`}
|
||||
onClick={() => clickCategory({ id: category?.id!, label: category?.name! })}
|
||||
>{category?.name}
|
||||
onClick={() => clickCategory(category!)}
|
||||
>{category?.icon && <i className={`fa-solid fa-${category?.icon} mr-8 text-gray-700`}></i>} {category?.name}
|
||||
</button>
|
||||
</li>)}
|
||||
{query.loading &&
|
||||
|
||||
@@ -3,6 +3,7 @@ query GetFilters {
|
||||
id
|
||||
name
|
||||
icon
|
||||
projectsCount
|
||||
}
|
||||
tags {
|
||||
id
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React from 'react'
|
||||
import { FiArrowLeft } from 'react-icons/fi'
|
||||
|
||||
import { MdClose } from 'react-icons/md'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import { Link } from 'react-router-dom'
|
||||
import ASSETS from 'src/assets'
|
||||
import Badge from 'src/Components/Badge/Badge'
|
||||
import { Category } from 'src/features/Projects/Components/Categories/Categories'
|
||||
import { PAGES_ROUTES } from 'src/utils/routing'
|
||||
import { useProjectsFilters } from '../filters-context'
|
||||
|
||||
type Props = {
|
||||
@@ -23,16 +18,16 @@ export default function Header(props: Props) {
|
||||
const onSearchPage = !onCategoryPage && !filtersEmpty
|
||||
|
||||
|
||||
const title = onCategoryPage ? `${props.selectedCategry?.projectsCount} projects` :
|
||||
filtersEmpty ? "Discover 1,592 lightning projects" : "Search results";
|
||||
const title = onCategoryPage ? `${props.selectedCategry?.name} projects` :
|
||||
filtersEmpty ? "Discover 1,592 lightning projects" : "All lightning projects";
|
||||
|
||||
const subtitle = onCategoryPage ? props.selectedCategry?.name :
|
||||
const subtitle = onCategoryPage ? "" :
|
||||
filtersEmpty ? "Explore a directory of lightning startups, projects, and companies"
|
||||
: ""
|
||||
|
||||
|
||||
return (
|
||||
<div className='h-[280px] rounded-20 overflow-hidden relative flex flex-col justify-center items-center gap-8'>
|
||||
<div className='h-[280px] rounded-20 overflow-hidden relative text-center flex flex-col justify-center items-center gap-8'>
|
||||
<img src="/assets/images/cover.png" alt="" className='absolute inset-0 opacity-20 w-full h-full object-cover z-[-1]' />
|
||||
{/* <div className='absolute inset-0 w-full h-full bg-gray-300 bg-opacity-50 z-[-1]' /> */}
|
||||
{/* <Link
|
||||
@@ -53,11 +48,11 @@ export default function Header(props: Props) {
|
||||
{!filtersEmpty && <div className=" ">
|
||||
<p className="text-gray-500 font-medium text-body4 mb-8 mt-8 text-center">filtered by</p>
|
||||
<div className="flex gap-8 flex-wrap">
|
||||
{filters?.categories && filters.categories.length > 0 && <Badge size='sm'>Category: <span className='font-bold text-gray-700'>{filters.categories[0].name}</span> <button onClick={() => removeFilter("categories")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.tags && filters.tags.length > 0 && <Badge size='sm'>🏷️ <span className='font-bold text-gray-700'>{filters.tags.map(t => t.label).join(', ')}</span> <button onClick={() => removeFilter("tags")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.yearFounded && <Badge size='sm'>📆 Founded in <span className='font-bold text-gray-700'>{filters.yearFounded}</span> <button onClick={() => removeFilter("yearFounded")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.projectStatus && <Badge size='sm'>🌱 Status: <span className='font-bold text-gray-700'>{filters?.projectStatus}</span> <button onClick={() => removeFilter("projectStatus")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.projectLicense && <Badge size='sm'>💻 <span className='font-bold text-gray-700'>{filters.projectLicense}</span> <button onClick={() => removeFilter("projectLicense")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.categories && filters.categories.length > 0 && <Badge size='sm'>Category: <span className='font-bold text-gray-700'>{filters.categories[0].label}</span> <button onClick={() => removeFilter("categories")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
{filters?.tags && filters.tags.length > 0 && <Badge size='sm'>🏷️ <span className='font-bold text-gray-700'>{filters.tags.map(t => t.label).join(', ')}</span> <button onClick={() => removeFilter("tags")} className='ml-4 text-gray-600 hover:scale-125'><MdClose /></button> </Badge>}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
query ExplorePage($filter: JSON, $page: JSON, $pageSize: JSON) {
|
||||
projects(_filter: $filter, _page: $page, _page_size: $pageSize) {
|
||||
projects(
|
||||
_order_by: ["dead", "title"]
|
||||
_filter: $filter
|
||||
_page: $page
|
||||
_page_size: $pageSize
|
||||
) {
|
||||
id
|
||||
title
|
||||
logo
|
||||
|
||||
@@ -411,7 +411,7 @@ export type AllCategoriesQuery = { __typename?: 'Query', categoryList: Array<{ _
|
||||
export type GetFiltersQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetFiltersQuery = { __typename?: 'Query', categoryList: Array<{ __typename?: 'categoryList', id: string | null, name: string | null, icon: string | null } | null> | null, tags: Array<{ __typename?: 'tags', id: string | null, name: string | null, icon: string | null } | null> | null };
|
||||
export type GetFiltersQuery = { __typename?: 'Query', categoryList: Array<{ __typename?: 'categoryList', id: string | null, name: string | null, icon: string | null, projectsCount: string | null } | null> | null, tags: Array<{ __typename?: 'tags', id: string | null, name: string | null, icon: string | null } | null> | null };
|
||||
|
||||
export type ExplorePageQueryVariables = Exact<{
|
||||
filter: InputMaybe<Scalars['JSON']>;
|
||||
@@ -475,6 +475,7 @@ export const GetFiltersDocument = gql`
|
||||
id
|
||||
name
|
||||
icon
|
||||
projectsCount
|
||||
}
|
||||
tags {
|
||||
id
|
||||
@@ -512,7 +513,12 @@ export type GetFiltersLazyQueryHookResult = ReturnType<typeof useGetFiltersLazyQ
|
||||
export type GetFiltersQueryResult = Apollo.QueryResult<GetFiltersQuery, GetFiltersQueryVariables>;
|
||||
export const ExplorePageDocument = gql`
|
||||
query ExplorePage($filter: JSON, $page: JSON, $pageSize: JSON) {
|
||||
projects(_filter: $filter, _page: $page, _page_size: $pageSize) {
|
||||
projects(
|
||||
_order_by: ["dead", "title"]
|
||||
_filter: $filter
|
||||
_page: $page
|
||||
_page_size: $pageSize
|
||||
) {
|
||||
id
|
||||
title
|
||||
logo
|
||||
|
||||
Reference in New Issue
Block a user