Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterd
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
MBetterd
Commits
e37a9a8d
Commit
e37a9a8d
authored
Sep 26, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'refs/remotes/origin/master'
parents
607e46d6
4a984e2d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
246 additions
and
136 deletions
+246
-136
fixture_detail.html
app/templates/main/fixture_detail.html
+165
-92
match_detail.html
app/templates/main/match_detail.html
+81
-44
No files found.
app/templates/main/fixture_detail.html
View file @
e37a9a8d
...
@@ -528,78 +528,114 @@
...
@@ -528,78 +528,114 @@
</div>
</div>
<script>
<script>
const
CHUNK_SIZE
=
1024
*
1024
;
// 1MB chunks
let
activeUploads
=
new
Map
();
let
activeUploads
=
new
Map
();
function
startUpload
(
matchId
)
{
function
uploadFileInChunks
(
file
,
matchId
)
{
const
fileInput
=
document
.
getElementById
(
`zip_file_
${
matchId
}
`
);
const
totalChunks
=
Math
.
ceil
(
file
.
size
/
CHUNK_SIZE
);
const
file
=
fileInput
.
files
[
0
];
const
uploadId
=
Date
.
now
()
+
'_'
+
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
);
if
(
!
file
)
{
let
currentChunk
=
0
;
return
;
}
// Show progress bar and hide upload form
// Show progress bar and hide upload form
const
uploadForm
=
document
.
getElementById
(
`upload_form_
${
matchId
}
`
);
const
uploadForm
=
document
.
getElementById
(
`upload_form_
${
matchId
}
`
);
const
progressContainer
=
document
.
getElementById
(
`progress_
${
matchId
}
`
);
const
progressContainer
=
document
.
getElementById
(
`progress_
${
matchId
}
`
);
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
uploadForm
.
style
.
display
=
'none'
;
uploadForm
.
style
.
display
=
'none'
;
progressContainer
.
style
.
display
=
'block'
;
progressContainer
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
textContent
=
'Uploading...'
;
statusDiv
.
textContent
=
'Uploading... 0%'
;
// Create FormData
function
uploadChunk
()
{
const
formData
=
new
FormData
();
if
(
currentChunk
>=
totalChunks
)
{
formData
.
append
(
'zip_file'
,
file
);
// All chunks uploaded, finalize
formData
.
append
(
'match_id'
,
matchId
);
finalizeUpload
(
uploadId
,
file
.
name
,
matchId
);
return
;
// Create XMLHttpRequest for progress tracking
const
xhr
=
new
XMLHttpRequest
();
activeUploads
.
set
(
matchId
,
xhr
);
// Progress event handler
xhr
.
upload
.
addEventListener
(
'progress'
,
function
(
e
)
{
if
(
e
.
lengthComputable
)
{
const
percentComplete
=
Math
.
round
((
e
.
loaded
/
e
.
total
)
*
100
);
updateProgress
(
matchId
,
percentComplete
);
}
}
});
// Load event handler (upload complete)
const
start
=
currentChunk
*
CHUNK_SIZE
;
xhr
.
addEventListener
(
'load'
,
function
()
{
const
end
=
Math
.
min
(
start
+
CHUNK_SIZE
,
file
.
size
);
if
(
xhr
.
status
===
200
)
{
const
chunk
=
file
.
slice
(
start
,
end
);
// Success
const
formData
=
new
FormData
();
formData
.
append
(
'chunk'
,
chunk
);
formData
.
append
(
'chunkIndex'
,
currentChunk
);
formData
.
append
(
'totalChunks'
,
totalChunks
);
formData
.
append
(
'uploadId'
,
uploadId
);
formData
.
append
(
'fileName'
,
file
.
name
);
formData
.
append
(
'matchId'
,
matchId
);
fetch
(
'/upload/chunk'
,
{
method
:
'POST'
,
body
:
formData
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
currentChunk
++
;
const
progress
=
(
currentChunk
/
totalChunks
)
*
100
;
updateProgress
(
matchId
,
Math
.
round
(
progress
));
statusDiv
.
textContent
=
`Uploading...
${
Math
.
round
(
progress
)}
%`
;
uploadChunk
();
}
else
{
throw
new
Error
(
data
.
error
||
'Upload failed'
);
}
})
.
catch
(
error
=>
{
statusDiv
.
className
=
'upload-status error'
;
statusDiv
.
textContent
=
'Upload failed: '
+
error
.
message
;
});
}
uploadChunk
();
}
function
finalizeUpload
(
uploadId
,
fileName
,
matchId
)
{
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
fetch
(
'/upload/finalize'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
},
body
:
JSON
.
stringify
({
uploadId
:
uploadId
,
fileName
:
fileName
,
matchId
:
matchId
})
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
updateProgress
(
matchId
,
100
);
updateProgress
(
matchId
,
100
);
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
textContent
=
'Upload successful!'
;
statusDiv
.
textContent
=
'Upload successful!'
;
// Reload page after a short delay to show updated status
// Reload page after a short delay to show updated status
setTimeout
(()
=>
{
setTimeout
(()
=>
{
window
.
location
.
reload
();
window
.
location
.
reload
();
},
1500
);
},
1500
);
}
else
{
}
else
{
// Error
statusDiv
.
className
=
'upload-status error'
;
handleUploadError
(
matchId
,
'Upload failed
'
);
statusDiv
.
textContent
=
'Finalization failed: '
+
(
data
.
error
||
'Unknown error
'
);
}
}
activeUploads
.
delete
(
matchId
);
})
.
catch
(
error
=>
{
statusDiv
.
className
=
'upload-status error'
;
statusDiv
.
textContent
=
'Finalization failed: '
+
error
.
message
;
});
});
}
// Error event handler
function
startUpload
(
matchId
)
{
xhr
.
addEventListener
(
'error'
,
function
()
{
const
fileInput
=
document
.
getElementById
(
`zip_file_
${
matchId
}
`
);
handleUploadError
(
matchId
,
'Network error'
);
const
file
=
fileInput
.
files
[
0
];
activeUploads
.
delete
(
matchId
);
});
// Abort event handler
if
(
!
file
)
{
xhr
.
addEventListener
(
'abort'
,
function
()
{
return
;
handleUploadError
(
matchId
,
'Upload cancelled'
);
}
activeUploads
.
delete
(
matchId
);
});
// Start upload
uploadFileInChunks
(
file
,
matchId
);
xhr
.
open
(
'POST'
,
'{{ url_for("upload.upload_zip") }}'
,
true
);
xhr
.
send
(
formData
);
}
}
function
updateProgress
(
matchId
,
percent
)
{
function
updateProgress
(
matchId
,
percent
)
{
...
@@ -651,80 +687,117 @@
...
@@ -651,80 +687,117 @@
// Fixture-level upload functionality
// Fixture-level upload functionality
let
fixtureUploadActive
=
false
;
let
fixtureUploadActive
=
false
;
function
startFixtureUpload
()
{
function
uploadFixtureFileInChunks
(
file
)
{
const
fileInput
=
document
.
getElementById
(
'fixture_zip_file'
);
const
totalChunks
=
Math
.
ceil
(
file
.
size
/
CHUNK_SIZE
);
const
file
=
fileInput
.
files
[
0
];
const
uploadId
=
Date
.
now
()
+
'_'
+
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
);
if
(
!
file
)
{
return
;
}
if
(
fixtureUploadActive
)
{
let
currentChunk
=
0
;
return
;
// Prevent multiple uploads
}
fixtureUploadActive
=
true
;
// Show progress bar and hide upload form
// Show progress bar and hide upload form
const
uploadForm
=
document
.
getElementById
(
'fixture_upload_form'
);
const
uploadForm
=
document
.
getElementById
(
'fixture_upload_form'
);
const
progressContainer
=
document
.
getElementById
(
'fixture_progress_container'
);
const
progressContainer
=
document
.
getElementById
(
'fixture_progress_container'
);
const
statusDiv
=
document
.
getElementById
(
'fixture_upload_status'
);
const
statusDiv
=
document
.
getElementById
(
'fixture_upload_status'
);
uploadForm
.
style
.
display
=
'none'
;
uploadForm
.
style
.
display
=
'none'
;
progressContainer
.
style
.
display
=
'block'
;
progressContainer
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
textContent
=
'Uploading to all matches...'
;
statusDiv
.
textContent
=
'Uploading to all matches... 0%'
;
function
uploadChunk
()
{
if
(
currentChunk
>=
totalChunks
)
{
// All chunks uploaded, finalize
finalizeFixtureUpload
(
uploadId
,
file
.
name
);
return
;
}
// Create FormData
const
start
=
currentChunk
*
CHUNK_SIZE
;
const
formData
=
new
FormData
();
const
end
=
Math
.
min
(
start
+
CHUNK_SIZE
,
file
.
size
);
formData
.
append
(
'zip_file'
,
file
);
const
chunk
=
file
.
slice
(
start
,
end
);
const
formData
=
new
FormData
();
formData
.
append
(
'chunk'
,
chunk
);
formData
.
append
(
'chunkIndex'
,
currentChunk
);
formData
.
append
(
'totalChunks'
,
totalChunks
);
formData
.
append
(
'uploadId'
,
uploadId
);
formData
.
append
(
'fileName'
,
file
.
name
);
fetch
(
'/upload/chunk'
,
{
method
:
'POST'
,
body
:
formData
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
currentChunk
++
;
const
progress
=
(
currentChunk
/
totalChunks
)
*
100
;
updateFixtureProgress
(
Math
.
round
(
progress
));
statusDiv
.
textContent
=
`Uploading to all matches...
${
Math
.
round
(
progress
)}
%`
;
uploadChunk
();
}
else
{
throw
new
Error
(
data
.
error
||
'Upload failed'
);
}
})
.
catch
(
error
=>
{
statusDiv
.
className
=
'upload-status error'
;
statusDiv
.
textContent
=
'Upload failed: '
+
error
.
message
;
fixtureUploadActive
=
false
;
});
}
// Create XMLHttpRequest for progress tracking
uploadChunk
();
const
xhr
=
new
XMLHttpRequest
();
}
// Progress event handler
function
finalizeFixtureUpload
(
uploadId
,
fileName
)
{
xhr
.
upload
.
addEventListener
(
'progress'
,
function
(
e
)
{
const
statusDiv
=
document
.
getElementById
(
'fixture_upload_status'
);
if
(
e
.
lengthComputable
)
{
const
percentComplete
=
Math
.
round
((
e
.
loaded
/
e
.
total
)
*
100
);
updateFixtureProgress
(
percentComplete
);
}
});
// Load event handler (upload complete)
fetch
(
'/upload/finalize'
,
{
xhr
.
addEventListener
(
'load'
,
function
()
{
method
:
'POST'
,
if
(
xhr
.
status
===
200
)
{
headers
:
{
// Success
'Content-Type'
:
'application/json'
,
},
body
:
JSON
.
stringify
({
uploadId
:
uploadId
,
fileName
:
fileName
})
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
updateFixtureProgress
(
100
);
updateFixtureProgress
(
100
);
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
textContent
=
'Upload successful! All matches are now active.'
;
statusDiv
.
textContent
=
'Upload successful! All matches are now active.'
;
// Reload page after a short delay to show updated status
// Reload page after a short delay to show updated status
setTimeout
(()
=>
{
setTimeout
(()
=>
{
window
.
location
.
reload
();
window
.
location
.
reload
();
},
2000
);
},
2000
);
}
else
{
}
else
{
// Error
statusDiv
.
className
=
'upload-status error'
;
handleFixtureUploadError
(
'Upload failed
'
);
statusDiv
.
textContent
=
'Finalization failed: '
+
(
data
.
error
||
'Unknown error
'
);
}
}
fixtureUploadActive
=
false
;
fixtureUploadActive
=
false
;
});
})
.
catch
(
error
=>
{
// Error event handler
statusDiv
.
className
=
'upload-status error'
;
xhr
.
addEventListener
(
'error'
,
function
()
{
statusDiv
.
textContent
=
'Finalization failed: '
+
error
.
message
;
handleFixtureUploadError
(
'Network error'
);
fixtureUploadActive
=
false
;
fixtureUploadActive
=
false
;
});
});
}
// Abort event handler
function
startFixtureUpload
()
{
xhr
.
addEventListener
(
'abort'
,
function
()
{
const
fileInput
=
document
.
getElementById
(
'fixture_zip_file'
);
handleFixtureUploadError
(
'Upload cancelled'
);
const
file
=
fileInput
.
files
[
0
];
fixtureUploadActive
=
false
;
});
// Start upload
if
(
!
file
)
{
xhr
.
open
(
'POST'
,
uploadForm
.
action
,
true
);
return
;
xhr
.
send
(
formData
);
}
if
(
fixtureUploadActive
)
{
return
;
// Prevent multiple uploads
}
fixtureUploadActive
=
true
;
uploadFixtureFileInChunks
(
file
);
}
}
function
updateFixtureProgress
(
percent
)
{
function
updateFixtureProgress
(
percent
)
{
...
...
app/templates/main/match_detail.html
View file @
e37a9a8d
...
@@ -761,76 +761,113 @@
...
@@ -761,76 +761,113 @@
}
}
// ZIP Upload Functions
// ZIP Upload Functions
function
startUpload
(
matchId
)
{
const
CHUNK_SIZE
=
1024
*
1024
;
// 1MB chunks
const
fileInput
=
document
.
getElementById
(
`zip_file_
${
matchId
}
`
);
const
file
=
fileInput
.
files
[
0
];
function
uploadFileInChunks
(
file
,
matchId
)
{
const
totalChunks
=
Math
.
ceil
(
file
.
size
/
CHUNK_SIZE
);
if
(
!
file
)
{
const
uploadId
=
Date
.
now
()
+
'_'
+
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
);
return
;
}
let
currentChunk
=
0
;
// Show progress bar and hide upload form
// Show progress bar and hide upload form
const
uploadForm
=
document
.
getElementById
(
`upload_form_
${
matchId
}
`
);
const
uploadForm
=
document
.
getElementById
(
`upload_form_
${
matchId
}
`
);
const
progressContainer
=
document
.
getElementById
(
`progress_
${
matchId
}
`
);
const
progressContainer
=
document
.
getElementById
(
`progress_
${
matchId
}
`
);
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
uploadForm
.
style
.
display
=
'none'
;
uploadForm
.
style
.
display
=
'none'
;
progressContainer
.
style
.
display
=
'block'
;
progressContainer
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
style
.
display
=
'block'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
className
=
'upload-status uploading'
;
statusDiv
.
textContent
=
'Uploading...'
;
statusDiv
.
textContent
=
'Uploading...
0%
'
;
// Create FormData
function
uploadChunk
()
{
const
formData
=
new
FormData
();
if
(
currentChunk
>=
totalChunks
)
{
formData
.
append
(
'zip_file'
,
file
);
// All chunks uploaded, finalize
formData
.
append
(
'match_id'
,
matchId
);
finalizeUpload
(
uploadId
,
file
.
name
,
matchId
);
return
;
}
// Create XMLHttpRequest for progress tracking
const
start
=
currentChunk
*
CHUNK_SIZE
;
const
xhr
=
new
XMLHttpRequest
(
);
const
end
=
Math
.
min
(
start
+
CHUNK_SIZE
,
file
.
size
);
activeUploads
.
set
(
matchId
,
xhr
);
const
chunk
=
file
.
slice
(
start
,
end
);
// Progress event handler
const
formData
=
new
FormData
();
xhr
.
upload
.
addEventListener
(
'progress'
,
function
(
e
)
{
formData
.
append
(
'chunk'
,
chunk
);
if
(
e
.
lengthComputable
)
{
formData
.
append
(
'chunkIndex'
,
currentChunk
);
const
percentComplete
=
Math
.
round
((
e
.
loaded
/
e
.
total
)
*
100
);
formData
.
append
(
'totalChunks'
,
totalChunks
);
updateProgress
(
matchId
,
percentComplete
);
formData
.
append
(
'uploadId'
,
uploadId
);
}
formData
.
append
(
'fileName'
,
file
.
name
);
});
formData
.
append
(
'matchId'
,
matchId
);
fetch
(
'/upload/chunk'
,
{
method
:
'POST'
,
body
:
formData
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
currentChunk
++
;
const
progress
=
(
currentChunk
/
totalChunks
)
*
100
;
updateProgress
(
matchId
,
Math
.
round
(
progress
));
statusDiv
.
textContent
=
`Uploading...
${
Math
.
round
(
progress
)}
%`
;
uploadChunk
();
}
else
{
throw
new
Error
(
data
.
error
||
'Upload failed'
);
}
})
.
catch
(
error
=>
{
statusDiv
.
className
=
'upload-status error'
;
statusDiv
.
textContent
=
'Upload failed: '
+
error
.
message
;
});
}
// Load event handler (upload complete)
uploadChunk
();
xhr
.
addEventListener
(
'load'
,
function
()
{
}
if
(
xhr
.
status
===
200
)
{
// Success
function
finalizeUpload
(
uploadId
,
fileName
,
matchId
)
{
const
statusDiv
=
document
.
getElementById
(
`status_
${
matchId
}
`
);
fetch
(
'/upload/finalize'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
},
body
:
JSON
.
stringify
({
uploadId
:
uploadId
,
fileName
:
fileName
,
matchId
:
matchId
})
})
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
updateProgress
(
matchId
,
100
);
updateProgress
(
matchId
,
100
);
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
className
=
'upload-status success'
;
statusDiv
.
textContent
=
'Upload successful!'
;
statusDiv
.
textContent
=
'Upload successful!'
;
// Reload page after a short delay to show updated status
// Reload page after a short delay to show updated status
setTimeout
(()
=>
{
setTimeout
(()
=>
{
window
.
location
.
reload
();
window
.
location
.
reload
();
},
1500
);
},
1500
);
}
else
{
}
else
{
// Error
statusDiv
.
className
=
'upload-status error'
;
handleUploadError
(
matchId
,
'Upload failed
'
);
statusDiv
.
textContent
=
'Finalization failed: '
+
(
data
.
error
||
'Unknown error
'
);
}
}
activeUploads
.
delete
(
matchId
);
})
.
catch
(
error
=>
{
statusDiv
.
className
=
'upload-status error'
;
statusDiv
.
textContent
=
'Finalization failed: '
+
error
.
message
;
});
});
}
// Error event handler
function
startUpload
(
matchId
)
{
xhr
.
addEventListener
(
'error'
,
function
()
{
const
fileInput
=
document
.
getElementById
(
`zip_file_
${
matchId
}
`
);
handleUploadError
(
matchId
,
'Network error'
);
const
file
=
fileInput
.
files
[
0
];
activeUploads
.
delete
(
matchId
);
});
// Abort event handler
if
(
!
file
)
{
xhr
.
addEventListener
(
'abort'
,
function
()
{
return
;
handleUploadError
(
matchId
,
'Upload cancelled'
);
}
activeUploads
.
delete
(
matchId
);
});
// Start upload
uploadFileInChunks
(
file
,
matchId
);
xhr
.
open
(
'POST'
,
'{{ url_for("upload.upload_zip") }}'
,
true
);
xhr
.
send
(
formData
);
}
}
function
updateProgress
(
matchId
,
percent
)
{
function
updateProgress
(
matchId
,
percent
)
{
...
...
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