Shop Purchase Update
This commit is contained in:
@@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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>,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user