Improve user experience quality of life
parent
d3abf67d88
commit
42b7e603f0
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterLink, RouterView } from "vue-router";
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
import { useAuthStore } from "./stores/auth";
|
import { useAuthStore } from "./stores/auth";
|
||||||
|
import ProfileDropdown from "./components/ProfileDropdown.vue";
|
||||||
|
|
||||||
const baseUrl = window.location.origin;
|
const baseUrl = window.location.origin;
|
||||||
|
|
||||||
|
@ -11,12 +12,23 @@ const authStore = useAuthStore();
|
||||||
<header>
|
<header>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<nav>
|
<nav>
|
||||||
<h1>availabili.tf</h1>
|
<h1>
|
||||||
<RouterLink to="/">Home</RouterLink>
|
<RouterLink class="header-link" to="/">availabili.tf</RouterLink>
|
||||||
<RouterLink to="/schedule">Schedule</RouterLink>
|
</h1>
|
||||||
<div v-if="authStore.isLoggedIn">
|
<div class="nav-links">
|
||||||
|
<a
|
||||||
|
class="button"
|
||||||
|
href="https://github.com/HumanoidSandvichDispenser/availabili.tf"
|
||||||
|
v-tooltip="'View on GitHub'"
|
||||||
|
>
|
||||||
|
<button class="icon">
|
||||||
|
<i class="bi bi-github" />
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<ProfileDropdown v-if="authStore.isLoggedIn" />
|
||||||
|
<!--button v-if="authStore.isLoggedIn" class="profile-button">
|
||||||
Welcome {{ authStore.username }}
|
Welcome {{ authStore.username }}
|
||||||
</div>
|
</button-->
|
||||||
<form
|
<form
|
||||||
v-else
|
v-else
|
||||||
action="https://steamcommunity.com/openid/login"
|
action="https://steamcommunity.com/openid/login"
|
||||||
|
@ -29,8 +41,12 @@ const authStore = useAuthStore();
|
||||||
<input type="hidden" name="openid.ns" value="http://specs.openid.net/auth/2.0" />
|
<input type="hidden" name="openid.ns" value="http://specs.openid.net/auth/2.0" />
|
||||||
<input type="hidden" name="openid.mode" value="checkid_setup" />
|
<input type="hidden" name="openid.mode" value="checkid_setup" />
|
||||||
<input type="hidden" name="openid.return_to" :value="baseUrl + '/login'" />
|
<input type="hidden" name="openid.return_to" :value="baseUrl + '/login'" />
|
||||||
<button type="submit">Log in through Steam</button>
|
<!--button type="submit">Log in through Steam</button-->
|
||||||
|
<button type="submit" class="sign-in-button">
|
||||||
|
<img src="https://community.fastly.steamstatic.com/public/images/signinthroughsteam/sits_01.png" />
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@ -46,6 +62,10 @@ header {
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.header-link {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto 2rem;
|
margin: 0 auto 2rem;
|
||||||
|
@ -53,28 +73,36 @@ header {
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .nav-links {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 11pt;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.profile-button {
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a.router-link-exact-active {
|
nav a.router-link-exact-active {
|
||||||
color: var(--crust);
|
color: var(--text);
|
||||||
background-color: var(--accent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
nav a {
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
color: var(--subtext-0);
|
color: var(--subtext-0);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:hover {
|
nav a:hover {
|
||||||
color: var(--accent);
|
background-color: transparent;
|
||||||
background-color: var(--accent-transparent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav > h1 {
|
nav > h1 {
|
||||||
|
@ -82,6 +110,12 @@ nav > h1 {
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.sign-in-button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
header {
|
header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -102,7 +136,6 @@ nav > h1 {
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: -1rem;
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
|
|
|
@ -271,7 +271,7 @@ hr {
|
||||||
/*box-shadow: 0 0 4px var(--text);*/
|
/*box-shadow: 0 0 4px var(--text);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
[role="menu"] {
|
[role="menu"], [role="listbox"] {
|
||||||
background-color: var(--base);
|
background-color: var(--base);
|
||||||
border: 1px solid var(--overlay-0);
|
border: 1px solid var(--overlay-0);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -279,6 +279,16 @@ hr {
|
||||||
min-width: 8rem;
|
min-width: 8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[role="listbox"] [role="option"] {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="listbox"] [role="option"][data-highlighted] {
|
||||||
|
background-color: var(--surface-0);
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
[role="menu"] button {
|
[role="menu"] button {
|
||||||
background-color: var(--base);
|
background-color: var(--base);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -297,17 +307,6 @@ hr {
|
||||||
[role="menu"] button > i.bi.margin {
|
[role="menu"] button > i.bi.margin {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
div[role="menu"] div[role="menuitem"]:first-child button {
|
|
||||||
border-top-left-radius: var(--border-radius);
|
|
||||||
border-top-right-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
div[role="menu"] div[role="menuitem"]:last-child button {
|
|
||||||
border-bottom-left-radius: var(--border-radius);
|
|
||||||
border-bottom-right-radius: var(--border-radius);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
[role="menu"] button:hover {
|
[role="menu"] button:hover {
|
||||||
background-color: var(--surface-0);
|
background-color: var(--surface-0);
|
||||||
|
|
|
@ -1,91 +1,53 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts" generic="T extends AcceptableValue">
|
||||||
import { computed, defineModel, defineProps, ref } from "vue";
|
import { defineModel, defineProps, ref } from "vue";
|
||||||
|
import {
|
||||||
|
ComboboxContent,
|
||||||
|
ComboboxInput,
|
||||||
|
ComboboxItem,
|
||||||
|
ComboboxRoot,
|
||||||
|
ComboboxPortal,
|
||||||
|
ComboboxTrigger,
|
||||||
|
ComboboxAnchor,
|
||||||
|
ComboboxViewport,
|
||||||
|
} from "radix-vue";
|
||||||
|
import type { AcceptableValue } from "node_modules/radix-vue/dist/shared/types";
|
||||||
|
|
||||||
const model = defineModel();
|
const selectedValue = defineModel<T>();
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
options: Array<String>,
|
|
||||||
isDisabled: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isOpen = ref(false);
|
const isOpen = ref(false);
|
||||||
const selectedOption = computed(() => props.options[model.value]);
|
|
||||||
|
|
||||||
function selectOption(index) {
|
withDefaults(defineProps<{
|
||||||
model.value = index;
|
values: T[];
|
||||||
isOpen.value = false;
|
//mapper? (value: T): string;
|
||||||
}
|
//keyMapper? (value: T): string;
|
||||||
|
display: string;
|
||||||
|
keyField: string;
|
||||||
|
}>(), {
|
||||||
|
//mapper: (value: T) => value.toString(),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'dropdown-container': true, 'is-open': isOpen }">
|
<ComboboxRoot
|
||||||
<button @click="isOpen = !isOpen" :disabled="isDisabled">
|
v-model="selectedValue"
|
||||||
{{ selectedOption }}
|
v-model:open="isOpen"
|
||||||
<i class="bi bi-caret-down-fill"></i>
|
defaultOpen
|
||||||
</button>
|
>
|
||||||
<ul class="dropdown" v-if="isOpen" @blur="isOpen = false">
|
<ComboboxAnchor>
|
||||||
<li v-for="(option, i) in options" :key="i" @click="selectOption(i)">
|
<ComboboxInput />
|
||||||
<button :class="{ 'is-selected': i == model }">
|
<ComboboxTrigger>
|
||||||
{{ option }}
|
hi
|
||||||
</button>
|
</ComboboxTrigger>
|
||||||
</li>
|
</ComboboxAnchor>
|
||||||
</ul>
|
|
||||||
</div>
|
<ComboboxPortal>
|
||||||
|
<ComboboxContent position="popper">
|
||||||
|
<ComboboxViewport>
|
||||||
|
</ComboboxViewport>
|
||||||
|
</ComboboxContent>
|
||||||
|
</ComboboxPortal>
|
||||||
|
</ComboboxRoot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dropdown-container {
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-container button {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: 4px;
|
|
||||||
transition-duration: 200ms;
|
|
||||||
background-color: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-container button:hover {
|
|
||||||
background-color: var(--crust);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-container.is-open ul.dropdown {
|
|
||||||
box-shadow: 1px 1px 8px var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.dropdown {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--base);
|
|
||||||
position: absolute;
|
|
||||||
margin-top: 8px;
|
|
||||||
padding: 0;
|
|
||||||
z-index: 2;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.dropdown > li {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown li > button {
|
|
||||||
padding: 8px 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown li > button.is-selected {
|
|
||||||
background-color: var(--accent-transparent);
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -27,8 +27,8 @@ function disableIntegration() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h2>logs.tf Integration</h2>
|
<h2>logs.tf Auto-Tracking</h2>
|
||||||
<p>Automatically track match history from logs.tf.</p>
|
<p>Automatically fetch and track match history from logs.tf.</p>
|
||||||
<div v-if="model">
|
<div v-if="model">
|
||||||
<div class="form-group margin">
|
<div class="form-group margin">
|
||||||
<h3>logs.tf API key (optional)</h3>
|
<h3>logs.tf API key (optional)</h3>
|
||||||
|
|
|
@ -5,11 +5,12 @@ import { useRosterStore } from "../stores/roster";
|
||||||
|
|
||||||
const rosterStore = useRosterStore();
|
const rosterStore = useRosterStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = withDefaults(defineProps<{
|
||||||
roleTitle: String,
|
roleTitle: string,
|
||||||
player: Object as PropType<PlayerTeamRoleFlat>,
|
player: PlayerTeamRoleFlat | undefined,
|
||||||
isRoster: Boolean,
|
isRoster: boolean,
|
||||||
isRinger: Boolean,
|
}>(), {
|
||||||
|
isRoster: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isSelected = computed(() => {
|
const isSelected = computed(() => {
|
||||||
|
@ -17,11 +18,9 @@ const isSelected = computed(() => {
|
||||||
return rosterStore.selectedRole == props.roleTitle;
|
return rosterStore.selectedRole == props.roleTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.isRinger) {
|
const selectedPlayers = rosterStore.selectedPlayers;
|
||||||
return rosterStore.selectedPlayers[props.roleTitle]?.playtime == -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.values(rosterStore.selectedPlayers).includes(props.player);
|
return selectedPlayers[props.roleTitle]?.steamId == props.player?.steamId;
|
||||||
});
|
});
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
|
@ -35,26 +34,14 @@ function onClick() {
|
||||||
// we are selecting the player
|
// we are selecting the player
|
||||||
if (isSelected.value) {
|
if (isSelected.value) {
|
||||||
rosterStore.selectPlayerForRole(undefined, props.roleTitle);
|
rosterStore.selectPlayerForRole(undefined, props.roleTitle);
|
||||||
} else {
|
|
||||||
if (props.isRinger) {
|
|
||||||
const ringerPlayer: PlayerTeamRoleFlat = {
|
|
||||||
steamId: "0",
|
|
||||||
name: "Ringer",
|
|
||||||
role: props.roleTitle ?? "",
|
|
||||||
isMain: false,
|
|
||||||
availability: 1,
|
|
||||||
playtime: -1,
|
|
||||||
};
|
|
||||||
rosterStore.selectPlayerForRole(ringerPlayer, props.roleTitle);
|
|
||||||
} else {
|
} else {
|
||||||
rosterStore.selectPlayerForRole(props.player, props.roleTitle);
|
rosterStore.selectPlayerForRole(props.player, props.roleTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const playtime = computed(() => {
|
const playtime = computed(() => {
|
||||||
let hours = props.player?.playtime / 3600 ?? 0;
|
let hours = props.player?.playtime ?? 0 / 3600;
|
||||||
return hours.toFixed(1);
|
return hours.toFixed(1);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,7 +49,7 @@ const playtime = computed(() => {
|
||||||
<template>
|
<template>
|
||||||
<button :class="{
|
<button :class="{
|
||||||
'player-card': true,
|
'player-card': true,
|
||||||
'no-player': !player && !isRinger,
|
'no-player': !player,
|
||||||
'selected': isSelected,
|
'selected': isSelected,
|
||||||
'can-be-available': player?.availability == 1
|
'can-be-available': player?.availability == 1
|
||||||
}" @click="onClick">
|
}" @click="onClick">
|
||||||
|
@ -79,21 +66,12 @@ const playtime = computed(() => {
|
||||||
(alternate)
|
(alternate)
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="playtime > 0">
|
<span v-if="Number(playtime) > 0">
|
||||||
{{ playtime }} hours
|
{{ playtime }} hours
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="isRinger" class="role-info">
|
|
||||||
<span>
|
|
||||||
<h4 class="player-name">Ringer</h4>
|
|
||||||
<div class="subtitle">
|
|
||||||
<span>{{ rosterStore.roleNames[roleTitle] }}</span>
|
|
||||||
<!--span>nobody likes to play {{ roleTitle }}</span-->
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-else class="role-info">
|
<div v-else class="role-info">
|
||||||
<span>
|
<span>
|
||||||
{{ rosterStore.roleNames[roleTitle] }}
|
{{ rosterStore.roleNames[roleTitle] }}
|
||||||
|
|
|
@ -131,9 +131,14 @@ const rightIndicator = computed(() => {
|
||||||
:availability="player.availability[1]"
|
:availability="player.availability[1]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
class="player-name"
|
||||||
|
:href="`https://steamcommunity.com/profiles/${player.steamId}`"
|
||||||
|
>
|
||||||
<h3>
|
<h3>
|
||||||
{{ player.username }}
|
{{ player.username }}
|
||||||
</h3>
|
</h3>
|
||||||
|
</a>
|
||||||
<svg-icon
|
<svg-icon
|
||||||
v-if="player.isTeamLeader"
|
v-if="player.isTeamLeader"
|
||||||
:class="[
|
:class="[
|
||||||
|
@ -256,6 +261,14 @@ const rightIndicator = computed(() => {
|
||||||
background-color: var(--green);
|
background-color: var(--green);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.player-name {
|
||||||
|
color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.player-name:hover {
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-middle {
|
.flex-middle {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
import {
|
||||||
|
DropdownMenuRoot,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
} from "radix-vue";
|
||||||
|
import { RouterLink } from "vue-router";
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
authStore.logout();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DropdownMenuRoot>
|
||||||
|
<DropdownMenuTrigger className="profile-button">
|
||||||
|
{{ authStore.username }}
|
||||||
|
<i class="bi bi-chevron-down" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuPortal>
|
||||||
|
<DropdownMenuContent className="shadow">
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<RouterLink class="button" :to="{ 'name': 'user-settings' }">
|
||||||
|
<button>
|
||||||
|
<i class="bi bi-gear margin" />
|
||||||
|
Settings
|
||||||
|
</button>
|
||||||
|
</RouterLink>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<RouterLink class="button" to="/teams">
|
||||||
|
<button>
|
||||||
|
<i class="bi bi-people margin" />
|
||||||
|
Teams
|
||||||
|
</button>
|
||||||
|
</RouterLink>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<button class="destructive" @click="logout">
|
||||||
|
<i class="bi bi-box-arrow-right margin" />
|
||||||
|
Log out
|
||||||
|
</button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuRoot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.profile-button {
|
||||||
|
background-color: transparent;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-button:hover {
|
||||||
|
background-color: var(--surface-0);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -6,6 +6,7 @@ import { createPinia } from "pinia";
|
||||||
import VueSelect from "vue-select";
|
import VueSelect from "vue-select";
|
||||||
import { TooltipDirective } from "vue3-tooltip";
|
import { TooltipDirective } from "vue3-tooltip";
|
||||||
import "vue3-tooltip/tooltip.css";
|
import "vue3-tooltip/tooltip.css";
|
||||||
|
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const useRosterStore = defineStore("roster", () => {
|
||||||
|
|
||||||
// TODO: move roster state to a composable
|
// TODO: move roster state to a composable
|
||||||
|
|
||||||
const neededRoles: Reactive<Array<String>> = reactive([
|
const neededRoles = ref([
|
||||||
"PocketScout",
|
"PocketScout",
|
||||||
"FlankScout",
|
"FlankScout",
|
||||||
"PocketSoldier",
|
"PocketSoldier",
|
||||||
|
@ -95,7 +95,7 @@ export const useRosterStore = defineStore("roster", () => {
|
||||||
"Spy": "Spy",
|
"Spy": "Spy",
|
||||||
});
|
});
|
||||||
|
|
||||||
function selectPlayerForRole(player: PlayerTeamRoleFlat, role: string) {
|
function selectPlayerForRole(player: PlayerTeamRoleFlat | undefined, role: string) {
|
||||||
if (player && player.steamId) {
|
if (player && player.steamId) {
|
||||||
const existingRole = Object.keys(selectedPlayers).find((selectedRole) => {
|
const existingRole = Object.keys(selectedPlayers).find((selectedRole) => {
|
||||||
return selectedPlayers[selectedRole]?.steamId == player.steamId &&
|
return selectedPlayers[selectedRole]?.steamId == player.steamId &&
|
||||||
|
|
|
@ -79,13 +79,10 @@ onMounted(async () => {
|
||||||
<span v-if="!hasAvailablePlayers && rosterStore.selectedRole">
|
<span v-if="!hasAvailablePlayers && rosterStore.selectedRole">
|
||||||
No players are currently available for this role.
|
No players are currently available for this role.
|
||||||
</span>
|
</span>
|
||||||
<h3 v-if="hasAvailablePlayers">Alternates</h3>
|
<h3 v-if="hasAlternates">Alternates</h3>
|
||||||
<PlayerCard v-for="player in rosterStore.alternateRoles"
|
<PlayerCard v-for="player in rosterStore.alternateRoles"
|
||||||
:player="player"
|
:player="player"
|
||||||
:role-title="player.role" />
|
:role-title="player.role" />
|
||||||
<PlayerCard v-if="rosterStore.selectedRole"
|
|
||||||
is-ringer
|
|
||||||
:role-title="rosterStore.selectedRole" />
|
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button class="accent" @click="closeSelection">
|
<button class="accent" @click="closeSelection">
|
||||||
<i class="bi bi-check" />
|
<i class="bi bi-check" />
|
||||||
|
|
|
@ -27,18 +27,24 @@ const hasAvailablePlayers = computed(() => {
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h3 v-if="hasAvailablePlayers">Available</h3>
|
<h3 v-if="hasAvailablePlayers">Available</h3>
|
||||||
<PlayerCard v-for="player in rosterStore.definitelyAvailableAll"
|
<PlayerCard
|
||||||
|
v-for="player in rosterStore.definitelyAvailable"
|
||||||
:player="player"
|
:player="player"
|
||||||
:role-title="player.role" />
|
:role-title="player.role"
|
||||||
|
:is-roster="false"
|
||||||
|
/>
|
||||||
<span v-if="!hasAvailablePlayers">
|
<span v-if="!hasAvailablePlayers">
|
||||||
No players are currently available for this role.
|
No players are currently available for this role.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h3 v-if="hasAvailablePlayers">Available if needed</h3>
|
<h3 v-if="hasAvailablePlayers">Available if needed</h3>
|
||||||
<PlayerCard v-for="player in rosterStore.canBeAvailableAll"
|
<PlayerCard
|
||||||
|
v-for="player in rosterStore.canBeAvailable"
|
||||||
:player="player"
|
:player="player"
|
||||||
:role-title="player.role" />
|
:role-title="player.role"
|
||||||
|
:is-roster="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DiscordIntegrationForm from "@/components/DiscordIntegrationForm.vue";
|
import DiscordIntegrationForm from "@/components/DiscordIntegrationForm.vue";
|
||||||
import IntegrationDetails from "@/components/IntegrationDetails.vue";
|
import IntegrationDetails from "@/components/IntegrationDetails.vue";
|
||||||
|
import LoaderContainer from "@/components/LoaderContainer.vue";
|
||||||
import LogsTfIntegrationForm from "@/components/LogsTfIntegrationForm.vue";
|
import LogsTfIntegrationForm from "@/components/LogsTfIntegrationForm.vue";
|
||||||
import { useTeamDetails } from "@/composables/team-details";
|
import { useTeamDetails } from "@/composables/team-details";
|
||||||
import { useTeamsStore } from "@/stores/teams";
|
import { useTeamsStore } from "@/stores/teams";
|
||||||
|
@ -11,20 +12,33 @@ const teamsStore = useTeamsStore();
|
||||||
const integrationsStore = useIntegrationsStore();
|
const integrationsStore = useIntegrationsStore();
|
||||||
const { teamId } = useTeamDetails();
|
const { teamId } = useTeamDetails();
|
||||||
|
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
//function createIntegration() {
|
//function createIntegration() {
|
||||||
// integrationsStore.createIntegration(teamId.value, "discord");
|
// integrationsStore.createIntegration(teamId.value, "discord");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
isLoading.value = true;
|
||||||
teamsStore.fetchTeam(teamId.value)
|
teamsStore.fetchTeam(teamId.value)
|
||||||
.then(() => integrationsStore.getIntegrations(teamId.value));
|
.then(() => {
|
||||||
|
integrationsStore.getIntegrations(teamId.value)
|
||||||
|
.then(() => {
|
||||||
|
isLoading.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="team-integrations">
|
<div class="team-integrations">
|
||||||
|
<div v-if="isLoading">
|
||||||
|
<LoaderContainer />
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
<DiscordIntegrationForm v-model="integrationsStore.discordIntegration" />
|
<DiscordIntegrationForm v-model="integrationsStore.discordIntegration" />
|
||||||
<LogsTfIntegrationForm v-model="integrationsStore.logsTfIntegration" />
|
<LogsTfIntegrationForm v-model="integrationsStore.logsTfIntegration" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue