mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-27 02:04:24 +01:00
feat: Hackathons page, hackathons filter, hackathons mocks api
This commit is contained in:
@@ -76,6 +76,16 @@ export interface NexusGenObjects {
|
||||
id: number; // Int!
|
||||
title: string; // String!
|
||||
}
|
||||
Hackathon: { // root type
|
||||
cover_image: string; // String!
|
||||
description: string; // String!
|
||||
end_date: NexusGenScalars['Date']; // Date!
|
||||
id: number; // Int!
|
||||
location: string; // String!
|
||||
start_date: NexusGenScalars['Date']; // Date!
|
||||
title: string; // String!
|
||||
website: string; // String!
|
||||
}
|
||||
LnurlDetails: { // root type
|
||||
commentAllowed?: number | null; // Int
|
||||
maxSendable?: number | null; // Int
|
||||
@@ -198,6 +208,17 @@ export interface NexusGenFieldTypes {
|
||||
title: string; // String!
|
||||
votes_sum: number; // Int!
|
||||
}
|
||||
Hackathon: { // field return type
|
||||
cover_image: string; // String!
|
||||
description: string; // String!
|
||||
end_date: NexusGenScalars['Date']; // Date!
|
||||
id: number; // Int!
|
||||
location: string; // String!
|
||||
start_date: NexusGenScalars['Date']; // Date!
|
||||
title: string; // String!
|
||||
topics: NexusGenRootTypes['Topic'][]; // [Topic!]!
|
||||
website: string; // String!
|
||||
}
|
||||
LnurlDetails: { // field return type
|
||||
commentAllowed: number | null; // Int
|
||||
maxSendable: number | null; // Int
|
||||
@@ -235,6 +256,7 @@ export interface NexusGenFieldTypes {
|
||||
allCategories: NexusGenRootTypes['Category'][]; // [Category!]!
|
||||
allProjects: NexusGenRootTypes['Project'][]; // [Project!]!
|
||||
allTopics: NexusGenRootTypes['Topic'][]; // [Topic!]!
|
||||
getAllHackathons: NexusGenRootTypes['Hackathon'][]; // [Hackathon!]!
|
||||
getCategory: NexusGenRootTypes['Category']; // Category!
|
||||
getFeed: NexusGenRootTypes['Post'][]; // [Post!]!
|
||||
getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails!
|
||||
@@ -347,6 +369,17 @@ export interface NexusGenFieldTypeNames {
|
||||
title: 'String'
|
||||
votes_sum: 'Int'
|
||||
}
|
||||
Hackathon: { // field return type name
|
||||
cover_image: 'String'
|
||||
description: 'String'
|
||||
end_date: 'Date'
|
||||
id: 'Int'
|
||||
location: 'String'
|
||||
start_date: 'Date'
|
||||
title: 'String'
|
||||
topics: 'Topic'
|
||||
website: 'String'
|
||||
}
|
||||
LnurlDetails: { // field return type name
|
||||
commentAllowed: 'Int'
|
||||
maxSendable: 'Int'
|
||||
@@ -384,6 +417,7 @@ export interface NexusGenFieldTypeNames {
|
||||
allCategories: 'Category'
|
||||
allProjects: 'Project'
|
||||
allTopics: 'Topic'
|
||||
getAllHackathons: 'Hackathon'
|
||||
getCategory: 'Category'
|
||||
getFeed: 'Post'
|
||||
getLnurlDetailsForProject: 'LnurlDetails'
|
||||
@@ -474,6 +508,10 @@ export interface NexusGenArgTypes {
|
||||
skip?: number | null; // Int
|
||||
take: number | null; // Int
|
||||
}
|
||||
getAllHackathons: { // args
|
||||
sortBy?: string | null; // String
|
||||
topic?: number | null; // Int
|
||||
}
|
||||
getCategory: { // args
|
||||
id: number; // Int!
|
||||
}
|
||||
|
||||
@@ -47,6 +47,18 @@ type Category {
|
||||
"""Date custom scalar type"""
|
||||
scalar Date
|
||||
|
||||
type Hackathon {
|
||||
cover_image: String!
|
||||
description: String!
|
||||
end_date: Date!
|
||||
id: Int!
|
||||
location: String!
|
||||
start_date: Date!
|
||||
title: String!
|
||||
topics: [Topic!]!
|
||||
website: String!
|
||||
}
|
||||
|
||||
type LnurlDetails {
|
||||
commentAllowed: Int
|
||||
maxSendable: Int
|
||||
@@ -105,6 +117,7 @@ type Query {
|
||||
allCategories: [Category!]!
|
||||
allProjects(skip: Int = 0, take: Int = 50): [Project!]!
|
||||
allTopics: [Topic!]!
|
||||
getAllHackathons(sortBy: String, topic: Int): [Hackathon!]!
|
||||
getCategory(id: Int!): Category!
|
||||
getFeed(skip: Int = 0, sortBy: String = "all", take: Int = 10, topic: Int = 0): [Post!]!
|
||||
getLnurlDetailsForProject(project_id: Int!): LnurlDetails!
|
||||
|
||||
@@ -5,6 +5,7 @@ const {
|
||||
extendType,
|
||||
nonNull,
|
||||
} = require('nexus');
|
||||
const { prisma } = require('../prisma')
|
||||
|
||||
|
||||
|
||||
@@ -15,13 +16,14 @@ const Hackathon = objectType({
|
||||
t.nonNull.string('title');
|
||||
t.nonNull.string('description');
|
||||
t.nonNull.string('cover_image');
|
||||
t.nonNull.string('date');
|
||||
t.nonNull.date('start_date');
|
||||
t.nonNull.date('end_date');
|
||||
t.nonNull.string('location');
|
||||
t.nonNull.string('website');
|
||||
t.nonNull.list.nonNull.field('topics', {
|
||||
type: "Topic",
|
||||
resolve: (parent) => {
|
||||
return []
|
||||
return prisma.hackathon.findUnique({ where: { id: parent.id } }).topics();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -29,15 +31,41 @@ const Hackathon = objectType({
|
||||
|
||||
const getAllHackathons = extendType({
|
||||
type: "Query",
|
||||
args: {
|
||||
sortBy: stringArg(),
|
||||
topic: stringArg(),
|
||||
},
|
||||
definition(t) {
|
||||
t.nonNull.list.nonNull.field('getAllHackathons', {
|
||||
type: "Hackathon",
|
||||
args: {
|
||||
sortBy: stringArg(),
|
||||
topic: intArg(),
|
||||
},
|
||||
resolve(_, args) {
|
||||
return [];
|
||||
const { sortBy, topic } = args;
|
||||
return prisma.hackathon.findMany({
|
||||
where: {
|
||||
...(sortBy === 'Upcoming' && {
|
||||
start_date: {
|
||||
gte: new Date(),
|
||||
}
|
||||
}),
|
||||
...(sortBy === 'Live' && {
|
||||
start_date: { lte: new Date() },
|
||||
end_date: { gte: new Date() }
|
||||
}),
|
||||
...(sortBy === 'Finished' && {
|
||||
end_date: {
|
||||
lt: new Date()
|
||||
}
|
||||
}),
|
||||
|
||||
...(topic && {
|
||||
topics: {
|
||||
some: {
|
||||
id: topic
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ const project = require('./project')
|
||||
const vote = require('./vote')
|
||||
const post = require('./post')
|
||||
const users = require('./users')
|
||||
const hackathon = require('./hackathon')
|
||||
|
||||
module.exports = {
|
||||
...scalars,
|
||||
@@ -11,5 +12,6 @@ module.exports = {
|
||||
...project,
|
||||
...vote,
|
||||
...post,
|
||||
...users
|
||||
...users,
|
||||
...hackathon
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import LoadingPage from "./Components/LoadingPage/LoadingPage";
|
||||
|
||||
// Pages
|
||||
const FeedPage = React.lazy(() => import("./features/Posts/pages/FeedPage/FeedPage"))
|
||||
const HackathonsPage = React.lazy(() => import("./features/Hackathons/pages/HackathonsPage/HackathonsPage"))
|
||||
const HottestPage = React.lazy(() => import("src/features/Projects/pages/HottestPage/HottestPage"))
|
||||
const PostDetailsPage = React.lazy(() => import("./features/Posts/pages/PostDetailsPage/PostDetailsPage"))
|
||||
const CategoryPage = React.lazy(() => import("src/features/Projects/pages/CategoryPage/CategoryPage"))
|
||||
@@ -47,6 +48,7 @@ function App() {
|
||||
<Route path="/category/:id" element={<CategoryPage />} />
|
||||
<Route path="/blog/post/:type/:id" element={<PostDetailsPage />} />
|
||||
<Route path="/blog" element={<FeedPage />} />
|
||||
<Route path="/hackathons" element={<HackathonsPage />} />
|
||||
<Route path="/" element={<ExplorePage />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { setNavHeight } from "src/redux/features/ui.slice";
|
||||
import NavDesktop from "./NavDesktop";
|
||||
import { useMediaQuery } from "@react-hookz/web";
|
||||
import { MEDIA_QUERIES } from "src/utils/theme/media_queries";
|
||||
import { IoMdTrophy } from "react-icons/io";
|
||||
|
||||
|
||||
export const navLinks = [
|
||||
@@ -18,6 +19,12 @@ export const navLinks = [
|
||||
icon: MdComment,
|
||||
color: "text-primary-600",
|
||||
},
|
||||
{
|
||||
text: "Hackathons",
|
||||
url: "/hackathons",
|
||||
icon: IoMdTrophy,
|
||||
color: "text-primary-600",
|
||||
},
|
||||
{
|
||||
text: "Hottest",
|
||||
url: "/hottest",
|
||||
@@ -41,6 +48,7 @@ export default function Navbar() {
|
||||
}));
|
||||
|
||||
const isLargeScreen = useMediaQuery(MEDIA_QUERIES.isLarge)
|
||||
console.log(isLargeScreen, MEDIA_QUERIES.isLarge);
|
||||
|
||||
|
||||
const onConnectWallet = () => {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Hackathon } from "src/features/Hackathons/types"
|
||||
import { IoLocationOutline } from 'react-icons/io5'
|
||||
import Button from "src/Components/Button/Button"
|
||||
import dayjs from "dayjs";
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
||||
import { trimText } from "src/utils/helperFunctions";
|
||||
dayjs.extend(advancedFormat)
|
||||
|
||||
export type HackathonCardType = Hackathon;
|
||||
|
||||
@@ -18,20 +22,20 @@ export default function HackathonCard({ hackathon }: Props) {
|
||||
{hackathon.title}
|
||||
</h3>
|
||||
<p className="text-body3 font-medium text-gray-900">
|
||||
{hackathon.date}
|
||||
{`${dayjs(hackathon.start_date).format('Do')} - ${dayjs(hackathon.end_date).format('Do MMMM, YYYY')}`}
|
||||
</p>
|
||||
<p className="text-body4 font-medium text-gray-600">
|
||||
<IoLocationOutline className="mr-8" /> {hackathon.location}
|
||||
</p>
|
||||
<p className="text-body4 text-gray-600">
|
||||
{hackathon.description}
|
||||
{trimText(hackathon.description, 110)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-16 flex flex-wrap gap-8">
|
||||
{hackathon.topics.map(topic => <div key={topic.id} className="p-8 bg-gray-50 rounded-8 text-body5">{topic.title}</div>)}
|
||||
{hackathon.topics.map(topic => <div key={topic.id} className="p-8 bg-gray-50 rounded-8 text-body5">{topic.icon} {topic.title}</div>)}
|
||||
|
||||
</div>
|
||||
<Button href={hackathon.url} newTab color="gray" fullWidth className="mt-16">
|
||||
<Button href={hackathon.website} newTab color="gray" fullWidth className="mt-16">
|
||||
Learn more
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -6,35 +6,33 @@ const filters = [
|
||||
value: 'Upcoming'
|
||||
}, {
|
||||
text: "Live",
|
||||
value: 'live'
|
||||
value: 'Live'
|
||||
}, {
|
||||
text: "Complete",
|
||||
value: 'complete'
|
||||
text: "Finished",
|
||||
value: 'Finished'
|
||||
},
|
||||
]
|
||||
|
||||
interface Props {
|
||||
filterChanged?: (newFilter: string) => void
|
||||
filterChanged?: (newFilter: string | null) => void
|
||||
}
|
||||
|
||||
export default function SortByFilter({ filterChanged }: Props) {
|
||||
|
||||
const [selected, setSelected] = useState(filters[0].value);
|
||||
const [selected, setSelected] = useState<string | null>(null);
|
||||
|
||||
const filterClicked = (newValue: string) => {
|
||||
if (selected === newValue)
|
||||
return
|
||||
const filterClicked = (_newValue: string) => {
|
||||
const newValue = selected !== _newValue ? _newValue : null;
|
||||
setSelected(newValue);
|
||||
filterChanged?.(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<p className="text-body2 font-bolder text-black mb-16">Sort By</p>
|
||||
<ul>
|
||||
{filters.map((f, idx) => <li
|
||||
key={f.value}
|
||||
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-100'}`}
|
||||
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
|
||||
onClick={() => filterClicked(f.value)}
|
||||
>
|
||||
{f.text}
|
||||
|
||||
@@ -1,39 +1,22 @@
|
||||
import { useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { useAllTopicsQuery, usePopularTopicsQuery } from 'src/graphql';
|
||||
|
||||
|
||||
const filters = [
|
||||
{
|
||||
text: 'Design',
|
||||
value: 'Design',
|
||||
icon: "🎨"
|
||||
},
|
||||
{
|
||||
text: 'Development',
|
||||
value: 'Development',
|
||||
icon: "💻"
|
||||
},
|
||||
{
|
||||
text: 'Startups',
|
||||
value: 'Startups',
|
||||
icon: "🚀"
|
||||
},
|
||||
{
|
||||
text: 'Lightning Network',
|
||||
value: 'Lightning Network',
|
||||
icon: "⚡️"
|
||||
},
|
||||
]
|
||||
|
||||
interface Props {
|
||||
filterChanged?: (newFilter: string) => void
|
||||
filterChanged?: (newFilter: number | null) => void
|
||||
}
|
||||
|
||||
export default function TopicsFilter({ filterChanged }: Props) {
|
||||
export default function PopularTopicsFilter({ filterChanged }: Props) {
|
||||
|
||||
const [selected, setSelected] = useState(filters[0].value);
|
||||
const [selected, setSelected] = useState<number | null>(null);
|
||||
|
||||
const filterClicked = (newValue: string) => {
|
||||
if (selected === newValue)
|
||||
return
|
||||
const topicsQuery = useAllTopicsQuery();
|
||||
|
||||
|
||||
const filterClicked = (_newValue: number) => {
|
||||
const newValue = selected !== _newValue ? _newValue : null;
|
||||
setSelected(newValue);
|
||||
filterChanged?.(newValue);
|
||||
}
|
||||
@@ -42,16 +25,28 @@ export default function TopicsFilter({ filterChanged }: Props) {
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
|
||||
<ul className=' flex flex-col gap-16'>
|
||||
{filters.map((f, idx) => <li
|
||||
key={f.value}
|
||||
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-50'}`}
|
||||
onClick={() => filterClicked(f.value)}
|
||||
>
|
||||
<span className='bg-gray-50 rounded-8 p-8'>{f.icon}</span>
|
||||
<span className="self-center px-16">
|
||||
{f.text}
|
||||
</span>
|
||||
</li>)}
|
||||
{topicsQuery.loading ?
|
||||
Array(4).fill(0).map((_, idx) => <li
|
||||
key={idx}
|
||||
className={`flex items-start rounded-8 font-bold`}
|
||||
|
||||
>
|
||||
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
|
||||
<span className="self-center px-16"><Skeleton width={'7ch'} />
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
:
|
||||
topicsQuery.data?.allTopics.map((f, idx) => <li
|
||||
key={f.id}
|
||||
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.id === selected && 'bg-gray-200'}`}
|
||||
onClick={() => filterClicked(f.id)}
|
||||
>
|
||||
<span className={`${f.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{f.icon}</span>
|
||||
<span className="self-center px-16">
|
||||
{f.title}
|
||||
</span>
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
query allTopics {
|
||||
allTopics {
|
||||
id
|
||||
title
|
||||
icon
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import HackathonsPage from './HackathonsPage';
|
||||
|
||||
export default {
|
||||
title: 'Hackathons/Hackathons Page/Page',
|
||||
component: HackathonsPage,
|
||||
argTypes: {
|
||||
backgroundColor: { control: 'color' },
|
||||
},
|
||||
} as ComponentMeta<typeof HackathonsPage>;
|
||||
|
||||
|
||||
const Template: ComponentStory<typeof HackathonsPage> = (args) => <HackathonsPage {...args as any} ></HackathonsPage>
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
|
||||
import { useReducer, useState } from 'react'
|
||||
import { useFeedQuery } from 'src/graphql'
|
||||
import Button from 'src/Components/Button/Button'
|
||||
import { useGetHackathonsQuery } from 'src/graphql'
|
||||
import { useAppSelector, useInfiniteQuery } from 'src/utils/hooks'
|
||||
import HackathonsList from '../../Components/HackathonsList/HackathonsList'
|
||||
import SortByFilter from '../../Components/SortByFilter/SortByFilter'
|
||||
import TopicsFilter from '../../Components/TopicsFilter/TopicsFilter'
|
||||
import styles from './styles.module.scss'
|
||||
@@ -9,19 +11,15 @@ import styles from './styles.module.scss'
|
||||
|
||||
export default function HackathonsPage() {
|
||||
|
||||
const [sortByFilter, setSortByFilter] = useState('all')
|
||||
const [topicsFilter, setTopicsFilter] = useState('all')
|
||||
const [sortByFilter, setSortByFilter] = useState<string | null>(null)
|
||||
const [topicsFilter, setTopicsFilter] = useState<number | null>(null)
|
||||
|
||||
|
||||
const feedQuery = useFeedQuery({
|
||||
const hackathonsQuery = useGetHackathonsQuery({
|
||||
variables: {
|
||||
take: 10,
|
||||
skip: 0,
|
||||
sortBy: sortByFilter,
|
||||
topic: Number(topicsFilter)
|
||||
},
|
||||
})
|
||||
const { fetchMore, isFetchingMore } = useInfiniteQuery(feedQuery, 'getFeed')
|
||||
const { navHeight } = useAppSelector((state) => ({
|
||||
navHeight: state.ui.navHeight
|
||||
}));
|
||||
@@ -31,7 +29,7 @@ export default function HackathonsPage() {
|
||||
className={`page-container pt-16 w-full ${styles.grid}`}
|
||||
>
|
||||
<aside className='no-scrollbar'>
|
||||
<div className="sticky"
|
||||
<div className="sticky flex flex-col gap-24"
|
||||
style={{
|
||||
top: `${navHeight + 16}px`,
|
||||
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
|
||||
@@ -43,9 +41,21 @@ export default function HackathonsPage() {
|
||||
<TopicsFilter
|
||||
filterChanged={setTopicsFilter}
|
||||
/>
|
||||
<Button
|
||||
href='https://airtable.com/some-registration-form'
|
||||
newTab
|
||||
color='primary'
|
||||
fullWidth
|
||||
>
|
||||
List Your Hackathon
|
||||
</Button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div className="self-start">
|
||||
<HackathonsList
|
||||
isLoading={hackathonsQuery.loading}
|
||||
items={hackathonsQuery.data?.getAllHackathons} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
query getHackathons($sortBy: String, $topic: Int) {
|
||||
getAllHackathons(sortBy: $sortBy, topic: $topic) {
|
||||
id
|
||||
title
|
||||
description
|
||||
cover_image
|
||||
start_date
|
||||
end_date
|
||||
location
|
||||
website
|
||||
topics {
|
||||
id
|
||||
title
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,11 @@
|
||||
gap: 0;
|
||||
|
||||
@media screen and (min-width: 680px) {
|
||||
grid-template-columns: 1fr 2fr 0;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
grid-template-columns:
|
||||
minmax(200px, 1fr)
|
||||
minmax(50%, 70ch)
|
||||
minmax(200px, 1fr);
|
||||
grid-template-columns: calc(min(30%, 326px)) 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
export interface Hackathon {
|
||||
id: number
|
||||
title: string
|
||||
date: string
|
||||
location: string
|
||||
description: string
|
||||
cover_image: string
|
||||
topics: Array<{
|
||||
id: number,
|
||||
title: string
|
||||
}>,
|
||||
url: string
|
||||
}
|
||||
import { Hackathon as ApiHackathon, } from "src/graphql"
|
||||
|
||||
export type Hackathon = ApiHackathon
|
||||
@@ -2,27 +2,6 @@ import React, { useState } from 'react'
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { usePopularTopicsQuery } from 'src/graphql';
|
||||
|
||||
const filters = [
|
||||
{
|
||||
text: "🔥 All",
|
||||
value: 0
|
||||
}, {
|
||||
text: "Lightning Network",
|
||||
value: 1
|
||||
}, {
|
||||
text: "Bitcoin",
|
||||
value: 2
|
||||
}, {
|
||||
text: "Cybersecurity",
|
||||
value: 3
|
||||
}, {
|
||||
text: "Bounties",
|
||||
value: 4
|
||||
}, {
|
||||
text: "Grants",
|
||||
value: 5
|
||||
},
|
||||
]
|
||||
|
||||
interface Props {
|
||||
filterChanged?: (newFilter: number | null) => void
|
||||
@@ -30,7 +9,7 @@ interface Props {
|
||||
|
||||
export default function PopularTopicsFilter({ filterChanged }: Props) {
|
||||
|
||||
const [selected, setSelected] = useState<number | null>(filters[0].value);
|
||||
const [selected, setSelected] = useState<number | null>(null);
|
||||
|
||||
const topicsQuery = usePopularTopicsQuery();
|
||||
|
||||
@@ -59,10 +38,10 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
|
||||
:
|
||||
topicsQuery.data?.popularTopics.map((f, idx) => <li
|
||||
key={f.id}
|
||||
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.id === selected && 'bg-gray-50'}`}
|
||||
className={`flex items-start rounded-8 cursor-pointer font-bold ${f.id === selected && 'bg-gray-200'}`}
|
||||
onClick={() => filterClicked(f.id)}
|
||||
>
|
||||
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'>{f.icon}</span>
|
||||
<span className={`${f.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{f.icon}</span>
|
||||
<span className="self-center px-16">
|
||||
{f.title}
|
||||
</span>
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Nullable } from 'remirror';
|
||||
|
||||
const filters = [
|
||||
{
|
||||
text: "🔥 Popular",
|
||||
value: 'popular'
|
||||
text: "🔥 Hottest",
|
||||
value: 'hottest'
|
||||
}, {
|
||||
text: "📆 Newest",
|
||||
value: 'newest'
|
||||
@@ -34,7 +34,7 @@ export default function SortBy({ filterChanged }: Props) {
|
||||
<ul>
|
||||
{filters.map((f, idx) => <li
|
||||
key={f.value}
|
||||
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-100'}`}
|
||||
className={`p-12 rounded-8 cursor-pointer font-bold ${f.value === selected && 'bg-gray-200'}`}
|
||||
onClick={() => filterClicked(f.value)}
|
||||
>
|
||||
{f.text}
|
||||
|
||||
@@ -63,6 +63,19 @@ export type Category = {
|
||||
votes_sum: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type Hackathon = {
|
||||
__typename?: 'Hackathon';
|
||||
cover_image: Scalars['String'];
|
||||
description: Scalars['String'];
|
||||
end_date: Scalars['Date'];
|
||||
id: Scalars['Int'];
|
||||
location: Scalars['String'];
|
||||
start_date: Scalars['Date'];
|
||||
title: Scalars['String'];
|
||||
topics: Array<Topic>;
|
||||
website: Scalars['String'];
|
||||
};
|
||||
|
||||
export type LnurlDetails = {
|
||||
__typename?: 'LnurlDetails';
|
||||
commentAllowed: Maybe<Scalars['Int']>;
|
||||
@@ -139,6 +152,7 @@ export type Query = {
|
||||
allCategories: Array<Category>;
|
||||
allProjects: Array<Project>;
|
||||
allTopics: Array<Topic>;
|
||||
getAllHackathons: Array<Hackathon>;
|
||||
getCategory: Category;
|
||||
getFeed: Array<Post>;
|
||||
getLnurlDetailsForProject: LnurlDetails;
|
||||
@@ -159,6 +173,12 @@ export type QueryAllProjectsArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetAllHackathonsArgs = {
|
||||
sortBy: InputMaybe<Scalars['String']>;
|
||||
topic: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetCategoryArgs = {
|
||||
id: Scalars['Int'];
|
||||
};
|
||||
@@ -297,6 +317,19 @@ export type SearchProjectsQueryVariables = Exact<{
|
||||
|
||||
export type SearchProjectsQuery = { __typename?: 'Query', searchProjects: Array<{ __typename?: 'Project', id: number, thumbnail_image: string, title: string, category: { __typename?: 'Category', title: string, id: number } }> };
|
||||
|
||||
export type AllTopicsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type AllTopicsQuery = { __typename?: 'Query', allTopics: Array<{ __typename?: 'Topic', id: number, title: string, icon: string }> };
|
||||
|
||||
export type GetHackathonsQueryVariables = Exact<{
|
||||
sortBy: InputMaybe<Scalars['String']>;
|
||||
topic: InputMaybe<Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type GetHackathonsQuery = { __typename?: 'Query', getAllHackathons: Array<{ __typename?: 'Hackathon', id: number, title: string, description: string, cover_image: string, start_date: any, end_date: any, location: string, website: string, topics: Array<{ __typename?: 'Topic', id: number, title: string, icon: string }> }> };
|
||||
|
||||
export type TrendingPostsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -450,6 +483,90 @@ export function useSearchProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOpt
|
||||
export type SearchProjectsQueryHookResult = ReturnType<typeof useSearchProjectsQuery>;
|
||||
export type SearchProjectsLazyQueryHookResult = ReturnType<typeof useSearchProjectsLazyQuery>;
|
||||
export type SearchProjectsQueryResult = Apollo.QueryResult<SearchProjectsQuery, SearchProjectsQueryVariables>;
|
||||
export const AllTopicsDocument = gql`
|
||||
query allTopics {
|
||||
allTopics {
|
||||
id
|
||||
title
|
||||
icon
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useAllTopicsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useAllTopicsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useAllTopicsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useAllTopicsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useAllTopicsQuery(baseOptions?: Apollo.QueryHookOptions<AllTopicsQuery, AllTopicsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<AllTopicsQuery, AllTopicsQueryVariables>(AllTopicsDocument, options);
|
||||
}
|
||||
export function useAllTopicsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AllTopicsQuery, AllTopicsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<AllTopicsQuery, AllTopicsQueryVariables>(AllTopicsDocument, options);
|
||||
}
|
||||
export type AllTopicsQueryHookResult = ReturnType<typeof useAllTopicsQuery>;
|
||||
export type AllTopicsLazyQueryHookResult = ReturnType<typeof useAllTopicsLazyQuery>;
|
||||
export type AllTopicsQueryResult = Apollo.QueryResult<AllTopicsQuery, AllTopicsQueryVariables>;
|
||||
export const GetHackathonsDocument = gql`
|
||||
query getHackathons($sortBy: String, $topic: Int) {
|
||||
getAllHackathons(sortBy: $sortBy, topic: $topic) {
|
||||
id
|
||||
title
|
||||
description
|
||||
cover_image
|
||||
start_date
|
||||
end_date
|
||||
location
|
||||
website
|
||||
topics {
|
||||
id
|
||||
title
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetHackathonsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetHackathonsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetHackathonsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetHackathonsQuery({
|
||||
* variables: {
|
||||
* sortBy: // value for 'sortBy'
|
||||
* topic: // value for 'topic'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetHackathonsQuery(baseOptions?: Apollo.QueryHookOptions<GetHackathonsQuery, GetHackathonsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetHackathonsQuery, GetHackathonsQueryVariables>(GetHackathonsDocument, options);
|
||||
}
|
||||
export function useGetHackathonsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetHackathonsQuery, GetHackathonsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetHackathonsQuery, GetHackathonsQueryVariables>(GetHackathonsDocument, options);
|
||||
}
|
||||
export type GetHackathonsQueryHookResult = ReturnType<typeof useGetHackathonsQuery>;
|
||||
export type GetHackathonsLazyQueryHookResult = ReturnType<typeof useGetHackathonsLazyQuery>;
|
||||
export type GetHackathonsQueryResult = Apollo.QueryResult<GetHackathonsQuery, GetHackathonsQueryVariables>;
|
||||
export const TrendingPostsDocument = gql`
|
||||
query TrendingPosts {
|
||||
getTrendingPosts {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Hackathon } from "src/graphql"
|
||||
import { topics } from "./topics"
|
||||
import { getCoverImage, getItems } from "./utils"
|
||||
|
||||
@@ -12,61 +13,67 @@ export const hackathons = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Fulmo Hackday',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Lightning Leagues',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Surfing on Lightning',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Lightning Startups',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Design-a-thon',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Lightning Olympics',
|
||||
date: '22nd - 28th March, 2022',
|
||||
start_date: new Date(2022, 2, 22),
|
||||
end_date: new Date(2022, 2, 28),
|
||||
location: "Instanbul, Turkey",
|
||||
cover_image: getCoverImage(),
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
|
||||
topics: generateTopics(),
|
||||
url: "https://bolt.fun/hackathons/shock-the-web"
|
||||
website: "https://bolt.fun/hackathons/shock-the-web"
|
||||
},
|
||||
]
|
||||
].map(i => ({ ...i, __typename: "Hackathon" })) as Hackathon[]
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { graphql } from 'msw'
|
||||
import { allCategories, getCategory, getFeed, getPostById, getProject, getTrendingPosts, hottestProjects, newProjects, popularTopics, projectsByCategory, searchProjects } from './resolvers'
|
||||
import { allCategories, getAllHackathons, getCategory, getFeed, getPostById, getProject, getTrendingPosts, hottestProjects, newProjects, popularTopics, projectsByCategory, searchProjects } from './resolvers'
|
||||
import {
|
||||
NavCategoriesQuery,
|
||||
ExploreProjectsQuery,
|
||||
@@ -21,6 +21,10 @@ import {
|
||||
TrendingPostsQuery,
|
||||
PopularTopicsQuery,
|
||||
PopularTopicsQueryVariables,
|
||||
GetHackathonsQuery,
|
||||
GetHackathonsQueryVariables,
|
||||
AllTopicsQuery,
|
||||
AllTopicsQueryVariables,
|
||||
} from 'src/graphql'
|
||||
|
||||
const delay = (ms = 1000) => new Promise((res) => setTimeout(res, ms + Math.random() * 1000))
|
||||
@@ -112,6 +116,15 @@ export const handlers = [
|
||||
)
|
||||
}),
|
||||
|
||||
graphql.query<AllTopicsQuery, AllTopicsQueryVariables>('allTopics', async (req, res, ctx) => {
|
||||
await delay()
|
||||
return res(
|
||||
ctx.data({
|
||||
allTopics: popularTopics()
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
|
||||
graphql.query<FeedQuery, FeedQueryVariables>('Feed', async (req, res, ctx) => {
|
||||
await delay()
|
||||
@@ -148,4 +161,15 @@ export const handlers = [
|
||||
)
|
||||
}),
|
||||
|
||||
|
||||
graphql.query<GetHackathonsQuery, GetHackathonsQueryVariables>('getHackathons', async (req, res, ctx) => {
|
||||
await delay()
|
||||
|
||||
return res(
|
||||
ctx.data({
|
||||
getAllHackathons: getAllHackathons()
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
]
|
||||
@@ -2,6 +2,7 @@ import { MOCK_DATA } from "./data";
|
||||
import { Query, QueryGetFeedArgs, QueryGetPostByIdArgs } from 'src/graphql'
|
||||
import { Chance } from "chance";
|
||||
import { topics } from "./data/topics";
|
||||
import { hackathons } from "./data/hackathon";
|
||||
|
||||
const chance = new Chance()
|
||||
|
||||
@@ -63,4 +64,8 @@ export function getTrendingPosts(): Query['getTrendingPosts'] {
|
||||
|
||||
export function popularTopics() {
|
||||
return topics;
|
||||
}
|
||||
|
||||
export function getAllHackathons() {
|
||||
return hackathons;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebLNProvider } from "webln";
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ export function randomItem(...args: any[]) {
|
||||
}
|
||||
|
||||
export function randomItems(cnt: number, ...args: any[]) {
|
||||
console.log(cnt);
|
||||
|
||||
return shuffle(args).slice(0, cnt);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { QueryResult } from "@apollo/client";
|
||||
import { useUpdateEffect } from "@react-hookz/web";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export const useInfiniteQuery = (query: QueryResult, dataField: string) => {
|
||||
|
||||
Reference in New Issue
Block a user