From c1733b3bb2311da78b696ffb37fb82466b326fea Mon Sep 17 00:00:00 2001 From: VIktorGrev <viktog2210@gmail.com> Date: Fri, 3 May 2024 21:08:44 +0200 Subject: [PATCH] feat: Adding same features to external profile --- src/components/Shop/ItemShop.vue | 420 ++++++++++-------- .../UserProfile/ExternalProfile.vue | 73 +-- src/components/UserProfile/MyProfile.vue | 3 +- 3 files changed, 273 insertions(+), 223 deletions(-) diff --git a/src/components/Shop/ItemShop.vue b/src/components/Shop/ItemShop.vue index 38173e0..60f544d 100644 --- a/src/components/Shop/ItemShop.vue +++ b/src/components/Shop/ItemShop.vue @@ -1,191 +1,240 @@ <template> - <div id="background"> - <br /> - <div id="dropdownContainer"> - <h1 class="box">Butikk</h1> - <div> - <p class="mb-1 h2" data-cy="points">{{ points }}<img src="@/assets/items/pigcoin.png" style="width: 4rem" /></p> - </div> + <div id="background"> + <br /> + <div id="dropdownContainer"> + <h1 class="box">Butikk</h1> + <div> + <p class="mb-1 h2" data-cy="points">{{ points }}<img src="@/assets/items/pigcoin.png" style="width: 4rem" /></p> </div> - <div class="container d-flex justify-content-center"> - <div class="row col-md-10"> - <div class="col-md-12"> - <h1>Stash</h1> - <div class="category row mb-2 m-2"> - <div class="card text-center justify-content-center align-items-center" style="width: 8rem; border: none"> - <img src="../../assets/items/adfree.png" class="card-img-top" alt="..." style="width: 100px; height: 100px;" /> - <div class="card-body"> - <h5 class="card-title">Adfree</h5> - <button type="button" class="btn btn-primary" id="buttonStyle" @click="buyNoAds"> - +35kr - </button> - </div> + </div> + <div class="container d-flex justify-content-center"> + <div class="row col-md-10"> + <div class="col-md-12"> + <h1>Stash</h1> + <div class="category row mb-2 m-2"> + <div class="card text-center justify-content-center align-items-center" style="width: 8rem; border: none"> + <img src="../../assets/items/adfree.png" class="card-img-top" alt="..." + style="width: 100px; height: 100px;" /> + <div class="card-body"> + <h5 class="card-title">Adfree</h5> + <button type="button" class="btn btn-primary" id="buttonStyle" data-toggle="modal" + data-target="#adfreeModal"> + +35kr + </button> </div> - <div class="card text-center justify-content-center align-items-center" style="width: 8rem; border: none"> - <img src="../../assets/items/piggybank.webp" class="card-img-top" alt="..." style="width: 100px; height: 100px;" /> - <div class="card-body"> - <h5 class="card-title">Premium</h5> - <button type="button" class="btn btn-primary" id="buttonStyle" @click="buyPremium"> - +50kr - </button> - </div> + </div> + <div class="card text-center justify-content-center align-items-center" style="width: 8rem; border: none"> + <img src="../../assets/items/piggybank.webp" class="card-img-top" alt="..." + style="width: 100px; height: 100px;" /> + <div class="card-body"> + <h5 class="card-title">Premium</h5> + <button type="button" class="btn btn-primary" id="buttonStyle" data-toggle="modal" + data-target="#premiumModal"> + +50kr + </button> </div> </div> </div> - <div class="col-md-12"> - <h1>Items</h1> - <div class="category row mb-2 m-2"> - <div v-for="product in products" :key="product.id" class="card text-center d-flex justify-content-center align-items-center" - style="width: 16rem; border: none"> - <img :src="apiUrl + `/api/images/${product.imageId}`" style="width: 200px; height: 100px;" class="card-img-top" alt="..." /> - <div class="card-body"> - <h5 class="card-title">{{ product.itemName }}</h5> - <h6>{{ product.price }}<img src="../../assets/items/pigcoin.png" style="width: 2rem" /></h6> - <ShopButton - v-if="!product.alreadyBought" - button-text="Buy item" - :disabled="product.price > points" - @click="buyItem(product.id)" - /> - <p v-else>Owned</p> - </div> + </div> + <div class="col-md-12"> + <h1>Items</h1> + <div class="category row mb-2 m-2"> + <div v-for="product in products" :key="product.id" + class="card text-center d-flex justify-content-center align-items-center" + style="width: 16rem; border: none"> + <img :src="apiUrl + `/api/images/${product.imageId}`" style="width: 200px; height: 100px;" + class="card-img-top" alt="..." /> + <div class="card-body"> + <h5 class="card-title">{{ product.itemName }}</h5> + <h6>{{ product.price }}<img src="../../assets/items/pigcoin.png" style="width: 2rem" /></h6> + <ShopButton v-if="!product.alreadyBought" button-text="Buy item" :disabled="product.price > points" + @click="buyItem(product.id)" /> + <p v-else>Owned</p> </div> </div> </div> - <div class="col-md-12"> - <h1>Cool items</h1> - <div class="category row mb-2 m-2"> - <div class="card text-center d-flex justify-content-center align-items-center" style="width: 8rem; border: none"> - <img src="../../assets/items/coffee.jpg" class="card-img-top" alt="..." style="width: 100px; height: 100px;"> - <div class="card-body"> - <h5 class="card-title">Free Coffee</h5> - <h6>500<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> - <ShopButton - button-text="Buy item" - :disabled="500 > points" - @click="buySomething()" - /> - </div> - </div> - <div class="card text-center d-flex justify-content-center align-items-center" style="width: 8rem; border: none"> - <img src="../../assets/items/viaplay.jpg" class="card-img-top" alt="..." style="width: 100px; height: 100px;"> - <div class="card-body"> - <h5 class="card-title">1 Month</h5> - <h6>10 000<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> - <ShopButton - button-text="Buy item" - :disabled="10000 > points" - @click="buySomething()" - /> - </div> - </div> - <div class="card text-center d-flex justify-content-center align-items-center" style="width: 8rem; border: none"> - <img src="../../assets/items/pirbad.png" class="card-img-top" alt="..." style="width: 100px; height: 100px;"> - <div class="card-body"> - <h5 class="card-title">-10% rabatt</h5> - <h6>1000<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> - <ShopButton - button-text="Buy item" - :disabled="1000 > points" - @click="buySomething()" - /> - </div> - </div> - </div> + </div> + <div class="col-md-12"> + <h1>Cool items</h1> + <div class="category row mb-2 m-2"> + <div class="card text-center d-flex justify-content-center align-items-center" + style="width: 8rem; border: none"> + <img src="../../assets/items/coffee.jpg" class="card-img-top" alt="..." + style="width: 100px; height: 100px;"> + <div class="card-body"> + <h5 class="card-title">Free Coffee</h5> + <h6>500<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> + <ShopButton button-text="Buy item" :disabled="500 > points" @click="buySomething()" /> + </div> + </div> + <div class="card text-center d-flex justify-content-center align-items-center" + style="width: 8rem; border: none"> + <img src="../../assets/items/viaplay.jpg" class="card-img-top" alt="..." + style="width: 100px; height: 100px;"> + <div class="card-body"> + <h5 class="card-title">1 Month</h5> + <h6>10 000<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> + <ShopButton button-text="Buy item" :disabled="10000 > points" @click="buySomething()" /> + </div> </div> + <div class="card text-center d-flex justify-content-center align-items-center" + style="width: 8rem; border: none"> + <img src="../../assets/items/pirbad.png" class="card-img-top" alt="..." + style="width: 100px; height: 100px;"> + <div class="card-body"> + <h5 class="card-title">-10% rabatt</h5> + <h6>1000<img src="../../assets/items/pigcoin.png" style="width: 2rem"></h6> + <ShopButton button-text="Buy item" :disabled="1000 > points" @click="buySomething()" /> + </div> + </div> + </div> + </div> + </div> + </div> + + <div class="modal fade" id="premiumModal" tabindex="-1" role="dialog" aria-labelledby="premiumModalLabel" + aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="premiumModalLabel">Premium Package</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <!-- Add premium package information here --> + <p>Unlock exclusive features with our Premium Package!</p> + <ul> + <li>Feature 1</li> + <li>Feature 2</li> + <li>Feature 3</li> + </ul> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + </div> + </div> + </div> + </div> + + <!-- Modal for Ad-free Package --> + <div class="modal fade" id="adfreeModal" tabindex="-1" role="dialog" aria-labelledby="adfreeModalLabel" + aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="adfreeModalLabel">Ad-free Package</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <!-- Add ad-free package information here --> + <p>Enjoy uninterrupted browsing with our Ad-free Package!</p> + <ul> + <li>No more annoying ads</li> + <li>Fast loading times</li> + <li>Exclusive content</li> + </ul> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + </div> </div> </div> </div> - </template> - - <script setup lang="ts"> - import ShopButton from '@/components/Shop/ShopButton.vue'; - import { ref, onMounted } from 'vue'; - import { UserService } from '@/api'; - import { useUserInfoStore } from '@/stores/UserStore'; - import { ItemService } from '@/api'; - import handleUnknownError from '@/components/Exceptions/unkownErrorHandler'; + </div> +</template> + +<script setup lang="ts"> +import ShopButton from '@/components/Shop/ShopButton.vue'; +import { ref, onMounted } from 'vue'; +import { UserService } from '@/api'; +import { useUserInfoStore } from '@/stores/UserStore'; +import { ItemService } from '@/api'; +import handleUnknownError from '@/components/Exceptions/unkownErrorHandler'; - let apiUrl = import.meta.env.VITE_APP_API_URL; - const products = ref([] as any); - const points = ref(); +let apiUrl = import.meta.env.VITE_APP_API_URL; +const products = ref([] as any); +const points = ref(); - /** - * Retrieves the store's products and updates the products list. - */ - const getStore = async () => { - try { - const response = await ItemService.getStore(); - products.value = response; - } catch (error) { - handleUnknownError(error); - console.log(error); - } +/** + * Retrieves the store's products and updates the products list. + */ +const getStore = async () => { + try { + const response = await ItemService.getStore(); + products.value = response; + } catch (error) { + handleUnknownError(error); + console.log(error); } +} - /** - * Retrieves the user's current points and updates the points reference. - */ - const getPoints = async () => { - try { - const response = await UserService.getUser(); - points.value = response.point?.currentPoints; - } catch (error) { - handleUnknownError(error); - console.log(error); - } +/** + * Retrieves the user's current points and updates the points reference. + */ +const getPoints = async () => { + try { + const response = await UserService.getUser(); + points.value = response.point?.currentPoints; + } catch (error) { + handleUnknownError(error); + console.log(error); } +} - /** - * Buys an item with the specified item ID. - * Sends a request to buy the item, then refreshes the store and points information. - * - * @param {number} itemId - The ID of the item to buy. - */ - const buyItem = async (itemId: number) => { - try { - await ItemService.buyItem({ itemId: itemId }); - await getStore(); - await getPoints(); - } catch (error) { - handleUnknownError(error); - console.log(error); - } +/** + * Buys an item with the specified item ID. + * Sends a request to buy the item, then refreshes the store and points information. + * + * @param {number} itemId - The ID of the item to buy. + */ +const buyItem = async (itemId: number) => { + try { + await ItemService.buyItem({ itemId: itemId }); + await getStore(); + await getPoints(); + } catch (error) { + handleUnknownError(error); + console.log(error); } +} - /** - * Buys a premium subscription for the user. - * Sends a request to update the user's subscription level to 'PREMIUM'. - * Updates the user's subscription level in the store. - */ - const buyPremium = async () => { - try { - await UserService.updateSubscriptionLevel({ subscriptionLevel: 'PREMIUM' }); - useUserInfoStore().setUserInfo({ - subscriptionLevel: 'PREMIUM', - }) - } catch (error) { - handleUnknownError(error); - console.log(error); - } +/** + * Buys a premium subscription for the user. + * Sends a request to update the user's subscription level to 'PREMIUM'. + * Updates the user's subscription level in the store. + */ +const buyPremium = async () => { + try { + await UserService.updateSubscriptionLevel({ subscriptionLevel: 'PREMIUM' }); + useUserInfoStore().setUserInfo({ + subscriptionLevel: 'PREMIUM', + }) + } catch (error) { + handleUnknownError(error); + console.log(error); } +} - /** - * Buys a subscription to remove ads for the user. - * Sends a request to update the user's subscription level to 'NO_ADS'. - * Updates the user's subscription level in the store. - */ - const buyNoAds = async () => { - try { - await UserService.updateSubscriptionLevel({ subscriptionLevel: 'NO_ADS' }); - useUserInfoStore().setUserInfo({ - subscriptionLevel: 'NO_ADS', - }) - } catch (error) { - handleUnknownError(error); - console.log(error); - } +/** + * Buys a subscription to remove ads for the user. + * Sends a request to update the user's subscription level to 'NO_ADS'. + * Updates the user's subscription level in the store. + */ +const buyNoAds = async () => { + try { + await UserService.updateSubscriptionLevel({ subscriptionLevel: 'NO_ADS' }); + useUserInfoStore().setUserInfo({ + subscriptionLevel: 'NO_ADS', + }) + } catch (error) { + handleUnknownError(error); + console.log(error); } +} /** * Generates a random code of the specified length. @@ -224,43 +273,42 @@ onMounted(() => { <style scoped> .card { - box-shadow: none; - margin: 10px; - border-radius: 8px; - padding-left: 5px; - padding-right: 5px; - height: 225px; + box-shadow: none; + margin: 10px; + border-radius: 8px; + padding-left: 5px; + padding-right: 5px; + height: 225px; } .box { - width: 90%; - justify-content: center; - text-align: center; - font-size: 5rem; - font-weight: 700; + width: 90%; + justify-content: center; + text-align: center; + font-size: 5rem; + font-weight: 700; } .card:hover { - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .card-body { - height: 100px; - padding: 5px; + height: 100px; + padding: 5px; } .col-md-12 { - border-bottom: 2px solid #000000; + border-bottom: 2px solid #000000; } #dropdownContainer { - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 2rem; - flex-direction: column; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 2rem; + flex-direction: column; } -#background { -} +#background {} </style> \ No newline at end of file diff --git a/src/components/UserProfile/ExternalProfile.vue b/src/components/UserProfile/ExternalProfile.vue index 0baf368..f95f188 100644 --- a/src/components/UserProfile/ExternalProfile.vue +++ b/src/components/UserProfile/ExternalProfile.vue @@ -5,6 +5,7 @@ import { useUserInfoStore } from "@/stores/UserStore"; import {UserService, BadgeService, GoalService, type GoalDTO, type BadgeDTO, FriendService} from "@/api"; import { ItemService } from "@/api"; import handleUnknownError from '@/components/Exceptions/unkownErrorHandler' +import bannerImage from '@/assets/banners/stacked.svg' let apiUrl = import.meta.env.VITE_APP_API_URL; @@ -12,6 +13,8 @@ let firstname = ref(); let lastname = ref(); const imageUrl = ref(`../src/assets/userprofile.png`); +const bannerImageUrl = ref(bannerImage); + let hasBadges = ref(false) let hasInventory = ref(false) @@ -26,6 +29,8 @@ const streak = ref(0 as any); const isFriend = ref(false); const isRequestSent = ref(false); +const isMe = ref(false); + /** * Sets up the form for displaying user profile information. * Retrieves user profile data including first name, last name, points, streak, profile image, inventory, and badges. @@ -50,7 +55,12 @@ async function setupForm() { if (response.profileImage) { imageUrl.value = apiUrl + "/api/images/" + response.profileImage; } - getInventory(); + if (response.bannerImage != 0 && response.bannerImage !== null) { + console.log(response.bannerImage) + bannerImageUrl.value = apiUrl + "/api/images/" + response.bannerImage; + } + let userId = route.params.id; + isMe.value = String(userId) !== String(useUserInfoStore().id); getBadges(); } catch (err) { handleUnknownError(err) @@ -143,14 +153,34 @@ const removeFriend = () => { <div class="row d-flex justify-content-center align-items-center h-100"> <div class="col 12"> <div class="card"> - <div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;" id="banner"> - <div class=" d-flex flex-column align-items-center justify-content-center"> - <img :src="imageUrl" alt="Generisk plassholderbilde" class="img-fluid img-thumbnail" - style="width: 150px; height:150px; margin-left: 25px; margin-right: 15px;"> + <div class="rounded-top text-white d-flex flex-row bg-primary justify-content-between" :style="{ + height: '200px', + backgroundImage: `url(${bannerImageUrl})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat' + }"> + <div class=" text-white d-flex flex-row"> + <div class=" d-flex flex-column align-items-center justify-content-center"> + <img :src="imageUrl" alt="Generisk plassholderbilde" class="img-fluid img-thumbnail" + style="width: 150px; height:150px; margin-left: 25px; margin-right: 15px;"> + </div> + <h1 data-cy="firstname" style="display: flex; align-items: end; margin-bottom: 20px;">{{ firstname }} {{ + lastname }}</h1> + </div> + <div class="d-flex align-items-end text-white my-3 mx-5"> + <div class="d-flex align-items-center flex-column"> + <p class="mb-1 h2 d-flex flex-column align-items-center" data-cy="points"><img + src="@/assets/items/pigcoin.png" style="width: 80px; height: 80px" data-toggle="tooltip" + title="Points"> {{ points }}</p> + </div> + <div class="d-flex align-items-center flex-column px-3"> + <p class="mb-1 h2 d-flex flex-column align-items-center" data-cy="streak"><img + src="@/assets/icons/fire.png" style="width: 80px; height: 80px" data-toggle="tooltip" + title="Points"> {{ streak }}</p> + </div> </div> - <h1 data-cy="firstname" style="display: flex; align-items: end; margin-bottom: 20px;">{{ firstname }} {{ lastname }}</h1> </div> - <div class="p-3 text-black" style="background-color: #f8f9fa;"> + <div v-if="isMe" class="p-3 text-black" style="background-color: #f8f9fa;"> <div class="d-flex justify-content-end text-center py-1"> <div style="width: 100%; display: flex; justify-content: start"> <button @@ -178,35 +208,6 @@ const removeFriend = () => { Fjern venn </button> </div> - <div> - <p class="mb-1 h2" data-cy="points">{{ points }} <img src="@/assets/items/pigcoin.png" style="width: 4rem"></p> - <p class="small text-muted mb-0">Poeng</p> - </div> - <div class="px-3"> - <p class="mb-1 h2" data-cy="streak">{{ streak }} <img src="@/assets/icons/fire.png" style="width: 4rem"></p> - <p class="small text-muted mb-0">Streak</p> - </div> - </div> - </div> - <hr> - <div class="card-body p-1 text-black"> - <div class="row"> - <div class="col"> - <div class="container-fluid"> - <h1 class="mt-1 text-start badges-text">Lageret ditt</h1> - <div v-if="hasInventory" class="scrolling-wrapper-badges row flex-row flex-nowrap mt-2 pb-2 pt-2"> - <div v-for="product in inventory" :key="product.id" class="card text-center" - style="width: 12rem; border: none; cursor: pointer; margin: 1rem; border: 2px solid black"> - <img :src="apiUrl + `/api/images/${product.imageId}`" class="card-img-top" - alt="..." /> - <div class="card-body"> - <h5 class="card-title">{{ product.itemName }}</h5> - </div> - </div> - </div> - <div v-else>Ingen gjenstander</div> - </div> - </div> </div> </div> <hr> diff --git a/src/components/UserProfile/MyProfile.vue b/src/components/UserProfile/MyProfile.vue index 6e20e5d..e91e5b3 100644 --- a/src/components/UserProfile/MyProfile.vue +++ b/src/components/UserProfile/MyProfile.vue @@ -250,7 +250,8 @@ const toUpdateUserSettings = () => { <div class="card-body"> <h5 class="card-title">{{ goals[index]['name'] }}</h5> <p class="card-text">{{ goals[index]['description'] }}</p> - <p class="card-text"><small class="text-muted">{{ goals[index]['targetAmount'] }}</small> + <p class="card-text"><small class="text-muted">{{ goals[index]['targetAmount'] + }}</small> </p> <a href="#" class="btn stretched-link" @click="toRoadmap"></a> </div> -- GitLab