mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
[WIP] New client challenges (#8842)
* Added initial challenge pages * Added challenge item and find guilds page * Added challenge detail * Added challenge modals * Ported over challenge service code * Ported over challenge ctrl code * Added styles and column * Minor modal updates * Removed duplicate keys * Fixed casing
This commit is contained in:
115
website/client/assets/svg/for-css/support-habitica-gems.svg
Normal file
115
website/client/assets/svg/for-css/support-habitica-gems.svg
Normal file
@@ -0,0 +1,115 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="319" height="86" viewBox="0 0 319 86">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#BDA8FF" d="M32.772 28.281l-5.886 1.607 4.35 4.364 4.351 4.365 1.536-5.972 1.536-5.971zM50.432 23.46l-5.886 1.608-5.887 1.606 4.35 4.365 4.351 4.364 1.536-5.97z"/>
|
||||
<path fill="#E7E0FF" d="M50.432 23.46l-1.536 5.972-1.536 5.971 5.887-1.607 5.886-1.606-4.35-4.365z"/>
|
||||
<path fill="#CDBEFF" d="M37.123 32.646l-1.536 5.97 5.887-1.606 5.886-1.606-4.35-4.365-4.351-4.365z"/>
|
||||
<path fill="#C8B6FF" d="M25.35 35.859l-1.536 5.971 5.886-1.607 5.887-1.606-4.35-4.365-4.352-4.364z"/>
|
||||
<path fill="#CDBEFF" d="M23.814 41.83l22.32 12.609-10.547-15.822z"/>
|
||||
<path fill="#BDA8FF" d="M47.374 35.4l-1.24 19.038 13.013-22.251z"/>
|
||||
<path fill="#E7E0FF" d="M35.587 38.617l10.547 15.822 1.226-19.036z"/>
|
||||
<g>
|
||||
<path fill="#CDC2FF" d="M40.85 1.097L39.314.75l.462 1.522.463 1.521 1.073-1.174 1.074-1.175z"/>
|
||||
<path fill="#CDC2FF" d="M45.458 2.139l-1.536-.348-1.536-.347.463 1.522.462 1.522 1.074-1.175z"/>
|
||||
<path fill="#EDE9FF" d="M45.458 2.139l-1.073 1.174-1.074 1.175 1.536.347 1.537.347-.463-1.522z"/>
|
||||
<path fill="#D9D1FF" d="M41.312 2.619L40.24 3.793l1.536.347 1.536.348-.462-1.522-.463-1.522z"/>
|
||||
<path fill="#D5CCFF" d="M38.24 1.924L37.167 3.1l1.536.347 1.536.347-.463-1.521L39.314.75z"/>
|
||||
<path fill="#D9D1FF" d="M37.167 3.099l3.559 5.577-.487-4.883z"/>
|
||||
<path fill="#CDC2FF" d="M43.315 4.488l-2.59 4.188 5.662-3.493z"/>
|
||||
<path fill="#EDE9FF" d="M40.239 3.793l.486 4.883 2.586-4.188z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#9A62FF" d="M32.47 28.036l-5.994 1.638 4.443 4.493 4.442 4.493 1.551-6.13 1.552-6.132zM50.452 23.124l-5.994 1.637-5.994 1.637 4.443 4.495 4.442 4.493 1.551-6.131z"/>
|
||||
<path fill="#DBC6FF" d="M50.452 23.124L48.9 29.255l-1.551 6.131 5.994-1.638 5.994-1.636-4.443-4.495z"/>
|
||||
<path fill="#B389FF" d="M36.912 32.53l-1.55 6.13 5.993-1.637 5.993-1.637-4.442-4.494-4.442-4.493z"/>
|
||||
<path fill="#AA7BFF" d="M24.925 35.804l-1.552 6.131 5.994-1.637 5.994-1.637-4.442-4.494-4.443-4.494z"/>
|
||||
<path fill="#B389FF" d="M23.373 41.935l22.77 13.004L35.36 38.66z"/>
|
||||
<path fill="#9A62FF" d="M47.363 35.382l-1.22 19.557L59.35 32.108z"/>
|
||||
<path fill="#DBC6FF" d="M35.361 38.66L46.142 54.94l1.207-19.553z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#9A62FF" d="M67.006 79.1l-1.25-.575.104 1.416.104 1.416 1.146-.84 1.147-.841z"/>
|
||||
<path fill="#9A62FF" d="M70.758 80.826l-1.25-.575-1.251-.575.104 1.416.104 1.415 1.146-.84z"/>
|
||||
<path fill="#DBC6FF" d="M70.758 80.826l-1.147.84-1.146.841 1.25.575 1.25.576-.103-1.416z"/>
|
||||
<path fill="#B389FF" d="M67.11 80.516l-1.146.841 1.25.575 1.25.575-.103-1.416-.104-1.415z"/>
|
||||
<path fill="#AA7BFF" d="M64.609 79.366l-1.147.84 1.25.576 1.251.575-.103-1.416-.104-1.416z"/>
|
||||
<path fill="#B389FF" d="M63.462 80.207l1.979 5.518.522-4.368z"/>
|
||||
<path fill="#9A62FF" d="M68.468 82.509l-3.027 3.216 5.528-2.066z"/>
|
||||
<path fill="#DBC6FF" d="M65.963 81.357l-.522 4.368 3.024-3.218z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#BDA8FF" d="M89.04 45.436l-3.876-2.038.147 4.42.147 4.419 3.728-2.382 3.729-2.382z"/>
|
||||
<path fill="#BDA8FF" d="M100.666 51.548l-3.876-2.037-3.875-2.038.147 4.42.147 4.42 3.728-2.383z"/>
|
||||
<path fill="#E7E0FF" d="M100.666 51.548l-3.729 2.382-3.728 2.382 3.876 2.037 3.875 2.038-.147-4.42z"/>
|
||||
<path fill="#CDBEFF" d="M89.186 49.855l-3.728 2.382 3.876 2.037 3.875 2.038-.147-4.42-.147-4.419z"/>
|
||||
<path fill="#C8B6FF" d="M81.436 45.78l-3.729 2.382 3.876 2.037 3.875 2.038-.147-4.42-.147-4.42z"/>
|
||||
<path fill="#CDBEFF" d="M77.707 48.162l5.544 17.54 2.207-13.465z"/>
|
||||
<path fill="#BDA8FF" d="M93.218 56.317L83.251 65.7l17.718-5.309z"/>
|
||||
<path fill="#E7E0FF" d="M85.458 52.237L83.25 65.701l9.96-9.389z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#6133B4" d="M3.54 59.3L.42 60.913l3.015 2.026 3.015 2.026.104-3.637.105-3.637zM12.899 54.469l-3.12 1.61-3.12 1.611 3.015 2.027 3.014 2.026.105-3.637z"/>
|
||||
<path fill="#C6B6E4" d="M12.899 54.469l-.106 3.637-.105 3.637 3.12-1.611 3.12-1.61-3.015-2.027z"/>
|
||||
<path fill="#8966C7" d="M6.554 61.327l-.104 3.637 3.119-1.611 3.12-1.61-3.015-2.027-3.015-2.026z"/>
|
||||
<path fill="#7A54C0" d="M.316 64.548L.21 68.185l3.12-1.61 3.12-1.611-3.015-2.026L.42 60.912z"/>
|
||||
<path fill="#8966C7" d="M.21 68.185l14.217 4.727-7.978-7.948z"/>
|
||||
<path fill="#6133B4" d="M12.696 61.739l1.73 11.173 4.509-14.394z"/>
|
||||
<path fill="#C6B6E4" d="M6.45 64.964l7.977 7.948-1.738-11.17z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#CDC2FF" d="M314 60.13l-.957.627 1.006.49 1.006.49-.048-1.117-.05-1.117zM316.873 58.249l-.957.627-.958.627 1.006.49 1.007.49-.05-1.117z"/>
|
||||
<path fill="#EDE9FF" d="M316.873 58.249l.049 1.117.049 1.117.957-.627.958-.627-1.006-.49z"/>
|
||||
<path fill="#D9D1FF" d="M315.007 60.62l.048 1.117.958-.627.958-.627-1.007-.49-1.006-.49z"/>
|
||||
<path fill="#D5CCFF" d="M313.091 61.874l.049 1.117.958-.627.957-.627-1.006-.49-1.007-.49z"/>
|
||||
<path fill="#D9D1FF" d="M313.14 62.99l4.637.834-2.722-2.087z"/>
|
||||
<path fill="#CDC2FF" d="M316.973 60.481l.804 3.343 1.111-4.597z"/>
|
||||
<path fill="#EDE9FF" d="M315.055 61.737l2.722 2.087-.806-3.341z"/>
|
||||
<g>
|
||||
<path fill="#CDC2FF" d="M219.597 71.759l-1.136.247.79.905.79.904.345-1.152.346-1.152zM223.002 71.016l-1.135.247-1.135.248.79.904.79.905.345-1.152z"/>
|
||||
<path fill="#EDE9FF" d="M223.002 71.016l-.345 1.152-.346 1.152 1.136-.248 1.135-.248-.79-.904z"/>
|
||||
<path fill="#D9D1FF" d="M220.386 72.663l-.345 1.152 1.135-.248 1.135-.247-.79-.905-.79-.904z"/>
|
||||
<path fill="#D5CCFF" d="M218.116 73.158l-.346 1.152 1.136-.247 1.135-.248-.79-.904-.79-.905z"/>
|
||||
<path fill="#D9D1FF" d="M217.77 74.31l4.14 2.723-1.87-3.218z"/>
|
||||
<path fill="#CDC2FF" d="M222.314 73.32l-.405 3.713 2.676-4.21z"/>
|
||||
<path fill="#EDE9FF" d="M220.04 73.815l1.87 3.218.401-3.713z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#9A62FF" d="M236.787 31.737l-5.608 3.681 6.05 3.112 6.048 3.113-.441-6.793-.44-6.794z"/>
|
||||
<path fill="#9A62FF" d="M253.614 20.695l-5.609 3.681-5.609 3.68 6.05 3.114 6.048 3.113-.44-6.794z"/>
|
||||
<path fill="#DBC6FF" d="M253.614 20.695l.44 6.794.44 6.794 5.608-3.681 5.61-3.68-6.05-3.114z"/>
|
||||
<path fill="#B389FF" d="M242.836 34.85l.44 6.793 5.61-3.68 5.608-3.68-6.05-3.114-6.048-3.112z"/>
|
||||
<path fill="#AA7BFF" d="M231.62 42.211l.44 6.794 5.608-3.681 5.609-3.68-6.05-3.114-6.049-3.112z"/>
|
||||
<path fill="#B389FF" d="M232.06 49.005l27.678 5.691-16.462-13.052z"/>
|
||||
<path fill="#9A62FF" d="M254.507 34.274l5.23 20.422 5.987-27.783z"/>
|
||||
<path fill="#DBC6FF" d="M243.277 41.643l16.46 13.053-5.243-20.414z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#6133B4" d="M276.012 56.987l-5.04-.954 1.671 4.94 1.67 4.938 3.37-3.985 3.369-3.985z"/>
|
||||
<path fill="#6133B4" d="M291.131 59.85l-5.04-.954-5.039-.955 1.67 4.94 1.67 4.939 3.37-3.985z"/>
|
||||
<path fill="#C6B6E4" d="M291.131 59.85l-3.37 3.985-3.368 3.985 5.04.954 5.039.954-1.67-4.94z"/>
|
||||
<path fill="#8966C7" d="M277.682 61.926l-3.368 3.985 5.04.954 5.038.955-1.67-4.94-1.67-4.939z"/>
|
||||
<path fill="#7A54C0" d="M267.603 60.018l-3.369 3.984 5.04.955 5.04.954-1.67-4.94-1.672-4.938z"/>
|
||||
<path fill="#8966C7" d="M264.234 64.002l12.19 17.88-2.11-15.971z"/>
|
||||
<path fill="#6133B4" d="M284.404 67.822l-7.98 14.059 18.06-12.15z"/>
|
||||
<path fill="#C6B6E4" d="M274.314 65.911l2.11 15.97 7.969-14.061z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#6133B4" d="M276.468 8.001l-1.219-.23.401 1.211.4 1.211.819-.98.818-.981zM280.126 8.692l-1.22-.23-1.218-.23.4 1.211.4 1.211.819-.98z"/>
|
||||
<path fill="#C6B6E4" d="M280.126 8.692l-.82.981-.818.981 1.22.23 1.218.231-.4-1.211z"/>
|
||||
<path fill="#8966C7" d="M276.869 9.213l-.819.98 1.22.23 1.218.231-.4-1.211-.4-1.211z"/>
|
||||
<path fill="#7A54C0" d="M274.431 8.752l-.819.98 1.22.231 1.218.23-.4-1.21-.4-1.212z"/>
|
||||
<path fill="#8966C7" d="M273.612 9.733l2.937 4.38-.499-3.92z"/>
|
||||
<path fill="#6133B4" d="M278.491 10.655l-1.942 3.458 4.38-2.997z"/>
|
||||
<path fill="#C6B6E4" d="M276.05 10.193l.499 3.92 1.94-3.459z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#BDA8FF" d="M298.634 21.087l-2.861-1.025.518 3.051.517 3.05 2.343-2.025 2.344-2.025zM307.216 24.164l-2.86-1.026-2.861-1.025.517 3.05.518 3.052 2.343-2.026z"/>
|
||||
<path fill="#E7E0FF" d="M307.216 24.164l-2.343 2.025-2.343 2.026 2.86 1.025 2.861 1.026-.517-3.052z"/>
|
||||
<path fill="#CDBEFF" d="M299.151 24.138l-2.343 2.026 2.861 1.025 2.86 1.026-.517-3.051-.517-3.051z"/>
|
||||
<path fill="#C8B6FF" d="M293.43 22.088l-2.344 2.025 2.861 1.025 2.861 1.026-.517-3.051-.518-3.051z"/>
|
||||
<path fill="#CDBEFF" d="M291.086 24.113l5.47 11.612.252-9.561z"/>
|
||||
<path fill="#BDA8FF" d="M302.536 28.217l-5.98 7.508 11.702-5.457z"/>
|
||||
<path fill="#E7E0FF" d="M296.808 26.164l-.252 9.56 5.974-7.51z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
3
website/client/assets/svg/member-icon.svg
Normal file
3
website/client/assets/svg/member-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 20 16">
|
||||
<path fill="#A5A1AC" fill-rule="evenodd" d="M14 14a5.968 5.968 0 0 0-1.537-4H14c2.206 0 4 1.794 4 4h-4zM2 14c0-2.206 1.794-4 4-4h2c2.206 0 4 1.794 4 4H2zM7 2a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm4.315.519a3 3 0 1 1 0 4.962A4.94 4.94 0 0 0 12 5a4.94 4.94 0 0 0-.685-2.481zm5.193 6.036A4.983 4.983 0 0 0 18 5c0-2.757-2.243-5-5-5-1.13 0-2.162.391-3 1.026A4.948 4.948 0 0 0 7 0C4.243 0 2 2.243 2 5c0 1.39.573 2.648 1.492 3.555A5.994 5.994 0 0 0 0 14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2 5.994 5.994 0 0 0-3.492-5.445z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 598 B |
@@ -24,7 +24,7 @@ div
|
||||
router-link.dropdown-item(:to="{name: 'tavern'}") {{ $t('tavern') }}
|
||||
router-link.dropdown-item(:to="{name: 'myGuilds'}") {{ $t('myGuilds') }}
|
||||
router-link.dropdown-item(:to="{name: 'guildsDiscovery'}") {{ $t('guildsDiscovery') }}
|
||||
router-link.nav-item(tag="li", :to="{name: 'challenges'}", exact)
|
||||
router-link.nav-item(tag="li", :to="{name: 'myChallenges'}", exact)
|
||||
a.nav-link(v-once) {{ $t('challenges') }}
|
||||
router-link.nav-item.dropdown(tag="li", to="/help", :class="{'active': $route.path.startsWith('/help')}")
|
||||
a.nav-link(v-once) {{ $t('help') }}
|
||||
|
||||
199
website/client/components/challenges/challengeDetail.vue
Normal file
199
website/client/components/challenges/challengeDetail.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
challenge-modal(:challenge='challenge')
|
||||
close-challenge-modal
|
||||
|
||||
.col-8.standard-page
|
||||
.row
|
||||
.col-8
|
||||
h1 {{challenge.name}}
|
||||
div
|
||||
strong(v-once) {{$t('createdBy')}}
|
||||
span {{challenge.author}}
|
||||
strong.margin-left(v-once)
|
||||
.svg-icon.calendar-icon(v-html="icons.calendarIcon")
|
||||
| {{$t('endDate')}}
|
||||
span {{challenge.endDate}}
|
||||
.tags
|
||||
span.tag(v-for='tag in challenge.tags') {{tag}}
|
||||
.col-4
|
||||
.box
|
||||
.svg-icon.member-icon(v-html="icons.memberIcon")
|
||||
| {{challenge.memberCount}}
|
||||
.details(v-once) {{$t('participants')}}
|
||||
.box
|
||||
.svg-icon.gem-icon(v-html="icons.gemIcon")
|
||||
| {{challenge.prize}}
|
||||
.details(v-once) {{$t('prize')}}
|
||||
.row
|
||||
task-column.col-6(v-for="column in columns", :type="column", :key="column")
|
||||
.col-4.sidebar.standard-page
|
||||
.acitons
|
||||
div(v-if='!isMember && !isLeader')
|
||||
button.btn.btn-success(v-once) {{$t('joinChallenge')}}
|
||||
div(v-if='isMember')
|
||||
button.btn.btn-danger(v-once) {{$t('leaveChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
button.btn.btn-success(v-once) {{$t('addTask')}}
|
||||
div(v-if='isLeader')
|
||||
button.btn.btn-secondary(v-once, @click='edit()') {{$t('editChallenge')}}
|
||||
div(v-if='isLeader')
|
||||
button.btn.btn-danger(v-once, @click='closeChallenge()') {{$t('endChallenge')}}
|
||||
.description-section
|
||||
h2(v-once) {{$t('challengeDescription')}}
|
||||
p {{challenge.description}}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
.margin-left {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.calendar-icon {
|
||||
width: 12px;
|
||||
display: inline-block;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.tag {
|
||||
border-radius: 30px;
|
||||
background-color: $gray-600;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-color: $gray-600;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: inline-block;
|
||||
padding: 1em;
|
||||
border-radius: 2px;
|
||||
background-color: $white;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
margin-left: 1em;
|
||||
width: 120px;
|
||||
height: 76px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
vertical-align: bottom;
|
||||
|
||||
.svg-icon {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
margin-right: .2em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: 12px;
|
||||
margin-top: 0.4em;
|
||||
color: $gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
.acitons {
|
||||
width: 100%;
|
||||
|
||||
div, button {
|
||||
width: 60%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.description-section {
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import closeChallengeModal from './closeChallengeModal';
|
||||
import Column from '../tasks/column';
|
||||
import challengeModal from './challengeModal';
|
||||
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import memberIcon from 'assets/svg/member-icon.svg';
|
||||
import calendarIcon from 'assets/svg/calendar.svg';
|
||||
|
||||
export default {
|
||||
props: ['challengeId'],
|
||||
components: {
|
||||
closeChallengeModal,
|
||||
challengeModal,
|
||||
TaskColumn: Column,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columns: ['habit', 'daily', 'todo', 'reward'],
|
||||
icons: Object.freeze({
|
||||
gemIcon,
|
||||
memberIcon,
|
||||
calendarIcon,
|
||||
}),
|
||||
challenge: {
|
||||
// _id: 1,
|
||||
// title: 'I am the Night! (Official TAKE THIS Challenge June 2017)',
|
||||
// memberCount: 5261,
|
||||
// endDate: '2017-04-04',
|
||||
// tags: ['Habitica Official', 'Tag'],
|
||||
// prize: 10,
|
||||
// description: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium.',
|
||||
// counts: {
|
||||
// habit: 0,
|
||||
// dailies: 2,
|
||||
// todos: 2,
|
||||
// rewards: 0,
|
||||
// },
|
||||
// author: 'SabreCat',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
isMember () {
|
||||
return this.user.challenges.indexOf(this.challenge._id) !== -1;
|
||||
},
|
||||
isLeader () {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.getChallenge();
|
||||
},
|
||||
methods: {
|
||||
async getChallenge () {
|
||||
this.challenge = await this.$store.dispatch('challenges:getChallenge', {challengeId: this.challengeId});
|
||||
},
|
||||
async joinChallenge () {
|
||||
// this.challenge = this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId});
|
||||
},
|
||||
async leaveChallenge () {
|
||||
// this.challenge = this.$store.dispatch('challenges:leaveChallenge', {challengeId: this.challengeId});
|
||||
},
|
||||
closeChallenge () {
|
||||
this.$root.$emit('show::modal', 'close-challenge-modal');
|
||||
},
|
||||
edit () {
|
||||
// @TODO: set working challenge
|
||||
this.$root.$emit('show::modal', 'challenge-modal');
|
||||
},
|
||||
// @TODO: view members
|
||||
},
|
||||
};
|
||||
</script>
|
||||
178
website/client/components/challenges/challengeItem.vue
Normal file
178
website/client/components/challenges/challengeItem.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template lang="pug">
|
||||
.card
|
||||
router-link(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
|
||||
h3 {{challenge.name}}
|
||||
.row
|
||||
.col-6
|
||||
div.details
|
||||
span
|
||||
.svg-icon.member-icon(v-html="icons.memberIcon")
|
||||
span {{challenge.memberCount}}
|
||||
span
|
||||
.svg-icon.calendar-icon(v-html="icons.calendarIcon")
|
||||
span
|
||||
strong End Date:
|
||||
span {{challenge.endDate}}
|
||||
div.tags
|
||||
span.tag(v-for='tag in challenge.tags') {{tag}}
|
||||
.col-6.prize-section
|
||||
div
|
||||
span.svg-icon.gem(v-html="icons.gemIcon")
|
||||
span.prize {{challenge.prize}}
|
||||
div Challenge Prize
|
||||
.row.description
|
||||
.col-12
|
||||
| {{challenge.description}}
|
||||
.container.well-wrapper(v-if='challenge.counts')
|
||||
.well.row
|
||||
.col-3
|
||||
.count-details
|
||||
.svg-icon.habit-icon(v-html="icons.habitIcon")
|
||||
span.count {{challenge.counts.habit}}
|
||||
div {{$t('habit')}}
|
||||
.col-3
|
||||
.count-details
|
||||
.svg-icon.daily-icon(v-html="icons.dailyIcon")
|
||||
span.count {{challenge.counts.dailies}}
|
||||
div {{$t('daily')}}
|
||||
.col-3
|
||||
.count-details
|
||||
.svg-icon.todo-icon(v-html="icons.todoIcon")
|
||||
span.count {{challenge.counts.todos}}
|
||||
div {{$t('todo')}}
|
||||
.col-3
|
||||
.count-details
|
||||
.svg-icon.reward-icon(v-html="icons.rewardIcon")
|
||||
span.count {{challenge.counts.rewards}}
|
||||
div {{$t('reward')}}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.card {
|
||||
background-color: $white;
|
||||
box-shadow: 0 2px 2px 0 $gray-600, 0 1px 4px 0 $gray-600;
|
||||
padding: 1em;
|
||||
height: 372px;
|
||||
|
||||
.gem {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.member-icon {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.calendar-icon {
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
color: $gray-200;
|
||||
margin-right: 1em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.details {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.tag {
|
||||
border-radius: 30px;
|
||||
background-color: $gray-600;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.prize {
|
||||
color: $gray-100;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.prize-section {
|
||||
text-align: right;
|
||||
padding-right: 2em;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $gray-200;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.well-wrapper {
|
||||
padding: .8em;
|
||||
}
|
||||
|
||||
.well {
|
||||
background-color: $gray-700;
|
||||
text-align: center;
|
||||
padding: 2em;
|
||||
border-radius: 4px;
|
||||
|
||||
.svg-icon {
|
||||
display: inline-block;
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.habit-icon {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.todo-icon {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.daily-icon {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.reward-icon {
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.count-details {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 20px;
|
||||
margin-left: .5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import memberIcon from 'assets/svg/member-icon.svg';
|
||||
import calendarIcon from 'assets/svg/calendar.svg';
|
||||
import habitIcon from 'assets/svg/habit.svg';
|
||||
import todoIcon from 'assets/svg/todo.svg';
|
||||
import dailyIcon from 'assets/svg/daily.svg';
|
||||
import rewardIcon from 'assets/svg/reward.svg';
|
||||
|
||||
export default {
|
||||
props: ['challenge'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gemIcon,
|
||||
memberIcon,
|
||||
calendarIcon,
|
||||
habitIcon,
|
||||
todoIcon,
|
||||
dailyIcon,
|
||||
rewardIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
160
website/client/components/challenges/challengeModal.vue
Normal file
160
website/client/components/challenges/challengeModal.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
b-modal#challenge-modal(:title="$t('createChallenge')", size='lg')
|
||||
form(@submit.stop.prevent="submit")
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('name')}}*
|
||||
b-form-input(type="text", :placeholder="$t('challengeNamePlaceHolder')", v-model="workingChallenge.name")
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('description')}}*
|
||||
div.description-count.float-right {{charactersRemaining}} {{ $t('charactersRemaining') }}
|
||||
b-form-input.description-textarea(type="text", textarea, :placeholder="$t('challengeDescriptionPlaceHolder')", v-model="workingChallenge.description")
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('guildInformation')}}*
|
||||
a.float-right {{ $t('markdownFormattingHelp') }}
|
||||
b-form-input.information-textarea(type="text", textarea, :placeholder="$t('challengeInformationPlaceHolder')", v-model="workingChallenge.information")
|
||||
.form-group(v-if='creating')
|
||||
label
|
||||
strong(v-once) {{$t('where')}}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(@click='sort(option.value)')
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('categories')}}*
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(@click='sort(option.value)')
|
||||
| Member
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('endDate')}}
|
||||
b-form-input.end-date-input
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('prize')}}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(@click='sort(option.value)')
|
||||
| Member
|
||||
.row.footer-wrap
|
||||
.col-12.text-center.submit-button-wrapper
|
||||
button.btn.btn-primary(v-once) {{$t('createChallenge')}}
|
||||
.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.5em;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.description-textarea {
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.information-textarea {
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 130px;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.end-date-input {
|
||||
width: 150px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.submit-button-wrapper {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.footer-wrap {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
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';
|
||||
|
||||
export default {
|
||||
props: ['challenge'],
|
||||
components: {
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
bFormInput,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
creating: true,
|
||||
charactersRemaining: 250,
|
||||
workingChallenge: {
|
||||
name: '',
|
||||
description: '',
|
||||
information: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
if (this.challenge) {
|
||||
this.workingChallenge = this.challenge;
|
||||
this.creating = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
maxPrize () {
|
||||
// var groupBalance = 0;
|
||||
// var group = _.find($scope.groups, { _id: gid });
|
||||
//
|
||||
// if (group && group.balance && group.leader === User.user._id) {
|
||||
// groupBalance = group.balance * 4;
|
||||
// }
|
||||
//
|
||||
// return groupBalance;
|
||||
// return userBalance + availableGroupBalance;
|
||||
},
|
||||
insufficientGemsForTavernChallenge () {
|
||||
// var balance = User.user.balance || 0;
|
||||
// var isForTavern = $scope.newChallenge.group == TAVERN_ID;
|
||||
//
|
||||
// if (isForTavern) {
|
||||
// return balance <= 0;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
createChallenge () {
|
||||
// this.$store.dispatch('challenges:createChallenge', {challenge: this.workingChallenge});
|
||||
},
|
||||
updateChallenge () {
|
||||
// this.$store.dispatch('challenges:updateChallenge', {challenge: this.workingChallenge});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
104
website/client/components/challenges/closeChallengeModal.vue
Normal file
104
website/client/components/challenges/closeChallengeModal.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
b-modal#close-challenge-modal(:title="$t('createGuild')", size='md')
|
||||
.header-wrap(slot="modal-header")
|
||||
h2.text-center(v-once) {{$t('endChallenge')}}
|
||||
.row.text-center
|
||||
.col-12
|
||||
.support-habitica
|
||||
// @TODO: Add challenge achievement badge here
|
||||
.col-12
|
||||
strong(v-once) {{$t('selectChallengeWinnersDescription')}}
|
||||
.col-12
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(@click='sort(option.value)')
|
||||
| Member
|
||||
.col-12
|
||||
button.btn.btn-primary(v-once) {{$t('awardWinners')}}
|
||||
.col-12
|
||||
hr
|
||||
.or {{$t('or')}}
|
||||
.col-12
|
||||
strong(v-once) {{$t('doYouWantedToDeleteChallenge')}}
|
||||
.col-12
|
||||
button.btn.btn-danger(v-once) {{$t('deleteChallenge')}}
|
||||
.footer-wrap(slot="modal-footer")
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
#close-challenge-modal {
|
||||
h2 {
|
||||
color: $purple-200
|
||||
}
|
||||
|
||||
#close-challenge-modal_modal_body {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.header-wrap {
|
||||
width: 100%;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.support-habitica {
|
||||
background-image: url('~client/assets/svg/for-css/support-habitica-gems.svg');
|
||||
width: 325px;
|
||||
height: 89px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.modal-footer, .modal-header {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.footer-wrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col-12 {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.or {
|
||||
margin-top: -2em;
|
||||
background: $white;
|
||||
width: 40px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
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';
|
||||
|
||||
export default {
|
||||
props: ['challenge'],
|
||||
components: {
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
closeChallenge () {
|
||||
// this.challenge = this.$store.dispatch('challenges:selectChallengeWinner', {
|
||||
// challengeId: this.challengeId,
|
||||
// winnerId: this.winnerId,
|
||||
// });
|
||||
},
|
||||
deleteChallenge () {
|
||||
// this.challenge = this.$store.dispatch('challenges:deleteChallenge', {challengeId: this.challengeId});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
89
website/client/components/challenges/findChallenges.vue
Normal file
89
website/client/components/challenges/findChallenges.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
challenge-modal
|
||||
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
|
||||
|
||||
.col-10.standard-page
|
||||
.row.header-row
|
||||
.col-md-8.text-left
|
||||
h1(v-once) {{$t('findChallenges')}}
|
||||
.col-md-4
|
||||
span.dropdown-label {{ $t('sortBy') }}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
|
||||
button.btn.btn-secondary.create-challenge-button
|
||||
.svg-icon.positive-icon(v-html="icons.positiveIcon")
|
||||
span(v-once, @click='createChallenge()') {{$t('createChallenge')}}
|
||||
.row
|
||||
.col-6(v-for='challenge in challenges')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.header-row {
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
.create-challenge-button {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.positive-icon {
|
||||
color: $green-10;
|
||||
width: 10px;
|
||||
display: inline-block;
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
import Sidebar from './sidebar';
|
||||
import ChallengeItem from './challengeItem';
|
||||
import challengeModal from './challengeModal';
|
||||
|
||||
import positiveIcon from 'assets/svg/positive.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sidebar,
|
||||
ChallengeItem,
|
||||
challengeModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
positiveIcon,
|
||||
}),
|
||||
challenges: [],
|
||||
sortOptions: [],
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.loadchallanges();
|
||||
|
||||
// @TODO: do we need to load groups for filters still?
|
||||
},
|
||||
methods: {
|
||||
updateSearch () {
|
||||
|
||||
},
|
||||
updateFilters () {
|
||||
|
||||
},
|
||||
createChallenge () {
|
||||
this.$root.$emit('show::modal', 'challenge-modal');
|
||||
},
|
||||
async loadchallanges () {
|
||||
this.challenges = await this.$store.dispatch('challenges:getUserChallenges');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
19
website/client/components/challenges/index.vue
Normal file
19
website/client/components/challenges/index.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
secondary-menu.col-12
|
||||
router-link.nav-link(:to="{name: 'myChallenges'}", :class="{'active': $route.name === 'myChallenges'}") {{ $t('myChallenges') }}
|
||||
router-link.nav-link(:to="{name: 'findChallenges'}", :class="{'active': $route.name === 'findChallenges'}") {{ $t('findChallenges') }}
|
||||
|
||||
.col-12
|
||||
router-view
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SecondaryMenu from 'client/components/secondaryMenu';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
142
website/client/components/challenges/myChallenges.vue
Normal file
142
website/client/components/challenges/myChallenges.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template lang="pug">
|
||||
.row
|
||||
challenge-modal
|
||||
sidebar(v-on:search="updateSearch", v-on:filter="updateFilters")
|
||||
|
||||
.col-10.standard-page
|
||||
.row.header-row
|
||||
.col-md-8.text-left
|
||||
h1(v-once) {{$t('myChallenges')}}
|
||||
.col-md-4
|
||||
span.dropdown-label {{ $t('sortBy') }}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
|
||||
button.btn.btn-secondary.create-challenge-button
|
||||
.svg-icon.positive-icon(v-html="icons.positiveIcon")
|
||||
span(v-once, @click='createChallenge()') {{$t('createChallenge')}}
|
||||
|
||||
.row
|
||||
.no-challenges.text-center.col-md-6.offset-3(v-if='challenges.length === 0')
|
||||
.svg-icon(v-html="icons.challengeIcon")
|
||||
h2(v-once) {{$t('noChallengeTitle')}}
|
||||
p(v-once) {{$t('challengeDescription1')}}
|
||||
p(v-once) {{$t('challengeDescription2')}}
|
||||
|
||||
.row
|
||||
.col-6(v-for='challenge in challenges')
|
||||
challenge-item(:challenge='challenge')
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.header-row {
|
||||
h1 {
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
.create-challenge-button {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.positive-icon {
|
||||
color: $green-10;
|
||||
width: 10px;
|
||||
display: inline-block;
|
||||
margin-right: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
.no-challenges {
|
||||
color: $gray-300;
|
||||
margin-top: 10em;
|
||||
|
||||
h2 {
|
||||
color: $gray-300;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 88.7px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
import Sidebar from './sidebar';
|
||||
import ChallengeItem from './challengeItem';
|
||||
import challengeModal from './challengeModal';
|
||||
|
||||
import challengeIcon from 'assets/svg/challenge.svg';
|
||||
import positiveIcon from 'assets/svg/positive.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sidebar,
|
||||
ChallengeItem,
|
||||
challengeModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
challengeIcon,
|
||||
positiveIcon,
|
||||
}),
|
||||
challenges: [
|
||||
// {
|
||||
// _id: 1,
|
||||
// title: 'I am the Night! (Official TAKE THIS Challenge June 2017)',
|
||||
// memberCount: 5261,
|
||||
// endDate: '2017-04-04',
|
||||
// tags: ['Habitica Official', 'Tag'],
|
||||
// prize: 10,
|
||||
// description: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium.',
|
||||
// counts: {
|
||||
// habit: 0,
|
||||
// dailies: 2,
|
||||
// todos: 2,
|
||||
// rewards: 0,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// _id: 2,
|
||||
// title: '30-Day Money Cleanse 💰',
|
||||
// memberCount: 112,
|
||||
// endDate: '2017-04-05',
|
||||
// tags: ['Owned', 'Tag'],
|
||||
// prize: 10,
|
||||
// description: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa.',
|
||||
// counts: {
|
||||
// habit: 0,
|
||||
// dailies: 2,
|
||||
// todos: 30,
|
||||
// rewards: 0,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
sortOptions: [],
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.loadchallanges();
|
||||
},
|
||||
methods: {
|
||||
updateSearch () {
|
||||
|
||||
},
|
||||
updateFilters () {
|
||||
|
||||
},
|
||||
createChallenge () {
|
||||
this.$root.$emit('show::modal', 'challenge-modal');
|
||||
},
|
||||
async loadchallanges () {
|
||||
this.challenges = await this.$store.dispatch('challenges:getUserChallenges');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
156
website/client/components/challenges/sidebar.vue
Normal file
156
website/client/components/challenges/sidebar.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template lang="pug">
|
||||
.col-2.standard-sidebar
|
||||
.form-group
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
|
||||
form
|
||||
h3(v-once) {{ $t('filter') }}
|
||||
.form-group
|
||||
h5 Category
|
||||
.form-check(
|
||||
v-for="group in categoryOptions",
|
||||
:key="group.key",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value='group.key' v-model="categoryFilters")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||
.form-group
|
||||
h5 Membership
|
||||
.form-check(
|
||||
v-for="group in roleOptions",
|
||||
:key="group.key",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value='group.key' v-model="roleFilters")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||
.form-group
|
||||
h5 Guild Size
|
||||
.form-check(
|
||||
v-for="group in guildSizeOptions",
|
||||
:key="group.key",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value='group.key' v-model="guildSizeFilters")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ $t(group.label) }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import throttle from 'lodash/throttle';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
categoryFilters: [],
|
||||
categoryOptions: [
|
||||
{
|
||||
label: 'habiticaOfficial',
|
||||
key: 'official',
|
||||
},
|
||||
{
|
||||
label: 'animals',
|
||||
key: 'animals',
|
||||
},
|
||||
{
|
||||
label: 'artDesign',
|
||||
key: 'art_design',
|
||||
},
|
||||
{
|
||||
label: 'booksWriting',
|
||||
key: 'books_writing',
|
||||
},
|
||||
{
|
||||
label: 'comicsHobbies',
|
||||
key: 'comics_hobbies',
|
||||
},
|
||||
{
|
||||
label: 'diyCrafts',
|
||||
key: 'diy_crafts',
|
||||
},
|
||||
{
|
||||
label: 'education',
|
||||
key: 'education',
|
||||
},
|
||||
{
|
||||
label: 'foodCooking',
|
||||
key: 'food_cooking',
|
||||
},
|
||||
{
|
||||
label: 'healthFitness',
|
||||
key: 'health_fitness',
|
||||
},
|
||||
{
|
||||
label: 'music',
|
||||
key: 'music',
|
||||
},
|
||||
{
|
||||
label: 'relationship',
|
||||
key: 'relationship',
|
||||
},
|
||||
{
|
||||
label: 'scienceTech',
|
||||
key: 'science_tech ',
|
||||
},
|
||||
],
|
||||
roleFilters: [],
|
||||
roleOptions: [
|
||||
{
|
||||
label: 'participating',
|
||||
key: 'participating',
|
||||
},
|
||||
{
|
||||
label: 'not_participating',
|
||||
key: 'not_participating',
|
||||
},
|
||||
{
|
||||
label: 'either',
|
||||
key: 'either',
|
||||
},
|
||||
],
|
||||
guildSizeFilters: [],
|
||||
guildSizeOptions: [
|
||||
{
|
||||
label: 'owned',
|
||||
key: 'owned',
|
||||
},
|
||||
{
|
||||
label: 'not_owned',
|
||||
key: 'not_owned',
|
||||
},
|
||||
{
|
||||
label: 'either',
|
||||
key: 'either',
|
||||
},
|
||||
],
|
||||
searchTerm: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
categoryFilters: function categoryFilters () {
|
||||
this.emitFilters();
|
||||
},
|
||||
roleFilters: function roleFilters () {
|
||||
this.emitFilters();
|
||||
},
|
||||
guildSizeFilters: function guildSizeFilters () {
|
||||
this.emitFilters();
|
||||
},
|
||||
searchTerm: throttle(function searchTerm (newSearch) {
|
||||
this.$emit('search', {
|
||||
searchTerm: newSearch,
|
||||
});
|
||||
}, 250),
|
||||
},
|
||||
methods: {
|
||||
emitFilters () {
|
||||
this.$emit('filter', {
|
||||
categories: this.categoryFilters,
|
||||
roles: this.roleFilters,
|
||||
guildSize: this.guildSizeFilters,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -24,26 +24,26 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
.sort-select {
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
.no-guilds {
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
margin-top: 15em;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
.sort-select {
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
.no-guilds-wrapper {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
.no-guilds {
|
||||
text-align: center;
|
||||
color: $gray-200;
|
||||
margin-top: 15em;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
}
|
||||
|
||||
.no-guilds-wrapper {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -72,6 +72,12 @@ const MyGuilds = () => import(/* webpackChunkName: "guilds" */ './components/gui
|
||||
const GuildsDiscoveryPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/discovery');
|
||||
const GuildPage = () => import(/* webpackChunkName: "guilds" */ './components/guilds/guild');
|
||||
|
||||
// Challenges
|
||||
const ChallengeIndex = () => import(/* webpackChunkName: "challenges" */ './components/challenges/index');
|
||||
const MyChallenges = () => import(/* webpackChunkName: "challenges" */ './components/challenges/myChallenges');
|
||||
const FindChallenges = () => import(/* webpackChunkName: "challenges" */ './components/challenges/findChallenges');
|
||||
const ChallengeDetail = () => import(/* webpackChunkName: "challenges" */ './components/challenges/challengeDetail');
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const router = new VueRouter({
|
||||
@@ -124,7 +130,29 @@ const router = new VueRouter({
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'challenges', path: 'challenges', component: Page },
|
||||
{
|
||||
name: 'challenges',
|
||||
path: '/challenges',
|
||||
component: ChallengeIndex,
|
||||
children: [
|
||||
{
|
||||
name: 'myChallenges',
|
||||
path: 'myChallenges',
|
||||
component: MyChallenges,
|
||||
},
|
||||
{
|
||||
name: 'findChallenges',
|
||||
path: 'findChallenges',
|
||||
component: FindChallenges,
|
||||
},
|
||||
{
|
||||
name: 'challenge',
|
||||
path: 'challenges/:challengeId',
|
||||
component: ChallengeDetail,
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
component: ParentPage,
|
||||
|
||||
75
website/client/store/actions/challenges.js
Normal file
75
website/client/store/actions/challenges.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import axios from 'axios';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export async function createChallenge (store, payload) {
|
||||
let response = await axios.post('/api/v3/challenges', {
|
||||
data: payload.challenge,
|
||||
});
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function joinChallenge (store, payload) {
|
||||
let response = await axios.post(`/api/v3/challenges/${payload.challengeId}/join`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function leaveChallenge (store, payload) {
|
||||
let response = await axios.post(`/api/v3/challenges/${payload.challengeId}/join`, {
|
||||
data: {
|
||||
keep: payload.keep,
|
||||
},
|
||||
});
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function getUserChallenges () {
|
||||
let response = await axios.get('/api/v3/challenges/user');
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function getGroupChallenges (store, payload) {
|
||||
let response = await axios.get(`/api/v3/challenges/groups/${payload.groupId}`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function getChallenge (store, payload) {
|
||||
let response = await axios.get(`/api/v3/challenges/${payload.challengeId}`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function exportChallengeCsv (store, payload) {
|
||||
let response = await axios.get(`/api/v3/challenges/challenges/${payload.challengeId}/export/csv`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
|
||||
export async function updateChallenge (store, payload) {
|
||||
let challengeDataToSend = omit(payload.challenge, ['tasks', 'habits', 'todos', 'rewards', 'group']);
|
||||
|
||||
if (challengeDataToSend.leader && challengeDataToSend.leader._id) challengeDataToSend.leader = challengeDataToSend.leader._id;
|
||||
|
||||
let response = await axios.post(`/api/v3/challenges/${payload.challenge._id}`, {
|
||||
data: challengeDataToSend,
|
||||
});
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function deleteChallenge (store, payload) {
|
||||
let response = await axios.delete(`/api/v3/challenges/challenges/${payload.challengeId}`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function selectChallengeWinner (store, payload) {
|
||||
let response = await axios.post(`/api/v3/challenges/challenges/${payload.challengeId}/selectWinner/${payload.winnerId}`);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import * as party from './party';
|
||||
import * as members from './members';
|
||||
import * as auth from './auth';
|
||||
import * as quests from './quests';
|
||||
import * as challenges from './challenges';
|
||||
|
||||
// Actions should be named as 'actionName' and can be accessed as 'namespace:actionName'
|
||||
// Example: fetch in user.js -> 'user:fetch'
|
||||
@@ -21,6 +22,7 @@ const actions = flattenAndNamespace({
|
||||
members,
|
||||
auth,
|
||||
quests,
|
||||
challenges,
|
||||
});
|
||||
|
||||
export default actions;
|
||||
|
||||
@@ -186,5 +186,27 @@
|
||||
"emptyMessagesLine1": "You don’t have any messages",
|
||||
"emptyMessagesLine2": "Send a message to start a conversation!",
|
||||
"contributing": "Contributing",
|
||||
"askAQuestion": "Ask a Question"
|
||||
"askAQuestion": "Ask a Question",
|
||||
"myChallenges": "My Challenges",
|
||||
"findChallenges": "Find Challenges",
|
||||
"noChallengeTitle": "You don’t have any Challenges.",
|
||||
"challengeDescription1": "Challenges are community events in which players compete and earn prizes by completing a group of related tasks.",
|
||||
"challengeDescription2": "Find recommended Challenges based on your interests, browse Habitica’s public Challenges, or create your own Challenges.",
|
||||
"createdBy": "Created By:",
|
||||
"endDate": "End Date:",
|
||||
"joinChallenge": "Join Challenge",
|
||||
"leaveChallenge": "Leave Challenge",
|
||||
"addTask": "Add Task",
|
||||
"editChallenge": "Edit Challenge",
|
||||
"challengeDescription": "Challenge Description",
|
||||
"selectChallengeWinnersDescription": "Select winners from the Challenge participants",
|
||||
"awardWinners": "Award Winners",
|
||||
"doYouWantedToDeleteChallenge": "Do you want to delete this Callenge?",
|
||||
"deleteChallenge": "Delete Challenge",
|
||||
"challengeNamePlaceHolder": "What is your Challenge name?",
|
||||
"challengeDescriptionPlaceHolder": "Write a short description advertising your Challenge to other Habiticans. What is the main purpose of your Challenge and why should people join it? Try to include useful keywords in the description so that Habiticans can easily find it when they search!",
|
||||
"markdownFormattingHelp": "Markdown formatting help",
|
||||
"challengeInformationPlaceHolder": "Write a short description advertising your Challenge to other Habiticans. What is the main purpose of your Challenge and why should people join it? Try to include useful keywords in the description so that Habiticans can easily find it when they search!",
|
||||
"where": "Where*",
|
||||
"challengeMinimum": "Minimum 1 Gem for public Challenges (helps prevent spam, it really does)."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user