Browse Source

adds scoring interface for all challenges

master
Aaron Parecki 7 years ago
parent
commit
d80ea59907
No known key found for this signature in database GPG Key ID: 276C2817346D6056
11 changed files with 789 additions and 19 deletions
  1. +13
    -0
      app/Http/Controllers/DashboardController.php
  2. +1
    -0
      app/Http/Controllers/TwitterController.php
  3. +3
    -3
      app/Tweet.php
  4. +32
    -0
      database/migrations/2017_07_03_213453_twitter_id.php
  5. +1
    -1
      database/seeds/DocumentSeeder.php
  6. +63
    -1
      public/css/app.css
  7. +450
    -8
      public/js/app.js
  8. +166
    -3
      resources/assets/js/components/Scorecard.vue
  9. +2
    -1
      resources/assets/js/components/TweetQueue.vue
  10. +57
    -2
      resources/assets/sass/app.scss
  11. +1
    -0
      routes/web.php

+ 13
- 0
app/Http/Controllers/DashboardController.php View File

@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use Twitter; use Twitter;
use App\Tweet; use App\Tweet;
use App\Events\NewTweetEvent, App\Events\TweetClaimedEvent; use App\Events\NewTweetEvent, App\Events\TweetClaimedEvent;
use DB;
class DashboardController extends Controller class DashboardController extends Controller
{ {
@ -63,4 +64,16 @@ class DashboardController extends Controller
} }
return response()->json(['result'=>'ok']); 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
]);
}
} }

+ 1
- 0
app/Http/Controllers/TwitterController.php View File

@ -50,6 +50,7 @@ class TwitterController extends BaseController
} }
$tweet = new Tweet; $tweet = new Tweet;
$tweet->tweet_id = $data['id_str'];
$tweet->player_id = $player ? $player->id : 0; $tweet->player_id = $player ? $player->id : 0;
$tweet->team_id = $player ? $player->team->id : 0; $tweet->team_id = $player ? $player->team->id : 0;
$tweet->text = $text; $tweet->text = $text;

+ 3
- 3
app/Tweet.php View File

@ -8,7 +8,7 @@ use DB;
class Tweet extends Model class Tweet extends Model
{ {
protected $fillable = [ 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', 'm1_transit_line_id', 'm1_non_trimet',
'm2_transit_center_id', 'm2_with_other_team', 'm2_transit_center_id', 'm2_with_other_team',
'm3_complete', 'm4_complete', 'm5_complete', 'm5_tip', 'm3_complete', 'm4_complete', 'm5_complete', 'm5_tip',
@ -28,8 +28,8 @@ class Tweet extends Model
} }
public static function claimed_timeout() { 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(); $tweets = Tweet::where('claimed_at', '<', date('Y-m-d H:i:s', time()-$timeout))->get();
foreach($tweets as $tweet) { foreach($tweets as $tweet) {
$tweet->claimed_at = null; $tweet->claimed_at = null;

+ 32
- 0
database/migrations/2017_07_03_213453_twitter_id.php View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class TwitterId extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tweets', function (Blueprint $table) {
$table->string('tweet_id', 255);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tweets', function (Blueprint $table) {
$table->dropColumn('tweet_id');
});
}
}

+ 1
- 1
database/seeds/DocumentSeeder.php View File

@ -17,7 +17,7 @@ class DocumentSeeder extends Seeder
['Purchase at Books with Pictures', 'A receipt for a purchase at Books with Pictures'], ['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'], ['TriMet Clothing', 'A team member wearing a TriMet-branded item of clothing or gear'],
['Biketown', 'A team member holding up their Biketown card'], ['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'], ['Streetcar Dancing', 'Team members dancing on the Portland Streetcar'],
['Vintage Heavy Rail', 'Your team posing with a vintage heavy-rail train'], ['Vintage Heavy Rail', 'Your team posing with a vintage heavy-rail train'],
['Public Art', 'Your team posing with a piece of public art'], ['Public Art', 'Your team posing with a piece of public art'],

+ 63
- 1
public/css/app.css View File

@ -8541,23 +8541,30 @@ button.close {
.tweet-queue .tweet { .tweet-queue .tweet {
border-bottom: 1px #ccc solid; border-bottom: 1px #ccc solid;
cursor: pointer; cursor: pointer;
margin-top: 10px;
} }
.tweet-queue .tweet .mission { .tweet-queue .tweet .mission {
font-weight: bold; font-weight: bold;
margin-top: 3px; margin-top: 3px;
text-align: right; text-align: right;
float: right;
} }
.tweet-queue .tweet .text { .tweet-queue .tweet .text {
margin-top: 6px; margin-top: 6px;
font-size: 10px;
margin-bottom: 3px;
font-size: 11px;
line-height: 12px; line-height: 12px;
max-height: 36px; max-height: 36px;
overflow: hidden; overflow: hidden;
white-space: pre-wrap; white-space: pre-wrap;
} }
.tweet-queue .tweet:first-child {
margin-top: 0;
}
.tweet-queue .tweet:hover { .tweet-queue .tweet:hover {
background: #e4f1f7; background: #e4f1f7;
} }
@ -8570,6 +8577,9 @@ button.close {
display: -webkit-box; display: -webkit-box;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
} }
.scorecard .panel-heading .team { .scorecard .panel-heading .team {
@ -8578,14 +8588,66 @@ button.close {
flex: 1 0; flex: 1 0;
} }
.scorecard .panel-heading button {
margin-left: 10px;
}
.scorecard .tweet .text { .scorecard .tweet .text {
white-space: pre-wrap; white-space: pre-wrap;
font-size: 1.2em;
margin: 8px 0;
} }
.scorecard .tweet img { .scorecard .tweet img {
max-width: 100%; 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 { .multi-photo {
overflow: hidden; overflow: hidden;
border-radius: 6px; border-radius: 6px;

+ 450
- 8
public/js/app.js View File

@ -46534,7 +46534,8 @@ module.exports = {
_this.queue.push(e); _this.queue.push(e);
// If the new tweet is one that timed out, dismiss it // If the new tweet is one that timed out, dismiss it
if (_this.show && _this.tweet.tweet_id == e.tweet_id) { if (_this.show && _this.tweet.tweet_id == e.tweet_id) {
_this.dismissTweet();
_this.tweet = {};
_this.show = false;
} }
} }
}).listen('TweetClaimedEvent', function (e) { }).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) style: ('background-color: #' + _vm.tweet.team_color)
}), _vm._v(" "), _c('span', { }), _vm._v(" "), _c('span', {
staticClass: "team-name" 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", staticClass: "close",
attrs: { attrs: {
"type": "button" "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', { })) : _vm._e(), _vm._v(" "), _c('div', {
staticClass: "multi-photo-clear" 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" 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 module.exports.render._withStripped = true
if (false) { if (false) {
module.hot.accept() module.hot.accept()
@ -46785,6 +47062,119 @@ if (false) {
/* 61 */ /* 61 */
/***/ (function(module, exports) { /***/ (function(module, exports) {
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// //
// //
// //
@ -46837,12 +47227,58 @@ if (false) {
module.exports = { module.exports = {
props: ['show', 'tweet'], props: ['show', 'tweet'],
data: function data() { 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: { methods: {
dismiss: function dismiss() { dismiss: function dismiss() {
this.$emit('dismiss'); this.$emit('dismiss');
this.selectedDocument = false;
this.selectedTransitCenter = false;
this.selectedTransitLine = false;
this.selectedNonTrimetLine = '';
this.selectedPhotoHasAnotherTeam = false;
this.replyText = '';
} }
}, },
watch: { watch: {
@ -46852,11 +47288,17 @@ module.exports = {
// https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue // https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue
Vue.nextTick(function () { Vue.nextTick(function () {
$(".multi-photo .photo").featherlight(); $(".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));
}
}; };
/***/ }), /***/ }),

+ 166
- 3
resources/assets/js/components/Scorecard.vue View File

@ -5,6 +5,7 @@
<span class="team-icon" :style="'background-color: #'+tweet.team_color"></span> <span class="team-icon" :style="'background-color: #'+tweet.team_color"></span>
<span class="team-name">{{ tweet.team_name }}</span> <span class="team-name">{{ tweet.team_name }}</span>
</div> </div>
<h2>{{ tweet.mission }}</h2>
<button type="button" class="close" v-on:click="dismiss"><span>&times;</span></button> <button type="button" class="close" v-on:click="dismiss"><span>&times;</span></button>
</div> </div>
@ -30,12 +31,124 @@
</div> </div>
</div> </div>
<div class="tweet-reply">
<textarea class="form-control" rows="2" v-model="replyText" style="margin-bottom: 4px;"></textarea>
<button type="submit" class="btn btn-primary" :disabled="isTweetReplyDisabled">Reply</button>
<div class="clear"></div>
</div>
<div class="tweet-actions">
<button type="button" class="btn btn-success" :disabled="isAcceptDisabled">Accept</button>
<button type="button" class="btn btn-danger">Reject</button>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div>{{ tweet.mission }}</div>
<!-- MISSION 1 -->
<template v-if="tweet.mission_id == 1">
<p class="instructions">
The photo must show the sign with the transit line
</p>
<div class="form-group">
<select class="form-control" v-model="selectedTransitLine">
<option value=""></option>
<option v-for="line in lines" :value="line.id">{{ line.name }}</option>
</select>
</div>
<div class="form-group">
<input type="text" v-model="selectedNonTrimetLine" placeholder="Non-Trimet Line" class="form-control">
</div>
</template>
<!-- MISSION 2 -->
<template v-if="tweet.mission_id == 2">
<p class="instructions">
<ul>
<li>Photo must show the sign with the transit center</li>
<li>Bonus points if another team and their pennant are visible!</li>
</ul>
</p>
<div class="form-group">
<select class="form-control" v-model="selectedTransitCenter">
<option value=""></option>
<option v-for="stop in centers" :value="stop.id">{{ stop.name }}</option>
</select>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" v-model="selectedPhotoHasAnotherTeam">
Another team is in the photo
</label>
</div>
</div>
</template>
<!-- MISSION 3 -->
<template v-if="tweet.mission_id == 3">
<p class="instructions">
<ul>
<li>Make sure this photo is on the tram or at the top tram station</li>
<li>Reject if it shows the rooftop with the code!</li>
</ul>
</p>
</template>
<!-- MISSION 4 -->
<template v-if="tweet.mission_id == 4">
<p class="instructions">
<ul>
<li>The photo must include the radio</li>
<li>Reject if it shows the decoded message</li>
</ul>
</p>
</template>
<!-- MISSION 5 -->
<template v-if="tweet.mission_id == 5">
<p class="instructions">
Accept a photo of either a team member singing, or a photo of tipping the bus driver
</p>
<div class="form-group">
<label>
<input type="checkbox" v-model="selectedM5Singing">
Singing
</label>
<label>
<input type="checkbox" v-model="selectedM5Tipping">
Tipping the driver
</label>
</div>
</template>
<!-- MISSION 6 -->
<template v-if="tweet.mission_id == 6">
<p class="instructions">
The photo must show all team members with their completed visas
</p>
</template>
<!-- MISSION 7 -->
<template v-if="tweet.mission_id == 7">
<p class="instructions">
Accept a photo that completes one of the below documents:
</p>
<div class="form-group">
<div v-for="doc in documents" class="radio">
<label>
<input type="radio" name="selectedDocument" v-model="selectedDocument" :value="doc.id">
{{ doc.description }}
</label>
</div>
</div>
</template>
@ -51,11 +164,56 @@ module.exports = {
props: ['show', 'tweet'], props: ['show', 'tweet'],
data () { data () {
return { 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: { methods: {
dismiss() { dismiss() {
this.$emit('dismiss')
this.$emit('dismiss');
this.selectedDocument = false;
this.selectedTransitCenter = false;
this.selectedTransitLine = false;
this.selectedNonTrimetLine = '';
this.selectedPhotoHasAnotherTeam = false;
this.replyText = '';
} }
}, },
watch: { watch: {
@ -65,11 +223,16 @@ module.exports = {
// https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue // https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue
Vue.nextTick(function() { Vue.nextTick(function() {
$(".multi-photo .photo").featherlight(); $(".multi-photo .photo").featherlight();
});
}.bind(this));
} }
} }
}, },
created() { created() {
$.get("/dashboard/dropdowns", function(response){
this.centers = response.transit_centers;
this.lines = response.transit_lines;
this.documents = response.documents;
}.bind(this));
} }
} }
</script> </script>

+ 2
- 1
resources/assets/js/components/TweetQueue.vue View File

@ -46,7 +46,8 @@ module.exports = {
this.queue.push(e); this.queue.push(e);
// If the new tweet is one that timed out, dismiss it // If the new tweet is one that timed out, dismiss it
if(this.show && this.tweet.tweet_id == e.tweet_id) { if(this.show && this.tweet.tweet_id == e.tweet_id) {
this.dismissTweet();
this.tweet = {};
this.show = false;
} }
} }
}) })

+ 57
- 2
resources/assets/sass/app.scss View File

@ -60,16 +60,19 @@
.tweet { .tweet {
border-bottom: 1px #ccc solid; border-bottom: 1px #ccc solid;
cursor:pointer; cursor:pointer;
margin-top: 10px;
.mission { .mission {
font-weight: bold; font-weight: bold;
margin-top: 3px; margin-top: 3px;
text-align: right; text-align: right;
float: right;
} }
.text { .text {
margin-top: 6px; margin-top: 6px;
font-size: 10px;
margin-bottom: 3px;
font-size: 11px;
line-height: 12px; line-height: 12px;
max-height: 36px; max-height: 36px;
overflow: hidden; overflow: hidden;
@ -77,6 +80,10 @@
} }
} }
.tweet:first-child {
margin-top: 0;
}
.tweet:hover { .tweet:hover {
background: #e4f1f7; background: #e4f1f7;
} }
@ -91,16 +98,22 @@
.panel-heading { .panel-heading {
display: flex; display: flex;
align-items: center;
.team { .team {
flex: 1 0; flex: 1 0;
} }
button {
margin-left: 10px;
}
} }
.tweet { .tweet {
.text { .text {
white-space: pre-wrap; white-space: pre-wrap;
font-size: 1.2em;
margin: 8px 0;
} }
img { 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 { .multi-photo {

+ 1
- 0
routes/web.php View File

@ -20,6 +20,7 @@ Auth::routes();
Route::get('/dashboard', 'DashboardController@index')->name('dashboard'); Route::get('/dashboard', 'DashboardController@index')->name('dashboard');
Route::get('/dashboard/queue', 'DashboardController@queue')->name('queue'); Route::get('/dashboard/queue', 'DashboardController@queue')->name('queue');
Route::get('/dashboard/ping', 'DashboardController@ping'); Route::get('/dashboard/ping', 'DashboardController@ping');
Route::get('/dashboard/dropdowns', 'DashboardController@load_dropdowns');
Route::post('/dashboard/claim-tweet', 'DashboardController@claim_tweet'); Route::post('/dashboard/claim-tweet', 'DashboardController@claim_tweet');
Route::get('/teams', 'TeamController@index')->name('teams'); Route::get('/teams', 'TeamController@index')->name('teams');

Loading…
Cancel
Save