2022-02-27 23:50:35 +08:00
|
|
|
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
import React from "react";
|
2022-12-12 00:42:45 +08:00
|
|
|
import {Button, Descriptions, Modal, Spin} from "antd";
|
|
|
|
import {CheckCircleTwoTone} from "@ant-design/icons";
|
2022-02-27 23:50:35 +08:00
|
|
|
import i18next from "i18next";
|
|
|
|
import * as ProductBackend from "./backend/ProductBackend";
|
2022-03-06 22:46:02 +08:00
|
|
|
import * as Setting from "./Setting";
|
2022-02-27 23:50:35 +08:00
|
|
|
|
|
|
|
class ProductBuyPage extends React.Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
classes: props,
|
|
|
|
productName: props.match?.params.productName,
|
|
|
|
product: null,
|
2022-03-07 00:33:45 +08:00
|
|
|
isPlacingOrder: false,
|
2022-12-12 00:42:45 +08:00
|
|
|
qrCodeModalProvider: null,
|
2022-02-27 23:50:35 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
UNSAFE_componentWillMount() {
|
|
|
|
this.getProduct();
|
|
|
|
}
|
|
|
|
|
|
|
|
getProduct() {
|
|
|
|
ProductBackend.getProduct("admin", this.state.productName)
|
|
|
|
.then((product) => {
|
|
|
|
this.setState({
|
|
|
|
product: product,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getProductObj() {
|
|
|
|
if (this.props.product !== undefined) {
|
|
|
|
return this.props.product;
|
|
|
|
} else {
|
|
|
|
return this.state.product;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getCurrencySymbol(product) {
|
|
|
|
if (product?.currency === "USD") {
|
|
|
|
return "$";
|
|
|
|
} else if (product?.currency === "CNY") {
|
|
|
|
return "¥";
|
|
|
|
} else {
|
|
|
|
return "(Unknown currency)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getCurrencyText(product) {
|
|
|
|
if (product?.currency === "USD") {
|
|
|
|
return i18next.t("product:USD");
|
|
|
|
} else if (product?.currency === "CNY") {
|
|
|
|
return i18next.t("product:CNY");
|
|
|
|
} else {
|
|
|
|
return "(Unknown currency)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-26 16:42:25 +08:00
|
|
|
getPrice(product) {
|
|
|
|
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
|
|
|
|
}
|
|
|
|
|
2022-03-06 22:46:02 +08:00
|
|
|
buyProduct(product, provider) {
|
2022-12-12 00:42:45 +08:00
|
|
|
if (provider.clientId.startsWith("http")) {
|
|
|
|
this.setState({
|
|
|
|
qrCodeModalProvider: provider,
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-07 00:33:45 +08:00
|
|
|
this.setState({
|
|
|
|
isPlacingOrder: true,
|
|
|
|
});
|
|
|
|
|
2022-03-06 22:46:02 +08:00
|
|
|
ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name)
|
|
|
|
.then((res) => {
|
2022-12-02 00:06:28 +08:00
|
|
|
if (res.status === "ok") {
|
2022-03-06 22:46:02 +08:00
|
|
|
const payUrl = res.data;
|
|
|
|
Setting.goToLink(payUrl);
|
|
|
|
} else {
|
2022-12-02 00:06:28 +08:00
|
|
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
2022-03-07 00:33:45 +08:00
|
|
|
|
|
|
|
this.setState({
|
|
|
|
isPlacingOrder: false,
|
|
|
|
});
|
2022-03-06 22:46:02 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
2022-12-02 00:06:28 +08:00
|
|
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
2022-03-06 22:46:02 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-12-12 00:42:45 +08:00
|
|
|
renderQrCodeModal() {
|
|
|
|
return (
|
|
|
|
<Modal title={
|
|
|
|
<div>
|
|
|
|
<CheckCircleTwoTone twoToneColor="rgb(45,120,213)" />
|
|
|
|
{" " + i18next.t("product:Please scan the QR code to pay")}
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
open={this.state.qrCodeModalProvider !== null}
|
|
|
|
onOk={() => {
|
|
|
|
Setting.goToLink(this.state.product.returnUrl);
|
|
|
|
}}
|
|
|
|
onCancel={() => {
|
|
|
|
this.setState({
|
|
|
|
qrCodeModalProvider: null,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
okText={i18next.t("product:I have completed the payment")}
|
|
|
|
cancelText={i18next.t("general:Cancel")}>
|
|
|
|
<p key={this.state.qrCodeModalProvider?.name}>
|
|
|
|
{
|
|
|
|
i18next.t("product:Please provide your username in the remark")
|
|
|
|
}
|
|
|
|
:
|
|
|
|
{
|
|
|
|
Setting.getTag("default", this.props.account.name)
|
|
|
|
}
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
<img src={this.state.qrCodeModalProvider?.clientId} alt={this.state.qrCodeModalProvider?.name} width={"472px"} style={{marginBottom: "20px"}} />
|
|
|
|
</p>
|
|
|
|
</Modal>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-27 23:50:35 +08:00
|
|
|
getPayButton(provider) {
|
|
|
|
let text = provider.type;
|
|
|
|
if (provider.type === "Alipay") {
|
|
|
|
text = i18next.t("product:Alipay");
|
|
|
|
} else if (provider.type === "WeChat Pay") {
|
|
|
|
text = i18next.t("product:WeChat Pay");
|
|
|
|
} else if (provider.type === "Paypal") {
|
|
|
|
text = i18next.t("product:Paypal");
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Button style={{height: "50px", borderWidth: "2px"}} shape="round" icon={
|
2022-04-21 21:52:34 +08:00
|
|
|
<img style={{marginRight: "10px"}} width={36} height={36} src={Setting.getProviderLogoURL(provider)} alt={provider.displayName} />
|
2022-02-27 23:50:35 +08:00
|
|
|
} size={"large"} >
|
|
|
|
{
|
|
|
|
text
|
|
|
|
}
|
|
|
|
</Button>
|
2022-07-10 15:45:55 +08:00
|
|
|
);
|
2022-02-27 23:50:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
renderProviderButton(provider, product) {
|
|
|
|
return (
|
|
|
|
<span key={provider.name} style={{width: "200px", marginRight: "20px", marginBottom: "10px"}}>
|
2022-03-06 22:46:02 +08:00
|
|
|
<span style={{width: "200px", cursor: "pointer"}} onClick={() => this.buyProduct(product, provider)}>
|
2022-02-27 23:50:35 +08:00
|
|
|
{
|
|
|
|
this.getPayButton(provider)
|
|
|
|
}
|
2022-03-06 22:46:02 +08:00
|
|
|
</span>
|
2022-02-27 23:50:35 +08:00
|
|
|
</span>
|
2022-07-10 15:45:55 +08:00
|
|
|
);
|
2022-02-27 23:50:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
renderPay(product) {
|
|
|
|
if (product === undefined || product === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (product.state !== "Published") {
|
|
|
|
return i18next.t("product:This product is currently not in sale.");
|
|
|
|
}
|
2022-08-07 15:44:57 +08:00
|
|
|
if (product.providerObjs.length === 0) {
|
2022-02-27 23:50:35 +08:00
|
|
|
return i18next.t("product:There is no payment channel for this product.");
|
|
|
|
}
|
|
|
|
|
2022-08-07 15:44:57 +08:00
|
|
|
return product.providerObjs.map(provider => {
|
2022-02-27 23:50:35 +08:00
|
|
|
return this.renderProviderButton(provider, product);
|
2022-07-10 15:45:55 +08:00
|
|
|
});
|
2022-02-27 23:50:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const product = this.getProductObj();
|
|
|
|
|
2022-03-13 16:25:54 +08:00
|
|
|
if (product === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-02-27 23:50:35 +08:00
|
|
|
return (
|
|
|
|
<div>
|
2022-03-07 00:33:45 +08:00
|
|
|
<Spin spinning={this.state.isPlacingOrder} size="large" tip={i18next.t("product:Placing order...")} style={{paddingTop: "10%"}} >
|
|
|
|
<Descriptions title={i18next.t("product:Buy Product")} bordered>
|
|
|
|
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
|
2022-07-10 15:45:55 +08:00
|
|
|
<span style={{fontSize: 28}}>
|
2022-12-07 01:53:03 +08:00
|
|
|
{Setting.getLanguageText(product?.displayName)}
|
2022-07-10 15:45:55 +08:00
|
|
|
</span>
|
2022-03-07 00:33:45 +08:00
|
|
|
</Descriptions.Item>
|
2022-12-07 01:53:03 +08:00
|
|
|
<Descriptions.Item label={i18next.t("product:Detail")}><span style={{fontSize: 16}}>{Setting.getLanguageText(product?.detail)}</span></Descriptions.Item>
|
2022-03-07 00:33:45 +08:00
|
|
|
<Descriptions.Item label={i18next.t("product:Tag")}><span style={{fontSize: 16}}>{product?.tag}</span></Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:SKU")}><span style={{fontSize: 16}}>{product?.name}</span></Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:Image")} span={3}>
|
2022-07-10 15:45:55 +08:00
|
|
|
<img src={product?.image} alt={product?.name} height={90} style={{marginBottom: "20px"}} />
|
2022-03-07 00:33:45 +08:00
|
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:Price")}>
|
2022-07-10 15:45:55 +08:00
|
|
|
<span style={{fontSize: 28, color: "red", fontWeight: "bold"}}>
|
|
|
|
{
|
|
|
|
this.getPrice(product)
|
|
|
|
}
|
|
|
|
</span>
|
2022-03-07 00:33:45 +08:00
|
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:Quantity")}><span style={{fontSize: 16}}>{product?.quantity}</span></Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:Sold")}><span style={{fontSize: 16}}>{product?.sold}</span></Descriptions.Item>
|
|
|
|
<Descriptions.Item label={i18next.t("product:Pay")} span={3}>
|
|
|
|
{
|
|
|
|
this.renderPay(product)
|
|
|
|
}
|
|
|
|
</Descriptions.Item>
|
|
|
|
</Descriptions>
|
|
|
|
</Spin>
|
2022-12-12 00:42:45 +08:00
|
|
|
{
|
|
|
|
this.renderQrCodeModal()
|
|
|
|
}
|
2022-02-27 23:50:35 +08:00
|
|
|
</div>
|
2022-07-10 15:45:55 +08:00
|
|
|
);
|
2022-02-27 23:50:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ProductBuyPage;
|