Commit ecf247f9 by Sergey

initial fixed widget

parent a992c8ee
# react-feedback-widget
# react-atma-feedback-test
> Made with create-react-library
[![NPM](https://img.shields.io/npm/v/react-feedback-widget.svg)](https://www.npmjs.com/package/react-feedback-widget) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
[![NPM](https://img.shields.io/npm/v/react-atma-feedback-test.svg)](https://www.npmjs.com/package/react-atma-feedback-test) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## Install
```bash
npm install --save react-feedback-widget
npm install --save https://gitlab.atma.company/sergey/react-feedback-widget.git
```
## Usage
```jsx
import React, { Component } from 'react'
import MyComponent from 'react-feedback-widget'
import 'react-feedback-widget/dist/index.css'
class Example extends Component {
render() {
return <MyComponent />
}
}
import React from 'react'
import {FeedbackWidget} from 'react-feedback-widget'
import 'react-atma-feedback-test/dist/index.css'
const App = () => {
return (
<FeedbackWidget
baseSite='https://tips.atmaguru.online'
sendIdeaApi='/api/feedback/add-idea/'
uploadUrl='/api/web/url-upload/'
categoriesProps={[]}
typesProps={[]}
/>
)
};
```
## License
MIT © [jaxi](https://github.com/jaxi)
MIT © [mrjaxi](https://github.com/mrjaxi)
import React from 'react'
import { ExampleComponent } from 'react-feedback-widget'
import { FeedbackWidget } from 'react-feedback-widget'
import 'react-feedback-widget/dist/index.css'
const App = () => {
return <ExampleComponent text="Create React Library Example 😄" />
}
return (
<FeedbackWidget
baseSite='https://tips.atmaguru.online'
sendIdeaApi='/api/feedback/add-idea/'
uploadUrl='/api/web/url-upload/'
categoriesProps={[]}
typesProps={[]}
/>
)
};
export default App
import './index.css'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
......
......@@ -12,8 +12,8 @@
"node": ">=10"
},
"scripts": {
"build": "microbundle-crl --no-compress --format modern,cjs",
"start": "microbundle-crl watch --no-compress --format modern,cjs",
"build": "microbundle-crl --no-compress --format modern,cjs --css-modules false",
"start": "microbundle-crl watch --no-compress --format modern,cjs --css-modules false",
"prepare": "run-s build",
"test": "run-s test:unit test:lint test:build",
"test:build": "run-s build",
......@@ -27,7 +27,6 @@
"react": "^16.0.0"
},
"devDependencies": {
"microbundle-crl": "^0.13.10",
"babel-eslint": "^10.0.3",
"cross-env": "^7.0.2",
"eslint": "^6.8.0",
......@@ -41,6 +40,7 @@
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-standard": "^4.0.1",
"gh-pages": "^2.2.0",
"microbundle-crl": "^0.13.10",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"react": "^16.13.1",
......@@ -49,5 +49,10 @@
},
"files": [
"dist"
]
],
"dependencies": {
"@ant-design/icons": "^4.7.0",
"antd": "^4.22.3",
"axios": "^0.27.2"
}
}
import React from 'react'
import styles from './styles.module.css'
import React, {useState, useEffect} from "react";
import 'antd/dist/antd.css';
import './styles.module.css'
import {Button, Form, Input, Modal, Result, Select, Tooltip, Upload} from "antd";
import axios from "axios";
import {UploadOutlined, WechatOutlined} from "@ant-design/icons";
const {Option} = Select;
const {TextArea} = Input;
import {notification} from 'antd'
import {
CheckOutlined,
WarningOutlined,
CloseOutlined
} from "@ant-design/icons";
export const ExampleComponent = ({ text }) => {
return <div className={styles.test}>Example Component: {text}</div>
}
export const FeedbackWidget = ({
baseSite = 'https://tips.atmaguru.online',
sendIdeaApi = '/api/feedback/add-idea/',
uploadUrl = '/api/web/url-upload/',
categoriesProps = [], typesProps = []
}) => {
const [formRef] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false)
const [categories, setCategories] = useState(categoriesProps)
const [types, setTypes] = useState(typesProps)
const [loadingSubmit, setLoadingSubmit] = useState(false)
const [completed, setCompleted] = useState(false)
const [url, setUrl] = useState(baseSite)
const [footer, setFooter] = useState([])
const [uploadLink, setUploadLink] = useState(uploadUrl)
const [fileList, setFileList] = useState([])
useEffect(() => {
setFooter(
[
<Button key={2} onClick={() => {
manageModal('cancel')
}}>Закрыть</Button>,
<Button type="primary" form="feedback" key="submit" htmlType="submit"
loading={loadingSubmit}>Отправить</Button>
]);
}, []);
const openNotification = (message, description = '', type = 'success') => {
notification.open({
message: message,
description: description,
icon: type === 'success' ? <CheckOutlined style={{color: '#108ee9'}}/> : (type === 'warn' ?
<WarningOutlined style={{color: 'darkorange'}}/> : <CloseOutlined style={{color: "red"}}/>),
});
};
const manageModal = (type = 'cancel') => {
if (type === 'open') {
setIsModalVisible(true)
} else {
if (completed) {
setCompleted(false);
setUrl('');
setIsModalVisible(false);
setFooter(
[
<Button key={2} onClick={() => {
manageModal('cancel')
}}>Закрыть</Button>,
<Button type="primary" form="feedback" key="submit" htmlType="submit"
loading={loadingSubmit}>Отправить</Button>
]
);
} else {
setIsModalVisible(false);
}
}
}
const submit = (values) => {
setLoadingSubmit(true);
values.href = window.location.href;
values.photo = '';
if (fileList.length > 0) {
fileList.map((item) => {
values.photo += item.response.file_path + ';';
})
}
axios.post(sendIdeaApi, values, {withCredentials: true})
.then(response => {
if (response.data.state === "success") {
formRef.resetFields();
setCompleted(true);
setUrl(response.data.url);
setLoadingSubmit(false);
setFooter(false);
setFileList([])
openNotification('Сохранено');
} else {
formRef.resetFields();
setCompleted(false);
setLoadingSubmit(false);
setIsModalVisible(false);
setFileList([]);
openNotification('Ошибка', response.data.message, 'error');
}
})
}
const renderOptions = (items) => {
let _options = [];
if (typeof items !== 'undefined' && items.length > 0) {
items.map((item) => {
_options.push(
<Option value={item.id} key={item.id}>{item.name}</Option>
);
})
}
return _options;
}
return (
<div>
<Tooltip
mouseLeaveDelay={0}
title={'Предложить идею или сообщить об ошибке'}
placement={'left'}>
<Button
size="large"
style={{position: "fixed", right: '0', top: '50%', zIndex: '999'}}
type="primary"
icon={
<WechatOutlined style={{fontSize: '27px'}}/>
}
onClick={() => manageModal('open')}
/>
</Tooltip>
<Modal
className={'feedback-modal'}
title="Есть идея?"
visible={isModalVisible}
onCancel={() => manageModal('cancel')}
footer={footer}
width={550}
>
{!completed &&
<Form
form={formRef}
name="feedback"
layout={'vertical'}
onFinish={(values) => submit(values)}
>
<div style={{marginBottom: '20px'}}><span>Вы можете предложить свою идею по улучшению платформы или приложения, рассказать о своем опыте или просто сообщить об ошибке.
<br/><br/>Проголосовать за идеи других пользователей <a
href={baseSite} target={'_blank'}>тут</a> </span>
</div>
<Form.Item
label={""}
name="title"
rules={[
{
required: true,
message: 'Введите заголовок',
},
{min: 5, message: 'Минимум 5 символов'},
]}
>
<Input size={'middle'} style={{ borderRadius: 8 }}
placeholder={"Заголовок"}/>
</Form.Item>
<Form.Item
label={""}
name="category"
rules={[
{
required: true,
message: 'Выберите категорию',
},
]}
>
<Select
size="middle"
placeholder="Выберите категорию"
>
{renderOptions(categories)}
</Select>
</Form.Item>
<Form.Item
label={""}
name="type"
rules={[
{
required: true,
message: 'Выберите тип',
},
]}
>
<Select
size="middle"
placeholder="Выберите тип"
>
{renderOptions(types)}
</Select>
</Form.Item>
<Form.Item
label={""}
name="description"
rules={[
{
required: true,
message: 'Описание обязательно',
},
{min: 10, message: 'Минимум 10 символов'},
]}
>
<TextArea style={{ padding: '8px 15px', borderRadius: 8 }}
placeholder={"Описание"}/>
</Form.Item>
<Upload
fileList={fileList}
action={uploadLink}
listType="text"
accept={'image/png,image/jpeg'}
onChange={(info) => {
setFileList(info.fileList);
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
console.log(info);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} ошибка загрузки файла.`);
}
}}
onRemove={(file) => {
let files = (fileList || []).filter(v => v.url !== file.url);
setFileList(files);
}}
multiple={true}
>
<Button shape={'round'} size={'middle'} icon={<UploadOutlined/>}>Загрузить файл</Button>
</Upload>
</Form>
}
{completed &&
<Result
status="success"
title="Ваше предложение отправлено"
subTitle="Вы можете отслеживать его статус нажав по кнопке ниже"
extra={[
<Button key="buy" onClick={() => {
setCompleted(false);
setUrl('');
setFooter([
<Button key={2} onClick={() => {
manageModal('cancel')
}}>Закрыть</Button>,
<Button type="primary" form="feedback" key="submit" htmlType="submit"
loading={loadingSubmit}>Отправить</Button>
]);
}}>Создать еще</Button>,
<Button key="buy" onClick={() => {
manageModal('cancel')
}}>Закрыть</Button>,
<Button type="primary" key="console" style={{marginTop: '10px'}} onClick={() => {
window.open(url)
}}>
Посмотреть моё предложение
</Button>,
]}
/>
}
</Modal>
</div>
);
};
export default FeedbackWidget;
/* add css module styles here (optional) */
.feedback-modal .ant-form-item-label {
display: none!important;
}
.feedback-modal .ant-select-selector {
border-radius: 8px!important;
}
.feedback-modal .ant-form-item {
margin-bottom: 15px!important;
}
.test {
margin: 2em;
padding: 0.5em;
border: 2px solid #000;
font-size: 2em;
text-align: center;
.feedback-modal .ant-modal-content {
position: relative!important;
background-color: #fff!important;
background-clip: padding-box!important;
border: 0!important;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05)!important;
pointer-events: auto!important;
border-radius: 8px!important;
}
.feedback-modal .ant-modal-header {
padding: 16px 24px!important;
color: rgba(0, 0, 0, 0.85)!important;
background: #fff!important;
border-bottom: 1px solid #f0f0f0!important;
border-radius: 8px!important;
}
.feedback-modal .ant-btn {
border-radius: 8px!important;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment