Added editing and viewing roles
parent
708aafed9e
commit
5d620c07ed
|
@ -10,6 +10,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"bootstrap-icons": "^1.11.3",
|
"bootstrap-icons": "^1.11.3",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"moment-timezone": "^0.5.46",
|
||||||
"pinia": "^2.2.4",
|
"pinia": "^2.2.4",
|
||||||
"v-tooltip": "^2.1.3",
|
"v-tooltip": "^2.1.3",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
|
@ -5138,6 +5140,27 @@
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment-timezone": {
|
||||||
|
"version": "0.5.46",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz",
|
||||||
|
"integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|
|
@ -12,11 +12,13 @@
|
||||||
"type-check": "vue-tsc --build --force",
|
"type-check": "vue-tsc --build --force",
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"openapi-generate": "openapi --input 'http://localhost:5000/apidoc/openapi.json' --output src/client --name AvailabilitfClient"
|
"openapi-generate": "openapi --input 'http://localhost:8000/apidoc/openapi.json' --output src/client --name AvailabilitfClient"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"bootstrap-icons": "^1.11.3",
|
"bootstrap-icons": "^1.11.3",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"moment-timezone": "^0.5.46",
|
||||||
"pinia": "^2.2.4",
|
"pinia": "^2.2.4",
|
||||||
"v-tooltip": "^2.1.3",
|
"v-tooltip": "^2.1.3",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
|
|
|
@ -12,6 +12,7 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||||
|
|
||||||
export type { AddPlayerJson } from './models/AddPlayerJson';
|
export type { AddPlayerJson } from './models/AddPlayerJson';
|
||||||
export type { CreateTeamJson } from './models/CreateTeamJson';
|
export type { CreateTeamJson } from './models/CreateTeamJson';
|
||||||
|
export type { EditMemberRolesJson } from './models/EditMemberRolesJson';
|
||||||
export type { PutScheduleForm } from './models/PutScheduleForm';
|
export type { PutScheduleForm } from './models/PutScheduleForm';
|
||||||
export type { RoleSchema } from './models/RoleSchema';
|
export type { RoleSchema } from './models/RoleSchema';
|
||||||
export { TeamRole } from './models/TeamRole';
|
export { TeamRole } from './models/TeamRole';
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { RoleSchema } from './RoleSchema';
|
||||||
|
export type EditMemberRolesJson = {
|
||||||
|
roles: Array<RoleSchema>;
|
||||||
|
};
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { RoleSchema } from './RoleSchema';
|
import type { RoleSchema } from './RoleSchema';
|
||||||
export type ViewTeamMembersResponse = {
|
export type ViewTeamMembersResponse = {
|
||||||
|
availability: number;
|
||||||
|
createdAt: string;
|
||||||
|
playtime: number;
|
||||||
roles: Array<RoleSchema>;
|
roles: Array<RoleSchema>;
|
||||||
steamId: string;
|
steamId: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { AddPlayerJson } from '../models/AddPlayerJson';
|
import type { AddPlayerJson } from '../models/AddPlayerJson';
|
||||||
import type { CreateTeamJson } from '../models/CreateTeamJson';
|
import type { CreateTeamJson } from '../models/CreateTeamJson';
|
||||||
|
import type { EditMemberRolesJson } from '../models/EditMemberRolesJson';
|
||||||
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
||||||
import type { ViewScheduleResponse } from '../models/ViewScheduleResponse';
|
import type { ViewScheduleResponse } from '../models/ViewScheduleResponse';
|
||||||
import type { ViewTeamMembersResponseList } from '../models/ViewTeamMembersResponseList';
|
import type { ViewTeamMembersResponseList } from '../models/ViewTeamMembersResponseList';
|
||||||
|
@ -210,6 +211,35 @@ export class DefaultService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* edit_member_roles <PATCH>
|
||||||
|
* @param teamId
|
||||||
|
* @param targetPlayerId
|
||||||
|
* @param requestBody
|
||||||
|
* @returns void
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public editMemberRoles(
|
||||||
|
teamId: string,
|
||||||
|
targetPlayerId: string,
|
||||||
|
requestBody?: EditMemberRolesJson,
|
||||||
|
): CancelablePromise<void> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: '/api/team/id/{team_id}/edit-player/{target_player_id}',
|
||||||
|
path: {
|
||||||
|
'team_id': teamId,
|
||||||
|
'target_player_id': targetPlayerId,
|
||||||
|
},
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
403: `Forbidden`,
|
||||||
|
404: `Not Found`,
|
||||||
|
422: `Unprocessable Entity`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* add_player <PUT>
|
* add_player <PUT>
|
||||||
* @param teamId
|
* @param teamId
|
||||||
|
|
|
@ -1,14 +1,74 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PlayerTeamRole } from "../player";
|
import type { PlayerTeamRole } from "../player";
|
||||||
import { computed, type PropType } from "vue";
|
import { computed, type PropType, ref, watch } from "vue";
|
||||||
|
import { useTeamsStore } from "../stores/teams";
|
||||||
import { useRosterStore } from "../stores/roster";
|
import { useRosterStore } from "../stores/roster";
|
||||||
import { type ViewTeamMembersResponse } from "@/client";
|
import { type ViewTeamMembersResponse, type TeamSchema } from "@/client";
|
||||||
|
import RoleTag from "../components/RoleTag.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
player: Object as PropType<ViewTeamMembersResponse>,
|
player: Object as PropType<ViewTeamMembersResponse>,
|
||||||
|
team: Object as PropType<TeamSchema>,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const teamsStore = useTeamsStore();
|
||||||
|
|
||||||
const rosterStore = useRosterStore();
|
const rosterStore = useRosterStore();
|
||||||
|
|
||||||
|
//const roles = computed({
|
||||||
|
// get: () => ({
|
||||||
|
// "PocketScout": "",
|
||||||
|
// "FlankScout": "",
|
||||||
|
// "PocketSoldier": "",
|
||||||
|
// "Roamer": "",
|
||||||
|
// "Demoman": "",
|
||||||
|
// "Medic": "",
|
||||||
|
// }),
|
||||||
|
//});
|
||||||
|
|
||||||
|
const isEditing = ref(false);
|
||||||
|
|
||||||
|
// this is the roles of the player we are editing
|
||||||
|
const roles = ref([]);
|
||||||
|
const updatedRoles = ref([]);
|
||||||
|
|
||||||
|
//const rolesMap = reactive({
|
||||||
|
// "Role.PocketScout": undefined,
|
||||||
|
// "Role.FlankScout": undefined,
|
||||||
|
// "Role.PocketSoldier": undefined,
|
||||||
|
// "Role.Roamer": undefined,
|
||||||
|
// "Role.Demoman": undefined,
|
||||||
|
// "Role.Medic": undefined,
|
||||||
|
//});
|
||||||
|
|
||||||
|
const possibleRoles = [
|
||||||
|
"PocketScout",
|
||||||
|
"FlankScout",
|
||||||
|
"PocketSoldier",
|
||||||
|
"Roamer",
|
||||||
|
"Demoman",
|
||||||
|
"Medic",
|
||||||
|
];
|
||||||
|
|
||||||
|
watch(isEditing, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
// editing
|
||||||
|
roles.value = possibleRoles.map((roleName) => {
|
||||||
|
console.log(roleName);
|
||||||
|
return props.player.roles
|
||||||
|
.find((playerRole) => playerRole.role == roleName) ?? undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateRoles() {
|
||||||
|
isEditing.value = false;
|
||||||
|
updatedRoles.value = roles.value.filter(x => x);
|
||||||
|
props.player.roles = updatedRoles.value;
|
||||||
|
console.log(roles.value);
|
||||||
|
console.log(updatedRoles.value);
|
||||||
|
teamsStore.updateRoles(props.team.id, props.player.steamId, updatedRoles.value);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -23,24 +83,39 @@ const rosterStore = useRosterStore();
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="role-icons flex-middle">
|
<div class="role-icons flex-middle">
|
||||||
|
<div class="role-buttons" v-if="isEditing">
|
||||||
|
<RoleTag
|
||||||
|
v-for="role, i in possibleRoles"
|
||||||
|
:role="role"
|
||||||
|
:player="player"
|
||||||
|
v-model="roles[i]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
<i
|
<i
|
||||||
v-for="role in player.roles"
|
v-for="role in player.roles"
|
||||||
:class="{
|
:class="{
|
||||||
[rosterStore.roleIcons[role.role]]: true,
|
[rosterStore.roleIcons[role.role]]: true,
|
||||||
main: role.is_main,
|
main: role.isMain,
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ player.playtime.toFixed(1) }} hours
|
{{ player.playtime.toFixed(1) }} hours
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ new Date(player.created_at).toLocaleString() }}
|
{{ new Date(player.createdAt).toLocaleString() }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="edit-group">
|
<div class="edit-group">
|
||||||
<button>
|
<template v-if="isEditing">
|
||||||
|
<button class="editing" @click="updateRoles()">
|
||||||
|
<i class="bi bi-check-lg" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<button v-else @click="isEditing = true">
|
||||||
<i class="bi bi-pencil-fill edit-icon" />
|
<i class="bi bi-pencil-fill edit-icon" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,7 +169,7 @@ const rosterStore = useRosterStore();
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-icons {
|
.role-icons i {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
color: var(--overlay-0);
|
color: var(--overlay-0);
|
||||||
|
@ -104,6 +179,12 @@ const rosterStore = useRosterStore();
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.role-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-group {
|
.edit-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
@ -122,4 +203,8 @@ const rosterStore = useRosterStore();
|
||||||
.player-card:hover .edit-group > button {
|
.player-card:hover .edit-group > button {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-group > button.editing {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type ViewTeamMembersResponse } from "@/client";
|
||||||
|
import { useRosterStore } from "../stores/roster";
|
||||||
|
|
||||||
|
const rosterStore = useRosterStore();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
role: String,
|
||||||
|
player: Object as PropType<ViewTeamMembersResponse>,
|
||||||
|
});
|
||||||
|
|
||||||
|
const roleObject = defineModel();
|
||||||
|
|
||||||
|
function toggle(isMain) {
|
||||||
|
if (isMain == roleObject.value?.isMain) {
|
||||||
|
roleObject.value = undefined;
|
||||||
|
} else {
|
||||||
|
if (!roleObject.value) {
|
||||||
|
// create a new role object
|
||||||
|
roleObject.value = {
|
||||||
|
role: props.role,
|
||||||
|
isMain: isMain
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
roleObject.value.isMain = isMain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="role">
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'role-info': true,
|
||||||
|
'unselected': !roleObject,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
:class="{
|
||||||
|
[rosterStore.roleIcons[role]]: true,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{{ rosterStore.roleNames[role] }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
:class="{
|
||||||
|
'center': true,
|
||||||
|
'selected': roleObject?.isMain
|
||||||
|
}"
|
||||||
|
@click="toggle(true)"
|
||||||
|
>
|
||||||
|
Main
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="{
|
||||||
|
'right': true,
|
||||||
|
'selected': !(roleObject?.isMain ?? true)
|
||||||
|
}"
|
||||||
|
@click="toggle(false)"
|
||||||
|
>
|
||||||
|
Alternate
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.role {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--mantle);
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
font-size: 10pt;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-info.unselected {
|
||||||
|
color: var(--overlay-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role button {
|
||||||
|
font-size: 10pt;
|
||||||
|
background-color: var(--mantle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role button.center {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role button.right {
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role button.selected {
|
||||||
|
background-color: var(--accent-transparent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role i {
|
||||||
|
line-height: unset;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -27,7 +27,7 @@ onMounted(() => {
|
||||||
v-for="team in teams.teams"
|
v-for="team in teams.teams"
|
||||||
>
|
>
|
||||||
<RouterLink :to="'/team/id/' + team.id">
|
<RouterLink :to="'/team/id/' + team.id">
|
||||||
{{ team.team_name }}
|
{{ team.teamName }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -177,13 +177,15 @@ export const useRosterStore = defineStore("roster", () => {
|
||||||
"Roamer": "tf2-FlankSoldier",
|
"Roamer": "tf2-FlankSoldier",
|
||||||
"Demoman": "tf2-Demo",
|
"Demoman": "tf2-Demo",
|
||||||
"Medic": "tf2-Medic",
|
"Medic": "tf2-Medic",
|
||||||
|
});
|
||||||
|
|
||||||
"Role.PocketScout": "tf2-PocketScout",
|
const roleNames = reactive({
|
||||||
"Role.FlankScout": "tf2-FlankScout",
|
"PocketScout": "Pocket Scout",
|
||||||
"Role.PocketSoldier": "tf2-PocketSoldier",
|
"FlankScout": "Flank Scout",
|
||||||
"Role.Roamer": "tf2-FlankSoldier",
|
"PocketSoldier": "Pocket Soldier",
|
||||||
"Role.Demoman": "tf2-Demo",
|
"Roamer": "Roamer",
|
||||||
"Role.Medic": "tf2-Medic",
|
"Demoman": "Demoman",
|
||||||
|
"Medic": "Medic",
|
||||||
});
|
});
|
||||||
|
|
||||||
function selectPlayerForRole(player: PlayerTeamRole, role: string) {
|
function selectPlayerForRole(player: PlayerTeamRole, role: string) {
|
||||||
|
@ -211,6 +213,7 @@ export const useRosterStore = defineStore("roster", () => {
|
||||||
definitelyAvailable,
|
definitelyAvailable,
|
||||||
canBeAvailable,
|
canBeAvailable,
|
||||||
roleIcons,
|
roleIcons,
|
||||||
|
roleNames,
|
||||||
mainRoles,
|
mainRoles,
|
||||||
alternateRoles,
|
alternateRoles,
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,15 +30,15 @@ export const useScheduleStore = defineStore("schedule", () => {
|
||||||
function getWindowStart(team: TeamSchema) {
|
function getWindowStart(team: TeamSchema) {
|
||||||
// convert local 00:00 to league timezone
|
// convert local 00:00 to league timezone
|
||||||
let localMidnight = moment().startOf("isoWeek");
|
let localMidnight = moment().startOf("isoWeek");
|
||||||
let leagueTime = localMidnight.clone().tz(team.tz_timezone);
|
let leagueTime = localMidnight.clone().tz(team.tzTimezone);
|
||||||
|
|
||||||
let nextMinuteOffsetTime = leagueTime.clone();
|
let nextMinuteOffsetTime = leagueTime.clone();
|
||||||
|
|
||||||
if (nextMinuteOffsetTime.minute() > team.minute_offset) {
|
if (nextMinuteOffsetTime.minute() > team.minuteOffset) {
|
||||||
nextMinuteOffsetTime.add(1, "hour");
|
nextMinuteOffsetTime.add(1, "hour");
|
||||||
}
|
}
|
||||||
|
|
||||||
nextMinuteOffsetTime.minute(team.minute_offset);
|
nextMinuteOffsetTime.minute(team.minuteOffset);
|
||||||
|
|
||||||
const deltaMinutes = nextMinuteOffsetTime.diff(leagueTime, "minutes");
|
const deltaMinutes = nextMinuteOffsetTime.diff(leagueTime, "minutes");
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Cacheable from "@/cacheable";
|
import Cacheable from "@/cacheable";
|
||||||
import { AvailabilitfClient, type TeamSpec, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse } from "@/client";
|
import { AvailabilitfClient, type RoleSchema, type TeamSpec, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse } from "@/client";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { computed, reactive, ref, type Reactive, type Ref } from "vue";
|
import { computed, reactive, ref, type Reactive, type Ref } from "vue";
|
||||||
import { useClientStore } from "./client";
|
import { useClientStore } from "./client";
|
||||||
|
@ -47,11 +47,12 @@ export const useTeamsStore = defineStore("teams", () => {
|
||||||
response = response
|
response = response
|
||||||
.map((member): ViewTeamMembersResponse => {
|
.map((member): ViewTeamMembersResponse => {
|
||||||
// TODO: snake_case to camelCase
|
// TODO: snake_case to camelCase
|
||||||
member.roles = member.roles.sort((a, b) => {
|
member.roles = member.roles
|
||||||
if (a.is_main == b.is_main) {
|
.sort((a, b) => {
|
||||||
|
if (a.isMain == b.isMain) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return a.is_main ? -1 : 1;
|
return a.isMain ? -1 : 1;
|
||||||
});
|
});
|
||||||
return member;
|
return member;
|
||||||
});
|
});
|
||||||
|
@ -70,6 +71,13 @@ export const useTeamsStore = defineStore("teams", () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateRoles(teamId: number, playerId: number, roles: RoleSchema[]) {
|
||||||
|
return await client.default
|
||||||
|
.editMemberRoles(teamId.toString(), playerId.toString(), {
|
||||||
|
roles,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
teams,
|
teams,
|
||||||
teamMembers,
|
teamMembers,
|
||||||
|
@ -77,5 +85,6 @@ export const useTeamsStore = defineStore("teams", () => {
|
||||||
fetchTeam,
|
fetchTeam,
|
||||||
fetchTeamMembers,
|
fetchTeamMembers,
|
||||||
createTeam,
|
createTeam,
|
||||||
|
updateRoles
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,12 +45,15 @@ onMounted(() => {
|
||||||
teamsStore.fetchTeams()
|
teamsStore.fetchTeams()
|
||||||
.then((teamsList) => {
|
.then((teamsList) => {
|
||||||
options.value = Object.values(teamsList.teams);
|
options.value = Object.values(teamsList.teams);
|
||||||
|
|
||||||
// select team with id in query parameter if exists
|
// select team with id in query parameter if exists
|
||||||
const queryTeam = teamsList.teams.find(x => x.id == route.query.teamId);
|
const queryTeam = teamsList.teams.find(x => x.id == route.query.teamId);
|
||||||
if (queryTeam) {
|
if (queryTeam) {
|
||||||
selectedTeam.value = queryTeam;
|
selectedTeam.value = queryTeam;
|
||||||
schedule.team = queryTeam;
|
schedule.team = queryTeam;
|
||||||
schedule.fetchSchedule();
|
schedule.fetchSchedule();
|
||||||
|
} else {
|
||||||
|
selectedTeam.value = options.value[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -64,7 +67,7 @@ onMounted(() => {
|
||||||
Availability for
|
Availability for
|
||||||
<v-select
|
<v-select
|
||||||
:options="options"
|
:options="options"
|
||||||
label="team_name"
|
label="teamName"
|
||||||
v-model="selectedTeam"
|
v-model="selectedTeam"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter, RouterLink } from "vue-router";
|
||||||
import { useTeamsStore } from "../stores/teams";
|
import { useTeamsStore } from "../stores/teams";
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
import PlayerTeamCard from "../components/PlayerTeamCard.vue";
|
import PlayerTeamCard from "../components/PlayerTeamCard.vue";
|
||||||
|
@ -12,6 +12,11 @@ const team = computed(() => {
|
||||||
return teamsStore.teams[route.params.id];
|
return teamsStore.teams[route.params.id];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const availableMembers = computed(() => {
|
||||||
|
return teamsStore.teamMembers[route.params.id]
|
||||||
|
.filter((member) => member.availability > 0);
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
teamsStore.fetchTeam(route.params.id)
|
teamsStore.fetchTeam(route.params.id)
|
||||||
.then(() => teamsStore.fetchTeamMembers(route.params.id));
|
.then(() => teamsStore.fetchTeamMembers(route.params.id));
|
||||||
|
@ -22,10 +27,20 @@ onMounted(() => {
|
||||||
<main>
|
<main>
|
||||||
<template v-if="team">
|
<template v-if="team">
|
||||||
<h1>
|
<h1>
|
||||||
{{ team.team_name }}
|
{{ team.teamName }}
|
||||||
|
<RouterLink :to="'/schedule?teamId=' + team.id">
|
||||||
|
<button class="accent">
|
||||||
|
<i class="bi bi-calendar-fill margin"></i>
|
||||||
|
View schedule
|
||||||
|
</button>
|
||||||
|
</RouterLink>
|
||||||
|
<em class="aside" v-if="teamsStore.teamMembers[route.params.id]">
|
||||||
|
{{ teamsStore.teamMembers[route.params.id]?.length }} member(s),
|
||||||
|
{{ availableMembers?.length }} currently available
|
||||||
|
</em>
|
||||||
</h1>
|
</h1>
|
||||||
<table class="member-table">
|
<table class="member-table">
|
||||||
<thead>
|
<!--thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Name
|
Name
|
||||||
|
@ -40,11 +55,13 @@ onMounted(() => {
|
||||||
Joined
|
Joined
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead-->
|
||||||
<tbody>
|
<tbody>
|
||||||
<PlayerTeamCard
|
<PlayerTeamCard
|
||||||
v-for="member in teamsStore.teamMembers[route.params.id]"
|
v-for="member in teamsStore.teamMembers[route.params.id]"
|
||||||
:player="member"
|
:player="member"
|
||||||
|
:team="team"
|
||||||
|
:key="member.username"
|
||||||
/>
|
/>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -55,6 +72,13 @@ onMounted(() => {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
h1 {
|
h1 {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 > em.aside {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.member-table {
|
table.member-table {
|
||||||
|
|
|
@ -274,7 +274,7 @@ def view_team_members(player: Player, team_id: int, **kwargs):
|
||||||
|
|
||||||
def map_role_to_schema(player_team_role: PlayerTeamRole):
|
def map_role_to_schema(player_team_role: PlayerTeamRole):
|
||||||
return ViewTeamMembersResponse.RoleSchema(
|
return ViewTeamMembersResponse.RoleSchema(
|
||||||
role=str(player_team_role.role),
|
role=player_team_role.role.name,
|
||||||
is_main=player_team_role.is_main,
|
is_main=player_team_role.is_main,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -297,3 +297,59 @@ def view_team_members(player: Player, team_id: int, **kwargs):
|
||||||
).dict(by_alias=True)
|
).dict(by_alias=True)
|
||||||
|
|
||||||
return list(map(map_to_response, player_teams))
|
return list(map(map_to_response, player_teams))
|
||||||
|
|
||||||
|
class EditMemberRolesJson(BaseModel):
|
||||||
|
roles: list[ViewTeamMembersResponse.RoleSchema]
|
||||||
|
|
||||||
|
@api_team.patch("/id/<team_id>/edit-player/<target_player_id>")
|
||||||
|
@spec.validate(
|
||||||
|
resp=Response(
|
||||||
|
HTTP_204=None,
|
||||||
|
HTTP_403=None,
|
||||||
|
HTTP_404=None,
|
||||||
|
),
|
||||||
|
operation_id="edit_member_roles"
|
||||||
|
)
|
||||||
|
@requires_authentication
|
||||||
|
def edit_member_roles(
|
||||||
|
json: EditMemberRolesJson,
|
||||||
|
player: Player,
|
||||||
|
team_id: int,
|
||||||
|
target_player_id: int,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
print("hiiii lol")
|
||||||
|
target_player = db.session.query(
|
||||||
|
PlayerTeam
|
||||||
|
).where(
|
||||||
|
PlayerTeam.player_id == target_player_id
|
||||||
|
).where(
|
||||||
|
PlayerTeam.team_id == team_id
|
||||||
|
).options(
|
||||||
|
joinedload(PlayerTeam.player),
|
||||||
|
joinedload(PlayerTeam.player_roles),
|
||||||
|
).one_or_none()
|
||||||
|
|
||||||
|
if not target_player:
|
||||||
|
abort(401)
|
||||||
|
|
||||||
|
# TODO: change this to a MERGE statement
|
||||||
|
|
||||||
|
for role in target_player.player_roles:
|
||||||
|
# delete role if not found in json
|
||||||
|
f = filter(lambda x: x.role == role.role.name, json.roles)
|
||||||
|
matched_role = next(f, None)
|
||||||
|
|
||||||
|
if not matched_role:
|
||||||
|
db.session.delete(role)
|
||||||
|
|
||||||
|
for schema in json.roles:
|
||||||
|
role = PlayerTeamRole()
|
||||||
|
role.player_team = target_player
|
||||||
|
role.role = PlayerTeamRole.Role[schema.role]
|
||||||
|
role.is_main = schema.is_main
|
||||||
|
db.session.merge(role)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return make_response({ }, 204)
|
||||||
|
|
Loading…
Reference in New Issue