feat: Implement changing team settings
							parent
							
								
									f0e2e28d65
								
							
						
					
					
						commit
						40641f80a3
					
				| 
						 | 
				
			
			@ -38,6 +38,7 @@
 | 
			
		|||
  --crust: #dce0e8;
 | 
			
		||||
 | 
			
		||||
  --red: #d20f39;
 | 
			
		||||
  --blue: #1e66f5;
 | 
			
		||||
 | 
			
		||||
  --flamingo: #dd7878;
 | 
			
		||||
  --flamingo-transparent: #f0c6c655;
 | 
			
		||||
| 
						 | 
				
			
			@ -52,13 +53,11 @@
 | 
			
		|||
*/
 | 
			
		||||
  --green-transparent: color-mix(in srgb, var(--green), transparent 80%);
 | 
			
		||||
  --yellow-transparent: color-mix(in srgb, var(--yellow), transparent 80%);
 | 
			
		||||
  /*
 | 
			
		||||
  --green-transparent-50: #a6e3a1;
 | 
			
		||||
  --yellow-transparent-50: #f9e2af;
 | 
			
		||||
*/
 | 
			
		||||
  --blue-transparent: color-mix(in srgb, var(--blue), transparent 80%);
 | 
			
		||||
 | 
			
		||||
  --lavender: #7287fd;
 | 
			
		||||
  --accent: var(--lavender);
 | 
			
		||||
  --lavender-transparent: color-mix(in srgb, var(--lavender), transparent 80%);
 | 
			
		||||
  --accent-transparent-80: color-mix(in srgb, var(--accent), transparent 80%);
 | 
			
		||||
  --accent-transparent-50: color-mix(in srgb, var(--accent), transparent 50%);
 | 
			
		||||
  --accent-transparent: var(--accent-transparent-80);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -313,3 +313,18 @@ div[role="menu"] div[role="menuitem"]:last-child button {
 | 
			
		|||
  background-color: var(--surface-0);
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.banner {
 | 
			
		||||
  padding: 0.5rem 1rem;
 | 
			
		||||
  font-size: 10pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.banner.warning {
 | 
			
		||||
  background-color: var(--yellow-transparent);
 | 
			
		||||
  color: var(--yellow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.banner.info {
 | 
			
		||||
  background-color: var(--lavender-transparent);
 | 
			
		||||
  color: var(--lavender);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ import type { SetUsernameJson } from '../models/SetUsernameJson';
 | 
			
		|||
import type { TeamIntegrationSchema } from '../models/TeamIntegrationSchema';
 | 
			
		||||
import type { TeamInviteSchema } from '../models/TeamInviteSchema';
 | 
			
		||||
import type { TeamInviteSchemaList } from '../models/TeamInviteSchemaList';
 | 
			
		||||
import type { TeamSchema } from '../models/TeamSchema';
 | 
			
		||||
import type { UpdateEventJson } from '../models/UpdateEventJson';
 | 
			
		||||
import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
 | 
			
		||||
import type { ViewScheduleResponse } from '../models/ViewScheduleResponse';
 | 
			
		||||
| 
						 | 
				
			
			@ -458,6 +459,30 @@ export class DefaultService {
 | 
			
		|||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * update_team <PATCH>
 | 
			
		||||
     * @param teamId
 | 
			
		||||
     * @param requestBody
 | 
			
		||||
     * @returns TeamSchema OK
 | 
			
		||||
     * @throws ApiError
 | 
			
		||||
     */
 | 
			
		||||
    public updateTeam(
 | 
			
		||||
        teamId: number,
 | 
			
		||||
        requestBody?: CreateTeamJson,
 | 
			
		||||
    ): CancelablePromise<TeamSchema> {
 | 
			
		||||
        return this.httpRequest.request({
 | 
			
		||||
            method: 'PATCH',
 | 
			
		||||
            url: '/api/team/id/{team_id}/',
 | 
			
		||||
            path: {
 | 
			
		||||
                'team_id': teamId,
 | 
			
		||||
            },
 | 
			
		||||
            body: requestBody,
 | 
			
		||||
            mediaType: 'application/json',
 | 
			
		||||
            errors: {
 | 
			
		||||
                422: `Unprocessable Content`,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * consume_invite <POST>
 | 
			
		||||
     * @param teamId
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import { defineStore } from "pinia";
 | 
			
		|||
import { reactive, type Reactive } from "vue";
 | 
			
		||||
import { useClientStore } from "./client";
 | 
			
		||||
import { useAuthStore } from "./auth";
 | 
			
		||||
import { type TeamSchema, type RoleSchema, type ViewTeamMembersResponse } from "@/client";
 | 
			
		||||
import { type TeamSchema, type RoleSchema, type ViewTeamMembersResponse, type CreateTeamJson } from "@/client";
 | 
			
		||||
 | 
			
		||||
export type TeamMap = { [id: number]: TeamSchema };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +55,11 @@ export const useTeamsStore = defineStore("teams", () => {
 | 
			
		|||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function updateTeam(teamId: number, args: CreateTeamJson) {
 | 
			
		||||
    return await client.default.updateTeam(teamId, args)
 | 
			
		||||
      .then((response) => teams[teamId] = response);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function updateRoles(teamId: number, playerId: string, roles: RoleSchema[]) {
 | 
			
		||||
    return await client.default.editMemberRoles(teamId.toString(), playerId, { roles });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +75,7 @@ export const useTeamsStore = defineStore("teams", () => {
 | 
			
		|||
    fetchTeam,
 | 
			
		||||
    fetchTeamMembers,
 | 
			
		||||
    createTeam,
 | 
			
		||||
    updateTeam,
 | 
			
		||||
    updateRoles,
 | 
			
		||||
    leaveTeam,
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,13 @@ function createTeam() {
 | 
			
		|||
          past the hour.
 | 
			
		||||
        </em>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="form-group margin">
 | 
			
		||||
        <div class="banner info">
 | 
			
		||||
          <i class="bi bi-info-circle" />
 | 
			
		||||
          Note: changing the timezone or minute offset after team creation
 | 
			
		||||
          will remove all team members' availability data.
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="form-group margin">
 | 
			
		||||
        <div class="action-buttons">
 | 
			
		||||
          <button class="accent" @click="createTeam">Create team</button>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,10 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useTeamSettings } from '@/composables/team-settings';
 | 
			
		||||
import { useTeamSettings } from "@/composables/team-settings";
 | 
			
		||||
import timezones from "@/assets/timezones.json";
 | 
			
		||||
import { onMounted, ref } from "vue";
 | 
			
		||||
import { useTeamsStore } from "@/stores/teams";
 | 
			
		||||
import { useTeamDetails } from "@/composables/team-details";
 | 
			
		||||
import { computed } from "@vue/reactivity";
 | 
			
		||||
 | 
			
		||||
const {
 | 
			
		||||
  teamName,
 | 
			
		||||
| 
						 | 
				
			
			@ -8,9 +12,61 @@ const {
 | 
			
		|||
  minuteOffset,
 | 
			
		||||
} = useTeamSettings();
 | 
			
		||||
 | 
			
		||||
function updateTeamSettings() {
 | 
			
		||||
const { teamId } = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
function updateTeamSettings() {
 | 
			
		||||
  teamsStore.updateTeam(teamId.value, {
 | 
			
		||||
    teamName: teamName.value,
 | 
			
		||||
    leagueTimezone: timezone.value,
 | 
			
		||||
    minuteOffset: minuteOffset.value,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function resetChanges() {
 | 
			
		||||
  teamName.value = team.value.teamName;
 | 
			
		||||
  timezone.value = team.value.tzTimezone;
 | 
			
		||||
  minuteOffset.value = team.value.minuteOffset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const hasChangedDetails = computed(() => {
 | 
			
		||||
  if (!team) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    teamName.value !== team.value.teamName ||
 | 
			
		||||
    timezone.value !== team.value.tzTimezone ||
 | 
			
		||||
    minuteOffset.value !== team.value.minuteOffset
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const hasChangedTimeDetails = computed(() => {
 | 
			
		||||
  if (!team) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    timezone.value !== team.value.tzTimezone ||
 | 
			
		||||
    minuteOffset.value !== team.value.minuteOffset
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const isLoaded = ref(false);
 | 
			
		||||
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
 | 
			
		||||
const team = computed(() => teamsStore.teams[teamId.value]);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  isLoaded.value = true;
 | 
			
		||||
  teamsStore.fetchTeam(teamId.value)
 | 
			
		||||
    .then((response) => {
 | 
			
		||||
      teamName.value = response.team.teamName;
 | 
			
		||||
      timezone.value = response.team.tzTimezone;
 | 
			
		||||
      minuteOffset.value = response.team.minuteOffset;
 | 
			
		||||
      isLoaded.value = false;
 | 
			
		||||
    });
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -53,11 +109,25 @@ function updateTeamSettings() {
 | 
			
		|||
          past the hour.
 | 
			
		||||
        </em>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="form-group margin" v-if="hasChangedTimeDetails">
 | 
			
		||||
        <div class="banner warning">
 | 
			
		||||
          <i class="bi bi-exclamation-triangle-fill margin"></i>
 | 
			
		||||
          Warning: changing the timezone or minute offset will remove all
 | 
			
		||||
          current availability data.
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="form-group margin">
 | 
			
		||||
        <div class="action-buttons">
 | 
			
		||||
          <button class="transparent" v-if="hasChangedDetails" @click="resetChanges">
 | 
			
		||||
            <i class="bi bi-arrow-counterclockwise"></i>
 | 
			
		||||
            Undo changes
 | 
			
		||||
          </button>
 | 
			
		||||
          <button class="accent" @click="updateTeamSettings">Save</button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ from models.player_team import PlayerTeam
 | 
			
		|||
from models.player_team_availability import PlayerTeamAvailability
 | 
			
		||||
from models.player_team_role import PlayerTeamRole, RoleSchema
 | 
			
		||||
from models.team import Team, TeamSchema
 | 
			
		||||
from middleware import requires_authentication
 | 
			
		||||
from middleware import assert_team_authority, requires_authentication, requires_team_membership
 | 
			
		||||
from spec import spec, BaseModel
 | 
			
		||||
from team_invite import api_team_invite
 | 
			
		||||
from team_integration import api_team_integration
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +86,27 @@ def create_team(json: CreateTeamJson, player: Player, **kwargs):
 | 
			
		|||
    response = ViewTeamResponse(team=TeamSchema.from_model(team))
 | 
			
		||||
    return response.dict(by_alias=True), 200
 | 
			
		||||
 | 
			
		||||
@api_team.patch("/id/<int:team_id>/")
 | 
			
		||||
@spec.validate(
 | 
			
		||||
    resp=Response(
 | 
			
		||||
        HTTP_200=TeamSchema,
 | 
			
		||||
    ),
 | 
			
		||||
    operation_id="update_team",
 | 
			
		||||
)
 | 
			
		||||
@requires_authentication
 | 
			
		||||
@requires_team_membership()
 | 
			
		||||
def update_team(player_team: PlayerTeam, team_id: int, json: CreateTeamJson, **kwargs):
 | 
			
		||||
    assert_team_authority(player_team)
 | 
			
		||||
 | 
			
		||||
    team = player_team.team
 | 
			
		||||
    team.team_name = json.team_name
 | 
			
		||||
    team.tz_timezone = json.league_timezone
 | 
			
		||||
    team.minute_offset = json.minute_offset
 | 
			
		||||
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
 | 
			
		||||
    return TeamSchema.from_model(team).dict(by_alias=True), 200
 | 
			
		||||
 | 
			
		||||
@api_team.delete("/id/<team_id>/")
 | 
			
		||||
@spec.validate(
 | 
			
		||||
    resp=Response(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue