Add product detail page.

This commit is contained in:
Yang Luo 2022-02-27 23:50:35 +08:00
parent 39ab71c5db
commit e19f07c521
12 changed files with 349 additions and 25 deletions

View File

@ -28,6 +28,7 @@ type Product struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
Image string `xorm:"varchar(100)" json:"image"` Image string `xorm:"varchar(100)" json:"image"`
Detail string `xorm:"varchar(100)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"` Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"` Currency string `xorm:"varchar(100)" json:"currency"`
Price int `json:"price"` Price int `json:"price"`

View File

@ -45,6 +45,7 @@ import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage"; import CertEditPage from "./CertEditPage";
import ProductListPage from "./ProductListPage"; import ProductListPage from "./ProductListPage";
import ProductEditPage from "./ProductEditPage"; import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import PaymentListPage from "./PaymentListPage"; import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage"; import PaymentEditPage from "./PaymentEditPage";
import AccountPage from "./account/AccountPage"; import AccountPage from "./account/AccountPage";
@ -503,6 +504,7 @@ class App extends Component {
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)}/> <Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)}/>
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)}/> <Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)}/>
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)}/> <Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)}/>
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)}/>
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)}/> <Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)}/>
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)}/> <Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)}/>
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)}/> <Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)}/>

View File

@ -443,7 +443,6 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("application:Grant types"), i18next.t("application:Grant types - Tooltip"))} : {Setting.getLabel(i18next.t("application:Grant types"), i18next.t("application:Grant types - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: '100%'}} <Select virtual={false} mode="tags" style={{width: '100%'}}
value={this.state.application.grantTypes} value={this.state.application.grantTypes}
onChange={(value => { onChange={(value => {
@ -518,13 +517,13 @@ class ApplicationEditPage extends React.Component {
if (!Setting.isMobile()) { if (!Setting.isMobile()) {
return ( return (
<React.Fragment> <React.Fragment>
<Col span={11} style={{display:'flex',flexDirection:'column'}}> <Col span={11} style={{display:"flex", flexDirection: "column"}}>
<a style={{marginBottom: '10px',display:'flex'}} target="_blank" rel="noreferrer" href={signUpUrl}> <a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={signUpUrl}>
<Button type="primary">{i18next.t("application:Test signup page..")}</Button> <Button type="primary">{i18next.t("application:Test signup page..")}</Button>
</a> </a>
<br/> <br/>
<br/> <br/>
<div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888" ,alignItems:'center',overflow:'auto',flexDirection:'column',flex:'auto'}}> <div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems:"center", overflow:"auto", flexDirection:"column", flex: "auto"}}>
{ {
this.state.application.enablePassword ? ( this.state.application.enablePassword ? (
<SignupPage application={this.state.application} /> <SignupPage application={this.state.application} />
@ -534,13 +533,13 @@ class ApplicationEditPage extends React.Component {
} }
</div> </div>
</Col> </Col>
<Col span={11} style={{display:'flex',flexDirection:'column'}}> <Col span={11} style={{display:"flex", flexDirection: "column"}}>
<a style={{marginBottom: '10px',display:'flex'}} target="_blank" rel="noreferrer" href={signInUrl}> <a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={signInUrl}>
<Button type="primary">{i18next.t("application:Test signin page..")}</Button> <Button type="primary">{i18next.t("application:Test signin page..")}</Button>
</a> </a>
<br/> <br/>
<br/> <br/>
<div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888",alignItems:'center',overflow:'auto',flexDirection:'column',flex:'auto' }}> <div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems:"center", overflow:"auto", flexDirection:"column", flex: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} /> <LoginPage type={"login"} mode={"signin"} application={this.state.application} />
</div> </div>
</Col> </Col>
@ -549,11 +548,11 @@ class ApplicationEditPage extends React.Component {
} else{ } else{
return( return(
<React.Fragment> <React.Fragment>
<Col span={24} style={{display:'flex',flexDirection:'column'}}> <Col span={24} style={{display:"flex", flexDirection: "column"}}>
<a style={{marginBottom: '10px',display:'flex'}} target="_blank" rel="noreferrer" href={signUpUrl}> <a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={signUpUrl}>
<Button type="primary">{i18next.t("application:Test signup page..")}</Button> <Button type="primary">{i18next.t("application:Test signup page..")}</Button>
</a> </a>
<div style={{marginBottom:'10px', width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888" ,alignItems:'center',overflow:'auto',flexDirection:'column',flex:'auto'}}> <div style={{marginBottom:"10px", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
{ {
this.state.application.enablePassword ? ( this.state.application.enablePassword ? (
<SignupPage application={this.state.application} /> <SignupPage application={this.state.application} />
@ -562,10 +561,10 @@ class ApplicationEditPage extends React.Component {
) )
} }
</div> </div>
<a style={{marginBottom: '10px',display:'flex'}} target="_blank" rel="noreferrer" href={signInUrl}> <a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={signInUrl}>
<Button type="primary">{i18next.t("application:Test signin page..")}</Button> <Button type="primary">{i18next.t("application:Test signin page..")}</Button>
</a> </a>
<div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888",alignItems:'center',overflow:'auto',flexDirection:'column',flex:'auto' }}> <div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} /> <LoginPage type={"login"} mode={"signin"} application={this.state.application} />
</div> </div>
</Col> </Col>
@ -579,13 +578,13 @@ class ApplicationEditPage extends React.Component {
return ( return (
<React.Fragment> <React.Fragment>
<Col span={(Setting.isMobile()) ? 24 : 11} style={{display:'flex',flexDirection:'column',flex:'auto'}} > <Col span={(Setting.isMobile()) ? 24 : 11} style={{display:"flex", flexDirection: "column", flex: "auto"}} >
<a style={{marginBottom: '10px'}} target="_blank" rel="noreferrer" href={promptUrl}> <a style={{marginBottom: "10px"}} target="_blank" rel="noreferrer" href={promptUrl}>
<Button type="primary">{i18next.t("application:Test prompt page..")}</Button> <Button type="primary">{i18next.t("application:Test prompt page..")}</Button>
</a> </a>
<br style={(Setting.isMobile()) ? {display:'none'} : {}} /> <br style={(Setting.isMobile()) ? {display: "none"} : {}} />
<br style={(Setting.isMobile()) ? {display:'none'} : {}} /> <br style={(Setting.isMobile()) ? {display: "none"} : {}} />
<div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888",flexDirection:'column',flex:'auto'}}> <div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}>
<PromptPage application={this.state.application} account={this.props.account} /> <PromptPage application={this.state.application} account={this.props.account} />
</div> </div>
</Col> </Col>

196
web/src/ProductBuyPage.js Normal file
View File

@ -0,0 +1,196 @@
// 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";
import {Button, Descriptions} from "antd";
import i18next from "i18next";
import * as ProductBackend from "./backend/ProductBackend";
import * as ProviderBackend from "./backend/ProviderBackend";
import * as Provider from "./auth/Provider";
class ProductBuyPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
productName: props.match?.params.productName,
product: null,
providers: [],
};
}
UNSAFE_componentWillMount() {
this.getProduct();
this.getPaymentProviders();
}
getProduct() {
ProductBackend.getProduct("admin", this.state.productName)
.then((product) => {
this.setState({
product: product,
});
});
}
getPaymentProviders() {
ProviderBackend.getProviders("admin")
.then((res) => {
this.setState({
providers: res.filter(provider => provider.category === "Payment"),
});
});
}
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)";
}
}
getProviders(product) {
if (this.state.providers.length === 0 || product.providers.length === 0) {
return [];
}
let providerMap = {};
this.state.providers.forEach(provider => {
providerMap[provider.name] = provider;
})
return product.providers.map(providerName => providerMap[providerName]);
}
getPayUrl(product, provider) {
if (product === null || provider === null) {
return "";
}
return `https://${provider.type}`;
// if (provider.type === "WeChat") {
// return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
// } else if (provider.type === "GitHub") {
// return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
// }
}
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={
<img style={{marginRight: "10px"}} width={36} height={36} src={Provider.getProviderLogo(provider)} alt={provider.displayName} />
} size={"large"} >
{
text
}
</Button>
)
}
renderProviderButton(provider, product) {
return (
<span key={provider.name} style={{width: "200px", marginRight: "20px", marginBottom: "10px"}}>
<a style={{width: "200px"}} href={this.getPayUrl(product, provider)}>
{
this.getPayButton(provider)
}
</a>
</span>
)
}
renderPay(product) {
if (product === undefined || product === null) {
return null;
}
if (product.state !== "Published") {
return i18next.t("product:This product is currently not in sale.");
}
if (product.providers.length === 0) {
return i18next.t("product:There is no payment channel for this product.");
}
const providers = this.getProviders(product);
return providers.map(provider => {
return this.renderProviderButton(provider, product);
})
}
render() {
const product = this.getProductObj();
return (
<div>
<Descriptions title={i18next.t("product:Buy Product")} bordered>
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
<span style={{fontSize: 28}}>
{product?.displayName}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Detail")}><span style={{fontSize: 16}}>{product?.detail}</span></Descriptions.Item>
<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}>
<img src={product?.image} alt={product?.image} height={90} style={{marginBottom: '20px'}}/>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Price")}>
<span style={{fontSize: 28, color: "red", fontWeight: "bold"}}>
{`${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`}
</span>
</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>
</div>
)
}
}
export default ProductBuyPage;

View File

@ -19,6 +19,7 @@ import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import * as ProviderBackend from "./backend/ProviderBackend"; import * as ProviderBackend from "./backend/ProviderBackend";
import ProductBuyPage from "./ProductBuyPage";
const { Option } = Select; const { Option } = Select;
@ -142,6 +143,16 @@ class ProductEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Detail"), i18next.t("product:Detail - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.product.detail} onChange={e => {
this.updateProductField('detail', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: '20px'}} > <Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Currency"), i18next.t("product:Currency - Tooltip"))} : {Setting.getLabel(i18next.t("product:Currency"), i18next.t("product:Currency - Tooltip"))} :
@ -218,10 +229,34 @@ class ProductEditPage extends React.Component {
</Select> </Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: '20px'}} >
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :
</Col>
{
this.renderPreview()
}
</Row>
</Card> </Card>
) )
} }
renderPreview() {
let buyUrl = `/products/${this.state.product.name}/buy`;
return (
<Col span={22} style={{display: "flex", flexDirection: "column"}}>
<a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={buyUrl}>
<Button type="primary">{i18next.t("product:Test buy page..")}</Button>
</a>
<br/>
<br/>
<div style={{width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
<ProductBuyPage product={this.state.product} />
</div>
</Col>
)
}
submitProductEdit(willExist) { submitProductEdit(willExist) {
let product = Setting.deepCopy(this.state.product); let product = Setting.deepCopy(this.state.product);
ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product) ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product)

View File

@ -272,22 +272,35 @@
"Resources": "Ressourcen" "Resources": "Ressourcen"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "Zugangsschlüssel", "Access key": "Zugangsschlüssel",

View File

@ -272,22 +272,35 @@
"Resources": "Resources" "Resources": "Resources"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "Access key", "Access key": "Access key",

View File

@ -272,22 +272,35 @@
"Resources": "Ressource" "Resources": "Ressource"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "Clé d'accès", "Access key": "Clé d'accès",

View File

@ -272,22 +272,35 @@
"Resources": "リソース" "Resources": "リソース"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "アクセスキー", "Access key": "アクセスキー",

View File

@ -272,22 +272,35 @@
"Resources": "Resources" "Resources": "Resources"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "Access key", "Access key": "Access key",

View File

@ -272,22 +272,35 @@
"Resources": "Ресурсы" "Resources": "Ресурсы"
}, },
"product": { "product": {
"Alipay": "Alipay",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip", "Currency - Tooltip": "Currency - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Image - Tooltip", "Image - Tooltip": "Image - Tooltip",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip", "Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price - Tooltip", "Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip", "Quantity - Tooltip": "Quantity - Tooltip",
"SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip", "Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip" "Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
}, },
"provider": { "provider": {
"Access key": "Ключ доступа", "Access key": "Ключ доступа",

View File

@ -272,22 +272,35 @@
"Resources": "资源" "Resources": "资源"
}, },
"product": { "product": {
"Alipay": "支付宝",
"Buy Product": "购买商品",
"CNY": "人民币",
"Currency": "币种", "Currency": "币种",
"Currency - Tooltip": "币种 - 工具提示", "Currency - Tooltip": "币种 - 工具提示",
"Detail": "详情",
"Detail - Tooltip": "详情 - 工具提示",
"Edit Product": "编辑商品", "Edit Product": "编辑商品",
"Image": "图片", "Image": "图片",
"Image - Tooltip": "图片 - 工具提示", "Image - Tooltip": "图片 - 工具提示",
"New Product": "添加商品", "New Product": "添加商品",
"Pay": "支付方式",
"Payment providers": "支付提供商", "Payment providers": "支付提供商",
"Payment providers - Tooltip": "支付提供商 - 工具提示", "Payment providers - Tooltip": "支付提供商 - 工具提示",
"Paypal": "Paypal",
"Price": "价格", "Price": "价格",
"Price - Tooltip": "价格 - 工具提示", "Price - Tooltip": "价格 - 工具提示",
"Quantity": "库存", "Quantity": "库存",
"Quantity - Tooltip": "库存 - 工具提示", "Quantity - Tooltip": "库存 - 工具提示",
"SKU": "货号",
"Sold": "售出", "Sold": "售出",
"Sold - Tooltip": "售出 - 工具提示", "Sold - Tooltip": "售出 - 工具提示",
"Tag": "标签", "Tag": "类别",
"Tag - Tooltip": "标签 - 工具提示" "Tag - Tooltip": "类别 - 工具提示",
"Test buy page..": "测试购买页面..",
"There is no payment channel for this product.": "该商品没有付款方式。",
"This product is currently not in sale.": "该商品目前未在售。",
"USD": "美元",
"WeChat Pay": "微信支付"
}, },
"provider": { "provider": {
"Access key": "访问密钥", "Access key": "访问密钥",