feat: Improve site user experience

- Added changes for better mobile responsive UI
- AvailabilityGrid shows players available at the selected time
master
John Montagu, the 4th Earl of Sandvich 2024-12-08 12:10:42 -08:00
parent 36bc19c96d
commit b620470739
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
9 changed files with 76 additions and 22 deletions

View File

@ -9,6 +9,8 @@ const model = defineModel();
const selectedTime = defineModel("selectedTime"); const selectedTime = defineModel("selectedTime");
const selectedIndex = defineModel("selectedIndex");
const hoveredIndex = defineModel("hoveredIndex"); const hoveredIndex = defineModel("hoveredIndex");
const props = defineProps({ const props = defineProps({
@ -156,6 +158,7 @@ function onSlotClick(dayIndex, hour) {
} }
selectedTime.value = getTimeAtCell(dayIndex, hour); selectedTime.value = getTimeAtCell(dayIndex, hour);
scheduleStore.selectIndex(24 * dayIndex + hour);
} }
function onKeyUp($event) { function onKeyUp($event) {

View File

@ -1,9 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type { EventWithPlayerSchema } from "@/client"; import type { EventWithPlayerSchema, TeamSchema } from "@/client";
import EventCard from "./EventCard.vue"; import EventCard from "./EventCard.vue";
const props = defineProps<{ const props = defineProps<{
events: EventWithPlayerSchema[]; events: EventWithPlayerSchema[];
teamContext: TeamSchema;
}>(); }>();
</script> </script>
@ -12,7 +13,20 @@ const props = defineProps<{
<EventCard v-for="event in props.events" :key="event.event.id" :event="event" /> <EventCard v-for="event in props.events" :key="event.event.id" :event="event" />
</div> </div>
<div class="events-list" v-else> <div class="events-list" v-else>
<em class="subtext">No upcoming events.</em> <em class="subtext">
No upcoming events. Create one in the
<router-link
:to="{
name: 'schedule',
query: {
teamId: props.teamContext.id
}
}"
>
schedule
</router-link>
page.
</em>
</div> </div>
</template> </template>

View File

@ -28,7 +28,7 @@ function disableIntegration() {
<template> <template>
<h2>logs.tf Auto-Tracking</h2> <h2>logs.tf Auto-Tracking</h2>
<p>Automatically fetch and track match history from logs.tf.</p> <p>Automatically fetch and track match history from logs.tf. (CURRENTLY NOT IMPLEMENTED)</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>

View File

@ -215,7 +215,7 @@ const rightIndicator = computed(() => {
} }
.player-card > td { .player-card > td {
padding: 1em 2em; padding: 0.5em 1em;
} }
.player-card h3 { .player-card h3 {
@ -314,4 +314,10 @@ a.player-name:hover {
.edit-group > button.editing { .edit-group > button.editing {
opacity: 1; opacity: 1;
} }
@media (max-width: 1024px) {
.player-card > td {
padding: 0.5em 0em;
}
}
</style> </style>

View File

@ -33,10 +33,10 @@ function logout() {
</RouterLink> </RouterLink>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem> <DropdownMenuItem>
<RouterLink class="button" to="/teams"> <RouterLink class="button" to="/schedule">
<button> <button>
<i class="bi bi-people margin" /> <i class="bi bi-calendar-fill margin" />
Teams Schedule
</button> </button>
</RouterLink> </RouterLink>
</DropdownMenuItem> </DropdownMenuItem>

View File

@ -15,11 +15,20 @@ const isTeamTzLocal = computed(() => {
return selectedTimeTz.value.utcOffset() == props.selectedTime.utcOffset(); return selectedTimeTz.value.utcOffset() == props.selectedTime.utcOffset();
}); });
const props = defineProps({ //const props = defineProps({
selectedTime: Object // selectedTime: Object
}); //});
const props = defineProps<{
selectedTime?: moment.Moment;
selectedIndex?: number;
}>();
function scheduleRoster() { function scheduleRoster() {
if (!props.selectedTime) {
return;
}
router.push({ router.push({
name: "roster-builder", name: "roster-builder",
params: { params: {
@ -39,19 +48,20 @@ function scheduleRoster() {
:player="record" :player="record"
/> />
</div> </div>
<h4> <h4 v-if="selectedTime">
<template v-if="selectedTime"> <div>
<div> {{ selectedTime.format("L LT z") }}
{{ selectedTime.format("L LT z") }} </div>
</div> <div v-if="!isTeamTzLocal">
<div v-if="!isTeamTzLocal"> {{ selectedTimeTz.format("L LT z") }}
{{ selectedTimeTz.format("L LT z") }} </div>
</div>
</template>
</h4> </h4>
<button @click="scheduleRoster" v-if="selectedTime"> <button @click="scheduleRoster" v-if="selectedTime">
Schedule for {{ selectedTime.format("L LT") }} Schedule for {{ selectedTime.format("L LT") }}
</button> </button>
<div v-else class="subtext">
<em>Select a time to schedule</em>
</div>
</div> </div>
</template> </template>

View File

@ -7,9 +7,16 @@ const scheduleStore = useScheduleStore();
const hoveredIndex = computed(() => scheduleStore.hoveredIndex); const hoveredIndex = computed(() => scheduleStore.hoveredIndex);
const selectedIndex = computed(() => scheduleStore.selectedIndex);
const availabilityAtHoveredIndex = computed(() => { const availabilityAtHoveredIndex = computed(() => {
if (hoveredIndex.value && props.player?.availability) { if (props.player?.availability) {
return props.player.availability[hoveredIndex.value] ?? 0; if (hoveredIndex.value) {
return props.player.availability[hoveredIndex.value] ?? 0;
}
if (scheduleStore.selectedIndexAvailability[props.player.steamId] != undefined) {
return scheduleStore.selectedIndexAvailability[props.player.steamId] ?? 0;
}
} }
return undefined; return undefined;
}); });

View File

@ -60,7 +60,16 @@ export const useScheduleStore = defineStore("schedule", () => {
const selectedMembers = reactive<{ [id: string]: boolean }>({ }); const selectedMembers = reactive<{ [id: string]: boolean }>({ });
const hoveredIndex: Ref<number | undefined> = ref(); const hoveredIndex = ref<number | undefined>();
const selectedIndexAvailability = ref<{ [id: string]: number; }>({ });
function selectIndex(index: number) {
playerAvailability.value.forEach((value) => {
if (value.availability) {
selectedIndexAvailability.value[value.steamId] = value.availability[index] ?? 0;
}
});
}
const team = ref(); const team = ref();
@ -83,10 +92,12 @@ export const useScheduleStore = defineStore("schedule", () => {
} }
watch(dateStart, () => { watch(dateStart, () => {
selectedIndexAvailability.value = { };
fetchTeamSchedule(); fetchTeamSchedule();
}); });
watch(team, () => { watch(team, () => {
selectedIndexAvailability.value = { };
dateStart.value = getWindowStart(team.value); dateStart.value = getWindowStart(team.value);
console.log(dateStart.value); console.log(dateStart.value);
}); });
@ -145,6 +156,8 @@ export const useScheduleStore = defineStore("schedule", () => {
hoveredMember, hoveredMember,
selectedMembers, selectedMembers,
hoveredIndex, hoveredIndex,
selectedIndexAvailability,
selectIndex,
fetchSchedule, fetchSchedule,
fetchTeamSchedule, fetchTeamSchedule,
saveSchedule, saveSchedule,

View File

@ -27,6 +27,7 @@ const availability = schedule.availability;
const selectionMode = ref(1); const selectionMode = ref(1);
const selectedTime = ref(undefined); const selectedTime = ref(undefined);
const selectedIndex = ref(undefined);
const availabilityOverlay = computed(() => schedule.overlay); const availabilityOverlay = computed(() => schedule.overlay);