Tasks Index for a Project

Work in progress
This commit is contained in:
2016-09-21 14:02:05 +02:00
parent 5095367c2e
commit 38703bf583
12 changed files with 1330 additions and 46 deletions

View File

@@ -1,30 +1,42 @@
function save_task(task_id, task_url) {
console.log('Saving task to', task_url);
var $form = $('.task form');
var $form = $('#task-view form');
var $button = $form.find("button[type='submit']");
var payload = $form.serialize();
var task = '#task-' + task_id;
$button.attr('disabled', true);
$(task).addClass('processing');
$('#status-bar').text('Saving task...');
if (console) console.log('Sending:', payload);
$.post(task_url, payload)
.done(function(data) {
if (console) console.log('Done saving', data);
// Update the task list.
// NOTE: this is tightly linked to the HTML of the task list in for_project.jade.
$('#task-' + task_id).text($form.find("input[name='name']").val());
$(task + ' span.name').text($form.find("input[name='name']").val());
$(task + ' span.status').text($form.find("select[name='status']").val());
$(task + ' span.status-indicator')
.removeAttr('class')
.addClass('status-indicator ' + $form.find("select[name='status']").val());
$('#status-bar').text('Saved task. ' + data.time);
})
.fail(function(xhr_or_response_data) {
// jQuery sends the response data (if JSON), or an XHR object (if not JSON).
if (console) console.log('Failed saving', xhr_or_response_data);
$('#task-details').html(xhr_or_response_data.responseText);
$('#status-bar').text('Failed saving. ' + xhr_or_response_data);
})
.always(function() {
$button.attr('disabled', false);
$(task).removeClass('processing');
})
;
;
return false; // prevent synchronous POST to current page.
}

View File

@@ -60,7 +60,7 @@ def save(project, task_id):
task = current_task_manager.edit_task(task_id, **request.form.to_dict())
return flask.jsonify({'task_id': task_id, 'etag': task._etag})
return flask.jsonify({'task_id': task_id, 'etag': task._etag, 'time': task._updated })
# TODO: remove GET method once Pablo has made a proper button to call this URL with a POST.
@@ -75,4 +75,5 @@ def create_task(project, task_type=None):
project_url=project['url'],
task_id=task['_id'])
resp.status_code = 201
return resp
return flask.make_response(flask.jsonify({'task_id': task['_id']}), 201)

97
src/styles/_base.sass Normal file
View File

@@ -0,0 +1,97 @@
body
background-color: $color-background
position: relative
height: 100%
width: 100%
margin: 0
padding: 0
font-family: 'Roboto'
a
color: $color-primary
&:hover
color: $color-primary-dark
#app-main
display: flex
align-items: stretch
height: 100%
nav.sidebar
position: fixed
left: 0
width: $sidebar-width
height: 100%
background-color: $color-background-nav
dl
dd
margin: 0
a
display: flex
color: $color-text-light-hint
font-size: 1.5em
align-items: center
justify-content: center
padding: 10px 0
&:hover
color: $color-text-light-primary
#col_main
display: flex
flex-direction: column
position: relative
width: 50%
height: 100%
flex: 1
margin-left: $sidebar-width
box-shadow: 1px 0 0 $color-background-dark
#col_right
display: flex
flex-direction: column
position: relative
background-color: $color-background
width: 50%
height: 100%
flex: 1
.col_header
position: relative
display: flex
align-items: center
width: 100%
background-color: white
padding: 10px 15px
font:
size: 1.2em
border-bottom: thin solid $color-background
box-shadow: 0 -2px 20px rgba(black, .2)
.col-list-item
display: flex
align-items: center
width: 100%
background-color: white
border-bottom: thin solid $color-background
.input-transparent-group
width: 100%
.input-transparent
border: thin solid transparent
border-radius: 3px
padding: 5px
margin-top: 5px
margin-bottom: 5px
width: 100%
&:hover
border: thin solid $color-text-dark-hint
#status-bar
color: $color-text-dark-secondary
margin-left: auto
font-size: .9em

75
src/styles/_config.sass Normal file
View File

