Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -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);
}
});
@@ -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);
}
});
@@ -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()
}
});
}
});
@@ -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.
<% } %>
@@ -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>
@@ -0,0 +1 @@
<div class="Dropdown-content Dropdown-content--withScroll js-content"></div>
@@ -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.
<% } %>
@@ -0,0 +1 @@
Just a reminder, your <strong><%- accountType %></strong> trial will finish the next <%- trialEnd %>. Happy mapping!
@@ -0,0 +1,3 @@
Start your trial to experience the full CARTO.
<br/>
<a href="<%- upgradeUrl %>">Start now</a>
@@ -0,0 +1 @@
Welcome to your brand new <strong><%- accountType %></strong> CARTO. Now we love you even more than before ;)
@@ -0,0 +1,5 @@
<i class="UserNotifications-Icon CDB-IconFont CDB-IconFont-alert"></i>
<% if (notificationsCount > 0) { %>
<span class="Badge UserNotifications-badge"><%- notificationsCount %></span>
<% } %>
@@ -0,0 +1,13 @@
const Backbone = require('backbone');
/**
* User notification default model
*/
module.exports = Backbone.Model.extend({
defaults: {
type: '',
message: '',
opened: false
}
});
@@ -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
}
});