diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts index 4737ec7d938ef7451001e67e5c7e773a07fa2592..36da17d91d83088b5b36dea0cfc610eef187bbbf 100644 --- a/frontend/src/metabase-types/api/mocks/settings.ts +++ b/frontend/src/metabase-types/api/mocks/settings.ts @@ -130,6 +130,7 @@ export const createMockSettings = (opts?: Partial<Settings>): Settings => ({ "ldap-enabled": false, "loading-message": "doing-science", "persisted-models-enabled": false, + "report-timezone-short": "UTC", "saml-configured": false, "saml-enabled": false, "session-cookies": null, diff --git a/frontend/src/metabase-types/api/settings.ts b/frontend/src/metabase-types/api/settings.ts index 39f1aa708e63a8232a637aa4fa2b226e1dee12b8..a84ae0f84cb00215ba983160d7767b5a2875f0fe 100644 --- a/frontend/src/metabase-types/api/settings.ts +++ b/frontend/src/metabase-types/api/settings.ts @@ -71,12 +71,15 @@ export interface EngineSourceContact { } export interface ScheduleSettings { - schedule_day?: ScheduleDayType; - schedule_frame?: ScheduleFrameType; - schedule_hour?: boolean; - schedule_minute?: number; + schedule_type?: ScheduleType | null; + schedule_day?: ScheduleDayType | null; + schedule_frame?: ScheduleFrameType | null; + schedule_hour?: number | null; + schedule_minute?: number | null; } +export type ScheduleType = "hourly" | "daily" | "weekly" | "monthly"; + export type ScheduleDayType = | "sun" | "mon" @@ -156,6 +159,7 @@ export interface Settings { "ldap-enabled": boolean; "loading-message": LoadingMessage; "persisted-models-enabled": boolean; + "report-timezone-short": string; "saml-configured"?: boolean; "saml-enabled"?: boolean; "session-cookies": boolean | null; diff --git a/frontend/src/metabase/admin/databases/components/widgets/CacheFieldValuesScheduleWidget.jsx b/frontend/src/metabase/admin/databases/components/widgets/CacheFieldValuesScheduleWidget.jsx index 00ac84c5a828fdaa356f72c7827eb7aac2545e0c..50754b1e89ae5fff66c47a03f15188b57ed6a629 100644 --- a/frontend/src/metabase/admin/databases/components/widgets/CacheFieldValuesScheduleWidget.jsx +++ b/frontend/src/metabase/admin/databases/components/widgets/CacheFieldValuesScheduleWidget.jsx @@ -7,7 +7,7 @@ import cx from "classnames"; import { color } from "metabase/lib/colors"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; export default function CacheFieldValuesScheduleWidget({ field, diff --git a/frontend/src/metabase/admin/databases/components/widgets/MetadataSyncScheduleWidget.jsx b/frontend/src/metabase/admin/databases/components/widgets/MetadataSyncScheduleWidget.jsx index 56e6463b7d18b4950eed7d246d0548da3b932614..49b22472425a61c8627c3558b8efc10acc6a49ed 100644 --- a/frontend/src/metabase/admin/databases/components/widgets/MetadataSyncScheduleWidget.jsx +++ b/frontend/src/metabase/admin/databases/components/widgets/MetadataSyncScheduleWidget.jsx @@ -3,7 +3,7 @@ import React from "react"; import _ from "underscore"; import { t } from "ttag"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; export default function MetadataSyncScheduleWidget({ field }) { return ( diff --git a/frontend/src/metabase/components/SchedulePicker/SchedulePicker.stories.tsx b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.stories.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b8e43fc4c39880cef1e05a3d2101be1f82319e8c --- /dev/null +++ b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.stories.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +import type { ComponentStory } from "@storybook/react"; +import { useArgs } from "@storybook/client-api"; + +import SchedulePicker from "./SchedulePicker"; + +export default { + title: "Components/SchedulePicker", + component: SchedulePicker, +}; + +const Template: ComponentStory<typeof SchedulePicker> = args => { + const [ + { + schedule, + scheduleOptions = ["daily", "weekly", "monthly"], + timezone = "UTC", + }, + updateArgs, + ] = useArgs(); + const handleChange = (schedule: unknown) => updateArgs({ schedule }); + return ( + <SchedulePicker + {...args} + schedule={schedule} + scheduleOptions={scheduleOptions} + timezone={timezone} + onScheduleChange={handleChange} + /> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + schedule: { + schedule_day: "mon", + schedule_frame: null, + schedule_hour: 0, + schedule_type: "daily", + }, + textBeforeInterval: "Deliver", +}; diff --git a/frontend/src/metabase/components/SchedulePicker/SchedulePicker.styled.tsx b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.styled.tsx new file mode 100644 index 0000000000000000000000000000000000000000..433ac92e2c73882a24f79b2a981081150091969e --- /dev/null +++ b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.styled.tsx @@ -0,0 +1,25 @@ +import styled from "@emotion/styled"; +import { color } from "metabase/lib/colors"; + +export const PickerRoot = styled.div` + margin-top: 1.5rem; +`; + +export const PickerRow = styled.div` + display: flex; + align-items: center; +`; + +export const PickerSpacedRow = styled(PickerRow)` + margin-top: 1rem; +`; + +export const PickerText = styled.span` + font-weight: bold; + min-width: 48px; +`; + +export const ScheduleDescriptionContainer = styled.div` + margin-top: 1rem; + color: ${color("text-medium")}; +`; diff --git a/frontend/src/metabase/components/SchedulePicker.jsx b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.tsx similarity index 56% rename from frontend/src/metabase/components/SchedulePicker.jsx rename to frontend/src/metabase/components/SchedulePicker/SchedulePicker.tsx index 97983d64aa3754c7a1fd96c28c18b7547220bc54..47db0d86cc130358432e269768bf3c0210e6c0c2 100644 --- a/frontend/src/metabase/components/SchedulePicker.jsx +++ b/frontend/src/metabase/components/SchedulePicker/SchedulePicker.tsx @@ -1,14 +1,26 @@ -/* eslint "react/prop-types": "warn" */ import React, { Component } from "react"; -import PropTypes from "prop-types"; - import { t } from "ttag"; import _ from "underscore"; + +import Select, { SelectChangeEvent } from "metabase/core/components/Select"; import { SegmentedControl } from "metabase/components/SegmentedControl"; -import Select from "metabase/core/components/Select"; -import Settings from "metabase/lib/settings"; -import { capitalize } from "metabase/lib/formatting"; +import { capitalize } from "metabase/lib/formatting/strings"; + +import type { + ScheduleDayType, + ScheduleFrameType, + ScheduleType, + ScheduleSettings, +} from "metabase-types/api"; + +import { + PickerText, + PickerRoot, + PickerRow, + PickerSpacedRow, + ScheduleDescriptionContainer, +} from "./SchedulePicker.styled"; export const HOUR_OPTIONS = _.times(12, n => ({ name: (n === 0 ? 12 : n) + ":00", @@ -48,32 +60,30 @@ const optionNameTranslations = { monthly: t`Monthly`, }; -/** - * Picker for selecting a hourly/daily/weekly/monthly schedule. - * - * TODO Atte Keinänen 6/30/17: This could use text input fields instead of dropdown for time (hour + AM/PM) pickers - */ -export default class SchedulePicker extends Component { - // TODO: How does this tread an empty schedule? - - static propTypes = { - // the currently chosen schedule, e.g. { schedule_day: "mon", schedule_frame: "null", schedule_hour: 4, schedule_type: "daily" } - schedule: PropTypes.object.isRequired, - // TODO: hourly option? - // available schedules, e.g. [ "daily", "weekly", "monthly"] - scheduleOptions: PropTypes.array.isRequired, - // text before Daily/Weekly/Monthly... option - textBeforeInterval: PropTypes.string, - // text prepended to "12:00 PM PST, your Metabase timezone" - textBeforeSendTime: PropTypes.string, - onScheduleChange: PropTypes.func.isRequired, - minutesOnHourPicker: PropTypes.bool, - }; - - DEFAULT_DAY = "mon"; - - handleChangeProperty(name, value) { - let newSchedule = { +type ScheduleProperty = keyof ScheduleSettings; +type ScheduleChangeProp = { name: ScheduleProperty; value: unknown }; + +export interface SchedulePickerProps { + schedule: ScheduleSettings; + scheduleOptions: ScheduleType[]; + timezone: string; + textBeforeInterval?: string; + textBeforeSendTime?: string; + minutesOnHourPicker?: boolean; + onScheduleChange: ( + nextSchedule: ScheduleSettings, + change: ScheduleChangeProp, + ) => void; +} + +const DEFAULT_DAY = "mon"; + +class SchedulePicker extends Component<SchedulePickerProps> { + handleChangeProperty( + name: ScheduleProperty, + value: ScheduleSettings[typeof name], + ) { + let newSchedule: ScheduleSettings = { ...this.props.schedule, [name]: value, }; @@ -111,7 +121,7 @@ export default class SchedulePicker extends Component { if (value === "weekly") { newSchedule = { ...newSchedule, - schedule_day: this.DEFAULT_DAY, + schedule_day: DEFAULT_DAY, schedule_frame: null, }; } @@ -121,7 +131,7 @@ export default class SchedulePicker extends Component { newSchedule = { ...newSchedule, schedule_frame: "first", - schedule_day: this.DEFAULT_DAY, + schedule_day: DEFAULT_DAY, }; } } else if (name === "schedule_frame") { @@ -132,31 +142,29 @@ export default class SchedulePicker extends Component { // first or last, needs a day of the week newSchedule = { ...newSchedule, - schedule_day: newSchedule.schedule_day || this.DEFAULT_DAY, + schedule_day: newSchedule.schedule_day || DEFAULT_DAY, }; } } - const changedProp = { name, value }; - this.props.onScheduleChange(newSchedule, changedProp); + this.props.onScheduleChange(newSchedule, { name, value }); } renderMonthlyPicker() { const { schedule } = this.props; - const DAY_OPTIONS = DAY_OF_WEEK_OPTIONS.slice(0); - DAY_OPTIONS.unshift({ name: t`Calendar Day`, value: null }); + const DAY_OPTIONS = [ + { name: t`Calendar Day`, value: null }, + ...DAY_OF_WEEK_OPTIONS, + ]; return ( - <div className="flex align-center mt1"> - <span - className="text-bold" - style={{ minWidth: "48px" }} - >{t`on the`}</span> + <PickerSpacedRow> + <PickerText>{t`on the`}</PickerText> <Select value={schedule.schedule_frame} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_frame", value) + onChange={(e: SelectChangeEvent<ScheduleFrameType>) => + this.handleChangeProperty("schedule_frame", e.target.value) } options={MONTH_DAY_OPTIONS} /> @@ -164,14 +172,14 @@ export default class SchedulePicker extends Component { <span className="mx1"> <Select value={schedule.schedule_day} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_day", value) + onChange={(e: SelectChangeEvent<ScheduleDayType>) => + this.handleChangeProperty("schedule_day", e.target.value) } options={DAY_OPTIONS} /> </span> )} - </div> + </PickerSpacedRow> ); } @@ -179,67 +187,63 @@ export default class SchedulePicker extends Component { const { schedule } = this.props; return ( - <span className="flex align-center"> + <PickerRow> <span className="text-bold mx1">{t`on`}</span> <Select value={schedule.schedule_day} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_day", value) + onChange={(e: SelectChangeEvent<ScheduleDayType>) => + this.handleChangeProperty("schedule_day", e.target.value) } options={DAY_OF_WEEK_OPTIONS} /> - </span> + </PickerRow> ); } renderMinutePicker() { const { schedule } = this.props; - const minuteOfHour = isNaN(schedule.schedule_minute) + const minuteOfHour = isNaN(schedule.schedule_minute as number) ? 0 : schedule.schedule_minute; return ( - <div className="mt1"> - <div className="flex align-center"> - <span - className="text-bold" - style={{ minWidth: "48px" }} - >{t`at`}</span> - <Select - className="mr1" - value={minuteOfHour} - options={MINUTE_OPTIONS} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_minute", value) - } - /> - <span className="text-bold">{t`minutes past the hour`}</span> - </div> - </div> + <PickerSpacedRow> + <PickerText>{t`at`}</PickerText> + <Select + className="mr1" + value={minuteOfHour} + options={MINUTE_OPTIONS} + onChange={(e: SelectChangeEvent<number>) => + this.handleChangeProperty("schedule_minute", e.target.value) + } + /> + <span className="text-bold">{t`minutes past the hour`}</span> + </PickerSpacedRow> ); } renderHourPicker() { - const { schedule, textBeforeSendTime } = this.props; + const { schedule, timezone, textBeforeSendTime } = this.props; - const hourOfDay = isNaN(schedule.schedule_hour) + const hourOfDay = isNaN(schedule.schedule_hour as number) ? 8 - : schedule.schedule_hour; + : schedule.schedule_hour || 0; + const hour = hourOfDay % 12; const amPm = hourOfDay >= 12 ? 1 : 0; - const timezone = Settings.get("report-timezone-short"); + return ( - <div className="mt1"> - <div className="flex align-center"> - <span - className="text-bold" - style={{ minWidth: "48px" }} - >{t`at`}</span> + <> + <PickerSpacedRow> + <PickerText>{t`at`}</PickerText> <Select className="mr1" value={hour} options={HOUR_OPTIONS} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_hour", value + amPm * 12) + onChange={(e: SelectChangeEvent<number>) => + this.handleChangeProperty( + "schedule_hour", + e.target.value + amPm * 12, + ) } /> <SegmentedControl @@ -250,14 +254,14 @@ export default class SchedulePicker extends Component { options={AM_PM_OPTIONS} fullWidth /> - </div> + </PickerSpacedRow> {textBeforeSendTime && ( - <div className="mt1 text-medium pt2"> + <ScheduleDescriptionContainer> {textBeforeSendTime} {hour === 0 ? 12 : hour}:00{" "} {amPm ? "PM" : "AM"} {timezone}, {t`your Metabase timezone`}. - </div> + </ScheduleDescriptionContainer> )} - </div> + </> ); } @@ -267,22 +271,22 @@ export default class SchedulePicker extends Component { const scheduleType = schedule.schedule_type; return ( - <div className="mt3"> - <div className="flex align-center"> - <span className="text-bold" style={{ minWidth: "48px" }}> - {textBeforeInterval} - </span> + <PickerRoot> + <PickerRow> + <PickerText>{textBeforeInterval}</PickerText> <Select value={scheduleType} - onChange={({ target: { value } }) => - this.handleChangeProperty("schedule_type", value) + onChange={(e: SelectChangeEvent<ScheduleType>) => + this.handleChangeProperty("schedule_type", e.target.value) } options={scheduleOptions} - optionNameFn={o => optionNameTranslations[o] || capitalize(o)} - optionValueFn={o => o} + optionNameFn={(o: ScheduleType) => + optionNameTranslations[o] || capitalize(o) + } + optionValueFn={(o: ScheduleType) => o} /> {scheduleType === "weekly" && this.renderDayPicker()} - </div> + </PickerRow> {scheduleType === "hourly" && this.props.minutesOnHourPicker && this.renderMinutePicker()} @@ -291,7 +295,9 @@ export default class SchedulePicker extends Component { scheduleType === "weekly" || scheduleType === "monthly") && this.renderHourPicker()} - </div> + </PickerRoot> ); } } + +export default SchedulePicker; diff --git a/frontend/src/metabase/components/SchedulePicker/index.ts b/frontend/src/metabase/components/SchedulePicker/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ae945aaf5a56feace0cfd68ca89359c46b0956e --- /dev/null +++ b/frontend/src/metabase/components/SchedulePicker/index.ts @@ -0,0 +1,3 @@ +export { default } from "./SchedulePicker"; +export type { SchedulePickerProps } from "./SchedulePicker"; +export * from "./SchedulePicker"; diff --git a/frontend/src/metabase/containers/SchedulePicker.tsx b/frontend/src/metabase/containers/SchedulePicker.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0ba5a083e3197d148b7352e1f12391f43ac18f4e --- /dev/null +++ b/frontend/src/metabase/containers/SchedulePicker.tsx @@ -0,0 +1,25 @@ +import { connect } from "react-redux"; +import { getSetting } from "metabase/selectors/settings"; +import type { State } from "metabase-types/store"; +import SchedulePicker, { + SchedulePickerProps, +} from "metabase/components/SchedulePicker"; + +type StateProps = { + timezone: string; +}; + +function mapStateToProps(state: State): StateProps { + return { + timezone: getSetting(state, "report-timezone-short"), + }; +} + +export * from "metabase/components/SchedulePicker"; + +export default connect< + StateProps, + unknown, + Omit<SchedulePickerProps, "timezone">, + State +>(mapStateToProps)(SchedulePicker); diff --git a/frontend/src/metabase/lib/formatting/strings.ts b/frontend/src/metabase/lib/formatting/strings.ts index 4613925c8cde0339a2d7f0641f5d8d69e02102ed..3f930853998701974a302371ce93cf73b7f562da 100644 --- a/frontend/src/metabase/lib/formatting/strings.ts +++ b/frontend/src/metabase/lib/formatting/strings.ts @@ -40,20 +40,6 @@ export function humanize(str: string, lowFirstLetter?: boolean) { return inflection.humanize(str, lowFirstLetter); } -// fallback for formatting a string without a column semantic_type -export function formatStringFallback(value: any, options: OptionsType = {}) { - if (options.view_as !== null) { - value = formatUrl(value, options); - if (typeof value === "string") { - value = formatEmail(value, options); - } - if (typeof value === "string") { - value = formatImage(value, options); - } - } - return value; -} - export function conjunct(list: string[], conjunction: string) { return ( list.slice(0, -1).join(`, `) + diff --git a/frontend/src/metabase/lib/formatting/value.tsx b/frontend/src/metabase/lib/formatting/value.tsx index 0cc679fa7cbebfd2dde2e0d64e663d1a58a44ff8..58d7ef7abc9cf413ac32330279c8879fa8647bbb 100644 --- a/frontend/src/metabase/lib/formatting/value.tsx +++ b/frontend/src/metabase/lib/formatting/value.tsx @@ -26,7 +26,6 @@ import { formatUrl } from "./url"; import { formatDateTimeWithUnit, formatRange } from "./date"; import { formatNumber } from "./numbers"; import { formatCoordinate } from "./geography"; -import { formatStringFallback } from "./strings"; import { formatImage } from "./image"; import { OptionsType } from "./types"; @@ -111,6 +110,20 @@ export function getRemappedValue( } } +// fallback for formatting a string without a column semantic_type +function formatStringFallback(value: any, options: OptionsType = {}) { + if (options.view_as !== null) { + value = formatUrl(value, options); + if (typeof value === "string") { + value = formatEmail(value, options); + } + if (typeof value === "string") { + value = formatImage(value, options); + } + } + return value; +} + export function formatValueRaw( value: unknown, options: OptionsType = {}, diff --git a/frontend/src/metabase/lib/settings.ts b/frontend/src/metabase/lib/settings.ts index 8ef0b3cb35c75a270371401bbc5c156d8daf5301..ae4df6eb8b78feffbcd4db1fabb11eaa9b7345ca 100644 --- a/frontend/src/metabase/lib/settings.ts +++ b/frontend/src/metabase/lib/settings.ts @@ -107,7 +107,8 @@ export type SettingName = | "application-font" | "available-fonts" | "enable-query-caching" - | "start-of-week"; + | "start-of-week" + | "report-timezone-short"; type SettingsMap = Record<SettingName, any>; // provides access to Metabase application settings diff --git a/frontend/src/metabase/pulse/components/PulseEditChannels.jsx b/frontend/src/metabase/pulse/components/PulseEditChannels.jsx index 070173277219bd25aa3a06973b366fb9fde44aa4..ae1558e91ca681dd8177823d3f1b0f26d5603285 100644 --- a/frontend/src/metabase/pulse/components/PulseEditChannels.jsx +++ b/frontend/src/metabase/pulse/components/PulseEditChannels.jsx @@ -5,7 +5,7 @@ import _ from "underscore"; import { assoc, assocIn } from "icepick"; import { t } from "ttag"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; import ActionButton from "metabase/components/ActionButton"; import Toggle from "metabase/core/components/Toggle"; import Icon from "metabase/components/Icon"; diff --git a/frontend/src/metabase/query_builder/components/AlertListPopoverContent.jsx b/frontend/src/metabase/query_builder/components/AlertListPopoverContent.jsx index 7bddd35a6b20d70d973a8fb5da58d7e7eea7959d..fa51c650de74209ce1805c28d753b9fb879359ab 100644 --- a/frontend/src/metabase/query_builder/components/AlertListPopoverContent.jsx +++ b/frontend/src/metabase/query_builder/components/AlertListPopoverContent.jsx @@ -12,7 +12,7 @@ import { AM_PM_OPTIONS, DAY_OF_WEEK_OPTIONS, HOUR_OPTIONS, -} from "metabase/components/SchedulePicker"; +} from "metabase/containers/SchedulePicker"; import Icon from "metabase/components/Icon"; import Modal from "metabase/components/Modal"; import { diff --git a/frontend/src/metabase/query_builder/components/AlertModals.jsx b/frontend/src/metabase/query_builder/components/AlertModals.jsx index 0025411d71e046adf2c4d1a849f41dc33a2e5a2a..4926517eaeee0a2d548161153b27dfd06953fbbe 100644 --- a/frontend/src/metabase/query_builder/components/AlertModals.jsx +++ b/frontend/src/metabase/query_builder/components/AlertModals.jsx @@ -6,7 +6,7 @@ import _ from "underscore"; // components import Button from "metabase/core/components/Button"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; import ModalContent from "metabase/components/ModalContent"; import DeleteModalWithConfirm from "metabase/components/DeleteModalWithConfirm"; import ModalWithTrigger from "metabase/components/ModalWithTrigger"; diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx index 7068ba32427b405c1832fced30ba2c4418378992..ff2e30fdc88feb4194eb31c1c1daf03ae8014c27 100644 --- a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx +++ b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditEmailSidebar.jsx @@ -8,7 +8,7 @@ import { dashboardPulseIsValid } from "metabase/lib/pulse"; import Icon from "metabase/components/Icon"; import Toggle from "metabase/core/components/Toggle"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; import Sidebar from "metabase/dashboard/components/Sidebar"; import EmailAttachmentPicker from "metabase/sharing/components/EmailAttachmentPicker"; import RecipientPicker from "metabase/pulse/components/RecipientPicker"; diff --git a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx index 7be385fec6b4cdbd56353cc0fbdb51e17d920bb7..108a1dc2fe91c0df9ccc26b3b0880a7cd171438e 100644 --- a/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx +++ b/frontend/src/metabase/sharing/components/AddEditSidebar/AddEditSlackSidebar.jsx @@ -4,7 +4,7 @@ import { t } from "ttag"; import _ from "underscore"; import Icon from "metabase/components/Icon"; -import SchedulePicker from "metabase/components/SchedulePicker"; +import SchedulePicker from "metabase/containers/SchedulePicker"; import SendTestPulse from "metabase/components/SendTestPulse"; import Sidebar from "metabase/dashboard/components/Sidebar"; import Toggle from "metabase/core/components/Toggle";