ProductListPage.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Copyright 2022 The Casdoor Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import React from "react";
  15. import {Link} from "react-router-dom";
  16. import {Button, Col, List, Row, Table, Tooltip} from "antd";
  17. import moment from "moment";
  18. import * as Setting from "./Setting";
  19. import * as ProductBackend from "./backend/ProductBackend";
  20. import i18next from "i18next";
  21. import BaseListPage from "./BaseListPage";
  22. import {EditOutlined} from "@ant-design/icons";
  23. import PopconfirmModal from "./common/modal/PopconfirmModal";
  24. class ProductListPage extends BaseListPage {
  25. newProduct() {
  26. const randomName = Setting.getRandomName();
  27. return {
  28. owner: "admin",
  29. name: `product_${randomName}`,
  30. createdTime: moment().format(),
  31. displayName: `New Product - ${randomName}`,
  32. image: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
  33. tag: "Casdoor Summit 2022",
  34. currency: "USD",
  35. price: 300,
  36. quantity: 99,
  37. sold: 10,
  38. providers: [],
  39. state: "Published",
  40. };
  41. }
  42. addProduct() {
  43. const newProduct = this.newProduct();
  44. ProductBackend.addProduct(newProduct)
  45. .then((res) => {
  46. if (res.status === "ok") {
  47. this.props.history.push({pathname: `/products/${newProduct.name}`, mode: "add"});
  48. Setting.showMessage("success", i18next.t("general:Successfully added"));
  49. } else {
  50. Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
  51. }
  52. })
  53. .catch(error => {
  54. Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
  55. });
  56. }
  57. deleteProduct(i) {
  58. ProductBackend.deleteProduct(this.state.data[i])
  59. .then((res) => {
  60. if (res.status === "ok") {
  61. Setting.showMessage("success", i18next.t("general:Successfully deleted"));
  62. this.setState({
  63. data: Setting.deleteRow(this.state.data, i),
  64. pagination: {total: this.state.pagination.total - 1},
  65. });
  66. } else {
  67. Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
  68. }
  69. })
  70. .catch(error => {
  71. Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
  72. });
  73. }
  74. renderTable(products) {
  75. const columns = [
  76. {
  77. title: i18next.t("general:Name"),
  78. dataIndex: "name",
  79. key: "name",
  80. width: "140px",
  81. fixed: "left",
  82. sorter: true,
  83. ...this.getColumnSearchProps("name"),
  84. render: (text, record, index) => {
  85. return (
  86. <Link to={`/products/${text}`}>
  87. {text}
  88. </Link>
  89. );
  90. },
  91. },
  92. {
  93. title: i18next.t("general:Created time"),
  94. dataIndex: "createdTime",
  95. key: "createdTime",
  96. width: "160px",
  97. sorter: true,
  98. render: (text, record, index) => {
  99. return Setting.getFormattedDate(text);
  100. },
  101. },
  102. {
  103. title: i18next.t("general:Display name"),
  104. dataIndex: "displayName",
  105. key: "displayName",
  106. width: "170px",
  107. sorter: true,
  108. ...this.getColumnSearchProps("displayName"),
  109. },
  110. {
  111. title: i18next.t("product:Image"),
  112. dataIndex: "image",
  113. key: "image",
  114. width: "170px",
  115. render: (text, record, index) => {
  116. return (
  117. <a target="_blank" rel="noreferrer" href={text}>
  118. <img src={text} alt={text} width={150} />
  119. </a>
  120. );
  121. },
  122. },
  123. {
  124. title: i18next.t("user:Tag"),
  125. dataIndex: "tag",
  126. key: "tag",
  127. width: "160px",
  128. sorter: true,
  129. ...this.getColumnSearchProps("tag"),
  130. },
  131. {
  132. title: i18next.t("payment:Currency"),
  133. dataIndex: "currency",
  134. key: "currency",
  135. width: "120px",
  136. sorter: true,
  137. ...this.getColumnSearchProps("currency"),
  138. },
  139. {
  140. title: i18next.t("product:Price"),
  141. dataIndex: "price",
  142. key: "price",
  143. width: "120px",
  144. sorter: true,
  145. ...this.getColumnSearchProps("price"),
  146. },
  147. {
  148. title: i18next.t("product:Quantity"),
  149. dataIndex: "quantity",
  150. key: "quantity",
  151. width: "120px",
  152. sorter: true,
  153. ...this.getColumnSearchProps("quantity"),
  154. },
  155. {
  156. title: i18next.t("product:Sold"),
  157. dataIndex: "sold",
  158. key: "sold",
  159. width: "120px",
  160. sorter: true,
  161. ...this.getColumnSearchProps("sold"),
  162. },
  163. {
  164. title: i18next.t("general:State"),
  165. dataIndex: "state",
  166. key: "state",
  167. width: "120px",
  168. sorter: true,
  169. ...this.getColumnSearchProps("state"),
  170. },
  171. {
  172. title: i18next.t("product:Payment providers"),
  173. dataIndex: "providers",
  174. key: "providers",
  175. width: "500px",
  176. ...this.getColumnSearchProps("providers"),
  177. render: (text, record, index) => {
  178. const providers = text;
  179. if (providers.length === 0) {
  180. return `(${i18next.t("general:empty")})`;
  181. }
  182. const half = Math.floor((providers.length + 1) / 2);
  183. const getList = (providers) => {
  184. return (
  185. <List
  186. size="small"
  187. locale={{emptyText: " "}}
  188. dataSource={providers}
  189. renderItem={(providerName, i) => {
  190. return (
  191. <List.Item>
  192. <div style={{display: "inline"}}>
  193. <Tooltip placement="topLeft" title="Edit">
  194. <Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/providers/${providerName}`)} />
  195. </Tooltip>
  196. <Link to={`/providers/${providerName}`}>
  197. {providerName}
  198. </Link>
  199. </div>
  200. </List.Item>
  201. );
  202. }}
  203. />
  204. );
  205. };
  206. return (
  207. <div>
  208. <Row>
  209. <Col span={12}>
  210. {
  211. getList(providers.slice(0, half))
  212. }
  213. </Col>
  214. <Col span={12}>
  215. {
  216. getList(providers.slice(half))
  217. }
  218. </Col>
  219. </Row>
  220. </div>
  221. );
  222. },
  223. },
  224. {
  225. title: i18next.t("general:Action"),
  226. dataIndex: "",
  227. key: "op",
  228. width: "230px",
  229. fixed: (Setting.isMobile()) ? "false" : "right",
  230. render: (text, record, index) => {
  231. return (
  232. <div>
  233. <Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/products/${record.name}/buy`)}>{i18next.t("product:Buy")}</Button>
  234. <Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/products/${record.name}`)}>{i18next.t("general:Edit")}</Button>
  235. <PopconfirmModal
  236. title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
  237. onConfirm={() => this.deleteProduct(index)}
  238. >
  239. </PopconfirmModal>
  240. </div>
  241. );
  242. },
  243. },
  244. ];
  245. const paginationProps = {
  246. total: this.state.pagination.total,
  247. showQuickJumper: true,
  248. showSizeChanger: true,
  249. showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
  250. };
  251. return (
  252. <div>
  253. <Table scroll={{x: "max-content"}} columns={columns} dataSource={products} rowKey="name" size="middle" bordered pagination={paginationProps}
  254. title={() => (
  255. <div>
  256. {i18next.t("general:Products")}&nbsp;&nbsp;&nbsp;&nbsp;
  257. <Button type="primary" size="small" onClick={this.addProduct.bind(this)}>{i18next.t("general:Add")}</Button>
  258. </div>
  259. )}
  260. loading={this.state.loading}
  261. onChange={this.handleTableChange}
  262. />
  263. </div>
  264. );
  265. }
  266. fetch = (params = {}) => {
  267. let field = params.searchedColumn, value = params.searchText;
  268. const sortField = params.sortField, sortOrder = params.sortOrder;
  269. if (params.type !== undefined && params.type !== null) {
  270. field = "type";
  271. value = params.type;
  272. }
  273. this.setState({loading: true});
  274. ProductBackend.getProducts("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
  275. .then((res) => {
  276. if (res.status === "ok") {
  277. this.setState({
  278. loading: false,
  279. data: res.data,
  280. pagination: {
  281. ...params.pagination,
  282. total: res.data2,
  283. },
  284. searchText: params.searchText,
  285. searchedColumn: params.searchedColumn,
  286. });
  287. } else {
  288. if (Setting.isResponseDenied(res)) {
  289. this.setState({
  290. loading: false,
  291. isAuthorized: false,
  292. });
  293. }
  294. }
  295. });
  296. };
  297. }
  298. export default ProductListPage;