import React from "react" import { Link } from "react-router-dom" import { Row, Col, Modal, Form, Input, Select, Button, Table, Icon, Tooltip, Tag, Popconfirm, notification, Radio } from "antd" import Utils from "./utils" import Subscriber from "./Subscriber" import * as cs from "./constants" const tagColors = { enabled: "green", blacklisted: "red" } class ListsFormDef extends React.PureComponent { state = { modalWaiting: false } // 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 } if (this.props.allRowsSelected) { values["list_ids"] = this.props.listIDs values["query"] = this.props.query } else { values["ids"] = this.props.selectedRows.map(r => r.id) } this.setState({ modalWaiting: true }) this.props .request( !this.props.allRowsSelected ? cs.Routes.AddSubscribersToLists : cs.Routes.AddSubscribersToListsByQuery, cs.MethodPut, values ) .then(() => { notification["success"]({ message: "Lists changed", description: `Lists changed for selected subscribers` }) this.props.clearSelectedRows() this.props.fetchRecords() this.setState({ modalWaiting: false }) this.props.onClose() }) .catch(e => { notification["error"]({ message: "Error", description: e.message }) this.setState({ modalWaiting: false }) }) } render() { const { getFieldDecorator } = this.props.form const formItemLayout = { labelCol: { xs: { span: 16 }, sm: { span: 4 } }, wrapperCol: { xs: { span: 16 }, sm: { span: 18 } } } return (
{getFieldDecorator("action", { initialValue: "add", rules: [{ required: true }] })( Add Remove Mark as unsubscribed )} {getFieldDecorator("target_list_ids", { rules: [{ required: true }] })( )}
) } } const ListsForm = Form.create()(ListsFormDef) class Subscribers extends React.PureComponent { defaultPerPage = 20 state = { formType: null, listsFormVisible: false, modalForm: 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: [] }, listModalVisible: false, allRowsSelected: false, selectedRows: [] } // 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(
{ // Open the individual subscriber page on ctrl+click // and the modal otherwise. if (!e.ctrlKey) { this.handleShowEditForm(record) e.preventDefault() } }} > {text}
) if (record.lists.length > 0) { for (let i = 0; i < record.lists.length; i++) { out.push( {record.lists[i].name} {" "} {record.lists[i].subscription_status} ) } } return out } }, { title: "Name", dataIndex: "name", sorter: true, width: "15%", render: (text, record) => { return ( { // Open the individual subscriber page on ctrl+click // and the modal otherwise. if (!e.ctrlKey) { this.handleShowEditForm(record) e.preventDefault() } }} > {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 (
{/* */} this.handleShowEditForm(record)} > this.handleDeleteRecord(record)} >
) } } ] } 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 }) }) } handleDeleteRecords = records => { this.props .modelRequest( cs.ModelSubscribers, cs.Routes.DeleteSubscribers, cs.MethodDelete, { id: records.map(r => r.id) } ) .then(() => { notification["success"]({ message: "Subscriber(s) deleted", description: "Selected subscribers deleted" }) // Reload the table. this.fetchRecords() }) .catch(e => { notification["error"]({ message: "Error", description: e.message }) }) } handleBlacklistSubscribers = records => { this.props .request(cs.Routes.BlacklistSubscribers, cs.MethodPut, { ids: records.map(r => r.id) }) .then(() => { notification["success"]({ message: "Subscriber(s) blacklisted", description: "Selected subscribers blacklisted" }) // Reload the table. this.fetchRecords() }) .catch(e => { notification["error"]({ message: "Error", description: e.message }) }) } // Arbitrary query based calls. handleDeleteRecordsByQuery = (listIDs, query) => { this.props .modelRequest( cs.ModelSubscribers, cs.Routes.DeleteSubscribersByQuery, cs.MethodPost, { list_ids: listIDs, query: query } ) .then(() => { notification["success"]({ message: "Subscriber(s) deleted", description: "Selected subscribers have been deleted" }) // Reload the table. this.fetchRecords() }) .catch(e => { notification["error"]({ message: "Error", description: e.message }) }) } handleBlacklistSubscribersByQuery = (listIDs, query) => { this.props .request(cs.Routes.BlacklistSubscribersByQuery, cs.MethodPut, { list_ids: listIDs, query: query }) .then(() => { notification["success"]({ message: "Subscriber(s) blacklisted", description: "Selected subscribers have been blacklisted" }) // 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.handleToggleListModal() }) .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 }) } handleToggleListsForm = () => { this.setState({ listsFormVisible: !this.state.listsFormVisible }) } handleSearch = q => { q = q.trim().toLowerCase() if (q === "") { this.fetchRecords({ query: null }) return } q = q.replace(/'/g, "''") const query = `(name ~* '${q}' OR email ~* '${q}')` this.fetchRecords({ query: query }) } handleSelectRow = (_, records) => { this.setState({ allRowsSelected: false, selectedRows: records }) } handleSelectAllRows = () => { this.setState({ allRowsSelected: true, selectedRows: this.props.data[cs.ModelSubscribers].results }) } clearSelectedRows = (_, records) => { this.setState({ allRowsSelected: false, selectedRows: [] }) } handleToggleQueryForm = () => { this.setState({ queryFormVisible: !this.state.queryFormVisible }) } handleToggleListModal = () => { this.setState({ listModalVisible: !this.state.listModalVisible }) } 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.props.data[cs.ModelSubscribers].total > 0 && ( ({this.props.data[cs.ModelSubscribers].total}) )} {this.state.queryParams.list && ( » {this.state.queryParams.list.name} )}

{" "}
Advanced
{this.state.queryFormVisible && (

{ this.setState({ queryParams: { ...this.state.queryParams, query: e.target.value } }) }} value={this.state.queryParams.query} autosize={{ minRows: 2, maxRows: 10 }} /> Write a partial SQL expression to query the subscribers based on their primary information or attributes. Learn more.

{" "}

)} {this.state.selectedRows.length > 0 && ( )}
`sub-${record.id}`} dataSource={(() => { if ( !this.props.data[cs.ModelSubscribers] || !this.props.data[cs.ModelSubscribers].hasOwnProperty("results") ) { return [] } return this.props.data[cs.ModelSubscribers].results })()} loading={this.props.reqStates[cs.ModelSubscribers] !== cs.StateDone} pagination={pagination} rowSelection={{ columnWidth: "5%", onChange: this.handleSelectRow, selectedRowKeys: this.state.selectedRows.map(r => `sub-${r.id}`) }} /> {this.state.formType !== null && ( { if (!this.state.modalForm) { return } // This submits the form embedded in the Subscriber component. this.state.modalForm.submitForm(e, ok => { if (ok) { this.handleHideForm() this.fetchRecords() } }) }} onCancel={this.handleHideForm} okButtonProps={{ disabled: this.props.reqStates[cs.ModelSubscribers] === cs.StatePending }} > { if (!r) { return } this.setState({ modalForm: r }) }} /> )} {this.state.listsFormVisible && ( )} ) } } export default Subscribers