todo listo y presentable. preparandonos para entregar fase 1
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
<h2>📥 Export / Tools</h2>
|
<h2>📥 Export / Tools</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button class="btn btn-export" @click="downloadCsv">Descargar resultados (CSV)</button>
|
<button class="btn btn-export" @click="downloadCsvByRoom">Descargar por sala (CSV)</button>
|
||||||
|
<button class="btn btn-export alt" @click="downloadCsvByUuid">Descargar por UUID (CSV)</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -41,19 +42,19 @@ function csvEscape(v: any): string {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCsv(): string {
|
function buildCsvByRoom(): string {
|
||||||
const headers = [
|
const headers = [
|
||||||
'roomId', 'variant', 'round', 'status',
|
'roomId', 'variant', 'round', 'status',
|
||||||
'p1_name', 'p1_pavo', 'p1_elote',
|
'p1_uuid', 'p1_name', 'p1_pavo', 'p1_elote', 'p1_shame',
|
||||||
'p2_name', 'p2_pavo', 'p2_elote'
|
'p2_uuid', 'p2_name', 'p2_pavo', 'p2_elote', 'p2_shame'
|
||||||
];
|
];
|
||||||
const lines: string[] = [headers.join(',')];
|
const lines: string[] = [headers.join(',')];
|
||||||
|
|
||||||
gameRooms.value.forEach(room => {
|
gameRooms.value.forEach(room => {
|
||||||
const det = props.roomDetails[room.roomId] || {};
|
const det = (props.roomDetails[room.roomId] || {}) as any;
|
||||||
const status = (det as any).gameStatus || room?.metadata?.gameStatus || 'waiting';
|
const status = det.gameStatus || room?.metadata?.gameStatus || 'waiting';
|
||||||
const variant = (det as any).variant || room?.metadata?.currentVariant || 'G1';
|
const variant = det.variant || room?.metadata?.currentVariant || 'G1';
|
||||||
const round = (det as any).round || room?.metadata?.currentRound || 1;
|
const round = det.round || room?.metadata?.currentRound || 1;
|
||||||
const players = (det.players || []) as PlayerRow[];
|
const players = (det.players || []) as PlayerRow[];
|
||||||
const p1 = players.find(p => p.role === 'P1') || players[0] || ({} as any);
|
const p1 = players.find(p => p.role === 'P1') || players[0] || ({} as any);
|
||||||
const p2 = players.find(p => p.role === 'P2') || players[1] || ({} as any);
|
const p2 = players.find(p => p.role === 'P2') || players[1] || ({} as any);
|
||||||
@@ -63,12 +64,16 @@ function buildCsv(): string {
|
|||||||
variant,
|
variant,
|
||||||
round,
|
round,
|
||||||
status,
|
status,
|
||||||
|
p1?.uuid || '',
|
||||||
p1?.name || '',
|
p1?.name || '',
|
||||||
p1?.pavoTokens ?? 0,
|
p1?.pavoTokens ?? 0,
|
||||||
p1?.eloteTokens ?? 0,
|
p1?.eloteTokens ?? 0,
|
||||||
|
p1?.shameTokens ?? 0,
|
||||||
|
p2?.uuid || '',
|
||||||
p2?.name || '',
|
p2?.name || '',
|
||||||
p2?.pavoTokens ?? 0,
|
p2?.pavoTokens ?? 0,
|
||||||
p2?.eloteTokens ?? 0
|
p2?.eloteTokens ?? 0,
|
||||||
|
p2?.shameTokens ?? 0
|
||||||
].map(csvEscape).join(',');
|
].map(csvEscape).join(',');
|
||||||
lines.push(row);
|
lines.push(row);
|
||||||
});
|
});
|
||||||
@@ -76,13 +81,45 @@ function buildCsv(): string {
|
|||||||
return lines.join('\n') + '\n';
|
return lines.join('\n') + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadCsv() {
|
function buildCsvByUuid(): string {
|
||||||
const csv = buildCsv();
|
const headers = [
|
||||||
|
'roomId', 'variant', 'round', 'status',
|
||||||
|
'uuid', 'name', 'role', 'pavo', 'elote', 'shame'
|
||||||
|
];
|
||||||
|
const lines: string[] = [headers.join(',')];
|
||||||
|
|
||||||
|
gameRooms.value.forEach(room => {
|
||||||
|
const det = (props.roomDetails[room.roomId] || {}) as any;
|
||||||
|
const status = det.gameStatus || room?.metadata?.gameStatus || 'waiting';
|
||||||
|
const variant = det.variant || room?.metadata?.currentVariant || 'G1';
|
||||||
|
const round = det.round || room?.metadata?.currentRound || 1;
|
||||||
|
const players = (det.players || []) as PlayerRow[];
|
||||||
|
players.forEach(p => {
|
||||||
|
const row = [
|
||||||
|
room.roomId,
|
||||||
|
variant,
|
||||||
|
round,
|
||||||
|
status,
|
||||||
|
p?.uuid || '',
|
||||||
|
p?.name || '',
|
||||||
|
p?.role || '',
|
||||||
|
p?.pavoTokens ?? 0,
|
||||||
|
p?.eloteTokens ?? 0,
|
||||||
|
p?.shameTokens ?? 0
|
||||||
|
].map(csvEscape).join(',');
|
||||||
|
lines.push(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return lines.join('\n') + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerDownload(csv: string, suffix: string) {
|
||||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const pad = (n: number) => String(n).padStart(2, '0');
|
const pad = (n: number) => String(n).padStart(2, '0');
|
||||||
const fname = `snatch-results-${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.csv`;
|
const fname = `snatch-results-${suffix}-${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.csv`;
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = fname;
|
a.download = fname;
|
||||||
@@ -91,6 +128,9 @@ function downloadCsv() {
|
|||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadCsvByRoom() { triggerDownload(buildCsvByRoom(), 'by-room'); }
|
||||||
|
function downloadCsvByUuid() { triggerDownload(buildCsvByUuid(), 'by-uuid'); }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -107,6 +147,6 @@ function downloadCsv() {
|
|||||||
.buttons { display:flex; gap: 10px; flex-wrap: wrap; }
|
.buttons { display:flex; gap: 10px; flex-wrap: wrap; }
|
||||||
.btn { padding: 10px 14px; border:none; border-radius: 10px; font-weight:700; cursor:pointer; }
|
.btn { padding: 10px 14px; border:none; border-radius: 10px; font-weight:700; cursor:pointer; }
|
||||||
.btn-export { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color:white; }
|
.btn-export { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color:white; }
|
||||||
|
.btn-export.alt { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
|
||||||
.btn-export:hover { filter: brightness(1.05); }
|
.btn-export:hover { filter: brightness(1.05); }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rooms-section">
|
<div class="rooms-section">
|
||||||
|
<DashboardActions :rooms="rooms" :room-details="roomDetails" />
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>Active Game Rooms</h2>
|
<h2>Active Game Rooms</h2>
|
||||||
<div class="view-controls">
|
<div class="view-controls">
|
||||||
@@ -235,6 +236,7 @@ import { colyseusService } from '../services/colyseus';
|
|||||||
import RoomCard from '../components/RoomCard.vue';
|
import RoomCard from '../components/RoomCard.vue';
|
||||||
import RoomsTable from '../components/RoomsTable.vue';
|
import RoomsTable from '../components/RoomsTable.vue';
|
||||||
import RoomModal from '../components/RoomModal.vue';
|
import RoomModal from '../components/RoomModal.vue';
|
||||||
|
import DashboardActions from '../components/DashboardActions.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const rooms = ref<any[]>([]);
|
const rooms = ref<any[]>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user