costco-grocery-list/frontend/src/components/manage/ManageHousehold.jsx

244 lines
8.1 KiB
JavaScript

import React, { useContext, useEffect, useState } from "react";
import {
deleteHousehold,
getHouseholdMembers,
refreshInviteCode,
removeMember,
updateHousehold,
updateMemberRole
} from "../../api/households";
import { AuthContext } from "../../context/AuthContext";
import { HouseholdContext } from "../../context/HouseholdContext";
import "../../styles/components/manage/ManageHousehold.css";
export default function ManageHousehold() {
const { userId } = useContext(AuthContext);
const { activeHousehold, refreshHouseholds } = useContext(HouseholdContext);
const [members, setMembers] = useState([]);
const [loading, setLoading] = useState(true);
const [editingName, setEditingName] = useState(false);
const [newName, setNewName] = useState("");
const [showInviteCode, setShowInviteCode] = useState(false);
const isAdmin = activeHousehold?.role === "admin";
useEffect(() => {
loadMembers();
}, [activeHousehold?.id]);
const loadMembers = async () => {
if (!activeHousehold?.id) return;
setLoading(true);
try {
const response = await getHouseholdMembers(activeHousehold.id);
setMembers(response.data);
} catch (error) {
console.error("Failed to load members:", error);
} finally {
setLoading(false);
}
};
const handleUpdateName = async () => {
if (!newName.trim() || newName === activeHousehold.name) {
setEditingName(false);
return;
}
try {
console.log('Updating household:', activeHousehold.id, 'with name:', newName);
const response = await updateHousehold(activeHousehold.id, newName);
console.log('Update response:', response);
await refreshHouseholds();
setEditingName(false);
} catch (error) {
console.error("Failed to update household name:", error);
console.error("Error response:", error.response?.data);
alert(`Failed to update household name: ${error.response?.data?.error || error.message}`);
}
};
const handleRefreshInvite = async () => {
if (!confirm("Generate a new invite code? The old code will no longer work.")) return;
try {
const response = await refreshInviteCode(activeHousehold.id);
await refreshHouseholds();
const refreshedInviteCode = response.data?.household?.invite_code;
if (refreshedInviteCode) {
alert(`New invite code: ${refreshedInviteCode}`);
} else {
alert("Invite code refreshed successfully");
}
} catch (error) {
console.error(
"Failed to refresh invite code:",
error?.response?.data?.error?.message ||
error?.response?.data?.message ||
error?.message
);
alert("Failed to refresh invite code");
}
};
const handleUpdateRole = async (userId, currentRole) => {
const newRole = currentRole === "admin" ? "member" : "admin";
try {
await updateMemberRole(activeHousehold.id, userId, newRole);
await loadMembers();
} catch (error) {
console.error("Failed to update role:", error);
alert("Failed to update member role");
}
};
const handleRemoveMember = async (userId, username) => {
if (!confirm(`Remove ${username} from this household?`)) return;
try {
await removeMember(activeHousehold.id, userId);
await loadMembers();
} catch (error) {
console.error("Failed to remove member:", error);
alert("Failed to remove member");
}
};
const handleDeleteHousehold = async () => {
if (!confirm(`Delete "${activeHousehold.name}"? This will delete all lists and data. This cannot be undone.`)) return;
if (!confirm("Are you absolutely sure? Type DELETE to confirm.")) return;
try {
await deleteHousehold(activeHousehold.id);
await refreshHouseholds();
} catch (error) {
console.error("Failed to delete household:", error);
alert("Failed to delete household");
}
};
const copyInviteCode = () => {
navigator.clipboard.writeText(activeHousehold.invite_code);
alert("Invite code copied to clipboard!");
};
return (
<div className="manage-household">
{/* Household Name Section */}
<section key="household-name" className="manage-section">
<h2>Household Name</h2>
{editingName ? (
<div className="edit-name-form">
<input
type="text"
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder="Household name"
autoFocus
/>
<button onClick={handleUpdateName} className="btn-primary">Save</button>
<button onClick={() => setEditingName(false)} className="btn-secondary">Cancel</button>
</div>
) : (
<div className="name-display">
<h3>{activeHousehold.name}</h3>
{isAdmin && (
<button
onClick={() => {
setNewName(activeHousehold.name);
setEditingName(true);
}}
className="btn-secondary"
>
Edit Name
</button>
)}
</div>
)}
</section>
{/* Invite Code Section */}
{isAdmin && (
<section key="invite-code" className="manage-section">
<h2>Invite Code</h2>
<p className="section-description">
Share this code with others to invite them to your household.
</p>
<div className="invite-actions">
<button onClick={() => setShowInviteCode(!showInviteCode)} className="btn-secondary">
{showInviteCode ? "Hide Code" : "Show Code"}
</button>
{showInviteCode && (
<React.Fragment key="invite-code-display">
<code className="invite-code">{activeHousehold.invite_code}</code>
<button onClick={copyInviteCode} className="btn-secondary">Copy</button>
</React.Fragment>
)}
<button onClick={handleRefreshInvite} className="btn-secondary">
Generate New Code
</button>
</div>
</section>
)}
{/* Members Section */}
<section key="members" className="manage-section">
<h2>Members ({members.length})</h2>
{loading ? (
<p>Loading members...</p>
) : (
<div className="members-list">
{members.map((member) => (
<div key={member.id} className="member-card">
<div className="member-info">
<span className="member-role">
{member.role}
</span>
<span className="member-name">
{`
${member.username}
[${member.id}]
${(member.id === parseInt(userId) ? " (You)" : "")}
`}
</span>
</div>
{isAdmin && member.id !== parseInt(userId) && (
<div className="member-actions">
<button
onClick={() => handleUpdateRole(member.id, member.role)}
className="btn-secondary btn-small"
>
{member.role === "admin" ? "Make Member" : "Make Admin"}
</button>
<button
onClick={() => handleRemoveMember(member.id, member.username)}
className="btn-danger btn-small"
>
Remove
</button>
</div>
)}
</div>
))}
</div>
)}
</section>
{/* Danger Zone */}
{isAdmin && (
<section key="danger-zone" className="manage-section danger-zone">
<h2>Danger Zone</h2>
<p className="section-description">
Deleting a household is permanent and will delete all lists, items, and history.
</p>
<button onClick={handleDeleteHousehold} className="btn-danger">
Delete Household
</button>
</section>
)}
</div>
);
}