From d80ea599076729cd76443382ac1072c3302734ee Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Mon, 3 Jul 2017 15:03:51 -0700 Subject: [PATCH] adds scoring interface for all challenges --- app/Http/Controllers/DashboardController.php | 13 + app/Http/Controllers/TwitterController.php | 1 + app/Tweet.php | 6 +- .../2017_07_03_213453_twitter_id.php | 32 ++ database/seeds/DocumentSeeder.php | 2 +- public/css/app.css | 64 ++- public/js/app.js | 458 +++++++++++++++++- resources/assets/js/components/Scorecard.vue | 169 ++++++- resources/assets/js/components/TweetQueue.vue | 3 +- resources/assets/sass/app.scss | 59 ++- routes/web.php | 1 + 11 files changed, 789 insertions(+), 19 deletions(-) create mode 100644 database/migrations/2017_07_03_213453_twitter_id.php diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index f200a03..ed3123d 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use Twitter; use App\Tweet; use App\Events\NewTweetEvent, App\Events\TweetClaimedEvent; +use DB; class DashboardController extends Controller { @@ -63,4 +64,16 @@ class DashboardController extends Controller } return response()->json(['result'=>'ok']); } + + public function load_dropdowns() { + $documents = DB::table('m7_documents')->orderBy('id')->get(); + $transit_centers = DB::table('transit_centers')->orderBy('name')->get(); + $transit_lines = DB::table('transit_lines')->orderBy('sort')->get(); + + return response()->json([ + 'documents' => $documents, + 'transit_centers' => $transit_centers, + 'transit_lines' => $transit_lines + ]); + } } diff --git a/app/Http/Controllers/TwitterController.php b/app/Http/Controllers/TwitterController.php index 239e6ed..f28c1d5 100644 --- a/app/Http/Controllers/TwitterController.php +++ b/app/Http/Controllers/TwitterController.php @@ -50,6 +50,7 @@ class TwitterController extends BaseController } $tweet = new Tweet; + $tweet->tweet_id = $data['id_str']; $tweet->player_id = $player ? $player->id : 0; $tweet->team_id = $player ? $player->team->id : 0; $tweet->text = $text; diff --git a/app/Tweet.php b/app/Tweet.php index 48b7684..ae58bc5 100644 --- a/app/Tweet.php +++ b/app/Tweet.php @@ -8,7 +8,7 @@ use DB; class Tweet extends Model { protected $fillable = [ - 'player_id', 'team_id', 'text', 'photo', 'claimed_at', 'processed', 'mission_id', 'tweet_date', + 'tweet_id', 'player_id', 'team_id', 'text', 'photo', 'claimed_at', 'processed', 'mission_id', 'tweet_date', 'm1_transit_line_id', 'm1_non_trimet', 'm2_transit_center_id', 'm2_with_other_team', 'm3_complete', 'm4_complete', 'm5_complete', 'm5_tip', @@ -28,8 +28,8 @@ class Tweet extends Model } public static function claimed_timeout() { - // find tweets claimed longer than a minute ago - $timeout = 60; + // time out tweets if they aren't processed after the specified time + $timeout = 520; $tweets = Tweet::where('claimed_at', '<', date('Y-m-d H:i:s', time()-$timeout))->get(); foreach($tweets as $tweet) { $tweet->claimed_at = null; diff --git a/database/migrations/2017_07_03_213453_twitter_id.php b/database/migrations/2017_07_03_213453_twitter_id.php new file mode 100644 index 0000000..c1c1b97 --- /dev/null +++ b/database/migrations/2017_07_03_213453_twitter_id.php @@ -0,0 +1,32 @@ +string('tweet_id', 255); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tweets', function (Blueprint $table) { + $table->dropColumn('tweet_id'); + }); + } +} diff --git a/database/seeds/DocumentSeeder.php b/database/seeds/DocumentSeeder.php index 8209da6..f86d573 100644 --- a/database/seeds/DocumentSeeder.php +++ b/database/seeds/DocumentSeeder.php @@ -17,7 +17,7 @@ class DocumentSeeder extends Seeder ['Purchase at Books with Pictures', 'A receipt for a purchase at Books with Pictures'], ['TriMet Clothing', 'A team member wearing a TriMet-branded item of clothing or gear'], ['Biketown', 'A team member holding up their Biketown card'], - ['High-Five', 'A team member high-fiving a TriMet employee who is wearing official agency clothing'], + ['TriMet High-Five', 'A team member high-fiving a TriMet employee who is wearing official agency clothing'], ['Streetcar Dancing', 'Team members dancing on the Portland Streetcar'], ['Vintage Heavy Rail', 'Your team posing with a vintage heavy-rail train'], ['Public Art', 'Your team posing with a piece of public art'], diff --git a/public/css/app.css b/public/css/app.css index 92ca8bd..b38afb8 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -8541,23 +8541,30 @@ button.close { .tweet-queue .tweet { border-bottom: 1px #ccc solid; cursor: pointer; + margin-top: 10px; } .tweet-queue .tweet .mission { font-weight: bold; margin-top: 3px; text-align: right; + float: right; } .tweet-queue .tweet .text { margin-top: 6px; - font-size: 10px; + margin-bottom: 3px; + font-size: 11px; line-height: 12px; max-height: 36px; overflow: hidden; white-space: pre-wrap; } +.tweet-queue .tweet:first-child { + margin-top: 0; +} + .tweet-queue .tweet:hover { background: #e4f1f7; } @@ -8570,6 +8577,9 @@ button.close { display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .scorecard .panel-heading .team { @@ -8578,14 +8588,66 @@ button.close { flex: 1 0; } +.scorecard .panel-heading button { + margin-left: 10px; +} + .scorecard .tweet .text { white-space: pre-wrap; + font-size: 1.2em; + margin: 8px 0; } .scorecard .tweet img { max-width: 100%; } +.scorecard h2 { + margin: 0; +} + +.scorecard .instructions ul { + padding: 0; + padding-left: 10px; +} + +.scorecard .tweet-reply { + margin-top: 8px; + border: 1px #d3e0e9 solid; + border-radius: 6px; + padding: 8px; + background-color: #f5f8fa; +} + +.scorecard .tweet-reply button { + float: right; +} + +.scorecard .tweet-reply .clear { + clear: both; +} + +.scorecard .tweet-actions { + margin-top: 12px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.scorecard .tweet-actions button { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.scorecard .tweet-actions button:first-child { + margin-right: 2px; +} + +.scorecard .tweet-actions button:last-child { + margin-left: 2px; +} + .multi-photo { overflow: hidden; border-radius: 6px; diff --git a/public/js/app.js b/public/js/app.js index 144c4a4..36923c7 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -46534,7 +46534,8 @@ module.exports = { _this.queue.push(e); // If the new tweet is one that timed out, dismiss it if (_this.show && _this.tweet.tweet_id == e.tweet_id) { - _this.dismissTweet(); + _this.tweet = {}; + _this.show = false; } } }).listen('TweetClaimedEvent', function (e) { @@ -46724,7 +46725,7 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c style: ('background-color: #' + _vm.tweet.team_color) }), _vm._v(" "), _c('span', { staticClass: "team-name" - }, [_vm._v(_vm._s(_vm.tweet.team_name))])]), _vm._v(" "), _c('button', { + }, [_vm._v(_vm._s(_vm.tweet.team_name))])]), _vm._v(" "), _c('h2', [_vm._v(_vm._s(_vm.tweet.mission))]), _vm._v(" "), _c('button', { staticClass: "close", attrs: { "type": "button" @@ -46769,10 +46770,286 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c })]) })) : _vm._e(), _vm._v(" "), _c('div', { staticClass: "multi-photo-clear" - })]) : _vm._e()])]), _vm._v(" "), _c('div', { + })]) : _vm._e()]), _vm._v(" "), _c('div', { + staticClass: "tweet-reply" + }, [_c('textarea', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.replyText), + expression: "replyText" + }], + staticClass: "form-control", + staticStyle: { + "margin-bottom": "4px" + }, + attrs: { + "rows": "2" + }, + domProps: { + "value": (_vm.replyText) + }, + on: { + "input": function($event) { + if ($event.target.composing) { return; } + _vm.replyText = $event.target.value + } + } + }), _vm._v(" "), _c('button', { + staticClass: "btn btn-primary", + attrs: { + "type": "submit", + "disabled": _vm.isTweetReplyDisabled + } + }, [_vm._v("Reply")]), _vm._v(" "), _c('div', { + staticClass: "clear" + })]), _vm._v(" "), _c('div', { + staticClass: "tweet-actions" + }, [_c('button', { + staticClass: "btn btn-success", + attrs: { + "type": "button", + "disabled": _vm.isAcceptDisabled + } + }, [_vm._v("Accept")]), _vm._v(" "), _c('button', { + staticClass: "btn btn-danger", + attrs: { + "type": "button" + } + }, [_vm._v("Reject")])])]), _vm._v(" "), _c('div', { staticClass: "col-md-4" - }, [_c('div', [_vm._v(_vm._s(_vm.tweet.mission))])])])])]) : _vm._e() -},staticRenderFns: []} + }, [(_vm.tweet.mission_id == 1) ? [_c('p', { + staticClass: "instructions" + }, [_vm._v("\n The photo must show the sign with the transit line\n ")]), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, [_c('select', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedTransitLine), + expression: "selectedTransitLine" + }], + staticClass: "form-control", + on: { + "change": function($event) { + var $$selectedVal = Array.prototype.filter.call($event.target.options, function(o) { + return o.selected + }).map(function(o) { + var val = "_value" in o ? o._value : o.value; + return val + }); + _vm.selectedTransitLine = $event.target.multiple ? $$selectedVal : $$selectedVal[0] + } + } + }, [_c('option', { + attrs: { + "value": "" + } + }), _vm._v(" "), _vm._l((_vm.lines), function(line) { + return _c('option', { + domProps: { + "value": line.id + } + }, [_vm._v(_vm._s(line.name))]) + })], 2)]), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, [_c('input', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedNonTrimetLine), + expression: "selectedNonTrimetLine" + }], + staticClass: "form-control", + attrs: { + "type": "text", + "placeholder": "Non-Trimet Line" + }, + domProps: { + "value": (_vm.selectedNonTrimetLine) + }, + on: { + "input": function($event) { + if ($event.target.composing) { return; } + _vm.selectedNonTrimetLine = $event.target.value + } + } + })])] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 2) ? [_vm._m(0), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, [_c('select', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedTransitCenter), + expression: "selectedTransitCenter" + }], + staticClass: "form-control", + on: { + "change": function($event) { + var $$selectedVal = Array.prototype.filter.call($event.target.options, function(o) { + return o.selected + }).map(function(o) { + var val = "_value" in o ? o._value : o.value; + return val + }); + _vm.selectedTransitCenter = $event.target.multiple ? $$selectedVal : $$selectedVal[0] + } + } + }, [_c('option', { + attrs: { + "value": "" + } + }), _vm._v(" "), _vm._l((_vm.centers), function(stop) { + return _c('option', { + domProps: { + "value": stop.id + } + }, [_vm._v(_vm._s(stop.name))]) + })], 2)]), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, [_c('div', { + staticClass: "checkbox" + }, [_c('label', [_c('input', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedPhotoHasAnotherTeam), + expression: "selectedPhotoHasAnotherTeam" + }], + attrs: { + "type": "checkbox" + }, + domProps: { + "checked": Array.isArray(_vm.selectedPhotoHasAnotherTeam) ? _vm._i(_vm.selectedPhotoHasAnotherTeam, null) > -1 : (_vm.selectedPhotoHasAnotherTeam) + }, + on: { + "__c": function($event) { + var $$a = _vm.selectedPhotoHasAnotherTeam, + $$el = $event.target, + $$c = $$el.checked ? (true) : (false); + if (Array.isArray($$a)) { + var $$v = null, + $$i = _vm._i($$a, $$v); + if ($$c) { + $$i < 0 && (_vm.selectedPhotoHasAnotherTeam = $$a.concat($$v)) + } else { + $$i > -1 && (_vm.selectedPhotoHasAnotherTeam = $$a.slice(0, $$i).concat($$a.slice($$i + 1))) + } + } else { + _vm.selectedPhotoHasAnotherTeam = $$c + } + } + } + }), _vm._v("\n Another team is in the photo\n ")])])])] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 3) ? [_vm._m(1)] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 4) ? [_vm._m(2)] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 5) ? [_c('p', { + staticClass: "instructions" + }, [_vm._v("\n Accept a photo of either a team member singing, or a photo of tipping the bus driver\n ")]), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, [_c('label', [_c('input', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedM5Singing), + expression: "selectedM5Singing" + }], + attrs: { + "type": "checkbox" + }, + domProps: { + "checked": Array.isArray(_vm.selectedM5Singing) ? _vm._i(_vm.selectedM5Singing, null) > -1 : (_vm.selectedM5Singing) + }, + on: { + "__c": function($event) { + var $$a = _vm.selectedM5Singing, + $$el = $event.target, + $$c = $$el.checked ? (true) : (false); + if (Array.isArray($$a)) { + var $$v = null, + $$i = _vm._i($$a, $$v); + if ($$c) { + $$i < 0 && (_vm.selectedM5Singing = $$a.concat($$v)) + } else { + $$i > -1 && (_vm.selectedM5Singing = $$a.slice(0, $$i).concat($$a.slice($$i + 1))) + } + } else { + _vm.selectedM5Singing = $$c + } + } + } + }), _vm._v("\n Singing\n ")]), _vm._v(" "), _c('label', [_c('input', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedM5Tipping), + expression: "selectedM5Tipping" + }], + attrs: { + "type": "checkbox" + }, + domProps: { + "checked": Array.isArray(_vm.selectedM5Tipping) ? _vm._i(_vm.selectedM5Tipping, null) > -1 : (_vm.selectedM5Tipping) + }, + on: { + "__c": function($event) { + var $$a = _vm.selectedM5Tipping, + $$el = $event.target, + $$c = $$el.checked ? (true) : (false); + if (Array.isArray($$a)) { + var $$v = null, + $$i = _vm._i($$a, $$v); + if ($$c) { + $$i < 0 && (_vm.selectedM5Tipping = $$a.concat($$v)) + } else { + $$i > -1 && (_vm.selectedM5Tipping = $$a.slice(0, $$i).concat($$a.slice($$i + 1))) + } + } else { + _vm.selectedM5Tipping = $$c + } + } + } + }), _vm._v("\n Tipping the driver\n ")])])] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 6) ? [_c('p', { + staticClass: "instructions" + }, [_vm._v("\n The photo must show all team members with their completed visas\n ")])] : _vm._e(), _vm._v(" "), (_vm.tweet.mission_id == 7) ? [_c('p', { + staticClass: "instructions" + }, [_vm._v("\n Accept a photo that completes one of the below documents:\n ")]), _vm._v(" "), _c('div', { + staticClass: "form-group" + }, _vm._l((_vm.documents), function(doc) { + return _c('div', { + staticClass: "radio" + }, [_c('label', [_c('input', { + directives: [{ + name: "model", + rawName: "v-model", + value: (_vm.selectedDocument), + expression: "selectedDocument" + }], + attrs: { + "type": "radio", + "name": "selectedDocument" + }, + domProps: { + "value": doc.id, + "checked": _vm._q(_vm.selectedDocument, doc.id) + }, + on: { + "__c": function($event) { + _vm.selectedDocument = doc.id + } + } + }), _vm._v("\n " + _vm._s(doc.description) + "\n ")])]) + }))] : _vm._e()], 2)])])]) : _vm._e() +},staticRenderFns: [function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h; + return _c('p', { + staticClass: "instructions" + }, [_c('ul', [_c('li', [_vm._v("Photo must show the sign with the transit center")]), _vm._v(" "), _c('li', [_vm._v("Bonus points if another team and their pennant are visible!")])])]) +},function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h; + return _c('p', { + staticClass: "instructions" + }, [_c('ul', [_c('li', [_vm._v("Make sure this photo is on the tram or at the top tram station")]), _vm._v(" "), _c('li', [_vm._v("Reject if it shows the rooftop with the code!")])])]) +},function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h; + return _c('p', { + staticClass: "instructions" + }, [_c('ul', [_c('li', [_vm._v("The photo must include the radio")]), _vm._v(" "), _c('li', [_vm._v("Reject if it shows the decoded message")])])]) +}]} module.exports.render._withStripped = true if (false) { module.hot.accept() @@ -46785,6 +47062,119 @@ if (false) { /* 61 */ /***/ (function(module, exports) { +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// // // // @@ -46837,12 +47227,58 @@ if (false) { module.exports = { props: ['show', 'tweet'], data: function data() { - return {}; + return { + documents: [], + centers: [], + lines: [], + replyText: '', + selectedDocument: false, + selectedTransitCenter: false, + selectedTransitLine: false, + selectedNonTrimetLine: '', + selectedPhotoHasAnotherTeam: false, + selectedM5Singing: false, + selectedM5Tipping: false + }; }, + computed: { + isAcceptDisabled: function isAcceptDisabled() { + if (this.show) { + switch (this.tweet.mission_id) { + case 1: + return this.selectedTransitLine == false && this.selectedNonTrimetLine == ''; + case 2: + return this.selectedTransitCenter == false; + case 3: + case 4: + case 6: + // Nothing to check + return false; + case 5: + return this.selectedM5Singing == false && this.selectedM5Tipping == false; + case 7: + return this.selectedDocument == false; + default: + return true; + } + } else { + return true; + } + }, + isTweetReplyDisabled: function isTweetReplyDisabled() { + return this.replyText == ''; + } + }, methods: { dismiss: function dismiss() { this.$emit('dismiss'); + this.selectedDocument = false; + this.selectedTransitCenter = false; + this.selectedTransitLine = false; + this.selectedNonTrimetLine = ''; + this.selectedPhotoHasAnotherTeam = false; + this.replyText = ''; } }, watch: { @@ -46852,11 +47288,17 @@ module.exports = { // https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue Vue.nextTick(function () { $(".multi-photo .photo").featherlight(); - }); + }.bind(this)); } } }, - created: function created() {} + created: function created() { + $.get("/dashboard/dropdowns", function (response) { + this.centers = response.transit_centers; + this.lines = response.transit_lines; + this.documents = response.documents; + }.bind(this)); + } }; /***/ }), diff --git a/resources/assets/js/components/Scorecard.vue b/resources/assets/js/components/Scorecard.vue index bb13faa..cdd2034 100644 --- a/resources/assets/js/components/Scorecard.vue +++ b/resources/assets/js/components/Scorecard.vue @@ -5,6 +5,7 @@ {{ tweet.team_name }} +

{{ tweet.mission }}

@@ -30,12 +31,124 @@ +
+ + +
+
+ +
+ + +
-
{{ tweet.mission }}
+ + + + + + + + + + + + + + + + + + + + @@ -51,11 +164,56 @@ module.exports = { props: ['show', 'tweet'], data () { return { + documents: [], + centers: [], + lines: [], + replyText: '', + selectedDocument: false, + selectedTransitCenter: false, + selectedTransitLine: false, + selectedNonTrimetLine: '', + selectedPhotoHasAnotherTeam: false, + selectedM5Singing: false, + selectedM5Tipping: false + } + }, + computed: { + isAcceptDisabled() { + if(this.show) { + switch(this.tweet.mission_id) { + case 1: + return this.selectedTransitLine == false && this.selectedNonTrimetLine == ''; + case 2: + return this.selectedTransitCenter == false; + case 3: + case 4: + case 6: + // Nothing to check + return false; + case 5: + return this.selectedM5Singing == false && this.selectedM5Tipping == false; + case 7: + return this.selectedDocument == false; + default: + return true; + } + } else { + return true; + } + }, + isTweetReplyDisabled() { + return this.replyText == ''; } }, methods: { dismiss() { - this.$emit('dismiss') + this.$emit('dismiss'); + this.selectedDocument = false; + this.selectedTransitCenter = false; + this.selectedTransitLine = false; + this.selectedNonTrimetLine = ''; + this.selectedPhotoHasAnotherTeam = false; + this.replyText = ''; } }, watch: { @@ -65,11 +223,16 @@ module.exports = { // https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue Vue.nextTick(function() { $(".multi-photo .photo").featherlight(); - }); + }.bind(this)); } } }, created() { + $.get("/dashboard/dropdowns", function(response){ + this.centers = response.transit_centers; + this.lines = response.transit_lines; + this.documents = response.documents; + }.bind(this)); } } \ No newline at end of file diff --git a/resources/assets/js/components/TweetQueue.vue b/resources/assets/js/components/TweetQueue.vue index 55c462d..18a4801 100644 --- a/resources/assets/js/components/TweetQueue.vue +++ b/resources/assets/js/components/TweetQueue.vue @@ -46,7 +46,8 @@ module.exports = { this.queue.push(e); // If the new tweet is one that timed out, dismiss it if(this.show && this.tweet.tweet_id == e.tweet_id) { - this.dismissTweet(); + this.tweet = {}; + this.show = false; } } }) diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss index 59ad259..e041475 100644 --- a/resources/assets/sass/app.scss +++ b/resources/assets/sass/app.scss @@ -60,16 +60,19 @@ .tweet { border-bottom: 1px #ccc solid; cursor:pointer; + margin-top: 10px; .mission { font-weight: bold; margin-top: 3px; text-align: right; + float: right; } .text { margin-top: 6px; - font-size: 10px; + margin-bottom: 3px; + font-size: 11px; line-height: 12px; max-height: 36px; overflow: hidden; @@ -77,6 +80,10 @@ } } + .tweet:first-child { + margin-top: 0; + } + .tweet:hover { background: #e4f1f7; } @@ -91,16 +98,22 @@ .panel-heading { display: flex; + align-items: center; .team { flex: 1 0; } + + button { + margin-left: 10px; + } } .tweet { .text { white-space: pre-wrap; - + font-size: 1.2em; + margin: 8px 0; } img { @@ -108,6 +121,48 @@ } } + h2 { + margin: 0; + } + + .instructions { + ul { + padding: 0; + padding-left: 10px; + } + } + + .tweet-reply { + margin-top: 8px; + border: 1px $laravel-border-color solid; + border-radius: 6px; + padding: 8px; + background-color: $body-bg; + + button { + float: right; + } + + .clear { + clear: both; + } + } + + .tweet-actions { + margin-top: 12px; + display: flex; + + button { + flex: 1; + } + button:first-child { + margin-right: 2px; + } + button:last-child { + margin-left: 2px; + } + } + } .multi-photo { diff --git a/routes/web.php b/routes/web.php index c1d587a..7f0c59b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,6 +20,7 @@ Auth::routes(); Route::get('/dashboard', 'DashboardController@index')->name('dashboard'); Route::get('/dashboard/queue', 'DashboardController@queue')->name('queue'); Route::get('/dashboard/ping', 'DashboardController@ping'); +Route::get('/dashboard/dropdowns', 'DashboardController@load_dropdowns'); Route::post('/dashboard/claim-tweet', 'DashboardController@claim_tweet'); Route::get('/teams', 'TeamController@index')->name('teams');