S62 Added
parent
d69f0aeaba
commit
d52fa7f45e
@ -1 +0,0 @@
|
||||
web: node index.js
|
@ -1,26 +1,102 @@
|
||||
import AppNavBar from "./components/AppNavBar";
|
||||
import { Container } from "react-bootstrap";
|
||||
import "./App.css";
|
||||
import { useState, useEffect } from 'react';
|
||||
import AppNavBar from './components/AppNavBar';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import { UserProvider } from './UserContext';
|
||||
|
||||
// Pages
|
||||
// import Home from "./pages/Home";
|
||||
// import Courses from "./pages/Courses";
|
||||
import Register from "./pages/Register";
|
||||
import Login from "./pages/Login";
|
||||
import Home from './pages/Home';
|
||||
import Courses from './pages/Courses';
|
||||
import Register from './pages/Register';
|
||||
import Login from './pages/Login';
|
||||
import Logout from './pages/Logout';
|
||||
import Profile from './pages/Profile';
|
||||
import CourseView from './pages/CourseView';
|
||||
import AddCourse from './pages/AddCourse';
|
||||
import NotFound from './pages/Error';
|
||||
|
||||
function App() {
|
||||
|
||||
// let user = { token: localStorage.getItem('token') }
|
||||
const [user, setUser] = useState({
|
||||
id: null,
|
||||
isAdmin: null
|
||||
})
|
||||
|
||||
const unsetUser = () => {
|
||||
localStorage.clear()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
// console.log(localStorage);
|
||||
|
||||
fetch(`${process.env.REACT_APP_API_URL}/users/details`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${ localStorage.getItem('token') }`
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
// console.log(data);
|
||||
|
||||
if(typeof data._id !== "undefined"){
|
||||
|
||||
setUser({
|
||||
id: data._id,
|
||||
isAdmin: data.isAdmin
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
setUser({
|
||||
id: null,
|
||||
isAdmin: null
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}, [])
|
||||
|
||||
console.log(user);
|
||||
|
||||
return (
|
||||
// JSX - JavaScript XML Code
|
||||
// JSX - JavaScript XML code
|
||||
|
||||
// Fragments
|
||||
<>
|
||||
// <></> - Fragments
|
||||
<UserProvider value={{ user, setUser, unsetUser }}>
|
||||
<Router>
|
||||
<AppNavBar />
|
||||
<Container>
|
||||
{/* <Register/> */}
|
||||
<Login/>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/courses" element={<Courses />} />
|
||||
{/*MINI ACTIVITY - continue completing the routes*/}
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/logout" element={<Logout />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/courses/:courseId" element={<CourseView />} />
|
||||
<Route path='/add-course' element={<AddCourse/>} />
|
||||
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</Container>
|
||||
</>
|
||||
</Router>
|
||||
</UserProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
/**
|
||||
* Mini Activity 2
|
||||
* Import the Highlights component
|
||||
* Add the Highlights component in the JSX inside the Container and after the Banner Component
|
||||
* 2 mins
|
||||
*/
|
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
// Creates a Context object
|
||||
// A context object as the name states is a data type of an object that can be used to store information that can be shared to other components within the app
|
||||
// The context object is a different approach to passing information between components and allows easier access by avoiding the use of prop-drilling
|
||||
const UserContext = React.createContext();
|
||||
|
||||
// The "Provider" component allows other components to consume/use the context object and supply the necessary information needed to the context object
|
||||
export const UserProvider = UserContext.Provider;
|
||||
|
||||
export default UserContext;
|
@ -0,0 +1,84 @@
|
||||
// AdminView.js
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Table } from 'react-bootstrap';
|
||||
import EditCourse from './EditCourse';
|
||||
import ArchiveCourse from './ArchiveCourse';
|
||||
|
||||
const fetchDataFromDatabase = async () => {
|
||||
try {
|
||||
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/all`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new Error('Error fetching data from the database');
|
||||
}
|
||||
};
|
||||
|
||||
export default function AdminView() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [courses, setCourses] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await fetchDataFromDatabase();
|
||||
setCourses(data.map(course => (
|
||||
<tr key={course._id}>
|
||||
<td>{course._id}</td>
|
||||
<td>{course.name}</td>
|
||||
<td>{course.description}</td>
|
||||
<td>{course.price}</td>
|
||||
<td className={course.isActive ? 'text-success' : 'text-danger'}>
|
||||
{course.isActive ? 'Available' : 'Unavailable'}
|
||||
</td>
|
||||
<td><EditCourse course={course._id} fetchData={fetchData} /></td>
|
||||
<td>
|
||||
<ArchiveCourse
|
||||
course={course._id}
|
||||
fetchData={fetchData}
|
||||
isActive={course.isActive}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)));
|
||||
} catch (error) {
|
||||
setError(error.message || 'An error occurred');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-center my-4"> Admin Dashboard</h1>
|
||||
|
||||
{loading && <p>Loading...</p>}
|
||||
{error && <p style={{ color: 'red' }}>{error}</p>}
|
||||
|
||||
{!loading && !error && (
|
||||
<Table striped bordered hover responsive>
|
||||
<thead>
|
||||
<tr className="text-center">
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Price</th>
|
||||
<th>Availability</th>
|
||||
<th colSpan="2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{courses}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
export default function ArchiveCourse({ course, fetchData, isActive }) {
|
||||
|
||||
|
||||
const archiveToggle = (courseId) => {
|
||||
fetch(`${process.env.REACT_APP_API_URL}/courses/${courseId}/archive`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
title: 'Success!',
|
||||
icon: 'success',
|
||||
text: 'Course Successfully Archived'
|
||||
});
|
||||
fetchData(); // Ensure it fetches the updated data
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
icon: 'error',
|
||||
text: 'Please try again'
|
||||
});
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
const activateToggle = (courseId) => {
|
||||
fetch(`${ process.env.REACT_APP_API_URL}/courses/${courseId}/activate`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
title: 'Success!',
|
||||
icon: 'success',
|
||||
text: 'Course Successfully Activated'
|
||||
});
|
||||
fetchData();
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
icon: 'error',
|
||||
text: ' Please try again'
|
||||
});
|
||||
fetchData();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isActive ? (
|
||||
<Button variant="danger" size="sm" onClick={() => archiveToggle(course)}>
|
||||
Archive
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="success" size="sm" onClick={() => activateToggle(course)}>
|
||||
Activate
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,12 +1,18 @@
|
||||
import { Button, Row, Col } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export default function Banner({data}) {
|
||||
|
||||
console.log(data);
|
||||
|
||||
const {title, content, destination, label} = data;
|
||||
|
||||
export default function Banner() {
|
||||
return (
|
||||
<Row>
|
||||
<Col>
|
||||
<h1>Zuitt Coding Bootcamp</h1>
|
||||
<p>Opportunites for everyone, everywhere.</p>
|
||||
<Button variant='primary'>Enroll Now</Button>
|
||||
<Col className="p-5 text-center">
|
||||
<h1>{title}</h1>
|
||||
<p>{content}</p>
|
||||
<Link className="btn btn-primary" to={destination}>{label}</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
|
@ -1,54 +1,55 @@
|
||||
import { Button, Card } from "react-bootstrap";
|
||||
import PropTypes from 'prop-types';
|
||||
import { Card, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react'
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function CourseCard({ courseProp }) {
|
||||
|
||||
const { name, description, price } = courseProp;
|
||||
// Deconstruct the course properties into their own variables
|
||||
const { _id, name, description, price } = courseProp;
|
||||
|
||||
// Use the state hook to be able to store state (data) in this component
|
||||
// States are used to keep track of information related to individual component
|
||||
// Syntax:
|
||||
/**
|
||||
* const [ getter, setterFunction ] = useState(initialGetterValue)
|
||||
/*
|
||||
const [getter, setterFunction] = useState(initialGetterValue)
|
||||
*/
|
||||
// const [count, setCount] = useState(0);
|
||||
// const [seats, setSeats] = useState(10);
|
||||
|
||||
const [ count, setCount ] = useState(0);
|
||||
const enroll = () => {
|
||||
if (count < 30) {
|
||||
setCount(count + 1);
|
||||
} else {
|
||||
alert('No More Seats');
|
||||
}
|
||||
};
|
||||
// function enroll(){
|
||||
// if(seats > 0) {
|
||||
// setCount(count + 1)
|
||||
// setSeats(seats - 1);
|
||||
// } else {
|
||||
// alert("No more seats available")
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<Card.Title>{name}</Card.Title>
|
||||
<Card.Title>Description</Card.Title>
|
||||
<Card.Subtitle>Description:</Card.Subtitle>
|
||||
<Card.Text>{description}</Card.Text>
|
||||
<Card.Title>Price:</Card.Title>
|
||||
<Card.Subtitle>Price:</Card.Subtitle>
|
||||
<Card.Text>Php {price}</Card.Text>
|
||||
<Card.Text>Enrollees: {count}</Card.Text>
|
||||
<Button variant="primary" onClick={enroll}>
|
||||
Enroll
|
||||
</Button>
|
||||
<Link className="btn btn-primary" to={`/courses/${_id}`} >Details</Link>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the CourseCard component is getting the correct prop types
|
||||
// PropTypes are used for validating information passed to a component.
|
||||
CourseCard.propTypes = {
|
||||
// The 'shape' method is used to check if a prop object conforms to a specific "shape"
|
||||
// The 'shape' method is used to check if a prop object comforms to a specific "shape"
|
||||
courseProp: PropTypes.shape({
|
||||
// Define the properties and their expected types
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
price: PropTypes.number.isRequired
|
||||
}).isRequired // Make sure the entire courseProp object is required
|
||||
};
|
||||
})
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import React, { useState } from 'react';
|
||||
import CourseCard from './CourseCard';
|
||||
|
||||
const CourseSearch = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/search`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ courseName: searchQuery })
|
||||
});
|
||||
const data = await response.json();
|
||||
setSearchResults(data);
|
||||
} catch (error) {
|
||||
console.error('Error searching for courses:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Course Search</h2>
|
||||
<div className="form-group">
|
||||
<label htmlFor="courseName">Course Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="courseName"
|
||||
className="form-control"
|
||||
value={searchQuery}
|
||||
onChange={event => setSearchQuery(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-primary" onClick={handleSearch}>
|
||||
Search
|
||||
</button>
|
||||
<h3>Search Results:</h3>
|
||||
<ul>
|
||||
{searchResults.map(course => (
|
||||
<CourseCard courseProp={course} key={course._id}/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CourseSearch;
|
@ -0,0 +1,127 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Modal, Form } from 'react-bootstrap';
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
// Add the props course to get the specific id of the course
|
||||
export default function EditCourse({ course, fetchData }){
|
||||
|
||||
// State for courseId for the fetch URL
|
||||
const [courseId, setCourseId] = useState('')
|
||||
|
||||
// Forms state
|
||||
// Add state for the forms of course
|
||||
const [name, setName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [price, setPrice] = useState('');
|
||||
|
||||
// state for editCourse Modals to open/close
|
||||
const [showEdit, setShowEdit] = useState(false)
|
||||
|
||||
// Function for opening the modal
|
||||
const openEdit = (courseId) => {
|
||||
fetch(`http://localhost:4000/courses/${courseId}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// Populate all the input values with course info that we fetched
|
||||
setCourseId(data._id);
|
||||
setName(data.name);
|
||||
setDescription(data.description);
|
||||
setPrice(data.price)
|
||||
})
|
||||
|
||||
// Open the modal
|
||||
setShowEdit(true)
|
||||
}
|
||||
|
||||
const closeEdit = () =>{
|
||||
setShowEdit(false);
|
||||
setName('');
|
||||
setDescription('');
|
||||
setPrice(0);
|
||||
}
|
||||
|
||||
// Function to update the course
|
||||
const editCourse = (e, courseId) => {
|
||||
e.preventDefault();
|
||||
|
||||
fetch(`http://localhost:4000/courses/${courseId}`,{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
description: description,
|
||||
price: price
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
|
||||
if(data === true){
|
||||
Swal.fire({
|
||||
title: 'Success!',
|
||||
icon: 'success',
|
||||
text: 'Course Successfully Updated'
|
||||
})
|
||||
closeEdit();
|
||||
fetchData();
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
icon: 'error',
|
||||
text: ' Please try again'
|
||||
})
|
||||
closeEdit();
|
||||
fetchData();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return(
|
||||
<>
|
||||
<Button variant="primary" size="sm" onClick={() => openEdit(course)}> Edit </Button>
|
||||
|
||||
{/*Edit Modal*/}
|
||||
<Modal show={showEdit} onHide={closeEdit}>
|
||||
<Form onSubmit={e => editCourse(e, courseId)}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Edit Course</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form.Group>
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
required/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
required/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Price</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
value={price}
|
||||
onChange={e => setPrice(e.target.value)}
|
||||
required/>
|
||||
</Form.Group>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={closeEdit}>Close</Button>
|
||||
<Button variant="success" type="submit">Submit</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { CardGroup } from 'react-bootstrap'
|
||||
import PreviewCourses from './PreviewCourses'
|
||||
|
||||
|
||||
export default function FeaturedCourses(){
|
||||
|
||||
const [previews, setPreviews] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`${ process.env.REACT_APP_API_URL}/courses/`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
|
||||
// Create two empty arrays to be used to store random numbers and featured course data.
|
||||
const numbers = []
|
||||
const featured = []
|
||||
|
||||
|
||||
// This function generates a random number between 0 and the length of the data array (the fetched course data). It checks if the random number has already been added to the numbers array. If not, it adds the random number to the numbers array. If the random number already exists in the numbers array, it recursively calls itself to generate a new random number.
|
||||
const generateRandomNums = () => {
|
||||
let randomNum = Math.floor(Math.random() * data.length)
|
||||
|
||||
if(numbers.indexOf(randomNum) === -1){
|
||||
numbers.push(randomNum)
|
||||
}else{
|
||||
generateRandomNums()
|
||||
}
|
||||
}
|
||||
|
||||
// A loop is used to iterate five times (from 0 to 4). Inside the loop, the generateRandomNums function is called to generate a random number and push it into the numbers array.
|
||||
for(let i = 0; i < 5; i++){
|
||||
generateRandomNums()
|
||||
|
||||
|
||||
// For each iteration of the loop, the PreviewCourses component is rendered with the corresponding course data from the data array based on the random number. The key prop is set to the _id of the course for React's reconciliation to track components efficiently. The resulting JSX is pushed into the featured array.
|
||||
featured.push(
|
||||
|
||||
// the breakPoint here is for columns
|
||||
<PreviewCourses data={data[numbers[i]]} key={data[numbers[i]]._id} breakPoint={2} />
|
||||
)
|
||||
}
|
||||
|
||||
// After the loop finishes, the setPreviews function is called to update the state of the component with the featured array. This state update triggers a re-render, and the PreviewCourses components are displayed on the page.
|
||||
setPreviews(featured)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return(
|
||||
<>
|
||||
<h2 className="text-center">Featured Courses</h2>
|
||||
<CardGroup className="justify-content-center">
|
||||
{/*add the state here*/}
|
||||
{previews}
|
||||
</CardGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { Col, Card } from 'react-bootstrap'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export default function Product(props){
|
||||
|
||||
// props is used here to get the data and breakPoint from the FeaturedCourses.js
|
||||
const { breakPoint, data } = props
|
||||
|
||||
// Destructure the courses data here
|
||||
const { _id, name, description, price } = data
|
||||
|
||||
return(
|
||||
<Col xs={12} md={ breakPoint }>
|
||||
|
||||
{/*add spacing for x-axis*/}
|
||||
<Card className="cardHighlight mx-2">
|
||||
<Card.Body>
|
||||
<Card.Title className="text-center">
|
||||
|
||||
{/*Add the specific details of course link*/}
|
||||
<Link to={`/courses/${ _id}`}>{ name }</Link>
|
||||
</Card.Title>
|
||||
<Card.Text>{ description }</Card.Text>
|
||||
|
||||
</Card.Body>
|
||||
<Card.Footer>
|
||||
<h5 className="text-center">₱{ price }</h5>
|
||||
<Link className="btn btn-primary d-block" to={`/courses/${ _id}`}>Details</Link>
|
||||
</Card.Footer>
|
||||
</Card>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const ResetPassword = () => {
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
const handleResetPassword = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
setMessage('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token'); // Replace with your actual JWT token
|
||||
const response = await fetch(`${process.env.REACT_APP_API_URL}/users/reset-password`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ newPassword: password }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setMessage('Password reset successfully');
|
||||
setPassword('');
|
||||
setConfirmPassword('');
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
setMessage(errorData.message);
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage('An error occurred. Please try again.');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<h2>Reset Password</h2>
|
||||
<form onSubmit={handleResetPassword}>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="password" className="form-label">
|
||||
New Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="confirmPassword" className="form-label">
|
||||
Confirm Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="confirmPassword"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{message && <div className="alert alert-danger">{message}</div>}
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Reset Password
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetPassword;
|
@ -0,0 +1,34 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import CourseCard from './CourseCard';
|
||||
import CourseSearch from './CourseSearch';
|
||||
|
||||
|
||||
export default function UserView({coursesData}) {
|
||||
|
||||
const [courses, setCourses] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const coursesArr = coursesData.map(course => {
|
||||
|
||||
//only render the active courses since the route used is /all from Course.js page
|
||||
if(course.isActive === true) {
|
||||
return (
|
||||
<CourseCard courseProp={course} key={course._id}/>
|
||||
)
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
||||
//set the courses state to the result of our map function, to bring our returned course component outside of the scope of our useEffect where our return statement below can see.
|
||||
setCourses(coursesArr)
|
||||
|
||||
}, [coursesData])
|
||||
|
||||
return(
|
||||
<>
|
||||
<CourseSearch />
|
||||
{ courses }
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
import Form from 'react-bootstrap/Form';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import UserContext from '../UserContext';
|
||||
import Swal from 'sweetalert2';
|
||||
|
||||
const AddCourse = () => {
|
||||
const { user } = useContext(UserContext);
|
||||
const navigate = useNavigate();
|
||||
const [courseData, setCourseData] = useState({
|
||||
name: '',
|
||||
description: '',
|
||||
price: 0,
|
||||
// Add other course-related fields here
|
||||
});
|
||||
const [isCourseAdded, setCourseAdded] = useState(false);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCourseData((prevData) => ({
|
||||
...prevData,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/add-course/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`, // Assuming you have a token-based authentication
|
||||
},
|
||||
body: JSON.stringify(courseData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setCourseAdded(true);
|
||||
Swal.fire({
|
||||
icon: "success",
|
||||
title: "Courses added"
|
||||
})
|
||||
} else {
|
||||
console.error('Failed to add the course');
|
||||
// Optionally handle error response here
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Failed to add the course"
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error adding course:', error);
|
||||
// Optionally handle network or other errors here
|
||||
}
|
||||
};
|
||||
|
||||
if (!user || !user.isAdmin) {
|
||||
navigate('/');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isCourseAdded) {
|
||||
navigate('/courses');
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h2>Add New Course</h2>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group controlId="name">
|
||||
<Form.Label>Course Name</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Enter the course name"
|
||||
name="name"
|
||||
value={courseData.name}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="description">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
placeholder="Enter the course description"
|
||||
name="description"
|
||||
value={courseData.description}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="price">
|
||||
<Form.Label>Price</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
placeholder="Enter the course price"
|
||||
name="price"
|
||||
value={courseData.price}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
{/* Add other form fields for additional course information */}
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Add Course
|
||||
</Button>
|
||||
</Form>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddCourse;
|
@ -0,0 +1,96 @@
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { Container, Card, Button, Row, Col } from 'react-bootstrap';
|
||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
||||
import Swal from 'sweetalert2';
|
||||
import UserContext from '../UserContext'
|
||||
|
||||
export default function CourseView() {
|
||||
|
||||
// useParams hook allows us to retrieve the courseId passed via the URL
|
||||
const { courseId } = useParams();
|
||||
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
// Allow us to redirect to different pages in our functions
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [price, setPrice] = useState(0);
|
||||
|
||||
const enroll = (coursId) => {
|
||||
|
||||
fetch(`${ process.env.REACT_APP_API_URL }/users/enroll`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
courseId: courseId
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
|
||||
if(data.message === 'Enrolled Successfully!') {
|
||||
|
||||
Swal.fire({
|
||||
title: "Successfully Enrolled",
|
||||
icon: "success",
|
||||
text: "You have successfully enrolled for this course."
|
||||
})
|
||||
|
||||
navigate("/courses");
|
||||
} else {
|
||||
|
||||
Swal.fire({
|
||||
title: "Something went wrong",
|
||||
icon: "error",
|
||||
text: "Please try again."
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
fetch(`${ process.env.REACT_APP_API_URL }/courses/${courseId}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
setName(data.name);
|
||||
setDescription(data.description);
|
||||
setPrice(data.price)
|
||||
})
|
||||
|
||||
}, [courseId]);
|
||||
|
||||
return (
|
||||
<Container className="mt-5">
|
||||
<Row>
|
||||
<Col lg={{ span: 6, offset: 3 }}>
|
||||
<Card>
|
||||
<Card.Body className="text-center">
|
||||
<Card.Title>{name}</Card.Title>
|
||||
<Card.Subtitle>Description:</Card.Subtitle>
|
||||
<Card.Text>{description}</Card.Text>
|
||||
<Card.Subtitle>Price:</Card.Subtitle>
|
||||
<Card.Text>PhP {price}</Card.Text>
|
||||
<Card.Subtitle>Class Schedule</Card.Subtitle>
|
||||
<Card.Text>8 am - 5 pm</Card.Text>
|
||||
{(user.id !== null) ?
|
||||
<Button variant="primary" block onClick={() => enroll(courseId)}>Enroll</Button>
|
||||
:
|
||||
<Link className="btn btn-danger btn-block" to="/login">Log in to Enroll</Link>
|
||||
}
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
@ -1,21 +1,67 @@
|
||||
import { useEffect, useState, useContext } from 'react';
|
||||
import CourseCard from '../components/CourseCard';
|
||||
// import coursesData from '../data/coursesData';
|
||||
import UserContext from '../UserContext';
|
||||
|
||||
import UserView from '../components/UserView';
|
||||
import AdminView from '../components/AdminView';
|
||||
|
||||
|
||||
import CourseCard from "../components/CourseCard"
|
||||
import coursesData from "../data/courseData"
|
||||
export default function Courses() {
|
||||
|
||||
console.log(coursesData);
|
||||
console.log(coursesData[0]);
|
||||
// console.log(coursesData);
|
||||
// console.log(coursesData[0])
|
||||
|
||||
const courses = coursesData.map(course => {
|
||||
return (
|
||||
<CourseCard courseProp={course} />
|
||||
)
|
||||
})
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
|
||||
const [courses, setCourses] = useState([]);
|
||||
|
||||
const fetchData = () => {
|
||||
fetch(`${process.env.REACT_APP_API_URL}/courses/all`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
console.log(data);
|
||||
|
||||
// Sets the "courses" state to map the data retrieved from the fetch request into several "CourseCard" components
|
||||
setCourses(data);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Retrieves the courses from the database upon initial render of the "Courses" component
|
||||
useEffect(() => {
|
||||
|
||||
fetchData()
|
||||
|
||||
}, []);
|
||||
|
||||
// The "map" method loops through the individual course objects in our array and returns a component for each course
|
||||
// Multiple components created through the map method must have a unique key that will help React JS identify which components/elements have been changed, added or removed
|
||||
// Everytime the map method loops through the data, it creates a "CourseCard" component and then passes the current element in our coursesData array using the courseProp
|
||||
// const courses = coursesData.map(course => {
|
||||
// // The "courseProp" in the CourseCard component is called a "prop" which is a shorthand for "property" since components are considered as objects in React JS
|
||||
// // The curly braces ({}) are used for props to signify that we are providing information using JavaScript expressions rather than hard coded values which use double quotes ("")
|
||||
// // We can pass information from one component to another using props. This is referred to as "props drilling"
|
||||
// return (
|
||||
// <CourseCard courseProp={course} />
|
||||
// )
|
||||
// })
|
||||
|
||||
// Component Body
|
||||
return (
|
||||
<>
|
||||
{courses}
|
||||
{
|
||||
(user.isAdmin === true) ?
|
||||
<AdminView coursesData={courses} />
|
||||
|
||||
:
|
||||
|
||||
<UserView coursesData={courses} />
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import Banner from '../components/Banner';
|
||||
|
||||
export default function Error() {
|
||||
|
||||
const data = {
|
||||
title: "404 - Not found",
|
||||
content: "The page you are looking for cannot be found",
|
||||
destination: "/",
|
||||
label: "Back home"
|
||||
}
|
||||
|
||||
return (
|
||||
<Banner data={data}/>
|
||||
)
|
||||
}
|
@ -1,11 +1,20 @@
|
||||
import Banner from "../components/Banner";
|
||||
import Highlights from "../components/Highlights";
|
||||
import Banner from '../components/Banner';
|
||||
import FeaturedCourses from '../components/FeaturedCourses';
|
||||
import Highlights from '../components/Highlights';
|
||||
|
||||
export default function Home() {
|
||||
const data = {
|
||||
title: "Zuitt Coding Bootcamp",
|
||||
content: "Opportunities for everyone, everywhere",
|
||||
destination: "/courses",
|
||||
label: "Enroll now!"
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Banner />
|
||||
<Banner data={data}/>
|
||||
<FeaturedCourses/>
|
||||
<Highlights />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
@ -1,97 +1,140 @@
|
||||
import React, { useState } from "react";
|
||||
import { Form, Button } from "react-bootstrap";
|
||||
|
||||
const Login = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [isValid, setIsValid] = useState(false);
|
||||
|
||||
const checkFormValidity = () => {
|
||||
// Simple validation for demonstration purposes
|
||||
const isValid = email !== "" && password !== "";
|
||||
setIsValid(isValid);
|
||||
};
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
// Update state based on the input field name
|
||||
switch (name) {
|
||||
case "email":
|
||||
setEmail(value);
|
||||
break;
|
||||
case "password":
|
||||
setPassword(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import { Form, Button } from 'react-bootstrap';
|
||||
import Swal from 'sweetalert2';
|
||||
import UserContext from '../UserContext';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
export default function Login() {
|
||||
|
||||
// Allows us to consume the User context and it's properties to use for validation
|
||||
const { user, setUser } = useContext(UserContext);
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
function authenticate(e) {
|
||||
|
||||
try {
|
||||
const response = await fetch("http://localhost:4000/users/login", {
|
||||
// Prevent page redirection via form submission
|
||||
e.preventDefault()
|
||||
|
||||
fetch(`${process.env.REACT_APP_API_URL}/users/login`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if(data.access){
|
||||
|
||||
// Set the token of the authenticated user in the local storage
|
||||
/*
|
||||
Syntax:
|
||||
localStorage.setItem('propertyName', value);
|
||||
*/
|
||||
localStorage.setItem("token", data.access);
|
||||
|
||||
// function for retrieving user details
|
||||
retrieveUserDetails(data.access);
|
||||
|
||||
Swal.fire({
|
||||
title: "Login Successful",
|
||||
icon: "success",
|
||||
text: "Welcome to Zuitt"
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert("Thank you for logging in");
|
||||
console.log("Login success")
|
||||
// Handle successful login, e.g., redirect to another page
|
||||
} else {
|
||||
alert("Unsucessful login");
|
||||
// Handle invalid credentials, e.g., show an error message
|
||||
Swal.fire({
|
||||
title: "Authentication failed",
|
||||
icon: "error",
|
||||
text: "Check your login details and try again"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during login:", error);
|
||||
// Handle other errors, e.g., network issues
|
||||
})
|
||||
|
||||
// Clearing our input fields after submission
|
||||
setEmail('');
|
||||
setPassword('');
|
||||
|
||||
}
|
||||
|
||||
const retrieveUserDetails = (token) => {
|
||||
|
||||
fetch(`${process.env.REACT_APP_API_URL}/users/details`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
};
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
console.log(data);
|
||||
|
||||
setUser({
|
||||
id: data._id,
|
||||
isAdmin: data.isAdmin
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if(email !== '' && password !== '') {
|
||||
setIsActive(true);
|
||||
} else {
|
||||
setIsActive(false);
|
||||
}
|
||||
|
||||
}, [email, password])
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
(user.id !== null) ?
|
||||
<Navigate to='/courses' />
|
||||
:
|
||||
<Form onSubmit={(e) => authenticate(e)} >
|
||||
<h1 className="my-5 text-center">Login</h1>
|
||||
<Form.Group>
|
||||
<Form.Label>Email Address:</Form.Label>
|
||||
<Form.Group controlId="userEmail">
|
||||
<Form.Label>Email address</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
placeholder="Enter Email"
|
||||
required
|
||||
name="email"
|
||||
placeholder="Enter email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
handleInputChange(e);
|
||||
checkFormValidity();
|
||||
}}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Password:</Form.Label>
|
||||
|
||||
<Form.Group controlId="password">
|
||||
<Form.Label>Password</Form.Label>
|
||||
<Form.Control
|
||||
type="password"
|
||||
placeholder="Enter Password"
|
||||
required
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
handleInputChange(e);
|
||||
checkFormValidity();
|
||||
}}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button variant="success" type="submit" disabled={!isValid}>
|
||||
Login
|
||||
|
||||
|
||||
{ isActive ?
|
||||
<Button variant="primary" type="submit" id="submitBtn">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
:
|
||||
|
||||
export default Login;
|
||||
<Button variant="danger" type="submit" id="submitBtn" disabled>
|
||||
Submit
|
||||
</Button>
|
||||
}
|
||||
</Form>
|
||||
)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import UserContext from '../UserContext';
|
||||
|
||||
export default function Logout() {
|
||||
|
||||
const { unsetUser, setUser } = useContext(UserContext);
|
||||
|
||||
unsetUser();
|
||||
|
||||
useEffect(() => {
|
||||
setUser({
|
||||
id: null,
|
||||
isAdmin: null
|
||||
})
|
||||
})
|
||||
|
||||
// Redirect back to login
|
||||
return (
|
||||
<Navigate to='/login' />
|
||||
)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import Banner from '../components/Banner';
|
||||
|
||||
export default function NotFound(){
|
||||
|
||||
const data = {
|
||||
|
||||
title: "404 - Not found",
|
||||
content: "Sorry, the page you are looking for does not exist.",
|
||||
destination: "/",
|
||||
label: "Back home"
|
||||
|
||||
}
|
||||
|
||||
return(
|
||||
|
||||
<Banner data={data} />
|
||||
|
||||
)
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
import { useEffect, useContext, useState } from 'react';
|
||||
import { Row, Col } from 'react-bootstrap';
|
||||
import UserContext from '../UserContext';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import ResetPassword from '../components/ResetPassword';
|
||||
|
||||
export default function Profile() {
|
||||
const { user } = useContext(UserContext);
|
||||
const [profileData, setProfileData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (user && user.id) {
|
||||
// Fetch user profile data when the component mounts
|
||||
fetchUserProfile();
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const fetchUserProfile = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${process.env.REACT_APP_API_URL}/users/details`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('Profile Data:', data);
|
||||
|
||||
if (data && data.firstName && data.lastName && data.email && data.mobileNo) {
|
||||
setProfileData(data);
|
||||
} else {
|
||||
console.error('Invalid user profile data received from the server.');
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch user profile');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching user profile:', error);
|
||||
}
|
||||
};
|
||||
|
||||
if (!user || user.id === null) {
|
||||
return <Navigate to="/courses" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Col className="p-5 bg-primary text-white">
|
||||
<h1 className="my-5">Profile</h1>
|
||||
{profileData ? (
|
||||
<>
|
||||
<h2 className="mt-3">
|
||||
{profileData.firstName} {profileData.lastName}
|
||||
</h2>
|
||||
<hr />
|
||||
<h4>Contacts</h4>
|
||||
<ul>
|
||||
<li>
|
||||
Name: {profileData.firstName} {profileData.lastName}
|
||||
</li>
|
||||
<li>Email: {profileData.email}</li>
|
||||
<li>Mobile No: {profileData.mobileNo}</li>
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<p>Loading profile...</p>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="pt-4 mt-4">
|
||||
<Col>
|
||||
<ResetPassword />
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1 @@
|
||||
Subproject commit a9253e2e4f52925478c882822da78456d1526540
|
Loading…
Reference in New Issue