Skip to content
Merged
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
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,10 @@
<img src="./readme/demo/Diagrams/ERD.png"/>
</center>

### Component Diagram
### System Architect Diagram

<center>
<img src="./readme/demo/Diagrams/Component Diagram.png"/>
</center>

### Flow Diagram

<center>
<img src="./readme/demo/Diagrams/Flow Diagram.png"/>
<img src="./readme/demo/Diagrams/System Architect Diagram.png"/>
</center>

### Project Box Design
Expand All @@ -55,6 +49,12 @@ I used for this project ESP32 DevKit V1. It is based on the ESP32 microcontrolle

- <b>Personalized AI Optimization Plans:</b> Clients receive intelligent, data-driven strategies to optimize energy usage, reduce costs, and promote sustainability based on their consumption behaviors.

### Remarkable Features

<center>
<img src="./readme/demo/Highlights/Highlight Section.png"/>
</center>

<br><br>

<!-- Demo -->
Expand Down Expand Up @@ -88,6 +88,10 @@ I used for this project ESP32 DevKit V1. It is based on the ESP32 microcontrolle
<!-- Development & Testing -->
<img src="./readme/title6.svg"/>

#### Postman API Documentation

- You can check the full API documentation using this [link](https://documenter.getpostman.com/view/42830816/2sB2qXji4H).

### Code Test Cases

| Test Case | Test Case |
Expand Down Expand Up @@ -138,9 +142,13 @@ I used for this project ESP32 DevKit V1. It is based on the ESP32 microcontrolle
<!-- Deployment -->
<img src="./readme/title8.svg"/>

### EC2 Docker containers deployment
### Deployment Diagram

- You can check the full API documentation using this [link](https://documenter.getpostman.com/view/42830816/2sB2qXji4H).
<center>
<img src="./readme/demo/Diagrams/Flow Diagram.png"/>
</center>

### EC2 Docker containers deployment

| Deployment Pipeline Sample | GitHub Deployment Pipeline Success | EC2 Instance docker deployed |
| --------------------------------------- | ------------------------------------- | ------------------------------------- |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,56 +36,4 @@ const InputField = ({
);
};

// V2
// const InputField = ({
// label,
// placeholder,
// value,
// onChange,
// width,
// textarea,
// type,
// required = true,
// }) => {
// return (
// <div className="input-field-container" style={{ width: width || "100%" }}>
// <label className="subtitle black-color">
// {label} {required && <span>*</span>}
// </label>
// {textarea ? (
// <textarea placeholder={placeholder} value={value} onChange={onChange} />
// ) : (
// <input
// type={type || "text"}
// placeholder={placeholder}
// value={value}
// onChange={onChange}
// />
// )}
// </div>
// );
// };

// const InputField = ({ label, placeholder, value, onChange, width, textarea, type }) => {
// return (
// <div className="input-field-container" style={{ width: width || '100%' }}>
// <label className="subtitle black-color">{label}*</label>
// {textarea ? (
// <textarea
// placeholder={placeholder}
// value={value}
// onChange={onChange}
// />
// ) : (
// <input
// type={type?type:"text"}
// placeholder={placeholder}
// value={value}
// onChange={onChange}
// />
// )}
// </div>
// );
// }

export default InputField;
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import "./styles.css";
import { useState } from "react";
import { Link } from "react-router-dom";
import { useLogout } from "../../../Hooks/useLogoutHook";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faSignOutAlt,
faUser,
faChevronDown,
} from "@fortawesome/free-solid-svg-icons";

const ProfileMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const logout = useLogout();

const handleToggle = () => {
setIsOpen((prev) => !prev);
};

const handleLogout = async () => {
try {
await logout();
} catch (error) {
console.error("Logout failed:", error);
}
};

return (
<div className="profile-menu-container">
<div className="profile-summary" onClick={handleToggle}>
<span className="username">Profile Menu</span>
<FontAwesomeIcon
icon={faChevronDown}
className={`arrow-icon ${isOpen ? "open" : ""}`}
/>
</div>

{isOpen && (
<div className="dropdown-menu">
<Link to="/provider/profile" className="dropdown-item">
<FontAwesomeIcon icon={faUser} className="item-icon" />
Profile
</Link>
<button onClick={handleLogout} className="dropdown-item logout">
<FontAwesomeIcon icon={faSignOutAlt} className="item-icon" />
Logout
</button>
</div>
)}
</div>
);
};

export default ProfileMenu;
81 changes: 81 additions & 0 deletions amp-client/src/Components/CommonComponents/ProfileMenu/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
.profile-menu-container {
position: relative;
display: flex;
}

