244 lines
8.1 KiB
JavaScript
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>
|
|
);
|
|
}
|