mirror of
https://github.com/EdiFarcas/Giveaway-app.git
synced 2026-06-22 07:00:57 +03:00
Winner selection
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { giveawaysFind } from "./GiveawayManagementCardServer";
|
import { giveawaysFind } from "./GiveawayManagementCardServer";
|
||||||
|
import Modal from "./ModalGiveawayWinner";
|
||||||
|
|
||||||
interface Giveaway {
|
interface Giveaway {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -16,6 +17,13 @@ interface Giveaway {
|
|||||||
const GiveawayManagementCards: React.FC = () => {
|
const GiveawayManagementCards: React.FC = () => {
|
||||||
const [activeGiveaways, setActiveGiveaways] = useState<Giveaway[]>([]);
|
const [activeGiveaways, setActiveGiveaways] = useState<Giveaway[]>([]);
|
||||||
const [finishedGiveaways, setFinishedGiveaways] = useState<Giveaway[]>([]);
|
const [finishedGiveaways, setFinishedGiveaways] = useState<Giveaway[]>([]);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [selectedGiveaway, setSelectedGiveaway] = useState<{
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
prize: string;
|
||||||
|
value: number;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
const fetchGiveaways = async () => {
|
const fetchGiveaways = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -40,19 +48,30 @@ const GiveawayManagementCards: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectWinner = async (giveawayId: string) => {
|
|
||||||
alert(`Selecting winner for giveaway ID: ${giveawayId}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchGiveaways();
|
fetchGiveaways();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleSelectWinner = (giveawayId: string, giveawayTitle: string, giveawayPrize: string, giveawayValue: number) => {
|
||||||
|
setSelectedGiveaway({
|
||||||
|
id: giveawayId,
|
||||||
|
title: giveawayTitle,
|
||||||
|
prize: giveawayPrize,
|
||||||
|
value: giveawayValue,
|
||||||
|
}); // Set the selected giveaway
|
||||||
|
setIsModalOpen(true); // Open the modal
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModalOpen(false); // Close the modal
|
||||||
|
setSelectedGiveaway(null); // Clear the selected giveaway
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-10">
|
<div className="space-y-10">
|
||||||
|
|
||||||
{/* Finished Giveaways Section */}
|
{/* Finished Giveaways Section */}
|
||||||
<div className="bg-white border border-gray-200 p-6 rounded-2xl shadow-lg">
|
<div className="bg-white border border-gray-200 p-6 rounded-2xl shadow-lg">
|
||||||
<h2 className="text-2xl font-semibold text-gray-700 mb-4">🏁 Finished Giveaways</h2>
|
<h2 className="text-2xl font-semibold text-gray-700 mb-4">🏁 Finished Giveaways</h2>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{finishedGiveaways.map((giveaway) => (
|
{finishedGiveaways.map((giveaway) => (
|
||||||
@@ -61,47 +80,61 @@ const GiveawayManagementCards: React.FC = () => {
|
|||||||
className="p-4 border border-gray-200 rounded-lg bg-gray-50 hover:bg-gray-100 transition relative"
|
className="p-4 border border-gray-200 rounded-lg bg-gray-50 hover:bg-gray-100 transition relative"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-bold text-gray-800">{giveaway.title}</h3>
|
<h3 className="text-lg font-bold text-gray-800">{giveaway.title}</h3>
|
||||||
<p className="text-gray-600">{giveaway.description}</p>
|
<p className="text-gray-600">{giveaway.description}</p>
|
||||||
<p className="text-gray-600 mt-2">Prize: {giveaway.prize}</p>
|
<p className="text-gray-600 mt-2">Prize: {giveaway.prize}</p>
|
||||||
<p className="text-gray-600 mt-2">Value: {giveaway.value} coins</p>
|
<p className="text-gray-600 mt-2">Value: {giveaway.value} coins</p>
|
||||||
<p className="text-gray-600 mt-2">
|
<p className="text-gray-600 mt-2">
|
||||||
Ended At: {giveaway.endsAt ? new Date(giveaway.endsAt).toLocaleString() : "N/A"}
|
Ended At: {giveaway.endsAt ? new Date(giveaway.endsAt).toLocaleString() : "N/A"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSelectWinner(giveaway.id)}
|
onClick={() => handleSelectWinner(giveaway.id, giveaway.title, giveaway.prize, giveaway.value)}
|
||||||
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition absolute bottom-4 right-4"
|
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition absolute bottom-4 right-4"
|
||||||
style={{ position: "absolute", bottom: "10px", right: "10px" }}
|
style={{ position: "absolute", bottom: "10px", right: "10px" }}
|
||||||
>
|
>
|
||||||
Select Winner
|
Select Winner
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Active Giveaways Section */}
|
{/* Active Giveaways Section */}
|
||||||
<div className="bg-white border border-blue-200 p-6 rounded-2xl shadow-lg">
|
<div className="bg-white border border-blue-200 p-6 rounded-2xl shadow-lg">
|
||||||
<h2 className="text-2xl font-semibold text-blue-700 mb-4">🎉 Active Giveaways</h2>
|
<h2 className="text-2xl font-semibold text-blue-700 mb-4">🎉 Active Giveaways</h2>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{activeGiveaways.map((giveaway) => (
|
{activeGiveaways.map((giveaway) => (
|
||||||
<div
|
<div
|
||||||
key={giveaway.id}
|
key={giveaway.id}
|
||||||
className="p-4 border border-blue-200 rounded-lg bg-blue-50 hover:bg-blue-100 transition"
|
className="p-4 border border-blue-200 rounded-lg bg-blue-50 hover:bg-blue-100 transition"
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-bold text-blue-800">{giveaway.title}</h3>
|
<h3 className="text-lg font-bold text-blue-800">{giveaway.title}</h3>
|
||||||
<p className="text-gray-600">{giveaway.description}</p>
|
<p className="text-gray-600">{giveaway.description}</p>
|
||||||
<p className="text-gray-600 mt-2">Prize: {giveaway.prize}</p>
|
<p className="text-gray-600 mt-2">Prize: {giveaway.prize}</p>
|
||||||
<p className="text-gray-600 mt-2">Value: {giveaway.value} coins</p>
|
<p className="text-gray-600 mt-2">Value: {giveaway.value} coins</p>
|
||||||
<p className="text-gray-600 mt-2">
|
<p className="text-gray-600 mt-2">
|
||||||
Ends At: {giveaway.endsAt ? new Date(giveaway.endsAt).toLocaleString() : "N/A"}
|
Ends At: {giveaway.endsAt ? new Date(giveaway.endsAt).toLocaleString() : "N/A"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Modal */}
|
||||||
|
{isModalOpen && (
|
||||||
|
<Modal
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onClose={handleCloseModal}
|
||||||
|
title="Select Giveaway Winner"
|
||||||
|
giveawayId={selectedGiveaway?.id || null}
|
||||||
|
giveawayTitle={selectedGiveaway?.title || ""}
|
||||||
|
giveawayPrize={selectedGiveaway?.prize || ""}
|
||||||
|
giveawayValue={selectedGiveaway?.value || 0}
|
||||||
|
>
|
||||||
|
<p>Here you can select the winner for the giveaway.</p>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { selectWinner } from "./SelectWinerServer"
|
||||||
|
|
||||||
|
|
||||||
|
interface ModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
giveawayId: string | null;
|
||||||
|
giveawayTitle: string | null;
|
||||||
|
giveawayPrize: string | null;
|
||||||
|
giveawayValue: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modal: React.FC<ModalProps> = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
giveawayId,
|
||||||
|
giveawayTitle,
|
||||||
|
giveawayPrize,
|
||||||
|
giveawayValue,
|
||||||
|
}) => {
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
const handleSelectWinner = () => {
|
||||||
|
if (giveawayId) {
|
||||||
|
selectWinner(giveawayId)
|
||||||
|
.then((winner) => {
|
||||||
|
alert(`Winner selected: ${winner.youtubeHandle}`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error selecting winner:", error);
|
||||||
|
alert("Failed to select a winner or no entries in the Giveaway. Please try again.");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert("No giveaway ID provided.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-opacity-60 flex justify-center items-center z-50 backdrop-blur-md">
|
||||||
|
<div className="bg-white p-8 rounded-xl shadow-2xl w-96 relative">
|
||||||
|
<h2 className="text-2xl font-semibold mb-6 text-center text-gray-800">{title}</h2>
|
||||||
|
{giveawayTitle && (
|
||||||
|
<p className="text-lg font-medium text-gray-700 mb-2">
|
||||||
|
<strong>Giveaway Title:</strong> {giveawayTitle}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{giveawayPrize && (
|
||||||
|
<p className="text-lg font-medium text-gray-700 mb-2">
|
||||||
|
<strong>Prize:</strong> {giveawayPrize}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{giveawayValue !== null && (
|
||||||
|
<p className="text-lg font-medium text-gray-700 mb-4">
|
||||||
|
<strong>Value:</strong> {giveawayValue} coins
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="text-gray-700">{children}</div>
|
||||||
|
<button
|
||||||
|
className="mt-4 w-full bg-green-600 text-white py-2 px-4 rounded-lg hover:bg-green-700 transition duration-200"
|
||||||
|
onClick={handleSelectWinner}
|
||||||
|
>
|
||||||
|
Select Winner
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="mt-6 w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition duration-200"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { db } from "@/lib/db";
|
||||||
|
|
||||||
|
async function selectWinner(giveawayId: string) {
|
||||||
|
try {
|
||||||
|
const giveaway = await db.giveaway.findUnique({
|
||||||
|
where: { id: giveawayId },
|
||||||
|
include: { entries: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!giveaway) {
|
||||||
|
throw new Error("Giveaway not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (giveaway.entries.length === 0) {
|
||||||
|
throw new Error("No entries in this giveaway");
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomIndex = Math.floor(Math.random() * giveaway.entries.length);
|
||||||
|
const winnerEntry = giveaway.entries[randomIndex];
|
||||||
|
|
||||||
|
const user = await db.user.findUnique({
|
||||||
|
where: { id: winnerEntry.userId },
|
||||||
|
select: { youtubeHandle: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user || !user.youtubeHandle) {
|
||||||
|
throw new Error("Winner's YouTube handle not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const winner = {
|
||||||
|
entry: winnerEntry,
|
||||||
|
youtubeHandle: user.youtubeHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here you can add logic to notify the winner, e.g., send an email or a message
|
||||||
|
|
||||||
|
return winner;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error selecting winner:", error);
|
||||||
|
throw new Error("Failed to select winner");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { selectWinner };
|
||||||
Reference in New Issue
Block a user