Skip to content

Commit b94eec8

Browse files
committed
Use a tree to display game events and proposals
1 parent 91cfdf8 commit b94eec8

File tree

4 files changed

+173
-98
lines changed

4 files changed

+173
-98
lines changed

frontend/src/components/match/GameEventItem.vue

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {computed} from "vue";
33
import TeamBadge from "@/components/common/TeamBadge.vue";
44
import {gameEventNames} from "@/helpers/texts";
55
import type {GameEvent} from "@/proto/ssl_gc_game_event";
6-
import {gameEventForTeam, originIcon} from "@/helpers";
7-
import GameEventDetailsTree from "@/components/match/GameEventDetailsTree.vue";
6+
import {gameEventForTeam} from "@/helpers";
87
import dayjs from "dayjs";
8+
import OriginIcon from "@/components/common/OriginIcon.vue";
99
1010
const props = defineProps<{
1111
gameEvent: GameEvent,
@@ -32,26 +32,26 @@ const time = computed(() => {
3232
</script>
3333

3434
<template>
35-
<q-expansion-item expand-separator>
36-
<template v-slot:header>
37-
<q-item-section>
38-
<q-item-label>
39-
<TeamBadge :team="team"/>
40-
{{ label }}
41-
</q-item-label>
42-
<q-item-label caption v-if="time">{{ time }}</q-item-label>
43-
</q-item-section>
44-
<q-item-section side>
45-
<div class="row">
46-
<q-icon class="q-mx-xs" :name="originIcon(origin)" color="primary" :alt="origin"
47-
v-for="(origin, key) in origins" :key="key">
48-
<q-tooltip>
49-
{{ origin }}
50-
</q-tooltip>
51-
</q-icon>
52-
</div>
53-
</q-item-section>
54-
</template>
55-
<GameEventDetailsTree :game-event="props.gameEvent"/>
56-
</q-expansion-item>
35+
<q-item dense>
36+
<q-item-section side>
37+
<q-icon name="warning"/>
38+
</q-item-section>
39+
<q-item-section>
40+
<q-item-label>
41+
<TeamBadge :team="team"/>
42+
{{ label }}
43+
</q-item-label>
44+
<q-item-label caption v-if="time">{{ time }}</q-item-label>
45+
</q-item-section>
46+
<q-item-section side>
47+
<div class="row">
48+
<OriginIcon
49+
v-for="(gameEventOrigin, key) in origins"
50+
:key="key"
51+
:origin="gameEventOrigin"
52+
tooltip
53+
/>
54+
</div>
55+
</q-item-section>
56+
</q-item>
5757
</template>
Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
<script setup lang="ts">
2-
import {computed, inject} from "vue";
3-
import GameEventProposalItem from "@/components/match/GameEventProposalItem.vue";
2+
import {computed} from "vue";
43
import {gameEventNames} from "@/helpers/texts";
5-
import type {ControlApi} from "@/providers/controlApi/ControlApi";
64
import type {ProposalGroup} from "@/proto/ssl_gc_state";
75
import type {GameEvent} from "@/proto/ssl_gc_game_event";
86
import TeamBadge from "@/components/common/TeamBadge.vue";
9-
import {gameEventForTeam, originIcon} from "@/helpers";
10-
import GameEventItem from "@/components/match/GameEventItem.vue";
7+
import {gameEventForTeam} from "@/helpers";
8+
import OriginIcon from "@/components/common/OriginIcon.vue";
9+
import dayjs from "dayjs";
1110
1211
const props = defineProps<{
1312
proposalGroup: ProposalGroup,
1413
groupId: string,
1514
acceptedGameEvent?: GameEvent,
1615
}>()
1716
18-
const control = inject<ControlApi>('control-api')
19-
2017
const proposals = computed(() => {
2118
return props.proposalGroup.proposals
2219
})
@@ -46,8 +43,18 @@ const origins = computed(() => {
4643
return Array.from(new Set(proposals.value?.flatMap(p => p.gameEvent?.origin || []).values()))
4744
})
4845
49-
const pending = computed(() => {
50-
return !props.proposalGroup.accepted
46+
const createdTimestamp = computed(() => {
47+
if (props.acceptedGameEvent) {
48+
return props.acceptedGameEvent.createdTimestamp
49+
}
50+
return proposals.value?.flatMap(p => p.gameEvent?.createdTimestamp).sort()[0]
51+
})
52+
53+
const time = computed(() => {
54+
if (createdTimestamp.value) {
55+
return dayjs(createdTimestamp.value / 1e3).format("MMM, DD YYYY HH:mm:ss,SSS")
56+
}
57+
return undefined
5158
})
5259
5360
const groupIcon = computed(() => {
@@ -56,43 +63,29 @@ const groupIcon = computed(() => {
5663
}
5764
return 'pending'
5865
})
59-
60-
const acceptGroup = () => {
61-
control?.AcceptProposalGroup(props.groupId)
62-
}
6366
</script>
6467

6568
<template>
66-
<q-expansion-item
67-
expand-separator
68-
expand-icon-toggle
69-
:default-opened="pending"
70-
>
71-
<template v-slot:header>
72-
<q-item-section side v-if="pending">
73-
<q-icon :name="groupIcon"/>
74-
</q-item-section>
75-
<q-item-section>
76-
<q-item-label>
77-
<TeamBadge v-for="team in teams" :team="team" :key="team"/>
78-
{{ label }}
79-
</q-item-label>
80-
</q-item-section>
81-
<q-item-section side>
82-
<div class="row">
83-
<q-icon class="q-mx-xs" :name="originIcon(origin)" color="primary" :alt="origin"
84-
v-for="(origin, key) in origins" :key="key">
85-
<q-tooltip>
86-
{{ origin }}
87-
</q-tooltip>
88-
</q-icon>
89-
</div>
90-
<q-btn flat dense round icon="done" @click="acceptGroup" v-if="pending"/>
91-
</q-item-section>
92-
</template>
93-
<q-list>
94-
<GameEventItem :game-event="acceptedGameEvent" v-if="acceptedGameEvent"/>
95-
<GameEventProposalItem :proposal="proposal" v-for="(proposal, key) in proposals" :key="key"/>
96-
</q-list>
97-
</q-expansion-item>
69+
<q-item dense>
70+
<q-item-section side>
71+
<q-icon :name="groupIcon"/>
72+
</q-item-section>
73+
<q-item-section>
74+
<q-item-label>
75+
<TeamBadge v-for="team in teams" :team="team" :key="team"/>
76+
{{ label }}
77+
</q-item-label>
78+
<q-item-label caption v-if="time">{{ time }}</q-item-label>
79+
</q-item-section>
80+
<q-item-section side>
81+
<div class="row">
82+
<OriginIcon
83+
v-for="(gameEventOrigin, key) in origins"
84+
:key="key"
85+
:origin="gameEventOrigin"
86+
tooltip
87+
/>
88+
</div>
89+
</q-item-section>
90+
</q-item>
9891
</template>

frontend/src/components/match/GameEventProposalItem.vue

Lines changed: 0 additions & 17 deletions
This file was deleted.

frontend/src/components/match/GameEvents.vue

Lines changed: 111 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
<script setup lang="ts">
2-
import {computed} from "vue";
2+
import {computed, inject} from "vue";
33
import GameEventItem from "@/components/match/GameEventItem.vue";
44
import GameEventProposalGroupItem from "@/components/match/GameEventProposalGroupItem.vue";
55
import {useMatchStateStore} from "@/store/matchState";
66
import type {ProposalGroup} from "@/proto/ssl_gc_state";
77
import type {GameEvent} from "@/proto/ssl_gc_game_event";
8+
import GameEventDetailsTree from "@/components/match/GameEventDetailsTree.vue";
9+
import type {ControlApi} from "@/providers/controlApi/ControlApi";
810
911
const store = useMatchStateStore()
12+
const control = inject<ControlApi>('control-api')
1013
1114
type GameEventWrappedItem = {
1215
id: string
@@ -15,6 +18,18 @@ type GameEventWrappedItem = {
1518
gameEvent?: GameEvent
1619
}
1720
21+
interface GameEventTreeNode {
22+
id: string
23+
header: string
24+
proposalGroup?: ProposalGroup
25+
gameEvent?: GameEvent
26+
children?: GameEventTreeNode[]
27+
}
28+
29+
interface Prop {
30+
node: GameEventTreeNode
31+
}
32+
1833
const gameEventItems = computed(() => {
1934
const gameEvents = store.matchState.gameEvents!
2035
const proposalGroups = store.matchState.proposalGroups!
@@ -46,25 +61,109 @@ const gameEventItems = computed(() => {
4661
return items
4762
})
4863
64+
function gameEventNode(gameEvent: GameEvent): GameEventTreeNode {
65+
return {
66+
id: gameEvent.id || '',
67+
header: 'game-event',
68+
gameEvent: gameEvent,
69+
children: [
70+
{
71+
id: (gameEvent.id || '') + "-details",
72+
header: 'game-event-details',
73+
gameEvent: gameEvent,
74+
}
75+
]
76+
}
77+
}
78+
79+
const nodes = computed(() => {
80+
const nodeList: GameEventTreeNode[] = []
81+
82+
for (const gameEventItem of gameEventItems.value) {
83+
if (gameEventItem.proposalGroup) {
84+
let children = gameEventItem.proposalGroup.proposals!.map(proposal => {
85+
return gameEventNode(proposal.gameEvent!)
86+
});
87+
if (!gameEventItem.proposalGroup.accepted) {
88+
children.unshift({
89+
id: gameEventItem.id + "-accept",
90+
header: 'accept',
91+
proposalGroup: gameEventItem.proposalGroup,
92+
gameEvent: gameEventItem.gameEvent,
93+
})
94+
}
95+
nodeList.push({
96+
id: gameEventItem.id,
97+
header: 'proposal',
98+
gameEvent: gameEventItem.gameEvent,
99+
proposalGroup: gameEventItem.proposalGroup,
100+
children: children
101+
})
102+
} else if (gameEventItem.gameEvent) {
103+
nodeList.push(gameEventNode(gameEventItem.gameEvent))
104+
}
105+
}
106+
return nodeList
107+
})
108+
109+
const acceptGroup = (groupId: string) => {
110+
control?.AcceptProposalGroup(groupId)
111+
}
112+
49113
</script>
50114

51115
<template>
52-
<q-list bordered class="rounded-borders" v-if="gameEventItems.length > 0">
53-
<template v-for="(item) in gameEventItems">
116+
<q-tree
117+
v-if="gameEventItems.length > 0"
118+
:nodes="nodes"
119+
node-key="id"
120+
dense
121+
class="full-width"
122+
>
123+
<!--suppress VueUnrecognizedSlot -->
124+
<template #header-accept="prop: Prop">
125+
<q-item class="full-width">
126+
<q-item-section side>
127+
<q-icon name="done"/>
128+
</q-item-section>
129+
<q-item-section>
130+
<q-btn
131+
dense
132+
color="primary"
133+
label="Accept"
134+
@click="() => acceptGroup(prop.node.proposalGroup?.id!)"
135+
v-if="!prop.node.proposalGroup?.accepted"/>
136+
</q-item-section>
137+
</q-item>
138+
</template>
139+
140+
<!--suppress VueUnrecognizedSlot -->
141+
<template #header-proposal="prop: Prop">
54142
<GameEventProposalGroupItem
55-
v-if="item.proposalGroup"
56-
:proposal-group="item.proposalGroup"
57-
:group-id="item.id"
58-
:accepted-game-event="item.gameEvent"
59-
:key="item.id"
143+
class="full-width"
144+
:proposal-group="prop.node.proposalGroup!"
145+
:group-id="prop.node.id"
146+
:accepted-game-event="prop.node.gameEvent!"
60147
/>
148+
</template>
149+
150+
<!--suppress VueUnrecognizedSlot -->
151+
<template #header-game-event="prop: Prop">
61152
<GameEventItem
62-
v-else-if="item.gameEvent"
63-
:game-event="item.gameEvent"
64-
:key="item.id"
153+
class="full-width"
154+
:game-event="prop.node.gameEvent!"
65155
/>
66156
</template>
67-
</q-list>
157+
158+
<!--suppress VueUnrecognizedSlot -->
159+
<template v-slot:header-game-event-details="prop: Prop">
160+
<GameEventDetailsTree
161+
class="full-width"
162+
:game-event="prop.node.gameEvent!"
163+
/>
164+
</template>
165+
</q-tree>
166+
68167
<div v-else>
69168
No recent game event proposals
70169
</div>

0 commit comments

Comments
 (0)