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>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,9 @@ import { computed, reactive, ref, type Reactive, type Ref } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useRosterStore = defineStore("roster", () => {
 | 
					export const useRosterStore = defineStore("roster", () => {
 | 
				
			||||||
  const neededRoles: Reactive<Array<String>> = reactive([
 | 
					  const neededRoles: Reactive<Array<String>> = reactive([
 | 
				
			||||||
    "Pocket Scout",
 | 
					    "PocketScout",
 | 
				
			||||||
    "Flank Scout",
 | 
					    "FlankScout",
 | 
				
			||||||
    "Pocket Soldier",
 | 
					    "PocketSoldier",
 | 
				
			||||||
    "Roamer",
 | 
					    "Roamer",
 | 
				
			||||||
    "Demoman",
 | 
					    "Demoman",
 | 
				
			||||||
    "Medic",
 | 
					    "Medic",
 | 
				
			||||||
| 
						 | 
					@ -171,19 +171,21 @@ export const useRosterStore = defineStore("roster", () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const roleIcons = reactive({
 | 
					  const roleIcons = reactive({
 | 
				
			||||||
    "Pocket Scout": "tf2-PocketScout",
 | 
					    "PocketScout": "tf2-PocketScout",
 | 
				
			||||||
    "Flank Scout": "tf2-FlankScout",
 | 
					    "FlankScout": "tf2-FlankScout",
 | 
				
			||||||
    "Pocket Soldier": "tf2-PocketSoldier",
 | 
					    "PocketSoldier": "tf2-PocketSoldier",
 | 
				
			||||||
    "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