mirror of
https://github.com/PhaseII-eAmusement-Network/PhaseWeb3-Vue.git
synced 2026-03-21 17:54:26 -05:00
arcade paseli charge support, fiz table bug
Some checks failed
Build / build (push) Has been cancelled
Some checks failed
Build / build (push) Has been cancelled
This commit is contained in:
parent
f2001b4126
commit
7bbc2d2c77
145
index.html
145
index.html
|
|
@ -1,75 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<link rel="apple-touch-startup-image" href="/favicon.png">
|
||||
<meta name="apple-mobile-web-app-title" content="PhaseII">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<link rel="manifest" href="/data-sources/standalone.json" />
|
||||
<title>PhaseWeb 3</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
/* @media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var pattern = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
||||
var current = 0;
|
||||
|
||||
var keyHandler = function (event) {
|
||||
if (pattern.indexOf(event.key) < 0 || event.key !== pattern[current]) {
|
||||
current = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
current++;
|
||||
|
||||
if (pattern.length === current) {
|
||||
current = 0;
|
||||
document.getElementById('audio').play();
|
||||
window.alert('+30 lives.');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', keyHandler, false);
|
||||
</script>
|
||||
</head>
|
||||
<body class="scroll-smooth">
|
||||
<noscript>
|
||||
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<!-- Particles -->
|
||||
<!-- <div id="particles-js"></div>
|
||||
<script src="/assets/particles.js"></script> -->
|
||||
<!-- <script src="/assets/network.js"></script> -->
|
||||
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- Bats! -->
|
||||
<!-- <script src="https://code.jquery.com/jquery.js"></script>
|
||||
<script src="/assets/halloween-bats.js"></script>
|
||||
<script type="text/javascript">
|
||||
window.halloweenBats = $.halloweenBats({
|
||||
image: "/assets/bats.png",
|
||||
});
|
||||
</script> -->
|
||||
|
||||
<audio id="audio" src="/assets/sounds/konami.mp3" class="hidden">
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<link rel="apple-touch-startup-image" href="/favicon.png">
|
||||
<meta name="apple-mobile-web-app-title" content="PhaseII">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<link rel="manifest" href="/data-sources/standalone.json" />
|
||||
<title>PhaseWeb 3</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
/* @media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
} */
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var pattern = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
|
||||
var current = 0;
|
||||
|
||||
var keyHandler = function (event) {
|
||||
if (pattern.indexOf(event.key) < 0 || event.key !== pattern[current]) {
|
||||
current = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
current++;
|
||||
|
||||
if (pattern.length === current) {
|
||||
current = 0;
|
||||
document.getElementById('audio').play();
|
||||
window.alert('+30 lives.');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', keyHandler, false);
|
||||
</script>
|
||||
</head>
|
||||
<body class="scroll-smooth">
|
||||
<noscript>
|
||||
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<!-- Particles -->
|
||||
<!-- <div id="particles-js"></div>
|
||||
<script src="/assets/particles.js"></script> -->
|
||||
<!-- <script src="/assets/network.js"></script> -->
|
||||
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- Bats! -->
|
||||
<script src="https://code.jquery.com/jquery.js"></script>
|
||||
<script src="/assets/halloween-bats.js"></script>
|
||||
|
||||
<audio id="audio" src="/assets/sounds/konami.mp3" class="hidden">
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -27,5 +27,6 @@
|
|||
"3.0.25-oMini": ["- (Major ++) Move from Material Design Icons to Phosphor Icons. Icons are currently set to their initial picks, but may change (suggest a change!)", "- (Major) Add initial icon animations, start work on icon color standards", "- (Optimization) Improve random greeting selection (SoftieTechCat) ", "- (Minor) Fix ESlint config"],
|
||||
"3.0.26": ["- (Minor) Game player table is more sortable", "- (Minor) Greetings now support custom styling", "- (Minor) Added the first styled greeting"],
|
||||
"3.0.27": ["- (Major) Profile data exporting added.", "- (Minor) More work for rivals."],
|
||||
"3.0.28": ["- (Major) Full support for rivals across applicable games", "- (Major) Move from Vue Hash routing to standard routing", "- (Bugfix) Fix issue when customizing on iOS"]
|
||||
"3.0.28": ["- (Major) Full support for rivals across applicable games", "- (Major) Move from Vue Hash routing to standard routing", "- (Bugfix) Fix issue when customizing on iOS"],
|
||||
"3.0.29": ["- (Major) Finish arcade PASELI support", "- (Minor) Clean up arcade page, add button for opening owner", "- (Bugfix) Fix table upper curved edges"]
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ function downloadJSON() {
|
|||
header-text-direction="left"
|
||||
body-text-direction="left"
|
||||
:prevent-context-menu-row="false"
|
||||
class="pt-2"
|
||||
@click-row="handleRowClick"
|
||||
>
|
||||
<template #loading>
|
||||
|
|
|
|||
|
|
@ -80,6 +80,20 @@ export async function APIGetArcadePASELI(arcadeId) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function APICreditUserPASELI(arcadeId, userId, cardId, credit) {
|
||||
try {
|
||||
const data = await mainStore.callApi(`/arcade/${arcadeId}/paseli`, "POST", {
|
||||
userId: userId,
|
||||
cardId: cardId,
|
||||
credit: credit,
|
||||
});
|
||||
return data.data;
|
||||
} catch (error) {
|
||||
console.log("Error fetching arcade PASELI data:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function APIStartTakeover(PCBID) {
|
||||
try {
|
||||
const data = await mainStore.callApi(`/arcade/takeover?PCBID=${PCBID}`);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import FormControl from "@/components/FormControl.vue";
|
|||
import PillTag from "@/components/PillTag.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import BaseButtons from "@/components/BaseButtons.vue";
|
||||
import BaseDivider from "@/components/BaseDivider.vue";
|
||||
import CardBoxWidget from "@/components/CardBoxWidget.vue";
|
||||
import UserAvatar from "@/components/UserAvatar.vue";
|
||||
|
|
@ -237,6 +238,10 @@ async function adminDeleteArcade() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const navigateToProfile = (userID) => {
|
||||
$router.push(`/profiles/${userID}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -373,11 +378,18 @@ async function adminDeleteArcade() {
|
|||
{{ owner.username }}
|
||||
</h1>
|
||||
</div>
|
||||
<BaseButton
|
||||
color="danger"
|
||||
label="Remove"
|
||||
@click="removeManager(owner.id)"
|
||||
/>
|
||||
<BaseButtons>
|
||||
<BaseButton
|
||||
label="Open Profile"
|
||||
color="info"
|
||||
@click="navigateToProfile(owner.id)"
|
||||
/>
|
||||
<BaseButton
|
||||
color="danger"
|
||||
label="Remove"
|
||||
@click="removeManager(owner.id)"
|
||||
/>
|
||||
</BaseButtons>
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,35 @@
|
|||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { PhCashRegister, PhCurrencyJpy } from "@phosphor-icons/vue";
|
||||
import {
|
||||
PhCashRegister,
|
||||
PhCreditCard,
|
||||
PhCurrencyJpy,
|
||||
PhPlusCircle,
|
||||
} from "@phosphor-icons/vue";
|
||||
import SectionMain from "@/components/SectionMain.vue";
|
||||
import GeneralTable from "@/components/GeneralTable.vue";
|
||||
import CardBox from "@/components/CardBox.vue";
|
||||
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import ArcadeCard from "@/components/ArcadeCard.vue";
|
||||
import { APIGetArcade, APIGetArcadePASELI } from "@/stores/api/arcade";
|
||||
import PillTag from "@/components/PillTag.vue";
|
||||
import FormControl from "@/components/FormControl.vue";
|
||||
import FormField from "@/components/FormField.vue";
|
||||
import {
|
||||
APIGetArcade,
|
||||
APIGetArcadePASELI,
|
||||
APICreditUserPASELI,
|
||||
} from "@/stores/api/arcade";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import CardBoxWidget from "@/components/CardBoxWidget.vue";
|
||||
|
||||
const arcadeData = ref({});
|
||||
const paseliData = ref({});
|
||||
const loading = ref(true);
|
||||
|
||||
const newBalance = ref({});
|
||||
|
||||
const $route = useRoute();
|
||||
const arcadeId = parseInt($route.params.id);
|
||||
|
||||
|
|
@ -32,6 +48,7 @@ async function loadPASELI() {
|
|||
paseliData.value = null;
|
||||
const data = await APIGetArcadePASELI(arcadeId);
|
||||
paseliData.value = data;
|
||||
paseliData.value.transactions?.reverse();
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch arcade PASELI:", error);
|
||||
|
|
@ -91,6 +108,106 @@ function filterTransactions(transactions) {
|
|||
|
||||
return filteredTransactions;
|
||||
}
|
||||
|
||||
function cardInput(event) {
|
||||
var input = event.target.value;
|
||||
input = input.replace(/[^A-Za-z0-9]/g, "");
|
||||
|
||||
// Format the input to XXXX-XXXX-XXXX-XXXX
|
||||
input = input
|
||||
.toUpperCase()
|
||||
.replace(/(.{4})/g, "$1-")
|
||||
.replace(/-$/, "");
|
||||
|
||||
newBalance.value.cardId = input;
|
||||
}
|
||||
|
||||
function numInput(event) {
|
||||
var input = event.target.value;
|
||||
input = input.replace(/[^0-9]/g, "");
|
||||
|
||||
newBalance.value.credit = input;
|
||||
}
|
||||
|
||||
const paseliNetWorth = computed(() => {
|
||||
if (!paseliData.value?.balances) return 0;
|
||||
return paseliData.value.balances.reduce(
|
||||
(sum, entry) => sum + (entry.balance || 0),
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
const paseliStats = computed(() => {
|
||||
if (!paseliData.value?.balances?.length) return {};
|
||||
|
||||
const balances = paseliData.value.balances.filter((b) => b.balance > 0);
|
||||
const total = balances.reduce((sum, b) => sum + b.balance, 0);
|
||||
const avg = total / balances.length;
|
||||
const top = balances.reduce(
|
||||
(a, b) => (b.balance > a.balance ? b : a),
|
||||
balances[0],
|
||||
);
|
||||
|
||||
return {
|
||||
total: total,
|
||||
avg: avg,
|
||||
count: balances.length,
|
||||
topPlayer: top.username || "(anonymous)",
|
||||
topBalance: top.balance,
|
||||
};
|
||||
});
|
||||
|
||||
const paseliTransactions = computed(() => {
|
||||
if (!paseliData.value?.transactions)
|
||||
return { totalDelta: 0, count: 0, avgDelta: 0, last: null };
|
||||
|
||||
const tx = paseliData.value.transactions;
|
||||
const totalDelta = tx.reduce((sum, t) => sum + (t.data?.delta || 0), 0);
|
||||
const avgDelta = totalDelta / (tx.length || 1);
|
||||
const lastTx = tx[tx.length - 1] || null;
|
||||
|
||||
return {
|
||||
totalDelta,
|
||||
count: tx.length,
|
||||
avgDelta,
|
||||
last: lastTx,
|
||||
};
|
||||
});
|
||||
|
||||
const existingBalances = computed(() => {
|
||||
var balances = [];
|
||||
for (const player of paseliData.value.balances) {
|
||||
balances.push({
|
||||
id: player.userId,
|
||||
label: player.username == "" ? "Unclaimed Account" : player.username,
|
||||
});
|
||||
}
|
||||
return balances;
|
||||
});
|
||||
|
||||
async function creditCard() {
|
||||
if (!newBalance.value.cardId && !newBalance.value.userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newBalance.value.credit) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await APICreditUserPASELI(
|
||||
arcadeId,
|
||||
newBalance.value.userId,
|
||||
newBalance.value.cardId,
|
||||
newBalance.value.credit,
|
||||
);
|
||||
|
||||
newBalance.value.userId = null;
|
||||
newBalance.value.cardId = null;
|
||||
newBalance.value.credit = null;
|
||||
await loadPASELI();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -98,29 +215,147 @@ function filterTransactions(transactions) {
|
|||
<SectionMain>
|
||||
<template v-if="!loading && arcadeData">
|
||||
<ArcadeCard class="mb-6" :arcade="arcadeData" :use-small="true" />
|
||||
|
||||
<SectionTitleLine :icon="PhCurrencyJpy" title="PASELI Stats" main />
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 mb-6">
|
||||
<CardBoxWidget
|
||||
v-if="paseliNetWorth"
|
||||
:number="paseliNetWorth"
|
||||
label="Total PASELI Net Worth"
|
||||
:icon="PhCurrencyJpy"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="paseliTransactions.count"
|
||||
:number="paseliTransactions.count"
|
||||
label="Recent Transactions"
|
||||
:icon="PhCashRegister"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="paseliTransactions.totalDelta"
|
||||
:number="paseliTransactions.totalDelta"
|
||||
label="Recent Net Flow"
|
||||
:icon="PhCurrencyJpy"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="paseliStats.avg"
|
||||
:number="paseliStats.avg"
|
||||
label="Average Player Balance"
|
||||
:icon="PhCurrencyJpy"
|
||||
/>
|
||||
<CardBoxWidget
|
||||
v-if="paseliStats.topBalance"
|
||||
:number="paseliStats.topBalance"
|
||||
:label="`Top Player: ${paseliStats.topPlayer}`"
|
||||
:icon="PhCurrencyJpy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SectionTitleLine
|
||||
:icon="PhCurrencyJpy"
|
||||
title="Player PASELI Balances"
|
||||
main
|
||||
/>
|
||||
<CardBox has-table>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"
|
||||
>
|
||||
<div class="w-full">
|
||||
<GeneralTable
|
||||
:headers="balanceHeaders"
|
||||
:items="paseliData.balances"
|
||||
<div class="grid md:grid-cols-2 gap-6 mb-6">
|
||||
<div class="grid gap-6">
|
||||
<CardBox is-form>
|
||||
<PillTag
|
||||
label="Update balance"
|
||||
color="success"
|
||||
class="mb-4"
|
||||
:icon="PhPlusCircle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<FormField
|
||||
label="Card ID"
|
||||
help="The ACCESS CODE provided by the game."
|
||||
>
|
||||
<FormControl
|
||||
v-model="newBalance.cardId"
|
||||
:icon="PhCreditCard"
|
||||
name="cardId"
|
||||
type="card"
|
||||
placeholder="XXXX-XXXX-XXXX-XXXX"
|
||||
:minlength="19"
|
||||
:maxlength="19"
|
||||
required
|
||||
@input="cardInput"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Credit Amount">
|
||||
<FormControl
|
||||
v-model="newBalance.credit"
|
||||
:icon="PhCurrencyJpy"
|
||||
name="credit"
|
||||
required
|
||||
:maxlength="7"
|
||||
inputmode="numeric"
|
||||
@input="numInput"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseButton
|
||||
label="Credit Account"
|
||||
color="success"
|
||||
@click="creditCard"
|
||||
/>
|
||||
</CardBox>
|
||||
|
||||
<CardBox is-form>
|
||||
<PillTag
|
||||
label="Credit existing account"
|
||||
color="info"
|
||||
class="mb-4"
|
||||
:icon="PhPlusCircle"
|
||||
/>
|
||||
|
||||
<FormField label="Select Player">
|
||||
<FormControl
|
||||
v-model="newBalance.userId"
|
||||
:options="existingBalances"
|
||||
required
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Credit Amount">
|
||||
<FormControl
|
||||
v-model="newBalance.credit"
|
||||
:icon="PhCurrencyJpy"
|
||||
name="credit"
|
||||
required
|
||||
:maxlength="7"
|
||||
inputmode="numeric"
|
||||
@input="numInput"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<BaseButton
|
||||
label="Credit Selected Player"
|
||||
color="info"
|
||||
@click="creditCard"
|
||||
/>
|
||||
</CardBox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<CardBox has-table>
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"
|
||||
>
|
||||
<div class="w-full">
|
||||
<GeneralTable
|
||||
:headers="balanceHeaders"
|
||||
:items="paseliData.balances"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
</div>
|
||||
</div>
|
||||
<SectionTitleLine
|
||||
:icon="PhCashRegister"
|
||||
title="PASELI Transaction History"
|
||||
main
|
||||
class="pt-6"
|
||||
/>
|
||||
<CardBox has-table>
|
||||
<div
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user