Move matches tab to team settings
							parent
							
								
									52d8ea5988
								
							
						
					
					
						commit
						13fd7fdfc0
					
				| 
						 | 
				
			
			@ -21,6 +21,7 @@ export type { EventSchema } from './models/EventSchema';
 | 
			
		|||
export type { EventWithPlayerSchema } from './models/EventWithPlayerSchema';
 | 
			
		||||
export type { EventWithPlayerSchemaList } from './models/EventWithPlayerSchemaList';
 | 
			
		||||
export type { GetEventPlayersResponse } from './models/GetEventPlayersResponse';
 | 
			
		||||
export type { GetMatchQuery } from './models/GetMatchQuery';
 | 
			
		||||
export type { MatchSchema } from './models/MatchSchema';
 | 
			
		||||
export type { PlayerEventRolesSchema } from './models/PlayerEventRolesSchema';
 | 
			
		||||
export type { PlayerRoleSchema } from './models/PlayerRoleSchema';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/* generated using openapi-typescript-codegen -- do not edit */
 | 
			
		||||
/* istanbul ignore file */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
export type GetMatchQuery = {
 | 
			
		||||
    limit: (number | null);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
export type MatchSchema = {
 | 
			
		||||
    blueScore: number;
 | 
			
		||||
    createdAt: string;
 | 
			
		||||
    duration: string;
 | 
			
		||||
    duration: number;
 | 
			
		||||
    logsTfId: number;
 | 
			
		||||
    logsTfTitle: string;
 | 
			
		||||
    matchTime: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,5 +4,6 @@
 | 
			
		|||
/* eslint-disable */
 | 
			
		||||
export type SubmitMatchJson = {
 | 
			
		||||
    matchIds: Array<number>;
 | 
			
		||||
    teamId: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import type { MatchSchema } from './MatchSchema';
 | 
			
		|||
export type TeamMatchSchema = {
 | 
			
		||||
    match: MatchSchema;
 | 
			
		||||
    ourScore: number;
 | 
			
		||||
    teamColor: string;
 | 
			
		||||
    theirScore: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -348,11 +348,13 @@ export class DefaultService {
 | 
			
		|||
    /**
 | 
			
		||||
     * get_matches_for_team <GET>
 | 
			
		||||
     * @param teamId
 | 
			
		||||
     * @param limit
 | 
			
		||||
     * @returns TeamMatchSchemaList OK
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public getMatchesForTeam(
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        limit: (number | null),
 | 
			
		||||
    ): CancelablePromise<TeamMatchSchemaList> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
| 
						 | 
				
			
			@ -360,6 +362,9 @@ export class DefaultService {
 | 
			
		|||
            path: {
 | 
			
		||||
                'team_id': teamId,
 | 
			
		||||
            },
 | 
			
		||||
            query: {
 | 
			
		||||
                'limit': limit,
 | 
			
		||||
            },
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Unprocessable Content`,
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			@ -520,7 +525,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public deleteTeam(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<any> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'DELETE',
 | 
			
		||||
| 
						 | 
				
			
			@ -542,7 +547,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public getTeam(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<ViewTeamResponse> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
| 
						 | 
				
			
			@ -590,8 +595,8 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public editMemberRoles(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        targetPlayerId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        targetPlayerId: number,
 | 
			
		||||
        requestBody?: EditMemberRolesJson,
 | 
			
		||||
    ): CancelablePromise<void> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
| 
						 | 
				
			
			@ -617,7 +622,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public getIntegrations(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<TeamIntegrationSchema> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
| 
						 | 
				
			
			@ -638,7 +643,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public updateIntegrations(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        requestBody?: TeamIntegrationSchema,
 | 
			
		||||
    ): CancelablePromise<TeamIntegrationSchema> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
| 
						 | 
				
			
			@ -661,7 +666,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public getInvites(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<TeamInviteSchemaList> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
| 
						 | 
				
			
			@ -682,7 +687,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public createInvite(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<TeamInviteSchema> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
| 
						 | 
				
			
			@ -704,7 +709,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public revokeInvite(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        key: string,
 | 
			
		||||
    ): CancelablePromise<void> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
| 
						 | 
				
			
			@ -729,8 +734,8 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public createOrUpdatePlayer(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        playerId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        playerId: number,
 | 
			
		||||
        requestBody?: AddPlayerJson,
 | 
			
		||||
    ): CancelablePromise<any> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
| 
						 | 
				
			
			@ -757,8 +762,8 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public removePlayerFromTeam(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        targetPlayerId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        targetPlayerId: number,
 | 
			
		||||
    ): CancelablePromise<any> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'DELETE',
 | 
			
		||||
| 
						 | 
				
			
			@ -781,7 +786,7 @@ export class DefaultService {
 | 
			
		|||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public getTeamMembers(
 | 
			
		||||
        teamId: string,
 | 
			
		||||
        teamId: number,
 | 
			
		||||
    ): CancelablePromise<ViewTeamMembersResponseList> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,13 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useTeamDetails } from "@/composables/team-details";
 | 
			
		||||
import { useMatchesStore } from "@/stores/matches";
 | 
			
		||||
import { DialogClose, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger } from "radix-vue";
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
 | 
			
		||||
const matchesStore = useMatchesStore();
 | 
			
		||||
 | 
			
		||||
const { teamId } = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
const urlsText = ref("");
 | 
			
		||||
 | 
			
		||||
function submit() {
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +18,10 @@ function submit() {
 | 
			
		|||
    })
 | 
			
		||||
    .filter((id) => !isNaN(id));
 | 
			
		||||
 | 
			
		||||
  matchesStore.submitMatches(ids);
 | 
			
		||||
  matchesStore.submitMatches(ids, teamId.value)
 | 
			
		||||
    .then(() => {
 | 
			
		||||
      urlsText.value = "";
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ function disableIntegration() {
 | 
			
		|||
 | 
			
		||||
<template>
 | 
			
		||||
  <h2>logs.tf Auto-Tracking</h2>
 | 
			
		||||
  <p>Automatically fetch and track match history from logs.tf. (CURRENTLY NOT IMPLEMENTED)</p>
 | 
			
		||||
  <p>Automatically fetch and track match history from logs.tf.</p>
 | 
			
		||||
  <div v-if="model">
 | 
			
		||||
    <div class="form-group margin">
 | 
			
		||||
      <h3>logs.tf API key (optional)</h3>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,60 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import type { TeamMatchSchema, TeamSchema } from '@/client';
 | 
			
		||||
import moment from 'moment';
 | 
			
		||||
import { computed } from 'vue';
 | 
			
		||||
 | 
			
		||||
const matchTime = computed(() => moment(props.teamMatch.match.matchTime)
 | 
			
		||||
  .format("LL LT"));
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  team: TeamSchema,
 | 
			
		||||
  teamMatch: TeamMatchSchema,
 | 
			
		||||
}>();
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="match-card">
 | 
			
		||||
    <div class="match-title">
 | 
			
		||||
      <h3>
 | 
			
		||||
        {{ teamMatch.match.logsTfTitle }}
 | 
			
		||||
      </h3>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="match-scores">
 | 
			
		||||
      <div class="team-and-score">
 | 
			
		||||
        <span class="team-name">
 | 
			
		||||
          NVBLU
 | 
			
		||||
          <span v-if="teamMatch.teamColor == 'Blue'">
 | 
			
		||||
            BLU
 | 
			
		||||
          </span>
 | 
			
		||||
        <span class="score">
 | 
			
		||||
          3
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <span class="score">-</span>
 | 
			
		||||
      <div class="team-and-score">
 | 
			
		||||
        <span class="score">
 | 
			
		||||
          2
 | 
			
		||||
        </span>
 | 
			
		||||
        <span class="team-name">
 | 
			
		||||
          <span v-else>
 | 
			
		||||
            RED
 | 
			
		||||
          </span>
 | 
			
		||||
        </span>
 | 
			
		||||
        <span class="score">
 | 
			
		||||
          {{ teamMatch.ourScore }}
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="team-and-score">
 | 
			
		||||
        <span class="score">
 | 
			
		||||
          {{ teamMatch.theirScore }}
 | 
			
		||||
        </span>
 | 
			
		||||
        <span class="team-name">
 | 
			
		||||
          <span v-if="teamMatch.teamColor == 'Blue'">
 | 
			
		||||
            RED
 | 
			
		||||
          </span>
 | 
			
		||||
          <span v-else>
 | 
			
		||||
            BLU
 | 
			
		||||
          </span>
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="bottom-row">
 | 
			
		||||
      <div class="subtext">
 | 
			
		||||
        {{ matchTime }}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div>
 | 
			
		||||
        <a :href="'https://logs.tf/' + teamMatch.match.logsTfId" target="_blank" class="button">
 | 
			
		||||
          #{{ teamMatch.match.logsTfId }}
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +63,29 @@
 | 
			
		|||
<style scoped>
 | 
			
		||||
.match-card {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  padding: 1rem;
 | 
			
		||||
  border: 1px solid var(--text);
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  gap: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.match-title {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 0.5rem;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bottom-row {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bottom-row > div {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  gap: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.match-scores {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,8 +11,8 @@ import TeamSettingsView from "@/views/TeamSettingsView.vue";
 | 
			
		|||
import TeamSettingsGeneralView from "@/views/TeamSettings/GeneralView.vue";
 | 
			
		||||
import TeamSettingsIntegrationsView from "@/views/TeamSettings/IntegrationsView.vue";
 | 
			
		||||
import TeamSettingsInvitesView from "@/views/TeamSettings/InvitesView.vue";
 | 
			
		||||
import TeamSettingsMatchesView from "@/views/TeamSettings/MatchesView.vue";
 | 
			
		||||
import UserSettingsView from "@/views/UserSettingsView.vue";
 | 
			
		||||
import MatchesView from "@/views/MatchesView.vue";
 | 
			
		||||
 | 
			
		||||
const router = createRouter({
 | 
			
		||||
  history: createWebHistory(import.meta.env.BASE_URL),
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +72,11 @@ const router = createRouter({
 | 
			
		|||
          name: "team-settings/invites",
 | 
			
		||||
          component: TeamSettingsInvitesView,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: "matches",
 | 
			
		||||
          name: "team-settings/matches",
 | 
			
		||||
          component: TeamSettingsMatchesView,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,11 +84,6 @@ const router = createRouter({
 | 
			
		|||
      name: "user-settings",
 | 
			
		||||
      component: UserSettingsView,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      path: "/matches",
 | 
			
		||||
      name: "matches",
 | 
			
		||||
      component: MatchesView,
 | 
			
		||||
    },
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,9 @@ export const useMatchesStore = defineStore("matches", () => {
 | 
			
		|||
 | 
			
		||||
  const matches = ref<{ [id: number]: MatchSchema }>({ });
 | 
			
		||||
 | 
			
		||||
  const teamMatches = ref<{ [id: number]: TeamMatchSchema }>({ });
 | 
			
		||||
  const teamMatches = ref<{ [teamId: number]: TeamMatchSchema[] }>({ });
 | 
			
		||||
 | 
			
		||||
  const recentMatches = ref<TeamMatchSchema[]>([]);
 | 
			
		||||
 | 
			
		||||
  function fetchMatches() {
 | 
			
		||||
    return clientStore.call(
 | 
			
		||||
| 
						 | 
				
			
			@ -18,21 +20,52 @@ export const useMatchesStore = defineStore("matches", () => {
 | 
			
		|||
      (response) => {
 | 
			
		||||
        response.forEach((match) => {
 | 
			
		||||
          matches.value[match.match.logsTfId] = match.match;
 | 
			
		||||
          teamMatches.value[match.match.logsTfId] = match;
 | 
			
		||||
          //teamMatches.value[match.match.logsTfId] = match;
 | 
			
		||||
        });
 | 
			
		||||
        return response;
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function submitMatches(logsTfIds: number[]) {
 | 
			
		||||
    return client.default.submitMatch({ matchIds: logsTfIds });
 | 
			
		||||
  function fetchMatchesForTeam(teamId: number) {
 | 
			
		||||
    return clientStore.call(
 | 
			
		||||
      fetchMatchesForTeam.name,
 | 
			
		||||
      () => client.default.getMatchesForTeam(teamId, 1024),
 | 
			
		||||
      (response) => {
 | 
			
		||||
        teamMatches.value[teamId] = [];
 | 
			
		||||
        response.forEach((match) => {
 | 
			
		||||
          matches.value[match.match.logsTfId] = match.match;
 | 
			
		||||
          teamMatches.value[teamId].push(match);
 | 
			
		||||
        });
 | 
			
		||||
        return response;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function fetchRecentMatchesForTeam(teamId: number, limit: number) {
 | 
			
		||||
    return clientStore.call(
 | 
			
		||||
      fetchMatchesForTeam.name,
 | 
			
		||||
      () => client.default.getMatchesForTeam(teamId, limit),
 | 
			
		||||
      (response) => {
 | 
			
		||||
        recentMatches.value = [];
 | 
			
		||||
        response.forEach((match) => {
 | 
			
		||||
          matches.value[match.match.logsTfId] = match.match;
 | 
			
		||||
          recentMatches.value.push(match);
 | 
			
		||||
        });
 | 
			
		||||
        return response;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function submitMatches(logsTfIds: number[], teamId: number) {
 | 
			
		||||
    return client.default.submitMatch({ matchIds: logsTfIds, teamId });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    matches,
 | 
			
		||||
    teamMatches,
 | 
			
		||||
    recentMatches,
 | 
			
		||||
    fetchMatches,
 | 
			
		||||
    fetchMatchesForTeam,
 | 
			
		||||
    fetchRecentMatchesForTeam,
 | 
			
		||||
    submitMatches,
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,10 +9,12 @@ import moment from "moment";
 | 
			
		|||
import EventList from "@/components/EventList.vue";
 | 
			
		||||
import { useTeamsEventsStore } from "@/stores/teams/events";
 | 
			
		||||
import MatchCard from "@/components/MatchCard.vue";
 | 
			
		||||
import { useMatchesStore } from "@/stores/matches";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
const invitesStore = useInvitesStore();
 | 
			
		||||
const matchesStore = useMatchesStore();
 | 
			
		||||
const { team, teamId } = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
const creationDate = computed(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +27,7 @@ const key = computed(() => route.query.key);
 | 
			
		|||
 | 
			
		||||
const teamsEventsStore = useTeamsEventsStore();
 | 
			
		||||
const events = computed(() => teamsEventsStore.teamEvents[teamId.value]);
 | 
			
		||||
const matches = computed(() => matchesStore.recentMatches);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  let doFetchTeam = () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +35,7 @@ onMounted(() => {
 | 
			
		|||
      .then(() => {
 | 
			
		||||
        teamsStore.fetchTeamMembers(teamId.value);
 | 
			
		||||
        teamsEventsStore.fetchTeamEvents(teamId.value);
 | 
			
		||||
        matchesStore.fetchRecentMatchesForTeam(teamId.value, 5);
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,14 +80,21 @@ onMounted(() => {
 | 
			
		|||
          <EventList :events="events" :team-context="team" />
 | 
			
		||||
          <h2 id="recent-matches-header">
 | 
			
		||||
            Recent Matches
 | 
			
		||||
            <!--RouterLink class="button" to="/">
 | 
			
		||||
            <RouterLink class="button" :to="{ name: 'team-settings/matches' }">
 | 
			
		||||
              <button class="icon" v-tooltip="'View all'">
 | 
			
		||||
                <i class="bi bi-arrow-right-circle-fill"></i>
 | 
			
		||||
              </button>
 | 
			
		||||
            </RouterLink-->
 | 
			
		||||
            </RouterLink>
 | 
			
		||||
          </h2>
 | 
			
		||||
          <em class="subtext" v-if="true">No recent matches.</em>
 | 
			
		||||
          <MatchCard v-else />
 | 
			
		||||
          <em class="subtext" v-if="!matches">
 | 
			
		||||
            No recent matches.
 | 
			
		||||
          </em>
 | 
			
		||||
          <MatchCard
 | 
			
		||||
            v-else
 | 
			
		||||
            v-for="match in matches"
 | 
			
		||||
            :team-match="match"
 | 
			
		||||
            :team="team"
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,21 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import AddMatchDialog from "@/components/AddMatchDialog.vue";
 | 
			
		||||
import { useTeamDetails } from "@/composables/team-details";
 | 
			
		||||
import { useMatchesStore } from "@/stores/matches";
 | 
			
		||||
import { onMounted } from "vue";
 | 
			
		||||
import { useTeamsStore } from "@/stores/teams";
 | 
			
		||||
import moment from "moment";
 | 
			
		||||
import { computed, onMounted } from "vue";
 | 
			
		||||
 | 
			
		||||
const matchesStore = useMatchesStore();
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
 | 
			
		||||
const { team, teamId } = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
const matches = computed(() => matchesStore.teamMatches[teamId.value]);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  matchesStore.fetchMatches();
 | 
			
		||||
  teamsStore.fetchTeam(teamId.value)
 | 
			
		||||
    .then(() => matchesStore.fetchMatchesForTeam(teamId.value));
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +24,7 @@ onMounted(() => {
 | 
			
		|||
    <div class="header">
 | 
			
		||||
      <h1>
 | 
			
		||||
        <i class="bi bi-trophy-fill margin"></i>
 | 
			
		||||
        Matches you've played
 | 
			
		||||
        Matches
 | 
			
		||||
      </h1>
 | 
			
		||||
      <div class="button-group">
 | 
			
		||||
        <AddMatchDialog />
 | 
			
		||||
| 
						 | 
				
			
			@ -26,18 +35,20 @@ onMounted(() => {
 | 
			
		|||
        <tr>
 | 
			
		||||
          <th>RED</th>
 | 
			
		||||
          <th>BLU</th>
 | 
			
		||||
          <th>Team</th>
 | 
			
		||||
          <th>Match Date</th>
 | 
			
		||||
          <th>logs.tf URL</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody>
 | 
			
		||||
        <tr v-for="match in matchesStore.matches" :key="match.logsTfId">
 | 
			
		||||
          <td>{{ match.redScore }}</td>
 | 
			
		||||
          <td>{{ match.blueScore }}</td>
 | 
			
		||||
          <td>{{ match.matchTime }}</td>
 | 
			
		||||
        <tr v-for="teamMatch in matches">
 | 
			
		||||
          <td>{{ teamMatch.match.redScore }}</td>
 | 
			
		||||
          <td>{{ teamMatch.match.blueScore }}</td>
 | 
			
		||||
          <td>{{ teamMatch.teamColor == 'Blue' ? 'BLU' : 'RED' }}</td>
 | 
			
		||||
          <td>{{ moment(teamMatch.match.matchTime).format("LL LT") }}</td>
 | 
			
		||||
          <td>
 | 
			
		||||
            <a :href="`https://logs.tf/${match.logsTfId}`" target="_blank">
 | 
			
		||||
              #{{ match.logsTfId }}
 | 
			
		||||
            <a :href="`https://logs.tf/${teamMatch.match.logsTfId}`" target="_blank">
 | 
			
		||||
              #{{ teamMatch.match.logsTfId }}
 | 
			
		||||
            </a>
 | 
			
		||||
          </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +42,9 @@ onMounted(() => {
 | 
			
		|||
        <RouterLink class="tab" :to="{ name: 'team-settings/invites' }">
 | 
			
		||||
          Invites
 | 
			
		||||
        </RouterLink>
 | 
			
		||||
        <RouterLink class="tab" :to="{ name: 'team-settings/matches' }">
 | 
			
		||||
          Matches
 | 
			
		||||
        </RouterLink>
 | 
			
		||||
        <hr>
 | 
			
		||||
        <button class="destructive-on-hover icon-end" @click="leaveTeam">
 | 
			
		||||
          Leave team
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
from typing import Optional
 | 
			
		||||
from flask import Blueprint, abort
 | 
			
		||||
from pydantic.v1 import validator
 | 
			
		||||
#from pydantic.functional_validators import field_validator
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +42,7 @@ def get_match(player: Player, match_id: int, **_):
 | 
			
		|||
 | 
			
		||||
class SubmitMatchJson(BaseModel):
 | 
			
		||||
    match_ids: list[int]
 | 
			
		||||
    team_id: int
 | 
			
		||||
 | 
			
		||||
    @validator("match_ids")
 | 
			
		||||
    @classmethod
 | 
			
		||||
| 
						 | 
				
			
			@ -59,15 +61,13 @@ class SubmitMatchJson(BaseModel):
 | 
			
		|||
)
 | 
			
		||||
@requires_authentication
 | 
			
		||||
def submit_match(json: SubmitMatchJson, **_):
 | 
			
		||||
    import sys
 | 
			
		||||
    print(json, file=sys.stderr)
 | 
			
		||||
    if json.match_ids is None:
 | 
			
		||||
        print("json.match_ids is None", file=sys.stderr)
 | 
			
		||||
 | 
			
		||||
    for id in json.match_ids:
 | 
			
		||||
        load_specific_match.delay(id, None)
 | 
			
		||||
        load_specific_match.delay(id, json.team_id)
 | 
			
		||||
    return { }, 204
 | 
			
		||||
 | 
			
		||||
class GetMatchQuery(BaseModel):
 | 
			
		||||
    limit: Optional[int]
 | 
			
		||||
 | 
			
		||||
@api_match.get("/team/<int:team_id>")
 | 
			
		||||
@spec.validate(
 | 
			
		||||
    resp=Response(
 | 
			
		||||
| 
						 | 
				
			
			@ -77,14 +77,19 @@ def submit_match(json: SubmitMatchJson, **_):
 | 
			
		|||
)
 | 
			
		||||
@requires_authentication
 | 
			
		||||
@requires_team_membership()
 | 
			
		||||
def get_matches_for_team(team_id: Team, **_):
 | 
			
		||||
    matches = (
 | 
			
		||||
def get_matches_for_team(team_id: Team, query: GetMatchQuery, **_):
 | 
			
		||||
    q = (
 | 
			
		||||
        db.session.query(TeamMatch)
 | 
			
		||||
        .where(TeamMatch.team_id == team_id)
 | 
			
		||||
        .options(joinedload(TeamMatch.match))
 | 
			
		||||
        .all()
 | 
			
		||||
        .order_by(TeamMatch.match_id.desc())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if query and query.limit:
 | 
			
		||||
        q = q.limit(query.limit)
 | 
			
		||||
 | 
			
		||||
    matches = q.all()
 | 
			
		||||
 | 
			
		||||
    return [TeamMatchSchema.from_model(match).dict(by_alias=True) for match in matches], 200
 | 
			
		||||
 | 
			
		||||
@api_match.get("/player")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@ class TeamMatchSchema(spec.BaseModel):
 | 
			
		|||
    match: "MatchSchema"
 | 
			
		||||
    our_score: int
 | 
			
		||||
    their_score: int
 | 
			
		||||
    team_color: str
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_model(cls, model: "TeamMatch"):
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +30,7 @@ class TeamMatchSchema(spec.BaseModel):
 | 
			
		|||
            match=MatchSchema.from_model(model.match),
 | 
			
		||||
            our_score=our_score,
 | 
			
		||||
            their_score=their_score,
 | 
			
		||||
            team_color=model.team_color,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,8 @@ services:
 | 
			
		|||
      context: ./availabili.tf
 | 
			
		||||
    environment:
 | 
			
		||||
      VITE_API_URL: http://localhost:8000  # API endpoint
 | 
			
		||||
    ports:
 | 
			
		||||
      - 5173:5173
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./availabili.tf:/app
 | 
			
		||||
    networks:
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +64,7 @@ services:
 | 
			
		|||
    ports:
 | 
			
		||||
      - "8000:80"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./nginx:/etc/nginx/conf.d
 | 
			
		||||
      - ./nginx/development.conf:/etc/nginx/nginx.conf
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - backend
 | 
			
		||||
      - frontend
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
server {
 | 
			
		||||
    listen 80;
 | 
			
		||||
 | 
			
		||||
    # Proxy for the Vite frontend
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://frontend:5173;
 | 
			
		||||
        proxy_set_header Host $host;
 | 
			
		||||
 | 
			
		||||
        proxy_http_version 1.1;
 | 
			
		||||
        proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
        proxy_set_header Connection "upgrade";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Proxy for the Flask backend API
 | 
			
		||||
    location /api/ {
 | 
			
		||||
        proxy_pass http://backend:5000;
 | 
			
		||||
        proxy_set_header Host $host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    location /apidoc/ {
 | 
			
		||||
        proxy_pass http://backend:5000;
 | 
			
		||||
        proxy_set_header Host $host;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
events {
 | 
			
		||||
    worker_connections 1024;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
http {
 | 
			
		||||
    server {
 | 
			
		||||
        listen 80;
 | 
			
		||||
 | 
			
		||||
        # Proxy for the Vite frontend
 | 
			
		||||
        location / {
 | 
			
		||||
            proxy_pass http://frontend:5173;
 | 
			
		||||
            proxy_set_header Host $host;
 | 
			
		||||
 | 
			
		||||
            proxy_http_version 1.1;
 | 
			
		||||
            proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
            proxy_set_header Connection "upgrade";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        # Proxy for the Flask backend API
 | 
			
		||||
        location /api/ {
 | 
			
		||||
            proxy_pass http://backend:5000;
 | 
			
		||||
            proxy_set_header Host $host;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        location /apidoc/ {
 | 
			
		||||
            proxy_pass http://backend:5000;
 | 
			
		||||
            proxy_set_header Host $host;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue