Initial commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var UndoManager = require('builder/data/undo-manager.js');
|
||||
|
||||
/**
|
||||
* Data SQL Undo-redo model
|
||||
*/
|
||||
module.exports = Backbone.Model.extend({
|
||||
|
||||
defaults: {
|
||||
content: ''
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
this._history = this._generateHistory(opts && opts.history);
|
||||
|
||||
UndoManager.init(this, {
|
||||
track: true,
|
||||
history: this._history
|
||||
});
|
||||
},
|
||||
|
||||
_generateHistory: function (history) {
|
||||
if (history && history.length) {
|
||||
var data = _.reduce(history, function (memo, sql) {
|
||||
memo.push({
|
||||
content: sql
|
||||
});
|
||||
return memo;
|
||||
}, [], this);
|
||||
return data;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getHistory: function () {
|
||||
return _.pluck(
|
||||
this.getUndoHistory(),
|
||||
'content'
|
||||
);
|
||||
}
|
||||
});
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var ModalExportDataView = require('builder/components/modals/export-data/modal-export-data-view');
|
||||
var SyncInfoView = require('./dataset-content-sync-view');
|
||||
var template = require('./dataset-content-options.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodeModel',
|
||||
'configModel',
|
||||
'modals',
|
||||
'userModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'Dataset-viewOptions u-flex u-justifySpace u-alignCenter',
|
||||
|
||||
events: {
|
||||
'click .js-addRow': '_onAddRowClick',
|
||||
'click .js-addColumn': '_onAddColumnClick',
|
||||
'click .js-export': '_onExportClick'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
|
||||
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
|
||||
this._queryGeometryModel = this._analysisDefinitionNodeModel.queryGeometryModel;
|
||||
this._syncModel = this._tableModel.getSyncModel();
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(
|
||||
template({
|
||||
isEditable: !this._analysisDefinitionNodeModel.isReadOnly(),
|
||||
isCustomQueryApplied: this._analysisDefinitionNodeModel.isCustomQueryApplied()
|
||||
})
|
||||
);
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._querySchemaModel, 'change:query', this.render);
|
||||
this.listenTo(this._syncModel, 'destroy', this.render);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
if (this._tableModel.isSync() && this._tableModel.isOwner(this._userModel)) {
|
||||
var syncInfo = new SyncInfoView({
|
||||
modals: this._modals,
|
||||
syncModel: this._syncModel,
|
||||
tableModel: this._tableModel,
|
||||
querySchemaModel: this._querySchemaModel
|
||||
});
|
||||
|
||||
this.$('.js-sync').html(syncInfo.render().el);
|
||||
this.addView(syncInfo);
|
||||
}
|
||||
},
|
||||
|
||||
_onExportClick: function () {
|
||||
this._modals.create(function (modalModel) {
|
||||
return new ModalExportDataView({
|
||||
modalModel: modalModel,
|
||||
fromView: 'dataset',
|
||||
configModel: this._configModel,
|
||||
filename: this._tableModel.getUnquotedName(),
|
||||
queryGeometryModel: this._queryGeometryModel,
|
||||
querySchemaModel: this._querySchemaModel,
|
||||
canHideColumns: false
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_onAddRowClick: function () {
|
||||
this.trigger('addRow', this);
|
||||
},
|
||||
|
||||
_onAddColumnClick: function () {
|
||||
this.trigger('addColumn', this);
|
||||
}
|
||||
});
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<div class="js-sync"></div>
|
||||
<div class="js-dataset">
|
||||
<ul class="u-flex u-justifySpace">
|
||||
<% if (isEditable) { %>
|
||||
<li>
|
||||
<button class="CDB-Text CDB-Size-small is-semibold u-actionTextColor u-upperCase js-addRow">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<svg width="16px" height="18px" viewBox="0 0 16 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<path d="M4,3 L4,0 L3,0 L3,3 L0,3 L0,4 L3,4 L3,7 L4,7 L4,4 L7,4 L7,3 L4,3 Z" id="path-1"></path>
|
||||
<mask id="mask-2" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="7" height="7" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
</defs>
|
||||
<g id="Add-Row" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<use id="Combined-Shape" stroke="#1181FB" mask="url(#mask-2)" stroke-width="2" xlink:href="#path-1"></use>
|
||||
<path d="M16,0 L16,3 L9,3 L9,0 L16,0 Z M10.0020142,1 L10.000989,2.01795972 L14.9926249,2.00301586 L14.9962966,1.00580263 L10.0020142,1 Z" id="Combined-Shape" fill="#1181FB"></path>
|
||||
<path d="M16,4 L16,7 L9,7 L9,4 L16,4 Z M10.0020142,5 L10.000989,6.01795972 L14.9926249,6.00301586 L14.9962966,5.00580263 L10.0020142,5 Z" id="Combined-Shape-Copy" fill="#1181FB"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="u-lSpace">
|
||||
<%- _t('dataset.options.add-row') %>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
<li class="u-lSpace--xl">
|
||||
<button class="CDB-Text CDB-Size-small is-semibold u-actionTextColor u-upperCase js-addColumn">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<svg width="16px" height="18px" viewBox="0 4 16 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Add-Column" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 4.000000)">
|
||||
<path d="M4,3 L4,0 L3,0 L3,3 L0,3 L0,4 L3,4 L3,7 L4,7 L4,4 L7,4 L7,3 L4,3 Z" id="Combined-Shape" fill="#1181FB"></path>
|
||||
<path d="M9,0 L12,0 L12,7 L9,7 L9,0 Z M10,1 L11,1 L11,6 L10,6 L10,1 Z" id="Combined-Shape" fill="#1181FB"></path>
|
||||
<path d="M13,0 L16,0 L16,7 L13,7 L13,0 Z M14,1 L15,1 L15,6 L14,6 L14,1 Z" id="Combined-Shape-Copy-2" fill="#1181FB"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="u-lSpace">
|
||||
<%- _t('dataset.options.add-column') %>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
<% } %>
|
||||
<li class="u-lSpace--xl">
|
||||
<button class="CDB-Text CDB-Size-small is-semibold u-actionTextColor u-upperCase is-disabled js-export">
|
||||
<div class="u-flex u-alignCenter">
|
||||
<svg width="11px" height="18px" viewBox="-1 2 11 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="Esport" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 3.000000)" stroke-linecap="square">
|
||||
<path d="M4.5,0.5 L4.5,6.5" id="Line" stroke="#1181FB"></path>
|
||||
<path d="M4.5,7.5 L8.5,3.5" id="Line" stroke="#1181FB"></path>
|
||||
<path d="M4.5,7.5 L0.5,3.5" id="Line" stroke="#1181FB"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="u-lSpace">
|
||||
<%- _t('dataset.options.export') %>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
|
||||
var SyncOptionsModalView = require('builder/components/modals/sync-options/sync-options-modal-view');
|
||||
var SyncModel = require('builder/data/synchronization-model');
|
||||
var template = require('./dataset-content-sync.tpl');
|
||||
var moment = require('moment');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'syncModel',
|
||||
'tableModel',
|
||||
'querySchemaModel',
|
||||
'modals'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
tagName: 'div',
|
||||
className: 'SyncInfo',
|
||||
|
||||
events: {
|
||||
'click .js-options': '_onClickOptions',
|
||||
'click .js-syncNow': '_onClickSyncNow'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
this._startSync = this._startSync.bind(this);
|
||||
|
||||
this._initBinds();
|
||||
|
||||
// Check sync now button
|
||||
if (this._syncModel.isSyncing()) {
|
||||
this._showSyncNowModal();
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
var runAt = this._syncModel.get('run_at');
|
||||
|
||||
var d = {
|
||||
canSyncNow: this._syncModel.canSyncNow(),
|
||||
fromExternalSource: this._syncModel.get('from_external_source'),
|
||||
ranAt: moment(this._syncModel.get('ran_at') || new Date()).fromNow(),
|
||||
runAt: moment(runAt).fromNow(),
|
||||
state: this._syncModel.get('state'),
|
||||
errorCode: this._syncModel.get('error_code'),
|
||||
errorMessage: this._syncModel.get('error_message')
|
||||
};
|
||||
|
||||
// Due to the time we need to polling, we have to display to the user
|
||||
// that the sync will be in a moment
|
||||
if (!runAt || (new Date(runAt) <= new Date())) {
|
||||
d.runAt = _t('dataset.sync.in-a-moment');
|
||||
}
|
||||
|
||||
this.$el
|
||||
.html(template(d))
|
||||
.removeClass()
|
||||
.addClass(this.className + ' ' + 'is-' + d.state);
|
||||
|
||||
// Tipsy
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-syncNowDisabled'),
|
||||
title: function () {
|
||||
return _t('dataset.sync.disabled', {
|
||||
gap: SyncModel.SYNC_GAP / (1000 * 60)
|
||||
});
|
||||
}
|
||||
});
|
||||
this.addView(tooltip);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
// Unbind in clean method
|
||||
this._syncModel.bind('change', this.render, this);
|
||||
},
|
||||
|
||||
_bindChangeSyncEvent: function () {
|
||||
this._syncModel.bind('change:state', this._finishSync, this);
|
||||
},
|
||||
|
||||
_unbindChangeSyncEvent: function () {
|
||||
this._syncModel.unbind(null, null, this);
|
||||
},
|
||||
|
||||
_onClickSyncNow: function (e) {
|
||||
if (e) this.killEvent(e);
|
||||
|
||||
if (this._syncModel.canSyncNow()) {
|
||||
this._showSyncNowModal();
|
||||
}
|
||||
},
|
||||
|
||||
_onClickOptions: function (e) {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnqualifiedName();
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new SyncOptionsModalView({
|
||||
modalModel: modalModel,
|
||||
syncModel: self._syncModel,
|
||||
tableName: tableName
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_startSync: function () {
|
||||
this.render();
|
||||
this._bindChangeSyncEvent();
|
||||
this._syncModel.pollCheck();
|
||||
},
|
||||
|
||||
_finishSync: function () {
|
||||
if (!this._syncModel.isSyncing()) {
|
||||
this._unbindChangeSyncEvent();
|
||||
this._syncModel.destroyCheck();
|
||||
|
||||
// Reload table
|
||||
this._querySchemaModel.set({
|
||||
status: 'unfetched',
|
||||
query: 'SELECT * FROM ' + this._tableModel.getUnquotedName()
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
_showSyncNowModal: function (e) {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnqualifiedName();
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new CreationModalView({
|
||||
modalModel: modalModel,
|
||||
loadingTitle: _t('dataset.sync.loading', { tableName: tableName }),
|
||||
errorTitle: _t('dataset.sync.error', { tableName: tableName }),
|
||||
runAction: function (opts) {
|
||||
self._syncModel.set({
|
||||
state: 'syncing',
|
||||
error_code: '',
|
||||
error_message: ''
|
||||
});
|
||||
|
||||
self._syncModel.bind('change:state', function () {
|
||||
if (!self._syncModel.isSyncing()) {
|
||||
modalModel.destroy();
|
||||
}
|
||||
}, self);
|
||||
|
||||
self._syncModel.syncNow(self._startSync);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._unbindChangeSyncEvent();
|
||||
CoreView.prototype.clean.apply(this);
|
||||
}
|
||||
|
||||
});
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<p class="CDB-Text CDB-Size-small u-secondaryTextColor">
|
||||
<span class="SyncInfo-state SyncInfo-state--small SyncInfo-state--<%- state %> u-rSpace--s"></span>
|
||||
<span class="u-upperCase">
|
||||
<% if (state === "success") { %>
|
||||
<%- _t('dataset.sync.synced', { ranAt: ranAt }) %>.
|
||||
<% } else if (state === "syncing") { %>
|
||||
<%- _t('dataset.sync.syncing') %>...
|
||||
<% } else if (state === "failure") { %>
|
||||
<%- _t('dataset.sync.sync-failed') %>.
|
||||
<% } %>
|
||||
</span>
|
||||
|
||||
<% if (state === "success") { %>
|
||||
<%- _t('dataset.sync.next', { runAt: runAt }) %>.
|
||||
<% } else if (state === "syncing") { %>
|
||||
<% /* There's no need to add extra text in this case. */ %>
|
||||
<% } else if (errorCode || errorMessage) { %>
|
||||
<%- _t('dataset.sync.error-code', { errorCode: errorCode }) %>: <%- errorMessage %>.
|
||||
<% } %>
|
||||
|
||||
<% if (!fromExternalSource) { %>
|
||||
<% if (state !== "syncing") { %>
|
||||
<button class="<% if (canSyncNow) { %>u-actionTextColor js-syncNow<% } else { %>is-disabled js-syncNowDisabled<% } %>"><%- _t('dataset.sync.sync-now') %></button>.
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<% if (state !== "syncing") { %>
|
||||
<button class="js-options u-actionTextColor"><%- _t('dataset.sync.view-options') %></button>.
|
||||
<% } %>
|
||||
</p>
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var TableManager = require('builder/components/table/table-manager');
|
||||
var DatasetContentOptionsView = require('./dataset-content-options-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodeModel',
|
||||
'configModel',
|
||||
'modals',
|
||||
'userModel',
|
||||
'visModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
this.listenTo(this._visModel, 'change:name', this._onChangeTableVisName);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._tableView = TableManager.create({
|
||||
relativePositionated: true,
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
configModel: this._configModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel
|
||||
});
|
||||
this.addView(this._tableView);
|
||||
this.$el.append(this._tableView.render().el);
|
||||
|
||||
var datasetContentOptionsView = new DatasetContentOptionsView({
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
configModel: this._configModel
|
||||
});
|
||||
datasetContentOptionsView.bind('addColumn', function () {
|
||||
this._tableView.addColumn();
|
||||
}, this);
|
||||
datasetContentOptionsView.bind('addRow', function () {
|
||||
this._tableView.addRow();
|
||||
}, this);
|
||||
|
||||
this.addView(datasetContentOptionsView);
|
||||
this.$el.prepend(datasetContentOptionsView.render().el);
|
||||
},
|
||||
|
||||
_onChangeTableVisName: function () {
|
||||
// Although all operations related with table rename are managed in the analysis-definition-source-node-model
|
||||
// this columnsCollection doesn't belong to it and we need to take from it is generated.
|
||||
this._tableView && this._tableView.getColumnsCollection().setTableName(this._visModel.get('name'));
|
||||
}
|
||||
});
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
var _ = require('underscore');
|
||||
var ContextMenuView = require('builder/components/context-menu/context-menu-view');
|
||||
var CustomListCollection = require('builder/components/custom-list/custom-list-collection');
|
||||
var REQUIRED_OPTS = [
|
||||
'ev',
|
||||
'isTableOwner',
|
||||
'isSync',
|
||||
'isCustomQuery',
|
||||
'triggerElementID',
|
||||
'canCreateDatasets'
|
||||
];
|
||||
|
||||
module.exports = function (opts) {
|
||||
var data = {};
|
||||
_.each(REQUIRED_OPTS, function (item) {
|
||||
if (opts[item] === undefined) throw new Error(item + ' is required');
|
||||
data['_' + item] = opts[item];
|
||||
});
|
||||
|
||||
var position = { x: data._ev.clientX, y: data._ev.clientY };
|
||||
var menuItems = [
|
||||
{
|
||||
label: _t('dataset.duplicate.' + (data._isCustomQuery ? 'customOption' : 'option')),
|
||||
val: 'duplicate',
|
||||
action: function () {
|
||||
this.trigger('duplicate');
|
||||
},
|
||||
disabled: !data._canCreateDatasets
|
||||
}
|
||||
];
|
||||
|
||||
if (data._isTableOwner) {
|
||||
if (!data._isSync) {
|
||||
menuItems.push({
|
||||
label: _t('dataset.rename.option'),
|
||||
val: 'rename',
|
||||
action: function () {
|
||||
this.trigger('rename');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
menuItems = menuItems.concat([
|
||||
{
|
||||
label: _t('dataset.metadata.option'),
|
||||
val: 'metadata',
|
||||
action: function () {
|
||||
this.trigger('metadata');
|
||||
}
|
||||
}, {
|
||||
label: _t('dataset.lock.option'),
|
||||
val: 'lock',
|
||||
action: function () {
|
||||
this.trigger('lock');
|
||||
}
|
||||
}, {
|
||||
label: _t('dataset.delete.option'),
|
||||
val: 'delete',
|
||||
destructive: true,
|
||||
action: function () {
|
||||
this.trigger('delete');
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
var collection = new CustomListCollection(menuItems);
|
||||
|
||||
var view = new ContextMenuView({
|
||||
collection: collection,
|
||||
triggerElementID: data._triggerElementID,
|
||||
position: position
|
||||
});
|
||||
|
||||
collection.bind('change:selected', function (menuItem) {
|
||||
var action = menuItem.get('action');
|
||||
action && action.bind(view)(menuItem);
|
||||
}, view);
|
||||
|
||||
view.model.bind('change:visible', function (model, isContextMenuVisible) {
|
||||
if (view && !isContextMenuVisible) {
|
||||
collection.unbind(null, null, view);
|
||||
view.model.unbind(null, null, view);
|
||||
view.remove();
|
||||
}
|
||||
}, view);
|
||||
|
||||
return view;
|
||||
};
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./dataset-header.tpl');
|
||||
var moment = require('moment');
|
||||
var InlineEditorView = require('builder/components/inline-editor/inline-editor-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var renameTableOperation = require('builder/dataset/operations/table-rename-operation');
|
||||
var templateInlineEditor = require('./inline-editor.tpl');
|
||||
var ConfirmationModalView = require('builder/components/modals/confirmation/modal-confirmation-view');
|
||||
var RemoveDatasetModalView = require('builder/components/modals/remove-dataset/remove-dataset-view');
|
||||
var PublishView = require('builder/components/modals/publish/publish-view');
|
||||
var PrivacyDropdown = require('builder/components/privacy-dropdown/privacy-dropdown-view');
|
||||
var PrivacyCollection = require('builder/components/modals/publish/privacy-collection');
|
||||
var CreatePrivacyOptions = require('builder/components/modals/publish/create-privacy-options');
|
||||
var ShareWith = require('builder/components/modals/publish/share-with-view');
|
||||
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
|
||||
var EditMetadataView = require('builder/components/modals/dataset-metadata/dataset-metadata-view');
|
||||
var VisTableModel = require('builder/data/visualization-table-model');
|
||||
var templateConfirmation = require('./rename-confirmation-modal.tpl');
|
||||
var createContextMenu = require('./create-context-menu');
|
||||
var tableDuplicationOperation = require('builder/dataset/operations/table-duplication-operation');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var TITLE_SUFFIX = ' | 快点';
|
||||
|
||||
var PRIVACY_MAP = {
|
||||
public: 'green',
|
||||
link: 'orange',
|
||||
private: 'red'
|
||||
};
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodeModel',
|
||||
'configModel',
|
||||
'layerDefinitionModel',
|
||||
'modals',
|
||||
'router',
|
||||
'userModel',
|
||||
'visModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'Editor-HeaderInfoEditor',
|
||||
|
||||
events: {
|
||||
'click .js-options': '_showContextMenu'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
var privacyOptions = CreatePrivacyOptions(this._visModel, this._userModel);
|
||||
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
|
||||
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
|
||||
this._syncModel = this._tableModel.getSyncModel();
|
||||
this._privacyCollection = new PrivacyCollection(privacyOptions);
|
||||
|
||||
this._setDocumentTitle();
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
var privacy = this._visModel.get('privacy');
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
title: this._tableModel.getUnqualifiedName(),
|
||||
privacy: privacy,
|
||||
isOwner: this._isDatasetOwner(),
|
||||
isCustomQueryApplied: this._isCustomQueryApplied(),
|
||||
isSync: this._tableModel.isSync(),
|
||||
syncState: this._syncModel.get('state'),
|
||||
cssClass: PRIVACY_MAP[privacy.toLowerCase()],
|
||||
ago: moment(this._visModel.get('updated_at')).fromNow(),
|
||||
avatar: this._userModel.get('avatar_url'),
|
||||
isInsideOrg: this._userModel.isInsideOrg(),
|
||||
hasWriteAccess: this._hasWriteAccess()
|
||||
})
|
||||
);
|
||||
|
||||
this._initViews();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_hasWriteAccess: function () {
|
||||
var permissionModel = this._tableModel._permissionModel;
|
||||
|
||||
return permissionModel.isOwner(this._userModel) || permissionModel.hasWriteAccess(this._userModel);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._syncModel, 'change:state change:interval destroy', this.render);
|
||||
this.listenTo(this._visModel, 'change:name', this._onChangeName);
|
||||
this.listenTo(this._visModel, 'change:privacy change:name', this.render);
|
||||
this.listenTo(this._querySchemaModel, 'change:query', this.render);
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var isDatasetOwner = this._isDatasetOwner();
|
||||
|
||||
var toggleMenuTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-toggle'),
|
||||
gravity: 'w',
|
||||
title: function () {
|
||||
return this._isHidden() ? _t('editor.layers.options.show') : _t('editor.layers.options.hide');
|
||||
}.bind(this)
|
||||
});
|
||||
this.addView(toggleMenuTooltip);
|
||||
|
||||
if (isDatasetOwner) {
|
||||
if (this._tableModel.isSync()) {
|
||||
var syncTooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-syncState'),
|
||||
gravity: 'n',
|
||||
title: function () {
|
||||
return this._getSyncInfo();
|
||||
}.bind(this)
|
||||
});
|
||||
this.addView(syncTooltip);
|
||||
} else {
|
||||
this._inlineEditor = new InlineEditorView({
|
||||
template: templateInlineEditor,
|
||||
renderOptions: {
|
||||
name: this._tableModel.getUnqualifiedName()
|
||||
},
|
||||
onEdit: this._onRenameTable.bind(this)
|
||||
});
|
||||
|
||||
this.$('.js-name').html(this._inlineEditor.render().el);
|
||||
this.addView(this._inlineEditor);
|
||||
}
|
||||
|
||||
if (this._userModel.isInsideOrg()) {
|
||||
var shareWith = new ShareWith({
|
||||
visDefinitionModel: this._visModel,
|
||||
userModel: this._userModel,
|
||||
separationClass: 'u-rSpace--m',
|
||||
clickPrivacyAction: this._clickPrivacy.bind(this)
|
||||
});
|
||||
this.$('.js-share-users').append(shareWith.render().el);
|
||||
this.addView(shareWith);
|
||||
}
|
||||
}
|
||||
|
||||
var privacyDropdown = new PrivacyDropdown({
|
||||
privacyCollection: this._privacyCollection,
|
||||
visDefinitionModel: this._visModel,
|
||||
userModel: this._userModel,
|
||||
configModel: this._configModel,
|
||||
isOwner: isDatasetOwner,
|
||||
ownerName: !isDatasetOwner && this._tableModel.getOwnerName()
|
||||
});
|
||||
|
||||
this.$('.js-dropdown').append(privacyDropdown.render().el);
|
||||
this.addView(privacyDropdown);
|
||||
},
|
||||
|
||||
_showContextMenu: function (ev) {
|
||||
var isSync = this._tableModel.isSync();
|
||||
var triggerElementID = 'context-menu-trigger-' + this._tableModel.cid;
|
||||
this.$('.js-options').attr('id', triggerElementID);
|
||||
|
||||
if (this._menuView && this._menuView.isVisible()) {
|
||||
this._menuView.hide();
|
||||
this._menuView.clean();
|
||||
delete this._menuView;
|
||||
return;
|
||||
}
|
||||
|
||||
this._menuView = createContextMenu({
|
||||
ev: ev,
|
||||
isTableOwner: this._isDatasetOwner(),
|
||||
isCustomQuery: this._isCustomQueryApplied(),
|
||||
isSync: isSync,
|
||||
triggerElementID: triggerElementID,
|
||||
canCreateDatasets: this._userModel.canCreateDatasets()
|
||||
});
|
||||
this._menuView.bind('rename', function () {
|
||||
this._inlineEditor && this._inlineEditor.edit();
|
||||
}, this);
|
||||
this._menuView.bind('delete', this._deleteDataset, this);
|
||||
this._menuView.bind('duplicate', this._duplicateDataset, this);
|
||||
this._menuView.bind('lock', this._lockDataset, this);
|
||||
this._menuView.bind('metadata', this._metadataDataset, this);
|
||||
|
||||
this._menuView.show();
|
||||
this.addView(this._menuView);
|
||||
},
|
||||
|
||||
_deleteDataset: function () {
|
||||
var self = this;
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new RemoveDatasetModalView({
|
||||
modalModel: modalModel,
|
||||
userModel: self._userModel,
|
||||
visModel: self._visModel,
|
||||
tableModel: self._tableModel,
|
||||
configModel: self._configModel
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_duplicateDataset: function () {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnquotedName();
|
||||
var name = this._isCustomQueryApplied() ? _t('dataset.duplicate.query') : tableName;
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new CreationModalView({
|
||||
modalModel: modalModel,
|
||||
loadingTitle: _t('dataset.duplicate.loading', { name: name }),
|
||||
errorTitle: _t('dataset.duplicate.error', { name: name }),
|
||||
runAction: function (opts) {
|
||||
tableDuplicationOperation({
|
||||
query: self._querySchemaModel.get('query'),
|
||||
tableModel: self._tableModel,
|
||||
configModel: self._configModel,
|
||||
onSuccess: function (importModel) {
|
||||
var tableName = importModel.get('table_name');
|
||||
var visTableModel = new VisTableModel({
|
||||
id: tableName,
|
||||
table: {
|
||||
name: tableName
|
||||
}
|
||||
}, {
|
||||
configModel: self._configModel
|
||||
});
|
||||
|
||||
window.location = visTableModel.datasetURL();
|
||||
},
|
||||
onError: function (importModel) {
|
||||
var error = importModel.get('get_error_text');
|
||||
var errorMessage = error && error.title;
|
||||
opts.error && opts.error(errorMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, {
|
||||
escapeOptionsDisabled: true
|
||||
});
|
||||
},
|
||||
|
||||
_lockDataset: function () {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnquotedName();
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new CreationModalView({
|
||||
modalModel: modalModel,
|
||||
loadingTitle: _t('dataset.lock.loading', { tableName: tableName }),
|
||||
errorTitle: _t('dataset.lock.error', { tableName: tableName }),
|
||||
runAction: function (opts) {
|
||||
self._visModel.save({
|
||||
locked: true
|
||||
}, {
|
||||
wait: true,
|
||||
success: function () {
|
||||
window.location = self._configModel.get('base_url') + '/dashboard/datasets';
|
||||
},
|
||||
error: function () {
|
||||
opts.error && opts.error();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_metadataDataset: function () {
|
||||
var isLocked = this._tableModel.isSync();
|
||||
var self = this;
|
||||
this._modals.create(function (modalModel) {
|
||||
return new EditMetadataView({
|
||||
modalModel: modalModel,
|
||||
visDefinitionModel: self._visModel,
|
||||
configModel: self._configModel,
|
||||
isLocked: isLocked
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_onRenameTable: function (newName) {
|
||||
var self = this;
|
||||
this._inlineEditor.hide();
|
||||
|
||||
if (newName === this._visModel.get('name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new ConfirmationModalView({
|
||||
modalModel: modalModel,
|
||||
template: templateConfirmation,
|
||||
renderOpts: {
|
||||
tableName: self._visModel.get('name')
|
||||
},
|
||||
runAction: function () {
|
||||
modalModel.destroy();
|
||||
|
||||
renameTableOperation({
|
||||
visModel: self._visModel,
|
||||
newName: newName,
|
||||
onError: self._onRenameFailed.bind(self)
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_onRenameFailed: function () {
|
||||
this._visModel.set('name', this._visModel.previous('name'), { silent: true });
|
||||
this.render();
|
||||
},
|
||||
|
||||
_onChangeName: function () {
|
||||
var name = this._visModel.get('name');
|
||||
this._analysisDefinitionNodeModel.setTableName(name);
|
||||
|
||||
this._setDocumentTitle();
|
||||
|
||||
this._layerDefinitionModel.save({
|
||||
sql: this._analysisDefinitionNodeModel.getDefaultQuery()
|
||||
});
|
||||
|
||||
this._router.navigate(name, {
|
||||
replace: true
|
||||
});
|
||||
},
|
||||
|
||||
_getSyncInfo: function () {
|
||||
var state = this._syncModel.get('state');
|
||||
if (state === 'success') {
|
||||
return _t('dataset.sync.synced', { ranAt: moment(this._syncModel.get('ran_at') || new Date()).fromNow() });
|
||||
} else if (state === 'failure') {
|
||||
var errorCode = this._syncModel.get('error_code');
|
||||
var errorMessage = this._syncModel.get('error_message');
|
||||
return _t('dataset.sync.error-code', { errorCode: errorCode }) + ':' + errorMessage;
|
||||
} else {
|
||||
return _t('dataset.sync.syncing');
|
||||
}
|
||||
},
|
||||
|
||||
_clickPrivacy: function () {
|
||||
var self = this;
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new PublishView({
|
||||
mapcapsCollection: self._mapcapsCollection,
|
||||
modalModel: modalModel,
|
||||
visDefinitionModel: self._visModel,
|
||||
privacyCollection: self._privacyCollection,
|
||||
userModel: self._userModel,
|
||||
configModel: self._configModel,
|
||||
mode: 'share',
|
||||
isOwner: self._isDatasetOwner()
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_setDocumentTitle: function () {
|
||||
document.title = this._tableModel.get('name') + TITLE_SUFFIX;
|
||||
},
|
||||
|
||||
_isCustomQueryApplied: function () {
|
||||
return this._analysisDefinitionNodeModel.isCustomQueryApplied();
|
||||
},
|
||||
|
||||
_isEditable: function () {
|
||||
return !this._analysisDefinitionNodeModel.isReadOnly();
|
||||
},
|
||||
|
||||
_isDatasetOwner: function () {
|
||||
return this._tableModel.isOwner(this._userModel);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="Editor-HeaderInfo-inner">
|
||||
<div class="Editor-HeaderInfo-title u-bSpace--m u-flex u-alignCenter">
|
||||
<% if (isSync && isOwner) { %>
|
||||
<div class="Editor-HeaderInfo-actions">
|
||||
<span class="SyncInfo-state SyncInfo-state--<%- syncState %> u-rSpace--m js-syncState"></span>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="Editor-HeaderInfo-titleText is-larger js-name">
|
||||
<h2 class="u-ellipsis CDB-Text CDB-Size-huge is-light"><%- title %></h2>
|
||||
</div>
|
||||
|
||||
<div class="Editor-HeaderInfo-details u-lSpace">
|
||||
<% if (!hasWriteAccess) { %>
|
||||
<span class="Tag CDB-Text CDB-Size-small u-rSpace u-upperCase js-readPermissionTag"><%- _t('dataset.read') %></span>
|
||||
<% } %>
|
||||
|
||||
<% if (isCustomQueryApplied) { %>
|
||||
<span class="Tag Tag--outline Tag-outline--dark CDB-Text CDB-Size-small u-secondaryTextColor u-rSpace"><%- _t('dataset.sql') %></span>
|
||||
<% } %>
|
||||
|
||||
<button class="CDB-Shape js-options">
|
||||
<div class="CDB-Shape-threePoints is-blue is-small">
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
<div class="CDB-Shape-threePointsItem"></div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="u-bSpace--xl u-flex u-alignCenter">
|
||||
<div class="js-dropdown u-rSpace--m"></div>
|
||||
<% if (isOwner && isInsideOrg) { %>
|
||||
<div class="js-share-users"></div>
|
||||
<% } %>
|
||||
<span class="CDB-Text CDB-Size-medium u-altTextColor"><%- _t('dataset.updated', { ago: ago }) %></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="Inline-editor" title="<%- name %>">
|
||||
<h2 class="CDB-Text CDB-Size-huge is-light u-ellipsis js-title Inline-editor-text is-larger"><%- name %></h2>
|
||||
<input type="text" name="text" class="Inline-editor-input Inline-editor-input--small CDB-Text CDB-InputText js-input" value="<%- name %>" readonly>
|
||||
</div>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<div class="u-flex u-justifyCenter">
|
||||
<div class="Modal-inner Modal-inner--grid u-flex u-justifyCenter">
|
||||
<div class="Modal-icon">
|
||||
<svg width="12px" height="23px" viewBox="0 0 12 23" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M3.85948829,15.9093411 L3.85948829,15.113874 C3.85948829,13.9648603 4.03625698,13.0196388 4.38979966,12.2781813 C4.74334234,11.5367237 5.40131468,10.7535402 6.36373642,9.92860729 C7.6993421,8.79923484 8.54144849,7.94976308 8.89008086,7.38016654 C9.23871322,6.81057 9.41302679,6.12313622 9.41302679,5.31784456 C9.41302679,4.3161403 9.09140598,3.54277728 8.44815472,2.99773232 C7.80490345,2.45268735 6.87932296,2.18016896 5.67138547,2.18016896 C4.8955557,2.18016896 4.13937853,2.27100842 3.40283128,2.45269008 C2.66628403,2.63437173 1.82172252,2.96581302 0.869121409,3.44702389 L0,1.45835626 C1.85609907,0.486113893 3.7956444,0 5.81869418,0 C7.69443451,0 9.15277619,0.461562686 10.193763,1.38470191 C11.2347497,2.30784113 11.7552353,3.60905508 11.7552353,5.28838281 C11.7552353,6.0052888 11.6594856,6.63625482 11.4679833,7.18129978 C11.2764811,7.72634475 10.9941422,8.24192009 10.6209582,8.72804127 C10.2477743,9.21416246 9.44249472,9.99734595 8.20509534,11.0776153 C7.21321171,11.9221894 6.55769449,12.6243539 6.23852401,13.1841299 C5.91935354,13.7439058 5.7597707,14.4902624 5.7597707,15.4232223 L5.7597707,15.9093411 L3.85948829,15.9093411 Z M2.95990869,20.3988536 C2.95990869,19.3235103 3.49337277,18.7858468 4.56031694,18.7858468 C5.07600662,18.7858468 5.47388191,18.9242161 5.75395476,19.2009588 C6.0340276,19.4777016 6.17406192,19.8769958 6.17406192,20.3988536 C6.17406192,20.9048974 6.03180483,21.2982616 5.74728639,21.5789578 C5.46276794,21.859654 5.06711542,22 4.56031694,22 C4.09797446,22 3.71565854,21.8754676 3.41335769,21.6263992 C3.11105685,21.3773307 2.95990869,20.9681529 2.95990869,20.3988536 Z" stroke="none" fill="#FEB100" fill-rule="evenodd" opacity="1"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class=" CDB-Text CDB-Size-huge is-light u-bSpace--xl">
|
||||
<%- _t('dataset.rename.title', { tableName: tableName }) %>
|
||||
</h2>
|
||||
<p class="CDB-Text CDB-Size-large u-altTextColor"><%- _t('dataset.rename.desc') %></p>
|
||||
<ul class="Modal-listActions u-flex u-alignCenter">
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--secondary CDB-Button--big js-cancel">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('dataset.rename.cancel') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--primary CDB-Button--big js-confirm">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('dataset.rename.confirm') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<ul class="CodeMirror-error">
|
||||
<li class="CodeMirror-errorMessage u-lSpace--xl u-rSpace--xl">
|
||||
<%- _t('components.codemirror.syntax-error') %>: <span><%- message %></span>
|
||||
</li>
|
||||
<li class="CodeMirror-errorDocs">
|
||||
<a href="https://carto.com/developers/sql-api/" target="_black"><%- _t('components.codemirror.docs') %></a>
|
||||
</li>
|
||||
</ul>
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./dataset-actions.tpl');
|
||||
var UndoButtons = require('builder/components/undo-redo/undo-redo-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'mapAction',
|
||||
'previewAction',
|
||||
'trackModel',
|
||||
'editorModel',
|
||||
'queryGeometryModel',
|
||||
'querySchemaModel',
|
||||
'onApply',
|
||||
'onClear',
|
||||
'clearSQLModel',
|
||||
'applyButtonStatusModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Dataset-options-actions',
|
||||
|
||||
events: {
|
||||
'click .js-createMap': '_onCreateMap',
|
||||
'click .js-previewMap': '_onPreviewMap'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._initViewState();
|
||||
this.listenTo(this._queryGeometryModel, 'change:status', this._setViewState);
|
||||
this.listenTo(this._querySchemaModel, 'change:status', this._updateApplyLoadingButtonLoading);
|
||||
this.listenTo(this._viewState, 'change', this.render);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(
|
||||
template({
|
||||
hasGeometry: this._viewState.get('hasGeom'),
|
||||
canCreateMap: true
|
||||
})
|
||||
);
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViewState: function () {
|
||||
this._viewState = new Backbone.Model({
|
||||
hasGeom: true
|
||||
});
|
||||
this._setViewState();
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var view = new UndoButtons({
|
||||
className: 'u-rSpace--xl',
|
||||
trackModel: this._trackModel,
|
||||
editorModel: this._editorModel,
|
||||
clearModel: this._clearSQLModel,
|
||||
applyStatusModel: this._applyButtonStatusModel,
|
||||
applyButton: true,
|
||||
clearButton: true,
|
||||
onApplyClick: this._handleApply.bind(this),
|
||||
onClearClick: this._handleClear.bind(this)
|
||||
});
|
||||
|
||||
this.$('.js-createMap').before(view.render().el);
|
||||
this.addView(view);
|
||||
},
|
||||
|
||||
_handleApply: function () {
|
||||
if (this._onApply) { this._onApply(); }
|
||||
},
|
||||
|
||||
_handleClear: function () {
|
||||
if (this._onClear) { this._onClear(); }
|
||||
},
|
||||
|
||||
_onCreateMap: function () {
|
||||
if (this._mapAction) { this._mapAction(); }
|
||||
},
|
||||
|
||||
_onPreviewMap: function () {
|
||||
if (this._previewAction) { this._previewAction(); }
|
||||
},
|
||||
|
||||
_updateApplyLoadingButtonLoading: function () {
|
||||
this._applyButtonStatusModel.set('loading', this._querySchemaModel.isFetching());
|
||||
},
|
||||
|
||||
_setViewState: function () {
|
||||
var self = this;
|
||||
this._queryGeometryModel.hasValueAsync()
|
||||
.then(function (hasGeom) {
|
||||
self._viewState.set('hasGeom', hasGeom);
|
||||
});
|
||||
}
|
||||
});
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var template = require('./dataset-actions.tpl');
|
||||
|
||||
// 'mapAction' is not required because a viewer user can't create maps
|
||||
var REQUIRED_OPTS = {
|
||||
previewAction: true,
|
||||
queryGeometryModel: true,
|
||||
mapAction: false
|
||||
};
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Dataset-options-actions',
|
||||
|
||||
events: {
|
||||
'click .js-createMap': '_onCreateMap',
|
||||
'click .js-previewMap': '_onPreviewMap'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
_.each(REQUIRED_OPTS, function (isRequired, item) {
|
||||
if (opts[item]) {
|
||||
this['_' + item] = opts[item];
|
||||
} else if (isRequired) {
|
||||
throw new Error(item + ' is required');
|
||||
}
|
||||
}, this);
|
||||
|
||||
this._initViewState();
|
||||
this.listenTo(this._queryGeometryModel, 'change:status', this._setViewState);
|
||||
this.listenTo(this._viewState, 'change', this.render);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(
|
||||
template({
|
||||
hasGeometry: this._viewState.get('hasGeometry'),
|
||||
canCreateMap: !!this._mapAction
|
||||
})
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViewState: function () {
|
||||
this._viewState = new Backbone.Model({
|
||||
hasGeometry: true
|
||||
});
|
||||
this._setViewState();
|
||||
},
|
||||
|
||||
_onPreviewMap: function () {
|
||||
this._previewAction && this._previewAction();
|
||||
},
|
||||
|
||||
_onCreateMap: function () {
|
||||
this._mapAction && this._mapAction();
|
||||
},
|
||||
|
||||
_setViewState: function () {
|
||||
this._queryGeometryModel.hasValueAsync()
|
||||
.then(function (hasGeom) {
|
||||
this._viewState.set('hasGeometry', hasGeom);
|
||||
}.bind(this))
|
||||
.catch(function () {
|
||||
this._viewState.set('hasGeometry', false);
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
<% if (hasGeometry) { %>
|
||||
<button class="u-rSpace--xl Dataset-tablePreview js-previewMap">
|
||||
<span class="u-upperCase CDB-Button-Text CDB-Text CDB-Size-small"><%- _t('dataset.preview-map.preview') %></span>
|
||||
</button>
|
||||
<% } %>
|
||||
<% if (canCreateMap) { %>
|
||||
<button class="CDB-Button CDB-Button--primary u-upperCase js-createMap">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-small"><%- _t('dataset.create-map.title') %></span>
|
||||
</button>
|
||||
<% } %>
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
var Backbone = require('backbone');
|
||||
var $ = require('jquery');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var VisDefinitionModel = require('builder/data/vis-definition-model');
|
||||
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
|
||||
var DatasetBaseView = require('builder/components/dataset/dataset-base-view');
|
||||
var errorParser = require('builder/helpers/error-parser');
|
||||
var PanelWithOptionsView = require('builder/components/view-options/panel-with-options-view');
|
||||
var TabPaneView = require('builder/components/tab-pane/tab-pane-view');
|
||||
var TabPaneCollection = require('builder/components/tab-pane/tab-pane-collection');
|
||||
var Toggler = require('builder/components/toggler/toggler-view');
|
||||
var DatasetEditorView = require('./dataset-sql-view');
|
||||
var ActionView = require('./dataset-actions-view');
|
||||
var PreviewMapView = require('./preview-map-view');
|
||||
var ActionViewEdition = require('./dataset-actions-edition-view');
|
||||
var DataSQLModel = require('builder/dataset/data-sql-model');
|
||||
var SQLNotifications = require('builder/sql-notifications');
|
||||
|
||||
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
|
||||
var MetricsTypes = require('builder/components/metrics/metrics-types');
|
||||
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodeModel',
|
||||
'configModel',
|
||||
'editorModel',
|
||||
'layerDefinitionModel',
|
||||
'modals',
|
||||
'onToggleEdition',
|
||||
'router',
|
||||
'userModel',
|
||||
'visModel'
|
||||
];
|
||||
|
||||
module.exports = DatasetBaseView.extend({
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
|
||||
|
||||
if (!this._layerDefinitionModel.sqlModel) {
|
||||
var sqlHistory = this._layerDefinitionModel.options && this._layerDefinitionModel.options.sql_history;
|
||||
this._layerDefinitionModel.sqlModel = new DataSQLModel({
|
||||
content: this._querySchemaModel.get('query')
|
||||
}, {
|
||||
history: sqlHistory || []
|
||||
});
|
||||
}
|
||||
|
||||
DatasetBaseView.prototype.initialize.call(this, {
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
editorModel: this._editorModel,
|
||||
configModel: this._configModel,
|
||||
querySchemaModel: this._querySchemaModel
|
||||
});
|
||||
|
||||
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
|
||||
this._queryGeometryModel = this._analysisDefinitionNodeModel.queryGeometryModel;
|
||||
this._canCreateMap = this._userModel.hasCreateMapsFeature();
|
||||
this._parseSQL = this._internalParseSQL.bind(this);
|
||||
|
||||
SQLNotifications.track(this);
|
||||
|
||||
this._togglerModel = new Backbone.Model({
|
||||
labels: [_t('dataset.data'), _t('dataset.sql')],
|
||||
active: this._editorModel.isEditing(),
|
||||
disabled: this._editorModel.isDisabled()
|
||||
});
|
||||
|
||||
this._configPanes();
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this._initViews();
|
||||
this._checkClearButton();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this.listenTo(this._editorModel, 'change:edition', this._onChangeEdition);
|
||||
this.listenTo(this._editorModel, 'change:disabled', this._onChangeDisabled);
|
||||
this.listenTo(this._togglerModel, 'change:active', this._onTogglerChanged);
|
||||
this.listenTo(this._querySchemaModel, 'change:query_errors', this._showErrors);
|
||||
this.listenTo(this._visModel, 'change:name', this._onChangeName);
|
||||
this.listenTo(this._sqlModel, 'undo redo', function () {
|
||||
this._codemirrorModel.set('content', this._sqlModel.get('content'));
|
||||
});
|
||||
},
|
||||
|
||||
_onChangeName: function (model, name) {
|
||||
this._codemirrorModel.set('content', this._analysisDefinitionNodeModel.getDefaultQuery());
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var self = this;
|
||||
|
||||
var panelWithOptionsView = new PanelWithOptionsView({
|
||||
editorModel: self._editorModel,
|
||||
createContentView: function () {
|
||||
return new TabPaneView({
|
||||
collection: self._collectionPane
|
||||
});
|
||||
},
|
||||
createControlView: function () {
|
||||
return new Toggler({
|
||||
model: self._togglerModel
|
||||
});
|
||||
},
|
||||
createActionView: function () {
|
||||
return new TabPaneView({
|
||||
collection: self._collectionPane,
|
||||
createContentKey: 'createActionView'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.append(panelWithOptionsView.render().el);
|
||||
this.addView(panelWithOptionsView);
|
||||
},
|
||||
|
||||
_onChangeEdition: function () {
|
||||
this._onToggleEdition();
|
||||
|
||||
var edition = this._editorModel.get('edition');
|
||||
var index = edition ? 1 : 0;
|
||||
this._collectionPane.at(index).set({ selected: true });
|
||||
this._togglerModel.set({ active: edition });
|
||||
},
|
||||
|
||||
_onChangeDisabled: function () {
|
||||
var disabled = this._editorModel.get('disabled');
|
||||
this._togglerModel.set({ disabled: disabled });
|
||||
},
|
||||
|
||||
_onTogglerChanged: function () {
|
||||
var checked = this._togglerModel.get('active');
|
||||
this._editorModel.set({ edition: checked });
|
||||
},
|
||||
|
||||
_configPanes: function () {
|
||||
var self = this;
|
||||
var tabPaneTabs = [{
|
||||
selected: !this._editorModel.get('edition'),
|
||||
createContentView: function () {
|
||||
return new CoreView();
|
||||
},
|
||||
createActionView: function () {
|
||||
var actionViewOptions = {
|
||||
queryGeometryModel: self._queryGeometryModel,
|
||||
previewAction: self._previewMap.bind(self)
|
||||
};
|
||||
if (self._canCreateMap) {
|
||||
actionViewOptions.mapAction = self._createMap.bind(self);
|
||||
}
|
||||
return new ActionView(actionViewOptions);
|
||||
}
|
||||
}, {
|
||||
selected: this._editorModel.get('edition'),
|
||||
createContentView: function () {
|
||||
return new DatasetEditorView({
|
||||
editorModel: self._editorModel,
|
||||
codemirrorModel: self._codemirrorModel,
|
||||
onApplyEvent: self._parseSQL,
|
||||
layerDefinitionModel: self._layerDefinitionModel,
|
||||
querySchemaModel: self._querySchemaModel
|
||||
});
|
||||
},
|
||||
createActionView: function () {
|
||||
var actionViewOptions = {
|
||||
clearSQLModel: self._clearSQLModel,
|
||||
trackModel: self._sqlModel,
|
||||
editorModel: self._editorModel,
|
||||
queryGeometryModel: self._queryGeometryModel,
|
||||
querySchemaModel: self._querySchemaModel,
|
||||
onApply: self._parseSQL,
|
||||
previewAction: self._previewMap.bind(self),
|
||||
onClear: self._clearSQL.bind(self),
|
||||
applyButtonStatusModel: self._applyButtonStatusModel
|
||||
};
|
||||
if (self._canCreateMap) {
|
||||
actionViewOptions.mapAction = self._createMap.bind(self);
|
||||
}
|
||||
return new ActionViewEdition(actionViewOptions);
|
||||
}
|
||||
}];
|
||||
|
||||
this._collectionPane = new TabPaneCollection(tabPaneTabs);
|
||||
},
|
||||
|
||||
_runQuery: function (query, callback) {
|
||||
this._querySchemaModel.set({
|
||||
query: query,
|
||||
status: 'unfetched'
|
||||
});
|
||||
|
||||
this._queryGeometryModel.set({
|
||||
query: query,
|
||||
simple_geom: '',
|
||||
status: 'unfetched'
|
||||
}, {
|
||||
silent: true
|
||||
});
|
||||
|
||||
this._querySchemaModel.fetch({
|
||||
success: callback
|
||||
});
|
||||
|
||||
this._queryGeometryModel.fetch();
|
||||
},
|
||||
|
||||
_saveSQL: function () {
|
||||
var content = this._codemirrorModel.get('content');
|
||||
this._sqlModel.set('content', content);
|
||||
this._querySchemaModel.set('query_errors', []);
|
||||
|
||||
if (this._tableModel.hasWriteAccess(this._userModel)) {
|
||||
this._layerDefinitionModel.save({
|
||||
sql: content
|
||||
});
|
||||
|
||||
MetricsTracker.track(MetricsTypes.APPLIED_SQL, {
|
||||
dataset_id: this._tableModel.get('id'),
|
||||
sql: content
|
||||
});
|
||||
}
|
||||
|
||||
SQLNotifications.showNotification({
|
||||
status: 'success',
|
||||
info: _t('notifications.sql.success'),
|
||||
closable: true
|
||||
});
|
||||
|
||||
this._checkClearButton();
|
||||
},
|
||||
|
||||
_defaultSQL: function () {
|
||||
return this._analysisDefinitionNodeModel.getDefaultQuery();
|
||||
},
|
||||
|
||||
_previewMap: function () {
|
||||
var previewMap = new PreviewMapView({
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
configModel: this._configModel,
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
visModel: this._visModel
|
||||
});
|
||||
$('body').append(previewMap.render().el);
|
||||
this.addView(previewMap);
|
||||
},
|
||||
|
||||
_createMap: function () {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnquotedName();
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new CreationModalView({
|
||||
modalModel: modalModel,
|
||||
loadingTitle: _t('dataset.create-map.loading', { tableName: tableName }),
|
||||
errorTitle: _t('dataset.create-map.error', { tableName: tableName }),
|
||||
runAction: function (opts) {
|
||||
var newVisModel = new VisDefinitionModel({
|
||||
name: self._visModel.get('name') + ' ' + _t('editor.map')
|
||||
}, {
|
||||
configModel: self._configModel
|
||||
});
|
||||
|
||||
newVisModel.save({
|
||||
source_visualization_id: self._visModel.get('id')
|
||||
}, {
|
||||
success: function (visModel) {
|
||||
window.location = visModel.builderURL();
|
||||
},
|
||||
error: function (mdl, e) {
|
||||
opts.error && opts.error(errorParser(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
<ul class="Options-bar u-flex u-justifySpace js-theme">
|
||||
<li class="CDB-InfoBox-footerItem js-controls"></li>
|
||||
<li class="CDB-InfoBox-footerItem CDB-InfoBox-footerItem--right js-actions"></li>
|
||||
</ul>
|
||||
@@ -0,0 +1,58 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var CodeMirrorView = require('builder/components/code-mirror/code-mirror-view');
|
||||
var ErrorTemplate = require('./code-mirror-error.tpl');
|
||||
var FactoryHints = require('builder/editor/editor-hints/factory-hints');
|
||||
var SQLHints = require('builder/editor/editor-hints/sql-hints');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'codemirrorModel',
|
||||
'onApplyEvent',
|
||||
'querySchemaModel',
|
||||
'layerDefinitionModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
module: 'dataset:dataset-options:dataset-sql-view',
|
||||
|
||||
className: 'Editor-content Dataset-editor',
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
FactoryHints.init({
|
||||
querySchemaModel: this._querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
tokens: SQLHints
|
||||
});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.empty();
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var hints = FactoryHints.reset().hints;
|
||||
this.codeMirrorView = new CodeMirrorView({
|
||||
className: 'Dataset-codemirror',
|
||||
model: this._codemirrorModel,
|
||||
hints: hints,
|
||||
mode: 'text/x-pgsql',
|
||||
tips: [
|
||||
_t('editor.data.code-mirror.tip')
|
||||
],
|
||||
errorTemplate: ErrorTemplate
|
||||
});
|
||||
|
||||
this.codeMirrorView.bind('codeSaved', this._triggerCodeSaved, this);
|
||||
this.addView(this.codeMirrorView);
|
||||
this.$el.append(this.codeMirrorView.render().el);
|
||||
},
|
||||
|
||||
_triggerCodeSaved: function (code, view) {
|
||||
this._onApplyEvent && this._onApplyEvent();
|
||||
}
|
||||
});
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var $ = require('jquery');
|
||||
var cdb = require('internal-carto.js');
|
||||
var CreationModalView = require('builder/components/modals/creation/modal-creation-view');
|
||||
var VisDefinitionModel = require('builder/data/vis-definition-model');
|
||||
var errorParser = require('builder/helpers/error-parser');
|
||||
var template = require('./preview-map.tpl');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
var ESC_KEY_CODE = 27;
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'analysisDefinitionNodeModel',
|
||||
'configModel',
|
||||
'modals',
|
||||
'userModel',
|
||||
'visModel'
|
||||
];
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
className: 'PreviewMap',
|
||||
|
||||
events: {
|
||||
'click .js-back': 'clean',
|
||||
'click .js-createMap': '_createMap'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
|
||||
this._tableModel = this._analysisDefinitionNodeModel.getTableModel();
|
||||
this._querySchemaModel = this._analysisDefinitionNodeModel.querySchemaModel;
|
||||
this._syncModel = this._tableModel.getSyncModel();
|
||||
this._canCreateMap = this._userModel.hasCreateMapsFeature();
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
|
||||
this._initKeydownBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(
|
||||
template({
|
||||
isOwner: this._tableModel.isOwner(this._userModel),
|
||||
isSync: this._tableModel.isSync(),
|
||||
syncState: this._syncModel.get('state'),
|
||||
name: this._tableModel.get('name'),
|
||||
isCustomQueryApplied: this._analysisDefinitionNodeModel.isCustomQueryApplied(),
|
||||
canCreateMap: this._canCreateMap
|
||||
})
|
||||
);
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
this._map = cdb.createVis(
|
||||
this.$('.js-map'),
|
||||
this._visModel.vizjsonURL(),
|
||||
{
|
||||
legends: false,
|
||||
authToken: this._configModel.get('auth_tokens')
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_initKeydownBinds: function () {
|
||||
$(document).bind('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
_destroyKeydownBinds: function () {
|
||||
$(document).unbind('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
_onKeyDown: function (ev) {
|
||||
var keyCode = ev.which;
|
||||
if (keyCode === ESC_KEY_CODE) {
|
||||
this.clean();
|
||||
}
|
||||
},
|
||||
|
||||
_createMap: function () {
|
||||
var self = this;
|
||||
var tableName = this._tableModel.getUnquotedName();
|
||||
|
||||
this._modals.create(function (modalModel) {
|
||||
return new CreationModalView({
|
||||
modalModel: modalModel,
|
||||
loadingTitle: _t('dataset.create-map.loading', { tableName: tableName }),
|
||||
errorTitle: _t('dataset.create-map.error', { tableName: tableName }),
|
||||
runAction: function (opts) {
|
||||
var newVisModel = new VisDefinitionModel({
|
||||
name: self._visModel.get('name') + ' ' + _t('editor.map')
|
||||
}, {
|
||||
configModel: self._configModel
|
||||
});
|
||||
|
||||
newVisModel.save({
|
||||
source_visualization_id: self._visModel.get('id')
|
||||
}, {
|
||||
success: function (visModel) {
|
||||
window.location = visModel.builderURL();
|
||||
},
|
||||
error: function (mdl, e) {
|
||||
opts.error && opts.error(errorParser(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._destroyKeydownBinds();
|
||||
CoreView.prototype.clean.call(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="PreviewMap-canvas">
|
||||
<div class="PreviewMap-map js-map"></div>
|
||||
<div class="PreviewMap-info u-flex u-justifySpace u-alignCenter js-info">
|
||||
<div class="PreviewMap-infoName u-flex u-alignCenter">
|
||||
<% if (isSync && isOwner) { %>
|
||||
<span class="SyncInfo-state SyncInfo-state--<%- syncState %> u-rSpace--m js-syncState"></span>
|
||||
<% } %>
|
||||
<h2 class="u-ellipsis CDB-Text CDB-Size-large u-rSpace--m is-light"><%- name %></h2>
|
||||
<% if (isCustomQueryApplied) { %>
|
||||
<span class="CDB-Tag CDB-Tag--opaque u-secondaryTextColor CDB-Text CDB-Size-medium u-upperCase"><%- _t('dataset.sql') %></span>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="PreviewMap-infoActions">
|
||||
<button class="u-upperCase CDB-Text CDB-Size-small u-lSpace--xl u-actionTextColor js-back">
|
||||
<%- _t('dataset.preview-map.back') %>
|
||||
</button>
|
||||
<% if (canCreateMap) { %>
|
||||
<button class="CDB-Button CDB-Button--primary u-upperCase u-lSpace--xl js-createMap">
|
||||
<span class="CDB-Button-Text CDB-Text CDB-Size-small"><%- _t('dataset.create-map.title') %></span>
|
||||
</button>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,115 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var errorParser = require('builder/helpers/error-parser');
|
||||
var template = require('./dataset-unlock-modal.tpl');
|
||||
var renderLoading = require('builder/components/loading/render-loading');
|
||||
var ErrorView = require('builder/components/error/error-view');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'tableName',
|
||||
'visModel',
|
||||
'configModel',
|
||||
'modalModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* Remove confirmation dialog
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Dialog-content',
|
||||
|
||||
events: {
|
||||
'click .js-confirm': '_onConfirm',
|
||||
'click .js-cancel': '_onCancel'
|
||||
},
|
||||
|
||||
initialize: function (opts) {
|
||||
_.each(REQUIRED_OPTS, function (item) {
|
||||
if (!opts[item]) throw new Error(item + ' is required');
|
||||
this['_' + item] = opts[item];
|
||||
}, this);
|
||||
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(
|
||||
template({
|
||||
tableName: this._tableName
|
||||
})
|
||||
);
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
$(document).bind('keydown', this._onKeyDown);
|
||||
|
||||
this._modalModel.bind('change:show', function () {
|
||||
if (this._visModel.get('locked')) {
|
||||
this._onCancel();
|
||||
}
|
||||
}, this);
|
||||
this.add_related_model(this._modalModel);
|
||||
},
|
||||
|
||||
_disableBinds: function () {
|
||||
$(document).unbind('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
_onKeyDown: function (ev) {
|
||||
var keyCode = ev.which;
|
||||
if (keyCode === $.ui.keyCode.ENTER) {
|
||||
this._onConfirm();
|
||||
}
|
||||
},
|
||||
|
||||
_renderLoadingView: function () {
|
||||
this.$el.html(
|
||||
renderLoading({
|
||||
title: _t('dataset.unlock.loading', { tableName: this._tableName })
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_renderErrorView: function (errorMessage) {
|
||||
var errorView = new ErrorView({
|
||||
title: _t('dataset.unlock.error', { tableName: this._tableName }),
|
||||
desc: errorMessage
|
||||
});
|
||||
this.$el.html(errorView.render().el);
|
||||
this.addView(errorView);
|
||||
},
|
||||
|
||||
_$content: function () {
|
||||
return this.$('.js-content');
|
||||
},
|
||||
|
||||
_onConfirm: function () {
|
||||
this._renderLoadingView();
|
||||
|
||||
this._visModel.save({
|
||||
locked: false
|
||||
}, {
|
||||
wait: true,
|
||||
success: function () {
|
||||
this._modalModel.destroy();
|
||||
}.bind(this),
|
||||
error: function (mdl, e) {
|
||||
this._renderErrorView(errorParser(e));
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
_onCancel: function () {
|
||||
window.location = this._configModel.get('base_url');
|
||||
},
|
||||
|
||||
clean: function () {
|
||||
this._disableBinds();
|
||||
CoreView.prototype.clean.apply(this);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="u-flex u-justifyCenter">
|
||||
<div class="Modal-inner Modal-inner--grid u-flex u-justifyCenter">
|
||||
<div class="Modal-icon">
|
||||
<svg width="16px" height="24px" viewBox="0 5 16 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="lock-open" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(0.000000, 5.000000)">
|
||||
<path d="M14,10.727 L14,5.5 C14,2.467 11.532,0 8.5,0 C5.468,0 3,2.467 3,5.5 L4,5.5 C4,3.019 6.019,1 8.5,1 C10.981,1 13,3.019 13,5.5 L13,9.765 C11.629,8.663 9.892,8 8,8 C3.589,8 0,11.589 0,16 C0,20.411 3.589,24 8,24 C12.411,24 16,20.411 16,16 C16,13.979 15.24,12.136 14,10.727 L14,10.727 Z M8,23 C4.141,23 1,19.86 1,16 C1,12.14 4.141,9 8,9 C11.859,9 15,12.14 15,16 C15,19.86 11.859,23 8,23 L8,23 Z" id="Shape" fill="#000000 "></path>
|
||||
<path d="M8,15 C7.448,15 7,15.449 7,16 C7,16.365 7.207,16.672 7.5,16.847 L7.5,19.5 C7.5,19.776 7.724,20 8,20 C8.276,20 8.5,19.776 8.5,19.5 L8.5,16.847 C8.793,16.672 9,16.365 9,16 C9,15.449 8.552,15 8,15 L8,15 Z" id="Shape" fill="#000000 "></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class=" CDB-Text CDB-Size-huge is-light u-bSpace--xl"><%- _t('dataset.unlock.title', { tableName: tableName }) %></h2>
|
||||
<p class="CDB-Text CDB-Size-large u-altTextColor"><%- _t('dataset.unlock.desc') %></p>
|
||||
<ul class="Modal-listActions u-flex u-alignCenter">
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--secondary CDB-Button--big js-cancel">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('dataset.unlock.cancel') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="Modal-listActionsitem">
|
||||
<button class="CDB-Button CDB-Button--primary CDB-Button--big js-confirm">
|
||||
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-medium u-upperCase">
|
||||
<%- _t('dataset.unlock.confirm') %>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,95 @@
|
||||
var CoreView = require('backbone/core-view');
|
||||
var template = require('./dataset.tpl');
|
||||
var HeaderView = require('./dataset-header/dataset-header-view');
|
||||
var DatasetContentView = require('./dataset-content/dataset-content-view');
|
||||
var DatasetOptionsView = require('./dataset-options/dataset-options-view');
|
||||
var TipsyTooltipView = require('builder/components/tipsy-tooltip-view');
|
||||
var checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
var REQUIRED_OPTS = [
|
||||
'router',
|
||||
'modals',
|
||||
'editorModel',
|
||||
'configModel',
|
||||
'userModel',
|
||||
'visModel',
|
||||
'analysisDefinitionNodeModel',
|
||||
'layerDefinitionModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* Remove confirmation dialog
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
className: 'Editor js-editor',
|
||||
|
||||
initialize: function (opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
this.$el.html(
|
||||
template({
|
||||
url: this._configModel.get('base_url')
|
||||
})
|
||||
);
|
||||
this._initViews();
|
||||
return this;
|
||||
},
|
||||
|
||||
_initViews: function () {
|
||||
var datasetHeaderView = new HeaderView({
|
||||
router: this._router,
|
||||
modals: this._modals,
|
||||
configModel: this._configModel,
|
||||
userModel: this._userModel,
|
||||
visModel: this._visModel,
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel
|
||||
});
|
||||
this.$('.js-info').append(datasetHeaderView.render().el);
|
||||
this.addView(datasetHeaderView);
|
||||
|
||||
var datasetContentView = new DatasetContentView({
|
||||
modals: this._modals,
|
||||
userModel: this._userModel,
|
||||
visModel: this._visModel,
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
configModel: this._configModel
|
||||
});
|
||||
this.$('.js-table').append(datasetContentView.render().el);
|
||||
this.addView(datasetContentView);
|
||||
|
||||
var datasetOptionsView = new DatasetOptionsView({
|
||||
onToggleEdition: this._onToggleEdition.bind(this),
|
||||
editorModel: this._editorModel,
|
||||
router: this._router,
|
||||
modals: this._modals,
|
||||
configModel: this._configModel,
|
||||
userModel: this._userModel,
|
||||
visModel: this._visModel,
|
||||
analysisDefinitionNodeModel: this._analysisDefinitionNodeModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel
|
||||
});
|
||||
this._getDatasetOptionsElement().append(datasetOptionsView.render().el);
|
||||
this.addView(datasetOptionsView);
|
||||
|
||||
var tooltip = new TipsyTooltipView({
|
||||
el: this.$('.js-editor-logo'),
|
||||
title: function () {
|
||||
return _t('back-to-dashboard');
|
||||
},
|
||||
gravity: 'w'
|
||||
});
|
||||
this.addView(tooltip);
|
||||
},
|
||||
|
||||
_getDatasetOptionsElement: function () {
|
||||
return this.$('.js-datasetOptions');
|
||||
},
|
||||
|
||||
_onToggleEdition: function () {
|
||||
this._getDatasetOptionsElement().toggleClass('is-dark', this._editorModel.isEditing());
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="Editor-wrapper">
|
||||
<div class="EditorMenu">
|
||||
<a class="EditorMenu-logo js-editor-logo" href="<%- url %>/dashboard/datasets">
|
||||
<svg width="32px" height="32px" viewBox="762 -58 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g class="imago" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" transform="translate(762.000000, -58.000000)">
|
||||
<circle class="Halo" fill="#FFFFFF" opacity="0.2" cx="16" cy="16" r="16"></circle>
|
||||
<circle class="point" fill="#FFFFFF" cx="16" cy="16" r="5.5"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Dataset">
|
||||
<div class="Dataset-view">
|
||||
<div class="Dataset-viewInfo js-info"></div>
|
||||
<div class="Dataset-viewTable js-table"></div>
|
||||
</div>
|
||||
<div class="Dataset-options js-datasetOptions"></div>
|
||||
<div class="Dataset-notifier js-notifier"></div>
|
||||
</div>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
var Notifier = require('builder/components/notifier/notifier');
|
||||
var errorParser = require('builder/helpers/error-parser');
|
||||
|
||||
module.exports = function (opts) {
|
||||
if (!opts.visModel) { throw new Error('visModel is required'); }
|
||||
|
||||
var visModel = opts.visModel;
|
||||
var successCallback = opts.onSuccess;
|
||||
var errorCallback = opts.onError;
|
||||
|
||||
visModel.destroy({
|
||||
wait: true,
|
||||
success: function (mdl, attrs) {
|
||||
successCallback && successCallback(mdl, attrs);
|
||||
},
|
||||
error: function (mdl, e) {
|
||||
errorCallback && errorCallback(mdl, e);
|
||||
Notifier.addNotification({
|
||||
status: 'error',
|
||||
info: _t('dataset.delete.error', {
|
||||
tableName: visModel.get('name'),
|
||||
error: errorParser(e)
|
||||
}),
|
||||
closable: true
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
var ImportModel = require('builder/data/background-importer/import-model');
|
||||
|
||||
module.exports = function (opts) {
|
||||
if (!opts.tableModel) throw new Error('tableModel is required');
|
||||
if (!opts.query) throw new Error('query is required');
|
||||
if (!opts.configModel) throw new Error('configModel is required');
|
||||
|
||||
var tableModel = opts.tableModel;
|
||||
var configModel = opts.configModel;
|
||||
var query = opts.query;
|
||||
var successCallback = opts.onSuccess;
|
||||
var errorCallback = opts.onError;
|
||||
var attrs = {
|
||||
table_name: tableModel.getUnqualifiedName() + '_copy',
|
||||
sql: query
|
||||
};
|
||||
|
||||
var importModel = new ImportModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
importModel.save(attrs, {
|
||||
error: errorCallback && errorCallback,
|
||||
success: function (model, changes) {
|
||||
model.bind('change:state', function () {
|
||||
var state = this.get('state');
|
||||
var success = this.get('success');
|
||||
if (state === 'failure') {
|
||||
errorCallback && errorCallback(model);
|
||||
} else if (state === 'complete' && success) {
|
||||
successCallback && successCallback(model);
|
||||
}
|
||||
}, model);
|
||||
}
|
||||
});
|
||||
};
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var Notifier = require('builder/components/notifier/notifier');
|
||||
var errorParser = require('builder/helpers/error-parser');
|
||||
|
||||
module.exports = function (opts) {
|
||||
if (!opts.visModel) { throw new Error('visModel is required'); }
|
||||
if (!opts.newName) { throw new Error('newName is required'); }
|
||||
|
||||
var visModel = opts.visModel;
|
||||
var newName = opts.newName;
|
||||
var successCallback = opts.onSuccess;
|
||||
var errorCallback = opts.onError;
|
||||
var notification = Notifier.addNotification({
|
||||
status: 'loading',
|
||||
info: _t('dataset.rename.loading', { tableName: visModel.get('name') }),
|
||||
closable: false
|
||||
});
|
||||
|
||||
visModel.save({ name: newName }, {
|
||||
wait: true,
|
||||
success: function (mdl, attrs) {
|
||||
successCallback && successCallback(mdl, attrs);
|
||||
notification.set({
|
||||
status: 'success',
|
||||
info: _t('dataset.rename.success', { tableName: visModel.get('name') }),
|
||||
closable: true
|
||||
});
|
||||
},
|
||||
error: function (mdl, e) {
|
||||
errorCallback && errorCallback(mdl, e);
|
||||
notification.set({
|
||||
status: 'error',
|
||||
info: _t('dataset.rename.error', {
|
||||
tableName: visModel.get('name'),
|
||||
error: errorParser(e)
|
||||
}),
|
||||
closable: true
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user