Skip to content
Snippets Groups Projects
Unverified Commit 3da188a5 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Add models educational modal to Admin > Data Model (#19832)


* Move DataModelApp to own directory + refactor

* Extract styled components

* Add models education button

* Add images for educational modal

* Add `centeredTitle` prop to `ModalContent`

* Add educational modal to Admin > Data Model

* Fix translation

* "Dataset" > "Model"

* Add link

* update the educational modal illustration

Co-authored-by: default avatarMaz Ameli <maz@metabase.com>
parent 780d934e
No related branches found
No related tags found
No related merge requests found
Showing
with 187 additions and 49 deletions
/* eslint-disable react/prop-types */
import React from "react";
import { push } from "react-router-redux";
import { connect } from "react-redux";
import { t } from "ttag";
import Radio from "metabase/components/Radio";
const mapDispatchToProps = {
onChangeTab: tab => push(`/admin/datamodel/${tab}`),
};
@connect(null, mapDispatchToProps)
export default class DataModelApp extends React.Component {
render() {
const {
children,
onChangeTab,
location: { pathname },
} = this.props;
// this ugly nested ternary allows both "/segment/" and "/segments/" to work
const value = /\/segments?/.test(pathname)
? "segments"
: /\/metrics?/.test(pathname)
? "metrics"
: "database";
return (
<div>
<div className="px3 border-bottom">
<Radio
variant="underlined"
value={value}
options={[
{ name: t`Data`, value: "database" },
{ name: t`Segments`, value: "segments" },
{ name: t`Metrics`, value: "metrics" },
]}
onChange={onChangeTab}
/>
</div>
{children}
</div>
);
}
}
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import { push } from "react-router-redux";
import { connect } from "react-redux";
import { t } from "ttag";
import { useToggle } from "metabase/hooks/use-toggle";
import Radio from "metabase/components/Radio";
import { ModelEducationalModal } from "./ModelEducationalModal";
import { NavBar, ModelEducationButton } from "./DataModelApp.styled";
const propTypes = {
onChangeTab: PropTypes.func.isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
children: PropTypes.node.isRequired,
};
const mapDispatchToProps = {
onChangeTab: tab => push(`/admin/datamodel/${tab}`),
};
const TAB = {
SEGMENTS: "segments",
METRICS: "metrics",
DATABASE: "database",
};
function DataModelApp({ children, onChangeTab, location: { pathname } }) {
const [
isModelEducationalModalShown,
{ turnOn: showModelEducationalModal, turnOff: hideModelEducationalModal },
] = useToggle(false);
const currentTab = useMemo(() => {
if (/\/segments?/.test(pathname)) {
return TAB.SEGMENTS;
}
if (/\/metrics?/.test(pathname)) {
return TAB.METRICS;
}
return TAB.DATABASE;
}, [pathname]);
return (
<React.Fragment>
<NavBar>
<Radio
value={currentTab}
options={[
{ name: t`Data`, value: TAB.DATABASE },
{ name: t`Segments`, value: TAB.SEGMENTS },
{ name: t`Metrics`, value: TAB.METRICS },
]}
onChange={onChangeTab}
variant="underlined"
/>
<ModelEducationButton
onClick={showModelEducationalModal}
>{t`Simplify your schema with Models`}</ModelEducationButton>
</NavBar>
<ModelEducationalModal
isOpen={isModelEducationalModalShown}
onClose={hideModelEducationalModal}
/>
{children}
</React.Fragment>
);
}
DataModelApp.propTypes = propTypes;
export default connect(null, mapDispatchToProps)(DataModelApp);
import styled from "styled-components";
import { color } from "metabase/lib/colors";
import Button from "metabase/core/components/Button";
export const NavBar = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 1.5rem;
padding-right: 1.5rem;
border-bottom: 1px solid ${color("border")};
`;
export const ModelEducationButton = styled(Button).attrs({
icon: "model",
borderless: true,
})`
color: ${color("text-dark")};
`;
import React from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
import Modal from "metabase/components/Modal";
import ModalContent from "metabase/components/ModalContent";
import {
Content,
Description,
ButtonLink,
CenteredRow,
} from "./ModelEducationalModal.styled";
ModelEducationalModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
};
const EDUCATION_URL = "https://metabase.com/learn/getting-started/models";
export function ModelEducationalModal({ isOpen, onClose }) {
return (
<Modal isOpen={isOpen} medium onClose={onClose}>
<ModalContent
title={t`Create models to make it easier for your team to explore.`}
centeredTitle
onClose={onClose}
>
<Content>
<img
width="520px"
className="mx1"
src="app/assets/img/models-education.png"
srcSet="
app/assets/img/models-education.png 1x,
app/assets/img/models-education@2x.png 2x
"
/>
<Description>
{t`Instead of having your end users wade through your complex raw data, you can create custom models that are easy to find, understand, and explore.`}
</Description>
<CenteredRow>
<ButtonLink href={EDUCATION_URL}>{t`Learn how`}</ButtonLink>
</CenteredRow>
</Content>
</ModalContent>
</Modal>
);
}
import styled from "styled-components";
import { color } from "metabase/lib/colors";
import ExternalLink from "metabase/components/ExternalLink";
export const Content = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
export const Description = styled.p`
font-size: 1.143em;
line-height: 1.5em;
color: ${color("text-dark")};
text-align: center;
width: 80%;
margin-top: 24px;
margin-bottom: 24px;
`;
export const ButtonLink = styled(ExternalLink).attrs({
className: "Button Button--primary",
})`
text-align: center;
`;
export const CenteredRow = styled.div`
display: flex;
align-items: center;
justify-content: center;
`;
export { default } from "./DataModelApp";
......@@ -8,6 +8,7 @@ export default class ModalContent extends Component {
static propTypes = {
id: PropTypes.string,
title: PropTypes.string,
centeredTitle: PropTypes.bool,
onClose: PropTypes.func,
// takes over the entire screen
fullPageModal: PropTypes.bool,
......@@ -30,6 +31,7 @@ export default class ModalContent extends Component {
render() {
const {
title,
centeredTitle,
footer,
onClose,
children,
......@@ -58,7 +60,11 @@ export default class ModalContent extends Component {
/>
)}
{title && (
<ModalHeader fullPageModal={fullPageModal} formModal={formModal}>
<ModalHeader
fullPageModal={fullPageModal}
centeredTitle={centeredTitle}
formModal={formModal}
>
{title}
</ModalHeader>
)}
......@@ -77,12 +83,12 @@ export default class ModalContent extends Component {
const FORM_WIDTH = 500 + 32 * 2; // includes padding
export const ModalHeader = ({ children, fullPageModal, formModal }) => (
export const ModalHeader = ({ children, fullPageModal, centeredTitle }) => (
<div className={cx("ModalHeader flex-no-shrink px4 py4 full")}>
<h2
className={cx(
"text-bold",
{ "text-centered": fullPageModal },
{ "text-centered": fullPageModal || centeredTitle },
{ mr4: !fullPageModal },
)}
>
......
resources/frontend_client/app/assets/img/models-education.png

9.2 KiB

resources/frontend_client/app/assets/img/models-education@2x.png

18.6 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment