-
+
- {{ $t("matchmaking.go_to_match") }}
+ {{ draftGameId ? $t("draft_games.room.go_to_room") : $t("matchmaking.go_to_match") }}
@@ -134,6 +134,12 @@ export default {
},
isOnMatchPage(): boolean {
const path = this.$route?.path || "";
+ if (
+ this.draftGameId &&
+ path.startsWith(`/draft-room/${this.draftGameId}`)
+ ) {
+ return true;
+ }
return !!this.match && path.startsWith(`/matches/${this.match.id}`);
},
shouldShow(): boolean {
@@ -155,6 +161,14 @@ export default {
const b = this.match?.lineup_2?.name || this.$t("common.tbd");
return `${a} vs ${b}`;
},
+ draftGameId(): string | null {
+ return this.match?.draft_games?.[0]?.id || null;
+ },
+ targetLink(): string {
+ return this.draftGameId
+ ? `/draft-room/${this.draftGameId}`
+ : `/matches/${this.match.id}`;
+ },
},
watch: {
matchKey(next, prev) {
diff --git a/components/match/MatchForm.vue b/components/match/MatchForm.vue
index f64858f5c..5644d2532 100644
--- a/components/match/MatchForm.vue
+++ b/components/match/MatchForm.vue
@@ -1,6 +1,7 @@
@@ -74,21 +71,13 @@ const tacLabelClasses =
{{ activeLobbyAccessDescription(value) }}
-
-
-
+
diff --git a/components/match/MatchLiveStreams.vue b/components/match/MatchLiveStreams.vue
index 4292df1f2..927338b62 100644
--- a/components/match/MatchLiveStreams.vue
+++ b/components/match/MatchLiveStreams.vue
@@ -263,7 +263,11 @@ import StreamViewerBadge from "~/components/match/StreamViewerBadge.vue";
>
{{ $t("common.cancel") }}
-
-
+
{{ $t("streams.add") }}
diff --git a/components/match/MatchMapVeto.vue b/components/match/MatchMapVeto.vue
index 709749c67..98cba9f90 100644
--- a/components/match/MatchMapVeto.vue
+++ b/components/match/MatchMapVeto.vue
@@ -165,10 +165,8 @@ import MatchPicksDisplay from "~/components/match/MatchPicksDisplay.vue";
-
-
-
-
+
+
@@ -195,6 +193,10 @@ export default {
type: Object,
required: true,
},
+ matchId: {
+ type: String,
+ required: false,
+ },
},
apollo: {
$subscribe: {
@@ -202,7 +204,7 @@ export default {
variables: function () {
return {
order_by: order_by.asc,
- matchId: this.$route.params.id,
+ matchId: (this.matchId || this.$route.params.id),
};
},
query: typedGql("subscription")({
@@ -225,6 +227,7 @@ export default {
map: {
id: true,
name: true,
+ label: true,
patch: true,
poster: true,
},
@@ -235,6 +238,9 @@ export default {
{},
{
name: true,
+ team: {
+ avatar_url: true,
+ },
},
],
},
@@ -346,7 +352,7 @@ export default {
side,
}
: {}),
- match_id: this.$route.params.id,
+ match_id: (this.matchId || this.$route.params.id),
match_lineup_id: this.match.map_veto_picking_lineup_id,
},
mutation: generateMutation({
@@ -431,9 +437,6 @@ export default {
},
];
},
- hasAssignedRegion() {
- return this.match.options.region_veto && this.match.e_region;
- },
mapPool() {
return this.match.options?.map_pool?.maps;
},
diff --git a/components/match/MatchOptionsDisplay.vue b/components/match/MatchOptionsDisplay.vue
index 90b54848e..61b216ef2 100644
--- a/components/match/MatchOptionsDisplay.vue
+++ b/components/match/MatchOptionsDisplay.vue
@@ -1,12 +1,24 @@
@@ -39,140 +51,193 @@ import MiniMapDisplay from "~/components/MinIMapDisplay.vue";
-
-
-
-
- {{
- $t("match.options.group.game_settings")
- }}
-
-
-
- {{
- $t("match.options.max_rounds")
- }}
- {{ options.mr }}
-
-
-
{{
- $t("match.options.tv_delay")
- }}
-
{{ options.tv_delay }} {{ $t("match.options.seconds") }}
+
+
+
+
+
+ {{ $t("match.options.group.game_settings") }}
+
+
+
+
- {{
+ $t("match.options.type.label")
+ }}
+ - {{ options.type }}
+
+
+
- {{
+ $t("match.options.best_of.label")
+ }}
+ - {{
+ options.best_of
+ }}
+
+
+
- {{
+ $t("match.options.max_rounds")
+ }}
+ - {{
+ options.mr
+ }}
+
+
+
- {{
+ $t("match.options.tv_delay")
+ }}
+ -
+ {{ options.tv_delay }}
+ {{
+ $t("match.options.seconds")
+ }}
+
+
+
+
- {{
+ $t("match.options.overtime")
+ }}
+
+
+
+
- {{
+ $t("match.options.knife_round")
+ }}
+
+
+
+
- {{
+ $t("match.options.default_player_models")
+ }}
+
+
+
+
+
+
+
+
+
+ {{ $t("match.options.group.control_settings") }}
+
+
+
+
- {{
+ $t("match.options.timeout_setting")
+ }}
+ - {{
+ options.timeout_setting
+ }}
+
+
+
- {{
+ $t("match.options.tech_timeout_setting")
+ }}
+ - {{
+ options.tech_timeout_setting
+ }}
+
+
+
- {{
+ $t("match.options.ready_setting")
+ }}
+ - {{ options.ready_setting }}
+
+
+
- {{
+ $t("match.options.check_in_setting")
+ }}
+ - {{
+ options.check_in_setting
+ }}
+
+
+
- {{
+ $t("match.options.advanced.match_mode.label")
+ }}
+ - {{
+ options.match_mode === "auto"
+ ? $t("match.options.advanced.match_mode.options.auto")
+ : $t("match.options.advanced.match_mode.options.admin")
+ }}
+
+
+
- {{
+ $t("match.options.advanced.auto_cancellation.label")
+ }}
+
+
+
+
- {{
+ $t(
+ "match.options.advanced.auto_cancellation.auto_cancel_duration.label",
+ )
+ }}
+ - {{
+ options.auto_cancel_duration
+ }}
+
+
-
-
- {{
- $t("match.options.overtime")
- }}
-
-
-
- {{
- $t("match.options.knife_round")
- }}
-
-
-
- {{
- $t("match.options.default_player_models")
- }}
-
-
-
-
-
-
-
-
- {{
- $t("match.options.group.team_settings")
- }}
-
-
-
- {{
- $t("common.coaches")
- }}
-
-
-
- {{
- $t("match.options.substitutes")
- }}
- {{
- options.number_of_substitutes
- }}
-
-
-
-
-
-
-
- {{
- $t("match.options.group.veto_settings")
- }}
-
-
-
- {{
- $t("common.map_veto")
- }}
-
-
-
- {{
- $t("match.options.region_veto")
- }}
-
-
-
-
-
-
-
-
- {{
- $t("match.options.group.control_settings")
- }}
-
-
-
- {{
- $t("match.options.timeout_setting")
- }}
- {{
- options.timeout_setting
- }}
-
-
- {{
- $t("match.options.tech_timeout_setting")
- }}
- {{
- options.tech_timeout_setting
- }}
-
-
- {{
- $t("match.options.ready_setting")
- }}
- {{
- options.ready_setting
- }}
-
-
- {{
- $t("match.options.check_in_setting")
- }}
- {{
- options.check_in_setting
- }}
-
-
-
+ - {{
+ $t(
+ "match.options.advanced.auto_cancellation.live_match_timeout.label",
+ )
+ }}
+ - {{
+ options.live_match_timeout
+ }}
+
+
+
+
+
+
+
+
+ {{ $t("match.options.group.team_settings") }}
+
+
+
+
- {{ $t("common.coaches") }}
+
+
+
+
- {{
+ $t("match.options.substitutes")
+ }}
+ - {{
+ options.number_of_substitutes
+ }}
+
+
+
+
+
+
+
+
+ {{ $t("match.options.group.veto_settings") }}
+
+
+
+
- {{ $t("common.map_veto") }}
+
+
+
+
- {{
+ $t("match.options.region_veto")
+ }}
+
+
+
+
+
@@ -197,3 +262,150 @@ export default {
},
};
+
+
diff --git a/components/match/MatchPicksDisplay.vue b/components/match/MatchPicksDisplay.vue
index cda8856cd..d59ec02c7 100644
--- a/components/match/MatchPicksDisplay.vue
+++ b/components/match/MatchPicksDisplay.vue
@@ -14,7 +14,7 @@ import cleanMapName from "~/utilities/cleanMapName";
}}
{{ $t("match.picks.empty") }}
@@ -118,6 +118,16 @@ import cleanMapName from "~/utilities/cleanMapName";
+
+
+
+ {{ visiblePicks.length + n }}
+
@@ -133,10 +143,18 @@ export default {
type: Object,
required: true,
},
+ picks: {
+ type: Array,
+ required: false,
+ default: undefined,
+ },
},
emits: ["update:count"],
apollo: {
match_map_veto_picks: {
+ skip() {
+ return Array.isArray(this.picks);
+ },
variables() {
return {
order_by: order_by.asc,
@@ -182,28 +200,40 @@ export default {
],
}),
result({ data }: { data: any }) {
- this.picks = data?.match_map_veto_picks ?? [];
+ this.queriedPicks = data?.match_map_veto_picks ?? [];
this.$emit(
"update:count",
- this.picks.filter((p: any) => p.type !== "Side").length,
+ this.queriedPicks.filter((p: any) => p.type !== "Side").length,
);
},
},
},
data() {
return {
- picks: undefined as any[] | undefined,
+ queriedPicks: undefined as any[] | undefined,
};
},
computed: {
+ sourcePicks(): any[] | undefined {
+ return Array.isArray(this.picks) ? this.picks : this.queriedPicks;
+ },
loading() {
- return this.picks === undefined;
+ return this.sourcePicks === undefined;
},
apiDomain() {
return useRuntimeConfig().public.apiDomain;
},
visiblePicks(): any[] {
- return (this.picks ?? []).filter((p: any) => p.type !== "Side");
+ return (this.sourcePicks ?? []).filter((p: any) => p.type !== "Side");
+ },
+ // Every map in the pool ends up picked/banned/decider (sides excluded), so
+ // the pool size is the final grid size — reserve that many slots up front.
+ totalSlots(): number {
+ const poolSize = this.match?.options?.map_pool?.maps?.length ?? 0;
+ return Math.max(poolSize, this.visiblePicks.length);
+ },
+ placeholderCount(): number {
+ return Math.max(0, this.totalSlots - this.visiblePicks.length);
},
},
methods: {
@@ -218,7 +248,7 @@ export default {
sidePickForMap(mapId: string | undefined | null) {
if (!mapId) return null;
return (
- this.picks?.find(
+ this.sourcePicks?.find(
(p: any) => p.type === "Side" && p.map?.id === mapId,
) ?? null
);
@@ -306,6 +336,25 @@ export default {
border-color 200ms ease,
box-shadow 200ms ease;
}
+/* A locked pick materializes into its slot rather than snapping in. */
+.pick-card:not(.pick-card--placeholder) {
+ animation: pick-pop 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
+}
+@keyframes pick-pop {
+ from {
+ opacity: 0;
+ transform: scale(0.9) translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: none;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ .pick-card:not(.pick-card--placeholder) {
+ animation: none;
+ }
+}
.pick-card:hover {
border-color: hsl(var(--tac-amber) / 0.45);
box-shadow: 0 0 0 1px hsl(var(--tac-amber) / 0.15);
@@ -316,6 +365,26 @@ export default {
box-shadow: 0 0 0 2px hsl(var(--tac-amber) / 0.35);
}
+.pick-card--placeholder {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 8.5rem;
+ border-style: dashed;
+ border-color: hsl(var(--border) / 0.5);
+ background: hsl(var(--card) / 0.3);
+}
+.pick-card--placeholder:hover {
+ border-color: hsl(var(--border) / 0.5);
+ box-shadow: none;
+}
+.placeholder-num {
+ font-family: var(--font-mono, monospace);
+ font-size: 1.1rem;
+ font-weight: 800;
+ color: hsl(var(--muted-foreground) / 0.35);
+}
+
.type-pill {
display: inline-flex;
align-items: center;
diff --git a/components/match/MatchRegionVeto.vue b/components/match/MatchRegionVeto.vue
index 57f73c50e..4be055754 100644
--- a/components/match/MatchRegionVeto.vue
+++ b/components/match/MatchRegionVeto.vue
@@ -224,13 +224,17 @@ export default {
type: Object,
required: true,
},
+ matchId: {
+ type: String,
+ required: false,
+ },
},
apollo: {
$subscribe: {
match_map_veto_picks: {
variables: function () {
return {
- matchId: this.$route.params.id,
+ matchId: (this.matchId || this.$route.params.id),
};
},
query: typedGql("subscription")({
@@ -393,7 +397,7 @@ export default {
update_matches_by_pk: [
{
pk_columns: {
- id: this.$route.params.id,
+ id: (this.matchId || this.$route.params.id),
},
_set: {
region: $("region", "String!"),
@@ -431,7 +435,7 @@ export default {
variables: {
region,
type: e_veto_pick_types_enum.Ban,
- match_id: this.$route.params.id,
+ match_id: (this.matchId || this.$route.params.id),
match_lineup_id: this.match.region_veto_picking_lineup_id,
},
mutation: generateMutation({
diff --git a/components/match/MatchSelectServer.vue b/components/match/MatchSelectServer.vue
index 058915660..483d6e692 100644
--- a/components/match/MatchSelectServer.vue
+++ b/components/match/MatchSelectServer.vue
@@ -3,7 +3,6 @@ import {
FormControl,
FormField,
FormItem,
- FormLabel,
FormMessage,
} from "~/components/ui/form";
import {
@@ -14,13 +13,14 @@ import {
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
+import SettingHeader from "~/components/match/SettingHeader.vue";