From 55587963a6de8129079178eafc30392824d58fdf Mon Sep 17 00:00:00 2001
From: Anurag Negi <115611556+anuragnegi000@users.noreply.github.com>
Date: Thu, 29 Aug 2024 19:58:41 +0530
Subject: [PATCH] fix: Improvement: Landing page improvement #271 (#278)
* removed login and ModeToggle button(for dark/light mode switch) added infinite scroll for companies logo added debouncing and skeleton in place of jobs on load of screen
* removed unwanted images
* fixes
---
package.json | 1 +
public/google.svg | 10 ++
public/microsoft.svg | 10 ++
public/solana.svg | 12 ++
src/app/globals.css | 209 +++++++++++++++++++++++++++++
src/components/BackgroundSvg.tsx | 2 +-
src/components/hero-section.tsx | 11 +-
src/components/infinitescroll.tsx | 115 ++++++++++++++++
src/components/job-card-loader.tsx | 37 +++++
src/components/job-landing.tsx | 10 +-
src/components/ui/Marquee.tsx | 52 +++++++
src/components/ui/skeleton.tsx | 15 +++
src/layouts/header.tsx | 31 ++---
src/layouts/jobs-header.tsx | 42 +++++-
tailwind.config.ts | 21 ++-
15 files changed, 531 insertions(+), 47 deletions(-)
create mode 100644 public/google.svg
create mode 100644 public/microsoft.svg
create mode 100644 public/solana.svg
create mode 100644 src/components/infinitescroll.tsx
create mode 100644 src/components/job-card-loader.tsx
create mode 100644 src/components/ui/Marquee.tsx
create mode 100644 src/components/ui/skeleton.tsx
diff --git a/package.json b/package.json
index da59a0e3..940ac277 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
+ "@uidotdev/usehooks": "^2.4.1",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
diff --git a/public/google.svg b/public/google.svg
new file mode 100644
index 00000000..95932e27
--- /dev/null
+++ b/public/google.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/public/microsoft.svg b/public/microsoft.svg
new file mode 100644
index 00000000..89fba50e
--- /dev/null
+++ b/public/microsoft.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/solana.svg b/public/solana.svg
new file mode 100644
index 00000000..5a68bf0f
--- /dev/null
+++ b/public/solana.svg
@@ -0,0 +1,12 @@
+
diff --git a/src/app/globals.css b/src/app/globals.css
index e4aecf04..6228a08a 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,6 +3,9 @@
@tailwind utilities;
@layer base {
+ .paused {
+ animation-play-state: paused;
+ }
:root {
--stroke-primary: 0 0% 90%;
--stroke-secondary: 0 0% 92.44%;
@@ -88,3 +91,209 @@
stroke-dashoffset: 0;
}
}
+@keyframes scroll {
+ 0% {
+ transform: translateX(0);
+ }
+ 100% {
+ transform: translateX(-100%);
+ }
+}
+
+.wrapper {
+ margin-top: 2rem;
+ width: 90%;
+ max-width: 1536px;
+ margin-inline: auto;
+ height: 200px;
+ position: relative;
+ overflow: hidden;
+ mask-image: linear-gradient(
+ to right,
+ rgba(0, 0, 0, 0),
+ rgba(0, 0, 0, 1) 5%,
+ rgba(0, 0, 0, 1) 95%,
+ rgba(0, 0, 0, 0)
+ );
+}
+.itemLeft img {
+ height: 100%;
+ width: 100%;
+ border-radius: 10px;
+}
+.itemRight img {
+ height: 100%;
+ width: 100%;
+ border-radius: 10px;
+}
+
+@keyframes scrollLeft {
+ to {
+ left: -200px;
+ }
+}
+
+@keyframes scrollRight {
+ to {
+ right: -200px;
+ }
+}
+
+.itemLeft,
+.itemRight {
+ width: 200px;
+ height: 180px;
+ /* background-color: #e11d48; */
+ border-radius: 10px;
+ position: absolute;
+ animation-timing-function: linear;
+ animation-duration: 30s;
+ animation-iteration-count: infinite;
+}
+
+.itemLeft {
+ left: max(calc(200px * 8), 100%);
+ animation-name: scrollLeft;
+}
+
+.itemRight {
+ right: max(calc(200px * 8), calc(100% + 200px));
+ animation-name: scrollRight;
+}
+
+.item1 {
+ animation-delay: calc(30s / 8 * (8 - 1) * -1);
+}
+
+.item2 {
+ animation-delay: calc(30s / 8 * (8 - 2) * -1);
+}
+
+.item3 {
+ animation-delay: calc(30s / 8 * (8 - 3) * -1);
+}
+
+.item4 {
+ animation-delay: calc(30s / 8 * (8 - 4) * -1);
+}
+
+.item5 {
+ animation-delay: calc(30s / 8 * (8 - 5) * -1);
+}
+
+.item6 {
+ animation-delay: calc(30s / 8 * (8 - 6) * -1);
+}
+
+.item7 {
+ animation-delay: calc(30s / 8 * (8 - 7) * -1);
+}
+
+.item8 {
+ animation-delay: calc(30s / 8 * (8 - 8) * -1);
+}
+
+/* Responsive Adjustments */
+@media (max-width: 768px) {
+ .wrapper {
+ height: 150px;
+ }
+
+ .itemLeft,
+ .itemRight {
+ width: 150px;
+ height: 130px;
+ }
+
+ .itemLeft {
+ left: max(calc(150px * 8), 100%);
+ }
+
+ .itemRight {
+ right: max(calc(150px * 8), calc(100% + 150px));
+ }
+}
+
+@media (max-width: 480px) {
+ .wrapper {
+ height: 100px;
+ }
+
+ .itemLeft,
+ .itemRight {
+ width: 100px;
+ height: 80px;
+ }
+
+ .itemLeft {
+ left: max(calc(100px * 8), 100%);
+ }
+
+ .itemRight {
+ right: max(calc(100px * 8), calc(100% + 100px));
+ }
+}
+.scroll-container {
+ overflow: hidden;
+ white-space: nowrap;
+ width: 100%;
+ position: relative;
+}
+
+.scroll-content {
+ display: flex;
+ animation: scroll 30s linear infinite;
+}
+
+.scroll-content::after {
+ content: '';
+ display: flex;
+ animation: scroll 30s linear infinite;
+}
+
+.scroll-content a {
+ flex: 0 0 auto;
+}
+
+@keyframes scroll {
+ 0% {
+ transform: translateX(0%);
+ }
+ 100% {
+ transform: translateX(-50%);
+ }
+}
+
+/* For small devices */
+@media (max-width: 600px) {
+ .scroll-content,
+ .scroll-content::after {
+ animation: scroll-small 1s linear infinite;
+ }
+
+ @keyframes scroll-small {
+ 0% {
+ transform: translateX(0%);
+ }
+ 100% {
+ transform: translateX(-50%);
+ }
+ }
+}
+.loader {
+ border: 8px solid #f3f3f3;
+ border-radius: 50%;
+ border-top: 8px solid #3498db;
+ width: 40px;
+ height: 40px;
+ animation: spin 2s linear infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/BackgroundSvg.tsx b/src/components/BackgroundSvg.tsx
index c492b6c3..dcf5c381 100644
--- a/src/components/BackgroundSvg.tsx
+++ b/src/components/BackgroundSvg.tsx
@@ -12,7 +12,7 @@ export default function BackgroundSvg() {
left: 0,
zIndex: -1,
minHeight: '100vh',
- minWidth: '100vw',
+ maxWidth: '100vw',
objectFit: 'cover',
overflow: 'hidden',
}}
diff --git a/src/components/hero-section.tsx b/src/components/hero-section.tsx
index 5f216a17..097f83c1 100644
--- a/src/components/hero-section.tsx
+++ b/src/components/hero-section.tsx
@@ -1,7 +1,7 @@
import { GITHUB_REPO } from '@/lib/constant/app.constant';
import Link from 'next/link';
import Icon from './ui/icon';
-import Image from 'next/image';
+import { MarqueeDemo } from './infinitescroll';
const HeroSection = () => {
return (
@@ -35,14 +35,7 @@ const HeroSection = () => {
-
-
-
+
>
);
diff --git a/src/components/infinitescroll.tsx b/src/components/infinitescroll.tsx
new file mode 100644
index 00000000..78155f28
--- /dev/null
+++ b/src/components/infinitescroll.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import Marquee from './ui/Marquee';
+import Image from 'next/image';
+// import Marquee from "@/components/magicui/marquee";
+
+const reviews = [
+ {
+ name: 'Jack',
+ username: '@jack',
+ body: "I've never seen anything like this before. It's amazing. I love it.",
+ img: 'https://avatar.vercel.sh/jack',
+ },
+ {
+ name: 'Jill',
+ username: '@jill',
+ body: "I don't know what to say. I'm speechless. This is amazing.",
+ img: 'https://avatar.vercel.sh/jill',
+ },
+ {
+ name: 'John',
+ username: '@john',
+ body: "I'm at a loss for words. This is amazing. I love it.",
+ img: 'https://avatar.vercel.sh/john',
+ },
+ {
+ name: 'Jane',
+ username: '@jane',
+ body: "I'm at a loss for words. This is amazing. I love it.",
+ img: 'https://avatar.vercel.sh/jane',
+ },
+ {
+ name: 'Jenny',
+ username: '@jenny',
+ body: "I'm at a loss for words. This is amazing. I love it.",
+ img: 'https://avatar.vercel.sh/jenny',
+ },
+ {
+ name: 'James',
+ username: '@james',
+ body: "I'm at a loss for words. This is amazing. I love it.",
+ img: 'https://avatar.vercel.sh/james',
+ },
+];
+
+const imageList = [
+ {
+ id: 1,
+ src: './microsoft.svg',
+ },
+ {
+ id: 2,
+ src: './solana.svg',
+ },
+ {
+ id: 3,
+ src: './google.svg',
+ },
+];
+
+const firstRow = reviews.slice(0, reviews.length / 2);
+
+const ReviewCard = ({}) => {
+ return (
+
+ {imageList.concat(imageList).map((item, index) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export function MarqueeDemo() {
+ return (
+
+
+
+ {/*
+
*/}
+
+ );
+}
+
+//
diff --git a/src/components/job-card-loader.tsx b/src/components/job-card-loader.tsx
new file mode 100644
index 00000000..2ae82b45
--- /dev/null
+++ b/src/components/job-card-loader.tsx
@@ -0,0 +1,37 @@
+import { Skeleton } from '@/components/ui/skeleton';
+import React from 'react';
+
+const JobCardLoader = () => {
+ return (
+
+ );
+};
+
+export default JobCardLoader;
diff --git a/src/components/job-landing.tsx b/src/components/job-landing.tsx
index 6df56fc6..e986c8a9 100644
--- a/src/components/job-landing.tsx
+++ b/src/components/job-landing.tsx
@@ -1,11 +1,11 @@
import { getAllJobs } from '@/actions/job.action';
import { formatSalary } from '@/lib/utils';
import Link from 'next/link';
+import JobCardLoader from '@/components/job-card-loader';
import { DEFAULT_PAGE, JOBS_PER_PAGE } from '@/config/app.config';
import JobsHeader from '@/layouts/jobs-header';
import { Suspense } from 'react';
-import { Loader } from 'lucide-react';
import { JobQuerySchemaType } from '@/lib/validators/jobs.validator';
import { Pagination, PaginationContent, PaginationItem } from './ui/pagination';
import {
@@ -32,13 +32,7 @@ export const JobLanding = async ({
diff --git a/src/components/ui/Marquee.tsx b/src/components/ui/Marquee.tsx
new file mode 100644
index 00000000..98a3a56f
--- /dev/null
+++ b/src/components/ui/Marquee.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { cn } from '../../lib/utils';
+
+interface MarqueeProps {
+ className?: string;
+ reverse?: boolean;
+ pauseOnHover?: boolean;
+ children?: React.ReactNode;
+ vertical?: boolean;
+ repeat?: number;
+ [key: string]: any;
+}
+
+export default function Marquee({
+ className,
+ reverse,
+ pauseOnHover = false,
+ children,
+ vertical = false,
+ repeat = 4,
+ ...props
+}: MarqueeProps) {
+ return (
+
+ {Array(repeat)
+ .fill(0)
+ .map((_, i) => (
+
+ {children}
+
+ ))}
+
+ );
+}
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
new file mode 100644
index 00000000..a626d9ba
--- /dev/null
+++ b/src/components/ui/skeleton.tsx
@@ -0,0 +1,15 @@
+import { cn } from '@/lib/utils';
+
+function Skeleton({
+ className,
+ ...props
+}: React.HTMLAttributes) {
+ return (
+
+ );
+}
+
+export { Skeleton };
diff --git a/src/layouts/header.tsx b/src/layouts/header.tsx
index 23fce6d1..1686ab2c 100644
--- a/src/layouts/header.tsx
+++ b/src/layouts/header.tsx
@@ -1,13 +1,11 @@
'use client';
import { MobileNav } from '@/layouts/mobile-nav';
-import { ModeToggle } from '@/components/ui/theme-toggle';
-import APP_PATHS from '@/config/path.config';
import { navbar } from '@/lib/constant/app.constant';
import { useSession } from 'next-auth/react';
import Link from 'next/link';
-import { ProfileMenu } from '@/components/profile-menu';
import { NavItem } from '@/components/navitem';
import Image from 'next/image';
+import { Skeleton } from '@/components/ui/skeleton';
const CompanyLogo = () => {
return (
@@ -39,28 +37,15 @@ const Header = () => {
-
-
- {session.status !== 'loading' && !session.data?.user && (
- <>
-
- Login
-
- >
- )}
- {session.status !== 'loading' && session.data?.user && (
-
- )}
-
-
diff --git a/src/layouts/jobs-header.tsx b/src/layouts/jobs-header.tsx
index 1daf3ef2..125bd4ec 100644
--- a/src/layouts/jobs-header.tsx
+++ b/src/layouts/jobs-header.tsx
@@ -17,6 +17,8 @@ import { JobQuerySchemaType } from '@/lib/validators/jobs.validator';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
+import { useRouter } from 'next/navigation';
+
import {
Form,
FormControl,
@@ -24,12 +26,13 @@ import {
FormItem,
FormMessage,
} from '@/components/ui/form';
+import { useDebounce } from '@uidotdev/usehooks';
import { Input } from '@/components/ui/input';
import { usePathname } from 'next/navigation';
import JobFilters from './job-filters';
import Icon from '@/components/ui/icon';
import APP_PATHS from '@/config/path.config';
-
+import { useEffect } from 'react';
const FormSchema = z.object({
search: z.string().optional(),
});
@@ -43,6 +46,11 @@ const JobsHeader = ({
}) => {
const pathname = usePathname();
const isHome = pathname === APP_PATHS.HOME;
+ const router = useRouter();
+
+ function sortChangeHandler(value: SortByEnums) {
+ jobFilterQuery({ ...searchParams, sortby: value, page: 1 }, baseUrl);
+ }
let debounceTimeout: NodeJS.Timeout;
@@ -52,11 +60,35 @@ const JobsHeader = ({
search: '',
},
});
+
+ const searchValue = form.watch('search');
+ const debouncedSearchValue = useDebounce(searchValue, 100);
+
+ useEffect(() => {
+ const fetch = async () => {
+ if (debouncedSearchValue !== 'undefined') {
+ if (debouncedSearchValue?.length) {
+ await onSubmit({ search: debouncedSearchValue });
+ } else {
+ router.push(baseUrl);
+ }
+ } else {
+ router.push(baseUrl);
+ }
+ };
+
+ fetch();
+ }, [debouncedSearchValue]);
+
function onSubmit(data: z.infer
) {
- jobFilterQuery({ ...searchParams, search: data.search, page: 1 }, baseUrl);
- }
- function sortChangeHandler(value: SortByEnums) {
- jobFilterQuery({ ...searchParams, sortby: value, page: 1 }, baseUrl);
+ jobFilterQuery(
+ {
+ ...searchParams,
+ search: data.search,
+ page: 1,
+ },
+ baseUrl
+ );
}
return (
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 201536c3..38354fd9 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -26,7 +26,6 @@ const config = {
backgroundImage: {
'blue-gradient': 'radial-gradient(circle, #3b82f6, #1e3a8a)',
},
-
colors: {
'stroke-primary': 'hsl(var(--stroke-primary))',
'stroke-secondary': 'hsl(var(--stroke-secondary))',
@@ -70,6 +69,14 @@ const config = {
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
+ marquee: {
+ from: { transform: 'translateX(0)' },
+ to: { transform: 'translateX(calc(-100% - var(--gap)))' },
+ },
+ 'marquee-vertical': {
+ from: { transform: 'translateY(0)' },
+ to: { transform: 'translateY(calc(-100% - var(--gap)))' },
+ },
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
@@ -78,10 +85,22 @@ const config = {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
+ scroll: {
+ '0%': { transform: 'translateX(0)' },
+ '100%': { transform: 'translateX(-100%)' },
+ },
+ 'loop-scroll': {
+ from: { transform: 'translateX(0%)' },
+ to: { transform: 'translateX(-100%)' },
+ },
},
animation: {
+ marquee: 'marquee var(--duration) linear infinite',
+ 'marquee-vertical': 'marquee-vertical var(--duration) linear infinite',
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
+ scroll: 'scroll 5s linear infinite',
+ 'loop-scroll': 'loop-scroll 50s linear infinite',
},
},
},