@xcpcio/board-app 0.83.2 → 0.85.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/404.html +1 -1
- package/dist/assets/Balloon-B3cPN2yD.js +1 -0
- package/dist/assets/Board-BpIhDrix.css +1 -0
- package/dist/assets/Board-CWwqX2IB.js +190 -0
- package/dist/assets/ContestStateBadge-CQOaPmT8.js +1 -0
- package/dist/assets/ContestStateBadge-DgwznQrX.css +1 -0
- package/dist/assets/Countdown-BvWlPnYq.js +1 -0
- package/dist/assets/DataSourceInput-DCNrUj45.js +1 -0
- package/dist/assets/NavBar-O1kqnfE4.css +1 -0
- package/dist/assets/NavBar-t1eXyPKg.js +1 -0
- package/dist/assets/Resolver-CRBstFE3.css +1 -0
- package/dist/assets/Resolver-D_GRj6HP.js +1 -0
- package/dist/assets/RightArrowIcon-F6HBDgDW.js +1 -0
- package/dist/assets/TheInput-Btyuor_6.js +1 -0
- package/dist/assets/Tooltip-C9jfn33Z.css +1 -0
- package/dist/assets/Tooltip-DPDTUI-s.js +27 -0
- package/dist/assets/_..-D9qurBBH.css +1 -0
- package/dist/assets/_...all_-Chjz45WV.js +1 -0
- package/dist/assets/_...all_-TFChZhkr.js +6 -0
- package/dist/assets/_intlify-i18n-0-BmghNkij.js +1 -0
- package/dist/assets/_intlify-i18n-1-DzpDgiG5.js +1 -0
- package/dist/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
- package/dist/assets/balloon-BlTynoB_.js +1 -0
- package/dist/assets/board-A69GsfUB.js +1 -0
- package/dist/assets/board-layout-mk2Y30Yp.js +1 -0
- package/dist/assets/constant-YlOY-yXZ.js +1 -0
- package/dist/assets/countdown-DQRGLfdE.js +1 -0
- package/dist/assets/default-BNqFZJwJ.js +1 -0
- package/dist/assets/headless-BSf0YrvV.js +1 -0
- package/dist/assets/index-C1jdVImx.css +1 -0
- package/dist/assets/index-D57KOcpK.js +12 -0
- package/dist/assets/index-layout-Cg_FpvvD.js +1 -0
- package/dist/assets/pages-DZG1eNR4.css +1 -0
- package/dist/assets/{index-C0JhRtxz.js → pages-kJjvENga.js} +2 -2
- package/dist/assets/pagination-EIqoVe6M.js +21 -0
- package/dist/assets/person-DrN1htE6.js +1 -0
- package/dist/assets/query-CoPl3fiI.js +20 -0
- package/dist/assets/rank-Bjo0QJlM.js +1 -0
- package/dist/assets/rating-B3sXjXNN.js +1 -0
- package/dist/assets/rating-DiCQYzia.css +1 -0
- package/dist/assets/resolver-Db22e0q8.js +1 -0
- package/dist/assets/route-block-ChiJXsfH.js +1 -0
- package/dist/assets/utils-Cfm-h8Z9.js +1 -0
- package/dist/assets/vue.runtime.esm-bundler-DTMOfGWR.js +4 -0
- package/dist/index.html +1 -1
- package/package.json +45 -45
- package/src/auto-imports.d.ts +4 -4
- package/src/components/board/Board.vue +7 -4
- package/src/components/board/Export.vue +5 -1
- package/src/components/board/PendingPage.vue +85 -47
- package/src/components/board/Progress.vue +1 -0
- package/src/components/board/SeatMapView.vue +265 -0
- package/src/components/board/SubmissionsTable.vue +1 -2
- package/src/components/board/SubmitHeatMap.less +11 -3
- package/src/components/board/SubmitHeatMap.vue +56 -86
- package/src/components/board/TeamCard.vue +6 -6
- package/src/components/board/TeamOverview.vue +1 -0
- package/src/components/common/SegmentedControl.vue +87 -0
- package/src/components/flowbite/Tooltip.vue +79 -13
- package/src/components.d.ts +2 -1
- package/src/composables/useQueryBoardData.ts +18 -0
- package/dist/assets/Balloon.vue_vue_type_script_setup_true_lang-CZ_Wx4To.js +0 -1
- package/dist/assets/Board-B0CAQsJI.css +0 -1
- package/dist/assets/Board-BVDDayjC.js +0 -194
- package/dist/assets/ContestStateBadge-D6uf8wH-.css +0 -1
- package/dist/assets/ContestStateBadge-DV_spC5E.js +0 -1
- package/dist/assets/Countdown-BlEqPiY_.js +0 -1
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-CwU_Xgr1.js +0 -1
- package/dist/assets/NavBar-DCPwz9qZ.js +0 -1
- package/dist/assets/NavBar-aT2xEvYz.css +0 -1
- package/dist/assets/Resolver-CY9BFOM6.js +0 -1
- package/dist/assets/Resolver-DV9FleNP.css +0 -1
- package/dist/assets/RightArrowIcon-CtYUhYc6.js +0 -1
- package/dist/assets/TheInput.vue_vue_type_script_setup_true_lang-DPzm-iVE.js +0 -1
- package/dist/assets/Tooltip-DzyPDwbo.css +0 -1
- package/dist/assets/Tooltip.vue_vue_type_script_setup_true_lang-DONjqLXY.js +0 -27
- package/dist/assets/_..-5I0UWqRG.css +0 -1
- package/dist/assets/_...all_-B7l1949F.js +0 -6
- package/dist/assets/_...all_-T0zBVu5S.js +0 -1
- package/dist/assets/_plugin-vue_export-helper-DlAUqK2U.js +0 -1
- package/dist/assets/board-layout-B5DfJEXx.js +0 -1
- package/dist/assets/constant-C2gH6ZII.js +0 -1
- package/dist/assets/dayjs-DjxrjdEm.js +0 -1
- package/dist/assets/default-B200Q7K-.js +0 -1
- package/dist/assets/en-BEousmNN.js +0 -1
- package/dist/assets/headless-OPBem1cy.js +0 -1
- package/dist/assets/index-B0gZ0sxU.js +0 -15
- package/dist/assets/index-BSgd4Lsj.js +0 -1
- package/dist/assets/index-Bk7pJUWz.js +0 -1
- package/dist/assets/index-Bojw84ll.css +0 -1
- package/dist/assets/index-D-lXJTYs.css +0 -1
- package/dist/assets/index-D-msPqye.css +0 -1
- package/dist/assets/index-DqbvaKi_.js +0 -1
- package/dist/assets/index-layout-DBiZ6YWC.js +0 -1
- package/dist/assets/index-uFy-gvEE.js +0 -1
- package/dist/assets/index-uV5YmJst.js +0 -1
- package/dist/assets/pagination-DQg-v0kh.js +0 -3
- package/dist/assets/person-EUz_T6aJ.js +0 -1
- package/dist/assets/rank-BUkksYCC.js +0 -1
- package/dist/assets/route-block-B_A1xBdJ.js +0 -1
- package/dist/assets/use-vmodel-Cl0DXIYt.js +0 -20
- package/dist/assets/zh-CN-DB4l8OJD.js +0 -1
- package/src/components/board/HeatMapTooltip.less +0 -115
- package/src/components/board/HeatMapTooltip.vue +0 -68
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Rank, SelectOptionItem } from "@xcpcio/core";
|
|
3
3
|
import type { Lang } from "@xcpcio/types";
|
|
4
|
+
import type { SegmentedControlOption } from "../common/SegmentedControl.vue";
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{
|
|
6
7
|
rank: Rank;
|
|
@@ -11,6 +12,15 @@ const lang = computed(() => locale.value as unknown as Lang);
|
|
|
11
12
|
|
|
12
13
|
const rank = computed(() => props.rank);
|
|
13
14
|
|
|
15
|
+
// Tab state - only show seat map tab if config exists
|
|
16
|
+
const hasSeatMap = computed(() => !!rank.value.contest.seatMap);
|
|
17
|
+
const currentTab = ref<"teams" | "seat-map">("teams");
|
|
18
|
+
|
|
19
|
+
const tabOptions = computed<SegmentedControlOption[]>(() => [
|
|
20
|
+
{ value: "teams", label: t("pending_page.teams_tab") },
|
|
21
|
+
{ value: "seat-map", label: t("pending_page.seat_map_tab") },
|
|
22
|
+
]);
|
|
23
|
+
|
|
14
24
|
const orgOptions = computed(() => {
|
|
15
25
|
return rank.value.organizations.map((o) => {
|
|
16
26
|
return {
|
|
@@ -99,72 +109,100 @@ const hasFilters = computed(() => {
|
|
|
99
109
|
|| rank.value.originTeams.length > 0
|
|
100
110
|
|| rank.value.contest.group.size > 0;
|
|
101
111
|
});
|
|
112
|
+
|
|
113
|
+
// Compute filtered team IDs for seat map highlighting
|
|
114
|
+
const filteredTeamIds = computed(() => {
|
|
115
|
+
return new Set(filteredTeams.value.map(t => t.id));
|
|
116
|
+
});
|
|
102
117
|
</script>
|
|
103
118
|
|
|
104
119
|
<template>
|
|
105
120
|
<div class="w-full">
|
|
106
121
|
<div
|
|
107
|
-
v-if="hasFilters"
|
|
122
|
+
v-if="hasFilters || hasSeatMap"
|
|
108
123
|
flex="~ wrap gap-3 items-center"
|
|
109
|
-
mb
|
|
124
|
+
mb-6
|
|
110
125
|
>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
<!-- Filters on the left -->
|
|
127
|
+
<div flex="~ wrap gap-3 items-center" flex-1>
|
|
128
|
+
<div
|
|
129
|
+
v-if="groupOptions.length > 0"
|
|
130
|
+
w-48
|
|
131
|
+
>
|
|
132
|
+
<TheMultiSelect
|
|
133
|
+
:options="groupOptions"
|
|
134
|
+
:selected-options="groupSelectedItems"
|
|
135
|
+
:placeholder="t('standings.group')"
|
|
136
|
+
@select="groupOnSelect"
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div
|
|
141
|
+
v-if="rank.contest.options.enableOrganization && orgOptions.length > 0"
|
|
142
|
+
w-80
|
|
143
|
+
>
|
|
144
|
+
<TheMultiSelect
|
|
145
|
+
:options="orgOptions"
|
|
146
|
+
:selected-options="orgSelectedItems"
|
|
147
|
+
:placeholder="t('standings.organization')"
|
|
148
|
+
@select="orgOnSelect"
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div
|
|
153
|
+
v-if="teamsOptions.length > 0"
|
|
154
|
+
w-80
|
|
155
|
+
>
|
|
156
|
+
<TheMultiSelect
|
|
157
|
+
:options="teamsOptions"
|
|
158
|
+
:selected-options="teamsSelectedItems"
|
|
159
|
+
:placeholder="t('standings.team')"
|
|
160
|
+
@select="teamsOnSelect"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
121
163
|
</div>
|
|
122
164
|
|
|
165
|
+
<!-- Tab switcher on the right side -->
|
|
166
|
+
<SegmentedControl
|
|
167
|
+
v-if="hasSeatMap"
|
|
168
|
+
v-model="currentTab"
|
|
169
|
+
:options="tabOptions"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- Teams Grid View -->
|
|
174
|
+
<template v-if="currentTab === 'teams'">
|
|
123
175
|
<div
|
|
124
|
-
v-if="
|
|
125
|
-
|
|
176
|
+
v-if="hasTeams"
|
|
177
|
+
class="grid gap-4"
|
|
178
|
+
grid="~ cols-1 sm:cols-2 lg:cols-3 xl:cols-4"
|
|
126
179
|
>
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
:
|
|
130
|
-
:
|
|
131
|
-
|
|
180
|
+
<TeamCard
|
|
181
|
+
v-for="team in filteredTeams"
|
|
182
|
+
:key="team.id"
|
|
183
|
+
:rank="rank"
|
|
184
|
+
:team="team"
|
|
132
185
|
/>
|
|
133
186
|
</div>
|
|
134
187
|
|
|
135
188
|
<div
|
|
136
|
-
v-
|
|
137
|
-
|
|
189
|
+
v-else
|
|
190
|
+
flex="~ col items-center justify-center"
|
|
191
|
+
py="12"
|
|
192
|
+
text="gray-500 dark:gray-400"
|
|
138
193
|
>
|
|
139
|
-
<
|
|
140
|
-
:options="teamsOptions"
|
|
141
|
-
:selected-options="teamsSelectedItems"
|
|
142
|
-
:placeholder="t('standings.team')"
|
|
143
|
-
@select="teamsOnSelect"
|
|
144
|
-
/>
|
|
194
|
+
<span text="lg">{{ t("pending_page.no_teams") }}</span>
|
|
145
195
|
</div>
|
|
146
|
-
</
|
|
196
|
+
</template>
|
|
147
197
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
grid="~ cols-1 sm:cols-2 lg:cols-3 xl:cols-4"
|
|
152
|
-
>
|
|
153
|
-
<TeamCard
|
|
154
|
-
v-for="team in filteredTeams"
|
|
155
|
-
:key="team.id"
|
|
198
|
+
<!-- Seat Map View -->
|
|
199
|
+
<template v-else-if="currentTab === 'seat-map' && hasSeatMap">
|
|
200
|
+
<SeatMapView
|
|
156
201
|
:rank="rank"
|
|
157
|
-
:
|
|
202
|
+
:seat-map="rank.contest.seatMap!"
|
|
203
|
+
:filtered-team-ids="filteredTeamIds"
|
|
204
|
+
:all-teams="rank.originTeams"
|
|
158
205
|
/>
|
|
159
|
-
</
|
|
160
|
-
|
|
161
|
-
<div
|
|
162
|
-
v-else
|
|
163
|
-
flex="~ col items-center justify-center"
|
|
164
|
-
py="12"
|
|
165
|
-
text="gray-500 dark:gray-400"
|
|
166
|
-
>
|
|
167
|
-
<span text="lg">{{ t("pending_page.no_teams") }}</span>
|
|
168
|
-
</div>
|
|
206
|
+
</template>
|
|
169
207
|
</div>
|
|
170
208
|
</template>
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Rank, SeatMap, SeatMapSection, Team } from "@xcpcio/core";
|
|
3
|
+
import type { Lang } from "@xcpcio/types";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
rank: Rank;
|
|
7
|
+
seatMap: SeatMap;
|
|
8
|
+
filteredTeamIds: Set<string>;
|
|
9
|
+
allTeams: Team[];
|
|
10
|
+
}>();
|
|
11
|
+
|
|
12
|
+
const { locale } = useI18n();
|
|
13
|
+
const lang = computed(() => locale.value as unknown as Lang);
|
|
14
|
+
|
|
15
|
+
// === Seat Sizing ===
|
|
16
|
+
const SEAT_HEIGHT = 24;
|
|
17
|
+
const GAP_SIZE = 4;
|
|
18
|
+
const FONT_SIZE = 10;
|
|
19
|
+
const SEAT_PADDING = 8;
|
|
20
|
+
|
|
21
|
+
// Calculate max seat ID length in a section
|
|
22
|
+
function getMaxSeatIdLength(section: SeatMapSection): number {
|
|
23
|
+
let maxLen = 2; // minimum 2 characters
|
|
24
|
+
for (const row of section.grid) {
|
|
25
|
+
for (const seatId of row) {
|
|
26
|
+
if (seatId && seatId.length > maxLen) {
|
|
27
|
+
maxLen = seatId.length;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return maxLen;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Calculate seat width based on max seat ID length
|
|
35
|
+
function getSeatWidth(section: SeatMapSection): number {
|
|
36
|
+
const maxLen = getMaxSeatIdLength(section);
|
|
37
|
+
const charWidth = FONT_SIZE * 0.6;
|
|
38
|
+
return Math.round(maxLen * charWidth + SEAT_PADDING);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Build seat ID to team mapping
|
|
42
|
+
const seatToTeam = computed(() => {
|
|
43
|
+
return props.seatMap.buildSeatToTeamMap(props.allTeams);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Get team for a given seat
|
|
47
|
+
function getTeamForSeat(seatId: string | null): Team | undefined {
|
|
48
|
+
if (!seatId) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return seatToTeam.value.get(seatId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get tooltip content for a seat
|
|
55
|
+
function getTooltipContent(seatId: string): string {
|
|
56
|
+
const team = getTeamForSeat(seatId);
|
|
57
|
+
if (!team) {
|
|
58
|
+
return seatId;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const name = team.name.getOrDefault(lang.value);
|
|
62
|
+
const org = team.organization?.name.getOrDefault(lang.value);
|
|
63
|
+
return org ? `${name} - ${org}` : name;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check if a seat's team should be highlighted
|
|
67
|
+
function isSeatHighlighted(seatId: string | null): boolean {
|
|
68
|
+
const team = getTeamForSeat(seatId);
|
|
69
|
+
if (!team) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return props.filteredTeamIds.size === 0 || props.filteredTeamIds.has(team.id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Get max columns in the grid
|
|
76
|
+
function getMaxCols(section: SeatMapSection): number {
|
|
77
|
+
if (section.grid.length === 0) {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
return Math.max(...section.grid.map(row => row.length));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Normalize row to have consistent column count
|
|
84
|
+
function getNormalizedRow(row: Array<string | null>, maxCols: number): Array<string | null> {
|
|
85
|
+
if (row.length >= maxCols) {
|
|
86
|
+
return row;
|
|
87
|
+
}
|
|
88
|
+
return [...row, ...Array.from({ length: maxCols - row.length }).fill(null) as Array<string | null>];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get grid style for a section
|
|
92
|
+
function getSectionGridStyle(section: SeatMapSection) {
|
|
93
|
+
const cols = getMaxCols(section);
|
|
94
|
+
const width = getSeatWidth(section);
|
|
95
|
+
return {
|
|
96
|
+
display: "grid",
|
|
97
|
+
gridTemplateColumns: `repeat(${cols}, ${width}px)`,
|
|
98
|
+
gap: `${GAP_SIZE}px`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getSeatStyle(section: SeatMapSection) {
|
|
103
|
+
return {
|
|
104
|
+
width: `${getSeatWidth(section)}px`,
|
|
105
|
+
height: `${SEAT_HEIGHT}px`,
|
|
106
|
+
fontSize: `${FONT_SIZE}px`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getRowLabelStyle() {
|
|
111
|
+
return {
|
|
112
|
+
height: `${SEAT_HEIGHT}px`,
|
|
113
|
+
marginBottom: `${GAP_SIZE}px`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<div class="seat-map-container" flex="~ col items-center">
|
|
120
|
+
<div
|
|
121
|
+
v-for="(section, sectionIdx) in seatMap.sections"
|
|
122
|
+
:key="sectionIdx"
|
|
123
|
+
class="seat-map-section"
|
|
124
|
+
mb-8
|
|
125
|
+
>
|
|
126
|
+
<h3
|
|
127
|
+
v-if="section.title.getOrDefault(lang)"
|
|
128
|
+
text="lg center gray-700 dark:gray-300"
|
|
129
|
+
font="semibold"
|
|
130
|
+
mb-4
|
|
131
|
+
>
|
|
132
|
+
{{ section.title.getOrDefault(lang) }}
|
|
133
|
+
</h3>
|
|
134
|
+
|
|
135
|
+
<div
|
|
136
|
+
class="seat-grid-wrapper dark:bg-gray-800/40 dark:border-gray-700/50"
|
|
137
|
+
flex="~ justify-center"
|
|
138
|
+
bg-white
|
|
139
|
+
rounded-xl
|
|
140
|
+
p-4
|
|
141
|
+
border
|
|
142
|
+
border-gray-200
|
|
143
|
+
>
|
|
144
|
+
<!-- Row labels -->
|
|
145
|
+
<div
|
|
146
|
+
v-if="section.rowLabels.length > 0"
|
|
147
|
+
class="row-labels"
|
|
148
|
+
flex="~ col"
|
|
149
|
+
mr-3
|
|
150
|
+
>
|
|
151
|
+
<div
|
|
152
|
+
v-for="(label, rowIdx) in section.rowLabels"
|
|
153
|
+
:key="rowIdx"
|
|
154
|
+
:style="getRowLabelStyle()"
|
|
155
|
+
flex="~ items-center justify-center"
|
|
156
|
+
text="xs gray-400 dark:gray-500"
|
|
157
|
+
font-medium
|
|
158
|
+
w-6
|
|
159
|
+
>
|
|
160
|
+
{{ label ?? "" }}
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<!-- Seat grid -->
|
|
165
|
+
<div :style="getSectionGridStyle(section)">
|
|
166
|
+
<template v-for="(row, rowIdx) in section.grid" :key="rowIdx">
|
|
167
|
+
<template v-for="(seatId, colIdx) in getNormalizedRow(row, getMaxCols(section))" :key="`${rowIdx}-${colIdx}`">
|
|
168
|
+
<Tooltip v-if="seatId" placement="bottom">
|
|
169
|
+
<div
|
|
170
|
+
class="seat"
|
|
171
|
+
:class="{
|
|
172
|
+
'seat-occupied': getTeamForSeat(seatId),
|
|
173
|
+
'seat-highlighted': isSeatHighlighted(seatId),
|
|
174
|
+
'seat-dimmed': filteredTeamIds.size > 0 && !isSeatHighlighted(seatId) && getTeamForSeat(seatId),
|
|
175
|
+
}"
|
|
176
|
+
:style="getSeatStyle(section)"
|
|
177
|
+
flex="~ items-center justify-center"
|
|
178
|
+
rounded="sm"
|
|
179
|
+
font="mono"
|
|
180
|
+
cursor-pointer
|
|
181
|
+
>
|
|
182
|
+
{{ seatId }}
|
|
183
|
+
</div>
|
|
184
|
+
<template #popper>
|
|
185
|
+
{{ getTooltipContent(seatId) }}
|
|
186
|
+
</template>
|
|
187
|
+
</Tooltip>
|
|
188
|
+
<div
|
|
189
|
+
v-else
|
|
190
|
+
:style="getSeatStyle(section)"
|
|
191
|
+
/>
|
|
192
|
+
</template>
|
|
193
|
+
</template>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</template>
|
|
199
|
+
|
|
200
|
+
<style scoped>
|
|
201
|
+
/* Empty seat - flat gray */
|
|
202
|
+
.seat {
|
|
203
|
+
background-color: #f3f4f6;
|
|
204
|
+
border: 1px solid #d1d5db;
|
|
205
|
+
transition: all 0.2s ease;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.seat:hover {
|
|
209
|
+
background-color: #e5e7eb;
|
|
210
|
+
border-color: #9ca3af;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Occupied seat - flat blue */
|
|
214
|
+
.seat-occupied {
|
|
215
|
+
background-color: #bfdbfe;
|
|
216
|
+
border-color: #60a5fa;
|
|
217
|
+
color: #1e40af;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.seat-occupied:hover {
|
|
221
|
+
background-color: #93c5fd;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Highlighted seat - deep blue + slight scale */
|
|
225
|
+
.seat-highlighted {
|
|
226
|
+
background-color: #3b82f6;
|
|
227
|
+
border-color: #2563eb;
|
|
228
|
+
color: white;
|
|
229
|
+
transform: scale(1.05);
|
|
230
|
+
z-index: 1;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Dimmed state */
|
|
234
|
+
.seat-dimmed {
|
|
235
|
+
opacity: 0.35;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* === Dark Mode === */
|
|
239
|
+
.dark .seat {
|
|
240
|
+
background-color: #374151;
|
|
241
|
+
border-color: #4b5563;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.dark .seat:hover {
|
|
245
|
+
background-color: #4b5563;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.dark .seat-occupied {
|
|
249
|
+
background-color: #1e3a5f;
|
|
250
|
+
border-color: #3b82f6;
|
|
251
|
+
color: #93c5fd;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.dark .seat-occupied:hover {
|
|
255
|
+
background-color: #1e4a7f;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* Dark mode highlight - blue glow effect */
|
|
259
|
+
.dark .seat-highlighted {
|
|
260
|
+
background-color: #3b82f6;
|
|
261
|
+
border-color: #60a5fa;
|
|
262
|
+
color: white;
|
|
263
|
+
box-shadow: 0 0 16px rgba(59, 130, 246, 0.5);
|
|
264
|
+
}
|
|
265
|
+
</style>
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
@correct-colors: #9be9a8, #40c463, #30a14e, #216e39;
|
|
2
2
|
@incorrect-colors: #ff8c8c, #ff4d4f, #ff1a1a, #d40d0d;
|
|
3
|
+
@correct-colors-dark: #216e39, #30a14e, #40c463, #9be9a8;
|
|
4
|
+
@incorrect-colors-dark: #d40d0d, #ff1a1a, #ff4d4f, #ff8c8c;
|
|
3
5
|
|
|
4
|
-
.generate-heat-rules(@class, @colors) {
|
|
6
|
+
.generate-heat-rules(@class, @colors, @dark-colors) {
|
|
5
7
|
each(@colors, {
|
|
6
8
|
.@{class}[data-level="@{index}"] {
|
|
7
9
|
background-color: @value;
|
|
8
10
|
}
|
|
9
11
|
});
|
|
12
|
+
|
|
13
|
+
each(@dark-colors, {
|
|
14
|
+
.dark .@{class}[data-level="@{index}"] {
|
|
15
|
+
background-color: @value;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
10
18
|
}
|
|
11
19
|
|
|
12
|
-
.generate-heat-rules(correct-heat, @correct-colors);
|
|
13
|
-
.generate-heat-rules(incorrect-heat, @incorrect-colors);
|
|
20
|
+
.generate-heat-rules(correct-heat, @correct-colors, @correct-colors-dark);
|
|
21
|
+
.generate-heat-rules(incorrect-heat, @incorrect-colors, @incorrect-colors-dark);
|