Tasks Index for a Project
Work in progress
This commit is contained in:
@@ -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.
|
||||
}
|
||||
|
@@ -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
97
src/styles/_base.sass
Normal 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
75
src/styles/_config.sass
Normal 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
427
src/styles/_normalize.scss
Normal 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
508
src/styles/_utils.sass
Normal 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
5
src/styles/main.sass
Normal file
@@ -0,0 +1,5 @@
|
||||
@import _normalize
|
||||
@import _config
|
||||
@import _utils
|
||||
|
||||
@import _base
|
81
src/styles/tasks.sass
Normal file
81
src/styles/tasks.sass
Normal 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
|
@@ -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 %}
|
||||
|
||||
|
@@ -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 %}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user