Improve code quality
							parent
							
								
									cb9e29b402
								
							
						
					
					
						commit
						b4deeddfba
					
				| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
        "@vue/eslint-config-typescript": "^14.0.1",
 | 
			
		||||
        "@vue/test-utils": "^2.4.6",
 | 
			
		||||
        "@vue/tsconfig": "^0.5.1",
 | 
			
		||||
        "@vue/typescript-plugin": "^2.1.10",
 | 
			
		||||
        "autoprefixer": "^10.4.20",
 | 
			
		||||
        "cypress": "^13.15.0",
 | 
			
		||||
        "eslint": "^9.12.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -1620,30 +1621,30 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@volar/language-core": {
 | 
			
		||||
      "version": "2.4.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.6.tgz",
 | 
			
		||||
      "integrity": "sha512-FxUfxaB8sCqvY46YjyAAV6c3mMIq/NWQMVvJ+uS4yxr1KzOvyg61gAuOnNvgCvO4TZ7HcLExBEsWcDu4+K4E8A==",
 | 
			
		||||
      "version": "2.4.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz",
 | 
			
		||||
      "integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@volar/source-map": "2.4.6"
 | 
			
		||||
        "@volar/source-map": "2.4.10"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@volar/source-map": {
 | 
			
		||||
      "version": "2.4.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.6.tgz",
 | 
			
		||||
      "integrity": "sha512-Nsh7UW2ruK+uURIPzjJgF0YRGP5CX9nQHypA2OMqdM2FKy7rh+uv3XgPnWPw30JADbKvZ5HuBzG4gSbVDYVtiw==",
 | 
			
		||||
      "version": "2.4.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz",
 | 
			
		||||
      "integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@volar/typescript": {
 | 
			
		||||
      "version": "2.4.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.6.tgz",
 | 
			
		||||
      "integrity": "sha512-NMIrA7y5OOqddL9VtngPWYmdQU03htNKFtAYidbYfWA0TOhyGVd9tfcP4TsLWQ+RBWDZCbBqsr8xzU0ZOxYTCQ==",
 | 
			
		||||
      "version": "2.4.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz",
 | 
			
		||||
      "integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@volar/language-core": "2.4.6",
 | 
			
		||||
        "@volar/language-core": "2.4.10",
 | 
			
		||||
        "path-browserify": "^1.0.1",
 | 
			
		||||
        "vscode-uri": "^3.0.8"
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -1861,6 +1862,43 @@
 | 
			
		|||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@vue/typescript-plugin": {
 | 
			
		||||
      "version": "2.1.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-2.1.10.tgz",
 | 
			
		||||
      "integrity": "sha512-NrS3BB3l5vuZHU4Vp8l9TbT5pC7VjBfwZKqc24dAXF3Z+dJyGs4mcC3zo59gUggLMQSah8mdXj8xqEfMkrps8w==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@volar/typescript": "~2.4.8",
 | 
			
		||||
        "@vue/language-core": "2.1.10",
 | 
			
		||||
        "@vue/shared": "^3.5.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@vue/typescript-plugin/node_modules/@vue/language-core": {
 | 
			
		||||
      "version": "2.1.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz",
 | 
			
		||||
      "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@volar/language-core": "~2.4.8",
 | 
			
		||||
        "@vue/compiler-dom": "^3.5.0",
 | 
			
		||||
        "@vue/compiler-vue2": "^2.7.16",
 | 
			
		||||
        "@vue/shared": "^3.5.0",
 | 
			
		||||
        "alien-signals": "^0.2.0",
 | 
			
		||||
        "minimatch": "^9.0.3",
 | 
			
		||||
        "muggle-string": "^0.4.1",
 | 
			
		||||
        "path-browserify": "^1.0.1"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "typescript": "*"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependenciesMeta": {
 | 
			
		||||
        "typescript": {
 | 
			
		||||
          "optional": true
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/abbrev": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1938,6 +1976,13 @@
 | 
			
		|||
        "url": "https://github.com/sponsors/epoberezkin"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/alien-signals": {
 | 
			
		||||
      "version": "0.2.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz",
 | 
			
		||||
      "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/ansi-colors": {
 | 
			
		||||
      "version": "4.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@
 | 
			
		|||
    "@vue/eslint-config-typescript": "^14.0.1",
 | 
			
		||||
    "@vue/test-utils": "^2.4.6",
 | 
			
		||||
    "@vue/tsconfig": "^0.5.1",
 | 
			
		||||
    "@vue/typescript-plugin": "^2.1.10",
 | 
			
		||||
    "autoprefixer": "^10.4.20",
 | 
			
		||||
    "cypress": "^13.15.0",
 | 
			
		||||
    "eslint": "^9.12.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,15 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { type TeamInviteSchema } from "../client";
 | 
			
		||||
import { useTeamsStore } from "../stores/teams";
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { computed, type PropType } from "vue";
 | 
			
		||||
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  invite: Object as PropType<TeamInviteSchema>,
 | 
			
		||||
  invite: {
 | 
			
		||||
    type: Object as PropType<TeamInviteSchema>,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const inviteLink = computed(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
import { useTeamsStore } from "@/stores/teams";
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { useRoute } from "vue-router";
 | 
			
		||||
 | 
			
		||||
export function useTeamDetails() {
 | 
			
		||||
  const route = useRoute();
 | 
			
		||||
  const teamsStore = useTeamsStore();
 | 
			
		||||
 | 
			
		||||
  const teamId = computed(() => Number(route.params.id));
 | 
			
		||||
 | 
			
		||||
  const team = computed(() => {
 | 
			
		||||
    return teamsStore.teams[teamId.value];
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const invites = computed(() => {
 | 
			
		||||
    return teamsStore.teamInvites[teamId.value];
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const teamMembers = computed(() => {
 | 
			
		||||
    return teamsStore.teamMembers[teamId.value];
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const availableMembers = computed(() => {
 | 
			
		||||
    return teamsStore.teamMembers[teamId.value]
 | 
			
		||||
      .filter((member) => member.availability[0] > 0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const availableMembersNextHour = computed(() => {
 | 
			
		||||
    return teamsStore.teamMembers[teamId.value]
 | 
			
		||||
      .filter((member) => member.availability[1] > 0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    team,
 | 
			
		||||
    teamId,
 | 
			
		||||
    invites,
 | 
			
		||||
    teamMembers,
 | 
			
		||||
    availableMembers,
 | 
			
		||||
    availableMembersNextHour,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
import { ref } from "vue";
 | 
			
		||||
 | 
			
		||||
export function useTeamSettings() {
 | 
			
		||||
  const teamName = ref("");
 | 
			
		||||
 | 
			
		||||
  const timezone = ref(
 | 
			
		||||
    Intl.DateTimeFormat().resolvedOptions().timeZone ??
 | 
			
		||||
      "Etc/UTC"
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import LoginView from "../views/LoginView.vue";
 | 
			
		|||
import TeamRegistrationView from "../views/TeamRegistrationView.vue";
 | 
			
		||||
import TeamDetailsView from "../views/TeamDetailsView.vue";
 | 
			
		||||
import { useAuthStore } from "@/stores/auth";
 | 
			
		||||
import TeamDetailsMembersListView from "../views/TeamDetailsMembersListView.vue";
 | 
			
		||||
 | 
			
		||||
const router = createRouter({
 | 
			
		||||
  history: createWebHistory(import.meta.env.BASE_URL),
 | 
			
		||||
| 
						 | 
				
			
			@ -39,10 +40,16 @@ const router = createRouter({
 | 
			
		|||
      path: "/team/id/:id",
 | 
			
		||||
      name: "team-details",
 | 
			
		||||
      component: TeamDetailsView,
 | 
			
		||||
      //children: [
 | 
			
		||||
      //  path: "members",
 | 
			
		||||
      //  component:
 | 
			
		||||
      //],
 | 
			
		||||
      children: [
 | 
			
		||||
        {
 | 
			
		||||
          path: "",
 | 
			
		||||
          component: TeamDetailsMembersListView,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: "",
 | 
			
		||||
          component: TeamDetailsMembersListView,
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
import { defineStore } from "pinia";
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import { useClientStore } from "./client";
 | 
			
		||||
import type { LocationQuery } from "vue-router";
 | 
			
		||||
 | 
			
		||||
export const useAuthStore = defineStore("auth", () => {
 | 
			
		||||
  const clientStore = useClientStore();
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +26,7 @@ export const useAuthStore = defineStore("auth", () => {
 | 
			
		|||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function login(queryParams: { [key: string]: string }) {
 | 
			
		||||
  async function login(queryParams: LocationQuery) {
 | 
			
		||||
    return fetch(import.meta.env.VITE_API_BASE_URL + "/login/authenticate", {
 | 
			
		||||
      headers: {
 | 
			
		||||
        "Content-Type": "application/json",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,13 +12,6 @@ const auth = useAuthStore();
 | 
			
		|||
const registerUsername = ref("");
 | 
			
		||||
 | 
			
		||||
function register() {
 | 
			
		||||
  //const params = {
 | 
			
		||||
  //  ...queryParams.value,
 | 
			
		||||
  //  username: registerUsername.value,
 | 
			
		||||
  //};
 | 
			
		||||
 | 
			
		||||
  //auth.login(params)
 | 
			
		||||
  //  .then(() => router.push("/"));
 | 
			
		||||
  auth.setUsername(registerUsername.value)
 | 
			
		||||
    .then(() => router.push("/"));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -39,8 +32,8 @@ onMounted(() => {
 | 
			
		|||
      <template v-if="auth.isRegistering">
 | 
			
		||||
        <h1>New account</h1>
 | 
			
		||||
        <p>
 | 
			
		||||
          Your account has been newly created. Select a username to be
 | 
			
		||||
          associated with this account.
 | 
			
		||||
          Your account has been created with your username set to your Steam ID
 | 
			
		||||
          by default. Select a new username to be associated with this account.
 | 
			
		||||
        </p>
 | 
			
		||||
        <div class="form-group margin">
 | 
			
		||||
          <h3>Username</h3>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useTeamsStore } from "../stores/teams";
 | 
			
		||||
import { useRoute, useRouter, RouterLink } from "vue-router";
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { useTeamDetails } from "../composables/team-details";
 | 
			
		||||
import PlayerTeamCard from "../components/PlayerTeamCard.vue";
 | 
			
		||||
import InviteEntry from "../components/InviteEntry.vue";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
const {
 | 
			
		||||
  team,
 | 
			
		||||
  invites,
 | 
			
		||||
  availableMembers,
 | 
			
		||||
  availableMembersNextHour,
 | 
			
		||||
  teamMembers,
 | 
			
		||||
} = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
function createInvite() {
 | 
			
		||||
  teamsStore.createInvite(team.value.id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function leaveTeam() {
 | 
			
		||||
  teamsStore.leaveTeam(team.value.id)
 | 
			
		||||
    .then(() => {
 | 
			
		||||
      teamsStore.fetchTeams()
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          router.push("/");
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="member-list-header">
 | 
			
		||||
    <h2>Members</h2>
 | 
			
		||||
    <em class="aside" v-if="teamMembers">
 | 
			
		||||
      {{ teamMembers?.length }} member(s),
 | 
			
		||||
      {{ availableMembers?.length }} currently available,
 | 
			
		||||
      {{ availableMembersNextHour?.length }} available in the next hour
 | 
			
		||||
    </em>
 | 
			
		||||
    <div class="team-details-button-group">
 | 
			
		||||
      <RouterLink class="button" :to="'/schedule?teamId=' + team.id">
 | 
			
		||||
        <button class="accent">
 | 
			
		||||
          <i class="bi bi-calendar-fill margin"></i>
 | 
			
		||||
          View schedule
 | 
			
		||||
        </button>
 | 
			
		||||
      </RouterLink>
 | 
			
		||||
      <button
 | 
			
		||||
        class="destructive"
 | 
			
		||||
        @click="leaveTeam"
 | 
			
		||||
      >
 | 
			
		||||
        Leave
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <table class="member-table">
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <PlayerTeamCard
 | 
			
		||||
        v-for="member in teamMembers"
 | 
			
		||||
        :player="member"
 | 
			
		||||
        :team="team"
 | 
			
		||||
        :key="member.username"
 | 
			
		||||
      />
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
  <h2>Active Invites</h2>
 | 
			
		||||
  <div>
 | 
			
		||||
    <details>
 | 
			
		||||
      <summary>View all invites</summary>
 | 
			
		||||
      <span v-if="invites?.length == 0">
 | 
			
		||||
        There are currently no active invites to this team.
 | 
			
		||||
      </span>
 | 
			
		||||
      <table id="invite-table" v-else>
 | 
			
		||||
        <thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>
 | 
			
		||||
              Key (hover to reveal)
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>
 | 
			
		||||
              Creation time
 | 
			
		||||
            </th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <InviteEntry
 | 
			
		||||
            v-for="invite in invites"
 | 
			
		||||
            :invite="invite"
 | 
			
		||||
          />
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </table>
 | 
			
		||||
      <div class="create-invite-group">
 | 
			
		||||
        <button class="accent" @click="createInvite">
 | 
			
		||||
          <i class="bi bi-person-fill-add margin" />
 | 
			
		||||
          Create Invite
 | 
			
		||||
        </button>
 | 
			
		||||
        <span class="small aside">
 | 
			
		||||
          Invites are usable once and expire after 24 hours.
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </details>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.member-list-header {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 0.5em;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.member-list-header > .aside {
 | 
			
		||||
  font-size: 12pt;
 | 
			
		||||
  font-style: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.member-table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.member-table th {
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  padding-left: 2em;
 | 
			
		||||
  font-weight: 700;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#invite-table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  border: 1px solid var(--text);
 | 
			
		||||
  margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.team-details-button-group {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: end;
 | 
			
		||||
  gap: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.create-invite-group {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 8px;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +1,14 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useRoute, useRouter, RouterLink } from "vue-router";
 | 
			
		||||
import { useRoute, useRouter, RouterLink, RouterView } from "vue-router";
 | 
			
		||||
import { useTeamsStore } from "../stores/teams";
 | 
			
		||||
import { computed, onMounted, ref } from "vue";
 | 
			
		||||
import PlayerTeamCard from "../components/PlayerTeamCard.vue";
 | 
			
		||||
import InviteEntry from "../components/InviteEntry.vue";
 | 
			
		||||
import { useTeamDetails } from "../composables/team-details";
 | 
			
		||||
import moment from "moment";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const teamsStore = useTeamsStore();
 | 
			
		||||
 | 
			
		||||
const team = computed(() => {
 | 
			
		||||
  return teamsStore.teams[route.params.id];
 | 
			
		||||
});
 | 
			
		||||
const { team, teamId } = useTeamDetails();
 | 
			
		||||
 | 
			
		||||
const creationDate = computed(() => {
 | 
			
		||||
  if (team.value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,55 +16,21 @@ const creationDate = computed(() => {
 | 
			
		|||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const invites = computed(() => {
 | 
			
		||||
  return teamsStore.teamInvites[route.params.id];
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const availableMembers = computed(() => {
 | 
			
		||||
  return teamsStore.teamMembers[route.params.id]
 | 
			
		||||
    .filter((member) => member.availability[0] > 0);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const availableMembersNextHour = computed(() => {
 | 
			
		||||
  return teamsStore.teamMembers[route.params.id]
 | 
			
		||||
    .filter((member) => member.availability[1] > 0);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function createInvite() {
 | 
			
		||||
  teamsStore.createInvite(team.value.id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function revokeInvite(key) {
 | 
			
		||||
  teamsStore.revokeInvite(team.value.id, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function leaveTeam() {
 | 
			
		||||
  teamsStore.leaveTeam(team.value.id)
 | 
			
		||||
    .then(() => {
 | 
			
		||||
      teamsStore.fetchTeams()
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          router.push("/");
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  let key = route.query.key;
 | 
			
		||||
  let teamId = route.params.id;
 | 
			
		||||
const key = computed(() => route.query.key);
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  let doFetchTeam = () => {
 | 
			
		||||
    teamsStore.fetchTeam(teamId)
 | 
			
		||||
      .then(() => teamsStore.fetchTeamMembers(teamId))
 | 
			
		||||
      .then(() => teamsStore.getInvites(teamId));
 | 
			
		||||
    teamsStore.fetchTeam(teamId.value)
 | 
			
		||||
      .then(() => teamsStore.fetchTeamMembers(teamId.value))
 | 
			
		||||
      .then(() => teamsStore.getInvites(teamId.value));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (key) {
 | 
			
		||||
    teamsStore.consumeInvite(teamId, key)
 | 
			
		||||
  if (key.value) {
 | 
			
		||||
    teamsStore.consumeInvite(teamId.value, key.value.toString())
 | 
			
		||||
      .finally(doFetchTeam);
 | 
			
		||||
  } else {
 | 
			
		||||
    doFetchTeam();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,90 +45,7 @@ onMounted(async () => {
 | 
			
		|||
          Formed on {{ creationDate }}
 | 
			
		||||
        </span>
 | 
			
		||||
      </center>
 | 
			
		||||
      <div class="member-list-header">
 | 
			
		||||
        <h2>Members</h2>
 | 
			
		||||
        <em class="aside" v-if="teamsStore.teamMembers[route.params.id]">
 | 
			
		||||
          {{ teamsStore.teamMembers[route.params.id]?.length }} member(s),
 | 
			
		||||
          {{ availableMembers?.length }} currently available,
 | 
			
		||||
          {{ availableMembersNextHour?.length }} available in the next hour
 | 
			
		||||
        </em>
 | 
			
		||||
        <div class="team-details-button-group">
 | 
			
		||||
          <RouterLink class="button" :to="'/schedule?teamId=' + team.id">
 | 
			
		||||
            <button class="accent">
 | 
			
		||||
              <i class="bi bi-calendar-fill margin"></i>
 | 
			
		||||
              View schedule
 | 
			
		||||
            </button>
 | 
			
		||||
          </RouterLink>
 | 
			
		||||
          <button
 | 
			
		||||
            class="destructive"
 | 
			
		||||
            @click="leaveTeam"
 | 
			
		||||
          >
 | 
			
		||||
            Leave
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <table class="member-table">
 | 
			
		||||
        <!--thead>
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th>
 | 
			
		||||
              Name
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>
 | 
			
		||||
              Roles
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>
 | 
			
		||||
              Playtime on team
 | 
			
		||||
            </th>
 | 
			
		||||
            <th>
 | 
			
		||||
              Joined
 | 
			
		||||
            </th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead-->
 | 
			
		||||
        <tbody>
 | 
			
		||||
          <PlayerTeamCard
 | 
			
		||||
            v-for="member in teamsStore.teamMembers[route.params.id]"
 | 
			
		||||
            :player="member"
 | 
			
		||||
            :team="team"
 | 
			
		||||
            :key="member.username"
 | 
			
		||||
          />
 | 
			
		||||
        </tbody>
 | 
			
		||||
      </table>
 | 
			
		||||
      <h2>Active Invites</h2>
 | 
			
		||||
      <div>
 | 
			
		||||
        <details>
 | 
			
		||||
          <summary>View all invites</summary>
 | 
			
		||||
          <span v-if="invites?.length == 0">
 | 
			
		||||
            There are currently no active invites to this team.
 | 
			
		||||
          </span>
 | 
			
		||||
          <table id="invite-table" v-else>
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th>
 | 
			
		||||
                  Key (hover to reveal)
 | 
			
		||||
                </th>
 | 
			
		||||
                <th>
 | 
			
		||||
                  Creation time
 | 
			
		||||
                </th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <InviteEntry
 | 
			
		||||
                v-for="invite in invites"
 | 
			
		||||
                :invite="invite"
 | 
			
		||||
              />
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
          <div class="create-invite-group">
 | 
			
		||||
            <button class="accent" @click="createInvite">
 | 
			
		||||
              <i class="bi bi-person-fill-add margin" />
 | 
			
		||||
              Create Invite
 | 
			
		||||
            </button>
 | 
			
		||||
            <span class="small aside">
 | 
			
		||||
              Invites are usable once and expire after 24 hours.
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
        </details>
 | 
			
		||||
      </div>
 | 
			
		||||
      <RouterView />
 | 
			
		||||
    </template>
 | 
			
		||||
  </main>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			@ -175,59 +54,4 @@ onMounted(async () => {
 | 
			
		|||
.team-info {
 | 
			
		||||
  margin: 4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.member-list-header {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 0.5em;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.member-list-header > .aside {
 | 
			
		||||
  font-size: 12pt;
 | 
			
		||||
  font-style: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.member-table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.member-table th {
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  padding-left: 2em;
 | 
			
		||||
  font-weight: 700;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
div.member-grid {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 8px;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#invite-table {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  border: 1px solid var(--text);
 | 
			
		||||
  margin: 8px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.team-details-button-group {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: end;
 | 
			
		||||
  gap: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.create-invite-group {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  gap: 8px;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<script lang="ts" setup>
 | 
			
		||||
import { ref, watch } from "vue";
 | 
			
		||||
import { useTeamsStore } from "../stores/teams.ts"
 | 
			
		||||
import { useTeamsStore } from "../stores/teams";
 | 
			
		||||
import timezones from "../assets/timezones.json";
 | 
			
		||||
import { useRouter } from "vue-router";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue