Improve team page usability
parent
abc456b7d1
commit
9b2153266a
|
@ -52,6 +52,10 @@ button {
|
||||||
transition-duration: 200ms;
|
transition-duration: 200ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.icon-end {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
button.icon {
|
button.icon {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
@ -98,6 +102,10 @@ button.destructive {
|
||||||
color: var(--base);
|
color: var(--base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.destructive-on-hover:hover {
|
||||||
|
background-color: var(--flamingo);
|
||||||
|
color: var(--base);
|
||||||
|
}
|
||||||
|
|
||||||
button.accent:hover {
|
button.accent:hover {
|
||||||
background-color: var(--text);
|
background-color: var(--text);
|
||||||
|
|
|
@ -203,7 +203,7 @@ function getAvailabilityCell(day: number, hour: number) {
|
||||||
const currentTimezone = computed(() =>
|
const currentTimezone = computed(() =>
|
||||||
Intl.DateTimeFormat().resolvedOptions().timeZone);
|
Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||||
|
|
||||||
function getHour(offset, tz) {
|
function getHour(offset, tz?) {
|
||||||
let time = props.dateStart.clone()
|
let time = props.dateStart.clone()
|
||||||
if (tz) {
|
if (tz) {
|
||||||
time = time.tz(tz);
|
time = time.tz(tz);
|
||||||
|
@ -226,9 +226,9 @@ function getHour(offset, tz) {
|
||||||
</div>
|
</div>
|
||||||
<div class="height-24px hour-marker-container">
|
<div class="height-24px hour-marker-container">
|
||||||
<span class="hour-marker">
|
<span class="hour-marker">
|
||||||
{{ getHour(hour + 1).format("HH:mm z") }}
|
{{ getHour(lastHour + 1).format("HH:mm z") }}
|
||||||
<span v-if="scheduleStore.team.tzTimezone != currentTimezone">
|
<span v-if="scheduleStore.team.tzTimezone != currentTimezone">
|
||||||
/ {{ getHour(hour + 1, scheduleStore.team.tzTimezone).format("HH:mm z") }}
|
/ {{ getHour(lastHour + 1, scheduleStore.team.tzTimezone).format("HH:mm z") }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,7 +33,7 @@ function scheduleRoster() {
|
||||||
<template>
|
<template>
|
||||||
<div class="schedule-player-list">
|
<div class="schedule-player-list">
|
||||||
<h3>{{ scheduleStore.team?.teamName }}</h3>
|
<h3>{{ scheduleStore.team?.teamName }}</h3>
|
||||||
<div>
|
<div class="list">
|
||||||
<SchedulePlayerListItem
|
<SchedulePlayerListItem
|
||||||
v-for="record in scheduleStore.playerAvailability"
|
v-for="record in scheduleStore.playerAvailability"
|
||||||
:player="record"
|
:player="record"
|
||||||
|
@ -75,4 +75,9 @@ h4, h4 > div {
|
||||||
.player:hover {
|
.player:hover {
|
||||||
background-color: var(--mantle);
|
background-color: var(--mantle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,8 +8,8 @@ const scheduleStore = useScheduleStore();
|
||||||
const hoveredIndex = computed(() => scheduleStore.hoveredIndex);
|
const hoveredIndex = computed(() => scheduleStore.hoveredIndex);
|
||||||
|
|
||||||
const availabilityAtHoveredIndex = computed(() => {
|
const availabilityAtHoveredIndex = computed(() => {
|
||||||
if (hoveredIndex.value) {
|
if (hoveredIndex.value && props.player?.availability) {
|
||||||
return props.player?.availability[hoveredIndex.value] ?? 0;
|
return props.player.availability[hoveredIndex.value] ?? 0;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
@ -19,12 +19,14 @@ const props = defineProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMouseOver() {
|
function onMouseOver() {
|
||||||
scheduleStore.overlay = props.player;
|
if (props.player) {
|
||||||
|
scheduleStore.hoveredMember = props.player;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseLeave() {
|
function onMouseLeave() {
|
||||||
if (scheduleStore.overlay == props.player) {
|
if (scheduleStore.hoveredMember == props.player) {
|
||||||
scheduleStore.overlay = undefined;
|
scheduleStore.hoveredMember = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -32,10 +34,21 @@ function onMouseLeave() {
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="player"
|
class="player"
|
||||||
@mouseover="onMouseOver(player)"
|
v-if="player"
|
||||||
|
@mouseover="onMouseOver"
|
||||||
@mouseleave="onMouseLeave"
|
@mouseleave="onMouseLeave"
|
||||||
>
|
>
|
||||||
<span v-if="availabilityAtHoveredIndex > 0">
|
<input
|
||||||
|
class="player-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="scheduleStore.selectedMembers[player.steamId]"
|
||||||
|
:value="player"
|
||||||
|
:id="player.steamId"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
:for="player.steamId"
|
||||||
|
>
|
||||||
|
<span v-if="availabilityAtHoveredIndex ?? 0 > 0">
|
||||||
<span v-if="availabilityAtHoveredIndex == 1" class="can-be-available">
|
<span v-if="availabilityAtHoveredIndex == 1" class="can-be-available">
|
||||||
{{ player.username }}
|
{{ player.username }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -49,11 +62,28 @@ function onMouseLeave() {
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ player.username }}
|
{{ player.username }}
|
||||||
</span>
|
</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.player:hover {
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player label {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player label:hover {
|
||||||
background-color: var(--mantle);
|
background-color: var(--mantle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,9 @@ export const useAuthStore = defineStore("auth", () => {
|
||||||
getUser.name,
|
getUser.name,
|
||||||
() => client.default.getUser(),
|
() => client.default.getUser(),
|
||||||
(response) => {
|
(response) => {
|
||||||
|
isLoggedIn.value = true;
|
||||||
steamId.value = response.steamId;
|
steamId.value = response.steamId;
|
||||||
username.value = username.value;
|
username.value = response.username;
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,9 +29,36 @@ export const useScheduleStore = defineStore("schedule", () => {
|
||||||
|
|
||||||
const playerAvailability: Ref<AvailabilitySchema[]> = ref([ ]);
|
const playerAvailability: Ref<AvailabilitySchema[]> = ref([ ]);
|
||||||
|
|
||||||
const overlay: Ref<AvailabilitySchema[] | undefined> = ref();
|
const overlay = computed<number[] | undefined>(() => {
|
||||||
|
let members = Object.keys(selectedMembers)
|
||||||
|
.filter((x) => selectedMembers[x]);
|
||||||
|
if (members.length > 0) {
|
||||||
|
const min = Array(168).fill(0);
|
||||||
|
|
||||||
const selectedMembers = ref<AvailabilitySchema[]>();
|
const candidates = playerAvailability.value
|
||||||
|
.filter((x) => members.includes(x.steamId));
|
||||||
|
for (let i = 0; i < 168; i++) {
|
||||||
|
min[i] = Math.min(
|
||||||
|
...candidates.map((c) => c.availability ? c.availability[i] : 0));
|
||||||
|
}
|
||||||
|
//for (const availability of candidates) {
|
||||||
|
//}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hoveredMember.value) {
|
||||||
|
return playerAvailability.value
|
||||||
|
.find((x) => x.steamId == hoveredMember.value?.steamId)
|
||||||
|
?.availability;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hoveredMember = ref<AvailabilitySchema>();
|
||||||
|
|
||||||
|
const selectedMembers = reactive<{ [id: string]: boolean }>({ });
|
||||||
|
|
||||||
const hoveredIndex: Ref<number | undefined> = ref();
|
const hoveredIndex: Ref<number | undefined> = ref();
|
||||||
|
|
||||||
|
@ -115,6 +142,7 @@ export const useScheduleStore = defineStore("schedule", () => {
|
||||||
availability,
|
availability,
|
||||||
playerAvailability,
|
playerAvailability,
|
||||||
overlay,
|
overlay,
|
||||||
|
hoveredMember,
|
||||||
selectedMembers,
|
selectedMembers,
|
||||||
hoveredIndex,
|
hoveredIndex,
|
||||||
fetchSchedule,
|
fetchSchedule,
|
||||||
|
|
|
@ -28,7 +28,7 @@ const selectionMode = ref(1);
|
||||||
|
|
||||||
const selectedTime = ref(undefined);
|
const selectedTime = ref(undefined);
|
||||||
|
|
||||||
const availabilityOverlay = computed(() => schedule.overlay?.availability);
|
const availabilityOverlay = computed(() => schedule.overlay);
|
||||||
|
|
||||||
const isEditing = ref(false);
|
const isEditing = ref(false);
|
||||||
|
|
||||||
|
@ -132,7 +132,8 @@ onMounted(() => {
|
||||||
Copy previous week
|
Copy previous week
|
||||||
</button>
|
</button>
|
||||||
<button class="accent" @click="isEditing = true">
|
<button class="accent" @click="isEditing = true">
|
||||||
<i class="bi bi-pencil-fill"></i>
|
<i class="bi bi-pencil-fill margin"></i>
|
||||||
|
Edit
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,6 +7,10 @@ const {
|
||||||
timezone,
|
timezone,
|
||||||
minuteOffset,
|
minuteOffset,
|
||||||
} = useTeamSettings();
|
} = useTeamSettings();
|
||||||
|
|
||||||
|
function updateTeamSettings() {
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -2,15 +2,21 @@
|
||||||
import { useTeamsStore } from "@/stores/teams";
|
import { useTeamsStore } from "@/stores/teams";
|
||||||
import { useTeamDetails } from "@/composables/team-details";
|
import { useTeamDetails } from "@/composables/team-details";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { RouterLink, RouterView } from "vue-router";
|
import { RouterLink, RouterView, useRouter } from "vue-router";
|
||||||
|
|
||||||
const teamsStore = useTeamsStore();
|
const teamsStore = useTeamsStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
team,
|
team,
|
||||||
teamId,
|
teamId,
|
||||||
} = useTeamDetails();
|
} = useTeamDetails();
|
||||||
|
|
||||||
|
function leaveTeam() {
|
||||||
|
teamsStore.leaveTeam(teamId.value)
|
||||||
|
.then(() => router.push("/"));
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
teamsStore.fetchTeam(teamId.value);
|
teamsStore.fetchTeam(teamId.value);
|
||||||
});
|
});
|
||||||
|
@ -37,6 +43,10 @@ onMounted(() => {
|
||||||
Invites
|
Invites
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<hr>
|
<hr>
|
||||||
|
<button class="destructive-on-hover icon-end" @click="leaveTeam">
|
||||||
|
Leave team
|
||||||
|
<i class="bi bi-box-arrow-left" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="view">
|
<div class="view">
|
||||||
|
@ -96,4 +106,17 @@ nav.sidebar a.tab.router-link-exact-active {
|
||||||
background-color: var(--crust);
|
background-color: var(--crust);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.sidebar button {
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--overlay-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.sidebar button:hover {
|
||||||
|
background-color: var(--crust);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue