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

Wire up admin checklist to backend, add 'next step' panel on homepage

parent a6966a32
Branches
Tags
No related merge requests found
import React, { Component, PropTypes } from "react";
import Icon from "metabase/components/Icon.jsx";
const TASK_LIST = [
{
name: 'Get connected',
tasks: [
{
title: 'Connect to a database',
description: 'test description',
completed: true,
link: ''
},
{
title: 'Set up Slack',
description: 'Does your team use Slack? If so, you can send automated updates via pulses and ask questions with Metabot',
completed: false,
link: ''
},
{
title: 'Invite team members',
description: 'test description',
completed: true,
link: ''
}
]
},
{
name: 'Curate data',
tasks: [
{
title: 'Hide some irrelevant or technical tables',
description: 'test description',
completed: true,
link: ''
},
{
title: 'Create metrics',
description: 'Keep everyone on the same page by creating canonnical sets of filters anyone can use while asking questions.',
completed: false,
link: ''
}
]
}
]
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper.jsx";
const TaskList = ({tasks}) =>
<ol>
......@@ -61,7 +19,7 @@ const TaskSection = ({name, tasks}) =>
const TaskTitle = ({title, titleClassName}) =>
<h3 className={titleClassName}>{title}</h3>
const TaskDescription = ({description}) => <p className="m0">{description}</p>
const TaskDescription = ({description}) => <p className="m0 mt1">{description}</p>
const CompletionBadge = ({completed}) =>
<div className="mr2 flex align-center justify-center flex-no-shrink" style={{
......@@ -77,8 +35,8 @@ const CompletionBadge = ({completed}) =>
</div>
const Task = ({title, description, completed}) =>
<div className="bordered rounded flex align-center p2">
export const Task = ({title, description, completed, link}) =>
<a className="bordered rounded flex align-center p2 no-decoration" href={link}>
<CompletionBadge completed={completed} />
<div>
<TaskTitle title={title} titleClassName={
......@@ -86,30 +44,57 @@ const Task = ({title, description, completed}) =>
} />
{ !completed ? <TaskDescription description={description} /> : null }
</div>
</div>
</a>
const SettingsSetupList = () =>
<div className="px2">
<h2>Getting set up</h2>
<p>A few things you can do to get the most out of Metabase.</p>
<div style={{maxWidth: 468}}>
<TaskSection
name="Recommended next step"
tasks={[
{
title: 'Test 3',
description: 'Do the 3rd test thing',
completed: false,
link: 'derp'
}
]}
/>
{
TASK_LIST.map((section, index) =>
<TaskSection {...section} key={index} />
)
}
</div>
</div>
export default class SettingsSetupList extends Component {
constructor(props, context) {
super(props, context);
this.state = {
tasks: null,
error: null
};
}
export default SettingsSetupList
async componentWillMount() {
let response = await fetch("/api/setup/admin_checklist", { credentials: 'same-origin' });
if (response.status !== 200) {
this.setState({ error: await response.json() })
} else {
this.setState({ tasks: await response.json() });
}
}
render() {
let tasks, nextTask;
if (this.state.tasks) {
tasks = this.state.tasks.map(section => ({
...section,
tasks: section.tasks.filter(task => {
if (task.is_next_step) {
nextTask = task;
}
return !task.is_next_step;
})
}))
}
return (
<div className="px2">
<h2>Getting set up</h2>
<p>A few things you can do to get the most out of Metabase.</p>
<LoadingAndErrorWrapper loading={!this.state.tasks} error={this.state.error} >
{ () =>
<div style={{maxWidth: 468}}>
<TaskSection name="Recommended next step" tasks={[nextTask]} />
{
tasks.map((section, index) =>
<TaskSection {...section} key={index} />
)
}
</div>
}
</LoadingAndErrorWrapper>
</div>
);
}
}
......@@ -7,6 +7,7 @@ import Activity from "./Activity.jsx";
import RecentViews from "./RecentViews.jsx";
import Smile from './Smile.jsx';
import NewUserOnboardingModal from './NewUserOnboardingModal.jsx';
import NextStep from "./NextStep.jsx";
export default class Homepage extends Component {
......@@ -70,6 +71,7 @@ export default class Homepage extends Component {
</div>
</div>
<div className="Layout-sidebar flex-no-shrink">
<NextStep />
<RecentViews {...this.props} />
</div>
</div>
......
import React, { Component, PropTypes } from "react";
import SidebarSection from "./SidebarSection.jsx";
export default class NextStep extends Component {
constructor(props, context) {
super(props, context);
this.state = {
next: null
};
}
async componentWillMount() {
let response = await fetch("/api/setup/admin_checklist", { credentials: 'same-origin' });
if (response.status === 200) {
let sections = await response.json();
for (let section of sections) {
for (let task of section.tasks) {
if (task.is_next_step) {
this.setState({ next: task });
break;
}
}
}
}
}
render() {
const { next } = this.state;
if (next) {
return (
<SidebarSection title="Setup Tip" icon="clock" extra={<a className="text-brand no-decoration" href="/admin/settings">View all</a>}>
<a className="block p3 no-decoration" href={next.link}>
<h4 className="text-brand text-bold">{next.title}</h4>
<p className="m0 mt1">{next.description}</p>
</a>
</SidebarSection>
)
} else {
return <span className="hide" />
}
}
}
import React, { Component, PropTypes } from "react";
import Icon from "metabase/components/Icon.jsx";
import SidebarSection from "./SidebarSection.jsx";
import Urls from "metabase/lib/urls";
export default class RecentViews extends Component {
......@@ -43,29 +44,23 @@ export default class RecentViews extends Component {
let { recentViews } = this.props;
return (
<div className="p2">
<div className="text-dark-grey clearfix pt2 pb2">
<Icon className="float-left" name={'clock'} width={18} height={18}></Icon>
<span className="pl1 Sidebar-header">Recently Viewed</span>
</div>
<div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}>
{recentViews.length > 0 ?
<ul className="p2">
{recentViews.map((item, index) =>
<li key={index} className="py1 ml1 flex align-center clearfix">
{this.renderIllustration(item)}
<a data-metabase-event={"Recent Views;"+item.model+";"+item.cnt} className="ml1 flex-full link" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a>
</li>
)}
</ul>
:
<div className="flex flex-column layout-centered text-normal text-grey-2">
<span className="QuestionCircle mt4">!</span>
<p className="p3 text-centered text-grey-4" style={{ "maxWidth": "100%" }}>You haven't looked at any Dashboards or Questions recently</p>
</div>
}
</div>
</div>
<SidebarSection title="Recently Viewed" icon="clock">
{recentViews.length > 0 ?
<ul className="p2">
{recentViews.map((item, index) =>
<li key={index} className="py1 ml1 flex align-center clearfix">
{this.renderIllustration(item)}
<a data-metabase-event={"Recent Views;"+item.model+";"+item.cnt} className="ml1 flex-full link" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a>
</li>
)}
</ul>
:
<div className="flex flex-column layout-centered text-normal text-grey-2">
<span className="QuestionCircle mt4">!</span>
<p className="p3 text-centered text-grey-4" style={{ "maxWidth": "100%" }}>You haven't looked at any Dashboards or Questions recently</p>
</div>
}
</SidebarSection>
);
}
}
import React, { Component, PropTypes } from "react";
import Icon from "metabase/components/Icon.jsx";
const SidebarSection = ({ title, icon, extra, children }) =>
<div className="px2 pt1">
<div className="text-dark-grey clearfix pt2 pb2">
<Icon className="float-left" name={icon} width={18} height={18}></Icon>
<span className="pl1 Sidebar-header">{title}</span>
{ extra && <span className="float-right">{extra}</span>}
</div>
<div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}>
{ children }
</div>
</div>
export default SidebarSection;
......@@ -102,49 +102,57 @@
num-tables (db/select-one-count 'Table)
num-cards (db/select-one-count 'Card)
num-users (db/select-one-count 'User)]
[{:name "Add a database"
[{:title "Add a database"
:group "Get connected"
:description "TODO - Write something good here"
:link "/admin/databases/create"
:completed has-dbs?
:triggered :always}
{:name "Set email credentials"
{:title "Set email credentials"
:group "Get connected"
:description "TODO - Write something good here"
:link "/admin/settings/?section=Email"
:completed (email/email-configured?)
:triggered :always}
{:name "Set slack credentials"
{:title "Set Slack credentials"
:group "Get connected"
:description "TODO - Write something good here"
:description "Does your team use Slack? If so, you can send automated updates via pulses and ask questions with Metabot."
:link "/admin/settings/?section=Slack"
:completed (slack/slack-configured?)
:triggered :always}
{:name "Invite other users"
{:title "Invite other users"
:group "Get connected"
:description "TODO - Write something good here"
:link "/admin/people/"
:completed (>= num-users 1)
:triggered (or has-dashboards?
has-pulses?
(>= num-cards 5))}
{:name "Hide tables"
{:title "Hide tables"
:group "Curate your data"
:description "TODO - Write something good here"
:link "/admin/datamodel/database"
:completed has-hidden-tables?
:triggered (>= num-tables 20)}
{:name "Organize questions"
{:title "Organize questions"
:group "Curate your data"
:description "TODO - Write something good here"
:link "/questions/all"
:completed (not has-labels?)
:triggered (>= num-cards 30)}
{:name "Create metrics"
{:title "Create metrics"
:group "Curate your data"
:description "TODO - Write something good here"
:link "/admin/datamodel/database"
:completed has-metrics?
:triggered (>= num-cards 30)}
{:name "Create segments"
{:title "Create segments"
:group "Curate your data"
:description "TODO - Write something good here"
:description "Keep everyone on the same page by creating canonnical sets of filters anyone can use while asking questions."
:link "/admin/datamodel/database"
:completed has-segments?
:triggered (>= num-cards 30)}
{:name "Create getting started guide"
{:title "Create getting started guide"
:group "Curate your data"
:description "TODO - Write something good here"
:completed false ; TODO - how do we determine this?
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment