From c0af76abd3822e0f164b28e176dab87f0b81e277 Mon Sep 17 00:00:00 2001 From: Edd Date: Wed, 31 Dec 2025 18:28:29 +0000 Subject: [PATCH] Implemented coin purchase-flow --- .../Controllers/CoinPurchaseController.php | 3 +- api/sample-requests.http | 20 ++- frontend/src/components/layout/NavBar.vue | 22 +-- .../src/pages/purchase/CoinPurchasePage.vue | 139 ++++++++++++++++++ frontend/src/router/index.js | 7 + frontend/src/stores/purchase.js | 16 ++ frontend/src/stores/user.js | 20 ++- 7 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 frontend/src/pages/purchase/CoinPurchasePage.vue create mode 100644 frontend/src/stores/purchase.js diff --git a/api/app/Http/Controllers/CoinPurchaseController.php b/api/app/Http/Controllers/CoinPurchaseController.php index 5827e08..7fe31c2 100644 --- a/api/app/Http/Controllers/CoinPurchaseController.php +++ b/api/app/Http/Controllers/CoinPurchaseController.php @@ -15,7 +15,7 @@ class CoinPurchaseController extends Controller { $validated = $request->validate([ 'euros' => 'required|numeric|min:1|max:100', - 'payment_type' => 'required|in:MBWAY,IBAN,MB,VISA,PAYPAL', + 'payment_type' => 'required|in:MBWAY,MB,VISA,PAYPAL', 'payment_reference' => [ 'required', function ($attribute, $value, $fail) use ($request) { @@ -24,7 +24,6 @@ class CoinPurchaseController extends Controller switch ($type) { case 'MBWAY': $regex = '/^9[0-9]{8}$/'; break; - case 'IBAN': $regex = '/^[A-Z]{2}[0-9]{23}$/'; break; case 'MB': $regex = '/^[0-9]{5}-[0-9]{9}$/'; break; case 'VISA': $regex = '/^4[0-9]{15}$/'; break; case 'PAYPAL': diff --git a/api/sample-requests.http b/api/sample-requests.http index b8f4745..2d3ba73 100644 --- a/api/sample-requests.http +++ b/api/sample-requests.http @@ -25,6 +25,12 @@ Content-Type: application/json Accept: application/json Authorization: Bearer {{token}} +### Get me coins +GET http://localhost:8000/api/v1/users/me/coins +Content-Type: application/json +Accept: application/json +Authorization: Bearer {{token}} + ### Get me matches GET http://localhost:8000/api/v1/users/{{id}}/matches Content-Type: application/json @@ -35,4 +41,16 @@ Authorization: Bearer {{token}} GET http://localhost:8000/api/v1/users/{{id}}/transactions?type=C Content-Type: application/json Accept: application/json -Authorization: Bearer {{token}} \ No newline at end of file +Authorization: Bearer {{token}} + +### post coin-purchases +POST http://localhost:8000/api/v1/coin-purchases +Content-Type: application/json +Accept: application/json +Authorization: Bearer {{token}} + +{ + "euros": 1, + "payment_type": "MBWAY", + "payment_reference": "912345678" +} \ No newline at end of file diff --git a/frontend/src/components/layout/NavBar.vue b/frontend/src/components/layout/NavBar.vue index facc2e0..60230b9 100644 --- a/frontend/src/components/layout/NavBar.vue +++ b/frontend/src/components/layout/NavBar.vue @@ -23,14 +23,14 @@ -
$
- {{ coins }} Coins + {{ userStore.coins }} Coins
@@ -56,13 +56,12 @@ import { NavigationMenuTrigger, } from '@/components/ui/navigation-menu' import router from '@/router'; -import { ref, watch } from 'vue'; +import { watch } from 'vue'; import { useUserStore } from '@/stores/user'; const emits = defineEmits(['logout']) const { userLoggedIn } = defineProps(['userLoggedIn']) -const coins = ref(0); const biscaStore = useBiscaStore() @@ -84,22 +83,13 @@ const logoutClickHandler = () => { router.push('/login') } -const fetchUserCoins = async () => { - if (userLoggedIn) { - try { - const response = await useUserStore().getCoins() - coins.value = response.data?.coins || 0 - } catch (error) { - console.error('Error fetching user coins', error) - } - } -} +const userStore = useUserStore() watch(() => userLoggedIn, async (isLoggedIn) => { if (isLoggedIn) { - await fetchUserCoins() + await userStore.fetchCoins() } else { - coins.value = 0 + userStore.coins = 0 } }, { immediate: true }) \ No newline at end of file diff --git a/frontend/src/pages/purchase/CoinPurchasePage.vue b/frontend/src/pages/purchase/CoinPurchasePage.vue new file mode 100644 index 0000000..bfb715c --- /dev/null +++ b/frontend/src/pages/purchase/CoinPurchasePage.vue @@ -0,0 +1,139 @@ + + + \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index d1b8c0f..df46c27 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -10,6 +10,7 @@ import TestAllAnimations from '@/pages/TestAllAnimations.vue' import TestGameBoard from '@/pages/TestGameBoard.vue' import SinglePlayerGamePage from '@/pages/game/SinglePlayerGamePage.vue' import RegisterPage from '@/pages/register/RegisterPage.vue' +import CoinsPurchasePage from '@/pages/purchase/CoinPurchasePage.vue' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -46,6 +47,12 @@ const router = createRouter({ { path: '/user', component: UserPage, + meta: { requiresAuth: true }, + }, + { + path: '/coins-purchase', + component: CoinsPurchasePage, + meta: { requiresAuth: true }, }, { path: '/testing', diff --git a/frontend/src/stores/purchase.js b/frontend/src/stores/purchase.js new file mode 100644 index 0000000..4c103b1 --- /dev/null +++ b/frontend/src/stores/purchase.js @@ -0,0 +1,16 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { inject } from 'vue' + +export const usePurchaseStore = defineStore('purchase', () => { + + const API_BASE_URL = inject('apiBaseURL') + + const purchase = async (data) => { + return await axios.post(`${API_BASE_URL}/coin-purchases`, data) + } + + return { + purchase, + } +}) diff --git a/frontend/src/stores/user.js b/frontend/src/stores/user.js index db36d7f..f410ab9 100644 --- a/frontend/src/stores/user.js +++ b/frontend/src/stores/user.js @@ -1,16 +1,24 @@ import { defineStore } from 'pinia' import axios from 'axios' -import { inject } from 'vue' +import { inject, ref } from 'vue' export const useUserStore = defineStore('user', () => { - const API_BASE_URL = inject('apiBaseURL') + const coins = ref(0) - const getCoins = async () => { - return await axios.get(`${API_BASE_URL}/users/me/coins`) + const fetchCoins = async () => { + try { + const response = await axios.get(`${API_BASE_URL}/users/me/coins`) + coins.value = response.data?.coins || 0 + return response + } catch (error) { + console.error('Error fetching coins', error) + throw error + } } return { - getCoins, + coins, + fetchCoins, } -}) +}) \ No newline at end of file -- 2.54.0