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

Merge branch 'master' into orgless

parents 561a8fd1 4fc0a5b2
Branches
Tags
No related merge requests found
Showing
with 381 additions and 337 deletions
......@@ -22,6 +22,8 @@
(expect-when-testing-against-dataset 1)
(expect-when-testing-mongo 1)
(expect-with-all-drivers 1)
(expect-with-dataset 1)
(expect-with-datasets 1)
(ins 1)
(let-400 1)
(let-404 1)
......@@ -31,7 +33,7 @@
(macrolet 1)
(org-perms-case 1)
(pdoseq 1)
(qp-expect-with-all-drivers 1)
(qp-expect-with-datasets 1)
(resolve-private-fns 1)
(symbol-macrolet 1)
(sync-in-context 2)
......
......@@ -14,14 +14,14 @@
[org.clojure/data.csv "0.1.2"] ; CSV parsing / generation
[org.clojure/data.json "0.2.6"] ; JSON parsing / generation
[org.clojure/java.classpath "0.2.2"]
[org.clojure/java.jdbc "0.3.6"] ; basic jdbc access from clojure
[org.clojure/java.jdbc "0.3.7"] ; basic jdbc access from clojure
[org.clojure/tools.logging "0.3.1"] ; logging framework
[org.clojure/tools.macro "0.1.5"] ; tools for writing macros
[org.clojure/tools.namespace "0.2.10"]
[org.clojure/tools.reader "0.9.2"] ; Need to explictly specify this dep otherwise expectations doesn't seem to work right :'(
[org.clojure/tools.trace "0.7.8"] ; "tracing macros/fns to help you see what your code is doing"
[amalloy/ring-gzip-middleware "0.1.3"] ; Ring middleware to GZIP responses if client can handle it
[cheshire "5.4.0"] ; fast JSON encoding (used by Ring JSON middleware)
[cheshire "5.5.0"] ; fast JSON encoding (used by Ring JSON middleware)
[clj-http-lite "0.2.1"] ; HTTP client; lightweight version of clj-http that uses HttpURLConnection instead of Apache
[clj-time "0.9.0"] ; library for dealing with date/time
[colorize "0.1.1" :exclusions [org.clojure/clojure]] ; string output with ANSI color codes (for logging)
......@@ -40,7 +40,7 @@
com.sun.jdmk/jmxtools
com.sun.jmx/jmxri]]
[medley "0.6.0"] ; lightweight lib of useful functions
[org.liquibase/liquibase-core "3.3.3"] ; migration management (Java lib)
[org.liquibase/liquibase-core "3.3.5"] ; migration management (Java lib)
[org.slf4j/slf4j-log4j12 "1.7.12"]
[org.yaml/snakeyaml "1.15"] ; YAML parser (required by liquibase)
[postgresql "9.3-1102.jdbc41"] ; Postgres driver
......
......@@ -155,34 +155,108 @@ DatabasesControllers.controller('DatabaseEdit', ['$scope', '$routeParams', '$loc
}
]);
DatabasesControllers.controller('DatabaseTables', ['$scope', '$routeParams', '$location', 'Metabase',
function($scope, $routeParams, $location, Metabase) {
Metabase.db_get({
'dbId': $routeParams.databaseId
}, function(database) {
$scope.database = database;
}, function(error) {
console.log('error loading database', error);
if (error.status == 404) {
$location.path('/admin/databases/');
DatabasesControllers.controller('DatabaseMasterDetail', ['$scope', '$route', '$routeParams',
function($scope, $route, $routeParams) {
$scope.pane = 'settings';
// mildly hacky way to prevent reloading controllers as the URL changes
var lastRoute = $route.current;
$scope.$on('$locationChangeSuccess', function (event) {
if ($route.current.$$route.controller === 'DatabaseMasterDetail') {
var params = $route.current.params;
$route.current = lastRoute;
angular.forEach(params, function(value, key) {
$route.current.params[key] = value;
$routeParams[key] = value;
});
}
});
Metabase.db_tables({
'dbId': $routeParams.databaseId
}, function(tables) {
$scope.tables = tables;
}, function(error) {
$scope.routeParams = $routeParams;
$scope.$watch('routeParams', function() {
$scope.pane = $routeParams.mode;
}, true);
}
]);
});
DatabasesControllers.controller('DatabaseTables', ['$scope', '$routeParams', '$location', '$q', 'Metabase',
function($scope, $routeParams, $location, $q, Metabase) {
$scope.tableFields = {};
$scope.routeParams = $routeParams;
$scope.$watch('routeParams', function() {
loadData();
}, true);
function loadData() {
return loadDatabase()
.then(function() {
return updateTable();
})
.catch(function(error) {
console.log('error loading data', error);
if (error.status == 404) {
$location.path('/admin/databases');
}
});
}
function loadDatabase() {
if ($scope.$parent.database && $scope.$parent.database.id == $routeParams.databaseId) {
return $q.all([]); // just return an empty promise if we already loaded this db
} else {
return $q.all([
Metabase.db_get({ 'dbId': $routeParams.databaseId }).$promise
.then(function(database) {
$scope.$parent.database = database;
}),
Metabase.db_tables({ 'dbId': $routeParams.databaseId }).$promise
.then(function(tables) {
$scope.tables = tables;
return $q.all(tables.map(function(table) {
return Metabase.table_query_metadata({ 'tableId': table.id }).$promise
.then(function(result) {
$scope.tableFields[table.id] = result;
});
}));
})
]);
}
}
function updateTable() {
if ($routeParams.tableId !== undefined) {
$scope.$parent.table = $scope.tables.filter(function(t) { return $routeParams.tableId == t.id; })[0];
if (!$scope.$parent.table) {
$location.path('/admin/databases/'+$routeParams.databaseId+'/tables');
}
}
}
}
]);
DatabasesControllers.controller('DatabaseTable', ['$scope', '$routeParams', '$location', 'Metabase', 'ForeignKey',
function($scope, $routeParams, $location, Metabase, ForeignKey) {
$scope.routeParams = $routeParams;
$scope.$watch('routeParams', function() {
loadData();
}, true);
function loadData() {
Metabase.table_query_metadata({
'tableId': $routeParams.tableId
}, function(result) {
$scope.table = result;
$scope.getIdFields();
$scope.decorateWithTargets();
}, function(error) {
console.log(error);
if (error.status == 404) {
$location.path('/');
}
});
}
$scope.getIdFields = function() {
// fetch the ID fields
......@@ -316,13 +390,11 @@ DatabasesControllers.controller('DatabaseTable', ['$scope', '$routeParams', '$lo
};
$scope.fields = [];
$scope.dragControlListeners = {
containment: '.EntityGroup',
orderChanged: function(event) {
// Change order here
var new_order = _.map($scope.fields, function(field) {
var new_order = _.map($scope.table.fields, function(field) {
return field.id;
});
Metabase.table_reorder_fields({
......@@ -331,21 +403,6 @@ DatabasesControllers.controller('DatabaseTable', ['$scope', '$routeParams', '$lo
});
}
};
Metabase.table_query_metadata({
'tableId': $routeParams.tableId
}, function(result) {
$scope.table = result;
$scope.fields = $scope.table.fields;
$scope.getIdFields();
$scope.decorateWithTargets();
}, function(error) {
console.log(error);
if (error.status == 404) {
$location.path('/');
}
});
}
]);
......
......@@ -5,28 +5,25 @@ var AdminDatabases = angular.module('corvusadmin.databases', [
]);
AdminDatabases.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/admin/databases/', {
$routeProvider.when('/:orgSlug/admin/databases', {
templateUrl: '/app/admin/databases/partials/database_list.html',
controller: 'DatabaseList'
});
$routeProvider.when('/admin/databases/create', {
$routeProvider.when('/:orgSlug/admin/databases/create', {
templateUrl: '/app/admin/databases/partials/database_edit.html',
controller: 'DatabaseEdit'
});
$routeProvider.when('/admin/databases/:databaseId', {
templateUrl: '/app/admin/databases/partials/database_edit.html',
controller: 'DatabaseEdit'
});
$routeProvider.when('/admin/databases/:databaseId/tables/', {
templateUrl: '/app/admin/databases/partials/database_tables.html',
controller: 'DatabaseTables'
$routeProvider.when('/:orgSlug/admin/databases/:databaseId', {
redirectTo: '/:orgSlug/admin/databases/:databaseId/tables'
});
$routeProvider.when('/admin/databases/table/:tableId', {
templateUrl: '/app/admin/databases/partials/database_table.html',
controller: 'DatabaseTable'
$routeProvider.when('/:orgSlug/admin/databases/:databaseId/:mode', {
templateUrl: '/app/admin/databases/partials/database_master_detail.html',
controller: 'DatabaseMasterDetail'
});
$routeProvider.when('/admin/databases/field/:fieldId', {
templateUrl: '/app/admin/databases/partials/database_table_field.html',
controller: 'DatabaseTableField'
$routeProvider.when('/:orgSlug/admin/databases/:databaseId/:mode/:tableId', {
templateUrl: '/app/admin/databases/partials/database_master_detail.html',
controller: 'DatabaseMasterDetail'
});
}]);
......@@ -6,36 +6,6 @@
<h2 class="Breadcrumb Breadcrumb--page" ng-if="database.id">{{database.name}}</h2>
</section>
<section class="Grid Grid--gutters Grid--full">
<div class="Grid-cell Cell--2of3">
<form class="Form-new bordered rounded shadowed" name="form" novalidate>
<!-- Form -->
<div ng-include="'/app/admin/databases/partials/database_edit_forms.html'"></div>
<!-- Bottom Actions -->
<div class="Form-actions">
<button class="Button" ng-class="{'Button--primary': form.$valid}" ng-click="save(database, details)" ng-disabled="!form.$valid">
Save
</button>
<mb-form-message></mb-form-message>
</div>
</form>
</div>
<!-- Sidebar Actions -->
<div class="Grid-cell Cell--1of3" ng-if="database.id">
<div class="Actions">
<h3>Actions</h3>
<div class="Actions-group">
<button class="Button" mb-action-button="sync" success-text="Sync triggered!" failed-text="Failed to sync" active-text="Starting ...">Sync</button>
</div>
<div class="Actions-group Actions--dangerZone">
<label class="Actions-groupLabel block">Danger Zone:</label>
<!-- TODO: this doesn't do anything because its unclear if its really safe to delete dbs -->
<button class="Button Button--danger">Remove this database</button>
</div>
</div>
</div>
<section class="Grid Grid--gutters Grid--full" ng-include="'/app/admin/databases/partials/database_edit_pane.html'" >
</section>
</div>
<div class="Grid-cell Cell--2of3">
<form class="Form-new bordered rounded shadowed" name="form" novalidate>
<!-- Form -->
<div ng-include="'/app/admin/databases/partials/database_edit_forms.html'"></div>
<!-- Bottom Actions -->
<div class="Form-actions">
<button class="Button" ng-class="{'Button--primary': form.$valid}" ng-click="save(database, details)" ng-disabled="!form.$valid">
Save
</button>
<mb-form-message></mb-form-message>
</div>
</form>
</div>
<!-- Sidebar Actions -->
<div class="Grid-cell Cell--1of3" ng-if="database.id">
<div class="Actions bordered rounded shadowed">
<h3>Actions</h3>
<div class="Actions-group">
<button class="Button" mb-action-button="sync" success-text="Sync triggered!" failed-text="Failed to sync" active-text="Starting ...">Sync</button>
</div>
<div class="Actions-group Actions--dangerZone">
<label class="Actions-groupLabel block">Danger Zone:</label>
<!-- TODO: this doesn't do anything because its unclear if its really safe to delete dbs -->
<button class="Button Button--danger">Remove this database</button>
</div>
</div>
</div>
<div class="wrapper">
<section class="PageHeader clearfix">
<a class="Button Button--primary float-right" href="/admin/databases/create">Add database</a>
<a class="Button Button--primary float-right" cv-org-href="/admin/databases/create">Add database</a>
<h2 class="PageTitle">Databases</h2>
</section>
......@@ -22,14 +22,13 @@
</tr>
<tr ng-repeat="database in databases">
<td>
<a class="text-bold link" href="/admin/databases/{{database.id}}">{{database.name}}</a>
<a class="text-bold link" cv-org-href="/admin/databases/{{database.id}}/tables">{{database.name}}</a>
</td>
<td>
{{database.engine}}
</td>
<td class="Table-actions">
<!-- TODO: sync now button? -->
<a class="Button" href="/admin/databases/{{database.id}}/tables/">Tables</a>
<button class="Button Button--danger" ng-click="delete(database.id)" delete-confirm>Delete</button>
</td>
</tr>
......
<div class="wrapper full-height flex flex-column">
<section class="my3 clearfix">
<div class="py2 float-right">
<div class="Button-group Button-group--blue text-uppercase text-bold">
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'tables' }" cv-org-href="/admin/databases/{{database.id}}/tables/{{table.id}}">Data</a>
<a class="Button AdminHoverItem" ng-class="{ 'Button--active': pane == 'settings' }" cv-org-href="/admin/databases/{{database.id}}/settings">Connection Details</a>
</div>
</div>
<h3 class="py2">{{database.name}}</h2>
</section>
<section class="DatabaseTablesAdmin Grid bordered rounded shadowed flex-full mb4" ng-show="pane == 'tables'">
<div class="DatabaseTablesAdminSidebar relative Grid-cell Cell--1of3 border-right" ng-controller="DatabaseTables" ng-include="'/app/admin/databases/partials/database_table_list.html'">
</div>
<div class="DatabaseTableAdmin relative Grid-cell" ng-controller="DatabaseTable" ng-if="table" ng-include="'/app/admin/databases/partials/database_table_detail.html'">
</div>
<div class="Grid-cell flex layout-centered relative" ng-if="!table">
<h2 class="text-grey-3">Select any table to see its schema and add or edit metadata.</h2>
</div>
</section>
<section ng-show="pane == 'settings'" ng-controller="DatabaseEdit">
<div class="Grid Grid--gutters Grid--full" ng-include="'/app/admin/databases/partials/database_edit_pane.html'" >
</div>
</section>
</div>
<svg width="0" height="0">
<defs>
<pattern id="dragger" width=".50" height=".08">
<rect width="2px" height="2px" fill="#cacaca">
</pattern>
</defs>
</svg>
<div class="wrapper">
<section class="Breadcrumbs">
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/">Databases</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/{{table.db.id}}/tables/">{{table.db.name}}</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<h2 class="Breadcrumb Breadcrumb--page">{{table.name}}</h2>
</section>
<section>
<div class="pb2 clearfix" ng-if="table">
<div class="clearfix">
<div class="text-grey-3 float-right" ng-if="table.entity_name">
<p class="text-right">actual table name: {{table.name}}</p>
<p class="py1 text-right clearfix">
<a class="link float-right" ng-click="syncMetadata()">Sync Metadata</a>
</p>
</div>
<div class="float-left">
<h2 class="inline-block">
<a class="text-brand" href="#" ng-class="{EditedEntity: table.entity_name}" editable-text="table.entity_name" onaftersave="inlineSave()">
<span ng-if="table.entity_name">{{table.entity_name}}</span>
<span ng-if="!table.entity_name">{{table.name}}</span>
</a>
</h2>
<label class="Select mx2 inline-block">
<select ng-class="{CustomTypeApplied: table.entity_type }" ng-model="table.entity_type" ng-change="inlineSave()" ng-options="ent_type.id as ent_type.name for ent_type in utils.table_entity_types">
</select>
</label>
<div class="text-grey-4 mt1">
<a e-class="full" href="#" ng-class="{EditedEntity: table.description }" editable-text="table.description" onaftersave="inlineSave()">
{{table.description}}
<span ng-if="!table.description">Add a description ...</span>
</a>
</div>
</div>
</div>
</div>
<div class="DragBoundary mt2 border-bottom">
<ul as-sortable="dragControlListeners" ng-model="fields">
<li class="border-top py2 relative text-grey-3" ng-repeat="field in table.fields" as-sortable-item>
<div class="Drag-handle" title="Reorder" as-sortable-item-handle>
<svg width="9" height="36">
<rect class="Dragger" fill="url(#dragger)" width="6" height="42">
</svg>
</div>
<div class="ml3 mr1 clearfix">
<div class="float-right">
<span class="EntityOriginalType float-left">{{field.base_type}}</span>
</div>
<div>
<input ng-model="field.preview_display" type="checkbox" ng-change="inlineSaveField($index)">
<h3 class="text-bold inline-block">
<a class="link mr1" href="/admin/databases/field/{{field.id}}">{{field.name}}</a>
</h3>
<label class="Select mx1">
<select ng-class="{CustomTypeApplied: field.field_type }" ng-model="field.field_type" ng-change="inlineSaveField($index)" ng-options="spec_type.id as spec_type.name for spec_type in utils.field_field_types">
</select>
</label>
<label class="Select mx1">
<select ng-class="{CustomTypeApplied: field.special_type }" ng-model="field.special_type" ng-change="inlineSpecialTypeChange($index)" ng-options="spec_type.id as spec_type.name for spec_type in utils.field_special_types">
</select>
</label>
<label class="Select mx1" ng-if="field.special_type=='fk'">
<select ng-model="field.target_id" ng-change="inlineChangeFKTarget($index)" ng-options="idf.id as idf.displayName for idf in idfields">
<option value="" disabled selected>Target Field</option>
</select>
</label>
</div>
<div class="pt1 full">
<a e-class="full" href="#" ng-class="{EditedEntity: field.description }" editable-text="field.description" onaftersave="inlineSaveField($index)">
{{field.description}}
<span ng-if="!field.description">Add a description...</span>
</a>
</div>
</div>
</li>
</ul>
</div>
</section>
</div>
\ No newline at end of file
<div class="TableField py4 pr4">
<label class="Select Select--small float-right">
<select ng-class="{CustomTypeApplied: table.entity_type, 'Select--unselected': !table.entity_type }" ng-model="table.entity_type" ng-change="inlineSave()" ng-options="ent_type.id as ent_type.name for ent_type in utils.table_entity_types"></select>
</label>
<div>
<span class="h2 text-bold" editable-text="table.entity_name" e-form="tableNameForm" onaftersave="inlineSave()">
{{table.entity_name || table.name}}
</span>
<span class="h5" ng-click="tableNameForm.$show()" ng-hide="tableNameForm.$visible">
Rename
</span>
</div>
<div class="mt2 full">
<a e-class="full" href="#" editable-text="table.description" onaftersave="console.log('nyi', $index)">
{{table.description || "Add a description..."}}
</a>
</div>
</div>
<div class="TableFieldList" mb-scroll-shadow>
<ul as-sortable="dragControlListeners" ng-model="table.fields">
<li class="TableField AdminHoverItem py2 pr4 relative" ng-repeat="field in table.fields track by field.id" as-sortable-item>
<div class="Drag-handle" title="Reorder" as-sortable-item-handle>
<svg width="9" height="36">
<rect class="Dragger" fill="url(#dragger)" width="6" height="42">
</svg>
</div>
<div class="Grid">
<div class="Grid-cell">
<input ng-model="field.preview_display" type="checkbox" ng-change="inlineSaveField($index)">
<h3 class="TableField-name inline-block">
{{field.name}}
</h3>
<span class="EntityOriginalType inline-block">{{field.base_type}}</span>
<div class="full">
<a e-class="full" href="#" ng-class="{EditedEntity: field.description }" editable-text="field.description" onaftersave="inlineSaveField($index)">
{{field.description || "Add a description..."}}
</a>
</div>
</div>
<div class="Grid Grid-cell Cell--1of3 py1 flex align-start">
<label class="Grid-cell mx2 Select Select--small Select--blue">
<select ng-model="field.field_type" ng-change="inlineSaveField($index)" ng-options="spec_type.id as spec_type.name for spec_type in utils.field_field_types">
</select>
</label>
<div class="Grid-cell flex flex-column">
<label class="Select flex-full Select--small Select--purple">
<select ng-model="field.special_type" ng-change="inlineSpecialTypeChange($index)" ng-options="spec_type.id as spec_type.name for spec_type in utils.field_special_types">
</select>
</label>
<label class="Select Select--small mt1" ng-if="field.special_type=='fk'">
<select ng-model="field.target_id" ng-change="inlineChangeFKTarget($index)" ng-options="idf.id as idf.displayName for idf in idfields">
<option value="" disabled selected>Target Field</option>
</select>
</label>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="wrapper">
<section class="Breadcrumbs">
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/">Databases</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/{{field.table.db.id}}/tables/">{{field.table.db.name}}</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/table/{{field.table.id}}">{{field.table.name}}</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<h2 class="Breadcrumb Breadcrumb--page">{{field.name}}</h2>
</section>
<section>
<div>
<h3>Information</h3>
<b>Data Type:</b> {{field.base_type}} <br/>
<b>User Assigned Type:</b> <a href="#" editable-select="field.special_type" onaftersave="inlineSave()" e-ng-options="spec_type.id as spec_type.name for spec_type in utils.field_special_types">{{utils.fieldSpecialType(field.special_type)}}</a> <br/>
<b>Display in preview:</b> <a href="#" editable-select="field.preview_display" onaftersave="inlineSave()" e-ng-options="spec_type.id as spec_type.name for spec_type in utils.boolean_types">{{field.preview_display && "Yes" || "No"}}</a> <br/>
<b>Description:</b> <a href="#" editable-textarea="field.description" e-rows="4" e-cols="50" onaftersave="inlineSave()">{{field.description}}<span ng-if="!field.description">Description</span></a> <br/>
<div ng-if="fks">
<b>Relationships:</b><br>
<table class="Table">
<thead>
<tr>
<th>Destination Table</th>
<th>Destination Field</th>
<th>Relationship Type</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="fk in fks">
<td>{{fk.destination.table.name}}</td>
<td>{{fk.destination.name}}</td>
<td>{{fk.relationship}}</td>
<td> <a class="Button Button--remove" href="#" ng-click="deleteRelationship(fk.id)" delete-confirm>x</a></td>
</tr>
</tbody>
</table>
</div>
<hr />
<cv-comments type="field" id="field.id" title="Notes"></cv-comments>
</div>
<div>
<h3>Summary</h3>
<table class="table table-striped">
<thead>
<tr>
<th>Characteristic</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in field_summary">
<td>{{item[0]}}</td>
<td>{{item[1]}}</td>
</tr>
</tbody>
</table>
<div class="border-top py2 clearfix">
<div class="py2">
<input class="Button Button--primary float-right" type="button" value="Update Mapped Values" role="button" ng-click="updateMappedValues()" />
<h3>Valid Values</h3>
</div>
<table class="Table">
<thead>
<tr>
<th>Value</th>
<th>Remapped Value</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="value in field_values.values">
<td>{{value || "Empty Field"}}</td>
<td> <input class="input" ng-model="field_values.human_readable_values[value]"> </td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
<div class="border-bottom py1">
<cv-search-icon class="ml2 text-grey-2" width="18" height="18"></cv-search-icon>
<input class="Form-input Form-input--medium mx1" type="text" placeholder="Search for a table name" value="" ng-model="tableSearchText" autofocus>
</div>
<div class="DatabaseList" mb-scroll-shadow>
<ul class="p2">
<li class="flex flex-column layout-centered full-height text-grey-3" ng-show="!tables">
<cv-loading-icon class="text-brand"></cv-loading-icon>
<h3>Loading ...</h3>
</li>
<li class="DatabaseListItem AdminHoverItem rounded py1 px2 text-grey-4" ng-class="{ 'DatabaseListItem--active': t.id == table.id }" ng-repeat="t in tables | filter:tableSearchText">
<a class="link link--nohover text-current" cv-org-href="/admin/databases/{{database.id}}/tables/{{t.id}}">
<h4>{{t.name}}</h4>
</a>
</li>
</ul>
</div>
<div class="wrapper">
<section class="Breadcrumbs">
<a class="Breadcrumb Breadcrumb--path" href="/admin/databases/">Databases</a>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<h2 class="Breadcrumb Breadcrumb--page">{{database.name}}</h2>
<cv-chevron-right-icon class="Breadcrumb-divider" width="12px" height="12px"></cv-chevron-right-icon>
<h2 class="Breadcrumb Breadcrumb--page">Tables</h2>
</section>
<section>
<table class="ContentTable">
<thead>
<tr>
<th>Table</th>
<th>Rows</th>
</tr>
</thead>
<tbody>
<tr ng-show="!tables">
<td colspan=4>
<cv-loading-icon></cv-loading-icon>
<h3>Loading ...</h3>
</td>
</tr>
<tr ng-repeat="table in tables">
<td>
<a class="text-bold link" href="/admin/databases/table/{{table.id}}">{{table.name}}</a>
</td>
<td>
{{table.rows}}
</td>
</tr>
</tbody>
</table>
</section>
</div>
<section class="wrapper">
<div ng-repeat="db in databases">
<div class="py2 clearfix">
<div class="float-right">
<a class="Button Button--primary" ng-click="syncDatabase(db.id)">Re-Sync Database</a>
</div>
<h2>{{db.name}}</h2>
</div>
<ul class="bg-white bordered rounded shadowed">
<li class="p2 border-bottom" ng-repeat="table in db.tables">
<h4>
<a class="EntityListItem link" ng-class="{ EditedEntity: table.entity_name, EditedEntityMarker: table.entity_name }" href="/admin/datasets/{{table.id}}">
<span class="ModifiedEntity">{{table.entity_name}}</span>
<span ng-if="!table.entity_name">{{table.name}}</span>
</a>
</h4>
</li>
</ul>
</div>
</section>
......@@ -70,6 +70,7 @@
border-bottom: none;
border-right: none;
border-radius: 0;
box-shadow: none;
}
.Button-group .Button--active {
......@@ -81,6 +82,19 @@
border-left: none;
}
.Button-group--blue {
border-color: rgb(194,216,242);
}
.Button-group--blue .Button {
color: rgb(147,155,178);
}
.Button-group--blue .Button--active {
background-color: rgb(227,238,250);
color: rgb(74,144,226);
}
.Button:disabled {
opacity: 0.5;
cursor: not-allowed;
......
......@@ -2,6 +2,8 @@
--form-padding: 1em;
--form-input-placeholder-color: #C0C0C0;
--form-input-size: 1.571rem;
--form-input-size-medium: 1.25rem;
--form-input-size-small: 1.0rem;
--form-label-color: #949494;
--form-offset: 2.4rem;
......@@ -54,6 +56,14 @@
font-family: var(--default-font-family);
}
.Form-input--medium {
font-size: var(--form-input-size-medium);
}
.Form-input--small {
font-size: var(--form-input-size-small);
}
.Form-input:focus {
outline: none;
}
......
<svg viewBox="0 0 66 85" ng-attr-width="{{width}}" ng-attr-height="{{height}}">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo"fill="#4990E2">
<path d="M46.8253288,70.4935014 C49.5764899,70.4935014 51.8067467,68.1774705 51.8067467,65.3205017 C51.8067467,62.4635329 49.5764899,60.147502 46.8253288,60.147502 C44.0741676,60.147502 41.8439108,62.4635329 41.8439108,65.3205017 C41.8439108,68.1774705 44.0741676,70.4935014 46.8253288,70.4935014 Z M32.8773585,84.9779005 C35.6285197,84.9779005 37.8587764,82.6618697 37.8587764,79.8049008 C37.8587764,76.947932 35.6285197,74.6319011 32.8773585,74.6319011 C30.1261973,74.6319011 27.8959405,76.947932 27.8959405,79.8049008 C27.8959405,82.6618697 30.1261973,84.9779005 32.8773585,84.9779005 Z M32.8773585,70.4935014 C35.6285197,70.4935014 37.8587764,68.1774705 37.8587764,65.3205017 C37.8587764,62.4635329 35.6285197,60.147502 32.8773585,60.147502 C30.1261973,60.147502 27.8959405,62.4635329 27.8959405,65.3205017 C27.8959405,68.1774705 30.1261973,70.4935014 32.8773585,70.4935014 Z M18.9293882,70.4935014 C21.6805494,70.4935014 23.9108062,68.1774705 23.9108062,65.3205017 C23.9108062,62.4635329 21.6805494,60.147502 18.9293882,60.147502 C16.1782271,60.147502 13.9479703,62.4635329 13.9479703,65.3205017 C13.9479703,68.1774705 16.1782271,70.4935014 18.9293882,70.4935014 Z M46.8253288,56.0091023 C49.5764899,56.0091023 51.8067467,53.6930714 51.8067467,50.8361026 C51.8067467,47.9791337 49.5764899,45.6631029 46.8253288,45.6631029 C44.0741676,45.6631029 41.8439108,47.9791337 41.8439108,50.8361026 C41.8439108,53.6930714 44.0741676,56.0091023 46.8253288,56.0091023 Z M18.9293882,56.0091023 C21.6805494,56.0091023 23.9108062,53.6930714 23.9108062,50.8361026 C23.9108062,47.9791337 21.6805494,45.6631029 18.9293882,45.6631029 C16.1782271,45.6631029 13.9479703,47.9791337 13.9479703,50.8361026 C13.9479703,53.6930714 16.1782271,56.0091023 18.9293882,56.0091023 Z M46.8253288,26.8995984 C49.5764899,26.8995984 51.8067467,24.5835675 51.8067467,21.7265987 C51.8067467,18.8696299 49.5764899,16.553599 46.8253288,16.553599 C44.0741676,16.553599 41.8439108,18.8696299 41.8439108,21.7265987 C41.8439108,24.5835675 44.0741676,26.8995984 46.8253288,26.8995984 Z M32.8773585,41.5247031 C35.6285197,41.5247031 37.8587764,39.2086723 37.8587764,36.3517034 C37.8587764,33.4947346 35.6285197,31.1787037 32.8773585,31.1787037 C30.1261973,31.1787037 27.8959405,33.4947346 27.8959405,36.3517034 C27.8959405,39.2086723 30.1261973,41.5247031 32.8773585,41.5247031 Z M32.8773585,10.3459994 C35.6285197,10.3459994 37.8587764,8.02996853 37.8587764,5.17299969 C37.8587764,2.31603085 35.6285197,0 32.8773585,0 C30.1261973,0 27.8959405,2.31603085 27.8959405,5.17299969 C27.8959405,8.02996853 30.1261973,10.3459994 32.8773585,10.3459994 Z M32.8773585,26.8995984 C35.6285197,26.8995984 37.8587764,24.5835675 37.8587764,21.7265987 C37.8587764,18.8696299 35.6285197,16.553599 32.8773585,16.553599 C30.1261973,16.553599 27.8959405,18.8696299 27.8959405,21.7265987 C27.8959405,24.5835675 30.1261973,26.8995984 32.8773585,26.8995984 Z M18.9293882,26.8995984 C21.6805494,26.8995984 23.9108062,24.5835675 23.9108062,21.7265987 C23.9108062,18.8696299 21.6805494,16.553599 18.9293882,16.553599 C16.1782271,16.553599 13.9479703,18.8696299 13.9479703,21.7265987 C13.9479703,24.5835675 16.1782271,26.8995984 18.9293882,26.8995984 Z" id="Oval-14" opacity="0.2"></path>
<path d="M60.773299,70.4935014 C63.5244602,70.4935014 65.754717,68.1774705 65.754717,65.3205017 C65.754717,62.4635329 63.5244602,60.147502 60.773299,60.147502 C58.0221379,60.147502 55.7918811,62.4635329 55.7918811,65.3205017 C55.7918811,68.1774705 58.0221379,70.4935014 60.773299,70.4935014 Z M4.98141795,70.3527958 C7.73257912,70.3527958 9.96283591,68.0367649 9.96283591,65.1797961 C9.96283591,62.3228273 7.73257912,60.0067964 4.98141795,60.0067964 C2.23025679,60.0067964 0,62.3228273 0,65.1797961 C0,68.0367649 2.23025679,70.3527958 4.98141795,70.3527958 Z M60.773299,56.0091023 C63.5244602,56.0091023 65.754717,53.6930714 65.754717,50.8361026 C65.754717,47.9791337 63.5244602,45.6631029 60.773299,45.6631029 C58.0221379,45.6631029 55.7918811,47.9791337 55.7918811,50.8361026 C55.7918811,53.6930714 58.0221379,56.0091023 60.773299,56.0091023 Z M32.8773585,56.0091023 C35.6285197,56.0091023 37.8587764,53.6930714 37.8587764,50.8361026 C37.8587764,47.9791337 35.6285197,45.6631029 32.8773585,45.6631029 C30.1261973,45.6631029 27.8959405,47.9791337 27.8959405,50.8361026 C27.8959405,53.6930714 30.1261973,56.0091023 32.8773585,56.0091023 Z M4.98141795,55.8683967 C7.73257912,55.8683967 9.96283591,53.5523658 9.96283591,50.695397 C9.96283591,47.8384281 7.73257912,45.5223973 4.98141795,45.5223973 C2.23025679,45.5223973 0,47.8384281 0,50.695397 C0,53.5523658 2.23025679,55.8683967 4.98141795,55.8683967 Z M60.773299,41.5247031 C63.5244602,41.5247031 65.754717,39.2086723 65.754717,36.3517034 C65.754717,33.4947346 63.5244602,31.1787037 60.773299,31.1787037 C58.0221379,31.1787037 55.7918811,33.4947346 55.7918811,36.3517034 C55.7918811,39.2086723 58.0221379,41.5247031 60.773299,41.5247031 Z M46.8253288,41.5247031 C49.5764899,41.5247031 51.8067467,39.2086723 51.8067467,36.3517034 C51.8067467,33.4947346 49.5764899,31.1787037 46.8253288,31.1787037 C44.0741676,31.1787037 41.8439108,33.4947346 41.8439108,36.3517034 C41.8439108,39.2086723 44.0741676,41.5247031 46.8253288,41.5247031 Z M60.773299,26.8995984 C63.5244602,26.8995984 65.754717,24.5835675 65.754717,21.7265987 C65.754717,18.8696299 63.5244602,16.553599 60.773299,16.553599 C58.0221379,16.553599 55.7918811,18.8696299 55.7918811,21.7265987 C55.7918811,24.5835675 58.0221379,26.8995984 60.773299,26.8995984 Z M18.9293882,41.5247031 C21.6805494,41.5247031 23.9108062,39.2086723 23.9108062,36.3517034 C23.9108062,33.4947346 21.6805494,31.1787037 18.9293882,31.1787037 C16.1782271,31.1787037 13.9479703,33.4947346 13.9479703,36.3517034 C13.9479703,39.2086723 16.1782271,41.5247031 18.9293882,41.5247031 Z M4.98141795,41.3839975 C7.73257912,41.3839975 9.96283591,39.0679667 9.96283591,36.2109978 C9.96283591,33.354029 7.73257912,31.0379981 4.98141795,31.0379981 C2.23025679,31.0379981 0,33.354029 0,36.2109978 C0,39.0679667 2.23025679,41.3839975 4.98141795,41.3839975 Z M4.98141795,26.8995984 C7.73257912,26.8995984 9.96283591,24.5835675 9.96283591,21.7265987 C9.96283591,18.8696299 7.73257912,16.553599 4.98141795,16.553599 C2.23025679,16.553599 0,18.8696299 0,21.7265987 C0,24.5835675 2.23025679,26.8995984 4.98141795,26.8995984 Z" id="Oval-14" sketch:type="MSShapeGroup"></path>
</g>
</g>
<svg viewBox="0 0 66 85" ng-attr-width="{{width}}" ng-attr-height="{{height}}" fill="currentcolor">
<path d="M46.8253288,70.4935014 C49.5764899,70.4935014 51.8067467,68.1774705 51.8067467,65.3205017 C51.8067467,62.4635329 49.5764899,60.147502 46.8253288,60.147502 C44.0741676,60.147502 41.8439108,62.4635329 41.8439108,65.3205017 C41.8439108,68.1774705 44.0741676,70.4935014 46.8253288,70.4935014 Z M32.8773585,84.9779005 C35.6285197,84.9779005 37.8587764,82.6618697 37.8587764,79.8049008 C37.8587764,76.947932 35.6285197,74.6319011 32.8773585,74.6319011 C30.1261973,74.6319011 27.8959405,76.947932 27.8959405,79.8049008 C27.8959405,82.6618697 30.1261973,84.9779005 32.8773585,84.9779005 Z M32.8773585,70.4935014 C35.6285197,70.4935014 37.8587764,68.1774705 37.8587764,65.3205017 C37.8587764,62.4635329 35.6285197,60.147502 32.8773585,60.147502 C30.1261973,60.147502 27.8959405,62.4635329 27.8959405,65.3205017 C27.8959405,68.1774705 30.1261973,70.4935014 32.8773585,70.4935014 Z M18.9293882,70.4935014 C21.6805494,70.4935014 23.9108062,68.1774705 23.9108062,65.3205017 C23.9108062,62.4635329 21.6805494,60.147502 18.9293882,60.147502 C16.1782271,60.147502 13.9479703,62.4635329 13.9479703,65.3205017 C13.9479703,68.1774705 16.1782271,70.4935014 18.9293882,70.4935014 Z M46.8253288,56.0091023 C49.5764899,56.0091023 51.8067467,53.6930714 51.8067467,50.8361026 C51.8067467,47.9791337 49.5764899,45.6631029 46.8253288,45.6631029 C44.0741676,45.6631029 41.8439108,47.9791337 41.8439108,50.8361026 C41.8439108,53.6930714 44.0741676,56.0091023 46.8253288,56.0091023 Z M18.9293882,56.0091023 C21.6805494,56.0091023 23.9108062,53.6930714 23.9108062,50.8361026 C23.9108062,47.9791337 21.6805494,45.6631029 18.9293882,45.6631029 C16.1782271,45.6631029 13.9479703,47.9791337 13.9479703,50.8361026 C13.9479703,53.6930714 16.1782271,56.0091023 18.9293882,56.0091023 Z M46.8253288,26.8995984 C49.5764899,26.8995984 51.8067467,24.5835675 51.8067467,21.7265987 C51.8067467,18.8696299 49.5764899,16.553599 46.8253288,16.553599 C44.0741676,16.553599 41.8439108,18.8696299 41.8439108,21.7265987 C41.8439108,24.5835675 44.0741676,26.8995984 46.8253288,26.8995984 Z M32.8773585,41.5247031 C35.6285197,41.5247031 37.8587764,39.2086723 37.8587764,36.3517034 C37.8587764,33.4947346 35.6285197,31.1787037 32.8773585,31.1787037 C30.1261973,31.1787037 27.8959405,33.4947346 27.8959405,36.3517034 C27.8959405,39.2086723 30.1261973,41.5247031 32.8773585,41.5247031 Z M32.8773585,10.3459994 C35.6285197,10.3459994 37.8587764,8.02996853 37.8587764,5.17299969 C37.8587764,2.31603085 35.6285197,0 32.8773585,0 C30.1261973,0 27.8959405,2.31603085 27.8959405,5.17299969 C27.8959405,8.02996853 30.1261973,10.3459994 32.8773585,10.3459994 Z M32.8773585,26.8995984 C35.6285197,26.8995984 37.8587764,24.5835675 37.8587764,21.7265987 C37.8587764,18.8696299 35.6285197,16.553599 32.8773585,16.553599 C30.1261973,16.553599 27.8959405,18.8696299 27.8959405,21.7265987 C27.8959405,24.5835675 30.1261973,26.8995984 32.8773585,26.8995984 Z M18.9293882,26.8995984 C21.6805494,26.8995984 23.9108062,24.5835675 23.9108062,21.7265987 C23.9108062,18.8696299 21.6805494,16.553599 18.9293882,16.553599 C16.1782271,16.553599 13.9479703,18.8696299 13.9479703,21.7265987 C13.9479703,24.5835675 16.1782271,26.8995984 18.9293882,26.8995984 Z" opacity="0.2"></path>
<path d="M60.773299,70.4935014 C63.5244602,70.4935014 65.754717,68.1774705 65.754717,65.3205017 C65.754717,62.4635329 63.5244602,60.147502 60.773299,60.147502 C58.0221379,60.147502 55.7918811,62.4635329 55.7918811,65.3205017 C55.7918811,68.1774705 58.0221379,70.4935014 60.773299,70.4935014 Z M4.98141795,70.3527958 C7.73257912,70.3527958 9.96283591,68.0367649 9.96283591,65.1797961 C9.96283591,62.3228273 7.73257912,60.0067964 4.98141795,60.0067964 C2.23025679,60.0067964 0,62.3228273 0,65.1797961 C0,68.0367649 2.23025679,70.3527958 4.98141795,70.3527958 Z M60.773299,56.0091023 C63.5244602,56.0091023 65.754717,53.6930714 65.754717,50.8361026 C65.754717,47.9791337 63.5244602,45.6631029 60.773299,45.6631029 C58.0221379,45.6631029 55.7918811,47.9791337 55.7918811,50.8361026 C55.7918811,53.6930714 58.0221379,56.0091023 60.773299,56.0091023 Z M32.8773585,56.0091023 C35.6285197,56.0091023 37.8587764,53.6930714 37.8587764,50.8361026 C37.8587764,47.9791337 35.6285197,45.6631029 32.8773585,45.6631029 C30.1261973,45.6631029 27.8959405,47.9791337 27.8959405,50.8361026 C27.8959405,53.6930714 30.1261973,56.0091023 32.8773585,56.0091023 Z M4.98141795,55.8683967 C7.73257912,55.8683967 9.96283591,53.5523658 9.96283591,50.695397 C9.96283591,47.8384281 7.73257912,45.5223973 4.98141795,45.5223973 C2.23025679,45.5223973 0,47.8384281 0,50.695397 C0,53.5523658 2.23025679,55.8683967 4.98141795,55.8683967 Z M60.773299,41.5247031 C63.5244602,41.5247031 65.754717,39.2086723 65.754717,36.3517034 C65.754717,33.4947346 63.5244602,31.1787037 60.773299,31.1787037 C58.0221379,31.1787037 55.7918811,33.4947346 55.7918811,36.3517034 C55.7918811,39.2086723 58.0221379,41.5247031 60.773299,41.5247031 Z M46.8253288,41.5247031 C49.5764899,41.5247031 51.8067467,39.2086723 51.8067467,36.3517034 C51.8067467,33.4947346 49.5764899,31.1787037 46.8253288,31.1787037 C44.0741676,31.1787037 41.8439108,33.4947346 41.8439108,36.3517034 C41.8439108,39.2086723 44.0741676,41.5247031 46.8253288,41.5247031 Z M60.773299,26.8995984 C63.5244602,26.8995984 65.754717,24.5835675 65.754717,21.7265987 C65.754717,18.8696299 63.5244602,16.553599 60.773299,16.553599 C58.0221379,16.553599 55.7918811,18.8696299 55.7918811,21.7265987 C55.7918811,24.5835675 58.0221379,26.8995984 60.773299,26.8995984 Z M18.9293882,41.5247031 C21.6805494,41.5247031 23.9108062,39.2086723 23.9108062,36.3517034 C23.9108062,33.4947346 21.6805494,31.1787037 18.9293882,31.1787037 C16.1782271,31.1787037 13.9479703,33.4947346 13.9479703,36.3517034 C13.9479703,39.2086723 16.1782271,41.5247031 18.9293882,41.5247031 Z M4.98141795,41.3839975 C7.73257912,41.3839975 9.96283591,39.0679667 9.96283591,36.2109978 C9.96283591,33.354029 7.73257912,31.0379981 4.98141795,31.0379981 C2.23025679,31.0379981 0,33.354029 0,36.2109978 C0,39.0679667 2.23025679,41.3839975 4.98141795,41.3839975 Z M4.98141795,26.8995984 C7.73257912,26.8995984 9.96283591,24.5835675 9.96283591,21.7265987 C9.96283591,18.8696299 7.73257912,16.553599 4.98141795,16.553599 C2.23025679,16.553599 0,18.8696299 0,21.7265987 C0,24.5835675 2.23025679,26.8995984 4.98141795,26.8995984 Z"></path>
</svg>
......@@ -17,10 +17,8 @@
.Select:after {
content: '';
position: absolute;
top: 50%;
right: 1rem;
right: 0;
width: 0;
height: 0;
pointer-events: none; /* no clicks since arrow is just for appearance */
......@@ -60,6 +58,43 @@
box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}
.Select--blue select {
color: rgb(78,146,223);
border-color: rgb(195,216,241);
background-color: rgb(227,238,249);
}
.Select--blue:after {
border-top: .3rem solid rgb(78,146,223);
}
.Select--blue:before {
border-bottom: .3rem solid rgb(78,146,223);
}
.Select--purple select {
color: rgb(168,138,195);
border-color: rgb(203,186,219);
background-color: rgb(231,223,239);
}
.Select--purple:after {
border-top: .3rem solid rgb(168,138,195);
}
.Select--purple:before {
border-bottom: .3rem solid rgb(168,138,195);
}
.Select--small select {
padding: 0.25rem 1.5rem 0.25rem 0.5rem;
font-size: 0.7em;
line-height: 1.5em;
}
.Select--small:after {
margin-top: -.1rem;
right: 0.5em;
}
.Select--small:before {
border-bottom: none;
}
.Select select:focus {
outline: none;
}
......@@ -37,6 +37,12 @@
display: none;
}
/* utility to get a simple common hover state for admin items */
.AdminHoverItem:hover {
background-color: #F3F8FD;
transition: background .2s linear;
}
.UserNick {
border-width: 1px;
border-style: solid;
......@@ -130,6 +136,7 @@
padding: 1em;
}
/* TODO: remove this and apply AdminHoverItem to content rows */
.ContentTable tbody tr:hover {
background-color: rgba(74, 144, 226, 0.04);
}
......@@ -139,7 +146,62 @@
transition: opacity .2s linear;
}
.DatabaseList,
.TableFieldList {
padding-bottom: 3em;
position: absolute;
left: 0;
right: 0;
height: 100%;
overflow-y: scroll;
}
.DatabaseListItem .h4 {
line-height: 0.8em;
}
.DatabaseListItem.DatabaseListItem--active {
background-color: rgb(227,238,250);
color: rgb(74,144,226);
}
/* TODO: this is for the ace editor and should be handled without an ID */
#id_sql {
height: 200px;
}
.DatabaseTablesAdmin {
overflow: hidden;
}
.TableField {
border-bottom: 1px solid #F0F0F0;
padding-left: 3em;
}
.TableField-name {
color: #676C72;
}
.DatabaseTablesAdmin .editable-click {
font-size: 0.8em;
border-bottom: none;
font-style: normal;
}
.DatabaseTablesAdmin .editable-click:hover, .DatabaseTablesAdmin .editable-empty:hover {
color: inherit !important;
font-style: normal;
}
.DatabaseTablesAdmin .editable-empty {
font-style: normal;
}
.Select--unselected {
border-style: dotted !important;
}
.ScrollShadow {
border-top: 1px solid rgba(0, 0, 0, .05);
box-shadow: 0 -1px 0 rgba(0, 0, 0, .12);
}
:root {
--default-font-family: "Lato";
--default-font-size: 1em;
--default-font-color: #727479;
}
html {
......@@ -10,8 +11,8 @@ html {
body {
font-family: var(--default-font-family), "Helvetica Neue", Helvetica, sans-serif;
font-size: var(--default-font-size);
color: var(--default-font-color);
margin: 0;
box-sizing: border-box;
height: 100%; /* ensure the entire page will fill the window */
display: flex;
flex-direction: column;
......
/* set main elements to box-sizing border-box for more reliable box model calc */
body,
div,
nav,
article,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment