Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,57 @@ You will:
```
src/
├── components/ # Reusable UI components
├── pages/ # Page components
├── hooks/ # Custom React hooks
├── context/ # React context providers
├── api/ # API integration functions
├── utils/ # Utility functions
│ ├── Button.jsx # Button component with variants
│ ├── Card.jsx # Card component for content display
│ ├── Footer.jsx # Footer component with links
│ ├── Layout.jsx # Layout component with Navbar and Footer
│ ├── Navbar.jsx # Navbar component with theme toggle
│ ├── Posts.jsx # Posts component for API data
│ └── TaskManager.jsx # Task manager component
├── pages/ # Page components
│ ├── Home.jsx # Home page
│ ├── Posts.jsx # Posts page
│ └── Tasks.jsx # Tasks page
├── hooks/ # Custom React hooks
│ └── useApiData.js # Hook for API data fetching
├── context/ # React context providers
│ └── ThemeContext.js # Theme context for light/dark mode
├── api/ # API integration functions
│ └── posts.js # Functions for fetching posts
├── utils/ # Utility functions
│ └── dateFormatter.js # Date formatting utilities
└── App.jsx # Main application component
```

## Features Implemented

1. **Component Architecture**:
- Reusable Button component with multiple variants
- Card component for consistent content display
- Navbar and Footer components
- Layout component that includes Navbar and Footer
- TaskManager component for task management
- Posts component for API data display

2. **State Management and Hooks**:
- useState for managing component state
- useEffect for side effects
- useContext for theme management
- Custom useApiData hook for API integration
- Custom localStorage persistence for tasks

3. **API Integration**:
- Fetching data from JSONPlaceholder API
- Loading and error states
- Pagination support
- Search functionality

4. **Styling with Tailwind CSS**:
- Responsive design for mobile, tablet, and desktop
- Light/dark theme switcher
- Consistent styling with Tailwind utility classes
- Interactive elements with hover and focus states

## Submission

