diff --git a/.env.devolopment b/.env.devolopment new file mode 100644 index 0000000..fa1310a --- /dev/null +++ b/.env.devolopment @@ -0,0 +1 @@ +API_URL=https://localhost:8080/api diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..812d31f --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +API_URL = https://client-side-routing-backend-nu.vercel.app/api \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 63f01f0..845a9e4 100644 --- a/dist/index.html +++ b/dist/index.html @@ -4,9 +4,9 @@ Client-Side Routing Assignment - +
- + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 16f6f39..c28285e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "ttp-react-forms", + "name": "ttp-client-side-routing", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "ttp-react-forms", + "name": "ttp-client-side-routing", "version": "1.0.0", "license": "ISC", "dependencies": { @@ -13,6 +13,7 @@ "@babel/preset-react": "^7.27.1", "axios": "^1.10.0", "babel-loader": "^10.0.0", + "dotenv": "^17.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router": "^7.6.3", @@ -2323,6 +2324,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.0.tgz", + "integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index ddd3e8e..01c882f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@babel/preset-react": "^7.27.1", "axios": "^1.10.0", "babel-loader": "^10.0.0", + "dotenv": "^17.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router": "^7.6.3", diff --git a/src/App.jsx b/src/App.jsx index 14763f7..65899aa 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,14 +5,20 @@ import "./AppStyles.css"; import TaskList from "./components/TaskList"; import AddTask from "./components/AddTask"; import NavBar from "./components/NavBar"; -import { BrowserRouter as Router, Routes } from "react-router"; - +import CompletedTasks from "./components/CompleteTasks"; +import TaskDetail from "./components/SingleTask"; +import { BrowserRouter as Router, Routes, Route } from "react-router"; +import SingleTask from "./components/SingleTask"; +import api from "./api/axiosInstance"; +import IncompleteTasks from "./components/IncompleteTasks"; +// comment const App = () => { const [tasks, setTasks] = useState([]); async function fetchAllTasks() { try { - const response = await axios.get("http://localhost:8080/api/tasks"); + const response = await api.get(`/tasks`); + console.log("✅ API response:", response.data); setTasks(response.data); } catch (error) { console.error("Error fetching tasks:", error); @@ -26,12 +32,15 @@ const App = () => { return (
- - + }/> {/* Currently, we don't have any routes defined. And you can see above that we're rendering the TaskList and AddTask components directly, no matter what our URL looks like. Let's fix that! */} + }> + } /> + }> + }>
); diff --git a/src/api/axiosInstance.js b/src/api/axiosInstance.js new file mode 100644 index 0000000..fa6adc9 --- /dev/null +++ b/src/api/axiosInstance.js @@ -0,0 +1,7 @@ +import axios from 'axios'; + +const api = axios.create({ + baseURL: "https://client-side-routing-backend-nu.vercel.app/api", +}); + +export default api; \ No newline at end of file diff --git a/src/components/AddTask.jsx b/src/components/AddTask.jsx index 3673cf1..29907b0 100644 --- a/src/components/AddTask.jsx +++ b/src/components/AddTask.jsx @@ -1,15 +1,17 @@ import React, { useState } from "react"; import axios from "axios"; import "./AddTaskStyles.css"; +import { useNavigate } from "react-router"; +import api from "../api/axiosInstance"; const AddTask = ({ fetchAllTasks }) => { const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); - + const navigate = useNavigate(); const handleSubmit = async (event) => { event.preventDefault(); try { - await axios.post("http://localhost:8080/api/tasks", { + await api.post("/tasks", { title, description, }); @@ -36,7 +38,9 @@ const AddTask = ({ fetchAllTasks }) => { value={description} onChange={(e) => setDescription(e.target.value)} /> - + ); diff --git a/src/components/CompleteTasks.jsx b/src/components/CompleteTasks.jsx new file mode 100644 index 0000000..515ad81 --- /dev/null +++ b/src/components/CompleteTasks.jsx @@ -0,0 +1,9 @@ +import React from "react"; +import TaskList from "./TaskList"; + +const CompletedTasks = ({ tasks, fetchAllTasks }) => { + const completedTasks = tasks.filter((task) => task.completed); + return ; +}; + +export default CompletedTasks; \ No newline at end of file diff --git a/src/components/IncompleteTasks.jsx b/src/components/IncompleteTasks.jsx new file mode 100644 index 0000000..46c5772 --- /dev/null +++ b/src/components/IncompleteTasks.jsx @@ -0,0 +1,9 @@ +import React from "react"; +import TaskList from "./TaskList"; + +const IncompleteTasks = ({ tasks, fetchAllTasks }) => { + const incompleteTasks = tasks.filter((task) => !task.completed); + return ; +}; + +export default IncompleteTasks; \ No newline at end of file diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 9e63901..17a0cd9 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,17 +1,19 @@ import React from "react"; +import { NavLink } from "react-router"; + import "./NavBarStyles.css"; const NavBar = () => { return ( ); }; diff --git a/src/components/SingleTask.css b/src/components/SingleTask.css new file mode 100644 index 0000000..1f2ee56 --- /dev/null +++ b/src/components/SingleTask.css @@ -0,0 +1,3 @@ +.user-name { + color: red; +} \ No newline at end of file diff --git a/src/components/SingleTask.jsx b/src/components/SingleTask.jsx new file mode 100644 index 0000000..49139a4 --- /dev/null +++ b/src/components/SingleTask.jsx @@ -0,0 +1,59 @@ +import axios from "axios"; +import React, {use, useEffect, useState} from "react" +import TaskCard from "./TaskCard"; +import { useParams } from "react-router"; +import api from "../api/axiosInstance"; +import './SingleTask.css' + +const SingleTask = (props) => { + const {tasks} = props; + console.log("This is tasks state-->", tasks) + const params = useParams(); + const id = Number(params.id); + + const [currentTask, setCurrentTask] = useState([]); + const [currentUserId, setCurrentuserId] = useState([]); + const [user, setUser] = useState([]); + let userId = Number(currentUserId) + // console.log("Current user-->", currentUser) + + const fetchTaskById = async () => { + try { + const response = await api.get(`/tasks/${id}`); + setCurrentTask(response.data) + const user = response.data.userId; + setCurrentuserId(user) + } catch(error) { + console.log("failed to fetch task by id", error) + } + } + + useEffect(() => { + fetchTaskById(); + }, [id]); + + const fetchUserByID = async () => { + try { + const response = await api.get(`/users/${userId}`); + setUser(response.data) + } catch(error) { + console.log("failed to fetch user by id", error) + } + } + + useEffect(() => { + fetchUserByID(); + },[currentUserId]); + + + return ( +
{user.name ?

This task is assigned to {user.name}

+ : +

This task is not assigned

} + + {}}/> +
+ ) +}; + +export default SingleTask; \ No newline at end of file diff --git a/src/components/TaskCard.jsx b/src/components/TaskCard.jsx index 82258d9..30e8c2c 100644 --- a/src/components/TaskCard.jsx +++ b/src/components/TaskCard.jsx @@ -1,11 +1,13 @@ import React from "react"; import axios from "axios"; +import {Link} from "react-router"; import "./TaskCardStyles.css"; +import api from "../api/axiosInstance"; -const TaskCard = ({ task, fetchAllTasks }) => { +const TaskCard = ({ task, fetchAllTasks, currentTask}) => { const handleCompleteTask = async () => { try { - await axios.patch(`http://localhost:8080/api/tasks/${task.id}`, { + await api.patch(`/tasks/${task.id}`, { completed: !task.completed, }); fetchAllTasks(); @@ -16,7 +18,7 @@ const TaskCard = ({ task, fetchAllTasks }) => { const handleDeleteTask = async () => { try { - await axios.delete(`http://localhost:8080/api/tasks/${task.id}`); + await api.delete(`/tasks/${task.id}`); fetchAllTasks(); } catch (error) { console.error("Error deleting task:", error); @@ -26,7 +28,7 @@ const TaskCard = ({ task, fetchAllTasks }) => { return (
-

{task.title}

+

{task.title}

{task.completed ? (

🔄

diff --git a/webpack.config.js b/webpack.config.js index a187b02..a594d2b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,8 @@ const path = require("path"); +const webpack = require("webpack"); +const dotenv = require("dotenv"); +const env = dotenv.config({ path: `.env.${process.env.NODE_ENV}` }).parsed || {}; module.exports = { mode: "development", entry: "./src/App.jsx", @@ -37,4 +40,9 @@ module.exports = { historyApiFallback: true, port: 3000, }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.API_URL': JSON.stringify(env.API_URL), + }) + ] };