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")}: + + + + {this.state.product.image} + + + + + + + + {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 ( + + {text} + + ) + } + }, + { + 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 ( + +
+ +
+
+ ) + }} + /> + ) + } + + 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!": "请选择您的国家/地区",