Working on calendar layout

This commit is contained in:
Justin Lewis Salmon 2013-06-01 15:24:46 +01:00
parent 8200d40cf1
commit 19210b734c
8 changed files with 325 additions and 141 deletions

BIN
app.db

Binary file not shown.

View file

@ -0,0 +1,32 @@
from sqlalchemy import *
from migrate import *
from migrate.changeset import schema
pre_meta = MetaData()
post_meta = MetaData()
task = Table('task', post_meta,
Column('id', Integer, primary_key=True, nullable=False),
Column('name', String(length=256)),
Column('start_date', Date),
Column('end_date', Date),
Column('info', String(length=256)),
Column('project_id', Integer),
Column('owner_id', Integer),
Column('parent_task', Integer),
)
def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine; bind
# migrate_engine to your metadata
pre_meta.bind = migrate_engine
post_meta.bind = migrate_engine
post_meta.tables['task'].columns['info'].create()
def downgrade(migrate_engine):
# Operations to reverse the above upgrade go here.
pre_meta.bind = migrate_engine
post_meta.bind = migrate_engine
post_meta.tables['task'].columns['info'].drop()

View file

@ -0,0 +1,17 @@
{% from "macros.html" import alert, field, input, date %}
<form class="form-vertical" action="/create-task" method="post" id="create-task">
<fieldset>
<h3>Create Task</h3>
{{ field(form.name, hide_label=True, placeholder=True) }}
{{ field(form.info, hide_label=True, placeholder=True) }}
{{ date(form.start_date, hide_label=True, placeholder=True) }}
{{ date(form.end_date, hide_label=True, placeholder=True) }}
{{ field(form.team, input_classes='typeahead', hide_label=True, placeholder=True, autocomplete='off') }}
{{ form.hidden_tag() }}
<button type="submit" class="btn btn-primary">Submit</button>
<a href="#" class="btn">Cancel</a>
</fieldset>
</form>

View file

