listmonk/frontend/src/Lists.js

426 lines
11 KiB
JavaScript
Raw Normal View History

2018-10-25 15:51:47 +02:00
import React from "react"
import { Link } from "react-router-dom"
2019-03-09 08:46:47 +01:00
import {
Row,
Col,
Modal,
Form,
Input,
Select,
Button,
Table,
Icon,
Tooltip,
Tag,
Popconfirm,
Spin,
notification
} from "antd"
2018-10-25 15:51:47 +02:00
import Utils from "./utils"
import * as cs from "./constants"
const tagColors = {
2019-03-09 08:46:47 +01:00
private: "orange",
public: "green"
}
2018-10-25 15:51:47 +02:00
class CreateFormDef extends React.PureComponent {
2019-03-09 08:46:47 +01:00
state = {
confirmDirty: false,
modalWaiting: false
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
// Handle create / edit form submission.
handleSubmit = e => {
e.preventDefault()
this.props.form.validateFields((err, values) => {
if (err) {
return
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
this.setState({ modalWaiting: true })
if (this.props.formType === cs.FormCreate) {
// Create a new list.
this.props
.modelRequest(
cs.ModelLists,
cs.Routes.CreateList,
cs.MethodPost,
values
)
.then(() => {
notification["success"]({
placement: cs.MsgPosition,
message: "List created",
description: `"${values["name"]}" created`
})
this.props.fetchRecords()
this.props.onClose()
this.setState({ modalWaiting: false })
})
.catch(e => {
notification["error"]({ message: "Error", description: e.message })
this.setState({ modalWaiting: false })
})
} else {
// Edit a list.
this.props
.modelRequest(cs.ModelLists, cs.Routes.UpdateList, cs.MethodPut, {
...values,
id: this.props.record.id
})
.then(() => {
notification["success"]({
placement: cs.MsgPosition,
message: "List modified",
description: `"${values["name"]}" modified`
})
this.props.fetchRecords()
this.props.onClose()
this.setState({ modalWaiting: false })
})
.catch(e => {
notification["error"]({
placement: cs.MsgPosition,
message: "Error",
description: e.message
})
this.setState({ modalWaiting: false })
})
}
})
}
2019-03-09 08:46:47 +01:00
modalTitle(formType, record) {
if (formType === cs.FormCreate) {
return "Create a list"
}
2019-03-09 08:46:47 +01:00
return (
<div>
<Tag
color={
tagColors.hasOwnProperty(record.type) ? tagColors[record.type] : ""
}
>
{record.type}
</Tag>{" "}
{record.name}
<br />
<span className="text-tiny text-grey">
ID {record.id} / UUID {record.uuid}
</span>
</div>
)
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
render() {
const { formType, record, onClose } = this.props
const { getFieldDecorator } = this.props.form
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
const formItemLayout = {
labelCol: { xs: { span: 16 }, sm: { span: 4 } },
wrapperCol: { xs: { span: 16 }, sm: { span: 18 } }
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
if (formType === null) {
return null
2018-10-25 15:51:47 +02:00
}
2019-03-09 08:46:47 +01:00
return (
<Modal
visible={true}
title={this.modalTitle(this.state.form, record)}
okText={this.state.form === cs.FormCreate ? "Create" : "Save"}
confirmLoading={this.state.modalWaiting}
onCancel={onClose}
onOk={this.handleSubmit}
>
<div id="modal-alert-container" />
<Spin
spinning={this.props.reqStates[cs.ModelLists] === cs.StatePending}
>
<Form onSubmit={this.handleSubmit}>
<Form.Item {...formItemLayout} label="Name">
{getFieldDecorator("name", {
initialValue: record.name,
rules: [{ required: true }]
2019-03-28 13:34:27 +01:00
})(<Input autoFocus maxLength={200} />)}
2019-03-09 08:46:47 +01:00
</Form.Item>
<Form.Item
{...formItemLayout}
name="type"
label="Type"
extra="Public lists are open to the world to subscribe"
>
{getFieldDecorator("type", {
initialValue: record.type ? record.type : "private",
rules: [{ required: true }]
})(
<Select style={{ maxWidth: 120 }}>
<Select.Option value="private">Private</Select.Option>
<Select.Option value="public">Public</Select.Option>
</Select>
)}
</Form.Item>
<Form.Item
{...formItemLayout}
label="Tags"
extra="Hit Enter after typing a word to add multiple tags"
>
{getFieldDecorator("tags", { initialValue: record.tags })(
<Select mode="tags" />
)}
</Form.Item>
</Form>
</Spin>
</Modal>
)
}
2018-10-25 15:51:47 +02:00
}
const CreateForm = Form.create()(CreateFormDef)
class Lists extends React.PureComponent {
2019-05-14 13:11:05 +02:00
defaultPerPage = 20
2019-03-09 08:46:47 +01:00
state = {
formType: null,
2019-05-14 13:11:05 +02:00
record: {},
queryParams: {}
}
// Pagination config.
paginationOptions = {
hideOnSinglePage: false,
showSizeChanger: true,
showQuickJumper: true,
defaultPageSize: this.defaultPerPage,
pageSizeOptions: ["20", "50", "70", "100"],
position: "both",
showTotal: (total, range) => `${range[0]} to ${range[1]} of ${total}`,
onChange: (page, perPage) => {
this.fetchRecords({ page: page, per_page: perPage })
},
onShowSizeChange: (page, perPage) => {
this.fetchRecords({ page: page, per_page: perPage })
}
2019-03-09 08:46:47 +01:00
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
constructor(props) {
super(props)
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
this.columns = [
{
title: "Name",
dataIndex: "name",
sorter: true,
width: "40%",
render: (text, record) => {
const out = []
out.push(
<div className="name" key={`name-${record.id}`}>
2019-03-28 13:34:27 +01:00
<a role="button" onClick={() => this.handleShowEditForm(record)}>
{text}
</a>
2019-03-09 08:46:47 +01:00
</div>
)
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
if (record.tags.length > 0) {
for (let i = 0; i < record.tags.length; i++) {
out.push(<Tag key={`tag-${i}`}>{record.tags[i]}</Tag>)
2018-10-25 15:51:47 +02:00
}
2019-03-09 08:46:47 +01:00
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
return out
}
},
{
title: "Type",
dataIndex: "type",
width: "10%",
render: (type, _) => {
let color = type === "private" ? "orange" : "green"
return <Tag color={color}>{type}</Tag>
}
},
{
title: "Subscribers",
dataIndex: "subscriber_count",
width: "15%",
align: "center",
render: (text, record) => {
return (
<div className="name" key={`name-${record.id}`}>
<Link to={`/subscribers/lists/${record.id}`}>{text}</Link>
</div>
)
}
},
{
title: "Created",
dataIndex: "created_at",
render: (date, _) => {
return Utils.DateString(date)
}
},
{
title: "Updated",
dataIndex: "updated_at",
render: (date, _) => {
return Utils.DateString(date)
}
},
{
title: "",
dataIndex: "actions",
width: "10%",
render: (text, record) => {
return (
<div className="actions">
<Tooltip title="Send a campaign">
<Link to={`/campaigns/new?list_id=${record.id}`}>
2019-03-09 08:46:47 +01:00
<Icon type="rocket" />
</Link>
2019-03-09 08:46:47 +01:00
</Tooltip>
<Tooltip title="Edit list">
<a
role="button"
onClick={() => this.handleShowEditForm(record)}
>
<Icon type="edit" />
</a>
</Tooltip>
<Popconfirm
title="Are you sure?"
onConfirm={() => this.deleteRecord(record)}
>
<Tooltip title="Delete list" placement="bottom">
<a role="button">
<Icon type="delete" />
</a>
</Tooltip>
</Popconfirm>
</div>
)
}
}
]
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
componentDidMount() {
this.props.pageTitle("Lists")
this.fetchRecords()
}
2018-10-25 15:51:47 +02:00
2019-05-14 13:11:05 +02:00
fetchRecords = params => {
let qParams = {
page: this.state.queryParams.page,
per_page: this.state.queryParams.per_page
}
if (params) {
qParams = { ...qParams, ...params }
}
this.props
.modelRequest(cs.ModelLists, cs.Routes.GetLists, cs.MethodGet, qParams)
.then(() => {
this.setState({
queryParams: {
...this.state.queryParams,
total: this.props.data[cs.ModelLists].total,
perPage: this.props.data[cs.ModelLists].per_page,
page: this.props.data[cs.ModelLists].page,
query: this.props.data[cs.ModelLists].query
}
})
})
2019-03-09 08:46:47 +01:00
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
deleteRecord = record => {
this.props
.modelRequest(cs.ModelLists, cs.Routes.DeleteList, cs.MethodDelete, {
id: record.id
})
.then(() => {
notification["success"]({
placement: cs.MsgPosition,
message: "List deleted",
description: `"${record.name}" deleted`
})
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
// Reload the table.
this.fetchRecords()
})
.catch(e => {
notification["error"]({
placement: cs.MsgPosition,
message: "Error",
description: e.message
})
})
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
handleHideForm = () => {
this.setState({ formType: null })
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
handleShowCreateForm = () => {
this.setState({ formType: cs.FormCreate, record: {} })
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
handleShowEditForm = record => {
this.setState({ formType: cs.FormEdit, record: record })
}
2018-10-25 15:51:47 +02:00
2019-03-09 08:46:47 +01:00
render() {
return (
<section className="content">
<Row>
<Col span={22}>
2019-05-14 13:11:05 +02:00
<h1>Lists ({this.props.data[cs.ModelLists].total}) </h1>
2019-03-09 08:46:47 +01:00
</Col>
<Col span={2}>
<Button
type="primary"
icon="plus"
onClick={this.handleShowCreateForm}
>
Create list
</Button>
</Col>
</Row>
<br />
<Table
className="lists"
columns={this.columns}
rowKey={record => record.uuid}
2019-05-14 13:11:05 +02:00
dataSource={(() => {
if (
!this.props.data[cs.ModelLists] ||
!this.props.data[cs.ModelLists].hasOwnProperty("results")
) {
return []
}
return this.props.data[cs.ModelLists].results
})()}
2019-03-09 08:46:47 +01:00
loading={this.props.reqStates[cs.ModelLists] !== cs.StateDone}
2019-05-14 13:11:05 +02:00
pagination={{ ...this.paginationOptions, ...this.state.queryParams }}
2019-03-09 08:46:47 +01:00
/>
<CreateForm
{...this.props}
formType={this.state.formType}
record={this.state.record}
onClose={this.handleHideForm}
fetchRecords={this.fetchRecords}
/>
</section>
)
}
2018-10-25 15:51:47 +02:00
}
export default Lists