diff --git a/controllers/product.go b/controllers/product.go
new file mode 100644
index 00000000..0548fa78
--- /dev/null
+++ b/controllers/product.go
@@ -0,0 +1,116 @@
+// 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.
+
+package controllers
+
+import (
+ "encoding/json"
+
+ "github.com/astaxie/beego/utils/pagination"
+ "github.com/casdoor/casdoor/object"
+ "github.com/casdoor/casdoor/util"
+)
+
+// GetProducts
+// @Title GetProducts
+// @Tag Product API
+// @Description get products
+// @Param owner query string true "The owner of products"
+// @Success 200 {array} object.Product The Response object
+// @router /get-products [get]
+func (c *ApiController) GetProducts() {
+ owner := c.Input().Get("owner")
+ limit := c.Input().Get("pageSize")
+ page := c.Input().Get("p")
+ field := c.Input().Get("field")
+ value := c.Input().Get("value")
+ sortField := c.Input().Get("sortField")
+ sortOrder := c.Input().Get("sortOrder")
+ if limit == "" || page == "" {
+ c.Data["json"] = object.GetProducts(owner)
+ c.ServeJSON()
+ } else {
+ limit := util.ParseInt(limit)
+ paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetProductCount(owner, field, value)))
+ products := object.GetPaginationProducts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
+ c.ResponseOk(products, paginator.Nums())
+ }
+}
+
+// @Title GetProduct
+// @Tag Product API
+// @Description get product
+// @Param id query string true "The id of the product"
+// @Success 200 {object} object.Product The Response object
+// @router /get-product [get]
+func (c *ApiController) GetProduct() {
+ id := c.Input().Get("id")
+
+ c.Data["json"] = object.GetProduct(id)
+ c.ServeJSON()
+}
+
+// @Title UpdateProduct
+// @Tag Product API
+// @Description update product
+// @Param id query string true "The id of the product"
+// @Param body body object.Product true "The details of the product"
+// @Success 200 {object} controllers.Response The Response object
+// @router /update-product [post]
+func (c *ApiController) UpdateProduct() {
+ id := c.Input().Get("id")
+
+ var product object.Product
+ err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
+ if err != nil {
+ panic(err)
+ }
+
+ c.Data["json"] = wrapActionResponse(object.UpdateProduct(id, &product))
+ c.ServeJSON()
+}
+
+// @Title AddProduct
+// @Tag Product API
+// @Description add product
+// @Param body body object.Product true "The details of the product"
+// @Success 200 {object} controllers.Response The Response object
+// @router /add-product [post]
+func (c *ApiController) AddProduct() {
+ var product object.Product
+ err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
+ if err != nil {
+ panic(err)
+ }
+
+ c.Data["json"] = wrapActionResponse(object.AddProduct(&product))
+ c.ServeJSON()
+}
+
+// @Title DeleteProduct
+// @Tag Product API
+// @Description delete product
+// @Param body body object.Product true "The details of the product"
+// @Success 200 {object} controllers.Response The Response object
+// @router /delete-product [post]
+func (c *ApiController) DeleteProduct() {
+ var product object.Product
+ err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
+ if err != nil {
+ panic(err)
+ }
+
+ c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))
+ c.ServeJSON()
+}
diff --git a/object/adapter.go b/object/adapter.go
index 19f7fa02..3618c102 100644
--- a/object/adapter.go
+++ b/object/adapter.go
@@ -17,7 +17,6 @@ package object
import (
"fmt"
"runtime"
- "xorm.io/core"
"github.com/astaxie/beego"
"github.com/casdoor/casdoor/conf"
@@ -25,6 +24,7 @@ import (
//_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql
//_ "github.com/lib/pq" // db = postgres
+ "xorm.io/core"
"xorm.io/xorm"
)
@@ -183,6 +183,11 @@ func (a *Adapter) createTable() {
panic(err)
}
+ err = a.Engine.Sync2(new(Product))
+ if err != nil {
+ panic(err)
+ }
+
err = a.Engine.Sync2(new(Payment))
if err != nil {
panic(err)
diff --git a/object/product.go b/object/product.go
new file mode 100644
index 00000000..47d7d7d5
--- /dev/null
+++ b/object/product.go
@@ -0,0 +1,129 @@
+// 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.
+
+package object
+
+import (
+ "fmt"
+
+ "github.com/casdoor/casdoor/util"
+ "xorm.io/core"
+)
+
+type Product struct {
+ Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
+ Name string `xorm:"varchar(100) notnull pk" json:"name"`
+ CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
+ DisplayName string `xorm:"varchar(100)" json:"displayName"`
+
+ Image string `xorm:"varchar(100)" json:"image"`
+ Tag string `xorm:"varchar(100)" json:"tag"`
+ Currency string `xorm:"varchar(100)" json:"currency"`
+ Price int `json:"price"`
+ Quantity int `json:"quantity"`
+ Sold int `json:"sold"`
+ Providers []string `xorm:"varchar(100)" json:"providers"`
+
+ State string `xorm:"varchar(100)" json:"state"`
+}
+
+func GetProductCount(owner, field, value string) int {
+ session := GetSession(owner, -1, -1, field, value, "", "")
+ count, err := session.Count(&Product{})
+ if err != nil {
+ panic(err)
+ }
+
+ return int(count)
+}
+
+func GetProducts(owner string) []*Product {
+ products := []*Product{}
+ err := adapter.Engine.Desc("created_time").Find(&products, &Product{Owner: owner})
+ if err != nil {
+ panic(err)
+ }
+
+ return products
+}
+
+func GetPaginationProducts(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Product {
+ products := []*Product{}
+ session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
+ err := session.Find(&products)
+ if err != nil {
+ panic(err)
+ }
+
+ return products
+}
+
+func getProduct(owner string, name string) *Product {
+ if owner == "" || name == "" {
+ return nil
+ }
+
+ product := Product{Owner: owner, Name: name}
+ existed, err := adapter.Engine.Get(&product)
+ if err != nil {
+ panic(err)
+ }
+
+ if existed {
+ return &product
+ } else {
+ return nil
+ }
+}
+
+func GetProduct(id string) *Product {
+ owner, name := util.GetOwnerAndNameFromId(id)
+ return getProduct(owner, name)
+}
+
+func UpdateProduct(id string, product *Product) bool {
+ owner, name := util.GetOwnerAndNameFromId(id)
+ if getProduct(owner, name) == nil {
+ return false
+ }
+
+ affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(product)
+ if err != nil {
+ panic(err)
+ }
+
+ return affected != 0
+}
+
+func AddProduct(product *Product) bool {
+ affected, err := adapter.Engine.Insert(product)
+ if err != nil {
+ panic(err)
+ }
+
+ return affected != 0
+}
+
+func DeleteProduct(product *Product) bool {
+ affected, err := adapter.Engine.ID(core.PK{product.Owner, product.Name}).Delete(&Product{})
+ if err != nil {
+ panic(err)
+ }
+
+ return affected != 0
+}
+
+func (product *Product) GetId() string {
+ return fmt.Sprintf("%s/%s", product.Owner, product.Name)
+}
diff --git a/routers/router.go b/routers/router.go
index 1d1b5afb..1a7c4b0e 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -149,6 +149,12 @@ func initAPI() {
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
beego.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert")
+ beego.Router("/api/get-products", &controllers.ApiController{}, "GET:GetProducts")
+ beego.Router("/api/get-product", &controllers.ApiController{}, "GET:GetProduct")
+ beego.Router("/api/update-product", &controllers.ApiController{}, "POST:UpdateProduct")
+ beego.Router("/api/add-product", &controllers.ApiController{}, "POST:AddProduct")
+ beego.Router("/api/delete-product", &controllers.ApiController{}, "POST:DeleteProduct")
+
beego.Router("/api/get-payments", &controllers.ApiController{}, "GET:GetPayments")
beego.Router("/api/get-payment", &controllers.ApiController{}, "GET:GetPayment")
beego.Router("/api/update-payment", &controllers.ApiController{}, "POST:UpdatePayment")
diff --git a/web/src/App.js b/web/src/App.js
index f5a9cf45..91803166 100644
--- a/web/src/App.js
+++ b/web/src/App.js
@@ -43,6 +43,8 @@ import SyncerListPage from "./SyncerListPage";
import SyncerEditPage from "./SyncerEditPage";
import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage";
+import ProductListPage from "./ProductListPage";
+import ProductEditPage from "./ProductEditPage";
import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage";
import AccountPage from "./account/AccountPage";
@@ -128,6 +130,8 @@ class App extends Component {
this.setState({ selectedMenuKey: '/syncers' });
} else if (uri.includes('/certs')) {
this.setState({ selectedMenuKey: '/certs' });
+ } else if (uri.includes('/products')) {
+ this.setState({ selectedMenuKey: '/products' });
} else if (uri.includes('/payments')) {
this.setState({ selectedMenuKey: '/payments' });
} else if (uri.includes('/signup')) {
@@ -413,6 +417,13 @@ class App extends Component {
);
// res.push(
+ //
+ //
+ // {i18next.t("general:Products")}
+ //
+ //
+ // );
+ // res.push(
//
//
// {i18next.t("general:Payments")}
@@ -490,6 +501,8 @@ class App extends Component {
this.renderLoginIfNotLoggedIn()}/>
this.renderLoginIfNotLoggedIn()}/>
this.renderLoginIfNotLoggedIn()}/>
+ this.renderLoginIfNotLoggedIn()}/>
+ this.renderLoginIfNotLoggedIn()}/>
this.renderLoginIfNotLoggedIn()}/>
this.renderLoginIfNotLoggedIn()}/>
this.renderLoginIfNotLoggedIn()}/>
diff --git a/web/src/ApplicationEditPage.js b/web/src/ApplicationEditPage.js
index 7437ff91..cae41e5f 100644
--- a/web/src/ApplicationEditPage.js
+++ b/web/src/ApplicationEditPage.js
@@ -166,12 +166,12 @@ class ApplicationEditPage extends React.Component {
- {Setting.getLabel("Logo", i18next.t("general:Logo - Tooltip"))} :
+ {Setting.getLabel("general:Logo", i18next.t("general:Logo - Tooltip"))} :
- URL:
+ {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
} value={this.state.application.logo} onChange={e => {
@@ -440,7 +440,7 @@ 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"))} :
diff --git a/web/src/OrganizationEditPage.js b/web/src/OrganizationEditPage.js
index f5bf1af3..9fdc9fb0 100644
--- a/web/src/OrganizationEditPage.js
+++ b/web/src/OrganizationEditPage.js
@@ -113,12 +113,12 @@ class OrganizationEditPage extends React.Component {
- {Setting.getLabel("Favicon", i18next.t("general:Favicon - Tooltip"))} :
+ {Setting.getLabel("general:Favicon", i18next.t("general:Favicon - Tooltip"))} :
- URL:
+ {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
} value={this.state.organization.favicon} onChange={e => {
@@ -188,7 +188,7 @@ class OrganizationEditPage extends React.Component {
- URL:
+ {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
} value={this.state.organization.defaultAvatar} onChange={e => {
diff --git a/web/src/PaymentEditPage.js b/web/src/PaymentEditPage.js
index 40845001..29325092 100644
--- a/web/src/PaymentEditPage.js
+++ b/web/src/PaymentEditPage.js
@@ -13,15 +13,10 @@
// limitations under the License.
import React from "react";
-import {Button, Card, Col, Input, Row, Select, Switch} from 'antd';
+import {Button, Card, Col, Input, Row} from 'antd';
import * as PaymentBackend from "./backend/PaymentBackend";
-import * as OrganizationBackend from "./backend/OrganizationBackend";
-import * as UserBackend from "./backend/UserBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
-import * as RoleBackend from "./backend/RoleBackend";
-
-const { Option } = Select;
class PaymentEditPage extends React.Component {
constructor(props) {
@@ -105,16 +100,6 @@ class PaymentEditPage extends React.Component {
}} />
-
-
- {Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
-
-
- {
- // this.updatePaymentField('name', e.target.value);
- }} />
-
-
{Setting.getLabel(i18next.t("general:Provider"), i18next.t("general:Provider - Tooltip"))} :
diff --git a/web/src/PaymentListPage.js b/web/src/PaymentListPage.js
index 3bf667af..ee45893c 100644
--- a/web/src/PaymentListPage.js
+++ b/web/src/PaymentListPage.js
@@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
-import {Button, Popconfirm, Switch, Table} from 'antd';
+import {Button, Popconfirm, Table} from 'antd';
import moment from "moment";
import * as Setting from "./Setting";
import * as PaymentBackend from "./backend/PaymentBackend";
diff --git a/web/src/ProductEditPage.js b/web/src/ProductEditPage.js
new file mode 100644
index 00000000..43a2c45a
--- /dev/null
+++ b/web/src/ProductEditPage.js
@@ -0,0 +1,276 @@
+// 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, Card, Col, Input, InputNumber, Row, Select} from 'antd';
+import * as ProductBackend from "./backend/ProductBackend";
+import * as Setting from "./Setting";
+import i18next from "i18next";
+import {LinkOutlined} from "@ant-design/icons";
+import * as ProviderBackend from "./backend/ProviderBackend";
+
+const { Option } = Select;
+
+class ProductEditPage extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ classes: props,
+ organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
+ productName: props.match.params.productName,
+ product: null,
+ providers: [],
+ mode: props.location.mode !== undefined ? props.location.mode : "edit",
+ };
+ }
+
+ 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"),
+ });
+ });
+ }
+
+ parseProductField(key, value) {
+ if ([""].includes(key)) {
+ value = Setting.myParseInt(value);
+ }
+ return value;
+ }
+
+ updateProductField(key, value) {
+ value = this.parseProductField(key, value);
+
+ let product = this.state.product;
+ product[key] = value;
+ this.setState({
+ product: product,
+ });
+ }
+
+ renderProduct() {
+ return (
+
+ {this.state.mode === "add" ? i18next.t("product:New Product") : i18next.t("product:Edit Product")}
+
+
+ {this.state.mode === "add" ? : null}
+
+ } style={(Setting.isMobile())? {margin: '5px'}:{}} type="inner">
+
+
+ {Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
+
+
+ {
+ this.updateProductField('name', e.target.value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
+
+
+ {
+ this.updateProductField('displayName', e.target.value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Image"), i18next.t("product:Image - Tooltip"))} :
+
+
+
+
+ {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
+
+
+ } value={this.state.product.image} onChange={e => {
+ this.updateProductField('image', e.target.value);
+ }} />
+
+
+
+
+ {i18next.t("general:Preview")}:
+
+
+
+
+
+
+
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Tag"), i18next.t("product:Tag - Tooltip"))} :
+
+
+ {
+ this.updateProductField('tag', e.target.value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Currency"), i18next.t("product:Currency - Tooltip"))} :
+
+
+
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Price"), i18next.t("product:Price - Tooltip"))} :
+
+
+ {
+ this.updateProductField('price', value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Quantity"), i18next.t("product:Quantity - Tooltip"))} :
+
+
+ {
+ this.updateProductField('quantity', value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Sold"), i18next.t("product:Sold - Tooltip"))} :
+
+
+ {
+ this.updateProductField('sold', value);
+ }} />
+
+
+
+
+ {Setting.getLabel(i18next.t("product:Payment providers"), i18next.t("product:Payment providers - Tooltip"))} :
+
+
+
+
+
+
+
+ {Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
+
+
+
+
+
+
+ )
+ }
+
+ submitProductEdit(willExist) {
+ let product = Setting.deepCopy(this.state.product);
+ ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product)
+ .then((res) => {
+ if (res.msg === "") {
+ Setting.showMessage("success", `Successfully saved`);
+ this.setState({
+ productName: this.state.product.name,
+ });
+
+ if (willExist) {
+ this.props.history.push(`/products`);
+ } else {
+ this.props.history.push(`/products/${this.state.product.name}`);
+ }
+ } else {
+ Setting.showMessage("error", res.msg);
+ this.updateProductField('name', this.state.productName);
+ }
+ })
+ .catch(error => {
+ Setting.showMessage("error", `Failed to connect to server: ${error}`);
+ });
+ }
+
+ deleteProduct() {
+ ProductBackend.deleteProduct(this.state.product)
+ .then(() => {
+ this.props.history.push(`/products`);
+ })
+ .catch(error => {
+ Setting.showMessage("error", `Product failed to delete: ${error}`);
+ });
+ }
+
+ render() {
+ return (
+
+ {
+ this.state.product !== null ? this.renderProduct() : null
+ }
+
+
+
+ {this.state.mode === "add" ? : null}
+
+
+ );
+ }
+}
+
+export default ProductEditPage;
diff --git a/web/src/ProductListPage.js b/web/src/ProductListPage.js
new file mode 100644
index 00000000..c01564f2
--- /dev/null
+++ b/web/src/ProductListPage.js
@@ -0,0 +1,295 @@
+// 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 {Link} from "react-router-dom";
+import {Button, Col, List, Popconfirm, Row, Table, Tooltip} from 'antd';
+import moment from "moment";
+import * as Setting from "./Setting";
+import * as ProductBackend from "./backend/ProductBackend";
+import i18next from "i18next";
+import BaseListPage from "./BaseListPage";
+import {EditOutlined} from "@ant-design/icons";
+
+class ProductListPage extends BaseListPage {
+ newProduct() {
+ const randomName = Setting.getRandomName();
+ return {
+ owner: "admin",
+ name: `product_${randomName}`,
+ createdTime: moment().format(),
+ displayName: `New Product - ${randomName}`,
+ image: "https://cdn.casdoor.com/logo/casdoor-logo_1185x256.png",
+ tag: "Casdoor Summit 2022",
+ currency: "USD",
+ price: 300,
+ quantity: 99,
+ sold: 10,
+ providers: [],
+ state: "Published",
+ }
+ }
+
+ addProduct() {
+ const newProduct = this.newProduct();
+ ProductBackend.addProduct(newProduct)
+ .then((res) => {
+ this.props.history.push({pathname: `/products/${newProduct.name}`, mode: "add"});
+ }
+ )
+ .catch(error => {
+ Setting.showMessage("error", `Product failed to add: ${error}`);
+ });
+ }
+
+ deleteProduct(i) {
+ ProductBackend.deleteProduct(this.state.data[i])
+ .then((res) => {
+ Setting.showMessage("success", `Product deleted successfully`);
+ this.setState({
+ data: Setting.deleteRow(this.state.data, i),
+ pagination: {total: this.state.pagination.total - 1},
+ });
+ }
+ )
+ .catch(error => {
+ Setting.showMessage("error", `Product failed to delete: ${error}`);
+ });
+ }
+
+ renderTable(products) {
+ const columns = [
+ {
+ title: i18next.t("general:Name"),
+ dataIndex: 'name',
+ key: 'name',
+ width: '140px',
+ fixed: 'left',
+ sorter: true,
+ ...this.getColumnSearchProps('name'),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ )
+ }
+ },
+ {
+ title: i18next.t("general:Created time"),
+ dataIndex: 'createdTime',
+ key: 'createdTime',
+ width: '160px',
+ sorter: true,
+ render: (text, record, index) => {
+ return Setting.getFormattedDate(text);
+ }
+ },
+ {
+ title: i18next.t("general:Display name"),
+ dataIndex: 'displayName',
+ key: 'displayName',
+ width: '170px',
+ sorter: true,
+ ...this.getColumnSearchProps('displayName'),
+ },
+ {
+ title: i18next.t("product:Image"),
+ dataIndex: 'image',
+ key: 'image',
+ width: '170px',
+ render: (text, record, index) => {
+ return (
+
+
+
+ )
+ }
+ },
+ {
+ title: i18next.t("product:Tag"),
+ dataIndex: 'tag',
+ key: 'tag',
+ width: '160px',
+ sorter: true,
+ ...this.getColumnSearchProps('tag'),
+ },
+ {
+ title: i18next.t("product:Currency"),
+ dataIndex: 'currency',
+ key: 'currency',
+ width: '120px',
+ sorter: true,
+ ...this.getColumnSearchProps('currency'),
+ },
+ {
+ title: i18next.t("product:Price"),
+ dataIndex: 'price',
+ key: 'price',
+ width: '120px',
+ sorter: true,
+ ...this.getColumnSearchProps('price'),
+ },
+ {
+ title: i18next.t("product:Quantity"),
+ dataIndex: 'quantity',
+ key: 'quantity',
+ width: '120px',
+ sorter: true,
+ ...this.getColumnSearchProps('quantity'),
+ },
+ {
+ title: i18next.t("product:Sold"),
+ dataIndex: 'sold',
+ key: 'sold',
+ width: '120px',
+ sorter: true,
+ ...this.getColumnSearchProps('sold'),
+ },
+ {
+ title: i18next.t("general:State"),
+ dataIndex: 'state',
+ key: 'state',
+ width: '120px',
+ sorter: true,
+ ...this.getColumnSearchProps('state'),
+ },
+ {
+ title: i18next.t("product:Payment providers"),
+ dataIndex: 'providers',
+ key: 'providers',
+ width: '500px',
+ ...this.getColumnSearchProps('providers'),
+ render: (text, record, index) => {
+ const providers = text;
+ if (providers.length === 0) {
+ return "(empty)";
+ }
+
+ const half = Math.floor((providers.length + 1) / 2);
+
+ const getList = (providers) => {
+ return (
+ {
+ return (
+
+
+
+ } size="small" onClick={() => Setting.goToLinkSoft(this, `/providers/${providerName}`)} />
+
+
+ {providerName}
+
+
+
+ )
+ }}
+ />
+ )
+ }
+
+ return (
+
+
+
+ {
+ getList(providers.slice(0, half))
+ }
+
+
+ {
+ getList(providers.slice(half))
+ }
+
+
+
+ )
+ },
+ },
+ {
+ title: i18next.t("general:Action"),
+ dataIndex: '',
+ key: 'op',
+ width: '170px',
+ fixed: (Setting.isMobile()) ? "false" : "right",
+ render: (text, record, index) => {
+ return (
+
+
+
this.deleteProduct(index)}
+ >
+
+
+
+ )
+ }
+ },
+ ];
+
+ const paginationProps = {
+ total: this.state.pagination.total,
+ showQuickJumper: true,
+ showSizeChanger: true,
+ showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
+ };
+
+ return (
+
+
(
+
+ {i18next.t("general:Products")}
+
+
+ )}
+ loading={this.state.loading}
+ onChange={this.handleTableChange}
+ />
+
+ );
+ }
+
+ fetch = (params = {}) => {
+ let field = params.searchedColumn, value = params.searchText;
+ let sortField = params.sortField, sortOrder = params.sortOrder;
+ if (params.type !== undefined && params.type !== null) {
+ field = "type";
+ value = params.type;
+ }
+ this.setState({ loading: true });
+ ProductBackend.getProducts("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
+ .then((res) => {
+ if (res.status === "ok") {
+ this.setState({
+ loading: false,
+ data: res.data,
+ pagination: {
+ ...params.pagination,
+ total: res.data2,
+ },
+ searchText: params.searchText,
+ searchedColumn: params.searchedColumn,
+ });
+ }
+ });
+ };
+}
+
+export default ProductListPage;
diff --git a/web/src/backend/ProductBackend.js b/web/src/backend/ProductBackend.js
new file mode 100644
index 00000000..cd130615
--- /dev/null
+++ b/web/src/backend/ProductBackend.js
@@ -0,0 +1,56 @@
+// 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 * as Setting from "../Setting";
+
+export function getProducts(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
+ return fetch(`${Setting.ServerUrl}/api/get-products?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
+ method: "GET",
+ credentials: "include"
+ }).then(res => res.json());
+}
+
+export function getProduct(owner, name) {
+ return fetch(`${Setting.ServerUrl}/api/get-product?id=${owner}/${encodeURIComponent(name)}`, {
+ method: "GET",
+ credentials: "include"
+ }).then(res => res.json());
+}
+
+export function updateProduct(owner, name, product) {
+ let newProduct = Setting.deepCopy(product);
+ return fetch(`${Setting.ServerUrl}/api/update-product?id=${owner}/${encodeURIComponent(name)}`, {
+ method: 'POST',
+ credentials: 'include',
+ body: JSON.stringify(newProduct),
+ }).then(res => res.json());
+}
+
+export function addProduct(product) {
+ let newProduct = Setting.deepCopy(product);
+ return fetch(`${Setting.ServerUrl}/api/add-product`, {
+ method: 'POST',
+ credentials: 'include',
+ body: JSON.stringify(newProduct),
+ }).then(res => res.json());
+}
+
+export function deleteProduct(product) {
+ let newProduct = Setting.deepCopy(product);
+ return fetch(`${Setting.ServerUrl}/api/delete-product`, {
+ method: 'POST',
+ credentials: 'include',
+ body: JSON.stringify(newProduct),
+ }).then(res => res.json());
+}
diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json
index 901bd260..e90477aa 100644
--- a/web/src/locales/de/data.json
+++ b/web/src/locales/de/data.json
@@ -14,6 +14,8 @@
"Enable signup": "Anmeldung aktivieren",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "Datei erfolgreich hochgeladen",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "Passwort AN",
"Password ON - Tooltip": "Whether to allow password login",
@@ -112,6 +114,7 @@
"Email": "E-Mail",
"Email - Tooltip": "email",
"Favicon - Tooltip": "Application icon",
+ "First name": "First name",
"Forget URL": "URL vergessen",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "Zuhause",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "Ist aktiviert - Tooltip",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
+ "Last name": "Last name",
"Logo - Tooltip": "App's image tag",
"Master password": "Master-Passwort",
"Master password - Tooltip": "Masterpasswort - Tooltip",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Vorschau",
"Preview - Tooltip": "The form in which the password is stored in the database",
+ "Products": "Products",
"Provider": "Anbieter",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Anbieter",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Die von Ihnen besuchte Seite existiert leider nicht.",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Syncers",
"Timestamp": "Zeitstempel",
"Tokens": "Token",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "Hoch",
"User": "Benutzer",
"User - Tooltip": "Benutzer - Tooltip",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "Ressourcentyp - Tooltip",
"Resources": "Ressourcen"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "Zugangsschlüssel",
"Access key - Tooltip": "Zugriffsschlüssel - Tooltip",
@@ -389,6 +415,8 @@
"Please input your address!": "Bitte geben Sie Ihre Adresse ein!",
"Please input your affiliation!": "Bitte geben Sie Ihre Zugehörigkeit ein!",
"Please input your display name!": "Bitte geben Sie Ihren Anzeigenamen ein!",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Bitte geben Sie Ihre Telefonnummer ein!",
"Please input your real name!": "Bitte geben Sie Ihren persönlichen Namen ein!",
"Please select your country/region!": "Bitte wählen Sie Ihr Land/Ihre Region!",
diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json
index 20bafac5..37a7dfbe 100644
--- a/web/src/locales/en/data.json
+++ b/web/src/locales/en/data.json
@@ -10,10 +10,12 @@
"Edit Application": "Edit Application",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
- "Enable signin session - Tooltip": "Whether to preserve the session in Casdoor after login",
+ "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",
"Enable signup - Tooltip": "Enable signup - Tooltip",
"File uploaded successfully": "File uploaded successfully",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "Password ON",
"Password ON - Tooltip": "Password ON - Tooltip",
@@ -112,6 +114,7 @@
"Email": "Email",
"Email - Tooltip": "Email - Tooltip",
"Favicon - Tooltip": "Favicon - Tooltip",
+ "First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Forget URL - Tooltip",
"Home": "Home",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "Is enabled - Tooltip",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
+ "Last name": "Last name",
"Logo - Tooltip": "Logo - Tooltip",
"Master password": "Master password",
"Master password - Tooltip": "Master password - Tooltip",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Phone prefix - Tooltip",
"Preview": "Preview",
"Preview - Tooltip": "Preview - Tooltip",
+ "Products": "Products",
"Provider": "Provider",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Providers",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Syncers",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "Up",
"User": "User",
"User - Tooltip": "User - Tooltip",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "Resource type - Tooltip",
"Resources": "Resources"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "Access key",
"Access key - Tooltip": "Access key - Tooltip",
@@ -389,6 +415,8 @@
"Please input your address!": "Please input your address!",
"Please input your affiliation!": "Please input your affiliation!",
"Please input your display name!": "Please input your display name!",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Please input your phone number!",
"Please input your real name!": "Please input your real name!",
"Please select your country/region!": "Please select your country/region!",
diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json
index 95cb5528..d8a34cd3 100644
--- a/web/src/locales/fr/data.json
+++ b/web/src/locales/fr/data.json
@@ -14,6 +14,8 @@
"Enable signup": "Activer l'inscription",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "Fichier téléchargé avec succès",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "Mot de passe activé",
"Password ON - Tooltip": "Whether to allow password login",
@@ -112,6 +114,7 @@
"Email": "Courriel",
"Email - Tooltip": "email",
"Favicon - Tooltip": "Application icon",
+ "First name": "First name",
"Forget URL": "Oublier l'URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "Domicile",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "Est activé - infobulle",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Infobulle",
+ "Last name": "Last name",
"Logo - Tooltip": "App's image tag",
"Master password": "Mot de passe maître",
"Master password - Tooltip": "Mot de passe maître - Infobulle",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Aperçu",
"Preview - Tooltip": "The form in which the password is stored in the database",
+ "Products": "Products",
"Provider": "Fournisseur",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Fournisseurs",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Désolé, la page que vous avez visitée n'existe pas.",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Synchronisateurs",
"Timestamp": "Horodatage",
"Tokens": "Jetons",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "Monter",
"User": "Utilisateur",
"User - Tooltip": "Utilisateur - infobulle",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "Type de ressource - infobulle",
"Resources": "Ressource"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "Clé d'accès",
"Access key - Tooltip": "Touche d'accès - Infobulle",
@@ -389,6 +415,8 @@
"Please input your address!": "Veuillez saisir votre adresse !",
"Please input your affiliation!": "Veuillez entrer votre affiliation !",
"Please input your display name!": "Veuillez entrer votre nom d'affichage !",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Veuillez entrer votre numéro de téléphone!",
"Please input your real name!": "Veuillez entrer votre nom personnel !",
"Please select your country/region!": "Veuillez sélectionner votre pays/région!",
diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json
index b1e03c99..f38511cc 100644
--- a/web/src/locales/ja/data.json
+++ b/web/src/locales/ja/data.json
@@ -14,6 +14,8 @@
"Enable signup": "サインアップを有効にする",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "ファイルが正常にアップロードされました",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "パスワードON",
"Password ON - Tooltip": "Whether to allow password login",
@@ -112,6 +114,7 @@
"Email": "Eメールアドレス",
"Email - Tooltip": "email",
"Favicon - Tooltip": "Application icon",
+ "First name": "First name",
"Forget URL": "URLを忘れた",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "ホーム",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "有効にする - ツールチップ",
"LDAPs": "LDAP",
"LDAPs - Tooltip": "LDAP - ツールチップ",
+ "Last name": "Last name",
"Logo - Tooltip": "App's image tag",
"Master password": "マスターパスワード",
"Master password - Tooltip": "マスターパスワード - ツールチップ",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "プレビュー",
"Preview - Tooltip": "The form in which the password is stored in the database",
+ "Products": "Products",
"Provider": "プロバイダー",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "プロバイダー",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "申し訳ありませんが、訪問したページは存在しません。",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Syncers",
"Timestamp": "タイムスタンプ",
"Tokens": "トークン",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "上へ",
"User": "ユーザー",
"User - Tooltip": "ユーザー → ツールチップ",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "リソースタイプ - ツールチップ",
"Resources": "リソース"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "アクセスキー",
"Access key - Tooltip": "アクセスキー → ツールチップ",
@@ -389,6 +415,8 @@
"Please input your address!": "住所を入力してください!",
"Please input your affiliation!": "所属を入力してください!",
"Please input your display name!": "表示名を入力してください。",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "電話番号を入力してください!",
"Please input your real name!": "個人名を入力してください!",
"Please select your country/region!": "あなたの国/地域を選択してください!",
diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json
index 6eb83617..e2dffea4 100644
--- a/web/src/locales/ko/data.json
+++ b/web/src/locales/ko/data.json
@@ -14,6 +14,8 @@
"Enable signup": "Enable signup",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "File uploaded successfully",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "Password ON",
"Password ON - Tooltip": "Whether to allow password login",
@@ -112,6 +114,7 @@
"Email": "Email",
"Email - Tooltip": "email",
"Favicon - Tooltip": "Application icon",
+ "First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "Home",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "Is enabled - Tooltip",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
+ "Last name": "Last name",
"Logo - Tooltip": "App's image tag",
"Master password": "Master password",
"Master password - Tooltip": "Master password - Tooltip",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Preview",
"Preview - Tooltip": "The form in which the password is stored in the database",
+ "Products": "Products",
"Provider": "Provider",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Providers",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Syncers",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "Up",
"User": "User",
"User - Tooltip": "User - Tooltip",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "Resource type - Tooltip",
"Resources": "Resources"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "Access key",
"Access key - Tooltip": "Access key - Tooltip",
@@ -389,6 +415,8 @@
"Please input your address!": "Please input your address!",
"Please input your affiliation!": "Please input your affiliation!",
"Please input your display name!": "Please input your display name!",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Please input your phone number!",
"Please input your real name!": "Please input your real name!",
"Please select your country/region!": "Please select your country/region!",
diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json
index 65a142c9..2f053377 100644
--- a/web/src/locales/ru/data.json
+++ b/web/src/locales/ru/data.json
@@ -14,6 +14,8 @@
"Enable signup": "Включить регистрацию",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"File uploaded successfully": "Файл успешно загружен",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "Grant types - Tooltip",
"New Application": "New Application",
"Password ON": "Пароль ВКЛ",
"Password ON - Tooltip": "Whether to allow password login",
@@ -112,6 +114,7 @@
"Email": "Почта",
"Email - Tooltip": "email",
"Favicon - Tooltip": "Application icon",
+ "First name": "First name",
"Forget URL": "Забыть URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Home": "Домашний",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "Включено - Подсказка",
"LDAPs": "LDAPы",
"LDAPs - Tooltip": "LDAPs - Подсказки",
+ "Last name": "Last name",
"Logo - Tooltip": "App's image tag",
"Master password": "Мастер-пароль",
"Master password - Tooltip": "Мастер-пароль - Tooltip",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Предпросмотр",
"Preview - Tooltip": "The form in which the password is stored in the database",
+ "Products": "Products",
"Provider": "Поставщик",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Поставщики",
@@ -164,11 +169,14 @@
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Извините, посещенная вами страница не существует.",
+ "State": "State",
+ "State - Tooltip": "State - Tooltip",
"Swagger": "Swagger",
"Syncers": "Синхронизаторы",
"Timestamp": "Отметка времени",
"Tokens": "Жетоны",
"URL": "URL",
+ "URL - Tooltip": "URL - Tooltip",
"Up": "Вверх",
"User": "Пользователь",
"User - Tooltip": "Пользователь - Подсказка",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "Тип ресурса - Подсказка",
"Resources": "Ресурсы"
},
+ "product": {
+ "Currency": "Currency",
+ "Currency - Tooltip": "Currency - Tooltip",
+ "Edit Product": "Edit Product",
+ "Image": "Image",
+ "Image - Tooltip": "Image - Tooltip",
+ "New Product": "New Product",
+ "Payment providers": "Payment providers",
+ "Payment providers - Tooltip": "Payment providers - Tooltip",
+ "Price": "Price",
+ "Price - Tooltip": "Price - Tooltip",
+ "Quantity": "Quantity",
+ "Quantity - Tooltip": "Quantity - Tooltip",
+ "Sold": "Sold",
+ "Sold - Tooltip": "Sold - Tooltip",
+ "Tag": "Tag",
+ "Tag - Tooltip": "Tag - Tooltip"
+ },
"provider": {
"Access key": "Ключ доступа",
"Access key - Tooltip": "Ключ доступа - Подсказка",
@@ -389,6 +415,8 @@
"Please input your address!": "Пожалуйста, введите ваш адрес!",
"Please input your affiliation!": "Пожалуйста, введите вашу партнерство!",
"Please input your display name!": "Пожалуйста, введите ваше отображаемое имя!",
+ "Please input your first name!": "Please input your first name!",
+ "Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Пожалуйста, введите ваш номер телефона!",
"Please input your real name!": "Пожалуйста, введите ваше личное имя!",
"Please select your country/region!": "Пожалуйста, выберите вашу страну/регион!",
diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json
index 0edad100..2cb028c8 100644
--- a/web/src/locales/zh/data.json
+++ b/web/src/locales/zh/data.json
@@ -14,6 +14,8 @@
"Enable signup": "启用注册",
"Enable signup - Tooltip": "是否允许用户注册",
"File uploaded successfully": "文件上传成功",
+ "Grant types": "Grant types",
+ "Grant types - Tooltip": "选择允许哪些OAuth协议中的Grant types",
"New Application": "添加应用",
"Password ON": "开启密码",
"Password ON - Tooltip": "是否允许密码登录",
@@ -112,6 +114,7 @@
"Email": "电子邮箱",
"Email - Tooltip": "电子邮件:",
"Favicon - Tooltip": "网站的图标",
+ "First name": "名字",
"Forget URL": "忘记密码URL",
"Forget URL - Tooltip": "忘记密码URL",
"Home": "首页",
@@ -122,6 +125,7 @@
"Is enabled - Tooltip": "是否启用",
"LDAPs": "LDAP",
"LDAPs - Tooltip": "LDAPs",
+ "Last name": "姓氏",
"Logo - Tooltip": "应用程序向外展示的图标",
"Master password": "万能密码",
"Master password - Tooltip": "可用来登录该组织下的所有用户,方便管理员以该用户身份登录,以解决技术问题",
@@ -146,6 +150,7 @@
"Phone prefix - Tooltip": "移动电话号码前缀,用于区分国家或地区",
"Preview": "预览",
"Preview - Tooltip": "预览",
+ "Products": "商品",
"Provider": "提供商",
"Provider - Tooltip": "第三方登录需要配置的提供方",
"Providers": "提供商",
@@ -164,11 +169,14 @@
"Signup application": "注册应用",
"Signup application - Tooltip": "表示用户注册时通过哪个应用注册的",
"Sorry, the page you visited does not exist.": "抱歉,您访问的页面不存在",
+ "State": "状态",
+ "State - Tooltip": "状态",
"Swagger": "API文档",
"Syncers": "同步器",
"Timestamp": "时间戳",
"Tokens": "令牌",
"URL": "链接",
+ "URL - Tooltip": "URL链接",
"Up": "上移",
"User": "用户",
"User - Tooltip": "用户 - 工具提示",
@@ -263,6 +271,24 @@
"Resource type - Tooltip": "授权资源的类型",
"Resources": "资源"
},
+ "product": {
+ "Currency": "币种",
+ "Currency - Tooltip": "币种 - 工具提示",
+ "Edit Product": "编辑商品",
+ "Image": "图片",
+ "Image - Tooltip": "图片 - 工具提示",
+ "New Product": "添加商品",
+ "Payment providers": "支付提供商",
+ "Payment providers - Tooltip": "支付提供商 - 工具提示",
+ "Price": "价格",
+ "Price - Tooltip": "价格 - 工具提示",
+ "Quantity": "库存",
+ "Quantity - Tooltip": "库存 - 工具提示",
+ "Sold": "售出",
+ "Sold - Tooltip": "售出 - 工具提示",
+ "Tag": "标签",
+ "Tag - Tooltip": "标签 - 工具提示"
+ },
"provider": {
"Access key": "访问密钥",
"Access key - Tooltip": "Access key",
@@ -389,6 +415,8 @@
"Please input your address!": "请输入您的地址!",
"Please input your affiliation!": "请输入您所在的工作单位!",
"Please input your display name!": "请输入您的显示名称!",
+ "Please input your first name!": "请输入您的名字!",
+ "Please input your last name!": "请输入您的姓氏!",
"Please input your phone number!": "请输入您的手机号码!",
"Please input your real name!": "请输入您的姓名!",
"Please select your country/region!": "请选择您的国家/地区",