<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://unpkg.com/moment@2.17.1"></script>
<script src="https://unpkg.com/interactjs@1"></script>
<script src="https://unpkg.com/angular@1.6.4/angular.js"></script>
<script src="https://unpkg.com/angular-animate@1.6.4/angular-animate.js"></script>
<script src="https://unpkg.com/angular-ui-bootstrap@2/dist/ui-bootstrap-tpls.js"></script>
<script src="https://unpkg.com/rrule@2"></script>
<script src="https://unpkg.com/angular-bootstrap-colorpicker@3"></script>
<script src="https://unpkg.com/angular-bootstrap-calendar"></script>
<script data-require="ui-select@*" data-semver="0.19.4" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-select/0.19.4/select.min.js"></script>
<link href="https://unpkg.com/bootstrap@3/dist/css/bootstrap.css" rel="stylesheet" />
<link href="https://unpkg.com/angular-bootstrap-colorpicker@3/css/colorpicker.min.css" rel="stylesheet" />
<link href="https://unpkg.com/angular-bootstrap-calendar/dist/css/angular-bootstrap-calendar.min.css" rel="stylesheet" />
<link data-require="ui-select@*" data-semver="0.19.4" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-select/0.19.4/select.min.css" />
<script src="example.js"></script>
<link rel="stylesheet" src="site.css" />
</head>
<body>
<div ng-controller="Controller as ctrl">
<script id="editModal.html" type="text/ng-template">
<div class="modal-header">
<h3 class="modal-title">Event action occurred!</h3>
</div>
<div class="modal-body">
<p>
Project:
<ui-select ng-model="ctrl.projects.selected"
theme="bootstrap"
reset-search-input="false"
title="Choose an project"
on-select="ctrl.event.title = $select.selected.projectName; $close()">
<ui-select-match placeholder="Choose a Project...">{{$select.selected.projectName}}</ui-select-match>
<ui-select-choices repeat="project in ctrl.projects | filter: $select.search" refresh-delay="0">
{{project.projectName}}
</ui-select-choices>
</ui-select>
</p>
</div>
</script>
<script id="calendarControls.html" type="text/ng-template">
<br>
<div class="row">
<div class="col-md-6 text-center">
<div class="btn-group">
<button class="btn btn-primary"
mwl-date-modifier
date="ctrl.viewDate"
decrement="week">
Previous
</button>
<button class="btn btn-default"
mwl-date-modifier
date="ctrl.viewDate"
set-to-today>
Today
</button>
<button class="btn btn-primary"
mwl-date-modifier
date="ctrl.viewDate"
increment="week">
Next
</button>
</div>
</div>
<br class="visible-xs visible-sm">
</div>
<br>
</script>
<script id="customDayView.html" type="text/ng-template">
<div class="cal-week-box cal-all-day-events-box" ng-if="vm.allDayEvents.length > 0" >
<div class="cal-day-panel clearfix">
<div class="row">
<div class="col-xs-12">
<div class="cal-row-fluid">
<div class="cal-cell-6 day-highlight"
ng-style="{backgroundColor: event.color.secondary}"
data-event-class
ng-repeat="event in vm.allDayEvents track by event.calendarEventId">
<strong>
<span ng-bind="event.startsAt | calendarDate:'datetime':true"></span>
<span ng-if="event.endsAt">
- <span ng-bind="event.endsAt | calendarDate:'datetime':true"></span>
</span>
</strong>
<a href="javascript:;"
class="event-item"
ng-click="vm.onEventClick({calendarEvent: event})"
ng-bind-html="vm.calendarEventTitle.dayView(event) | calendarTrustAsHtml">
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="cal-day-box" style="overflow: visible !important; " >
<div class="cal-day-panel clearfix" ng-style="{height: vm.dayViewHeight + 'px', minWidth: vm.viewWidth + 'px'}" >
<mwl-calendar-hour-list day-view-start="vm.dayViewStart"
day-view-end="vm.dayViewEnd"
day-view-split="vm.dayViewSplit"
on-timespan-click="vm.onTimespanClick"
on-date-range-select="vm.onDateRangeSelect"
on-event-times-changed="vm.onEventTimesChanged"
view-date="vm.viewDate"
custom-template-urls="vm.customTemplateUrls"
template-scope="vm.templateScope"
cell-modifier="vm.cellModifier"
view="day">
</mwl-calendar-hour-list>
<div class="pull-left day-event day-highlight"
ng-repeat="dayEvent in vm.nonAllDayEvents track by dayEvent.event.calendarEventId"
ng-class="dayEvent.event.cssClass"
ng-style="{
top: dayEvent.top - 1 + 'px',
left: dayEvent.left + vm.dayViewTimePositionOffset + 'px',
height: dayEvent.height + 'px',
width: dayEvent.width + 'px',
backgroundColor: dayEvent.event.color(dayEvent.event).secondary,
borderColor: dayEvent.event.color(dayEvent.event).primary,
zIndex: 999
}"
mwl-draggable="dayEvent.event.draggable === true"
axis="'xy'"
snap-grid="{y: vm.dayViewEventChunkSize || 30, x: 50}"
on-drag="vm.eventDragged(dayEvent.event, y / 30)"
on-drag-end="vm.eventDragComplete(dayEvent.event, y / 30)"
auto-scroll="vm.draggableAutoScroll"
mwl-resizable="dayEvent.event.resizable === true && dayEvent.event.endsAt"
resize-edges="{top: true, bottom: true}"
on-resize="vm.eventResized(dayEvent.event, edge, y / 30)"
on-resize-end="vm.eventResizeComplete(dayEvent.event, edge, y / 30)"
uib-tooltip-html="vm.calendarEventTitle.dayViewTooltip(dayEvent.event) | calendarTrustAsHtml"
tooltip-append-to-body="true"
ng-click="dayEvent.event.onClick(dayEvent.event.title); "
>
<span class="cal-hours">
<span>{{dayEvent.event.hours(dayEvent.event.startsAt, dayEvent.event.endsAt)}} hours</span>
</span>
<a href="javascript:;"
class="event-item-action"
ng-repeat="action in dayEvent.event.actions track by $index"
ng-class="action.cssClass"
ng-bind-html="action.label | calendarTrustAsHtml"
ng-click="action.onClick({calendarEvent: dayEvent.event})">
</a>
<br />
<span>
{{dayEvent.event.title}}
</span><br />
</div>
</div>
</div>
</script>
<div class="col-md-2">
<h3 ng-if="ctrl.totalHoursForWeek() > 0">Used This Week</h3>
<span ng-if="ctrl.totalHoursForWeek() > 0">Total Hours: {{ctrl.totalHoursForWeek()}}</span>
<div class="list-group">
<a ng-if="ctrl.totalHoursForProject(project.projectName) > 0" href="/#" ng-repeat="project in ctrl.recentProjects | orderBy : 'costNumber'" class="list-group-item" ng-class="{'active': project.isActive, 'list-group-item-action': !project.isActive}" ng-click="ctrl.setActive(project.projectName)">
Hours: {{ctrl.totalHoursForProject(project.projectName)}}, {{project.costNumber}} - {{project.projectName}}
</a>
</div>
<h3>Recently Used</h3>
<div class="list-group">
<a ng-if="ctrl.totalHoursForProject(project.projectName) === 0" href="/#" ng-repeat="project in ctrl.recentProjects | orderBy : 'costNumber'" class="list-group-item" ng-class="{'active': project.isActive, 'list-group-item-action': !project.isActive}" ng-click="ctrl.setActive(project.projectName)">
{{project.costNumber}} - {{project.projectName}}
</a>
</div>
</div>
<div class="col-md-10" >
<div class="row">
<div ng-cloak class="col-md-2" ng-repeat="day in ctrl.days" style="padding-left: 0px; padding-right: 0px">
<h3 ng-cloak>{{day.format('MM-DD')}}</h3>
<span>Hours: {{ ctrl.totalHoursForDay(day) }}</span>
<mwl-calendar id="{{day.format('YYYY-MM-DD')}}" events="ctrl.events"
view="ctrl.calendarView"
view-date="day"
on-event-times-changed="ctrl.eventTimesChanged(calendarEvent, calendarNewEventStart, calendarNewEventEnd)"
day-view-split="30"
on-date-range-select="ctrl.rangeSelected(calendarRangeStartDate, calendarRangeEndDate, ctrl.events)"
day-view-start="01:00"
day-view-end="8:59"
custom-template-urls="{calendarDayView: 'customDayView.html'}"
on-click="ctrl.setActive"
ng-attr-day-view-time-position="{{$index !== 0 && 'hidden' || 'default'}}">
</mwl-calendar>
</div>
</div>
<p>All time not blocked off for a specific project will be automatically assigned to your default cost center</p>
</div>
</div>
</body>
</html>
var app = angular.module('app', ['mwl.calendar', 'ngAnimate', 'ui.bootstrap', 'ui.select']);
app.factory('edit', function ($uibModal) {
function show(action, event, projects) {
return $uibModal.open({
templateUrl: 'editModal.html',
controller: function () {
var ctrl = this;
ctrl.event = event;
ctrl.projects = projects;
if (event.title.length > 0) {
for (var i in ctrl.projects) {
if (ctrl.event.title === ctrl.projects[i].projectName) {
ctrl.projects.selected = ctrl.projects[i];
break;
}
}
}
},
controllerAs: 'ctrl'
});
}
return {
show: show
};
});
app.controller('Controller', function ($scope, moment, calendarConfig, edit, interact, $filter) {
var ctrl = this;
var actions = [{
label: '<i class=\'glyphicon glyphicon-pencil\'></i>',
onClick: function (args) {
edit.show('Edited', args.calendarEvent, ctrl.recentProjects);
}
},
{
label: '<i class=\'glyphicon glyphicon-book\'></i>',
onClick: function (args) {
ctrl.copyEvent(args.calendarEvent)
}
},
{
label: '<i class=\'glyphicon glyphicon-trash\'></i>',
onClick: function (args) {
ctrl.deleteEvent(args.calendarEvent);
}
}];
ctrl.recentProjects = [
//{ costNumber: '900-90020', projectName: 'Default Org'},
{ costNumber: '900-90028', projectName: 'Pipeline Services West' },
{ costNumber: '900-90027', projectName: 'Pipeline Services East' },
{ costNumber: '900-90024', projectName: 'Opex - MSL Commercial' },
{ costNumber: '380-38000', projectName: 'Arrow Midstream' },
{ costNumber: '211-21102', projectName: 'OPEX - Marc I' },
{ costNumber: '214-21401', projectName: 'OPEX - Steuben' },
{ costNumber: '214-21402', projectName: 'OPEX - Thomas Corners' },
{ costNumber: '214-21403', projectName: 'OPEX - Seneca Lake' },
{ costNumber: '240-24000', projectName: 'OPEX - Tres Palacios' },
{ costNumber: '300-30201', projectName: 'OPEX - Cowtown Pipe' },
{ costNumber: '320-32010', projectName: 'OPEX - Willow Lake Pipeline' },
{ costNumber: '320-32011', projectName: 'OPEX - Willow Lake Facility' },
{ costNumber: '310-31011', projectName: 'OPEX - Granite Wash Facility' },
{ costNumber: '380-160072', projectName: 'CAPEX - Arrow DAPL Connection' },
{ costNumber: '380-160128', projectName: 'CAPEX - Arrow Gas Plant & Gathering FEED' },
{ costNumber: '211-21101', projectName: 'OPEX - Stagecoach Storage' },
{ costNumber: '300-30002', projectName: 'OPEX - Lake Arlington Facility' },
{ costNumber: '350-35040', projectName: 'OPEX - Stark Compressor Stn' },
{ costNumber: '350-35020', projectName: 'OPEX - Morgan Compressor Stn' },
{ costNumber: '353-35320', projectName: 'OPEX - Salem Compressor Stn' },
{ costNumber: '160111', projectName: 'SHEPERD - High Pressure Pipelines' },
{ costNumber: '160112', projectName: 'SHEPERD - Low Pressure Pipelines' },
{ costNumber: '110-11305', projectName: 'OPEX - Bath Storage Facility' },
{ costNumber: '200-20000', projectName: 'OPEX - Colt Terminal ' },
{ costNumber: '170266', projectName: 'Arrow System Constraints' }
];
ctrl.startOfWeek = moment().startOf('isoWeek');
ctrl.days = [
ctrl.startOfWeek
];
for (var i = 1; i < 5; i++) {
ctrl.days.push(moment(ctrl.startOfWeek).add(i, 'd'));
}
ctrl.copyEvent = function (event) {
var newEvent = Object.assign({}, event);
ctrl.events.push(newEvent);
};
ctrl.deleteEvent = function (eventToBeDeleted) {
for (var i in ctrl.events) {
if (ctrl.events[i].startsAt === eventToBeDeleted.startsAt && ctrl.events[i].endsAt === eventToBeDeleted.endsAt && ctrl.events[i].title === eventToBeDeleted.title) {
ctrl.events.splice(i, 1);
//ctrl.collapseEventsForDay(moment(eventToBeDeleted.startsAt).hours(0).minutes(0).seconds(0));
break;
}
}
};
ctrl.totalHoursForDay = function (date) {
var events = $filter('filter')(ctrl.events, { startsAt: date.format('DD') });
if (events.length > 0) {
return events.reduce(
function (sum, value) {
return sum + value.hours(value.startsAt, value.endsAt);
}, 0);
}
else {
return 0;
}
};
ctrl.totalHoursForProject = function (projectName) {
var events = $filter('filter')(ctrl.events, { title: projectName });
if (events.length > 0) {
return events.reduce(
function (sum, value) {
return sum + value.hours(value.startsAt, value.endsAt);
}, 0);
}
else {
return 0;
}
};
ctrl.totalHoursForWeek = function () {
if (ctrl.events.length > 0) {
return ctrl.events.reduce(
function (sum, value) {
return sum + value.hours(value.startsAt, value.endsAt);
}, 0);
}
else {
return 0;
}
};
ctrl.collapseEvents = function () {
for (var i in ctrl.days) {
ctrl.collapseEventsForDay(ctrl.days[i]);
}
};
ctrl.collapseEventsForDay = function (momentDay) {
var eventsForDay = $filter('filter')(ctrl.events, { startsAt: momentDay.format('DD') });
eventsForDay = $filter('orderBy')(eventsForDay, 'startsAt');
var lastEndHour = 1;
var lastEndMinute = 0;
if (eventsForDay.length > 0) {
for (var x in eventsForDay) {
var hourDiff = moment(eventsForDay[x].startsAt).hour() - lastEndHour;
var minuteDiff = moment(eventsForDay[x].startsAt).minute() - lastEndMinute;
eventsForDay[x].startsAt = moment(eventsForDay[x].startsAt).add(-1 * hourDiff, 'h').add(-1 * minuteDiff, 'm').toDate();
eventsForDay[x].endsAt = moment(eventsForDay[x].endsAt).add(-1 * hourDiff, 'h').add(-1 * minuteDiff, 'm').toDate();
lastEndHour = moment(eventsForDay[x].endsAt).hour();
lastEndMinute = moment(eventsForDay[x].endsAt).minute();
}
}
};
ctrl.hours = function (startsAt, endsAt) {
return (endsAt - startsAt) / (1000 * 60 * 60);
};
ctrl.color = function (event) {
if ($filter('filter')(ctrl.recentProjects, { isActive: true }).length > 0) {
var selected = $filter('filter')(ctrl.recentProjects, { isActive: true })[0];
if (event.title === selected.projectName)
return calendarConfig.colorTypes.special;
else
return calendarConfig.colorTypes.info;
}
else
return calendarConfig.colorTypes.info;
};
ctrl.setActive = function (projectName) {
for (var i in ctrl.recentProjects) {
if (ctrl.recentProjects[i].projectName === projectName)
ctrl.recentProjects[i].isActive = true;
else
ctrl.recentProjects[i].isActive = false;
}
};
ctrl.events = [
{
title: ctrl.recentProjects[15].projectName,
startsAt: ctrl.days[0].hours(5).minutes(0).toDate(),
endsAt: ctrl.days[0].hours(7).minutes(0).toDate(),
actions: actions,
color: ctrl.color,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
},
{
title: ctrl.recentProjects[1].projectName,
startsAt: ctrl.days[0].hours(3).minutes(30).toDate(),
endsAt: ctrl.days[0].hours(4).minutes(30).toDate(),
actions: actions,
color: ctrl.color,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
},
{
title: ctrl.recentProjects[2].projectName,
startsAt: ctrl.days[0].hours(2).minutes(0).toDate(),
endsAt: ctrl.days[0].hours(3).minutes(0).toDate(),
actions: actions,
color: ctrl.color, //calendarConfig.colorTypes.info,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
},
{
title: ctrl.recentProjects[4].projectName,
startsAt: ctrl.days[2].hours(3).minutes(0).toDate(),
endsAt: ctrl.days[2].hours(7).minutes(0).toDate(),
actions: actions,
color: ctrl.color,//calendarConfig.colorTypes.info,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
},
{
title: ctrl.recentProjects[7].projectName,
startsAt: ctrl.days[3].hours(5).minutes(0).toDate(),
endsAt: ctrl.days[3].hours(7).minutes(0).toDate(),
actions: actions,
color: ctrl.color,//calendarConfig.colorTypes.info,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
}
];
ctrl.eventToMove = {};
interact.on('dragend', function (event) {
if (event.dropzone) {
var dropElm = angular.element(event.dropzone._element);
var dayToMoveTo = moment(dropElm[0].parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.id);
if (dayToMoveTo.format('YYYY-MM-DD') !== moment(ctrl.eventToMove.startsAt).format('YYYY-MM-DD')) {
var oldDay = moment(ctrl.eventToMove.startsAt).hours(0).minutes(0).seconds(0);
ctrl.eventToMove.startsAt = dayToMoveTo.hours(moment(ctrl.eventToMove.startsAt).hours()).minutes(moment(ctrl.eventToMove.startsAt).minutes()).seconds(0).toDate();
ctrl.eventToMove.endsAt = dayToMoveTo.hours(moment(ctrl.eventToMove.endsAt).hours()).minutes(moment(ctrl.eventToMove.endsAt).minutes()).seconds(0).toDate();
var newDay = moment(ctrl.eventToMove.startsAt).hours(0).minutes(0).seconds(0);
//ctrl.collapseEventsForDay(oldDay);
//ctrl.collapseEventsForDay(newDay);
}
}
});
//ctrl.eventDropped = function (event, start, end) {
// var externalIndex = ctrl.externalEvents.indexOf(event);
// if (externalIndex > -1) {
// ctrl.externalEvents.splice(externalIndex, 1);
// ctrl.events.push(event);
// }
// event.startsAt = start;
// if (end) {
// event.endsAt = end;
// }
//};
ctrl.eventTimesChanged = function (event, newStart, newEnd) {
//if (ctrl.droppedOnDay._isAMomentObject) {
// newStart = ctrl.droppedOnDay.hour(moment(newStart).hour()).minute(moment(newStart).minute()).toDate();
// newEnd = ctrl.droppedOnDay.hour(moment(newEnd).hour()).minute(moment(newEnd).minute()).toDate();
//}
if (moment(newStart).format('HH') <= '08') {
event.startsAt = newStart;
if(moment(newEnd).format('HH:MM') > '09:00'){
event.endsAt = moment(newEnd).hours(9).minutes(0).toDate();
}
else {
event.endsAt = newEnd;
}
ctrl.eventToMove = event;
//ctrl.collapseEventsForDay(moment(newStart).hours(0).minutes(0).seconds(0));
}
else {
}
// do something here to save the change on the back end
};
calendarConfig.dateFormats.hour = 'H';
//calendarConfig.showTimesOnWeekView = true;
ctrl.calendarView = 'day';
ctrl.rangeSelected = function (startDate, endDate, events) {
if ($filter('filter')(ctrl.recentProjects, { isActive: true }).length > 0) {
events.push(
{
title: $filter('filter')(ctrl.recentProjects, { isActive: true })[0].projectName,
startsAt: startDate,
endsAt: endDate,
actions: actions,
color: ctrl.color, //calendarConfig.colorTypes.info,
resizable: true,
draggable: true,
hours: ctrl.hours,
onClick: ctrl.setActive
});
//ctrl.collapseEventsForDay(moment(startDate).hours(0).minutes(0).seconds(0));
}
else {
//growl.error('You must select a project on the right before selecting a time range');
}
};
//ctrl.collapseEvents();
});
.resource-overflow {
overflow:visible !important;
}