commit
d8d7f88af3
|
@ -51,7 +51,6 @@ Alternatively, to run a demo of listmonk, you can quickly spin up a container `d
|
||||||
- User auth, management, permissions
|
- User auth, management, permissions
|
||||||
- Ability to write raw campaign logs to a target
|
- Ability to write raw campaign logs to a target
|
||||||
- Analytics views and reports
|
- Analytics views and reports
|
||||||
- Make Ant design UI components responsive
|
|
||||||
- Better widgets on dashboard
|
- Better widgets on dashboard
|
||||||
- Tests!
|
- Tests!
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ import Delta from "quill-delta"
|
||||||
import "react-quill/dist/quill.snow.css"
|
import "react-quill/dist/quill.snow.css"
|
||||||
|
|
||||||
const formItemLayout = {
|
const formItemLayout = {
|
||||||
labelCol: { xs: { span: 16 }, sm: { span: 4 } },
|
labelCol: { xs: { span: 16 }, sm: { span: 10 }, md: { span: 4 } },
|
||||||
wrapperCol: { xs: { span: 16 }, sm: { span: 10 } }
|
wrapperCol: { xs: { span: 16 }, sm: { span: 14 }, md: { span: 10 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
class Editor extends React.PureComponent {
|
class Editor extends React.PureComponent {
|
||||||
|
@ -521,7 +521,7 @@ class TheFormDef extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col xs={24} sm={2}>
|
||||||
{this.state.sendLater &&
|
{this.state.sendLater &&
|
||||||
getFieldDecorator("send_at", {
|
getFieldDecorator("send_at", {
|
||||||
initialValue:
|
initialValue:
|
||||||
|
@ -656,7 +656,7 @@ class Campaign extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<section className="content campaign">
|
<section className="content campaign">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={16}>
|
<Col xs={24} sm={16}>
|
||||||
{!this.state.record.id && <h1>Create a campaign</h1>}
|
{!this.state.record.id && <h1>Create a campaign</h1>}
|
||||||
{this.state.record.id && (
|
{this.state.record.id && (
|
||||||
<div>
|
<div>
|
||||||
|
@ -675,7 +675,7 @@ class Campaign extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8} className="right">
|
<Col xs={24} sm={8} className="right header-action-break">
|
||||||
{!this.state.formDisabled && !this.state.loading && (
|
{!this.state.formDisabled && !this.state.loading && (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -651,10 +651,10 @@ class Campaigns extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<section className="content campaigns">
|
<section className="content campaigns">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={22}>
|
<Col xs={24} sm={14}>
|
||||||
<h1>Campaigns</h1>
|
<h1>Campaigns</h1>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2}>
|
<Col xs={24} sm={10} className="right header-action-break">
|
||||||
<Link to="/campaigns/new">
|
<Link to="/campaigns/new">
|
||||||
<Button type="primary" icon="plus" role="link">
|
<Button type="primary" icon="plus" role="link">
|
||||||
New campaign
|
New campaign
|
||||||
|
|
|
@ -45,16 +45,16 @@ class Dashboard extends React.PureComponent {
|
||||||
{this.state.stats && (
|
{this.state.stats && (
|
||||||
<div className="stats">
|
<div className="stats">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={16}>
|
<Col xs={24} sm={24} xl={16}>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={8}>
|
<Col xs={24} sm={12} md={8}>
|
||||||
<Card title="Active subscribers" bordered={false}>
|
<Card title="Active subscribers" bordered={false}>
|
||||||
<h1 className="count">
|
<h1 className="count">
|
||||||
{this.orZero(this.state.stats.subscribers.enabled)}
|
{this.orZero(this.state.stats.subscribers.enabled)}
|
||||||
</h1>
|
</h1>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col xs={24} sm={12} md={8}>
|
||||||
<Card title="Blacklisted subscribers" bordered={false}>
|
<Card title="Blacklisted subscribers" bordered={false}>
|
||||||
<h1 className="count">
|
<h1 className="count">
|
||||||
{this.orZero(
|
{this.orZero(
|
||||||
|
@ -63,7 +63,7 @@ class Dashboard extends React.PureComponent {
|
||||||
</h1>
|
</h1>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col xs={24} sm={12} md={8}>
|
||||||
<Card title="Orphaned subscribers" bordered={false}>
|
<Card title="Orphaned subscribers" bordered={false}>
|
||||||
<h1 className="count">
|
<h1 className="count">
|
||||||
{this.orZero(this.state.stats.orphan_subscribers)}
|
{this.orZero(this.state.stats.orphan_subscribers)}
|
||||||
|
@ -72,16 +72,16 @@ class Dashboard extends React.PureComponent {
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={6} offset={2}>
|
<Col xs={24} sm={24} xl={{ span: 6, offset: 2 }}>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={12}>
|
<Col xs={24} sm={12}>
|
||||||
<Card title="Public lists" bordered={false}>
|
<Card title="Public lists" bordered={false}>
|
||||||
<h1 className="count">
|
<h1 className="count">
|
||||||
{this.orZero(this.state.stats.lists.public)}
|
{this.orZero(this.state.stats.lists.public)}
|
||||||
</h1>
|
</h1>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col xs={24} sm={12}>
|
||||||
<Card title="Private lists" bordered={false}>
|
<Card title="Private lists" bordered={false}>
|
||||||
<h1 className="count">
|
<h1 className="count">
|
||||||
{this.orZero(this.state.stats.lists.private)}
|
{this.orZero(this.state.stats.lists.private)}
|
||||||
|
@ -93,9 +93,9 @@ class Dashboard extends React.PureComponent {
|
||||||
</Row>
|
</Row>
|
||||||
<hr />
|
<hr />
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={16}>
|
<Col xs={24} sm={24} xl={16}>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={12}>
|
<Col xs={24} sm={12}>
|
||||||
<Card
|
<Card
|
||||||
title="Campaign views (last 3 months)"
|
title="Campaign views (last 3 months)"
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
@ -124,7 +124,7 @@ class Dashboard extends React.PureComponent {
|
||||||
</Chart>
|
</Chart>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col xs={24} sm={12}>
|
||||||
<Card
|
<Card
|
||||||
title="Link clicks (last 3 months)"
|
title="Link clicks (last 3 months)"
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
@ -156,7 +156,7 @@ class Dashboard extends React.PureComponent {
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={6} offset={2}>
|
<Col xs={24} sm={12} xl={{ span: 6, offset: 2 }}>
|
||||||
<Card
|
<Card
|
||||||
title="Campaigns"
|
title="Campaigns"
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
|
|
@ -101,8 +101,8 @@ class TheFormDef extends React.PureComponent {
|
||||||
const { getFieldDecorator } = this.props.form
|
const { getFieldDecorator } = this.props.form
|
||||||
|
|
||||||
const formItemLayout = {
|
const formItemLayout = {
|
||||||
labelCol: { xs: { span: 16 }, sm: { span: 4 } },
|
labelCol: { sm: { span: 24 }, md: { span: 5 } },
|
||||||
wrapperCol: { xs: { span: 16 }, sm: { span: 10 } }
|
wrapperCol: { sm: { span: 24 }, md: { span: 10 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const formItemTailLayout = {
|
const formItemTailLayout = {
|
||||||
|
@ -163,7 +163,7 @@ class TheFormDef extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
{...formItemLayout}
|
{...formItemLayout}
|
||||||
label="CSV column delimiter"
|
label="CSV delimiter"
|
||||||
extra="Default delimiter is comma"
|
extra="Default delimiter is comma"
|
||||||
>
|
>
|
||||||
{getFieldDecorator("delim", {
|
{getFieldDecorator("delim", {
|
||||||
|
|
|
@ -31,6 +31,13 @@ class Base extends React.Component {
|
||||||
this.setState({ collapsed });
|
this.setState({ collapsed });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// For small screen devices collapse the menu by default.
|
||||||
|
if (window.screen.width < 768) {
|
||||||
|
this.setState({ collapsed: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Layout style={{ minHeight: "100vh" }}>
|
<Layout style={{ minHeight: "100vh" }}>
|
||||||
|
|
|
@ -378,10 +378,10 @@ class Lists extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<section className="content">
|
<section className="content">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={22}>
|
<Col xs={12} sm={18}>
|
||||||
<h1>Lists ({this.props.data[cs.ModelLists].total}) </h1>
|
<h1>Lists ({this.props.data[cs.ModelLists].total}) </h1>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2}>
|
<Col xs={12} sm={6} className="right">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
|
|
|
@ -357,7 +357,7 @@ class Subscriber extends React.PureComponent {
|
||||||
{this.state.record.id && (
|
{this.state.record.id && (
|
||||||
<div>
|
<div>
|
||||||
<h1>
|
<h1>
|
||||||
<Tag
|
<Tag className="subscriber-status"
|
||||||
color={
|
color={
|
||||||
tagColors.hasOwnProperty(this.state.record.status)
|
tagColors.hasOwnProperty(this.state.record.status)
|
||||||
? tagColors[this.state.record.status]
|
? tagColors[this.state.record.status]
|
||||||
|
@ -366,7 +366,9 @@ class Subscriber extends React.PureComponent {
|
||||||
>
|
>
|
||||||
{this.state.record.status}
|
{this.state.record.status}
|
||||||
</Tag>{" "}
|
</Tag>{" "}
|
||||||
{this.state.record.name} ({this.state.record.email})
|
<span className="subscriber-name">
|
||||||
|
{this.state.record.name} ({this.state.record.email})
|
||||||
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<span className="text-small text-grey">
|
<span className="text-small text-grey">
|
||||||
ID {this.state.record.id} / UUID {this.state.record.uuid}
|
ID {this.state.record.id} / UUID {this.state.record.uuid}
|
||||||
|
@ -374,13 +376,13 @@ class Subscriber extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2} className="right">
|
<Col span={2} className="right subscriber-export">
|
||||||
<Tooltip title="Export data" placement="top">
|
<Tooltip title="Export data" placement="top">
|
||||||
<a
|
<a
|
||||||
role="button"
|
role="button"
|
||||||
href={"/api/subscribers/" + this.state.record.id + "/export"}
|
href={"/api/subscribers/" + this.state.record.id + "/export"}
|
||||||
>
|
>
|
||||||
<Icon type="export" />
|
<Icon type="export" style={{ fontSize: "20px" }}/>
|
||||||
</a>
|
</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -583,10 +583,10 @@ class Subscribers extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="content">
|
<section className="content subscribers">
|
||||||
<header className="header">
|
<header className="header">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={20}>
|
<Col xs={24} sm={14}>
|
||||||
<h1>
|
<h1>
|
||||||
Subscribers
|
Subscribers
|
||||||
{this.props.data[cs.ModelSubscribers].total > 0 && (
|
{this.props.data[cs.ModelSubscribers].total > 0 && (
|
||||||
|
@ -597,7 +597,7 @@ class Subscribers extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2}>
|
<Col xs={24} sm={10} className="right header-action-break">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
|
@ -611,9 +611,9 @@ class Subscribers extends React.PureComponent {
|
||||||
|
|
||||||
<div className="subscriber-query">
|
<div className="subscriber-query">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={10}>
|
<Col sm={24} md={10}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={15}>
|
<Row>
|
||||||
<label>Search subscribers</label>
|
<label>Search subscribers</label>
|
||||||
<Input.Search
|
<Input.Search
|
||||||
name="name"
|
name="name"
|
||||||
|
@ -621,14 +621,12 @@ class Subscribers extends React.PureComponent {
|
||||||
enterButton
|
enterButton
|
||||||
onSearch={this.handleSearch}
|
onSearch={this.handleSearch}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
</Col>
|
</Row>
|
||||||
<Col span={8} offset={1}>
|
<Row style={{ marginTop: "10px" }}>
|
||||||
<label> </label>
|
|
||||||
<br />
|
|
||||||
<a role="button" onClick={this.handleToggleQueryForm}>
|
<a role="button" onClick={this.handleToggleQueryForm}>
|
||||||
<Icon type="setting" /> Advanced
|
<Icon type="setting" /> Advanced
|
||||||
</a>
|
</a>
|
||||||
</Col>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
{this.state.queryFormVisible && (
|
{this.state.queryFormVisible && (
|
||||||
<div className="advanced-query">
|
<div className="advanced-query">
|
||||||
|
@ -684,7 +682,7 @@ class Subscribers extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={14}>
|
<Col sm={24} md={{ span: 12, offset: 2 }} className="slc-subs-section">
|
||||||
{this.state.selectedRows.length > 0 && (
|
{this.state.selectedRows.length > 0 && (
|
||||||
<nav className="table-options">
|
<nav className="table-options">
|
||||||
<p>
|
<p>
|
||||||
|
@ -706,7 +704,7 @@ class Subscribers extends React.PureComponent {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p class="slc-subs-actions">
|
||||||
<a role="button" onClick={this.handleToggleListsForm}>
|
<a role="button" onClick={this.handleToggleListsForm}>
|
||||||
<Icon type="bars" /> Manage lists
|
<Icon type="bars" /> Manage lists
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -392,10 +392,10 @@ class Templates extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<section className="content templates">
|
<section className="content templates">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={22}>
|
<Col xs={24} sm={14}>
|
||||||
<h1>Templates ({this.props.data[cs.ModelTemplates].length}) </h1>
|
<h1>Templates ({this.props.data[cs.ModelTemplates].length}) </h1>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2}>
|
<Col xs={24} sm={10} className="right header-action-break">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
|
|
|
@ -314,3 +314,61 @@ td .ant-tag {
|
||||||
.preview-modal .ant-modal-footer button:first-child {
|
.preview-modal .ant-modal-footer button:first-child {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
.dashboard .ant-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1023px) {
|
||||||
|
.ant-table-content {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead > tr > th,
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.ant-modal {
|
||||||
|
top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscriber-query {
|
||||||
|
padding: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-action-break {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribers.content .slc-subs-section .table-options {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subscribers.content .slc-subs-actions a {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal.subscriber-modal .subscriber-export {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal.subscriber-modal .subscriber-name {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard {
|
||||||
|
margin: 24px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue