@qhristen/paygrid 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1 @@
1
- import React from 'react';
2
- export declare function LoginScreen(): React.JSX.Element;
1
+ export declare function LoginScreen(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useActionState } from 'react';
4
+ import { login } from './actions';
5
+ import { Loader2 } from 'lucide-react';
6
+ export function LoginScreen() {
7
+ const [state, formAction, isPending] = useActionState(login, null);
8
+ return (_jsxs("div", { className: "min-h-screen flex items-center justify-center bg-[#050505] text-white overflow-hidden relative", children: [_jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-indigo-900/20 via-[#050505] to-[#050505] z-0 pointer-events-none" }), _jsxs("div", { className: "w-full max-w-md p-8 rounded-2xl border border-white/10 bg-[#111]/80 backdrop-blur-xl shadow-2xl z-10", children: [_jsxs("div", { className: "mb-8 text-center", children: [_jsx("div", { className: "mx-auto w-12 h-12 bg-indigo-500 rounded-xl flex items-center justify-center mb-4 shadow-lg shadow-indigo-500/20", children: _jsx("span", { className: "text-xl font-bold text-white", children: "P" }) }), _jsx("h1", { className: "text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400", children: "Welcome Back" }), _jsx("p", { className: "text-gray-500 text-sm mt-2", children: "Sign in to your PayGrid dashboard" })] }), _jsxs("form", { action: formAction, className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "email", className: "text-sm font-medium text-gray-300", children: "Email" }), _jsx("input", { id: "email", name: "email", type: "email", required: true, placeholder: "admin@paygrid.com", className: "w-full bg-black/50 border border-white/10 rounded-lg px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition-all placeholder:text-gray-600 hover:border-white/20" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "password", className: "text-sm font-medium text-gray-300", children: "Password" }), _jsx("input", { id: "password", name: "password", type: "password", required: true, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: "w-full bg-black/50 border border-white/10 rounded-lg px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/50 transition-all placeholder:text-gray-600 hover:border-white/20" })] }), state?.error && (_jsx("div", { className: "p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm text-center", children: state.error })), _jsx("button", { type: "submit", disabled: isPending, className: "w-full bg-indigo-600 hover:bg-indigo-500 text-white font-medium py-3 rounded-lg transition-all shadow-lg shadow-indigo-600/20 flex items-center justify-center gap-2 mt-6 disabled:opacity-50 disabled:cursor-not-allowed", children: isPending ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), "Signing in..."] })) : ("Sign In") }), _jsx("div", { className: "mt-6 text-center", children: _jsx("p", { className: "text-xs text-gray-600", children: "Secure admin access only" }) })] })] })] }));
9
+ }
@@ -1,4 +1,5 @@
1
1
  import { PayGridResponseType } from "../types";
2
+ import '../index.css';
2
3
  export interface CheckoutModalProps {
3
4
  amount: number;
4
5
  method: "wallet-signing" | "manual-transfer";
@@ -6,5 +7,5 @@ export interface CheckoutModalProps {
6
7
  sender: string;
7
8
  onPaymentResponse?: (response: PayGridResponseType) => void;
8
9
  }
9
- export declare function CheckoutModal({ amount: propsAmount, method: propsMethod, tokenSymbol: propsTokenSymbol, sender: propsSender, onPaymentResponse, }: CheckoutModalProps): import("react").JSX.Element;
10
+ export declare function CheckoutModal({ amount: propsAmount, method: propsMethod, tokenSymbol: propsTokenSymbol, sender: propsSender, onPaymentResponse, }: CheckoutModalProps): import("react/jsx-runtime").JSX.Element;
10
11
  export default CheckoutModal;
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { initWASM, isWASMSupported, ShadowWireClient, } from "@radr/shadowwire";
4
+ import { useEffect, useState } from "react";
5
+ import { SUPPORTED_TOKENS } from "../config";
6
+ import { PaymentMethod } from "../types";
7
+ // @ts-ignore
8
+ import '../index.css';
9
+ export function CheckoutModal({ amount: propsAmount, method: propsMethod, tokenSymbol: propsTokenSymbol, sender: propsSender, onPaymentResponse, }) {
10
+ const [isCheckoutOpen, setIsCheckoutOpen] = useState(false);
11
+ const [step, setStep] = useState("config");
12
+ const [selectedToken, setSelectedToken] = useState(propsTokenSymbol);
13
+ const [selectedMethod, setSelectedMethod] = useState(propsMethod === "manual-transfer"
14
+ ? PaymentMethod.MANUAL_TRANSFER
15
+ : PaymentMethod.WALLET_SIGNING);
16
+ const [client] = useState(() => new ShadowWireClient());
17
+ const [amount, setAmount] = useState(propsAmount);
18
+ const [activeIntent, setActiveIntent] = useState(null);
19
+ const [isLoading, setIsLoading] = useState(false);
20
+ const [error, setError] = useState(null);
21
+ const apiKey = process.env.NEXT_PUBLIC_PAYGRID_API_SECRET;
22
+ const [wasmInitialized, setWasmInitialized] = useState(false);
23
+ const [balance, setBalance] = useState(null);
24
+ useEffect(() => {
25
+ async function init() {
26
+ if (!isWASMSupported()) {
27
+ setError("WebAssembly not supported");
28
+ return;
29
+ }
30
+ try {
31
+ await initWASM("/wasm/settler_wasm_bg.wasm");
32
+ setWasmInitialized(true);
33
+ await loadBalance();
34
+ }
35
+ catch (err) {
36
+ setError("Initialization failed: " + err.message);
37
+ }
38
+ }
39
+ init();
40
+ }, []);
41
+ const loadBalance = async () => {
42
+ try {
43
+ const data = await client.getBalance(propsSender, selectedToken);
44
+ setBalance(data.available / 1e9);
45
+ }
46
+ catch (err) {
47
+ console.error("Balance load failed:", err);
48
+ }
49
+ };
50
+ const handleStartPayment = async () => {
51
+ try {
52
+ setIsLoading(true);
53
+ setError(null);
54
+ const headers = {};
55
+ if (apiKey)
56
+ headers["x-api-key"] = apiKey;
57
+ const response = await fetch("/api/paygrid/payment-intents", {
58
+ method: "POST",
59
+ headers: {
60
+ ...headers,
61
+ "Content-Type": "application/json",
62
+ },
63
+ body: JSON.stringify({
64
+ amount,
65
+ method: propsMethod,
66
+ tokenSymbol: selectedToken,
67
+ sender: propsSender,
68
+ }),
69
+ });
70
+ // setStep("paying");
71
+ if (!response.ok) {
72
+ throw new Error(`Payment intent creation failed: ${response.statusText}`);
73
+ }
74
+ const data = (await response.json());
75
+ if (onPaymentResponse) {
76
+ onPaymentResponse(data);
77
+ }
78
+ setActiveIntent(data);
79
+ setStep("paying");
80
+ }
81
+ catch (err) {
82
+ const errorMessage = err instanceof Error ? err.message : "An error occurred";
83
+ setError(errorMessage);
84
+ console.error("Payment error:", err);
85
+ }
86
+ finally {
87
+ setIsLoading(false);
88
+ }
89
+ };
90
+ return (_jsxs("div", { children: [_jsx("button", { onClick: () => setIsCheckoutOpen(true), className: "bg-white/5 hover:bg-white/10 cursor-pointer border border-white/10 text-white px-8 py-4 rounded-2xl font-bold text-lg transition-all", children: "Pay now" }), isCheckoutOpen && (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [_jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: () => setIsCheckoutOpen(false) }), _jsxs("div", { className: "relative w-full max-w-md bg-[#111] border border-white/10 rounded-3xl overflow-hidden shadow-2xl", children: [_jsxs("div", { className: "p-6", children: [_jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-6 h-6 bg-indigo-600 rounded flex items-center justify-center text-[10px] font-bold", children: "P" }), _jsx("span", { className: "font-bold", children: "PayGrid checkout" })] }), _jsx("button", { onClick: () => setIsCheckoutOpen(false), className: "text-gray-500 cursor-pointer hover:text-white", children: "\u00D7" })] }), step === "config" && (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { className: "text-xs text-gray-500 font-bold uppercase block mb-2", children: "Select Token" }), _jsx("div", { className: "grid grid-cols-3 gap-2", children: SUPPORTED_TOKENS.map((token) => (_jsxs("button", { disabled: token.disabled, onClick: () => setSelectedToken(token.symbol), className: `p-3 rounded-xl cursor-pointer border transition-all flex flex-col items-center gap-1 ${selectedToken === token.symbol ? "border-indigo-500 bg-indigo-500/10" : "border-white/5 bg-white/5 hover:border-white/20"}`, children: [_jsx("div", { className: "w-6 h-6 rounded-full", style: { backgroundColor: token.color } }), _jsx("span", { className: "text-sm font-medium", children: token.symbol })] }, token.symbol))) })] }), _jsxs("div", { children: [_jsx("label", { className: "text-xs text-gray-500 font-bold uppercase block mb-2", children: "Payment Method" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("button", { onClick: () => setSelectedMethod(PaymentMethod.WALLET_SIGNING), className: `w-full p-4 cursor-pointer rounded-xl border text-left flex items-center gap-3 transition-all ${selectedMethod === PaymentMethod.WALLET_SIGNING ? "border-indigo-500 bg-indigo-500/10" : "border-white/5 bg-white/5"}`, children: [_jsx("div", { className: "p-2 bg-white/5 rounded-lg", children: "\u26A1" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold", children: "Wallet Signing" }), _jsx("p", { className: "text-xs text-gray-500", children: "Sign with Phantom, Solflare or Backpack" })] })] }), _jsxs("button", { onClick: () => setSelectedMethod(PaymentMethod.MANUAL_TRANSFER), disabled: true, className: `w-full p-4 cursor-pointer rounded-xl border text-left flex items-center gap-3 transition-all ${selectedMethod === PaymentMethod.MANUAL_TRANSFER ? "border-indigo-500 bg-indigo-500/10" : "border-white/5 bg-white/5"}`, children: [_jsx("div", { className: "p-2 bg-white/5 rounded-lg", children: "\uD83D\uDCCB" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold", children: "Manual Transfer" }), _jsx("p", { className: "text-xs text-gray-500", children: "Send tokens to a unique address" })] })] })] })] }), _jsxs("div", { className: "pt-4 border-t border-white/10", children: [_jsxs("div", { className: "flex justify-between items-end mb-4", children: [_jsx("span", { className: "text-gray-400 text-sm", children: "Total Due" }), _jsxs("div", { className: "text-right", children: [_jsxs("span", { className: "text-2xl font-bold", children: [amount, " ", selectedToken] }), _jsx("p", { className: "text-xs text-gray-500", children: `~${amount} ${selectedToken}` })] })] }), error && (_jsx("div", { className: "bg-red-500/10 border border-red-500/20 p-3 rounded-lg mb-4", children: _jsx("p", { className: "text-xs text-red-400", children: error }) })), _jsx("button", { onClick: handleStartPayment, disabled: isLoading, className: "w-full bg-white text-black py-4 rounded-2xl font-bold hover:bg-gray-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: isLoading ? "Processing..." : "Confirm & Pay" })] })] })), step === "paying" && (_jsxs("div", { className: "text-center py-8", children: [_jsxs("div", { className: "relative w-24 h-24 mx-auto mb-6", children: [_jsx("div", { className: "absolute inset-0 border-4 border-indigo-500/20 rounded-full" }), _jsx("div", { className: "absolute inset-0 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center font-bold text-lg", children: selectedToken })] }), _jsx("h3", { className: "text-xl font-bold mb-2", children: "Awaiting Transaction" }), _jsx("p", { className: "text-gray-500 text-sm mb-6 max-w-[280px] mx-auto", children: "Please complete the transaction in your wallet. We are monitoring the Solana network." }), _jsxs("div", { className: "bg-white/5 border border-white/10 p-4 rounded-2xl font-mono text-xs text-left", children: [_jsxs("div", { className: "flex justify-between mb-1", children: [_jsx("span", { className: "text-gray-500", children: "Network" }), _jsx("span", { children: "Solana Mainnet" })] }), _jsxs("div", { className: "flex justify-between mb-1", children: [_jsx("span", { className: "text-gray-500", children: "Status" }), _jsx("span", { className: "text-amber-400", children: "Monitoring..." })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-gray-500", children: "Intent ID" }), _jsx("span", { children: activeIntent?.id })] })] })] })), step === "success" && (_jsxs("div", { className: "text-center py-8", children: [_jsx("div", { className: "w-20 h-20 bg-emerald-500/20 text-emerald-500 rounded-full mx-auto flex items-center justify-center text-4xl mb-6", children: "\u2713" }), _jsx("h3", { className: "text-2xl font-bold mb-2", children: "Payment Settled!" }), _jsx("p", { className: "text-gray-400 text-sm mb-6", children: "Your transaction has been confirmed on the blockchain." })] }))] }), _jsx("div", { className: "bg-white/5 p-4 text-center", children: _jsx("p", { className: "text-[10px] text-gray-500 font-medium", children: "Powering the decentralized privacy economy with PayGrid" }) })] })] }))] }));
91
+ }
92
+ export default CheckoutModal;
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ const ApiKeysSection = ({ apiKey, baseUrl }) => {
5
+ const [keys, setKeys] = useState([]);
6
+ const [isLoading, setIsLoading] = useState(false);
7
+ const [newKeyData, setNewKeyData] = useState(null);
8
+ const fetchKeys = async () => {
9
+ setIsLoading(true);
10
+ try {
11
+ const headers = {};
12
+ if (apiKey)
13
+ headers['x-api-key'] = apiKey;
14
+ const res = await fetch(`${baseUrl}/api-keys`, { headers });
15
+ if (res.ok) {
16
+ const data = await res.json();
17
+ setKeys(data);
18
+ }
19
+ }
20
+ catch (error) {
21
+ console.error('Failed to fetch API keys:', error);
22
+ }
23
+ finally {
24
+ setIsLoading(false);
25
+ }
26
+ };
27
+ const createKey = async () => {
28
+ const name = prompt('Enter a name for the new API key:');
29
+ if (!name)
30
+ return;
31
+ setIsLoading(true);
32
+ try {
33
+ const headers = {
34
+ 'Content-Type': 'application/json',
35
+ };
36
+ if (apiKey)
37
+ headers['x-api-key'] = apiKey;
38
+ const res = await fetch(`${baseUrl}/api-keys`, {
39
+ method: 'POST',
40
+ headers,
41
+ body: JSON.stringify({ name }),
42
+ });
43
+ if (res.ok) {
44
+ const result = await res.json();
45
+ setNewKeyData({ key: result.key, name: result.apiKey.name });
46
+ fetchKeys();
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error('Failed to create API key:', error);
51
+ }
52
+ finally {
53
+ setIsLoading(false);
54
+ }
55
+ };
56
+ const revokeKey = async (id, name) => {
57
+ if (!confirm(`Are you sure you want to revoke the key "${name}"? This action cannot be undone.`))
58
+ return;
59
+ setIsLoading(true);
60
+ try {
61
+ const headers = {};
62
+ if (apiKey)
63
+ headers['x-api-key'] = apiKey;
64
+ const res = await fetch(`${baseUrl}/api-keys/${id}`, {
65
+ method: 'DELETE',
66
+ headers
67
+ });
68
+ if (res.ok) {
69
+ fetchKeys();
70
+ }
71
+ }
72
+ catch (error) {
73
+ console.error('Failed to revoke API key:', error);
74
+ }
75
+ finally {
76
+ setIsLoading(false);
77
+ }
78
+ };
79
+ useEffect(() => {
80
+ fetchKeys();
81
+ }, [apiKey, baseUrl]);
82
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col md:flex-row md:justify-between md:items-center gap-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-xl font-bold", children: "API Access Tokens" }), _jsx("p", { className: "text-gray-500 text-sm", children: "Generate keys to integrate PayGrid into your applications." })] }), _jsx("button", { onClick: createKey, disabled: isLoading, className: "w-full md:w-auto bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 rounded-xl text-sm font-semibold transition-colors disabled:opacity-50", children: isLoading ? 'Creating...' : '+ Create New Key' })] }), newKeyData && (_jsxs("div", { className: "bg-emerald-500/10 border border-emerald-500/20 p-6 rounded-2xl relative", children: [_jsx("button", { onClick: () => setNewKeyData(null), className: "absolute top-4 right-4 text-gray-400 hover:text-white", children: "\u2715" }), _jsx("h4", { className: "text-emerald-400 font-semibold mb-2", children: "New API Key Created!" }), _jsx("p", { className: "text-sm text-gray-400 mb-4", children: "Make sure to copy your API key now. You won't be able to see it again." }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("pre", { className: "bg-black/50 p-4 rounded-xl font-mono text-sm text-emerald-300 flex-1 overflow-x-auto", children: newKeyData.key }), _jsx("button", { onClick: () => navigator.clipboard.writeText(newKeyData.key), className: "bg-white/5 hover:bg-white/10 p-4 rounded-xl transition-colors", title: "Copy to clipboard", children: "\uD83D\uDCCB" })] })] })), _jsx("div", { className: "grid gap-4", children: isLoading && keys.length === 0 ? (_jsx("div", { className: "text-center py-12 text-gray-500", children: "Loading keys..." })) : keys.length === 0 ? (_jsx("div", { className: "text-center py-12 text-gray-500 font-medium bg-[#111] border border-white/10 rounded-2xl", children: "No API keys found. Create one to get started." })) : (keys.map(key => (_jsxs("div", { className: "bg-[#111] border border-white/10 p-4 md:p-6 rounded-2xl flex flex-col md:flex-row md:justify-between md:items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "w-10 h-10 bg-white/5 rounded-full flex-shrink-0 flex items-center justify-center", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "#888", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M21 2-2 2.23" }), _jsx("path", { d: "M7 22 22.77 6.23a2 2 0 0 0 0-2.83l-.77-.77a2 2 0 0 0-2.83 0L3.41 18.41A2 2 0 0 0 3 19.83V22h2.17a2 2 0 0 0 1.42-.59Z" }), _jsx("path", { d: "m15 5 4 4" })] }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("h4", { className: "font-semibold truncate", children: key.name }), _jsxs("p", { className: "text-xs font-mono text-gray-500 mt-1", children: [key.keyHint, "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"] })] })] }), _jsxs("div", { className: "flex items-center justify-between md:justify-end gap-6 border-t md:border-t-0 border-white/5 pt-4 md:pt-0", children: [_jsxs("div", { className: "text-left md:text-right", children: [_jsx("p", { className: "text-[10px] text-gray-500 uppercase font-bold", children: "Created" }), _jsx("p", { className: "text-sm", children: new Date(key.createdAt).toLocaleDateString() })] }), _jsx("button", { onClick: () => revokeKey(key.id, key.name), className: "text-red-400 hover:text-red-300 text-xs font-semibold p-2", children: "Revoke" })] })] }, key.id)))) }), _jsxs("div", { className: "bg-indigo-500/5 border border-indigo-500/20 p-6 rounded-2xl", children: [_jsx("h4", { className: "text-indigo-400 font-semibold mb-2", children: "Integration Guide" }), _jsx("p", { className: "text-sm text-gray-400 mb-4", children: "Initialize the SDK in your Next.js application using your API secret key." }), _jsx("pre", { className: "bg-black/50 p-4 rounded-xl font-mono text-xs text-indigo-300 overflow-x-auto", children: `const paygrid = initPayGrid({
83
+ apiKey: process.env.PAYGRID_API_KEY,
84
+ rpcUrl: 'https://api.devnet.solana.com',
85
+ network: 'devnet'
86
+ });` })] })] }));
87
+ };
88
+ export default ApiKeysSection;
@@ -1,13 +1,12 @@
1
- import React from 'react';
2
1
  export declare const SUPPORTED_TOKENS: {
3
2
  symbol: string;
4
3
  mint: string;
5
4
  color: string;
6
5
  }[];
7
6
  export declare const Icons: {
8
- Dashboard: () => React.JSX.Element;
9
- Payments: () => React.JSX.Element;
10
- Key: () => React.JSX.Element;
11
- Search: () => React.JSX.Element;
12
- External: () => React.JSX.Element;
7
+ Dashboard: () => import("react/jsx-runtime").JSX.Element;
8
+ Payments: () => import("react/jsx-runtime").JSX.Element;
9
+ Key: () => import("react/jsx-runtime").JSX.Element;
10
+ Search: () => import("react/jsx-runtime").JSX.Element;
11
+ External: () => import("react/jsx-runtime").JSX.Element;
13
12
  };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export const SUPPORTED_TOKENS = [
3
+ { symbol: 'SOL', mint: '11111111111111111111111111111111', color: '#14F195' },
4
+ { symbol: 'USDC', mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', color: '#2775CA' },
5
+ { symbol: 'BONK', mint: 'DezXAZ8z7PnrnRJjz3wXBoRgixJ6WoPBw5DRF6S49t38', color: '#FFA500' }
6
+ ];
7
+ export const Icons = {
8
+ Dashboard: () => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { width: "7", height: "9", x: "3", y: "3", rx: "1" }), _jsx("rect", { width: "7", height: "5", x: "14", y: "3", rx: "1" }), _jsx("rect", { width: "7", height: "9", x: "14", y: "12", rx: "1" }), _jsx("rect", { width: "7", height: "5", x: "3", y: "16", rx: "1" })] })),
9
+ Payments: () => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { width: "20", height: "14", x: "2", y: "5", rx: "2" }), _jsx("line", { x1: "2", x2: "22", y1: "10", y2: "10" })] })),
10
+ Key: () => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "m21 2-2 2.23" }), _jsx("path", { d: "M7 22 22.77 6.23a2 2 0 0 0 0-2.83l-.77-.77a2 2 0 0 0-2.83 0L3.41 18.41A2 2 0 0 0 3 19.83V22h2.17a2 2 0 0 0 1.42-.59Z" }), _jsx("path", { d: "m15 5 4 4" })] })),
11
+ Search: () => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.3-4.3" })] })),
12
+ External: () => (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 3h6v6" }), _jsx("path", { d: "M10 14 21 3" }), _jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" })] }))
13
+ };
@@ -1,3 +1,4 @@
1
+ import '../index.css';
1
2
  export declare function PayGridDashboard({ baseUrl, }: {
2
3
  baseUrl?: string;
3
- }): import("react").JSX.Element | null;
4
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,157 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { initWASM, isWASMSupported, ShadowWireClient } from "@radr/shadowwire";
4
+ import { clsx } from "clsx";
5
+ import { LogOut, Menu, X } from "lucide-react";
6
+ import { useEffect, useState } from "react";
7
+ import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
8
+ import { twMerge } from "tailwind-merge";
9
+ import { logout } from "../auth/actions";
10
+ import { LoginScreen } from "../auth/login-screen";
11
+ import ApiKeysSection from "./api-section";
12
+ import { Icons } from "./constant";
13
+ import PaymentsTable from "./payment-table";
14
+ // @ts-ignore
15
+ import '../index.css';
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ export function PayGridDashboard({ baseUrl = "/api/paygrid", }) {
20
+ const [activeTab, setActiveTab] = useState("overview");
21
+ const [analytics, setAnalytics] = useState(null);
22
+ const [intents, setIntents] = useState([]);
23
+ const [isLoading, setIsLoading] = useState(false);
24
+ const [isAnalyticsLoading, setIsAnalyticsLoading] = useState(false);
25
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
26
+ const [timeframe, setTimeframe] = useState(30);
27
+ const [searchQuery, setSearchQuery] = useState("");
28
+ const [isAdmin, setIsAdmin] = useState(null);
29
+ const apiKey = process.env.NEXT_PUBLIC_PAYGRID_API_SECRET;
30
+ const merchantAddress = process.env.NEXT_PUBLIC_MERCHANT_WALLET_ADDRESS;
31
+ const [client] = useState(() => new ShadowWireClient());
32
+ const [wasmInitialized, setWasmInitialized] = useState(false);
33
+ const [balance, setBalance] = useState(null);
34
+ const fetchAnalytics = async (showLoading = false) => {
35
+ if (showLoading)
36
+ setIsAnalyticsLoading(true);
37
+ try {
38
+ const headers = {};
39
+ if (apiKey)
40
+ headers["x-api-key"] = apiKey;
41
+ const res = await fetch(`${baseUrl}/analytics?days=${timeframe}`, {
42
+ headers,
43
+ });
44
+ if (res.ok) {
45
+ const stats = await res.json();
46
+ setAnalytics(stats);
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error("Failed to fetch analytics:", error);
51
+ }
52
+ finally {
53
+ if (showLoading)
54
+ setIsAnalyticsLoading(false);
55
+ }
56
+ };
57
+ const fetchPayments = async () => {
58
+ try {
59
+ const headers = {};
60
+ if (apiKey)
61
+ headers["x-api-key"] = apiKey;
62
+ const res = await fetch(`${baseUrl}/payments`, { headers });
63
+ if (res.ok) {
64
+ const list = await res.json();
65
+ setIntents(list);
66
+ }
67
+ }
68
+ catch (error) {
69
+ console.error("Failed to fetch payments:", error);
70
+ }
71
+ };
72
+ useEffect(() => {
73
+ async function init() {
74
+ if (!isWASMSupported()) {
75
+ // setError('WebAssembly not supported');
76
+ return;
77
+ }
78
+ try {
79
+ await initWASM("/wasm/settler_wasm_bg.wasm");
80
+ setWasmInitialized(true);
81
+ await loadBalance();
82
+ }
83
+ catch (err) {
84
+ // setError('Initialization failed: ' + err.message);
85
+ }
86
+ }
87
+ init();
88
+ }, []);
89
+ console.log(balance, "wallet balance");
90
+ const loadBalance = async () => {
91
+ try {
92
+ const data = await client.getBalance(merchantAddress ?? "", "USDC");
93
+ setBalance(data.available / 1e9);
94
+ }
95
+ catch (err) {
96
+ console.error("Balance load failed:", err);
97
+ }
98
+ };
99
+ // Initial load for payments
100
+ useEffect(() => {
101
+ setIsLoading(true);
102
+ fetchPayments().then(() => setIsLoading(false));
103
+ }, [apiKey, baseUrl]);
104
+ // Analytics load (including timeframe changes)
105
+ useEffect(() => {
106
+ fetchAnalytics(true);
107
+ // Check admin session cookie on client side
108
+ const hasSession = document.cookie.includes("paygrid_admin_session=true");
109
+ setIsAdmin(hasSession);
110
+ }, [apiKey, baseUrl, timeframe]);
111
+ const filteredIntents = intents.filter((intent) => {
112
+ const query = searchQuery.toLowerCase();
113
+ return (intent.id.toLowerCase().includes(query) ||
114
+ intent.status.toLowerCase().includes(query) ||
115
+ intent.amount.toString().includes(query) ||
116
+ (intent.walletAddress &&
117
+ intent.walletAddress.toLowerCase().includes(query)));
118
+ });
119
+ const renderContent = () => {
120
+ switch (activeTab) {
121
+ case "overview":
122
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-3 md:grid-cols-3 gap-2 md:gap-6", children: [_jsxs("div", { className: "bg-[#111] border border-white/10 p-6 rounded-2xl relative overflow-hidden", children: [isAnalyticsLoading && (_jsx("div", { className: "absolute inset-0 bg-black/20 backdrop-blur-[1px] flex items-center justify-center z-10", children: _jsx("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }) })), _jsx("p", { className: "text-gray-400 text-sm font-medium", children: "Total Volume" }), _jsxs("h3", { className: "text-3xl font-bold mt-1 text-white", children: ["$", analytics?.totalRevenue.toFixed(2)] }), _jsxs("p", { className: cn("text-xs mt-2", (analytics?.revenueGrowth || 0) >= 0
123
+ ? "text-emerald-400"
124
+ : "text-red-400"), children: [(analytics?.revenueGrowth || 0) >= 0 ? "↑" : "↓", " ", Math.abs(analytics?.revenueGrowth || 0).toFixed(1), "% from last ", timeframe === 30 ? "month" : "week"] })] }), _jsxs("div", { className: "bg-[#111] border border-white/10 p-6 rounded-2xl relative overflow-hidden", children: [isAnalyticsLoading && (_jsx("div", { className: "absolute inset-0 bg-black/20 backdrop-blur-[1px] flex items-center justify-center z-10", children: _jsx("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }) })), _jsx("p", { className: "text-gray-400 text-sm font-medium", children: "Payment Intents" }), _jsx("h3", { className: "text-3xl font-bold mt-1 text-white", children: analytics?.transactionCount }), _jsxs("p", { className: "text-gray-500 text-xs mt-2", children: ["Past ", timeframe, " days"] })] }), _jsxs("div", { className: "bg-[#111] border border-white/10 p-6 rounded-2xl relative overflow-hidden", children: [isAnalyticsLoading && (_jsx("div", { className: "absolute inset-0 bg-black/20 backdrop-blur-[1px] flex items-center justify-center z-10", children: _jsx("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }) })), _jsx("p", { className: "text-gray-400 text-sm font-medium", children: "Settlement Rate" }), _jsxs("h3", { className: "text-3xl font-bold mt-1 text-white", children: [analytics?.settlementRate.toFixed(1), "%"] }), _jsx("p", { className: "text-emerald-400 text-xs mt-2", children: "High performance" })] })] }), _jsxs("div", { className: "bg-[#111] border border-white/10 p-6 rounded-2xl h-[400px] relative overflow-hidden", children: [isAnalyticsLoading && (_jsx("div", { className: "absolute inset-0 bg-black/20 backdrop-blur-[1px] flex items-center justify-center z-10", children: _jsx("div", { className: "w-8 h-8 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }) })), _jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsx("h3", { className: "font-semibold text-lg", children: "Revenue Over Time" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { onClick: () => setTimeframe(7), className: cn("text-xs px-3 py-1 rounded-full transition-all border", timeframe === 7
125
+ ? "bg-indigo-500/20 border-indigo-500/30 text-indigo-400"
126
+ : "bg-white/5 border-white/10 text-gray-400 hover:bg-white/10"), children: "7D" }), _jsx("button", { onClick: () => setTimeframe(30), className: cn("text-xs px-3 py-1 rounded-full transition-all border", timeframe === 30
127
+ ? "bg-indigo-500/20 border-indigo-500/30 text-indigo-400"
128
+ : "bg-white/5 border-white/10 text-gray-400 hover:bg-white/10"), children: "30D" })] })] }), _jsx(ResponsiveContainer, { width: "100%", height: "80%", minWidth: 0, children: _jsxs(AreaChart, { data: analytics?.history || [], children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "colorAmt", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "5%", stopColor: "#6366f1", stopOpacity: 0.3 }), _jsx("stop", { offset: "95%", stopColor: "#6366f1", stopOpacity: 0 })] }) }), _jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "#222", vertical: false }), _jsx(XAxis, { dataKey: "date", stroke: "#444", fontSize: 12, tickLine: false, axisLine: false }), _jsx(YAxis, { stroke: "#444", fontSize: 12, tickLine: false, axisLine: false }), _jsx(Tooltip, { contentStyle: {
129
+ backgroundColor: "#111",
130
+ border: "1px solid #333",
131
+ borderRadius: "8px",
132
+ }, itemStyle: { color: "#fff" } }), _jsx(Area, { type: "monotone", dataKey: "amount", stroke: "#6366f1", fillOpacity: 1, fill: "url(#colorAmt)", strokeWidth: 2 })] }) })] }), _jsxs("div", { className: "bg-[#111] border border-white/10 rounded-2xl overflow-hidden", children: [_jsxs("div", { className: "px-6 py-4 border-b border-white/10 flex justify-between items-center", children: [_jsx("h3", { className: "font-semibold", children: "Recent Activity" }), _jsx("button", { onClick: () => setActiveTab("payments"), className: "text-xs text-indigo-400 hover:text-indigo-300", children: "View all" })] }), _jsx(PaymentsTable, { intents: filteredIntents.slice(0, 5) })] })] }));
133
+ case "payments":
134
+ return _jsx(PaymentsTable, { intents: filteredIntents, isFullPage: true });
135
+ case "apikeys":
136
+ return _jsx(ApiKeysSection, { apiKey: apiKey, baseUrl: baseUrl });
137
+ }
138
+ };
139
+ if (isAdmin === null) {
140
+ return null; // or loading spinner
141
+ }
142
+ if (!isAdmin) {
143
+ return _jsx(LoginScreen, {});
144
+ }
145
+ return (_jsxs("div", { className: "flex min-h-screen", children: [isSidebarOpen && (_jsx("div", { className: "fixed inset-0 bg-black/60 backdrop-blur-sm z-30 lg:hidden", onClick: () => setIsSidebarOpen(false) })), _jsxs("aside", { className: cn("w-64 border-r border-white/10 flex flex-col fixed h-full bg-[#050505] z-40 transition-transform duration-300 ease-in-out", isSidebarOpen
146
+ ? "translate-x-0"
147
+ : "-translate-x-full lg:translate-x-0"), children: [_jsxs("div", { className: "p-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-8", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center font-bold text-lg", children: "P" }), _jsx("span", { className: "font-bold text-xl tracking-tight", children: "PayGrid" })] }), _jsx("button", { className: "lg:hidden text-gray-400 hover:text-white", onClick: () => setIsSidebarOpen(false), children: _jsx(X, { size: 20 }) })] }), _jsxs("nav", { className: "space-y-1", children: [_jsxs("button", { onClick: () => {
148
+ setActiveTab("overview");
149
+ setIsSidebarOpen(false);
150
+ }, className: `w-full cursor-pointer flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${activeTab === "overview" ? "bg-white/10 text-white" : "text-gray-400 hover:text-white hover:bg-white/5"}`, children: [_jsx(Icons.Dashboard, {}), " Overview"] }), _jsxs("button", { onClick: () => {
151
+ setActiveTab("payments");
152
+ setIsSidebarOpen(false);
153
+ }, className: `w-full cursor-pointer flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${activeTab === "payments" ? "bg-white/10 text-white" : "text-gray-400 hover:text-white hover:bg-white/5"}`, children: [_jsx(Icons.Payments, {}), " Payments"] }), _jsxs("button", { onClick: () => {
154
+ setActiveTab("apikeys");
155
+ setIsSidebarOpen(false);
156
+ }, className: `w-full cursor-pointer flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${activeTab === "apikeys" ? "bg-white/10 text-white" : "text-gray-400 hover:text-white hover:bg-white/5"}`, children: [_jsx(Icons.Key, {}), " API Keys"] })] })] }), _jsxs("div", { className: "mt-auto p-6 border-t border-white/10", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("img", { src: "https://picsum.photos/32/32", className: "w-8 h-8 rounded-full", alt: "User" }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-sm font-medium", children: "Merchant Account" }), _jsx("span", { className: "text-xs text-gray-500", children: "Live Mode" })] })] }), _jsxs("button", { onClick: logout, className: "w-full cursor-pointer flex items-center gap-2 mt-4 px-2 py-2 text-red-400 hover:text-red-300 hover:bg-white/5 rounded-lg transition-all text-sm font-medium", children: [_jsx(LogOut, { size: 16 }), " Sign Out"] })] })] }), _jsxs("main", { className: "flex-1 lg:ml-64 p-4 md:p-8 pt-20 lg:pt-8 w-full bg-[#050505] overflow-hidden", children: [_jsxs("header", { className: "flex flex-col md:flex-row md:justify-between md:items-center mb-8 gap-4", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("button", { className: "lg:hidden p-2 bg-[#111] border border-white/10 rounded-lg text-gray-400", onClick: () => setIsSidebarOpen(true), children: _jsx(Menu, { size: 20 }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl md:text-2xl font-bold capitalize", children: activeTab }), _jsx("p", { className: "text-gray-500 text-xs md:text-sm", children: "Manage your Solana payments infrastructure" })] })] }), _jsx("div", { className: "flex gap-3 w-full md:w-auto", children: _jsxs("div", { className: "relative w-full md:w-auto", children: [_jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-500", children: _jsx(Icons.Search, {}) }), _jsx("input", { type: "text", placeholder: "Search intents...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full bg-[#111] border border-white/10 rounded-xl pl-10 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/50" })] }) })] }), isLoading ? (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsx("div", { className: "w-8 h-8 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }) })) : (renderContent())] })] }));
157
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { PaymentStatus } from '../types';
4
+ import { Icons } from './constant';
5
+ const statusColors = {
6
+ [PaymentStatus.SETTLED]: 'text-emerald-400 bg-emerald-400/10 border-emerald-400/20',
7
+ [PaymentStatus.PENDING_CONFIRMATION]: 'text-amber-400 bg-amber-400/10 border-amber-400/20',
8
+ [PaymentStatus.AWAITING_PAYMENT]: 'text-indigo-400 bg-indigo-400/10 border-indigo-400/20',
9
+ [PaymentStatus.CREATED]: 'text-gray-400 bg-gray-400/10 border-gray-400/20',
10
+ [PaymentStatus.EXPIRED]: 'text-red-400 bg-red-400/10 border-red-400/20',
11
+ [PaymentStatus.FAILED]: 'text-red-500 bg-red-500/10 border-red-500/20',
12
+ };
13
+ const PaymentsTable = ({ intents, isFullPage }) => {
14
+ return (_jsxs("div", { className: `overflow-x-auto ${isFullPage ? 'bg-[#111] border border-white/10 rounded-2xl' : ''}`, children: [_jsxs("table", { className: "hidden md:table w-full text-left text-sm min-w-[800px]", children: [_jsx("thead", { className: "border-b border-white/10 text-gray-400 font-medium", children: _jsxs("tr", { children: [_jsx("th", { className: "px-6 py-4", children: "Status" }), _jsx("th", { className: "px-6 py-4", children: "Amount" }), _jsx("th", { className: "px-6 py-4", children: "Intent ID" }), _jsx("th", { className: "px-6 py-4", children: "Destination" }), _jsx("th", { className: "px-6 py-4", children: "Created" }), _jsx("th", { className: "px-6 py-4 text-right", children: "Action" })] }) }), _jsxs("tbody", { className: "divide-y divide-white/5", children: [intents?.map((intent) => (_jsxs("tr", { className: "hover:bg-white/[0.02] transition-colors", children: [_jsx("td", { className: "px-6 py-4", children: _jsx("span", { className: `px-2 py-1 rounded-full text-[10px] uppercase font-bold border ${statusColors[intent.status]}`, children: intent.status.replace('_', ' ') }) }), _jsxs("td", { className: "px-6 py-4 font-mono font-medium", children: [intent.amount, " ", intent.tokenSymbol] }), _jsx("td", { className: "px-6 py-4 text-gray-400 font-mono text-xs", children: intent.id }), _jsx("td", { className: "px-6 py-4 text-gray-500 text-xs font-mono", children: intent?.walletAddress ? `${intent.walletAddress.slice(0, 8)}...${intent.walletAddress.slice(-8)}` : 'Main Treasury' }), _jsx("td", { className: "px-6 py-4 text-gray-500 whitespace-nowrap text-xs", children: new Date(intent.createdAt).toLocaleString() }), _jsx("td", { className: "px-6 py-4 text-right", children: _jsx("button", { className: "p-2 hover:bg-white/5 rounded-lg text-gray-400 hover:text-white transition-colors", children: _jsx(Icons.External, {}) }) })] }, intent.id))), intents?.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 6, className: "px-6 py-12 text-center text-gray-500", children: "No payment intents found." }) }))] })] }), _jsxs("div", { className: "md:hidden divide-y divide-white/5", children: [intents?.map((intent) => (_jsxs("div", { className: "p-4 space-y-3", children: [_jsxs("div", { className: "flex justify-between items-center", children: [_jsx("span", { className: `px-2 py-0.5 rounded-full text-[10px] uppercase font-bold border ${statusColors[intent.status]}`, children: intent.status.replace('_', ' ') }), _jsx("p", { className: "text-gray-500 text-[10px]", children: new Date(intent.createdAt).toLocaleString() })] }), _jsxs("div", { className: "flex justify-between items-end", children: [_jsxs("div", { children: [_jsxs("h4", { className: "text-lg font-bold text-white", children: [intent.amount, " ", intent.tokenSymbol] }), _jsxs("p", { className: "text-xs text-gray-500 font-mono mt-1", children: [intent.id.slice(0, 18), "..."] })] }), _jsx("button", { className: "p-2 bg-white/5 rounded-lg text-gray-400", children: _jsx(Icons.External, {}) })] })] }, intent.id))), intents?.length === 0 && (_jsx("div", { className: "px-6 py-12 text-center text-gray-500", children: "No payment intents found." }))] })] }));
15
+ };
16
+ export default PaymentsTable;