mirror of
https://github.com/PhaseII-eAmusement-Network/PhaseWeb3-Vue.git
synced 2026-08-27 19:44:14 -05:00
Add basic API key creation/deletion page. Clean up Update Modal, fill out Admin Event page, Fix user avatar formatting.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
VITE_APP_VERSION="3.0.2"
|
||||
VITE_APP_VERSION="3.0.3"
|
||||
VITE_API_URL="http://10.5.7.5:8000/"
|
||||
VITE_API_KEY="your_api_key_should_be_here"
|
||||
VITE_ASSET_PATH="/assets"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VITE_APP_VERSION="3.0.2"
|
||||
VITE_APP_VERSION="3.0.3"
|
||||
VITE_API_URL="https://restfulsleep.phaseii.network"
|
||||
VITE_API_KEY="your_api_key_should_be_here"
|
||||
VITE_ASSET_PATH="https://cdn.phaseii.network/file/PhaseII/web-assets"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"3.0.1": ["- (Feature) Added update popup", "- (Optimization) Disabled ParticlesJS for the time being. This will be returned as an option in the future.", "- (Optimization) Fill in more score table data for GFDM.", "- (soon™️) I've started rival support, but it's not ready just yet. Stay tuned!"],
|
||||
"3.0.2": ["- (Feature) Added new greetings", "- (Feature) Added new user background", "- (Feature) Added toggle for disabling/enabling this popup", "- (Feature) Added version info to footer", "- (Feature) Fill in more profile data on personal profile view", "- (Fix) DDR stats now work again", "- (Fix) DDR player weight can now be set (kinda)"]
|
||||
"3.0.2": ["- (Feature) Added new greetings", "- (Feature) Added new user background", "- (Feature) Added toggle for disabling/enabling this popup", "- (Feature) Added version info to footer", "- (Feature) Fill in more profile data on personal profile view", "- (Fix) DDR stats now work again", "- (Fix) DDR player weight can now be set (kinda)"],
|
||||
"3.0.3": ["- (Feature) Discord avatars are now reactive!", "- (Feature) New profile badges! I'll be working on a backend for these soon™️", "- (Optimization) Fix this box's formatting on mobile."]
|
||||
}
|
||||
@@ -85,8 +85,10 @@ async function updateUserData(disable = false) {
|
||||
w="w-20"
|
||||
:size="45"
|
||||
/>
|
||||
<h1 class="text-2xl font-bold">Since you've been gone...</h1>
|
||||
<h1 class="text-lg md:text-xl">
|
||||
<h1 class="text-xl xl:text-2xl font-bold">
|
||||
While you were away...
|
||||
</h1>
|
||||
<h1 class="text-lg xl:text-xl">
|
||||
We've updated the site! These updates include feature updates and
|
||||
bugfixes.
|
||||
</h1>
|
||||
@@ -96,7 +98,7 @@ async function updateUserData(disable = false) {
|
||||
|
||||
<div
|
||||
v-if="changelog"
|
||||
class="text-lg md:text-xl space-y-2 text-left text-pink-500 bg-slate-950 p-2"
|
||||
class="text-sm md:text-md space-y-2 text-left text-pink-500 bg-slate-950 p-2 max-h-[120px] overflow-scroll"
|
||||
>
|
||||
<h1>Changelog:</h1>
|
||||
<h2
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { useMainStore } from "@/stores/main";
|
||||
import {
|
||||
mdiSecurity,
|
||||
// mdiTestTube,
|
||||
// mdiAccountStar,
|
||||
// mdiCodeBraces,
|
||||
mdiTestTube,
|
||||
mdiAccountStar,
|
||||
mdiCodeBraces,
|
||||
mdiAccountOff,
|
||||
// mdiAccountCheck,
|
||||
} from "@mdi/js";
|
||||
@@ -50,7 +50,9 @@ function getCardStyle() {
|
||||
type="justify-around lg:justify-center md:space-x-4 lg:space-x-0"
|
||||
class="bg-white dark:bg-slate-900/90 rounded-2xl p-3"
|
||||
>
|
||||
<UserAvatarCurrentUser class="w-28 md:w-32 lg:w-44 lg:mx-12" />
|
||||
<UserAvatarCurrentUser
|
||||
class="w-28 md:w-30 lg:w-[128px] lg:mx-12 lg:m-2"
|
||||
/>
|
||||
<div class="space-y-3 text-center md:text-left lg:mx-12">
|
||||
<div class="space-y-2 md:space-y-0">
|
||||
<h1
|
||||
@@ -69,18 +71,42 @@ function getCardStyle() {
|
||||
</div>
|
||||
<div
|
||||
v-if="!evenSmaller"
|
||||
class="md:grid md:grid-flow-col md:auto-cols-max md:grid-rows-1 flex flex-wrap gap-4 md:place-content-start place-content-center"
|
||||
class="flex flex-wrap gap-2 md:place-content-start place-content-center px-5 sm:px-0 py-2 sm:py-0 md:max-w-[400px]"
|
||||
>
|
||||
<PillTag
|
||||
v-if="mainStore.userAdmin"
|
||||
label="System Admin"
|
||||
color="danger"
|
||||
:icon="mdiSecurity"
|
||||
small
|
||||
/>
|
||||
<PillTag
|
||||
v-if="mainStore.userData.beta"
|
||||
label="Beta Tester"
|
||||
color="warning"
|
||||
:icon="mdiTestTube"
|
||||
small
|
||||
/>
|
||||
<PillTag
|
||||
v-if="mainStore.userData.vet"
|
||||
label="Veteran"
|
||||
color="success"
|
||||
:icon="mdiAccountStar"
|
||||
small
|
||||
/>
|
||||
<PillTag
|
||||
v-if="mainStore.userData.dev"
|
||||
label="Active Dev"
|
||||
color="info"
|
||||
:icon="mdiCodeBraces"
|
||||
small
|
||||
/>
|
||||
<PillTag
|
||||
label="Private Profile"
|
||||
color="info"
|
||||
:icon="mdiAccountOff"
|
||||
small
|
||||
/>
|
||||
<!-- <PillTag label="Beta Tester" color="warning" :icon="mdiTestTube" />
|
||||
<PillTag label="Veteran" color="success" :icon="mdiAccountStar" />
|
||||
<PillTag label="Active Dev" color="info" :icon="mdiCodeBraces" /> -->
|
||||
<PillTag label="Private Profile" color="info" :icon="mdiAccountOff" />
|
||||
<!-- <PillTag
|
||||
label="Public Profile"
|
||||
color="success"
|
||||
|
||||
@@ -65,3 +65,23 @@ export async function APIAdminCreateMaintenancePeriod(newPeriod) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function APIAdminClients() {
|
||||
try {
|
||||
const data = await mainStore.callApi(`/admin/client`);
|
||||
return data.data;
|
||||
} catch (error) {
|
||||
console.log("Error loading client:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function APIAdminCreateClient(newClient) {
|
||||
try {
|
||||
const data = await mainStore.callApi(`/admin/client`, "POST", newClient);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.log("Error posting client:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,140 @@
|
||||
<script setup>
|
||||
import { mdiFlagCheckered, mdiSecurity } from "@mdi/js";
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { mdiCloudKeyOutline, mdiKeyPlus } from "@mdi/js";
|
||||
import SectionMain from "@/components/SectionMain.vue";
|
||||
import CardBox from "@/components/CardBox.vue";
|
||||
import CardBoxWidget from "@/components/CardBoxWidget.vue";
|
||||
import GeneralTable from "@/components/GeneralTable.vue";
|
||||
import FormField from "@/components/FormField.vue";
|
||||
import FormControl from "@/components/FormControl.vue";
|
||||
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
import { APIAdminClients, APIAdminCreateClient } from "@/stores/api/admin";
|
||||
|
||||
const clientData = ref([]);
|
||||
const clientHeaders = [
|
||||
{
|
||||
text: "Create Timestamp",
|
||||
value: "timestamp",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Name",
|
||||
value: "name",
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
const initClient = {
|
||||
name: null,
|
||||
};
|
||||
|
||||
const newClient = reactive({
|
||||
...initClient,
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const data = await APIAdminClients();
|
||||
var formattedItems = [];
|
||||
for (var item of data) {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.timestamp = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
}
|
||||
|
||||
clientData.value = formattedItems;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch client data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function createClient() {
|
||||
try {
|
||||
const data = await APIAdminCreateClient(newClient);
|
||||
clientData.value = data;
|
||||
} catch (error) {
|
||||
console.error("Failed to post client:", error);
|
||||
}
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
const copyToClipboard = (item) => {
|
||||
navigator.clipboard
|
||||
.writeText(item.token)
|
||||
.then(() => {
|
||||
alert("Copied to clipboard!");
|
||||
})
|
||||
.catch(() => {
|
||||
alert("Failed to copy to clipboard!");
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutAuthenticated>
|
||||
<SectionMain>
|
||||
<CardBox class="mb-6 p-1">
|
||||
<h1 class="text-3xl">Data API</h1>
|
||||
<p class="text-sm text-gray-400">Used for RestfulSleep and BEMAPI</p>
|
||||
</CardBox>
|
||||
|
||||
<SectionTitleLine
|
||||
:icon="mdiSecurity"
|
||||
title="Network Administration"
|
||||
color="text-red-600"
|
||||
:icon="mdiCloudKeyOutline"
|
||||
title="All Active Clients"
|
||||
color="text-blue-400"
|
||||
main
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 mb-6">
|
||||
<CardBoxWidget :number="1" label="User Account(s)" />
|
||||
<CardBoxWidget :number="2" label="Registered Arcades" />
|
||||
<CardBoxWidget :number="3" label="Published Scores" />
|
||||
</div>
|
||||
<CardBox has-table class="mb-6">
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"
|
||||
>
|
||||
<div class="w-full">
|
||||
<GeneralTable
|
||||
:headers="clientHeaders"
|
||||
:items="clientData"
|
||||
@row-clicked="copyToClipboard"
|
||||
/>
|
||||
<h1 class="mx-4 mt-1 mb-2 text-md lg:text-lg">
|
||||
Click row to copy token
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<SectionTitleLine :icon="mdiFlagCheckered" title="Recent Errors" />
|
||||
<CardBox> </CardBox>
|
||||
<SectionTitleLine
|
||||
:icon="mdiKeyPlus"
|
||||
title="Create New Client"
|
||||
color="text-emerald-600"
|
||||
main
|
||||
/>
|
||||
<CardBox is-form class="row-span-1" @submit.prevent="createClient()">
|
||||
<FormField
|
||||
label="Name"
|
||||
help="Name of the client you'd like to onboard."
|
||||
>
|
||||
<FormControl v-model="newClient.name" required />
|
||||
</FormField>
|
||||
|
||||
<div>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
color="success"
|
||||
label="Create"
|
||||
:small="false"
|
||||
/>
|
||||
</div>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
|
||||
@@ -25,7 +25,7 @@ onMounted(async () => {
|
||||
const eventHeaders = [
|
||||
{
|
||||
text: "Timestamp",
|
||||
value: "timestamp",
|
||||
value: "date",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
@@ -58,7 +58,7 @@ const eventHeaders = [
|
||||
const transactionHeaders = [
|
||||
{
|
||||
text: "Timestamp",
|
||||
value: "timestamp",
|
||||
value: "date",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
@@ -89,7 +89,7 @@ function formatTransactions(events) {
|
||||
if (item.type == "paseli_transaction") {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.timestamp = date.toLocaleString();
|
||||
item.date = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
@@ -105,7 +105,7 @@ function formatEvents(events) {
|
||||
if (item.type == "pcbevent") {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.timestamp = date.toLocaleString();
|
||||
item.date = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
|
||||
@@ -1,30 +1,166 @@
|
||||
<script setup>
|
||||
import { mdiFlagCheckered, mdiSecurity } from "@mdi/js";
|
||||
import { ref, onMounted } from "vue";
|
||||
import { mdiFlagCheckered, mdiCashEdit } from "@mdi/js";
|
||||
import SectionMain from "@/components/SectionMain.vue";
|
||||
import CardBox from "@/components/CardBox.vue";
|
||||
import CardBoxWidget from "@/components/CardBoxWidget.vue";
|
||||
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
|
||||
import SectionTitleLine from "@/components/SectionTitleLine.vue";
|
||||
import GeneralTable from "@/components/GeneralTable.vue";
|
||||
|
||||
import { APIAdminDashboard } from "@/stores/api/admin";
|
||||
const dashboardData = ref({});
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await APIAdminDashboard();
|
||||
dashboardData.value = data;
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.log("Failed to fetch admin data:", error);
|
||||
}
|
||||
});
|
||||
|
||||
const eventHeaders = [
|
||||
{
|
||||
text: "Timestamp",
|
||||
value: "date",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "IP Address",
|
||||
value: "data.ip",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Game",
|
||||
value: "data.model",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "PCBID",
|
||||
value: "data.pcbid",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Name",
|
||||
value: "data.name",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Value",
|
||||
value: "data.value",
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
const transactionHeaders = [
|
||||
{
|
||||
text: "Timestamp",
|
||||
value: "date",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "PCBID",
|
||||
value: "data.pcbid",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Delta",
|
||||
value: "data.delta",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Remaining Balance",
|
||||
value: "data.balance",
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
text: "Reason",
|
||||
value: "data.reason",
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
function formatTransactions(events) {
|
||||
var formattedItems = [];
|
||||
for (var item of events) {
|
||||
if (item.type == "paseli_transaction") {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.date = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return formattedItems;
|
||||
}
|
||||
|
||||
function formatEvents(events) {
|
||||
var formattedItems = [];
|
||||
for (var item of events) {
|
||||
if (item.type == "pcbevent") {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.date = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return formattedItems;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutAuthenticated>
|
||||
<SectionMain>
|
||||
<SectionTitleLine
|
||||
:icon="mdiSecurity"
|
||||
title="Network Administration"
|
||||
color="text-red-600"
|
||||
main
|
||||
/>
|
||||
<template v-if="!loading">
|
||||
<CardBox class="mb-6 p-1">
|
||||
<h1 class="text-3xl">Service Logs</h1>
|
||||
</CardBox>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 mb-6">
|
||||
<CardBoxWidget :number="1" label="User Account(s)" />
|
||||
<CardBoxWidget :number="2" label="Registered Arcades" />
|
||||
<CardBoxWidget :number="3" label="Published Scores" />
|
||||
</div>
|
||||
<SectionTitleLine
|
||||
:icon="mdiFlagCheckered"
|
||||
title="Recent PCB Events"
|
||||
color="text-amber-400"
|
||||
main
|
||||
/>
|
||||
<CardBox has-table class="mb-4">
|
||||
<div
|
||||
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"
|
||||
>
|
||||
<div class="w-full">
|
||||
<GeneralTable
|
||||
:headers="eventHeaders"
|
||||
:items="formatEvents(dashboardData?.audit)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<SectionTitleLine :icon="mdiFlagCheckered" title="Recent Errors" />
|
||||
<CardBox> </CardBox>
|
||||
<SectionTitleLine
|
||||
:icon="mdiCashEdit"
|
||||
title="Recent PASELI Transactions"
|
||||
color="text-emerald-400"
|
||||
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="transactionHeaders"
|
||||
:items="formatTransactions(dashboardData?.audit)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardBox>
|
||||
</template>
|
||||
</SectionMain>
|
||||
</LayoutAuthenticated>
|
||||
</template>
|
||||
|
||||
@@ -49,27 +49,48 @@ onMounted(async () => {
|
||||
async function loadMaintenance() {
|
||||
try {
|
||||
const data = await APIAdminMaintenancePeriods();
|
||||
maintData.value = data;
|
||||
var formattedItems = [];
|
||||
for (var item of data) {
|
||||
if (item.timestamp) {
|
||||
const date = new Date(item.timestamp * 1000);
|
||||
item.timestamp = date.toLocaleString();
|
||||
}
|
||||
|
||||
if (item.data.endTimestamp) {
|
||||
const date = new Date(item.data.endTimestamp * 1000);
|
||||
item.data.endTimestamp = date.toLocaleString();
|
||||
}
|
||||
|
||||
formattedItems.push(item);
|
||||
}
|
||||
|
||||
maintData.value = formattedItems;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch maint data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function enterMaintenance() {
|
||||
try {
|
||||
const data = await APIAdminCreateMaintenancePeriod(newMaint);
|
||||
maintData.value = data;
|
||||
} catch (error) {
|
||||
console.error("Failed to post maint:", error);
|
||||
const confirmed = window.confirm("Are you really?");
|
||||
if (confirmed) {
|
||||
try {
|
||||
const data = await APIAdminCreateMaintenancePeriod(newMaint);
|
||||
maintData.value = data;
|
||||
loadMaintenance();
|
||||
} catch (error) {
|
||||
console.error("Failed to post maint:", error);
|
||||
}
|
||||
}
|
||||
|
||||
loadMaintenance();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutAuthenticated>
|
||||
<SectionMain>
|
||||
<CardBox class="mb-6 p-1">
|
||||
<h1 class="text-3xl">Network Maintenance</h1>
|
||||
</CardBox>
|
||||
|
||||
<SectionTitleLine
|
||||
:icon="mdiWrenchCogOutline"
|
||||
title="Recent Maintenance Periods"
|
||||
@@ -93,7 +114,7 @@ async function enterMaintenance() {
|
||||
color="text-yellow-600"
|
||||
main
|
||||
/>
|
||||
<CardBox is-form class="row-span-1" @submit="enterMaintenance()">
|
||||
<CardBox is-form class="row-span-1" @submit.prevent="enterMaintenance()">
|
||||
<FormField label="Reason" help="Why are we going under maintenance?">
|
||||
<FormControl v-model="newMaint.reason" required />
|
||||
</FormField>
|
||||
|
||||
@@ -16,7 +16,7 @@ import GeneralTable from "@/components/GeneralTable.vue";
|
||||
|
||||
import { APIGetAllProfiles } from "@/stores/api/profile";
|
||||
import { getGameInfo } from "@/constants";
|
||||
import { getVideoSource, getCardStyle } from "@/constants/sources";
|
||||
import { getCardStyle } from "@/constants/sources";
|
||||
import { dashCode } from "@/constants/userData";
|
||||
import { getIIDXDan } from "@/constants/danClass";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user