Initial commit
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
<button class="Header-navigationBreadcrumbLink <%- dropdownEnabled ? "DropdownLink DropdownLink--white" : "is-disabled" %>"><%- title %></button>
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
const $ = require('jquery');
|
||||
const checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
const navigateThroughRouter = require('builder/helpers/navigate-through-router');
|
||||
const AdminDropdownMenu = require('dashboard/components/dropdown/dropdown-admin-view');
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'viewModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* The content of the dropdown menu opened by the link at the end of the breadcrumbs menu, e.g.
|
||||
* username > [Maps]
|
||||
* ______/\____
|
||||
* | |
|
||||
* | this |
|
||||
* |____________|
|
||||
*/
|
||||
|
||||
module.exports = AdminDropdownMenu.extend({
|
||||
className: 'Dropdown BreadcrumbsDropdown',
|
||||
|
||||
events: {
|
||||
'click a': '_navigateToLinksHref'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
AdminDropdownMenu.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var dashboardUrl = this.model.viewUrl().dashboard();
|
||||
var datasetsUrl = dashboardUrl.datasets();
|
||||
var deepInsightsUrl = dashboardUrl.deepInsights();
|
||||
var mapsUrl = dashboardUrl.maps();
|
||||
|
||||
this.$el.html(this.template({
|
||||
avatarUrl: this.model.get('avatar_url'),
|
||||
userName: this.model.get('username'),
|
||||
mapsUrl: mapsUrl,
|
||||
datasetsUrl: datasetsUrl,
|
||||
deepInsightsUrl: deepInsightsUrl,
|
||||
lockedDatasetsUrl: datasetsUrl.lockedItems(),
|
||||
lockedMapsUrl: mapsUrl.lockedItems(),
|
||||
isDeepInsights: this._viewModel.isDisplayingDeepInsights(),
|
||||
isDatasets: this._viewModel.isDisplayingDatasets(),
|
||||
isMaps: this._viewModel.isDisplayingMaps(),
|
||||
isLocked: this._viewModel.isDisplayingLockedItems()
|
||||
}));
|
||||
|
||||
// Necessary to hide dialog on click outside popup, for example.
|
||||
// TODO: Handle this
|
||||
// cdb.god.bind('closeDialogs', this.hide, this);
|
||||
|
||||
// TODO: taken from existing code, how should dropdowns really be added to the DOM?
|
||||
$('body').append(this.el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_navigateToLinksHref: function () {
|
||||
this.hide(); // Hide must be called before routing for proper deconstruct of dropdown
|
||||
|
||||
if (this.options.router) {
|
||||
navigateThroughRouter.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
<ul class="BreadcrumbsDropdown-list CDB-Text CDB-Size-medium">
|
||||
<li class="BreadcrumbsDropdown-listItem">
|
||||
<span class="BreadcrumbsDropdown-icon u-rSpace--xl">
|
||||
<img class="UserAvatar-img UserAvatar-img--small" src="<%- avatarUrl %>" title="<%- userName %>" alt="<%- userName %>" />
|
||||
</span>
|
||||
<nav class="BreadcrumbsDropdown-options">
|
||||
<a href="<%- mapsUrl %>" class="BreadcrumbsDropdown-optionsItem <%- isMaps && !isDeepInsights && !isLocked ? 'is-selected' : '' %>">Your maps</a>
|
||||
<a href="<%- datasetsUrl %>" class="BreadcrumbsDropdown-optionsItem has-margin <%- isDatasets && !isLocked ? 'is-selected' : '' %>">Your datasets</a>
|
||||
</nav>
|
||||
</li>
|
||||
<li class="BreadcrumbsDropdown-listItem is-dark u-flex">
|
||||
<span class="BreadcrumbsDropdown-lockIcon BreadcrumbsDropdown-icon u-flex u-alignCenter u-justifyCenter u-rSpace--xl">
|
||||
<i class="CDB-IconFont CDB-IconFont-lock"></i>
|
||||
</span>
|
||||
<nav class="BreadcrumbsDropdown-options">
|
||||
<a href="<%- lockedMapsUrl %>" class="BreadcrumbsDropdown-optionsItem <%- isMaps && isLocked ? 'is-selected' : '' %>">Your locked maps</a>
|
||||
<a href="<%- lockedDatasetsUrl %>" class="BreadcrumbsDropdown-optionsItem has-margin <%- isDatasets && isLocked ? 'is-selected' : '' %>">Your locked datasets</a>
|
||||
</nav>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,16 @@
|
||||
<a class="Logo" href="<%- homeUrl %>">
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="92px" height="36px" viewBox="0 0 92 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(-162.000000, -282.000000)" fill="#FFFFFF">
|
||||
<g transform="translate(162.000000, 282.000000)">
|
||||
<path d="M74,36 C83.9411255,36 92,27.9411255 92,18 C92,8.0588745 83.9411255,0 74,0 C64.0588745,0 56,8.0588745 56,18 C56,27.9411255 64.0588745,36 74,36 Z" id="halo" fill-opacity="0.200000018"></path>
|
||||
<path d="M6.25280899,23.981602 C8.76747566,23.981602 10.220757,22.882802 11.2984713,21.390402 L8.9144367,19.684802 C8.22861851,20.521202 7.52647133,21.078802 6.33445401,21.078802 C4.73421159,21.078802 3.60751029,19.734002 3.60751029,18.012002 L3.60751029,17.979202 C3.60751029,16.306402 4.73421159,14.928802 6.33445401,14.928802 C7.4284973,14.928802 8.1796315,15.470002 8.83279168,16.273602 L11.2168263,14.420402 C10.204428,13.026402 8.70215964,12.042402 6.36711202,12.042402 C2.9053631,12.042402 0.358038428,14.666402 0.358038428,18.012002 L0.358038428,18.044802 C0.358038428,21.472402 2.98700813,23.981602 6.25280899,23.981602 L6.25280899,23.981602 Z M16.732047,23.752002 L20.0468349,23.752002 L20.8632851,21.685602 L25.2884453,21.685602 L26.1048955,23.752002 L29.5013284,23.752002 L24.6352851,12.190002 L21.5817613,12.190002 L16.732047,23.752002 Z M21.7940384,19.209202 L23.0840297,15.962002 L24.357692,19.209202 L21.7940384,19.209202 Z M35.6697093,23.752002 L38.8375361,23.752002 L38.8375361,20.275202 L40.2418305,20.275202 L42.5442201,23.752002 L46.1855881,23.752002 L43.4586443,19.750402 C44.8792677,19.143602 45.810021,17.979202 45.810021,16.208002 L45.810021,16.175202 C45.810021,15.043602 45.4671119,14.174402 44.7976227,13.502002 C44.0301595,12.731202 42.8218132,12.272002 41.0746097,12.272002 L35.6697093,12.272002 L35.6697093,23.752002 Z M38.8375361,17.782402 L38.8375361,15.010802 L40.9276487,15.010802 C41.9727049,15.010802 42.6421941,15.470002 42.6421941,16.388402 L42.6421941,16.421202 C42.6421941,17.257602 42.005363,17.782402 40.9439777,17.782402 L38.8375361,17.782402 Z M55.2605317,23.752002 L58.4283585,23.752002 L58.4283585,15.060002 L61.8574495,15.060002 L61.8574495,12.272002 L51.8477698,12.272002 L51.8477698,15.060002 L55.2605317,15.060002 L55.2605317,23.752002 Z M74,24 C77.3137085,24 80,21.3137085 80,18 C80,14.6862915 77.3137085,12 74,12 C70.6862915,12 68,14.6862915 68,18 C68,21.3137085 70.6862915,24 74,24 Z" id="logotype"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<% if (googleEnabled) { %>
|
||||
<span class="Logo-sub Logo-sub--google"></span>
|
||||
<% } %>
|
||||
</a>
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
const $ = require('jquery');
|
||||
const ScrollView = require('builder/components/scroll/scroll-view');
|
||||
const ViewFactory = require('builder/components/view-factory');
|
||||
const AdminDropdownMenu = require('dashboard/components/dropdown/dropdown-admin-view');
|
||||
const template = require('./templates/dropdown-content.tpl');
|
||||
|
||||
/**
|
||||
* User notifications dropdown, rendering notifications
|
||||
* from the collection
|
||||
*/
|
||||
|
||||
module.exports = AdminDropdownMenu.extend({
|
||||
className: 'Dropdown',
|
||||
|
||||
initialize: function (options) {
|
||||
AdminDropdownMenu.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.clearSubViews();
|
||||
|
||||
this.$el.html(this.template());
|
||||
this._renderDropdown();
|
||||
this._checkScroll();
|
||||
|
||||
$('body').append(this.el);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_renderDropdown: function () {
|
||||
this.dropdown_content = ViewFactory.createByTemplate(template, {
|
||||
items: this.collection.toJSON(),
|
||||
unreadItems: this.collection.filter(item => !item.get('opened')).length
|
||||
});
|
||||
this.addView(this.dropdown_content);
|
||||
|
||||
this.$('.js-content').html(this.dropdown_content.render().el);
|
||||
},
|
||||
|
||||
_checkScroll: function () {
|
||||
// we need to wait until dropdown has appeared,
|
||||
// then if it is taller than 300px we wrap the content in a ScrollView,
|
||||
// this is a fix for IE11, which needs a fixed height when using flex in a child element
|
||||
setTimeout(function () {
|
||||
if (this.$el.height() >= 300) {
|
||||
var view = new ScrollView({
|
||||
createContentView: function () {
|
||||
return this.dropdown_content;
|
||||
}.bind(this)
|
||||
});
|
||||
this.addView(view);
|
||||
|
||||
this.$el.addClass('Dropdown--withScroll');
|
||||
this.$('.js-content').html(view.render().el);
|
||||
}
|
||||
}.bind(this), 301);
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
// TODO: Handle event
|
||||
// cdb.god.bind('closeDialogs', this.hide, this);
|
||||
}
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
const Backbone = require('backbone');
|
||||
const UserNotificationModel = require('./user-notification-model');
|
||||
const OrganizationNotificationModel = require('./organization-notification-model');
|
||||
const checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'configModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* User notification default collection, it will
|
||||
* require the user notification model
|
||||
*/
|
||||
|
||||
module.exports = Backbone.Collection.extend({
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
model: function (attrs, options) {
|
||||
return attrs.type === 'org_notification'
|
||||
? new OrganizationNotificationModel(attrs, {
|
||||
...options,
|
||||
configModel: options.collection._configModel
|
||||
})
|
||||
: new UserNotificationModel(attrs);
|
||||
}
|
||||
});
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
const UserNotificationModel = require('./user-notification-model');
|
||||
const checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'userId',
|
||||
'configModel',
|
||||
'apiKey'
|
||||
];
|
||||
|
||||
/**
|
||||
* User notification default model
|
||||
*/
|
||||
|
||||
module.exports = UserNotificationModel.extend({
|
||||
url: function () {
|
||||
return `/api/v3/users/${this._userId}/notifications/${this.id}?api_key=${this._apiKey}`;
|
||||
},
|
||||
|
||||
initialize: function (attrs, opts) {
|
||||
checkAndBuildOpts(opts, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
markAsRead: function () {
|
||||
this.save({
|
||||
notification: {
|
||||
read_at: new Date()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
Hey <strong><%- userName %></strong>, looks like you're about to reach your account limit.
|
||||
<% if (userType === "admin") { %>
|
||||
<a href="mailto:<%- upgradeContactEmail %>">Contact us</a> for upgrading your account.
|
||||
<% } %>
|
||||
<% if (userType === "org") { %>
|
||||
Start thinking about <a href="mailto:<%- upgradeContactEmail %>">contacting your admin</a>.
|
||||
<% } %>
|
||||
<% if (userType === "regular") { %>
|
||||
Start thinking about <a href="<%- upgradeUrl %>?utm_source=Dashboard_Limits_Nearing&utm_medium=referral&utm_campaign=Upgrade_from_Dashboard&utm_content=upgrading%20your%20plan" class ="underline">upgrading your plan</a>.
|
||||
<% } %>
|
||||
<% if (userType === "internal") { %>
|
||||
Feel free to <a href="mailto:<%- upgradeContactEmail %>">contact us</a> for more resources.
|
||||
<% } %>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<ul class="NotificationsDropdown ">
|
||||
<% if (items.length > 0) { %>
|
||||
<% _.each(items, function(item){ %>
|
||||
<% if (!item.opened) { %>
|
||||
<li class="u-flex NotificationsDropdown-item is-new CDB-Text CDB-Size-medium">
|
||||
<i class="u-hintTextColor u-flex u-alignCenter u-justifyCenter NotificationsDropdown-icon CDB-IconFont <%- item.iconFont %> <%- item.severity %>"></i>
|
||||
<p class="NotificationsDropdown-text u-altTextColor"><%= cdb.core.sanitize.html(item.msg) %></p>
|
||||
</li>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
|
||||
<% _.each(items, function(item){ %>
|
||||
<% if (item.opened) { %>
|
||||
<li class="u-flex NotificationsDropdown-item CDB-Text CDB-Size-medium">
|
||||
<i class="u-hintTextColor u-flex u-alignCenter u-justifyCenter NotificationsDropdown-icon CDB-IconFont <%- item.iconFont %>"></i>
|
||||
<p class="NotificationsDropdown-text u-altTextColor"><%= cdb.core.sanitize.html(item.msg) %></p>
|
||||
</li>
|
||||
<% } %>
|
||||
<% }) %>
|
||||
|
||||
<% } else { %>
|
||||
<li class="u-flex u-alignCenter NotificationsDropdown-item NotificationsDropdown-item--no-notifications CDB-Text CDB-Size-medium">
|
||||
<p class="NotificationsDropdown-text u-altTextColor">There are no notifications :)</p>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
+1
@@ -0,0 +1 @@
|
||||
<div class="Dropdown-content Dropdown-content--withScroll js-content"></div>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
Hey <strong><%- userName %></strong>, you're over your disk limits.
|
||||
<% if (userType === "admin") { %>
|
||||
<a href="mailto:<%- upgradeContactEmail %>">Contact us</a> for upgrading your account.
|
||||
<% } %>
|
||||
<% if (userType === "org") { %>
|
||||
Start thinking about <a href="mailto:<%- upgradeContactEmail %>">contacting your admin</a>.
|
||||
<% } %>
|
||||
<% if (userType === "regular") { %>
|
||||
Start thinking about <a href="<%- upgradeUrl %>?utm_source=Dashboard_Limits_Nearing&utm_medium=referral&utm_campaign=Upgrade_from_Dashboard&utm_content=upgrading%20your%20plan" class ="underline">upgrading your plan</a>.
|
||||
<% } %>
|
||||
<% if (userType === "internal") { %>
|
||||
Feel free to <a href="mailto:<%- upgradeContactEmail %>">contact us</a> for more resources.
|
||||
<% } %>
|
||||
+1
@@ -0,0 +1 @@
|
||||
Just a reminder, your <strong><%- accountType %></strong> trial will finish the next <%- trialEnd %>. Happy mapping!
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Start your trial to experience the full CARTO.
|
||||
<br/>
|
||||
<a href="<%- upgradeUrl %>">Start now</a>
|
||||
+1
@@ -0,0 +1 @@
|
||||
Welcome to your brand new <strong><%- accountType %></strong> CARTO. Now we love you even more than before ;)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<i class="UserNotifications-Icon CDB-IconFont CDB-IconFont-alert"></i>
|
||||
|
||||
<% if (notificationsCount > 0) { %>
|
||||
<span class="Badge UserNotifications-badge"><%- notificationsCount %></span>
|
||||
<% } %>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
const Backbone = require('backbone');
|
||||
|
||||
/**
|
||||
* User notification default model
|
||||
*/
|
||||
|
||||
module.exports = Backbone.Model.extend({
|
||||
defaults: {
|
||||
type: '',
|
||||
message: '',
|
||||
opened: false
|
||||
}
|
||||
});
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
const $ = require('jquery');
|
||||
const moment = require('moment');
|
||||
const CoreView = require('backbone/core-view');
|
||||
const UserNotificationsCollection = require('./notifications-collection');
|
||||
const NotificationsDropdown = require('./dropdown-view');
|
||||
const template = require('./templates/user-notifications.tpl');
|
||||
const dropdownTemplate = require('./templates/dropdown.tpl');
|
||||
const LocalStorage = require('../../../helpers/local-storage');
|
||||
const checkAndBuildOpts = require('../../../../builder/helpers/required-opts');
|
||||
|
||||
const TEMPLATES = {
|
||||
tryTrial: require('./templates/try-trial.tpl'),
|
||||
limitsExceeded: require('./templates/limits-exceeded.tpl'),
|
||||
closeLimits: require('./templates/close-limits.tpl'),
|
||||
upgradedMessage: require('./templates/upgraded-message.tpl'),
|
||||
trialEndsSoon: require('./templates/trial-ends-soon.tpl')
|
||||
};
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'configModel',
|
||||
'user'
|
||||
];
|
||||
|
||||
/**
|
||||
* User notifactions view used to show alerts from the application
|
||||
*
|
||||
* In storage we will check these attributes, managed by a collection:
|
||||
*
|
||||
* try_trial -> trial_end_at is null && user is not paid user
|
||||
* limits_exceeded -> check table quota size
|
||||
* close_limits -> check table quota size < 80%
|
||||
* upgraded -> check upgraded_at less than one week
|
||||
* trial_ends_soon -> trial_end_at is not null and it is close to be finished
|
||||
* new_dashboard -> new dashboard
|
||||
* notification -> check notification
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
attributes: {
|
||||
href: '#/notifications'
|
||||
},
|
||||
|
||||
tagName: 'a',
|
||||
className: 'UserNotifications',
|
||||
|
||||
events: {
|
||||
'click': '_openNotifications'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
|
||||
this.localStorage = new LocalStorage();
|
||||
this.collection = new UserNotificationsCollection({
|
||||
configModel: options.configModel
|
||||
});
|
||||
this.collection.reset(this._generateCollection(), {
|
||||
userId: this._user.get('id'),
|
||||
apiKey: this._user.get('api_key'),
|
||||
silent: true
|
||||
});
|
||||
|
||||
this._initBinds();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var notificationsCount = this.collection.filter(item => !item.get('opened')).length;
|
||||
|
||||
this.$el.html(
|
||||
template({
|
||||
notificationsCount: notificationsCount
|
||||
})
|
||||
);
|
||||
|
||||
this.$el.toggleClass('has--alerts', notificationsCount > 0);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_initBinds: function () {
|
||||
this._user.bind('change', this._onUserChange, this);
|
||||
this.collection.bind('reset', this.render, this);
|
||||
this.collection.bind('remove', this.render, this);
|
||||
this.add_related_model(this._user);
|
||||
this.add_related_model(this.collection);
|
||||
},
|
||||
|
||||
_onUserChange: function () {
|
||||
// When api is ready, we will make a valid fetch :)
|
||||
this.collection.reset(this._generateCollection(), {
|
||||
userId: this._user.get('id'),
|
||||
apiKey: this._user.get('api_key')
|
||||
});
|
||||
this.render();
|
||||
},
|
||||
|
||||
// This method will check notifications and create a collection with them
|
||||
// Also it will check if those have been opened or not with Local Storage.
|
||||
_generateCollection: function () {
|
||||
var arr = [];
|
||||
var data = {}; // data
|
||||
var userUrl = this._user.viewUrl();
|
||||
var comHosted = this._configModel.get('cartodb_com_hosted');
|
||||
|
||||
data.isInsideOrg = this._user.isInsideOrg();
|
||||
data.isOrgOwner = this._user.isOrgOwner();
|
||||
data.accountType = this._user.get('account_type').toLowerCase();
|
||||
data.remainingQuota = this._user.get('remaining_byte_quota');
|
||||
data.publicProfileUrl = userUrl.publicProfile();
|
||||
data.bytesQuota = this._user.get('quota_in_bytes');
|
||||
data.userType = 'regular';
|
||||
data.upgradeUrl = window.upgrade_url || '';
|
||||
data.upgradeContactEmail = this._user.upgradeContactEmail();
|
||||
data.trialEnd = this._user.get('trial_ends_at') && moment(this._user.get('trial_ends_at')).format('YYYY-MM-DD');
|
||||
data.userName = this._user.get('name') || this._user.get('username');
|
||||
|
||||
// Get user type
|
||||
if (data.isInsideOrg && !data.isOrgOwner) {
|
||||
data.userType = 'org';
|
||||
} else if (data.isOrgOwner) {
|
||||
data.userType = 'admin';
|
||||
} else if (data.accountType === 'internal' || data.accountType === 'partner' || data.accountType === 'ambassador') {
|
||||
data.userType = 'internal';
|
||||
}
|
||||
|
||||
// try_trial -> trial_end_at is null && user is not paid user
|
||||
if (!comHosted && !data.isInsideOrg && data.accountType === 'free' && this._user.get('table_count') > 0) {
|
||||
arr.push({
|
||||
iconFont: 'CDB-IconFont-gift',
|
||||
severity: 'NotificationsDropdown-itemIcon--positive',
|
||||
type: 'try_trial',
|
||||
msg: TEMPLATES.tryTrial(data),
|
||||
opened: this.localStorage.get('notification.try_trial')
|
||||
});
|
||||
} else {
|
||||
this.localStorage.remove('notification.try_trial');
|
||||
}
|
||||
|
||||
// limits_exceeded -> check table quota size
|
||||
if (!comHosted && data.bytesQuota > 0 && data.remainingQuota <= 0) {
|
||||
arr.push({
|
||||
iconFont: 'CDB-IconFont-barometer',
|
||||
severity: 'NotificationsDropdown-itemIcon--negative',
|
||||
type: 'limits_exceeded',
|
||||
msg: TEMPLATES.limitsExceeded(data),
|
||||
opened: this.localStorage.get('notification.limits_exceeded')
|
||||
});
|
||||
} else {
|
||||
this.localStorage.remove('notification.limits_exceeded');
|
||||
}
|
||||
|
||||
// close_limits -> check table quota size < 80%
|
||||
if (!comHosted && data.bytesQuota > 0 && ((data.remainingQuota * 100) / data.bytesQuota) < 20) {
|
||||
arr.push({
|
||||
iconFont: 'CDB-IconFont-barometer',
|
||||
severity: 'NotificationsDropdown-itemIcon--alert',
|
||||
type: 'close_limits',
|
||||
msg: TEMPLATES.closeLimits(data),
|
||||
opened: this.localStorage.get('notification.close_limits')
|
||||
});
|
||||
} else {
|
||||
this.localStorage.remove('notification.close_limits');
|
||||
}
|
||||
|
||||
// upgraded -> check upgraded_at less than ... one week?
|
||||
if (!comHosted && this._user.get('show_upgraded_message')) {
|
||||
arr.push({
|
||||
iconFont: 'CDB-IconFont-heartFill',
|
||||
severity: 'NotificationsDropdown-itemIcon--positive',
|
||||
type: 'upgraded_message',
|
||||
msg: TEMPLATES.upgradedMessage(data),
|
||||
opened: this.localStorage.get('notification.upgraded_message')
|
||||
});
|
||||
} else {
|
||||
this.localStorage.remove('notification.upgraded_message');
|
||||
}
|
||||
|
||||
// trial_ends_soon -> show_trial_reminder flag
|
||||
if (this._user.get('show_trial_reminder')) {
|
||||
arr.push({
|
||||
iconFont: 'CDB-IconFont-clock',
|
||||
severity: 'NotificationsDropdown-itemIcon--alert',
|
||||
type: 'trial_ends_soon',
|
||||
msg: TEMPLATES.trialEndsSoon(data),
|
||||
opened: this.localStorage.get('notification.trial_ends_soon')
|
||||
});
|
||||
} else {
|
||||
this.localStorage.remove('notification.trial_ends_soon');
|
||||
}
|
||||
|
||||
const organizationNotifications = window.organization_notifications || this.options.organizationNotifications;
|
||||
|
||||
if (organizationNotifications) {
|
||||
for (var n = 0; n < organizationNotifications.length; n++) {
|
||||
var notification = organizationNotifications[n];
|
||||
var icon = notification.icon ? ('CDB-IconFont-' + notification.icon) : 'CDB-IconFont-alert';
|
||||
|
||||
arr.push({
|
||||
iconFont: icon,
|
||||
severity: 'NotificationsDropdown-itemIcon--alert',
|
||||
id: notification.id,
|
||||
msg: notification.html_body,
|
||||
read_at: notification.read_at,
|
||||
type: 'org_notification'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
_openNotifications: function (event) {
|
||||
if (event) this.killEvent(event);
|
||||
|
||||
if (this.notification) {
|
||||
this.notification.hide();
|
||||
delete this.notification;
|
||||
return this;
|
||||
}
|
||||
|
||||
var view = this.notification = new NotificationsDropdown({
|
||||
target: this.$el,
|
||||
collection: this.collection,
|
||||
horizontal_offset: 5,
|
||||
vertical_offset: -5,
|
||||
template: dropdownTemplate
|
||||
});
|
||||
|
||||
$(view.options.target).unbind('click', view._handleClick);
|
||||
this._closeAnyOtherOpenDialogs();
|
||||
|
||||
view.on('onDropdownHidden', () => this._onDropdownHidden(view));
|
||||
|
||||
view.render();
|
||||
view.open();
|
||||
|
||||
this.addView(view);
|
||||
},
|
||||
|
||||
_onDropdownHidden: function (view) {
|
||||
// All notifications have been seen, opened -> true
|
||||
this.collection.each(notification => {
|
||||
const notificationType = notification.get('type');
|
||||
|
||||
if (notificationType === 'org_notification') {
|
||||
notification.markAsRead();
|
||||
} else if (notificationType) {
|
||||
notification.set('opened', true);
|
||||
this.localStorage.set({
|
||||
[`notification.${notificationType}`]: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Clean collection because all notifications should
|
||||
// removed from the collection
|
||||
this.collection.reset();
|
||||
|
||||
// Clean dropdown
|
||||
view.clean();
|
||||
// Remove it from subviews
|
||||
this.removeView(view);
|
||||
// Remove count
|
||||
this.$el.removeClass('has--alerts');
|
||||
// No local notification set
|
||||
delete this.notification;
|
||||
},
|
||||
|
||||
_closeAnyOtherOpenDialogs: function () {
|
||||
// cdb.god.trigger("closeDialogs"); TODO: handle event
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
<div class="u-inner Header-inner">
|
||||
<div class="Header-navigation">
|
||||
<ul class="Header-navigationList">
|
||||
<li class="js-logo"></li>
|
||||
<li>
|
||||
<ul class="Header-navigationBreadcrumb">
|
||||
<% if (organizationName) { %>
|
||||
<li class="Header-navigationBreadcrumbItem CDB-Text CDB-Size-large"><p class="Header-navigationBreadcrumbParagraph"><%= organizationName %></p> /</li>
|
||||
<% } %>
|
||||
<li class="Header-navigationBreadcrumbItem CDB-Text CDB-Size-large">
|
||||
<p class="Header-navigationBreadcrumbParagraph"><a href="<%= homeUrl %>" class="Header-navigationBreadcrumbLink"><%= nameOrUsername %></a></p> /
|
||||
</li>
|
||||
<li class="Header-navigationBreadcrumbItem js-breadcrumb-dropdown CDB-Text CDB-Size-large"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="Header-settings">
|
||||
<ul class="Header-settingsList CDB-Text CDB-Size-medium">
|
||||
<% if (!isCartoDBHosted) { %>
|
||||
<li class="Header-settingsItem">
|
||||
<a target="_blank" href="https://carto.com/learn/guides" class="CDB-Text is-semibold Header-settingsLink Header-settingsLink--dashboard">Guides</a>
|
||||
</li>
|
||||
<li class="Header-settingsItem">
|
||||
<a target="_blank" href="https://carto.com/developers" class="CDB-Text is-semibold Header-settingsLink Header-settingsLink--dashboard">Developers</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li class="Header-settingsItem Header-settingsItemNotifications js-user-notifications">
|
||||
<button class="UserNotifications">
|
||||
<i class="UserNotifications-Icon CDB-IconFont CDB-IconFont-Alert"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li class="Header-settingsItem Header-settingsItem--avatar">
|
||||
<button class="UserAvatar js-settings-dropdown">
|
||||
<img src="<%= avatar %>" class="UserAvatar-img UserAvatar-img--medium">
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
const _ = require('underscore');
|
||||
const $ = require('jquery');
|
||||
const checkAndBuildOpts = require('builder/helpers/required-opts');
|
||||
const DropdownAdminView = require('dashboard/components/dropdown/dropdown-admin-view');
|
||||
const Utils = require('builder/helpers/utils');
|
||||
const template = require('./settings-dropdown.tpl');
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'model',
|
||||
'configModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* The content of the dropdown menu opened by the user avatar in the top-right of the header, e.g.:
|
||||
* Explore, Learn, ♞
|
||||
* ______/\____
|
||||
* | |
|
||||
* | this |
|
||||
* |____________|
|
||||
*/
|
||||
|
||||
module.exports = DropdownAdminView.extend({
|
||||
className: 'Dropdown',
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
DropdownAdminView.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
shortDisplayName: function (user) {
|
||||
// This changes should also be done in Central, ./app/assets/javascripts/dashboard/users/views/user_avatar.js
|
||||
var accountTypeDisplayName = user.get('account_type_display_name');
|
||||
var displayName = _.isUndefined(accountTypeDisplayName) ? user.get('account_type') : accountTypeDisplayName;
|
||||
|
||||
if (_.isUndefined(displayName)) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
displayName = displayName.toLowerCase();
|
||||
|
||||
if (displayName === 'organization user') {
|
||||
return 'org. user';
|
||||
} else {
|
||||
return displayName.replace(/lump-sum/gi, '- A')
|
||||
.replace(/academic/gi, 'aca.')
|
||||
.replace(/ - Monthly/i, ' - M')
|
||||
.replace(/ - Annual/i, ' - A')
|
||||
.replace(/Non-Profit/i, 'NP')
|
||||
.replace(/On-premises/i, 'OP')
|
||||
.replace(/Internal use engine/i, 'engine')
|
||||
.replace(/Lite/i, 'L')
|
||||
.replace(/Cloud Engine &/i, 'C. Engine &')
|
||||
.replace(/& Enterprise Builder/i, '& E. Builder')
|
||||
.replace(/CARTO for /i, '')
|
||||
.replace(/CARTO /i, '');
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var user = this.model;
|
||||
var usedDataBytes = user.get('db_size_in_bytes');
|
||||
var quotaInBytes = user.get('quota_in_bytes');
|
||||
var usedDataPct = Math.round(usedDataBytes / quotaInBytes * 100);
|
||||
var progressBarClass = '';
|
||||
|
||||
if (usedDataPct > 80 && usedDataPct < 90) {
|
||||
progressBarClass = 'is--inAlert';
|
||||
} else if (usedDataPct > 89) {
|
||||
progressBarClass = 'is--inDanger';
|
||||
}
|
||||
|
||||
var accountType = this.shortDisplayName(user);
|
||||
|
||||
var userUrl = this.model.viewUrl();
|
||||
var upgradeUrl = window.upgrade_url || this._configModel.get('upgrade_url') || '';
|
||||
|
||||
this.$el.html(template({
|
||||
name: user.fullName() || user.get('username'),
|
||||
email: user.get('email'),
|
||||
accountType: accountType,
|
||||
isOrgAdmin: user.isOrgAdmin(),
|
||||
usedDataStr: Utils.readablizeBytes(usedDataBytes),
|
||||
usedDataPct: usedDataPct,
|
||||
progressBarClass: progressBarClass,
|
||||
availableDataStr: Utils.readablizeBytes(quotaInBytes),
|
||||
showUpgradeLink: upgradeUrl && (user.isOrgOwner() || !user.isInsideOrg()) && !this._configModel.get('cartodb_com_hosted'),
|
||||
upgradeUrl: upgradeUrl,
|
||||
publicProfileUrl: userUrl.publicProfile(),
|
||||
apiKeysUrl: userUrl.apiKeys(),
|
||||
organizationUrl: userUrl.organization(),
|
||||
accountProfileUrl: userUrl.accountProfile(),
|
||||
logoutUrl: userUrl.logout(),
|
||||
isViewer: user.isViewer(),
|
||||
isBuilder: user.isBuilder(),
|
||||
orgDisplayEmail: user.isInsideOrg() ? user.organization.display_email : null,
|
||||
engineEnabled: user.get('actions').engine_enabled,
|
||||
mobileAppsEnabled: user.get('actions').mobile_sdk_enabled
|
||||
}));
|
||||
|
||||
// Necessary to hide dialog on click outside popup, for example.
|
||||
// TODO: Handle closeDialogs
|
||||
// cdb.god.bind('closeDialogs', this.hide, this);
|
||||
|
||||
// TODO: taken from existing code, how should dropdowns really be added to the DOM?
|
||||
$('body').append(this.el);
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
<ul class="SettingsDropdown">
|
||||
<li>
|
||||
<div class="SettingsDropdown-sameline">
|
||||
<p class="CDB-Text CDB-Size-medium"><%- name %></p>
|
||||
<p class="SettingsDropdown-accountType CDB-Text CDB-Size-small u-altTextColor u-upperCase"><%- accountType %></p>
|
||||
</div>
|
||||
<p class="CDB-Text CDB-Size-medium u-altTextColor u-tSpace u-ellipsis">
|
||||
<%- email %>
|
||||
</p>
|
||||
</li>
|
||||
<li class="u-tSpace-xl">
|
||||
<p class="SettingsDropdown-userRole">
|
||||
<% if (isViewer) { %>
|
||||
<span class="UserRoleIndicator Viewer CDB-Text CDB-Size-small is-semibold u-altTextColor">VIEWER</span>
|
||||
<% if (orgDisplayEmail) { %>
|
||||
<a href="mailto:<%- orgDisplayEmail %>" class="CDB-Text CDB-Size-small">Become a Builder</a>
|
||||
<% } %>
|
||||
<% } %>
|
||||
<% if (isBuilder) { %>
|
||||
<span class="UserRoleIndicator Builder CDB-Text CDB-Size-small is-semibold u-altTextColor">BUILDER</span>
|
||||
<% } %>
|
||||
</p>
|
||||
</li>
|
||||
<li class="u-tSpace-xl">
|
||||
<% if (showUpgradeLink) { %>
|
||||
<a href="<%- upgradeUrl %>" class="SettingsDropdown-itemLink">
|
||||
<% } %>
|
||||
|
||||
<div class="SettingsDropdown-sameline u-bSpace CDB-Text CDB-Size-medium u-altTextColor">
|
||||
<p class="DefaultDescription"><%- usedDataStr %> of <%- availableDataStr %> used</p>
|
||||
<% if (showUpgradeLink) { %>
|
||||
<p class="SettingsDropdown-itemLinkText u-actionTextColor">Upgrade</p>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="SettingsDropdown-progressBar <%- progressBarClass %>">
|
||||
<div class="progress-bar">
|
||||
<span class="bar-2" style="width: <%- usedDataPct %>%"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if (showUpgradeLink) { %>
|
||||
</a>
|
||||
<% } %>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="BreadcrumbsDropdown-listItem is-dark CDB-Text CDB-Size-medium">
|
||||
<ul>
|
||||
<li class="u-bSpace--m"><a href="<%- publicProfileUrl %>">View your public profile</a></li>
|
||||
<li class="u-bSpace--m"><a href="<%- accountProfileUrl %>">Your account</a></li>
|
||||
<% if (isOrgAdmin) { %>
|
||||
<li class="u-bSpace--m"><a href="<%- organizationUrl %>">Your organization</a></li>
|
||||
<% } %>
|
||||
<% if (engineEnabled || mobileAppsEnabled) { %>
|
||||
<li class="u-bSpace--m"><a href="<%- apiKeysUrl %>">Your API keys</a></li>
|
||||
<% } %>
|
||||
<li><a href="<%- logoutUrl %>">Close session</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,42 @@
|
||||
const CoreView = require('backbone/core-view');
|
||||
const template = require('./user-support.tpl');
|
||||
const checkAndBuildOpts = require('../../../builder/helpers/required-opts');
|
||||
|
||||
const REQUIRED_OPTS = [
|
||||
'userModel'
|
||||
];
|
||||
|
||||
/**
|
||||
* View to render the user support link in the header.
|
||||
* Expected to be created from existing DOM element.
|
||||
*/
|
||||
module.exports = CoreView.extend({
|
||||
|
||||
initialize: function (options) {
|
||||
checkAndBuildOpts(options, REQUIRED_OPTS, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(
|
||||
template({
|
||||
userType: this._getUserType()
|
||||
})
|
||||
);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_getUserType: function () {
|
||||
var accountType = this._userModel.get('account_type').toLowerCase();
|
||||
|
||||
if (this._userModel.isInsideOrg()) {
|
||||
return 'org';
|
||||
} else if (accountType === 'internal' || accountType === 'partner' || accountType === 'ambassador') {
|
||||
return 'internal';
|
||||
} else if (accountType !== 'free') {
|
||||
return 'client';
|
||||
} else {
|
||||
return 'regular';
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
<% if (userType === 'org') { %>
|
||||
<a href="mailto:enterprise-support@carto.com" class="Header-navigationLink u-hideOnMobile">Support</a>
|
||||
<% } else if (userType === 'client' || userType === 'internal') { %>
|
||||
<a href="mailto:support@carto.com" class="Header-navigationLink u-hideOnMobile">Support</a>
|
||||
<% } else { %>
|
||||
<a href="http://gis.stackexchange.com/questions/tagged/carto" class="Header-navigationLink u-hideOnMobile">Support</a>
|
||||
<% } %>
|
||||
Reference in New Issue
Block a user