Merge pull request #14 from knadh/feat-responsive

Responsive design
This commit is contained in:
Kailash Nadh 2019-09-10 12:56:40 +05:30 committed by GitHub
commit d8d7f88af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 44 deletions

View File

@ -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!

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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", {

View File

@ -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" }}>

View File

@ -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"

View File

@ -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>

View File

@ -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>&nbsp;</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>

View File

@ -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"

View File

@ -240,7 +240,7 @@ td .ant-tag {
justify-content: center; justify-content: center;
min-height: 90px; min-height: 90px;
padding: 10px; padding: 10px;
border: 1px solid #eee; border: 1px solid #eee;
overflow: hidden; overflow: hidden;
@ -313,4 +313,62 @@ 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;
}
}