diff --git a/attract/static/js/tasks.js b/attract/static/js/tasks.js index 7498640..6e111cc 100644 --- a/attract/static/js/tasks.js +++ b/attract/static/js/tasks.js @@ -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. } diff --git a/attract/tasks.py b/attract/tasks.py index fc700cc..af6f386 100644 --- a/attract/tasks.py +++ b/attract/tasks.py @@ -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) diff --git a/src/styles/_base.sass b/src/styles/_base.sass new file mode 100644 index 0000000..c55f2b8 --- /dev/null +++ b/src/styles/_base.sass @@ -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 diff --git a/src/styles/_config.sass b/src/styles/_config.sass new file mode 100644 index 0000000..3d681e1 --- /dev/null +++ b/src/styles/_config.sass @@ -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 diff --git a/src/styles/_normalize.scss b/src/styles/_normalize.scss new file mode 100644 index 0000000..458eea1 --- /dev/null +++ b/src/styles/_normalize.scss @@ -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; +} diff --git a/src/styles/_utils.sass b/src/styles/_utils.sass new file mode 100644 index 0000000..5f46b97 --- /dev/null +++ b/src/styles/_utils.sass @@ -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
 is outside 

*/ + margin: + left: 20px + right: 20px + pre+p + margin-top: 30px + + p+pre + /* a

 right after a 

usually are related, remove some spacing */ + margin-top: -10px + + p + pre + /* We already have spacing on the sides inside

*/ + 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 diff --git a/src/styles/main.sass b/src/styles/main.sass new file mode 100644 index 0000000..1b3962f --- /dev/null +++ b/src/styles/main.sass @@ -0,0 +1,5 @@ +@import _normalize +@import _config +@import _utils + +@import _base diff --git a/src/styles/tasks.sass b/src/styles/tasks.sass new file mode 100644 index 0000000..23333e4 --- /dev/null +++ b/src/styles/tasks.sass @@ -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 diff --git a/src/templates/attract/layout.jade b/src/templates/attract/layout.jade index e2e2453..00722cd 100644 --- a/src/templates/attract/layout.jade +++ b/src/templates/attract/layout.jade @@ -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 %} + diff --git a/src/templates/attract/tasks/for_project.jade b/src/templates/attract/tasks/for_project.jade index 14eefd0..7f6150e 100644 --- a/src/templates/attract/tasks/for_project.jade +++ b/src/templates/attract/tasks/for_project.jade @@ -1,40 +1,49 @@ | {% 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; } - + $('#task-list').find('a').removeClass('active'); $('#task-' + task_id).addClass('active'); - + var base_url = "{{ url_for('attract.tasks.view_embed_task', project_url=project.url, task_id='TASKID') }}"; var task_url = base_url.replace("TASKID", task_id); @@ -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 %} diff --git a/src/templates/attract/tasks/index.jade b/src/templates/attract/tasks/index.jade index ef7169c..705897b 100644 --- a/src/templates/attract/tasks/index.jade +++ b/src/templates/attract/tasks/index.jade @@ -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 diff --git a/src/templates/attract/tasks/view_task_embed.jade b/src/templates/attract/tasks/view_task_embed.jade index 0dc9d92..df2f06a 100644 --- a/src/templates/attract/tasks/view_task_embed.jade +++ b/src/templates/attract/tasks/view_task_embed.jade @@ -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 %} | | {% 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 %} | | {% endfor %} - .input-group + + .input-transparent-group button.btn.btn-default(type=submit) Save task