Your work will be automatically submitted when you push to your GitHub Classroom repository. Make sure to:
Expand All @@ -67,4 +110,4 @@ Your work will be automatically submitted when you push to your GitHub Classroom
- [React Documentation](https://react.dev/)
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
- [Vite Documentation](https://vitejs.dev/guide/)
- [React Router Documentation](https://reactrouter.com/)
- [React Router Documentation](https://reactrouter.com/)
64 changes: 63 additions & 1 deletion Week3-Assignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,68 @@ Build a responsive React application using JSX and Tailwind CSS that demonstrate
npm run dev
```

## ✅ Implementation Status

### ✅ Task 1: Project Setup
- React application created with Vite
- Tailwind CSS installed and configured
- Project structure set up with all required folders
- React Router configured (in App.jsx)

### ✅ Task 2: Component Architecture
- Button component with variants (primary, secondary, danger, success, warning, outline)
- Card component for content display
- Navbar component with navigation links and theme toggle
- Footer component with links and copyright information
- Layout component that includes Navbar and Footer
- All components are customizable through props

### ✅ Task 3: State Management and Hooks
- TaskManager component with full CRUD functionality
- useState for managing task state
- useEffect for persisting tasks to localStorage
- Custom localStorage persistence hook implemented
- Theme management with context API

### ✅ Task 4: API Integration
- Posts component fetching data from JSONPlaceholder API
- Loading and error states implemented
- Pagination support
- Search functionality to filter posts
- Custom useApiData hook for API integration

### ✅ Task 5: Styling with Tailwind CSS
- Fully responsive design for all screen sizes
- Theme switcher for light/dark mode
- Consistent styling with Tailwind utility classes
- Interactive elements with hover and focus states

## 📁 Project Structure
```
src/
├── components/ # Reusable UI components
│ ├── Button.jsx # Button component with variants
│ ├── Card.jsx # Card component for content display
│ ├── Footer.jsx # Footer component with links
│ ├── Layout.jsx # Layout component with Navbar and Footer
│ ├── Navbar.jsx # Navbar component with theme toggle
│ ├── Posts.jsx # Posts component for API data
│ └── TaskManager.jsx # Task manager component
├── pages/ # Page components
│ ├── Home.jsx # Home page
│ ├── Posts.jsx # Posts page
│ └── Tasks.jsx # Tasks page
├── hooks/ # Custom React hooks
│ └── useApiData.js # Hook for API data fetching
├── context/ # React context providers
│ └── ThemeContext.js # Theme context for light/dark mode
├── api/ # API integration functions
│ └── posts.js # Functions for fetching posts
├── utils/ # Utility functions
│ └── dateFormatter.js # Date formatting utilities
└── App.jsx # Main application component
```

## ✅ Submission Instructions
1. Accept the GitHub Classroom assignment invitation
2. Clone your personal repository that was created by GitHub Classroom
Expand All @@ -76,4 +138,4 @@ Build a responsive React application using JSX and Tailwind CSS that demonstrate
6. Deploy your application to Vercel, Netlify, or GitHub Pages
7. Add the deployed URL to your README.md
8. Your submission will be automatically graded based on the criteria in the autograding configuration
9. The instructor will review your submission after the autograding is complete
9. The instructor will review your submission after the autograding is complete
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PLP Task Manager | React & Tailwind CSS</title>
</head>
<body class="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "react-js-jsx-and-css-mastering-front-end-development",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"prop-types": "^15.8.1"
},
"devDependencies": {
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.55.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.0.8"
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
1 change: 1 addition & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Additional custom styles can be added here */
84 changes: 24 additions & 60 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,35 @@
import { useState } from 'react';
import './App.css';

// Import your components here
// import Button from './components/Button';
// import Navbar from './components/Navbar';
// import Footer from './components/Footer';
// import TaskManager from './components/TaskManager';
import Layout from './components/Layout';
import TaskManager from './components/TaskManager';
import Posts from './components/Posts';

function App() {
const [count, setCount] = useState(0);

return (
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
{/* Navbar component will go here */}
<header className="bg-white dark:bg-gray-800 shadow">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h1 className="text-3xl font-bold">PLP Task Manager</h1>
</div>
</header>

<main className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg p-6">
<div className="flex flex-col items-center justify-center">
<p className="text-lg mb-4">
Edit <code className="font-mono bg-gray-200 dark:bg-gray-700 p-1 rounded">src/App.jsx</code> and save to test HMR
</p>

<div className="flex items-center gap-4 my-4">
<button
onClick={() => setCount((count) => count - 1)}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors"
>
-
</button>
<span className="text-xl font-bold">{count}</span>
<button
onClick={() => setCount((count) => count + 1)}
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition-colors"
>
+
</button>
</div>
const navLinks = [
{ name: 'Home', url: '#' },
{ name: 'Tasks', url: '#tasks' },
{ name: 'Posts', url: '#posts' },
];

<p className="text-gray-500 dark:text-gray-400 mt-4">
Implement your TaskManager component here
</p>
</div>
</div>

{/* API data display will go here */}
<div className="mt-8 bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg p-6">
<h2 className="text-2xl font-bold mb-4">API Data</h2>
<p className="text-gray-500 dark:text-gray-400">
Fetch and display data from an API here
</p>
</div>
</main>
const footerLinks = [
{ name: 'GitHub', url: 'https://github.com' },
{ name: 'Documentation', url: '#' },
{ name: 'Support', url: '#' },
];

{/* Footer component will go here */}
<footer className="bg-white dark:bg-gray-800 shadow mt-auto">
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<p className="text-center text-gray-500 dark:text-gray-400">
© {new Date().getFullYear()} PLP Task Manager. All rights reserved.
</p>
</div>
</footer>
</div>
return (
<Layout
title="PLP Task Manager"
navLinks={navLinks}
footerLinks={footerLinks}
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<TaskManager />
<Posts />
</div>
</Layout>
);
}

Expand Down
90 changes: 90 additions & 0 deletions src/api/posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* API service for fetching posts from JSONPlaceholder
*/

const BASE_URL = 'https://jsonplaceholder.typicode.com';

/**
* Fetch all posts
* @param {number} limit - Number of posts to fetch (optional)
* @returns {Promise<Array>} - Promise resolving to array of posts
*/
export const fetchPosts = async (limit = 10) => {
try {
const response = await fetch(`${BASE_URL}/posts?_limit=${limit}`);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching posts:', error);
throw error;
}
};

/**
* Fetch a single post by ID
* @param {number} id - Post ID
* @returns {Promise<Object>} - Promise resolving to post object
*/
export const fetchPostById = async (id) => {
try {
const response = await fetch(`${BASE_URL}/posts/${id}`);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching post with ID ${id}:`, error);
throw error;
}
};

/**
* Fetch posts with pagination
* @param {number} page - Page number (1-indexed)
* @param {number} limit - Number of posts per page
* @returns {Promise<Array>} - Promise resolving to array of posts
*/
export const fetchPostsPaginated = async (page = 1, limit = 10) => {
try {
const response = await fetch(`${BASE_URL}/posts?_page=${page}&_limit=${limit}`);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching posts for page ${page}:`, error);
throw error;
}
};

/**
* Search posts by title or body
* @param {string} query - Search query
* @returns {Promise<Array>} - Promise resolving to array of matching posts
*/
export const searchPosts = async (query) => {
try {
const response = await fetch(`${BASE_URL}/posts`);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const posts = await response.json();
return posts.filter(post =>
post.title.toLowerCase().includes(query.toLowerCase()) ||
post.body.toLowerCase().includes(query.toLowerCase())
);
} catch (error) {
console.error(`Error searching posts for "${query}":`, error);
throw error;
}
};

export default {
fetchPosts,
fetchPostById,
fetchPostsPaginated,
searchPosts
};
Loading