<template> <v-dialog v-model="dialog" @click:outside="onClose" max-width="900" overlay-color="white" content-class="rounded-xl" > <template v-slot:activator="{ on, attrs }"> <v-btn depressed dark color="grey" @click="dialog = true" block v-bind="attrs" v-on="on"> Register </v-btn> </template> <dialog-alert v-on:on-cancel="onClose" v-on:on-continue="onClose" v-if="registrationDisabled"> <template v-slot:title>Registration Disabled</template> <template v-slot:text> Registration is currently disabled by the Administrator! <v-form v-show="false" ref="form" v-model="form.valid" lazy-validation></v-form> <v-form v-show="false" ref="formMembers" v-model="form.validMembers" lazy-validation></v-form> </template> </dialog-alert> <v-card v-else> <v-card-title class="accent white--text pt-10 pb-4 text-h5 pl-9"> Register </v-card-title> <v-tabs v-model="tab" dark background-color="accent" fixed-tabs> <v-tab class="body-1">1. Create Team</v-tab> <v-tab class="body-1">2. Define Cycle Duration</v-tab> <v-tab class="body-1">3. Define Roles</v-tab> <v-tab class="body-1">4. Register Team</v-tab> </v-tabs> <v-card-text> <v-container fluid> <v-tabs-items v-model="tab" touchless> <v-tab-item eager> <v-form ref="form" v-model="form.valid" lazy-validation> <v-row no-gutters> <v-col cols="12"> <div class="text-h5 text--primary mb-3">Log In</div> <v-text-field label="Teamname" v-model="form.teamname" dense :disabled="registrationSuccess" outlined counter maxlength="64" :rules="[value => !!value || 'Teamname is required.']" ></v-text-field> </v-col> <v-col cols="12"> <div class="text-h5 text--primary mb-3">Password</div> <v-text-field label="Password" dense :disabled="registrationSuccess" v-model="form.password" :append-icon="form.show ? 'mdi-eye' : 'mdi-eye-off'" :type="form.show ? 'text' : 'password'" @click:append="form.show = !form.show" outlined :rules="[value => !!value || 'Password is required.']" ></v-text-field> </v-col> <v-col cols="12"> <div class="text-h5 text--primary mb-3">Confirm Password</div> <v-text-field label="Confirm Password" dense :disabled="registrationSuccess" v-model="form.password2" :append-icon="form.show ? 'mdi-eye' : 'mdi-eye-off'" :type="form.show ? 'text' : 'password'" @click:append="form.show = !form.show" outlined :rules="[value => !!value || 'Confirmation password is required.', value => value === form.password || 'Password confirmation does not match.']" ></v-text-field> </v-col> </v-row> </v-form> </v-tab-item> <v-tab-item eager> <v-row> <v-col cols="12" class="text--primary caption-2 my-5"> Cycles are time-bound development periods, ranging from 1 day to 3 weeks. Teams focus on specific sub-phases for building prototypes to test hypotheses. You can run more than one cycle according to your needs. For shorter projects like hackathons, opt for one-day cycle, while longer projects benefit from 1 to 3-week durations. Keep in mind that duration changes are only allowed when no active cycles are ongoing. </v-col> <v-col cols="12"> <div class="text-h5 text--primary mb-3">Cycle Duration</div> <v-select label="Select Cycle Duration" dense :items="form.sprintOptions" v-model="form.sprint" :disabled="registrationSuccess" item-value="value" outlined :rules="[value => !!value || 'Value is required.']" ></v-select> </v-col> </v-row> </v-tab-item> <v-tab-item eager> <v-row> <v-col cols="12" class="text--primary caption-2 my-5"> The team collaboration is enhanced by providing predefined accountabilities within PETRA. It is recommended to assign each role, but roles can be assigned multiple times (e.g., two Architects, three Component Designer, etc.). The following roles may be selected: <ul> <li><b>Project Manager (PM)</b> oversees workflow and ensures effective project management within the team. </li> <li><b>Problem Role (PR)</b> focuses on understanding user needs and ensuring product desirability. </li> <li><b>Tech Role (TR)</b> develops technically feasible solutions for the project. </li> <li><b>Business Role (BR)</b> analyzes the market and develops the project's business model. </li> </ul> </v-col> </v-row> <v-form ref="formMembers" v-model="form.validMembers" lazy-validation> <v-row> <v-col cols="12" md="4"> <div class="text-h5 text--primary mb-3">Add Team Member</div> <v-text-field label="Type Name" dense :disabled="registrationSuccess" v-model="form.name" outlined counter maxlength="64" :rules="[value => !!value || 'Name is required.', value => form.members.filter(member => member.name === value).length === 0 || 'Name already exists.']" ></v-text-field> </v-col> <v-col cols="12" md="4"> <div class="text-h5 text--primary mb-3">Role</div> <v-select label="Select Role" dense :disabled="registrationSuccess" :items="form.roleOptions" v-model="form.roleSelected" item-text="text" return-object outlined :rules="[value => !!value || 'Role is required.']" ></v-select> </v-col> <v-col cols="12" md="4"> <div class="text-h5 text--primary mb-3">Working Hours on Average</div> <v-select label="Select Working Hours" dense :disabled="registrationSuccess" :items="form.hoursOptions" v-model="form.hoursSelected" return-object outlined :rules="[value => !!value || 'Value is required.']" ></v-select> </v-col> <v-col cols="12" class="text-right"> <v-btn color="primary" depressed :disabled="registrationSuccess" @click="addMember"> Add Member </v-btn> </v-col> <v-col cols="12" v-for="(member, k) in form.members" :key="k" class="text-body-1 text--primary mb-2"> <v-avatar :color="colors[k % 4]" size="36" class="mr-3 text-uppercase white--text body-2" >{{ member.name.substr(0, 2) }} </v-avatar> {{ member.name }} - {{ member.role.text }} - {{ member.workingHours }}h <v-btn icon color="gray" class="ml-5" :disabled="registrationSuccess" @click="deleteMember(k)"> <v-icon>mdi-delete</v-icon> </v-btn> </v-col> </v-row> </v-form> </v-tab-item> <v-tab-item eager> <v-row> <v-col cols="12" md="6"> <div class="text-h5 text--primary">Summary</div> <v-row> <v-col cols="12"> <div class="text-body-1 font-weight-bold text--primary">Name</div> <div class="">{{ form.teamname }}</div> </v-col> <v-col cols="12"> <div class="text-body-1 font-weight-bold text--primary">Team</div> <div class="" v-for="(member, k) in form.members" :key="k"> {{ member.name }} - {{ member.role.text }} - {{ member.workingHours }}h </div> </v-col> </v-row> </v-col> <v-col cols="12" md="6"> <v-expand-transition> <v-alert outlined color="primary" class="ma-6" v-show="registrationSuccess"> <v-card class="" flat> <v-card-text class="text-center pt-10 pb-0"> <v-icon size="75" color="accent">mdi-information-outline</v-icon> </v-card-text> <v-card-title class="text-h5 justify-center"> Thank you for registering your Team! </v-card-title> <v-card-text class="text-center pb-10 text--primary"> Please contact the administrator (<a href="mailto:petra.lpl@ed.tum.de">petra.lpl@ed.tum.de</a>) to enable your dashboard. </v-card-text> </v-card> </v-alert> </v-expand-transition> </v-col> </v-row> </v-tab-item> </v-tabs-items> </v-container> </v-card-text> <v-card-actions class="pa-10"> <v-btn text @click="onClose" > Cancel </v-btn> <v-spacer></v-spacer> <v-btn v-if="tab < 3" color="primary" depressed min-width="150" @click="tab += 1" >Continue </v-btn> <v-btn v-else-if="registrationSuccess" color="primary" depressed min-width="150" @click="onClose()"> Exit </v-btn> <v-btn v-else color="primary" depressed min-width="150" :loading="loading" @click="onSubmit" > Register </v-btn> </v-card-actions> </v-card> <v-dialog v-model="dialogError" max-width="500px" overlay-color="white" style="z-index: 99" > <dialog-alert v-on:on-cancel="dialogError = false" v-on:on-continue="dialogError = false"> <template v-slot:title>Registration Error</template> <template v-slot:text> <div v-if="Object.keys(errors).length > 0"> <p v-for="(error, i) in errors" :key="i">{{ error }}</p> </div> <div v-else> Some fields are not valid. Fix errors to continue! </div> </template> </dialog-alert> </v-dialog> </v-dialog> </template> <script> import DialogAlert from "@/components/DialogAlert"; export default { name: "Register", components: {DialogAlert}, data: () => ({ dialog: false, tab: null, dialogError: null, form: { teamname: '', password: '', password2: '', show: false, members: [], name: '', roleOptions: [{ text: 'Project Manager (PM)', value: 'pm' }, { text: 'Problem Role (PR)', value: 'pr' }, { text: 'Tech Role (TR)', value: 'tr' }, { text: 'Business Role (BR)', value: 'br' }], roleSelected: '', rolesLongTerm: false, hoursOptions: [10, 20, 40], hoursSelected: '', sprintOptions: [ {text: '0.2 Weeks (1 Day)', value: 0.2}, {text: '0.6 Weeks (3 Days)', value: 0.6}, {text: '1 Week (5 Days)', value: 1}, {text: '2 Weeks (10 Days)', value: 2}, {text: '3 Weeks (15 Days)', value: 3}, ], sprint: '', }, valid: true, validMembers: true, errors: {}, remember: false, staySignedIn: true, fetchUser: true, // fixed i.e. always load team data from database (required) autoLogin: false, // registrations have to be approved by the admin i.e. login does not work & don't forget to enable tokens in /register endpoint loading: false, registrationSuccess: false, colors: ["error", "secondary", "primary", "info",], registrationDisabled: false, waitingForApproval: false, }), methods: { parse_errors(res) { this.errors = Object.fromEntries(res.data.errors.map(item => [item.field, item.msg])) }, addMember() { if (this.$refs.formMembers.validate()) { this.form.members.push({ name: this.form.name, role: this.form.roleSelected, workingHours: this.form.hoursSelected }) this.form.name = '' this.form.roleSelected = null this.$refs.formMembers.reset() } }, deleteMember(k) { this.form.members.splice(k, 1) }, onSubmit() { this.errors = {} if (this.$refs.form.validate()) { this.loading = true; this.$store.dispatch('auth/register', { data: this.form, remember: this.remember, fetchUser: this.fetchUser, autoLogin: this.autoLogin, staySignedIn: this.staySignedIn, }) .then(() => { // Success this.registrationSuccess = true; }, (res) => { // Error this.parse_errors(res.response) this.dialogError = true; }) } else { this.dialogError = true } this.loading = false }, onClose() { this.dialog = false this.$refs.form.reset() this.$refs.formMembers.reset() this.registrationSuccess = false; this.errors = {} this.form.members = [] this.tab = null } }, async created() { await this.$http.get('/settings', {}).then(res => { let settings = res.data.settings; this.registrationDisabled = !!settings.reg }) } } </script> <style scoped> </style>