@@ -0,0 +1,75 @@
$color-background: #eaebec
$color-background-light: lighten($color-background, 5%)
$color-background-dark: darken($color-background, 5%)
$color-background-nav: hsl(hue($color-background), 20%, 25%)
$color-background-nav-light: hsl(hue($color-background), 20%, 35%)
$color-background-nav-dark: hsl(hue($color-background), 20%, 15%)
$font-body: 'Roboto'
$font-headings: 'Lato'
$font-size: 14px
$color-text: #4d4e53
$color-text-dark: $color-text
$color-text-dark-primary: #646469 // rgba($color-text, .87)
$color-text-dark-secondary: #9E9FA2 // rgba($color-text, .54)
$color-text-dark-hint: #BBBBBD // rgba($color-text, .38)
$color-text-light: white
$color-text-light-primary: rgba($color-text-light, .87)
$color-text-light-secondary: rgba($color-text-light, .54)
$color-text-light-hint: rgba($color-text-light, .38)
$color-primary: #68B3C8
$color-primary-light: hsl(hue($color-primary), 30%, 90%)
$color-primary-dark: hsl(hue($color-primary), 80%, 30%)
$color-primary-accent: hsl(hue($color-primary), 100%, 50%)
$color-secondary: #f42942
$color-secondary-light: hsl(hue($color-secondary), 30%, 90%)
$color-secondary-dark: hsl(hue($color-secondary), 80%, 40%)
$color-secondary-accent: hsl(hue($color-secondary), 100%, 50%)
$color-warning: #F3BB45 !default
$color-info: #68B3C8 !default
$color-success: #27AE60 !default
$color-danger: #EB5E28 !default
/* Borrowed from dillo.space :) */
$color_upvote: #ff8b60
$color_downvote: #74a4ff
/* Label Status */
$color-status-invalid: lightgray
$color-status-todo: lightgray
$color-status-in_progress: #fff2cc
$color-status-on_hold: #fff2cc
$color-status-approved: #e4f5f9
$color-status-cbb: #e4f5f9
$color-status-final: #e7f5d3
$color-status-review: #e4f5f9
$color-status-active: #E6F3FD
$color-status-updated: #e7f5d3
/* Mobile Stuff */
$screen-xs: 480px !default
$screen-xs-min: $screen-xs
$screen-phone: $screen-xs-min
$screen-sm: 768px !default
$screen-sm-min: $screen-sm
$screen-tablet: $screen-sm-min
$screen-md: 1100px !default // 992px
$screen-md-min: $screen-md
$screen-desktop: $screen-md-min
$screen-lg: 1270px !default // 1200px
$screen-lg-min: $screen-lg
$screen-lg-desktop: $screen-lg-min
$screen-xs-max: $screen-sm-min - 1
$screen-sm-max: $screen-md-min - 1
$screen-md-max: $screen-lg-min - 1
$sidebar-width: 50px

427
src/styles/_normalize.scss Normal file
View File

@@ -0,0 +1,427 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

508
src/styles/_utils.sass Normal file
View File

@@ -0,0 +1,508 @@
/* Collection of mixins that can be plugged everywhere */
=clearfix
clear: both
&:after
// Basically same as .clearfix from bootstrap
clear: both
display: block
content: ' '
@mixin button-rounded($mixin-color, $roundness, $filled: false)
font-family: $font-body
text-transform: uppercase
opacity: .9
padding:
left: 20px
right: 20px
border-radius: $roundness
@if $filled
background: linear-gradient(lighten($mixin-color, 2%), $mixin-color)
color: white
border: thin solid darken($mixin-color, 5%)
text-shadow: 1px 1px 0 rgba(black, .15)
@else
background-color: transparent
color: $mixin-color
border: thin solid $mixin-color
text-shadow: none
transition: color 350ms ease-out, border 150ms ease-in-out, opacity 150ms ease-in-out, background-color 150ms ease-in-out
&:hover
opacity: 1
cursor: pointer
text-decoration: none
@if $filled
background: linear-gradient(lighten($mixin-color, 5%), lighten($mixin-color, 5%))
color: white
border-color: lighten($mixin-color, 5%)
@else
background-color: rgba($mixin-color, .1)
color: $mixin-color
border-color: $mixin-color
&:active, &:focus
outline: none
border-color: $mixin-color
background-color: $mixin-color
color: white
i
margin-right: 10px
small
font-size: .6em
@mixin overlay($from-color, $from-percentage, $to-color, $to-percentage)
position: absolute
top: 0
left: 0
right: 0
bottom: 0
background: linear-gradient(to bottom, $from-color $from-percentage, $to-color $to-percentage)
@mixin stripes($color-light, $color-dark, $deg, $size)
background-size: $size $size
background-image: linear-gradient($deg, $color-light 25%, $color-dark 25%, $color-dark 50%, $color-light 50%, $color-light 75%, $color-dark 75%, $color-dark)
=stripes-animate
animation:
name: background-slide
duration: 1s
delay: 0s
// fill-mode: forwards
iteration-count: infinite
timing-function: linear
=container-box
position: relative
background-color: white
border-radius: 3px
box-shadow: rgba(0, 0, 0, 0.298039) 0px 1px 4px -1px
=text-overflow-ellipsis
overflow: hidden
white-space: nowrap
text-overflow: ellipsis
=position-center-translate
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
=input-generic
color: $color-text-dark
box-shadow: none
font-family: $font-body
border-radius: 3px
border-color: $color-background-dark
background-color: $color-background-light
&:focus
border-color: $color-info
box-shadow: none
=label-generic
color: $color-text-dark
font-family: $font-body
font-weight: 300
@mixin badge($mixin-color, $roundness)
padding:
left: 10px
right: 10px
text-transform: uppercase
color: $mixin-color
border: 1px solid $mixin-color
border-radius: $roundness
i
margin-right: 10px
/* Smallest, like phones on portrait.
** Menu is collapsed, columns stack, no brand */
=media-xs
@media (max-width: #{$screen-tablet - 1px})
@content
/* Small but wide: phablets, iPads
** Menu is collapsed, columns stack, no brand */
=media-sm
@media (min-width: #{$screen-tablet}) and (max-width: #{$screen-desktop - 1px})
@content
/* Tablets portrait.
** Menu is expanded, but columns stack, brand is shown */
=media-md
@media (min-width: #{$screen-desktop})
@content
=media-lg
@media (min-width: #{$screen-lg-desktop})
@content
=media-print
@media print
@content
=spin
animation:
name: spin-once
duration: 1s
delay: 0s
fill-mode: forwards
iteration-count: infinite
timing-function: linear
=spin-once
+spin
animation:
iteration-count: 1
=pulse
animation:
name: pulse
duration: 1s
delay: 0s
fill-mode: forwards
iteration-count: infinite
=pulse-75
animation:
name: pulse-75
duration: 1s
delay: 0
fill-mode: forwards
iteration-count: infinite
.spin
+spin
&:before, &:after
+spin
@keyframes spin-once
from
transform: rotate(0deg)
to
transform: rotate(360deg)
@keyframes pulse
0
opacity: 1
50%
opacity: 0
100%
opacity: 1
@keyframes pulse-75
0
opacity: 1
50%
opacity: .8
100%
opacity: 1
@keyframes background-fill-left-right
from
background-position: right bottom
to
background-position: left bottom
@keyframes grow-bounce-in
0
transform: scale(0.8)
opacity: 0
50%
transform: scale(1.05)
opacity: 1
85%
transform: scale(1.0)
90%
transform: scale(0.99)
100%
transform: scale(1.0)
@keyframes grow-bounce-out
0
transform: scale(1.0)
opacity: 1
100%
transform: scale(0.9)
opacity: 0
@keyframes background-slide
from
background-position: 0 0
to
background-position: 50px 50px
@keyframes grow-bounce
0
transform: scale(1.0)
opacity: 1
50%
transform: scale(1.01)
opacity: .9
85%
transform: scale(1.0)
90%
transform: scale(0.99)
opacity: 1
100%
transform: scale(1.0)
@keyframes grow-bounce-heartbeat
0
transform: scale(1.0)
85%
transform: scale(1.0)
90%
transform: scale(1.15)
94%
transform: scale(0.9)
96%
transform: scale(1.05)
100%
transform: scale(1.0)
=list-bullets
ul
padding-left: 20px
list-style: none
li:before
content: '·'
font-weight: 400
position: relative
left: -10px
=node-details-description
padding: 15px 0 25px 0
color: darken($color-text-dark, 5%)
font:
family: $font-body
weight: 300
size: 1.2em
word-break: break-word
clear: both
+clearfix
+media-xs
font-size: 1.1em
strong, b
font-weight: 400
a:not([class])
color: $color-text-dark-primary
text-decoration: underline
&:hover
color: $color-primary
p
padding:
left: 20px
right: 20px
margin-bottom: 20px
line-height: 1.5em
word-wrap: break-word
h1, h2, h3, h4, h5, h6
padding:
top: 20px
left: 20px
right: 20px
blockquote
background-color: lighten($color-background, 5%)
text-shadow: 1px 1px 0 rgba(white, .2)
margin:
left: 20px
right: 20px
bottom: 30px
font-size: 1em
p
padding: 0
margin: 0
ul li blockquote
margin:
left: 0
top: 15px
img,
p img,
ul li img
max-width: 100%
padding:
top: 25px
// bottom: 10px
bottom: 25px
h2
margin-bottom: 15px
+media-xs
font-size: 1.5em
/* e.g. YouTube embed */
iframe
margin-top: 20px
width: 100%
max-width: 100%
height: auto
min-height: 354px
+media-sm
iframe
min-height: 314px
+media-xs
iframe
min-height: 314px
iframe[src^="https://w.soundcloud"]
min-height: auto
+list-bullets
ul
padding-left: 40px
margin-bottom: 25px
li
margin-bottom: 7px
img
display: block
padding:
top: 25px
bottom: 10px
ul, ul li ul
margin-top: 15px
padding-left: 20px
code, kbd, pre, samp
font-size: 1.3rem
pre
background-color: lighten($color-background, 5%)
border-color: $color-background
border-radius: 3px
color: $color-text
/* when <pre> is outside <p> */
margin:
left: 20px
right: 20px
pre+p
margin-top: 30px
p+pre
/* a <pre> right after a <p> usually are related, remove some spacing */
margin-top: -10px
p
pre
/* We already have spacing on the sides inside <p> */
margin:
left: 0
right: 0
=markdown-preview-container
border:
top: 1px solid $color-background
bottom: 1px solid $color-background
position: relative
margin: 40px auto 25px auto
padding: 10px 10px 25px 10px
color: $color-text-dark-primary
cursor: default
transition: all 150ms ease-in-out
+node-details-description
// Funny, normalize.css doesn't normalize when it's outside
h1
font-size: 2.8em
h2
margin-bottom: 15px
=ribbon
background-color: $color-success
cursor: default
overflow: hidden
white-space: nowrap
position: absolute
right: -40px
top: 10px
-webkit-transform: rotate(45deg)
-moz-transform: rotate(45deg)
-ms-transform: rotate(45deg)
-o-transform: rotate(45deg)
transform: rotate(45deg)
span
border: thin dashed rgba(white, .5)
color: white
display: block
font-size: 70%
margin: 1px 0
padding: 3px 50px
text:
align: center
transform: uppercase
@mixin text-background($text-color, $background-color, $roundness, $padding)
border-radius: $roundness
padding: $padding
background-color: rgba($background-color, .9)
box-shadow: 0.5em 0 0 rgba($background-color, .9),-0.5em 0 0 rgba($background-color, .9)
box-decoration-break: clone
color: $text-color
=list-meta
margin: 0
padding: 0
list-style: none
color: $color-text-dark-primary
li
display: inline-block
padding-left: 15px
position: relative
&:before
content: '·'
position: relative
top: 1px
left: -7px
color: $color-text-dark-secondary
&:first-child
padding-left: 0
&:before
content: ''
a
color: $color-text-dark-secondary
&:hover
color: $color-primary
/* Bootstrap's img-responsive class */
=img-responsive
display: block
max-width: 100%
height: auto

5
src/styles/main.sass Normal file
View File

@@ -0,0 +1,5 @@
@import _normalize
@import _config
@import _utils
@import _base

81
src/styles/tasks.sass Normal file
View File

@@ -0,0 +1,81 @@
@import _config
@import _utils
#task-add
margin-left: auto
font-size: .85em
.status-indicator
background-color: white
width: 16px
height: 16px
border-radius: 50%
margin: 10px
transform-origin: center
transition: all 150ms ease-in-out
&.invalid
background-color: hsl(hue($color-status-invalid), 100%, 75%)
&.todo
background-color: hsl(hue($color-status-todo), 100%, 75%)
&.in_progress
background-color: hsl(hue($color-status-in_progress), 100%, 60%)
&.on_hold
background-color: hsl(hue($color-status-on_hold), 100%, 75%)
&.approved
background-color: hsl(hue($color-status-approved), 100%, 75%)
&.cbb
background-color: hsl(hue($color-status-cbb), 100%, 75%)
&.final
background-color: hsl(hue($color-status-final), 100%, 40%)
&.review
background-color: hsl(hue($color-status-review), 100%, 75%)
.task-list-item
border-right: 5px solid transparent
color: $color-primary-dark
background-image: white
transition: all 150ms ease-in-out
&:hover
text-decoration: none
color: $color-primary
.status-indicator
transform: scale(1.1)
&.active
border-color: $color-background-dark
border-right-color: $color-primary
text-decoration: none
background-color: rgba(white, .5)
.status-indicator
transform: rotate(45deg)
border-radius: 15%
&.processing
+stripes(rgba(white, .5), transparent, -45deg, 25px)
+stripes-animate
animation-duration: 2s
.status
display: inline-block
margin-left: auto
margin-right: 10px
color: $color-text-dark-secondary
text-transform: uppercase
font-size: .8em
text-decoration: none
#task-details
margin: 10px
padding: 10px
width: 100%
+container-box
#task-view
width: 100%
.task-name
font-size: 1.6em

View File

@@ -1,4 +1,11 @@
| {% extends 'layout.html' %}
doctype html
html(lang="en")
head
meta(charset="utf-8")
title {% if self.page_title() %}{% block page_title %}{% endblock %} — {% endif %}Attract
meta(name="viewport", content="width=device-width, initial-scale=1.0")
| {% block head %}{% endblock %}
| {% block head %}
style(type='text/css').
option.invalid_task {
@@ -6,3 +13,36 @@ style(type='text/css').
background-color: #fee;
}
| {% endblock %}
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery-3.1.0.min.js')}}")
link(href='//fonts.googleapis.com/css?family=Roboto:300,400', rel='stylesheet', type='text/css')
link(href="{{ url_for('static_pillar', filename='assets/css/vendor/bootstrap.min.css') }}", rel="stylesheet")
link(href="{{ url_for('static_attract', filename='assets/css/main.css') }}", rel="stylesheet")
| {% block style %}{% endblock %}
body
#app-main
#col_left
nav.sidebar(role="navigation")
dl
dd
a(href="") A
dd
a(href="") B
dd
a(href="") C
dd
a(href="") A
| {% block body %}
#col_main
h1 Main
#col_right
h1 Right
| {% endblock %}
| {% block footer_scripts %}{% endblock %}

View File

@@ -1,34 +1,43 @@
| {% extends 'attract/layout.html' %}
| {% block page_title %}Tasks{% endblock %}
| {% block body %}
#page-container
#page-header
.page-title
| Tasks for project {{ project.name }}
#page-content
.page-triplet-container.homepage
.row
.col-md-4
p hey, here's a column too!
.col-md-4
.list-group#task-list
| {% for task in tasks %}
//- NOTE: this is tightly linked to the JS in tasks.js.
a.list-group-item(id="task-{{task._id}}",href="javascript:open_task('{{ task._id }}');") {{ task.name }}
| {% endfor %}
.col-md-4
.well
#task-details
| {% block page_title %}Tasks - {{ project.name }} {% endblock %}
| {% block style %}
link(href="{{ url_for('static_attract', filename='assets/css/tasks.css') }}", rel="stylesheet")
| {% endblock %}
| {% block body %}
#col_main
.col_header
| Tasks - {{ project.name }}
a#task-add(href="javascript:task_create();") + Create Task
#task-list.col-list
| {% for task in tasks %}
//- NOTE: this is tightly linked to the JS in tasks.js.
a.col-list-item.task-list-item(
id="task-{{task._id}}",
class="status-{{ task.properties.status }}"
href="javascript:task_open('{{ task._id }}');")
span.status-indicator(class="{{ task.properties.status }}")
span.name {{ task.name }}
span.status {{ task.properties.status | undertitle }}
| {% endfor %}
#col_right
.col_header
| Task Details
#status-bar Esa!
#task-details
| {% endblock %}
| {% block footer_scripts %}
script.
/**
* Shows a task in the #task-details div.
*/
function open_task(task_id) {
function task_open(task_id) {
if (task_id === undefined) {
if (console) console.log("open_task(undefined) called.");
if (console) console.log("task_open(undefined) called.");
return;
}
@@ -48,4 +57,23 @@ script.
$('#task-details').html(xhr.responseText);
});
}
/**
* Create a task and show it in the #task-details div.
*/
function task_create() {
var base_url = "{{ url_for('attract.tasks.create_task', project_url=project.url) }}";
$.get(base_url, function(task_data) {
task_open(task_data.task_id);
}).fail(function(xhr) {
if (console) {
console.log('Error creating task');
console.log('XHR:', xhr);
}
$('#task-details').html(xhr.responseText);
});
}
| {% endblock %}

View File

@@ -1,10 +1,10 @@
| {% extends 'attract/layout.html' %}
| {% block page_title %}Tasks{% endblock %}
| {% block page_title %}Tasks | {{ project.name }} {% endblock %}
| {% block body %}
#page-container
#page-header
.page-title
| Attract - tasks
| Attract - Tasks
#page-content
.page-triplet-container.homepage

View File

@@ -1,12 +1,23 @@
script(src="{{ url_for('static_attract', filename='js/tasks.js') }}",async=true)
.task
#task-view
form(onsubmit="return save_task('{{task._id}}', '{{ url_for('attract.tasks.save', project_url=project['url'], task_id=task._id) }}')")
.input-group
span.input-group-addon#task-addon-name(title='Name') N
input.form-control(name="name",autofocus=true,type=text,placeholder='Task name',aria-describedby="task-addon-name",value="{{ task.name|hide_none }}")
.input-group
span.input-group-addon#task-addon-type(title='Task type') T
select.form-control(name="task_type",aria-describedby="task-addon-task_type")
.input-transparent-group
input.input-transparent.task-name(
name="name",
type=text,
placeholder='Name',
aria-describedby="task-addon-name",
value="{{ task.name|hide_none }}")
.input-transparent-group
textarea.input-transparent(
name="description",
type=text,
placeholder='Description',
aria-describedby="task-addon-description") {{ task.description|hide_none }}
.input-transparent-group
select.input-transparent(name="task_type",aria-describedby="task-addon-task_type")
| {% for task_type in attract_props.task_types.attract_shot %}
| <option value="{{ task_type }}" {% if task_type == task.properties.task_type %}selected{% endif %}>{{ task_type|undertitle }}</option>
| {% endfor %}
@@ -14,14 +25,13 @@ script(src="{{ url_for('static_attract', filename='js/tasks.js') }}",async=true)
option(value="{{ task.properties.task_type }}",selected).invalid_task {{ task.properties.task_type|undertitle }}
| {% endif %}
.input-group
span.input-group-addon#task-addon-description(title='Description') D
textarea.form-control(name="description",type=text,placeholder='Task description',aria-describedby="task-addon-description") {{ task.description|hide_none }}
.input-group
span.input-group-addon#task-addon-status(title='Status') S
select.form-control(name="status",aria-describedby="task-addon-status")
.input-transparent-group
select.input-transparent(
name="status",
aria-describedby="task-addon-status")
| {% for status in task_node_type.dyn_schema.status.allowed %}
| <option value="{{ status }}" {% if status == task.properties.status %}selected{% endif %}>{{ status|undertitle }}</option>
| {% endfor %}
.input-group
.input-transparent-group
button.btn.btn-default(type=submit) Save task