mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-26 18:54:21 +01:00
fix: fix some QA issues
- fix navbar height - remove input shadow - change donateCard options style - add 2px borders to cards - remove search from nav on sections other than products - implement useMediaQuery
This commit is contained in:
13
src/App.tsx
13
src/App.tsx
@@ -3,7 +3,7 @@ import Navbar from "src/Components/Navbar/Navbar";
|
||||
import ModalsContainer from "src/Components/Modals/ModalsContainer/ModalsContainer";
|
||||
import { useAppSelector } from './utils/hooks';
|
||||
import { Wallet_Service } from "./services";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { useWrapperSetup } from "./utils/Wrapper";
|
||||
import LoadingPage from "./Components/LoadingPage/LoadingPage";
|
||||
|
||||
@@ -45,13 +45,18 @@ function App() {
|
||||
<Navbar />
|
||||
<Suspense fallback={<LoadingPage />}>
|
||||
<Routes>
|
||||
<Route path="/hottest" element={<HottestPage />} />
|
||||
<Route path="/category/:id" element={<CategoryPage />} />
|
||||
<Route path="/products/hottest" element={<HottestPage />} />
|
||||
<Route path="/products/category/:id" element={<CategoryPage />} />
|
||||
<Route path="/products" element={<ExplorePage />} />
|
||||
|
||||
<Route path="/blog/post/:type/:id" element={<PostDetailsPage />} />
|
||||
<Route path="/blog" element={<FeedPage />} />
|
||||
|
||||
<Route path="/hackathons" element={<HackathonsPage />} />
|
||||
|
||||
<Route path="/donate" element={<DonatePage />} />
|
||||
<Route path="/" element={<ExplorePage />} />
|
||||
|
||||
<Route path="/" element={<Navigate to="/products" />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
<ModalsContainer />
|
||||
|
||||
@@ -33,13 +33,14 @@ export default function CategoriesList({ classes = {}, onClick }: Props) {
|
||||
<>
|
||||
{data?.allCategories.map(category =>
|
||||
<MenuItem
|
||||
className={`w-full !p-16 text-body4 font-semibold hover:bg-gray-100 !rounded-8 flex w-items-center ${classes.item}`}
|
||||
onClick={() => {
|
||||
onClick?.(category.id)
|
||||
navigate(`/category/${category.id}`)
|
||||
}}
|
||||
key={category.id}
|
||||
href={`/category/${category.id}`}
|
||||
className={`w-full !p-16 text-body4 font-semibold hover:bg-gray-100 !rounded-8 flex w-items-center ${classes.item}`}
|
||||
href={`/products/category/${category.id}`}
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
onClick?.(category.id)
|
||||
navigate(`/products/category/${category.id}`)
|
||||
}}
|
||||
>
|
||||
<span className="text-body3 mr-8">{category.icon}</span> {category.title} <span className="ml-auto pl-8 text-body5 font-normal text-gray-400">{numberFormatter(category.votes_sum)}</span>
|
||||
</MenuItem>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { BsSearch } from "react-icons/bs";
|
||||
import { motion } from "framer-motion";
|
||||
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
|
||||
import { useAppDispatch, useAppSelector, useCurrentSection } from "src/utils/hooks";
|
||||
import { openModal } from "src/redux/features/modals.slice";
|
||||
import Button from "../Button/Button";
|
||||
import ASSETS from "src/assets";
|
||||
@@ -46,27 +46,11 @@ export default function NavDesktop() {
|
||||
};
|
||||
|
||||
|
||||
|
||||
const onConnectWallet = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
Modal: "Login_ScanningWalletCard",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onWithdraw = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
Modal: "Claim_FundWithdrawCard",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const currentSection = useCurrentSection();
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
return (<nav className="bg-white w-full flex fixed h-[118px] top-0 left-0 py-36 px-32 items-center z-[2010]">
|
||||
return (<nav className="bg-white w-full flex fixed h-[72px] top-0 left-0 py-36 px-32 items-center z-[2010]">
|
||||
<a href="https://bolt.fun/">
|
||||
<h2 className="text-h5 font-bold mr-40 lg:mr-64">
|
||||
<img className='h-40' src={ASSETS.Logo} alt="Bolt fun logo" />
|
||||
@@ -76,20 +60,20 @@ export default function NavDesktop() {
|
||||
<li>
|
||||
<Menu offsetY={24} menuClassName='!rounded-12' menuButton={<MenuButton className='text-body4 font-bold hover:text-primary-600'>LApps <FiChevronDown className="ml-8" /></MenuButton>}>
|
||||
<MenuItem
|
||||
href="/"
|
||||
href="/products"
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
navigate("/");
|
||||
navigate("/products");
|
||||
}}
|
||||
className='!px-24 !py-16 font-medium'
|
||||
>
|
||||
<MdOutlineExplore className={`text-body1 inline-block mr-12 text-primary-600 `} /> Explore
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
href="/hottest"
|
||||
href="/products/hottest"
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
navigate("/hottest");
|
||||
navigate("/products/hottest");
|
||||
}}
|
||||
className='!px-24 !py-16 font-medium'
|
||||
>
|
||||
@@ -172,9 +156,9 @@ export default function NavDesktop() {
|
||||
: <Button className="ml-16 py-12 px-16 lg:px-20" onClick={onConnectWallet}><AiFillThunderbolt className='inline-block text-thunder transform scale-125' /> Connect Wallet </Button>
|
||||
} */}
|
||||
|
||||
<IconButton className='ml-16 self-center' onClick={handleSearchClick}>
|
||||
{currentSection === 'products' && <IconButton className='ml-16 self-center' onClick={handleSearchClick}>
|
||||
<BsSearch className='scale-125 text-gray-400' />
|
||||
</IconButton>
|
||||
</IconButton>}
|
||||
</motion.div>
|
||||
<Search />
|
||||
|
||||
|
||||
@@ -163,20 +163,20 @@ export default function NavMobile({ }: Props) {
|
||||
<li>
|
||||
<Menu offsetY={24} menuClassName='!rounded-12' menuButton={<MenuButton className='text-body4 font-bold hover:text-primary-600'>LApps <FiChevronDown className="ml-8" /></MenuButton>}>
|
||||
<MenuItem
|
||||
href="/"
|
||||
href="/products"
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
navigate("/");
|
||||
navigate("/products");
|
||||
}}
|
||||
className='!px-24 !py-16 font-medium'
|
||||
>
|
||||
<MdOutlineExplore className={`text-body1 inline-block mr-12 text-primary-600 `} /> Explore
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
href="/hottest"
|
||||
href="/products/hottest"
|
||||
onClick={(e) => {
|
||||
e.syntheticEvent.preventDefault();
|
||||
navigate("/hottest");
|
||||
navigate("/products/hottest");
|
||||
}}
|
||||
className='!px-24 !py-16 font-medium'
|
||||
>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import NavMobile from "./NavMobile";
|
||||
import { MdComment, MdHomeFilled, MdLocalFireDepartment } from "react-icons/md";
|
||||
import { IoExtensionPuzzle } from "react-icons/io5";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useAppDispatch, useAppSelector, useMediaQuery } from "src/utils/hooks";
|
||||
import { openModal } from "src/redux/features/modals.slice";
|
||||
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";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
|
||||
export const navLinks = [
|
||||
@@ -39,34 +39,18 @@ export const navLinks = [
|
||||
// },
|
||||
];
|
||||
|
||||
|
||||
export default function Navbar() {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { isWalletConnected, isMobileScreen } = useAppSelector((state) => ({
|
||||
isWalletConnected: state.wallet.isConnected,
|
||||
isMobileScreen: state.ui.isMobileScreen
|
||||
}));
|
||||
// const { isWalletConnected, isMobileScreen } = useAppSelector((state) => ({
|
||||
// isWalletConnected: state.wallet.isConnected,
|
||||
// isMobileScreen: state.ui.isMobileScreen
|
||||
// }));
|
||||
|
||||
const isLargeScreen = useMediaQuery(MEDIA_QUERIES.isLarge)
|
||||
|
||||
|
||||
const onConnectWallet = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
Modal: "Login_ScanningWalletCard",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onWithdraw = () => {
|
||||
dispatch(
|
||||
openModal({
|
||||
Modal: "Claim_FundWithdrawCard",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const nav = document.querySelector("nav");
|
||||
|
||||
@@ -84,17 +68,15 @@ export default function Navbar() {
|
||||
document.body.style.paddingTop = oldPadding
|
||||
}
|
||||
|
||||
}, [dispatch, isMobileScreen, isLargeScreen])
|
||||
|
||||
|
||||
}, [dispatch])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{(isMobileScreen || !isLargeScreen) ?
|
||||
<NavMobile />
|
||||
:
|
||||
{(isLargeScreen) ?
|
||||
<NavDesktop />
|
||||
:
|
||||
<NavMobile />
|
||||
}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -15,18 +15,15 @@ const defaultOptions = [
|
||||
export default function DonateCard() {
|
||||
|
||||
const size = useWindowSize();
|
||||
const [selectedOption, setSelectedOption] = useState(-1);
|
||||
const [donationAmount, setDonationAmount] = useState<number>();
|
||||
|
||||
const { donate, paymentStatus, isLoading } = useDonate()
|
||||
|
||||
const onChangeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedOption(-1);
|
||||
setDonationAmount(Number(event.target.value));
|
||||
};
|
||||
|
||||
const onSelectOption = (idx: number) => {
|
||||
setSelectedOption(idx);
|
||||
setDonationAmount(defaultOptions[idx].value);
|
||||
}
|
||||
|
||||
@@ -37,13 +34,11 @@ export default function DonateCard() {
|
||||
onSuccess: () => {
|
||||
setTimeout(() => {
|
||||
setDonationAmount(undefined);
|
||||
setSelectedOption(-1);
|
||||
}, 4000);
|
||||
},
|
||||
onError: () => {
|
||||
setTimeout(() => {
|
||||
setDonationAmount(undefined);
|
||||
setSelectedOption(-1);
|
||||
}, 4000);
|
||||
}
|
||||
});
|
||||
@@ -60,7 +55,7 @@ export default function DonateCard() {
|
||||
className={`input-text input-removed-arrows`}
|
||||
value={donationAmount} onChange={onChangeInput}
|
||||
type="number"
|
||||
placeholder="1,000"
|
||||
placeholder="Select or enter amount"
|
||||
autoFocus
|
||||
/>
|
||||
<p className='px-16 shrink-0 self-center text-primary-400'>
|
||||
@@ -72,7 +67,7 @@ export default function DonateCard() {
|
||||
<button
|
||||
type='button'
|
||||
key={idx}
|
||||
className={`btn border px-12 rounded-md py-8 text-body5 bg-primary-100 hover:bg-primary-50 text-primary-600 ${idx === selectedOption && "border-primary-500 bg-primary-100 hover:bg-primary-100 text-primary-600"}`}
|
||||
className={`btn border-0 px-12 rounded-md py-8 text-body5 bg-primary-50 hover:bg-primary-100 outline-2 outline-primary-200 active:outline `}
|
||||
onClick={() => onSelectOption(idx)}
|
||||
>
|
||||
{option.text}
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function DonatePage() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-full`}
|
||||
className={`w-full bg-white`}
|
||||
>
|
||||
<Header />
|
||||
<div className={`${styles.faq}`}>
|
||||
|
||||
@@ -14,7 +14,7 @@ interface Props {
|
||||
|
||||
export default function HackathonCard({ hackathon }: Props) {
|
||||
return (
|
||||
<div className="rounded-16 bg-white overflow-hidden">
|
||||
<div className="rounded-16 bg-white overflow-hidden border-2">
|
||||
<img className="w-full h-[120px] object-cover" src={hackathon.cover_image} alt="" />
|
||||
<div className="p-16">
|
||||
<div className="flex flex-col gap-8">
|
||||
|
||||
@@ -22,7 +22,7 @@ interface Props {
|
||||
|
||||
export default function SortByFilter({ filterChanged }: Props) {
|
||||
|
||||
const [selected, setSelected] = useState<string | null>(null);
|
||||
const [selected, setSelected] = useState<string | null>('Upcoming');
|
||||
|
||||
const filterClicked = (_newValue: string | null) => {
|
||||
const newValue = selected !== _newValue ? _newValue : null;
|
||||
@@ -37,7 +37,7 @@ export default function SortByFilter({ filterChanged }: Props) {
|
||||
<>
|
||||
{
|
||||
isMdScreen ?
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<div className='bg-white border-2 rounded-12 p-16'>
|
||||
< p className="text-body2 font-bolder text-black mb-16" > Sort By</p >
|
||||
<ul>
|
||||
{filters.map((f, idx) => <li
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
|
||||
return (
|
||||
<>
|
||||
{isMdScreen ?
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<div className='bg-white border-2 rounded-12 p-16'>
|
||||
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
|
||||
<ul className=' flex flex-col gap-16'>
|
||||
{topicsQuery.loading ?
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function HackathonsPage() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`page-container pt-16 w-full ${styles.grid}`}
|
||||
className={`page-container pt-16 w-full ${styles.grid}`}
|
||||
>
|
||||
<aside className='no-scrollbar'>
|
||||
<div className="sticky flex flex-col gap-24 md:overflow-y-scroll"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
gap: 32px;
|
||||
gap: 24px;
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
grid-template-columns: 1fr 2fr 0;
|
||||
gap: 32px;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
|
||||
return (
|
||||
<>
|
||||
{isMdScreen ?
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<div className='bg-white border-2 rounded-12 p-16'>
|
||||
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
|
||||
<ul className=' flex flex-col gap-16'>
|
||||
{topicsQuery.loading ?
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function SortBy({ filterChanged }: Props) {
|
||||
<>
|
||||
{
|
||||
isMdScreen ?
|
||||
<div className='bg-white border rounded-12 p-16'>
|
||||
<div className='bg-white border-2 rounded-12 p-16'>
|
||||
< p className="text-body2 font-bolder text-black mb-16" > Sort By</p >
|
||||
<ul>
|
||||
{filters.map((f, idx) => <li
|
||||
|
||||
@@ -3,47 +3,47 @@
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply text-gray-900 py-12 px-42 bg-gray-25 hover:bg-gray-50 font-sans rounded-lg font-regular border border-gray-300;
|
||||
}
|
||||
.btn {
|
||||
@apply text-gray-900 py-12 px-42 bg-gray-25 hover:bg-gray-50 font-sans rounded-lg font-regular border border-gray-300;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-primary-500 border-0 shadow-sm hover:bg-primary-400 text-white;
|
||||
}
|
||||
.btn-primary {
|
||||
@apply bg-primary-500 border-0 shadow-sm hover:bg-primary-400 text-white;
|
||||
}
|
||||
|
||||
.btn-gray {
|
||||
@apply bg-gray-100 hover:bg-gray-200 text-gray-900;
|
||||
}
|
||||
.btn-gray {
|
||||
@apply bg-gray-100 hover:bg-gray-200 text-gray-900;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
@apply relative w-full border border-gray-300 rounded-lg shadow-xs flex focus-within:outline-primary-400 focus-within:border-primary-300 focus-within:ring focus-within:ring-primary-200 focus-within:ring-opacity-50;
|
||||
}
|
||||
.input-wrapper {
|
||||
@apply relative w-full border border-gray-300 rounded-lg flex focus-within:outline-primary-400 focus-within:border-primary-300 focus-within:ring focus-within:ring-primary-200 focus-within:ring-opacity-50;
|
||||
}
|
||||
|
||||
.input-text {
|
||||
@apply flex-grow border-none focus:border-0 focus:ring-0 bg-transparent min-w-0 text-primary-500;
|
||||
}
|
||||
.input-text {
|
||||
@apply flex-grow border-none focus:border-0 focus:ring-0 bg-transparent min-w-0 text-primary-500;
|
||||
}
|
||||
|
||||
.input-checkbox {
|
||||
@apply rounded-4 bg-gray-200 border-transparent focus:border-transparent focus:bg-primary-200 text-primary-700 focus:ring-1 focus:ring-offset-2 focus:ring-primary-500;
|
||||
}
|
||||
.input-checkbox {
|
||||
@apply rounded-4 bg-gray-200 border-transparent focus:border-transparent focus:bg-primary-200 text-primary-700 focus:ring-1 focus:ring-offset-2 focus:ring-primary-500;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
@apply h-full text-primary-500 flex-shrink-0 w-42 px-12 self-center;
|
||||
}
|
||||
.input-icon {
|
||||
@apply h-full text-primary-500 flex-shrink-0 w-42 px-12 self-center;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
@apply text-body6 text-red-500 mt-4;
|
||||
}
|
||||
.input-error {
|
||||
@apply text-body6 text-red-500 mt-4;
|
||||
}
|
||||
|
||||
.chip {
|
||||
@apply bg-gray-100 text-body4 px-16 py-8 rounded-24 font-regular;
|
||||
}
|
||||
.chip {
|
||||
@apply bg-gray-100 text-body4 px-16 py-8 rounded-24 font-regular;
|
||||
}
|
||||
|
||||
.chip-small {
|
||||
@apply bg-gray-100 text-body5 px-12 py-8 rounded-16 font-regular;
|
||||
}
|
||||
.chip-small {
|
||||
@apply bg-gray-100 text-body5 px-12 py-8 rounded-16 font-regular;
|
||||
}
|
||||
|
||||
.modal-card {
|
||||
@apply rounded-[40px] bg-gray-50 overflow-hidden w-full shadow-2xl z-10;
|
||||
}
|
||||
}
|
||||
.modal-card {
|
||||
@apply rounded-[40px] bg-gray-50 overflow-hidden w-full shadow-2xl z-10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,3 +7,5 @@ export * from "./useAutoResizableTextArea";
|
||||
export * from "./useCopyToClipboard";
|
||||
export * from "./useVote";
|
||||
export * from './useWindowSize'
|
||||
export * from './useMediaQuery'
|
||||
export * from './useCurrentSection'
|
||||
|
||||
18
src/utils/hooks/useCurrentSection.ts
Normal file
18
src/utils/hooks/useCurrentSection.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useLocation } from "react-router-dom"
|
||||
|
||||
|
||||
|
||||
|
||||
export const useCurrentSection = () => {
|
||||
const location = useLocation();
|
||||
|
||||
if (location.pathname.startsWith('/blog'))
|
||||
return 'blog';
|
||||
if (location.pathname.startsWith('/hackathons'))
|
||||
return 'hackathons';
|
||||
if (location.pathname.startsWith('/products'))
|
||||
return 'products';
|
||||
if (location.pathname.startsWith('/donate'))
|
||||
return 'donate';
|
||||
return 'other'
|
||||
}
|
||||
43
src/utils/hooks/useMediaQuery.ts
Normal file
43
src/utils/hooks/useMediaQuery.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
|
||||
export function useMediaQuery(query: string): boolean {
|
||||
const getMatches = (query: string): boolean => {
|
||||
// Prevents SSR issues
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.matchMedia(query).matches
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const [matches, setMatches] = useState<boolean>(getMatches(query))
|
||||
|
||||
function handleChange() {
|
||||
setMatches(getMatches(query))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const matchMedia = window.matchMedia(query)
|
||||
|
||||
// Triggered at the first client-side load and if query changes
|
||||
handleChange()
|
||||
|
||||
// Listen matchMedia
|
||||
if (matchMedia.addListener) {
|
||||
matchMedia.addListener(handleChange)
|
||||
} else {
|
||||
matchMedia.addEventListener('change', handleChange)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (matchMedia.removeListener) {
|
||||
matchMedia.removeListener(handleChange)
|
||||
} else {
|
||||
matchMedia.removeEventListener('change', handleChange)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [query])
|
||||
|
||||
return matches
|
||||
}
|
||||
Reference in New Issue
Block a user