From dde72f640f30fbde437c045aa175316b13b49d50 Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Wed, 24 Sep 2025 22:52:48 +0530 Subject: [PATCH 1/9] adding more functionality like filter by profession and salary --- src/app/page.tsx | 216 +++++++++++++++++++++++++++++++------- src/components/Navbar.tsx | 67 ++++++++++++ 2 files changed, 246 insertions(+), 37 deletions(-) create mode 100644 src/components/Navbar.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index 23eaf49..5f84a94 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,57 +1,199 @@ 'use client' import { WorkerType } from '@/types/workers' import Image from 'next/image' -import { useState, useEffect } from 'react' +import { useState, useEffect, useMemo } from 'react' +import Navbar from '@/components/Navbar' export default function WorkersPage() { const [workersData, setWorkersData] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + + // Pagination + const [currentPage, setCurrentPage] = useState(1) + const itemsPerPage = 9 + + // Filters + const [serviceType, setServiceType] = useState('all') + const [priceRange, setPriceRange] = useState<[number, number]>([0, 10000]) useEffect(() => { - const loadData = async () => { + const fetchWorkers = async () => { + setLoading(true) try { - const response = await import('../../workers.json') - setWorkersData(response.default) - } catch (error) { - console.error('Failed to load workers:', error) + const res = await fetch('/api/workers') + if (!res.ok) throw new Error('Failed to fetch workers') + const data = await res.json() + setWorkersData(data.data) // your API returns { success: true, data: [...] } + } catch (err: any) { + console.error(err) + setError(err.message || 'Something went wrong') + } finally { + setLoading(false) } } - loadData() - loadData() + + fetchWorkers() + + // Existing fallback logic (commented) + /* + import('../../workers.json').then(response => setWorkersData(response.default)) + .catch(err => console.error('Failed to load workers:', err)) + */ }, []) + // Filter and sort (memoized) + const filteredWorkers = useMemo(() => { + return workersData + .filter(worker => worker && worker.id != null && worker.pricePerDay > 0) + .filter(worker => + (serviceType === 'all' || worker.service === serviceType) && + worker.pricePerDay >= priceRange[0] && + worker.pricePerDay <= priceRange[1] + ) + .sort((a, b) => a.name.localeCompare(b.name)) + }, [workersData, serviceType, priceRange]) + + // Pagination slice + const currentWorkers = useMemo(() => { + const start = (currentPage - 1) * itemsPerPage + return filteredWorkers.slice(start, start + itemsPerPage) + }, [filteredWorkers, currentPage]) + + // Reset page when filters change + useEffect(() => { + setCurrentPage(1) + }, [serviceType, priceRange]) + + const totalPages = Math.ceil(filteredWorkers.length / itemsPerPage) + const skeletons = Array.from({ length: itemsPerPage }) + return ( -
-

Our Workers

- -
- {workersData - .filter((worker) => worker.pricePerDay > 0) - .filter((worker) => worker.id !== null) - .sort((a, b) => a.name.localeCompare(b.name)) - .map((worker: WorkerType) => ( -
-
- {worker.name} +
+ + +

+ Our Trusted Workers +

+ + {/* Filters */} +
+ + +
+ Max Price: + setPriceRange([0, +e.target.value])} + className="accent-blue-600" + /> + ₹{priceRange[1]} +
+
+ + {/* Workers Grid */} +
+ {loading + ? skeletons.map((_, index) => ( +
+
+
+
+
+
+
-
-

{worker.name}

-

{worker.service}

-

- ₹{Math.round(worker.pricePerDay * 1.18)} / day -

+ )) + : currentWorkers.map(worker => ( +
+
+ {worker.name} +
+ +
+

+ {worker.name} +

+

{worker.service}

+

+ ₹{Math.round(worker.pricePerDay * 1.18)} / day +

+
-
- ))} + ))}
+ + {/* Pagination */} + {!loading && totalPages > 1 && ( +
+ + + {Array.from({ length: totalPages }, (_, i) => i + 1) + .filter(pageNum => { + if (totalPages <= 5) return true + if (currentPage <= 3) return pageNum <= 5 + if (currentPage >= totalPages - 2) return pageNum > totalPages - 5 + return Math.abs(pageNum - currentPage) <= 2 + }) + .map(pageNum => ( + + ))} + + +
+ )} + + {/* Error message */} + {error &&

{error}

} + +
) } diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 0000000..74eb509 --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,67 @@ +import Link from "next/link"; +import { useState } from "react"; + +export default function Navbar() { + const [open, setOpen] = useState(false); + + return ( +
+ +
+ ); +} From d465becf70b7b2a25772364641e81ba320032d1a Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Wed, 24 Sep 2025 23:16:24 +0530 Subject: [PATCH 2/9] update readme file --- README.md | 85 ------------------------------------------------------- 1 file changed, 85 deletions(-) diff --git a/README.md b/README.md index bb6a8c8..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,85 +0,0 @@ -# Frontend Developer Intern Assignment - -## Mandatory Tasks -- Follow SolveEase on [Github](https://github.com/solve-ease) and [Linkedin](https://www.linkedin.com/company/solve-ease) -- Star this repo - -## Objective -This assignment is designed to assess your practical skills in **React, Next.js, TypeScript, Tailwind CSS, and frontend optimizations**. You will work on an existing **Next.js application** that contains layout/design issues and some configuration bugs. Your task is to identify and resolve these issues, and implement the listed features to enhance the overall user experience. - ---- - -## Tasks - -### 1. Fix Cards Layout & Responsiveness -- Correct the existing card grid layout. -- Improve the overall card design (UI/UX sensibility expected). -- Ensure the page is fully responsive across devices (desktop, tablet, mobile). - -### 2. Add Navbar (Sticky) -- Implement a navigation bar that remains fixed at the top while scrolling. -- Design should be clean and responsive. - -### 3. Optimize Page Load & Performance -- Implement optimizations such as: - - **Lazy loading** for images and non-critical components. - - **Memoization** to avoid unnecessary re-renders. - - **Skeleton loading screens** for better UX during data fetch. - -### 4. Implement Pagination -- Add pagination for the workers listing page. -- Each page should load a suitable number of items (e.g., 9–12 cards per page). - -### 5. Service Filters -- Implement filters for workers based on **price/day** and **type of service**. -- Filters should work seamlessly with pagination. - -### 6. Bug Fixes -- Identify and fix any existing issues in `page.tsx` or configuration files. -- Resolve console warnings or errors. -- Ensure clean and maintainable code following best practices. - -### 7. API Integration -- Currently, the workers’ data is being imported directly from `workers.json`. -- Your task is to **serve this data via /api/wprkers API route**. -- Update the frontend page to fetch this data using `fetch` (or any modern method such as `useEffect`, `useSWR`, or React Query). -- Donot delete the existing data loading logic, comment it out. -- Implement: - - **Loading state** (use skeleton screens). - - **Error handling** (show a friendly error message if API fails). - - **Basic caching or memoization** to prevent redundant calls. - ---- - -## Expectations -- Use **TypeScript** and **Tailwind CSS** consistently. -- Follow **component-driven development** principles. -- Write **clean, readable, and reusable code**. -- Optimize for **performance and accessibility**. -- Maintain **Git commit history** (no single "final commit"). - ---- - -## Deliverables -1. Fork the assignment repo, make changes there. -2. Fill in the Goggle Form with your details for submission. - ---- - -## Evaluation Criteria -- Code quality, readability, and structure. -- UI/UX improvements and responsiveness. -- Correctness of functionality (filters, pagination, sticky navbar, optimizations). -- Debugging and problem-solving approach. -- Git usage and commit practices. -- Handling of API calls, loading states, and error cases. - ---- - -## Notes -- You are free to use libraries like **SWR** or **React Query**, but keep the implementation clean. -- Focus on **real-world production quality code**, not just quick fixes. -- Add comment for any **bug fix or optimization.** -- Document any **extra improvements** you make in your submission. - -Good luck 🚀 From 2ff5c22b94426bef62e0aecaa0d60d23d24c0e3b Mon Sep 17 00:00:00 2001 From: Piyush Singh <132852291+Piyush6766@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:16:09 +0530 Subject: [PATCH 3/9] Update README.md add documentation --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index e69de29..3e2efe2 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,50 @@ +# Frontend Developer Intern Assignment + +## Mandatory Tasks +- Follow SolveEase on [Github](https://github.com/solve-ease) and [Linkedin](https://www.linkedin.com/company/solve-ease) +- Star this repo + +## Objective +This assignment is designed to assess your practical skills in **React, Next.js, TypeScript, Tailwind CSS, and frontend optimizations**. You will work on an existing **Next.js application** that contains layout/design issues and some configuration bugs. Your task is to identify and resolve these issues, and implement the listed features to enhance the overall user experience. + +--- + +## Tasks + +### 1. Fix Cards Layout & Responsiveness +- Correct the existing card grid layout. +- Improve the overall card design (UI/UX sensibility expected). +- Ensure the page is fully responsive across devices (desktop, tablet, mobile). + +### 2. Add Navbar (Sticky) +- Implement a navigation bar that remains fixed at the top while scrolling. +- Design should be clean and responsive. + +### 3. Optimize Page Load & Performance +- Implement optimizations such as: + - **Lazy loading** for images and non-critical components. + - **Memoization** to avoid unnecessary re-renders. + - **Skeleton loading screens** for better UX during data fetch. + +### 4. Implement Pagination +- Add pagination for the workers listing page. +- Each page should load a suitable number of items (e.g., 9–12 cards per page). + +### 5. Service Filters +- Implement filters for workers based on **price/day** and **type of service**. +- Filters should work seamlessly with pagination. + +### 6. Bug Fixes +- Identify and fix any existing issues in `page.tsx` or configuration files. +- Resolve console warnings or errors. +- Ensure clean and maintainable code following best practices. + +### 7. API Integration +- Currently, the workers’ data is being imported directly from `workers.json`. +- Your task is to **serve this data via /api/wprkers API route**. +- Update the frontend page to fetch this data using `fetch` (or any modern method such as `useEffect`, `useSWR`, or React Query). +- Donot delete the existing data loading logic, comment it out. +- Implement: + - **Loading state** (use skeleton screens). + - **Error handling** (show a friendly error message if API fails). + - **Basic caching or memoization** to prevent redundant calls. From 976d78b9d52c0145f9a40bde484935238a6c6c67 Mon Sep 17 00:00:00 2001 From: Piyush Singh <132852291+Piyush6766@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:31:47 +0530 Subject: [PATCH 4/9] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 3e2efe2..a7fe54a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # Frontend Developer Intern Assignment -## Mandatory Tasks -- Follow SolveEase on [Github](https://github.com/solve-ease) and [Linkedin](https://www.linkedin.com/company/solve-ease) -- Star this repo - ## Objective This assignment is designed to assess your practical skills in **React, Next.js, TypeScript, Tailwind CSS, and frontend optimizations**. You will work on an existing **Next.js application** that contains layout/design issues and some configuration bugs. Your task is to identify and resolve these issues, and implement the listed features to enhance the overall user experience. @@ -48,3 +44,4 @@ This assignment is designed to assess your practical skills in **React, Next.js, - **Loading state** (use skeleton screens). - **Error handling** (show a friendly error message if API fails). - **Basic caching or memoization** to prevent redundant calls. + From 528a106690db69c3101b19f27cd575ec72f26787 Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Thu, 25 Sep 2025 15:17:43 +0530 Subject: [PATCH 5/9] worker flow error --- src/app/api/services/route.ts | 4 ++-- src/app/page.tsx | 45 +++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/app/api/services/route.ts b/src/app/api/services/route.ts index 1a660a9..6179c4b 100644 --- a/src/app/api/services/route.ts +++ b/src/app/api/services/route.ts @@ -7,10 +7,10 @@ export async function GET(request: NextRequest) { // Simulate API delay await new Promise(resolve => setTimeout(resolve, 50 + Math.random() * 100)) - // Extract unique services from workers data + const services = Array.from(new Set(workersData.map(worker => worker.service))) - // Get service statistics + const serviceStats = services.map(service => { const workersInService = workersData.filter(worker => worker.service === service) const avgPrice = Math.round( diff --git a/src/app/page.tsx b/src/app/page.tsx index 5f84a94..f195d80 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -17,30 +17,35 @@ export default function WorkersPage() { const [serviceType, setServiceType] = useState('all') const [priceRange, setPriceRange] = useState<[number, number]>([0, 10000]) - useEffect(() => { - const fetchWorkers = async () => { - setLoading(true) - try { - const res = await fetch('/api/workers') - if (!res.ok) throw new Error('Failed to fetch workers') - const data = await res.json() - setWorkersData(data.data) // your API returns { success: true, data: [...] } - } catch (err: any) { - console.error(err) - setError(err.message || 'Something went wrong') - } finally { - setLoading(false) + useEffect(() => { + const fetchWorkers = async () => { + setLoading(true) + try { + const res = await fetch('/api/workers') + if (!res.ok) throw new Error('Failed to fetch workers') + const data = await res.json() + setWorkersData(data.data) + } catch (err: unknown) { + console.error(err) + if (err instanceof Error) { + setError(err.message) + } else { + setError('Something went wrong') } + } finally { + setLoading(false) } + } + + fetchWorkers() - fetchWorkers() + // Existing fallback logic (commented) + /* + import('../../workers.json').then(response => setWorkersData(response.default)) + .catch(err => console.error('Failed to load workers:', err)) + */ +}, []) - // Existing fallback logic (commented) - /* - import('../../workers.json').then(response => setWorkersData(response.default)) - .catch(err => console.error('Failed to load workers:', err)) - */ - }, []) // Filter and sort (memoized) const filteredWorkers = useMemo(() => { From 1552d345248e2616ed22afeba2a370507ce3c95f Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Thu, 25 Sep 2025 15:28:10 +0530 Subject: [PATCH 6/9] image issue --- next.config.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/next.config.ts b/next.config.ts index 156ac95..6099aec 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,10 +1,10 @@ -import type { NextConfig } from "next"; +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + images: { + domains: ['randomuser.me'], // external domain allow karo + }, +} -const nextConfig: NextConfig = { - /* config options here */ - images:{ - domains: ['images.unsplash.com','randomuser.me'], - } -}; +module.exports = nextConfig -export default nextConfig; From ed6b90d944c85273fd093dcf554ba228360f262e Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Thu, 25 Sep 2025 15:39:17 +0530 Subject: [PATCH 7/9] now image source is valid --- next.config.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/next.config.ts b/next.config.ts index 6099aec..f739e77 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,10 +1,21 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { reactStrictMode: true, images: { - domains: ['randomuser.me'], // external domain allow karo + remotePatterns: [ + { + protocol: 'https', + hostname: 'randomuser.me', + pathname: '/**', // allow all paths + }, + { + protocol: 'https', + hostname: 'images.unsplash.com', + pathname: '/**', + } + ] }, -} - -module.exports = nextConfig +}; +export default nextConfig; From 7a6bffdd01ac7718d42022bd5809f95e612d1190 Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Thu, 25 Sep 2025 15:47:38 +0530 Subject: [PATCH 8/9] worker api --- src/app/api/workers/route.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/api/workers/route.ts b/src/app/api/workers/route.ts index 44a245e..04954a9 100644 --- a/src/app/api/workers/route.ts +++ b/src/app/api/workers/route.ts @@ -1,18 +1,18 @@ -import { NextResponse } from 'next/server' -import workersData from '../../../../workers.json' +import { NextResponse } from 'next/server'; +import workersData from '../../../../workers.json'; export async function GET() { try { return NextResponse.json({ success: true, - data: workersData - }) - } catch (error) { - console.error('API Error:', error) + data: workersData, + timestamp: new Date().toISOString() + }); + } catch (err) { + console.error('API Error:', err); return NextResponse.json({ success: false, - error: 'Failed to fetch workers data' - }, { status: 500 }) + error: 'Internal Server Error' + }, { status: 500 }); } } - From 24cc13dcd9d86a582a70344eae0a4e2fb62a998d Mon Sep 17 00:00:00 2001 From: Piyush Singh Date: Thu, 25 Sep 2025 15:50:43 +0530 Subject: [PATCH 9/9] some important changes --- next.config.ts | 2 +- src/app/api/workers/route.ts | 2 +- src/app/page.tsx | 69 +++++++++++------------------------- 3 files changed, 23 insertions(+), 50 deletions(-) diff --git a/next.config.ts b/next.config.ts index f739e77..2dd1a2e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -7,7 +7,7 @@ const nextConfig: NextConfig = { { protocol: 'https', hostname: 'randomuser.me', - pathname: '/**', // allow all paths + pathname: '/**', }, { protocol: 'https', diff --git a/src/app/api/workers/route.ts b/src/app/api/workers/route.ts index 04954a9..7872f50 100644 --- a/src/app/api/workers/route.ts +++ b/src/app/api/workers/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from 'next/server'; -import workersData from '../../../../workers.json'; +import workersData from '../../../../workers.json'; export async function GET() { try { diff --git a/src/app/page.tsx b/src/app/page.tsx index f195d80..d3ab417 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -17,35 +17,25 @@ export default function WorkersPage() { const [serviceType, setServiceType] = useState('all') const [priceRange, setPriceRange] = useState<[number, number]>([0, 10000]) - useEffect(() => { - const fetchWorkers = async () => { - setLoading(true) - try { - const res = await fetch('/api/workers') - if (!res.ok) throw new Error('Failed to fetch workers') - const data = await res.json() - setWorkersData(data.data) - } catch (err: unknown) { - console.error(err) - if (err instanceof Error) { - setError(err.message) - } else { - setError('Something went wrong') + useEffect(() => { + const fetchWorkers = async () => { + setLoading(true) + try { + const res = await fetch('/api/workers') + if (!res.ok) throw new Error('Failed to fetch workers') + const data = await res.json() + setWorkersData(data.data) + } catch (err: unknown) { + console.error(err) + if (err instanceof Error) setError(err.message) + else setError('Something went wrong') + } finally { + setLoading(false) } - } finally { - setLoading(false) } - } - - fetchWorkers() - - // Existing fallback logic (commented) - /* - import('../../workers.json').then(response => setWorkersData(response.default)) - .catch(err => console.error('Failed to load workers:', err)) - */ -}, []) + fetchWorkers() + }, []) // Filter and sort (memoized) const filteredWorkers = useMemo(() => { @@ -65,7 +55,6 @@ export default function WorkersPage() { return filteredWorkers.slice(start, start + itemsPerPage) }, [filteredWorkers, currentPage]) - // Reset page when filters change useEffect(() => { setCurrentPage(1) }, [serviceType, priceRange]) @@ -113,10 +102,7 @@ export default function WorkersPage() {
{loading ? skeletons.map((_, index) => ( -
+
@@ -126,10 +112,7 @@ export default function WorkersPage() {
)) : currentWorkers.map(worker => ( -
+
-
-

- {worker.name} -

+

{worker.name}

{worker.service}

-

- ₹{Math.round(worker.pricePerDay * 1.18)} / day -

+

₹{Math.round(worker.pricePerDay * 1.18)} / day

))} @@ -175,11 +153,7 @@ export default function WorkersPage() { @@ -197,7 +171,6 @@ export default function WorkersPage() { {/* Error message */} {error &&

{error}

} -
)