Skip to content
Snippets Groups Projects
Commit 335ef759 authored by Vilde Min Vikan's avatar Vilde Min Vikan
Browse files

Merge branch 'updateMethods' into 'master'

Added new and updated methods for trivio retrieval from new and updated method...

See merge request !5
parents 3ca7cb34 fb998814
No related branches found
No related tags found
2 merge requests!8Updates to the history view,!5Added new and updated methods for trivio retrieval from new and updated method...
<script setup lang="ts">
import TrivioCard from "@/components/TrivioCard.vue";
import router from "@/router";
import {getPicture, getTriviosByUser, getTriviosByOtherUser} from "@/utils/httputils";
import {useTokenStore} from "@/stores/token";
import { defineProps, onMounted, ref, watch } from 'vue'
const tokenStore = useTokenStore()
//data-components
const trivios = ref(null);
const trivioBlobUrls = ref(new Map()); // Store blob URLs for each trivio
//filters-components
const tags = ref<Tag[] | null>(null)
const difficulty = ref<string | null>(null)
const category = ref<string | null>(null)
//page-orientation
let page = 0;
let pages = 0;
let currentPage = 0;
const props = defineProps<{
fetchMethod: Function,
title: String,
}>();
interface Tag{
tag: string;
}
onMounted(async () => {
try{
page = 0;
difficulty.value = null;
category.value = null;
tags.value = null;
await fetchTrivios();
await fetchBlobUrls();
}catch (error){
console.log(error)
}
});
const goToPage = (pageNumber: number) => {
if (pageNumber >= 0 && pageNumber < pages) {
page = pageNumber;
fetchTrivios();
}
};
const nextPage = () => {
if (page < pages-1) {
page ++;
fetchTrivios();
}
};
const previousPage = () => {
if (page > 0) {
page--;
fetchTrivios();
}
};
const fetchTrivios = (async () => {
try {
const response = await props.fetchMethod(tokenStore.jwtToken, page, 10, category.value, difficulty.value, tags.value);
if(response){
try {
const { content, totalPages, number } = response.data; // Extract pagination data
trivios.value = content;
pages= totalPages;
currentPage = number;
} catch (error){
console.log('The response do not contain trivios')
}
} else{
console.log('Response is undefined!')
}
} catch (error){
console.log('Error fetching trivios', error)
}
});
const fetchBlobUrls = async () => {
if (trivios.value) {
for (const trivio of trivios.value) {
const blobUrl = await getBlobUrl(trivio.multimedia_url);
trivioBlobUrls.value.set(trivio.id, blobUrl); // Store blob URL in the map with trivio ID as key
}
} else {
console.log('Value of trivios is null!')
}
};
const getBlobUrl = async (multimediaUrl: string) => {
if (!multimediaUrl) return "";
try {
return await getPicture(multimediaUrl, tokenStore.jwtToken);
} catch (error) {
console.error("Error fetching blobUrl:", error);
return "";
}
};
const handleCardClick = (id: number) => {
try {
console.log('Clicked TrivioCard with ID:', id);
router.push({ name: 'start', params: { id } })
} catch (error){
console.error('Issues routing to trivio', error)
}
};
//Add an empty tag
const addTag = () => {
if(tags.value) {
tags.value.push({ tag: '' })
} else{
tags.value = []
tags.value.push({tag: ''})
}
};
//Remove an existing tag
const deleteTag = (index: number) => {
if(tags.value){
tags.value.splice(index, 1);
if(tags.value.length != 0){
fetchTrivios();
} else {
tags.value = null
fetchTrivios();
}
} else {
console.error('Issues removing tag')
}
};
//Prevent space in a tag
const preventSpace = (event: KeyboardEvent) => {
if (event.keyCode === 32) {
event.preventDefault();
}
};
watch(category, fetchTrivios);
watch(difficulty, fetchTrivios);
</script>
<template>
<div class="MyTrivios">
<div class="FilterOptions">
<h3>Category: </h3>
<select v-model="category" class="option">
<option :value="null">Category</option>
<option value="Random">Random</option>
<option value="Sport">Sport</option>
<option value="Music">Music</option>
</select>
<h3>Difficulty: </h3>
<select v-model="difficulty" class="option">
<option :value="null">Difficulty</option>
<option value="Easy">Easy</option>
<option value="Medium">Medium</option>
<option value="Hard">Hard</option>
</select>
</div>
<h1>{{props.title}}</h1>
<div class="tag-section">
<button @click="addTag" class="addTag">Add Tag +</button>
<div class="tags">
<div v-for="(tag, index) in tags" :key="index" class="tag-box">
<input type="text"
v-model='tag.tag'
:placeholder="'Tag ' + (index + 1)"
class="tag"
@keypress="preventSpace"
@keypress.enter="fetchTrivios">
<img src="@/components/icons/RemoveTag.png" alt="removeTag" @click="deleteTag(index)" width="20px" height="20px">
</div>
</div>
</div>
<div class="card-container">
<TrivioCard
v-for="(trivio, index) in trivios"
:key="index"
:title="trivio.title"
:category="trivio.category"
:image="trivio.image"
:username="trivio.user.username"
:number-of-question="trivio.questionList.length"
:id="trivio.id"
:difficulty="trivio.difficulty"
:media="trivioBlobUrls.get(trivio.id)"
@click="handleCardClick(trivio.id)"
></TrivioCard>
</div>
<div class="pagination">
<button @click="previousPage" :disabled="currentPage === 0">Previous</button>
<div v-if="pages>0" class="page-numbers">
<button
v-for="pageNumber in pages"
:key="pageNumber-2"
@click="goToPage(pageNumber-1)"
:class="{ active: pageNumber-1 === currentPage }"
>{{ pageNumber}}</button>
</div>
<button @click="nextPage" :disabled="currentPage === pages - 1">Next</button>
</div>
</div>
</template>
<style scoped>
.MyTrivios{
display: flex;
flex-direction: column;
padding: 20px;
height: 100%;
width: 100%;
}
h1 {
color: #5E5CE5;
}
.FilterOptions{
display: flex;
flex-direction: row;
width: 100%;
place-items: center;
place-content: start;
gap: 10px;
}
.card-container {
display: flex;
flex-wrap: wrap;
width: 100%;
column-gap: 20px;
}
.tag-section{
display: flex;
flex-direction: row;
width: 100%;
gap: 15px;
}
.addTag{
height: 30px;
width: 100px;
font-size: medium;
}
.tags{
display: flex;
flex-wrap: wrap;
place-items: center;
align-items: center;
gap: 10px;
max-width: 90%;
}
.tag-box{
display: flex;
flex-direction: row;
align-items: center;
gap: 2px;
}
.tag{
width: 100px;
height: 30px;
border-radius: 5px;
border: 2px solid darkgray;
text-align: center;
font-size: medium;
}
.option{
width: 100px;
height: 30px;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.pagination button {
padding: 5px 10px;
margin: 0 5px;
border: 1px solid #ccc;
border-radius: 3px;
background-color: transparent;
cursor: pointer;
}
.pagination button:hover {
background-color: #f0f0f0;
}
.pagination button:disabled {
color: #ccc;
cursor: not-allowed;
}
.page-numbers {
display: flex;
}
.page-numbers button {
padding: 5px 10px;
margin: 0 2px;
border: 1px solid #ccc;
border-radius: 3px;
background-color: transparent;
cursor: pointer;
}
.page-numbers button.active {
background-color: #5E5CE5;
color: white;
}
.page-numbers button:hover {
background-color: #f0f0f0;
}
.page-numbers button:disabled {
color: #ccc;
cursor: not-allowed;
}
</style>
...@@ -23,25 +23,123 @@ export const getUserInfo = (token: any) => { ...@@ -23,25 +23,123 @@ export const getUserInfo = (token: any) => {
const userId = decodedToken.sub const userId = decodedToken.sub
return axios.get("http://localhost:8080/users/"+userId, config) return axios.get("http://localhost:8080/users/"+userId, config)
} }
export const getTriviosByUser = (token: any) => { export const getTriviosByUser = (token: any, page: number, size: number, category: any, difficulty: any, tags:any) => {
const config = { try {
const config: any = {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"Authorization": "Bearer " + token "Authorization": "Bearer " + token
}, },
params: {
'page': page,
'size': size
}
}; };
return axios.get("http://localhost:8080/trivios/user", config)
// Conditionally add category, difficulty, and tags to the params object
if (category !== null) {
config.params['category'] = category;
}
if (difficulty !== null) {
config.params['difficulty'] = difficulty;
} }
export const getPublicTriviosFromOtherUsers = (token: any) => { if(tags !== null){
const config ={ const nonEmptyTags = tags.filter(tag => tag.tag.trim() !== "");
//Check if there is any valid tags at all
if(nonEmptyTags.length !== 0) {
const tagString = nonEmptyTags.map(tag => tag.tag).join(',');
config.params['tagString'] = tagString;
}
}
console.log(config);
return axios.get(`http://localhost:8080/trivios/user`, config)
} catch (error) {
console.log(error)
}
}
export const getTriviosByOtherUser = (token: any, page: number, size: number, category: any, difficulty: any, tags:any) => {
try {
const config: any = {
headers: {
"Content-type": "application/json",
"Authorization": "Bearer " + token
},
params: {
'page': page,
'size': size
}
};
// Conditionally add category, difficulty, and tags to the params object
if (category !== null) {
config.params['category'] = category;
}
if (difficulty !== null) {
config.params['difficulty'] = difficulty;
}
if(tags !== null){
const nonEmptyTags = tags.filter(tag => tag.tag.trim() !== "");
//Check if there is any valid tags at all
if(nonEmptyTags.length !== 0) {
const tagString = nonEmptyTags.map(tag => tag.tag).join(',');
config.params['tagString'] = tagString;
}
}
console.log(config);
return axios.get(`http://localhost:8080/trivios/discover`, config)
} catch (error) {
console.log(error)
}
}
export const getSharedTriviosByUser = (token: any, page: number, size: number, category: any, difficulty: any, tags:any) => {
try {
const config: any = {
headers: { headers: {
"Content-type": "application/json", "Content-type": "application/json",
"Authorization": "Bearer " + token "Authorization": "Bearer " + token
}, },
params: {
'page': page,
'size': size
}
}; };
return axios.get("http://localhost:8080/trivios/discovery", config)
// Conditionally add category, difficulty, and tags to the params object
if (category !== null) {
config.params['category'] = category;
} }
if (difficulty !== null) {
config.params['difficulty'] = difficulty;
}
if(tags !== null){
const nonEmptyTags = tags.filter(tag => tag.tag.trim() !== "");
//Check if there is any valid tags at all
if(nonEmptyTags.length !== 0) {
const tagString = nonEmptyTags.map(tag => tag.tag).join(',');
config.params['tagString'] = tagString;
}
}
console.log(config);
return axios.get(`http://localhost:8080/trivios/shared`, config)
} catch (error) {
console.log(error)
}
}
export const getTrivioById = (token: any, id: any) => { export const getTrivioById = (token: any, id: any) => {
const config = { const config = {
......
<script setup lang="ts"> <script setup lang="ts">
import { getTriviosByOtherUser } from '@/utils/httputils'
import { ref, onMounted, watch } from 'vue' import TrivioDisplay from '@/components/TrivioDisplay.vue'
import TrivioCard from "@/components/TrivioCard.vue";
import router from "@/router";
import {getPicture, getPublicTriviosFromOtherUsers} from '@/utils/httputils'
import { useTokenStore } from '@/stores/token'
const trivios = ref(null);
const triviosToDisplay= ref(null)
const tokenStore = useTokenStore()
const triviosBlobUrls = ref(new Map())
interface Tag{
tag: String;
}
onMounted(async () => {
if(!tokenStore.jwtToken){
console.log("unauthorized user")
} else {
//let response = await getUserInfo(tokenStore.jwtToken)
//user.value = response.data
const response = await getPublicTriviosFromOtherUsers(tokenStore.jwtToken);
trivios.value = response.data
console.log(trivios.value)
await fetchBlobUrls()
filterMyTrivios();
}
});
const handleCardClick = (id) => {
console.log('Clicked TrivioCard with ID:', id);
router.push({name: 'start', params: {id}})
};
const tags = ref<Tag[]>([])
const difficulty = ref<string>('all')
const category = ref<string>('all')
const addTag = () => {
tags.value.push({tag: ''})
};
const deleteTag = (index: number) => {
tags.value.splice(index, 1);
};
const filterMyTrivios = () => {
// Apply filters based on category, difficulty, and tags
triviosToDisplay.value = trivios.value.filter((trivio: any) => {
// Convert both question tags and selected tags to lowercase for case-insensitive comparison
const questionTags = new Set(trivio.questionList.flatMap((question: any) => question.tags.map((tag: string) => tag.toLowerCase())));
// Filter out empty string tags
const filteredTags = tags.value.filter(tag => tag.tag.trim() !== '');
const selectedTags = new Set(filteredTags.map((tag: Tag) => tag.tag.toLowerCase()));
// Check if all selected non-empty tags are present in the question tags
const tagsMatch = filteredTags.length === 0 || [...selectedTags].every((tag: string) => questionTags.has(tag));
// Check if category and difficulty match
const categoryMatch = category.value === 'all' || trivio.category.toLowerCase() === category.value.toLowerCase();
const difficultyMatch = difficulty.value === 'all' || trivio.difficulty.toLowerCase() === difficulty.value.toLowerCase();
// Return true if all conditions are met
return categoryMatch && difficultyMatch && tagsMatch;
});
};
const getBlobUrl = async (multimediaUrl: string) => {
if (!multimediaUrl) return "";
try {
return await getPicture(multimediaUrl, tokenStore.jwtToken);
} catch (error) {
console.error("Error fetching blobUrl:", error);
return "";
}
};
const fetchBlobUrls = async () => {
for (const trivio of trivios.value) {
const blobUrl = await getBlobUrl(trivio.multimedia_url);
triviosBlobUrls.value.set(trivio.id, blobUrl); // Store blob URL in the map with trivio ID as key
}
};
watch(tags, (newTags, oldTags) => {
filterMyTrivios();
}, { deep: true });
watch(category, filterMyTrivios);
watch(difficulty, filterMyTrivios);
</script> </script>
<template> <template>
<div class="MyTrivios"> <div class="discover-trivio-view">
<div class="FilterOptions"> <div class="discover-trivios">
<h3>Category: </h3> <TrivioDisplay
<select v-model="category" class="option"> :fetchMethod="getTriviosByOtherUser"
<option value="all">Category</option> title="Discovery">
<option value="random">Random</option> </TrivioDisplay>
<option value="sport">Sport</option>
<option value="music">Music</option>
</select>
<h3>Difficulty: </h3>
<select v-model="difficulty" class="option">
<option value="all">Difficulty</option>
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="hard">Hard</option>
</select>
</div>
<h1>Discover Trivios!</h1>
<div class="tag-section">
<button @click="addTag" class="addTag">Add Tag +</button>
<div class="tags">
<div v-for="(tag, index) in tags" :key="index" class="tag-box">
<input type="text" v-model="tag.tag" :placeholder="'Tag ' + (index + 1)" class="tag" @keypress="preventSpace">
<img src="@/components/icons/RemoveTag.png" alt="removeTag" @click="deleteTag(index)" width="20px" height="20px">
</div>
</div>
</div>
<div class="card-container">
<TrivioCard
v-for="(trivio, index) in triviosToDisplay"
:key="index"
:title="trivio.title"
:category="trivio.category"
:image="trivio.image"
:username="trivio.user.username"
:number-of-question="trivio.questionList.length"
:id="trivio.id"
:difficulty="trivio.difficulty"
:media="triviosBlobUrls.get(trivio.id)"
@click="handleCardClick(trivio.id)"
></TrivioCard>
</div> </div>
</div> </div>
</template> </template>
<style scoped>
.MyTrivios{ <style scoped>
.discover-trivio-view{
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px;
}
h1 {
color: #5E5CE5;
}
.FilterOptions{
display: flex;
flex-direction: row;
width: 100%;
place-items: center;
place-content: start;
gap: 10px;
}
.card-container {
display: flex;
flex-wrap: wrap;
width: 100%;
column-gap: 20px;
}
.tag-section{
display: flex;
flex-direction: row;
width: 100%; width: 100%;
gap: 15px; height:100%;
} }
.addTag{
height: 30px;
width: 100px;
font-size: medium;
}
.tags{
display: flex;
flex-wrap: wrap;
place-items: center;
align-items: center;
gap: 10px;
max-width: 90%;
}
.tag-box{
display: flex;
flex-direction: row;
align-items: center;
gap: 2px;
}
.tag{
width: 100px;
height: 30px;
border-radius: 5px;
border: 2px solid darkgray;
text-align: center;
font-size: medium;
}
.option{
width: 100px;
height: 30px;
}
</style> </style>
<script setup lang="ts"> <script setup lang="ts">
import { getSharedTriviosByUser, getTriviosByUser } from '@/utils/httputils'
import TrivioDisplay from '@/components/TrivioDisplay.vue'
import {onMounted, ref, watch} from 'vue'
import TrivioCard from "@/components/TrivioCard.vue";
import router from "@/router";
import {getPicture, getTriviosByUser} from "@/utils/httputils";
import {useTokenStore} from "@/stores/token";
const trivios = ref(null);
const triviosToDisplay= ref(null)
const tokenStore = useTokenStore()
const trivioBlobUrls = ref(new Map()); // Store blob URLs for each trivio
interface Tag{
tag: String;
}
const image = ref({
blobUrl: "",
imageUrl: ""
});
onMounted(async () => {
if(!tokenStore.jwtToken){
console.log("unauthorized user")
} else {
//let response = await getUserInfo(tokenStore.jwtToken)
//user.value = response.data
const response = await getTriviosByUser(tokenStore.jwtToken);
trivios.value = response.data
console.log(trivios.value)
await fetchBlobUrls()
filterMyTrivios();
}
});
const handleCardClick = (id) => {
console.log('Clicked TrivioCard with ID:', id);
router.push({name: 'start', params: {id}})
};
const tags = ref<Tag[]>([])
const difficulty = ref<string>('all')
const category = ref<string>('all')
const addTag = () => {
tags.value.push({tag: ''})
};
const deleteTag = (index: number) => {
tags.value.splice(index, 1);
};
const getBlobUrl = async (multimediaUrl: string) => {
if (!multimediaUrl) return "";
try {
return await getPicture(multimediaUrl, tokenStore.jwtToken);
} catch (error) {
console.error("Error fetching blobUrl:", error);
return "";
}
};
const fetchBlobUrls = async () => {
for (const trivio of trivios.value) {
const blobUrl = await getBlobUrl(trivio.multimedia_url);
trivioBlobUrls.value.set(trivio.id, blobUrl); // Store blob URL in the map with trivio ID as key
}
};
const filterMyTrivios = () => {
// Apply filters based on category, difficulty, and tags
triviosToDisplay.value = trivios.value.filter((trivio: any) => {
// Convert both question tags and selected tags to lowercase for case-insensitive comparison
const questionTags = new Set(trivio.questionList.flatMap((question: any) => question.tags.map((tag: string) => tag.toLowerCase())));
// Filter out empty string tags
const filteredTags = tags.value.filter(tag => tag.tag.trim() !== '');
const selectedTags = new Set(filteredTags.map((tag: Tag) => tag.tag.toLowerCase()));
// Check if all selected non-empty tags are present in the question tags
const tagsMatch = filteredTags.length === 0 || [...selectedTags].every((tag: string) => questionTags.has(tag));
// Check if category and difficulty match
const categoryMatch = category.value === 'all' || trivio.category.toLowerCase() === category.value.toLowerCase();
const difficultyMatch = difficulty.value === 'all' || trivio.difficulty.toLowerCase() === difficulty.value.toLowerCase();
// Return true if all conditions are met
return categoryMatch && difficultyMatch && tagsMatch;
});
};
watch(tags, (newTags, oldTags) => {
filterMyTrivios();
}, { deep: true });
watch(category, filterMyTrivios);
watch(difficulty, filterMyTrivios);
</script> </script>
<template> <template>
<div class="MyTrivios"> <div class="my-trivio-view">
<div class="FilterOptions"> <div class="my-trivios">
<h3>Category: </h3> <TrivioDisplay
<select v-model="category" class="option"> :fetchMethod="getTriviosByUser"
<option value="all">Category</option> title="My Trivios">
<option value="random">Random</option> </TrivioDisplay>
<option value="sport">Sport</option>
<option value="music">Music</option>
</select>
<h3>Difficulty: </h3>
<select v-model="difficulty" class="option">
<option value="all">Difficulty</option>
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="hard">Hard</option>
</select>
</div>
<h1>My Trivios</h1>
<div class="tag-section">
<button @click="addTag" class="addTag">Add Tag +</button>
<div class="tags">
<div v-for="(tag, index) in tags" :key="index" class="tag-box">
<input type="text" v-model="tag.tag" :placeholder="'Tag ' + (index + 1)" class="tag" @keypress="preventSpace">
<img src="@/components/icons/RemoveTag.png" alt="removeTag" @click="deleteTag(index)" width="20px" height="20px">
</div>
</div>
</div> </div>
<div class="shared-trivios">
<div class="card-container"> <TrivioDisplay
<TrivioCard :fetchMethod="getSharedTriviosByUser"
v-for="(trivio, index) in triviosToDisplay" title="Shared With Me">
:key="index" </TrivioDisplay>
:title="trivio.title"
:category="trivio.category"
:image="trivio.image"
:username="trivio.user.username"
:number-of-question="trivio.questionList.length"
:id="trivio.id"
:difficulty="trivio.difficulty"
:media="trivioBlobUrls.get(trivio.id)"
@click="handleCardClick(trivio.id)"
></TrivioCard>
</div> </div>
</div> </div>
</template> </template>
<style scoped>
.MyTrivios{ <style scoped>
.my-trivio-view{
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px;
}
h1 {
color: #5E5CE5;
}
.FilterOptions{
display: flex;
flex-direction: row;
width: 100%;
place-items: center;
place-content: start;
gap: 10px;
}
.card-container {
display: flex;
flex-wrap: wrap;
width: 100%; width: 100%;
column-gap: 20px; height: 100%;
} }
.tag-section{
display: flex;
flex-direction: row;
width: 100%;
gap: 15px;
}
.addTag{
height: 30px;
width: 100px;
font-size: medium;
}
.tags{
display: flex;
flex-wrap: wrap;
place-items: center;
align-items: center;
gap: 10px;
max-width: 90%;
}
.tag-box{
display: flex;
flex-direction: row;
align-items: center;
gap: 2px;
}
.tag{
width: 100px;
height: 30px;
border-radius: 5px;
border: 2px solid darkgray;
text-align: center;
font-size: medium;
}
.option{
width: 100px;
height: 30px;
}
</style> </style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment