From 455ad15c8f56ef42edf2ae9161585f98c9943793 Mon Sep 17 00:00:00 2001
From: Yuxuan Cai <1093369126@qq.com>
Date: Tue, 2 Jul 2024 12:45:23 -0400
Subject: [PATCH] payment page
---
package-lock.json | 9 +-
package.json | 5 +-
src/App.js | 2 +
src/components/Sidebar.js | 161 ++++++++++++++++++++-------------
src/index.css | 4 +
src/pages/Payment.js | 185 ++++++++++++++++++++++++++++++++++++++
tailwind.config.js | 12 +++
7 files changed, 314 insertions(+), 64 deletions(-)
create mode 100644 src/pages/Payment.js
create mode 100644 tailwind.config.js
diff --git a/package-lock.json b/package-lock.json
index 56d165d..15f38e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,9 @@
"socket.io-client": "^4.7.5",
"styled-components": "^6.1.8",
"web-vitals": "^2.1.4"
+ },
+ "devDependencies": {
+ "tailwindcss": "^3.4.4"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -18722,9 +18725,9 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
},
"node_modules/tailwindcss": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
- "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
+ "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
diff --git a/package.json b/package.json
index 5c4b2b6..7c7162b 100644
--- a/package.json
+++ b/package.json
@@ -15,8 +15,8 @@
"i18next": "^23.11.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "^9.1.2",
"react-i18next": "^14.1.1",
+ "react-redux": "^9.1.2",
"react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
"socket.io-client": "^4.7.5",
@@ -46,5 +46,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "tailwindcss": "^3.4.4"
}
}
diff --git a/src/App.js b/src/App.js
index d103282..cfc55c8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -3,6 +3,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import Main from "./pages/Main.js";
import Register from "./pages/Register.js";
import Login from "./pages/Login.js";
+import Payment from "./pages/Payment.js";
function App() {
return (
// already in index.js
@@ -10,6 +11,7 @@ function App() {
} />
} />
} />
+ } />
//
);
diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js
index 34ed2f5..3d73003 100644
--- a/src/components/Sidebar.js
+++ b/src/components/Sidebar.js
@@ -9,8 +9,19 @@ import {
HistoryOutlined,
GoogleOutlined,
LogoutOutlined,
+ SendOutlined,
} from "@ant-design/icons";
-import { Avatar, Button, Layout, List, Menu, Skeleton, Input, Space, ConfigProvider } from "antd";
+import {
+ Avatar,
+ Button,
+ Layout,
+ List,
+ Menu,
+ Skeleton,
+ Input,
+ Space,
+ ConfigProvider,
+} from "antd";
import { onAuthStateChanged, signOut } from "firebase/auth";
import { auth } from "../firebase-config.js";
import { useNavigate } from "react-router-dom";
@@ -18,11 +29,12 @@ import { useCollapsed } from "./Contexts.js";
import GoogleSignIn from "./GoogleSignIn.js";
import { useTranslation } from "react-i18next";
import i18n from "../translations/i18n.js";
-import { useSelector, useDispatch } from 'react-redux'
-import { setUser, delUser } from './userSlice.js'
-import { addChat, setChat, logoutChat } from './chatSlice.js'
-import { changeChat, logoutMsg } from './messageSlice.js'
+import { useSelector, useDispatch } from "react-redux";
+import { setUser, delUser } from "./userSlice.js";
+import { addChat, setChat, logoutChat } from "./chatSlice.js";
+import { changeChat, logoutMsg } from "./messageSlice.js";
import axios from "axios";
+import Payment from "../pages/Payment.js";
const { Sider } = Layout;
function getItem(label, key, icon, children) {
@@ -58,43 +70,43 @@ const Sidebar = () => {
return () => unsubscribe();
}, []);
- const updateChat = async ()=>{
- try{
+ const updateChat = async () => {
+ try {
const resp = await axios.get(
- process.env.REACT_APP_DB_URL + "chats/" + user
- )
+ process.env.REACT_APP_DB_URL + "chats/" + user,
+ );
dispatch(setChat(resp.data.data));
return resp.data.data;
- }catch(e){
+ } catch (e) {
console.log("Load chat history from server failed");
- console.log(e)
+ console.log(e);
}
- }
- useEffect(()=>{
+ };
+ useEffect(() => {
// fetch chat list from database when user login
- if (!user) return
+ if (!user) return;
- updateChat().then(()=>{});
-
- }, [user])
+ updateChat().then(() => {});
+ }, [user]);
const [chatNameIpt, setChatNameIpt] = useState("");
- const handleAddChat = (chatName) => {
- if (!chatName) return
- if (!user) return
- axios.post(process.env.REACT_APP_DB_URL + 'chats', {
- 'userId': user,
- 'chatName': chatName,
- }).then(()=>{
- updateChat().then((resp)=>{
- const chatIds = resp.map((x)=>x.chatId);
- const newId = chatIds.reduce((x,y)=>Math.max(x,y), -Infinity);
- dispatch(changeChat(newId));
- setChatNameIpt("");
+ const handleAddChat = (chatName) => {
+ if (!chatName) return;
+ if (!user) return;
+ axios
+ .post(process.env.REACT_APP_DB_URL + "chats", {
+ userId: user,
+ chatName: chatName,
+ })
+ .then(() => {
+ updateChat().then((resp) => {
+ const chatIds = resp.map((x) => x.chatId);
+ const newId = chatIds.reduce((x, y) => Math.max(x, y), -Infinity);
+ dispatch(changeChat(newId));
+ setChatNameIpt("");
+ });
});
-
- })
- }
+ };
const items = [
getItem(t("History"), "1", ),
@@ -126,10 +138,13 @@ const Sidebar = () => {
}
};
-
const handleSignOut = () => {
signOut(auth).catch((error) => console.error("Error signing out: ", error));
};
+ const handleSubscribe = () => {
+ window.open("/payment", "_blank");
+ };
+
return (
{
items={items}
onClick={handleMenuClick}
/> */}
- {handleAddChat(val)}} value={chatNameIpt} onChange={(e)=>{setChatNameIpt(e.target.value)}} disabled={user == null} style={{display: user==null?'none':''}} />
-
+ {
+ handleAddChat(val);
+ }}
+ value={chatNameIpt}
+ onChange={(e) => {
+ setChatNameIpt(e.target.value);
+ }}
+ disabled={user == null}
+ style={{ display: user == null ? "none" : "" }}
+ />
+
-
}
- dataSource={chats}
- renderItem={(item, idx) => (
-
-
-
- )}
- />
+ },
+ },
+ }}
+ >
+
}
+ dataSource={chats}
+ renderItem={(item, idx) => (
+
+
+
+ )}
+ />
{/* Sidebar content*/}
{/**/}
{/* */}
+
);
};
diff --git a/src/index.css b/src/index.css
index ec2585e..17df0e7 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,3 +1,7 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
diff --git a/src/pages/Payment.js b/src/pages/Payment.js
new file mode 100644
index 0000000..2d7df1b
--- /dev/null
+++ b/src/pages/Payment.js
@@ -0,0 +1,185 @@
+import React, { useState, useEffect } from "react";
+
+const Payment = () => {
+ const [selectedPlan, setSelectedPlan] = useState("yearly");
+ const [cardNumber, setCardNumber] = useState("");
+ const [expiry, setExpiry] = useState("");
+ const [cvv, setCvv] = useState("");
+ const [postalCode, setPostalCode] = useState("");
+ const [errors, setErrors] = useState({});
+
+ const handlePlanChange = (event) => {
+ setSelectedPlan(event.target.id);
+ };
+
+ const validateInputs = () => {
+ const newErrors = {};
+ if (!/^\d{16}$/.test(cardNumber.replace(/\s+/g, ""))) {
+ newErrors.cardNumber = "Card number must be 16 digits";
+ }
+ if (!/^(0[1-9]|1[0-2])\/([0-9]{2})$/.test(expiry)) {
+ newErrors.expiry = "Expiry date must be in MM/YY format";
+ }
+ if (!/^\d{3,4}$/.test(cvv)) {
+ newErrors.cvv = "CVV must be 3 or 4 digits";
+ }
+ if (!/^\d{5}$/.test(postalCode)) {
+ newErrors.postalCode = "Postal code must be 5 digits";
+ }
+ return newErrors;
+ };
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ const newErrors = validateInputs();
+ if (Object.keys(newErrors).length === 0) {
+ alert("Payment processed successfully!");
+ // Process payment here
+ } else {
+ setErrors(newErrors);
+ }
+ };
+
+ useEffect(() => {
+ setErrors(validateInputs());
+ }, [cardNumber, expiry, cvv, postalCode]);
+
+ const formatCardNumber = (value) => {
+ const cleaned = value.replace(/\D/g, "");
+ const match = cleaned.match(/.{1,4}/g);
+ return match ? match.join(" ") : value;
+ };
+
+ return (
+
+
+
Order Summary
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Payment;
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..fdb91b2
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,12 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
+ darkMode: false,
+ theme: {
+ extend: {},
+ },
+ variants: {
+ extend: {},
+ },
+ plugins: [],
+};