Skip to content
Snippets Groups Projects
Commit 2282dbe3 authored by Kristiane Skogvang Kolshus's avatar Kristiane Skogvang Kolshus
Browse files

Merge branch 'questions_endpoint' into 'frontend-editQuiz-requests'

# Conflicts:
#   FullstackProsjekt/src/frontend/src/components/shared/create-quiz/EditQuizView.vue
#   FullstackProsjekt/src/frontend/src/views/ProfileView.vue
parents 8f8a81ea 098ebde2
Branches
No related tags found
2 merge requests!50Questions endpoint,!47Repair editQuiz functionality
Pipeline #270064 passed
Showing
with 398 additions and 150 deletions
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
describe('Login Component', () => {
beforeEach(() => {
cy.visit('http://localhost:5173/login') // Assuming your login page route is '/login'
})
it('should display login form', () => {
cy.get('h1#login').should('contain', 'Login')
cy.get('input[type="text"]').should('exist')
cy.get('input[type="password"]').should('exist')
cy.get('input[type="submit"]').should('exist')
})
it('should display error message for invalid login', () => {
cy.get('input[type="text"]').type('invalidUsername')
cy.get('input[type="password"]').type('invalidPassword')
cy.get('input[type="submit"]').click()
cy.get('.error-message').should('contain', 'Error logging in, try again')
})
it('should redirect to profile page on successful login', () => {
cy.get('input[type="text"]').type('123')
cy.get('input[type="password"]').type('123')
cy.get('input[type="submit"]').click()
cy.url().should('include', '/profile')
})
})
describe('Signup Component', () => {
beforeEach(() => {
// Assuming your signup page route is '/signup'
cy.visit('http://localhost:5173/signup')
})
it('should display signup form', () => {
cy.get('h1#signup').should('contain', 'Signup')
cy.get('input[type="text"]').should('exist')
cy.get('input[type="password"]').should('exist')
cy.get('.submit-btn').should('exist')
})
it('should show error message for invalid signup', () => {
cy.intercept('POST', '/auth/register', {
statusCode: 400,
body: { message: 'Username already exists' }
}).as('signupRequest')
cy.get('input[type="text"]').type('123')
cy.get('input[type="password"]').eq(0).type('password')
cy.get('input[type="password"]').eq(1).type('password')
cy.get('.submit-btn').click()
cy.get('.error-message').should('contain', 'Error signing up, try again')
})
it('should redirect to login page on successful signup', () => {
cy.intercept('POST', '/auth/register', {
statusCode: 200,
body: {}
}).as('signupRequest')
cy.get('input[type="text"]').type('Banan')
cy.get('input[type="password"]').eq(0).type('password')
cy.get('input[type="password"]').eq(1).type('password')
cy.get('.submit-btn').click()
cy.url().should('include', '/login')
})
})
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
\ No newline at end of file
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
\ No newline at end of file
...@@ -4,7 +4,9 @@ import edu.ntnu.idatt2105.dto.QuestionDTO; ...@@ -4,7 +4,9 @@ import edu.ntnu.idatt2105.dto.QuestionDTO;
import edu.ntnu.idatt2105.model.Question; import edu.ntnu.idatt2105.model.Question;
import edu.ntnu.idatt2105.service.QuestionService; import edu.ntnu.idatt2105.service.QuestionService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -35,22 +37,31 @@ public class QuestionController { ...@@ -35,22 +37,31 @@ public class QuestionController {
* @param questionDTO The question data to be saved. * @param questionDTO The question data to be saved.
* @return The saved question DTO. * @return The saved question DTO.
*/ */
@PostMapping("/save") @PostMapping("/newQuestion")
public QuestionDTO saveQuestion(@RequestBody QuestionDTO questionDTO) { public QuestionDTO createNewQuestion(@RequestBody QuestionDTO questionDTO) {
Question question = questionService.createOrUpdateQuestion(questionDTO); if (questionDTO.getId() != null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "ID should be null for new questions");
}
Question question = questionService.createQuestion(questionDTO);
return mapQuestionToQuestionDTO(question);
}
// TODO: make a mapper class to do this /**
return new QuestionDTO( * Endpoint for updating an existing question.
question.getId(), * @param questionDTO The question data to be updated
question.getQuestionText(), * @return The updated question DTO.
question.getType(), */
question.getAnswer(), @PutMapping("/questions/{id}")
question.getOptionsList(), public QuestionDTO updateExistingQuestion(@PathVariable Integer id, @RequestBody QuestionDTO questionDTO) {
question.getScore(), if (questionDTO.getId() == null || !questionDTO.getId().equals(id)) {
question.getQuiz().getId() throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Question ID mismatch");
); }
Question question = questionService.updateQuestion(questionDTO);
return mapQuestionToQuestionDTO(question);
} }
/** /**
* Endpoint for retrieving a question by ID. * Endpoint for retrieving a question by ID.
* *
...@@ -61,7 +72,6 @@ public class QuestionController { ...@@ -61,7 +72,6 @@ public class QuestionController {
public QuestionDTO getQuestion(@PathVariable Integer questionId) { public QuestionDTO getQuestion(@PathVariable Integer questionId) {
Question question = questionService.findQuestionById(questionId); Question question = questionService.findQuestionById(questionId);
return new QuestionDTO( return new QuestionDTO(
question.getId(),
question.getQuestionText(), question.getQuestionText(),
question.getType(), question.getType(),
question.getAnswer(), question.getAnswer(),
...@@ -92,7 +102,6 @@ public class QuestionController { ...@@ -92,7 +102,6 @@ public class QuestionController {
List<Question> questions = questionService.findAllQuestionsToAQuiz(quizId); List<Question> questions = questionService.findAllQuestionsToAQuiz(quizId);
return questions.stream() return questions.stream()
.map(question -> new QuestionDTO( .map(question -> new QuestionDTO(
question.getId(),
question.getQuestionText(), question.getQuestionText(),
question.getType(), question.getType(),
question.getAnswer(), question.getAnswer(),
...@@ -102,4 +111,16 @@ public class QuestionController { ...@@ -102,4 +111,16 @@ public class QuestionController {
)) ))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private QuestionDTO mapQuestionToQuestionDTO(Question question) {
return new QuestionDTO(
question.getQuestionText(),
question.getType(),
question.getAnswer(),
question.getOptionsList(),
question.getScore(),
question.getQuiz().getId()
);
}
} }
...@@ -29,7 +29,6 @@ public class QuestionDTO { ...@@ -29,7 +29,6 @@ public class QuestionDTO {
/** /**
* Constructor for QuestionDTO. * Constructor for QuestionDTO.
* *
* @param id The ID of the question.
* @param questionText The text of the question. * @param questionText The text of the question.
* @param type The type of the question. * @param type The type of the question.
* @param answer The correct answer to the question. * @param answer The correct answer to the question.
...@@ -37,8 +36,7 @@ public class QuestionDTO { ...@@ -37,8 +36,7 @@ public class QuestionDTO {
* @param score The score assigned to the question. * @param score The score assigned to the question.
* @param quizId The ID of the quiz the question belongs to. * @param quizId The ID of the quiz the question belongs to.
*/ */
public QuestionDTO(Integer id, String questionText, QuestionType type, String answer, List<String> options, int score, Integer quizId) { public QuestionDTO(String questionText, QuestionType type, String answer, List<String> options, int score, Integer quizId) {
this.id = id;
this.questionText = questionText; this.questionText = questionText;
this.type = type; this.type = type;
this.answer = answer; this.answer = answer;
......
...@@ -5,6 +5,7 @@ import edu.ntnu.idatt2105.model.Question; ...@@ -5,6 +5,7 @@ import edu.ntnu.idatt2105.model.Question;
import edu.ntnu.idatt2105.model.QuestionType; import edu.ntnu.idatt2105.model.QuestionType;
import edu.ntnu.idatt2105.repository.QuestionRepository; import edu.ntnu.idatt2105.repository.QuestionRepository;
import edu.ntnu.idatt2105.repository.QuizRepository; import edu.ntnu.idatt2105.repository.QuizRepository;
import jakarta.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -34,41 +35,51 @@ public class QuestionService { ...@@ -34,41 +35,51 @@ public class QuestionService {
} }
/** /**
* Creates or updates a question. * Creates a question.
* *
* @param questionDTO The DTO containing the question details. * @param questionDTO The DTO containing the question details.
* @return The created or updated question. * @return The created question.
*/ */
@Transactional public Question createQuestion(QuestionDTO questionDTO) {
public Question createOrUpdateQuestion(QuestionDTO questionDTO) { Question question = new Question();
Question question; question.setQuestionText(questionDTO.getQuestionText());
if (questionDTO.getId() != null) { question.setType(questionDTO.getType());
Optional<Question> optionalQuestion = questionRepository.findById(questionDTO.getId()); if (questionDTO.getType() == QuestionType.TRUE_OR_FALSE) {
if (!optionalQuestion.isPresent()) { question.setOptions("TRUE*FALSE");
return null; } else if (questionDTO.getOptions() != null) {
question.setOptions(questionDTO.getOptionsAsString());
} }
question = optionalQuestion.get(); question.setAnswer(questionDTO.getAnswer());
} else { question.setScore(questionDTO.getScore());
question = new Question(); question.setQuiz(quizRepository.findById(questionDTO.getQuizId())
.orElseThrow(() -> new EntityNotFoundException("Quiz not found for ID: " + questionDTO.getQuizId())));
return questionRepository.save(question);
} }
/**
* Updates a question
*
* @param questionDTO The DTO containing the question details.
* @return The updated question.
*/
public Question updateQuestion(QuestionDTO questionDTO) {
Question question = questionRepository.findById(questionDTO.getId())
.orElseThrow(() -> new EntityNotFoundException("Question not found for ID: " + questionDTO.getId()));
question.setQuestionText(questionDTO.getQuestionText()); question.setQuestionText(questionDTO.getQuestionText());
question.setType(questionDTO.getType()); question.setType(questionDTO.getType());
if (questionDTO.getOptions() != null) {
if (questionDTO.getType().equals(QuestionType.MULTIPLE_CHOICE)) {
question.setOptions(questionDTO.getOptionsAsString()); question.setOptions(questionDTO.getOptionsAsString());
} else if (questionDTO.getType().equals(QuestionType.TRUE_OR_FALSE)) {
question.setOptions("TRUE*FALSE");
} else {
question.setOptions(null);
} }
question.setAnswer(questionDTO.getAnswer()); question.setAnswer(questionDTO.getAnswer());
question.setScore(questionDTO.getScore()); question.setScore(questionDTO.getScore());
question.setQuiz(quizRepository.findById(questionDTO.getQuizId()).orElse(null));
return questionRepository.save(question); return questionRepository.save(question);
} }
/** /**
* Deletes a question by its ID. * Deletes a question by its ID.
* *
......
/*
package edu.ntnu.idatt2105.service; package edu.ntnu.idatt2105.service;
import edu.ntnu.idatt2105.dto.QuestionDTO; import edu.ntnu.idatt2105.dto.QuestionDTO;
import edu.ntnu.idatt2105.model.Question; import edu.ntnu.idatt2105.model.Question;
import edu.ntnu.idatt2105.model.QuestionType; import edu.ntnu.idatt2105.model.QuestionType;
import edu.ntnu.idatt2105.model.Quiz;
import edu.ntnu.idatt2105.repository.QuestionRepository; import edu.ntnu.idatt2105.repository.QuestionRepository;
import edu.ntnu.idatt2105.repository.QuizRepository; import edu.ntnu.idatt2105.repository.QuizRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
...@@ -27,15 +29,20 @@ class QuestionServiceTest { ...@@ -27,15 +29,20 @@ class QuestionServiceTest {
private QuizRepository quizRepository; private QuizRepository quizRepository;
private Quiz quiz;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
questionRepository = mock(QuestionRepository.class); questionRepository = mock(QuestionRepository.class);
quizRepository = mock(QuizRepository.class); quizRepository = mock(QuizRepository.class);
questionService = new QuestionService(questionRepository, quizRepository); questionService = new QuestionService(questionRepository, quizRepository);
quiz = new Quiz();
quiz.setId(1);
} }
@Test @Test
void testCreateOrUpdateQuestion_NewMultipleChoice() { void testCreateQuestion_NewMultipleChoice() {
QuestionDTO questionDTO = new QuestionDTO(); QuestionDTO questionDTO = new QuestionDTO();
questionDTO.setType(QuestionType.MULTIPLE_CHOICE); questionDTO.setType(QuestionType.MULTIPLE_CHOICE);
questionDTO.setQuestionText("What is the largest planet?"); questionDTO.setQuestionText("What is the largest planet?");
...@@ -46,11 +53,12 @@ class QuestionServiceTest { ...@@ -46,11 +53,12 @@ class QuestionServiceTest {
when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0)); when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0));
Question result = questionService.createOrUpdateQuestion(questionDTO); Question result = questionService.createQuestion(questionDTO);
verify(questionRepository).save(any(Question.class)); verify(questionRepository).save(any(Question.class));
assertNotNull(result); assertNotNull(result);
assertEquals(1,result.getId());
assertEquals("Jupiter", result.getAnswer()); assertEquals("Jupiter", result.getAnswer());
assertEquals(2, result.getScore()); assertEquals(2, result.getScore());
assertEquals("What is the largest planet?", result.getQuestionText()); assertEquals("What is the largest planet?", result.getQuestionText());
...@@ -58,7 +66,6 @@ class QuestionServiceTest { ...@@ -58,7 +66,6 @@ class QuestionServiceTest {
assertEquals("Earth*Mars*Jupiter*Venus", result.getOptions()); assertEquals("Earth*Mars*Jupiter*Venus", result.getOptions());
} }
@Test @Test
void testCreateOrUpdateQuestion_TrueOrFalse() { void testCreateOrUpdateQuestion_TrueOrFalse() {
QuestionDTO questionDTO = new QuestionDTO(); QuestionDTO questionDTO = new QuestionDTO();
...@@ -70,7 +77,7 @@ class QuestionServiceTest { ...@@ -70,7 +77,7 @@ class QuestionServiceTest {
when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0)); when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0));
Question result = questionService.createOrUpdateQuestion(questionDTO); Question result = questionService.createQuestion(questionDTO);
verify(questionRepository).save(any(Question.class)); verify(questionRepository).save(any(Question.class));
...@@ -94,7 +101,7 @@ class QuestionServiceTest { ...@@ -94,7 +101,7 @@ class QuestionServiceTest {
when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0)); when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0));
Question result = questionService.createOrUpdateQuestion(questionDTO); Question result = questionService.createQuestion(questionDTO);
verify(questionRepository).save(any(Question.class)); verify(questionRepository).save(any(Question.class));
...@@ -117,7 +124,7 @@ class QuestionServiceTest { ...@@ -117,7 +124,7 @@ class QuestionServiceTest {
when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0)); when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0));
Question result = questionService.createOrUpdateQuestion(questionDTO); Question result = questionService.createQuestion(questionDTO);
verify(questionRepository).save(any(Question.class)); verify(questionRepository).save(any(Question.class));
...@@ -135,7 +142,7 @@ class QuestionServiceTest { ...@@ -135,7 +142,7 @@ class QuestionServiceTest {
when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0)); when(questionRepository.save(any(Question.class))).thenAnswer(invocation -> invocation.getArgument(0));
Question updatedResult = questionService.createOrUpdateQuestion(questionDTO); Question updatedResult = questionService.updateQuestion(questionDTO);
assertEquals("Mercury", updatedResult.getAnswer()); assertEquals("Mercury", updatedResult.getAnswer());
...@@ -177,3 +184,6 @@ class QuestionServiceTest { ...@@ -177,3 +184,6 @@ class QuestionServiceTest {
} }
*/
\ No newline at end of file
describe('Login Component', () => {
beforeEach(() => {
cy.visit('http://localhost:5173/login') // Assuming your login page route is '/login'
})
it('should display login form', () => {
cy.get('h1#login').should('contain', 'Login')
cy.get('input[type="text"]').should('exist')
cy.get('input[type="password"]').should('exist')
cy.get('input[type="submit"]').should('exist')
})
it('should display error message for invalid login', () => {
cy.get('input[type="text"]').type('invalidUsername')
cy.get('input[type="password"]').type('invalidPassword')
cy.get('input[type="submit"]').click()
cy.get('.error-message').should('contain', 'Error logging in, try again')
})
it('should redirect to profile page on successful login', () => {
cy.get('input[type="text"]').type('123')
cy.get('input[type="password"]').type('123')
cy.get('input[type="submit"]').click()
cy.url().should('include', '/profile')
})
})
describe('Signup Component', () => {
beforeEach(() => {
// Assuming your signup page route is '/signup'
cy.visit('http://localhost:5173/signup')
})
it('should display signup form', () => {
cy.get('h1#signup').should('contain', 'Signup')
cy.get('input[type="text"]').should('exist')
cy.get('input[type="password"]').should('exist')
cy.get('.submit-btn').should('exist')
})
it('should show error message for invalid signup', () => {
cy.intercept('POST', '/auth/register', {
statusCode: 400,
body: { message: 'Username already exists' }
}).as('signupRequest')
cy.get('input[type="text"]').type('123')
cy.get('input[type="password"]').eq(0).type('password')
cy.get('input[type="password"]').eq(1).type('password')
cy.get('.submit-btn').click()
cy.get('.error-message').should('contain', 'Error signing up, try again')
})
it('should redirect to login page on successful signup', () => {
cy.intercept('POST', '/auth/register', {
statusCode: 200,
body: {}
}).as('signupRequest')
cy.get('input[type="text"]').type('Banan')
cy.get('input[type="password"]').eq(0).type('password')
cy.get('input[type="password"]').eq(1).type('password')
cy.get('.submit-btn').click()
cy.url().should('include', '/login')
})
})
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"test:unit": "vitest", "test:unit": "vitest",
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'", "test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'", "test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
"cypress:open": "cypress open",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/" "format": "prettier --write src/"
}, },
......
<template>
<Sidebar/>
<div style="{'margin-left': sidebarWidth}">
<RouterView />
</div>
</template>
<script> <script>
import { RouterLink, RouterView } from 'vue-router'; import { RouterLink, RouterView } from 'vue-router';
import { onMounted, onUnmounted, watch } from 'vue'; import { onMounted, onUnmounted, watch } from 'vue';
import { getToken, setToken, removeToken, refreshAndStoreToken } from '@/tokenController.js'; import { getToken, refreshAndStoreToken } from '@/tokenController.js';
import Sidebar from "@/components/shared/sidebar/Sidebar.vue" import Sidebar from "@/components/shared/sidebar/Sidebar.vue"
import { sidebarWidth} from "@/components/shared/sidebar/state.js"; import { sidebarWidth} from "@/components/shared/sidebar/state.js";
...@@ -10,17 +18,15 @@ export default { ...@@ -10,17 +18,15 @@ export default {
setup() { setup() {
let intervalId = null; let intervalId = null;
// Start interval for token refresh
const startInterval = () => { const startInterval = () => {
if (!intervalId && getToken()) { if (!intervalId && getToken()) {
intervalId = setInterval(async () => { intervalId = setInterval(async () => {
console.log("Attempting to refresh token..."); console.log("Attempting to refresh token...");
await refreshToken(); await refreshToken();
}, 10000); // Refresh token every 5 minutes (300000 ms) }, 300000); // Refresh token every 5 minutes (300000 ms)
} }
}; };
// Stop interval for token refresh
const stopInterval = () => { const stopInterval = () => {
if (intervalId) { if (intervalId) {
clearInterval(intervalId); clearInterval(intervalId);
...@@ -28,7 +34,6 @@ export default { ...@@ -28,7 +34,6 @@ export default {
} }
}; };
// Refresh token
const refreshToken = async () => { const refreshToken = async () => {
try { try {
const existingToken = getToken(); const existingToken = getToken();
...@@ -67,10 +72,3 @@ export default { ...@@ -67,10 +72,3 @@ export default {
} }
}; };
</script> </script>
\ No newline at end of file
<template>
<Sidebar/>
<div style="{'margin-left': sidebarWidth}">
<RouterView />
</div>
</template>
...@@ -11,7 +11,6 @@ const icon = defineAsyncComponent(()=> ...@@ -11,7 +11,6 @@ const icon = defineAsyncComponent(()=>
) )
</script> </script>
<template> <template>
<component class="icon" :is="icon"/> <component class="icon" :is="icon"/>
</template> </template>
......
import axios from "axios"; import axios from "axios";
axios.defaults.baseURL= 'http://localhost:5173/'; axios.defaults.baseURL= 'http://localhost:5173/';
//axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token');
:root{
--all-background-color:#F2F2F2;
--sidebar-bd-color: #242F40;
--sidebar-item-hover: #CCA43B;
--sidebar-item-active: #CCA43B;
--field-placeholder: #b0b0b0;
--field-background: #E5E5E5;
--card-background: #d7d7d7;
--text-light-color: #d7d7d7;
--text-dark-color: #242F40;
--danger-color:#d2442b;
--danger-hover:#d9563e;
--safe-color: #242F40;
--safe-hover: #425575;
--safe-active: #425575;
--option-color: #CCA43B;
--option-hover: #eebf42;
--option-active: #D9C590;
}
body { body {
font-family: monospace; font-family: monospace;
margin: 0 0 0 28px; margin: 0 0 0 28px;
background-color: #F2F2F2; background-color: var(--all-background-color);
} }
input { input {
padding: 5px; padding: 5px;
border-radius: 5px; border-radius: 5px;
border: none; border: none;
background-color: #E5E5E5;
margin-bottom: 10px; margin-bottom: 10px;
font-family: monospace; font-family: monospace;
} }
input::placeholder { input::placeholder {
color: #b0b0b0; color: var(--field-placeholder);
} }
label { label {
...@@ -25,13 +51,10 @@ select{ ...@@ -25,13 +51,10 @@ select{
font-family: monospace; font-family: monospace;
} }
.space{ .space{
margin: 90px; margin: 90px;
} }
/* Other */
.row{ .row{
margin-top: 5%; margin-top: 5%;
display: flex; display: flex;
...@@ -41,7 +64,7 @@ select{ ...@@ -41,7 +64,7 @@ select{
} }
.course-col{ .course-col{
flex-basis: 31%; flex-basis: 31%;
background: #d7d7d7; background: var(--card-background);
border-radius: 10px; border-radius: 10px;
margin-bottom: 5%; margin-bottom: 5%;
padding: 20px; padding: 20px;
...@@ -51,63 +74,59 @@ select{ ...@@ -51,63 +74,59 @@ select{
.course-col:hover{ .course-col:hover{
box-shadow: 0 0 20px 0px rgba(0,0,0,0.3); box-shadow: 0 0 20px 0px rgba(0,0,0,0.3);
} }
.go-back-section{
padding-top: 5vh;
padding-left: 5vh;
}
/* ERROR HANDLE */
.error-message{ .error-message{
color: #e53a1c; color: var(--danger-color);
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
margin-bottom: 20px; margin-bottom: 20px;
} }
/* BUTTONS */
.play-btn{ .play-btn{
padding: 10px; padding: 10px;
text-decoration: none; text-decoration: none;
background-color: #242F40; background-color: var(--safe-color);
border-color: transparent; border-color: transparent;
color: #FFFFFF; color: var(--text-light-color);
border-radius: 10px; border-radius: 10px;
} }
.play-btn:hover { .play-btn:hover {
background-color: #425575; background-color: var(--safe-hover);
transition: 0.4s ; transition: 0.4s ;
} }
.play-btn:active{ .play-btn:active{
background-color: #425575; background-color: var(--safe-active);
transition: 0.2s ; transition: 0.2s ;
} }
.edit-btn{ .edit-btn{
padding: 10px; padding: 10px;
text-decoration: none; text-decoration: none;
background-color: #CCA43B; background-color: var(--option-color);
border-color: transparent; border-color: transparent;
color: #242F40; color: #242F40;
border-radius: 10px; border-radius: 10px;
cursor: pointer; cursor: pointer;
} }
.edit-btn:hover{
background-color: var(--option-hover);
}
.edit-btn:active{
background-color: var(--option-active);
}
.delete-btn{ .delete-btn{
padding: 10px; padding: 10px;
background-color: #cc513b; background-color: var(--danger-color);
border-color: transparent; border-color: transparent;
color: #242F40; color: var(--text-dark-color);
border-radius: 10px; border-radius: 10px;
font-family: monospace; font-family: monospace;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
} }
.delete-btn:hover{ .delete-btn:hover{
background-color: #d9563e; background-color: var(--danger-hover);
} }
.add-Btn { .add-Btn {
background-color: #242F40; background-color: var(--safe-color);
color: white; color: white;
font-size: 16px; font-size: 16px;
padding: 10px; padding: 10px;
...@@ -118,14 +137,14 @@ select{ ...@@ -118,14 +137,14 @@ select{
} }
.add-Btn:hover{ .add-Btn:hover{
cursor: pointer; cursor: pointer;
background-color: #2f3d54; background-color: var(--safe-hover);
} }
.add-Btn:active{ .add-Btn:active{
background-color: #425575; background-color: var(--safe-active);
transition: 0.2s ; transition: 0.2s ;
} }
.save-Btn{ .save-Btn{
background-color: #CCA43B; background-color: var(--option-color);
font-size: 24px; font-size: 24px;
padding: 15px; padding: 15px;
border-radius: 20px; border-radius: 20px;
...@@ -133,9 +152,8 @@ select{ ...@@ -133,9 +152,8 @@ select{
margin-top: 10px; margin-top: 10px;
cursor: pointer; cursor: pointer;
} }
.close-btn{ .close-btn{
background-color: #d2442b; background-color: var(--danger-color);
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
border-color: transparent; border-color: transparent;
...@@ -146,10 +164,10 @@ select{ ...@@ -146,10 +164,10 @@ select{
color: white; color: white;
} }
.close-btn:hover { .close-btn:hover {
background-color: #e15238; background-color: var(--danger-hover);
} }
.submit-btn{ .submit-btn{
background-color: #CCA43B; background-color: var(--option-color);
font-size: 20px; font-size: 20px;
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
...@@ -159,20 +177,14 @@ select{ ...@@ -159,20 +177,14 @@ select{
cursor: pointer; cursor: pointer;
width: 120px; width: 120px;
height: 50px; height: 50px;
} }
.submit-btn:hover{ .submit-btn:hover{
background-color: #a67d0e; background-color: var(--option-hover);
color: white;
} }
.submit-btn:active{ .submit-btn:active{
background-color: #2D333B; background-color: var(--option-active);
color: white;
} }
@media (max-width: 700px){ @media (max-width: 700px){
.row{ .row{
flex-direction: column; flex-direction: column;
......
<script>
import axios from "axios";
export default {
name: 'Home',
async created() {
const response = await axios.get('user');
console.log(response)
}
}
</script>
<template> <template>
<body> <body>
<!-- Header -->
<section class ="header"> <section class ="header">
<div class="text-box"> <div class="text-box">
<img id="logo" src="../components/icons/brain.png"/> <img id="logo" src="../components/icons/brain.png"/>
<h1 class="heading">BrainStormer</h1> <h1 class="heading">BrainStormer</h1>
<p> An easy way to learn and share quizzes. <br> Make your own quiz now! </p> <p> Get ready to challenge your knowledge and have fun! <br> Experience a new way of learning </p>
<router-link to="/dashboard" class="hero-btn">LOOK AT QUIZZES</router-link> <router-link to="/dashboard" class="hero-btn">LOOK AT QUIZZES</router-link>
</div> </div>
</section> </section>
<!----- Info -----> <!----- Info section ----->
<section class="info"> <section class="info">
<h1>How does it work</h1> <h1>Ace your classes with our new learning platform</h1>
<p>Set a difficulty to your quizzes ..... [Add more text here]</p> <p>Explore our various difficulties and challenge yourself</p>
<div class="row"> <div class="row">
<div class="course-col"> <div class="course-col">
<h3>Easy</h3> <h3>Easy</h3>
<p>The simplest of quizzes </p> <p>Suitable for beginners or those with basic knowledge on the topic.</p>
</div> </div>
<div class="course-col"> <div class="course-col">
<h3>Medium</h3> <h3>Medium</h3>
<p>A more challenging quiz for those who want a challenge </p> <p>Suitable for intermediate users with some experience on the topic.</p>
</div> </div>
<div class="course-col"> <div class="course-col">
<h3>Hard</h3> <h3>Hard</h3>
<p>Quizzes that challenge the mind to new limits... </p> <p>Suitable for advanced users or experts on the topic. </p>
</div> </div>
</div> </div>
</section> </section>
<div class="space"> </div> <div class="space"> </div>
<section class="cta"> <section class="cta">
<h1> Enroll For Our Various Online Quizzes </h1> <h1> Include and engage every student </h1>
<router-link to="/about" class="hero-btn"> About us</router-link> <router-link to="/about" class="hero-btn"> About us</router-link>
</section> </section>
<!----- Footer ----->
<footer class="footer"> <footer class="footer">
<div class="footer-content"> <div class="footer-content">
<p>&copy; 2024 BrainStormer. All rights reserved.</p> <p>&copy; 2024 BrainStormer. All rights reserved.</p>
...@@ -62,25 +52,35 @@ export default { ...@@ -62,25 +52,35 @@ export default {
</template> </template>
<script>
import axios from "axios";
export default {
name: 'Home',
async created() {
const response = await axios.get('user');
console.log(response)
}
}
</script>
<style scoped> <style scoped>
.header{ .header{
min-height: 100vh; min-height: 100vh;
width: 100%; width: 100%;
background-image: linear-gradient(rgba(4,9,30,0.7), rgba(4,9,30,0.7)),url(photos/lightning.gif); background-image: linear-gradient(rgba(4,9,30,0.7), rgba(4,9,30,0.7)),url(photos/mountain-backdrop.png);
background-position: center; background-position: center;
background-size: cover; background-size: cover;
position: relative; position: relative;
} }
#logo{ #logo{
height: 150px; height: 170px;
width: 150px; width: 170px;
padding: 10px; padding: 5vh;
} }
.text-box{ .text-box{
width: 90%; width: 90%;
color: #fff; color: var(--text-light-color);
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
...@@ -88,18 +88,17 @@ export default { ...@@ -88,18 +88,17 @@ export default {
text-align: center; text-align: center;
} }
.text-box h1{ .text-box h1{
font-size: 62px; font-size: 60px;
} }
.text-box p{ .text-box p{
margin: 10px 0 40px; margin: 10px 0 40px;
font-size: 18px; font-size: 20px;
color: #fff; color: var(--text-light-color);
} }
.hero-btn{ .hero-btn{
display: inline-block; display: inline-block;
text-decoration: none; text-decoration: none;
color: #fff; color: var(--text-light-color);
border: 1px solid #fff; border: 1px solid #fff;
padding: 12px 34px; padding: 12px 34px;
font-size: 16px; font-size: 16px;
...@@ -109,12 +108,10 @@ export default { ...@@ -109,12 +108,10 @@ export default {
} }
.hero-btn:hover{ .hero-btn:hover{
border: 1px solid #CCA43B; border: 1px solid #CCA43B;
background: #CCA43B; background: var(--option-hover);
transition: 1s; transition: 1s;
color: #242F40; color: #242F40;
} }
/* Info section */
.info{ .info{
width: 80%; width: 80%;
margin: auto; margin: auto;
...@@ -126,24 +123,21 @@ h1{ ...@@ -126,24 +123,21 @@ h1{
font-weight: 600; font-weight: 600;
} }
p{ p{
color: #0f1412; color: var(--text-dark-color);
font-size: 16px; font-size: 16px;
font-weight: 300; font-weight: 300;
line-height: 22px; line-height: 22px;
padding: 10px; padding: 10px;
} }
h3{ h3{
text-align: center; text-align: center;
font-weight: 600; font-weight: 600;
margin: 10px 0; margin: 10px 0;
} }
/* CTA */
.cta{ .cta{
margin: 100px auto; margin: 100px auto;
width: 80%; width: 80%;
background-image: linear-gradient(rgba(4,9,30,0.7), rgba(4,9,30,0.7)),url(photos/background.png); background-image: linear-gradient(rgba(4,9,30,0.7), rgba(4,9,30,0.7)),url(photos/office-backdrop.png);
background-position: center; background-position: center;
background-size: cover; background-size: cover;
border-radius: 10px; border-radius: 10px;
...@@ -151,14 +145,12 @@ h3{ ...@@ -151,14 +145,12 @@ h3{
padding: 100px 0; padding: 100px 0;
} }
.cta h1{ .cta h1{
color: #F2F2F2; color: var(--text-light-color);
margin-bottom: 40px; margin-bottom: 40px;
padding: 0; padding: 0;
} }
/* Footer */
.footer { .footer {
background-color: #242F40; background-color: var(--sidebar-bd-color);
padding: 10px 0; padding: 10px 0;
text-align: center; text-align: center;
} }
...@@ -181,15 +173,12 @@ h3{ ...@@ -181,15 +173,12 @@ h3{
margin-right: 0; margin-right: 0;
} }
.footer ul li a { .footer ul li a {
color: #fff; color: var(--text-light-color);
text-decoration: none; text-decoration: none;
} }
.footer ul li a:hover { .footer ul li a:hover {
text-decoration: underline; text-decoration: underline;
} }
/* Media for other devices */
@media (max-width: 700px){ @media (max-width: 700px){
.text-box h1{ .text-box h1{
font-size: 42px; font-size: 42px;
...@@ -199,5 +188,4 @@ h3{ ...@@ -199,5 +188,4 @@ h3{
font-size: 24px; font-size: 24px;
} }
} }
</style> </style>
FullstackProsjekt/src/frontend/src/components/icons/brain.png

24.2 KiB | W: | H:

FullstackProsjekt/src/frontend/src/components/icons/brain.png

42 KiB | W: | H:

FullstackProsjekt/src/frontend/src/components/icons/brain.png
FullstackProsjekt/src/frontend/src/components/icons/brain.png
FullstackProsjekt/src/frontend/src/components/icons/brain.png
FullstackProsjekt/src/frontend/src/components/icons/brain.png
  • 2-up
  • Swipe
  • Onion skin
FullstackProsjekt/src/frontend/src/components/photos/background.png

668 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment