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,28 +34,56 @@ 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
 | 
				
			||||||
      <span v-if="availabilityAtHoveredIndex == 1" class="can-be-available">
 | 
					      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">
 | 
				
			||||||
 | 
					          {{ player.username }}
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					        <span v-else class="available">
 | 
				
			||||||
 | 
					          {{ player.username }}
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					      <s v-else-if="availabilityAtHoveredIndex == 0">
 | 
				
			||||||
 | 
					        {{ player.username }}
 | 
				
			||||||
 | 
					      </s>
 | 
				
			||||||
 | 
					      <span v-else>
 | 
				
			||||||
        {{ player.username }}
 | 
					        {{ player.username }}
 | 
				
			||||||
      </span>
 | 
					      </span>
 | 
				
			||||||
      <span v-else class="available">
 | 
					    </label>
 | 
				
			||||||
        {{ player.username }}
 | 
					 | 
				
			||||||
      </span>
 | 
					 | 
				
			||||||
    </span>
 | 
					 | 
				
			||||||
    <s v-else-if="availabilityAtHoveredIndex == 0">
 | 
					 | 
				
			||||||
      {{ player.username }}
 | 
					 | 
				
			||||||
    </s>
 | 
					 | 
				
			||||||
    <span v-else>
 | 
					 | 
				
			||||||
      {{ player.username }}
 | 
					 | 
				
			||||||
    </span>
 | 
					 | 
				
			||||||
  </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