Skip to content
Snippets Groups Projects
Commit b08a9823 authored by Allen Gilliland's avatar Allen Gilliland
Browse files

Merge branch 'segments' of github.com:metabase/metabase into segments

parents f2a1a541 bea51908
Branches
Tags
No related merge requests found
Showing
with 346 additions and 39 deletions
......@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.12.1.991</string>
<string>0.13.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.12.1.991</string>
<string>0.13.0.0</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
......
......@@ -8,6 +8,9 @@ You'll need to run the following commands before building the app:
# Fetch and initialize git submodule
git submodule update --init
# Install libcurl (needed by WWW::Curl::Simple)
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
......
import React, { Component, PropTypes } from "react";
import Icon from "metabase/components/Icon.jsx";
import PopoverWithTrigger from "metabase/components/PopoverWithTrigger.jsx";
export default class UserActionsSelect extends Component {
static propTypes = {
aggregation: PropTypes.object.isRequired
};
onRemoveAggregation() {
}
render() {
let { aggregation } = this.props;
return (
<PopoverWithTrigger
ref="popover"
className="block"
triggerElement={<span className="text-grey-1 text-grey-4-hover"><Icon name={'ellipsis'}></Icon></span>}
>
<ul className="UserActionsSelect">
<li>
<a href={"/admin/datamodel/aggregation/"+aggregation.id} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Edit Aggregation
</a>
</li>
<li>
<a href={"/admin/datamodel/aggregation/"+aggregation.id+"/revisions"} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Revision History
</a>
</li>
<li className="mt1 p2 border-top bg-error-hover text-error text-white-hover cursor-pointer" onClick={this.onRemoveAggregation.bind(this)}>
Remove Aggregation
</li>
</ul>
</PopoverWithTrigger>
);
}
}
import React, { Component, PropTypes } from "react";
import AggregationActionSelect from "./AggregationActionSelect.jsx";
export default class AggregationItem extends Component {
static propTypes = {
aggregation: PropTypes.object.isRequired,
};
render() {
let { aggregation } = this.props;
return (
<tr className="mt1 mb3">
<td className="px1">
{aggregation.name}
</td>
<td className="px1 text-ellipsis">
{aggregation.formula}
</td>
<td className="px1 text-centered">
<AggregationActionSelect aggregation={aggregation}/>
</td>
</tr>
)
}
}
import React, { Component, PropTypes } from "react";
import AggregationItem from "./AggregationItem.jsx";
export default class AggregationsList extends Component {
static propTypes = {};
render() {
let { table } = this.props;
table.aggregations = [
{ id: "1", name: "Average Customer Age", formula: "Average of Age, Filtered by Customers" }
];
return (
<div className="mb4">
<div className="flex mb1">
<h2 className="px1 text-green">Aggregations</h2>
<a className="flex-align-right float-right text-bold text-brand no-decoration" href={"/admin/datamodel/aggregation/create?table="+table.id}>+ Add an Aggregations</a>
</div>
<table className="AdminTable">
<thead>
<tr>
<th>Name</th>
<th className="full">Formula</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{table.aggregations.map(aggregation =>
<AggregationItem
key={aggregation.id}
aggregation={aggregation}
/>
)}
</tbody>
</table>
</div>
);
}
}
......@@ -7,7 +7,7 @@ import MetabaseCore from "metabase/lib/core";
import _ from "underscore";
export default class MetadataField extends Component {
export default class Column extends Component {
constructor(props, context) {
super(props, context);
this.isVisibilityType = this.isVisibilityType.bind(this);
......
import React, { Component, PropTypes } from "react";
import ColumnItem from "./ColumnItem.jsx";
export default class ColumnsList extends Component {
constructor(props, context) {
super(props, context);
this.state = {};
}
static propTypes = {};
static defaultProps = {};
render() {
let { table } = this.props;
return (
<div>
<h2 className="px1 text-orange">Columns</h2>
<div className="text-uppercase text-grey-3 py1">
<div style={{minWidth: 420}} className="float-left px1">Column</div>
<div className="flex clearfix">
<div className="flex-half px1">Visibility</div>
<div className="flex-half px1">Type</div>
<div className="flex-half px1">Details</div>
</div>
</div>
<ol className="border-top border-bottom">
{table.fields.map((field) => {
return (
<ColumnItem
key={field.id}
field={field}
idfields={this.props.idfields}
updateField={this.props.updateField}
updateFieldSpecialType={this.props.updateFieldSpecialType}
updateFieldTarget={this.props.updateFieldTarget}
/>
);
})}
</ol>
</div>
);
}
}
......@@ -59,8 +59,8 @@ export default class MetadataHeader extends Component {
render() {
return (
<div className="MetadataEditor-header flex align-center">
<div className="MetadataEditor-headerSection h2">
<span className="text-grey-4">Edit Metadata for</span> {this.renderDbSelector()}
<div className="MetadataEditor-headerSection h2 mb2">
<span className="text-grey-4">Current database:</span> {this.renderDbSelector()}
</div>
<div className="MetadataEditor-headerSection flex-align-right flex align-center">
<SaveStatus ref="status" />
......
import React, { Component, PropTypes } from "react";
import AggregationsList from "./AggregationsList.jsx";
import ColumnsList from "./ColumnsList.jsx";
import SegmentsList from "./SegmentsList.jsx";
import Input from "metabase/components/Input.jsx";
import MetadataField from "./MetadataField.jsx";
import ProgressBar from "metabase/components/ProgressBar.jsx";
import cx from "classnames";
......@@ -74,24 +77,11 @@ export default class MetadataTable extends Component {
return false;
}
var fields = this.props.table.fields.map((field) => {
return (
<MetadataField
key={field.id}
field={field}
idfields={this.props.idfields}
updateField={this.props.updateField}
updateFieldSpecialType={this.props.updateFieldSpecialType}
updateFieldTarget={this.props.updateFieldTarget}
/>
);
});
return (
<div className="MetadataTable px2 flex-full">
<div className="MetadataTable-title flex flex-column bordered rounded">
<Input className="AdminInput TableEditor-table-name text-bold border-bottom rounded-top" type="text" value={this.props.table.display_name} onBlurChange={this.onNameChange}/>
<Input className="AdminInput TableEditor-table-description rounded-bottom" type="text" value={this.props.table.description} onBlurChange={this.onDescriptionChange} placeholder="No table description yet" />
<Input className="AdminInput TableEditor-table-name text-bold border-bottom rounded-top" type="text" value={table.display_name} onBlurChange={this.onNameChange}/>
<Input className="AdminInput TableEditor-table-description rounded-bottom" type="text" value={table.description} onBlurChange={this.onDescriptionChange} placeholder="No table description yet" />
</div>
<div className="MetadataTable-header flex align-center py2 text-grey-3">
<span className="mx1 text-uppercase">Visibility</span>
......@@ -102,17 +92,19 @@ export default class MetadataTable extends Component {
</span>
</div>
<div className={"mt2 " + (this.isHidden() ? "disabled" : "")}>
<div className="text-uppercase text-grey-3 py1">
<div style={{minWidth: 420}} className="float-left">Column</div>
<div className="flex clearfix">
<div className="flex-half px1">Visibility</div>
<div className="flex-half px1">Type</div>
<div className="flex-half px1">Details</div>
</div>
</div>
<ol className="border-top border-bottom">
{fields}
</ol>
<SegmentsList
table={table}
/>
<AggregationsList
table={table}
/>
<ColumnsList
table={table}
idfields={this.props.idfields}
updateField={this.props.updateField}
updateFieldSpecialType={this.props.updateFieldSpecialType}
updateFieldTarget={this.props.updateFieldTarget}
/>
</div>
</div>
);
......
......@@ -20,7 +20,7 @@ export default class MetadataTableList extends Component {
static propTypes = {
tableId: PropTypes.number,
tables: PropTypes.object.isRequired,
tables: PropTypes.array.isRequired,
selectTable: PropTypes.func.isRequired
};
......
import React, { Component, PropTypes } from "react";
import Icon from "metabase/components/Icon.jsx";
import PopoverWithTrigger from "metabase/components/PopoverWithTrigger.jsx";
export default class UserActionsSelect extends Component {
static propTypes = {
segment: PropTypes.object.isRequired
};
onRemoveSegment() {
}
render() {
let { segment } = this.props;
return (
<PopoverWithTrigger
ref="popover"
className="block"
triggerElement={<span className="text-grey-1 text-grey-4-hover"><Icon name={'ellipsis'}></Icon></span>}
>
<ul className="UserActionsSelect">
<li>
<a href={"/admin/datamodel/segment/"+segment.id} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Edit Segment
</a>
</li>
<li>
<a href={"/admin/datamodel/segment/"+segment.id+"/revisions"} className="py1 px2 block bg-brand-hover text-white-hover no-decoration cursor-pointer">
Revision History
</a>
</li>
<li className="mt1 p2 border-top bg-error-hover text-error text-white-hover cursor-pointer" onClick={this.onRemoveSegment.bind(this)}>
Remove Segment
</li>
</ul>
</PopoverWithTrigger>
);
}
}
import React, { Component, PropTypes } from "react";
import SegmentActionSelect from "./SegmentActionSelect.jsx";
export default class SegmentItem extends Component {
static propTypes = {
segment: PropTypes.object.isRequired,
};
render() {
let { segment } = this.props;
return (
<tr className="mt1 mb3">
<td className="px1">
{segment.name}
</td>
<td className="px1 text-ellipsis">
Filtered by {segment.rule}
</td>
<td className="px1 text-centered">
<SegmentActionSelect segment={segment}/>
</td>
</tr>
)
}
}
import React, { Component, PropTypes } from "react";
import SegmentItem from "./SegmentItem.jsx";
export default class SegmentsList extends Component {
static propTypes = {};
render() {
let { table } = this.props;
table.segments = [
{ id: "1", name: "Monthly New Users", rule: "Created At, Classification, and Email → Not Employee" },
{ id: "2", name: "Active Users", rule: "User ID → Last Login, Classification, and Email → Not Employee" },
{ id: "3", name: "Customers", rule: "Classification, Created At, and User ID → Total Purchases" },
{ id: "4", name: "Agents", rule: "Classification and Email → Not Employee" }
];
return (
<div className="mb4">
<div className="flex mb1">
<h2 className="px1 text-purple">Segments</h2>
<a className="flex-align-right float-right text-bold text-brand no-decoration" href={"/admin/datamodel/segment/create?table="+table.id}>+ Add a Segment</a>
</div>
<table className="AdminTable">
<thead>
<tr>
<th>Name</th>
<th className="full">Rule</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{table.segments.map(segment =>
<SegmentItem
key={segment.id}
segment={segment}
/>
)}
</tbody>
</table>
</div>
);
}
}
......@@ -3,7 +3,7 @@ import _ from "underscore";
import MetadataEditor from './components/MetadataEditor.jsx';
angular
.module('metabase.admin.metadata.controllers', [
.module('metabase.admin.datamodel.controllers', [
'metabase.services',
'metabase.directives',
'metabase.forms'
......@@ -80,11 +80,11 @@ function($scope, $route, $routeParams, $location, $q, $timeout, databases, Metab
}
$scope.selectDatabase = function(db) {
$location.path('/admin/metadata/'+db.id);
$location.path('/admin/datamodel/database/'+db.id);
};
$scope.selectTable = function(table) {
$location.path('/admin/metadata/'+table.db_id+'/table/'+table.id);
$location.path('/admin/datamodel/database/'+table.db_id+'/table/'+table.id);
};
$scope.updateTable = function(table) {
......
angular
.module('metabase.admin.metadata', [
'metabase.admin.metadata.controllers'
.module('metabase.admin.datamodel', [
'metabase.admin.datamodel.controllers'
])
.config(['$routeProvider', function ($routeProvider) {
var metadataRoute = {
......@@ -13,8 +13,8 @@ angular
}
};
$routeProvider.when('/admin/metadata', metadataRoute);
$routeProvider.when('/admin/metadata/:databaseId', metadataRoute);
$routeProvider.when('/admin/metadata/:databaseId/:mode', metadataRoute);
$routeProvider.when('/admin/metadata/:databaseId/:mode/:tableId', metadataRoute);
$routeProvider.when('/admin/datamodel/database', metadataRoute);
$routeProvider.when('/admin/datamodel/database/:databaseId', metadataRoute);
$routeProvider.when('/admin/datamodel/database/:databaseId/:mode', metadataRoute);
$routeProvider.when('/admin/datamodel/database/:databaseId/:mode/:tableId', metadataRoute);
}]);
......@@ -17,7 +17,7 @@ var Metabase = angular.module('metabase', [
'metabaseadmin.databases',
'metabaseadmin.people',
'metabaseadmin.settings',
'metabase.admin.metadata',
'metabase.admin.datamodel',
]);
Metabase.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode({
......
......@@ -62,8 +62,8 @@ export default class Navbar extends Component {
</a>
</li>
<li>
<a className={cx(classes, {"is--selected": this.isActive("/admin/metadata")})} href="/admin/metadata/">
Metadata
<a className={cx(classes, {"is--selected": this.isActive("/admin/datamodel")})} href="/admin/datamodel/database">
Data Model
</a>
</li>
<li>
......
......@@ -426,3 +426,26 @@
padding-top: 1em;
min-width: 180px;
}
.AdminTable {
border-collapse: collapse;
border-spacing: 0;
white-space: nowrap;
text-align: left;
}
.AdminTable th {
text-transform: uppercase;
color: color(var(--base-grey) shade(40%));
padding: var(--padding-1);
font-weight: normal;
}
.AdminTable thead {
border-bottom: var(--border-size) var(--border-style) var(--border-color);
}
.AdminTable tbody tr:first-child td {
padding-top: var(--margin-1);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment