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

Merge branch 'master' of github.com:metabase/metabase into release-0.19.2

parents 97e547aa 50c4ef3d
No related branches found
No related tags found
No related merge requests found
Showing
with 131 additions and 82 deletions
......@@ -35,6 +35,7 @@
"react/prop-types": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/no-find-dom-node": 0,
"flow-vars/define-flow-type": 1,
"flow-vars/use-flow-type": 1
},
......
#! /usr/bin/perl -I./bin
#! /usr/bin/env perl -I./bin
use strict;
use warnings;
......
#! /usr/bin/perl -I./bin
#! /usr/bin/env perl -I./bin
use strict;
use warnings;
......
#!/usr/bin/env bash
VERSION="v0.19.2"
VERSION="v0.20.0-snapshot"
# dynamically pull more interesting stuff from latest git commit
HASH=$(git show-ref --head --hash=7 head) # first 7 letters of hash should be enough; that's what GitHub uses
......
......@@ -37,7 +37,7 @@ test:
# 4) runs Eastwood linter, Bikeshed linter, docstring-checker & ./bin/reflection-linter
# 5) runs JS linter + JS test
# 6) runs lein uberjar. (We don't run bin/build because we're not really concerned about `npm install` (etc) in this test, which runs elsewhere)
- case $CIRCLE_NODE_INDEX in 0) ENGINES=h2,mongo,mysql,bigquery lein test ;; 1) ENGINES=h2,sqlserver MB_DB_TYPE=postgres MB_DB_DBNAME=circle_test MB_DB_PORT=5432 MB_DB_USER=ubuntu MB_DB_HOST=localhost lein test ;; 2) ENGINES=h2,postgres,sqlite,crate MB_DB_TYPE=mysql MB_DB_DBNAME=circle_test MB_DB_PORT=3306 MB_DB_USER=ubuntu MB_DB_HOST=localhost lein test ;; 3) ENGINES=h2,redshift,druid lein test ;; 4) lein eastwood && lein bikeshed && lein docstring-checker && ./bin/reflection-linter ;; 5) npm install && npm run lint && npm run build && npm run test ;; 6) lein uberjar ;; esac:
- case $CIRCLE_NODE_INDEX in 0) ENGINES=h2,mongo,mysql,bigquery lein test ;; 1) ENGINES=h2,sqlserver MB_DB_TYPE=postgres MB_DB_DBNAME=circle_test MB_DB_PORT=5432 MB_DB_USER=ubuntu MB_DB_HOST=localhost lein test ;; 2) ENGINES=h2,postgres,sqlite,crate MB_DB_TYPE=mysql MB_DB_DBNAME=circle_test MB_DB_PORT=3306 MB_DB_USER=ubuntu MB_DB_HOST=localhost lein test ;; 3) ENGINES=h2,redshift,druid lein test ;; 4) lein eastwood && lein bikeshed && lein docstring-checker && ./bin/reflection-linter ;; 5) npm install && npm run lint && npm run test ;; 6) lein uberjar ;; esac:
parallel: true
deployment:
master:
......
......@@ -23,5 +23,5 @@ Now just click the `Create token` button next to the team you want to integrate
Paste the value into the text box for `Slack API Token` and click the button to save your changes. That's it! Metabase will automatically run a quick test to check that the API token is working properly and if not you'll get an error message.
## Next: Single Sign On
Learn how to [configure Single Sign On](08-single-sign-on.md) to let users sign in or sign up with just a click.
## Next: Single Sign-On
Learn how to [configure Single Sign-On](08-single-sign-on.md) to let users sign in or sign up with just a click.
## Single Sign On with Google
## Single Sign-On with Google
Enabling single sign on lets your team log in with a click instead of using email and password and can optionally let them sign up for Metabase accounts without an admin having to create them first.
Enabling single sign-on lets your team log in with a click instead of using email and password and can optionally let them sign up for Metabase accounts without an admin having to create them first.
Currently Metabase works with Google accounts for single sign on. As time goes on we may add other auth providers. If you have a service you’d like to see work with Metabase please let us know by [filing an issue](http://github.com/metabase/metabase/issues/new).
Currently Metabase works with Google accounts for single sign-on. As time goes on we may add other auth providers. If you have a service you’d like to see work with Metabase please let us know by [filing an issue](http://github.com/metabase/metabase/issues/new).
### Enabling Sign in
......@@ -12,7 +12,7 @@ To create a new application follow [the instructions from Google here](https://d
Note that when creating the app you only need to specify the url of your Metabase install in the “Javascript Origins” field. You should leave the “redirect-url” blank.
Once you have your client_id, copy and paste it into the box on the Single Sign on sections of your Metabase Admin settings page. ```XXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com```
Once you have your client_id, copy and paste it into the box on the Single Sign-On sections of your Metabase Admin settings page. ```XXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com```
Now existing Metabase users signed into a Google account that matches their Metabase account email can sign in with just a click.
......@@ -20,6 +20,6 @@ Now existing Metabase users signed into a Google account that matches their Meta
If you’ve added your Google client id to your Metabase settings you can also let users sign up on their own without creating accounts for them.
To enable this, check the box on the Single Sign On Admin Settings page and specify the email domain you want to allow. For example if you work at WidgetCo you could enter widgetco.com in the field to let anyone with a company email sign up on their own.
To enable this, check the box on the Single Sign-On Admin Settings page and specify the email domain you want to allow. For example if you work at WidgetCo you could enter widgetco.com in the field to let anyone with a company email sign up on their own.
Note: Metabase accounts created with Single Sign On do not have passwords and must use Google to sign in to Metabase.
Note: Metabase accounts created with Single Sign-On do not have passwords and must use Google to sign in to Metabase.
......@@ -2,15 +2,16 @@
Are you in charge of managing Metabase for your organization? Then you're in the right spot. You are the chosen one.
**This guide will teach you how to:**
**This guide will teach you about:**
* [Connect Metabase to databases in your organization](01-managing-databases.md)
* [Enable features that send email (SMTP)](02-setting-up-email.md)
* [Edit your database metadata](03-metadata-editing.md)
* [Manage user accounts](04-managing-users.md)
* [Connecting Metabase to databases in your organization](01-managing-databases.md)
* [Enabling features that send email (SMTP)](02-setting-up-email.md)
* [Editing your database metadata](03-metadata-editing.md)
* [Managing user accounts](04-managing-users.md)
* [Creating segments and metrics](05-segments-and-metrics.md)
* [Configure settings](06-configuration-settings.md)
* [Setting up Slack Integration](07-setting-up-slack.md)
* [Configuring settings](06-configuration-settings.md)
* [Setting up Slack integration](07-setting-up-slack.md)
* [Enabling single sign-on with Google](08-single-sign-on.md)
First things first, you'll need to install Metabase. If you haven’t done that yet, our [Installation Guide](../operations-guide/start.md#installing-and-running-metabase) will help you through the process.
......
......@@ -6,17 +6,41 @@
2. Run `./bin/build` to build the latest version of the uberjar.
3. Next, you'll need to run the following commands before building the app:
3. Update Perl. I'm not sure these steps are actually needed, so feel free to try skipping it and come back to it if it fails:
```bash
# Upgrade Perl
brew install perl
# Add new version of perl to your $PATH
# (replace "5.24.0_1" below with whatever version you installed)
echo 'export PATH="/usr/local/Cellar/perl/5.24.0_1/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
# Double-check that we're using the newer version of CPAN
# (If this is your first time running CPAN, use the default config settings when prompted)
cpan --version # You should see a line like "running under Perl version 5.24.0."
```
4. Next, you'll need to run the following commands before building the app:
```bash
# Fetch and initialize git submodule
git submodule update --init
# Install libcurl (needed by WWW::Curl::Simple)
# Install libcurl (needed by WWW::Curl::Simple (I think))
brew install curl && brew link curl --force
# Install Perl modules used by ./setup and ./release
sudo cpan install File::Copy::Recursive JSON Readonly String::Util Text::Caml WWW::Curl::Simple
# The new version of LLVM is snippy so have CPAN pass compiler flags to fix errors
# (Make sure this file exists first. If you didn't upgrade Perl in the step above,
# it might be in a different location; perhaps called "Config.pm".
# You may need to run "cpan" (no arguments) to generate an appropriate initial config.
# As above, you can go with the defaults).
sed -i '' -e "s/'make_arg' => q\[\]/'make_arg' => q\[CCFLAGS=\"-Wno-return-type\"\]/" ~/.cpan/CPAN/MyConfig.pm
# Install Perl modules used by ./bin/osx-setup and ./bin/osx-release
# You may have to run this as sudo if you didn't upgrade perl as described in step above
cpan install File::Copy::Recursive JSON Readonly String::Util Text::Caml WWW::Curl::Simple
# Copy JRE and uberjar
./bin/osx-setup
......@@ -25,17 +49,22 @@
`./bin/osx-setup` will build run commands to build the uberjar for you if needed.
Run `./bin/osx-setup` again at any time in the future to copy the latest version of the uberjar into the project.
(If the script fails near the end, you can just copy the JARs in question to `OSX/Resources/metabase.jar` and `OSX/Resources/reset-password.jar`.)
## Releasing
A handy Perl script called `./bin/osx-release` takes care of all of the details for you. Before you run it for the first time, you'll need to set up a few additional things:
```bash
# Install aws command-line client (if needed)
brew install awscli
# Configure AWS Credentials
# You'll need credentials that give you permission to write the metabase-osx-releases S3 bucket.
# You just need the access key ID and secret key; use the defaults for locale and other options.
aws configure --profile metabase
# Copy & Edit Config file
# Copy & Edit Config file. Alternative ask Cam for a copy of his
cp bin/config.json.template bin/config.json
emacs bin/config.json
......@@ -44,7 +73,7 @@ emacs bin/config.json
cp /path/to/private/key.pem OSX/dsa_priv.pem
```
You'll probably also want an Apple Developer ID Application Certificate in your computer's keychain (ask Cam).
You'll probably also want an Apple Developer ID Application Certificate in your computer's keychain. You'll need to generate a Certificate Signing Request from Keychain Access, and have Sameer go to [the Apple Developer Site](https://developer.apple.com/account/mac/certificate/) and generate one for you, then load the file on your computer.
After that, you are good to go:
```bash
......
## SQL Parameters
---
Metabase has the flexible ability to allow variables in native (SQL) queries. This lets you dynamically replace values in your queries using filter widgets or through the query's URL.
......@@ -9,44 +10,48 @@ Options and settings for your variables will appear in the `Variables` side pane
![Variables](images/sql-parameters/01-variables.png)
### Defining Variables
Typing `{{variable_name}}` in your native query creates a variable called `variable_name`. Variables can be given types in the side panel, which changes their behavior. All variable types other than `field filter` will cause a filter widget to be placed on this question corresponding to the chosen variable type. When a value is selected via a filter widget, that value replaces the corresponding variable in the SQL template, wherever it appears.
Typing `{% raw %}{{variable_name}}{% endraw %}` in your native query creates a variable called `variable_name`. Variables can be given types in the side panel, which changes their behavior. All variable types other than `field filter` will cause a filter widget to be placed on this question corresponding to the chosen variable type. When a value is selected via a filter widget, that value replaces the corresponding variable in the SQL template, wherever it appears.
This example defines a variable called `cat`, allowing you to dynamically change the `WHERE` clause in this query:
```
SELECT count(*)
FROM products
WHERE category = {{cat}}
WHERE category = {% raw %}{{cat}}{% endraw %}
```
#### The `Field filter` variable type
Giving a variable the `Field filter` type allows you to connect SQL cards to dashboard filter widgets. A field filter variable inserts SQL similar to that generated by the GUI query builder when adding filters on existing columns. This is useful because it lets you do things like insert dynamic date range filters into your native query. When adding a field filter, you should link that variable to a specific column. Field filter variables should be used inside of a `WHERE` clause.
Example:
```
SELECT count(*)
FROM products
WHERE {{created_at}}
WHERE {% raw %}{{created_at}}{% endraw %}
```
### Optional Clauses
To make an optional clause in your native query, type `[[brackets around a {{variable}}]]`. If `variable` is given a value, then the entire clause is placed into the template. If not, then the entire clause is ignored.
To make an optional clause in your native query, type `[[brackets around a {% raw %}{{variable}}{% endraw %}]]`. If `variable` is given a value, then the entire clause is placed into the template. If not, then the entire clause is ignored.
In this example, if no value is given to `cat` from its filter widget or URL, then the query will just select all the rows from the `products` table. But if `cat` does have a value, like `Widget`, then the query will only grab the products with a category type of `Widget`:
```
SELECT count(*)
FROM products
[[WHERE category = {{cat}}]]
[[WHERE category = {% raw %}{{cat}}{% endraw %}]]
```
To use multiple optional clauses you must include at least one regular `WHERE` clause followed by optional clauses, each starting with `AND`.
Example:
```
SELECT count(*)
FROM products
WHERE True
[[AND id = {{id}}]]
[[AND category = {{category}}]]
[[AND id = {% raw %}{{id}}{% endraw %}]]
[[AND category = {% raw %}{{category}}{% endraw %}]]
```
---
......
......@@ -24,4 +24,6 @@
> [Some helpful tips on building your data model](11-data-model-reference.md)
> [Creating SQL Templates](12-sql-parameters.md)
Let's get started with an overview of [What Metabase does](01-what-is-metabase.md).
import React, { Component, PropTypes } from "react";
import Navbar from "metabase/nav/containers/Navbar.jsx";
export default class App extends Component {
render() {
const { children, location } = this.props;
return (
<div className="spread flex flex-column">
<Navbar location={location} className="flex-no-shrink" />
{children}
</div>
)
}
}
import React, { Component, PropTypes } from "react";
import { Link } from "react-router";
import ModalContent from "metabase/components/ModalContent.jsx";
export default class CreatedDatabaseModal extends Component {
static propTypes = {
databaseId: PropTypes.number.isRequired,
onClose: PropTypes.func.isRequired,
onDone: PropTypes.func.isRequired
};
render() {
const { onClose, onDone, databaseId } = this.props;
return (
<ModalContent
title="Your database has been added!"
closeFn={this.props.onClose}
closeFn={onClose}
>
<div className="Form-inputs mb4">
<p>We're analyzing its schema now to make some educated guesses about its metadata. Click on the Metadata section to see what we've found and to make edits.</p>
<p>
We're analyzing its schema now to make some educated guesses about its
metadata. <Link to={"/admin/datamodel/database/"+databaseId}>View this
database</Link> in the Data Model section to see what we've found and to
make edits, or <Link to={"/q?db="+databaseId}>ask a question</Link> about
this database.
</p>
</div>
<div className="Form-actions flex layout-centered">
<button className="Button Button--primary px3" onClick={() => this.props.onDone()}>Done</button>
<button className="Button Button--primary px3" onClick={onDone}>Done</button>
</div>
</ModalContent>
);
......
import React, { Component, PropTypes } from "react";
import { connect } from "react-redux";
import MetabaseSettings from "metabase/lib/settings";
import DeleteDatabaseModal from "../components/DeleteDatabaseModal.jsx";
import DatabaseEditForms from "../components/DatabaseEditForms.jsx";
......@@ -17,15 +18,14 @@ import * as databaseActions from "../database";
const mapStateToProps = (state, props) => {
return {
databaseId: state.router && state.router.params && state.router.params.databaseId,
databaseId: props.params.databaseId,
database: getEditingDatabase(state),
formState: getFormState(state),
onChangeLocation: props.onChangeLocation
formState: getFormState(state)
}
}
const mapDispatchToProps = {
...databaseActions
...databaseActions,
}
@connect(mapStateToProps, mapDispatchToProps)
......@@ -38,7 +38,7 @@ export default class DatabaseEditApp extends Component {
};
componentWillMount() {
this.props.initializeDatabase(this.props.databaseId, this.props.onChangeLocation);
this.props.initializeDatabase(this.props.databaseId);
}
render() {
......@@ -47,7 +47,7 @@ export default class DatabaseEditApp extends Component {
return (
<div className="wrapper">
<Breadcrumbs crumbs={[
["Databases", "/admin/databases/"],
["Databases", "/admin/databases"],
[database && database.id != null ? database.name : "Add Database"]
]} />
<section className="Grid Grid--gutters Grid--2-of-3">
......
import React, { Component, PropTypes } from "react";
import { connect } from "react-redux";
import { Link } from "react-router";
import cx from "classnames";
import MetabaseSettings from "metabase/lib/settings";
import ModalWithTrigger from "metabase/components/ModalWithTrigger.jsx";
......@@ -17,7 +19,7 @@ import * as databaseActions from "../database";
const mapStateToProps = (state, props) => {
return {
created: state.router && state.router.params && state.router.params.created,
created: props.location.query.created,
databases: getDatabasesSorted(state),
hasSampleDataset: hasSampleDataset(state),
engines: MetabaseSettings.get('engines')
......@@ -46,7 +48,7 @@ export default class DatabaseList extends Component {
return (
<div className="wrapper">
<section className="PageHeader px2 clearfix">
<a className="Button Button--primary float-right" href="/admin/databases/create">Add database</a>
<Link to="/admin/databases/create" className="Button Button--primary float-right">Add database</Link>
<h2 className="PageTitle">Databases</h2>
</section>
<section>
......@@ -63,7 +65,7 @@ export default class DatabaseList extends Component {
databases.map(database =>
<tr key={database.id}>
<td>
<a className="text-bold link" href={"/admin/databases/"+database.id}>{database.name}</a>
<Link to={"/admin/databases/"+database.id} className="text-bold link">{database.name}</Link>
</td>
<td>
{engines && engines[database.engine] ? engines[database.engine]['driver-name'] : database.engine}
......@@ -106,6 +108,7 @@ export default class DatabaseList extends Component {
isInitiallyOpen={created}
>
<CreatedDatabaseModal
databaseId={parseInt(created)}
onDone={() => this.refs.createdDatabaseModal.toggle() }
onClose={() => this.refs.createdDatabaseModal.toggle() }
/>
......
......@@ -2,6 +2,7 @@ import _ from "underscore";
import { createAction } from "redux-actions";
import { handleActions, combineReducers, AngularResourceProxy, createThunkAction } from "metabase/lib/redux";
import { push } from "react-router-redux";
import MetabaseAnalytics from "metabase/lib/analytics";
import MetabaseSettings from "metabase/lib/settings";
......@@ -26,15 +27,11 @@ export const fetchDatabases = createThunkAction("FETCH_DATABASES", function() {
});
// initializeDatabase
export const initializeDatabase = createThunkAction("INITIALIZE_DATABASE", function(databaseId, onChangeLocation) {
export const initializeDatabase = createThunkAction("INITIALIZE_DATABASE", function(databaseId) {
return async function(dispatch, getState) {
if (databaseId) {
try {
let database = await MetabaseApi.db_get({"dbId": databaseId});
return {
database,
onChangeLocation
}
return await MetabaseApi.db_get({"dbId": databaseId});
} catch (error) {
if (error.status == 404) {
//$location.path('/admin/databases/');
......@@ -42,16 +39,12 @@ export const initializeDatabase = createThunkAction("INITIALIZE_DATABASE", funct
console.log("error fetching database", databaseId, error);
}
}
} else {
return {
database: {
name: '',
engine: Object.keys(MetabaseSettings.get('engines'))[0],
details: {},
created: false
},
onChangeLocation
name: '',
engine: Object.keys(MetabaseSettings.get('engines'))[0],
details: {},
created: false
}
}
}
......@@ -75,8 +68,6 @@ export const addSampleDataset = createThunkAction("ADD_SAMPLE_DATASET", function
// saveDatabase
export const saveDatabase = createThunkAction("SAVE_DATABASE", function(database, details) {
return async function(dispatch, getState) {
const { databases: { onChangeLocation } } = getState();
let savedDatabase, formState;
try {
......@@ -91,7 +82,7 @@ export const saveDatabase = createThunkAction("SAVE_DATABASE", function(database
//$scope.$emit("database:created", new_database);
savedDatabase = await MetabaseApi.db_create(database);
MetabaseAnalytics.trackEvent("Databases", "Create", database.engine);
onChangeLocation('/admin/databases?created');
dispatch(push('/admin/databases?created='+savedDatabase.id));
}
// this object format is what FormMessage expects:
......@@ -114,15 +105,11 @@ export const saveDatabase = createThunkAction("SAVE_DATABASE", function(database
// deleteDatabase
export const deleteDatabase = createThunkAction("DELETE_DATABASE", function(databaseId, redirect=false) {
return async function(dispatch, getState) {
const { databases: { onChangeLocation } } = getState();
try {
console.log("deleting database", databaseId);
await MetabaseApi.db_delete({"dbId": databaseId});
MetabaseAnalytics.trackEvent("Databases", "Delete", redirect ? "Using Detail" : "Using List");
if (redirect) {
console.log("redirecting");
onChangeLocation('/admin/databases/');
dispatch(push('/admin/databases/'));
}
return databaseId;
} catch(error) {
......@@ -133,7 +120,7 @@ export const deleteDatabase = createThunkAction("DELETE_DATABASE", function(data
// syncDatabase
export const syncDatabase = createThunkAction("SYNC_DATABASE", function(databaseId) {
return async function(dispatch, getState) {
return function(dispatch, getState) {
try {
let call = MetabaseApi.db_sync_metadata({"dbId": databaseId});
MetabaseAnalytics.trackEvent("Databases", "Manual Sync");
......@@ -147,11 +134,6 @@ export const syncDatabase = createThunkAction("SYNC_DATABASE", function(database
// reducers
// this is a backwards compatibility thing with angular to allow programmatic route changes. remove/change this when going to ReduxRouter
const onChangeLocation = handleActions({
["INITIALIZE_DATABASE"]: { next: (state, { payload }) => payload ? payload.onChangeLocation : state },
}, () => null);
const databases = handleActions({
["FETCH_DATABASES"]: { next: (state, { payload }) => payload },
["ADD_SAMPLE_DATASET"]: { next: (state, { payload }) => payload ? [...state, payload] : state },
......@@ -159,7 +141,7 @@ const databases = handleActions({
}, null);
const editingDatabase = handleActions({
["INITIALIZE_DATABASE"]: { next: (state, { payload }) => payload ? payload.database : state },
["INITIALIZE_DATABASE"]: { next: (state, { payload }) => payload },
["SAVE_DATABASE"]: { next: (state, { payload }) => payload.database || state },
["DELETE_DATABASE"]: { next: (state, { payload }) => null },
["SELECT_ENGINE"]: { next: (state, { payload }) => ({...state, engine: payload }) }
......@@ -172,6 +154,5 @@ const formState = handleActions({
export default combineReducers({
databases,
editingDatabase,
formState,
onChangeLocation
formState
});
import React, { Component, PropTypes } from "react";
import { Link } from "react-router";
import Icon from "metabase/components/Icon.jsx";
import PopoverWithTrigger from "metabase/components/PopoverWithTrigger.jsx";
......@@ -30,14 +31,14 @@ export default class ObjectActionsSelect extends Component {
>
<ul className="UserActionsSelect">
<li>
<a data-metabase-event={"Data Model;"+objectType+" Edit Page"}href={"/admin/datamodel/" + objectType + "/" + object.id} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
<Link to={"/admin/datamodel/" + objectType + "/" + object.id} data-metabase-event={"Data Model;"+objectType+" Edit Page"} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Edit {capitalize(objectType)}
</a>
</Link>
</li>
<li>
<a data-metabase-event={"Data Model;"+objectType+" History"} href={"/admin/datamodel/" + objectType + "/" + object.id + "/revisions"} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
<Link to={"/admin/datamodel/" + objectType + "/" + object.id + "/revisions"} data-metabase-event={"Data Model;"+objectType+" History"} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Revision History
</a>
</Link>
</li>
<li className="mt1 border-top">
<ModalWithTrigger
......
......@@ -43,7 +43,7 @@ export default class MetadataTableList extends Component {
_.each(tables, (table) => {
var row = (
<li key={table.id}>
<a href="#" className={cx("AdminList-item flex align-center no-decoration", { selected: this.props.tableId === table.id })} onClick={this.props.selectTable.bind(null, table)}>
<a className={cx("AdminList-item flex align-center no-decoration", { selected: this.props.tableId === table.id })} onClick={this.props.selectTable.bind(null, table)}>
{table.display_name}
<ProgressBar className="ProgressBar ProgressBar--mini flex-align-right" percentage={table.metadataStrength} />
</a>
......
import React, { Component, PropTypes } from "react";
import { Link } from "react-router";
import MetricItem from "./MetricItem.jsx";
......@@ -18,7 +19,7 @@ export default class MetricsList extends Component {
<div className="my3">
<div className="flex mb1">
<h2 className="px1 text-green">Metrics</h2>
<a data-metabase-event="Data Model;Add Metric Page" className="flex-align-right float-right text-bold text-brand no-decoration" href={"/admin/datamodel/metric/create?table="+tableMetadata.id}>+ Add a Metric</a>
<Link to={"/admin/datamodel/metric/create?table="+tableMetadata.id} data-metabase-event="Data Model;Add Metric Page" className="flex-align-right float-right text-bold text-brand no-decoration">+ Add a Metric</Link>
</div>
<table className="AdminTable">
<thead>
......
import React, { Component, PropTypes } from "react";
import { Link } from "react-router";
import SegmentItem from "./SegmentItem.jsx";
......@@ -18,7 +19,7 @@ export default class SegmentsList extends Component {
<div className="my3">
<div className="flex mb1">
<h2 className="px1 text-purple">Segments</h2>
<a data-metabase-event="Data Model;Add Segment Page" className="flex-align-right float-right text-bold text-brand no-decoration" href={"/admin/datamodel/segment/create?table="+tableMetadata.id}>+ Add a Segment</a>
<Link to={"/admin/datamodel/segment/create?table="+tableMetadata.id} data-metabase-event="Data Model;Add Segment Page" className="flex-align-right float-right text-bold text-brand no-decoration">+ Add a Segment</Link>
</div>
<table className="AdminTable">
<thead>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment