Shop Purchase Update

This commit is contained in:
2025-04-13 04:53:44 -04:00
parent 01d8986dd2
commit bc1fb0e3c7
3 changed files with 156 additions and 136 deletions

View File

@@ -1,9 +1,8 @@
"use client"; "use client";
import { useDevice } from "@/lib/context/DeviceContext"; import { useDevice } from "@/lib/context/DeviceContext";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
// Default Profile Pictures are /avatar/p1.png to /avatar/p5.png
const defaultPics = [ const defaultPics = [
{ src: "/avatar/p1.png", id: 1 }, { src: "/avatar/p1.png", id: 1 },
{ src: "/avatar/p2.png", id: 2 }, { src: "/avatar/p2.png", id: 2 },
@@ -12,117 +11,126 @@ const defaultPics = [
{ src: "/avatar/p5.png", id: 5 }, { src: "/avatar/p5.png", id: 5 },
]; ];
// Purchaseable Profile Pictures are /avatar/p6.png to /avatar/p16.png
const purchasablePics = [ const purchasablePics = [
{ src: "/avatar/p6.png", price: 250 }, { id: 6, src: "/avatar/p6.png", price: 250 },
{ src: "/avatar/p7.png", price: 250 }, { id: 7, src: "/avatar/p7.png", price: 250 },
{ src: "/avatar/p8.png", price: 2000 }, { id: 8, src: "/avatar/p8.png", price: 2000 },
{ src: "/avatar/p9.png", price: 2000 }, { id: 9, src: "/avatar/p9.png", price: 2000 },
{ src: "/avatar/p10.png", price: 2000 }, { id: 10, src: "/avatar/p10.png", price: 2000 },
{ src: "/avatar/p11.png", price: 2000 }, { id: 11, src: "/avatar/p11.png", price: 2000 },
{ src: "/avatar/p12.png", price: 2000 }, { id: 12, src: "/avatar/p12.png", price: 2000 },
{ src: "/avatar/p13.png", price: 10000 }, { id: 13, src: "/avatar/p13.png", price: 10000 },
{ src: "/avatar/p14.png", price: 10000 }, { id: 14, src: "/avatar/p14.png", price: 10000 },
{ src: "/avatar/p15.png", price: 10000 }, { id: 15, src: "/avatar/p15.png", price: 10000 },
{ src: "/avatar/p16.png", price: 10000 }, { id: 16, src: "/avatar/p16.png", price: 10000 },
]; ];
export default function ShopPage() { export default function ShopPage() {
const { isAuthenticated, session } = useDevice(); const { isAuthenticated, session } = useDevice();
const [points, setPoints] = useState(session?.points || 0);
const [avatar, setAvatar] = useState(session?.avatar || 1);
const [ownedPics, setOwnedPics] = useState<string[]>(session?.ownedPics || []);
const handleAvatarSelect = async (id: number) => { const [points, setPoints] = useState(0);
const formData = new FormData(); const [avatar, setAvatar] = useState(1);
formData.append("avatar", id.toString()); const [ownedPics, setOwnedPics] = useState<number[]>([]);
try { useEffect(() => {
const response = await fetch("/api/me", { if (isAuthenticated && session) {
method: "POST", setPoints(session.points || 0);
body: formData, setAvatar(session.avatar || 1);
}); setOwnedPics(session.inventory || []);
}
}, [session]);
const data = await response.json(); const handleAvatarSelect = async (id: number) => {
if (response.ok) { const formData = new FormData();
alert("Avatar updated successfully!"); formData.append("avatar", id.toString());
setAvatar(id);
} else {
alert(data.message || "Failed to update avatar.");
}
} catch (error) {
console.error("Error updating avatar:", error);
alert("An error occurred while updating the avatar.");
}
};
const handlePurchase = async (src: string, price: number) => { try {
if (points < price) { const response = await fetch("/api/me", {
alert("You don't have enough points to purchase this profile picture."); method: "POST",
return; body: formData,
} });
// Update the user's avatar and points on the server const data = await response.json();
const formData = new FormData(); if (response.ok) {
formData.append("avatar", src); alert("Avatar updated successfully!");
formData.append("points", (points - price).toString()); setAvatar(id);
} else {
alert(data.message || "Failed to update avatar.");
}
} catch (error) {
console.error("Error updating avatar:", error);
alert("An error occurred while updating the avatar.");
}
};
try { const handlePurchase = async (id: number, price: number) => {
const response = await fetch("/api/me", { if (points < price) {
method: "POST", alert("You don't have enough points to purchase this profile picture.");
body: formData, return;
}); }
const data = await response.json(); // Update the user's avatar, points, and inventory on the server
if (response.ok) { const formData = new FormData();
alert("Profile picture purchased successfully!"); formData.append("avatar", id.toString());
setPoints(points - price); formData.append("points", (points - price).toString());
setAvatar(src); formData.append("inventory", id.toString());
setOwnedPics([...ownedPics, src]);
} else {
alert(data.message || "Failed to purchase profile picture.");
}
} catch (error) {
console.error("Error purchasing profile picture:", error);
alert("An error occurred while purchasing the profile picture.");
}
};
return ( try {
<div className="px-6 py-10 max-w-full lg:max-w-4xl mx-auto font-sans text-neutral-100"> const response = await fetch("/api/me", {
{/* User Header */} method: "POST",
<div className="items-center gap-4 mb-6"> body: formData,
<div className="w-full text-center bg-[color:var(--color-surface-600)]/60 backdrop-blur-md rounded-xl px-6 py-5 my-6 gap-4 shadow-sm inline-flex items-center"> });
<div className="w-16 h-16 rounded-full bg-neutral-800">
<img
src={"/avatar/p" + avatar + ".png"}
alt="Current PFP"
className="rounded-full w-full h-full object-cover"
/>
</div>
<h1 className="text-2xl font-bold text-[color:var(--color-warning-300)]">
{isAuthenticated ? session.username : "Username"}
</h1>
</div>
</div>
{/* Total Points */} const data = await response.json();
<div className="bg-[color:var(--color-success-600)] rounded-xl px-6 py-4 mb-8 shadow-sm"> if (response.ok) {
<h2 className="text-xl font-bold text-[color:var(--color-warning-300)] mb-1"> alert("Profile picture purchased successfully!");
Total Points setPoints(points - price);
</h2> setAvatar(id);
<p className="text-lg">{points}</p> setOwnedPics(prev => [...prev, id]);
</div> } else {
alert(data.message || "Failed to purchase profile picture.");
}
} catch (error) {
console.error("Error purchasing profile picture:", error);
alert("An error occurred while purchasing the profile picture.");
}
};
{/* Default Profile Pictures */} return (
<div className="mb-8"> <div className="px-6 py-10 my-10 max-w-full lg:max-w-4xl mx-auto font-sans text-neutral-100">
<div className="bg-[color:var(--color-success-600)]/70 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm"> {/* User Header */}
<h2 className="text-xl font-semibold text-[color:var(--color-warning-300)] mb-0"> <div className="items-center gap-4 mb-6">
Default Profile Pics <div className="w-full text-center bg-[color:var(--color-surface-600)]/60 backdrop-blur-md rounded-xl px-6 py-5 my-6 gap-4 shadow-sm inline-flex items-center">
</h2> <div className="w-16 h-16 rounded-full bg-neutral-800">
</div> <img
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4"> src={"/avatar/p" + avatar + ".png"}
{defaultPics.map((pfp) => ( alt="Current PFP"
className="rounded-full w-full h-full object-cover"
/>
</div>
<h1 className="text-2xl font-bold text-[color:var(--color-warning-300)]">
{isAuthenticated ? session.username : "Username"}
</h1>
</div>
</div>
{/* Total Points */}
<div className="bg-[color:var(--color-success-600)] rounded-xl px-6 py-4 mb-8 shadow-sm">
<h2 className="text-xl font-bold text-[color:var(--color-warning-300)] mb-1">
Total Points
</h2>
<p className="text-lg">{points}</p>
</div>
{/* Default Profile Pictures */}
<div className="mb-8">
<div className="bg-[color:var(--color-success-600)]/70 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm">
<h2 className="text-xl font-semibold text-[color:var(--color-warning-300)] mb-0">
Default Profile Pics
</h2>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
{defaultPics.map((pfp) => (
<div <div
key={pfp.src} key={pfp.src}
className="bg-[color:var(--color-surface-600)] rounded-lg p-3 flex flex-col items-center text-center shadow-sm" className="bg-[color:var(--color-surface-600)] rounded-lg p-3 flex flex-col items-center text-center shadow-sm"
@@ -134,40 +142,52 @@ export default function ShopPage() {
className="w-32 h-32 object-cover rounded mb-2" className="w-32 h-32 object-cover rounded mb-2"
/> />
</div> </div>
))} ))}
</div> </div>
</div> </div>
{/* Purchaseable Profile Pictures */} {/* Purchaseable Profile Pictures */}
<div> <div>
<div className="bg-[color:var(--color-tertiary-600)]/90 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm"> <div className="bg-[color:var(--color-tertiary-600)]/90 backdrop-blur-md rounded-xl px-6 py-5 my-6 shadow-sm">
<h2 className="text-xl font-semibold text-[color:var(--color-warning-200)] mb-0"> <h2 className="text-xl font-semibold text-[color:var(--color-warning-200)] mb-0">
Purchase New Profile Pics! Purchase New Profile Pics!
</h2> </h2>
</div> </div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-6"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-6">
{purchasablePics.map((pic) => ( {purchasablePics.map((pic) => (
<div <div
key={pic.src} key={pic.id}
className="bg-[color:var(--color-surface-600)] rounded-lg p-3 flex flex-col items-center text-center shadow-sm" className="bg-[color:var(--color-surface-600)] rounded-lg p-3 flex flex-col items-center text-center shadow-sm"
> >
<img <img
src={pic.src} src={pic.src}
alt="PFP" alt="PFP"
className="w-24 h-24 object-cover rounded border border-gray-500 mb-2" className="w-24 h-24 object-cover rounded mb-2"
/> />
<p className="text-sm mb-1">{pic.price} pts</p> <p className="text-sm mb-1">{pic.price} pts</p>
<button {ownedPics.includes(pic.id) ? (
className="px-3 py-1 bg-success-600 text-sm text-white rounded hover:bg-success-700" <button
onClick={() => handlePurchase(pic.src, pic.price)} className={`px-3 py-1 ${
disabled={ownedPics.includes(pic.src)} avatar === pic.id
> ? "bg-success-700 text-white"
{ownedPics.includes(pic.src) ? "Owned" : "Buy"} : "bg-success-600 text-white hover:bg-success-700"
</button> } text-sm rounded`}
</div> onClick={() => handleAvatarSelect(pic.id)}
))} >
</div> {avatar === pic.id ? "Selected" : "Select"}
</div> </button>
</div> ) : (
); <button
className="px-3 py-1 bg-success-600 text-sm text-white rounded hover:bg-success-700"
onClick={() => handlePurchase(pic.id, pic.price)}
>
Purchase
</button>
)}
</div>
))}
</div>
</div>
</div>
);
} }

View File

@@ -42,7 +42,7 @@ export async function POST(req: Request) {
if(inventory) { if(inventory) {
let inventoryData = userData.inventory; let inventoryData = userData.inventory;
if (!inventoryData) inventoryData = []; if (!inventoryData) inventoryData = [];
inventoryData.push(inventory.toString()); inventoryData.push(parseInt(inventory.toString()));
userData = await db.users.update(userData.id, { inventory: inventoryData }); userData = await db.users.update(userData.id, { inventory: inventoryData });
if (!userData) return NextResponse.json({ message: "Failed to update inventory" }, { status: 500 }); if (!userData) return NextResponse.json({ message: "Failed to update inventory" }, { status: 500 });

View File

@@ -12,7 +12,7 @@ const userSchema = new mongoose.Schema({
bio: String, bio: String,
avatar: Number, avatar: Number,
points: Number, points: Number,
inventory: Array, inventory: Array<Number>,
friends: Array<String>, friends: Array<String>,
requests: Array<String>, requests: Array<String>,
}); });