update storefront page

This commit is contained in:
g
2025-02-14 17:41:26 +00:00
parent 76c6b45149
commit eb4df7c0f3
2 changed files with 188 additions and 54 deletions

View File

@@ -170,4 +170,4 @@ export default function ShippingPage() {
/> />
</Layout> </Layout>
); );
} }

View File

@@ -6,7 +6,7 @@ import Layout from "@/components/layout/layout";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Save, Send, Key, MessageSquare, Shield, Globe } from "lucide-react"; import { Save, Send, Key, MessageSquare, Shield, Globe, Wallet } from "lucide-react";
import { apiRequest } from "@/lib/storeHelper"; import { apiRequest } from "@/lib/storeHelper";
import { toast, Toaster } from "sonner"; import { toast, Toaster } from "sonner";
import BroadcastDialog from "@/components/modals/broadcast-dialog"; import BroadcastDialog from "@/components/modals/broadcast-dialog";
@@ -17,6 +17,8 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
const SHIPPING_REGIONS = [ const SHIPPING_REGIONS = [
{ value: "UK", label: "United Kingdom", emoji: "🇬🇧" }, { value: "UK", label: "United Kingdom", emoji: "🇬🇧" },
@@ -31,8 +33,45 @@ interface Storefront {
telegramToken: string; telegramToken: string;
shipsFrom: typeof SHIPPING_REGIONS[number]["value"]; shipsFrom: typeof SHIPPING_REGIONS[number]["value"];
shipsTo: typeof SHIPPING_REGIONS[number]["value"]; shipsTo: typeof SHIPPING_REGIONS[number]["value"];
wallets: {
bitcoin?: string;
litecoin: string;
monero?: string;
};
enabledWallets: {
bitcoin: boolean;
litecoin: boolean;
monero: boolean;
};
} }
const WALLET_OPTIONS = [
{
id: 'bitcoin',
name: 'Bitcoin',
emoji: '₿',
placeholder: 'Your BTC address',
disabled: true,
comingSoon: true
},
{
id: 'litecoin',
name: 'Litecoin',
emoji: 'Ł',
placeholder: 'Your LTC address',
disabled: false,
comingSoon: false
},
{
id: 'monero',
name: 'Monero',
emoji: 'ɱ',
placeholder: 'Your XMR address',
disabled: true,
comingSoon: true
},
] as const;
export default function StorefrontPage() { export default function StorefrontPage() {
const router = useRouter(); const router = useRouter();
const [storefront, setStorefront] = useState<Storefront>({ const [storefront, setStorefront] = useState<Storefront>({
@@ -41,6 +80,16 @@ export default function StorefrontPage() {
telegramToken: "", telegramToken: "",
shipsFrom: "UK", shipsFrom: "UK",
shipsTo: "WW", shipsTo: "WW",
wallets: {
bitcoin: '',
litecoin: '',
monero: ''
},
enabledWallets: {
bitcoin: false,
litecoin: false,
monero: false
}
}); });
const [broadcastOpen, setBroadcastOpen] = useState<boolean>(false); const [broadcastOpen, setBroadcastOpen] = useState<boolean>(false);
@@ -61,8 +110,24 @@ export default function StorefrontPage() {
const fetchStorefront = async () => { const fetchStorefront = async () => {
try { try {
setLoading(true); setLoading(true);
const data: Storefront = await apiRequest("/storefront"); const data = await apiRequest("/storefront");
setStorefront(data); setStorefront({
pgpKey: data.pgpKey || "",
welcomeMessage: data.welcomeMessage || "",
telegramToken: data.telegramToken || "",
shipsFrom: data.shipsFrom || "UK",
shipsTo: data.shipsTo || "WW",
wallets: {
bitcoin: data.wallets?.bitcoin || '',
litecoin: data.wallets?.litecoin || '',
monero: data.wallets?.monero || ''
},
enabledWallets: {
bitcoin: data.enabledWallets?.bitcoin || false,
litecoin: data.enabledWallets?.litecoin || false,
monero: data.enabledWallets?.monero || false
}
});
} catch (error) { } catch (error) {
toast.error("Failed to load storefront data."); toast.error("Failed to load storefront data.");
} finally { } finally {
@@ -95,10 +160,8 @@ export default function StorefrontPage() {
return ( return (
<Layout> <Layout>
<Toaster position="top-right" theme="dark" duration={3000} /> <div className="max-w-4xl mx-auto p-6 space-y-6">
<BroadcastDialog open={broadcastOpen} setOpen={setBroadcastOpen} /> {/* PGP Key Section */}
<div className="max-w-4xl mx-auto p-6 space-y-8">
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700"> <div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700">
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<Key className="h-6 w-6 text-purple-600 dark:text-purple-400" /> <Key className="h-6 w-6 text-purple-600 dark:text-purple-400" />
@@ -106,56 +169,47 @@ export default function StorefrontPage() {
PGP Encryption Key PGP Encryption Key
</h2> </h2>
</div> </div>
<div className="space-y-4"> <Textarea
<Textarea value={storefront.pgpKey}
name="pgpKey" onChange={(e) => setStorefront(prev => ({ ...prev, pgpKey: e.target.value }))}
placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----" placeholder="Enter your PGP public key"
className="h-48 text-sm bg-gray-50 dark:bg-zinc-900 dark:bg-[#0F0F12]focus:ring-2 focus:ring-purple-500" className="font-mono text-sm h-40"
spellCheck={false} />
value={storefront.pgpKey}
onChange={handleInputChange}
/>
</div>
</div> </div>
{/* Telegram Configuration */} {/* Telegram Section */}
<div className="grid gap-6 md:grid-cols-2"> <div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700">
{/* Bot Token */} <div className="flex items-center gap-3 mb-4">
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700"> <MessageSquare className="h-6 w-6 text-emerald-600 dark:text-emerald-400" />
<div className="flex items-center gap-3 mb-4"> <h2 className="text-xl font-semibold text-gray-900 dark:text-zinc-100">
<Shield className="h-6 w-6 text-emerald-600 dark:text-emerald-400" /> Telegram Bot Token
<h2 className="text-xl font-semibold text-gray-900 dark:text-zinc-100"> </h2>
Telegram Bot Token
</h2>
</div>
<Input
name="telegramToken"
type="password"
placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
className="font-mono text-sm bg-gray-50 dark:bg-zinc-900 border-gray-200 dark:border-zinc-700"
value={storefront.telegramToken}
onChange={handleInputChange}
/>
</div>
{/* Welcome Message */}
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border border-gray-100 dark:border-zinc-700">
<div className="flex items-center gap-3 mb-4">
<MessageSquare className="h-6 w-6 text-amber-600 dark:text-amber-400" />
<h2 className="text-xl font-semibold text-gray-900 dark:text-zinc-100">
/start Welcome Message
</h2>
</div>
<Textarea
name="welcomeMessage"
placeholder="Welcome to our store!"
className="h-32 text-sm bg-gray-50 dark:bg-zinc-900 border-gray-200 dark:border-zinc-700 focus:ring-2 focus:ring-amber-500"
value={storefront.welcomeMessage}
onChange={handleInputChange}
/>
</div> </div>
<Input
type="password"
value={storefront.telegramToken}
onChange={(e) => setStorefront(prev => ({ ...prev, telegramToken: e.target.value }))}
placeholder="Enter your Telegram bot token"
/>
</div> </div>
{/* Welcome Message Section */}
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700">
<div className="flex items-center gap-3 mb-4">
<MessageSquare className="h-6 w-6 text-blue-600 dark:text-blue-400" />
<h2 className="text-xl font-semibold text-gray-900 dark:text-zinc-100">
/start Welcome Message
</h2>
</div>
<Textarea
value={storefront.welcomeMessage}
onChange={(e) => setStorefront(prev => ({ ...prev, welcomeMessage: e.target.value }))}
placeholder="Enter your welcome message"
className="h-[104px]"
/>
</div>
{/* Shipping Locations Section */}
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700"> <div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700">
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<Globe className="h-6 w-6 text-blue-600 dark:text-blue-400" /> <Globe className="h-6 w-6 text-blue-600 dark:text-blue-400" />
@@ -228,7 +282,82 @@ export default function StorefrontPage() {
</div> </div>
</div> </div>
<div className="sticky bottom-6 mt-8 flex justify-between"> {/* Cryptocurrency Wallets Section */}
<div className="bg-white dark:bg-[#0F0F12] rounded-xl shadow-lg p-6 border dark:border-zinc-700">
<div className="flex items-center gap-3 mb-4">
<Wallet className="h-6 w-6 text-yellow-600 dark:text-yellow-400" />
<h2 className="text-xl font-semibold text-gray-900 dark:text-zinc-100">
Cryptocurrency Wallets
</h2>
</div>
<div className="space-y-4">
{WALLET_OPTIONS.map((wallet) => (
<div
key={wallet.id}
className={`p-4 rounded-lg border dark:border-zinc-700
${wallet.disabled ? 'opacity-60' : ''}`}
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="text-lg font-mono">{wallet.emoji}</span>
<span className="font-medium">{wallet.name}</span>
{wallet.comingSoon && (
<span className="text-xs bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 px-2 py-0.5 rounded">
Coming Soon
</span>
)}
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<Switch
checked={storefront.enabledWallets[wallet.id]}
onCheckedChange={(checked) =>
setStorefront(prev => ({
...prev,
enabledWallets: {
...prev.enabledWallets,
[wallet.id]: checked
}
}))
}
disabled={wallet.disabled}
/>
</div>
</TooltipTrigger>
<TooltipContent>
{wallet.disabled
? `${wallet.name} payments coming soon`
: `Enable ${wallet.name} payments`
}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<Input
type="text"
placeholder={wallet.placeholder}
value={storefront.wallets[wallet.id] || ''}
onChange={(e) =>
setStorefront(prev => ({
...prev,
wallets: {
...prev.wallets,
[wallet.id]: e.target.value
}
}))
}
disabled={wallet.disabled || !storefront.enabledWallets[wallet.id]}
className="font-mono text-sm"
/>
</div>
))}
</div>
</div>
{/* Action Buttons */}
<div className="flex justify-between">
<Button <Button
onClick={() => setBroadcastOpen(true)} onClick={() => setBroadcastOpen(true)}
className="gap-2 bg-emerald-600 hover:bg-emerald-700 text-white" className="gap-2 bg-emerald-600 hover:bg-emerald-700 text-white"
@@ -245,6 +374,11 @@ export default function StorefrontPage() {
{saving ? "Saving..." : "Save Configuration"} {saving ? "Saving..." : "Save Configuration"}
</Button> </Button>
</div> </div>
<BroadcastDialog
open={broadcastOpen}
setOpen={setBroadcastOpen}
/>
</div> </div>
</Layout> </Layout>
); );