Compare commits
2 Commits
d7bae268da
...
457d2a5161
| Author | SHA1 | Date | |
|---|---|---|---|
| 457d2a5161 | |||
| 1358aaa705 |
+2
-2
@@ -367,12 +367,12 @@ components:
|
||||
RuleResponse:
|
||||
type: object
|
||||
properties:
|
||||
rules:
|
||||
clauses:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/RuleClauseResponse"
|
||||
required:
|
||||
- rules
|
||||
- clauses
|
||||
UpdateRuleBookRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
+2
-1
@@ -2,11 +2,12 @@
|
||||
import { computed } from 'vue';
|
||||
import { RouterView, useRoute } from 'vue-router';
|
||||
import { Sidebar } from './sidebar';
|
||||
import { routeNames } from '@/routes';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const hideSidebar = computed(() => {
|
||||
return route.name === 'callback' || route.name === 'about';
|
||||
return route.name === routeNames.callback || route.name === routeNames.about;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,22 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/vue/24/outline';
|
||||
import {vOnClickOutside} from '@vueuse/components';
|
||||
import {useEventListener} from '@vueuse/core';
|
||||
import {ref} from 'vue';
|
||||
|
||||
interface Props {
|
||||
inline?: boolean;
|
||||
autoClose: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
inline: false,
|
||||
autoClose: true
|
||||
})
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
const doAutoClose = () => {
|
||||
if (props.autoClose) {
|
||||
isOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
useEventListener('keyup', e => {
|
||||
if (e.key === 'Escape') {
|
||||
isOpen.value = false;
|
||||
doAutoClose();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="() => isOpen = false">
|
||||
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="doAutoClose">
|
||||
<button @click="isOpen = !isOpen">
|
||||
<slot name="button" />
|
||||
<Transition
|
||||
enter-active-class="transition-transform"
|
||||
enter-from-class="rotate-180"
|
||||
@@ -25,6 +40,7 @@ useEventListener('keyup', e => {
|
||||
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
||||
<ChevronUpIcon v-else class="chevron" />
|
||||
</Transition>
|
||||
<slot name="button" />
|
||||
</button>
|
||||
|
||||
<Transition
|
||||
@@ -32,7 +48,10 @@ useEventListener('keyup', e => {
|
||||
enter-from-class="opacity-0"
|
||||
leave-from-class="transition-opacity"
|
||||
leave-to-class="opacity-0">
|
||||
<div v-if="isOpen" class="relative">
|
||||
<div v-if="inline && isOpen">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-else-if="isOpen" class="relative">
|
||||
<div class="z-10 divide-y rounded-b-md absolute">
|
||||
<slot />
|
||||
</div>
|
||||
@@ -45,6 +64,6 @@ useEventListener('keyup', e => {
|
||||
@reference "tailwindcss";
|
||||
|
||||
.chevron {
|
||||
@apply w-4 h-4 ms-1;
|
||||
@apply w-4 h-4 me-1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -110,7 +110,7 @@ export const RuleClauseResponseRateEnum = {
|
||||
export type RuleClauseResponseRateEnum = typeof RuleClauseResponseRateEnum[keyof typeof RuleClauseResponseRateEnum];
|
||||
|
||||
export interface RuleResponse {
|
||||
'rules': Array<RuleClauseResponse>;
|
||||
'clauses': Array<RuleClauseResponse>;
|
||||
}
|
||||
export interface SetCharacterRuleBookRequest {
|
||||
'ruleBookId': string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ClipboardButton } from '@/components';
|
||||
import { InformationCircleIcon } from '@heroicons/vue/24/outline';
|
||||
import { routeNames } from '@/routes';
|
||||
|
||||
|
||||
interface Props {
|
||||
@@ -21,7 +22,7 @@ withDefaults(defineProps<Props>(), {
|
||||
<img v-if="id" :src="`https://images.evetech.net/types/${id}/icon?size=32`" class="inline-block w-5 h-5 me-1 mt-1" alt="" />
|
||||
<template v-if="name">
|
||||
{{ name }}
|
||||
<RouterLink v-if="id" :to="{ name: 'market-types', params: { type: id } }" class="button btn-icon ms-1 me-1 mt-1" title="Show item info">
|
||||
<RouterLink v-if="id" :to="{ name: routeNames.marketTypes, params: { type: id } }" class="button btn-icon ms-1 me-1 mt-1" title="Show item info">
|
||||
<InformationCircleIcon />
|
||||
</RouterLink>
|
||||
<ClipboardButton v-if="!hideCopy" :value="name" />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import {RouterLink, RouterView} from 'vue-router';
|
||||
import {routeNames} from '@/routes';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<div class="flex border-b-2 border-emerald-500">
|
||||
<RouterLink :to="{name: 'market-types'}" class="tab">
|
||||
<RouterLink :to="{name: routeNames.marketTypes}" class="tab">
|
||||
<span>Item Info</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/market/tracking" class="tab">
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {RouterLink, RouterView} from "vue-router";
|
||||
import {routeNames} from '@/routes';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<div class="flex border-b-2 border-emerald-500">
|
||||
<RouterLink :to="{ name: 'list-rule-books' }" class="tab">
|
||||
<RouterLink :to="{ name: routeNames.listRuleBooks }" class="tab">
|
||||
<span>Rule Books</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/characters/rules" class="tab">
|
||||
|
||||
@@ -7,6 +7,7 @@ import {BookmarkIcon, BookmarkSlashIcon, ShoppingCartIcon} from '@heroicons/vue/
|
||||
import log from "loglevel";
|
||||
import {computed, ref, watch} from "vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import {routeNames} from "@/routes";
|
||||
import {computedAsync} from "@vueuse/core";
|
||||
|
||||
const buyModal = ref<typeof BuyModal>();
|
||||
@@ -52,7 +53,7 @@ const view = () => {
|
||||
}
|
||||
|
||||
router.push({
|
||||
name: 'market-types',
|
||||
name: routeNames.marketTypes,
|
||||
params: {
|
||||
type: inputItem.value.id
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import {useDebounceFn} from "@vueuse/core";
|
||||
import log from "loglevel";
|
||||
import {activityTypes, RuleInput, Rules, useRuleBooksStore} from "@/rules";
|
||||
import {PlusIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
||||
import {routeNames} from "@/routes";
|
||||
import {Dropdown} from "@/components";
|
||||
|
||||
const ruleBookId = ref<string>();
|
||||
const name = ref<string>('');
|
||||
@@ -21,7 +23,7 @@ const save = async () => {
|
||||
ledgerRefs: ledgerRefs.value,
|
||||
rules: rules.value
|
||||
})
|
||||
await router.push({ name: 'edit-rule-book', params: {ruleBookId: created.ruleBookId}})
|
||||
await router.push({ name: routeNames.editRuleBook, params: {ruleBookId: created.ruleBookId}})
|
||||
|
||||
} else {
|
||||
await update(ruleBookId.value, {
|
||||
@@ -88,8 +90,13 @@ watch(useRoute(), async route => {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col grow border-b-1" v-for="activityType in activityTypes" :key="activityType.key">
|
||||
<span>{{ activityType.name }}</span>
|
||||
<RuleInput :ledgerRefs="ledgerRefs" v-model="rules[activityType.key]" />
|
||||
<Dropdown :inline="true" :autoClose="false" class="rule-dropdown">
|
||||
<template #button>
|
||||
<span>{{ activityType.name }}</span>
|
||||
</template>
|
||||
<RuleInput :ledgerRefs="ledgerRefs" v-model="rules[activityType.key]" />
|
||||
</Dropdown>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 justify-end flex">
|
||||
@@ -98,4 +105,16 @@ watch(useRoute(), async route => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@reference "tailwindcss";
|
||||
|
||||
.rule-dropdown :deep(>button) {
|
||||
@apply bg-slate-800 hover:bg-slate-800 border-none flex items-center w-full;
|
||||
}
|
||||
|
||||
.rule-dropdown.dropdown-open :deep(>button) {
|
||||
@apply bg-slate-800 rounded-b-none;
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@
|
||||
import {storeToRefs} from "pinia";
|
||||
import {CharacterLabel, useCharactersStore} from "@/characters";
|
||||
import {PencilSquareIcon} from "@heroicons/vue/24/outline";
|
||||
import {routeNames} from "@/routes";
|
||||
|
||||
const {characters} = storeToRefs(useCharactersStore());
|
||||
|
||||
@@ -11,7 +12,7 @@ const {characters} = storeToRefs(useCharactersStore());
|
||||
<div class="grid mb-2 mt-4">
|
||||
<div v-for="character in characters" :key="character.characterId" class="flex items-center mb-2">
|
||||
<CharacterLabel class="flex grow" :character="character" />
|
||||
<RouterLink class="btn-icon ms-2" :to="{ name: 'character-rulebook', params: { characterId: character.characterId } }"><PencilSquareIcon /></RouterLink>
|
||||
<RouterLink class="btn-icon ms-2" :to="{ name: routeNames.characterRulebook, params: { characterId: character.characterId } }"><PencilSquareIcon /></RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,6 +2,7 @@
|
||||
import {storeToRefs} from "pinia";
|
||||
import {PencilSquareIcon, TrashIcon} from "@heroicons/vue/24/outline";
|
||||
import {useRuleBooksStore} from "@/rules";
|
||||
import {routeNames} from "@/routes";
|
||||
|
||||
const {ruleBooks} = storeToRefs(useRuleBooksStore());
|
||||
|
||||
@@ -10,11 +11,11 @@ const {ruleBooks} = storeToRefs(useRuleBooksStore());
|
||||
<template>
|
||||
<div class="grid mb-2 mt-4">
|
||||
<div class="flex justify-end border-b-1">
|
||||
<RouterLink class="button mb-2 ms-2" :to="{ name: 'new-rule-book'}">New Rule Book</RouterLink>
|
||||
<RouterLink class="button mb-2 ms-2" :to="{ name: routeNames.newRuleBook}">New Rule Book</RouterLink>
|
||||
</div>
|
||||
<div v-for="ruleBook in ruleBooks" :key="ruleBook.ruleBookId" class="flex items-center mt-2">
|
||||
<span class="flex grow me-2">{{ruleBook.name}}</span>
|
||||
<RouterLink class="btn-icon me-1" :to="{ name: 'edit-rule-book', params: { ruleBookId: ruleBook.ruleBookId } }"><PencilSquareIcon /></RouterLink>
|
||||
<RouterLink class="btn-icon me-1" :to="{ name: routeNames.editRuleBook, params: { ruleBookId: ruleBook.ruleBookId } }"><PencilSquareIcon /></RouterLink>
|
||||
<button class="btn-icon"><TrashIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+24
-11
@@ -1,8 +1,19 @@
|
||||
import {RouteRecordRaw} from 'vue-router';
|
||||
|
||||
export const routeNames = {
|
||||
home: 'home',
|
||||
callback: 'callback',
|
||||
listRuleBooks: 'list-rule-books',
|
||||
newRuleBook: 'new-rule-book',
|
||||
editRuleBook: 'edit-rule-book',
|
||||
characterRulebook: 'character-rulebook',
|
||||
marketTypes: 'market-types',
|
||||
about: 'about',
|
||||
} as const;
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{path: '/', name: 'home', component: () => import('@/pages/Index.vue')},
|
||||
{path: '/callback', name: 'callback', component: () => import('@/pages/Index.vue')},
|
||||
{path: '/', name: routeNames.home, component: () => import('@/pages/Index.vue')},
|
||||
{path: '/callback', name: routeNames.callback, component: () => import('@/pages/Index.vue')},
|
||||
|
||||
{path: '/ledgers', component: () => import('@/pages/Ledgers.vue'), children: [
|
||||
{path: '', component: () => import('@/pages/ledger/ListLedgers.vue')},
|
||||
@@ -11,17 +22,19 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: '/rules', component: () => import('@/pages/Rules.vue'), children: [
|
||||
{path: '', redirect: '/rule-books'},
|
||||
{path: '/rule-books', children: [
|
||||
{path: '', name: 'list-rule-books', component: () => import('./pages/rules/ListRuleBooks.vue')},
|
||||
{path: 'new', name: 'new-rule-book', component: () => import('@/pages/rules/EditRuleBook.vue')},
|
||||
{path: ':ruleBookId', name: 'edit-rule-book', component: () => import('@/pages/rules/EditRuleBook.vue')},
|
||||
]},
|
||||
{path: '/characters/rules', component: () => import('./pages/rules/ListCharacterRuleBooks.vue')},
|
||||
{path: '/characters/:characterId/rules', name: 'character-rulebook', component: () => import('@/pages/rules/EditCharacterRuleBook.vue')},
|
||||
{path: '', name: routeNames.listRuleBooks, component: () => import('@/pages/rules/ListRuleBooks.vue')},
|
||||
{path: 'new', name: routeNames.newRuleBook, component: () => import('@/pages/rules/EditRuleBook.vue')},
|
||||
{path: ':ruleBookId', name: routeNames.editRuleBook, component: () => import('@/pages/rules/EditRuleBook.vue')},
|
||||
]},
|
||||
{path: '/characters/rules', children: [
|
||||
{path: '', component: () => import('@/pages/rules/ListCharacterRuleBooks.vue')},
|
||||
{path: '/characters/:characterId/rules', name: routeNames.characterRulebook, component: () => import('@/pages/rules/EditCharacterRuleBook.vue')},
|
||||
]}
|
||||
]},
|
||||
|
||||
{path: '/market', component: () => import('@/pages/Market.vue'), children: [
|
||||
{path: '', redirect: '/market/types'},
|
||||
{path: 'types/:type?', name: 'market-types', component: () => import('@/pages/market/TypeInfo.vue')},
|
||||
{path: 'types/:type?', name: routeNames.marketTypes, component: () => import('@/pages/market/TypeInfo.vue')},
|
||||
{path: 'tracking', component: () => import('@/pages/market/Tracking.vue')},
|
||||
{path: 'acquisitions', component: () => import('@/pages/market/Acquisitions.vue')},
|
||||
]},
|
||||
@@ -31,5 +44,5 @@ export const routes: RouteRecordRaw[] = [
|
||||
{path: '/tools', component: () => import('@/pages/Tools.vue')},
|
||||
|
||||
{path: '/characters', component: () => import('@/pages/Characters.vue')},
|
||||
{path: '/about', name: 'about', component: () => import('@/pages/About.vue')},
|
||||
];
|
||||
{path: '/about', name: routeNames.about, component: () => import('@/pages/About.vue')},
|
||||
] as const;
|
||||
+23
-18
@@ -5,6 +5,7 @@ import RuleClauseInput from "@/rules/RuleClauseInput.vue";
|
||||
import {computed, useTemplateRef} from "vue";
|
||||
import {Bars4Icon, PlusIcon, TrashIcon} from '@heroicons/vue/24/outline';
|
||||
import {useSortable} from "@vueuse/integrations/useSortable";
|
||||
import {systemLedgerRef} from "@/ledger";
|
||||
|
||||
interface Props {
|
||||
ledgerRefs: string[];
|
||||
@@ -12,49 +13,54 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const rule = defineModel<RuleResponse>({default: {rules:{}}});
|
||||
const rules = computed<RuleClauseResponse[]>({
|
||||
get: () => rule.value && rule.value.rules ? rule.value.rules : [],
|
||||
set: value => rule.value = {rules: value}
|
||||
const rule = defineModel<RuleResponse>({default: {clauses:[]}});
|
||||
const clauses = computed<RuleClauseResponse[]>({
|
||||
get: () => rule.value && rule.value.clauses ? rule.value.clauses : [],
|
||||
set: value => rule.value = {clauses: value}
|
||||
})
|
||||
|
||||
const addRule = () => {
|
||||
rules.value = [...rules.value, {rate: RuleClauseResponseRateEnum.None}]
|
||||
const addClause = () => {
|
||||
clauses.value = [...clauses.value, {
|
||||
rate: RuleClauseResponseRateEnum.None,
|
||||
fromLedgerRef: systemLedgerRef,
|
||||
toLedgerRef: systemLedgerRef
|
||||
}]
|
||||
}
|
||||
|
||||
const setRule = (index: number, rule?: RuleClauseResponse) => {
|
||||
if (!rule) {
|
||||
const setClause = (index: number, clause?: RuleClauseResponse) => {
|
||||
if (!clause) {
|
||||
return;
|
||||
}
|
||||
rules.value = rules.value.with(index, rule)
|
||||
clauses.value = clauses.value.with(index, clause)
|
||||
}
|
||||
|
||||
const removeRule = (index: number) => {
|
||||
rules.value = rules.value.toSpliced(index, 1)
|
||||
const removeClause = (index: number) => {
|
||||
clauses.value = clauses.value.toSpliced(index, 1)
|
||||
}
|
||||
|
||||
const sortableContainer = useTemplateRef('sortable-container')
|
||||
useSortable(sortableContainer, rules, { handle: '.sortable-handle'});
|
||||
useSortable(sortableContainer, clauses, { handle: '.sortable-handle'});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-col">
|
||||
<div ref="sortable-container" class="flex-col">
|
||||
<div class="flex items-end gap-2 mt-2" v-for="(rule, index) in rules" :key="index">
|
||||
<div class="flex items-end gap-2 mt-2" v-for="(clause, index) in clauses" :key="index">
|
||||
<span class="sortable-handle flex">
|
||||
<Bars4Icon class="w-6"/>
|
||||
</span>
|
||||
<RuleClauseInput :ledgerRefs="ledgerRefs" :modelValue="rule" @update:modelValue="v => setRule(index, v)" />
|
||||
<button class="btn-icon" @click="removeRule(index)"><TrashIcon /></button>
|
||||
<RuleClauseInput :ledgerRefs="ledgerRefs" :modelValue="clause" @update:modelValue="v => setClause(index, v)" />
|
||||
<button class="btn-icon" @click="removeClause(index)"><TrashIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end mb-2 mt-2">
|
||||
<button class="btn-icon" @click="addRule"><PlusIcon /></button>
|
||||
<button class="btn-icon" @click="addClause"><PlusIcon /></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@reference "tailwindcss";
|
||||
|
||||
.sortable-handle {
|
||||
@apply cursor-grab;
|
||||
@@ -67,5 +73,4 @@ useSortable(sortableContainer, rules, { handle: '.sortable-handle'});
|
||||
.sortable-chosen .sortable-handle {
|
||||
@apply cursor-grabbing;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
import {characterRuleBookApi, ledgerApi, ruleBookApi} from "@/mammon";
|
||||
import {characterRuleBookApi, ruleBookApi} from "@/mammon";
|
||||
import {
|
||||
CharacterRuleBookResponse,
|
||||
CreateRuleBookRequest,
|
||||
@@ -13,6 +13,8 @@ import {ref, triggerRef} from "vue";
|
||||
export const activityTypes = {
|
||||
itemBought: {key: "ITEM_BOUGHT", name: "Item Bought"},
|
||||
itemSold: {key: "ITEM_SOLD", name: "Item Sold"},
|
||||
itemAcquiredManually: {key: "ITEM_ACQUIRED_MANUALLY", name: "Item Acquired Manually"},
|
||||
itemConsumedManually: {key: "ITEM_CONSUME_MANUALLY", name: "Item Consumed Manually"},
|
||||
// bountyEarned: {id: "BOUNTY_EARNED", name: "Bounty Earned"},
|
||||
// itemManufactured: {id: "ITEM_MANUFACTURED", name: "Item Manufactured"}
|
||||
} as const;
|
||||
@@ -53,7 +55,7 @@ export const useRuleBooksStore = defineStore('rule-books', () => {
|
||||
|
||||
const findById = (ruleBookId: string): RuleBook | undefined => ruleBooks.value.find(rb => rb.ruleBookId === ruleBookId);
|
||||
const create = (ruleBook: CreateRuleBookRequest) => ruleBookApi.createRuleBook(ruleBook).then(response => addRuleBook(response.data));
|
||||
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ledgerApi.updateMainLedger(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
||||
const update = (ruleBookId: string, ruleBook: CreateRuleBookRequest) => ruleBookApi.updateRuleBook(ruleBookId, ruleBook).then(response => replaceRuleBook(response.data));
|
||||
|
||||
const refresh = () => ruleBookApi.findAllRuleBooks().then(response => ruleBooks.value = response.data as RuleBook[]);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import {Dropdown} from '@/components';
|
||||
import {RouterLink} from 'vue-router';
|
||||
import {routeNames} from '@/routes';
|
||||
|
||||
const links = [
|
||||
{name: "Ledger", path: "/ledgers"},
|
||||
@@ -28,7 +29,7 @@ const logout = async () => {
|
||||
<RouterLink class="sidebar-button py-0.5 px-2" to="/characters">Characters</RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<RouterLink class="sidebar-button py-0.5 px-2" :to="{name: 'about'}">About EVE Online</RouterLink>
|
||||
<RouterLink class="sidebar-button py-0.5 px-2" :to="{name: routeNames.about}">About EVE Online</RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<a class="sidebar-button py-0.5 px-2 text-amber-700" @click="logout">Logout</a>
|
||||
|
||||
Reference in New Issue
Block a user