Move from MDI to PHI, fix eslint config, add icon animations

This commit is contained in:
Trenton Zimmer 2025-08-04 16:53:02 -04:00
parent f5b9d57e8c
commit 9f1b7c04a6
94 changed files with 563 additions and 2395 deletions

View File

@ -1,4 +1,4 @@
VITE_APP_VERSION="3.0.24"
VITE_APP_VERSION="3.0.25"
VITE_API_URL="http://10.5.7.5:8000/"
VITE_API_KEY="your_api_key_should_be_here"
VITE_ASSET_PATH="/assets"

View File

@ -1,4 +1,4 @@
VITE_APP_VERSION="3.0.24"
VITE_APP_VERSION="3.0.25"
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"

View File

@ -1,494 +0,0 @@
# Free Laravel Vue 3.x Tailwind 3.x Dashboard
[![Vue 3.x Tailwind 3.x admin dashboard demo](https://static.justboil.me/templates/one/repo-styles.png)](https://justboil.github.io/admin-one-vue-tailwind/)
This guide will help you integrate your Laravel application with [Admin One - free Vue 3 Tailwind 3 Admin Dashboard with dark mode](https://github.com/justboil/admin-one-vue-tailwind).
**Admin One** is simple, fast and free Vue.js 3.x Tailwind CSS 3.x admin dashboard with Laravel 9.x integration.
- Built with **Vue.js 3**, **Tailwind CSS 3** framework & **Composition API**
- **Laravel** build tools
- **Laravel Breeze** with **Inertia + Vue** stack
- **SFC** `<script setup>` [Info](https://v3.vuejs.org/api/sfc-script-setup.html)
- **Pinia** state library (official Vuex 5)
- **Dark mode**
- **Styled** scrollbars
- **Production CSS** is only **&thickapprox;38kb**
- Reusable components
- Free under MIT License
## Table of contents
- [Install](#install)
- [Copy styles, components and scripts](#copy-styles-components-and-scripts)
- [Add pages](#add-pages)
- [Fix router links](#fix-router-links)
- [Add Inertia-related stuff](#add-inertia-related-stuff)
- [Upgrading to Premium version](#upgrading-to-premium-version)
- [Optional steps](#optional-steps)
- [Laravel & Inertia docs](#laravel--inertia-docs)
## Install
### Install Laravel
First, [install Laravel](https://laravel.com/docs/installation) application
### Install Breeze
Then `cd` to project dir and install Breeze with Vue option
```bash
composer require laravel/breeze --dev
php artisan breeze:install vue
php artisan migrate
npm install
```
### Install dependencies
```bash
npm i pinia @tailwindcss/line-clamp @mdi/js chart.js numeral -D
```
## Copy styles, components and scripts
**Before you start,** we recommend to rename Laravel Breeze's original folders (so you'll keep them for future reference) — `resources/js/Components` `resources/js/Layouts` `resources/js/Pages` to something like ComponentsBreeze, LayoutsBreeze, etc.
Now clone [justboil/admin-one-vue-tailwind](https://github.com/justboil/admin-one-vue-tailwind) project somewhere locally (into any separate folder)
Next, copy these files **from justboil/admin-one-vue-tailwind project** directory **to laravel project** directory:
- Copy `tailwind.config.js` to `/`
- Copy `src/components` `src/layouts` `src/stores` `src/colors.js` `src/config.js` `src/menuAside.js` `src/menuNavBar.js` `src/styles.js` to `resources/js/`
- Copy `.laravel-guide/resources/js/` to `resources/js/`
- Delete `resources/css/app.css`
- Copy `src/css` to `resources/css`
### [optional] lowecase vs Capitalized folder names
Fresh Laravel install with Breeze provides **Capitalized** folder names such as `Components`, `Layouts`, etc. For the sake of simplicity we just follow Vue conventions with lowercase folder names. However, you may opt-in to capitalize folder names:
- Make sure you've removed original Laravel Breeze's `resources/js/Layouts` and `resources/js/Components` folders
- Rename the folders you've copied in the previous section: `resources/js/layouts` to `Layouts`; `components` to `Components`; `stores` to `Stores`
- Replace everywhere in imports: `@/layouts/` with `@/Layouts/`; `@/components/` with `@/Components/`; `@/stores/` with `@/Stores/`
### In tailwind.config.js
Please make sure, you've copied template's `tailwind.config.js`. Then replace `content`, to reflect Laravel's structure:
```js
module.exports = {
content: [
"./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php",
"./storage/framework/views/*.php",
"./resources/views/**/*.blade.php",
"./resources/js/**/*.vue",
"./resources/js/**/*.js",
],
// ...
};
```
### In resources/views/app.blade.php
- Remove `<link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap">`
## Add Pages
Let's just add first page. You can repeat these steps for other pages, if you wish to.
First, copy `src/views/HomeView.vue` (from justboil/admin-one-vue-tailwind project) to `resources/js/Pages/` (in your Laravel project).
Then, open `resources/js/Pages/HomeView.vue` and add `<Head>`:
```vue
<script setup>
import { Head } from "@inertiajs/vue3";
// ...
</script>
<template>
<LayoutAuthenticated>
<Head title="Dashboard" />
<!-- ... -->
</LayoutAuthenticated>
</template>
```
Add route in `routes/web.php`. There's a `/dashboard` route already defined by default, so just replace `Inertia::render('Dashboard')` with `Inertia::render('HomeView')`:
```php
Route::get('/dashboard', function () {
return Inertia::render('HomeView');
})->middleware(['auth', 'verified'])->name('dashboard');
```
## Fix router links
Here we replace RouterLink with Inertia Link.
### resources/js/menuAside.js and resources/js/menuNavBar.js
Optionally, you can pass menu via Inertia shared props, so you'll be able to control it with PHP. Here we'd just use JS.
`to` should be replaced with `route` which specifies route name defined in `routes/web.php`. For external links `href` should be used instead. Here's an example for `menuAside.js`:
```javascript
export default [
"General",
[
{
route: "dashboard",
icon: mdiMonitor,
label: "Dashboard",
},
// {
// route: "another-route-name",
// icon: mdiMonitor,
// label: "Dashboard 2",
// },
{
href: "https://example.com/",
icon: mdiMonitor,
label: "Example.com",
},
],
];
```
Route names reflect ones defined in `routes/web.php`:
```php
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
return Inertia::render('Home');
})->name('dashboard');
// Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard-2', function () {
// return Inertia::render('Home2');
// })->name('another-route-name');
```
Now, let's update vue files, to make them work with route names and Inertia links.
### resources/js/components/AsideMenuItem.vue
Replace `RouterLink` imported from `vue-router` with `Link` import in `<script setup>` and add consts:
```vue
<script setup>
import { Link } from "@inertiajs/vue3";
// import { RouterLink } from "vue-router";
// ...
// Add itemHref
const itemHref = computed(() =>
props.item.route ? route(props.item.route) : props.item.href
);
// Add activeInactiveStyle
const activeInactiveStyle = computed(() =>
props.item.route && route().current(props.item.route)
? styleStore.asideMenuItemActiveStyle
: ""
);
// ...
</script>
```
In `<template>` section:
- In `<component>` remove `v-slot` and `:to` attributes; replace `:is` with `:is="item.route ? Link : 'a'"` and `:href` with `:href="itemHref"`
- Inside `<component>` replace `:class` attribute for both `<BaseIcon>` components, `<span>` and another `<BaseIcon>` with `:class="activeInactiveStyle"`
- ...and for `<span>` (also inside `<component>`) replace `:class` attribute with `:class="[{ 'pr-12': !hasDropdown }, activeInactiveStyle]"`
### resources/js/components/BaseButton.vue
Replace `RouterLink` imported from `vue-router` with `Link` import in `<script setup>`:
```vue
<script setup>
import { Link } from "@inertiajs/vue3";
// import { RouterLink } from "vue-router";
// ...
</script>
```
Replace `to` prop declaration with `routeName`:
```javascript
const props = defineProps({
// ...
routeName: {
type: String,
default: null,
},
// ...
});
```
Fix `const is` declaration, so it returns the `Link` component when `props.routeName` is set:
```javascript
const is = computed(() => {
if (props.as) {
return props.as;
}
if (props.routeName) {
return Link;
}
if (props.href) {
return "a";
}
return "button";
});
```
Remove `:to` and replace `:href` in `<component>` with `:href="routeName ? route(routeName) : href"`:
```vue
<template>
<component
:is="is"
:class="componentClass"
:href="routeName ? route(routeName) : href"
:type="computedType"
:target="target"
:disabled="disabled"
>
<!-- ... -->
</component>
</template>
```
### resources/js/components/NavBarItem.vue
Replace `RouterLink` imported from `vue-router` with `Link` import in `<script setup>`:
```vue
<script setup>
import { Link } from "@inertiajs/vue3";
// import { RouterLink } from "vue-router";
// ...
// Add itemHref
const itemHref = computed(() =>
props.item.route ? route(props.item.route) : props.item.href
);
// Update `const is` to return `Link` when `props.routeName` is set:
const is = computed(() => {
if (props.item.href) {
return "a";
}
if (props.item.route) {
return Link;
}
return "div";
});
</script>
```
Then, remove `:to` attribute and set `:href` attribute to `:href="itemHref"` in `<component>`.
## Add Inertia-related stuff
### resources/js/layouts/LayoutAuthenticated.vue
```vue
<script setup>
// Remove vue-router stuff:
// import { useRouter } from 'vue-router'
// const router = useRouter()
// router.beforeEach(() => {
// isAsideMobileExpanded.value = false
// isAsideLgActive.value = false
// })
// Add:
import { router } from "@inertiajs/vue3";
router.on("navigate", () => {
isAsideMobileExpanded.value = false;
isAsideLgActive.value = false;
});
// Replace `isLogout` logic:
const menuClick = (event, item) => {
// ...
if (item.isLogout) {
// Add:
router.post(route("logout"));
}
};
// ...
</script>
```
### resources/js/components/UserAvatarCurrentUser.vue
Let's fetch user avatar initials based on username stored in database.
```vue
<script setup>
import { computed } from "vue";
import { usePage } from "@inertiajs/vue3";
import UserAvatar from "@/components/UserAvatar.vue";
const userName = computed(() => usePage().props.auth.user.name);
</script>
<template>
<UserAvatar :username="userName" api="initials">
<slot />
</UserAvatar>
</template>
```
### resources/js/components/NavBarItem.vue
```vue
<script setup>
// Add usePage:
import { usePage } from "@inertiajs/vue3";
// Remove unused useMainStore:
// import { useMainStore } from '@/stores/main.js'
// ...
// Update itemLabel:
const itemLabel = computed(() =>
props.item.isCurrentUser ? usePage().props.auth.user.name : props.item.label
);
// ...
</script>
```
## Upgrading to Premium version
Please make sure you have completed all previous steps in this guide, so you have everything up and running. Then download and uzip the Premium version files. Next, follow steps described below.
### Add deps
```bash
npm i @headlessui/vue -D
```
### Copy files
Copy files to your Laravel project (overwrite free version ones or merge if you have changed something):
- Copy `src/components/Premium` to `resources/js/components/Premium`
- Copy `src/stores` to `resources/js/stores`
- Copy `src/config.js` to `resources/js/config.js`
- Copy `src/styles.js` to `resources/js/styles.js`
- Copy `src/sampleButtonMenuOptions.js` to `resources/js/sampleButtonMenuOptions.js`
- Copy `src/colorsPremium.js` to `resources/js/colorsPremium.js`
### Update tailwind.config.js
Replace `tailwind.config.js` in your Laravel project with the Premium one. Make sure to preserve `module.exports.content`:
```js
module.exports = {
content: [
"./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php",
"./storage/framework/views/*.php",
"./resources/views/**/*.blade.php",
"./resources/js/**/*.vue",
"./resources/js/**/*.js",
],
// ...
};
```
### Update resources/js/app.js
Add layout store to `resources/js/app.js`:
```js
// Add layout store
import { useLayoutStore } from "@/stores/layout.js";
const layoutStore = useLayoutStore(pinia);
layoutStore.responsiveLayoutControl();
window.onresize = () => layoutStore.responsiveLayoutControl();
```
### Update resources/js/layouts/LayoutAuthenticated.vue
Replace contents of `resources/js/layouts/LayoutAuthenticated.vue` with contents of `src/js/layouts/LayoutAuthenticated.vue` (from the Premium version)
```vue
<script setup>
// Replace router use:
// import { useRouter } from "vue-router";
import { router } from "@inertiajs/vue3";
// const router = useRouter();
// router.beforeEach(() => {
// layoutStore.isAsideMobileExpanded = false;
// });
router.on("navigate", () => {
layoutStore.isAsideMobileExpanded = false;
});
// Add logout:
const menuClick = (event, item) => {
// ...
if (item.isLogout) {
router.post(route("logout"));
}
};
</script>
```
### Use premium login and signup layouts
Optionally, you may update layouts of login and signup, located at `resources/js/Pages/Auth` with Premium version layouts from `src/views/Premium/LoginView.vue` and `src/views/Premium/SignupView.vue`
## Optional steps
### Default style
It's likely, you'll use only one app style, either `basic` or one of listed in `src/styles.js`. Follow [this guide](https://justboil.github.io/docs/customization/#default-style) to set one of choice.
### Fix .editorconfig
Add to .editorconfig:
```editorconfig
[*.{js,jsx,ts,tsx,vue,html,css}]
indent_size = 2
```
### resources/js/bootstrap.js
Global `lodash` and `axios` aren't needed, as we import them directly when needed. Most likely, you'd not need `axios` at all, as Laravel pushes all data via Inertia.
## Laravel & Inertia docs
- [Laravel Docs](https://laravel.com/docs)
- [Inertia](https://inertiajs.com/)

View File

@ -1,72 +0,0 @@
<script setup>
import { useForm, Head } from "@inertiajs/vue3";
import { ref } from "vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormControl from "@/components/FormControl.vue";
import FormField from "@/components/FormField.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
const form = useForm({
password: "",
});
const passwordInput = ref(null);
const submit = () => {
form.post(route("password.confirm"), {
onFinish: () => {
form.reset();
passwordInput.value?.focus();
},
});
};
</script>
<template>
<LayoutGuest>
<Head title="Secure Area" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<FormField>
<div class="mb-4 text-sm text-gray-600">
This is a secure area of the application. Please confirm your
password before continuing.
</div>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter your password to confirm"
>
<FormControl
id="password"
v-model="form.password"
type="password"
required
autocomplete="current-password"
@set-ref="passwordInput = $event"
/>
</FormField>
<BaseDivider />
<BaseButton
type="submit"
color="info"
label="Confirm"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,76 +0,0 @@
<script setup>
import { useForm, Head, Link } from "@inertiajs/vue3";
import { mdiEmail } from "@mdi/js";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
import BaseLevel from "@/components/BaseLevel.vue";
defineProps({
status: {
type: String,
default: null,
},
});
const form = useForm({
email: "",
});
const submit = () => {
form.post(route("password.email"));
};
</script>
<template>
<LayoutGuest>
<Head title="Forgot Password" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<NotificationBarInCard v-if="status" color="info">
{{ status }}
</NotificationBarInCard>
<FormField>
<div class="mb-4 text-sm text-gray-600">
Forgot your password? No problem. Just let us know your email
address and we will email you a password reset link that will allow
you to choose a new one.
</div>
</FormField>
<FormField label="Email" help="Please enter your email">
<FormControl
v-model="form.email"
:icon="mdiEmail"
autocomplete="email"
type="email"
required
/>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Email link"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<Link :href="route('login')"> Back to login </Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,103 +0,0 @@
<script setup>
import { useForm, Head, Link } from "@inertiajs/vue3";
import { mdiEmail, mdiFormTextboxPassword } from "@mdi/js";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
const props = defineProps({
email: {
type: String,
default: null,
},
token: {
type: String,
default: null,
},
});
const form = useForm({
token: props.token,
email: props.email,
password: "",
password_confirmation: "",
});
const submit = () => {
form.post(route("password.update"), {
onFinish: () => form.reset("password", "password_confirmation"),
});
};
</script>
<template>
<LayoutGuest>
<Head title="Reset Password" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
id="email"
v-model="form.email"
:icon="mdiEmail"
autocomplete="email"
type="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter new password"
>
<FormControl
id="password"
v-model="form.password"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<FormField
label="Confirm Password"
label-for="password_confirmation"
help="Please confirm new password"
>
<FormControl
id="password_confirmation"
v-model="form.password_confirmation"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<BaseDivider />
<BaseButton
type="submit"
color="info"
label="Reset password"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,117 +0,0 @@
<script setup>
import { useForm, Head } from "@inertiajs/vue3";
import { nextTick, ref } from "vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormControl from "@/components/FormControl.vue";
import FormField from "@/components/FormField.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
import BaseLevel from "@/components/BaseLevel.vue";
const recovery = ref(false);
const form = useForm({
code: "",
recovery_code: "",
});
const recoveryCodeInput = ref(null);
const codeInput = ref(null);
const toggleRecovery = async () => {
recovery.value ^= true;
await nextTick();
if (recovery.value) {
recoveryCodeInput.value?.focus();
form.code = "";
} else {
codeInput.value?.focus();
form.recovery_code = "";
}
};
const submit = () => {
form.post(route("two-factor.login"));
};
</script>
<template>
<LayoutGuest>
<Head title="Two-factor Confirmation" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<FormField>
<div class="mb-4 text-sm text-gray-600">
<template v-if="!recovery">
Please confirm access to your account by entering the
authentication code provided by your authenticator application.
</template>
<template v-else>
Please confirm access to your account by entering one of your
emergency recovery codes.
</template>
</div>
</FormField>
<FormField
v-if="!recovery"
label="Code"
label-for="code"
help="Please enter one-time code"
>
<FormControl
id="code"
v-model="form.code"
type="text"
inputmode="numeric"
autofocus
autocomplete="one-time-code"
@set-ref="codeInput = $event"
/>
</FormField>
<FormField
v-else
label="Recovery Code"
label-for="recovery_code"
help="Please enter recovery code"
>
<FormControl
id="recovery_code"
v-model="form.recovery_code"
type="text"
class="mt-1 block w-full"
autocomplete="one-time-code"
@set-ref="recoveryCodeInput = $event"
/>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Log in"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<button @click.prevent="toggleRecovery">
<template v-if="!recovery"> Use a recovery code </template>
<template v-else> Use an authentication code </template>
</button>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,115 +0,0 @@
<script setup>
import { useForm, Head, Link } from "@inertiajs/vue3";
import { mdiAccount, mdiAsterisk } from "@mdi/js";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormCheckRadioGroup from "@/components/FormCheckRadioGroup.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
import BaseLevel from "@/components/BaseLevel.vue";
const props = defineProps({
canResetPassword: Boolean,
status: {
type: String,
default: null,
},
});
const form = useForm({
email: "",
password: "",
remember: [],
});
const submit = () => {
form
.transform((data) => ({
...data,
remember: form.remember && form.remember.length ? "on" : "",
}))
.post(route("login"), {
onFinish: () => form.reset("password"),
});
};
</script>
<template>
<LayoutGuest>
<Head title="Login" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<NotificationBarInCard v-if="status" color="info">
{{ status }}
</NotificationBarInCard>
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
id="email"
v-model="form.email"
:icon="mdiAccount"
autocomplete="email"
type="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter your password"
>
<FormControl
id="password"
v-model="form.password"
:icon="mdiAsterisk"
type="password"
autocomplete="current-password"
required
/>
</FormField>
<FormCheckRadioGroup
v-model="form.remember"
name="remember"
:options="{ remember: 'Remember' }"
/>
<BaseDivider />
<BaseLevel>
<BaseButtons>
<BaseButton
type="submit"
color="info"
label="Login"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<BaseButton
v-if="canResetPassword"
route-name="password.request"
color="info"
outline
label="Remind"
/>
</BaseButtons>
<Link :href="route('register')"> Register </Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,131 +0,0 @@
<script setup>
import { useForm, usePage, Head } from "@inertiajs/vue3";
import { computed } from "vue";
import { mdiAccount, mdiEmail, mdiFormTextboxPassword } from "@mdi/js";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormCheckRadioGroup from "@/components/FormCheckRadioGroup.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
const form = useForm({
name: "",
email: "",
password: "",
password_confirmation: "",
terms: [],
});
const hasTermsAndPrivacyPolicyFeature = computed(
() => usePage().props.jetstream?.hasTermsAndPrivacyPolicyFeature,
);
const submit = () => {
form
.transform((data) => ({
...data,
terms: form.terms && form.terms.length,
}))
.post(route("register"), {
onFinish: () => form.reset("password", "password_confirmation"),
});
};
</script>
<template>
<LayoutGuest>
<Head title="Register" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox
:class="cardClass"
class="my-24"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<FormField label="Name" label-for="name" help="Please enter your name">
<FormControl
id="name"
v-model="form.name"
:icon="mdiAccount"
autocomplete="name"
type="text"
required
/>
</FormField>
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
id="email"
v-model="form.email"
:icon="mdiEmail"
autocomplete="email"
type="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter new password"
>
<FormControl
id="password"
v-model="form.password"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<FormField
label="Confirm Password"
label-for="password_confirmation"
help="Please confirm your password"
>
<FormControl
id="password_confirmation"
v-model="form.password_confirmation"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<FormCheckRadioGroup
v-if="hasTermsAndPrivacyPolicyFeature"
v-model="form.terms"
name="remember"
:options="{ agree: 'I agree to the Terms' }"
/>
<BaseDivider />
<BaseButtons>
<BaseButton
type="submit"
color="info"
label="Register"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<BaseButton route-name="login" color="info" outline label="Login" />
</BaseButtons>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,70 +0,0 @@
<script setup>
import { useForm, Head, Link } from "@inertiajs/vue3";
import { computed } from "vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
import BaseLevel from "@/components/BaseLevel.vue";
const props = defineProps({
status: {
type: String,
default: null,
},
});
const form = useForm();
const verificationLinkSent = computed(
() => props.status === "verification-link-sent",
);
const submit = () => {
form.post(route("verification.send"));
};
</script>
<template>
<LayoutGuest>
<Head title="Email Verification" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<NotificationBarInCard v-if="verificationLinkSent" color="info">
A new verification link has been sent to the email address you
provided during registration.
</NotificationBarInCard>
<FormField>
<div class="mb-4 text-sm text-gray-600">
Thanks for signing up! Before getting started, could you verify your
email address by clicking on the link we just emailed to you? If you
didn't receive the email, we will gladly send you another.
</div>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Resend Verification Email"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<Link :href="route('logout')" method="post" as="button">
Logout
</Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@ -1,47 +0,0 @@
import "../css/_base.css";
import { createPinia } from "pinia";
import { useStyleStore } from "@/stores/style.js";
import { darkModeKey, styleKey } from "@/config.js";
import { createApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/vue3";
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m";
const appName =
window.document.getElementsByTagName("title")[0]?.innerText || "Laravel";
const pinia = createPinia();
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob("./Pages/**/*.vue"),
),
setup({ el, App, props, plugin }) {
return createApp({ render: () => h(App, props) })
.use(plugin)
.use(pinia)
.use(ZiggyVue, Ziggy)
.mount(el);
},
progress: {
color: "#4B5563",
},
});
const styleStore = useStyleStore(pinia);
/* App style */
styleStore.setStyle(localStorage[styleKey] ?? "basic");
/* Dark mode */
if (
(!localStorage[darkModeKey] &&
window.matchMedia("(prefers-color-scheme: dark)").matches) ||
localStorage[darkModeKey] === "1"
) {
styleStore.setDarkMode(true);
}

View File

@ -1,16 +0,0 @@
<script setup>
import { computed } from "vue";
import { usePage } from "@inertiajs/vue3";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
const errors = computed(() => usePage().props.errors);
const hasErrors = computed(() => Object.keys(errors.value).length > 0);
</script>
<template>
<NotificationBarInCard v-if="hasErrors" color="danger">
<b>Whoops! Something went wrong.</b>
<span v-for="(error, key) in errors" :key="key">{{ error }}</span>
</NotificationBarInCard>
</template>

View File

@ -1,235 +0,0 @@
# Free Nuxt 3.x Vue 3.x Tailwind 3.x Dashboard
This guide will help you integrate your Nuxt.js 3.x application with [Admin One - free Vue 3 Tailwind 3 Admin Dashboard with dark mode](https://github.com/justboil/admin-one-vue-tailwind).
**Please note:** this document is work in progress and Nuxt 3 is in Release Candidate state, so some things can be missing and warnings may occur.
## Table of contents
... TOC is coming soon
## Install Nuxt.js 3.x app with Tailwind CSS
Check [Nuxt installation guide](https://v3.nuxtjs.org/getting-started/quick-start) for more information.
* Run `npx nuxi init sample-app`
* then `cd sample-app`
* and `npm install`
Then, let's install TailwindCSS. Check [Tailwind Nuxt installation guide](https://tailwindcss.com/docs/guides/nuxtjs) for more information.
```sh
# Install tailwind
npm install -D @nuxtjs/tailwindcss @tailwindcss/forms
# Install other required packages
npm i @mdi/js chart.js numeral
# Install Pinia (official Vuex 5). --legacy-peer-deps is required because of the package dependencies issue
npm install --legacy-peer-deps pinia @pinia/nuxt
```
### Copy styles, components and scripts
Now clone [justboil/admin-one-vue-tailwind](https://github.com/justboil/admin-one-vue-tailwind) project somewhere locally (into any separate folder)
Next, copy these files **from justboil/admin-one-vue-tailwind project** directory **to nuxt project** directory:
* Copy `tailwind.config.js` to `/`
* Copy `src/components` to `components/`
* Copy `src/layouts` to `layouts/`
* Copy `src/stores` to `stores/`
* Copy `src/colors.js` `src/config.js` `src/menuAside.js` `src/menuNavBar.js` `src/styles.js` to `configs/`
* Copy `src/css` to `assets/css/`
* Copy `public/favicon.png` to `public/`
### Prepare items
#### In nuxt.config.ts
```javascript
import { defineNuxtConfig } from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
buildModules: [
'@pinia/nuxt',
],
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
css: [
'@/assets/css/main.css',
]
})
```
#### In tailwind.config.js
Replace `content`:
```js
module.exports = {
content: [
'./composables/**/*.{js,vue,ts}',
'./components/**/*.{js,vue,ts}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
'./plugins/**/*.{js,ts}',
'app.vue'
],
// ...
}
```
#### In App.vue
```vue
<script setup>
import { useStyleStore } from '@/stores/style.js'
import { darkModeKey, styleKey } from '@/config.js'
useHead({
titleTemplate: (titleChunk) => {
const titleBase = 'Admin One Vue 3 Tailwind'
return titleChunk ? `${titleChunk} - ${titleBase}` : titleBase
}
})
const styleStore = useStyleStore()
const currentStyle = typeof localStorage !== 'undefined' && localStorage[styleKey]
? localStorage[styleKey]
: 'basic'
styleStore.setStyle(currentStyle)
const currentStoredDarkMode = typeof localStorage !== 'undefined' && localStorage[darkModeKey] === '1'
if ((!currentStoredDarkMode && typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches) || currentStoredDarkMode) {
styleStore.setDarkMode(true)
}
</script>
<template>
<div>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</div>
</template>
```
#### In stores/main.js
Remove `axios`, as you'll likely going to use Nuxt's `useFetch`. Then add some sample data for `clients` and `history`.
```javascript
// import axios from 'axios'
export const useMainStore = defineStore('main', {
state: () => ({
// ...
clients: [
{ id: 19, avatar: "https://avatars.dicebear.com/v2/gridy/Howell-Hand.svg", login: "percy64", name: "Howell Hand", company: "Kiehn-Green", city: "Emelyside", progress: 70, created: "Mar 3, 2021" },
{ id: 11, avatar: "https://avatars.dicebear.com/v2/gridy/Hope-Howe.svg", login: "dare.concepcion", name: "Hope Howe", company: "Nolan Inc", city: "Paristown", progress: 68, created: "Dec 1, 2021" },
{ id: 32, avatar: "https://avatars.dicebear.com/v2/gridy/Nelson-Jerde.svg", login: "geovanni.kessler", name: "Nelson Jerde", company: "Nitzsche LLC", city: "Jailynbury", progress: 49, created: "May 18, 2021"},
{ id: 22, avatar: "https://avatars.dicebear.com/v2/gridy/Kim-Weimann.svg", login: "macejkovic.dashawn", name: "Kim Weimann", company: "Brown-Lueilwitz", city: "New Emie", progress: 38, created: "May 4, 2021" }
],
history: [
{ amount: 375.53, name: "Home Loan Account", date: "3 days ago", type: "deposit", business: "Turcotte" },
{ amount: 470.26, name: "Savings Account", date: "3 days ago", type: "payment", business: "Murazik - Graham" },
{ amount: 971.34, name: "Checking Account", date: "5 days ago", type: "invoice", business: "Fahey - Keebler" },
{ amount: 374.63, name: "Auto Loan Account", date: "7 days ago", type: "withdrawal", business: "Collier - Hintz" }
]
}),
actions: {
// ...
fetch (sampleDataKey) {
// axios
// .get(`data-sources/${sampleDataKey}.json`)
// .then(r => {
// if (r.data && r.data.data) {
// this[sampleDataKey] = r.data.data
// }
// })
// .catch(error => {
// alert(error.message)
// })
}
}
})
```
## Rename layouts
* Rename `layouts/LayoutGuest.vue` to `default.vue`
* Rename `layouts/LayoutAuthenticated.vue` to `authenticated.vue`
## Copy pages
Let's copy `views/LoginView.vue` with a guest layout and `views/HomeView.vue` with an authenticated layout.
These pages will then be available under `/` and `/dashboard` url paths.
#### LoginView.vue
Copy `views/LoginView.vue` to `pages/index.vue`
Then, wrap the entire template with `<div>` and replace `<LayoutGuest>` with `<NuxtLayout>`
**Why we need a div wrapper?** If you use `<NuxtLayout>` within your pages, make sure it is not the root element (or disable layout/page transitions) &mdash; [Info](https://v3.nuxtjs.org/guide/directory-structure/layouts#overriding-a-layout-on-a-per-page-basis)
```vue
<script setup>
// import LayoutGuest from '@/layouts/LayoutGuest.vue'
// ...
</script>
<template>
<div>
<NuxtLayout>
<!-- ... -->
</NuxtLayout>
</div>
</template>
```
#### HomeView.vue
Copy `views/HomeView.vue` to `pages/dashboard.vue`
Then, wrap the entire template with `<div>` and replace `<LayoutGuest>` with `<NuxtLayout>` with a `name` prop.
```vue
<script setup>
// import LayoutAuthenticated from '@/layouts/LayoutAuthenticated.vue'
// ...
</script>
<template>
<div>
<NuxtLayout name="authenticated">
<!-- ... -->
</NuxtLayout>
</div>
</template>
```
## Replace `<RouterLink>` with `<NuxtLink>`
Details are coming soon...
## Remove/update imports
Nuxt automatically imports any components in your `components/` directory. So, you may safely remove that imports from `<script setup>`. The only exception is for `components/Charts`, so charts import should be left as is.
## Contributions open
WIP - work in progress. Some things are missing. Contributions open.

View File

@ -2,20 +2,27 @@ import { defineConfig } from "eslint/config";
import js from "@eslint/js";
import vue from "eslint-plugin-vue";
import prettierConfig from "@vue/eslint-config-prettier";
import globalsPkg from "globals";
const { browser: globalsBrowser, node: globalsNode } = globalsPkg;
export default defineConfig([
js.configs.recommended,
{
files: ["**/*.vue"],
plugins: {
vue,
},
files: ["**/*.vue"],
languageOptions: {
parser: await import("vue-eslint-parser"),
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
globals: {
...globalsBrowser,
},
},
rules: {
...vue.configs.recommended.rules,
@ -27,7 +34,12 @@ export default defineConfig([
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globalsNode,
...globalsBrowser,
},
},
},
prettierConfig,
]);

28
package-lock.json generated
View File

@ -8,7 +8,7 @@
"name": "phaseweb3",
"version": "3.0.2",
"dependencies": {
"@mdi/js": "^7.0.96",
"@phosphor-icons/vue": "^2.2.1",
"@tailwindcss/vite": "^4.1.11",
"@vuepic/vue-datepicker": "^11.0.1",
"axios": "^1.11.0",
@ -16,6 +16,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^17.2.1",
"downloadjs": "^1.4.7",
"globals": "^16.3.0",
"js-cookie": "^3.0.5",
"numeral": "^2.0.6",
"particles.js": "^2.0.0",
@ -745,10 +746,16 @@
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
},
"node_modules/@mdi/js": {
"version": "7.4.47",
"resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
"integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ=="
"node_modules/@phosphor-icons/vue": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@phosphor-icons/vue/-/vue-2.2.1.tgz",
"integrity": "sha512-3RNg1utc2Z5RwPKWFkW3eXI/0BfQAwXgtFxPUPeSzi55jGYUq16b+UqcgbKLazWFlwg5R92OCLKjDiJjeiJcnA==",
"engines": {
"node": ">=14"
},
"peerDependencies": {
"vue": ">=3.2.39"
}
},
"node_modules/@pkgr/core": {
"version": "0.2.9",
@ -2493,6 +2500,17 @@
"node": ">=10.13.0"
}
},
"node_modules/globals": {
"version": "16.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
"integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",

View File

@ -8,7 +8,7 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-pattern .gitignore"
},
"dependencies": {
"@mdi/js": "^7.0.96",
"@phosphor-icons/vue": "^2.2.1",
"@tailwindcss/vite": "^4.1.11",
"@vuepic/vue-datepicker": "^11.0.1",
"axios": "^1.11.0",
@ -16,6 +16,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^17.2.1",
"downloadjs": "^1.4.7",
"globals": "^16.3.0",
"js-cookie": "^3.0.5",
"numeral": "^2.0.6",
"particles.js": "^2.0.0",

View File

@ -23,5 +23,6 @@
"3.0.21": ["- (Admin) Finish admin machine/arcade pages", "- (Major) Finish initial public profile page", "- (Minor) Change table right-click behavior", "- (Admin) Finish admin News pages", "- (Admin) Add search for user via card ID"],
"3.0.22": ["- (Major) Finish first implementation of the public profile page", "- (Minor) Clean up dashboard game stat box", "- (Minor) Load profile name in game stat box"],
"3.0.23": ["- (Major) Moved to Tailwind V4 (SoftieTechCat)", "- (Major) Updated all dependencies (SoftieTechCat)", "- (Bugfix) Fix bug if user has no read news", "- (Backend) Insane optimization with user loading, data loading, cache parsing. (SoftieTechCat)", "- (Minor) Add new greetings."],
"3.0.24": ["- (Bugfix) Fix theme forcing for dark mode", "- (Bugfix) Fix animations across entire app", "- (Bugfix) Fix cursor on login screen easter egg", "- (Minor) Add new greeting"]
"3.0.24": ["- (Bugfix) Fix theme forcing for dark mode", "- (Bugfix) Fix animations across entire app", "- (Bugfix) Fix cursor on login screen easter egg", "- (Minor) Add new greeting"],
"3.0.25": ["- (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", "- (Minor) Fix ESlint config"]
}

View File

@ -1,12 +1,12 @@
<script setup>
import {
mdiTestTube,
mdiHomeOutline,
mdiNewspaperVariantMultiple,
mdiGamepad,
mdiCashRegister,
mdiHome,
} from "@mdi/js";
PhTestTube,
PhHouseLine,
PhNewspaper,
PhJoystick,
PhCashRegister,
PhHouse,
} from "@phosphor-icons/vue";
import { ref } from "vue";
// import { getArea } from "@/constants/area";
import BaseLevel from "@/components/BaseLevel.vue";
@ -64,9 +64,9 @@ function getCardStyle(path) {
v-if="arcadeData.is_beta"
label="Beta Enabled"
color="warning"
:icon="mdiTestTube"
:icon="PhTestTube"
/>
<PillTag label="Private Arcade" color="info" :icon="mdiHomeOutline" />
<PillTag label="Private Arcade" color="info" :icon="PhHouseLine" />
</div>
<hr class="border-t border w-full" />
@ -96,26 +96,26 @@ function getCardStyle(path) {
<div class="md:w-full grid grid-cols-1 md:grid-cols-4 gap-3">
<BaseButton
:href="`/#/arcade/${arcade.id}`"
:icon="mdiHome"
:icon="PhHouse"
color="info"
label="Arcade Home"
class="w-full md:w-[150px]"
/>
<BaseButton
:href="`/#/arcade/${arcade.id}/events`"
:icon="mdiNewspaperVariantMultiple"
:icon="PhNewspaper"
color="info"
label="Event Settings"
/>
<BaseButton
:href="`/#/arcade/${arcade.id}/machines`"
:icon="mdiGamepad"
:icon="PhJoystick"
color="info"
label="Machine List"
/>
<BaseButton
:href="`/#/arcade/${arcade.id}/paseli`"
:icon="mdiCashRegister"
:icon="PhCashRegister"
color="info"
label="PASELI"
/>

View File

@ -4,6 +4,7 @@ import { RouterLink } from "vue-router";
import { getButtonColor } from "@/colors.js";
import BaseIcon from "@/components/BaseIcon.vue";
import { useMainStore } from "@/stores/main";
import { PhShrimp } from "@phosphor-icons/vue";
const props = defineProps({
label: {
@ -11,7 +12,7 @@ const props = defineProps({
default: null,
},
icon: {
type: String,
type: Object,
default: null,
},
iconSize: {
@ -146,7 +147,13 @@ const mainStore = useMainStore();
>
{{ tooltip }}
</div>
<BaseIcon v-if="icon" :path="icon" :size="iconSize" />
<template v-if="mainStore?.userCustomize?.shrimpLinks">
<BaseIcon :icon="PhShrimp" :size="iconSize" />
</template>
<template v-else>
<BaseIcon v-if="icon" :icon="icon" :size="iconSize" />
</template>
<span v-if="label" :class="labelClass">{{
mainStore.userCustomize.shrimpLinks ? "Shrimp" : label
}}</span>

View File

@ -4,7 +4,13 @@ import { computed } from "vue";
const props = defineProps({
path: {
type: String,
required: true,
required: false,
default: null,
},
icon: {
type: Object,
required: false,
default: null,
},
w: {
type: String,
@ -22,6 +28,10 @@ const props = defineProps({
type: String,
default: "",
},
fill: {
type: String,
default: "duotone",
},
});
const spanClass = computed(
@ -29,19 +39,24 @@ const spanClass = computed(
`inline-flex justify-center items-center ${props.w} ${props.h} ${props.color}`,
);
const iconSize = computed(() => props.size ?? 16);
const iconSize = computed(() => props.size ?? (props.icon ? 20 : 18));
</script>
<template>
<span :class="spanClass">
<svg
viewBox="0 0 24 24"
:width="iconSize"
:height="iconSize"
class="inline-block"
>
<path fill="currentColor" :d="path" />
</svg>
<template v-if="props.path">
<svg
viewBox="0 0 24 24"
:width="iconSize"
:height="iconSize"
class="inline-block"
>
<path fill="currentColor" :d="path" />
</svg>
</template>
<template v-else-if="props.icon">
<icon :size="iconSize" :weight="props.fill" />
</template>
<slot />
</span>
</template>

View File

@ -1,84 +0,0 @@
<script setup>
import { computed } from "vue";
import { mdiTrendingDown, mdiTrendingUp, mdiTrendingNeutral } from "@mdi/js";
import CardBox from "@/components/CardBox.vue";
import BaseLevel from "@/components/BaseLevel.vue";
import PillTag from "@/components/PillTag.vue";
import UserAvatar from "@/components/UserAvatar.vue";
const props = defineProps({
name: {
type: String,
required: true,
},
login: {
type: String,
required: true,
},
date: {
type: String,
required: true,
},
progress: {
type: Number,
default: 0,
},
text: {
type: String,
default: null,
},
type: {
type: String,
default: null,
},
});
const pillType = computed(() => {
if (props.type) {
return props.type;
}
if (props.progress) {
if (props.progress >= 60) {
return "success";
}
if (props.progress >= 40) {
return "warning";
}
return "danger";
}
return "info";
});
const pillIcon = computed(() => {
return {
success: mdiTrendingUp,
warning: mdiTrendingNeutral,
danger: mdiTrendingDown,
info: null,
}[pillType.value];
});
const pillText = computed(() => props.text ?? `${props.progress}%`);
</script>
<template>
<CardBox class="mb-2" is-hoverable>
<BaseLevel>
<BaseLevel type="justify-start md:space-x-4">
<UserAvatar class="w-12" :username="name" />
<div class="text-center md:text-left overflow-hidden">
<h4 class="text-xl text-ellipsis">
{{ name }}
</h4>
<p class="text-gray-500 dark:text-slate-400">
{{ date }} @ {{ login }}
</p>
</div>
</BaseLevel>
<PillTag :color="pillType" :label="pillText" :icon="pillIcon" />
</BaseLevel>
</CardBox>
</template>

View File

@ -7,11 +7,11 @@ defineProps({
required: true,
},
icon: {
type: String,
type: Object,
default: null,
},
buttonIcon: {
type: String,
type: Object,
default: null,
},
});
@ -31,7 +31,7 @@ const buttonClick = (event) => {
class="flex items-center py-3 grow font-bold"
:class="[icon ? 'px-4' : 'px-6']"
>
<BaseIcon v-if="icon" :path="icon" class="mr-3" />
<BaseIcon v-if="icon" :icon="icon" class="mr-3" />
{{ title }}
</div>
<button
@ -39,7 +39,7 @@ const buttonClick = (event) => {
class="flex items-center p-2 justify-center ring-blue-700 focus:ring"
@click="buttonClick"
>
<BaseIcon :path="buttonIcon" />
<BaseIcon :icon="buttonIcon" />
</button>
</header>
</template>

View File

@ -11,7 +11,7 @@ defineProps({
default: 0,
},
icon: {
type: String,
type: Object,
default: null,
},
prefix: {

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { watch, ref } from "vue";
import { useRouter } from "vue-router";
import { mdiLoading } from "@mdi/js";
import { PhSpinnerBall } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import CardBox from "@/components/CardBox.vue";
@ -126,7 +126,7 @@ function revert() {
/>
<BaseIcon
v-if="loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { watch, ref, reactive } from "vue";
import { useRouter } from "vue-router";
import { mdiLoading } from "@mdi/js";
import { PhSpinnerBall } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import CardBox from "@/components/CardBox.vue";
@ -173,7 +173,7 @@ function revertEmblem() {
/>
<BaseIcon
v-if="loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -2,13 +2,13 @@
import { ref, watch } from "vue";
import { useRoute } from "vue-router";
import {
mdiHome,
mdiCog,
mdiAccountDetails,
mdiPlaylistMusicOutline,
mdiSwordCross,
mdiFormatListText,
} from "@mdi/js";
PhHouse,
PhGear,
PhUserList,
PhPlaylist,
PhSword,
PhListStar,
} from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import GameTitleLine from "@/components/GameTitleLine.vue";
import { getVideoSource, getCardStyle } from "@/constants/sources";
@ -41,7 +41,7 @@ function loadRoutes() {
label: `${
props.game.shortName ? props.game.shortName : props.game.name
} Home`,
icon: mdiHome,
icon: PhHouse,
path: `/#/games/${props.game.id}/`,
route: "game_page",
color: "success",
@ -51,7 +51,7 @@ function loadRoutes() {
if (userProfiles.value.some((profile) => profile.game === props.game.id)) {
navigationData.push({
label: "My Profile",
icon: mdiAccountDetails,
icon: PhUserList,
path: `/#/games/${props.game.id}/profiles/${mainStore.userId}`,
route: "game_profile",
color: "info",
@ -59,7 +59,7 @@ function loadRoutes() {
navigationData.push({
label: "Edit Profile",
icon: mdiCog,
icon: PhGear,
path: `/#/games/${props.game.id}/edit`,
route: "edit_profile",
color: "warning",
@ -68,7 +68,7 @@ function loadRoutes() {
if (!props.game.noRivals) {
navigationData.push({
label: "Rivals",
icon: mdiSwordCross,
icon: PhSword,
path: `/#/games/${props.game.id}/rivals`,
route: "game_rivals",
color: "danger",
@ -78,7 +78,7 @@ function loadRoutes() {
if (!props.game.noScores) {
navigationData.push({
label: "My Scores",
icon: mdiPlaylistMusicOutline,
icon: PhPlaylist,
path: `/#/games/${props.game.id}/scores/${mainStore.userId}`,
color: "info",
});
@ -86,7 +86,7 @@ function loadRoutes() {
navigationData.push({
label: "My Records",
icon: mdiFormatListText,
icon: PhListStar,
path: `/#/games/${props.game.id}/records/${mainStore.userId}`,
route: "personal_records",
color: "success",
@ -97,14 +97,14 @@ function loadRoutes() {
navigationData.push(
{
label: "Network Scores",
icon: mdiPlaylistMusicOutline,
icon: PhPlaylist,
path: `/#/games/${props.game.id}/scores`,
route: "all_scores",
color: "info",
},
{
label: "Network Records",
icon: mdiFormatListText,
icon: PhListStar,
path: `/#/games/${props.game.id}/records`,
route: "all_records",
color: "success",
@ -133,11 +133,11 @@ function loadRoutes() {
<div class="bg-white dark:bg-slate-900/90 rounded-2xl card-content">
<GameTitleLine :path="game.icon" :title="game.name">
<div class="w-full">
<div class="w-full">
<div class="w-full mb-4">
<slot />
</div>
<div
class="w-full grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:flex gap-3"
class="w-full grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-6 2xl:flex gap-3"
>
<template v-for="item of loadRoutes()" :key="item.path">
<BaseButton

View File

@ -1,16 +1,8 @@
<script setup>
import axios from "axios";
import {
mdiCog,
mdiAccountDetails,
mdiPlaylistMusicOutline,
mdiFormatListText,
mdiAxeBattle,
} from "@mdi/js";
import { ref, onMounted } from "vue";
import { dashCode } from "@/constants/userData";
import { GameConstants, getGameInfo, getRivalInfo } from "@/constants";
import BaseButton from "@/components/BaseButton.vue";
import { GameConstants, getGameInfo } from "@/constants";
import UserEmblem from "@/components/UserEmblem.vue";
import UserQpro from "@/components/UserQpro.vue";
import { getGitadoraColor, getJubilityColor } from "@/constants/skillColor.js";
@ -28,10 +20,6 @@ const props = defineProps({
type: Object,
required: true,
},
useSmall: {
type: Boolean,
default: false,
},
});
function colorText() {
@ -112,68 +100,6 @@ onMounted(async () => {
<p class="text-xl font-mono">{{ dashCode(profile.extid) }}</p>
</div>
</div>
<div class="space-y-2 text-center md:text-left lg:mx-12 p-2">
<div
class="pt-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 gap-3"
>
<BaseButton
v-if="!useSmall && game"
:icon="mdiAccountDetails"
:href="`/#/games/${game}/profiles/${profile.userId}`"
:outline="false"
color="info"
label="View Profile"
/>
<BaseButton
v-if="!useSmall && game"
:icon="mdiCog"
:href="`/#/games/${game}/edit`"
:outline="false"
color="info"
label="Edit Profile"
/>
<BaseButton
v-if="!useSmall && game && getRivalInfo(game, version)"
:icon="mdiAxeBattle"
:href="`/#/games/${game}/rivals`"
:outline="false"
color="info"
label="Rivals"
/>
<BaseButton
v-if="!useSmall && !thisGame.noScores"
:href="`/#/games/${game}/scores/${profile.userId}`"
:icon="mdiPlaylistMusicOutline"
:outline="false"
color="info"
label="My Scores"
/>
<BaseButton
v-if="!useSmall && !thisGame.noRecords"
:href="`/#/games/${game}/records/${profile.userId}`"
:icon="mdiFormatListText"
:outline="false"
color="info"
label="My Records"
/>
<BaseButton
v-if="!useSmall && !thisGame.noScores"
:href="`/#/games/${game}/scores`"
:icon="mdiPlaylistMusicOutline"
:outline="false"
color="info"
label="Network Scores"
/>
<BaseButton
v-if="!useSmall && !thisGame.noRecords"
:href="`/#/games/${game}/records`"
:icon="mdiFormatListText"
:outline="false"
color="info"
label="Network Records"
/>
</div>
</div>
</div>
<slot />
</template>

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { watch, ref, reactive } from "vue";
import { useRouter } from "vue-router";
import { mdiLoading } from "@mdi/js";
import { PhSpinnerBall } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import CardBox from "@/components/CardBox.vue";
@ -160,7 +160,7 @@ function revert() {
/>
<BaseIcon
v-if="loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -2,7 +2,7 @@
import axios from "axios";
import { watch, ref, reactive } from "vue";
import { useRouter } from "vue-router";
import { mdiLoading, mdiChevronDown, mdiChevronUp } from "@mdi/js";
import { PhSpinnerBall, PhCaretDown, PhCaretUp } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import CardBox from "@/components/CardBox.vue";
@ -170,7 +170,7 @@ function toggleStickerCollapse(index) {
<div class="flex justify-between items-center">
<span class="text-lg">Sticker {{ index + 1 }}</span>
<BaseButton
:icon="collapsedStickers[index] ? mdiChevronDown : mdiChevronUp"
:icon="collapsedStickers[index] ? PhCaretDown : PhCaretUp"
color="info"
@click="toggleStickerCollapse(index)"
/>
@ -264,7 +264,7 @@ function toggleStickerCollapse(index) {
/>
<BaseIcon
v-if="loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -1,6 +1,6 @@
<script setup>
import { containerMaxW } from "@/config.js";
import { mdiCash, mdiGithub } from "@mdi/js";
import { PhPaypalLogo, PhGithubLogo } from "@phosphor-icons/vue";
import BaseLevel from "@/components/BaseLevel.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -29,7 +29,7 @@ const appVersion = import.meta.env.VITE_APP_VERSION;
color="warning"
href="https://paypal.me/trmazi"
label="Donate"
:icon="mdiCash"
:icon="PhPaypalLogo"
icon-size="24"
/>
<BaseButton
@ -37,7 +37,7 @@ const appVersion = import.meta.env.VITE_APP_VERSION;
href="https://github.com/PhaseII-eAmusement-Network/PhaseWeb3-Vue"
target="_blank"
label="View on GitHub"
:icon="mdiGithub"
:icon="PhGithubLogo"
icon-size="24"
/>
</div>

View File

@ -41,7 +41,7 @@ const props = defineProps({
default: null,
},
icon: {
type: String,
type: Object,
default: null,
},
options: {

View File

@ -3,7 +3,7 @@ import BaseIcon from "@/components/BaseIcon.vue";
defineProps({
icon: {
type: String,
type: Object,
default: null,
},
h: {
@ -15,7 +15,7 @@ defineProps({
<template>
<BaseIcon
:path="icon"
:icon="icon"
w="w-10"
:h="h"
class="absolute top-0 left-0 z-10 pointer-events-none text-gray-500 dark:text-slate-400"

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiUpload } from "@mdi/js";
import { PhUpload } from "@phosphor-icons/vue";
import { computed, ref, watch } from "vue";
import BaseButton from "@/components/BaseButton.vue";
@ -13,8 +13,8 @@ const props = defineProps({
default: null,
},
icon: {
type: String,
default: mdiUpload,
type: Object,
default: PhUpload,
},
accept: {
type: String,

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiDownload, mdiChevronLeft, mdiChevronRight } from "@mdi/js";
import { PhDownload, PhCaretLeft, PhCaretRight } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import UserAvatar from "@/components/UserAvatar.vue";
import DownloadJS from "downloadjs";
@ -60,7 +60,7 @@ function downloadJSON() {
>
<div class="flex items-center">
<BaseButton
:icon="mdiChevronLeft"
:icon="PhCaretLeft"
color="info"
very-small
:disabled="isFirstPage"
@ -70,7 +70,7 @@ function downloadJSON() {
currentPaginationNumber
}}</span>
<BaseButton
:icon="mdiChevronRight"
:icon="PhCaretRight"
color="info"
very-small
small
@ -101,7 +101,7 @@ function downloadJSON() {
<div class="p-2 w-full flex md:justify-end">
<BaseButton
label="Export Table"
:icon="mdiDownload"
:icon="PhDownload"
color="info"
@click="downloadJSON"
/>

View File

@ -4,7 +4,7 @@ import BaseIcon from "@/components/BaseIcon.vue";
const props = defineProps({
icon: {
type: String,
type: Object,
required: true,
},
iconColor: {
@ -34,7 +34,7 @@ const spanColor = computed(() => props.iconColor);
<template>
<BaseIcon
:path="icon"
:icon="icon"
:w="w"
:h="h"
:color="spanColor"

View File

@ -2,7 +2,7 @@
import { ref, computed } from "vue";
import { RouterLink } from "vue-router";
import { useStyleStore } from "@/stores/style.js";
import { mdiMinus, mdiPlus } from "@mdi/js";
import { PhCaretUp } from "@phosphor-icons/vue";
import { getButtonColor } from "@/colors.js";
import BaseIcon from "@/components/BaseIcon.vue";
import AsideMenuList from "@/components/Menus/AsideMenuList.vue";
@ -59,7 +59,8 @@ const menuClick = (event) => {
>
<BaseIcon
v-if="item.icon"
:path="item.icon"
:icon="item.icon"
:fill="item.fill ? item.fill : 'regular'"
class="flex-none"
:class="[vSlot && vSlot.isExactActive ? asideMenuItemActiveStyle : '']"
w="w-16"
@ -75,10 +76,15 @@ const menuClick = (event) => {
>
<BaseIcon
v-if="hasDropdown"
:path="isDropdownActive ? mdiMinus : mdiPlus"
class="flex-none"
:class="[vSlot && vSlot.isExactActive ? asideMenuItemActiveStyle : '']"
w="w-12"
:icon="PhCaretUp"
:size="18"
:fill="item.fill ? item.fill : 'regular'"
class="flex-none transition-transform duration-150 ease-in-out"
:class="[
isDropdownActive ? 'rotate-0' : 'rotate-180',
vSlot && vSlot.isExactActive ? asideMenuItemActiveStyle : '',
]"
w="w-10"
/>
</component>
<Transition
@ -92,10 +98,7 @@ const menuClick = (event) => {
<AsideMenuList
v-show="isDropdownActive"
:menu="item.menu"
:class="[
styleStore.asideMenuDropdownStyle,
'dark:bg-slate-800/50',
]"
:class="[styleStore.asideMenuDropdownStyle, 'dark:bg-slate-800/50']"
is-dropdown-list
/>
</Transition>

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiLogout, mdiClose } from "@mdi/js";
import { PhSignOut, PhXCircle } from "@phosphor-icons/vue";
import { computed } from "vue";
import { useStyleStore } from "@/stores/style.js";
import AsideMenuList from "@/components/Menus/AsideMenuList.vue";
@ -18,14 +18,15 @@ const styleStore = useStyleStore();
const logoutItem = computed(() => ({
label: "Log out",
icon: mdiLogout,
icon: PhSignOut,
color: "info",
isLogout: true,
fill: "regular",
}));
const closeItem = computed(() => ({
label: "Close",
icon: mdiClose,
icon: PhXCircle,
color: "danger",
}));

View File

@ -5,7 +5,8 @@ defineProps({
isDropdownList: Boolean,
menu: {
type: Array,
required: true,
required: false,
default: null,
},
});

View File

@ -1,6 +1,6 @@
<script setup>
import { computed } from "vue";
import { mdiClose } from "@mdi/js";
import { PhXSquare } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import CardBox from "@/components/CardBox.vue";
@ -60,7 +60,7 @@ window.addEventListener("keydown", (e) => {
<CardBoxComponentTitle :title="title">
<BaseButton
v-if="hasCancel"
:icon="mdiClose"
:icon="PhXSquare"
color="whiteDark"
small
rounded-full

View File

@ -4,7 +4,7 @@ import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import { mdiEmailAlertOutline } from "@mdi/js";
import { PhEnvelopeOpen } from "@phosphor-icons/vue";
import { useMainStore } from "@/stores/main.js";
import { loadCookie, saveCookie } from "@/stores/cookies";
@ -47,7 +47,7 @@ function verifyLater() {
<div class="grid text-center justify-center grid-cols-1 gap-3">
<div class="place-self-center">
<BaseIcon
:path="mdiEmailAlertOutline"
:icon="PhEnvelopeOpen"
class="text-yellow-600"
w="w-20"
:size="45"

View File

@ -4,7 +4,7 @@ import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseButton from "@/components/BaseButton.vue";
import { mdiCheckOutline, mdiCloseOutline } from "@mdi/js";
import { PhCloudCheck, PhCloudX } from "@phosphor-icons/vue";
const $router = useRouter();
@ -59,7 +59,7 @@ function hotReload() {
>
<div class="place-self-center">
<BaseIcon
:path="mdiCheckOutline"
:icon="PhCloudCheck"
class="text-green-700"
w="w-20"
:size="45"
@ -73,12 +73,7 @@ function hotReload() {
class="grid text-center justify-center grid-cols-1 gap-3"
>
<div class="place-self-center">
<BaseIcon
:path="mdiCloseOutline"
class="text-red-500"
w="w-20"
:size="45"
/>
<BaseIcon :icon="PhCloudX" class="text-red-500" w="w-20" :size="45" />
</div>
<h1 class="text-xl md:text-2xl">Uh Oh!</h1>
<h1 class="text-lg md:text-xl">

View File

@ -5,7 +5,7 @@ import CardBox from "@/components/CardBox.vue";
import OverlayLayer from "@/components/OverlayLayer.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import { mdiSourceBranchRefresh } from "@mdi/js";
import { PhSparkle } from "@phosphor-icons/vue";
import { APIUserAppUpdate, APIUserCustomize } from "@/stores/api/account";
import { useMainStore } from "@/stores/main.js";
@ -80,7 +80,7 @@ async function updateUserData(disable = false) {
<div class="grid text-center justify-center grid-cols-1 gap-3">
<div class="place-self-center">
<BaseIcon
:path="mdiSourceBranchRefresh"
:icon="PhSparkle"
class="text-emerald-600"
w="w-20"
:size="45"

View File

@ -1,6 +1,6 @@
<script setup>
import { ref } from "vue";
import { mdiClose, mdiDotsVertical } from "@mdi/js";
import { PhCaretDoubleUp } from "@phosphor-icons/vue";
import { containerMaxW } from "@/config.js";
import BaseIcon from "@/components/BaseIcon.vue";
import NavBarMenuList from "@/components/NavBarMenuList.vue";
@ -35,8 +35,11 @@ const isMenuNavBarActive = ref(false);
@click.prevent="isMenuNavBarActive = !isMenuNavBarActive"
>
<BaseIcon
:path="isMenuNavBarActive ? mdiClose : mdiDotsVertical"
size="24"
:icon="PhCaretDoubleUp"
:size="24"
fill="bold"
class="flex-none transition-transform duration-150 ease-in-out"
:class="[isMenuNavBarActive ? 'rotate-0' : 'rotate-180']"
/>
</NavBarItemPlain>
</div>

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiChevronUp, mdiChevronDown } from "@mdi/js";
import { PhCaretUp } from "@phosphor-icons/vue";
import { RouterLink } from "vue-router";
import { computed, ref, onMounted, onBeforeUnmount } from "vue";
import { useStyleStore } from "@/stores/style.js";
@ -110,7 +110,12 @@ onBeforeUnmount(() => {
v-if="item.isCurrentUser"
class="w-6 h-6 mr-3 inline-flex"
/>
<BaseIcon v-if="item.icon" :path="item.icon" class="transition-colors" />
<BaseIcon
v-if="item.icon"
:icon="item.icon"
:fill="item.fill ?? 'duotone'"
class="transition-colors"
/>
<span
class="px-2 transition-colors"
:class="{ 'lg:hidden': item.isDesktopNoLabel && item.icon }"
@ -118,8 +123,11 @@ onBeforeUnmount(() => {
>
<BaseIcon
v-if="item.menu"
:path="isDropdownActive ? mdiChevronUp : mdiChevronDown"
class="hidden lg:inline-flex transition-colors"
:icon="PhCaretUp"
:size="18"
fill="regular"
class="flex-none transition-transform duration-150 ease-in-out invisible lg:visible"
:class="[isDropdownActive ? 'rotate-0' : 'rotate-180']"
/>
</div>
<div

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, computed, useSlots } from "vue";
import { mdiClose } from "@mdi/js";
import { PhX } from "@phosphor-icons/vue";
import { colorsBgLight, colorsOutline } from "@/colors.js";
import BaseLevel from "@/components/BaseLevel.vue";
import BaseIcon from "@/components/BaseIcon.vue";
@ -8,7 +8,7 @@ import BaseButton from "@/components/BaseButton.vue";
const props = defineProps({
icon: {
type: String,
type: Object,
default: null,
},
outline: {
@ -46,7 +46,7 @@ const hasRightSlot = computed(() => slots.right);
<div class="flex flex-col md:flex-row items-center">
<BaseIcon
v-if="icon"
:path="icon"
:icon="icon"
w="w-10 md:w-5"
h="h-10 md:h-5"
size="24"
@ -57,7 +57,7 @@ const hasRightSlot = computed(() => slots.right);
<slot v-if="hasRightSlot" name="right" />
<BaseButton
v-else
:icon="mdiClose"
:icon="PhX"
small
rounded-full
color="white"

View File

@ -13,7 +13,7 @@ const props = defineProps({
required: true,
},
icon: {
type: String,
type: Object,
default: null,
},
small: Boolean,

View File

@ -7,7 +7,7 @@ defineProps({
required: true,
},
icon: {
type: String,
type: Object,
default: null,
},
small: Boolean,
@ -21,7 +21,7 @@ defineProps({
>
<BaseIcon
v-if="icon"
:path="icon"
:icon="icon"
h="h-4"
w="w-4"
:class="small ? 'mr-1' : 'mr-2'"

View File

@ -1,6 +1,6 @@
<script setup>
import { computed } from "vue";
import { mdiChevronUp, mdiChevronDown, mdiAlertCircleOutline } from "@mdi/js";
import { PhCaretUp, PhCaretDown, PhWarningCircle } from "@phosphor-icons/vue";
import PillTag from "@/components/PillTag.vue";
const props = defineProps({
@ -18,21 +18,21 @@ const props = defineProps({
const trendStyle = computed(() => {
if (props.trendType === "up") {
return {
icon: mdiChevronUp,
icon: PhCaretUp,
style: "success",
};
}
if (props.trendType === "down") {
return {
icon: mdiChevronDown,
icon: PhCaretDown,
style: "danger",
};
}
if (props.trendType === "alert") {
return {
icon: mdiAlertCircleOutline,
icon: PhWarningCircle,
style: "warning",
};
}

View File

@ -5,7 +5,7 @@ import IconRounded from "@/components/IconRounded.vue";
defineProps({
icon: {
type: String,
type: Object,
default: null,
},
color: {
@ -38,7 +38,7 @@ const hasSlot = computed(() => useSlots().default);
/>
<BaseIcon
v-else-if="icon"
:path="icon"
:icon="icon"
:color="color"
class="mr-2"
size="20"

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiCog } from "@mdi/js";
import { PhGear } from "@phosphor-icons/vue";
import { useSlots, computed } from "vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -7,7 +7,7 @@ import IconRounded from "@/components/IconRounded.vue";
defineProps({
icon: {
type: String,
type: Object,
default: null,
},
title: {
@ -33,12 +33,12 @@ const hasSlot = computed(() => useSlots().default);
class="mr-3"
bg
/>
<BaseIcon v-else-if="icon" :path="icon" class="mr-2" size="20" />
<BaseIcon v-else-if="icon" :icon="icon" class="mr-2" size="20" />
<h1 :class="main ? 'text-3xl' : 'text-2xl'" class="leading-tight">
{{ title }}
</h1>
</div>
<slot v-if="hasSlot" />
<BaseButton v-else :icon="mdiCog" color="whiteDark" />
<BaseButton v-else :icon="PhGear" color="whiteDark" />
</section>
</template>

View File

@ -3,21 +3,21 @@ import { ref } from "vue";
import { useMainStore } from "@/stores/main";
import { RoleConstants } from "@/constants/discordRoles";
import {
mdiSecurity,
mdiTestTube,
mdiAccountStar,
mdiAccountOff,
mdiFlowerPoppy,
mdiSharkFinOutline,
mdiHandCoinOutline,
mdiAccountTieHat,
mdiAccountTie,
mdiHeartMultipleOutline,
mdiCheckDecagramOutline,
mdiLinkBoxVariantOutline,
mdiAccountCheck,
mdiGavel,
} from "@mdi/js";
PhCrown,
PhTestTube,
PhShieldStar,
PhEyeSlash,
PhFlower,
PhFish,
PhHandCoins,
PhShieldChevron,
PhBracketsCurly,
PhHandHeart,
PhSealCheck,
PhShrimp,
PhUserCheck,
PhGavel,
} from "@phosphor-icons/vue";
import BaseLevel from "@/components/BaseLevel.vue";
import UserAvatar from "@/components/UserAvatar.vue";
import CardBox from "@/components/CardBox.vue";
@ -151,105 +151,100 @@ function getCardStyle() {
v-if="cardData.userAdmin"
label="System Admin"
color="danger"
:icon="mdiSecurity"
:icon="PhCrown"
small
/>
<PillTag
v-if="cardData.userBanned"
label="Banned"
color="danger"
:icon="mdiGavel"
:icon="PhGavel"
small
/>
<PillTag
v-if="cardData.userId < 300"
label="Veteran"
color="success"
:icon="mdiAccountStar"
:icon="PhShieldStar"
small
/>
<PillTag
v-if="!cardData.userPublic"
label="Private Profile"
color="info"
:icon="mdiAccountOff"
:icon="PhEyeSlash"
small
/>
<PillTag
v-if="cardData.userPublic"
label="Public Profile"
color="success"
:icon="mdiAccountCheck"
:icon="PhUserCheck"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.PLAYER)"
label="Verified"
color="success"
:icon="mdiCheckDecagramOutline"
:icon="PhSealCheck"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.JACKASS)"
label="Jackass"
color="slight_danger"
:icon="mdiHeartMultipleOutline"
:icon="PhHandHeart"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.DEVELOPER)"
label="Developer"
color="success"
:icon="mdiAccountTie"
:icon="PhBracketsCurly"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.MODERATOR)"
label="Moderator"
color="slight_danger"
:icon="mdiAccountTieHat"
:icon="PhShieldChevron"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.BETA_TESTER)"
label="Beta Tester"
color="warning"
:icon="mdiTestTube"
:icon="PhTestTube"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.DONOR)"
label="Donor"
color="gold"
:icon="mdiHandCoinOutline"
:icon="PhHandCoins"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.BLAHAJ)"
label="Blåhaj"
color="info"
:icon="mdiSharkFinOutline"
:icon="PhFish"
small
/>
<PillTag
v-if="cardData.discordRoles?.includes(RoleConstants.RHYTHM_RIOT)"
label="Rhythm Riot"
color="sakura"
:icon="mdiFlowerPoppy"
:icon="PhFlower"
small
/>
<PillTag
v-if="cardData.userCustomize?.shrimpLinks"
label="Shrimp Links"
color="sakura"
:icon="mdiLinkBoxVariantOutline"
:icon="PhShrimp"
small
/>
<!-- <PillTag
label="Public Profile"
color="success"
:icon="mdiAccountCheck"
/> -->
</div>
</div>
<div

View File

@ -1,15 +1,14 @@
<script setup>
import {
mdiForwardburger,
mdiBackburger,
mdiMenu,
mdiMonitor,
mdiStoreCog,
mdiStorePlus,
mdiGamepad,
mdiSecurity,
mdiMultimedia,
} from "@mdi/js";
PhCaretDoubleLeft,
PhDotsThreeCircle,
PhMonitor,
PhStorefront,
PhStackPlus,
PhJoystick,
PhCrown,
PhFilmSlate,
} from "@phosphor-icons/vue";
import { ref, watch, onMounted, computed } from "vue";
import { useRouter, useRoute } from "vue-router";
import menuNavBar from "@/menuNavBar.js";
@ -173,12 +172,12 @@ const menuAside = computed(() => {
},
];
var sideMenu = [{ to: "/", icon: mdiMonitor, label: "Dashboard" }];
var sideMenu = [{ to: "/", icon: PhMonitor, label: "Dashboard" }];
if (mainStore.userAdmin) {
sideMenu.push({
label: "Admin",
icon: mdiSecurity,
icon: PhCrown,
menu: adminMenu,
});
}
@ -186,14 +185,14 @@ const menuAside = computed(() => {
if (sortedGames.length) {
sideMenu.push({
label: "Games",
icon: mdiGamepad,
icon: PhJoystick,
menu: sortedGames,
});
}
sideMenu.push({
label: "My Content",
icon: mdiMultimedia,
icon: PhFilmSlate,
menu: [
{
label: "Play Videos",
@ -209,14 +208,14 @@ const menuAside = computed(() => {
if (sortedArcades.length) {
sideMenu.push({
label: "My Arcades",
icon: mdiStoreCog,
icon: PhStorefront,
menu: sortedArcades,
});
}
sideMenu.push({
label: "Claim Arcade",
icon: mdiStorePlus,
icon: PhStackPlus,
to: `/arcade/claim`,
});
@ -265,15 +264,18 @@ const menuAside = computed(() => {
@click.prevent="isAsideMobileExpanded = !isAsideMobileExpanded"
>
<BaseIcon
:path="isAsideMobileExpanded ? mdiBackburger : mdiForwardburger"
size="24"
:icon="PhCaretDoubleLeft"
:size="24"
fill="bold"
class="flex-none transition-transform duration-150 ease-in-out"
:class="[isAsideMobileExpanded ? 'rotate-0' : 'rotate-180']"
/>
</NavBarItemPlain>
<NavBarItemPlain
display="hidden lg:flex xl:hidden"
@click.prevent="isAsideLgActive = true"
>
<BaseIcon :path="mdiMenu" size="24" />
<BaseIcon :icon="PhDotsThreeCircle" size="24" />
</NavBarItemPlain>
<div class="h-full flex place-items-center ml-4 gap-4">

View File

@ -1,45 +1,46 @@
import {
mdiAccount,
mdiLogout,
mdiBrushVariant,
mdiServerNetwork,
mdiCardAccountDetailsOutline,
mdiAccountArrowLeftOutline,
} from "@mdi/js";
PhUser,
PhSignOut,
PhPaintBrushBroad,
PhCloud,
PhIdentificationCard,
PhUserSwitch,
} from "@phosphor-icons/vue";
export default [
{
isCurrentUser: true,
menu: [
{
icon: mdiAccount,
icon: PhUser,
label: "Settings",
to: "/profile",
},
{
icon: mdiBrushVariant,
icon: PhPaintBrushBroad,
label: "Customize",
to: "/profile/customize",
},
{
icon: mdiServerNetwork,
icon: PhCloud,
label: "Integrations",
to: "/profile/integrate",
},
{
icon: mdiCardAccountDetailsOutline,
icon: PhIdentificationCard,
label: "Login Cards",
to: "/profile/cards",
},
{
icon: mdiAccountArrowLeftOutline,
icon: PhUserSwitch,
label: "Claim Profile",
to: "/profile/claim",
},
],
},
{
icon: mdiLogout,
icon: PhSignOut,
fill: "regular",
label: "Log out",
isLogout: true,
},

View File

@ -273,17 +273,6 @@ const routes = [
hotReload: true, // disables Hot Reload
},
},
{
meta: {
title: "All Players",
},
path: "/games/:id/players/",
name: "players_page",
component: () => import("@/views/Game/PlayersView.vue"),
options: {
hotReload: true, // disables Hot Reload
},
},
{
meta: {
title: "View Profile",

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, reactive, onMounted } from "vue";
import { mdiCloudKeyOutline, mdiKeyPlus } from "@mdi/js";
import { PhDatabase, PhKey } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
@ -89,7 +89,7 @@ const copyToClipboard = (item) => {
</CardBox>
<SectionTitleLine
:icon="mdiCloudKeyOutline"
:icon="PhDatabase"
title="All Active Clients"
color="text-blue-400"
main
@ -113,7 +113,7 @@ const copyToClipboard = (item) => {
</CardBox>
<SectionTitleLine
:icon="mdiKeyPlus"
:icon="PhKey"
title="Create New Client"
color="text-emerald-600"
main

View File

@ -2,10 +2,10 @@
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import {
mdiStoreOutline,
mdiStoreEditOutline,
mdiStorePlusOutline,
} from "@mdi/js";
PhStorefront,
PhMagnifyingGlass,
PhPlusCircle,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
@ -133,7 +133,7 @@ async function findMachine() {
</CardBox>
<SectionTitleLine
:icon="mdiStoreEditOutline"
:icon="PhMagnifyingGlass"
title="Search Arcades"
color="text-amber-600"
main
@ -207,7 +207,7 @@ async function findMachine() {
</div>
<SectionTitleLine
:icon="mdiStoreOutline"
:icon="PhStorefront"
title="All Arcades"
color="text-blue-400"
main
@ -230,7 +230,7 @@ async function findMachine() {
</CardBox>
<SectionTitleLine
:icon="mdiStorePlusOutline"
:icon="PhPlusCircle"
title="Create Arcade"
color="text-emerald-600"
main

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { mdiFlagCheckered, mdiSecurity } from "@mdi/js";
import { PhFlagCheckered, PhCrown } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import CardBoxWidget from "@/components/CardBoxWidget.vue";
@ -126,7 +126,7 @@ function formatEvents(events) {
</CardBox>
<SectionTitleLine
:icon="mdiSecurity"
:icon="PhCrown"
title="Admin Dashboard"
color="text-red-600"
main
@ -173,8 +173,12 @@ function formatEvents(events) {
/>
</div>
<SectionTitleLine :icon="mdiFlagCheckered" title="Recent PCB Events" />
<CardBox has-table>
<SectionTitleLine
:icon="PhFlagCheckered"
main
title="Recent PCB Events"
/>
<CardBox has-table class="mb-6">
<div
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"
>
@ -188,7 +192,8 @@ function formatEvents(events) {
</CardBox>
<SectionTitleLine
:icon="mdiFlagCheckered"
:icon="PhFlagCheckered"
main
title="Recent PASELI Transactions"
/>
<CardBox has-table>

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { mdiFlagCheckered, mdiCashEdit, mdiExclamationThick } from "@mdi/js";
import { PhFlagCheckered, PhCashRegister, PhSiren } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
@ -204,7 +204,7 @@ function extractExceptionData(xmlString) {
</CardBox>
<SectionTitleLine
:icon="mdiExclamationThick"
:icon="PhSiren"
title="Recent Tracebacks"
color="text-red-400"
main
@ -223,7 +223,7 @@ function extractExceptionData(xmlString) {
</CardBox>
<SectionTitleLine
:icon="mdiFlagCheckered"
:icon="PhFlagCheckered"
title="Recent PCB Events"
color="text-amber-400"
main
@ -242,7 +242,7 @@ function extractExceptionData(xmlString) {
</CardBox>
<SectionTitleLine
:icon="mdiCashEdit"
:icon="PhCashRegister"
title="Recent PASELI Transactions"
color="text-emerald-400"
main

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, reactive, onMounted } from "vue";
import { mdiWrenchCogOutline, mdiWrenchClock } from "@mdi/js";
import { PhWrench, PhClockCountdown } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
@ -92,7 +92,7 @@ async function enterMaintenance() {
</CardBox>
<SectionTitleLine
:icon="mdiWrenchCogOutline"
:icon="PhWrench"
title="Recent Maintenance Periods"
color="text-blue-400"
main
@ -109,7 +109,7 @@ async function enterMaintenance() {
</CardBox>
<SectionTitleLine
:icon="mdiWrenchClock"
:icon="PhClockCountdown"
title="Create Maintenance Period"
color="text-yellow-600"
main

View File

@ -2,11 +2,12 @@
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import {
mdiNewspaper,
mdiNewspaperVariantMultipleOutline,
mdiNewspaperMinus,
mdiNewspaperPlus,
} from "@mdi/js";
PhNewspaperClipping,
PhNewspaper,
PhPlusCircle,
PhMinusCircle,
PhPencilCircle,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
@ -145,7 +146,7 @@ function getSelectedNews(newsId) {
</CardBox>
<SectionTitleLine
:icon="mdiNewspaper"
:icon="PhNewspaperClipping"
title="Post Management"
color="text-emerald-600"
main
@ -156,7 +157,7 @@ function getSelectedNews(newsId) {
<PillTag
color="success"
label="Create Post"
:icon="mdiNewspaperPlus"
:icon="PhPlusCircle"
class="mb-2"
/>
@ -176,6 +177,7 @@ function getSelectedNews(newsId) {
color="success"
label="Create"
:small="false"
:icon="PhPlusCircle"
/>
</div>
</CardBox>
@ -184,7 +186,7 @@ function getSelectedNews(newsId) {
<PillTag
color="warning"
label="Edit Post"
:icon="mdiNewspaperMinus"
:icon="PhMinusCircle"
class="mb-2"
/>
<FormField label="Post">
@ -216,7 +218,7 @@ function getSelectedNews(newsId) {
color="info"
label="Update"
:small="false"
:icon="mdiNewspaper"
:icon="PhPencilCircle"
@click="updateNews()"
/>
@ -224,7 +226,7 @@ function getSelectedNews(newsId) {
color="danger"
label="Delete Post"
:small="false"
:icon="mdiNewspaperMinus"
:icon="PhMinusCircle"
@click="deleteNews()"
/>
</BaseButtons>
@ -233,7 +235,7 @@ function getSelectedNews(newsId) {
</div>
<SectionTitleLine
:icon="mdiNewspaperVariantMultipleOutline"
:icon="PhNewspaper"
title="All News"
color="text-blue-400"
main

View File

@ -1,6 +1,6 @@
<script setup>
import { reactive } from "vue";
import { mdiUpdate, mdiCloudUploadOutline } from "@mdi/js";
import { PhCloudArrowDown, PhCloudArrowUp } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import CardBoxWidget from "@/components/CardBoxWidget.vue";
@ -30,9 +30,9 @@ const newUpdate = reactive({
<LayoutAuthenticated>
<SectionMain>
<SectionTitleLine
:icon="mdiUpdate"
:icon="PhCloudArrowDown"
title="OTA Update Administration"
color="text-red-600"
color="text-sky-500"
main
/>
@ -41,8 +41,9 @@ const newUpdate = reactive({
</div>
<SectionTitleLine
:icon="mdiCloudUploadOutline"
:icon="PhCloudArrowUp"
title="Add an Update"
color="text-emerald-500"
main
/>
<CardBox is-form class="row-span-1">

View File

@ -2,12 +2,12 @@
import { reactive, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
mdiInformationOutline,
mdiCogOutline,
mdiSailBoat,
mdiCheckOutline,
mdiCloseOutline,
} from "@mdi/js";
PhInfo,
PhGear,
PhSailboat,
PhCheckCircle,
PhXCircle,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
@ -262,7 +262,7 @@ async function onboardArcade(exportArcade) {
<LayoutAuthenticated>
<SectionMain>
<SectionTitleLine
:icon="mdiSailBoat"
:icon="PhSailboat"
title="Arcade Onboarding"
color="text-green-400"
main
@ -273,7 +273,7 @@ async function onboardArcade(exportArcade) {
<PillTag
color="info"
label="General Information"
:icon="mdiInformationOutline"
:icon="PhInfo"
class="mb-2"
/>
<FormField label="Arcade Name">
@ -289,13 +289,13 @@ async function onboardArcade(exportArcade) {
<BaseButton color="info" label="Check Name" @click="checkName()" />
<BaseIcon
v-if="arcadeCheck == true"
:path="mdiCheckOutline"
:icon="PhCheckCircle"
color="text-green-400"
size="25"
/>
<BaseIcon
v-else-if="arcadeCheck == false"
:path="mdiCloseOutline"
:icon="PhXCircle"
color="text-red-400"
size="25"
/>
@ -338,12 +338,7 @@ async function onboardArcade(exportArcade) {
</CardBox>
<CardBox is-form class="row-span-1">
<PillTag
color="info"
label="Settings"
:icon="mdiCogOutline"
class="mb-2"
/>
<PillTag color="info" label="Settings" :icon="PhGear" class="mb-2" />
<div
class="grid grid-cols-1 w-full gap-2 md:gap-6 md:flex md:place-content-stretch"
>
@ -406,13 +401,7 @@ async function onboardArcade(exportArcade) {
</CardBox>
<CardBox class="row-span-1">
<PillTag
color="info"
label="Machines"
:icon="mdiInformationOutline"
class="mb-2"
/>
<PillTag color="info" label="Machines" :icon="PhInfo" class="mb-2" />
<div class="grid md:grid-cols-2 gap-6">
<form class="h-full" @submit.prevent="addMachine()">
<FormField

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { mdiAccountBadgeOutline } from "@mdi/js";
import { PhUsersThree, PhMagnifyingGlass } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
@ -129,6 +129,12 @@ async function findUser() {
<p class="text-sm text-gray-400">Click a row to open User</p>
</CardBox>
<SectionTitleLine
:icon="PhMagnifyingGlass"
title="Search"
color="text-amber-500"
main
/>
<div class="grid md:grid-cols-2 gap-6">
<CardBox class="mb-6">
<PillTag color="info" label="Search" class="mb-2" />
@ -201,7 +207,7 @@ async function findUser() {
</div>
<SectionTitleLine
:icon="mdiAccountBadgeOutline"
:icon="PhUsersThree"
title="All Users"
color="text-blue-400"
main

View File

@ -2,14 +2,12 @@
import { useRoute, useRouter } from "vue-router";
import { ref, onMounted, watch } from "vue";
import {
mdiStore,
mdiStoreCog,
mdiCogOutline,
mdiShieldEditOutline,
mdiCheckOutline,
mdiCloseOutline,
mdiInformationOutline,
} from "@mdi/js";
PhStorefront,
PhGear,
PhCheckCircle,
PhXCircle,
PhInfo,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -248,11 +246,7 @@ async function adminDeleteArcade() {
<ArcadeCard class="mb-6" :arcade="arcadeData" />
<template v-if="mainStore.userAdmin">
<SectionTitleLine
:icon="mdiShieldEditOutline"
title="Arcade Administration"
main
/>
<SectionTitleLine :icon="PhGear" title="Arcade Administration" main />
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<CardBox
is-form
@ -262,7 +256,7 @@ async function adminDeleteArcade() {
<PillTag
color="info"
label="General Information"
:icon="mdiInformationOutline"
:icon="PhInfo"
class="mb-2"
/>
<FormField label="Arcade Name">
@ -282,13 +276,13 @@ async function adminDeleteArcade() {
/>
<BaseIcon
v-if="arcadeCheck == true"
:path="mdiCheckOutline"
:icon="PhCheckCircle"
color="text-green-400"
size="25"
/>
<BaseIcon
v-else-if="arcadeCheck == false"
:path="mdiCloseOutline"
:icon="PhXCircle"
color="text-red-400"
size="25"
/>
@ -336,7 +330,7 @@ async function adminDeleteArcade() {
<PillTag
color="info"
label="Add Manager"
:icon="mdiInformationOutline"
:icon="PhInfo"
class="mb-2"
/>
<form class="h-full" @submit.prevent="addManager">
@ -392,14 +386,9 @@ async function adminDeleteArcade() {
</div>
</template>
<SectionTitleLine :icon="mdiStoreCog" title="Arcade Management" main />
<SectionTitleLine :icon="PhGear" title="Arcade Management" main />
<CardBox is-form class="mb-6" @submit.prevent="updateArcade">
<PillTag
color="info"
label="Settings"
:icon="mdiCogOutline"
class="mb-2"
/>
<PillTag color="info" label="Settings" :icon="PhGear" class="mb-2" />
<div
class="grid grid-cols-1 w-full gap-2 md:gap-6 md:flex md:place-content-stretch"
>
@ -447,7 +436,7 @@ async function adminDeleteArcade() {
/>
</CardBox>
<SectionTitleLine :icon="mdiStore" title="Arcade Overview" main />
<SectionTitleLine :icon="PhStorefront" title="Arcade Overview" main />
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 mb-6">
<CardBoxWidget

View File

@ -1,11 +1,12 @@
<script setup>
import { ref, reactive } from "vue";
import {
mdiStorePlus,
mdiScriptTextKeyOutline,
mdiLoading,
mdiAccountConvertOutline,
} from "@mdi/js";
PhSpinnerBall,
PhUnite,
PhTextbox,
PhStorefront,
PhQuestion,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
@ -56,7 +57,7 @@ async function saveSettings() {
<UserCard class="mb-6" use-small even-smaller />
<template v-if="!takeoverData">
<SectionTitleLine :icon="mdiStorePlus" title="Claim an Arcade" main />
<SectionTitleLine :icon="PhStorefront" title="Claim an Arcade" main />
<CardBox
is-form
class="row-span-2 mb-6"
@ -73,7 +74,7 @@ async function saveSettings() {
>
<FormControl
v-model="arcadeForm.PCBID"
:icon="mdiScriptTextKeyOutline"
:icon="PhTextbox"
name="cardId"
type="pcbid"
placeholder="XXXXXXXXXXXXXXXXX"
@ -87,7 +88,7 @@ async function saveSettings() {
<BaseButton type="submit" color="success" label="Start Claim" />
<BaseIcon
v-if="cardLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -96,7 +97,7 @@ async function saveSettings() {
</template>
<div v-if="takeoverData && saveState == null">
<SectionTitleLine :icon="mdiStorePlus" title="Confirm Merge" main />
<SectionTitleLine :icon="PhQuestion" title="Confirm Merge" main />
<CardBox>
<h2 class="text-2xl font-bold">
{{ takeoverData?.arcade?.name }}
@ -120,11 +121,7 @@ async function saveSettings() {
</div>
<div v-if="saveState == true">
<SectionTitleLine
:icon="mdiAccountConvertOutline"
title="Takeover Results"
main
/>
<SectionTitleLine :icon="PhUnite" title="Takeover Results" main />
<CardBox>
<h2 class="text-xl mb-6">
@ -142,14 +139,10 @@ async function saveSettings() {
</div>
<div v-if="saveState == false">
<SectionTitleLine
:icon="mdiAccountConvertOutline"
title="Merge Results"
main
/>
<SectionTitleLine :icon="PhUnite" title="Takeover Results" main />
<CardBox>
<h2 class="text-xl mb-6"> Failed to merge. Please try again.</h2>
<h2 class="text-xl mb-6"> Failed to takeover. Please try again.</h2>
<div class="mt-4">
<BaseButton color="warning" label="Retry" @click="saveSettings" />

View File

@ -1,7 +1,7 @@
<script setup>
import { reactive, ref, watch, onMounted } from "vue";
import { useRoute } from "vue-router";
import { mdiNewspaperVariantMultiple } from "@mdi/js";
import { PhNewspaper } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
@ -164,7 +164,7 @@ watch(
<template v-if="!loading">
<ArcadeCard class="mb-6" :arcade="arcadeData" :use-small="true" />
<SectionTitleLine
:icon="mdiNewspaperVariantMultiple"
:icon="PhNewspaper"
title="Game Event Settings"
main
/>

View File

@ -2,11 +2,11 @@
import { ref, reactive, onMounted } from "vue";
import { useRoute } from "vue-router";
import {
mdiDatabasePlusOutline,
mdiDatabaseEditOutline,
mdiGamepad,
mdiShieldEditOutline,
} from "@mdi/js";
PhPlusCircle,
PhPencilCircle,
PhJoystick,
PhGear,
} from "@phosphor-icons/vue";
import { useMainStore } from "@/stores/main";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
@ -193,7 +193,7 @@ async function deleteMachine() {
<template v-if="mainStore.userAdmin">
<SectionTitleLine
:icon="mdiShieldEditOutline"
:icon="PhGear"
title="Machine Administration"
main
/>
@ -202,7 +202,7 @@ async function deleteMachine() {
<PillTag
color="success"
label="Add Machine"
:icon="mdiDatabasePlusOutline"
:icon="PhPlusCircle"
class="mb-2"
/>
<form class="h-full" @submit.prevent="addMachine()">
@ -265,7 +265,7 @@ async function deleteMachine() {
<PillTag
color="warning"
label="Edit Machine"
:icon="mdiDatabaseEditOutline"
:icon="PhPencilCircle"
class="mb-2"
/>
<FormField label="Machine">
@ -330,7 +330,7 @@ async function deleteMachine() {
</div>
</template>
<SectionTitleLine :icon="mdiGamepad" title="Machines" main />
<SectionTitleLine :icon="PhJoystick" title="Machines" main />
<CardBox has-table>
<div
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import { mdiCashRegister, mdiAccountCash } from "@mdi/js";
import { PhCashRegister, PhCurrencyJpy } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import GeneralTable from "@/components/GeneralTable.vue";
import CardBox from "@/components/CardBox.vue";
@ -99,7 +99,7 @@ function filterTransactions(transactions) {
<template v-if="!loading && arcadeData">
<ArcadeCard class="mb-6" :arcade="arcadeData" :use-small="true" />
<SectionTitleLine
:icon="mdiAccountCash"
:icon="PhCurrencyJpy"
title="Player PASELI Balances"
main
/>
@ -117,7 +117,7 @@ function filterTransactions(transactions) {
</CardBox>
<SectionTitleLine
:icon="mdiCashRegister"
:icon="PhCashRegister"
title="PASELI Transaction History"
main
class="pt-6"

View File

@ -1,6 +1,6 @@
<script setup>
import { reactive } from "vue";
import { mdiStorefront } from "@mdi/js";
import { PhStorefront } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -34,7 +34,7 @@ export default {
<template>
<LayoutAuthenticated>
<SectionMain>
<SectionTitleLine :icon="mdiStorefront" title="Public Arcades" main />
<SectionTitleLine :icon="PhStorefront" title="Public Arcades" main />
<FormField label="Search" class="md:w-1/2 lg:w-1/3">
<FormControl v-model="filterForm.filter" name="search" />

View File

@ -1,6 +1,6 @@
<script setup>
import { useRouter } from "vue-router";
import { mdiAccount, mdiAsterisk } from "@mdi/js";
import { PhUser, PhPassword } from "@phosphor-icons/vue";
import { reactive } from "vue";
import { useMainStore } from "@/stores/main.js";
import CardBox from "@/components/CardBox.vue";
@ -67,7 +67,7 @@ const submit = async () => {
<FormField label="Username">
<FormControl
v-model="form.login"
:icon="mdiAccount"
:icon="PhUser"
name="login"
autocomplete="username"
required
@ -77,7 +77,7 @@ const submit = async () => {
<FormField label="Password">
<FormControl
v-model="form.pass"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
name="password"
autocomplete="current-password"

View File

@ -2,12 +2,12 @@
import { reactive, ref } from "vue";
import { useRouter } from "vue-router";
import {
mdiAccount,
mdiMail,
mdiAsterisk,
mdiCardAccountDetailsOutline,
mdiLoading,
} from "@mdi/js";
PhUser,
PhAt,
PhPassword,
PhCreditCard,
PhSpinnerBall,
} from "@phosphor-icons/vue";
import CardBox from "@/components/CardBox.vue";
import FormCheckRadio from "@/components/FormCheckRadio.vue";
import FormField from "@/components/FormField.vue";
@ -92,7 +92,7 @@ async function registerProfile() {
<FormField label="Desired Username">
<FormControl
v-model="profileForm.username"
:icon="mdiAccount"
:icon="PhUser"
name="username"
required
autocomplete="username"
@ -102,7 +102,7 @@ async function registerProfile() {
<FormField label="Email Address">
<FormControl
v-model="profileForm.email"
:icon="mdiMail"
:icon="PhAt"
type="email"
name="email"
required
@ -113,7 +113,7 @@ async function registerProfile() {
<FormField label="Password">
<FormControl
v-model="profileForm.newPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="newPassword"
type="password"
required
@ -123,7 +123,7 @@ async function registerProfile() {
<FormField label="Password Confirmation">
<FormControl
v-model="profileForm.confirmPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="confirmPassword"
type="password"
required
@ -136,7 +136,7 @@ async function registerProfile() {
>
<FormControl
v-model="profileForm.cardId"
:icon="mdiCardAccountDetailsOutline"
:icon="PhCreditCard"
name="cardId"
type="card"
placeholder="XXXX-XXXX-XXXX-XXXX"
@ -150,7 +150,7 @@ async function registerProfile() {
<FormField label="Game PIN">
<FormControl
v-model="profileForm.pin"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
name="pin"
required
@ -175,7 +175,7 @@ async function registerProfile() {
<BaseButton type="submit" label="Register" color="success" />
<BaseIcon
v-if="registerLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -1,12 +1,7 @@
<script setup>
import { reactive } from "vue";
import { useRouter } from "vue-router";
import {
mdiEmailOutline,
mdiShieldKeyOutline,
mdiAsterisk,
mdiLoading,
} from "@mdi/js";
import { PhAt, PhKey, PhPassword, PhSpinnerBall } from "@phosphor-icons/vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
@ -98,7 +93,7 @@ async function resetPassword() {
<FormField label="Email Address">
<FormControl
v-model="form.email"
:icon="mdiEmailOutline"
:icon="PhAt"
type="email"
autocomplete="email"
required
@ -107,7 +102,7 @@ async function resetPassword() {
<BaseButton label="Check" type="submit" color="info" />
<BaseIcon
v-if="form.loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -119,7 +114,7 @@ async function resetPassword() {
<FormField label="Auth Key">
<FormControl
v-model="form.authKey"
:icon="mdiShieldKeyOutline"
:icon="PhKey"
type="text"
name="token"
inputmode="numeric"
@ -131,7 +126,7 @@ async function resetPassword() {
<BaseButton label="Unlock" type="submit" color="info" />
<BaseIcon
v-if="form.loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -151,7 +146,7 @@ async function resetPassword() {
<FormField label="New Password">
<FormControl
v-model="form.newPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
required
minlength="8"
@ -160,7 +155,7 @@ async function resetPassword() {
<FormField label="Confirm Password">
<FormControl
v-model="form.confirmPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
required
minlength="8"
@ -173,7 +168,7 @@ async function resetPassword() {
/>
<BaseIcon
v-if="form.loading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -1,7 +1,7 @@
<script setup>
import { reactive, onMounted, watch, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiAccountTieHat } from "@mdi/js";
import { PhUserGear } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -141,7 +141,6 @@ async function updateProfile() {
:game="gameID"
:version="versionForm.currentVersion"
:profile="myProfile"
use-small
>
</ProfileCard>
</div>
@ -149,7 +148,7 @@ async function updateProfile() {
<SectionTitleLine
v-if="versionForm.currentVersion"
:icon="mdiAccountTieHat"
:icon="PhUserGear"
title="Profile Customizations"
main
/>

View File

@ -1,7 +1,7 @@
<script setup>
import { reactive, ref, onMounted, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiAccountMultiple } from "@mdi/js";
import { PhUsersThree } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import GameHeader from "@/components/Cards/GameHeader.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -173,13 +173,12 @@ const navigateToProfile = (item) => {
:game="gameID"
:version="versionForm.currentVersion"
:profile="myProfile"
use-small
>
</ProfileCard>
</div>
</GameHeader>
<SectionTitleLine :icon="mdiAccountMultiple" title="All Players" main />
<SectionTitleLine :icon="PhUsersThree" title="All Players" main />
<CardBox has-table>
<div
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, onMounted, reactive, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiFormatListNumbered } from "@mdi/js";
import { PhRanking } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -64,7 +64,7 @@ const filteredSongs = computed(() => {
<LayoutAuthenticated>
<SectionMain v-if="songData">
<GameHeader :game="thisGame" />
<SectionTitleLine :icon="mdiFormatListNumbered" title="Top Records" main>
<SectionTitleLine :icon="PhRanking" title="Top Records" main>
<template v-if="thisGame.versions">
<div class="md:w-1/3 md:text-right">
<h2 class="text-md sm:text-lg md:text-xl font-bold p-2">

View File

@ -2,7 +2,7 @@
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useMainStore } from "@/stores/main";
import { mdiCounter } from "@mdi/js";
import { PhMedal } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -166,7 +166,7 @@ const navigateToSong = (item) => {
<GameHeader :game="thisGame" />
<SectionTitleLine
:icon="mdiCounter"
:icon="PhMedal"
:title="`All ${
thisGame.shortName ? thisGame.shortName : thisGame.name
} Scores`"

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, onMounted, reactive, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiFormatListNumbered } from "@mdi/js";
import { PhRanking, PhUser, PhMedal } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -89,16 +89,16 @@ const filteredSongs = computed(() => {
<SectionMain v-if="songData">
<template v-if="myProfile">
<GameHeader :game="thisGame" :profile="myProfile" />
<div class="flex gap-2 mb-2 md:mb-0">
<div class="flex gap-2 mb-6 md:mb-0">
<BaseButton
:icon="mdiAccountDetails"
:icon="PhUser"
:href="`/#/games/${thisGame.id}/profiles/${myProfile.userId}`"
:outline="false"
color="info"
:label="`${myProfile.username}'s Profile`"
/>
<BaseButton
:icon="mdiAccountDetails"
:icon="PhMedal"
:href="`/#/games/${thisGame.id}/scores/${myProfile.userId}`"
:outline="false"
color="info"
@ -106,7 +106,7 @@ const filteredSongs = computed(() => {
/>
</div>
<SectionTitleLine
:icon="mdiFormatListNumbered"
:icon="PhRanking"
:title="`${myProfile.username}'s ${
thisGame.shortName ? thisGame.shortName : thisGame.name
} Records`"

View File

@ -2,7 +2,7 @@
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useMainStore } from "@/stores/main";
import { mdiCounter, mdiAccountDetails } from "@mdi/js";
import { PhRanking, PhUser, PhMedal } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -182,21 +182,29 @@ const navigateToSong = (item) => {
<SectionMain>
<template v-if="myProfile">
<GameHeader :game="thisGame" :profile="myProfile" />
<SectionTitleLine
:icon="mdiCounter"
:title="`${myProfile.username}'s ${
thisGame.shortName ? thisGame.shortName : thisGame.name
} Scores`"
main
>
<div class="flex gap-2 mb-6">
<BaseButton
:icon="mdiAccountDetails"
:icon="PhUser"
:href="`/#/games/${thisGame.id}/profiles/${myProfile.userId}`"
:outline="false"
color="info"
:label="`${myProfile.username}'s Profile`"
/>
</SectionTitleLine>
<BaseButton
:icon="PhRanking"
:href="`/#/games/${thisGame.id}/records/${myProfile.userId}`"
:outline="false"
color="info"
:label="`${myProfile.username}'s Records`"
/>
</div>
<SectionTitleLine
:icon="PhMedal"
:title="`${myProfile.username}'s ${
thisGame.shortName ? thisGame.shortName : thisGame.name
} Scores`"
main
/>
<CardBox has-table>
<GeneralTable

View File

@ -2,15 +2,15 @@
import { reactive, ref, onMounted, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
mdiAccountOutline,
mdiPlaylistMusicOutline,
mdiFormatListText,
mdiChartBarStacked,
mdiChartAreasplineVariant,
mdiChartTimeline,
mdiStickerOutline,
mdiChartDonutVariant,
} from "@mdi/js";
PhUser,
PhCalendarDots,
PhChartLine,
PhRanking,
PhMedal,
PhChartPieSlice,
PhSticker,
PhTarget,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBoxWidget from "@/components/CardBoxWidget.vue";
import CardBox from "@/components/CardBox.vue";
@ -359,7 +359,7 @@ async function generateTimeline(myProfile) {
<template v-if="myProfile">
<SectionMain>
<GameHeader :game="thisGame" />
<SectionTitleLine :icon="mdiAccountOutline" title="View Profile" main>
<SectionTitleLine :icon="PhUser" title="View Profile" main>
<div
v-if="thisGame.versions && myProfile"
class="mt-2 md:mt-0 md:w-1/3 md:text-right"
@ -391,18 +391,17 @@ async function generateTimeline(myProfile) {
>
<div class="w-full">
<ProfileCard
use-small
:game="gameID"
:version="versionForm.currentVersion"
:profile="myProfile"
>
<div
v-if="!thisGame.noScores"
class="md:w-1/3 grid grid-cols-1 md:grid-cols-2 gap-3"
class="md:w-1/3 grid grid-cols-1 md:grid-cols-2 gap-3 mt-4"
>
<BaseButton
:href="`/#/games/${gameID}/scores/${myProfile.userId}`"
:icon="mdiPlaylistMusicOutline"
:icon="PhMedal"
class="w-full md:w-auto"
color="info"
label="View Scores"
@ -410,7 +409,7 @@ async function generateTimeline(myProfile) {
<BaseButton
:href="`/#/games/${gameID}/records/${myProfile.userId}`"
:icon="mdiFormatListText"
:icon="PhRanking"
class="w-full md:w-auto"
color="info"
label="View Records"
@ -525,11 +524,7 @@ async function generateTimeline(myProfile) {
myProfile.battle_data
"
>
<SectionTitleLine
:icon="mdiChartAreasplineVariant"
title="Stats"
main
/>
<SectionTitleLine :icon="PhChartLine" title="Stats" main />
<div
class="my-6 grid grid-cols-2 md:grid-cols-5 xl:grid-cols-6 gap-6"
>
@ -671,11 +666,10 @@ async function generateTimeline(myProfile) {
<template v-if="myProfile.jubility">
<SectionTitleLine
:icon="mdiChartBarStacked"
:icon="PhChartPieSlice"
title="Jubility Breakdown"
main
/>
<div class="my-6 grid grid-cols-1 md:grid-cols-2 gap-6">
<CardBox v-if="myProfile.pick_up_breakdown">
<PillTag label="Pick-Up" color="info" />
@ -697,12 +691,7 @@ async function generateTimeline(myProfile) {
</template>
<template v-if="myProfile?.trbitem">
<SectionTitleLine
:icon="mdiStickerOutline"
title="Sticker Board"
main
/>
<SectionTitleLine :icon="PhSticker" title="Sticker Board" main />
<div class="my-6">
<CardBox class="w-full grid place-content-center">
<UserSticker
@ -720,11 +709,7 @@ async function generateTimeline(myProfile) {
versionForm.currentVersion >= 29
"
>
<SectionTitleLine
:icon="mdiChartDonutVariant"
title="Notes Radar"
main
/>
<SectionTitleLine :icon="PhTarget" title="Notes Radar" main />
<UserNotesRadar
:game="thisGame.id"
:version="versionForm.currentVersion"
@ -733,8 +718,7 @@ async function generateTimeline(myProfile) {
</template>
<template v-if="myProfile?.timeline">
<SectionTitleLine :icon="mdiChartTimeline" title="Timeline" main />
<SectionTitleLine :icon="PhCalendarDots" title="Timeline" main />
<div class="my-6">
<CardBox>
<ol

View File

@ -1,170 +0,0 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
mdiAccountMultiple,
mdiPlaylistMusicOutline,
mdiFormatListText,
} from "@mdi/js";
import SectionMain from "@/components/SectionMain.vue";
import BaseButton from "@/components/BaseButton.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import GameTitleLine from "@/components/GameTitleLine.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import CardBox from "@/components/CardBox.vue";
import GeneralTable from "@/components/GeneralTable.vue";
import { APIGetAllProfiles } from "@/stores/api/profile";
import { getGameInfo } from "@/constants";
import { getCardStyle } from "@/constants/sources";
import { dashCode } from "@/constants/userData";
import { getIIDXDan } from "@/constants/danClass";
const $route = useRoute();
const $router = useRouter();
var gameID = null;
var thisGame = null;
gameID = $route.params.id;
thisGame = getGameInfo(gameID);
if (thisGame == null) {
$router.push({
name: "ErrorPage",
params: {
catchAll: "404",
},
});
}
const profiles = ref([]);
onMounted(async () => {
try {
const data = await APIGetAllProfiles(gameID);
profiles.value = formatProfiles(data);
} catch (error) {
console.error("Failed to fetch profile data:", error);
}
});
const headers = [];
headers.push({
text: "Player",
value: "username",
sortable: true,
width: 120,
});
if (!thisGame.noRivals) {
headers.push({ text: "Rival ID", value: "extid", width: 100 });
}
headers.push(
{ text: "Last Play", value: "stats.last_play_timestamp", width: 150 },
{ text: "Last Arcade", value: "stats.last_play_arcade", width: 150 },
{ text: "Plays", value: "stats.total_plays", sortable: true, width: 50 },
);
if (thisGame.playerHeaders) {
for (var header of thisGame.playerHeaders) {
headers.push(header);
}
}
function formatProfiles(profiles) {
var formattedItems = [];
for (var item of profiles) {
if (item.extid) {
item.extid = dashCode(item.extid);
}
if (item.stats) {
if (item.stats.last_play_timestamp) {
const date = new Date(item.stats.last_play_timestamp * 1000);
item.stats.last_play_timestamp = date.toLocaleString();
}
if (item.sgrade) {
item.sgrade = getIIDXDan(item.sgrade).short;
}
if (item.dgrade) {
item.dgrade = getIIDXDan(item.dgrade).short;
}
}
formattedItems.push(item);
}
formattedItems.sort((a, b) => {
const totalPlaysA = a.stats ? a.stats.total_plays || 0 : 0;
const totalPlaysB = b.stats ? b.stats.total_plays || 0 : 0;
return totalPlaysB - totalPlaysA; // Sort in descending order
});
return formattedItems;
}
const navigateToProfile = (item) => {
const userID = item.userId;
$router.push(`/games/${gameID}/profiles/${userID}`);
};
</script>
<template>
<LayoutAuthenticated>
<SectionMain>
<div
:style="getCardStyle(thisGame)"
class="rounded-2xl mb-6 card-container"
>
<div
class="bg-white dark:bg-slate-900/90 rounded-2xl pt-6 p-3 card-content"
>
<div class="w-full">
<div
class="md:flex md:px-5 md:space-x-10 md:justify-between md:items-center"
>
<GameTitleLine :path="thisGame.icon" :title="thisGame.name" />
</div>
</div>
<div class="md:w-1/2 grid grid-cols-1 md:grid-cols-2 gap-3">
<BaseButton
v-if="!thisGame.noScores"
:href="`/#/games/${gameID}/scores`"
:icon="mdiPlaylistMusicOutline"
:outline="false"
color="info"
label="Network Scores"
/>
<BaseButton
v-if="!thisGame.noRecords"
:href="`/#/games/${gameID}/records`"
:icon="mdiFormatListText"
:outline="false"
color="info"
label="Network Records"
/>
</div>
</div>
</div>
<SectionTitleLine :icon="mdiAccountMultiple" title="All Players" 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="headers"
:items="profiles"
:path="`/#/${gameID}/profiles/`"
@row-clicked="navigateToProfile"
/>
</div>
</div>
</CardBox>
</SectionMain>
</LayoutAuthenticated>
</template>

View File

@ -1,7 +1,7 @@
<script setup>
import { reactive, watch, ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiSwordCross, mdiPlus } from "@mdi/js";
import { PhSword, PhPlusCircle } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -133,13 +133,12 @@ function filterProfiles() {
:game="gameID"
:version="versionForm.currentVersion"
:profile="profile"
use-small
>
</ProfileCard>
</div>
</GameHeader>
<SectionTitleLine :icon="mdiPlus" title="Add a Rival" main />
<SectionTitleLine :icon="PhPlusCircle" title="Add a Rival" main />
<CardBox v-if="versionForm.currentVersion" class="mb-6">
<FormField
label="Search"
@ -180,7 +179,7 @@ function filterProfiles() {
</div>
</CardBox>
<SectionTitleLine :icon="mdiSwordCross" title="Rivals" main />
<SectionTitleLine :icon="PhSword" title="Rivals" main />
<CardBox v-if="versionForm.currentVersion" class="mb-6">
<div class="grid gap-3">
<CardBox v-for="rival of profile?.rivals" :key="rival.id">

View File

@ -1,11 +1,7 @@
<script setup>
import { reactive, ref, onMounted, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
mdiPlaylistMusic,
mdiFormatListBulleted,
mdiFormatListNumbered,
} from "@mdi/js";
import { PhMusicNote, PhRanking, PhMedal } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -185,7 +181,7 @@ const navigateToProfile = (item) => {
<LayoutAuthenticated>
<SectionMain v-if="songData">
<GameHeader :game="thisGame" />
<SectionTitleLine :icon="mdiPlaylistMusic" title="Song Overview" main />
<SectionTitleLine :icon="PhMusicNote" title="Song Overview" main />
<CardBox class="mb-6" has-table>
<div class="grid gap-4 bg-slate-900/90 card-content">
<div>
@ -208,11 +204,7 @@ const navigateToProfile = (item) => {
</div>
</CardBox>
<SectionTitleLine
:icon="mdiFormatListNumbered"
title="Top Records"
main
/>
<SectionTitleLine :icon="PhRanking" title="Top Records" main />
<div class="grid grid-cols-2 lg:grid-cols-5 gap-4 mb-6">
<template v-for="chart of songData.charts" :key="chart.db_id">
<CardBoxWidget
@ -231,11 +223,7 @@ const navigateToProfile = (item) => {
</div>
<div class="flex place-content-between mb-2">
<SectionTitleLine
:icon="mdiFormatListBulleted"
title="All Scores"
main
/>
<SectionTitleLine :icon="PhMedal" title="All Scores" main />
<div class="md:w-1/3 md:text-right">
<h2 class="text-md sm:text-lg md:text-xl font-bold p-2">
Select Chart

View File

@ -1,14 +1,14 @@
<script setup>
import { computed, ref, onMounted } from "vue";
import {
mdiGamepad,
mdiNewspaperVariant,
mdiChartTimelineVariant,
mdiCounter,
mdiGamepadOutline,
mdiFire,
mdiTrendingUp,
} from "@mdi/js";
PhJoystick,
PhNewspaper,
PhTrendUp,
PhPlay,
PhFire,
PhMedal,
PhRanking,
} from "@phosphor-icons/vue";
import UserCard from "@/components/UserCard.vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
@ -159,56 +159,56 @@ const todayPlays = computed(() => {
const cardBoxes = ref([
{
label: "Cumulative Plays",
icon: mdiCounter,
icon: PhPlay,
iconColor: "text-emerald-600",
suffix: "play",
number: cumulativePlays,
},
{
label: "Games Played",
icon: mdiGamepadOutline,
icon: PhJoystick,
iconColor: "text-sky-300",
suffix: "game",
number: uniqueProfiles,
},
{
label: "Plays Today",
icon: mdiCounter,
icon: PhPlay,
iconColor: "text-sky-300",
suffix: "play",
number: todayPlays,
},
{
label: "Longest Play Streak",
icon: mdiFire,
icon: PhFire,
iconColor: "text-red-500",
suffix: "play",
number: longestStreak,
},
{
label: "Total Records",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhRanking,
iconColor: "text-amber-400",
suffix: "record",
number: totalRecords,
},
{
label: "Total Attempts",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhMedal,
iconColor: "text-pink-300",
suffix: "attempt",
number: totalAttempts,
},
{
label: "Records Today",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhRanking,
iconColor: "text-amber-400",
suffix: "record",
number: todayRecords,
},
{
label: "Attempts Today",
icon: mdiGamepadOutline,
icon: PhMedal,
iconColor: "text-sky-300",
suffix: "attempt",
number: todayAttempts,
@ -221,8 +221,7 @@ const cardBoxes = ref([
<SectionMain>
<UserCard class="mb-6 mt-2 shadow-xl" />
<SectionTitleLine :icon="mdiNewspaperVariant" title="Network News" main />
<SectionTitleLine :icon="PhNewspaper" title="Network News" main />
<div
v-if="newsData.length"
class="grid gap-4 grid-cols-1 xl:grid-cols-2 w-full pb-4"
@ -245,11 +244,7 @@ const cardBoxes = ref([
</div>
<CardBoxComponentEmpty v-if="!newsData || !newsData.length" />
<SectionTitleLine
:icon="mdiChartTimelineVariant"
title="Quick Stats"
main
/>
<SectionTitleLine :icon="PhTrendUp" title="Quick Stats" main />
<div
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
>
@ -265,7 +260,7 @@ const cardBoxes = ref([
</template>
</div>
<SectionTitleLine :icon="mdiGamepad" title="Showcase" main />
<SectionTitleLine :icon="PhJoystick" title="Showcase" main />
<div
class="grid grid-flow-row auto-rows-auto grid-cols-2 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-3 3xl:grid-cols-4 4xl:grid-cols-6 gap-5 mb-5"
>
@ -279,7 +274,7 @@ const cardBoxes = ref([
/>
</div>
<SectionTitleLine :icon="mdiTrendingUp" title="Play Trends" main />
<SectionTitleLine :icon="PhTrendUp" title="Play Trends" main />
<CardBox class="mb-6">
<div v-if="userProfiles">
<LineChart

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { mdiNewspaperVariant } from "@mdi/js";
import { PhNewspaper } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
@ -29,7 +29,7 @@ function humanReadableTime(timestamp) {
<template>
<LayoutAuthenticated>
<SectionMain>
<SectionTitleLine :icon="mdiNewspaperVariant" title="Network News" main />
<SectionTitleLine :icon="PhNewspaper" title="Network News" main />
<div class="grid gap-4 grid-cols-1 w-full">
<CardBoxNews

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiBackburger } from "@mdi/js";
import { PhCaretDoubleLeft } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import CardBox from "@/components/CardBox.vue";
@ -71,7 +71,7 @@ function goBack() {
<BaseButton
label="Home"
color="info"
:icon="mdiBackburger"
:icon="PhCaretDoubleLeft"
icon-size="20"
@click="goBack"
/>

View File

@ -1,10 +1,11 @@
<script setup>
import { ref, onMounted, reactive } from "vue";
import {
mdiCardAccountDetailsOutline,
mdiCreditCardPlusOutline,
mdiCreditCardEditOutline,
} from "@mdi/js";
PhIdentificationCard,
PhPlusCircle,
PhCreditCard,
PhSpinnerBall,
} from "@phosphor-icons/vue";
import { dashCode } from "@/constants/userData.js";
import SectionMain from "@/components/SectionMain.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -86,11 +87,7 @@ const copyToClipboard = (text) => {
<SectionMain>
<UserCard class="mb-6" use-small even-smaller />
<SectionTitleLine
:icon="mdiCreditCardPlusOutline"
title="Add a Card"
main
/>
<SectionTitleLine :icon="PhPlusCircle" title="Add a Card" main />
<CardBox is-form class="row-span-2 mb-6" @submit.prevent="submitCard()">
<FormField
label="Card ID"
@ -98,7 +95,7 @@ const copyToClipboard = (text) => {
>
<FormControl
v-model="cardForm.newCard"
:icon="mdiCreditCardEditOutline"
:icon="PhCreditCard"
name="cardId"
type="card"
placeholder="XXXX-XXXX-XXXX-XXXX"
@ -113,7 +110,7 @@ const copyToClipboard = (text) => {
<BaseButton type="submit" color="success" label="Add" />
<BaseIcon
v-if="cardLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -122,7 +119,7 @@ const copyToClipboard = (text) => {
<div class="grid md:grid-cols-2 mb-2">
<SectionTitleLine
:icon="mdiCardAccountDetailsOutline"
:icon="PhIdentificationCard"
title="Login Cards"
main
/>

View File

@ -1,13 +1,13 @@
<script setup>
import { ref, reactive } from "vue";
import {
mdiAccountArrowLeftOutline,
mdiCreditCardEditOutline,
mdiAsterisk,
mdiCardAccountDetails,
mdiLoading,
mdiAccountConvertOutline,
} from "@mdi/js";
PhUserCircleDashed,
PhCreditCard,
PhPassword,
PhUserSwitch,
PhSpinnerBall,
PhUnite,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
@ -132,11 +132,7 @@ async function saveSettings() {
<UserCard class="mb-6" use-small even-smaller />
<template v-if="!takeoverData">
<SectionTitleLine
:icon="mdiAccountArrowLeftOutline"
title="Claim a Profile"
main
/>
<SectionTitleLine :icon="PhUserSwitch" title="Claim a Profile" main />
<CardBox is-form class="row-span-2 mb-6" @submit.prevent="submitCard()">
<h2 class="text-xl mb-6 lg:w-1/2">
If you registered a card in game and need to claim it, you may do so
@ -149,7 +145,7 @@ async function saveSettings() {
>
<FormControl
v-model="profileForm.cardId"
:icon="mdiCreditCardEditOutline"
:icon="PhCreditCard"
name="cardId"
type="card"
placeholder="XXXX-XXXX-XXXX-XXXX"
@ -163,7 +159,7 @@ async function saveSettings() {
<FormField label="PIN" help="Used when logging into a game">
<FormControl
v-model="profileForm.pin"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
name="pin"
:minlength="4"
@ -179,7 +175,7 @@ async function saveSettings() {
<BaseButton type="submit" color="success" label="Start Claim" />
<BaseIcon
v-if="cardLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -189,7 +185,7 @@ async function saveSettings() {
<div v-if="takeoverData && saveState == null">
<SectionTitleLine
:icon="mdiAccountArrowLeftOutline"
:icon="PhUserCircleDashed"
title="Select Profiles to Merge"
main
/>
@ -227,7 +223,7 @@ async function saveSettings() {
<FormCheckRadio
v-model="mergeSettings.card"
:input-value="mergeSettings.card"
:icon="mdiCardAccountDetails"
:icon="PhCreditCard"
type="switch"
name="card"
/>
@ -245,12 +241,7 @@ async function saveSettings() {
</div>
<div v-if="saveState == true">
<SectionTitleLine
:icon="mdiAccountConvertOutline"
title="Merge Results"
main
/>
<SectionTitleLine :icon="PhUnite" title="Merge Results" main />
<CardBox>
<h2 class="text-xl mb-6">
Successfully merged {{ recordCount }} records. Yippie!
@ -263,12 +254,7 @@ async function saveSettings() {
</div>
<div v-if="saveState == false">
<SectionTitleLine
:icon="mdiAccountConvertOutline"
title="Merge Results"
main
/>
<SectionTitleLine :icon="PhUnite" title="Merge Results" main />
<CardBox>
<h2 class="text-xl mb-6"> Failed to merge. Please try again.</h2>

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { mdiFileUploadOutline } from "@mdi/js";
import { PhFileImage } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import GameIcon from "@/components/GameIcon.vue";
@ -57,11 +57,7 @@ function filterContent(data) {
<UserCard class="mb-6" use-small even-smaller />
<template v-if="contentData">
<SectionTitleLine
:icon="mdiFileUploadOutline"
title="Your Uploads"
main
/>
<SectionTitleLine :icon="PhFileImage" title="Your Uploads" main />
<CardBox
v-for="content of contentData"
:key="content.timestamp"

View File

@ -2,7 +2,7 @@
import { useMainStore } from "@/stores/main";
import { useRouter } from "vue-router";
import { ref, watch } from "vue";
import { mdiAccountTieHat } from "@mdi/js";
import { PhPaintBrushBroad } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
@ -63,7 +63,7 @@ async function revert() {
<SectionMain>
<UserCard class="mb-6" />
<SectionTitleLine
:icon="mdiAccountTieHat"
:icon="PhPaintBrushBroad"
title="Profile Customizations"
main
/>

View File

@ -1,5 +1,5 @@
<script setup>
import { mdiFlagCheckered, mdiFlagOff, mdiFlagOutline } from "@mdi/js";
import { PhFlag, PhFlagBannerFold, PhFlagCheckered } from "@phosphor-icons/vue";
import { reactive } from "vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
@ -94,7 +94,7 @@ const setGoals = [
<template>
<LayoutAuthenticated>
<SectionMain>
<SectionTitleLine :icon="mdiFlagOutline" title="Your Goals" main />
<SectionTitleLine :icon="PhFlag" title="Your Goals" main />
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 mb-6">
<CardBoxWidget
@ -114,14 +114,14 @@ const setGoals = [
/>
</div>
<SectionTitleLine :icon="mdiFlagCheckered" title="Active Goals" />
<SectionTitleLine :icon="PhFlagCheckered" title="Active Goals" />
<div class="mb-6">
<CardBox has-table>
<TableGoals :goals="setGoals" />
</CardBox>
</div>
<SectionTitleLine :icon="mdiFlagOff" title="Past Goals" />
<SectionTitleLine :icon="PhFlagBannerFold" title="Past Goals" />
<div class="mb-12">
<CardBox has-table>
<TableGoals :goals="setGoals" is-past />

View File

@ -2,7 +2,7 @@
import { useMainStore } from "@/stores/main";
import { ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { mdiServerNetwork, mdiMessage, mdiScoreboard } from "@mdi/js";
import { PhCloud, PhDiscordLogo, PhRanking } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -42,7 +42,7 @@ const services = [
{
id: "discord",
name: "Discord",
icon: mdiMessage,
icon: PhDiscordLogo,
oAuth: DISCORD_OAUTH_URL,
description:
"Linking your Discord account to PhaseII gives you an avatar, enables push notifications, and more via our Discord bot, BadManiac.\n\nPhaseII *does not* use Discord's API aside from the initial linkage. All data PhaseII uses is sent via our bot.\n\nNo data aside from your avatar, User ID, and name is saved.\n\nWe do not save a token nor a cookie.",
@ -50,7 +50,7 @@ const services = [
{
id: "tachi",
name: "Kamaitachi",
icon: mdiScoreboard,
icon: PhRanking,
oAuth: TACHI_OAUTH_URL,
description:
"Kamaitachi is an open source score tracker using standardized schemas.\n\nLinking Kamaitachi to PhaseII will enable the uploading of all supported scores to Kamaitachi directly, with no user intervention.\n\nYou must follow all rules of Kamaitachi. Failure to adhere to rules will not only put your Kamaitachi account at risk, but also your PhaseII account.\n\nCurrently supported games:\n- DDR\n- IIDX\n- SDVX\n- pop'n (some versions)\n- jubeat",
@ -91,12 +91,8 @@ async function integrateWith(service, code) {
<LayoutAuthenticated>
<SectionMain>
<UserCard class="mb-6" use-small even-smaller />
<SectionTitleLine
:icon="mdiServerNetwork"
title="Server Integrations"
main
/>
<SectionTitleLine :icon="PhCloud" title="Service Integrations" main />
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<CardBox v-for="service of services" :key="service.id">
<PillTag

View File

@ -7,7 +7,7 @@ import {
APIGetUserSessions,
APIDeleteUserSessions,
} from "@/stores/api/account";
import { mdiAccount, mdiMail, mdiAsterisk, mdiLoading } from "@mdi/js";
import { PhUser, PhAt, PhPassword, PhSpinnerBall } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import BaseDivider from "@/components/BaseDivider.vue";
@ -137,7 +137,7 @@ function userChanged(oldProfile, newProfile) {
<LayoutAuthenticated>
<SectionMain>
<UserCard class="mb-6" use-small even-smaller />
<SectionTitleLine :icon="mdiAccount" title="Profile Settings" main />
<SectionTitleLine :icon="PhUser" title="Profile Settings" main />
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<CardBox is-form class="row-span-1" @submit.prevent="submitProfile">
@ -145,7 +145,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="Username">
<FormControl
v-model="profileForm.username"
:icon="mdiAccount"
:icon="PhUser"
name="username"
required
autocomplete="username"
@ -154,7 +154,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="E-mail" help="Used for password resetting and 2FA">
<FormControl
v-model="profileForm.email"
:icon="mdiMail"
:icon="PhAt"
type="email"
name="email"
required
@ -165,7 +165,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="PIN" help="Used when logging into a game">
<FormControl
v-model="profileForm.pin"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
name="pin"
:minlength="4"
@ -199,7 +199,7 @@ function userChanged(oldProfile, newProfile) {
/>
<BaseIcon
v-if="profileLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>
@ -211,7 +211,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="Current Password">
<FormControl
v-model="passwordForm.currentPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="currentPassword"
type="password"
required
@ -222,7 +222,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="New Password">
<FormControl
v-model="passwordForm.newPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="newPassword"
type="password"
required
@ -232,7 +232,7 @@ function userChanged(oldProfile, newProfile) {
<FormField label="Confirm Password">
<FormControl
v-model="passwordForm.confirmPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="confirmPassword"
type="password"
required
@ -243,7 +243,7 @@ function userChanged(oldProfile, newProfile) {
<BaseButton type="submit" color="success" label="Update" />
<BaseIcon
v-if="passwordLoading"
:path="mdiLoading"
:icon="PhSpinnerBall"
color="text-yellow-500"
class="animate animate-spin"
/>

View File

@ -2,18 +2,18 @@
import { computed, ref, reactive, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
mdiAccount,
mdiGamepad,
mdiChartTimelineVariant,
mdiCounter,
mdiGamepadOutline,
mdiFire,
mdiTrendingUp,
mdiShieldEditOutline,
mdiInformationOutline,
mdiMail,
mdiAsterisk,
} from "@mdi/js";
PhUser,
PhJoystick,
PhTrendUp,
PhPlay,
PhFire,
PhMedal,
PhRanking,
PhUserGear,
PhInfo,
PhAt,
PhPassword,
} from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import CardBoxWidget from "@/components/CardBoxWidget.vue";
@ -183,56 +183,56 @@ const todayPlays = computed(() => {
const cardBoxes = ref([
{
label: "Cumulative Plays",
icon: mdiCounter,
icon: PhPlay,
iconColor: "text-emerald-600",
suffix: "play",
number: cumulativePlays,
},
{
label: "Games Played",
icon: mdiGamepadOutline,
icon: PhJoystick,
iconColor: "text-sky-300",
suffix: "game",
number: uniqueProfiles,
},
{
label: "Plays Today",
icon: mdiCounter,
icon: PhPlay,
iconColor: "text-sky-300",
suffix: "play",
number: todayPlays,
},
{
label: "Longest Play Streak",
icon: mdiFire,
icon: PhFire,
iconColor: "text-red-500",
suffix: "play",
number: longestStreak,
},
{
label: "Total Records",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhRanking,
iconColor: "text-amber-400",
suffix: "record",
number: totalRecords,
},
{
label: "Total Attempts",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhMedal,
iconColor: "text-pink-300",
suffix: "attempt",
number: totalAttempts,
},
{
label: "Records Today",
icon: mdiGamepadOutline,
iconColor: "text-sky-300",
icon: PhRanking,
iconColor: "text-amber-400",
suffix: "record",
number: todayRecords,
},
{
label: "Attempts Today",
icon: mdiGamepadOutline,
icon: PhMedal,
iconColor: "text-sky-300",
suffix: "attempt",
number: todayAttempts,
@ -284,7 +284,7 @@ const openArcade = (item) => {
<SectionMain>
<template v-if="userProfile !== null">
<SectionTitleLine
:icon="mdiAccount"
:icon="PhUser"
:title="`${userProfile.name}'s Profile`"
main
/>
@ -292,7 +292,7 @@ const openArcade = (item) => {
<template v-if="mainStore.userAdmin">
<SectionTitleLine
:icon="mdiShieldEditOutline"
:icon="PhUserGear"
title="User Administration"
main
/>
@ -301,7 +301,7 @@ const openArcade = (item) => {
<PillTag
color="info"
label="General Information"
:icon="mdiInformationOutline"
:icon="PhInfo"
class="mb-2"
/>
<div class="grid md:grid-cols-2 gap-x-4 mb-6">
@ -320,7 +320,7 @@ const openArcade = (item) => {
>
<FormControl
v-model="newUser.email"
:icon="mdiMail"
:icon="PhAt"
type="email"
name="email"
required
@ -330,7 +330,7 @@ const openArcade = (item) => {
<FormField label="PIN" help="Used when logging into a game">
<FormControl
v-model="newUser.pin"
:icon="mdiAsterisk"
:icon="PhPassword"
type="password"
name="pin"
:minlength="4"
@ -385,7 +385,7 @@ const openArcade = (item) => {
<FormField label="New Password">
<FormControl
v-model="passwordForm.newPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="newPassword"
type="password"
required
@ -395,7 +395,7 @@ const openArcade = (item) => {
<FormField label="Confirm Password">
<FormControl
v-model="passwordForm.confirmPassword"
:icon="mdiAsterisk"
:icon="PhPassword"
name="confirmPassword"
type="password"
required
@ -414,7 +414,9 @@ const openArcade = (item) => {
:key="arcade.id"
class="bg-slate-800 p-4 rounded-xl"
>
<div class="md:flex w-full place-content-between">
<div
class="grid grid-cols-1 gap-4 xl:flex w-full place-content-between"
>
<div>
<h1 class="text-md md:text-lg">{{ arcade.name }}</h1>
</div>
@ -433,11 +435,7 @@ const openArcade = (item) => {
</div>
</template>
<SectionTitleLine
:icon="mdiChartTimelineVariant"
title="Quick Stats"
main
/>
<SectionTitleLine :icon="PhTrendUp" title="Quick Stats" main />
<div
class="grid grid-cols-2 sm:grid-cols-3 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5 mb-6"
>
@ -453,7 +451,7 @@ const openArcade = (item) => {
</template>
</div>
<SectionTitleLine :icon="mdiGamepad" title="Showcase" main />
<SectionTitleLine :icon="PhJoystick" title="Showcase" main />
<div
class="grid grid-flow-row auto-rows-auto grid-cols-2 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-3 3xl:grid-cols-4 4xl:grid-cols-6 gap-5 mb-5"
>
@ -468,7 +466,7 @@ const openArcade = (item) => {
/>
</div>
<SectionTitleLine :icon="mdiTrendingUp" title="Play Trends" main />
<SectionTitleLine :icon="PhTrendUp" title="Play Trends" main />
<CardBox class="mb-6">
<div v-if="userProfiles">
<LineChart

View File

@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from "vue";
import { mdiVideoOutline, mdiVideoWirelessOutline } from "@mdi/js";
import { PhFilmReel, PhVideo } from "@phosphor-icons/vue";
import SectionMain from "@/components/SectionMain.vue";
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
@ -140,11 +140,7 @@ function openInNewTab(url) {
<UserCard class="mb-6" use-small even-smaller />
<template v-if="videoData[0]">
<SectionTitleLine
:icon="mdiVideoWirelessOutline"
title="Your Latest Upload"
main
/>
<SectionTitleLine :icon="PhVideo" title="Your Latest Video" main />
<CardBox class="mb-6">
<div
v-if="videoData[0]?.data?.status == 'uploaded'"
@ -201,8 +197,7 @@ function openInNewTab(url) {
</CardBox>
</template>
<SectionTitleLine :icon="mdiVideoOutline" title="All Play Videos" main />
<SectionTitleLine :icon="PhFilmReel" title="All Play Videos" main />
<CardBox has-table>
<div
class="bg-white dark:bg-slate-900/95 rounded-2xl lg:flex lg:justify-between"