Skip to content
Snippets Groups Projects
Commit 3562104a authored by Valentin Groues's avatar Valentin Groues :eyes:
Browse files

add daily planning section

parent 82d0ba46
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 1506 additions and 11 deletions
......@@ -15,7 +15,7 @@ Including another URLconf
"""
from django.conf.urls import url
from web.api_views import worker, location, subject, appointment_type, appointment, configuration
from web.api_views import worker, location, subject, appointment_type, appointment, configuration, daily_planning
urlpatterns = [
# appointments
......@@ -41,4 +41,10 @@ urlpatterns = [
# worker data
url(r'^specializations$', worker.specializations, name='web.api.specializations'),
url(r'^units$', worker.units, name='web.api.units'),
url(r'^workers$', worker.workers_for_daily_planning, name='web.api.workers'),
# daily planning events
url(r'^events/(?P<date>\d{4}-\d{2}-\d{2})/$', daily_planning.events, name='web.api.events'),
url(r'^events_persist$', daily_planning.events_persist, name='web.api.events_persist'),
]
import datetime
import json
import random
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from ..models import Appointment, AppointmentTypeLink
from ..views.notifications import get_filter_locations
# mix_color = (0, 166, 90)
mix_color = (155, 155, 155)
def get_random_color():
r = lambda: random.randint(0, 255)
return '#%02X%02X%02X' % ((r() + mix_color[0]) / 2, (r() + mix_color[1]) / 2, (r() + mix_color[2]) / 2)
RANDOM_COLORS = [get_random_color() for i in range(20)]
def build_duration(duration):
number_of_hours = duration // 60
minutes = duration - number_of_hours * 60
return "{0:02d}:{1:02d}".format(number_of_hours, minutes)
@login_required
def events(request, date):
appointments = Appointment.objects.filter(
datetime_when__date=date,
location__in=get_filter_locations(request.user),
status=Appointment.APPOINTMENT_STATUS_SCHEDULED,
visit__isnull=False).all()
subjects = {}
for i, appointment in enumerate(appointments):
appointment_subject = appointment.visit.subject
if appointment_subject.id not in subjects:
# create subject
subject = {
'name': str(appointment_subject),
'id': appointment_subject.id,
'color': RANDOM_COLORS[i],
'events': []
}
subjects[appointment_subject.id] = subject
links = AppointmentTypeLink.objects.filter(appointment=appointment).all()
for j, link in enumerate(links):
link_when = link.date_when
link_end = None
if link_when is not None:
link_when = link_when.replace(tzinfo=None)
link_end = link_when + datetime.timedelta(minutes=link.appointment_type.default_duration)
event = {
'title': link.appointment_type.description,
'duration': build_duration(link.appointment_type.default_duration),
'subject': str(appointment_subject),
'id': '{}-{}'.format(i, j),
'link_id': link.id,
'link_when': link_when,
'link_who': link.worker_id,
'link_end': link_end,
'location': str(appointment.location),
'flying_team_location': str(appointment.flying_team),
'appointment_start': appointment.datetime_when.replace(tzinfo=None),
'appointment_end': appointment.datetime_when.replace(tzinfo=None, hour=19, minute=0),
}
subject_events = subjects[appointment_subject.id]['events']
subject_events.append(event)
return JsonResponse({
"data": subjects.values(),
})
@login_required
def events_persist(request):
event_links = json.loads(request.POST.get('events_to_persist'))
for event_link in event_links:
try:
when = datetime.datetime.strptime(event_link['start'], '%Y-%m-%dT%H:%M:00')
except ValueError:
when = datetime.datetime.strptime(event_link['start'][0:-6], '%Y-%m-%dT%H:%M:00')
worker_id = event_link['link_who']
link_id = event_link['link_id']
appointment_type_link = get_object_or_404(AppointmentTypeLink, pk=link_id)
appointment_type_link.worker_id = worker_id
appointment_type_link.date_when = when
appointment_type_link.save()
events_to_clear = json.loads(request.POST.get('events_to_clear'))
for link_id in events_to_clear:
appointment_type_link = get_object_or_404(AppointmentTypeLink, pk=link_id)
appointment_type_link.worker_id = None
appointment_type_link.date_when = None
appointment_type_link.save()
return JsonResponse({}, status=201)
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from web.models import Worker
from ..models import Worker
from ..views.notifications import get_filter_locations
@login_required
......@@ -18,3 +19,18 @@ def units(request):
return JsonResponse({
"units": [x[0] for x in X]
})
@login_required
def workers_for_daily_planning(request):
workers = Worker.objects.filter(locations__in=get_filter_locations(request.user)).exclude(
role=Worker.ROLE_CHOICES_SECRETARY).distinct()
workers_list_for_json = []
for worker in workers:
worker_dict_for_json = {
'id': worker.id,
'title': u"{} ({})".format(unicode(worker), worker.get_role_display()[:1].upper()),
'role': worker.get_role_display()
}
workers_list_for_json.append(worker_dict_for_json)
return JsonResponse(workers_list_for_json, safe=False)
import datetime
from collections import OrderedDict
from django import forms
from django.forms import ModelForm, Form
from django.utils.dates import MONTHS
from models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt
from models import Subject, Worker, Appointment, Visit, AppointmentType, ContactAttempt, AppointmentTypeLink
from models.constants import SUBJECT_TYPE_CHOICES, SCREENING_NUMBER_PREFIXES_FOR_TYPE
"""
......@@ -24,6 +25,7 @@ DATETIMEPICKER_DATE_ATTRS = {
'data-date-format': 'Y-MM-DD HH:mm',
}
START_YEAR_STATISTICS = 2015
APPOINTMENT_TYPES_FIELD_POSITION = 1
def validate_subject_nd_number(self, cleaned_data):
......@@ -185,12 +187,11 @@ class AppointmentEditForm(ModelForm):
class Meta:
model = Appointment
fields = '__all__'
exclude = ['appointment_types']
datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)',
widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
)
appointment_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple,
queryset=AppointmentType.objects.all())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
......@@ -199,8 +200,22 @@ class AppointmentEditForm(ModelForm):
self.user = Worker.get_by_user(user)
if self.user is None:
raise TypeError("Worker not defined for: " + user.username)
super(ModelForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
initial_appointment_types = AppointmentTypeLink.objects.filter(appointment=kwargs['instance']).values_list(
'appointment_type', flat=True)
else:
initial_appointment_types = []
fields = OrderedDict()
for i, tuple in enumerate(self.fields.items()):
key, value = tuple
fields[key] = value
if i == APPOINTMENT_TYPES_FIELD_POSITION:
fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False,
widget=forms.CheckboxSelectMultiple,
queryset=AppointmentType.objects.all(),
initial=initial_appointment_types)
self.fields = fields
def clean_location(self):
location = self.cleaned_data['location']
......@@ -209,17 +224,24 @@ class AppointmentEditForm(ModelForm):
else:
return location
def save(self, commit=True):
appointment = super(AppointmentEditForm, self).save(commit)
AppointmentTypeLink.objects.filter(appointment=appointment).delete()
appointment_types = self.cleaned_data['appointment_types']
for appointment_type in appointment_types:
appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type)
appointment_type_link.save()
return appointment
class AppointmentAddForm(ModelForm):
class Meta:
model = Appointment
exclude = ['status']
exclude = ['status', 'appointment_types']
datetime_when = forms.DateTimeField(label='Appointment on (YYYY-MM-DD HH:MM)',
widget=forms.DateTimeInput(DATETIMEPICKER_DATE_ATTRS)
)
appointment_types = forms.ModelMultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple,
queryset=AppointmentType.objects.all())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
......@@ -230,6 +252,16 @@ class AppointmentAddForm(ModelForm):
raise TypeError("Worker not defined for: " + user.username)
super(ModelForm, self).__init__(*args, **kwargs)
fields = OrderedDict()
for i, tuple in enumerate(self.fields.items()):
key, value = tuple
fields[key] = value
if i == APPOINTMENT_TYPES_FIELD_POSITION:
fields['appointment_types'] = forms.ModelMultipleChoiceField(required=False,
widget=forms.CheckboxSelectMultiple,
queryset=AppointmentType.objects.all(),
)
self.fields = fields
def clean_location(self):
location = self.cleaned_data['location']
......@@ -238,6 +270,14 @@ class AppointmentAddForm(ModelForm):
else:
return location
def save(self, commit=True):
appointment = super(AppointmentAddForm, self).save(commit)
appointment_types = self.cleaned_data['appointment_types']
for appointment_type in appointment_types:
appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type)
appointment_type_link.save()
return appointment
class VisitDetailForm(ModelForm):
datetime_begin = forms.DateField(label="Visit begins on",
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-05-09 11:49
from __future__ import unicode_literals
import django.db.models.deletion
from django.db import migrations, models
from ..models import AppointmentTypeLink
def convert_records(apps, schema_editor):
Appointment = apps.get_model("web", "Appointment")
for appointment in Appointment.objects.all():
for appointment_type_id in appointment.appointment_types.values_list('id', flat=True):
new_link = AppointmentTypeLink(appointment_id=appointment.id, appointment_type_id=appointment_type_id)
new_link.save()
class Migration(migrations.Migration):
dependencies = [
('web', '0039_pd_family_default_unknown'),
]
operations = [
migrations.CreateModel(
name='AppointmentTypeLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_when', models.DateTimeField(null=True, default=None)),
('appointment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Appointment')),
('appointment_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.AppointmentType')),
('worker', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='web.Worker')),
],
),
migrations.AddField(
model_name='appointment',
name='appointment_types_new',
field=models.ManyToManyField(blank=True, related_name='new_appointment', through='web.AppointmentTypeLink', to='web.AppointmentType', verbose_name=b'Appointment types'),
),
migrations.RunPython(convert_records),
migrations.RunSQL(
"DROP TABLE web_appointment_appointment_types"),
migrations.RenameField(model_name='appointment', old_name='appointment_types_new', new_name='appointment_types')
]
......@@ -8,6 +8,7 @@ from django.contrib.auth.models import User
from configuration_item import ConfigurationItem
from flying_team import FlyingTeam
from location import Location
from appointment_type_link import AppointmentTypeLink
from room import Room
from visit import Visit
from worker import Worker
......@@ -27,4 +28,4 @@ def get_current_year():
__all__ = [FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject,
Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate]
Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate, AppointmentTypeLink]
......@@ -31,9 +31,11 @@ class Appointment(models.Model):
verbose_name='Worker conducting the assessment (if applicable)',
null=True, blank=True
)
appointment_types = models.ManyToManyField("web.AppointmentType",
verbose_name='Appointment types',
blank=True
blank=True, through="web.AppointmentTypeLink",
related_name="new_appointment"
)
room = models.ForeignKey("web.Room",
verbose_name='Room ID',
......
from django.db import models
class AppointmentTypeLink(models.Model):
appointment = models.ForeignKey("web.Appointment", on_delete=models.CASCADE)
appointment_type = models.ForeignKey("web.AppointmentType", on_delete=models.CASCADE)
date_when = models.DateTimeField(null=True, default=None)
worker = models.ForeignKey("web.Worker", null=True, default=None)
def save(self, *args, **kwargs):
if self.date_when is not None:
self.date_when = self.date_when.replace(tzinfo=None)
super(AppointmentTypeLink, self).save(*args, **kwargs)
return self
/*!
* FullCalendar v2.2.5 Print Stylesheet
* Docs & License: http://arshaw.com/fullcalendar/
* (c) 2013 Adam Shaw
*/
/*
* Include this stylesheet on your page to get a more printer-friendly calendar.
* When including this stylesheet, use the media='print' attribute of the <link> tag.
* Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
*/
.fc {
max-width: 100% !important;
}
/* Global Event Restyling
--------------------------------------------------------------------------------------------------*/
.fc-event {
page-break-inside: avoid;
}
.fc-event .fc-resizer {
display: none;
}
/* Table & Day-Row Restyling
--------------------------------------------------------------------------------------------------*/
/* kill the overlaid, absolutely-positioned common components */
.fc-bg,
.fc-bgevent-skeleton,
.fc-highlight-skeleton,
.fc-helper-skeleton {
display: none;
}
/* don't force a min-height on rows (for DayGrid) */
.fc tbody .fc-row {
height: auto !important; /* undo height that JS set in distributeHeight */
min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */
}
.fc tbody .fc-row .fc-content-skeleton {
position: static; /* undo .fc-rigid */
padding-bottom: 0 !important; /* use a more border-friendly method for this... */
}
.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { /* only works in newer browsers */
padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */
}
.fc tbody .fc-row .fc-content-skeleton table {
/* provides a min-height for the row, but only effective for IE, which exaggerates this value,
making it look more like 3em. for other browers, it will already be this tall */
height: 1em;
}
/* Undo month-view event limiting. Display all events and hide the "more" links
--------------------------------------------------------------------------------------------------*/
.fc-more-cell,
.fc-more {
display: none !important;
}
.fc tr.fc-limited {
display: table-row !important;
}
.fc td.fc-limited {
display: table-cell !important;
}
.fc-popover {
display: none; /* never display the "more.." popover in print mode */
}
/* TimeGrid Restyling
--------------------------------------------------------------------------------------------------*/
/* undo the min-height 100% trick used to fill the container's height */
.fc-time-grid {
min-height: 0 !important;
}
/* don't display the side axis at all ("all-day" and time cells) */
.fc-agenda-view .fc-axis {
display: none;
}
/* don't display the horizontal lines */
.fc-slats,
.fc-time-grid hr { /* this hr is used when height is underused and needs to be filled */
display: none !important; /* important overrides inline declaration */
}
/* let the container that holds the events be naturally positioned and create real height */
.fc-time-grid .fc-content-skeleton {
position: static;
}
/* in case there are no events, we still want some height */
.fc-time-grid .fc-content-skeleton table {
height: 4em;
}
/* kill the horizontal spacing made by the event container. event margins will be done below */
.fc-time-grid .fc-event-container {
margin: 0 !important;
}
/* TimeGrid *Event* Restyling
--------------------------------------------------------------------------------------------------*/
/* naturally position events, vertically stacking them */
.fc-time-grid .fc-event {
position: static !important;
margin: 3px 2px !important;
}
/* for events that continue to a future day, give the bottom border back */
.fc-time-grid .fc-event.fc-not-end {
border-bottom-width: 1px !important;
}
/* indicate the event continues via "..." text */
.fc-time-grid .fc-event.fc-not-end:after {
content: "...";
}
/* for events that are continuations from previous days, give the top border back */
.fc-time-grid .fc-event.fc-not-start {
border-top-width: 1px !important;
}
/* indicate the event is a continuation via "..." text */
.fc-time-grid .fc-event.fc-not-start:before {
content: "...";
}
/* time */
/* undo a previous declaration and let the time text span to a second line */
.fc-time-grid .fc-event .fc-time {
white-space: normal !important;
}
/* hide the the time that is normally displayed... */
.fc-time-grid .fc-event .fc-time span {
display: none;
}
/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */
.fc-time-grid .fc-event .fc-time:after {
content: attr(data-full);
}
/* Vertical Scroller & Containers
--------------------------------------------------------------------------------------------------*/
/* kill the scrollbars and allow natural height */
.fc-scroller,
.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */
.fc-time-grid-container { /* */
overflow: visible !important;
height: auto !important;
}
/* kill the horizontal border/padding used to compensate for scrollbars */
.fc-row {
border: 0 !important;
margin: 0 !important;
}
/* Button Controls
--------------------------------------------------------------------------------------------------*/
.fc-button-group,
.fc button {
display: none; /* don't display any button-related controls */
}
.fc-scroller {
margin: 0px !important;
}
.fc-timeline-event {
font-size: 8px;
}
\ No newline at end of file
#subjects {
margin-bottom: 60px;
}
.subjects-events h4 {
font-size: 16px;
margin-top: 0;
padding-top: 1em;
}
.subjects-events .fc-event {
margin: 10px 0;
cursor: pointer;
border: none;
color: #fff;
padding-left: 10px;
font-weight: bold;
font-size: 1em;
}
.popover-content ul {
list-style: none;
margin: 0;
padding: 0;
}
\ No newline at end of file
v1.6.2 (2017-04-27)
-------------------
- composer.js for Composer (PHP package manager) (#291)
- fixed removed background events coming back when collapsing & expanding a resource (#295)
- fixed refetchResourcesOnNavigate with refetchResources not receiving start & end (#296)
- internal refactor of async systems
v1.6.1 (2017-04-01)
-------------------
Bugfixes (code changes in v3.3.1 of core project):
- stale calendar title when navigate away from then back to the a view
- js error when gotoDate immediately after calendar initialization
- agenda view scrollbars causes misalignment in jquery 3.2.1
- navigation bug when trying to navigate to a day of another week
- dateIncrement not working when duration and dateIncrement have different units (#287)
v1.6.0 (2017-03-23)
-------------------
Adjustments to accommodate all date-related features in core v3.3.0, including:
- `visibleRange` - complete control over view's date range
- `validRange` - restrict date range
- `changeView` - pass in a date or visibleRange as second param
- `dateIncrement` - customize prev/next jump (#36)
- `dateAlignment` - custom view alignment, like start-of-week
- `dayCount` - force a fixed number-of-days, even with hiddenDays
- `disableNonCurrentDates` - option to hide day cells for prev/next months
Bugfixes:
- event titles strangely positioned while touch scrolling in Timeline (#223)
v1.5.1 (2017-02-14)
-------------------
- dragging an event that lives on multiple resources should maintain the
non-dragged resource IDs when dropped (#111)
- resources function/feed should receive start/end params (#246)
(when `refetchResourcesOnNavigate` is true)
- iOS 10, unwanted scrolling while dragging events/selection (#230)
- timeline, clicking scrollbars triggers dayClick (#256)
- timeline, external drag element droppable when outside of calendar (#256)
v1.5.0 (2016-12-05)
-------------------
- dynamically refetch resources upon navigation (#12):
- `refetchResourcesOnNavigate`
- only display resources with events (#98):
- `filterResourcesWithEvents`
- `navLinks` support (#218)
- timeline vertical scrolling wrongly resetting (#238)
- missing bottom border on last resource (#162)
- businessHours occupying whole view wouldn't display (#233)
- text-decoration on event elements lost when scrolling (#229)
- fc-today and other day-related classes in timeline header cells
- fix touch scrolling glitchiness regression
- made gulp-based build system consistent with core project
- as with the corresponding core project release, there was an internal refactor
related to timing of rendering and firing handlers. calls to rerender the current
date-range/events/resources from within handlers might not execute immediately.
instead, will execute after handler finishes.
v1.4.0 (2016-09-04)
-------------------
- `eventResourceEditable` for control over events changing resources (#140)
- `eventConstraint` accepts `resourceId` or `resourceIds` (#50)
- `eventAllow`, programmatic control over event dragging (#50)
- `selectAllow`, programmatic control over allowed selection
- adjustments to work with v3 of the core project
v1.3.3 (2016-07-31)
-------------------
- business hours per-resource (#61)
- fix non-business days without styles (#109)
- fix bug with scrollbars causing selection after the first (#192)
- certain rendering actions, such as initial rendering of a resource view,
would not always execute synchronously once jQuery 3 was introduced.
fixes have been made to ensure synchronous execution with jQuery 3.
- integration with TravisCI
v1.3.2 (2016-06-02)
-------------------
- refetchResources and view switching causes blank screen (#179)
- UMD definition for Node, defined core dependency (#172)
- setResources should return an array copy (#160)
- revertFunc misbehaves for events specified with multiple resourceIds (#156)
- nowIndicator sometimes incorrectly positioned on wide screens (#130)
- memory leak upon destroy (#87)
v1.3.1 (2016-05-01)
-------------------
- events offset by minTime in timelineWeek view (#151)
- icons for prev/next not working in MS Edge
v1.3.0 (2016-04-23)
-------------------
touch support introduced in core v2.7.0
v1.2.1 (2016-02-17)
-------------------
- allow minTime/maxTime to be negative or beyond 24hrs in timeline (#112)
- fix for nowIndicator not updating position on window resize (#130)
- fix for events resourceId/resourceIds being non-string integers (#120)
- fix external drag handlers not being unbound (#117, #118)
- fix refetchResources should rerender resources in vertical view (#100)
- fix events incorrectly rendered when clipped by minTime/maxTime (#96)
- fix resourceRender's resource object param when resources above dates (#91)
- fix eventOverlap:false with eventResourceField (#86)
- fix license key warning being rendered multiple times (#75)
- fix broken Resource Object eventClassName property
- fix separate event instances via multiple resourceIds, wrong color assignment
v1.2.0 (2016-01-07)
-------------------
- current time indicator (#9)
- resourceIds, allows associating an event to multiple resources (#13)
- pass resourceId into the drop event (#27)
- fix for refetchEvents reseting the scroll position (#89)
- fix for addResource/removeResource failing to rerender in vertical views (#84)
- fix for timeline resource rows sometimes being misaligned when column grouping (#80)
- fix for timeline events not rendering correctly when minTime specified (#78)
- fix for erradic resource ordering in verical views when no resourceOrder specified (#74)
- fix bug where external event dragging would not respect eventOverlap (#72)
v1.1.0 (2015-11-30)
-------------------
- vertical resource view (#5)
- fix event overlap not working in day/week/month view (#24)
v1.0.2 (2015-10-18)
-------------------
- incorrect rendering of events when using slotDuration equal to one day (#49)
- minimum jQuery is now v1.8.0 (solves #44)
- more tests
v1.0.1 (2015-10-13)
-------------------
- fix event rendering coordinates when timezone (#15)
- fix event rendering in non-expanded non-rendered resource rows (#30)
- fix accidentally caching result of resource fetching (#41)
- fix for dragging between resources when custom eventResourceField (#18)
- fix scroll jumping bug (#25)
- relax bower's ignore (#21)
v1.0.0 (2015-08-17)
-------------------
Issues resolved since v1.0.0-beta:
[2523], [2533], [2534], [2562]
[2523]: https://code.google.com/p/fullcalendar/issues/detail?id=2523
[2533]: https://code.google.com/p/fullcalendar/issues/detail?id=2533
[2534]: https://code.google.com/p/fullcalendar/issues/detail?id=2534
[2562]: https://code.google.com/p/fullcalendar/issues/detail?id=2562
## Reporting Bugs
Each bug report MUST have a [JSFiddle/JSBin] recreation before any work can begin. [further instructions &raquo;](http://fullcalendar.io/wiki/Reporting-Bugs/)
## Requesting Features
Please search the [Issue Tracker] to see if your feature has already been requested, and if so, subscribe to it. Otherwise, read these [further instructions &raquo;](http://fullcalendar.io/wiki/Requesting-Features/)
## Contributing Features
Each new feature should be designed as robustly as possible and be useful beyond the immediate usecase it was initially designed for. Feel free to start a ticket discussing the feature's specs before coding.
## Contributing Bugfixes
In the description of your [Pull Request][Using Pull Requests], please include recreation steps for the bug as well as a [JSFiddle/JSBin] demo. Communicating the buggy behavior is a requirement before a merge can happen.
## Other Ways to Contribute
[Read about other ways to contribute &raquo;](http://fullcalendar.io/wiki/Contributing/)
## What to edit
When modifying files, please do not edit the generated or minified files in the `dist/` directory. Please edit the original `src/` files.
[JSFiddle/JSBin]: http://fullcalendar.io/wiki/Reporting-Bugs/
[Issue Tracker]: https://github.com/fullcalendar/fullcalendar-scheduler/issues
[Using Pull Requests]: https://help.github.com/articles/using-pull-requests/
For complete licensing information, visit:
http://fullcalendar.io/scheduler/license/
FullCalendar Scheduler is tri-licensed, meaning you must choose
one of three licenses to use. Here is a summary of those licenses:
- Commercial License
(a paid license, meant for commercial use)
http://fullcalendar.io/scheduler/license-details/
- Creative Commons Non-Commercial No-Derivatives
(meant for trial and non-commercial use)
https://creativecommons.org/licenses/by-nc-nd/4.0/
- GPLv3 License
(meant for open-source projects)
http://www.gnu.org/licenses/gpl-3.0.en.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true, // enable draggable events
aspectRatio: 1.8,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceLabelText: 'Rooms',
resources: [
{ id: 'a', title: 'Auditorium A' },
{ id: 'b', title: 'Auditorium B', eventColor: 'green' },
{ id: 'c', title: 'Auditorium C', eventColor: 'orange' },
{ id: 'd', title: 'Auditorium D', children: [
{ id: 'd1', title: 'Room D1' },
{ id: 'd2', title: 'Room D2' }
] },
{ id: 'e', title: 'Auditorium E' },
{ id: 'f', title: 'Auditorium F', eventColor: 'red' },
{ id: 'g', title: 'Auditorium G' },
{ id: 'h', title: 'Auditorium H' },
{ id: 'i', title: 'Auditorium I' },
{ id: 'j', title: 'Auditorium J' },
{ id: 'k', title: 'Auditorium K' },
{ id: 'l', title: 'Auditorium L' },
{ id: 'm', title: 'Auditorium M' },
{ id: 'n', title: 'Auditorium N' },
{ id: 'o', title: 'Auditorium O' },
{ id: 'p', title: 'Auditorium P' },
{ id: 'q', title: 'Auditorium Q' },
{ id: 'r', title: 'Auditorium R' },
{ id: 's', title: 'Auditorium S' },
{ id: 't', title: 'Auditorium T' },
{ id: 'u', title: 'Auditorium U' },
{ id: 'v', title: 'Auditorium V' },
{ id: 'w', title: 'Auditorium W' },
{ id: 'x', title: 'Auditorium X' },
{ id: 'y', title: 'Auditorium Y' },
{ id: 'z', title: 'Auditorium Z' }
],
events: [
// background event, associated with a resource
{ id: 'bg1', resourceId: 'b', rendering: 'background', start: '2017-05-07T01:00:00', end: '2017-05-07T04:00:00' },
// background event, NOT associated with a resource
{ id: 'bg2', rendering: 'background', start: '2017-05-07T05:00:00', end: '2017-05-07T08:00:00' },
// normal events...
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
]
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true,
aspectRatio: 1.8,
scrollTime: '00:00',
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceAreaWidth: '40%',
resourceColumns: [
{
group: true,
labelText: 'Building',
field: 'building'
},
{
labelText: 'Room',
field: 'title'
},
{
labelText: 'Occupancy',
field: 'occupancy'
}
],
resources: [
{ id: 'a', building: '460 Bryant', title: 'Auditorium A', occupancy: 40 },
{ id: 'b', building: '460 Bryant', title: 'Auditorium B', occupancy: 40, eventColor: 'green' },
{ id: 'c', building: '460 Bryant', title: 'Auditorium C', occupancy: 40, eventColor: 'orange' },
{ id: 'd', building: '460 Bryant', title: 'Auditorium D', occupancy: 40, children: [
{ id: 'd1', title: 'Room D1', occupancy: 10 },
{ id: 'd2', title: 'Room D2', occupancy: 10 }
] },
{ id: 'e', building: '460 Bryant', title: 'Auditorium E', occupancy: 40 },
{ id: 'f', building: '460 Bryant', title: 'Auditorium F', occupancy: 40, eventColor: 'red' },
{ id: 'g', building: '564 Pacific', title: 'Auditorium G', occupancy: 40 },
{ id: 'h', building: '564 Pacific', title: 'Auditorium H', occupancy: 40 },
{ id: 'i', building: '564 Pacific', title: 'Auditorium I', occupancy: 40 },
{ id: 'j', building: '564 Pacific', title: 'Auditorium J', occupancy: 40 },
{ id: 'k', building: '564 Pacific', title: 'Auditorium K', occupancy: 40 },
{ id: 'l', building: '564 Pacific', title: 'Auditorium L', occupancy: 40 },
{ id: 'm', building: '564 Pacific', title: 'Auditorium M', occupancy: 40 },
{ id: 'n', building: '564 Pacific', title: 'Auditorium N', occupancy: 40 },
{ id: 'o', building: '564 Pacific', title: 'Auditorium O', occupancy: 40 },
{ id: 'p', building: '564 Pacific', title: 'Auditorium P', occupancy: 40 },
{ id: 'q', building: '564 Pacific', title: 'Auditorium Q', occupancy: 40 },
{ id: 'r', building: '564 Pacific', title: 'Auditorium R', occupancy: 40 },
{ id: 's', building: '564 Pacific', title: 'Auditorium S', occupancy: 40 },
{ id: 't', building: '564 Pacific', title: 'Auditorium T', occupancy: 40 },
{ id: 'u', building: '564 Pacific', title: 'Auditorium U', occupancy: 40 },
{ id: 'v', building: '564 Pacific', title: 'Auditorium V', occupancy: 40 },
{ id: 'w', building: '564 Pacific', title: 'Auditorium W', occupancy: 40 },
{ id: 'x', building: '564 Pacific', title: 'Auditorium X', occupancy: 40 },
{ id: 'y', building: '564 Pacific', title: 'Auditorium Y', occupancy: 40 },
{ id: 'z', building: '564 Pacific', title: 'Auditorium Z', occupancy: 40 }
],
events: [
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
]
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true,
aspectRatio: 1.8,
scrollTime: '00:00',
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceAreaWidth: '30%',
resourceColumns: [
{
labelText: 'Room',
field: 'title'
},
{
labelText: 'Occupancy',
field: 'occupancy'
}
],
resources: [
{ id: 'a', title: 'Auditorium A', occupancy: 40 },
{ id: 'b', title: 'Auditorium B', occupancy: 40, eventColor: 'green' },
{ id: 'c', title: 'Auditorium C', occupancy: 40, eventColor: 'orange' },
{ id: 'd', title: 'Auditorium D', occupancy: 40, children: [
{ id: 'd1', title: 'Room D1', occupancy: 10 },
{ id: 'd2', title: 'Room D2', occupancy: 10 }
] },
{ id: 'e', title: 'Auditorium E', occupancy: 40 },
{ id: 'f', title: 'Auditorium F', occupancy: 40, eventColor: 'red' },
{ id: 'g', title: 'Auditorium G', occupancy: 40 },
{ id: 'h', title: 'Auditorium H', occupancy: 40 },
{ id: 'i', title: 'Auditorium I', occupancy: 40 },
{ id: 'j', title: 'Auditorium J', occupancy: 40 },
{ id: 'k', title: 'Auditorium K', occupancy: 40 },
{ id: 'l', title: 'Auditorium L', occupancy: 40 },
{ id: 'm', title: 'Auditorium M', occupancy: 40 },
{ id: 'n', title: 'Auditorium N', occupancy: 40 },
{ id: 'o', title: 'Auditorium O', occupancy: 40 },
{ id: 'p', title: 'Auditorium P', occupancy: 40 },
{ id: 'q', title: 'Auditorium Q', occupancy: 40 },
{ id: 'r', title: 'Auditorium R', occupancy: 40 },
{ id: 's', title: 'Auditorium S', occupancy: 40 },
{ id: 't', title: 'Auditorium T', occupancy: 40 },
{ id: 'u', title: 'Auditorium U', occupancy: 40 },
{ id: 'v', title: 'Auditorium V', occupancy: 40 },
{ id: 'w', title: 'Auditorium W', occupancy: 40 },
{ id: 'x', title: 'Auditorium X', occupancy: 40 },
{ id: 'y', title: 'Auditorium Y', occupancy: 40 },
{ id: 'z', title: 'Auditorium Z', occupancy: 40 }
],
events: [
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
]
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true,
aspectRatio: 1.8,
scrollTime: '00:00',
header: {
left: 'promptResource today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
customButtons: {
promptResource: {
text: '+ room',
click: function() {
var title = prompt('Room name');
if (title) {
$('#calendar').fullCalendar(
'addResource',
{ title: title },
true // scroll to the new resource?
);
}
}
}
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceLabelText: 'Rooms',
resourceRender: function(resource, cellEls) {
cellEls.on('click', function() {
if (confirm('Are you sure you want to delete ' + resource.title + '?')) {
$('#calendar').fullCalendar('removeResource', resource);
}
});
},
resources: [
{ id: 'a', title: 'Auditorium A' },
{ id: 'b', title: 'Auditorium B', eventColor: 'green' },
{ id: 'c', title: 'Auditorium C', eventColor: 'orange' },
{ id: 'd', title: 'Auditorium D', children: [
{ id: 'd1', title: 'Room D1' },
{ id: 'd2', title: 'Room D2' }
] },
{ id: 'e', title: 'Auditorium E' },
{ id: 'f', title: 'Auditorium F', eventColor: 'red' },
{ id: 'g', title: 'Auditorium G' },
{ id: 'h', title: 'Auditorium H' },
{ id: 'i', title: 'Auditorium I' },
{ id: 'j', title: 'Auditorium J' },
{ id: 'k', title: 'Auditorium K' },
{ id: 'l', title: 'Auditorium L' },
{ id: 'm', title: 'Auditorium M' },
{ id: 'n', title: 'Auditorium N' },
{ id: 'o', title: 'Auditorium O' },
{ id: 'p', title: 'Auditorium P' },
{ id: 'q', title: 'Auditorium Q' },
{ id: 'r', title: 'Auditorium R' },
{ id: 's', title: 'Auditorium S' },
{ id: 't', title: 'Auditorium T' },
{ id: 'u', title: 'Auditorium U' },
{ id: 'v', title: 'Auditorium V' },
{ id: 'w', title: 'Auditorium W' },
{ id: 'x', title: 'Auditorium X' },
{ id: 'y', title: 'Auditorium Y' },
{ id: 'z', title: 'Auditorium Z' }
],
events: [
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
]
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
p {
text-align: center;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
.fc-resource-area td {
cursor: pointer;
}
</style>
</head>
<body>
<p>
HINT: click on a resource to delete it.
</p>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/jquery-ui.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
/* initialize the external events
-----------------------------------------------------------------*/
$('#external-events .fc-event').each(function() {
// store data so the calendar knows to render an event upon drop
$(this).data('event', {
title: $.trim($(this).text()), // use the element's text as the event title
stick: true // maintain when user navigates (see docs on the renderEvent method)
});
// make the event draggable using jQuery UI
$(this).draggable({
zIndex: 999,
revert: true, // will cause the event to go back to its
revertDuration: 0 // original position after the drag
});
});
/* initialize the calendar
-----------------------------------------------------------------*/
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true, // enable draggable events
droppable: true, // this allows things to be dropped onto the calendar
aspectRatio: 1.8,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceLabelText: 'Rooms',
resources: [
{ id: 'a', title: 'Auditorium A' },
{ id: 'b', title: 'Auditorium B', eventColor: 'green' },
{ id: 'c', title: 'Auditorium C', eventColor: 'orange' },
{ id: 'd', title: 'Auditorium D', children: [
{ id: 'd1', title: 'Room D1' },
{ id: 'd2', title: 'Room D2' }
] },
{ id: 'e', title: 'Auditorium E' },
{ id: 'f', title: 'Auditorium F', eventColor: 'red' },
{ id: 'g', title: 'Auditorium G' },
{ id: 'h', title: 'Auditorium H' },
{ id: 'i', title: 'Auditorium I' },
{ id: 'j', title: 'Auditorium J' },
{ id: 'k', title: 'Auditorium K' },
{ id: 'l', title: 'Auditorium L' },
{ id: 'm', title: 'Auditorium M' },
{ id: 'n', title: 'Auditorium N' },
{ id: 'o', title: 'Auditorium O' },
{ id: 'p', title: 'Auditorium P' },
{ id: 'q', title: 'Auditorium Q' },
{ id: 'r', title: 'Auditorium R' },
{ id: 's', title: 'Auditorium S' },
{ id: 't', title: 'Auditorium T' },
{ id: 'u', title: 'Auditorium U' },
{ id: 'v', title: 'Auditorium V' },
{ id: 'w', title: 'Auditorium W' },
{ id: 'x', title: 'Auditorium X' },
{ id: 'y', title: 'Auditorium Y' },
{ id: 'z', title: 'Auditorium Z' }
],
events: [
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
],
drop: function(date, jsEvent, ui, resourceId) {
console.log('drop', date.format(), resourceId);
// is the "remove after drop" checkbox checked?
if ($('#drop-remove').is(':checked')) {
// if so, remove the element from the "Draggable Events" list
$(this).remove();
}
},
eventReceive: function(event) { // called when a proper external event is dropped
console.log('eventReceive', event);
},
eventDrop: function(event) { // called when an event (already on the calendar) is moved
console.log('eventDrop', event);
}
});
});
</script>
<style>
body {
margin-top: 40px;
text-align: center;
font-size: 14px;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
}
#wrap {
width: 1100px;
margin: 0 auto;
}
#external-events {
float: left;
width: 150px;
padding: 0 10px;
border: 1px solid #ccc;
background: #eee;
text-align: left;
}
#external-events h4 {
font-size: 16px;
margin-top: 0;
padding-top: 1em;
}
#external-events .fc-event {
margin: 10px 0;
cursor: pointer;
}
#external-events p {
margin: 1.5em 0;
font-size: 11px;
color: #666;
}
#external-events p input {
margin: 0;
vertical-align: middle;
}
#calendar {
float: right;
width: 900px;
}
</style>
</head>
<body>
<div id='wrap'>
<div id='external-events'>
<h4>Draggable Events</h4>
<div class='fc-event'>My Event 1</div>
<div class='fc-event'>My Event 2</div>
<div class='fc-event'>My Event 3</div>
<div class='fc-event'>My Event 4</div>
<div class='fc-event'>My Event 5</div>
<p>
<input type='checkbox' id='drop-remove' />
<label for='drop-remove'>remove after drop</label>
</p>
</div>
<div id='calendar'></div>
<div style='clear:both'></div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../lib/gcal.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true, // enable draggable events
aspectRatio: 1.8,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineMonth,timelineYear'
},
defaultView: 'timelineMonth',
/*
NOTE: unfortunately, Scheduler doesn't know how to associated events from
Google Calendar with resources, so if you specify a resource list,
nothing will show up :( Working on some solutions.
*/
// THIS KEY WON'T WORK IN PRODUCTION!!!
// To make your own Google API key, follow the directions here:
// http://fullcalendar.io/docs/google_calendar/
googleCalendarApiKey: 'AIzaSyDcnW6WejpTOCffshGDDb4neIrXVUA1EAE',
// US Holidays
events: 'usa__en@holiday.calendar.google.com',
eventClick: function(event) {
// opens events in a popup window
window.open(event.url, 'gcalevent', 'width=700,height=600');
return false;
}
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link href='../lib/fullcalendar.min.css' rel='stylesheet' />
<link href='../lib/fullcalendar.print.min.css' rel='stylesheet' media='print' />
<link href='../scheduler.min.css' rel='stylesheet' />
<script src='../lib/moment.min.js'></script>
<script src='../lib/jquery.min.js'></script>
<script src='../lib/fullcalendar.min.js'></script>
<script src='../scheduler.min.js'></script>
<script>
$(function() { // document ready
$('#calendar').fullCalendar({
now: '2017-05-07',
editable: true,
aspectRatio: 1.8,
scrollTime: '00:00',
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineDay,timelineThreeDays,agendaWeek,month'
},
defaultView: 'timelineDay',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 3 }
}
},
resourceGroupField: 'building',
resources: [
{ id: 'a', building: '460 Bryant', title: 'Auditorium A' },
{ id: 'b', building: '460 Bryant', title: 'Auditorium B', eventColor: 'green' },
{ id: 'c', building: '460 Bryant', title: 'Auditorium C', eventColor: 'orange' },
{ id: 'd', building: '460 Bryant', title: 'Auditorium D', children: [
{ id: 'd1', title: 'Room D1', occupancy: 10 },
{ id: 'd2', title: 'Room D2', occupancy: 10 }
] },
{ id: 'e', building: '460 Bryant', title: 'Auditorium E' },
{ id: 'f', building: '460 Bryant', title: 'Auditorium F', eventColor: 'red' },
{ id: 'g', building: '564 Pacific', title: 'Auditorium G' },
{ id: 'h', building: '564 Pacific', title: 'Auditorium H' },
{ id: 'i', building: '564 Pacific', title: 'Auditorium I' },
{ id: 'j', building: '564 Pacific', title: 'Auditorium J' },
{ id: 'k', building: '564 Pacific', title: 'Auditorium K' },
{ id: 'l', building: '564 Pacific', title: 'Auditorium L' },
{ id: 'm', building: '564 Pacific', title: 'Auditorium M' },
{ id: 'n', building: '564 Pacific', title: 'Auditorium N' },
{ id: 'o', building: '564 Pacific', title: 'Auditorium O' },
{ id: 'p', building: '564 Pacific', title: 'Auditorium P' },
{ id: 'q', building: '564 Pacific', title: 'Auditorium Q' },
{ id: 'r', building: '564 Pacific', title: 'Auditorium R' },
{ id: 's', building: '564 Pacific', title: 'Auditorium S' },
{ id: 't', building: '564 Pacific', title: 'Auditorium T' },
{ id: 'u', building: '564 Pacific', title: 'Auditorium U' },
{ id: 'v', building: '564 Pacific', title: 'Auditorium V' },
{ id: 'w', building: '564 Pacific', title: 'Auditorium W' },
{ id: 'x', building: '564 Pacific', title: 'Auditorium X' },
{ id: 'y', building: '564 Pacific', title: 'Auditorium Y' },
{ id: 'z', building: '564 Pacific', title: 'Auditorium Z' }
],
events: [
{ id: '1', resourceId: 'b', start: '2017-05-07T02:00:00', end: '2017-05-07T07:00:00', title: 'event 1' },
{ id: '2', resourceId: 'c', start: '2017-05-07T05:00:00', end: '2017-05-07T22:00:00', title: 'event 2' },
{ id: '3', resourceId: 'd', start: '2017-05-06', end: '2017-05-08', title: 'event 3' },
{ id: '4', resourceId: 'e', start: '2017-05-07T03:00:00', end: '2017-05-07T08:00:00', title: 'event 4' },
{ id: '5', resourceId: 'f', start: '2017-05-07T00:30:00', end: '2017-05-07T02:30:00', title: 'event 5' }
]
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 50px auto;
}
</style>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment