2017-07-20 20:52:46 +00:00
< template lang = "pug" >
2017-09-07 19:26:53 +00:00
b - modal # challenge - modal ( : title = "title" , size = 'lg' )
2017-07-31 19:54:52 +00:00
. form
2017-07-20 20:52:46 +00:00
. form - group
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'name' ) } } *
b - form - input ( type = "text" , : placeholder = "$t('challengeNamePlaceholder')" , v - model = "workingChallenge.name" )
2017-07-31 19:54:52 +00:00
. form - group
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'shortName' ) } } *
2017-08-07 20:26:17 +00:00
b - form - input ( type = "text" , : placeholder = "$t('shortNamePlaceholder')" , v - model = "workingChallenge.shortName" )
2017-07-20 20:52:46 +00:00
. form - group
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'challengeSummary' ) } } *
div . summary - count { { charactersRemaining } } { { $t ( 'charactersRemaining' ) } }
2017-08-30 21:19:17 +00:00
textarea . summary - textarea . form - control ( : placeholder = "$t('challengeSummaryPlaceholder')" , v - model = "workingChallenge.summary" )
2017-08-17 17:03:32 +00:00
. form - group
2017-07-20 20:52:46 +00:00
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'challengeDescription' ) } } *
2017-07-20 20:52:46 +00:00
a . float - right { { $t ( 'markdownFormattingHelp' ) } }
2017-08-30 21:19:17 +00:00
textarea . description - textarea . form - control ( : placeholder = "$t('challengeDescriptionPlaceholder')" , v - model = "workingChallenge.description" )
2017-07-20 20:52:46 +00:00
. form - group ( v - if = 'creating' )
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'challengeGuild' ) } } *
2017-07-31 19:54:52 +00:00
select . form - control ( v - model = 'workingChallenge.group' )
option ( v - for = 'group in groups' , : value = 'group._id' ) { { group . name } }
. form - group ( v - if = 'workingChallenge.categories' )
2017-07-20 20:52:46 +00:00
label
2017-08-22 20:39:45 +00:00
strong ( v - once ) { { $t ( 'categories' ) } } *
2017-07-31 19:54:52 +00:00
div . category - wrap ( @ click . prevent = "toggleCategorySelect" )
span . category - select ( v - if = 'workingChallenge.categories.length === 0' ) { { $t ( 'none' ) } }
. category - label ( v - for = 'category in workingChallenge.categories' ) { { $t ( categoriesHashByKey [ category ] ) } }
. category - box ( v - if = "showCategorySelect" )
. form - check (
v - for = "group in categoryOptions" ,
: key = "group.key" ,
2017-08-23 03:58:13 +00:00
v - if = 'group.key !== "habitica_official" || user.contributor.admin'
2017-07-31 19:54:52 +00:00
)
label . custom - control . custom - checkbox
2017-08-23 03:58:13 +00:00
input . custom - control - input ( type = "checkbox" ,
: value = 'group.key' ,
v - model = "workingChallenge.categories" )
2017-07-31 19:54:52 +00:00
span . custom - control - indicator
span . custom - control - description ( v - once ) { { $t ( group . label ) } }
button . btn . btn - primary ( @ click . prevent = "toggleCategorySelect" ) { { $t ( 'close' ) } }
// @TODO: Implement in V2 .form-group
2017-07-20 20:52:46 +00:00
label
strong ( v - once ) { { $t ( 'endDate' ) } }
b - form - input . end - date - input
. form - group
label
strong ( v - once ) { { $t ( 'prize' ) } }
2017-08-23 03:58:13 +00:00
input ( type = 'number' , : min = 'minPrize' , : max = 'maxPrize' , v - model = "workingChallenge.prize" )
2017-07-20 20:52:46 +00:00
. row . footer - wrap
. col - 12. text - center . submit - button - wrapper
2017-07-31 19:54:52 +00:00
. alert . alert - warning ( v - if = 'insufficientGemsForTavernChallenge' )
You do not have enough gems to create a Tavern challenge
2017-09-08 19:23:58 +00:00
// @TODO if buy gems button is added, add analytics tracking to it
// see https://github.com/HabitRPG/habitica/blob/develop/website/views/options/social/challenges.jade#L134
2017-08-20 22:49:47 +00:00
button . btn . btn - primary ( v - once , v - if = 'creating' , @ click = 'createChallenge()' ) { { $t ( 'createChallengeAddTasks' ) } }
2017-07-31 19:54:52 +00:00
button . btn . btn - primary ( v - once , v - if = '!creating' , @ click = 'updateChallenge()' ) { { $t ( 'updateChallenge' ) } }
2017-07-20 20:52:46 +00:00
. col - 12. text - center
p ( v - once ) { { $t ( 'challengeMinimum' ) } }
< / template >
< style lang = 'scss' >
@ import '~client/assets/scss/colors.scss' ;
# challenge - modal {
h5 {
color : $purple - 200 ;
margin - bottom : 1.5 em ;
}
. modal - header {
border : none ;
}
. modal - footer {
display : none ;
}
2017-08-22 20:39:45 +00:00
. summary - count {
font - size : 12 px ;
line - height : 1.33 ;
margin - top : 1 em ;
color : $gray - 200 ;
text - align : right ;
}
. summary - textarea {
2017-07-20 20:52:46 +00:00
height : 90 px ;
}
2017-08-22 20:39:45 +00:00
. description - textarea {
2017-07-20 20:52:46 +00:00
height : 220 px ;
}
label {
margin - right : .5 em ;
}
. end - date - input {
width : 150 px ;
display : inline - block ;
}
. form - group {
margin - bottom : 2 em ;
}
. submit - button - wrapper {
margin - bottom : .5 em ;
}
. footer - wrap {
margin - top : 2 em ;
margin - bottom : 2 em ;
}
2017-07-31 19:54:52 +00:00
. category - wrap {
position : relative ;
}
. category - box {
2017-08-07 20:26:17 +00:00
top : - 120 px ! important ;
z - index : 10 ;
2017-07-31 19:54:52 +00:00
}
2017-07-20 20:52:46 +00:00
}
< / style >
< script >
2017-09-25 18:02:12 +00:00
import clone from 'lodash/clone' ;
2017-07-20 20:52:46 +00:00
import bModal from 'bootstrap-vue/lib/components/modal' ;
import bDropdown from 'bootstrap-vue/lib/components/dropdown' ;
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item' ;
import bFormInput from 'bootstrap-vue/lib/components/form-input' ;
2017-08-22 20:39:45 +00:00
import { TAVERN _ID , MIN _SHORTNAME _SIZE _FOR _CHALLENGES , MAX _SUMMARY _SIZE _FOR _CHALLENGES } from '../../../common/script/constants' ;
2017-07-31 19:54:52 +00:00
import { mapState } from 'client/libs/store' ;
2017-07-20 20:52:46 +00:00
export default {
2017-09-25 18:02:12 +00:00
props : [ 'groupId' , 'cloning' ] ,
2017-07-20 20:52:46 +00:00
components : {
bModal ,
bDropdown ,
bDropdownItem ,
bFormInput ,
} ,
data ( ) {
2017-07-31 19:54:52 +00:00
let categoryOptions = [
{
2017-08-09 16:56:48 +00:00
label : 'habitica_official' ,
2017-08-07 20:26:17 +00:00
key : 'habitica_official' ,
2017-07-31 19:54:52 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'academics' ,
key : 'academics' ,
2017-07-31 19:54:52 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'advocacy_causes' ,
2017-08-07 20:26:17 +00:00
key : 'advocacy_causes' ,
2017-07-31 19:54:52 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'creativity' ,
2017-08-07 20:26:17 +00:00
key : 'creativity' ,
2017-07-31 19:54:52 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'entertainment' ,
key : 'entertainment' ,
2017-08-07 20:26:17 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'finance' ,
2017-08-07 20:26:17 +00:00
key : 'finance' ,
} ,
{
2017-08-09 16:56:48 +00:00
label : 'health_fitness' ,
2017-07-31 19:54:52 +00:00
key : 'health_fitness' ,
} ,
{
2017-08-09 16:56:48 +00:00
label : 'hobbies_occupations' ,
key : 'hobbies_occupations' ,
2017-08-07 20:26:17 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'location_based' ,
2017-08-07 20:26:17 +00:00
key : 'location_based' ,
} ,
{
2017-08-09 16:56:48 +00:00
label : 'mental_health' ,
2017-09-25 18:02:12 +00:00
key : 'mental_health' ,
2017-07-31 19:54:52 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'getting_organized' ,
2017-09-25 18:02:12 +00:00
key : 'getting_organized' ,
2017-08-07 20:26:17 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'self_improvement' ,
2017-09-25 18:02:12 +00:00
key : 'self_improvement' ,
2017-08-07 20:26:17 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'spirituality' ,
2017-09-25 18:02:12 +00:00
key : 'spirituality' ,
2017-08-07 20:26:17 +00:00
} ,
{
2017-08-09 16:56:48 +00:00
label : 'time_management' ,
2017-08-07 20:26:17 +00:00
key : 'time_management' ,
2017-07-31 19:54:52 +00:00
} ,
] ;
let hashedCategories = { } ;
categoryOptions . forEach ( ( category ) => {
hashedCategories [ category . key ] = category . label ;
} ) ;
let categoriesHashByKey = hashedCategories ;
2017-07-20 20:52:46 +00:00
return {
2017-08-30 21:19:17 +00:00
workingChallenge : {
name : '' ,
summary : '' ,
description : '' ,
categories : [ ] ,
group : '' ,
dailys : [ ] ,
habits : [ ] ,
leader : '' ,
members : [ ] ,
official : false ,
prize : 1 ,
rewards : [ ] ,
shortName : '' ,
todos : [ ] ,
} ,
2017-07-31 19:54:52 +00:00
showCategorySelect : false ,
categoryOptions ,
categoriesHashByKey ,
groups : [ ] ,
2017-07-20 20:52:46 +00:00
} ;
} ,
2017-07-31 19:54:52 +00:00
async mounted ( ) {
this . groups = await this . $store . dispatch ( 'guilds:getMyGuilds' ) ;
2017-08-07 20:26:17 +00:00
if ( this . user . party . _id ) {
let party = await this . $store . dispatch ( 'guilds:getGroup' , { groupId : 'party' } ) ;
this . groups . push ( {
name : party . name ,
_id : party . _id ,
2017-08-23 03:58:13 +00:00
privacy : 'private' ,
2017-08-07 20:26:17 +00:00
} ) ;
}
2017-08-03 20:04:03 +00:00
this . groups . push ( {
2017-08-22 20:39:45 +00:00
name : this . $t ( 'publicChallengesTitle' ) ,
2017-08-03 20:04:03 +00:00
_id : TAVERN _ID ,
} ) ;
2017-09-07 19:26:53 +00:00
this . setUpWorkingChallenge ( ) ;
2017-07-31 19:54:52 +00:00
} ,
watch : {
user ( ) {
if ( ! this . challenge ) this . workingChallenge . leader = this . user . _id ;
} ,
2017-09-07 19:26:53 +00:00
challenge ( ) {
this . setUpWorkingChallenge ( ) ;
} ,
cloning ( ) {
this . setUpWorkingChallenge ( ) ;
} ,
2017-07-20 20:52:46 +00:00
} ,
computed : {
2017-07-31 19:54:52 +00:00
... mapState ( { user : 'user.data' } ) ,
2017-09-07 19:26:53 +00:00
creating ( ) {
return ! this . workingChallenge . id ;
} ,
title ( ) {
if ( this . creating ) {
return this . $t ( 'createChallenge' ) ;
}
return this . $t ( 'editingChallenge' ) ;
} ,
2017-08-22 20:39:45 +00:00
charactersRemaining ( ) {
let currentLength = this . workingChallenge . summary ? this . workingChallenge . summary . length : 0 ;
return MAX _SUMMARY _SIZE _FOR _CHALLENGES - currentLength ;
} ,
2017-07-20 20:52:46 +00:00
maxPrize ( ) {
2017-07-31 19:54:52 +00:00
let userBalance = this . user . balance || 0 ;
userBalance = userBalance * 4 ;
let groupBalance = 0 ;
2017-08-23 03:58:13 +00:00
let group ;
this . groups . forEach ( item => {
if ( item . _id === this . workingChallenge . group ) {
group = item ;
return ;
}
} ) ;
2017-07-31 19:54:52 +00:00
if ( group && group . balance && group . leader === this . user . _id ) {
groupBalance = group . balance * 4 ;
}
return userBalance + groupBalance ;
2017-07-20 20:52:46 +00:00
} ,
2017-08-23 03:58:13 +00:00
minPrize ( ) {
2017-09-25 23:33:46 +00:00
if ( this . workingChallenge . group === TAVERN _ID ) return 1 ;
return 0 ;
2017-08-23 03:58:13 +00:00
} ,
2017-07-20 20:52:46 +00:00
insufficientGemsForTavernChallenge ( ) {
2017-07-31 19:54:52 +00:00
let balance = this . user . balance || 0 ;
let isForTavern = this . workingChallenge . group === TAVERN _ID ;
if ( isForTavern ) {
return balance <= 0 ;
} else {
return false ;
}
2017-07-20 20:52:46 +00:00
} ,
2017-09-25 18:02:12 +00:00
challenge ( ) {
return this . $store . state . challengeOptions . workingChallenge ;
} ,
2017-07-20 20:52:46 +00:00
} ,
methods : {
2017-09-07 19:26:53 +00:00
setUpWorkingChallenge ( ) {
2017-09-25 18:02:12 +00:00
this . resetWorkingChallenge ( ) ;
2017-09-07 19:26:53 +00:00
if ( ! this . challenge ) return ;
this . workingChallenge = Object . assign ( { } , this . workingChallenge , this . challenge ) ;
this . workingChallenge . categories = [ ] ;
if ( this . challenge . categories ) {
this . challenge . categories . forEach ( category => {
this . workingChallenge . categories . push ( category . slug ) ;
} ) ;
}
if ( this . cloning ) {
this . $delete ( this . workingChallenge , '_id' ) ;
this . $delete ( this . workingChallenge , 'id' ) ;
}
} ,
2017-08-22 20:39:45 +00:00
resetWorkingChallenge ( ) {
2017-07-31 19:54:52 +00:00
this . workingChallenge = {
name : '' ,
2017-08-22 20:39:45 +00:00
summary : '' ,
2017-07-31 19:54:52 +00:00
description : '' ,
categories : [ ] ,
group : '' ,
dailys : [ ] ,
habits : [ ] ,
leader : '' ,
members : [ ] ,
official : false ,
prize : 1 ,
rewards : [ ] ,
shortName : '' ,
todos : [ ] ,
} ;
2017-09-25 18:02:12 +00:00
this . $store . state . workingChallenge = { } ;
2017-07-31 19:54:52 +00:00
} ,
2017-08-03 20:04:03 +00:00
async createChallenge ( ) {
2017-08-22 20:39:45 +00:00
// @TODO: improve error handling, add it to updateChallenge, make errors translatable. Suggestion: `<% fieldName %> is required` where possible, where `fieldName` is inserted as the translatable string that's used for the field header.
2017-09-22 21:47:16 +00:00
let errors = [ ] ;
if ( ! this . workingChallenge . name ) errors . push ( this . $t ( 'nameRequired' ) ) ;
if ( this . workingChallenge . shortName . length < MIN _SHORTNAME _SIZE _FOR _CHALLENGES ) errors . push ( this . $t ( 'tagTooShort' ) ) ;
if ( ! this . workingChallenge . summary ) errors . push ( this . $t ( 'summaryRequired' ) ) ;
if ( this . workingChallenge . summary . length > MAX _SUMMARY _SIZE _FOR _CHALLENGES ) errors . push ( this . $t ( 'summaryTooLong' ) ) ;
if ( ! this . workingChallenge . description ) errors . push ( this . $t ( 'descriptionRequired' ) ) ;
if ( ! this . workingChallenge . group ) errors . push ( this . $t ( 'locationRequired' ) ) ;
if ( ! this . workingChallenge . categories || this . workingChallenge . categories . length === 0 ) errors . push ( this . $t ( 'categoiresRequired' ) ) ;
if ( errors . length > 0 ) {
alert ( errors . join ( '\n' ) ) ;
return ;
}
this . workingChallenge . timestamp = new Date ( ) . getTime ( ) ;
let categoryKeys = this . workingChallenge . categories ;
let serverCategories = [ ] ;
categoryKeys . forEach ( key => {
let catName = this . categoriesHashByKey [ key ] ;
serverCategories . push ( {
slug : key ,
name : catName ,
2017-08-26 02:56:21 +00:00
} ) ;
2017-09-22 21:47:16 +00:00
} ) ;
2017-07-31 19:54:52 +00:00
2017-09-25 18:02:12 +00:00
let challengeDetails = clone ( this . workingChallenge ) ;
challengeDetails . categories = serverCategories ;
let challenge = await this . $store . dispatch ( 'challenges:createChallenge' , { challenge : challengeDetails } ) ;
2017-09-22 21:47:16 +00:00
// @TODO: When to remove from guild instead?
this . user . balance -= this . workingChallenge . prize / 4 ;
2017-08-09 16:56:48 +00:00
2017-09-22 21:47:16 +00:00
this . $emit ( 'createChallenge' , challenge ) ;
this . resetWorkingChallenge ( ) ;
2017-09-07 19:26:53 +00:00
2017-09-22 21:47:16 +00:00
if ( this . cloning ) this . $store . state . challengeOptions . cloning = true ;
2017-09-07 19:26:53 +00:00
2017-09-22 21:47:16 +00:00
this . $root . $emit ( 'hide::modal' , 'challenge-modal' ) ;
this . $router . push ( ` /challenges/ ${ challenge . _id } ` ) ;
2017-07-20 20:52:46 +00:00
} ,
updateChallenge ( ) {
2017-08-23 03:58:13 +00:00
let categoryKeys = this . workingChallenge . categories ;
let serverCategories = [ ] ;
categoryKeys . forEach ( key => {
2017-09-25 18:02:12 +00:00
let newKey = key . trim ( ) ;
let catName = this . categoriesHashByKey [ newKey ] ;
2017-08-23 03:58:13 +00:00
serverCategories . push ( {
2017-09-25 18:02:12 +00:00
slug : newKey ,
2017-08-23 03:58:13 +00:00
name : catName ,
} ) ;
} ) ;
2017-09-25 18:02:12 +00:00
let challengeDetails = clone ( this . workingChallenge ) ;
challengeDetails . categories = serverCategories ;
2017-08-23 03:58:13 +00:00
2017-07-31 19:54:52 +00:00
this . $emit ( 'updatedChallenge' , {
2017-09-25 18:02:12 +00:00
challenge : challengeDetails ,
2017-07-31 19:54:52 +00:00
} ) ;
2017-09-25 18:02:12 +00:00
this . $store . dispatch ( 'challenges:updateChallenge' , { challenge : challengeDetails } ) ;
2017-08-22 20:39:45 +00:00
this . resetWorkingChallenge ( ) ;
2017-07-31 19:54:52 +00:00
this . $root . $emit ( 'hide::modal' , 'challenge-modal' ) ;
} ,
toggleCategorySelect ( ) {
this . showCategorySelect = ! this . showCategorySelect ;
2017-07-20 20:52:46 +00:00
} ,
} ,
} ;
< / script >