import React from "react"
import { Link } from "react-router-dom"
import { Row, Col, Modal, Form, Input, Select, Button, Table, Icon, Tooltip, Tag, Popconfirm, Spin, notification } from "antd"
import Utils from "./utils"
import * as cs from "./constants"
const tagColors = {
"enabled": "green",
"blacklisted": "red"
}
class CreateFormDef extends React.PureComponent {
state = {
confirmDirty: false,
attribs: {},
modalWaiting: false
}
componentDidMount() {
this.setState({ attribs: this.props.record.attribs })
}
// Handle create / edit form submission.
handleSubmit = (e) => {
e.preventDefault()
var err = null, values = {}
this.props.form.validateFields((e, v) => {
err = e
values = v
})
if(err) {
return
}
values["attribs"] = {}
let a = this.props.form.getFieldValue("attribs-json")
if(a && a.length > 0) {
try {
values["attribs"] = JSON.parse(a)
if(values["attribs"] instanceof Array) {
notification["error"]({ message: "Invalid JSON type",
description: "Attributes should be a map {} and not an array []" })
return
}
} catch(e) {
notification["error"]({ message: "Invalid JSON in attributes", description: e.toString() })
return
}
}
this.setState({ modalWaiting: true })
if (this.props.formType === cs.FormCreate) {
// Add a subscriber.
this.props.modelRequest(cs.ModelSubscribers, cs.Routes.CreateSubscriber, cs.MethodPost, values).then(() => {
notification["success"]({ message: "Subscriber added", description: `${values["email"]} added` })
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 subscriber.
delete(values["keys"])
delete(values["vals"])
this.props.modelRequest(cs.ModelSubscribers, cs.Routes.UpdateSubscriber, cs.MethodPut, { ...values, id: this.props.record.id }).then(() => {
notification["success"]({ message: "Subscriber modified", description: `${values["email"]} modified` })
// Reload the table.
this.props.fetchRecords()
this.props.onClose()
this.setState({ modalWaiting: false })
}).catch(e => {
notification["error"]({ message: "Error", description: e.message })
this.setState({ modalWaiting: false })
})
}
}
modalTitle(formType, record) {
if(formType === cs.FormCreate) {
return "Add subscriber"
}
return (
{ record.status }
{" "}
{ record.name } ({ record.email })
)
}
render() {
const { formType, record, onClose } = this.props;
const { getFieldDecorator } = this.props.form
const formItemLayout = {
labelCol: { xs: { span: 16 }, sm: { span: 4 } },
wrapperCol: { xs: { span: 16 }, sm: { span: 18 } }
}
if (formType === null) {
return null
}
let subListIDs = []
let subStatuses = {}
if(this.props.record && this.props.record.lists) {
subListIDs = this.props.record.lists.map((v) => { return v["id"] })
subStatuses = this.props.record.lists.reduce((o, item) => ({ ...o, [item.id]: item.subscription_status}), {})
} else if(this.props.list) {
subListIDs = [ this.props.list.id ]
}
return (
{getFieldDecorator("email", {
initialValue: record.email,
rules: [{ required: true }]
})()}
{getFieldDecorator("name", {
initialValue: record.name,
rules: [{ required: true }]
})()}
{getFieldDecorator("status", { initialValue: record.status ? record.status : "enabled", rules: [{ required: true, message: "Type is required" }] })(
)}
{getFieldDecorator("lists", { initialValue: subListIDs })(
)}
Attributes
Attributes can be defined as a JSON map, for example:
{'{"age": 30, "color": "red", "is_user": true}'}. More info.
{getFieldDecorator("attribs-json", {
initialValue: JSON.stringify(this.state.attribs, null, 4)
})(
)}
)
}
}
const CreateForm = Form.create()(CreateFormDef)
class Subscribers extends React.PureComponent {
defaultPerPage = 20
state = {
formType: null,
record: {},
queryParams: {
page: 1,
total: 0,
perPage: this.defaultPerPage,
listID: this.props.route.match.params.listID ? parseInt(this.props.route.match.params.listID, 10) : 0,
list: null,
query: null,
targetLists: []
},
listAddVisible: false
}
// Pagination config.
paginationOptions = {
hideOnSinglePage: true,
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 })
}
}
constructor(props) {
super(props)
// Table layout.
this.columns = [{
title: "E-mail",
dataIndex: "email",
sorter: true,
width: "25%",
render: (text, record) => {
const out = [];
out.push(
)
if(record.lists.length > 0) {
for (let i = 0; i < record.lists.length; i++) {
out.push({ record.lists[i].name })
}
}
return out
}
},
{
title: "Name",
dataIndex: "name",
sorter: true,
width: "25%",
render: (text, record) => {
return (
this.handleShowEditForm(record)}>{text}
)
}
},
{
title: "Status",
dataIndex: "status",
width: "5%",
render: (status, _) => {
return { status }
}
},
{
title: "Lists",
dataIndex: "lists",
width: "10%",
align: "center",
render: (lists, _) => {
return { lists.reduce((def, item) => def + (item.subscription_status !== cs.SubscriptionStatusUnsubscribed ? 1 : 0), 0) }
}
},
{
title: "Created",
width: "10%",
dataIndex: "created_at",
render: (date, _) => {
return Utils.DateString(date)
}
},
{
title: "Updated",
width: "10%",
dataIndex: "updated_at",
render: (date, _) => {
return Utils.DateString(date)
}
},
{
title: "",
dataIndex: "actions",
width: "10%",
render: (text, record) => {
return (
)
}
}
]
}
componentDidMount() {
// Load lists on boot.
this.props.modelRequest(cs.ModelLists, cs.Routes.GetLists, cs.MethodGet).then(() => {
// If this is an individual list's view, pick up that list.
if(this.state.queryParams.listID) {
this.props.data[cs.ModelLists].forEach((l) => {
if(l.id === this.state.queryParams.listID) {
this.setState({ queryParams: { ...this.state.queryParams, list: l }})
return false
}
})
}
})
this.fetchRecords()
}
fetchRecords = (params) => {
let qParams = {
page: this.state.queryParams.page,
per_page: this.state.queryParams.per_page,
list_id: this.state.queryParams.listID,
query: this.state.queryParams.query
}
// The records are for a specific list.
if(this.state.queryParams.listID) {
qParams.list_id = this.state.queryParams.listID
}
if(params) {
qParams = { ...qParams, ...params }
}
this.props.modelRequest(cs.ModelSubscribers, cs.Routes.GetSubscribers, cs.MethodGet, qParams).then(() => {
this.setState({ queryParams: {
...this.state.queryParams,
total: this.props.data[cs.ModelSubscribers].total,
perPage: this.props.data[cs.ModelSubscribers].per_page,
page: this.props.data[cs.ModelSubscribers].page,
query: this.props.data[cs.ModelSubscribers].query,
}})
})
}
handleDeleteRecord = (record) => {
this.props.modelRequest(cs.ModelSubscribers, cs.Routes.DeleteSubscriber, cs.MethodDelete, { id: record.id })
.then(() => {
notification["success"]({ message: "Subscriber deleted", description: `${record.email} deleted` })
// Reload the table.
this.fetchRecords()
}).catch(e => {
notification["error"]({ message: "Error", description: e.message })
})
}
handleQuerySubscribersIntoLists = (query, sourceList, targetLists) => {
let params = {
query: query,
source_list: sourceList,
target_lists: targetLists
}
this.props.request(cs.Routes.QuerySubscribersIntoLists, cs.MethodPost, params).then((res) => {
notification["success"]({ message: "Subscriber(s) added", description: `${ res.data.data.count } added` })
this.handleToggleListAdd()
}).catch(e => {
notification["error"]({ message: "Error", description: e.message })
})
}
handleHideForm = () => {
this.setState({ formType: null })
}
handleShowCreateForm = () => {
this.setState({ formType: cs.FormCreate, attribs: [], record: {} })
}
handleShowEditForm = (record) => {
this.setState({ formType: cs.FormEdit, record: record })
}
handleToggleQueryForm = () => {
// The query form is being cancelled. Reset the results.
if(this.state.queryFormVisible) {
this.fetchRecords({
query: null
})
}
this.setState({ queryFormVisible: !this.state.queryFormVisible })
}
handleToggleListAdd = () => {
this.setState({ listAddVisible: !this.state.listAddVisible })
}
render() {
const pagination = {
...this.paginationOptions,
...this.state.queryParams
}
if(this.state.queryParams.list) {
this.props.pageTitle(this.state.queryParams.list.name + " / Subscribers")
} else {
this.props.pageTitle("Subscribers")
}
return (
Subscribers
{ this.state.queryParams.list &&
» { this.state.queryParams.list.name } }
{ !this.state.queryFormVisible &&
Advanced }
{ this.state.queryFormVisible &&
Write a partial SQL expression to query the subscribers based on their
primary information or attributes. Learn more.
{
this.setState({ queryParams: { ...this.state.queryParams, query: e.target.value } })
}}
autosize={{ minRows: 2, maxRows: 10 }} />
{" "}
{" "}
}
`${record.id}-${record.email}` }
dataSource={ this.props.data[cs.ModelSubscribers].results }
loading={ this.props.reqStates[cs.ModelSubscribers] !== cs.StateDone }
pagination={ pagination }
rowSelection = {{
fixed: true
}}
/>
{ this.state.formType !== null &&
}
{
if(this.state.queryParams.targetLists.length == 0) {
notification["warning"]({
message: "No lists selected",
description: "Select one or more lists"
})
return false
}
this.handleQuerySubscribersIntoLists(
this.state.queryParams.query,
this.state.queryParams.listID,
this.state.queryParams.targetLists
)
}}
okButtonProps={{ disabled: this.props.reqStates[cs.ModelSubscribers] === cs.StatePending }}>
)
}
}
export default Subscribers