Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
body:has(.s-dashboard) {
|
||||
background-color: var(--layer);
|
||||
--field: var(--field-02);
|
||||
--field-hover: var(--field-hover-02);
|
||||
}
|
||||
|
||||
.input-group-addon label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.vector-map {
|
||||
height: 350px;
|
||||
color: transparent;
|
||||
}
|
||||
.jvectormap-container {
|
||||
height:100%;
|
||||
width:100%;
|
||||
}
|
||||
.dashboard-widgets .sortable-placeholder {
|
||||
background-color: #fff;
|
||||
border: 1px dashed #4e5d9d;
|
||||
flex-grow: 0;
|
||||
}
|
||||
.dashboard-widgets.cards {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-webkit-flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px;
|
||||
}
|
||||
.dashboard-widgets .tile {
|
||||
--layer: var(--background);
|
||||
margin: 5px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-moz-transition-property: background-color, box-shadow, -moz-transform, border;
|
||||
-o-transition-property: background-color, box-shadow, text-shadow, -o-transform, border;
|
||||
-webkit-transition-property: background-color, box-shadow, text-shadow, -webkit-transform, border;
|
||||
transition-property: background-color, box-shadow, text-shadow, transform, border;
|
||||
-moz-transition-duration: 0.2s;
|
||||
-o-transition-duration: 0.2s;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
transition-duration: 0.2s;
|
||||
-moz-transition-timing-function: linear;
|
||||
-o-transition-timing-function: linear;
|
||||
-webkit-transition-timing-function: linear;
|
||||
transition-timing-function: linear;
|
||||
position: relative;
|
||||
}
|
||||
.dashboard-widgets .ui-sortable-helper .tile {
|
||||
box-shadow: rgba(255, 255, 255, 0.1) 0px 1px 1px 0px inset, rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px;
|
||||
max-width: 170px;
|
||||
max-height: 170px;
|
||||
overflow: hidden;
|
||||
transform: rotate(1deg);
|
||||
-webkit-animation: squareToCircle 1.5s .5s infinite alternate;
|
||||
}
|
||||
|
||||
@-webkit-keyframes squareToCircle {
|
||||
0% {border-radius: 0 0 0 0;}
|
||||
|
||||
100% {border-radius: 20px 20px 20px 20px;}
|
||||
}
|
||||
|
||||
.dashboard-widgets .widget-overlay {
|
||||
content: '';
|
||||
width: 101%;
|
||||
height: 101%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: var(--transition-all-expressive);
|
||||
backdrop-filter: blur(0);
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.dashboard-widgets .ui-sortable-helper .widget-overlay {
|
||||
backdrop-filter: blur(8px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dashboard-widgets h4 {
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.dashboard-widgets .dropdown-toggle .ri-more-2-fill {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
color: var(--icon-secondary);
|
||||
transition: var(--transition-all-productive);
|
||||
width: 30px;
|
||||
margin: -10px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
border-radius: var(--border-radius-md);
|
||||
}
|
||||
.dashboard-widgets .card-header h4 {
|
||||
width: 90%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.dashboard-widgets .tile table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
.dashboard-widgets .tile table td {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.dashboard-widgets .tile table td:last-child {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.chart-wrapper .legend {
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
bottom: -17px;
|
||||
width: 100%;
|
||||
-moz-border-radius: 0 0 4px 4px;
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
color: #fff;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
}
|
||||
.chart-wrapper:hover .legend {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.chart-wrapper .legend ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.chart-wrapper .legend li {
|
||||
margin: 0 10px;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.chart-wrapper .legend li span {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 8px;
|
||||
margin-right: 5px;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.dashboard-widgets .widget {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-widgets .spinner {
|
||||
text-align: center;
|
||||
line-height: 250px;
|
||||
font-size: 24px;
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
// DashboardBundle
|
||||
// Use absolute path to keep dashboard working when app is in subdir
|
||||
Mautic.widgetUrl = mauticBasePath + '/s/dashboard/widget/';
|
||||
|
||||
/**
|
||||
* @type jQuery DOM element to be replaced with spinner
|
||||
*/
|
||||
Mautic.dashboardSubmitButton = false; // Button text, to be get and shown instead of spinner
|
||||
|
||||
/**
|
||||
* Init dashboard events
|
||||
* @param container
|
||||
*/
|
||||
Mautic.dashboardOnLoad = function (container) {
|
||||
Mautic.loadWidgets();
|
||||
};
|
||||
|
||||
/**
|
||||
* Load all widgets on initial page render
|
||||
*/
|
||||
Mautic.loadWidgets = function () {
|
||||
Mautic.dashboardFilterPreventSubmit();
|
||||
|
||||
jQuery('.widget').each(function() {
|
||||
let widgetId = jQuery(this).attr('data-widget-id');
|
||||
let container = jQuery('.widget[data-widget-id="'+widgetId+'"]');
|
||||
jQuery.ajax({
|
||||
url: Mautic.widgetUrl+widgetId+'?ignoreAjax=true',
|
||||
}).done(function(response) {
|
||||
Mautic.widgetOnLoad(container, response);
|
||||
});
|
||||
});
|
||||
|
||||
jQuery(document).ajaxComplete(function(){
|
||||
Mautic.initDashboardFilter();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Init dashboard filter events after widget load
|
||||
*/
|
||||
Mautic.initDashboardFilter = function () {
|
||||
let form = jQuery('form[name="daterange"]');
|
||||
form.find('button')
|
||||
.replaceWith(Mautic.dashboardSubmitButton);
|
||||
form
|
||||
.unbind('submit')
|
||||
.on('submit', function(e){
|
||||
e.preventDefault();
|
||||
Mautic.dashboardFilterPreventSubmit();
|
||||
jQuery('.widget').each(function() {
|
||||
let widgetId = jQuery(this).attr('data-widget-id');
|
||||
let element = jQuery('.widget[data-widget-id="' + widgetId + '"]');
|
||||
jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: Mautic.widgetUrl + widgetId + '?ignoreAjax=true',
|
||||
data: form.serializeArray(),
|
||||
success: function (response) {
|
||||
Mautic.widgetOnLoad(element, response);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent filter from submit, show spinner instead of send button
|
||||
*/
|
||||
Mautic.dashboardFilterPreventSubmit = function() {
|
||||
let form = jQuery('form[name="daterange"]');
|
||||
let button = form.find('button:first');
|
||||
Mautic.dashboardSubmitButton = button.clone();
|
||||
button.width(button.width()+'px'); // Keep button width
|
||||
button.html('<i class="ri-loader-3-line ri-spin"></i>');
|
||||
jQuery('.widget').find('.card-body').html('<div class="spinner"><i class="ri-loader-3-line ri-spin"></i></div>');
|
||||
form
|
||||
.unbind('submit')
|
||||
.on('submit', function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
Mautic.dashboardOnUnload = function(id) {
|
||||
// Trash initialized dashboard vars on app content change.
|
||||
mQuery('.jvectormap-tip').remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render widget from XHR to DOM
|
||||
*
|
||||
* @param container
|
||||
* @param response
|
||||
*/
|
||||
Mautic.widgetOnLoad = function(container, response) {
|
||||
if (!response.widgetId) return;
|
||||
// target in DOM
|
||||
var widget = mQuery('.widget[data-widget-id="' + response.widgetId + '"]');
|
||||
// source from response
|
||||
var widgetHtml = mQuery(response.widgetHtml);
|
||||
|
||||
// initialize edit button modal again
|
||||
widgetHtml.find("*[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
|
||||
event.preventDefault();
|
||||
Mautic.ajaxifyModal(this, event);
|
||||
});
|
||||
|
||||
// Create the new widget wrapper and add it to the 0 position if doesn't exist (probably a new one)
|
||||
if (!widget.length) {
|
||||
widget = mQuery('<div/>')
|
||||
.addClass('widget')
|
||||
.attr('data-widget-id', response.widgetId);
|
||||
mQuery('#dashboard-widgets').prepend(widget);
|
||||
}
|
||||
|
||||
widget.html(widgetHtml)
|
||||
.css('width', response.widgetWidth + '%')
|
||||
.css('height', response.widgetHeight + '%');
|
||||
Mautic.renderCharts(widgetHtml);
|
||||
|
||||
const map = widgetHtml.find('.vector-map').first();
|
||||
if (map.length && !map.hasClass('map-rendered')) {
|
||||
Mautic.initMap(widgetHtml, 'regions');
|
||||
}
|
||||
|
||||
Mautic.initWidgetRemoveEvents();
|
||||
Mautic.initWidgetSorting();
|
||||
Mautic.initDashboardFilter();
|
||||
};
|
||||
|
||||
Mautic.initWidgetRemoveEvents = function () {
|
||||
jQuery('.remove-widget')
|
||||
.unbind('click')
|
||||
.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
element = jQuery(this);
|
||||
let url = element.attr('href');
|
||||
element.closest('.widget').remove();
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Mautic.initWidgetSorting = function () {
|
||||
var widgetsWrapper = mQuery('#dashboard-widgets');
|
||||
var bodyOverflow = {};
|
||||
|
||||
widgetsWrapper.sortable({
|
||||
handle: '.card-header h4',
|
||||
placeholder: 'sortable-placeholder',
|
||||
items: '.widget',
|
||||
opacity: 0.9,
|
||||
scroll: true,
|
||||
scrollSpeed: 10,
|
||||
tolerance: "pointer",
|
||||
cursor: 'move',
|
||||
appendTo: '#dashboard-widgets',
|
||||
|
||||
helper: function(e, ui) {
|
||||
// Ensure the draggable retains it's original size and that the margin doesn't cause things to bounce around
|
||||
ui.children().each(function() {
|
||||
mQuery(this).width(mQuery(this).width());
|
||||
mQuery(this).height(mQuery(this).height());
|
||||
});
|
||||
|
||||
// Fix body overflow that messes sortable up
|
||||
bodyOverflow.overflowX = mQuery('body').css('overflow-x');
|
||||
bodyOverflow.overflowY = mQuery('body').css('overflow-y');
|
||||
mQuery('body').css({
|
||||
overflowX: 'visible',
|
||||
overflowY: 'visible'
|
||||
});
|
||||
|
||||
mQuery("#dashboard-widgets .widget").each(function(i) {
|
||||
var item = mQuery(this);
|
||||
var item_clone = item.clone();
|
||||
|
||||
var canvas = item.find('canvas').first();
|
||||
if (canvas.length) {
|
||||
// Copy the canvas
|
||||
var destCanvas = item_clone.find('canvas').first();
|
||||
var destCtx = destCanvas[0].getContext('2d');
|
||||
destCtx.drawImage(canvas[0], 0, 0);
|
||||
}
|
||||
|
||||
item.data("clone", item_clone);
|
||||
var position = item.position();
|
||||
item_clone
|
||||
.css({
|
||||
left: position.left,
|
||||
top: position.top,
|
||||
width: item.width(),
|
||||
visibility: "visible",
|
||||
position: "absolute",
|
||||
zIndex: 1
|
||||
});
|
||||
|
||||
item.css('visibility', 'hidden');
|
||||
mQuery("#cloned-widgets").append(item_clone);
|
||||
});
|
||||
|
||||
return ui;
|
||||
},
|
||||
start: function(e, ui) {
|
||||
ui.helper.css('visibility', 'visible');
|
||||
ui.helper.data("clone").hide();
|
||||
},
|
||||
sort: function(e, ui) {
|
||||
var tile = ui.item.find('.tile').first();
|
||||
// Prevent margin from pushing the elements out of the way
|
||||
ui.placeholder.css({
|
||||
marginTop: "5px",
|
||||
marginBottom: "5px",
|
||||
marginLeft: 0,
|
||||
marginRight: 0
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
// Restore original overflow
|
||||
mQuery('body').css(bodyOverflow);
|
||||
|
||||
mQuery("#dashboard-widgets .widget.exclude-me").each(function() {
|
||||
var item = mQuery(this);
|
||||
var clone = item.data("clone");
|
||||
var position = item.position();
|
||||
|
||||
clone.css("left", position.left);
|
||||
clone.css("top", position.top);
|
||||
clone.show();
|
||||
item.removeClass("exclude-me");
|
||||
});
|
||||
|
||||
mQuery("#dashboard-widgets .widget").css("visibility", "visible");
|
||||
mQuery("#cloned-widgets .widget").remove();
|
||||
|
||||
Mautic.saveWidgetSorting();
|
||||
},
|
||||
change: function(e, ui) {
|
||||
mQuery("#dashboard-widgets .widget:not(.exclude-me)").each(function() {
|
||||
var item = mQuery(this);
|
||||
var clone = item.data("clone");
|
||||
clone.stop(true, false);
|
||||
var position = item.position();
|
||||
clone.animate({
|
||||
left: position.left,
|
||||
top: position.top
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
}).disableSelection();
|
||||
}
|
||||
|
||||
Mautic.saveWidgetSorting = function () {
|
||||
var widgetsWrapper = mQuery('#dashboard-widgets');
|
||||
var widgets = widgetsWrapper.children();
|
||||
var ordering = [];
|
||||
widgets.each(function(index, value) {
|
||||
ordering.push(mQuery(this).attr('data-widget-id'));
|
||||
});
|
||||
|
||||
Mautic.ajaxActionRequest('dashboard:updateWidgetOrdering', {'ordering': ordering}, function(response) {
|
||||
// @todo handle errors
|
||||
});
|
||||
}
|
||||
|
||||
Mautic.updateWidgetForm = function (element) {
|
||||
Mautic.activateLabelLoadingIndicator('widget_type');
|
||||
var formWrapper = mQuery(element).closest('form');
|
||||
var WidgetFormValues = formWrapper.serializeArray();
|
||||
Mautic.ajaxActionRequest('dashboard:updateWidgetForm', WidgetFormValues, function(response) {
|
||||
if (response.formHtml) {
|
||||
var formHtml = mQuery(response.formHtml);
|
||||
formHtml.find('#widget_buttons').addClass('hide hidden');
|
||||
formWrapper.html(formHtml.children());
|
||||
Mautic.onPageLoad('#widget_params');
|
||||
}
|
||||
Mautic.removeLabelLoadingIndicator();
|
||||
});
|
||||
};
|
||||
|
||||
Mautic.exportDashboardLayout = function(text, baseUrl) {
|
||||
var name = prompt(text, "");
|
||||
|
||||
if (name !== null) {
|
||||
if (name) {
|
||||
baseUrl = baseUrl + "?name=" + encodeURIComponent(name);
|
||||
}
|
||||
|
||||
window.location = baseUrl;
|
||||
}
|
||||
};
|
||||
|
||||
Mautic.saveDashboardLayout = function(text) {
|
||||
var name = prompt(text, "");
|
||||
|
||||
if (name) {
|
||||
mQuery.ajax({
|
||||
type: 'POST',
|
||||
url: mauticBaseUrl+'s/dashboard/save',
|
||||
data: {name: name}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Mautic.setDateRange = function(option) {
|
||||
var today = new Date();
|
||||
var fromDate, toDate;
|
||||
|
||||
switch(option) {
|
||||
case 'today':
|
||||
fromDate = today;
|
||||
toDate = today;
|
||||
break;
|
||||
case 'yesterday':
|
||||
fromDate = new Date(today.getTime() - (24 * 60 * 60 * 1000));
|
||||
toDate = fromDate;
|
||||
break;
|
||||
default:
|
||||
if (typeof option === 'number') {
|
||||
fromDate = new Date(today.getTime() - (option * 24 * 60 * 60 * 1000));
|
||||
toDate = today;
|
||||
} else {
|
||||
console.error('Invalid option');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('daterange_date_from').value = Mautic.formatDate(fromDate);
|
||||
document.getElementById('daterange_date_to').value = Mautic.formatDate(toDate);
|
||||
document.getElementById('daterange_apply').click();
|
||||
};
|
||||
|
||||
Mautic.formatDate = function(date) {
|
||||
var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
];
|
||||
|
||||
return monthNames[date.getMonth()] + " " + date.getDate() + ", " + date.getFullYear();
|
||||
};
|
||||
Reference in New Issue
Block a user