Add integration management for teams
- Add new models for team integrations - Create IntegrationDetails component for managing integrations - Update teams store with integration actions - Modify IntegrationsView to display and manage integrationsmaster
							parent
							
								
									8a00c53479
								
							
						
					
					
						commit
						c67bf14980
					
				| 
						 | 
					@ -10,6 +10,7 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
 | 
				
			||||||
export { OpenAPI } from './core/OpenAPI';
 | 
					export { OpenAPI } from './core/OpenAPI';
 | 
				
			||||||
export type { OpenAPIConfig } from './core/OpenAPI';
 | 
					export type { OpenAPIConfig } from './core/OpenAPI';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type { AbstractTeamIntegrationSchema } from './models/AbstractTeamIntegrationSchema';
 | 
				
			||||||
export type { AddPlayerJson } from './models/AddPlayerJson';
 | 
					export type { AddPlayerJson } from './models/AddPlayerJson';
 | 
				
			||||||
export type { AvailabilitySchema } from './models/AvailabilitySchema';
 | 
					export type { AvailabilitySchema } from './models/AvailabilitySchema';
 | 
				
			||||||
export type { CreateTeamJson } from './models/CreateTeamJson';
 | 
					export type { CreateTeamJson } from './models/CreateTeamJson';
 | 
				
			||||||
| 
						 | 
					@ -19,6 +20,9 @@ export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvaila
 | 
				
			||||||
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 type { SetUsernameJson } from './models/SetUsernameJson';
 | 
					export type { SetUsernameJson } from './models/SetUsernameJson';
 | 
				
			||||||
 | 
					export type { TeamDiscordIntegrationSchema } from './models/TeamDiscordIntegrationSchema';
 | 
				
			||||||
 | 
					export type { TeamIntegrationSchema } from './models/TeamIntegrationSchema';
 | 
				
			||||||
 | 
					export type { TeamIntegrationSchemaList } from './models/TeamIntegrationSchemaList';
 | 
				
			||||||
export type { TeamInviteSchema } from './models/TeamInviteSchema';
 | 
					export type { TeamInviteSchema } from './models/TeamInviteSchema';
 | 
				
			||||||
export type { TeamInviteSchemaList } from './models/TeamInviteSchemaList';
 | 
					export type { TeamInviteSchemaList } from './models/TeamInviteSchemaList';
 | 
				
			||||||
export { TeamRole } from './models/TeamRole';
 | 
					export { TeamRole } from './models/TeamRole';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { TeamDiscordIntegrationSchema } from './TeamDiscordIntegrationSchema';
 | 
				
			||||||
 | 
					import type { TeamIntegrationSchema } from './TeamIntegrationSchema';
 | 
				
			||||||
 | 
					export type AbstractTeamIntegrationSchema = (TeamDiscordIntegrationSchema | TeamIntegrationSchema);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type TeamDiscordIntegrationSchema = {
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					    integrationType: string;
 | 
				
			||||||
 | 
					    teamId: number;
 | 
				
			||||||
 | 
					    webhookUrl: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					export type TeamIntegrationSchema = {
 | 
				
			||||||
 | 
					    id: number;
 | 
				
			||||||
 | 
					    integrationType: string;
 | 
				
			||||||
 | 
					    teamId: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					/* generated using openapi-typescript-codegen -- do not edit */
 | 
				
			||||||
 | 
					/* istanbul ignore file */
 | 
				
			||||||
 | 
					/* tslint:disable */
 | 
				
			||||||
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { TeamIntegrationSchema } from './TeamIntegrationSchema';
 | 
				
			||||||
 | 
					export type TeamIntegrationSchemaList = Array<TeamIntegrationSchema>;
 | 
				
			||||||
| 
						 | 
					@ -2,12 +2,15 @@
 | 
				
			||||||
/* istanbul ignore file */
 | 
					/* istanbul ignore file */
 | 
				
			||||||
/* tslint:disable */
 | 
					/* tslint:disable */
 | 
				
			||||||
/* eslint-disable */
 | 
					/* eslint-disable */
 | 
				
			||||||
 | 
					import type { AbstractTeamIntegrationSchema } from '../models/AbstractTeamIntegrationSchema';
 | 
				
			||||||
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 { EditMemberRolesJson } from '../models/EditMemberRolesJson';
 | 
				
			||||||
import type { PlayerSchema } from '../models/PlayerSchema';
 | 
					import type { PlayerSchema } from '../models/PlayerSchema';
 | 
				
			||||||
import type { PutScheduleForm } from '../models/PutScheduleForm';
 | 
					import type { PutScheduleForm } from '../models/PutScheduleForm';
 | 
				
			||||||
import type { SetUsernameJson } from '../models/SetUsernameJson';
 | 
					import type { SetUsernameJson } from '../models/SetUsernameJson';
 | 
				
			||||||
 | 
					import type { TeamIntegrationSchema } from '../models/TeamIntegrationSchema';
 | 
				
			||||||
 | 
					import type { TeamIntegrationSchemaList } from '../models/TeamIntegrationSchemaList';
 | 
				
			||||||
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 { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
 | 
					import type { ViewAvailablePlayersResponse } from '../models/ViewAvailablePlayersResponse';
 | 
				
			||||||
| 
						 | 
					@ -314,6 +317,100 @@ export class DefaultService {
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * get_integrations <GET>
 | 
				
			||||||
 | 
					     * @param teamId
 | 
				
			||||||
 | 
					     * @returns TeamIntegrationSchemaList OK
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public getIntegrations(
 | 
				
			||||||
 | 
					        teamId: string,
 | 
				
			||||||
 | 
					    ): CancelablePromise<TeamIntegrationSchemaList> {
 | 
				
			||||||
 | 
					        return this.httpRequest.request({
 | 
				
			||||||
 | 
					            method: 'GET',
 | 
				
			||||||
 | 
					            url: '/api/team/id/{team_id}/integrations',
 | 
				
			||||||
 | 
					            path: {
 | 
				
			||||||
 | 
					                'team_id': teamId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                404: `Not Found`,
 | 
				
			||||||
 | 
					                422: `Unprocessable Entity`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * delete_integration <DELETE>
 | 
				
			||||||
 | 
					     * @param teamId
 | 
				
			||||||
 | 
					     * @param integrationId
 | 
				
			||||||
 | 
					     * @returns void
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public deleteIntegration(
 | 
				
			||||||
 | 
					        teamId: string,
 | 
				
			||||||
 | 
					        integrationId: string,
 | 
				
			||||||
 | 
					    ): CancelablePromise<void> {
 | 
				
			||||||
 | 
					        return this.httpRequest.request({
 | 
				
			||||||
 | 
					            method: 'DELETE',
 | 
				
			||||||
 | 
					            url: '/api/team/id/{team_id}/integrations/{integration_id}',
 | 
				
			||||||
 | 
					            path: {
 | 
				
			||||||
 | 
					                'team_id': teamId,
 | 
				
			||||||
 | 
					                'integration_id': integrationId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Unprocessable Entity`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * update_integration <PATCH>
 | 
				
			||||||
 | 
					     * @param teamId
 | 
				
			||||||
 | 
					     * @param integrationId
 | 
				
			||||||
 | 
					     * @param requestBody
 | 
				
			||||||
 | 
					     * @returns TeamIntegrationSchema OK
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public updateIntegration(
 | 
				
			||||||
 | 
					        teamId: string,
 | 
				
			||||||
 | 
					        integrationId: string,
 | 
				
			||||||
 | 
					        requestBody?: AbstractTeamIntegrationSchema,
 | 
				
			||||||
 | 
					    ): CancelablePromise<TeamIntegrationSchema> {
 | 
				
			||||||
 | 
					        return this.httpRequest.request({
 | 
				
			||||||
 | 
					            method: 'PATCH',
 | 
				
			||||||
 | 
					            url: '/api/team/id/{team_id}/integrations/{integration_id}',
 | 
				
			||||||
 | 
					            path: {
 | 
				
			||||||
 | 
					                'team_id': teamId,
 | 
				
			||||||
 | 
					                'integration_id': integrationId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            body: requestBody,
 | 
				
			||||||
 | 
					            mediaType: 'application/json',
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Unprocessable Entity`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * create_integration <POST>
 | 
				
			||||||
 | 
					     * @param teamId
 | 
				
			||||||
 | 
					     * @param integrationType
 | 
				
			||||||
 | 
					     * @returns TeamIntegrationSchema OK
 | 
				
			||||||
 | 
					     * @throws ApiError
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public createIntegration(
 | 
				
			||||||
 | 
					        teamId: string,
 | 
				
			||||||
 | 
					        integrationType: string,
 | 
				
			||||||
 | 
					    ): CancelablePromise<TeamIntegrationSchema> {
 | 
				
			||||||
 | 
					        return this.httpRequest.request({
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            url: '/api/team/id/{team_id}/integrations/{integration_type}',
 | 
				
			||||||
 | 
					            path: {
 | 
				
			||||||
 | 
					                'team_id': teamId,
 | 
				
			||||||
 | 
					                'integration_type': integrationType,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            errors: {
 | 
				
			||||||
 | 
					                422: `Unprocessable Entity`,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * get_invites <GET>
 | 
					     * get_invites <GET>
 | 
				
			||||||
     * @param teamId
 | 
					     * @param teamId
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,22 +35,22 @@ const isShiftDown = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const lowerBoundX = computed(() => {
 | 
					const lowerBoundX = computed(() => {
 | 
				
			||||||
  return isShiftDown.value ? 0 :
 | 
					  return isShiftDown.value ? 0 :
 | 
				
			||||||
    Math.min(selectionStart.x, selectionEnd.x)
 | 
					    Math.min(selectionStart.x ?? NaN, selectionEnd.x ?? NaN)
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const upperBoundX = computed(() => {
 | 
					const upperBoundX = computed(() => {
 | 
				
			||||||
  return isShiftDown.value ? 6 :
 | 
					  return isShiftDown.value ? 6 :
 | 
				
			||||||
    Math.max(selectionStart.x, selectionEnd.x)
 | 
					    Math.max(selectionStart.x ?? NaN, selectionEnd.x ?? NaN)
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const lowerBoundY = computed(() => {
 | 
					const lowerBoundY = computed(() => {
 | 
				
			||||||
  return isCtrlDown.value ? props.firstHour :
 | 
					  return isCtrlDown.value ? props.firstHour :
 | 
				
			||||||
    Math.min(selectionStart.y, selectionEnd.y)
 | 
					    Math.min(selectionStart.y ?? NaN, selectionEnd.y ?? NaN)
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const upperBoundY = computed(() => {
 | 
					const upperBoundY = computed(() => {
 | 
				
			||||||
  return isCtrlDown.value ? props.lastHour :
 | 
					  return isCtrlDown.value ? props.lastHour :
 | 
				
			||||||
    Math.max(selectionStart.y, selectionEnd.y)
 | 
					    Math.max(selectionStart.y ?? NaN, selectionEnd.y ?? NaN)
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function selectionInside(dayIndex, hour) {
 | 
					function selectionInside(dayIndex: number, hour: number) {
 | 
				
			||||||
  if (selectionStart.x != undefined) {
 | 
					  if (selectionStart.x != undefined) {
 | 
				
			||||||
    return (dayIndex >= lowerBoundX.value && dayIndex <= upperBoundX.value) &&
 | 
					    return (dayIndex >= lowerBoundX.value && dayIndex <= upperBoundX.value) &&
 | 
				
			||||||
      (hour >= lowerBoundY.value && hour <= upperBoundY.value);
 | 
					      (hour >= lowerBoundY.value && hour <= upperBoundY.value);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,76 @@
 | 
				
			||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import type { TeamIntegrationSchema, TeamDiscordIntegrationSchema } from "@/client";
 | 
				
			||||||
 | 
					import { useTeamDetails } from "@/composables/team-details";
 | 
				
			||||||
 | 
					import { useTeamsStore } from "@/stores/teams";
 | 
				
			||||||
 | 
					import { computed } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps<{
 | 
				
			||||||
 | 
					  integration: TeamIntegrationSchema,
 | 
				
			||||||
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const teamsStore = useTeamsStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { teamId } = useTeamDetails();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					const isDiscord = (x: TeamIntegrationSchema): x is TeamDiscordIntegrationSchema => x.integrationType === "team_discord_integrations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isDiscordIntegration = computed(() => {
 | 
				
			||||||
 | 
					  return isDiscord(props.integration);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const discordIntegration = computed(() => props.integration as TeamDiscordIntegrationSchema);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function deleteIntegration() {
 | 
				
			||||||
 | 
					  teamsStore.deleteIntegration(teamId.value, props.integration.id);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function saveIntegration() {
 | 
				
			||||||
 | 
					  teamsStore.updateIntegration(teamId.value, props.integration);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <details class="accordion">
 | 
				
			||||||
 | 
					    <summary>
 | 
				
			||||||
 | 
					      <span class="title">
 | 
				
			||||||
 | 
					        <h2 v-if="discordIntegration">
 | 
				
			||||||
 | 
					          Discord Integration
 | 
				
			||||||
 | 
					        </h2>
 | 
				
			||||||
 | 
					        <span class="aside">(id: {{ props.integration.id }})</span>
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="form-group margin">
 | 
				
			||||||
 | 
					      <h3>Webhook URL</h3>
 | 
				
			||||||
 | 
					      <input v-model="discordIntegration.webhookUrl" />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="button-group">
 | 
				
			||||||
 | 
					      <button class="destructive-on-hover" @click="deleteIntegration">
 | 
				
			||||||
 | 
					        <i class="bi bi-trash margin" />
 | 
				
			||||||
 | 
					        Delete
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					      <button @click="saveIntegration">Save</button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </details>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.button-group {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  gap: 4px;
 | 
				
			||||||
 | 
					  justify-content: end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					summary > .title {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 0.5em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					summary .aside {
 | 
				
			||||||
 | 
					  font-size: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import Cacheable from "@/cacheable";
 | 
					import Cacheable from "@/cacheable";
 | 
				
			||||||
import { AvailabilitfClient, type TeamInviteSchema, type RoleSchema, type TeamSchema, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse } from "@/client";
 | 
					import { AvailabilitfClient, type TeamInviteSchema, type RoleSchema, type TeamSchema, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse, type TeamIntegrationSchema, type AbstractTeamIntegrationSchema } 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";
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ export const useTeamsStore = defineStore("teams", () => {
 | 
				
			||||||
  const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({ });
 | 
					  const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({ });
 | 
				
			||||||
  const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({ });
 | 
					  const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({ });
 | 
				
			||||||
  const teamInvites: Reactive<{ [id: number]: TeamInviteSchema[] }> = reactive({ });
 | 
					  const teamInvites: Reactive<{ [id: number]: TeamInviteSchema[] }> = reactive({ });
 | 
				
			||||||
 | 
					  const teamIntegrations = reactive<{ [id: number]: TeamIntegrationSchema[] }>({ });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function fetchTeams() {
 | 
					  async function fetchTeams() {
 | 
				
			||||||
    return clientStore.call(
 | 
					    return clientStore.call(
 | 
				
			||||||
| 
						 | 
					@ -118,6 +119,47 @@ export const useTeamsStore = defineStore("teams", () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function getIntegrations(teamId: number) {
 | 
				
			||||||
 | 
					    return client.default.getIntegrations(teamId.toString())
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        teamIntegrations[teamId] = response;
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function createIntegration(teamId: number, integrationType: string) {
 | 
				
			||||||
 | 
					    return client.default
 | 
				
			||||||
 | 
					      .createIntegration(teamId.toString(), integrationType)
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        teamIntegrations[teamId].push(response);
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function deleteIntegration(teamId: number, integrationId: number) {
 | 
				
			||||||
 | 
					    return client.default
 | 
				
			||||||
 | 
					      .deleteIntegration(teamId.toString(), integrationId.toString())
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        teamIntegrations[teamId] = teamIntegrations[teamId]
 | 
				
			||||||
 | 
					          .filter((integration) => integration.id != integrationId);
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function updateIntegration(
 | 
				
			||||||
 | 
					    teamId: number,
 | 
				
			||||||
 | 
					    integration: AbstractTeamIntegrationSchema,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    return client.default
 | 
				
			||||||
 | 
					      .updateIntegration(teamId.toString(), integration.id.toString(), integration)
 | 
				
			||||||
 | 
					      .then((response) => {
 | 
				
			||||||
 | 
					        const index = teamIntegrations[teamId]
 | 
				
			||||||
 | 
					          .findIndex((x) => x.id == integration.id);
 | 
				
			||||||
 | 
					        teamIntegrations[teamId][index] = response;
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function leaveTeam(teamId: number) {
 | 
					  async function leaveTeam(teamId: number) {
 | 
				
			||||||
    return client.default
 | 
					    return client.default
 | 
				
			||||||
      .removePlayerFromTeam(teamId.toString(), authStore.steamId);
 | 
					      .removePlayerFromTeam(teamId.toString(), authStore.steamId);
 | 
				
			||||||
| 
						 | 
					@ -137,5 +179,11 @@ export const useTeamsStore = defineStore("teams", () => {
 | 
				
			||||||
    consumeInvite,
 | 
					    consumeInvite,
 | 
				
			||||||
    revokeInvite,
 | 
					    revokeInvite,
 | 
				
			||||||
    leaveTeam,
 | 
					    leaveTeam,
 | 
				
			||||||
 | 
					    // TODO: move to separate store
 | 
				
			||||||
 | 
					    teamIntegrations,
 | 
				
			||||||
 | 
					    getIntegrations,
 | 
				
			||||||
 | 
					    createIntegration,
 | 
				
			||||||
 | 
					    deleteIntegration,
 | 
				
			||||||
 | 
					    updateIntegration,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,41 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import IntegrationDetails from "@/components/IntegrationDetails.vue";
 | 
				
			||||||
 | 
					import { useTeamDetails } from "@/composables/team-details";
 | 
				
			||||||
 | 
					import { useTeamsStore } from "@/stores/teams";
 | 
				
			||||||
 | 
					import { computed, onMounted, ref } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const teamsStore = useTeamsStore();
 | 
				
			||||||
 | 
					const {
 | 
				
			||||||
 | 
					  teamId,
 | 
				
			||||||
 | 
					} = useTeamDetails();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const integrations = computed(() => teamsStore.teamIntegrations[teamId.value]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function createIntegration() {
 | 
				
			||||||
 | 
					  teamsStore.createIntegration(teamId.value, "discord");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  teamsStore.fetchTeam(teamId.value)
 | 
				
			||||||
 | 
					    .then(() => teamsStore.getIntegrations(teamId.value));
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="team-integrations">
 | 
					  <div class="team-integrations">
 | 
				
			||||||
    <h2>Team Integrations</h2>
 | 
					    <h2>Team Integrations</h2>
 | 
				
			||||||
    <div v-if="true">
 | 
					    <div v-if="integrations?.length == 0">
 | 
				
			||||||
      This team currently does not have any integrations.
 | 
					      This team currently does not have any integrations.
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div v-else>
 | 
					    <div v-else>
 | 
				
			||||||
      <details class="accordion">
 | 
					      <IntegrationDetails
 | 
				
			||||||
        <summary>
 | 
					        v-for="integration in integrations"
 | 
				
			||||||
          <h2>Discord Webhook</h2>
 | 
					        :integration="integration"
 | 
				
			||||||
        </summary>
 | 
					      />
 | 
				
			||||||
        <h3>Webhook URL</h3>
 | 
					 | 
				
			||||||
        <input hidden />
 | 
					 | 
				
			||||||
      </details>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <button class="accent" @click="createIntegration">
 | 
				
			||||||
 | 
					      <i class="bi bi-database-fill-add margin" />
 | 
				
			||||||
 | 
					      Create Integration
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
#from typing import cast, override
 | 
					#from typing import cast, override
 | 
				
			||||||
 | 
					from typing import TypeAlias, Union
 | 
				
			||||||
 | 
					from pydantic_core.core_schema import UnionSchema
 | 
				
			||||||
from sqlalchemy.orm import mapped_column, relationship
 | 
					from sqlalchemy.orm import mapped_column, relationship
 | 
				
			||||||
from sqlalchemy.orm.attributes import Mapped
 | 
					from sqlalchemy.orm.attributes import Mapped
 | 
				
			||||||
from sqlalchemy.orm.properties import ForeignKey
 | 
					from sqlalchemy.orm.properties import ForeignKey
 | 
				
			||||||
| 
						 | 
					@ -56,4 +58,11 @@ class TeamDiscordIntegrationSchema(TeamIntegrationSchema):
 | 
				
			||||||
            webhook_url=model.webhook_url
 | 
					            webhook_url=model.webhook_url
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ExampleIntegrationSchema(TeamIntegrationSchema):
 | 
				
			||||||
 | 
					    test: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AbstractTeamIntegrationSchema(spec.BaseModel):
 | 
				
			||||||
 | 
					    __root__: TeamDiscordIntegrationSchema | TeamIntegrationSchema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from models.team import Team
 | 
					from models.team import Team
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ from datetime import UTC, datetime, timedelta, timezone
 | 
				
			||||||
from random import randint, random
 | 
					from random import randint, random
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
from typing import List
 | 
					from typing import List, cast
 | 
				
			||||||
from flask import Blueprint, abort, jsonify, make_response, request
 | 
					from flask import Blueprint, abort, jsonify, make_response, request
 | 
				
			||||||
from pydantic.v1 import validator
 | 
					from pydantic.v1 import validator
 | 
				
			||||||
from spectree import Response
 | 
					from spectree import Response
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ 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 models.team_invite import TeamInvite, TeamInviteSchema
 | 
					from models.team_invite import TeamInvite, TeamInviteSchema
 | 
				
			||||||
from models.team_integration import TeamDiscordIntegration, TeamDiscordIntegrationSchema, TeamIntegration, TeamIntegrationSchema
 | 
					from models.team_integration import AbstractTeamIntegrationSchema, TeamDiscordIntegration, TeamDiscordIntegrationSchema, TeamIntegration, TeamIntegrationSchema
 | 
				
			||||||
from middleware import assert_team_authority, requires_authentication, requires_team_membership
 | 
					from middleware import assert_team_authority, requires_authentication, requires_team_membership
 | 
				
			||||||
import models
 | 
					import models
 | 
				
			||||||
from spec import spec, BaseModel
 | 
					from spec import spec, BaseModel
 | 
				
			||||||
| 
						 | 
					@ -630,7 +630,9 @@ def create_integration(player_team: PlayerTeam, integration_type: str, **_):
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    operation_id="delete_integration"
 | 
					    operation_id="delete_integration"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def delete_integration(player_team: PlayerTeam, integration_id: int):
 | 
					@requires_authentication
 | 
				
			||||||
 | 
					@requires_team_membership
 | 
				
			||||||
 | 
					def delete_integration(player_team: PlayerTeam, integration_id: int, **_):
 | 
				
			||||||
    assert_team_authority(player_team)
 | 
					    assert_team_authority(player_team)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    integration = db.session.query(
 | 
					    integration = db.session.query(
 | 
				
			||||||
| 
						 | 
					@ -656,10 +658,12 @@ def delete_integration(player_team: PlayerTeam, integration_id: int):
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    operation_id="update_integration"
 | 
					    operation_id="update_integration"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					@requires_authentication
 | 
				
			||||||
 | 
					@requires_team_membership
 | 
				
			||||||
def update_integration(
 | 
					def update_integration(
 | 
				
			||||||
    player_team: PlayerTeam,
 | 
					    player_team: PlayerTeam,
 | 
				
			||||||
    integration_id: int,
 | 
					    integration_id: int,
 | 
				
			||||||
    json: TeamIntegrationSchema,
 | 
					    json: AbstractTeamIntegrationSchema,
 | 
				
			||||||
    **_
 | 
					    **_
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    assert_team_authority(player_team)
 | 
					    assert_team_authority(player_team)
 | 
				
			||||||
| 
						 | 
					@ -676,8 +680,12 @@ def update_integration(
 | 
				
			||||||
        abort(404)
 | 
					        abort(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if isinstance(integration, TeamDiscordIntegration):
 | 
					    if isinstance(integration, TeamDiscordIntegration):
 | 
				
			||||||
        if isinstance(json, TeamDiscordIntegrationSchema):
 | 
					        print(json.dict(), file=sys.stderr)
 | 
				
			||||||
            integration.webhook_url = json.webhook_url
 | 
					        if json.__root__.integration_type == "team_discord_integrations":
 | 
				
			||||||
 | 
					            discord_integration = cast(TeamDiscordIntegration, json.__root__)
 | 
				
			||||||
 | 
					            integration.webhook_url = discord_integration.webhook_url
 | 
				
			||||||
 | 
					        #if isinstance(json, TeamDiscordIntegrationSchema):
 | 
				
			||||||
 | 
					        #    integration.webhook_url = json.webhook_url
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            abort(400)
 | 
					            abort(400)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue