VTS(H
Vu Thanh Son (K17 HCM)

Wallet Dashboard - Copy this React, Tailwind Component to your project

Import { useState, useEffect } from "react"; import { Card, Spin, Statistic, Table, Tag, Form, InputNumber, Select, Button, message, Row, Col, Input } from "antd"; import { motion } from "framer motion"; import getPublisherBalance from "../../../../modules/PublisherBalance"; // Thay bằng đường dẫn thực tế import getPayoutRequestsByPublisher from "../../../../modules/PublisherBalance/partials/getPayoutRequestsByPublisher"; import { createPayoutRequest } from "../../../../modules/PublisherBalance/partials/createPayoutRequest"; // Adjust path as needed import { createDepositRequest } from "../../../../modules/PublisherBalance/partials/createDepositRequest"; // Adjust path as needed const { Option } = Select; export default function WalletDashboard({ publisherId = 1 }) { const [walletData, setWalletData] = useState(null); const [payoutRequests, setPayoutRequests] = useState([]); const [loading, setLoading] = useState(true); const [payoutLoading, setPayoutLoading] = useState(true); const [payoutSubmitting, setPayoutSubmitting] = useState(false); const [depositSubmitting, setDepositSubmitting] = useState(false); const [payoutSearch, setPayoutSearch] = useState(""); const [payoutForm] = Form.useForm(); const [depositForm] = Form.useForm(); useEffect(() => { const fetchWalletData = async () => { try { setLoading(true); const data = await getPublisherBalance(); if (data) { setWalletData(data[0]); } setLoading(false); } catch (error) { console.error("Error fetching wallet data:", error); setLoading(false); } }; const fetchPayoutRequests = async () => { try { setPayoutLoading(true); const data = await getPayoutRequestsByPublisher(publisherId); if (data) { setPayoutRequests(data); } setPayoutLoading(false); } catch (error) { console.error("Error fetching payout requests:", error); setPayoutLoading(false); } }; fetchWalletData(); fetchPayoutRequests(); }, [publisherId]); const formatCurrency = (amount) => { return new Intl.NumberFormat("vi VN", { style: "currency", currency: "VND", }).format(amount); }; const formatDynamicCurrency = (amount, currencyCode) => { if (currencyCode === "VND") { return new Intl.NumberFormat("vi VN", { style: "currency", currency: "VND", }).format(amount); } else if (currencyCode === "USD") { return new Intl.NumberFormat("en US", { style: "currency", currency: "USD", }).format(amount); } return amount; }; // Xử lý gửi yêu cầu rút tiền const handlePayoutRequest = async (values) => { if (!walletData) { message.error("Không thể gửi yêu cầu: Dữ liệu ví không khả dụng."); return; } if (values.amount > walletData.availableBalance) { message.error("Số tiền yêu cầu vượt quá số dư khả dụng!"); return; } setPayoutSubmitting(true); try { const payoutData = { amount: values.amount, currencyCode: values.currencyCode, publisherId: parseInt(publisherId), }; const result = await createPayoutRequest(payoutData); if (result) { const updatedPayouts = await getPayoutRequestsByPublisher(publisherId); setPayoutRequests(updatedPayouts); const updatedWallet = await getPublisherBalance(); if (updatedWallet) { setWalletData(updatedWallet[0]); } payoutForm.resetFields(); } } catch (error) { console.error("Error submitting payout request:", error); } finally { setPayoutSubmitting(false); } }; // Xử lý gửi yêu cầu nạp tiền const handleDepositRequest = async (values) => { setDepositSubmitting(true); try { const depositData = { amount: values.amount, currencyCode: values.currencyCode, publisherId: parseInt(publisherId), paymentMethod: values.paymentMethod, }; const result = await createDepositRequest(depositData); if (result) { const updatedWallet = await getPublisherBalance(); if (updatedWallet) { setWalletData(updatedWallet[0]); } depositForm.resetFields(); } } catch (error) { console.error("Error submitting deposit request:", error); } finally { setDepositSubmitting(false); } }; const payoutColumns = [ { title: "Mã yêu cầu", dataIndex: "requestId", key: "requestId", width: 120, }, { title: "Nhà xuất bản", dataIndex: "publisherName", key: "publisherName", width: 200, }, { title: "Số tiền", dataIndex: "amount", key: "amount", render: (amount, record) => formatDynamicCurrency(amount, record.currencyCode), width: 150, }, { title: "Ngày yêu cầu", dataIndex: "requestDate", key: "requestDate", render: (date) => new Date(date).toLocaleDateString("vi VN"), width: 150, }, { title: "Trạng thái", dataIndex: "status", key: "status", render: (status) => ( <Tag color={status === "Pending" ? "orange" : "green"}>{status}</Tag> ), width: 120, }, ]; // Lọc payout requests theo tìm kiếm const filteredPayoutRequests = payoutRequests.filter((request) => request.requestId.toString().includes(payoutSearch) || request.publisherName?.toLowerCase().includes(payoutSearch.toLowerCase()) ); return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.5, duration: 0.8 }} className="p 6 bg gray 100 min h screen" > <h1 className="text 3xl font bold mb 6 text gray 800 text center"> Dashboard Ví </h1> {/* Tổng quan tài chính */} {loading ? ( <div className="text center"> <Spin size="large" /> </div> ) : walletData ? ( <Row gutter={[16, 16]} className="mb 6"> <Col xs={24} md={8}> <Card className="hover:shadow md transition shadow duration 200"> <Statistic title="Số dư khả dụng" value={walletData.availableBalance} formatter={(value) => formatCurrency(value)} valueStyle={{ color: "#2ecc71" }} /> </Card> </Col> <Col xs={24} md={8}> <Card className="hover:shadow md transition shadow duration 200"> <Statistic title="Số dư đang chờ" value={walletData.pendingBalance} formatter={(value) => formatCurrency(value)} valueStyle={{ color: "#f1c40f" }} /> </Card> </Col> <Col xs={24} md={8}> <Card className="hover:shadow md transition shadow duration 200"> <Statistic title="Tổng thu nhập" value={walletData.lifetimeEarnings} formatter={(value) => formatCurrency(value)} valueStyle={{ color: "#3498db" }} /> </Card> </Col> </Row> ) : ( <div className="text center text gray 500"> Không thể tải dữ liệu ví </div> )} {walletData && ( <div className="text right text gray 600 mb 6"> Cập nhật lần cuối:{" "} {new Date(walletData.lastUpdated).toLocaleDateString("vi VN")} </div> )} {/* Form nạp tiền và rút tiền */} <Row gutter={[16, 16]} className="mb 6"> {/* Form nạp tiền */} <Col xs={24} md={12}> <h2 className="text xl font semibold mb 4 text gray 800"> Nạp Tiền </h2> <Card className="p 4"> <Form form={depositForm} layout="vertical" onFinish={handleDepositRequest} initialValues={{ currencyCode: "VND", paymentMethod: "BankTransfer", }} > <Form.Item label="Số tiền" name="amount" rules={[{ required: true, message: "Vui lòng nhập số tiền!" }]} > <InputNumber min={1} style={{ width: "100%" }} formatter={(value) => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} parser={(value) => value.replace(/\$\s?|(,*)/g, "")} /> </Form.Item> <Form.Item label="Loại tiền tệ" name="currencyCode" rules={[{ required: true, message: "Vui lòng chọn loại tiền tệ!" }]} > <Select> <Option value="VND">VND</Option> <Option value="USD">USD</Option> </Select> </Form.Item> <Form.Item label="Phương thức thanh toán" name="paymentMethod" rules={[{ required: true, message: "Vui lòng chọn phương thức thanh toán!" }]} > <Select> <Option value="BankTransfer">Chuyển khoản ngân hàng</Option> <Option value="PayPal">PayPal</Option> <Option value="Momo">Momo</Option> </Select> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit" loading={depositSubmitting} block > Gửi Yêu Cầu Nạp Tiền </Button> </Form.Item> </Form> </Card> </Col> {/* Form rút tiền */} <Col xs={24} md={12}> <h2 className="text xl font semibold mb 4 text gray 800"> Rút Tiền </h2> <Card className="p 4"> <Form form={payoutForm} layout="vertical" onFinish={handlePayoutRequest} initialValues={{ currencyCode: "VND", }} > <Form.Item label="Số tiền" name="amount" rules={[{ required: true, message: "Vui lòng nhập số tiền!" }]} > <InputNumber min={1} style={{ width: "100%" }} formatter={(value) => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} parser={(value) => value.replace(/\$\s?|(,*)/g, "")} /> </Form.Item> <Form.Item label="Loại tiền tệ" name="currencyCode" rules={[{ required: true, message: "Vui lòng chọn loại tiền tệ!" }]} > <Select> <Option value="VND">VND</Option> <Option value="USD">USD</Option> </Select> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit" loading={payoutSubmitting} block > Gửi Yêu Cầu Rút Tiền </Button> </Form.Item> </Form> </Card> </Col> </Row> {/* Danh sách yêu cầu thanh toán */} <div className="mb 6"> <h2 className="text xl font semibold mb 4 text gray 800"> Yêu Cầu Thanh Toán </h2> <Card className="p 4"> <Input placeholder="Tìm kiếm theo mã yêu cầu hoặc nhà xuất bản..." value={payoutSearch} onChange={(e) => setPayoutSearch(e.target.value)} className="mb 4 max w sm" /> {payoutLoading ? ( <div className="text center"> <Spin size="large" /> </div> ) : filteredPayoutRequests.length > 0 ? ( <Table columns={payoutColumns} dataSource={filteredPayoutRequests} rowKey="requestId" pagination={{ pageSize: 5 }} scroll={{ x: "max content" }} /> ) : ( <div className="text center text gray 500"> Không có yêu cầu thanh toán nào </div> )} </Card> </div> </motion.div> ); } làm lại UI này cho đẹp hơn đi tôi muốn là 1 cái ví thật đẹp

Prompt
Component Preview

About

WalletDashboard - A sleek financial hub for publishers, featuring real-time balance stats, payout requests, and deposit forms, built wit. Access free code!

Share

Last updated 1 month ago