@ -1,5 +1,4 @@
{% extends "base/base_with_nav_responsive.html" %} {% extends "base/base_with_nav_responsive.html" %}
{% from "macros.html" import alert, field, input, date %}
{% block css %} {% block css %}
<style type="text/css"> <style type="text/css">
@ -58,21 +57,21 @@
<div class="navbar tabbable"> <div class="navbar tabbable">
<div class="navbar-inner navbar-justified"> <div class="navbar-inner navbar-justified">
<!-- .btn-navbar is used as the toggle for collapsed navbar content --> <!-- .btn-navbar is used as the toggle for collapsed navbar content -->
<a class="btn btn-navbar" data-toggle="collapse" data-target="#justified-nav-collapse"> <a class="btn btn-navbar" data-toggle="collapse" data-target="#justified-nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</a> </a>
<div id="justified-nav-collapse" class="nav-collapse collapse"> <div id="justified-nav-collapse" class="nav-collapse collapse">
<ul id="justified-nav" class="nav"> <ul id="justified-nav" class="nav">
<li class="active"><a href="#overview" data-toggle="tab">Overview</a></li> <li class="active"><a href="#overview" data-toggle="tab">Overview</a></li>
<li><a href="#todo" data-toggle="tab">To-Do</a></li> <li><a href="#todo" data-toggle="tab">To-Do</a></li>
<li><a href="#team" data-toggle="tab">Team Members</a></li> <li><a href="#team" data-toggle="tab">Team Members</a></li>
<li><a href="#settings" data-toggle="tab">Settings</a></li> <li><a href="#settings" data-toggle="tab">Settings</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<!-- /.navbar --> <!-- /.navbar -->
@ -88,57 +87,102 @@
</div> </div>
</div> </div>
{# <div class="span8">#}
{# <form class="form-horizontal" action="" method="post" name="create-task">#}
{# <fieldset>#}
{# <legend>Create Task</legend>#}
{##}
{# {{ field(form.name) }}#}
{# {{ field(form.info) }}#}
{# {{ date(form.start_date) }}#}
{# {{ date(form.end_date) }}#}
{# {{ field(form.team) }}#}
{##}
{# <div class="form-actions">#}
{# {{ form.hidden_tag() }}#}
{# <button type="submit" class="btn btn-primary">Submit</button>#}
{# <a href="#" class="btn">Cancel</a>#}
{# </div>#}
{# </fieldset>#}
{# </form>#}
{# </div>#}
{% endblock %} {% endblock %}
{% block content_side %} {% block content_side %}
<div class="thumbnail"> <div class="thumbnail">
<div class="caption"> <div class="caption">
{% include "forms/create_task.html" %}
</div>
</div>
<div class="clearfix"></div>
<div class="thumbnail">
<div class="caption" id="task-overview">
<h3>Thumbnail label</h3> <h3>Thumbnail label</h3>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus.
Nullam id dolor id nibh ultricies vehicula ut id elit.</p> Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
<p><a href="#" class="btn btn-primary">Action</a> <a href="#" class="btn">Action</a></p> <p><a href="#" rel="popover" data-original-title="A Title" class="btn btn-primary">Action</a> <a href="#"
class="btn">Action</a>
</p>
<div id="popover_content_wrapper" style="display: none">This is your div content</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
// Tabs
$(function () { $(function () {
var baseURL = '/'; var baseURL = '/';
//load content for first tab and initialize //load content for first tab and initialize
$('#overview').load(baseURL + 'overview', function () { $('#overview').load(baseURL + 'overview', function () {
$('#justified-nav').tab(); //initialize tabs //initialize tabs
$('#justified-nav').tab();
}); });
$('#justified-nav').bind('show', function (e) { $('#justified-nav').bind('show', function (e) {
var pattern = /#.+/gi //use regex to get anchor(==selector) //use regex to get anchor(==selector)
var contentID = e.target.toString().match(pattern)[0]; //get anchor var pattern = /#.+/gi
//get anchor
var contentID = e.target.toString().match(pattern)[0];
//load content for selected tab //load content for selected tab
$(contentID).load(baseURL + contentID.replace('#', ''), function () { $(contentID).load(baseURL + contentID.replace('#', ''), function () {
$('#justified-nav').tab(); //reinitialize tabs //reinitialize tabs
$('#justified-nav').tab();
}); });
}); });
}); });
// Datepickers
$('.datepicker').datepicker();
$('.popover').popover({
html: true,
trigger: 'hover',
content: function () {
return $('#popover_content_wrapper').html();
}
});
// Task creation form
{# $('#create-task').bind('submit', function (e) {#}
{# e.preventDefault();#}
{##}
{# $.post('/create-task', {#}
{# $('#create-task').serialize()#}
{# }).success(function (data) {#}
{# alert('yay');#}
{# console.log($(".datepicker"));#}
{# console.log(data);#}
{# $('#create-task').html(data).find(".datepicker").datepicker();#}
{##}
{# }).error(function (data) {#}
{# alert('aww');#}
{# $('#create-task').html(data).find(".datepicker").datepicker();#}
{##}
{# });#}
{##}
{# $.ajax({#}
{# type: "POST",#}
{# url: "{{ url_for('create_task') }}",#}
{# data: $('#create-task').serialize(),#}
{# success: function (result) {#}
{# console.log(result);#}
{# $('#create-task').html(result).find(".datepicker").datepicker();#}
{# #}
{# }#}
{# });#}
{##}
{# });#}
</script> </script>
{% endblock %} {% endblock %}

View file

@ -1,98 +1,108 @@
{% macro alert(content, type=None, alert_header=None, close_button=True) -%} {% macro alert(content, type=None, alert_header=None, close_button=True) -%}
{# type can be success, error (or danger), info. Defaults to a warning style. #} {# type can be success, error (or danger), info. Defaults to a warning style. #}
<div class="alert <div class="alert
{%- if alert_header %} alert-block{% endif -%} {%- if alert_header %} alert-block{% endif -%}
{%- if type %} alert-{{ type }}{% endif -%} {%- if type %} alert-{{ type }}{% endif -%}
{%- if close_button %} fade in{% endif %}"> {%- if close_button %} fade in{% endif %}">
{% if close_button -%} {% if close_button -%}
<a class="close" data-dismiss="alert">&times;</a> <a class="close" data-dismiss="alert">&times;</a>
{%- endif %} {%- endif %}
{% if alert_header -%} {% if alert_header -%}
<h4 class="alert-heading">{{ alert_header|safe }}</h4> <h4 class="alert-heading">{{ alert_header|safe }}</h4>
{%- endif %} {%- endif %}
{{ content|safe }} {{ content|safe }}
</div> </div>
{%- endmacro %} {%- endmacro %}
{% macro label(content, type='warning') -%} {% macro label(content, type='warning') -%}
<span class="label label-{{ type }}">{{ content|safe }}</span> <span class="label label-{{ type }}">{{ content|safe }}</span>
{%- endmacro %} {%- endmacro %}
{% macro non_field_errors(form) %} {% macro non_field_errors(form) %}
{% if form.non_field_errors %} {% if form.non_field_errors %}
{% for error in form.non_field_errors() %} {% for error in form.non_field_errors() %}
{{ alert(content=error, type='error', close_button=False) }} {{ alert(content=error, type='error', close_button=False) }}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% macro label(field) -%} {% macro label(field) -%}
<label class="control-label"{% if field.auto_id %} for="{{ field.auto_id|safe }}"{% endif %}>{{ field.label|safe }}</label> <label class="control-label"{% if field.auto_id %}
for="{{ field.auto_id|safe }}"{% endif %}>{{ field.label|safe }}</label>
{%- endmacro %} {%- endmacro %}
{% macro input(field, prepend_content=None, append_content=None) -%} {% macro input(field, input_classes=None, prepend_content=None, append_content=None, placeholder=None, autocomplete='on') -%}
{# Helper macro for rendering the input itself #} {# Helper macro for rendering the input itself #}
{% if prepend_content %} {% if prepend_content %}
<div class="input-prepend"> <div class="input-prepend">
{% elif append_content %} {% elif append_content %}
<div class="input-append"> <div class="input-append">
{% endif %} {% endif %}
{%- if prepend_content -%} {%- if prepend_content -%}
<span class="add-on">{{ prepend_content }}</span> <span class="add-on">{{ prepend_content }}</span>
{%- endif -%} {%- endif -%}
{% if placeholder %}
{{ field(placeholder=placeholder, class=input_classes, autocomplete=autocomplete) }}
{% else %}
{{ field|safe }}
{% endif %}
{%- if append_content -%}
<span class="add-on">{{ append_content }}</span>
{%- endif -%}
{% if field.errors %}
<span class="help-inline">{{ field.errors|join(' ')|safe }}</span>
{% endif %}
{% if append_content or prepend_content %}
</div>
{% endif %}
{%- endmacro %}
{% macro field(field, classes=None, input_classes=None, prepend_content=None, append_content=None, hide_label=False, inline=False, placeholder=False, autocomplete='on') -%}
{% if field.is_hidden %}
{{ field|safe }} {{ field|safe }}
{% else %}
<div class="control-group{% if field.errors %} error{% endif -%}{%- if classes %} {{ classes }}{% endif %}">
{% if not hide_label %}
{{ label(field) }}
{% endif %}
<div class="controls">
{%- if append_content -%} {{ input(field, input_classes, prepend_content, append_content, placeholder, autocomplete) }}
<span class="add-on">{{ append_content }}</span>
{%- endif -%}
{% if field.errors %} {% if field.description %}
<span class="help-inline">{{ field.errors|join(' ')|safe }}</span> <p class="help-block">{{ field.description|safe }}</p>
{% endif %} {% endif %}
</div>
{% if append_content or prepend_content %}
</div> </div>
{% endif %} {% endif %}
{%- endmacro %} {%- endmacro %}
{% macro field(field, classes=None, prepend_content=None, append_content=None, hide_label=False, inline=False) -%} {% macro date(field, hide_label=False, placeholder=False) %}
{% if field.is_hidden %}
{{ field|safe }}
{% else %}
<div class="control-group{% if field.errors %} error{% endif -%}{%- if classes %} {{ classes }}{% endif %}">
{% if not hide_label %}
{{ label(field) }}
{% endif %}
<div class="controls">
{{ input(field, prepend_content, append_content) }}
{% if field.description %}
<p class="help-block">{{ field.description|safe }}</p>
{% endif %}
</div>
</div>
{% endif %}
{%- endmacro %}
{% macro date(field) %}
<div class="control-group{% if field.errors %} error{% endif -%}{%- if classes %} {{ classes }}{% endif %}"> <div class="control-group{% if field.errors %} error{% endif -%}{%- if classes %} {{ classes }}{% endif %}">
{{ label(field) }} {% if not hide_label %}
{{ label(field) }}
{% endif %}
<div class="controls"> <div class="controls">
<div class="input-append date datepicker" <div class="input-append date datepicker" data-date-format="dd-mm-yyyy">
data-date="12-02-2012" {% if placeholder %}
data-date-format="dd-mm-yyyy"> {{ field(placeholder=field.name) }}
<input class="span" size="16" type="date" value="12-02-2012" id="{{ field.id }}" {% else %}
name="{{ field.id }}"> {{ field }}
{% endif %}
<span class="add-on"><i class="icon-th"></i></span> <span class="add-on"><i class="icon-th"></i></span>
</div> </div>
{% if field.errors %} {% if field.errors %}
<span class="help-inline">{{ field.errors|join(' ')|safe }}</span> <span class="help-inline">{{ field.errors|join(' ')|safe }}</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endmacro %} {% endmacro %}

View file

@ -14,8 +14,60 @@
center: 'title', center: 'title',
right: 'month,agendaWeek,agendaDay' right: 'month,agendaWeek,agendaDay'
}, },
eventSources: [
{
url: '/events',
type: 'POST',
data: {
custom_param1: 'something',
custom_param2: 'somethingelse'
},
error: function (e) {
alert('there was an error while fetching events!');
console.log(e)
},
success: function (data) {
console.log(data)
}
//className: 'popover'
//color: '#eee', // a non-ajax option
//textColor: 'black' // a non-ajax option
},
// any other sources...
'https://www.google.com/calendar/feeds/mccrustin%40gmail.com/public/basic'
],
eventClick: function (task) {
// opens events in a popup window
{# window.open(event.url, 'gcalevent', 'width=700,height=600');#}
$.ajax({
type: "POST",
url: '/task-overview',
data: task,
success: function (data) {
alert('lol')
$('#task-overview').html(data);
$('.popover').popover({
html: true,
trigger: 'hover',
content: function () {
return $('#popover_content_wrapper').html();
}
});
}
});
return false;
},
editable: true,
selectable: true, selectable: true,
selectHelper: true, selectHelper: true,
select: function (start, end, allDay) { select: function (start, end, allDay) {
var title = prompt('Event Title:'); var title = prompt('Event Title:');
if (title) { if (title) {
@ -31,13 +83,6 @@
} }
calendar.fullCalendar('unselect'); calendar.fullCalendar('unselect');
}, },
editable: true,
eventClick: function (event) {
// opens events in a popup window
window.open(event.url, 'gcalevent', 'width=700,height=600');
return false;
},
loading: function (bool) { loading: function (bool) {
if (bool) { if (bool) {
@ -45,34 +90,7 @@
} else { } else {
$('#loading').hide(); $('#loading').hide();
} }
}, }
eventSources: [
// your event source
{
url: '/events',
type: 'POST',
data: {
custom_param1: 'something',
custom_param2: 'somethingelse'
},
error: function () {
alert('there was an error while fetching events!');
},
success: function (data) {
console.log(data)
}
//color: '#eee', // a non-ajax option
//textColor: 'black' // a non-ajax option
},
// any other sources...
// 'http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic'
]
}); });
}); });

View file

@ -0,0 +1,9 @@
<h3>{{ task.title }}</h3>
<p>{{ task.info }}</p>
<p><a href="#" rel="popover" data-original-title="A Title" class="btn btn-primary">Action</a> <a href="#"
class="btn">Action</a>
</p>
<div id="popover_content_wrapper" style="display: none">This is your div content</div>

View file

@ -1,9 +1,10 @@
from flask import render_template, flash, redirect, session, url_for, g, request, jsonify from datetime import datetime
from flask import render_template, flash, redirect, session, url_for, g, request, jsonify, json
from flask.ext.login import login_user, current_user, login_required, logout_user from flask.ext.login import login_user, current_user, login_required, logout_user
from megaproject import app, lm, oid, db from megaproject import app, lm, oid, db
from forms import LoginForm, CreateProjectForm, CreateTaskForm from forms import LoginForm, CreateProjectForm, CreateTaskForm
from models import User, ROLE_USER, Project from models import User, ROLE_USER, Project, Task
@app.route('/') @app.route('/')
@ -98,16 +99,57 @@ def create_project():
return render_template('create_project.html', title='Create Project', user=user, form=form) return render_template('create_project.html', title='Create Project', user=user, form=form)
@app.route('/create-task', methods=['GET', 'POST']) @app.route('/create-task', methods=['POST'])
@login_required @login_required
def create_task(): def create_task():
user = g.user user = g.user
form = CreateTaskForm()
print 'form:', request.form
if form.validate_on_submit():
print 'ok'
# Check the task doesn't already exist
task = Task(name=form.name.data,
start_date=form.start_date.data,
end_date=form.end_date.data,
info=form.info.data)
db.session.add(task)
db.session.commit()
flash('created task %s' % form.name.data)
return redirect(url_for('index'))
return render_template('index.html', title='Index', user=user, form=form)
@app.route('/overview', methods=['GET', 'POST']) @app.route('/overview', methods=['GET', 'POST'])
@login_required @login_required
def overview(): def overview():
return render_template('views/selectable.html') return render_template('views/calendar.html')
@app.route('/events', methods=['GET', 'POST'])
@login_required
def events():
event1 = {'title': 'Task 1', 'start': '2013-05-05 12:30:00',
'end': '%s' % datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'color': '#ddd'}
event2 = {'title': 'Task 2', 'start': '2013-05-06 12:30:00',
'end': '%s' % datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'color': '#eee'}
events = [event1, event2]
for event in Task.query.all():
ev = event.__dict__
del ev['_sa_instance_state']
ev['title'] = ev['name']
ev['start'] = ev['start_date'].strftime('%Y-%m-%d')
del ev['start_date']
ev['end'] = ev['end_date'].strftime('%Y-%m-%d')
del ev['end_date']
events.append(ev)
print 'events: ', events
return json.dumps(events)
@app.route('/todo', methods=['GET', 'POST']) @app.route('/todo', methods=['GET', 'POST'])
@ -131,6 +173,18 @@ def settings():
@app.route('/typeahead', methods=['GET', 'POST']) @app.route('/typeahead', methods=['GET', 'POST'])
@login_required @login_required
def typeahead(): def typeahead():
response = jsonify(options=['red', 'blue', 'green', 'yellow']) # users = User.query.all()
# usernames = list()
#
# for user in users:
# usernames.append()
response = jsonify(options=[user.nickname for user in User.query.all()])
return response return response
@app.route('/task-overview', methods=['GET', 'POST'])
@login_required
def task_overview():
print 'lol ', request.form
return render_template('views/task_overview.html', task=request.form)