Improve team page usability

master
John Montagu, the 4th Earl of Sandvich 2024-11-18 11:59:13 -08:00
parent abc456b7d1
commit 9b2153266a
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
9 changed files with 129 additions and 29 deletions

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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);
} }

View File

@ -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;
} }
); );

View File

@ -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,

View File

@ -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>

View File

@ -7,6 +7,10 @@ const {
timezone, timezone,
minuteOffset, minuteOffset,
} = useTeamSettings(); } = useTeamSettings();
function updateTeamSettings() {
}
</script> </script>
<template> <template>

View File

@ -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>