Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
371b5832
Commit
371b5832
authored
Jan 02, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix barcode direct print
parent
69e46405
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
161 additions
and
47 deletions
+161
-47
routes.py
mbetterclient/web_dashboard/routes.py
+42
-9
dashboard.css
mbetterclient/web_dashboard/static/css/dashboard.css
+1
-7
base.html
mbetterclient/web_dashboard/templates/base.html
+1
-1
admin_bets.html
...rclient/web_dashboard/templates/dashboard/admin_bets.html
+46
-5
bet_details.html
...client/web_dashboard/templates/dashboard/bet_details.html
+25
-20
bets.html
mbetterclient/web_dashboard/templates/dashboard/bets.html
+46
-5
No files found.
mbetterclient/web_dashboard/routes.py
View file @
371b5832
...
@@ -4291,6 +4291,10 @@ def get_cashier_bets():
...
@@ -4291,6 +4291,10 @@ def get_cashier_bets():
else
:
else
:
target_date
=
date
.
today
()
target_date
=
date
.
today
()
# Get filter parameters
status_filter
=
request
.
args
.
get
(
'status'
,
''
)
.
strip
()
match_number_filter
=
request
.
args
.
get
(
'match_number'
,
''
)
.
strip
()
session
=
api_bp
.
db_manager
.
get_session
()
session
=
api_bp
.
db_manager
.
get_session
()
try
:
try
:
# Import models
# Import models
...
@@ -4326,6 +4330,17 @@ def get_cashier_bets():
...
@@ -4326,6 +4330,17 @@ def get_cashier_bets():
BetModel
.
bet_datetime
<=
end_datetime
BetModel
.
bet_datetime
<=
end_datetime
)
.
order_by
(
BetModel
.
bet_datetime
.
desc
())
)
.
order_by
(
BetModel
.
bet_datetime
.
desc
())
# Apply match number filter if specified
if
match_number_filter
:
try
:
match_number
=
int
(
match_number_filter
)
# Filter bets that have details for the specified match number
bets_query
=
bets_query
.
join
(
BetDetailModel
)
.
join
(
MatchModel
)
.
filter
(
MatchModel
.
match_number
==
match_number
)
.
distinct
()
except
ValueError
:
return
jsonify
({
"error"
:
"Invalid match number format"
}),
400
bets
=
bets_query
.
all
()
bets
=
bets_query
.
all
()
logger
.
info
(
f
"Found {len(bets)} bets in database for date {date_param}"
)
logger
.
info
(
f
"Found {len(bets)} bets in database for date {date_param}"
)
bets_data
=
[]
bets_data
=
[]
...
@@ -4347,13 +4362,14 @@ def get_cashier_bets():
...
@@ -4347,13 +4362,14 @@ def get_cashier_bets():
# Calculate total amount for this bet
# Calculate total amount for this bet
bet_total
=
sum
(
float
(
detail
.
amount
)
for
detail
in
bet_details
)
bet_total
=
sum
(
float
(
detail
.
amount
)
for
detail
in
bet_details
)
bet_data
[
'total_amount'
]
=
bet_total
bet_data
[
'total_amount'
]
=
bet_total
total_amount
+=
bet_total
# Determine overall bet status for statistics using same logic as bet details page
# Determine overall bet status for filtering and statistics using same logic as bet details page
overall_status
=
'pending'
if
bet_details
:
if
bet_details
:
won_count
=
0
won_count
=
0
lost_count
=
0
lost_count
=
0
pending_count
=
0
pending_count
=
0
cancelled_count
=
0
for
detail
in
bet_details
:
for
detail
in
bet_details
:
# Get match information for winning check
# Get match information for winning check
...
@@ -4366,16 +4382,33 @@ def get_cashier_bets():
...
@@ -4366,16 +4382,33 @@ def get_cashier_bets():
elif
detail
.
result
==
'pending'
:
elif
detail
.
result
==
'pending'
:
pending_count
+=
1
pending_count
+=
1
elif
detail
.
result
==
'cancelled'
:
elif
detail
.
result
==
'cancelled'
:
lost_count
+=
1
# Treat cancelled as lost for statistics
cancelled_count
+=
1
# Determine overall status
if
pending_count
>
0
:
overall_status
=
'pending'
elif
won_count
>
0
and
lost_count
==
0
and
cancelled_count
==
0
:
overall_status
=
'won'
elif
lost_count
>
0
or
cancelled_count
>
0
:
overall_status
=
'lost'
elif
cancelled_count
>
0
:
overall_status
=
'cancelled'
bet_data
[
'overall_status'
]
=
overall_status
if
won_count
>
0
and
lost_count
==
0
and
pending_count
==
0
:
# Apply status filter if specified
if
status_filter
and
status_filter
!=
overall_status
:
continue
# Skip this bet if it doesn't match the status filter
total_amount
+=
bet_total
# Update statistics counters
if
overall_status
==
'won'
:
won_bets
+=
1
won_bets
+=
1
elif
lost_count
>
0
:
elif
overall_status
==
'lost'
:
lost_bets
+=
1
lost_bets
+=
1
else
:
else
:
pending_bets
+=
1
pending_bets
+=
1
else
:
pending_bets
+=
1
bets_data
.
append
(
bet_data
)
bets_data
.
append
(
bet_data
)
...
...
mbetterclient/web_dashboard/static/css/dashboard.css
View file @
371b5832
...
@@ -330,13 +330,7 @@ body {
...
@@ -330,13 +330,7 @@ body {
}
}
}
}
/* Dark mode support (future enhancement) */
/* Dark mode support disabled - forced light mode */
@media
(
prefers-color-scheme
:
dark
)
{
:root
{
--bs-body-bg
:
#1a1a1a
;
--bs-body-color
:
#f8f9fa
;
}
}
/* Loading states */
/* Loading states */
.loading
{
.loading
{
...
...
mbetterclient/web_dashboard/templates/base.html
View file @
371b5832
<!DOCTYPE html>
<!DOCTYPE html>
<html
lang=
"en"
>
<html
lang=
"en"
data-bs-theme=
"light"
>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
...
...
mbetterclient/web_dashboard/templates/dashboard/admin_bets.html
View file @
371b5832
...
@@ -47,13 +47,13 @@
...
@@ -47,13 +47,13 @@
<div
class=
"card mb-4"
>
<div
class=
"card mb-4"
>
<div
class=
"card-header"
>
<div
class=
"card-header"
>
<div
class=
"row"
>
<div
class=
"row"
>
<div
class=
"col-md-
8
"
>
<div
class=
"col-md-
4
"
>
<h5
class=
"card-title mb-0"
>
<h5
class=
"card-title mb-0"
>
<i
class=
"fas fa-calendar-alt me-2"
></i>
Bets for Today
<i
class=
"fas fa-calendar-alt me-2"
></i>
Bets for Today
<span
class=
"badge bg-info ms-2"
id=
"bets-count"
>
0
</span>
<span
class=
"badge bg-info ms-2"
id=
"bets-count"
>
0
</span>
</h5>
</h5>
</div>
</div>
<div
class=
"col-md-
4
"
>
<div
class=
"col-md-
2
"
>
<div
class=
"input-group"
>
<div
class=
"input-group"
>
<span
class=
"input-group-text"
>
<span
class=
"input-group-text"
>
<i
class=
"fas fa-calendar"
></i>
<i
class=
"fas fa-calendar"
></i>
...
@@ -62,6 +62,19 @@
...
@@ -62,6 +62,19 @@
value=
"{{ today_date }}"
max=
"{{ today_date }}"
>
value=
"{{ today_date }}"
max=
"{{ today_date }}"
>
</div>
</div>
</div>
</div>
<div
class=
"col-md-3"
>
<select
class=
"form-select"
id=
"bet-status-filter"
>
<option
value=
""
>
All Status
</option>
<option
value=
"won"
>
Won
</option>
<option
value=
"lost"
>
Lost
</option>
<option
value=
"pending"
>
Pending
</option>
<option
value=
"cancelled"
>
Cancelled
</option>
</select>
</div>
<div
class=
"col-md-3"
>
<input
type=
"number"
class=
"form-control"
id=
"match-number-filter"
placeholder=
"Match # (optional)"
min=
"1"
>
</div>
</div>
</div>
</div>
</div>
<div
class=
"card-body"
>
<div
class=
"card-body"
>
...
@@ -331,6 +344,16 @@ document.addEventListener('DOMContentLoaded', function() {
...
@@ -331,6 +344,16 @@ document.addEventListener('DOMContentLoaded', function() {
loadBets
();
loadBets
();
});
});
// Status filter change event
document
.
getElementById
(
'bet-status-filter'
).
addEventListener
(
'change'
,
function
()
{
loadBets
();
});
// Match number filter change event
document
.
getElementById
(
'match-number-filter'
).
addEventListener
(
'input'
,
function
()
{
loadBets
();
});
// New bet button
// New bet button
document
.
getElementById
(
'btn-new-bet'
).
addEventListener
(
'click'
,
function
()
{
document
.
getElementById
(
'btn-new-bet'
).
addEventListener
(
'click'
,
function
()
{
window
.
location
.
href
=
'/bets/new'
;
window
.
location
.
href
=
'/bets/new'
;
...
@@ -398,14 +421,23 @@ function loadBets() {
...
@@ -398,14 +421,23 @@ function loadBets() {
const
container
=
document
.
getElementById
(
'bets-container'
);
const
container
=
document
.
getElementById
(
'bets-container'
);
const
countBadge
=
document
.
getElementById
(
'bets-count'
);
const
countBadge
=
document
.
getElementById
(
'bets-count'
);
const
dateInput
=
document
.
getElementById
(
'bet-date-picker'
);
const
dateInput
=
document
.
getElementById
(
'bet-date-picker'
);
const
statusFilter
=
document
.
getElementById
(
'bet-status-filter'
);
const
matchFilter
=
document
.
getElementById
(
'match-number-filter'
);
const
selectedDate
=
dateInput
.
value
;
const
selectedDate
=
dateInput
.
value
;
const
selectedStatus
=
statusFilter
.
value
;
const
selectedMatch
=
matchFilter
.
value
;
if
(
!
container
)
{
if
(
!
container
)
{
console
.
error
(
'❌ bets-container not found'
);
console
.
error
(
'❌ bets-container not found'
);
return
;
return
;
}
}
console
.
log
(
'📡 Making API request to /api/cashier/bets for date:'
,
selectedDate
);
console
.
log
(
'📡 Making API request to /api/cashier/bets with filters:'
,
{
date
:
selectedDate
,
status
:
selectedStatus
,
match
:
selectedMatch
});
// Show loading state
// Show loading state
container
.
innerHTML
=
`
container
.
innerHTML
=
`
...
@@ -414,7 +446,16 @@ function loadBets() {
...
@@ -414,7 +446,16 @@ function loadBets() {
</div>
</div>
`
;
`
;
fetch
(
`/api/cashier/bets?date=
${
selectedDate
}
`
)
// Build query string with filters
let
queryParams
=
`date=
${
selectedDate
}
`
;
if
(
selectedStatus
)
{
queryParams
+=
`&status=
${
selectedStatus
}
`
;
}
if
(
selectedMatch
)
{
queryParams
+=
`&match_number=
${
selectedMatch
}
`
;
}
fetch
(
`/api/cashier/bets?
${
queryParams
}
`
)
.
then
(
response
=>
{
.
then
(
response
=>
{
console
.
log
(
'📡 API response status:'
,
response
.
status
);
console
.
log
(
'📡 API response status:'
,
response
.
status
);
if
(
!
response
.
ok
)
{
if
(
!
response
.
ok
)
{
...
...
mbetterclient/web_dashboard/templates/dashboard/bet_details.html
View file @
371b5832
...
@@ -839,9 +839,10 @@ function generateVerificationCodes(betUuid) {
...
@@ -839,9 +839,10 @@ function generateVerificationCodes(betUuid) {
.
then
(
settings
=>
{
.
then
(
settings
=>
{
if
(
settings
.
success
&&
settings
.
settings
&&
settings
.
settings
.
show_on_thermal
)
{
if
(
settings
.
success
&&
settings
.
settings
&&
settings
.
settings
.
show_on_thermal
)
{
// Generate barcode image using the saved barcode data
// Generate barcode image using the saved barcode data
fetch
(
`/api/barcode/
${
betUuid
}
`
)
return
fetch
(
`/api/barcode/
${
betUuid
}
`
)
.
then
(
response
=>
response
.
blob
())
.
then
(
response
=>
response
.
blob
())
.
then
(
blob
=>
{
.
then
(
blob
=>
{
return
new
Promise
((
resolve
)
=>
{
const
reader
=
new
FileReader
();
const
reader
=
new
FileReader
();
reader
.
onload
=
function
()
{
reader
.
onload
=
function
()
{
const
base64
=
reader
.
result
;
const
base64
=
reader
.
result
;
...
@@ -859,8 +860,12 @@ function generateVerificationCodes(betUuid) {
...
@@ -859,8 +860,12 @@ function generateVerificationCodes(betUuid) {
if
(
barcodeElement
)
{
if
(
barcodeElement
)
{
barcodeElement
.
innerHTML
=
`<img src="
${
base64
}
" alt="Barcode" class="barcode-img" style="width: 200px; height: 100px;">`
;
barcodeElement
.
innerHTML
=
`<img src="
${
base64
}
" alt="Barcode" class="barcode-img" style="width: 200px; height: 100px;">`
;
}
}
resolve
();
};
};
reader
.
readAsDataURL
(
blob
);
reader
.
readAsDataURL
(
blob
);
});
})
.
then
(()
=>
{
checkComplete
();
checkComplete
();
})
})
.
catch
(
error
=>
{
.
catch
(
error
=>
{
...
...
mbetterclient/web_dashboard/templates/dashboard/bets.html
View file @
371b5832
...
@@ -47,13 +47,13 @@
...
@@ -47,13 +47,13 @@
<div
class=
"card mb-4"
>
<div
class=
"card mb-4"
>
<div
class=
"card-header"
>
<div
class=
"card-header"
>
<div
class=
"row"
>
<div
class=
"row"
>
<div
class=
"col-md-
8
"
>
<div
class=
"col-md-
4
"
>
<h5
class=
"card-title mb-0"
>
<h5
class=
"card-title mb-0"
>
<i
class=
"fas fa-calendar-alt me-2"
></i>
Bets for Today
<i
class=
"fas fa-calendar-alt me-2"
></i>
Bets for Today
<span
class=
"badge bg-info ms-2"
id=
"bets-count"
>
0
</span>
<span
class=
"badge bg-info ms-2"
id=
"bets-count"
>
0
</span>
</h5>
</h5>
</div>
</div>
<div
class=
"col-md-
4
"
>
<div
class=
"col-md-
2
"
>
<div
class=
"input-group"
>
<div
class=
"input-group"
>
<span
class=
"input-group-text"
>
<span
class=
"input-group-text"
>
<i
class=
"fas fa-calendar"
></i>
<i
class=
"fas fa-calendar"
></i>
...
@@ -62,6 +62,19 @@
...
@@ -62,6 +62,19 @@
value=
"{{ today_date }}"
max=
"{{ today_date }}"
>
value=
"{{ today_date }}"
max=
"{{ today_date }}"
>
</div>
</div>
</div>
</div>
<div
class=
"col-md-3"
>
<select
class=
"form-select"
id=
"bet-status-filter"
>
<option
value=
""
>
All Status
</option>
<option
value=
"won"
>
Won
</option>
<option
value=
"lost"
>
Lost
</option>
<option
value=
"pending"
>
Pending
</option>
<option
value=
"cancelled"
>
Cancelled
</option>
</select>
</div>
<div
class=
"col-md-3"
>
<input
type=
"number"
class=
"form-control"
id=
"match-number-filter"
placeholder=
"Match # (optional)"
min=
"1"
>
</div>
</div>
</div>
</div>
</div>
<div
class=
"card-body"
>
<div
class=
"card-body"
>
...
@@ -328,6 +341,16 @@ document.addEventListener('DOMContentLoaded', function() {
...
@@ -328,6 +341,16 @@ document.addEventListener('DOMContentLoaded', function() {
loadBets
();
loadBets
();
});
});
// Status filter change event
document
.
getElementById
(
'bet-status-filter'
).
addEventListener
(
'change'
,
function
()
{
loadBets
();
});
// Match number filter change event
document
.
getElementById
(
'match-number-filter'
).
addEventListener
(
'input'
,
function
()
{
loadBets
();
});
// New bet button
// New bet button
document
.
getElementById
(
'btn-new-bet'
).
addEventListener
(
'click'
,
function
()
{
document
.
getElementById
(
'btn-new-bet'
).
addEventListener
(
'click'
,
function
()
{
window
.
location
.
href
=
'/cashier/bets/new'
;
window
.
location
.
href
=
'/cashier/bets/new'
;
...
@@ -398,14 +421,23 @@ function loadBets() {
...
@@ -398,14 +421,23 @@ function loadBets() {
const
container
=
document
.
getElementById
(
'bets-container'
);
const
container
=
document
.
getElementById
(
'bets-container'
);
const
countBadge
=
document
.
getElementById
(
'bets-count'
);
const
countBadge
=
document
.
getElementById
(
'bets-count'
);
const
dateInput
=
document
.
getElementById
(
'bet-date-picker'
);
const
dateInput
=
document
.
getElementById
(
'bet-date-picker'
);
const
statusFilter
=
document
.
getElementById
(
'bet-status-filter'
);
const
matchFilter
=
document
.
getElementById
(
'match-number-filter'
);
const
selectedDate
=
dateInput
.
value
;
const
selectedDate
=
dateInput
.
value
;
const
selectedStatus
=
statusFilter
.
value
;
const
selectedMatch
=
matchFilter
.
value
;
if
(
!
container
)
{
if
(
!
container
)
{
console
.
error
(
'❌ bets-container not found'
);
console
.
error
(
'❌ bets-container not found'
);
return
;
return
;
}
}
console
.
log
(
'📡 Making API request to /api/cashier/bets for date:'
,
selectedDate
);
console
.
log
(
'📡 Making API request to /api/cashier/bets with filters:'
,
{
date
:
selectedDate
,
status
:
selectedStatus
,
match
:
selectedMatch
});
// Show loading state
// Show loading state
container
.
innerHTML
=
`
container
.
innerHTML
=
`
...
@@ -414,7 +446,16 @@ function loadBets() {
...
@@ -414,7 +446,16 @@ function loadBets() {
</div>
</div>
`
;
`
;
fetch
(
`/api/cashier/bets?date=
${
selectedDate
}
`
)
// Build query string with filters
let
queryParams
=
`date=
${
selectedDate
}
`
;
if
(
selectedStatus
)
{
queryParams
+=
`&status=
${
selectedStatus
}
`
;
}
if
(
selectedMatch
)
{
queryParams
+=
`&match_number=
${
selectedMatch
}
`
;
}
fetch
(
`/api/cashier/bets?
${
queryParams
}
`
)
.
then
(
response
=>
{
.
then
(
response
=>
{
console
.
log
(
'📡 API response status:'
,
response
.
status
);
console
.
log
(
'📡 API response status:'
,
response
.
status
);
if
(
!
response
.
ok
)
{
if
(
!
response
.
ok
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment