feat: Implement changing team settings
parent
f0e2e28d65
commit
40641f80a3
|
@ -38,6 +38,7 @@
|
||||||
--crust: #dce0e8;
|
--crust: #dce0e8;
|
||||||
|
|
||||||
--red: #d20f39;
|
--red: #d20f39;
|
||||||
|
--blue: #1e66f5;
|
||||||
|
|
||||||
--flamingo: #dd7878;
|
--flamingo: #dd7878;
|
||||||
--flamingo-transparent: #f0c6c655;
|
--flamingo-transparent: #f0c6c655;
|
||||||
|
@ -52,13 +53,11 @@
|
||||||
*/
|
*/
|
||||||
--green-transparent: color-mix(in srgb, var(--green), transparent 80%);
|
--green-transparent: color-mix(in srgb, var(--green), transparent 80%);
|
||||||
--yellow-transparent: color-mix(in srgb, var(--yellow), transparent 80%);
|
--yellow-transparent: color-mix(in srgb, var(--yellow), transparent 80%);
|
||||||
/*
|
--blue-transparent: color-mix(in srgb, var(--blue), transparent 80%);
|
||||||
--green-transparent-50: #a6e3a1;
|
|
||||||
--yellow-transparent-50: #f9e2af;
|
|
||||||
*/
|
|
||||||
|
|
||||||
--lavender: #7287fd;
|
--lavender: #7287fd;
|
||||||
--accent: var(--lavender);
|
--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-80: color-mix(in srgb, var(--accent), transparent 80%);
|
||||||
--accent-transparent-50: color-mix(in srgb, var(--accent), transparent 50%);
|
--accent-transparent-50: color-mix(in srgb, var(--accent), transparent 50%);
|
||||||
--accent-transparent: var(--accent-transparent-80);
|
--accent-transparent: var(--accent-transparent-80);
|
||||||
|
|
|
@ -313,3 +313,18 @@ div[role="menu"] div[role="menuitem"]:last-child button {
|
||||||
background-color: var(--surface-0);
|
background-color: var(--surface-0);
|
||||||
width: 100%;
|
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 { TeamIntegrationSchema } from '../models/TeamIntegrationSchema';
|
||||||
import type { TeamInviteSchema } from '../models/TeamInviteSchema';
|
import type { TeamInviteSchema } from '../models/TeamInviteSchema';
|
||||||
import type { TeamInviteSchemaList } from '../models/TeamInviteSchemaList';
|
import type { TeamInviteSchemaList } from '../models/TeamInviteSchemaList';
|
||||||
|
import type { TeamSchema } from '../models/TeamSchema';
|
||||||
import type { UpdateEventJson } from '../models/UpdateEventJson';
|
import type { UpdateEventJson } from '../models/UpdateEventJson';
|
||||||
import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
|
import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
|
||||||
import type { ViewScheduleResponse } from '../models/ViewScheduleResponse';
|
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>
|
* consume_invite <POST>
|
||||||
* @param teamId
|
* @param teamId
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defineStore } from "pinia";
|
||||||
import { reactive, type Reactive } from "vue";
|
import { reactive, type Reactive } from "vue";
|
||||||
import { useClientStore } from "./client";
|
import { useClientStore } from "./client";
|
||||||
import { useAuthStore } from "./auth";
|
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 };
|
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[]) {
|
async function updateRoles(teamId: number, playerId: string, roles: RoleSchema[]) {
|
||||||
return await client.default.editMemberRoles(teamId.toString(), playerId, { roles });
|
return await client.default.editMemberRoles(teamId.toString(), playerId, { roles });
|
||||||
}
|
}
|
||||||
|
@ -70,6 +75,7 @@ export const useTeamsStore = defineStore("teams", () => {
|
||||||
fetchTeam,
|
fetchTeam,
|
||||||
fetchTeamMembers,
|
fetchTeamMembers,
|
||||||
createTeam,
|
createTeam,
|
||||||
|
updateTeam,
|
||||||
updateRoles,
|
updateRoles,
|
||||||
leaveTeam,
|
leaveTeam,
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,6 +75,13 @@ function createTeam() {
|
||||||
past the hour.
|
past the hour.
|
||||||
</em>
|
</em>
|
||||||
</div>
|
</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="form-group margin">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button class="accent" @click="createTeam">Create team</button>
|
<button class="accent" @click="createTeam">Create team</button>
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useTeamSettings } from '@/composables/team-settings';
|
import { useTeamSettings } from "@/composables/team-settings";
|
||||||
import timezones from "@/assets/timezones.json";
|
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 {
|
const {
|
||||||
teamName,
|
teamName,
|
||||||
|
@ -8,9 +12,61 @@ const {
|
||||||
minuteOffset,
|
minuteOffset,
|
||||||
} = useTeamSettings();
|
} = 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -53,11 +109,25 @@ function updateTeamSettings() {
|
||||||
past the hour.
|
past the hour.
|
||||||
</em>
|
</em>
|
||||||
</div>
|
</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="form-group margin">
|
||||||
<div class="action-buttons">
|
<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>
|
<button class="accent" @click="updateTeamSettings">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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_availability import PlayerTeamAvailability
|
||||||
from models.player_team_role import PlayerTeamRole, RoleSchema
|
from models.player_team_role import PlayerTeamRole, RoleSchema
|
||||||
from models.team import Team, TeamSchema
|
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 spec import spec, BaseModel
|
||||||
from team_invite import api_team_invite
|
from team_invite import api_team_invite
|
||||||
from team_integration import api_team_integration
|
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))
|
response = ViewTeamResponse(team=TeamSchema.from_model(team))
|
||||||
return response.dict(by_alias=True), 200
|
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>/")
|
@api_team.delete("/id/<team_id>/")
|
||||||
@spec.validate(
|
@spec.validate(
|
||||||
resp=Response(
|
resp=Response(
|
||||||
|
|
Loading…
Reference in New Issue