Implement house points system: Add houseJoinPoints, houseLeavePoints, and houseCreatePoints to SitePreferences model and update related services. Enhance house management features to award and deduct points for house creation, membership removal, and leaving a house. Update environment configuration for PostgreSQL and adjust UI components to reflect new functionalities.
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled

This commit is contained in:
Julien Froidefond
2025-12-18 08:48:31 +01:00
parent 12bc44e3ac
commit 1b82bd9ee6
23 changed files with 1026 additions and 113 deletions

View File

@@ -60,6 +60,8 @@ export default function HouseCard({ house, onRequestSent }: HouseCardProps) {
const result = await requestToJoin(house.id);
if (result.success) {
// Rafraîchir le badge d'invitations/demandes dans le header
window.dispatchEvent(new Event("refreshInvitations"));
setSuccess("Demande envoyée avec succès");
onRequestSent?.();
} else {

View File

@@ -38,6 +38,10 @@ export default function HouseForm({
: await createHouse({ name, description: description || null });
if (result.success) {
// Rafraîchir le score dans le header si on crée une maison (pas si on met à jour)
if (!house) {
window.dispatchEvent(new Event("refreshUserScore"));
}
onSuccess?.();
} else {
setError(result.error || "Une erreur est survenue");

View File

@@ -7,7 +7,7 @@ import Button from "@/components/ui/Button";
import HouseForm from "./HouseForm";
import RequestList from "./RequestList";
import Alert from "@/components/ui/Alert";
import { deleteHouse, leaveHouse } from "@/actions/houses/update";
import { deleteHouse, leaveHouse, removeMember } from "@/actions/houses/update";
import { inviteUser } from "@/actions/houses/invitations";
interface House {
@@ -112,6 +112,8 @@ export default function HouseManagement({
startTransition(async () => {
const result = await deleteHouse(house.id);
if (result.success) {
// Rafraîchir le score dans le header (le créateur perd des points)
window.dispatchEvent(new Event("refreshUserScore"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors de la suppression");
@@ -128,6 +130,7 @@ export default function HouseManagement({
startTransition(async () => {
const result = await leaveHouse(house.id);
if (result.success) {
window.dispatchEvent(new Event("refreshUserScore"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors de la sortie");
@@ -144,6 +147,8 @@ export default function HouseManagement({
startTransition(async () => {
const result = await inviteUser(house.id, selectedUserId);
if (result.success) {
// Rafraîchir le badge d'invitations/demandes dans le header (pour l'invité)
window.dispatchEvent(new Event("refreshInvitations"));
setSuccess("Invitation envoyée");
setShowInviteForm(false);
setSelectedUserId("");
@@ -303,17 +308,45 @@ export default function HouseManagement({
</span>
</div>
</div>
<span
className="text-xs uppercase flex-shrink-0 px-2 py-1 rounded font-bold"
style={{
color: roleColor,
backgroundColor: `color-mix(in srgb, ${roleColor} 15%, transparent)`,
border: `1px solid color-mix(in srgb, ${roleColor} 30%, transparent)`
}}
>
{membership.role === "OWNER" && "👑 "}
{membership.role}
</span>
<div className="flex items-center gap-2 flex-shrink-0">
<span
className="text-xs uppercase px-2 py-1 rounded font-bold"
style={{
color: roleColor,
backgroundColor: `color-mix(in srgb, ${roleColor} 15%, transparent)`,
border: `1px solid color-mix(in srgb, ${roleColor} 30%, transparent)`
}}
>
{membership.role === "OWNER" && "👑 "}
{membership.role}
</span>
{isAdmin &&
!isCurrentUser &&
(isOwner || membership.role === "MEMBER") &&
membership.role !== "OWNER" && (
<Button
onClick={() => {
if (confirm(`Êtes-vous sûr de vouloir retirer ${membership.user.username} de la maison ?`)) {
startTransition(async () => {
const result = await removeMember(house.id, membership.user.id);
if (result.success) {
// Rafraîchir le score dans le header (le membre retiré perd des points)
window.dispatchEvent(new Event("refreshUserScore"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors du retrait du membre");
}
});
}
}}
disabled={isPending}
variant="danger"
size="sm"
>
Retirer
</Button>
)}
</div>
</div>
);
})}

View File

@@ -41,6 +41,10 @@ export default function InvitationList({
startTransition(async () => {
const result = await acceptInvitation(invitationId);
if (result.success) {
// Rafraîchir le score dans le header (l'utilisateur reçoit des points)
window.dispatchEvent(new Event("refreshUserScore"));
// Rafraîchir le badge d'invitations dans le header
window.dispatchEvent(new Event("refreshInvitations"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors de l'acceptation");
@@ -53,6 +57,8 @@ export default function InvitationList({
startTransition(async () => {
const result = await rejectInvitation(invitationId);
if (result.success) {
// Rafraîchir le badge d'invitations dans le header
window.dispatchEvent(new Event("refreshInvitations"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors du refus");

View File

@@ -37,6 +37,10 @@ export default function RequestList({
startTransition(async () => {
const result = await acceptRequest(requestId);
if (result.success) {
// Rafraîchir le score dans le header (le requester reçoit des points)
window.dispatchEvent(new Event("refreshUserScore"));
// Rafraîchir le badge d'invitations/demandes dans le header (le requester n'a plus de demande en attente)
window.dispatchEvent(new Event("refreshInvitations"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors de l'acceptation");
@@ -49,6 +53,8 @@ export default function RequestList({
startTransition(async () => {
const result = await rejectRequest(requestId);
if (result.success) {
// Rafraîchir le badge d'invitations/demandes dans le header (le requester n'a plus de demande en attente)
window.dispatchEvent(new Event("refreshInvitations"));
onUpdate?.();
} else {
setError(result.error || "Erreur lors du refus");