.profile-summary {
border: 2px solid #fff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 10px 14px;
font-size: 18px;
background-color: transparent;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
}

.profile-summary:hover {
background-color: #fff;
border: none;
color: #f9a43a;
}

.username {
font-size: 18px;
font-weight: 500;
}

.arrow-icon {
transition: transform 0.3s ease;
}

.arrow-icon.open {
transform: rotate(-180deg);
}

.dropdown-menu {
position: absolute;
bottom: 100%;
right: 0;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
margin-bottom: 2px;
min-width: 100%;
/* min-width: 160px; */
z-index: 100;
overflow: hidden;
}

.dropdown-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
font-size: 14px;
color: #374151;
text-decoration: none;
background-color: #fff;
border: none;
width: 100%;
text-align: left;
cursor: pointer;
}

.dropdown-item:hover {
background-color: #f3f4f6;
}

.logout {
color: #b91c1c;
}

.item-icon {
width: 16px;
height: 16px;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "./styles.css";
import { Link } from "react-router-dom";
import logo from "../../../assets/logo.png";
import LogoutButton from "../../CommonComponents/LogoutButton/LogoutButton";
import ProfileMenu from "../../CommonComponents/ProfileMenu/ProfileMenu";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faTachometerAlt,
Expand Down Expand Up @@ -34,13 +34,9 @@ const ProviderSidebar = () => {
<FontAwesomeIcon icon={faBolt} className="sidebar-icon" />
<span className="link-text">Power Prediction</span>
</Link>
<Link to="/provider/profile" className="sidebar-link">
<FontAwesomeIcon icon={faUser} className="sidebar-icon" />
<span className="link-text">Profile</span>
</Link>
</nav>
<div className="sidebar-footer">
<LogoutButton />
<ProfileMenu />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,102 @@
import "./styles.css";
import { useState } from "react";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import ProviderProfileService from "../Services/ProviderProfile/ProviderProfile";
import InputField from "../../../Components/CommonComponents/InputField/InputField";
import ActionButton from "../../../Components/CommonComponents/ActionButton/ActionButton";

const ProviderProfile = () => {
const [formData, setFormData] = useState({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Profile form fields initialized as empty

Fetch existing profile data on mount to pre-fill the form and avoid forcing users to re-enter unchanged values.

name: "",
email: "",
password: "",
phone_number: "",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use consistent camelCase naming for JavaScript fields

phone_number is snake_case; change it to phoneNumber and adjust the API mapping as needed.

});

const { updateProfile } = ProviderProfileService();

const handleChange = (field, value) => {
setFormData((prev) => ({
...prev,
[field]: value,
}));
};

const handleSubmit = async (e) => {
e.preventDefault();
await updateProfile(formData);
};

return (
<>
<div className="provider-profile-container">
<div className="main-content">
<h1 className="main-content-title section-titles">Provider Profile</h1>
<div className="provider-profile-wrapper">
<div className="provider-profile-main">
<h2 className="provider-profile-heading">Provider Profile</h2>

<div className="provider-profile-card">
<h2 className="provider-profile-subtitle">Edit Provider Profile</h2>
<form className="provider-profile-form">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Attach handleSubmit to form onSubmit instead of button onClick

Also update the button to type="submit" so pressing Enter submits the form and you don’t mix click and submit events.

Suggested implementation:

          <form className="provider-profile-form" onSubmit={handleSubmit}>
            <button
              type="submit"
              className="provider-profile-submit"
            >

<div className="provider-profile-form-group">
<InputField
label="Name"
placeholder="Enter your name"
value={formData.name}
onChange={(e) => handleChange("name", e.target.value)}
type="text"
width="90%"
required={false}
/>
</div>

<div className="provider-profile-form-group">
<InputField
label="Email"
placeholder="Enter your email"
value={formData.email}
onChange={(e) => handleChange("email", e.target.value)}
type="email"
width="90%"
required={false}
/>
</div>

<div className="provider-profile-form-group">
<InputField
label="Password"
placeholder="Enter new password"
value={formData.password}
onChange={(e) => handleChange("password", e.target.value)}
type="password"
width="90%"
required={false}
/>
</div>

<div className="provider-profile-form-group">
<InputField
label="Phone Number"
placeholder="Enter phone number"
value={formData.phone_number}
onChange={(e) => handleChange("phone_number", e.target.value)}
type="tel"
width="90%"
required={false}
/>
</div>

<ActionButton
text="Save Changes"
backgroundColor="#f9a43a"
className="submit-action-button"
color="#FFF"
width="50%"
margin="20px 0 0"
onClick={handleSubmit}
/>
</form>
</div>
</div>
</>
</div>
);
};

Expand Down
Loading
Loading