|
|
<!DOCTYPE html> |
|
|
<html lang="pt-br"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Gerenciador de Sessão de Poker</title> |
|
|
|
|
|
<script src="https://unpkg.com/lucide@latest"></script> |
|
|
<style> |
|
|
:root { |
|
|
--primary: #3b82f6; |
|
|
--primary-hover: #2563eb; |
|
|
--danger: #ef4444; |
|
|
--danger-hover: #dc2626; |
|
|
--success: #22c55e; |
|
|
--success-hover: #16a34a; |
|
|
--warning: #f59e0b; |
|
|
--warning-hover: #d97706; |
|
|
--dark: #1e293b; |
|
|
--light: #f8fafc; |
|
|
--gray: #64748b; |
|
|
--gray-light: #e2e8f0; |
|
|
--red-dark: #991b1b; |
|
|
--red-light: #fef2f2; |
|
|
--green-dark: #166534; |
|
|
--green-light: #f0fdf4; |
|
|
--border-radius: 0.5rem; |
|
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); |
|
|
--transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
* { |
|
|
box-sizing: border-box; |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
|
} |
|
|
|
|
|
body { |
|
|
background-color: #f1f5f9; |
|
|
color: var(--dark); |
|
|
line-height: 1.6; |
|
|
padding: 1rem; |
|
|
} |
|
|
|
|
|
.container { |
|
|
max-width: 1200px; |
|
|
margin: 0 auto; |
|
|
padding: 0 1rem; |
|
|
} |
|
|
|
|
|
|
|
|
.header { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 1rem; |
|
|
margin-bottom: 1.5rem; |
|
|
} |
|
|
|
|
|
.header h1 { |
|
|
font-size: 1.75rem; |
|
|
font-weight: 700; |
|
|
color: var(--dark); |
|
|
} |
|
|
|
|
|
.header p { |
|
|
color: var(--gray); |
|
|
font-size: 0.875rem; |
|
|
} |
|
|
|
|
|
|
|
|
.btn { |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
padding: 0.5rem 1rem; |
|
|
border-radius: var(--border-radius); |
|
|
font-weight: 500; |
|
|
font-size: 0.875rem; |
|
|
cursor: pointer; |
|
|
transition: var(--transition); |
|
|
border: 1px solid transparent; |
|
|
} |
|
|
|
|
|
.btn-primary { |
|
|
background-color: var(--primary); |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn-primary:hover { |
|
|
background-color: var(--primary-hover); |
|
|
} |
|
|
|
|
|
.btn-success { |
|
|
background-color: var(--success); |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn-success:hover { |
|
|
background-color: var(--success-hover); |
|
|
} |
|
|
|
|
|
.btn-danger { |
|
|
background-color: var(--danger); |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn-danger:hover { |
|
|
background-color: var(--danger-hover); |
|
|
} |
|
|
|
|
|
.btn-warning { |
|
|
background-color: var(--warning); |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn-warning:hover { |
|
|
background-color: var(--warning-hover); |
|
|
} |
|
|
|
|
|
.btn-outline { |
|
|
background-color: transparent; |
|
|
border-color: var(--gray-light); |
|
|
color: var(--dark); |
|
|
} |
|
|
|
|
|
.btn-outline:hover { |
|
|
background-color: var(--gray-light); |
|
|
} |
|
|
|
|
|
.btn-icon { |
|
|
margin-right: 0.5rem; |
|
|
} |
|
|
|
|
|
|
|
|
.quick-actions { |
|
|
display: flex; |
|
|
gap: 0.5rem; |
|
|
margin-bottom: 1.5rem; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
|
|
|
|
|
|
.grid { |
|
|
display: grid; |
|
|
gap: 1rem; |
|
|
} |
|
|
|
|
|
.grid-cols-1 { |
|
|
grid-template-columns: repeat(1, 1fr); |
|
|
} |
|
|
|
|
|
.grid-cols-2 { |
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
} |
|
|
|
|
|
.grid-cols-3 { |
|
|
grid-template-columns: repeat(3, 1fr); |
|
|
} |
|
|
|
|
|
|
|
|
.player-card { |
|
|
background-color: white; |
|
|
border-radius: var(--border-radius); |
|
|
box-shadow: var(--shadow); |
|
|
overflow: hidden; |
|
|
transition: var(--transition); |
|
|
} |
|
|
|
|
|
.player-card:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
|
|
|
.player-header { |
|
|
background-color: var(--primary); |
|
|
color: white; |
|
|
padding: 1rem; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.player-name { |
|
|
font-weight: 600; |
|
|
font-size: 1.125rem; |
|
|
} |
|
|
|
|
|
.player-balance { |
|
|
font-size: 1.5rem; |
|
|
font-weight: 700; |
|
|
margin: 0.5rem 0; |
|
|
} |
|
|
|
|
|
.player-content { |
|
|
padding: 1rem; |
|
|
display: grid; |
|
|
grid-template-columns: repeat(3, 1fr); |
|
|
gap: 0.5rem; |
|
|
} |
|
|
|
|
|
.player-stat { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.stat-label { |
|
|
font-size: 0.75rem; |
|
|
color: var(--gray); |
|
|
margin-bottom: 0.25rem; |
|
|
} |
|
|
|
|
|
.stat-value { |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.player-actions { |
|
|
display: flex; |
|
|
gap: 0.5rem; |
|
|
padding: 0 1rem 1rem; |
|
|
} |
|
|
|
|
|
.player-actions .btn { |
|
|
flex: 1; |
|
|
font-size: 0.75rem; |
|
|
padding: 0.25rem 0.5rem; |
|
|
} |
|
|
|
|
|
|
|
|
.summary-card { |
|
|
background-color: white; |
|
|
border-radius: var(--border-radius); |
|
|
box-shadow: var(--shadow); |
|
|
padding: 1rem; |
|
|
margin-top: 1.5rem; |
|
|
} |
|
|
|
|
|
.summary-header { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.summary-title { |
|
|
font-weight: 600; |
|
|
font-size: 1.125rem; |
|
|
} |
|
|
|
|
|
.summary-grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
gap: 1rem; |
|
|
} |
|
|
|
|
|
.summary-item { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
padding: 0.5rem 0; |
|
|
border-bottom: 1px solid var(--gray-light); |
|
|
} |
|
|
|
|
|
.summary-item-label { |
|
|
color: var(--gray); |
|
|
} |
|
|
|
|
|
.summary-item-value { |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.summary-total { |
|
|
font-size: 1.25rem; |
|
|
font-weight: 700; |
|
|
color: var(--primary); |
|
|
margin-top: 1rem; |
|
|
padding-top: 1rem; |
|
|
border-top: 1px solid var(--gray-light); |
|
|
} |
|
|
|
|
|
|
|
|
.loading { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
height: 100vh; |
|
|
} |
|
|
|
|
|
.spinner { |
|
|
width: 3rem; |
|
|
height: 3rem; |
|
|
border: 3px solid transparent; |
|
|
border-top-color: var(--primary); |
|
|
border-radius: 50%; |
|
|
animation: spin 1s linear infinite; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
@keyframes spin { |
|
|
to { transform: rotate(360deg); } |
|
|
} |
|
|
|
|
|
|
|
|
.error-card { |
|
|
background-color: var(--red-light); |
|
|
border: 1px solid var(--danger); |
|
|
border-radius: var(--border-radius); |
|
|
padding: 2rem; |
|
|
text-align: center; |
|
|
max-width: 600px; |
|
|
margin: 2rem auto; |
|
|
} |
|
|
|
|
|
.error-icon { |
|
|
color: var(--danger); |
|
|
width: 3rem; |
|
|
height: 3rem; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.error-title { |
|
|
font-size: 1.25rem; |
|
|
font-weight: 600; |
|
|
margin-bottom: 1rem; |
|
|
color: var(--danger); |
|
|
} |
|
|
|
|
|
.error-message { |
|
|
margin-bottom: 1.5rem; |
|
|
color: var(--red-dark); |
|
|
} |
|
|
|
|
|
.error-actions { |
|
|
display: flex; |
|
|
gap: 1rem; |
|
|
justify-content: center; |
|
|
} |
|
|
|
|
|
|
|
|
.modal-overlay { |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
right: 0; |
|
|
bottom: 0; |
|
|
background-color: rgba(0, 0, 0, 0.5); |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
z-index: 1000; |
|
|
} |
|
|
|
|
|
.modal { |
|
|
background-color: white; |
|
|
border-radius: var(--border-radius); |
|
|
width: 90%; |
|
|
max-width: 500px; |
|
|
max-height: 90vh; |
|
|
overflow-y: auto; |
|
|
padding: 1.5rem; |
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); |
|
|
} |
|
|
|
|
|
.modal-header { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
margin-bottom: 1.5rem; |
|
|
} |
|
|
|
|
|
.modal-title { |
|
|
font-size: 1.25rem; |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.modal-close { |
|
|
background: none; |
|
|
border: none; |
|
|
font-size: 1.5rem; |
|
|
cursor: pointer; |
|
|
color: var(--gray); |
|
|
} |
|
|
|
|
|
.form-group { |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.form-label { |
|
|
display: block; |
|
|
margin-bottom: 0.5rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
.form-input { |
|
|
width: 100%; |
|
|
padding: 0.5rem; |
|
|
border: 1px solid var(--gray-light); |
|
|
border-radius: var(--border-radius); |
|
|
font-size: 1rem; |
|
|
} |
|
|
|
|
|
.form-select { |
|
|
width: 100%; |
|
|
padding: 0.5rem; |
|
|
border: 1px solid var(--gray-light); |
|
|
border-radius: var(--border-radius); |
|
|
font-size: 1rem; |
|
|
background-color: white; |
|
|
} |
|
|
|
|
|
.modal-footer { |
|
|
display: flex; |
|
|
justify-content: flex-end; |
|
|
gap: 0.5rem; |
|
|
margin-top: 1.5rem; |
|
|
} |
|
|
|
|
|
|
|
|
.flex { |
|
|
display: flex; |
|
|
} |
|
|
|
|
|
.flex-col { |
|
|
flex-direction: column; |
|
|
} |
|
|
|
|
|
.items-center { |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.justify-center { |
|
|
justify-content: center; |
|
|
} |
|
|
|
|
|
.justify-between { |
|
|
justify-content: space-between; |
|
|
} |
|
|
|
|
|
.gap-1 { |
|
|
gap: 0.25rem; |
|
|
} |
|
|
|
|
|
.gap-2 { |
|
|
gap: 0.5rem; |
|
|
} |
|
|
|
|
|
.gap-4 { |
|
|
gap: 1rem; |
|
|
} |
|
|
|
|
|
.mt-1 { |
|
|
margin-top: 0.25rem; |
|
|
} |
|
|
|
|
|
.mt-2 { |
|
|
margin-top: 0.5rem; |
|
|
} |
|
|
|
|
|
.mt-4 { |
|
|
margin-top: 1rem; |
|
|
} |
|
|
|
|
|
.mb-1 { |
|
|
margin-bottom: 0.25rem; |
|
|
} |
|
|
|
|
|
.mb-2 { |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
|
|
|
.mb-4 { |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.p-4 { |
|
|
padding: 1rem; |
|
|
} |
|
|
|
|
|
.text-center { |
|
|
text-align: center; |
|
|
} |
|
|
|
|
|
|
|
|
@media (min-width: 640px) { |
|
|
.header { |
|
|
flex-direction: row; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.quick-actions { |
|
|
flex-wrap: nowrap; |
|
|
} |
|
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
|
.grid-cols-2, |
|
|
.grid-cols-3 { |
|
|
grid-template-columns: repeat(1, 1fr); |
|
|
} |
|
|
|
|
|
.summary-grid { |
|
|
grid-template-columns: repeat(1, 1fr); |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container" id="app"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', async () => { |
|
|
// Initialize Lucide icons |
|
|
lucide.createIcons(); |
|
|
|
|
|
// Simulate loading initial data |
|
|
const app = document.getElementById('app'); |
|
|
|
|
|
// Show loading state |
|
|
app.innerHTML = ` |
|
|
<div class="loading"> |
|
|
<div class="spinner"></div> |
|
|
<p class="text-gray-400">Carregando sessão...</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
// Simulate network request delay |
|
|
await new Promise(resolve => setTimeout(resolve, 1500)); |
|
|
|
|
|
// Simulated data |
|
|
const currentSession = { |
|
|
id: 'session-123', |
|
|
name: 'Sessão VIP - Torneio Mensal', |
|
|
startTime: new Date().toISOString(), |
|
|
isActive: true |
|
|
}; |
|
|
|
|
|
const players = [ |
|
|
{ id: 'player-1', name: 'João Silva', buyins: 500, cashouts: 200, payments: 50, pendingAmount: 250 }, |
|
|
{ id: 'player-2', name: 'Maria Oliveira', buyins: 300, cashouts: 150, payments: 100, pendingAmount: 50 }, |
|
|
{ id: 'player-3', name: 'Carlos Mendes', buyins: 1000, cashouts: 500, payments: 200, pendingAmount: 300 }, |
|
|
{ id: 'player-4', name: 'Ana Souza', buyins: 700, cashouts: 300, payments: 150, pendingAmount: 250 }, |
|
|
{ id: 'player-5', name: 'Pedro Rocha', buyins: 200, cashouts: 100, payments: 75, pendingAmount: 25 } |
|
|
]; |
|
|
|
|
|
const sessionSummary = { |
|
|
chipsInPlay: 1500, |
|
|
totalBuyins: 2700, |
|
|
totalCashouts: 1250, |
|
|
totalPayments: 575, |
|
|
cashReceived: 1200, |
|
|
cashPaid: 800, |
|
|
pixReceived: 500, |
|
|
pixPaid: 200, |
|
|
creditReceived: 300, |
|
|
creditPaid: 150, |
|
|
debitReceived: 200, |
|
|
debitPaid: 100, |
|
|
pendingToReceive: 500, |
|
|
pendingToPay: 250 |
|
|
}; |
|
|
|
|
|
const chipSets = [ |
|
|
{ id: 'chip-1', value: 1, quantity: 50 }, |
|
|
{ id: 'chip-2', value: 5, quantity: 40 }, |
|
|
{ id: 'chip-3', value: 10, quantity: 30 }, |
|
|
{ id: 'chip-4', value: 25, quantity: 20 }, |
|
|
{ id: 'chip-5', value: 50, quantity: 10 }, |
|
|
{ id: 'chip-6', value: 100, quantity: 5 } |
|
|
]; |
|
|
|
|
|
const chipSetTotal = chipSets.reduce((total, chip) => total + (chip.value * chip.quantity), 0); |
|
|
|
|
|
// State management |
|
|
let state = { |
|
|
currentSession, |
|
|
players, |
|
|
sessionSummary, |
|
|
chipSets, |
|
|
chipSetTotal, |
|
|
hidePlayersValues: false, |
|
|
hideSummaryValues: false, |
|
|
selectedPlayerId: null, |
|
|
modals: { |
|
|
addPlayer: false, |
|
|
buyChips: false, |
|
|
cashout: false, |
|
|
payment: false, |
|
|
security: false, |
|
|
log: false |
|
|
}, |
|
|
securityMode: 'players' |
|
|
}; |
|
|
|
|
|
// Helper functions |
|
|
function formatDate(dateString) { |
|
|
const date = new Date(dateString); |
|
|
return date.toLocaleDateString('pt-BR', { |
|
|
day: '2-digit', |
|
|
month: '2-digit', |
|
|
year: 'numeric', |
|
|
hour: '2-digit', |
|
|
minute: '2-digit' |
|
|
}); |
|
|
} |
|
|
|
|
|
function formatCurrency(amount) { |
|
|
return new Intl.NumberFormat('pt-BR', { |
|
|
style: 'currency', |
|
|
currency: 'BRL' |
|
|
}).format(amount); |
|
|
} |
|
|
|
|
|
// Toggle modal visibility |
|
|
function toggleModal(modalName, isOpen) { |
|
|
state.modals[modalName] = isOpen; |
|
|
render(); |
|
|
} |
|
|
|
|
|
function setSecurityMode(mode) { |
|
|
state.securityMode = mode; |
|
|
toggleModal('security', true); |
|
|
} |
|
|
|
|
|
function toggleHideValues(value) { |
|
|
if (state.securityMode === 'players') { |
|
|
state.hidePlayersValues = value; |
|
|
localStorage.setItem('hidePlayersValues', JSON.stringify(value)); |
|
|
} else { |
|
|
state.hideSummaryValues = value; |
|
|
localStorage.setItem('hideSummaryValues', JSON.stringify(value)); |
|
|
} |
|
|
toggleModal('security', false); |
|
|
} |
|
|
|
|
|
// Event handlers |
|
|
function handleAddPlayer(name) { |
|
|
const newPlayer = { |
|
|
id: `player-${Date.now()}`, |
|
|
name, |
|
|
buyins: 0, |
|
|
cashouts: 0, |
|
|
payments: 0, |
|
|
pendingAmount: 0 |
|
|
}; |
|
|
state.players = [...state.players, newPlayer]; |
|
|
toggleModal('addPlayer', false); |
|
|
render(); |
|
|
} |
|
|
|
|
|
function handleBuyChips({ playerId, amount, payNow, paymentMethod }) { |
|
|
const playerIndex = state.players.findIndex(p => p.id === playerId); |
|
|
if (playerIndex !== -1) { |
|
|
const updatedPlayer = { |
|
|
...state.players[playerIndex], |
|
|
buyins: state.players[playerIndex].buyins + amount, |
|
|
pendingAmount: payNow ? |
|
|
state.players[playerIndex].pendingAmount : |
|
|
state.players[playerIndex].pendingAmount + amount |
|
|
}; |
|
|
|
|
|
const updatedPlayers = [...state.players]; |
|
|
updatedPlayers[playerIndex] = updatedPlayer; |
|
|
state.players = updatedPlayers; |
|
|
|
|
|
// Update session summary |
|
|
state.sessionSummary = { |
|
|
...state.sessionSummary, |
|
|
totalBuyins: state.sessionSummary.totalBuyins + amount, |
|
|
chipsInPlay: state.sessionSummary.chipsInPlay + amount, |
|
|
...(payNow && { |
|
|
cashReceived: paymentMethod === 'cash' ? |
|
|
state.sessionSummary.cashReceived + amount : |
|
|
state.sessionSummary.cashReceived, |
|
|
pixReceived: paymentMethod === 'pix' ? |
|
|
state.sessionSummary.pixReceived + amount : |
|
|
state.sessionSummary.pixReceived, |
|
|
creditReceived: paymentMethod === 'credit' ? |
|
|
state.sessionSummary.creditReceived + amount : |
|
|
state.sessionSummary.creditReceived, |
|
|
debitReceived: paymentMethod === 'debit' ? |
|
|
state.sessionSummary.debitReceived + amount : |
|
|
state.sessionSummary.debitReceived |
|
|
}), |
|
|
...(!payNow && { |
|
|
pendingToReceive: state.sessionSummary.pendingToReceive + amount |
|
|
}) |
|
|
}; |
|
|
} |
|
|
toggleModal('buyChips', false); |
|
|
render(); |
|
|
} |
|
|
|
|
|
function handleCashout({ playerId, amount, payNow, paymentMethod }) { |
|
|
const playerIndex = state.players.findIndex(p => p.id === playerId); |
|
|
if (playerIndex !== -1) { |
|
|
const updatedPlayer = { |
|
|
...state.players[playerIndex], |
|
|
cashouts: state.players[playerIndex].cashouts + amount, |
|
|
pendingAmount: payNow ? |
|
|
state.players[playerIndex].pendingAmount - amount : |
|
|
state.players[playerIndex].pendingAmount |
|
|
}; |
|
|
|
|
|
const updatedPlayers = [...state.players]; |
|
|
updatedPlayers[playerIndex] = updatedPlayer; |
|
|
state.players = updatedPlayers; |
|
|
|
|
|
// Update session summary |
|
|
state.sessionSummary = { |
|
|
...state.sessionSummary, |
|
|
totalCashouts: state.sessionSummary.totalCashouts + amount, |
|
|
chipsInPlay: state.sessionSummary.chipsInPlay - amount, |
|
|
...(payNow && { |
|
|
cashPaid: paymentMethod === 'cash' ? |
|
|
state.sessionSummary.cashPaid + amount : |
|
|
state.sessionSummary.cashPaid, |
|
|
pixPaid: paymentMethod === 'pix' ? |
|
|
state.sessionSummary.pixPaid + amount : |
|
|
state.sessionSummary.pixPaid, |
|
|
creditPaid: paymentMethod === 'credit' ? |
|
|
state.sessionSummary.creditPaid + amount : |
|
|
state.sessionSummary.creditPaid, |
|
|
debitPaid: paymentMethod === 'debit' ? |
|
|
state.sessionSummary.debitPaid + amount : |
|
|
state.sessionSummary.debitPaid |
|
|
}), |
|
|
...(!payNow && { |
|
|
pendingToPay: state.sessionSummary.pendingToPay + amount |
|
|
}) |
|
|
}; |
|
|
} |
|
|
toggleModal('cashout', false); |
|
|
render(); |
|
|
} |
|
|
|
|
|
function handlePayment({ playerId, amount, paymentMethod, direction }) { |
|
|
const playerIndex = state.players.findIndex(p => p.id === playerId); |
|
|
if (playerIndex !== -1) { |
|
|
const paymentAmount = direction === 'in' ? amount : -amount; |
|
|
const updatedPlayer = { |
|
|
...state.players[playerIndex], |
|
|
payments: state.players[playerIndex].payments + paymentAmount, |
|
|
pendingAmount: direction === 'in' ? |
|
|
Math.max(0, state.players[playerIndex].pendingAmount - amount) : |
|
|
Math.min(0, state.players[playerIndex].pendingAmount - amount) |
|
|
}; |
|
|
|
|
|
const updatedPlayers = [...state.players]; |
|
|
updatedPlayers[playerIndex] = updatedPlayer; |
|
|
state.players = updatedPlayers; |
|
|
|
|
|
// Update session summary |
|
|
state.sessionSummary = { |
|
|
...state.sessionSummary, |
|
|
totalPayments: state.sessionSummary.totalPayments + amount, |
|
|
...(direction === 'in' && { |
|
|
cashReceived: paymentMethod === 'cash' ? |
|
|
state.sessionSummary.cashReceived + amount : |
|
|
state.sessionSummary.cashReceived, |
|
|
pixReceived: paymentMethod === 'pix' ? |
|
|
state.sessionSummary.pixReceived + amount : |
|
|
state.sessionSummary.pixReceived, |
|
|
creditReceived: paymentMethod === 'credit' ? |
|
|
state.sessionSummary.creditReceived + amount : |
|
|
state.sessionSummary.creditReceived, |
|
|
debitReceived: paymentMethod === 'debit' ? |
|
|
state.sessionSummary.debitReceived + amount : |
|
|
state.sessionSummary.debitReceived, |
|
|
pendingToReceive: Math.max(0, state.sessionSummary.pendingToReceive - amount) |
|
|
}), |
|
|
...(direction === 'out' && { |
|
|
cashPaid: paymentMethod === 'cash' ? |
|
|
state.sessionSummary.cashPaid + amount : |
|
|
state.sessionSummary.cashPaid, |
|
|
pixPaid: paymentMethod === 'pix' ? |
|
|
state.sessionSummary.pixPaid + amount : |
|
|
state.sessionSummary.pixPaid, |
|
|
creditPaid: paymentMethod === 'credit' ? |
|
|
state.sessionSummary.creditPaid + amount : |
|
|
state.sessionSummary.creditPaid, |
|
|
debitPaid: paymentMethod === 'debit' ? |
|
|
state.sessionSummary.debitPaid + amount : |
|
|
state.sessionSummary.debitPaid, |
|
|
pendingToPay: Math.max(0, state.sessionSummary.pendingToPay - amount) |
|
|
}) |
|
|
}; |
|
|
} |
|
|
toggleModal('payment', false); |
|
|
render(); |
|
|
} |
|
|
|
|
|
function updateChipSets(updatedChipSets) { |
|
|
state.chipSets = updatedChipSets; |
|
|
state.chipSetTotal = updatedChipSets.reduce((total, chip) => total + (chip.value * chip.quantity), 0); |
|
|
render(); |
|
|
} |
|
|
|
|
|
// Component rendering functions |
|
|
function renderPlayerCard(player) { |
|
|
const balance = player.buyins - player.cashouts + player.payments; |
|
|
return ` |
|
|
<div class="player-card"> |
|
|
<div class="player-header"> |
|
|
<div class="player-name">${player.name}</div> |
|
|
<div class="player-balance"> |
|
|
${state.hidePlayersValues ? '••••' : formatCurrency(balance)} |
|
|
</div> |
|
|
</div> |
|
|
<div class="player-content"> |
|
|
<div class="player-stat"> |
|
|
<span class="stat-label">Buy-ins</span> |
|
|
<span class="stat-value"> |
|
|
${state.hidePlayersValues ? '•••' : formatCurrency(player.buyins)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="player-stat"> |
|
|
<span class="stat-label">Cashouts</span> |
|
|
<span class="stat-value"> |
|
|
${state.hidePlayersValues ? '•••' : formatCurrency(player.cashouts)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="player-stat"> |
|
|
<span class="stat-label">Saldo Pendente</span> |
|
|
<span class="stat-value"> |
|
|
${state.hidePlayersValues ? '•••' : formatCurrency(player.pendingAmount)} |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="player-actions"> |
|
|
<button class="btn btn-primary" |
|
|
onclick="state.selectedPlayerId='${player.id}'; toggleModal('buyChips', true)"> |
|
|
<i data-lucide="plus-circle" class="btn-icon"></i> |
|
|
Comprar Fichas |
|
|
</button> |
|
|
<button class="btn btn-success" |
|
|
onclick="state.selectedPlayerId='${player.id}'; toggleModal('cashout', true)"> |
|
|
<i data-lucide="dollar-sign" class="btn-icon"></i> |
|
|
Cashout |
|
|
</button> |
|
|
<button class="btn btn-warning" |
|
|
onclick="state.selectedPlayerId='${player.id}'; toggleModal('payment', true)"> |
|
|
<i data-lucide="credit-card" class="btn-icon"></i> |
|
|
Pagamento |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderQuickActions() { |
|
|
return ` |
|
|
<div class="quick-actions"> |
|
|
<button class="btn btn-primary" onclick="toggleModal('addPlayer', true)"> |
|
|
<i data-lucide="user-plus" class="btn-icon"></i> |
|
|
Adicionar Jogador |
|
|
</button> |
|
|
<button class="btn btn-success" onclick="state.selectedPlayerId=null; toggleModal('buyChips', true)"> |
|
|
<i data-lucide="plus-circle" class="btn-icon"></i> |
|
|
Comprar Fichas |
|
|
</button> |
|
|
<button class="btn btn-warning" onclick="state.selectedPlayerId=null; toggleModal('cashout', true)"> |
|
|
<i data-lucide="dollar-sign" class="btn-icon"></i> |
|
|
Cashout |
|
|
</button> |
|
|
<button class="btn btn-danger" onclick="toggleModal('log', true)"> |
|
|
<i data-lucide="list" class="btn-icon"></i> |
|
|
Log da Sessão |
|
|
</button> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderSessionSummary() { |
|
|
const { |
|
|
chipsInPlay, |
|
|
totalBuyins, |
|
|
totalCashouts, |
|
|
totalPayments, |
|
|
cashReceived, |
|
|
cashPaid, |
|
|
pixReceived, |
|
|
pixPaid, |
|
|
creditReceived, |
|
|
creditPaid, |
|
|
debitReceived, |
|
|
debitPaid, |
|
|
pendingToReceive, |
|
|
pendingToPay |
|
|
} = state.sessionSummary; |
|
|
|
|
|
return ` |
|
|
<div class="summary-card"> |
|
|
<div class="summary-header"> |
|
|
<h3 class="summary-title">Resumo da Sessão</h3> |
|
|
<button class="btn ${state.hideSummaryValues ? 'btn-outline' : 'btn-success'}" |
|
|
onclick="setSecurityMode('summary')"> |
|
|
<i data-lucide="${state.hideSummaryValues ? 'eye-off' : 'eye'}" class="btn-icon"></i> |
|
|
${state.hideSummaryValues ? 'Mostrar' : 'Ocultar'} |
|
|
</button> |
|
|
</div> |
|
|
<div class="summary-grid"> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Fichas em Jogo</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(chipsInPlay)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Total Buy-ins</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(totalBuyins)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Total Cashouts</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(totalCashouts)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Total Pagamentos</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(totalPayments)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Caixa (Dinheiro)</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(cashReceived - cashPaid)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">PIX Líquido</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(pixReceived - pixPaid)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Crédito Líquido</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(creditReceived - creditPaid)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">Débito Líquido</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(debitReceived - debitPaid)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">A Receber</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(pendingToReceive)} |
|
|
</span> |
|
|
</div> |
|
|
<div class="summary-item"> |
|
|
<span class="summary-item-label">A Pagar</span> |
|
|
<span class="summary-item-value"> |
|
|
${state.hideSummaryValues ? '••••' : formatCurrency(pendingToPay)} |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="summary-total mt-4 text-center"> |
|
|
Total em Fichas: ${state.hideSummaryValues ? '••••' : formatCurrency(state.chipSetTotal)} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderAddPlayerModal() { |
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.addPlayer ? '' : 'style="display: none;"'}> |
|
|
<div class="modal"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title">Adicionar Jogador</h3> |
|
|
<button class="modal-close" onclick="toggleModal('addPlayer', false)">×</button> |
|
|
</div> |
|
|
<form id="addPlayerForm" onsubmit="event.preventDefault(); |
|
|
const name = document.getElementById('playerName').value; |
|
|
handleAddPlayer(name);"> |
|
|
<div class="form-group"> |
|
|
<label for="playerName" class="form-label">Nome do Jogador</label> |
|
|
<input type="text" id="playerName" class="form-input" required autofocus> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-outline" onclick="toggleModal('addPlayer', false)"> |
|
|
Cancelar |
|
|
</button> |
|
|
<button type="submit" class="btn btn-primary"> |
|
|
Adicionar |
|
|
</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderBuyChipsModal() { |
|
|
const player = state.selectedPlayerId ? |
|
|
state.players.find(p => p.id === state.selectedPlayerId) : null; |
|
|
|
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.buyChips ? '' : 'style="display: none;"'}> |
|
|
<div class="modal"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title"> |
|
|
${player ? `Comprar Fichas - ${player.name}` : 'Comprar Fichas'} |
|
|
</h3> |
|
|
<button class="modal-close" onclick="toggleModal('buyChips', false)">×</button> |
|
|
</div> |
|
|
<form id="buyChipsForm" onsubmit="event.preventDefault(); |
|
|
const formData = new FormData(document.getElementById('buyChipsForm')); |
|
|
handleBuyChips({ |
|
|
playerId: '${state.selectedPlayerId || ''}', |
|
|
amount: parseFloat(formData.get('amount')), |
|
|
payNow: formData.get('payNow') === 'true', |
|
|
paymentMethod: formData.get('paymentMethod') |
|
|
});"> |
|
|
${!player ? ` |
|
|
<div class="form-group"> |
|
|
<label for="playerSelect" class="form-label">Jogador</label> |
|
|
<select id="playerSelect" name="playerId" class="form-select" required> |
|
|
<option value="">Selecione um jogador</option> |
|
|
${state.players.map(p => ` |
|
|
<option value="${p.id}">${p.name}</option> |
|
|
`).join('')} |
|
|
</select> |
|
|
</div> |
|
|
` : ''} |
|
|
<div class="form-group"> |
|
|
<label for="amount" class="form-label">Valor</label> |
|
|
<input type="number" id="amount" name="amount" |
|
|
class="form-input" min="1" step="0.01" required> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Forma de Pagamento</label> |
|
|
<select name="paymentMethod" class="form-select" required> |
|
|
<option value="cash">Dinheiro</option> |
|
|
<option value="pix">PIX</option> |
|
|
<option value="credit">Crédito</option> |
|
|
<option value="debit">Débito</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Status do Pagamento</label> |
|
|
<div class="flex gap-2"> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="payNow" value="true" checked> |
|
|
Pagamento Recebido |
|
|
</label> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="payNow" value="false"> |
|
|
Pendente |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-outline" |
|
|
onclick="toggleModal('buyChips', false)"> |
|
|
Cancelar |
|
|
</button> |
|
|
<button type="submit" class="btn btn-primary"> |
|
|
Confirmar |
|
|
</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderCashoutModal() { |
|
|
const player = state.selectedPlayerId ? |
|
|
state.players.find(p => p.id === state.selectedPlayerId) : null; |
|
|
|
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.cashout ? '' : 'style="display: none;"'}> |
|
|
<div class="modal"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title"> |
|
|
${player ? `Cashout - ${player.name}` : 'Cashout'} |
|
|
</h3> |
|
|
<button class="modal-close" onclick="toggleModal('cashout', false)">×</button> |
|
|
</div> |
|
|
<form id="cashoutForm" onsubmit="event.preventDefault(); |
|
|
const formData = new FormData(document.getElementById('cashoutForm')); |
|
|
handleCashout({ |
|
|
playerId: '${state.selectedPlayerId || ''}', |
|
|
amount: parseFloat(formData.get('amount')), |
|
|
payNow: formData.get('payNow') === 'true', |
|
|
paymentMethod: formData.get('paymentMethod') |
|
|
});"> |
|
|
${!player ? ` |
|
|
<div class="form-group"> |
|
|
<label for="playerSelect" class="form-label">Jogador</label> |
|
|
<select id="playerSelect" name="playerId" class="form-select" required> |
|
|
<option value="">Selecione um jogador</option> |
|
|
${state.players.map(p => ` |
|
|
<option value="${p.id}">${p.name}</option> |
|
|
`).join('')} |
|
|
</select> |
|
|
</div> |
|
|
` : ''} |
|
|
<div class="form-group"> |
|
|
<label for="amount" class="form-label">Valor</label> |
|
|
<input type="number" id="amount" name="amount" |
|
|
class="form-input" min="1" step="0.01" required> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Forma de Pagamento</label> |
|
|
<select name="paymentMethod" class="form-select" required> |
|
|
<option value="cash">Dinheiro</option> |
|
|
<option value="pix">PIX</option> |
|
|
<option value="credit">Crédito</option> |
|
|
<option value="debit">Débito</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Status do Pagamento</label> |
|
|
<div class="flex gap-2"> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="payNow" value="true" checked> |
|
|
Pagamento Efetuado |
|
|
</label> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="payNow" value="false"> |
|
|
Pendente |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-outline" |
|
|
onclick="toggleModal('cashout', false)"> |
|
|
Cancelar |
|
|
</button> |
|
|
<button type="submit" class="btn btn-success"> |
|
|
Confirmar |
|
|
</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderPaymentModal() { |
|
|
const player = state.selectedPlayerId ? |
|
|
state.players.find(p => p.id === state.selectedPlayerId) : null; |
|
|
|
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.payment ? '' : 'style="display: none;"'}> |
|
|
<div class="modal"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title"> |
|
|
${player ? `Pagamento - ${player.name}` : 'Pagamento'} |
|
|
</h3> |
|
|
<button class="modal-close" onclick="toggleModal('payment', false)">×</button> |
|
|
</div> |
|
|
<form id="paymentForm" onsubmit="event.preventDefault(); |
|
|
const formData = new FormData(document.getElementById('paymentForm')); |
|
|
handlePayment({ |
|
|
playerId: '${state.selectedPlayerId || ''}', |
|
|
amount: parseFloat(formData.get('amount')), |
|
|
paymentMethod: formData.get('paymentMethod'), |
|
|
direction: formData.get('direction') |
|
|
});"> |
|
|
${!player ? ` |
|
|
<div class="form-group"> |
|
|
<label for="playerSelect" class="form-label">Jogador</label> |
|
|
<select id="playerSelect" name="playerId" class="form-select" required> |
|
|
<option value="">Selecione um jogador</option> |
|
|
${state.players.map(p => ` |
|
|
<option value="${p.id}">${p.name}</option> |
|
|
`).join('')} |
|
|
</select> |
|
|
</div> |
|
|
` : ''} |
|
|
<div class="form-group"> |
|
|
<label for="amount" class="form-label">Valor</label> |
|
|
<input type="number" id="amount" name="amount" |
|
|
class="form-input" min="1" step="0.01" required> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Direção</label> |
|
|
<div class="flex gap-2"> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="direction" value="in" checked> |
|
|
Recebimento |
|
|
</label> |
|
|
<label class="flex items-center gap-1"> |
|
|
<input type="radio" name="direction" value="out"> |
|
|
Pagamento |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Forma de Pagamento</label> |
|
|
<select name="paymentMethod" class="form-select" required> |
|
|
<option value="cash">Dinheiro</option> |
|
|
<option value="pix">PIX</option> |
|
|
<option value="credit">Crédito</option> |
|
|
<option value="debit">Débito</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-outline" |
|
|
onclick="toggleModal('payment', false)"> |
|
|
Cancelar |
|
|
</button> |
|
|
<button type="submit" class="btn btn-success"> |
|
|
Confirmar |
|
|
</button> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderSecurityModal() { |
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.security ? '' : 'style="display: none;"'}> |
|
|
<div class="modal"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title"> |
|
|
Ocultar Valores - ${state.securityMode === 'players' ? 'Jogadores' : 'Resumo'} |
|
|
</h3> |
|
|
<button class="modal-close" onclick="toggleModal('security', false)">×</button> |
|
|
</div> |
|
|
<div class="p-4"> |
|
|
<p class="mb-4"> |
|
|
${state.securityMode === 'players' ? |
|
|
'Deseja ocultar os valores dos jogadores para que outras pessoas não vejam?' : |
|
|
'Deseja ocultar os valores do resumo da sessão para que outras pessoas não vejam?'} |
|
|
</p> |
|
|
<div class="flex justify-center gap-4"> |
|
|
<button class="btn btn-outline" |
|
|
onclick="toggleHideValues(false)"> |
|
|
<i data-lucide="eye" class="btn-icon"></i> |
|
|
Manter Visível |
|
|
</button> |
|
|
<button class="btn btn-danger" |
|
|
onclick="toggleHideValues(true)"> |
|
|
<i data-lucide="eye-off" class="btn-icon"></i> |
|
|
Ocultar Valores |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
function renderLogModal() { |
|
|
// Simulated log entries |
|
|
const logEntries = [ |
|
|
{ id: 'log-1', type: 'player_join', playerName: 'João Silva', description: 'Jogador João Silva adicionado à sessão', timestamp: new Date().toISOString() }, |
|
|
{ id: 'log-2', type: 'buyin', playerName: 'Maria Oliveira', description: 'Buy-in de R$ 300.00 via pix', timestamp: new Date(Date.now() - 3600000).toISOString() }, |
|
|
{ id: 'log-3', type: 'cashout', playerName: 'Carlos Mendes', description: 'Cashout de R$ 500.00 via cash', timestamp: new Date(Date.now() - 7200000).toISOString() }, |
|
|
{ id: 'log-4', type: 'payment', playerName: 'Ana Souza', description: 'Recebimento de R$ 150.00 via credit', timestamp: new Date(Date.now() - 10800000).toISOString() } |
|
|
]; |
|
|
|
|
|
return ` |
|
|
<div class="modal-overlay" ${state.modals.log ? '' : 'style="display: none;"'}> |
|
|
<div class="modal" style="max-width: 800px;"> |
|
|
<div class="modal-header"> |
|
|
<h3 class="modal-title">Log da Sessão</h3> |
|
|
<button class="modal-close" onclick="toggleModal('log', false)">×</button> |
|
|
</div> |
|
|
<div style="max-height: 60vh; overflow-y: auto;"> |
|
|
<table style="width: 100%; border-collapse: collapse;"> |
|
|
<thead> |
|
|
<tr style="background-color: #f8fafc;"> |
|
|
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #e2e8f0;">Data/Hora</th> |
|
|
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #e2e8f0;">Tipo</th> |
|
|
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #e2e8f0;">Jogador</th> |
|
|
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #e2e8f0;">Descrição</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
${logEntries.map(log => ` |
|
|
<tr> |
|
|
<td style="padding: 0.75rem; border-bottom: 1px solid #e2e8f0;"> |
|
|
${formatDate(log.timestamp)} |
|
|
</td> |
|
|
<td style="padding: 0.75rem; border-bottom: 1px solid #e2e8f0;"> |
|
|
${log.type === 'player_join' ? 'Entrada' : |
|
|
log.type === 'buyin' ? 'Buy-in' : |
|
|
log.type === 'cashout' ? 'Cashout' : |
|
|
'Pagamento'} |
|
|
</td> |
|
|
<td style="padding: 0.75rem; border-bottom: 1px solid #e2e8f0;"> |
|
|
${log.playerName} |
|
|
</td> |
|
|
<td style="padding: 0.75rem; border-bottom: 1px solid #e2e8f0;"> |
|
|
${log.description} |
|
|
</td> |
|
|
</tr> |
|
|
`).join('')} |
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
<div class="modal-footer"> |
|
|
<button type="button" class="btn btn-outline" |
|
|
onclick="toggleModal('log', false)"> |
|
|
Fechar |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
// Main render function |
|
|
function render() { |
|
|
app.innerHTML = ` |
|
|
<div> |
|
|
<div class="header"> |
|
|
<div> |
|
|
<h1>${state.currentSession.name}</h1> |
|
|
<p>Iniciada em ${formatDate(state.currentSession.startTime)}</p> |
|
|
</div> |
|
|
<div class="flex flex-wrap gap-2"> |
|
|
<button class="btn ${state.hidePlayersValues ? 'btn-outline' : 'btn-success'}" |
|
|
onclick="setSecurityMode('players')"> |
|
|
<i data-lucide="${state.hidePlayersValues ? 'eye-off' : 'eye'}" class="btn-icon"></i> |
|
|
Jogadores |
|
|
</button> |
|
|
<button class="btn ${state.hideSummaryValues ? 'btn-outline' : 'btn-success'}" |
|
|
onclick="setSecurityMode('summary')"> |
|
|
<i data-lucide="${state.hideSummaryValues ? 'eye-off' : 'eye'}" class="btn-icon"></i> |
|
|
Resumo |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
${renderQuickActions()} |
|
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-6"> |
|
|
${state.players.map(player => renderPlayerCard(player)).join('')} |
|
|
</div> |
|
|
|
|
|
${renderSessionSummary()} |
|
|
|
|
|
${renderAddPlayerModal()} |
|
|
${renderBuyChipsModal()} |
|
|
${renderCashoutModal()} |
|
|
${renderPaymentModal()} |
|
|
${renderSecurityModal()} |
|
|
${renderLogModal()} |
|
|
</div> |
|
|
`; |
|
|
|
|
|
// Refresh Lucide icons after rendering |
|
|
lucide.createIcons(); |
|
|
} |
|
|
|
|
|
// Initial render |
|
|
render(); |
|
|
|
|
|
// Set references to functions in global scope for event handlers |
|
|
window.state = state; |
|
|
window.toggleModal = toggleModal; |
|
|
window.setSecurityMode = setSecurityMode; |
|
|
window.toggleHideValues = toggleHideValues; |
|
|
window.handleAddPlayer = handleAddPlayer; |
|
|
window.handleBuyChips = handleBuyChips; |
|
|
window.handleCashout = handleCashout; |
|
|
window.handlePayment = handlePayment; |
|
|
window.updateChipSets = updateChipSets; |
|
|
}); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> |
|
|
</html> |