Skip to content
Snippets Groups Projects
Commit eda2530f authored by Erik Borgeteien Hansen's avatar Erik Borgeteien Hansen
Browse files

Merge branch 'register-view-tailwind' into 'main'

Register view tailwind

See merge request !10
parents 401bd0b5 3b9d719e
No related branches found
No related tags found
1 merge request!10Register view tailwind
Pipeline #175879 passed
.DS_Store
node_modules
/dist
coverage
# local env files
......
......@@ -40,6 +40,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"jest": "^27.0.5",
"jest-serializer-vue": "^2.0.2",
"postcss": "^8.4.12",
"prettier": "^2.4.1",
"tailwindcss": "^3.0.24"
......@@ -58,15 +58,14 @@ export default {
rules: {
required: (value) => !!value || "Feltet er påkrevd",
min: (v) => v.length >= 8 || "Minimum 8 tegn",
passwordConfirmation: (v) => v === this.user.password || "Passordene må være like"
passwordConfirmation: (v) =>
v === this.user.password || "Passordene må være like",
},
};
},
methods: {
async setNewPassword() {
},
async setNewPassword() {},
validate() {
this.$refs.form.validate();
},
......
<template>
<v-form ref="form" v-model="valid" lazy-validation>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
<v-text-field
v-model="password"
:counter="32"
:rules="passwordRules"
label="Passord"
:append-icon="passwordHidden ? 'mdi-eye' : 'mdi-eye-off'"
:type="passwordHidden ? 'text' : 'password'"
@click:append="passwordHidden = !passwordHidden"
required
></v-text-field>
<v-container class="grey lighten-5">
<v-row>
<v-text-field
class="pr-2"
v-model="firstName"
:counter="32"
:rules="firstNameRules"
label="Fornavn"
required
></v-text-field>
<v-text-field
class="pl-2"
v-model="lastName"
:counter="32"
:rules="lastNameRules"
label="Etternavn"
required
></v-text-field>
</v-row>
</v-container>
<v-text-field
v-model="address"
:counter="32"
:rules="addressRules"
label="Addresse"
required
></v-text-field>
<!-- <v-text-field
v-model="confirmPassword"
:rules="confirmPasswordRules"
label="Bekreft passord"
:append-icon="confirmPasswordHidden ? 'mdi-eye' : 'mdi-eye-off'"
:type="confirmPasswordHidden ? 'text' : 'password'"
@click:append="confirmPasswordHidden = !confirmPasswordHidden"
required
></v-text-field> -->
<!-- <v-select
v-model="select"
:items="items"
:rules="[(v) => !!v || 'Item is required']"
label="Item"
required
></v-select> -->
<!-- <v-checkbox
v-model="checkbox"
:rules="[(v) => !!v || 'You must agree to continue!']"
label="Do you agree?"
required
></v-checkbox> -->
<v-btn :disabled="!valid" color="success" class="mr-4" @click="submit()"
>Registrer</v-btn
>
<v-btn color="error" class="mr-4" @click="reset()">Tøm felter</v-btn>
</v-form>
<section
class="max-w-4xl p-6 mx-auto bg-white rounded-md shadow-md dark:bg-gray-800"
>
<h2 class="text-lg font-semibold text-gray-700 capitalize dark:text-white">
Opprett ny bruker
</h2>
<form @submit.prevent>
<div class="grid grid-cols-1 gap-6 mt-4 sm:grid-cols-2">
<div>
<label class="text-gray-700 dark:text-gray-200" for="email"
>E-mail</label
>
<input
v-model="email"
id="email"
type="email"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="password"
>Passord</label
>
<input
v-model="password"
id="password"
type="password"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="confirmPassword"
>Bekreft Passord</label
>
<input
v-model="confirmPassword"
id="confirmPassword"
type="password"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="firstName"
>Fornavn</label
>
<input
data-test="firstNameTest"
v-model="firstName"
id="firstName"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="lastName"
>Etternavn</label
>
<input
v-model="lastName"
id="lastName"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="address"
>Addresse</label
>
<input
v-model="address"
id="address"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
</div>
<div class="flex justify-end mt-6">
<button
class="px-6 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded-md hover:bg-gray-600 focus:outline-none focus:bg-gray-600"
@click="submit()"
type="submit"
:disabled="loading"
>
<div v-if="loading">
<div v-if="loading" class="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div v-else>Lagre</div>
</button>
</div>
</form>
</section>
<ul data-test="errorMessageList">
<li v-if="errorMessage" data-test="customErrorMsg">{{ errorMessage }}</li>
<li v-for="error of v$.$errors" :key="error.$uid">
<!-- {{ error.$validator }} -->
Field
{{ error.$property }}
has error:
{{ error.$message }}
</li>
</ul>
</template>
<script>
import axios from "axios";
import useVuelidate from "@vuelidate/core";
import { doLogin, registerUser } from "@/utils/apiutil";
import { required, email, minLength, sameAs } from "@vuelidate/validators";
export default {
data: () => ({
passwordHidden: false,
// confirmPasswordHidden: false,
valid: true,
firstName: "",
firstNameRules: [
(v) => !!v || "Fornavn er påkrevd",
(v) => (v && v.length <= 32) || "Fornavn må være mindre enn 32 bokstaver",
],
lastName: "",
lastNameRules: [
(v) => !!v || "Etternavn er påkrevd",
(v) =>
(v && v.length <= 32) || "Etternavn må være mindre enn 32 bokstaver",
],
address: "",
addressRules: [
(v) => !!v || "Addresse er påkrevd",
(v) =>
(v && v.length <= 32) || "Addresse må være mindre enn 32 bokstaver",
],
password: "",
passwordRules: [
(v) => !!v || "Passord er påkrevd",
(v) => (v && v.length <= 32) || "Passord må være mindre enn 32 tegn",
(v) => (v && v.length >= 8) || "Passord må være større enn 8 tegn",
],
// confirmPassword: "",
// confirmPasswordRules: [
// (v) => !!v || "Passord er påkrevd",
// (v) => (v && v.length <= 32) || "Passord må være mindre enn 32 bokstaver",
// // (v) => v === this.password || "Passordene må være like",
// ],
email: "",
emailRules: [
(v) => !!v || "E-mail is required",
(v) => /.+@.+\..+/.test(v) || "E-mail must be valid",
],
// select: null,
// items: ["Item 1", "Item 2", "Item 3", "Item 4"],
// checkbox: false,
}),
// const isEmailTaken = (value) =>
// fetch(`/api/unique/${value}`).then((r) => r.json()); // check the email in the server
export default {
setup: () => ({ v$: useVuelidate() }),
data() {
return {
errorMessage: "",
loading: false,
email: "",
password: "",
confirmPassword: "",
firstName: "",
lastName: "",
address: "",
};
},
validations() {
return {
email: {
required,
email,
// isUnique: helpers.withAsync(isEmailTaken),
},
password: {
required,
minLength: minLength(8),
},
confirmPassword: { sameAs: sameAs(this.password) },
firstName: { required },
lastName: { required },
address: { required },
};
},
methods: {
submit() {
console.log("Attempting to register user");
this.valid = this.$refs.form.validate();
if (!this.valid) return;
this.valid = false;
console.log("User is validated");
axios
.post("http://localhost:3000/api/register", {
email: this.email,
firstName: this.firstName,
lastname: this.lastName,
password: this.password,
address: this.address,
})
.then(console.log("Sent"))
.catch((e) => console.log(e));
async submit() {
//Display loading symbol
this.loading = true;
//Validate form
const result = await this.v$.$validate();
if (!result) {
this.loading = false;
return;
}
//Send a request to create a user and save success as a bool
const userCreated = await this.sendRegisterRequest();
//If a user is created succsessfully, try to login
//If we get this far, we will be pushed anyway so there is no point updating "loading"
if (!userCreated) {
this.errorMessage = "Could not create user.";
return;
}
const loginRequest = {
email: this.email,
password: this.password,
};
const loginResponse = await doLogin(loginRequest);
if (loginResponse === "Failed login") {
this.errorMessage = "Failed to log in with new user";
this.$store.commit("logout");
this.$router.push("/login");
return;
}
this.$store.commit("saveToken", loginResponse);
this.$router.push("/");
},
reset() {
this.$refs.form.reset();
this.$refs.form.resetValidation();
this.valid = true;
async sendRegisterRequest() {
const registerInfo = {
email: this.email,
firstName: this.firstName,
lastname: this.lastName,
password: this.password,
address: this.address,
};
const response = await registerUser(registerInfo);
if (response.status === 200) return true;
return false;
},
},
};
</script>
<style scoped>
/* https://loading.io/css/ */
.lds-ring {
display: inline-block;
position: relative;
width: 20px;
height: 20px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 16px;
height: 16px;
margin: 2px;
border: 2px solid #fff;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
import axios from "axios";
const API_URL = process.env.VUE_APP_BASEURL;
export function doLogin(loginRequest) {
return axios
.post(process.env.VUE_APP_BASEURL + "login/authentication", loginRequest)
.post(API_URL + "login/authentication", loginRequest)
.then((response) => {
return response.data;
})
......@@ -11,3 +13,18 @@ export function doLogin(loginRequest) {
return error.response;
});
}
export function registerUser(registerInfo) {
return axios
.post(API_URL + "register", {
email: registerInfo.email,
firstName: registerInfo.firstName,
lastname: registerInfo.lastname,
password: registerInfo.password,
address: registerInfo.address,
})
.then((response) => {
return response;
})
.catch((err) => console.log(err));
}
<template>
<register-form-component id="form" class="pa-8" />
<div class="h-screen bg-gray-200 content-center grid place-items-center">
<RegisterFormComponent />
</div>
</template>
<script>
......@@ -11,10 +13,3 @@ export default {
},
};
</script>
<style scoped>
#form {
max-width: 600px;
margin: auto;
}
</style>
module.exports = {
darkMode: "class",
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
theme: {
extend: {},
......
import { mount } from "@vue/test-utils";
import RegisterFormComponent from "@/components/RegisterFormComponent";
describe("RegisterFormComponent", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(RegisterFormComponent);
});
it("renders correctly", () => {
expect(wrapper.element).toMatchSnapshot();
});
it("is instantiated", () => {
expect(wrapper.exists()).toBeTruthy();
});
it("renders error message to user", async () => {
await wrapper.setData({ errorMessage: "test message" });
expect(wrapper.find('li[data-test="customErrorMsg"]').text()).toBe(
"test message"
);
});
it("renders the h2 text correctly", () => {
expect(wrapper.find("h2").text()).toBe("Opprett ny bruker");
});
it("has a button", () => {
expect(wrapper.exists("button")).toBe(true);
});
it("updates data when field is updated", async () => {
await wrapper.find('input[data-test="firstNameTest"]').setValue("Gunnar");
expect(wrapper.vm.firstName).toBe("Gunnar");
});
it("displays 5 error messages when submit is clicked with no data", async () => {
await wrapper.find("button").trigger("click");
expect(wrapper.findAll("li").length).toBe(5);
});
/* it("button click with correct sum", () => {
wrapper.setData({ guess: "15" });
const button = wrapper.find("button");
button.trigger("click");
expect(wrapper.vm.message).toBe("SUCCESS!");
}); */
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RegisterFormComponent renders correctly 1`] = `
<div
data-v-app=""
>
<section
class="max-w-4xl p-6 mx-auto bg-white rounded-md shadow-md dark:bg-gray-800"
>
<h2
class="text-lg font-semibold text-gray-700 capitalize dark:text-white"
>
Opprett ny bruker
</h2>
<form>
<div
class="grid grid-cols-1 gap-6 mt-4 sm:grid-cols-2"
>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="email"
>
E-mail
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="email"
type="email"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="password"
>
Passord
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="password"
type="password"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="confirmPassword"
>
Bekreft Passord
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="confirmPassword"
type="password"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="firstName"
>
Fornavn
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
data-test="firstNameTest"
id="firstName"
type="text"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="lastName"
>
Etternavn
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="lastName"
type="text"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="address"
>
Addresse
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="address"
type="text"
/>
</div>
</div>
<div
class="flex justify-end mt-6"
>
<button
class="px-6 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded-md hover:bg-gray-600 focus:outline-none focus:bg-gray-600"
type="submit"
>
<div>
Lagre
</div>
</button>
</div>
</form>
</section>
<ul
data-test="errorMessageList"
>
<!--v-if-->
</ul>
</div>
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment