feat(frontend): Implement matches
parent
45ac071a7f
commit
caaee983f2
|
@ -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 { MatchSchema } from './models/MatchSchema';
|
||||
export type { PlayerEventRolesSchema } from './models/PlayerEventRolesSchema';
|
||||
export type { PlayerRoleSchema } from './models/PlayerRoleSchema';
|
||||
export type { PlayerSchema } from './models/PlayerSchema';
|
||||
|
@ -28,11 +29,14 @@ export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvaila
|
|||
export type { PutScheduleForm } from './models/PutScheduleForm';
|
||||
export type { RoleSchema } from './models/RoleSchema';
|
||||
export type { SetUsernameJson } from './models/SetUsernameJson';
|
||||
export type { SubmitMatchJson } from './models/SubmitMatchJson';
|
||||
export type { TeamDiscordIntegrationSchema } from './models/TeamDiscordIntegrationSchema';
|
||||
export type { TeamIntegrationSchema } from './models/TeamIntegrationSchema';
|
||||
export type { TeamInviteSchema } from './models/TeamInviteSchema';
|
||||
export type { TeamInviteSchemaList } from './models/TeamInviteSchemaList';
|
||||
export type { TeamLogsTfIntegrationSchema } from './models/TeamLogsTfIntegrationSchema';
|
||||
export type { TeamMatchSchema } from './models/TeamMatchSchema';
|
||||
export type { TeamMatchSchemaList } from './models/TeamMatchSchemaList';
|
||||
export { TeamRole } from './models/TeamRole';
|
||||
export type { TeamSchema } from './models/TeamSchema';
|
||||
export type { TeamWithRoleSchema } from './models/TeamWithRoleSchema';
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type MatchSchema = {
|
||||
blueScore: number;
|
||||
createdAt: string;
|
||||
duration: string;
|
||||
logsTfId: number;
|
||||
logsTfTitle: string;
|
||||
matchTime: string;
|
||||
redScore: number;
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type SubmitMatchJson = {
|
||||
matchIds: Array<number>;
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { MatchSchema } from './MatchSchema';
|
||||
export type TeamMatchSchema = {
|
||||
match: MatchSchema;
|
||||
ourScore: number;
|
||||
theirScore: number;
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { TeamMatchSchema } from './TeamMatchSchema';
|
||||
export type TeamMatchSchemaList = Array<TeamMatchSchema>;
|
|
@ -12,12 +12,15 @@ import type { EventSchema } from '../models/EventSchema';
|
|||
import type { EventWithPlayerSchema } from '../models/EventWithPlayerSchema';
|
||||
import type { EventWithPlayerSchemaList } from '../models/EventWithPlayerSchemaList';
|
||||
import type { GetEventPlayersResponse } from '../models/GetEventPlayersResponse';
|
||||
import type { MatchSchema } from '../models/MatchSchema';
|
||||
import type { PlayerSchema } from '../models/PlayerSchema';
|
||||
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
||||
import type { SetUsernameJson } from '../models/SetUsernameJson';
|
||||
import type { SubmitMatchJson } from '../models/SubmitMatchJson';
|
||||
import type { TeamIntegrationSchema } from '../models/TeamIntegrationSchema';
|
||||
import type { TeamInviteSchema } from '../models/TeamInviteSchema';
|
||||
import type { TeamInviteSchemaList } from '../models/TeamInviteSchemaList';
|
||||
import type { TeamMatchSchemaList } from '../models/TeamMatchSchemaList';
|
||||
import type { TeamSchema } from '../models/TeamSchema';
|
||||
import type { UpdateEventJson } from '../models/UpdateEventJson';
|
||||
import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
|
||||
|
@ -289,6 +292,79 @@ export class DefaultService {
|
|||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* submit_match <PUT>
|
||||
* @param requestBody
|
||||
* @returns void
|
||||
* @throws ApiError
|
||||
*/
|
||||
public submitMatch(
|
||||
requestBody?: SubmitMatchJson,
|
||||
): CancelablePromise<void> {
|
||||
return this.httpRequest.request({
|
||||
method: 'PUT',
|
||||
url: '/api/match/',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Unprocessable Content`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* get_match <GET>
|
||||
* @param matchId
|
||||
* @returns MatchSchema OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getApiMatchIdMatchId(
|
||||
matchId: number,
|
||||
): CancelablePromise<MatchSchema> {
|
||||
return this.httpRequest.request({
|
||||
method: 'GET',
|
||||
url: '/api/match/id/{match_id}',
|
||||
path: {
|
||||
'match_id': matchId,
|
||||
},
|
||||
errors: {
|
||||
422: `Unprocessable Content`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* get_matches_for_player_teams <GET>
|
||||
* @returns TeamMatchSchemaList OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getMatchesForPlayerTeams(): CancelablePromise<TeamMatchSchemaList> {
|
||||
return this.httpRequest.request({
|
||||
method: 'GET',
|
||||
url: '/api/match/player',
|
||||
errors: {
|
||||
422: `Unprocessable Content`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* get_matches_for_team <GET>
|
||||
* @param teamId
|
||||
* @returns TeamMatchSchemaList OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public getMatchesForTeam(
|
||||
teamId: number,
|
||||
): CancelablePromise<TeamMatchSchemaList> {
|
||||
return this.httpRequest.request({
|
||||
method: 'GET',
|
||||
url: '/api/match/team/{team_id}',
|
||||
path: {
|
||||
'team_id': teamId,
|
||||
},
|
||||
errors: {
|
||||
422: `Unprocessable Content`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* get <GET>
|
||||
* @param windowStart
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
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 urlsText = ref("");
|
||||
|
||||
function submit() {
|
||||
const ids = urlsText.value.split("\n")
|
||||
.map((url) => {
|
||||
const matchId = url.match(/logs\.tf\/(\d+)/);
|
||||
return matchId ? Number(matchId[1]) : NaN;
|
||||
})
|
||||
.filter((id) => !isNaN(id));
|
||||
|
||||
matchesStore.submitMatches(ids);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogRoot>
|
||||
<DialogTrigger>
|
||||
<i class="bi bi-file-earmark-plus-fill margin" />
|
||||
Submit logs.tf matches
|
||||
</DialogTrigger>
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="dialog-overlay" />
|
||||
<DialogContent>
|
||||
<DialogTitle>Submit logs.tf matches</DialogTitle>
|
||||
<DialogDescription>
|
||||
<p>
|
||||
Enter up to 10 logs.tf URLs (or match IDs) to submit them. This
|
||||
allows you to track your match stats and view them later.
|
||||
</p>
|
||||
</DialogDescription>
|
||||
<div class="form-group margin">
|
||||
<h3>logs.tf URLs</h3>
|
||||
<textarea
|
||||
v-model="urlsText"
|
||||
placeholder="Paste logs.tf URLs here (limit: 10)"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="action-buttons">
|
||||
<DialogClose class="accent" aria-label="Close" @click="submit">
|
||||
<i class="bi bi-check" />
|
||||
Submit
|
||||
</DialogClose>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</DialogRoot>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
[role="dialog"] {
|
||||
padding: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
|
@ -12,6 +12,7 @@ import TeamSettingsGeneralView from "@/views/TeamSettings/GeneralView.vue";
|
|||
import TeamSettingsIntegrationsView from "@/views/TeamSettings/IntegrationsView.vue";
|
||||
import TeamSettingsInvitesView from "@/views/TeamSettings/InvitesView.vue";
|
||||
import UserSettingsView from "@/views/UserSettingsView.vue";
|
||||
import MatchesView from "@/views/MatchesView.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
|
@ -78,6 +79,11 @@ const router = createRouter({
|
|||
name: "user-settings",
|
||||
component: UserSettingsView,
|
||||
},
|
||||
{
|
||||
path: "/matches",
|
||||
name: "matches",
|
||||
component: MatchesView,
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import type { MatchSchema, TeamMatchSchema } from "@/client";
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { useClientStore } from "./client";
|
||||
|
||||
export const useMatchesStore = defineStore("matches", () => {
|
||||
const clientStore = useClientStore();
|
||||
const client = clientStore.client;
|
||||
|
||||
const matches = ref<{ [id: number]: MatchSchema }>({ });
|
||||
|
||||
const teamMatches = ref<{ [id: number]: TeamMatchSchema }>({ });
|
||||
|
||||
function fetchMatches() {
|
||||
return clientStore.call(
|
||||
fetchMatches.name,
|
||||
() => client.default.getMatchesForPlayerTeams(),
|
||||
(response) => {
|
||||
response.forEach((match) => {
|
||||
matches.value[match.match.logsTfId] = match.match;
|
||||
teamMatches.value[match.match.logsTfId] = match;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function submitMatches(logsTfIds: number[]) {
|
||||
return client.default.submitMatch({ matchIds: logsTfIds });
|
||||
}
|
||||
|
||||
return {
|
||||
matches,
|
||||
teamMatches,
|
||||
fetchMatches,
|
||||
submitMatches,
|
||||
}
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
<script setup lang="ts">
|
||||
import AddMatchDialog from "@/components/AddMatchDialog.vue";
|
||||
import { useMatchesStore } from "@/stores/matches";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const matchesStore = useMatchesStore();
|
||||
|
||||
onMounted(() => {
|
||||
matchesStore.fetchMatches();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<div class="header">
|
||||
<h1>
|
||||
<i class="bi bi-trophy-fill margin"></i>
|
||||
Matches you've played
|
||||
</h1>
|
||||
<div class="button-group">
|
||||
<AddMatchDialog />
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<th>BLU</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>
|
||||
<td>
|
||||
<a :href="`https://logs.tf/${match.logsTfId}`" target="_blank">
|
||||
#{{ match.logsTfId }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
font-weight: 800;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue