Skip to content

Commit

Permalink
Merge pull request #40 from chingu-voyages/layout-predrag
Browse files Browse the repository at this point in the history
Chart contribution changes and fixes. Group/member image selection feature. Preparation for presentation. Bug fixes
  • Loading branch information
Mandla-tech authored Oct 7, 2024
2 parents afb878d + 35739f8 commit 76493bf
Show file tree
Hide file tree
Showing 47 changed files with 246 additions and 135 deletions.
Binary file removed expense-splitter/src/assets/animals4.jpg
Binary file not shown.
Binary file removed expense-splitter/src/assets/animals5.jpg
Binary file not shown.
Binary file removed expense-splitter/src/assets/flower.png
Binary file not shown.
Binary file removed expense-splitter/src/assets/forest.png
Binary file not shown.
Binary file removed expense-splitter/src/assets/gecko.png
Binary file not shown.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Binary file removed expense-splitter/src/assets/mountain.png
Binary file not shown.
Binary file removed expense-splitter/src/assets/panda.JPG
Binary file not shown.
Binary file removed expense-splitter/src/assets/people1.jpg
Binary file not shown.
Binary file removed expense-splitter/src/assets/people3.jpg
Binary file not shown.
Binary file removed expense-splitter/src/assets/people4.jpg
Binary file not shown.
Binary file added expense-splitter/src/assets/person1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added expense-splitter/src/assets/person10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added expense-splitter/src/assets/person11.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added expense-splitter/src/assets/person12.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added expense-splitter/src/assets/person13.jpg
Binary file added expense-splitter/src/assets/person14.jpg
Binary file added expense-splitter/src/assets/person15.jpg
Binary file added expense-splitter/src/assets/person2.jpg
Binary file added expense-splitter/src/assets/person3.jpg
Binary file added expense-splitter/src/assets/person4.jpg
Binary file added expense-splitter/src/assets/person5.jpg
Binary file added expense-splitter/src/assets/person6.jpg
Binary file added expense-splitter/src/assets/person7.jpg
Binary file added expense-splitter/src/assets/person8.jpg
Binary file added expense-splitter/src/assets/person9.jpg
Binary file removed expense-splitter/src/assets/river.jpg
Diff not rendered.
Binary file removed expense-splitter/src/assets/tiger.png
Diff not rendered.
Binary file added expense-splitter/src/assets/unknownPerson.jpg
102 changes: 42 additions & 60 deletions expense-splitter/src/components/Groups/GroupChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,64 +68,39 @@ function GroupChart({ groupId }) {

const hasMembers = group.members && group.members.length > 0;

const data = hasMembers
? group.members.map((member) => ({
name: member.name,
value: member.contribution || 1,
}))
: [];

const dispatch = useDispatch();
const { isOpen, openModal, closeModal, handleClickOutside } = useModal();

const [customContributions, setCustomContributions] = useState({});
const [remainingPercentage, setRemainingPercentage] = useState(100);

useEffect(() => {
if (hasMembers) {
const equalContribution = (100 / group.members.length).toFixed(1);
// Initialize contributions as empty fields
const initialContributions = group.members.reduce((acc, member) => {
acc[member.id] = equalContribution;
acc[member.id] = "";
return acc;
}, {});
setCustomContributions(initialContributions);
setRemainingPercentage(100);
}
}, [group.members, hasMembers]);

const openModalWithCurrentContributions = () => {
if (hasMembers) {
const currentContributions = group.members.reduce((acc, member) => {
acc[member.id] = member.contribution
? member.contribution.toFixed(1)
: (100 / group.members.length).toFixed(1);
return acc;
}, {});
setCustomContributions(currentContributions);
}
openModal();
};

const handleContributionChange = (memberId, newContribution) => {
let newContributionValue = parseFloat(newContribution);

if (newContributionValue > 100 || newContributionValue < 0) return;

const remainingContribution = 100 - newContributionValue;
const otherMembers = group.members.filter(
(member) => member.id !== memberId
);
const otherMembersCount = otherMembers.length;

const equalContribution = remainingContribution / otherMembersCount;
const newValue = parseFloat(newContribution);

setCustomContributions((prevContributions) => {
const updatedContributions = { ...prevContributions };
updatedContributions[memberId] = newContributionValue;

otherMembers.forEach((member) => {
updatedContributions[member.id] = parseFloat(
equalContribution.toFixed(2)
);
});
const updatedContributions = {
...prevContributions,
[memberId]: newValue || "",
};

// Calculate remaining percentage
const totalContributions = Object.values(updatedContributions).reduce(
(acc, val) => acc + (parseFloat(val) || 0),
0
);
setRemainingPercentage(100 - totalContributions);

return updatedContributions;
});
Expand All @@ -134,11 +109,12 @@ function GroupChart({ groupId }) {
const handleSubmit = (e) => {
e.preventDefault();

// Validate total contributions before submission
// Ensure all fields are filled and total equals 100%
const totalContributions = Object.values(customContributions).reduce(
(acc, val) => acc + val,
(acc, val) => acc + (parseFloat(val) || 0),
0
);

if (totalContributions !== 100) {
alert("Total contributions must equal 100%");
return;
Expand All @@ -159,37 +135,42 @@ function GroupChart({ groupId }) {
<p className="text-groupComponentHeader mr-auto font-bold text-secondary ml-3 dark:text-primary ">
Budget Split
</p>
<button
className="hover:bg-primary px-3 transition rounded-md border border-primary text-primary font-bold hover:text-white"
onClick={openModalWithCurrentContributions}
>
Edit Contributions
</button>
{hasMembers && (
<button
className="hover:bg-primary px-3 transition rounded-md border border-primary text-primary font-bold hover:text-white"
onClick={openModal}
>
Edit Contributions
</button>
)}
</div>

{/* contribution change */}
{isOpen && (
<Modal
content={
<form onSubmit={handleSubmit} className="space-y-3">
{group.members.map((member) => (
<div key={member.id}>
<label>{member.name}s Contribution</label>
<label>{member.name} Contribution</label>
<input
type="number"
value={Math.floor(customContributions[member.id])}
value={customContributions[member.id]}
onChange={(e) =>
handleContributionChange(member.id, e.target.value)
}
className="border mr-3 p-2 w-[90%] dark:bg-dark-input"
className="border mr-3 p-2 w-[10rem] dark:bg-dark-input"
required
/>{" "}
/>
%
</div>
))}
<p className="text-md">
Percentage left to divide: {remainingPercentage}%
</p>
<button
type="submit"
className="rounded-xl px-4 py-2 bg-primary text-white"
disabled={remainingPercentage !== 0}
>
Update Contributions
</button>
Expand All @@ -204,7 +185,10 @@ function GroupChart({ groupId }) {
<>
<PieChart className="my-6" width={250} height={250}>
<Pie
data={data}
data={group.members.map((member) => ({
name: member.name,
value: member.contribution || 1,
}))}
cx={120}
cy={120}
innerRadius={35.5}
Expand All @@ -215,21 +199,19 @@ function GroupChart({ groupId }) {
labelLine={false}
label={renderCustomizedLabel}
>
{data.map((entry, index) => (
{group.members.map((_, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
</PieChart>

{/* custom legend */}
<article className="p-3 ml-3 flex rounded-lg shadow-custom flex-wrap justify-start">
{data.map((entry, index) => (
{group.members.map((entry, index) => (
<div
key={index}
className="flex my-2 items-center mx-2 space-x-3 p-1 "
className="flex my-2 items-center mx-2 space-x-3 p-1"
>
<span
className="w-3 h-3 rounded-full"
Expand Down
43 changes: 34 additions & 9 deletions expense-splitter/src/components/Groups/GroupMembers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import Modal from "../Utils/Modal";
import useModal from "../Utils/useModal";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { imagesPeople } from "../Utils/images";
import unknownPerson from "../../assets/unknownPerson.jpg"


function GroupMembers() {
const { groupId } = useParams();
Expand All @@ -19,7 +22,8 @@ function GroupMembers() {
);

const { isOpen, openModal, closeModal, handleClickOutside } = useModal();
const [newMember, setNewMember] = useState({ name: ""});
const [newMember, setNewMember] = useState({ name: "", image: ""});
const [selectedImage, setSelectedImage] = useState("");

if (!group) {
return <div>Group not found.</div>;
Expand All @@ -34,7 +38,7 @@ function GroupMembers() {
dispatch(removeMember({ groupId: parseInt(groupId), memberId }));

// Show toast notification
toast.success(`${memberName} removed from the group`, {
toast.success(`${memberName} removed`, {
position: "top-right",
autoClose: 2000,
});
Expand All @@ -57,32 +61,38 @@ function GroupMembers() {
groupId: parseInt(groupId),
member: {
name: newMember.name,
image: selectedImage || unknownPerson
},
})
);
// Show toast notification
toast.success(`${newMember.name} added to the group`, {
toast.success(`${newMember.name} added`, {
position: "top-right",
autoClose: 2000,
});

setNewMember({ name: ""});
setNewMember({ name: "", image: ""});
setSelectedImage("")
closeModal();
};

const handleImageSelect = (image) => {
setSelectedImage(image);
};

return (
<section className="bg-white dark:bg-dark-secondary dark:border p-6 ml-8 rounded-lg shadow w-custom-width">
<p className="ml-3 mb-6 text-groupComponentHeader font-bold text-secondary dark:text-primary">
Members
</p>

{/* note to self, add bg-red-500 to line under to better checking for aligments */}
<article className="flex flex-wrap justify-start items-centre">
<article className="flex flex-wrap justify-start">

<div className="bg-white dark:bg-dark-secondary rounded-2xl flex items-center flex-col ml-6">
<div className="bg-white dark:bg-dark-secondary rounded-2xl flex items-center flex-col pl-3 pr-3">
<button
onClick={openModal}
className="w-16 h-16 rounded-full shadow-lg bg-primary dark:bg-dark-bg text-4xl text-white dark:text-dark-text hover:bg-primary"
className="w-[3.5rem] h-[3.5rem] rounded-full shadow-lg bg-primary dark:bg-dark-bg text-4xl text-white dark:text-dark-text hover:bg-primary"
>
+
</button>
Expand All @@ -92,13 +102,13 @@ function GroupMembers() {
<div
key={member.id}
// note to self, add bg-red-200 to line under to better checking for aligments
className="hover:bg-slate-100 flex flex-col items-center m-1 rounded-md"
className="hover:bg-slate-100 flex flex-col items-center rounded-md"
>
<Link to={`/friends/${member.name}`} className="hover:bg-slate-100 transition-colors rounded-xl">
<GroupsEachMember
member={{
name: member.name,
img: "https://img.freepik.com/free-photo/young-bearded-man-with-striped-shirt_273609-5677.jpg",
img: member.image,
}}
/>
</Link>
Expand Down Expand Up @@ -134,6 +144,21 @@ function GroupMembers() {
/>
</div>

<h2 className="text-2xl font-bold">Select an Image</h2>
<div className="flex space-x-2 flex-wrap gap-3">
{imagesPeople.map((image, index) => (
<img
key={index}
src={image}
alt={`image-${index}`}
className={`w-[4.3rem] h-[4.3rem] object-cover !m-0 rounded-full cursor-pointer ${
selectedImage === image ? "ring-[3px] ring-primary" : ""
}`}
onClick={() => handleImageSelect(image)}
/>
))}
</div>

<button
type="submit"
className="px-4 py-2 bg-primary text-white rounded-xl dark:bg-dark-primary"
Expand Down
34 changes: 17 additions & 17 deletions expense-splitter/src/components/Groups/GroupName.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import useModal from "../Utils/useModal";
import Modal from "../Utils/Modal";
import { images } from "../Utils/images";
import { updateGroupImage } from "../../features/groupsSlice";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";



function GroupName({ group }) {
const dispatch = useDispatch();
Expand All @@ -22,25 +26,20 @@ function GroupName({ group }) {
const handleEditToggle = () => {
setIsEditing(!isEditing);
dispatch(updateGroupName({ groupId: group.id, newName: groupName }));

};

useEffect(() => {
console.log(images); // This should log the array of image paths
}, []);

useEffect(() => {
console.log("Is Modal Open: ", isOpen); // Check if modal opens
}, [isOpen]);


const handleImageSelect = (image) => {
setSelectedImage(image);
console.log("Selected Image: ", image);
};

const handleSubmit = () => {
dispatch(updateGroupImage({ groupId: group.id, newImage: selectedImage })); // Update image in Redux
dispatch(updateGroupImage({ groupId: group.id, newImage: selectedImage }));
closeModal();
toast.success(`Group image changed`, {
position: "top-right",
autoClose: 2000,
});
};

const handleInputChange = (e) => {
Expand All @@ -67,21 +66,22 @@ function GroupName({ group }) {
{isEditing ? (
<input
type="text"
value={group.name}
value={groupName}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
className="text-header border rounded-lg px-3 font-bold text-secondary border-b-2 border-gray-300 focus:outline-none"
style={{ width: `${Math.max(groupName.length * 20, 200)}px` }}
/>
) : (
<>
<div>
<h1 className="text-header font-bold text-secondary dark:text-dark-text">
{groupName}
</h1>
<p className="text-body dark:text-dark-text">
{group.description}
</p>
</>
</p>
</div>

)}
<button
onClick={handleEditToggle}
Expand All @@ -95,13 +95,13 @@ function GroupName({ group }) {
content={
<div className="space-y-4">
<h2 className="text-2xl font-bold">Select an Image</h2>
<div className="flex space-x-2">
<div className="flex space-x-2 flex-wrap gap-3">
{images.map((image, index) => (
<img
key={index}
src={image}
alt={`image-${index}`}
className={`w-[4.3rem] h-[4.3rem] object-cover rounded-full cursor-pointer ${
className={`w-[4.3rem] h-[4.3rem] object-cover !m-0 rounded-full cursor-pointer ${
selectedImage === image ? "ring-[3px] ring-primary" : ""
}`}
onClick={() => handleImageSelect(image)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function HomeIndividualGroup({ group }) {
};

return (
<section className="p-4 border bg-white dark:bg-dark-secondary rounded-xl ml-8 w-custom-card h-custom-card-height flex flex-col items-center">
<section className="p-4 border bg-white dark:bg-dark-secondary rounded-xl w-custom-card h-custom-card-height flex flex-col items-center">
<img
className="w-20 h-20 mb-2 rounded-full object-cover flex justify-center"
src={group.image}
Expand Down
Loading

0 comments on commit 76493bf

Please sign in to comment.