fix app direction: no more app loops
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
"use client";
|
||||
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
export default function BillingPage() {
|
||||
const plans = [
|
||||
{
|
||||
name: "Free",
|
||||
price: "$0",
|
||||
description: "Perfect for getting started",
|
||||
features: [
|
||||
"10 messages/month",
|
||||
"Access to featured characters",
|
||||
"Basic support",
|
||||
],
|
||||
current: true,
|
||||
},
|
||||
{
|
||||
name: "Pro",
|
||||
price: "$9.99",
|
||||
period: "/month",
|
||||
description: "For serious enthusiasts",
|
||||
features: [
|
||||
"Unlimited messages",
|
||||
"Priority character creation",
|
||||
"Custom characters",
|
||||
"Ad-free experience",
|
||||
"Priority support",
|
||||
],
|
||||
current: false,
|
||||
},
|
||||
{
|
||||
name: "Premium",
|
||||
price: "$29.99",
|
||||
period: "/month",
|
||||
description: "Maximum experience",
|
||||
features: [
|
||||
"Everything in Pro",
|
||||
"Advanced AI models",
|
||||
"Character voice customization",
|
||||
"Private character storage",
|
||||
"White-label options",
|
||||
"24/7 VIP support",
|
||||
],
|
||||
current: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-5xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-12">
|
||||
<h1 className="text-3xl font-bold mb-2">Billing & Plans</h1>
|
||||
<p className="text-gray-600">Manage your subscription</p>
|
||||
</div>
|
||||
|
||||
{/* Current Plan */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-6 mb-12">
|
||||
<p className="text-sm text-blue-600 font-semibold">CURRENT PLAN</p>
|
||||
<h3 className="text-2xl font-bold mt-2">Free Plan</h3>
|
||||
<p className="text-gray-600 mt-1">You're currently on the free plan</p>
|
||||
</div>
|
||||
|
||||
{/* Plans Grid */}
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{plans.map((plan, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`rounded-xl border-2 p-8 transition ${
|
||||
plan.current
|
||||
? "border-black bg-white"
|
||||
: "border-gray-200 hover:border-black"
|
||||
}`}
|
||||
>
|
||||
{/* Badge */}
|
||||
{plan.current && (
|
||||
<div className="inline-block bg-black text-white px-3 py-1 rounded-full text-xs font-semibold mb-4">
|
||||
Current Plan
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Plan Name */}
|
||||
<h3 className="text-2xl font-bold mb-2">{plan.name}</h3>
|
||||
<p className="text-gray-600 text-sm mb-6">{plan.description}</p>
|
||||
|
||||
{/* Price */}
|
||||
<div className="mb-6">
|
||||
<span className="text-4xl font-bold">{plan.price}</span>
|
||||
{plan.period && <span className="text-gray-600">{plan.period}</span>}
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<button
|
||||
className={`w-full py-3 rounded-lg font-medium mb-6 transition ${
|
||||
plan.current
|
||||
? "bg-gray-100 text-gray-600 cursor-default"
|
||||
: "bg-black text-white hover:bg-gray-800"
|
||||
}`}
|
||||
disabled={plan.current}
|
||||
>
|
||||
{plan.current ? "Current Plan" : "Upgrade"}
|
||||
</button>
|
||||
|
||||
{/* Features */}
|
||||
<ul className="space-y-3">
|
||||
{plan.features.map((feature, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm">
|
||||
<Check className="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Billing History */}
|
||||
<div className="mt-12">
|
||||
<h2 className="text-2xl font-bold mb-6">Billing History</h2>
|
||||
<div className="bg-white border border-gray-200 rounded-xl overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 border-b border-gray-200">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold">Date</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold">Description</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold">Amount</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{ date: "Dec 1, 2024", desc: "Free Plan", amount: "$0.00", status: "Active" },
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="border-b border-gray-200 hover:bg-gray-50">
|
||||
<td className="px-6 py-4 text-sm">{row.date}</td>
|
||||
<td className="px-6 py-4 text-sm">{row.desc}</td>
|
||||
<td className="px-6 py-4 text-sm font-medium">{row.amount}</td>
|
||||
<td className="px-6 py-4 text-sm">
|
||||
<span className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-xs font-semibold">
|
||||
{row.status}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
"use client";
|
||||
|
||||
import { Upload, Wand2, Save, X } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function CreatePage() {
|
||||
const [characterData, setCharacterData] = useState({
|
||||
name: "",
|
||||
avatar: "",
|
||||
description: "",
|
||||
personality: "",
|
||||
category: "Fantasy",
|
||||
});
|
||||
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setCharacterData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (step < 4) setStep(step + 1);
|
||||
};
|
||||
|
||||
const handlePrev = () => {
|
||||
if (step > 1) setStep(step - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-2xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2">Create Character</h1>
|
||||
<p className="text-gray-600">Build your own AI character in 4 simple steps</p>
|
||||
</div>
|
||||
|
||||
{/* Progress */}
|
||||
<div className="flex gap-2 mb-8">
|
||||
{[1, 2, 3, 4].map((num) => (
|
||||
<div
|
||||
key={num}
|
||||
className={`flex-1 h-2 rounded-full transition ${
|
||||
num <= step ? "bg-black" : "bg-gray-200"
|
||||
}`}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 space-y-6">
|
||||
{/* Step 1: Basic Info */}
|
||||
{step === 1 && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold mb-2">Character Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={characterData.name}
|
||||
onChange={handleChange}
|
||||
placeholder="e.g., Luna, Alex, Sage"
|
||||
className="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:border-black focus:outline-none transition"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-semibold mb-2">Category</label>
|
||||
<select
|
||||
name="category"
|
||||
value={characterData.category}
|
||||
onChange={handleChange}
|
||||
className="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:border-black focus:outline-none transition"
|
||||
>
|
||||
<option>Fantasy</option>
|
||||
<option>Sci-Fi</option>
|
||||
<option>Romance</option>
|
||||
<option>Mystery</option>
|
||||
<option>Adventure</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600">
|
||||
Give your character a unique name and choose a category that best fits their personality.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Step 2: Avatar */}
|
||||
{step === 2 && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold mb-4">Character Avatar</label>
|
||||
|
||||
{/* Preview */}
|
||||
<div className="w-32 h-32 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 rounded-xl flex items-center justify-center mb-6">
|
||||
<span className="text-5xl">✨</span>
|
||||
</div>
|
||||
|
||||
{/* Upload Options */}
|
||||
<div className="space-y-3">
|
||||
<button className="w-full border-2 border-dashed border-gray-300 rounded-lg py-8 hover:border-gray-400 transition flex flex-col items-center gap-2">
|
||||
<Upload className="w-6 h-6 text-gray-600" />
|
||||
<span className="text-sm font-medium">Upload Image</span>
|
||||
<span className="text-xs text-gray-500">or drag and drop</span>
|
||||
</button>
|
||||
|
||||
<button className="w-full border-2 border-gray-200 rounded-lg py-3 font-medium hover:bg-gray-50 transition flex items-center justify-center gap-2">
|
||||
<Wand2 className="w-5 h-5" />
|
||||
Generate with AI
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Step 3: Description */}
|
||||
{step === 3 && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold mb-2">Character Description</label>
|
||||
<textarea
|
||||
name="description"
|
||||
value={characterData.description}
|
||||
onChange={handleChange}
|
||||
placeholder="Describe your character's appearance, background, and key traits..."
|
||||
rows={4}
|
||||
className="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:border-black focus:outline-none transition resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-semibold mb-2">Personality & Traits</label>
|
||||
<textarea
|
||||
name="personality"
|
||||
value={characterData.personality}
|
||||
onChange={handleChange}
|
||||
placeholder="Define their personality, interests, quirks, and conversation style..."
|
||||
rows={4}
|
||||
className="w-full px-4 py-3 border-2 border-gray-200 rounded-lg focus:border-black focus:outline-none transition resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Step 4: Review */}
|
||||
{step === 4 && (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-gray-50 rounded-lg p-6 space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 rounded-lg flex items-center justify-center text-3xl">
|
||||
✨
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-bold">{characterData.name || "Character Name"}</h3>
|
||||
<p className="text-sm text-gray-600">{characterData.category}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pt-4">
|
||||
<p className="text-sm">
|
||||
<span className="font-semibold">Description:</span>{" "}
|
||||
{characterData.description || "No description provided"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pt-4">
|
||||
<p className="text-sm">
|
||||
<span className="font-semibold">Personality:</span>{" "}
|
||||
{characterData.personality || "No personality traits provided"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600">
|
||||
Review your character and click "Publish" to make them available to the community!
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 pt-6 border-t border-gray-200">
|
||||
<button
|
||||
onClick={handlePrev}
|
||||
disabled={step === 1}
|
||||
className="flex-1 px-4 py-3 border-2 border-gray-200 rounded-lg font-medium hover:bg-gray-50 transition disabled:opacity-50"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
{step < 4 ? (
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="flex-1 px-4 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
) : (
|
||||
<button className="flex-1 px-4 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition flex items-center justify-center gap-2">
|
||||
<Save className="w-5 h-5" />
|
||||
Publish Character
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { Heart, MessageCircle, Share2, Trash2 } from "lucide-react";
|
||||
|
||||
function generateFavorites() {
|
||||
const characters = [
|
||||
{ id: 1, name: "Luna", avatar: "🌙", category: "Fantasy", messages: 250 },
|
||||
{ id: 2, name: "Alex", avatar: "⚡", category: "Sci-Fi", messages: 180 },
|
||||
{ id: 3, name: "Sage", avatar: "🧙", category: "Fantasy", messages: 340 },
|
||||
];
|
||||
return characters;
|
||||
}
|
||||
|
||||
export default function FavoritesPage() {
|
||||
const favorites = generateFavorites();
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2">Favorites</h1>
|
||||
<p className="text-gray-600">{favorites.length} favorite characters</p>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{favorites.map((char) => (
|
||||
<div
|
||||
key={char.id}
|
||||
className="bg-white border border-gray-200 rounded-xl overflow-hidden hover:shadow-lg transition"
|
||||
>
|
||||
{/* Character Card */}
|
||||
<div className="h-48 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 flex items-center justify-center relative group">
|
||||
<span className="text-6xl">{char.avatar}</span>
|
||||
<button className="absolute top-2 right-2 p-2 bg-white rounded-lg opacity-0 group-hover:opacity-100 transition">
|
||||
<Heart className="w-5 h-5 fill-red-600 text-red-600" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-4 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">{char.name}</h3>
|
||||
<p className="text-sm text-gray-600">{char.category} Character</p>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex gap-4 text-sm text-gray-600 pb-4 border-b border-gray-200">
|
||||
<div className="flex items-center gap-1">
|
||||
<MessageCircle className="w-4 h-4" />
|
||||
<span>{char.messages} messages</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2">
|
||||
<button className="flex-1 bg-black text-white py-2 rounded-lg font-medium hover:bg-gray-800 transition text-sm">
|
||||
Continue Chat
|
||||
</button>
|
||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition">
|
||||
<Share2 className="w-5 h-5" />
|
||||
</button>
|
||||
<button className="p-2 hover:bg-red-100 text-red-600 rounded-lg transition">
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{favorites.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<Heart className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||
<p className="text-gray-600">No favorite characters yet</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Compass,
|
||||
MessageCircle,
|
||||
PlusCircle,
|
||||
User,
|
||||
LogOut,
|
||||
ChevronDown,
|
||||
Sparkles,
|
||||
Bell,
|
||||
Search,
|
||||
} from "lucide-react";
|
||||
|
||||
export default function AppLayout({ children }: { children: React.ReactNode }) {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("discover");
|
||||
|
||||
const navigationItems = [
|
||||
{ id: "discover", label: "Discover", icon: Compass, href: "/app" },
|
||||
{ id: "roleplay", label: "Roleplay", icon: Sparkles, href: "/app/roleplay" },
|
||||
{ id: "create", label: "Create", icon: PlusCircle, href: "/app/create" },
|
||||
{ id: "messages", label: "Messages", icon: MessageCircle, href: "/app/messages" },
|
||||
{ id: "profile", label: "Profile", icon: User, href: "/app/profile" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col bg-white">
|
||||
{/* Header */}
|
||||
<header className="border-b border-gray-200 sticky top-0 z-40 bg-white">
|
||||
<div className="px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||
{/* Logo + Title */}
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-pink-600 to-purple-600 rounded-lg flex items-center justify-center">
|
||||
<span className="text-white text-sm font-bold">RP</span>
|
||||
</div>
|
||||
<span className="font-semibold text-lg hidden sm:inline">RoleplayAI</span>
|
||||
</Link>
|
||||
|
||||
{/* Center - Search Bar */}
|
||||
<div className="flex-1 max-w-md mx-4 hidden md:block">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search characters..."
|
||||
className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-full text-sm focus:outline-none focus:border-black transition"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right - Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Subscribe Button */}
|
||||
<button className="hidden sm:block px-4 py-2 border-2 border-black rounded-full font-medium hover:bg-black hover:text-white transition text-sm">
|
||||
Subscribe
|
||||
</button>
|
||||
|
||||
{/* Token Count */}
|
||||
<div className="flex items-center gap-2 px-4 py-2 bg-gray-50 rounded-full">
|
||||
<span className="text-sm font-semibold">⚡</span>
|
||||
<span className="text-sm font-medium">450</span>
|
||||
</div>
|
||||
|
||||
{/* Notifications */}
|
||||
<button className="relative p-2 hover:bg-gray-100 rounded-full transition">
|
||||
<Bell className="w-5 h-5" />
|
||||
<span className="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full"></span>
|
||||
</button>
|
||||
|
||||
{/* User Avatar & Dropdown */}
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setUserMenuOpen(!userMenuOpen)}
|
||||
className="flex items-center gap-2 p-1 hover:bg-gray-100 rounded-full transition"
|
||||
>
|
||||
<img
|
||||
src="https://api.dicebear.com/7.x/avataaars/svg?seed=user"
|
||||
alt="User"
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<ChevronDown className="w-4 h-4 text-gray-600" />
|
||||
</button>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{userMenuOpen && (
|
||||
<div className="absolute right-0 mt-2 w-56 bg-white border border-gray-200 rounded-xl shadow-lg z-50">
|
||||
{/* Profile Section */}
|
||||
<div className="p-4 border-b border-gray-200">
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
src="https://api.dicebear.com/7.x/avataaars/svg?seed=user"
|
||||
alt="User"
|
||||
className="w-10 h-10 rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<p className="font-semibold text-sm">John Doe</p>
|
||||
<p className="text-xs text-gray-500">@johndoe</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Menu Items */}
|
||||
<div className="py-2">
|
||||
<Link href="/app/profile" className="block px-4 py-2 text-sm hover:bg-gray-50 transition">
|
||||
My Profile
|
||||
</Link>
|
||||
<Link href="/app/settings" className="block px-4 py-2 text-sm hover:bg-gray-50 transition">
|
||||
Settings
|
||||
</Link>
|
||||
<Link href="/app/billing" className="block px-4 py-2 text-sm hover:bg-gray-50 transition">
|
||||
Billing & Plans
|
||||
</Link>
|
||||
<Link href="/app/favorites" className="block px-4 py-2 text-sm hover:bg-gray-50 transition">
|
||||
Favorites
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-gray-200"></div>
|
||||
|
||||
{/* Logout */}
|
||||
<button className="w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-50 transition flex items-center gap-2">
|
||||
<LogOut className="w-4 h-4" />
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* Sidebar */}
|
||||
<aside
|
||||
className={`${
|
||||
sidebarOpen ? "w-56" : "w-0"
|
||||
} border-r border-gray-200 bg-gray-50 flex flex-col transition-all duration-300 overflow-hidden`}
|
||||
>
|
||||
<nav className="flex-1 space-y-2 p-4">
|
||||
{navigationItems.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={item.href}
|
||||
onClick={() => setActiveTab(item.id)}
|
||||
className={`flex items-center gap-3 px-4 py-3 rounded-lg font-medium text-sm transition ${
|
||||
activeTab === item.id
|
||||
? "bg-white text-black shadow-sm"
|
||||
: "text-gray-600 hover:bg-white/50"
|
||||
}`}
|
||||
>
|
||||
<item.icon className="w-5 h-5" />
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Sidebar Footer */}
|
||||
<div className="border-t border-gray-200 p-4 space-y-3">
|
||||
<p className="text-xs text-gray-500 font-semibold">UPGRADE</p>
|
||||
<div className="bg-gradient-to-br from-pink-50 to-purple-50 rounded-lg p-3 space-y-2">
|
||||
<p className="text-xs font-semibold">Unlock Premium</p>
|
||||
<p className="text-xs text-gray-600">Get unlimited characters & faster responses</p>
|
||||
<button className="w-full bg-black text-white py-2 text-xs font-semibold rounded-lg hover:bg-gray-800 transition">
|
||||
Upgrade
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Main View */}
|
||||
<main className="flex-1 overflow-auto">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import { MessageSquare, Trash2, MoreHorizontal } from "lucide-react";
|
||||
|
||||
function generateMockMessages() {
|
||||
const messages = [
|
||||
{
|
||||
id: 1,
|
||||
character: "Luna",
|
||||
avatar: "🌙",
|
||||
preview: "That sounds interesting...",
|
||||
timestamp: "2 hours ago",
|
||||
unread: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
character: "Alex",
|
||||
avatar: "⚡",
|
||||
preview: "Hey! How have you been?",
|
||||
timestamp: "5 hours ago",
|
||||
unread: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
character: "Sage",
|
||||
avatar: "🧙",
|
||||
preview: "The ancient prophecy speaks of...",
|
||||
timestamp: "Yesterday",
|
||||
unread: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
character: "Nova",
|
||||
avatar: "✨",
|
||||
preview: "Ready for another space adventure?",
|
||||
timestamp: "2 days ago",
|
||||
unread: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
character: "Echo",
|
||||
avatar: "🔮",
|
||||
preview: "I sensed something in the shadows...",
|
||||
timestamp: "3 days ago",
|
||||
unread: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
character: "Storm",
|
||||
avatar: "⛈️",
|
||||
preview: "The journey awaits us!",
|
||||
timestamp: "1 week ago",
|
||||
unread: false,
|
||||
},
|
||||
];
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
export default function MessagesPage() {
|
||||
const messages = generateMockMessages();
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-3xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2">Messages</h1>
|
||||
<p className="text-gray-600">Your conversations with characters</p>
|
||||
</div>
|
||||
|
||||
{/* Messages List */}
|
||||
<div className="space-y-2">
|
||||
{messages.map((msg) => (
|
||||
<div
|
||||
key={msg.id}
|
||||
className={`border border-gray-200 rounded-xl p-4 hover:bg-gray-50 cursor-pointer transition flex items-center gap-4 group ${
|
||||
msg.unread ? "bg-blue-50" : "bg-white"
|
||||
}`}
|
||||
>
|
||||
{/* Avatar */}
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 rounded-full flex items-center justify-center text-xl flex-shrink-0">
|
||||
{msg.avatar}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="font-semibold">{msg.character}</h3>
|
||||
{msg.unread && <div className="w-2 h-2 bg-blue-600 rounded-full"></div>}
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 truncate">{msg.preview}</p>
|
||||
</div>
|
||||
|
||||
{/* Meta */}
|
||||
<div className="text-right flex-shrink-0">
|
||||
<p className="text-xs text-gray-500 mb-2">{msg.timestamp}</p>
|
||||
<div className="opacity-0 group-hover:opacity-100 transition flex gap-2">
|
||||
<button className="p-2 hover:bg-gray-200 rounded-lg transition">
|
||||
<MoreHorizontal className="w-4 h-4" />
|
||||
</button>
|
||||
<button className="p-2 hover:bg-red-100 text-red-600 rounded-lg transition">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Empty State */}
|
||||
{messages.length === 0 && (
|
||||
<div className="text-center py-16">
|
||||
<MessageSquare className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-semibold mb-2">No messages yet</h3>
|
||||
<p className="text-gray-600">Start a conversation with a character!</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
"use client";
|
||||
|
||||
import { Heart, MessageCircle, Share2 } from "lucide-react";
|
||||
|
||||
// Mock data generator
|
||||
function generateMockCharacters() {
|
||||
const baseCharacters = [
|
||||
{ name: "Luna", avatar: "🌙", description: "Mysterious night elf" },
|
||||
{ name: "Alex", avatar: "⚡", description: "Energetic hacker" },
|
||||
{ name: "Sage", avatar: "🧙", description: "Wise wizard" },
|
||||
{ name: "Nova", avatar: "✨", description: "Cosmic explorer" },
|
||||
];
|
||||
|
||||
return Array.from({ length: 12 }, (_, i) => {
|
||||
const char = baseCharacters[i % baseCharacters.length];
|
||||
return {
|
||||
id: i + 1,
|
||||
...char,
|
||||
messages: Math.floor(Math.random() * 500) + 50,
|
||||
likes: Math.floor(Math.random() * 1000) + 100,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default function DiscoverPage() {
|
||||
const characters = generateMockCharacters();
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2">Discover</h1>
|
||||
<p className="text-gray-600">Explore trending characters and conversations</p>
|
||||
</div>
|
||||
|
||||
{/* Waterfall Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{characters.map((char) => (
|
||||
<div
|
||||
key={char.id}
|
||||
className="bg-white border border-gray-200 rounded-xl overflow-hidden hover:shadow-lg transition group cursor-pointer"
|
||||
>
|
||||
{/* Character Card Header */}
|
||||
<div className="h-48 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 flex items-center justify-center">
|
||||
<span className="text-6xl">{char.avatar}</span>
|
||||
</div>
|
||||
|
||||
{/* Card Content */}
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">{char.name}</h3>
|
||||
<p className="text-sm text-gray-600">{char.description}</p>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex gap-4 text-sm text-gray-600 pb-4 border-b border-gray-200">
|
||||
<div className="flex items-center gap-1">
|
||||
<MessageCircle className="w-4 h-4" />
|
||||
<span>{char.messages}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Heart className="w-4 h-4" />
|
||||
<span>{char.likes}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2">
|
||||
<button className="flex-1 bg-black text-white py-2 rounded-lg font-medium hover:bg-gray-800 transition text-sm">
|
||||
Chat Now
|
||||
</button>
|
||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition">
|
||||
<Heart className="w-5 h-5" />
|
||||
</button>
|
||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition">
|
||||
<Share2 className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Load More */}
|
||||
<div className="mt-12 text-center">
|
||||
<button className="px-8 py-3 border-2 border-gray-200 rounded-full font-medium hover:bg-gray-50 transition">
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
"use client";
|
||||
|
||||
import { Edit2, Copy, LogOut } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ProfilePage() {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-3xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2">My Profile</h1>
|
||||
<p className="text-gray-600">Manage your account</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsEditing(!isEditing)}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-black text-white rounded-lg hover:bg-gray-800 transition"
|
||||
>
|
||||
<Edit2 className="w-4 h-4" />
|
||||
{isEditing ? "Done" : "Edit"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Profile Card */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 space-y-8 mb-8">
|
||||
{/* Avatar & Basic Info */}
|
||||
<div className="flex items-start gap-6">
|
||||
<img
|
||||
src="https://api.dicebear.com/7.x/avataaars/svg?seed=user"
|
||||
alt="Profile"
|
||||
className="w-24 h-24 rounded-xl"
|
||||
/>
|
||||
<div className="flex-1 space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-gray-600">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
defaultValue="John Doe"
|
||||
disabled={!isEditing}
|
||||
className="w-full px-4 py-2 border border-gray-200 rounded-lg disabled:bg-gray-50 focus:outline-none focus:border-black transition"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-gray-600">Handle</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
defaultValue="@johndoe"
|
||||
disabled={!isEditing}
|
||||
className="flex-1 px-4 py-2 border border-gray-200 rounded-lg disabled:bg-gray-50 focus:outline-none focus:border-black transition"
|
||||
/>
|
||||
{isEditing && (
|
||||
<button className="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200 transition">
|
||||
<Copy className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<label className="text-sm font-semibold text-gray-600">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
defaultValue="john@example.com"
|
||||
disabled
|
||||
className="w-full px-4 py-2 border border-gray-200 rounded-lg bg-gray-50 focus:outline-none mt-2"
|
||||
/>
|
||||
<p className="text-xs text-gray-500 mt-2">Connected via Google OAuth</p>
|
||||
</div>
|
||||
|
||||
{/* Bio */}
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<label className="text-sm font-semibold text-gray-600">Bio</label>
|
||||
<textarea
|
||||
defaultValue="AI enthusiast and roleplay lover"
|
||||
disabled={!isEditing}
|
||||
rows={3}
|
||||
className="w-full px-4 py-2 border border-gray-200 rounded-lg disabled:bg-gray-50 focus:outline-none focus:border-black transition resize-none mt-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-3 gap-4 mb-8">
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-6 text-center">
|
||||
<div className="text-2xl font-bold">12</div>
|
||||
<p className="text-sm text-gray-600 mt-2">Favorite Characters</p>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-6 text-center">
|
||||
<div className="text-2xl font-bold">48</div>
|
||||
<p className="text-sm text-gray-600 mt-2">Conversations</p>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-6 text-center">
|
||||
<div className="text-2xl font-bold">325</div>
|
||||
<p className="text-sm text-gray-600 mt-2">Messages</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Account Settings */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 space-y-4">
|
||||
<h3 className="text-lg font-semibold mb-4">Account Settings</h3>
|
||||
<button className="w-full text-left px-4 py-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition">
|
||||
Change Password
|
||||
</button>
|
||||
<button className="w-full text-left px-4 py-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition">
|
||||
Two-Factor Authentication
|
||||
</button>
|
||||
<button className="w-full text-left px-4 py-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition">
|
||||
Privacy Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Logout */}
|
||||
<button className="mt-8 w-full px-4 py-3 flex items-center justify-center gap-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition">
|
||||
<LogOut className="w-5 h-5" />
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
"use client";
|
||||
|
||||
import { Heart, MessageCircle, Share2, Filter } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
function generateRoleplayCharacters() {
|
||||
const categories = ["Fantasy", "Sci-Fi", "Romance", "Mystery", "Adventure"];
|
||||
const baseCharacters = [
|
||||
{ name: "Luna", avatar: "🌙", category: "Fantasy" },
|
||||
{ name: "Alex", avatar: "⚡", category: "Sci-Fi" },
|
||||
{ name: "Sage", avatar: "🧙", category: "Fantasy" },
|
||||
{ name: "Nova", avatar: "✨", category: "Sci-Fi" },
|
||||
{ name: "Echo", avatar: "🔮", category: "Mystery" },
|
||||
{ name: "Storm", avatar: "⛈️", category: "Adventure" },
|
||||
];
|
||||
|
||||
return Array.from({ length: 12 }, (_, i) => {
|
||||
const char = baseCharacters[i % baseCharacters.length];
|
||||
return {
|
||||
id: i + 1,
|
||||
...char,
|
||||
messages: Math.floor(Math.random() * 500) + 50,
|
||||
likes: Math.floor(Math.random() * 1000) + 100,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default function RoleplayPage() {
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const characters = generateRoleplayCharacters();
|
||||
const categories = ["Fantasy", "Sci-Fi", "Romance", "Mystery", "Adventure"];
|
||||
|
||||
const filtered = selectedCategory
|
||||
? characters.filter((c) => c.category === selectedCategory)
|
||||
: characters;
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8 space-y-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold mb-2">Roleplay</h1>
|
||||
<p className="text-gray-600">Immersive roleplay experiences</p>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<div className="flex items-center gap-3 overflow-x-auto pb-2">
|
||||
<button
|
||||
onClick={() => setSelectedCategory(null)}
|
||||
className={`px-4 py-2 rounded-full font-medium text-sm whitespace-nowrap transition ${
|
||||
selectedCategory === null
|
||||
? "bg-black text-white"
|
||||
: "border-2 border-gray-200 hover:border-black"
|
||||
}`}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
{categories.map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
onClick={() => setSelectedCategory(cat)}
|
||||
className={`px-4 py-2 rounded-full font-medium text-sm whitespace-nowrap transition ${
|
||||
selectedCategory === cat
|
||||
? "bg-black text-white"
|
||||
: "border-2 border-gray-200 hover:border-black"
|
||||
}`}
|
||||
>
|
||||
{cat}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{filtered.map((char) => (
|
||||
<div
|
||||
key={char.id}
|
||||
className="bg-white border border-gray-200 rounded-xl overflow-hidden hover:shadow-lg transition group cursor-pointer"
|
||||
>
|
||||
{/* Character Card */}
|
||||
<div className="h-48 bg-gradient-to-br from-blue-200 via-purple-200 to-pink-200 flex items-center justify-center relative">
|
||||
<span className="text-6xl">{char.avatar}</span>
|
||||
<div className="absolute top-2 right-2 bg-white px-3 py-1 rounded-full text-xs font-semibold">
|
||||
{char.category}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-4 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">{char.name}</h3>
|
||||
<p className="text-sm text-gray-600">{char.category} Character</p>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex gap-4 text-sm text-gray-600 pb-4 border-b border-gray-200">
|
||||
<div className="flex items-center gap-1">
|
||||
<MessageCircle className="w-4 h-4" />
|
||||
<span>{char.messages}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Heart className="w-4 h-4" />
|
||||
<span>{char.likes}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2">
|
||||
<button className="flex-1 bg-black text-white py-2 rounded-lg font-medium hover:bg-gray-800 transition text-sm">
|
||||
Start Roleplay
|
||||
</button>
|
||||
<button className="p-2 hover:bg-gray-100 rounded-lg transition">
|
||||
<Heart className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-600">No characters found in this category</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
"use client";
|
||||
|
||||
import { Save, Bell, Lock } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [settings, setSettings] = useState({
|
||||
emailNotifications: true,
|
||||
pushNotifications: false,
|
||||
marketingEmails: false,
|
||||
publicProfile: true,
|
||||
dataCollection: true,
|
||||
});
|
||||
|
||||
const handleToggle = (key: keyof typeof settings) => {
|
||||
setSettings((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-3xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold mb-2">Settings</h1>
|
||||
<p className="text-gray-600">Manage your preferences</p>
|
||||
</div>
|
||||
|
||||
{/* Notification Settings */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 mb-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Bell className="w-6 h-6" />
|
||||
<h3 className="text-lg font-semibold">Notifications</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{
|
||||
key: "emailNotifications" as const,
|
||||
label: "Email Notifications",
|
||||
desc: "Receive updates about new characters and messages",
|
||||
},
|
||||
{
|
||||
key: "pushNotifications" as const,
|
||||
label: "Push Notifications",
|
||||
desc: "Get notifications on your device",
|
||||
},
|
||||
{
|
||||
key: "marketingEmails" as const,
|
||||
label: "Marketing Emails",
|
||||
desc: "Receive promotional content and special offers",
|
||||
},
|
||||
].map((item) => (
|
||||
<div key={item.key} className="flex items-center justify-between py-3 border-b border-gray-200 last:border-0">
|
||||
<div>
|
||||
<p className="font-medium">{item.label}</p>
|
||||
<p className="text-sm text-gray-600">{item.desc}</p>
|
||||
</div>
|
||||
<label className="relative inline-flex cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings[item.key]}
|
||||
onChange={() => handleToggle(item.key)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-300 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-black rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-black"></div>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Privacy Settings */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 mb-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Lock className="w-6 h-6" />
|
||||
<h3 className="text-lg font-semibold">Privacy</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{
|
||||
key: "publicProfile" as const,
|
||||
label: "Public Profile",
|
||||
desc: "Allow other users to see your profile",
|
||||
},
|
||||
{
|
||||
key: "dataCollection" as const,
|
||||
label: "Data Collection",
|
||||
desc: "Allow us to collect usage data for improvements",
|
||||
},
|
||||
].map((item) => (
|
||||
<div key={item.key} className="flex items-center justify-between py-3 border-b border-gray-200 last:border-0">
|
||||
<div>
|
||||
<p className="font-medium">{item.label}</p>
|
||||
<p className="text-sm text-gray-600">{item.desc}</p>
|
||||
</div>
|
||||
<label className="relative inline-flex cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings[item.key]}
|
||||
onChange={() => handleToggle(item.key)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-300 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-black rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-black"></div>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<button className="flex items-center justify-center gap-2 w-full px-6 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition">
|
||||
<Save className="w-5 h-5" />
|
||||
Save Settings
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user