Skip to content
Snippets Groups Projects
Commit 0047924d authored by Tom Robinson's avatar Tom Robinson
Browse files

Implement pulse channel schedule editing

parent aeeda6c2
Branches
Tags
No related merge requests found
......@@ -7,7 +7,6 @@
.ColumnarSelector-column {
min-width: 180px;
min-height: 300px;
max-height: 340px;
flex: 1;
}
......
......@@ -338,6 +338,8 @@
}
.Toggle {
position: relative;
display: inline-block;
color: var(--brand-color);
box-sizing: border-box;
width: 48px;
......@@ -345,7 +347,6 @@
border-radius: 99px;
border: 1px solid #EAEAEA;
background-color: #F7F7F7;
position: relative;
transition: all 0.3s;
}
......
......@@ -60,7 +60,7 @@ export default class PulseEdit extends Component {
failedText="Save failed"
successText="Saved"
/>
<a className="text-bold flex-align-right" href="/pulse">Cancel</a>
<a className="text-bold flex-align-right no-decoration text-brand-hover" href="/pulse">Cancel</a>
</div>
</div>
);
......
......@@ -38,7 +38,7 @@ export default class PulseEditCard extends Component {
}
return (
<div className="py4">
<div className="py1">
<h2>Pick your data</h2>
<p>Pick up to five questions you'd like to send in this pulse</p>
<ol className="my3">
......
import React, { Component, PropTypes } from "react";
import Select from "metabase/components/Select.jsx";
// import CheckBox from "metabase/components/CheckBox.jsx";
import cx from "classnames";
// const SCHEDULE_NAMES = {
// "hourly": (<span>Hour<br /></span>),
// "daily": (<span>Day<br />(8 am every week day)</span>),
// "weekly": (<span>Week<br />(8 am on Mondays)</span>)
// };
//
// function getScheduleField(options) {
// return {
// name: "schedule",
// displayName: "Send every",
// type: "select-button",
// options: options.map(o => ({ name: SCHEDULE_NAMES[o], value: o })),
// required: true
// };
// }
// const CHANNELS = {
// "email": {
// displayName: "Email",
// fields: [
// {
// name: "recipients",
// displayName: "Send to",
// multi: true,
// type: "email",
// placeholder: "Enter email address these questions should be sent to",
// required: true
// },
// getScheduleField(["daily", "weekly"])
// ]
// },
// "slack": {
// displayName: "Slack",
// fields: [
// {
// name: "channel",
// displayName: "Send to",
// multi: false,
// type: "select",
// options: ["#general", "#random", "#ios"],
// required: true
// },
// getScheduleField(["hourly", "daily"])
// ]
// }
// };
import Toggle from "metabase/components/Toggle.jsx";
import { capitalize } from "metabase/lib/formatting";
import _ from "underscore";
const CHANNELS = [
{
type: "email",
name: "Email",
recipients: ["account", "email"],
schedules: ["daily", "weekly"]
},
{
type: "slack",
name: "Slack",
fields: [
{
name: "channel",
type: "select",
multi: false,
required: true,
options: ["#general", "#random", "#ios"],
displayName: "Post to"
}
],
schedules: ["hourly", "daily"]
}
];
const HOUR_OPTIONS = _.times(12, (n) => (
{ name: (n === 0 ? 12 : n)+":00", value: n }
));
const AM_PM_OPTIONS = [
{ name: "AM", value: 0 },
{ name: "PM", value: 1 }
];
const DAY_OF_WEEK_OPTIONS = [
{ name: "Sunday", value: "sun" },
{ name: "Monday", value: "mon" },
{ name: "Tuesday", value: "tue" },
{ name: "Wednesday", value: "wed" },
{ name: "Thursday", value: "thu" },
{ name: "Friday", value: "fri" },
{ name: "Saturday", value: "sat" }
];
export default class PulseEditChannel extends Component {
constructor(props) {
......@@ -63,16 +61,26 @@ export default class PulseEditChannel extends Component {
addChannel(type) {
let { pulse } = this.props;
let channel = { type: type, schedule: "daily" };
let channels = [...pulse.channels, channel];
this.props.setPulse({ ...pulse, channels });
let channelSpec = _.find(CHANNELS, (c) => c.type === type);
if (!channelSpec) {
return;
}
let channel = {
channel_type: type,
recipients: [],
details: {},
schedule_type: channelSpec.schedules[0],
schedule_details: { day_of_week: "mon", hour_of_day: 8 }
};
this.props.setPulse({ ...pulse, channels: pulse.channels.concat(channel) });
}
removeChannel(index) {
let { pulse } = this.props;
let channels = [...pulse.channels];
channels.splice(index, 1);
this.props.setPulse({ ...pulse, channels });
this.props.setPulse({ ...pulse, channels: pulse.channels.filter((c,i) => i !== index) });
}
onChannelPropertyChange(index, name, value) {
......@@ -82,90 +90,102 @@ export default class PulseEditChannel extends Component {
this.props.setPulse({ ...pulse, channels });
}
renderField(field, channel, index) {
switch (field.type) {
case "email":
return (
<input
className="input"
type="email"
multiple={true}
value={channel[field.name]}
onChange={(e) => this.onChannelPropertyChange(index, field.name, e.target.value)}
/>
);
case "select":
return (
<Select
value={channel[field.name]}
options={field.options}
optionNameFn={o => o}
optionValueFn={o => o}
onChange={(o) => this.onChannelPropertyChange(index, field.name, o)}
/>
);
case "select-button":
return (
<div className="Button-group flex">
{field.options.map(o =>
<a className={cx("Button flex-full", { "Button--primary": channel[field.name] === o.value })} onClick={() => this.onChannelPropertyChange(index, field.name, o.value)}>
{o.name}
</a>
)}
</div>
)
default:
return "unknown field type"
toggleChannel(type, enable) {
if (enable) {
this.addChannel(type)
} else {
let { pulse } = this.props;
this.props.setPulse({ ...pulse, channels: pulse.channels.filter((c) => c.channel_type !== type) });
}
}
render() {
// let { pulse } = this.props;
// let indexesForChannel = {};
// for (let [index, channel] of Object.entries(pulse.channels)) {
// indexesForChannel[channel.type] = indexesForChannel[channel.type] || []
// indexesForChannel[channel.type].push(index);
// }
let channels = [];
// Object.entries(CHANNELS).map(([type, CHANNEL]) => {
// if (indexesForChannel[type]) {
// indexesForChannel[type].map(index => {
// let channel = pulse.channels[index];
// channels.push(
// <div>
// <div className="flex align-center">
// <CheckBox checked={true} onChange={this.removeChannel.bind(this, index)} />
// <h3 className="ml1">{CHANNEL.displayName}</h3>
// </div>
// <ul className="ml3" >
// {CHANNEL.fields.map(field =>
// <li className="py1" key={field.name}>
// <h4 className="py1">{field.displayName}</h4>
// <div>{this.renderField(field, channel, index)}</div>
// </li>
// )}
// </ul>
// </div>
// )
// });
// } else {
// channels.push(
// <div className="flex align-center">
// <CheckBox checked={false} onChange={this.addChannel.bind(this, type)} />
// <h3 className="ml1">{CHANNEL.displayName}</h3>
// </div>
// )
// }
// });
renderDayPicker(c, index) {
return (
<span className="mt1">
<span className="mx1">on</span>
<Select
value={_.find(DAY_OF_WEEK_OPTIONS, (o) => o.value === c.schedule_details.day_of_week)}
options={DAY_OF_WEEK_OPTIONS}
optionNameFn={o => o.name}
optionValueFn={o => o.value}
onChange={(o) => this.onChannelPropertyChange(index, "schedule_details", { ...c.schedule_details, day_of_week: o }) }
/>
</span>
);
}
renderHourPicker(c, index) {
let hourOfDay = isNaN(c.schedule_details.hour_of_day) ? 8 : c.schedule_details.hour_of_day;
let hour = hourOfDay % 12;
let amPm = hourOfDay >= 12 ? 1 : 0;
return (
<div className="mt1">
<span className="mr1">at</span>
<Select
className="mr1"
value={_.find(HOUR_OPTIONS, (o) => o.value === hour)}
options={HOUR_OPTIONS}
optionNameFn={o => o.name}
optionValueFn={o => o.value}
onChange={(o) => this.onChannelPropertyChange(index, "schedule_details", { ...c.schedule_details, hour_of_day: o + amPm * 12 }) }
/>
<Select
value={_.find(AM_PM_OPTIONS, (o) => o.value === amPm)}
options={AM_PM_OPTIONS}
optionNameFn={o => o.name}
optionValueFn={o => o.value}
onChange={(o) => this.onChannelPropertyChange(index, "schedule_details", { ...c.schedule_details, hour_of_day: hour + o * 12 }) }
/>
</div>
);
}
renderChannel(channel, index, channelSpec) {
return (
<li className="py1">
<span className="mr1">Sent</span>
<Select
value={channel.schedule_type}
options={channelSpec.schedules}
optionNameFn={o => capitalize(o)}
optionValueFn={o => o}
onChange={(o) => this.onChannelPropertyChange(index, "schedule_type", o)}
/>
{ channel.schedule_type === "weekly" &&
this.renderDayPicker(channel, index)
}
{ (channel.schedule_type === "daily" || channel.schedule_type === "weekly") &&
this.renderHourPicker(channel, index)
}
</li>
)
}
renderChannelSection(channelSpec) {
let { pulse } = this.props;
return (
<li key={channelSpec.type} className="py2 border-row-divider">
<div className="flex align-center">
<h3>{channelSpec.name}</h3>
<Toggle className="flex-align-right" value={pulse.channels.some(c => c.channel_type === channelSpec.type)} onChange={this.toggleChannel.bind(this, channelSpec.type)} />
</div>
<ul>
{pulse.channels.map((channel, index) =>
channel.channel_type === channelSpec.type &&
this.renderChannel(channel, index, channelSpec)
)}
</ul>
</li>
)
}
render() {
return (
<div className="py4">
<div className="py1">
<h2>Where should this data go?</h2>
<ul>
{channels.map(channel =>
<li className="my2">{channel}</li>
{CHANNELS.map(channelSpec =>
this.renderChannelSection(channelSpec)
)}
</ul>
</div>
......
......@@ -21,7 +21,7 @@ export default class PulseEditName extends Component {
render() {
let { pulse } = this.props;
return (
<div className="py4">
<div className="py1">
<h2>Name your pulse</h2>
<p>Give your pulse a name to help others understand what it's about.</p>
<div className="my3">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment