costco-grocery-list/backend/models/household.model.js
2026-01-27 00:03:58 -08:00

192 lines
5.0 KiB
JavaScript

const pool = require("../db/pool");
// Get all households a user belongs to
exports.getUserHouseholds = async (userId) => {
const result = await pool.query(
`SELECT
h.id,
h.name,
h.invite_code,
h.created_at,
hm.role,
hm.joined_at,
(SELECT COUNT(*) FROM household_members WHERE household_id = h.id) as member_count
FROM households h
JOIN household_members hm ON h.id = hm.household_id
WHERE hm.user_id = $1
ORDER BY hm.joined_at DESC`,
[userId]
);
return result.rows;
};
// Get household by ID (with member check)
exports.getHouseholdById = async (householdId, userId) => {
const result = await pool.query(
`SELECT
h.id,
h.name,
h.invite_code,
h.created_at,
h.created_by,
hm.role as user_role,
(SELECT COUNT(*) FROM household_members WHERE household_id = h.id) as member_count
FROM households h
LEFT JOIN household_members hm ON h.id = hm.household_id AND hm.user_id = $2
WHERE h.id = $1`,
[householdId, userId]
);
return result.rows[0];
};
// Create new household
exports.createHousehold = async (name, createdBy) => {
// Generate random 6-digit invite code
const inviteCode = 'H' + Math.random().toString(36).substring(2, 8).toUpperCase();
const result = await pool.query(
`INSERT INTO households (name, created_by, invite_code)
VALUES ($1, $2, $3)
RETURNING id, name, invite_code, created_at`,
[name, createdBy, inviteCode]
);
// Add creator as admin
await pool.query(
`INSERT INTO household_members (household_id, user_id, role)
VALUES ($1, $2, 'admin')`,
[result.rows[0].id, createdBy]
);
return result.rows[0];
};
// Update household
exports.updateHousehold = async (householdId, updates) => {
const { name } = updates;
const result = await pool.query(
`UPDATE households
SET name = COALESCE($1, name)
WHERE id = $2
RETURNING id, name, invite_code, created_at`,
[name, householdId]
);
return result.rows[0];
};
// Delete household
exports.deleteHousehold = async (householdId) => {
await pool.query('DELETE FROM households WHERE id = $1', [householdId]);
};
// Refresh invite code
exports.refreshInviteCode = async (householdId) => {
const inviteCode = 'H' + Math.random().toString(36).substring(2, 8).toUpperCase();
const result = await pool.query(
`UPDATE households
SET invite_code = $1, code_expires_at = NULL
WHERE id = $2
RETURNING id, name, invite_code`,
[inviteCode, householdId]
);
return result.rows[0];
};
// Join household via invite code
exports.joinHousehold = async (inviteCode, userId) => {
const householdResult = await pool.query(
`SELECT id, name FROM households
WHERE invite_code = $1
AND (code_expires_at IS NULL OR code_expires_at > NOW())`,
[inviteCode]
);
if (householdResult.rows.length === 0) return null;
const household = householdResult.rows[0];
const existingMember = await pool.query(
`SELECT id FROM household_members
WHERE household_id = $1 AND user_id = $2`,
[household.id, userId]
);
if (existingMember.rows.length > 0) return { ...household, alreadyMember: true };
// Add as user role
await pool.query(
`INSERT INTO household_members (household_id, user_id, role)
VALUES ($1, $2, 'user')`,
[household.id, userId]
);
return { ...household, alreadyMember: false };
};
// Get household members
exports.getHouseholdMembers = async (householdId) => {
const result = await pool.query(
`SELECT
u.id,
u.username,
u.name,
u.display_name,
hm.role,
hm.joined_at
FROM household_members hm
JOIN users u ON hm.user_id = u.id
WHERE hm.household_id = $1
ORDER BY
CASE hm.role
WHEN 'admin' THEN 1
WHEN 'user' THEN 2
END,
hm.joined_at ASC`,
[householdId]
);
return result.rows;
};
// Update member role
exports.updateMemberRole = async (householdId, userId, newRole) => {
const result = await pool.query(
`UPDATE household_members
SET role = $1
WHERE household_id = $2 AND user_id = $3
RETURNING user_id, role`,
[newRole, householdId, userId]
);
return result.rows[0];
};
// Remove member from household
exports.removeMember = async (householdId, userId) => {
await pool.query(
`DELETE FROM household_members
WHERE household_id = $1 AND user_id = $2`,
[householdId, userId]
);
};
// Get user's role in household
exports.getUserRole = async (householdId, userId) => {
const result = await pool.query(
`SELECT role FROM household_members
WHERE household_id = $1 AND user_id = $2`,
[householdId, userId]
);
return result.rows[0]?.role || null;
};
// Check if user is household member
exports.isHouseholdMember = async (householdId, userId) => {
const result = await pool.query(
`SELECT 1 FROM household_members
WHERE household_id = $1 AND user_id = $2`,
[householdId, userId]
);
return result.rows.length > 0;
};