diff --git a/package.json b/package.json
index 39b9ea8..bbd3a72 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.0.0",
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
"@types/classnames": "^2.2.10",
"@types/jest": "^24.9.0",
"@types/lodash": "^4.14.149",
@@ -47,7 +48,6 @@
"@types/react-router-dom": "^5.1.3",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
"babel-jest": "28.1.1",
"babel-loader": "^8.2.3",
"babel-plugin-dynamic-import-node": "^2.2.0",
@@ -55,20 +55,20 @@
"babel-plugin-react-require": "^3.0.0",
"babel-plugin-react-transform": "^3.0.0",
"cookie-parser": "^1.4.3",
- "css-loader": "^5.2.6",
"css-hot-loader": "^1.4.4",
+ "css-loader": "^5.2.6",
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.9.1",
"eslint": "^7.11.0",
"eslint-import-resolver-webpack": "^0.13.0",
- "eslint-webpack-plugin": "^3.1.1",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.7.0",
"eslint-rich-reporter": "^0.0.10",
+ "eslint-webpack-plugin": "^3.1.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
- "friendly-errors-webpack-plugin": "^1.7.0",
"fork-ts-checker-webpack-plugin": "^6.5.0",
+ "friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^1.3.1",
"identity-obj-proxy": "^3.0.0",
@@ -98,6 +98,7 @@
"axios": "^0.21.1",
"classnames": "^2.2.5",
"commander": "^5.1.0",
+ "immer": "^9.0.15",
"lodash": "^4.17.19",
"lodash-decorators": "^4.5.0",
"moment": "^2.29.1",
@@ -109,7 +110,8 @@
"recoil": "^0.7.4",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
- "typescript": "^4.4.2"
+ "typescript": "^4.4.2",
+ "use-immer": "^0.7.0"
},
"husky": {
"hooks": {
diff --git a/src/components/App/index.js b/src/components/App/index.js
index 4eeff5a..6103b40 100644
--- a/src/components/App/index.js
+++ b/src/components/App/index.js
@@ -2,6 +2,7 @@ import {BrowserRouter, Route, Switch, Redirect} from 'react-router-dom';
import {Provider} from 'react-redux';
import {RecoilRoot} from 'recoil';
import {store} from '@/store';
+import JiraGenerator from '@/components/Home/JiraGenerator';
import {Home} from '..';
import styles from './styles.less';
@@ -12,6 +13,7 @@ const App = () => (
+
diff --git a/src/components/Home/JiraGenerator.less b/src/components/Home/JiraGenerator.less
new file mode 100644
index 0000000..581e0cd
--- /dev/null
+++ b/src/components/Home/JiraGenerator.less
@@ -0,0 +1,10 @@
+.list {
+ display: flex;
+ width: 100%;
+ align-items: center;
+ justify-content: flex-start;
+
+ & > * + * {
+ margin-left: 20px;
+ }
+}
diff --git a/src/components/Home/JiraGenerator.tsx b/src/components/Home/JiraGenerator.tsx
new file mode 100644
index 0000000..e9e9d62
--- /dev/null
+++ b/src/components/Home/JiraGenerator.tsx
@@ -0,0 +1,121 @@
+import {Input, InputNumber, Button, List, Divider} from 'antd';
+import {useCallback, useState} from 'react';
+import {cloneDeep, partial} from 'lodash';
+// why unresolved
+// eslint-disable-next-line import/no-unresolved
+import {produce} from 'immer';
+import styles from './JiraGenerator.less';
+
+// - [FE] / assignee:"wenwei.zhang@shopee.com" cfield:"Story Points:1"
+
+const templateRow = {
+ summary: '',
+ assignee: '',
+ storyPoint: 1,
+};
+
+type Row = typeof templateRow;
+
+const JiraGenerator = () => {
+ const [list, setList] = useState([cloneDeep(templateRow)]);
+ const [result, setResult] = useState([]);
+
+
+ const handleAdd = useCallback(
+ () => {
+ setList(prev => [...prev, cloneDeep(templateRow)]);
+ },
+ []
+ );
+
+ const handleRowChange = useCallback(
+ (index: number, key: string, value: string | number) => {
+ const result = produce(list, draft => {
+ const target = draft[index];
+ target[key] = value;
+ });
+ setList(result);
+ },
+ [list]
+ );
+
+ const handleInputEvent = useCallback(
+ (index: number, key: string, e) => {
+ handleRowChange(index, key, e.target.value);
+ },
+ [handleRowChange]
+ );
+
+ const handleGenerateClick = useCallback(
+ () => {
+ const str = list.map(item => (
+ // eslint-disable-next-line max-len
+ `- [FE]${item.summary} / assignee:"${item.assignee}@shopee.com" cfield:"Story Points:${item.storyPoint}"`
+ )).join('\n');
+ setResult(prev => [str, ...prev]);
+ },
+ [list]
+ );
+
+ return (
+
+
Jira Generator
+
{
+ return (
+
+
+ Summary:
+
+
+
+ Assignee:
+
+ @shopee.com
+
+
+ {'Story Points:'}
+
+
+
+ );
+ }}
+ />
+
+
+
+
+
Result:
+
(
+
+ {item}
+
+ )}
+ />
+
+
+ );
+};
+
+export default JiraGenerator;
diff --git a/yarn.lock b/yarn.lock
index c22c134..ab4bb23 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5712,6 +5712,11 @@ image-size@~0.5.0:
resolved "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1569841504754&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=
+immer@^9.0.15:
+ version "9.0.15"
+ resolved "https://registry.npmmirror.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
+ integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==
+
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/import-fresh/download/import-fresh-2.0.0.tgz?cache=0&sync_timestamp=1573665028675&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimport-fresh%2Fdownload%2Fimport-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@@ -10866,6 +10871,11 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
+use-immer@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.npmmirror.com/use-immer/-/use-immer-0.7.0.tgz#e3bfbb806b5e3ff6e37441be74c306d91c1e0962"
+ integrity sha512-Re4hjrP3a/2ABZjAc0b7AK9s626bnO+H33RO2VUhiDZ2StBz5B663K6WNNlr4QtHWaGUmvLpwt3whFvvWuolQw==
+
use@^3.1.0:
version "3.1.1"
resolved "https://registry.npm.taobao.org/use/download/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"