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

updating with changes to frontend_client files from python app.

parent 77f548b6
Branches
Tags
No related merge requests found
Showing
with 2039 additions and 240 deletions
......@@ -23,10 +23,10 @@
<div class="TableWrapper" ng-if="queryResultData.status == 'completed' && queryResultData.row_count > 0">
<table class="Table table table-striped table-condensed" ng-if="queryResultData.row_count < 5000">
<tr>
<th ng-repeat="column_name in queryResultData.columns">{{column_name}}</th>
<th ng-repeat="column_name in queryResultData.data.columns">{{column_name}}</th>
</tr>
<tr ng-repeat="row in queryResultData.data">
<tr ng-repeat="row in queryResultData.data.rows">
<td ng-repeat="cell in row track by $index">{{cell}}</td>
</tr>
</table>
......
......@@ -50,7 +50,7 @@
</div>
</form>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=3'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=4'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_recentexec.html?cb=2'"></div>
......
......@@ -40,6 +40,6 @@
</div>
</form>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=3'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=4'"></div>
</div>
......@@ -21,7 +21,7 @@
</div>
</div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=3'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=4'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_recentexec.html?cb=2'"></div>
</div>
......@@ -13,7 +13,7 @@
</div>
</div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=3'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_results.html?cb=4'"></div>
<div ng-include="'/app/admin/query/partials/query_detail_recentexec.html?cb=2'"></div>
</div>
This diff is collapsed.
......@@ -2,6 +2,9 @@
/*global setTimeout*/
/*global $*/
/*global CardRenderer*/
/* global React */
/* global document */
/* global QueryBuilder */
var CardDirectives = angular.module('corvus.card.directives', []);
......@@ -214,7 +217,7 @@ CardDirectives.directive('cvCard', ['Card', 'Metabase', 'CorvusAlert', 'Query',
//what the error was
dataTimeoutPromise = $timeout(function() {
var query = scope.card.dataset_query;
if (typeof query.database === "undefined" || (query.type === "query" && typeof query.query.source_table === "undefined")) {
if (typeof query.database === "undefined" || (query.type === "query" && (typeof query.query.source_table === "undefined" || !query.query.source_table))) {
scope.queryNotSet = true;
return;
}
......@@ -547,4 +550,20 @@ CardDirectives.directive('cvCardFavoriteButton', ['Card', function(Card) {
},
link: link
};
}]);
\ No newline at end of file
}]);
CardDirectives.directive('queryBuilder', function() {
return {
link: function(scope, element) {
scope.$on('query:updated', function () {
renderReact();
});
function renderReact() {
React.render(new QueryBuilder({
model: scope.model
}), document.getElementById('react'));
}
}
};
});
......@@ -26,8 +26,16 @@ Card.config(['$routeProvider', function($routeProvider) {
templateUrl: '/app/card/partials/card_detail.html',
controller: 'CardDetail'
});
$routeProvider.when('/:orgSlug/card/createnew/', {
templateUrl: '/app/card/partials/card_detail_new.html',
controller: 'CardDetailNew'
});
$routeProvider.when('/:orgSlug/card/:cardId', {
templateUrl: '/app/card/partials/card_detail.html',
controller: 'CardDetail'
});
$routeProvider.when('/:orgSlug/cool_new_card/:cardId', {
templateUrl: '/app/card/partials/card_detail_new.html',
controller: 'CardDetailNew'
});
}]);
......@@ -90,6 +90,231 @@ CardServices.factory('Card', ['$resource', '$cookies', function($resource, $cook
});
}]);
CardServices.service('VisualizationUtils', [ function () {
this.visualizationTypes = {
scalar: {
display: 'scalar',
label: 'Scalar',
available: false,
notAvailableReasons: []
},
table: {
display: 'table',
label: 'Table',
available: false,
notAvailableReasons: []
},
pie: {
display: 'pie',
label: 'Pie Chart',
available: false,
notAvailableReasons: []
},
bar: {
display: 'bar',
label: 'Bar Chart',
available: false,
notAvailableReasons: []
},
line: {
display: 'line',
label: 'Line Chart',
available: false,
notAvailableReasons: []
},
area: {
display: 'area',
label: 'Area Chart',
available: false,
notAvailableReasons: []
},
timeseries: {
display: 'timeseries',
label: 'Time Series',
available: false,
notAvailableReasons: []
},
pin_map: {
display: 'pin_map',
label: 'Pin Map',
available: false,
notAvailableReasons: []
},
state: {
display: 'state',
label: 'State Heatmap',
available: false,
notAvailableReasons: []
},
country: {
display: 'country',
label: 'World Heatmap',
available: false,
notAvailableReasons: []
}
};
this.zoomTypes = [
{
'label': 'Disabled',
'value': null
}, {
'label': 'X',
'value': 'x'
}, {
'label': 'Y',
'value': 'y'
}, {
'label': 'XY',
'value': 'xy'
}
];
}]);
CardServices.service('QueryUtils', function () {
this.limitOptions = [
{
label: "1",
value: 1
}, {
label: "10",
value: 10
}, {
label: "25",
value: 25
}, {
label: "50",
value: 50
}, {
label: "100",
value: 100
}, {
label: "1000",
value: 1000
}
];
this.emptyQuery = function () {
return {
filter: [
null,
null
],
source_table: null,
breakout: [
],
limit: null,
aggregation: [
],
database: 1,
type: null,
native: {}
};
};
// default query card settings
this.queryCardSettings = {
"allowFavorite": true,
"allowAddToDash": true,
"allowRemoveFromDash": false,
"allowCardPermalink": false,
"allowLinkToComments": false,
"allowSend": false,
"allowTitleEdits": false
};
this.populateQueryOptions = function (table) {
// create empty objects to store our lookups
table.fields_lookup = {};
table.aggregation_lookup = {};
table.breakout_lookup = {};
_.each(table.fields, function(field) {
table.fields_lookup[field.id] = field;
field.operators_lookup = {};
_.each(field.valid_operators, function(operator) {
field.operators_lookup[operator.name] = operator;
});
});
_.each(table.aggregation_options, function(agg) {
table.aggregation_lookup[agg.short] = agg;
});
_.each(table.breakout_options, function(br) {
table.breakout_lookup[br.short] = br;
});
return table;
};
// @TODO - this really should not touch $scope in any way
this.getFirstColumnBySpecialType = function(special_type, data) {
if (!data) {
return null;
}
var result;
data.cols.forEach(function(col, index) {
if (typeof col.special_type !== "undefined" && col.special_type == special_type) {
col.index = index;
if (typeof result == "undefined") {
result = col;
}
}
});
return result;
};
/* @check validity */
/// Check that QUERY is valid (i.e., can be ran or saved, to enable/disable corresponding buttons)
/// Try not to make this too expensive since it gets ran on basically every event loop in the Card Builder
///
/// Currently the only thing we're doing here is checking the 'filter' clause of QUERY
this.queryIsValid = function(query) {
if (!query) return false;
// ******************** CHECK THAT QUERY.FILTER IS VALID ******************** //
// if query.filter is undefined or [null, null] then we'll consider it to be "unset" which means it's ok
if (!query.filter || (query.filter.length === 2 && query.filter[0] === null && query.filter[1] === null)) return true;
// a filter is valid if it and its children don't contain any nulls
var containsNulls = function(obj) {
if (obj === null) return true;
// if we're looking at an Array recurse over each child
if (obj.constructor === Array) {
var len = obj.length;
for (var i = 0; i < len; i++) {
if (containsNulls(obj[i])) return true; // return immediately if we see a null
}
}
return false;
};
return !containsNulls(query.filter);
};
this.clearExtraQueryData = function(query) {
var typelist = ['native', 'query', 'result'];
for (var i = 0; i < typelist.length; i++) {
if (query.type != typelist[i]) {
delete query[typelist[i]];
}
}
return query;
};
this.getEncodedQuery = function(query) {
return encodeURIComponent(JSON.stringify(query));
};
});
CardServices.service('VisualizationSettings', [function() {
var DEFAULT_COLOR_HARMONY = [
......
<div class="QueryBuilder" query-builder>
<div id="react"></div>
</div>
......@@ -33,7 +33,7 @@
.Table th,
.Table td {
padding: 6px;
padding: 1em;
border: 1px solid var(--table-border-color);
}
......@@ -117,4 +117,3 @@
color: #999;
font-size: 0.8em;
}
html {
height: 100%;
}
body {
height: 100%;
background: #FBFCFD;
}
.SearchBar:focus {
outline: none;
}
.SaveModal {
background: #fff;
border-radius: 4px;
position: absolute;
bottom: 2em;
right: 2em;
width: 400px;
}
.SaveModal.Modal--showing {
border: 1px solid #ddd;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24);
}
.SaveModal .ModalContent {
display: none;
margin-bottom: 6em;
padding: 2em;
}
.SaveModal input {
border: none;
font-size: 1em;
border-bottom: 1px dotted #111;
display: block;
width: 100%;
padding: 1em;
}
.SaveModal input:focus {
outline: none;
border-bottom-color: #4A90E2;
}
.SaveModal.Modal--showing .ModalContent {
display: block;
}
.QueryName {
font-weight: 200;
margin-right: 1em;
font-size: 1.5em;
margin: 2em 0 1.25em;
}
.QueryBar .SelectionModule {
position: relative;
display: inline-block;
}
.SelectionModule .SelectionItems {
opacity: 0;
pointer-events: none;
position: absolute;
top: 2em;
border: 1px solid #ddd;
box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
border-radius: 4px;
background: #fff;
z-index: 2;
left: 0;
right: 0;
overflow: hidden;
max-height: 360px;
}
.SelectionList {
max-height: 420px;
overflow-y: scroll;
}
.SelectionItems.open {
opacity: 1;
pointer-events: all;
}
.SelectionItem {
padding: 0.8em;
font-size: 1em;
position: relative;
background: #fff;
display: flex;
align-items: center;
}
.SelectionItems {
min-width: 320px;
}
.SelectionItem:hover {
background: #FBFCFD;
transition: background .3s linear;
}
.SelectionItem:before {
content: '';
width: 16px;
height: 16px;
border: 2px solid #ddd;
border-radius: 99px;
margin-right: 1em;
}
.SelectionItem.selected {
color: #4A90E2;
color: currentColor;
}
.SelectionItem.selected:before {
color: inherit;
background-color: currentColor;
border-color: currentColor;
}
.QueryBar .SelectionItem:hover:before {
border-color: currentColor;
transition: border .3s linear;
}
.SelectionTitle {
display: block;
}
.SelectionTitle:hover {
cursor: pointer;
}
.QueryBar .SelectionTitle {
text-align: left;
font-size: 0.85em;
border: 1px dotted #999;
border-radius: 2px;
}
.QueryBar .ActionButton {
padding: 0.75em 1.25em;
border: 1px dotted #ddd;
box-shadow: none;
}
.SearchBar {
border: none;
border-bottom: 1px solid #ddd;
padding: 0.75em;
box-shadow: 0 2px 0 rgba(0, 0, 0, .12);
width: 100%;
box-sizing: border-box;
}
.QueryPicker-wrapper {
padding: 0 2em;
display: flex;
flex-direction: column;
}
.QueryBar .SelectionModule.open .SelectionItems {
opacity: 1;
pointer-events: all;
}
.SelectionModule-display {
color: #444;
}
.SelectionModule.selected .SelectionTitle {
display: inline-block;
margin: 0 0.25em;
border: 1px solid #ddd;
border-radius: 2px;
box-shadow: 0 1px 1px rgba(0, 0, 0, .08);
}
.RemoveTrigger {
display: inline-block;
}
.ActionBar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 1em;
}
.ActionButton {
text-decoration: none;
padding: 0.5em 0.75em;
background: #FBFCFD;
border: 1px solid #ddd;
color: #444;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
cursor: pointer;
}
.ActionButton:hover {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
transition: border .3s linear, box-shadow .3s linear;
}
.ActionButton--primary {
color: #fff;
background: #4A90E2;
border: 1px solid #3282DF;
transition: background .3s linear, border .3s linear, color .3s linear;
}
.ActionBar .SettingsButtons {
float: right;
}
.ActionButton-enter {
opacity: 0.01;
tansition: opacity .3s linear;
}
.ActionButton-enter-active {
opacity: 1;
}
.DimensionButton-enter {
opacity: 0.01
transition: opacity .3s linear;
}
.DimensionButton-enter-active {
opacity: 1;
}
.DimensionButton-leave {
opacity: 1;
transition: opacity .3s linear;
}
.DimensionButton-leave-active {
opacity: 0.01
}
.ActionBar .SaveModal,
.ActionBar .SettingsButtons .ActionButton {
display: inline-block;
}
.ActionButton-enter {
margin-top: 100px;
}
.ActionButton-enter-active {
margin-top: 0;
transition: margin .3s linear;
}
.VisualisationOptions-enter {
margin-top: 120px;
}
.VisualisationOptions-enter-active {
margin-top: 0;
transition: margin .3s linear .3s;
}
.FilterGroupWrapper {
position: relative;
display: inline-block;
}
.FilterGroupWrapper .FilterGroup {
margin-top: 1em;
}
.QueryPicker-group {
padding: 2em 0;
border-bottom: 1px solid #ddd;
background: #fff;
}
/*
.with-breakouts:after {
content: 'By';
display: block;
position: absolute;
top: 0;
right: -1.5em;
bottom: 0;
color: #999;
border: 1px solid #ddd;
border-radius: 99px;
background: #fff;
width: 3em;
line-height: 3em;
box-shadow: 0 1px 3px rgba(0, 0, 0, .12);
}
*/
.SaveButton {
position: relative;
z-index: 5;
}
.QueryBar .ActionButton {
font-size: 0.85em;
}
.QueryFilter {
background: #fff;
position: relative;
display: inline-block;
font-size: 0.85em;
margin-right: 1em;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 0px 3px rgba(0, 0, 0, .12);
}
.QueryFilter .input {
padding: 1em;
border-radius: 0;
}
.QueryFilter .SelectionModule.selected .SelectionTitle {
box-sizing: border-box;
margin: 0;
border: none;
border-right: 1px solid #ddd;
}
.QueryFilter .SelectionModule.selected .SelectionTitle:hover {
background: #FBFCFD;
}
.QueryFilter .RemoveTrigger {
margin: 0.75em 0.5em;
}
.FilterSection {
display: inline-block;
}
.FilterSection .SelectionItems {
position: absolute;
display: none;
}
.FilterSection .SelectionItems.open {
display: block;
}
.SelectionModule.removeable .SelectionTitle {
padding-right: 3em;
}
.QueryTable td,
.QueryTable th {
padding: 1em;
}
.QueryTable {
margin-top: 1em;
box-shadow: 0 1px 3px rgba(0,0,0, .12);
}
.Metric-sourceTable:after {
content: '•';
display: inline-block;
}
.DimensionList .RemoveTrigger {
position: absolute;
top: 12px;
right: 14px;
}
/* breakpoints */
@media screen and (min-width: 60em) {
.SelectionTitle {
padding: 0.75em 1.25em;
}
.QueryPicker-group {
padding: 1.2em;
}
}
@media screen and (min-width: 120em) {
body {
font-size: 1em;
}
.ActionButton,
.QueryBar .ActionButton {
padding: 1em 2em;
}
.Table-wrapper {
margin-top: 3em;
}
.SelectionItems.open {
top: 3em;
}
.DimensionList .RemoveTrigger {
top: 15px;
right: 17px;
}
.QueryName {
margin-bottom: 1em;
}
.ActionBar {
padding: 1em 0;
}
.QueryBar .SelectionTitle {
padding: 1em 2em;
}
}
.FilterTrigger {
border-radius: 99px;
padding: 0.85em;
position: relative;
color: #fff;
background: #A8C28e;
border-color: #A8C28e;
}
.FilterTrigger:hover {
color: #fff;
}
.FilterSection .SelectionModule,
.FilterSection .SelectionModule a {
color: #A8C28e;
}
.FilterSection .input {
color: #A8C28e;
border-color: transparent;
}
.FilterSection .input:focus {
border-color: transparent;
border-bottom: 1px solid #A8C28e;
}
.DimensionList .SelectionModule,
.DimensionList .SelectionModule a {
color: #B9A2CD;
}
.FilterTrigger .icon {
display: flex;
}
.QueryHeader {
overflow: hidden;
background: #fff;
}
This diff is collapsed.
......@@ -38,6 +38,7 @@ CorvusServices.factory('AppState', ['$rootScope', '$routeParams', '$q', '$locati
deferred.resolve(result);
}, function(error) {
console.log('unable to get current user', error);
$location.path('/unauthorized/');
deferred.reject(error);
});
......@@ -101,6 +102,7 @@ console.log('routeChangedImpl-withUser-orgSlug', $routeParams.orgSlug);
console.log("user is not authorized for this org!!!");
service.model.currentOrgSlug = null;
service.model.currentOrg = null;
PermissionViolation.create({'user': service.model.currentUser.id, 'url':$location.url()});
$location.path('/unauthorized/');
return;
} else if ($location.path().indexOf('/'+$routeParams.orgSlug+'/admin') === 0 &&
......@@ -108,6 +110,7 @@ console.log('routeChangedImpl-withUser-orgSlug', $routeParams.orgSlug);
console.log("user is not an admin for this org!!!");
service.model.currentOrgSlug = null;
service.model.currentOrg = null;
PermissionViolation.create({'user': service.model.currentUser.id, 'url':$location.url()});
$location.path('/unauthorized/');
return;
}
......@@ -715,3 +718,18 @@ CoreServices.factory('Organization', ['$resource', '$cookies', function($resourc
}
});
}]);
CoreServices.factory('PermissionViolation', ['$resource', '$cookies', function($resource, $cookies) {
return $resource('/api/permissions_violation', {}, {
create: {
url: '/api/permissions_violation',
method: 'POST',
headers: {
'X-CSRFToken': function() {
return $cookies.csrftoken;
}
}
},
});
}]);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment