User dropdown
User id in acquisition about
This commit is contained in:
@@ -1,14 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAuthStore } from '@/auth';
|
import { useAuthStore } from '@/auth';
|
||||||
import { RouterView } from 'vue-router';
|
import { computed } from 'vue';
|
||||||
|
import { RouterView, useRoute } from 'vue-router';
|
||||||
import { Sidebar } from './sidebar';
|
import { Sidebar } from './sidebar';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const hideSidebar = computed(() => {
|
||||||
|
return !authStore.isLoggedIn || route.name === 'callback' || route.name === 'about';
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="!authStore.isLoggedIn">
|
<template v-if="hideSidebar">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
const isLoggedIn = computed(() => !!user.value);
|
const isLoggedIn = computed(() => !!user.value);
|
||||||
const accessToken = computed(() => user.value?.access_token);
|
const accessToken = computed(() => user.value?.access_token);
|
||||||
const username = computed(() => user.value?.profile.name ?? "");
|
const username = computed(() => user.value?.profile.name ?? "");
|
||||||
|
const userId = computed(() => user.value?.profile.sub ?? "");
|
||||||
|
|
||||||
const redirect = async () => {
|
const redirect = async () => {
|
||||||
await userManager.signinRedirect();
|
await userManager.signinRedirect();
|
||||||
@@ -42,5 +43,5 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
userManager.events.addUserLoaded(setUser);
|
userManager.events.addUserLoaded(setUser);
|
||||||
userManager.getUser().then(setUser);
|
userManager.getUser().then(setUser);
|
||||||
return { redirect, login, logout, isLoggedIn, accessToken, username };
|
return { redirect, login, logout, isLoggedIn, accessToken, username, userId };
|
||||||
});
|
});
|
||||||
46
src/components/Dropdown.vue
Normal file
46
src/components/Dropdown.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<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';
|
||||||
|
|
||||||
|
const isOpen = ref(false);
|
||||||
|
|
||||||
|
useEventListener('keyup', e => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
isOpen.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dropdown" :class="{'dropdown-open': isOpen, 'dropdown-close': !isOpen}" v-on-click-outside="() => isOpen = false">
|
||||||
|
<button @click="isOpen = !isOpen">
|
||||||
|
<slot name="button" />
|
||||||
|
<ChevronDownIcon v-if="!isOpen" class="chevron" />
|
||||||
|
<ChevronUpIcon v-else class="chevron" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Transition name="fade">
|
||||||
|
<div v-if="isOpen" class="relative">
|
||||||
|
<div class="z-10 divide-y rounded-b-md absolute">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="postcss">
|
||||||
|
.chevron {
|
||||||
|
@apply w-4 h-4 ms-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from, .fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active, .fade-leave-active {
|
||||||
|
transition: opacity 100ms ease-out;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export { default as ClipboardButton } from './ClipboardButton.vue';
|
export { default as ClipboardButton } from './ClipboardButton.vue';
|
||||||
|
export { default as Dropdown } from './Dropdown.vue';
|
||||||
export { default as LoadingSpinner } from './LoadingSpinner.vue';
|
export { default as LoadingSpinner } from './LoadingSpinner.vue';
|
||||||
export { default as Modal } from './Modal.vue';
|
export { default as Modal } from './Modal.vue';
|
||||||
export { default as SliderCheckbox } from './SliderCheckbox.vue';
|
export { default as SliderCheckbox } from './SliderCheckbox.vue';
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ defineEmits<Emits>();
|
|||||||
|
|
||||||
const marketTaxStore = useMarketTaxStore();
|
const marketTaxStore = useMarketTaxStore();
|
||||||
|
|
||||||
const threshold = useStorage('market-aquisition-threshold', 10);
|
const threshold = useStorage('market-acquisition-threshold', 10);
|
||||||
const filter = ref("");
|
const filter = ref("");
|
||||||
const { sortedArray, headerProps } = useSort<Result>(computed(() => props.items
|
const { sortedArray, headerProps } = useSort<Result>(computed(() => props.items
|
||||||
.filter(r => r.type.name.toLowerCase().includes(filter.value.toLowerCase()))
|
.filter(r => r.type.name.toLowerCase().includes(filter.value.toLowerCase()))
|
||||||
@@ -107,7 +107,7 @@ const getLineColor = (result: Result) => {
|
|||||||
<td class="text-right">{{ formatIsk(r.sell) }}</td>
|
<td class="text-right">{{ formatIsk(r.sell) }}</td>
|
||||||
<td class="text-right">{{ formatIsk(r.price) }}</td>
|
<td class="text-right">{{ formatIsk(r.price) }}</td>
|
||||||
<td class="text-right">{{ r.count }}</td>
|
<td class="text-right">{{ r.count }}</td>
|
||||||
<td class="text-right">{{ percentFormater.format(r.precentProfit) }}</td>
|
<td class="text-right">{{ percentFormater.format(r.precentProfit) }}</td>
|
||||||
<td class="text-right">{{ formatIsk(r.iskProfit) }}</td>
|
<td class="text-right">{{ formatIsk(r.iskProfit) }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<button class="btn-icon me-1" @click="$emit('buy', r.type, r.price, r.buy, r.sell)"><PlusIcon /></button>
|
<button class="btn-icon me-1" @click="$emit('buy', r.type, r.price, r.buy, r.sell)"><PlusIcon /></button>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useAuthStore } from "@/auth";
|
||||||
import { marbasAxiosInstance } from "@/service";
|
import { marbasAxiosInstance } from "@/service";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
@@ -17,6 +18,7 @@ const endpoint = '/api/acquisitions';
|
|||||||
|
|
||||||
export const useAcquiredItemStore = defineStore('market-acquisition', () => {
|
export const useAcquiredItemStore = defineStore('market-acquisition', () => {
|
||||||
const _acquiredItems = ref<AcquiredMarketItem[]>([]);
|
const _acquiredItems = ref<AcquiredMarketItem[]>([]);
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
const items = computed(() => _acquiredItems.value);
|
const items = computed(() => _acquiredItems.value);
|
||||||
const addAcquiredItem = async (type: number, quantity: number, price: number) => {
|
const addAcquiredItem = async (type: number, quantity: number, price: number) => {
|
||||||
@@ -27,7 +29,7 @@ export const useAcquiredItemStore = defineStore('market-acquisition', () => {
|
|||||||
price: price,
|
price: price,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
source: 'bo',
|
source: 'bo',
|
||||||
user: 0 // TODO: get user id
|
user: authStore.userId,
|
||||||
})).data];
|
})).data];
|
||||||
};
|
};
|
||||||
const removeAcquiredItem = async (type: number, quantity: number) => {
|
const removeAcquiredItem = async (type: number, quantity: number) => {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ watchEffect(async () => {
|
|||||||
<template>
|
<template>
|
||||||
<div @click="() => isOpen = true" v-on-click-outside="() => isOpen = false">
|
<div @click="() => isOpen = true" v-on-click-outside="() => isOpen = false">
|
||||||
<div class="fake-input">
|
<div class="fake-input">
|
||||||
<img v-if="value?.id" :src="`https://images.evetech.net/types/${value.id}/icon`" />
|
<img v-if="value?.id" :src="`https://images.evetech.net/types/${value.id}/icon`" alt="" />
|
||||||
<input type="text" v-model="name" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp" />
|
<input type="text" v-model="name" @keyup.enter="submit" @keyup.down="moveDown" @keyup.up="moveUp" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="suggestions.length > 1" class="z-10 absolute w-96">
|
<div v-if="suggestions.length > 1" class="z-10 absolute w-96">
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
{ path: 'acquisitions', component: () => import('@/pages/market/Acquisitions.vue') },
|
{ path: 'acquisitions', component: () => import('@/pages/market/Acquisitions.vue') },
|
||||||
] },
|
] },
|
||||||
{ path: '/tools', component: () => import('@/pages/Tools.vue') },
|
{ path: '/tools', component: () => import('@/pages/Tools.vue') },
|
||||||
{ path: '/about', component: () => import('@/pages/About.vue') },
|
{ path: '/about', name: 'about', component: () => import('@/pages/About.vue') },
|
||||||
];
|
];
|
||||||
@@ -44,7 +44,13 @@ marbasAxiosInstance.interceptors.response.use(async r => {
|
|||||||
return r;
|
return r;
|
||||||
})
|
})
|
||||||
marbasAxiosInstance.interceptors.request.use(r => {
|
marbasAxiosInstance.interceptors.request.use(r => {
|
||||||
const accessToken = useAuthStore().accessToken;
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
if (!authStore.isLoggedIn) {
|
||||||
|
throw new Error("Not logged in");
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = authStore.accessToken;
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
r.headers.Authorization = `Bearer ${accessToken}`;
|
r.headers.Authorization = `Bearer ${accessToken}`;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAuthStore } from '@/auth';
|
import { useAuthStore } from '@/auth';
|
||||||
|
import { Dropdown } from '@/components';
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
@@ -18,22 +19,52 @@ const logout = async () => {
|
|||||||
<template>
|
<template>
|
||||||
<aside class="fixed top-0 left-0 w-64 h-screen transition-transform -translate-x-full sm:translate-x-0">
|
<aside class="fixed top-0 left-0 w-64 h-screen transition-transform -translate-x-full sm:translate-x-0">
|
||||||
<div class="h-full px-3 py-4 overflow-y-auto bg-slate-700 flex flex-col">
|
<div class="h-full px-3 py-4 overflow-y-auto bg-slate-700 flex flex-col">
|
||||||
|
<div class="mb-2 border-b-2 border-emerald-500">
|
||||||
|
<Dropdown class="mb-2 user-dropdown">
|
||||||
|
<template #button>
|
||||||
|
<span>{{ authStore.username }}</span>
|
||||||
|
</template>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<RouterLink class="sidebar-button py-0.5 px-2" :to="{name: 'about'}">About EVE Online</RouterLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="sidebar-button py-0.5 px-2 text-amber-700" @click="logout">Logout</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
<ul class="space-y-2 font-medium">
|
<ul class="space-y-2 font-medium">
|
||||||
<li v-for="link in links" :key="link.name">
|
<li v-for="link in links" :key="link.name">
|
||||||
<RouterLink :to="link.path" class="flex items-center p-2 rounded-md hover:bg-slate-800">
|
<RouterLink :to="link.path" class="sidebar-button p-2">
|
||||||
<span>{{ link.name }}</span>
|
<span>{{ link.name }}</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="mt-auto">
|
|
||||||
<button @click="logout">Logout</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="postcss">
|
<style scoped lang="postcss">
|
||||||
|
.sidebar-button {
|
||||||
|
@apply flex items-center rounded-md hover:bg-slate-800 cursor-pointer;
|
||||||
|
}
|
||||||
.router-link-active {
|
.router-link-active {
|
||||||
@apply bg-emerald-500 hover:bg-emerald-700;
|
@apply bg-emerald-500 hover:bg-emerald-700;
|
||||||
}
|
}
|
||||||
|
.user-dropdown {
|
||||||
|
@apply w-full;
|
||||||
|
:deep(>div) {
|
||||||
|
@apply w-full;
|
||||||
|
>div {
|
||||||
|
@apply w-full bg-slate-800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(>button) {
|
||||||
|
@apply bg-slate-700 hover:bg-slate-800 border-none flex items-center w-full;
|
||||||
|
}
|
||||||
|
&.dropdown-open:deep(>button) {
|
||||||
|
@apply bg-slate-800 rounded-b-none;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user