-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from copa-ch/feat/create-tournament
feat(home): add create tournament form
- Loading branch information
Showing
18 changed files
with
723 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,147 @@ | ||
<template> | ||
<section class="create-tournament-form"> | ||
<h1 class="title is-1 has-text-black">Create Form TODO</h1> | ||
<h1 class="subtitle is-2 has-text-black">Create Form TODO</h1> | ||
<section class="create-tournament-form has-text-left"> | ||
<h1 class="title is-2 has-text-black">{{ $t('createTournament.title') }}</h1> | ||
<h2 class="subtitle is-4 has-text-black">{{ $t('createTournament.subtitle') }}</h2> | ||
|
||
<transition name="fade"> | ||
<b-notification | ||
v-if="hasError" | ||
type="is-danger" | ||
aria-close-label="Close notification" | ||
role="alert"> | ||
{{ $t('createTournament.submitFailed') }} | ||
</b-notification> | ||
</transition> | ||
|
||
<TextInput icon="trophy" | ||
label="createTournament.name.label" | ||
placeholder="createTournament.name.placeholder" | ||
:state="nameState" | ||
:message="nameMessage" | ||
:loading="isPending" | ||
v-model="name"/> | ||
|
||
<TextInput icon="user" | ||
label="createTournament.owner.label" | ||
placeholder="createTournament.owner.placeholder" | ||
:state="ownerState" | ||
:message="ownerMessage" | ||
:loading="isPending" | ||
v-model="owner"/> | ||
|
||
<TextInput icon="envelope" | ||
label="createTournament.email.label" | ||
placeholder="createTournament.email.placeholder" | ||
:state="emailState" | ||
:message="emailMessage" | ||
:loading="isPending" | ||
v-model="email"/> | ||
|
||
<br> | ||
|
||
<b-button | ||
class="create-tournament-submit-button" | ||
type="is-primary" | ||
:disabled="isPending" | ||
:loading="isPending" | ||
@click="submit"> | ||
{{ $t('createTournament.submitButton') }} | ||
</b-button> | ||
|
||
</section> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import {defineComponent} from '@vue/composition-api' | ||
import {computed, defineComponent, ref, watch, Ref} from '@vue/composition-api' | ||
import TextInput from '@/app/components/form/TextInput.vue' | ||
import {useApiTournamentCreate} from '@/app/effects/tournament.api.effect' | ||
import isEmail from 'validator/lib/isEmail' | ||
import isEmpty from 'validator/lib/isEmpty' | ||
import Spinner from '@/app/components/layout/Spinner.vue' | ||
export default defineComponent({ | ||
setup() { | ||
components: { | ||
TextInput, Spinner, | ||
}, | ||
setup(props, {root}) { | ||
const isPristine = ref(true) | ||
const {name, nameState, nameMessage, isNameInvalid} = useNameInput(isPristine) | ||
const {owner, ownerState, ownerMessage, isOwnerInvalid} = useOwnerInput(isPristine) | ||
const {email, emailState, emailMessage, isEmailInvalid} = useEmailInput(isPristine) | ||
const {isPending, hasError, createTournament, createdTournament} = useApiTournamentCreate() | ||
const submit = async () => { | ||
isPristine.value = false | ||
if (!isNameInvalid.value && !isOwnerInvalid.value && !isEmailInvalid.value) { | ||
await createTournament({ | ||
name: name.value, | ||
owner: owner.value, | ||
email: email.value, | ||
}) | ||
if (!hasError) { | ||
await root.$router.push({ | ||
name: 'admin-teams', | ||
params: {id: createdTournament.value.adminId}, | ||
}) | ||
} | ||
} | ||
} | ||
return {} | ||
return { | ||
isPending, submit, hasError, | ||
name, nameState, nameMessage, | ||
owner, ownerState, ownerMessage, | ||
email, emailState, emailMessage, | ||
} | ||
}, | ||
}) | ||
function useNameInput(isPristine: Ref<boolean>) { | ||
const name = ref('') | ||
const isNameInvalid = ref(false) | ||
const isNamePristine = ref(true) | ||
watch(name, () => isNameInvalid.value = isEmpty(name.value)) | ||
return { | ||
nameState: computed(() => isPristine.value ? '' : isNameInvalid.value ? 'is-danger' : 'is-success'), | ||
nameMessage: computed(() => isNameInvalid.value && !isPristine.value ? 'createTournament.name.message' : ''), | ||
name, isNameInvalid, isNamePristine, | ||
} | ||
} | ||
function useOwnerInput(isPristine: Ref<boolean>) { | ||
const owner = ref('') | ||
const isOwnerInvalid = ref(false) | ||
const isOwnerPristine = ref(true) | ||
watch(owner, () => isOwnerInvalid.value = isEmpty(owner.value)) | ||
return { | ||
ownerState: computed(() => isPristine.value ? '' : isOwnerInvalid.value ? 'is-danger' : 'is-success'), | ||
ownerMessage: computed(() => isOwnerInvalid.value && !isPristine.value ? 'createTournament.owner.message' : ''), | ||
owner, isOwnerInvalid, isOwnerPristine, | ||
} | ||
} | ||
function useEmailInput(isPristine: Ref<boolean>) { | ||
const email = ref('') | ||
const isEmailInvalid = ref(false) | ||
const isEmailPristine = ref(true) | ||
watch(email, () => isEmailInvalid.value = !isEmail(email.value)) | ||
return { | ||
emailState: computed(() => isPristine.value ? '' : isEmailInvalid.value ? 'is-danger' : 'is-success'), | ||
emailMessage: computed(() => isEmailInvalid.value && !isPristine.value ? 'createTournament.email.message' : ''), | ||
email, isEmailInvalid, isEmailPristine, | ||
} | ||
} | ||
</script> | ||
|
||
<style lang="scss"> | ||
@import '../../styles/variables.scss'; | ||
@import 'src/styles/utilities/all'; | ||
@include mobile() { | ||
button.create-tournament-submit-button { | ||
display: flex; | ||
width: 100%; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<template> | ||
<div class="field copa-field" :class="loading ? 'is-loading':''"> | ||
<label class="label">{{ $t(label) }}</label> | ||
<p :class="`control ${hasIconLeft ? 'has-icons-left':''} has-icons-right`"> | ||
<input class="input" | ||
:class="state" | ||
:type="type" | ||
:disabled="disabled || loading" | ||
:readonly="loading" | ||
:maxlength="maxlength" | ||
:placeholder="$t(placeholder)" | ||
:value="modelValue" | ||
@input="onInput"/> | ||
<b-icon | ||
v-if="icon" | ||
class="is-left" | ||
:icon="icon" | ||
:pack="'fas'"/> | ||
<transition name="slide"> | ||
<b-icon | ||
v-if="hasIconRight && !loading" | ||
class="is-right" | ||
:class="iconRightClass" | ||
:icon="iconRight" | ||
:pack="'fas'"/> | ||
</transition> | ||
</p> | ||
<transition name="slide"> | ||
<p v-if="message" class="help" :class="iconRightClass">{{ $t(message) }}</p> | ||
</transition> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import {computed, defineComponent, ref, watch} from '@vue/composition-api' | ||
export default defineComponent({ | ||
props: { | ||
value: [Number, String], | ||
label: String, | ||
type: { | ||
type: String, | ||
default: 'text', | ||
}, | ||
state: String, | ||
message: String, | ||
icon: String, | ||
placeholder: String, | ||
maxlength: Number, | ||
loading: Boolean, | ||
disabled: Boolean, | ||
}, | ||
setup(props, {emit}) { | ||
const modelValue = ref(props.value) | ||
watch(() => props.value, (changedValue: any) => modelValue.value = changedValue) | ||
return { | ||
modelValue, | ||
onInput: (event: any) => { | ||
modelValue.value = event.target.value | ||
emit('input', modelValue.value) | ||
}, | ||
hasIconLeft: computed(() => !!props.icon), | ||
hasIconRight: computed(() => !!props.state || !!props.loading), | ||
iconRight: computed(() => { | ||
if (props.loading) { | ||
return '' | ||
} | ||
if (props.state) { | ||
if (props.state === 'is-success') { | ||
return 'check' | ||
} else if (props.state === 'is-danger') { | ||
return 'exclamation' | ||
} else if (props.state === 'is-warning') { | ||
return 'exclamation-triangle' | ||
} | ||
} | ||
return '' | ||
}), | ||
iconRightClass: computed(() => { | ||
if (props.state) { | ||
return `has-text-${props.state.substring(3)}` | ||
} | ||
return '' | ||
}), | ||
} | ||
}, | ||
}) | ||
</script> | ||
|
||
<style lang="scss"> | ||
@import 'src/styles/utilities/all'; | ||
.copa-field { | ||
position: relative; | ||
min-height: 105px; | ||
&:not(:last-child) { | ||
margin-bottom: 0.25rem; | ||
} | ||
input { | ||
border-left: none; | ||
border-right: none; | ||
border-top: none; | ||
border-bottom-width: 2px; | ||
border-radius: 4px 4px 0 0; | ||
outline: none !important; | ||
box-shadow: none !important; | ||
} | ||
&.is-loading input { | ||
background: linear-gradient(-45deg, $white-ter 40%, $white, $white-ter 60%); | ||
background-size: 300% 350%; | ||
animation: loading 2s infinite linear; | ||
animation-delay: 0s; | ||
border-color: $grey-lightest !important; | ||
} | ||
&:not(.is-loading) input:disabled { | ||
background: $white-bis; | ||
} | ||
&.is-loading .is-right { | ||
animation: rotating 2s linear infinite; | ||
color: $purple; | ||
} | ||
.control { | ||
&::after { | ||
content: ""; | ||
position: absolute; | ||
bottom: 0; | ||
height: 0; | ||
width: 0; | ||
left: 50%; | ||
transition: left 0.3s, width 0.3s, height 0.3s; | ||
background: linear-gradient(45deg, $purple, $turquoise); | ||
} | ||
} | ||
&:focus-within { | ||
.control::after { | ||
height: 2px; | ||
left: 0; | ||
width: 100%; | ||
} | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.