Spaces:
Running
Running
Add 3 files
Browse files- README.md +7 -5
- index.html +1469 -19
- prompts.txt +1 -0
README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: vid-project
|
| 3 |
+
emoji: 🐳
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite
|
| 10 |
---
|
| 11 |
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
index.html
CHANGED
|
@@ -1,19 +1,1469 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Workout Video Archiver | Trainer's Toolkit</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<style>
|
| 10 |
+
/* Custom styles that can't be done with Tailwind */
|
| 11 |
+
.video-upload-area {
|
| 12 |
+
border: 2px dashed #cbd5e0;
|
| 13 |
+
transition: all 0.3s ease;
|
| 14 |
+
}
|
| 15 |
+
.video-upload-area:hover {
|
| 16 |
+
border-color: #4f46e5;
|
| 17 |
+
background-color: #f8fafc;
|
| 18 |
+
}
|
| 19 |
+
.video-upload-area.dragover {
|
| 20 |
+
border-color: #4f46e5;
|
| 21 |
+
background-color: #e0e7ff;
|
| 22 |
+
}
|
| 23 |
+
.video-thumbnail {
|
| 24 |
+
transition: transform 0.2s ease;
|
| 25 |
+
}
|
| 26 |
+
.video-thumbnail:hover {
|
| 27 |
+
transform: scale(1.03);
|
| 28 |
+
}
|
| 29 |
+
.tag-cloud {
|
| 30 |
+
display: flex;
|
| 31 |
+
flex-wrap: wrap;
|
| 32 |
+
gap: 8px;
|
| 33 |
+
}
|
| 34 |
+
.tag-item {
|
| 35 |
+
cursor: pointer;
|
| 36 |
+
transition: all 0.2s ease;
|
| 37 |
+
}
|
| 38 |
+
.tag-item:hover {
|
| 39 |
+
transform: translateY(-2px);
|
| 40 |
+
}
|
| 41 |
+
.sidebar {
|
| 42 |
+
transition: all 0.3s ease;
|
| 43 |
+
}
|
| 44 |
+
@media (max-width: 768px) {
|
| 45 |
+
.sidebar {
|
| 46 |
+
transform: translateX(-100%);
|
| 47 |
+
position: fixed;
|
| 48 |
+
z-index: 50;
|
| 49 |
+
height: 100vh;
|
| 50 |
+
top: 0;
|
| 51 |
+
left: 0;
|
| 52 |
+
}
|
| 53 |
+
.sidebar.open {
|
| 54 |
+
transform: translateX(0);
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
.video-player-container {
|
| 58 |
+
aspect-ratio: 16/9;
|
| 59 |
+
}
|
| 60 |
+
</style>
|
| 61 |
+
</head>
|
| 62 |
+
<body class="bg-gray-50 font-sans">
|
| 63 |
+
<div class="flex h-screen overflow-hidden">
|
| 64 |
+
<!-- Sidebar -->
|
| 65 |
+
<div class="sidebar bg-indigo-700 text-white w-64 flex-shrink-0 p-4 md:block">
|
| 66 |
+
<div class="flex items-center justify-between mb-8">
|
| 67 |
+
<h1 class="text-2xl font-bold">Trainer's Toolkit</h1>
|
| 68 |
+
<button id="closeSidebar" class="md:hidden text-white">
|
| 69 |
+
<i class="fas fa-times"></i>
|
| 70 |
+
</button>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<nav>
|
| 74 |
+
<div class="mb-6">
|
| 75 |
+
<h2 class="text-sm uppercase font-semibold text-indigo-200 mb-2">Navigation</h2>
|
| 76 |
+
<ul class="space-y-1">
|
| 77 |
+
<li>
|
| 78 |
+
<button id="dashboardBtn" class="w-full text-left px-3 py-2 rounded-md bg-indigo-800 flex items-center">
|
| 79 |
+
<i class="fas fa-home mr-2"></i> Dashboard
|
| 80 |
+
</button>
|
| 81 |
+
</li>
|
| 82 |
+
<li>
|
| 83 |
+
<button id="uploadBtn" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 84 |
+
<i class="fas fa-upload mr-2"></i> Upload Videos
|
| 85 |
+
</button>
|
| 86 |
+
</li>
|
| 87 |
+
<li>
|
| 88 |
+
<button id="libraryBtn" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 89 |
+
<i class="fas fa-video mr-2"></i> Video Library
|
| 90 |
+
</button>
|
| 91 |
+
</li>
|
| 92 |
+
<li>
|
| 93 |
+
<button id="tagsBtn" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 94 |
+
<i class="fas fa-tags mr-2"></i> Manage Tags
|
| 95 |
+
</button>
|
| 96 |
+
</li>
|
| 97 |
+
</ul>
|
| 98 |
+
</div>
|
| 99 |
+
|
| 100 |
+
<div class="mb-6">
|
| 101 |
+
<h2 class="text-sm uppercase font-semibold text-indigo-200 mb-2">Quick Filters</h2>
|
| 102 |
+
<ul class="space-y-1">
|
| 103 |
+
<li>
|
| 104 |
+
<button data-filter="recent" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 105 |
+
<i class="fas fa-clock mr-2"></i> Recent Videos
|
| 106 |
+
</button>
|
| 107 |
+
</li>
|
| 108 |
+
<li>
|
| 109 |
+
<button data-filter="week" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 110 |
+
<i class="fas fa-calendar-week mr-2"></i> This Week
|
| 111 |
+
</button>
|
| 112 |
+
</li>
|
| 113 |
+
<li>
|
| 114 |
+
<button data-filter="month" class="w-full text-left px-3 py-2 rounded-md hover:bg-indigo-600 flex items-center">
|
| 115 |
+
<i class="fas fa-calendar-alt mr-2"></i> This Month
|
| 116 |
+
</button>
|
| 117 |
+
</li>
|
| 118 |
+
</ul>
|
| 119 |
+
</div>
|
| 120 |
+
|
| 121 |
+
<div>
|
| 122 |
+
<h2 class="text-sm uppercase font-semibold text-indigo-200 mb-2">Popular Tags</h2>
|
| 123 |
+
<div class="tag-cloud" id="popularTags">
|
| 124 |
+
<!-- Tags will be dynamically inserted here -->
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
</nav>
|
| 128 |
+
</div>
|
| 129 |
+
|
| 130 |
+
<!-- Main Content -->
|
| 131 |
+
<div class="flex-1 flex flex-col overflow-hidden">
|
| 132 |
+
<!-- Top Navigation -->
|
| 133 |
+
<header class="bg-white shadow-sm z-10">
|
| 134 |
+
<div class="flex items-center justify-between px-6 py-4">
|
| 135 |
+
<div class="flex items-center">
|
| 136 |
+
<button id="menuBtn" class="mr-4 text-gray-600 md:hidden">
|
| 137 |
+
<i class="fas fa-bars"></i>
|
| 138 |
+
</button>
|
| 139 |
+
<h2 id="pageTitle" class="text-xl font-semibold text-gray-800">Dashboard</h2>
|
| 140 |
+
</div>
|
| 141 |
+
<div class="flex items-center space-x-4">
|
| 142 |
+
<div class="relative">
|
| 143 |
+
<input type="text" placeholder="Search videos..." class="pl-10 pr-4 py-2 border rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 144 |
+
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
|
| 145 |
+
</div>
|
| 146 |
+
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-700 font-semibold">
|
| 147 |
+
T
|
| 148 |
+
</div>
|
| 149 |
+
</div>
|
| 150 |
+
</div>
|
| 151 |
+
</header>
|
| 152 |
+
|
| 153 |
+
<!-- Page Content -->
|
| 154 |
+
<main class="flex-1 overflow-y-auto p-6 bg-gray-50" id="mainContent">
|
| 155 |
+
<!-- Dashboard Content -->
|
| 156 |
+
<div id="dashboardContent">
|
| 157 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
| 158 |
+
<div class="bg-white p-6 rounded-lg shadow">
|
| 159 |
+
<div class="flex items-center justify-between">
|
| 160 |
+
<div>
|
| 161 |
+
<p class="text-gray-500">Total Videos</p>
|
| 162 |
+
<h3 class="text-2xl font-bold" id="totalVideos">0</h3>
|
| 163 |
+
</div>
|
| 164 |
+
<div class="p-3 rounded-full bg-indigo-100 text-indigo-600">
|
| 165 |
+
<i class="fas fa-video"></i>
|
| 166 |
+
</div>
|
| 167 |
+
</div>
|
| 168 |
+
</div>
|
| 169 |
+
<div class="bg-white p-6 rounded-lg shadow">
|
| 170 |
+
<div class="flex items-center justify-between">
|
| 171 |
+
<div>
|
| 172 |
+
<p class="text-gray-500">Total Tags</p>
|
| 173 |
+
<h3 class="text-2xl font-bold" id="totalTags">0</h3>
|
| 174 |
+
</div>
|
| 175 |
+
<div class="p-3 rounded-full bg-green-100 text-green-600">
|
| 176 |
+
<i class="fas fa-tags"></i>
|
| 177 |
+
</div>
|
| 178 |
+
</div>
|
| 179 |
+
</div>
|
| 180 |
+
<div class="bg-white p-6 rounded-lg shadow">
|
| 181 |
+
<div class="flex items-center justify-between">
|
| 182 |
+
<div>
|
| 183 |
+
<p class="text-gray-500">Storage Used</p>
|
| 184 |
+
<h3 class="text-2xl font-bold" id="storageUsed">0 MB</h3>
|
| 185 |
+
</div>
|
| 186 |
+
<div class="p-3 rounded-full bg-blue-100 text-blue-600">
|
| 187 |
+
<i class="fas fa-database"></i>
|
| 188 |
+
</div>
|
| 189 |
+
</div>
|
| 190 |
+
</div>
|
| 191 |
+
</div>
|
| 192 |
+
|
| 193 |
+
<div class="bg-white p-6 rounded-lg shadow mb-6">
|
| 194 |
+
<div class="flex justify-between items-center mb-4">
|
| 195 |
+
<h3 class="text-lg font-semibold">Recent Videos</h3>
|
| 196 |
+
<button id="viewAllVideos" class="text-indigo-600 hover:text-indigo-800 text-sm font-medium">View All</button>
|
| 197 |
+
</div>
|
| 198 |
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4" id="recentVideos">
|
| 199 |
+
<!-- Recent videos will be inserted here -->
|
| 200 |
+
</div>
|
| 201 |
+
</div>
|
| 202 |
+
|
| 203 |
+
<div class="bg-white p-6 rounded-lg shadow">
|
| 204 |
+
<h3 class="text-lg font-semibold mb-4">Quick Actions</h3>
|
| 205 |
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
| 206 |
+
<button id="quickUploadBtn" class="flex flex-col items-center justify-center p-4 border rounded-lg hover:bg-gray-50 transition-colors">
|
| 207 |
+
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-600 mb-2">
|
| 208 |
+
<i class="fas fa-upload"></i>
|
| 209 |
+
</div>
|
| 210 |
+
<span class="text-sm font-medium">Upload Video</span>
|
| 211 |
+
</button>
|
| 212 |
+
<button id="quickTagBtn" class="flex flex-col items-center justify-center p-4 border rounded-lg hover:bg-gray-50 transition-colors">
|
| 213 |
+
<div class="w-10 h-10 rounded-full bg-green-100 flex items-center justify-center text-green-600 mb-2">
|
| 214 |
+
<i class="fas fa-tags"></i>
|
| 215 |
+
</div>
|
| 216 |
+
<span class="text-sm font-medium">Manage Tags</span>
|
| 217 |
+
</button>
|
| 218 |
+
<button id="quickShareBtn" class="flex flex-col items-center justify-center p-4 border rounded-lg hover:bg-gray-50 transition-colors">
|
| 219 |
+
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mb-2">
|
| 220 |
+
<i class="fas fa-share-alt"></i>
|
| 221 |
+
</div>
|
| 222 |
+
<span class="text-sm font-medium">Share Videos</span>
|
| 223 |
+
</button>
|
| 224 |
+
<button id="quickStatsBtn" class="flex flex-col items-center justify-center p-4 border rounded-lg hover:bg-gray-50 transition-colors">
|
| 225 |
+
<div class="w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center text-purple-600 mb-2">
|
| 226 |
+
<i class="fas fa-chart-line"></i>
|
| 227 |
+
</div>
|
| 228 |
+
<span class="text-sm font-medium">View Stats</span>
|
| 229 |
+
</button>
|
| 230 |
+
</div>
|
| 231 |
+
</div>
|
| 232 |
+
</div>
|
| 233 |
+
|
| 234 |
+
<!-- Upload Content -->
|
| 235 |
+
<div id="uploadContent" class="hidden">
|
| 236 |
+
<div class="bg-white p-6 rounded-lg shadow mb-6">
|
| 237 |
+
<h2 class="text-xl font-semibold mb-4">Upload Workout Videos</h2>
|
| 238 |
+
|
| 239 |
+
<div class="video-upload-area rounded-lg p-8 text-center mb-6" id="dropArea">
|
| 240 |
+
<i class="fas fa-cloud-upload-alt text-4xl text-indigo-500 mb-3"></i>
|
| 241 |
+
<h3 class="text-lg font-medium mb-1">Drag & Drop your workout videos here</h3>
|
| 242 |
+
<p class="text-gray-500 mb-4">or</p>
|
| 243 |
+
<label for="videoUpload" class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 cursor-pointer">
|
| 244 |
+
<i class="fas fa-folder-open mr-2"></i> Browse Files
|
| 245 |
+
<input type="file" id="videoUpload" class="hidden" accept="video/*" multiple>
|
| 246 |
+
</label>
|
| 247 |
+
</div>
|
| 248 |
+
|
| 249 |
+
<div class="mb-6">
|
| 250 |
+
<h3 class="text-lg font-medium mb-3">Upload Queue</h3>
|
| 251 |
+
<div class="border rounded-lg overflow-hidden">
|
| 252 |
+
<table class="min-w-full divide-y divide-gray-200">
|
| 253 |
+
<thead class="bg-gray-50">
|
| 254 |
+
<tr>
|
| 255 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">File Name</th>
|
| 256 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Size</th>
|
| 257 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Progress</th>
|
| 258 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
|
| 259 |
+
</tr>
|
| 260 |
+
</thead>
|
| 261 |
+
<tbody class="bg-white divide-y divide-gray-200" id="uploadQueue">
|
| 262 |
+
<!-- Upload queue will be inserted here -->
|
| 263 |
+
</tbody>
|
| 264 |
+
</table>
|
| 265 |
+
</div>
|
| 266 |
+
</div>
|
| 267 |
+
|
| 268 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
| 269 |
+
<div>
|
| 270 |
+
<label for="workoutName" class="block text-sm font-medium text-gray-700 mb-1">Workout Name</label>
|
| 271 |
+
<input type="text" id="workoutName" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g. Upper Body Strength">
|
| 272 |
+
</div>
|
| 273 |
+
<div>
|
| 274 |
+
<label for="trainingName" class="block text-sm font-medium text-gray-700 mb-1">Training Name</label>
|
| 275 |
+
<input type="text" id="trainingName" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g. Shoulder Press Technique">
|
| 276 |
+
</div>
|
| 277 |
+
</div>
|
| 278 |
+
|
| 279 |
+
<div class="mt-6">
|
| 280 |
+
<label for="videoTags" class="block text-sm font-medium text-gray-700 mb-1">Tags (comma separated)</label>
|
| 281 |
+
<input type="text" id="videoTags" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g. strength, shoulders, technique">
|
| 282 |
+
</div>
|
| 283 |
+
|
| 284 |
+
<div class="mt-6">
|
| 285 |
+
<label for="videoDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
| 286 |
+
<textarea id="videoDescription" rows="3" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Add a description for this workout video..."></textarea>
|
| 287 |
+
</div>
|
| 288 |
+
|
| 289 |
+
<div class="mt-6 flex justify-end">
|
| 290 |
+
<button id="cancelUpload" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 mr-3">Cancel</button>
|
| 291 |
+
<button id="startUpload" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Start Upload</button>
|
| 292 |
+
</div>
|
| 293 |
+
</div>
|
| 294 |
+
</div>
|
| 295 |
+
|
| 296 |
+
<!-- Video Library Content -->
|
| 297 |
+
<div id="libraryContent" class="hidden">
|
| 298 |
+
<div class="bg-white p-6 rounded-lg shadow mb-6">
|
| 299 |
+
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6">
|
| 300 |
+
<h2 class="text-xl font-semibold mb-4 md:mb-0">Video Library</h2>
|
| 301 |
+
|
| 302 |
+
<div class="flex flex-col sm:flex-row gap-3">
|
| 303 |
+
<div class="relative">
|
| 304 |
+
<select id="sortBy" class="appearance-none pl-3 pr-8 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white">
|
| 305 |
+
<option value="date-desc">Sort by: Newest First</option>
|
| 306 |
+
<option value="date-asc">Sort by: Oldest First</option>
|
| 307 |
+
<option value="name-asc">Sort by: Name (A-Z)</option>
|
| 308 |
+
<option value="name-desc">Sort by: Name (Z-A)</option>
|
| 309 |
+
</select>
|
| 310 |
+
<i class="fas fa-chevron-down absolute right-3 top-3 text-gray-400 pointer-events-none"></i>
|
| 311 |
+
</div>
|
| 312 |
+
|
| 313 |
+
<div class="relative">
|
| 314 |
+
<select id="filterBy" class="appearance-none pl-3 pr-8 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white">
|
| 315 |
+
<option value="all">Filter by: All Videos</option>
|
| 316 |
+
<option value="week">This Week</option>
|
| 317 |
+
<option value="month">This Month</option>
|
| 318 |
+
<option value="year">This Year</option>
|
| 319 |
+
</select>
|
| 320 |
+
<i class="fas fa-chevron-down absolute right-3 top-3 text-gray-400 pointer-events-none"></i>
|
| 321 |
+
</div>
|
| 322 |
+
</div>
|
| 323 |
+
</div>
|
| 324 |
+
|
| 325 |
+
<div class="mb-4">
|
| 326 |
+
<div class="tag-cloud" id="activeFilters">
|
| 327 |
+
<!-- Active filters will be shown here -->
|
| 328 |
+
</div>
|
| 329 |
+
</div>
|
| 330 |
+
|
| 331 |
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6" id="videoGrid">
|
| 332 |
+
<!-- Videos will be inserted here -->
|
| 333 |
+
</div>
|
| 334 |
+
|
| 335 |
+
<div class="mt-6 flex justify-center" id="loadMoreContainer">
|
| 336 |
+
<button id="loadMoreVideos" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">Load More Videos</button>
|
| 337 |
+
</div>
|
| 338 |
+
</div>
|
| 339 |
+
</div>
|
| 340 |
+
|
| 341 |
+
<!-- Tags Management Content -->
|
| 342 |
+
<div id="tagsContent" class="hidden">
|
| 343 |
+
<div class="bg-white p-6 rounded-lg shadow mb-6">
|
| 344 |
+
<div class="flex justify-between items-center mb-6">
|
| 345 |
+
<h2 class="text-xl font-semibold">Manage Tags</h2>
|
| 346 |
+
<button id="addNewTag" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 flex items-center">
|
| 347 |
+
<i class="fas fa-plus mr-2"></i> New Tag
|
| 348 |
+
</button>
|
| 349 |
+
</div>
|
| 350 |
+
|
| 351 |
+
<div class="mb-6">
|
| 352 |
+
<div class="relative">
|
| 353 |
+
<input type="text" id="tagSearch" class="w-full pl-10 pr-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Search tags...">
|
| 354 |
+
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
|
| 355 |
+
</div>
|
| 356 |
+
</div>
|
| 357 |
+
|
| 358 |
+
<div class="border rounded-lg overflow-hidden">
|
| 359 |
+
<table class="min-w-full divide-y divide-gray-200">
|
| 360 |
+
<thead class="bg-gray-50">
|
| 361 |
+
<tr>
|
| 362 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tag Name</th>
|
| 363 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Color</th>
|
| 364 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Videos Count</th>
|
| 365 |
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
| 366 |
+
</tr>
|
| 367 |
+
</thead>
|
| 368 |
+
<tbody class="bg-white divide-y divide-gray-200" id="tagsTable">
|
| 369 |
+
<!-- Tags will be inserted here -->
|
| 370 |
+
</tbody>
|
| 371 |
+
</table>
|
| 372 |
+
</div>
|
| 373 |
+
</div>
|
| 374 |
+
</div>
|
| 375 |
+
|
| 376 |
+
<!-- Video Detail Modal -->
|
| 377 |
+
<div id="videoModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
| 378 |
+
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
| 379 |
+
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
| 380 |
+
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
| 381 |
+
</div>
|
| 382 |
+
|
| 383 |
+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
| 384 |
+
|
| 385 |
+
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
| 386 |
+
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
| 387 |
+
<div class="flex justify-between items-start">
|
| 388 |
+
<h3 class="text-lg font-medium text-gray-900" id="modalVideoTitle">Video Title</h3>
|
| 389 |
+
<button id="closeModal" class="text-gray-400 hover:text-gray-500">
|
| 390 |
+
<i class="fas fa-times"></i>
|
| 391 |
+
</button>
|
| 392 |
+
</div>
|
| 393 |
+
|
| 394 |
+
<div class="mt-4 video-player-container bg-black rounded-lg overflow-hidden">
|
| 395 |
+
<video controls class="w-full h-full" id="modalVideoPlayer">
|
| 396 |
+
Your browser does not support the video tag.
|
| 397 |
+
</video>
|
| 398 |
+
</div>
|
| 399 |
+
|
| 400 |
+
<div class="mt-4 grid grid-cols-1 md:grid-cols-3 gap-4">
|
| 401 |
+
<div class="md:col-span-2">
|
| 402 |
+
<div class="flex items-center mb-2">
|
| 403 |
+
<i class="fas fa-calendar-alt text-gray-500 mr-2"></i>
|
| 404 |
+
<span class="text-sm text-gray-600" id="modalVideoDate">Uploaded on: </span>
|
| 405 |
+
</div>
|
| 406 |
+
<div class="flex items-center mb-2">
|
| 407 |
+
<i class="fas fa-tag text-gray-500 mr-2"></i>
|
| 408 |
+
<div class="tag-cloud" id="modalVideoTags">
|
| 409 |
+
<!-- Tags will be inserted here -->
|
| 410 |
+
</div>
|
| 411 |
+
</div>
|
| 412 |
+
<div class="flex items-center mb-2">
|
| 413 |
+
<i class="fas fa-dumbbell text-gray-500 mr-2"></i>
|
| 414 |
+
<span class="text-sm text-gray-600" id="modalVideoWorkout">Workout: </span>
|
| 415 |
+
</div>
|
| 416 |
+
<div class="flex items-center mb-2">
|
| 417 |
+
<i class="fas fa-running text-gray-500 mr-2"></i>
|
| 418 |
+
<span class="text-sm text-gray-600" id="modalVideoTraining">Training: </span>
|
| 419 |
+
</div>
|
| 420 |
+
<div class="mt-3">
|
| 421 |
+
<p class="text-sm text-gray-700" id="modalVideoDescription">No description provided.</p>
|
| 422 |
+
</div>
|
| 423 |
+
</div>
|
| 424 |
+
|
| 425 |
+
<div class="bg-gray-50 p-4 rounded-lg">
|
| 426 |
+
<h4 class="font-medium mb-3">Share Options</h4>
|
| 427 |
+
<div class="space-y-3">
|
| 428 |
+
<button class="w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md bg-white text-gray-700 hover:bg-gray-50">
|
| 429 |
+
<i class="fab fa-whatsapp text-green-500 mr-2"></i> Share via WhatsApp
|
| 430 |
+
</button>
|
| 431 |
+
<button class="w-full flex items-center justify-center px-4 py-2 border border-gray-300 rounded-md bg-white text-gray-700 hover:bg-gray-50">
|
| 432 |
+
<i class="fas fa-envelope text-blue-500 mr-2"></i> Share via Email
|
| 433 |
+
</button>
|
| 434 |
+
<div class="mt-2">
|
| 435 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Direct Link</label>
|
| 436 |
+
<div class="flex">
|
| 437 |
+
<input type="text" id="videoLink" class="flex-1 px-3 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-indigo-500" readonly>
|
| 438 |
+
<button id="copyLink" class="px-3 py-2 bg-indigo-600 text-white rounded-r-md hover:bg-indigo-700">
|
| 439 |
+
<i class="fas fa-copy"></i>
|
| 440 |
+
</button>
|
| 441 |
+
</div>
|
| 442 |
+
</div>
|
| 443 |
+
</div>
|
| 444 |
+
|
| 445 |
+
<div class="mt-4 pt-4 border-t border-gray-200">
|
| 446 |
+
<h4 class="font-medium mb-3">Video Actions</h4>
|
| 447 |
+
<div class="space-y-2">
|
| 448 |
+
<button id="editVideoBtn" class="w-full flex items-center justify-start px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md">
|
| 449 |
+
<i class="fas fa-edit text-indigo-500 mr-2"></i> Edit Details
|
| 450 |
+
</button>
|
| 451 |
+
<button id="downloadVideoBtn" class="w-full flex items-center justify-start px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md">
|
| 452 |
+
<i class="fas fa-download text-green-500 mr-2"></i> Download Video
|
| 453 |
+
</button>
|
| 454 |
+
<button id="deleteVideoBtn" class="w-full flex items-center justify-start px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md">
|
| 455 |
+
<i class="fas fa-trash-alt text-red-500 mr-2"></i> Delete Video
|
| 456 |
+
</button>
|
| 457 |
+
</div>
|
| 458 |
+
</div>
|
| 459 |
+
</div>
|
| 460 |
+
</div>
|
| 461 |
+
</div>
|
| 462 |
+
</div>
|
| 463 |
+
</div>
|
| 464 |
+
</div>
|
| 465 |
+
|
| 466 |
+
<!-- Edit Video Modal -->
|
| 467 |
+
<div id="editVideoModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
| 468 |
+
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
| 469 |
+
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
| 470 |
+
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
| 471 |
+
</div>
|
| 472 |
+
|
| 473 |
+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
| 474 |
+
|
| 475 |
+
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full">
|
| 476 |
+
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
| 477 |
+
<div class="flex justify-between items-start">
|
| 478 |
+
<h3 class="text-lg font-medium text-gray-900">Edit Video Details</h3>
|
| 479 |
+
<button id="closeEditModal" class="text-gray-400 hover:text-gray-500">
|
| 480 |
+
<i class="fas fa-times"></i>
|
| 481 |
+
</button>
|
| 482 |
+
</div>
|
| 483 |
+
|
| 484 |
+
<div class="mt-4">
|
| 485 |
+
<input type="hidden" id="editVideoId">
|
| 486 |
+
|
| 487 |
+
<div class="mb-4">
|
| 488 |
+
<label for="editVideoTitle" class="block text-sm font-medium text-gray-700 mb-1">Video Title</label>
|
| 489 |
+
<input type="text" id="editVideoTitle" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 490 |
+
</div>
|
| 491 |
+
|
| 492 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
| 493 |
+
<div>
|
| 494 |
+
<label for="editWorkoutName" class="block text-sm font-medium text-gray-700 mb-1">Workout Name</label>
|
| 495 |
+
<input type="text" id="editWorkoutName" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 496 |
+
</div>
|
| 497 |
+
<div>
|
| 498 |
+
<label for="editTrainingName" class="block text-sm font-medium text-gray-700 mb-1">Training Name</label>
|
| 499 |
+
<input type="text" id="editTrainingName" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 500 |
+
</div>
|
| 501 |
+
</div>
|
| 502 |
+
|
| 503 |
+
<div class="mb-4">
|
| 504 |
+
<label for="editVideoTags" class="block text-sm font-medium text-gray-700 mb-1">Tags (comma separated)</label>
|
| 505 |
+
<input type="text" id="editVideoTags" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 506 |
+
</div>
|
| 507 |
+
|
| 508 |
+
<div>
|
| 509 |
+
<label for="editVideoDescription" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
| 510 |
+
<textarea id="editVideoDescription" rows="3" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
|
| 511 |
+
</div>
|
| 512 |
+
</div>
|
| 513 |
+
</div>
|
| 514 |
+
|
| 515 |
+
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
| 516 |
+
<button id="saveVideoChanges" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm">
|
| 517 |
+
Save Changes
|
| 518 |
+
</button>
|
| 519 |
+
<button id="cancelVideoEdit" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
| 520 |
+
Cancel
|
| 521 |
+
</button>
|
| 522 |
+
</div>
|
| 523 |
+
</div>
|
| 524 |
+
</div>
|
| 525 |
+
</div>
|
| 526 |
+
|
| 527 |
+
<!-- Add Tag Modal -->
|
| 528 |
+
<div id="addTagModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
| 529 |
+
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
| 530 |
+
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
| 531 |
+
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
| 532 |
+
</div>
|
| 533 |
+
|
| 534 |
+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
| 535 |
+
|
| 536 |
+
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-md sm:w-full">
|
| 537 |
+
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
| 538 |
+
<div class="flex justify-between items-start">
|
| 539 |
+
<h3 class="text-lg font-medium text-gray-900">Add New Tag</h3>
|
| 540 |
+
<button id="closeTagModal" class="text-gray-400 hover:text-gray-500">
|
| 541 |
+
<i class="fas fa-times"></i>
|
| 542 |
+
</button>
|
| 543 |
+
</div>
|
| 544 |
+
|
| 545 |
+
<div class="mt-4">
|
| 546 |
+
<div class="mb-4">
|
| 547 |
+
<label for="newTagName" class="block text-sm font-medium text-gray-700 mb-1">Tag Name</label>
|
| 548 |
+
<input type="text" id="newTagName" class="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 549 |
+
</div>
|
| 550 |
+
|
| 551 |
+
<div class="mb-4">
|
| 552 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Tag Color</label>
|
| 553 |
+
<div class="flex flex-wrap gap-2">
|
| 554 |
+
<div class="w-8 h-8 rounded-full bg-red-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="red"></div>
|
| 555 |
+
<div class="w-8 h-8 rounded-full bg-blue-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="blue"></div>
|
| 556 |
+
<div class="w-8 h-8 rounded-full bg-green-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="green"></div>
|
| 557 |
+
<div class="w-8 h-8 rounded-full bg-yellow-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="yellow"></div>
|
| 558 |
+
<div class="w-8 h-8 rounded-full bg-indigo-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="indigo"></div>
|
| 559 |
+
<div class="w-8 h-8 rounded-full bg-purple-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="purple"></div>
|
| 560 |
+
<div class="w-8 h-8 rounded-full bg-pink-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="pink"></div>
|
| 561 |
+
<div class="w-8 h-8 rounded-full bg-gray-500 cursor-pointer border-2 border-transparent hover:border-gray-300" data-color="gray"></div>
|
| 562 |
+
</div>
|
| 563 |
+
<input type="hidden" id="selectedTagColor" value="indigo">
|
| 564 |
+
</div>
|
| 565 |
+
</div>
|
| 566 |
+
</div>
|
| 567 |
+
|
| 568 |
+
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
| 569 |
+
<button id="saveNewTag" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm">
|
| 570 |
+
Add Tag
|
| 571 |
+
</button>
|
| 572 |
+
<button id="cancelNewTag" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
| 573 |
+
Cancel
|
| 574 |
+
</button>
|
| 575 |
+
</div>
|
| 576 |
+
</div>
|
| 577 |
+
</div>
|
| 578 |
+
</div>
|
| 579 |
+
|
| 580 |
+
<!-- Confirmation Modal -->
|
| 581 |
+
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
| 582 |
+
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
| 583 |
+
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
| 584 |
+
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
| 585 |
+
</div>
|
| 586 |
+
|
| 587 |
+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
| 588 |
+
|
| 589 |
+
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
|
| 590 |
+
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
| 591 |
+
<div class="sm:flex sm:items-start">
|
| 592 |
+
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
| 593 |
+
<i class="fas fa-exclamation-triangle text-red-600"></i>
|
| 594 |
+
</div>
|
| 595 |
+
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
| 596 |
+
<h3 class="text-lg leading-6 font-medium text-gray-900" id="confirmModalTitle">Confirm Action</h3>
|
| 597 |
+
<div class="mt-2">
|
| 598 |
+
<p class="text-sm text-gray-500" id="confirmModalMessage">Are you sure you want to perform this action?</p>
|
| 599 |
+
</div>
|
| 600 |
+
</div>
|
| 601 |
+
</div>
|
| 602 |
+
</div>
|
| 603 |
+
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
| 604 |
+
<button id="confirmAction" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
| 605 |
+
Confirm
|
| 606 |
+
</button>
|
| 607 |
+
<button id="cancelAction" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
| 608 |
+
Cancel
|
| 609 |
+
</button>
|
| 610 |
+
</div>
|
| 611 |
+
</div>
|
| 612 |
+
</div>
|
| 613 |
+
</div>
|
| 614 |
+
|
| 615 |
+
<!-- Notification Toast -->
|
| 616 |
+
<div id="toast" class="fixed bottom-4 right-4 hidden">
|
| 617 |
+
<div class="bg-green-500 text-white px-4 py-2 rounded-md shadow-lg flex items-center">
|
| 618 |
+
<i class="fas fa-check-circle mr-2"></i>
|
| 619 |
+
<span id="toastMessage">Operation completed successfully!</span>
|
| 620 |
+
<button id="closeToast" class="ml-4">
|
| 621 |
+
<i class="fas fa-times"></i>
|
| 622 |
+
</button>
|
| 623 |
+
</div>
|
| 624 |
+
</div>
|
| 625 |
+
</main>
|
| 626 |
+
</div>
|
| 627 |
+
</div>
|
| 628 |
+
|
| 629 |
+
<script>
|
| 630 |
+
// Sample data storage (in a real app, this would be a database)
|
| 631 |
+
let videos = [];
|
| 632 |
+
let tags = [
|
| 633 |
+
{ id: 1, name: 'strength', color: 'red', count: 0 },
|
| 634 |
+
{ id: 2, name: 'cardio', color: 'blue', count: 0 },
|
| 635 |
+
{ id: 3, name: 'flexibility', color: 'green', count: 0 },
|
| 636 |
+
{ id: 4, name: 'technique', color: 'yellow', count: 0 },
|
| 637 |
+
{ id: 5, name: 'beginner', color: 'indigo', count: 0 },
|
| 638 |
+
{ id: 6, name: 'advanced', color: 'purple', count: 0 }
|
| 639 |
+
];
|
| 640 |
+
|
| 641 |
+
// DOM Elements
|
| 642 |
+
const menuBtn = document.getElementById('menuBtn');
|
| 643 |
+
const closeSidebar = document.getElementById('closeSidebar');
|
| 644 |
+
const sidebar = document.querySelector('.sidebar');
|
| 645 |
+
const pageTitle = document.getElementById('pageTitle');
|
| 646 |
+
const mainContent = document.getElementById('mainContent');
|
| 647 |
+
|
| 648 |
+
// Content sections
|
| 649 |
+
const dashboardContent = document.getElementById('dashboardContent');
|
| 650 |
+
const uploadContent = document.getElementById('uploadContent');
|
| 651 |
+
const libraryContent = document.getElementById('libraryContent');
|
| 652 |
+
const tagsContent = document.getElementById('tagsContent');
|
| 653 |
+
|
| 654 |
+
// Navigation buttons
|
| 655 |
+
const dashboardBtn = document.getElementById('dashboardBtn');
|
| 656 |
+
const uploadBtn = document.getElementById('uploadBtn');
|
| 657 |
+
const libraryBtn = document.getElementById('libraryBtn');
|
| 658 |
+
const tagsBtn = document.getElementById('tagsBtn');
|
| 659 |
+
|
| 660 |
+
// Quick action buttons
|
| 661 |
+
const quickUploadBtn = document.getElementById('quickUploadBtn');
|
| 662 |
+
const quickTagBtn = document.getElementById('quickTagBtn');
|
| 663 |
+
const quickShareBtn = document.getElementById('quickShareBtn');
|
| 664 |
+
const quickStatsBtn = document.getElementById('quickStatsBtn');
|
| 665 |
+
const viewAllVideos = document.getElementById('viewAllVideos');
|
| 666 |
+
|
| 667 |
+
// Upload section elements
|
| 668 |
+
const dropArea = document.getElementById('dropArea');
|
| 669 |
+
const videoUpload = document.getElementById('videoUpload');
|
| 670 |
+
const uploadQueue = document.getElementById('uploadQueue');
|
| 671 |
+
const startUpload = document.getElementById('startUpload');
|
| 672 |
+
const cancelUpload = document.getElementById('cancelUpload');
|
| 673 |
+
|
| 674 |
+
// Library section elements
|
| 675 |
+
const videoGrid = document.getElementById('videoGrid');
|
| 676 |
+
const sortBy = document.getElementById('sortBy');
|
| 677 |
+
const filterBy = document.getElementById('filterBy');
|
| 678 |
+
const activeFilters = document.getElementById('activeFilters');
|
| 679 |
+
const loadMoreVideos = document.getElementById('loadMoreVideos');
|
| 680 |
+
const loadMoreContainer = document.getElementById('loadMoreContainer');
|
| 681 |
+
const popularTags = document.getElementById('popularTags');
|
| 682 |
+
|
| 683 |
+
// Tags section elements
|
| 684 |
+
const tagsTable = document.getElementById('tagsTable');
|
| 685 |
+
const addNewTag = document.getElementById('addNewTag');
|
| 686 |
+
const tagSearch = document.getElementById('tagSearch');
|
| 687 |
+
|
| 688 |
+
// Modal elements
|
| 689 |
+
const videoModal = document.getElementById('videoModal');
|
| 690 |
+
const modalVideoTitle = document.getElementById('modalVideoTitle');
|
| 691 |
+
const modalVideoPlayer = document.getElementById('modalVideoPlayer');
|
| 692 |
+
const modalVideoDate = document.getElementById('modalVideoDate');
|
| 693 |
+
const modalVideoTags = document.getElementById('modalVideoTags');
|
| 694 |
+
const modalVideoWorkout = document.getElementById('modalVideoWorkout');
|
| 695 |
+
const modalVideoTraining = document.getElementById('modalVideoTraining');
|
| 696 |
+
const modalVideoDescription = document.getElementById('modalVideoDescription');
|
| 697 |
+
const videoLink = document.getElementById('videoLink');
|
| 698 |
+
const copyLink = document.getElementById('copyLink');
|
| 699 |
+
const closeModal = document.getElementById('closeModal');
|
| 700 |
+
|
| 701 |
+
const editVideoModal = document.getElementById('editVideoModal');
|
| 702 |
+
const editVideoId = document.getElementById('editVideoId');
|
| 703 |
+
const editVideoTitle = document.getElementById('editVideoTitle');
|
| 704 |
+
const editWorkoutName = document.getElementById('editWorkoutName');
|
| 705 |
+
const editTrainingName = document.getElementById('editTrainingName');
|
| 706 |
+
const editVideoTags = document.getElementById('editVideoTags');
|
| 707 |
+
const editVideoDescription = document.getElementById('editVideoDescription');
|
| 708 |
+
const saveVideoChanges = document.getElementById('saveVideoChanges');
|
| 709 |
+
const cancelVideoEdit = document.getElementById('cancelVideoEdit');
|
| 710 |
+
const closeEditModal = document.getElementById('closeEditModal');
|
| 711 |
+
|
| 712 |
+
const addTagModal = document.getElementById('addTagModal');
|
| 713 |
+
const newTagName = document.getElementById('newTagName');
|
| 714 |
+
const selectedTagColor = document.getElementById('selectedTagColor');
|
| 715 |
+
const saveNewTag = document.getElementById('saveNewTag');
|
| 716 |
+
const cancelNewTag = document.getElementById('cancelNewTag');
|
| 717 |
+
const closeTagModal = document.getElementById('closeTagModal');
|
| 718 |
+
|
| 719 |
+
const confirmModal = document.getElementById('confirmModal');
|
| 720 |
+
const confirmModalTitle = document.getElementById('confirmModalTitle');
|
| 721 |
+
const confirmModalMessage = document.getElementById('confirmModalMessage');
|
| 722 |
+
const confirmAction = document.getElementById('confirmAction');
|
| 723 |
+
const cancelAction = document.getElementById('cancelAction');
|
| 724 |
+
|
| 725 |
+
const toast = document.getElementById('toast');
|
| 726 |
+
const toastMessage = document.getElementById('toastMessage');
|
| 727 |
+
const closeToast = document.getElementById('closeToast');
|
| 728 |
+
|
| 729 |
+
// App state
|
| 730 |
+
let currentView = 'dashboard';
|
| 731 |
+
let currentVideoPage = 1;
|
| 732 |
+
let videosPerPage = 8;
|
| 733 |
+
let selectedVideoId = null;
|
| 734 |
+
let filesToUpload = [];
|
| 735 |
+
let activeTagFilters = [];
|
| 736 |
+
let actionToConfirm = null;
|
| 737 |
+
|
| 738 |
+
// Initialize the app
|
| 739 |
+
function init() {
|
| 740 |
+
// Load sample data
|
| 741 |
+
loadSampleVideos();
|
| 742 |
+
updateTagCounts();
|
| 743 |
+
|
| 744 |
+
// Set up event listeners
|
| 745 |
+
setupEventListeners();
|
| 746 |
+
|
| 747 |
+
// Render initial views
|
| 748 |
+
renderDashboard();
|
| 749 |
+
renderPopularTags();
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
// Load sample videos for demo purposes
|
| 753 |
+
function loadSampleVideos() {
|
| 754 |
+
const sampleVideos = [
|
| 755 |
+
{
|
| 756 |
+
id: 1,
|
| 757 |
+
title: 'Shoulder Press Technique',
|
| 758 |
+
workout: 'Upper Body Strength',
|
| 759 |
+
training: 'Shoulder Press',
|
| 760 |
+
description: 'Detailed explanation of proper shoulder press technique to maximize results and prevent injury.',
|
| 761 |
+
tags: ['strength', 'shoulders', 'technique'],
|
| 762 |
+
date: new Date('2023-06-15'),
|
| 763 |
+
size: 45,
|
| 764 |
+
thumbnail: 'https://img.freepik.com/free-photo/fit-man-training-gym_651396-1025.jpg',
|
| 765 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 766 |
+
},
|
| 767 |
+
{
|
| 768 |
+
id: 2,
|
| 769 |
+
title: 'Squat Depth and Form',
|
| 770 |
+
workout: 'Lower Body Strength',
|
| 771 |
+
training: 'Barbell Squat',
|
| 772 |
+
description: 'How to achieve proper squat depth while maintaining good form throughout the movement.',
|
| 773 |
+
tags: ['strength', 'legs', 'technique', 'beginner'],
|
| 774 |
+
date: new Date('2023-06-10'),
|
| 775 |
+
size: 62,
|
| 776 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-healthy-man-athlete-doing-exercise-with-ropes-gym-single-male-model-performing-hard-training-his-upper-body-concept-healthy-lifestyle-sport-fitness-bodybuilding-wellbeing_155003-27804.jpg',
|
| 777 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 778 |
+
},
|
| 779 |
+
{
|
| 780 |
+
id: 3,
|
| 781 |
+
title: 'HIIT Circuit Demo',
|
| 782 |
+
workout: 'Cardio Blast',
|
| 783 |
+
training: 'HIIT Routine',
|
| 784 |
+
description: 'Full demonstration of our 20-minute high intensity interval training circuit.',
|
| 785 |
+
tags: ['cardio', 'hiit', 'full-body'],
|
| 786 |
+
date: new Date('2023-06-05'),
|
| 787 |
+
size: 85,
|
| 788 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-fitness-man-studio_7502-5008.jpg',
|
| 789 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 790 |
+
},
|
| 791 |
+
{
|
| 792 |
+
id: 4,
|
| 793 |
+
title: 'Deadlift Variations',
|
| 794 |
+
workout: 'Power Lifting',
|
| 795 |
+
training: 'Deadlift',
|
| 796 |
+
description: 'Comparison of conventional, sumo, and Romanian deadlift variations with pros and cons of each.',
|
| 797 |
+
tags: ['strength', 'deadlift', 'advanced'],
|
| 798 |
+
date: new Date('2023-05-28'),
|
| 799 |
+
size: 78,
|
| 800 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-fitness-man-studio_7502-5006.jpg',
|
| 801 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 802 |
+
},
|
| 803 |
+
{
|
| 804 |
+
id: 5,
|
| 805 |
+
title: 'Dynamic Warmup Routine',
|
| 806 |
+
workout: 'Pre-Workout',
|
| 807 |
+
training: 'Warmup',
|
| 808 |
+
description: '10-minute dynamic warmup routine to prepare your body for intense training sessions.',
|
| 809 |
+
tags: ['warmup', 'flexibility', 'beginner'],
|
| 810 |
+
date: new Date('2023-05-20'),
|
| 811 |
+
size: 35,
|
| 812 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-sports-man-training-gym_1303-14701.jpg',
|
| 813 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 814 |
+
},
|
| 815 |
+
{
|
| 816 |
+
id: 6,
|
| 817 |
+
title: 'Advanced Pushup Variations',
|
| 818 |
+
workout: 'Bodyweight Training',
|
| 819 |
+
training: 'Pushups',
|
| 820 |
+
description: '10 challenging pushup variations to take your upper body training to the next level.',
|
| 821 |
+
tags: ['strength', 'bodyweight', 'advanced'],
|
| 822 |
+
date: new Date('2023-05-15'),
|
| 823 |
+
size: 52,
|
| 824 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-sports-man-training-gym_1303-14696.jpg',
|
| 825 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 826 |
+
},
|
| 827 |
+
{
|
| 828 |
+
id: 7,
|
| 829 |
+
title: 'Foam Rolling Techniques',
|
| 830 |
+
workout: 'Recovery',
|
| 831 |
+
training: 'Mobility',
|
| 832 |
+
description: 'How to properly use a foam roller to improve recovery and mobility after workouts.',
|
| 833 |
+
tags: ['recovery', 'flexibility', 'beginner'],
|
| 834 |
+
date: new Date('2023-05-10'),
|
| 835 |
+
size: 48,
|
| 836 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-sports-man-training-gym_1303-14700.jpg',
|
| 837 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 838 |
+
},
|
| 839 |
+
{
|
| 840 |
+
id: 8,
|
| 841 |
+
title: 'Kettlebell Swing Form',
|
| 842 |
+
workout: 'Functional Training',
|
| 843 |
+
training: 'Kettlebell',
|
| 844 |
+
description: 'Step-by-step guide to mastering the kettlebell swing for power and endurance.',
|
| 845 |
+
tags: ['strength', 'kettlebell', 'technique'],
|
| 846 |
+
date: new Date('2023-05-05'),
|
| 847 |
+
size: 55,
|
| 848 |
+
thumbnail: 'https://img.freepik.com/free-photo/young-sports-man-training-gym_1303-14699.jpg',
|
| 849 |
+
videoUrl: 'https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'
|
| 850 |
+
}
|
| 851 |
+
];
|
| 852 |
+
|
| 853 |
+
videos = sampleVideos;
|
| 854 |
+
}
|
| 855 |
+
|
| 856 |
+
// Update tag counts based on videos
|
| 857 |
+
function updateTagCounts() {
|
| 858 |
+
tags.forEach(tag => {
|
| 859 |
+
tag.count = videos.filter(video => video.tags.includes(tag.name)).length;
|
| 860 |
+
});
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
// Set up all event listeners
|
| 864 |
+
function setupEventListeners() {
|
| 865 |
+
// Navigation
|
| 866 |
+
menuBtn.addEventListener('click', toggleSidebar);
|
| 867 |
+
closeSidebar.addEventListener('click', toggleSidebar);
|
| 868 |
+
|
| 869 |
+
dashboardBtn.addEventListener('click', () => switchView('dashboard'));
|
| 870 |
+
uploadBtn.addEventListener('click', () => switchView('upload'));
|
| 871 |
+
libraryBtn.addEventListener('click', () => switchView('library'));
|
| 872 |
+
tagsBtn.addEventListener('click', () => switchView('tags'));
|
| 873 |
+
|
| 874 |
+
// Quick actions
|
| 875 |
+
quickUploadBtn.addEventListener('click', () => switchView('upload'));
|
| 876 |
+
quickTagBtn.addEventListener('click', () => switchView('tags'));
|
| 877 |
+
quickShareBtn.addEventListener('click', () => showToast('Share feature coming soon!'));
|
| 878 |
+
quickStatsBtn.addEventListener('click', () => showToast('Statistics feature coming soon!'));
|
| 879 |
+
viewAllVideos.addEventListener('click', () => switchView('library'));
|
| 880 |
+
|
| 881 |
+
// Upload section
|
| 882 |
+
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
| 883 |
+
dropArea.addEventListener(eventName, preventDefaults, false);
|
| 884 |
+
});
|
| 885 |
+
|
| 886 |
+
['dragenter', 'dragover'].forEach(eventName => {
|
| 887 |
+
dropArea.addEventListener(eventName, highlight, false);
|
| 888 |
+
});
|
| 889 |
+
|
| 890 |
+
['dragleave', 'drop'].forEach(eventName => {
|
| 891 |
+
dropArea.addEventListener(eventName, unhighlight, false);
|
| 892 |
+
});
|
| 893 |
+
|
| 894 |
+
dropArea.addEventListener('drop', handleDrop, false);
|
| 895 |
+
videoUpload.addEventListener('change', handleFiles, false);
|
| 896 |
+
startUpload.addEventListener('click', uploadFiles);
|
| 897 |
+
cancelUpload.addEventListener('click', clearUploadQueue);
|
| 898 |
+
|
| 899 |
+
// Library section
|
| 900 |
+
sortBy.addEventListener('change', renderVideoLibrary);
|
| 901 |
+
filterBy.addEventListener('change', renderVideoLibrary);
|
| 902 |
+
loadMoreVideos.addEventListener('click', loadMoreVideosHandler);
|
| 903 |
+
|
| 904 |
+
// Tags section
|
| 905 |
+
addNewTag.addEventListener('click', showAddTagModal);
|
| 906 |
+
tagSearch.addEventListener('input', filterTags);
|
| 907 |
+
|
| 908 |
+
// Video modal
|
| 909 |
+
closeModal.addEventListener('click', () => videoModal.classList.add('hidden'));
|
| 910 |
+
copyLink.addEventListener('click', copyVideoLink);
|
| 911 |
+
editVideoBtn.addEventListener('click', showEditVideoModal);
|
| 912 |
+
downloadVideoBtn.addEventListener('click', downloadVideo);
|
| 913 |
+
deleteVideoBtn.addEventListener('click', confirmDeleteVideo);
|
| 914 |
+
|
| 915 |
+
// Edit video modal
|
| 916 |
+
closeEditModal.addEventListener('click', () => editVideoModal.classList.add('hidden'));
|
| 917 |
+
cancelVideoEdit.addEventListener('click', () => editVideoModal.classList.add('hidden'));
|
| 918 |
+
saveVideoChanges.addEventListener('click', saveVideoEdits);
|
| 919 |
+
|
| 920 |
+
// Add tag modal
|
| 921 |
+
closeTagModal.addEventListener('click', () => addTagModal.classList.add('hidden'));
|
| 922 |
+
cancelNewTag.addEventListener('click', () => addTagModal.classList.add('hidden'));
|
| 923 |
+
saveNewTag.addEventListener('click', addNewTagHandler);
|
| 924 |
+
|
| 925 |
+
// Color selection in add tag modal
|
| 926 |
+
document.querySelectorAll('[data-color]').forEach(el => {
|
| 927 |
+
el.addEventListener('click', function() {
|
| 928 |
+
document.querySelectorAll('[data-color]').forEach(item => {
|
| 929 |
+
item.classList.remove('border-gray-300');
|
| 930 |
+
item.classList.add('border-transparent');
|
| 931 |
+
});
|
| 932 |
+
this.classList.remove('border-transparent');
|
| 933 |
+
this.classList.add('border-gray-300');
|
| 934 |
+
selectedTagColor.value = this.getAttribute('data-color');
|
| 935 |
+
});
|
| 936 |
+
});
|
| 937 |
+
|
| 938 |
+
// Confirmation modal
|
| 939 |
+
closeEditModal.addEventListener('click', () => confirmModal.classList.add('hidden'));
|
| 940 |
+
cancelAction.addEventListener('click', () => confirmModal.classList.add('hidden'));
|
| 941 |
+
confirmAction.addEventListener('click', performConfirmedAction);
|
| 942 |
+
|
| 943 |
+
// Toast notification
|
| 944 |
+
closeToast.addEventListener('click', () => toast.classList.add('hidden'));
|
| 945 |
+
|
| 946 |
+
// Filter buttons
|
| 947 |
+
document.querySelectorAll('[data-filter]').forEach(btn => {
|
| 948 |
+
btn.addEventListener('click', function() {
|
| 949 |
+
const filter = this.getAttribute('data-filter');
|
| 950 |
+
applyQuickFilter(filter);
|
| 951 |
+
});
|
| 952 |
+
});
|
| 953 |
+
}
|
| 954 |
+
|
| 955 |
+
// Toggle sidebar on mobile
|
| 956 |
+
function toggleSidebar() {
|
| 957 |
+
sidebar.classList.toggle('open');
|
| 958 |
+
}
|
| 959 |
+
|
| 960 |
+
// Switch between different views
|
| 961 |
+
function switchView(view) {
|
| 962 |
+
currentView = view;
|
| 963 |
+
|
| 964 |
+
// Hide all content sections
|
| 965 |
+
dashboardContent.classList.add('hidden');
|
| 966 |
+
uploadContent.classList.add('hidden');
|
| 967 |
+
libraryContent.classList.add('hidden');
|
| 968 |
+
tagsContent.classList.add('hidden');
|
| 969 |
+
|
| 970 |
+
// Reset active states on nav buttons
|
| 971 |
+
dashboardBtn.classList.remove('bg-indigo-800');
|
| 972 |
+
uploadBtn.classList.remove('bg-indigo-800');
|
| 973 |
+
libraryBtn.classList.remove('bg-indigo-800');
|
| 974 |
+
tagsBtn.classList.remove('bg-indigo-800');
|
| 975 |
+
|
| 976 |
+
// Show the selected content section
|
| 977 |
+
switch(view) {
|
| 978 |
+
case 'dashboard':
|
| 979 |
+
dashboardContent.classList.remove('hidden');
|
| 980 |
+
dashboardBtn.classList.add('bg-indigo-800');
|
| 981 |
+
pageTitle.textContent = 'Dashboard';
|
| 982 |
+
renderDashboard();
|
| 983 |
+
break;
|
| 984 |
+
case 'upload':
|
| 985 |
+
uploadContent.classList.remove('hidden');
|
| 986 |
+
uploadBtn.classList.add('bg-indigo-800');
|
| 987 |
+
pageTitle.textContent = 'Upload Videos';
|
| 988 |
+
break;
|
| 989 |
+
case 'library':
|
| 990 |
+
libraryContent.classList.remove('hidden');
|
| 991 |
+
libraryBtn.classList.add('bg-indigo-800');
|
| 992 |
+
pageTitle.textContent = 'Video Library';
|
| 993 |
+
currentVideoPage = 1;
|
| 994 |
+
renderVideoLibrary();
|
| 995 |
+
break;
|
| 996 |
+
case 'tags':
|
| 997 |
+
tagsContent.classList.remove('hidden');
|
| 998 |
+
tagsBtn.classList.add('bg-indigo-800');
|
| 999 |
+
pageTitle.textContent = 'Manage Tags';
|
| 1000 |
+
renderTagsTable();
|
| 1001 |
+
break;
|
| 1002 |
+
}
|
| 1003 |
+
|
| 1004 |
+
// Close sidebar on mobile after selection
|
| 1005 |
+
if (window.innerWidth < 768) {
|
| 1006 |
+
sidebar.classList.remove('open');
|
| 1007 |
+
}
|
| 1008 |
+
}
|
| 1009 |
+
|
| 1010 |
+
// Render dashboard content
|
| 1011 |
+
function renderDashboard() {
|
| 1012 |
+
// Update stats
|
| 1013 |
+
document.getElementById('totalVideos').textContent = videos.length;
|
| 1014 |
+
document.getElementById('totalTags').textContent = tags.length;
|
| 1015 |
+
|
| 1016 |
+
const totalSize = videos.reduce((sum, video) => sum + video.size, 0);
|
| 1017 |
+
document.getElementById('storageUsed').textContent = `${totalSize} MB`;
|
| 1018 |
+
|
| 1019 |
+
// Render recent videos
|
| 1020 |
+
const recentVideosContainer = document.getElementById('recentVideos');
|
| 1021 |
+
recentVideosContainer.innerHTML = '';
|
| 1022 |
+
|
| 1023 |
+
// Sort by date (newest first) and take first 3
|
| 1024 |
+
const recentVideos = [...videos].sort((a, b) => b.date - a.date).slice(0, 3);
|
| 1025 |
+
|
| 1026 |
+
recentVideos.forEach(video => {
|
| 1027 |
+
const videoElement = createVideoCard(video, true);
|
| 1028 |
+
recentVideosContainer.appendChild(videoElement);
|
| 1029 |
+
});
|
| 1030 |
+
}
|
| 1031 |
+
|
| 1032 |
+
// Render video library
|
| 1033 |
+
function renderVideoLibrary() {
|
| 1034 |
+
videoGrid.innerHTML = '';
|
| 1035 |
+
currentVideoPage = 1;
|
| 1036 |
+
|
| 1037 |
+
let filteredVideos = [...videos];
|
| 1038 |
+
|
| 1039 |
+
// Apply tag filters if any
|
| 1040 |
+
if (activeTagFilters.length > 0) {
|
| 1041 |
+
filteredVideos = filteredVideos.filter(video =>
|
| 1042 |
+
activeTagFilters.some(tag => video.tags.includes(tag))
|
| 1043 |
+
);
|
| 1044 |
+
}
|
| 1045 |
+
|
| 1046 |
+
// Apply time filter
|
| 1047 |
+
const timeFilter = filterBy.value;
|
| 1048 |
+
if (timeFilter !== 'all') {
|
| 1049 |
+
const now = new Date();
|
| 1050 |
+
let startDate;
|
| 1051 |
+
|
| 1052 |
+
switch(timeFilter) {
|
| 1053 |
+
case 'week':
|
| 1054 |
+
startDate = new Date(now.setDate(now.getDate() - 7));
|
| 1055 |
+
break;
|
| 1056 |
+
case 'month':
|
| 1057 |
+
startDate = new Date(now.setMonth(now.getMonth() - 1));
|
| 1058 |
+
break;
|
| 1059 |
+
case 'year':
|
| 1060 |
+
startDate = new Date(now.setFullYear(now.getFullYear() - 1));
|
| 1061 |
+
break;
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
filteredVideos = filteredVideos.filter(video => video.date >= startDate);
|
| 1065 |
+
}
|
| 1066 |
+
|
| 1067 |
+
// Apply sorting
|
| 1068 |
+
const sortOption = sortBy.value;
|
| 1069 |
+
switch(sortOption) {
|
| 1070 |
+
case 'date-desc':
|
| 1071 |
+
filteredVideos.sort((a, b) => b.date - a.date);
|
| 1072 |
+
break;
|
| 1073 |
+
case 'date-asc':
|
| 1074 |
+
filteredVideos.sort((a, b) => a.date - b.date);
|
| 1075 |
+
break;
|
| 1076 |
+
case 'name-asc':
|
| 1077 |
+
filteredVideos.sort((a, b) => a.title.localeCompare(b.title));
|
| 1078 |
+
break;
|
| 1079 |
+
case 'name-desc':
|
| 1080 |
+
filteredVideos.sort((a, b) => b.title.localeCompare(a.title));
|
| 1081 |
+
break;
|
| 1082 |
+
}
|
| 1083 |
+
|
| 1084 |
+
// Display active filters
|
| 1085 |
+
renderActiveFilters();
|
| 1086 |
+
|
| 1087 |
+
// Check if there are videos to display
|
| 1088 |
+
if (filteredVideos.length === 0) {
|
| 1089 |
+
videoGrid.innerHTML = `
|
| 1090 |
+
<div class="col-span-full text-center py-10">
|
| 1091 |
+
<i class="fas fa-video-slash text-4xl text-gray-400 mb-3"></i>
|
| 1092 |
+
<h3 class="text-lg font-medium text-gray-700">No videos found</h3>
|
| 1093 |
+
<p class="text-gray-500">Try adjusting your filters or upload new videos</p>
|
| 1094 |
+
</div>
|
| 1095 |
+
`;
|
| 1096 |
+
loadMoreContainer.classList.add('hidden');
|
| 1097 |
+
return;
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
// Display first page of videos
|
| 1101 |
+
displayVideosPaginated(filteredVideos);
|
| 1102 |
+
|
| 1103 |
+
// Show/hide load more button
|
| 1104 |
+
if (filteredVideos.length > videosPerPage * currentVideoPage) {
|
| 1105 |
+
loadMoreContainer.classList.remove('hidden');
|
| 1106 |
+
} else {
|
| 1107 |
+
loadMoreContainer.classList.add('hidden');
|
| 1108 |
+
}
|
| 1109 |
+
}
|
| 1110 |
+
|
| 1111 |
+
// Display videos with pagination
|
| 1112 |
+
function displayVideosPaginated(videoList) {
|
| 1113 |
+
const startIndex = 0;
|
| 1114 |
+
const endIndex = Math.min(videoList.length, videosPerPage * currentVideoPage);
|
| 1115 |
+
|
| 1116 |
+
for (let i = startIndex; i < endIndex; i++) {
|
| 1117 |
+
const video = videoList[i];
|
| 1118 |
+
const videoElement = createVideoCard(video);
|
| 1119 |
+
videoGrid.appendChild(videoElement);
|
| 1120 |
+
}
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
// Load more videos handler
|
| 1124 |
+
function loadMoreVideosHandler() {
|
| 1125 |
+
currentVideoPage++;
|
| 1126 |
+
renderVideoLibrary();
|
| 1127 |
+
|
| 1128 |
+
// Scroll to the bottom of the video grid
|
| 1129 |
+
setTimeout(() => {
|
| 1130 |
+
videoGrid.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
| 1131 |
+
}, 100);
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
+
// Create a video card element
|
| 1135 |
+
function createVideoCard(video, isSmall = false) {
|
| 1136 |
+
const videoElement = document.createElement('div');
|
| 1137 |
+
videoElement.className = 'bg-white rounded-lg overflow-hidden shadow-md video-thumbnail';
|
| 1138 |
+
videoElement.dataset.id = video.id;
|
| 1139 |
+
|
| 1140 |
+
// Format date
|
| 1141 |
+
const formattedDate = video.date.toLocaleDateString('en-US', {
|
| 1142 |
+
year: 'numeric',
|
| 1143 |
+
month: 'short',
|
| 1144 |
+
day: 'numeric'
|
| 1145 |
+
});
|
| 1146 |
+
|
| 1147 |
+
if (isSmall) {
|
| 1148 |
+
videoElement.innerHTML = `
|
| 1149 |
+
<div class="cursor-pointer" onclick="openVideoModal(${video.id})">
|
| 1150 |
+
<div class="relative pb-[56.25%] bg-gray-200">
|
| 1151 |
+
<img src="${video.thumbnail}" alt="${video.title}" class="absolute h-full w-full object-cover">
|
| 1152 |
+
<div class="absolute bottom-2 right-2 bg-black bg-opacity-70 text-white text-xs px-2 py-1 rounded">
|
| 1153 |
+
${video.size} MB
|
| 1154 |
+
</div>
|
| 1155 |
+
</div>
|
| 1156 |
+
<div class="p-3">
|
| 1157 |
+
<h3 class="font-medium text-gray-800 truncate">${video.title}</h3>
|
| 1158 |
+
<p class="text-xs text-gray-500 mt-1">${formattedDate}</p>
|
| 1159 |
+
<div class="flex flex-wrap gap-1 mt-2">
|
| 1160 |
+
${video.tags.map(tag => createTagElement(tag)).join('')}
|
| 1161 |
+
</div>
|
| 1162 |
+
</div>
|
| 1163 |
+
</div>
|
| 1164 |
+
`;
|
| 1165 |
+
} else {
|
| 1166 |
+
videoElement.innerHTML = `
|
| 1167 |
+
<div class="cursor-pointer" onclick="openVideoModal(${video.id})">
|
| 1168 |
+
<div class="relative pb-[56.25%] bg-gray-200">
|
| 1169 |
+
<img src="${video.thumbnail}" alt="${video.title}" class="absolute h-full w-full object-cover">
|
| 1170 |
+
<div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 bg-black bg-opacity-30 transition-opacity">
|
| 1171 |
+
<div class="bg-white bg-opacity-80 rounded-full p-3">
|
| 1172 |
+
<i class="fas fa-play text-indigo-600"></i>
|
| 1173 |
+
</div>
|
| 1174 |
+
</div>
|
| 1175 |
+
<div class="absolute bottom-2 right-2 bg-black bg-opacity-70 text-white text-xs px-2 py-1 rounded">
|
| 1176 |
+
${video.size} MB
|
| 1177 |
+
</div>
|
| 1178 |
+
</div>
|
| 1179 |
+
<div class="p-4">
|
| 1180 |
+
<h3 class="font-medium text-gray-800 truncate">${video.title}</h3>
|
| 1181 |
+
<p class="text-sm text-gray-500 mt-1">${video.workout} • ${video.training}</p>
|
| 1182 |
+
<p class="text-xs text-gray-500 mt-1">${formattedDate}</p>
|
| 1183 |
+
<div class="flex flex-wrap gap-1 mt-2">
|
| 1184 |
+
${video.tags.map(tag => createTagElement(tag)).join('')}
|
| 1185 |
+
</div>
|
| 1186 |
+
</div>
|
| 1187 |
+
</div>
|
| 1188 |
+
`;
|
| 1189 |
+
}
|
| 1190 |
+
|
| 1191 |
+
return videoElement;
|
| 1192 |
+
}
|
| 1193 |
+
|
| 1194 |
+
// Create a tag element
|
| 1195 |
+
function createTagElement(tagName, isClickable = true) {
|
| 1196 |
+
const tag = tags.find(t => t.name === tagName);
|
| 1197 |
+
const color = tag ? tag.color : 'gray';
|
| 1198 |
+
|
| 1199 |
+
if (isClickable) {
|
| 1200 |
+
return `<span class="tag-item inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-${color}-100 text-${color}-800" onclick="event.stopPropagation(); filterByTag('${tagName}')">${tagName}</span>`;
|
| 1201 |
+
} else {
|
| 1202 |
+
return `<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-${color}-100 text-${color}-800">${tagName}</span>`;
|
| 1203 |
+
}
|
| 1204 |
+
}
|
| 1205 |
+
|
| 1206 |
+
// Render active filters
|
| 1207 |
+
function renderActiveFilters() {
|
| 1208 |
+
activeFilters.innerHTML = '';
|
| 1209 |
+
|
| 1210 |
+
if (activeTagFilters.length > 0) {
|
| 1211 |
+
const filterTitle = document.createElement('span');
|
| 1212 |
+
filterTitle.className = 'text-sm text-gray-500 mr-2';
|
| 1213 |
+
filterTitle.textContent = 'Active filters:';
|
| 1214 |
+
activeFilters.appendChild(filterTitle);
|
| 1215 |
+
|
| 1216 |
+
activeTagFilters.forEach(tag => {
|
| 1217 |
+
const tagElement = document.createElement('span');
|
| 1218 |
+
tagElement.className = 'tag-item inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800';
|
| 1219 |
+
tagElement.innerHTML = `${tag} <button class="ml-1 text-indigo-500 hover:text-indigo-700" onclick="removeTagFilter('${tag}')"><i class="fas fa-times"></i></button>`;
|
| 1220 |
+
activeFilters.appendChild(tagElement);
|
| 1221 |
+
});
|
| 1222 |
+
|
| 1223 |
+
if (activeTagFilters.length > 0) {
|
| 1224 |
+
const clearAll = document.createElement('button');
|
| 1225 |
+
clearAll.className = 'text-sm text-indigo-600 hover:text-indigo-800 ml-2';
|
| 1226 |
+
clearAll.textContent = 'Clear all';
|
| 1227 |
+
clearAll.onclick = clearAllFilters;
|
| 1228 |
+
activeFilters.appendChild(clearAll);
|
| 1229 |
+
}
|
| 1230 |
+
}
|
| 1231 |
+
}
|
| 1232 |
+
|
| 1233 |
+
// Filter by tag
|
| 1234 |
+
function filterByTag(tagName) {
|
| 1235 |
+
if (!activeTagFilters.includes(tagName)) {
|
| 1236 |
+
activeTagFilters.push(tagName);
|
| 1237 |
+
renderVideoLibrary();
|
| 1238 |
+
}
|
| 1239 |
+
}
|
| 1240 |
+
|
| 1241 |
+
// Remove tag filter
|
| 1242 |
+
function removeTagFilter(tagName) {
|
| 1243 |
+
activeTagFilters = activeTagFilters.filter(tag => tag !== tagName);
|
| 1244 |
+
renderVideoLibrary();
|
| 1245 |
+
}
|
| 1246 |
+
|
| 1247 |
+
// Clear all filters
|
| 1248 |
+
function clearAllFilters() {
|
| 1249 |
+
activeTagFilters = [];
|
| 1250 |
+
filterBy.value = 'all';
|
| 1251 |
+
renderVideoLibrary();
|
| 1252 |
+
}
|
| 1253 |
+
|
| 1254 |
+
// Apply quick filter from sidebar
|
| 1255 |
+
function applyQuickFilter(filter) {
|
| 1256 |
+
activeTagFilters = [];
|
| 1257 |
+
|
| 1258 |
+
switch(filter) {
|
| 1259 |
+
case 'recent':
|
| 1260 |
+
sortBy.value = 'date-desc';
|
| 1261 |
+
filterBy.value = 'all';
|
| 1262 |
+
break;
|
| 1263 |
+
case 'week':
|
| 1264 |
+
filterBy.value = 'week';
|
| 1265 |
+
break;
|
| 1266 |
+
case 'month':
|
| 1267 |
+
filterBy.value = 'month';
|
| 1268 |
+
break;
|
| 1269 |
+
}
|
| 1270 |
+
|
| 1271 |
+
switchView('library');
|
| 1272 |
+
}
|
| 1273 |
+
|
| 1274 |
+
// Render popular tags in sidebar
|
| 1275 |
+
function renderPopularTags() {
|
| 1276 |
+
popularTags.innerHTML = '';
|
| 1277 |
+
|
| 1278 |
+
// Sort tags by count (descending) and take top 6
|
| 1279 |
+
const popular = [...tags].sort((a, b) => b.count - a.count).slice(0, 6);
|
| 1280 |
+
|
| 1281 |
+
popular.forEach(tag => {
|
| 1282 |
+
const tagElement = document.createElement('span');
|
| 1283 |
+
tagElement.className = `tag-item inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-${tag.color}-100 text-${tag.color}-800`;
|
| 1284 |
+
tagElement.textContent = tag.name;
|
| 1285 |
+
tagElement.onclick = function() {
|
| 1286 |
+
switchView('library');
|
| 1287 |
+
filterByTag(tag.name);
|
| 1288 |
+
};
|
| 1289 |
+
popularTags.appendChild(tagElement);
|
| 1290 |
+
});
|
| 1291 |
+
}
|
| 1292 |
+
|
| 1293 |
+
// Render tags table
|
| 1294 |
+
function renderTagsTable() {
|
| 1295 |
+
tagsTable.innerHTML = '';
|
| 1296 |
+
|
| 1297 |
+
if (tags.length === 0) {
|
| 1298 |
+
tagsTable.innerHTML = `
|
| 1299 |
+
<tr>
|
| 1300 |
+
<td colspan="4" class="px-6 py-4 text-center text-gray-500">
|
| 1301 |
+
No tags found. Create your first tag to get started.
|
| 1302 |
+
</td>
|
| 1303 |
+
</tr>
|
| 1304 |
+
`;
|
| 1305 |
+
return;
|
| 1306 |
+
}
|
| 1307 |
+
|
| 1308 |
+
tags.forEach(tag => {
|
| 1309 |
+
const row = document.createElement('tr');
|
| 1310 |
+
row.innerHTML = `
|
| 1311 |
+
<td class="px-6 py-4 whitespace-nowrap">
|
| 1312 |
+
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-${tag.color}-100 text-${tag.color}-800">
|
| 1313 |
+
${tag.name}
|
| 1314 |
+
</span>
|
| 1315 |
+
</td>
|
| 1316 |
+
<td class="px-6 py-4 whitespace-nowrap">
|
| 1317 |
+
<div class="w-4 h-4 rounded-full bg-${tag.color}-500"></div>
|
| 1318 |
+
</td>
|
| 1319 |
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
| 1320 |
+
${tag.count} videos
|
| 1321 |
+
</td>
|
| 1322 |
+
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
| 1323 |
+
<button class="text-indigo-600 hover:text-indigo-900 mr-3" onclick="editTag(${tag.id})">
|
| 1324 |
+
<i class="fas fa-edit"></i>
|
| 1325 |
+
</button>
|
| 1326 |
+
<button class="text-red-600 hover:text-red-900" onclick="confirmDeleteTag(${tag.id})">
|
| 1327 |
+
<i class="fas fa-trash-alt"></i>
|
| 1328 |
+
</button>
|
| 1329 |
+
</td>
|
| 1330 |
+
`;
|
| 1331 |
+
tagsTable.appendChild(row);
|
| 1332 |
+
});
|
| 1333 |
+
}
|
| 1334 |
+
|
| 1335 |
+
// Filter tags based on search input
|
| 1336 |
+
function filterTags() {
|
| 1337 |
+
const searchTerm = tagSearch.value.toLowerCase();
|
| 1338 |
+
|
| 1339 |
+
document.querySelectorAll('#tagsTable tr').forEach(row => {
|
| 1340 |
+
if (row.cells.length === 1) return; // Skip the "no tags" row
|
| 1341 |
+
|
| 1342 |
+
const tagName = row.cells[0].textContent.toLowerCase();
|
| 1343 |
+
if (tagName.includes(searchTerm)) {
|
| 1344 |
+
row.style.display = '';
|
| 1345 |
+
} else {
|
| 1346 |
+
row.style.display = 'none';
|
| 1347 |
+
}
|
| 1348 |
+
});
|
| 1349 |
+
}
|
| 1350 |
+
|
| 1351 |
+
// Show add tag modal
|
| 1352 |
+
function showAddTagModal() {
|
| 1353 |
+
newTagName.value = '';
|
| 1354 |
+
selectedTagColor.value = 'indigo';
|
| 1355 |
+
|
| 1356 |
+
// Reset color selection
|
| 1357 |
+
document.querySelectorAll('[data-color]').forEach(item => {
|
| 1358 |
+
item.classList.remove('border-gray-300');
|
| 1359 |
+
item.classList.add('border-transparent');
|
| 1360 |
+
});
|
| 1361 |
+
|
| 1362 |
+
// Select indigo by default
|
| 1363 |
+
document.querySelector('[data-color="indigo"]').classList.add('border-gray-300');
|
| 1364 |
+
|
| 1365 |
+
addTagModal.classList.remove('hidden');
|
| 1366 |
+
}
|
| 1367 |
+
|
| 1368 |
+
// Add new tag handler
|
| 1369 |
+
function addNewTagHandler() {
|
| 1370 |
+
const name = newTagName.value.trim();
|
| 1371 |
+
const color = selectedTagColor.value;
|
| 1372 |
+
|
| 1373 |
+
if (!name) {
|
| 1374 |
+
showToast('Please enter a tag name');
|
| 1375 |
+
return;
|
| 1376 |
+
}
|
| 1377 |
+
|
| 1378 |
+
if (tags.some(tag => tag.name === name)) {
|
| 1379 |
+
showToast('Tag already exists');
|
| 1380 |
+
return;
|
| 1381 |
+
}
|
| 1382 |
+
|
| 1383 |
+
const newTag = {
|
| 1384 |
+
id: tags.length > 0 ? Math.max(...tags.map(t => t.id)) + 1 : 1,
|
| 1385 |
+
name,
|
| 1386 |
+
color,
|
| 1387 |
+
count: 0
|
| 1388 |
+
};
|
| 1389 |
+
|
| 1390 |
+
tags.push(newTag);
|
| 1391 |
+
renderTagsTable();
|
| 1392 |
+
renderPopularTags();
|
| 1393 |
+
addTagModal.classList.add('hidden');
|
| 1394 |
+
showToast('Tag added successfully');
|
| 1395 |
+
}
|
| 1396 |
+
|
| 1397 |
+
// Edit tag
|
| 1398 |
+
function editTag(tagId) {
|
| 1399 |
+
// In a real app, this would open an edit modal
|
| 1400 |
+
showToast('Edit tag feature coming soon!');
|
| 1401 |
+
}
|
| 1402 |
+
|
| 1403 |
+
// Confirm tag deletion
|
| 1404 |
+
function confirmDeleteTag(tagId) {
|
| 1405 |
+
const tag = tags.find(t => t.id === tagId);
|
| 1406 |
+
|
| 1407 |
+
if (!tag) return;
|
| 1408 |
+
|
| 1409 |
+
if (tag.count > 0) {
|
| 1410 |
+
showToast('Cannot delete tag that is in use by videos');
|
| 1411 |
+
return;
|
| 1412 |
+
}
|
| 1413 |
+
|
| 1414 |
+
confirmModalTitle.textContent = 'Delete Tag';
|
| 1415 |
+
confirmModalMessage.textContent = `Are you sure you want to delete the tag "${tag.name}"? This action cannot be undone.`;
|
| 1416 |
+
|
| 1417 |
+
actionToConfirm = () => {
|
| 1418 |
+
tags = tags.filter(t => t.id !== tagId);
|
| 1419 |
+
renderTagsTable();
|
| 1420 |
+
renderPopularTags();
|
| 1421 |
+
confirmModal.classList.add('hidden');
|
| 1422 |
+
showToast('Tag deleted successfully');
|
| 1423 |
+
};
|
| 1424 |
+
|
| 1425 |
+
confirmModal.classList.remove('hidden');
|
| 1426 |
+
}
|
| 1427 |
+
|
| 1428 |
+
// Perform confirmed action
|
| 1429 |
+
function performConfirmedAction() {
|
| 1430 |
+
if (actionToConfirm) {
|
| 1431 |
+
actionToConfirm();
|
| 1432 |
+
actionToConfirm = null;
|
| 1433 |
+
}
|
| 1434 |
+
confirmModal.classList.add('hidden');
|
| 1435 |
+
}
|
| 1436 |
+
|
| 1437 |
+
// Upload section functions
|
| 1438 |
+
function preventDefaults(e) {
|
| 1439 |
+
e.preventDefault();
|
| 1440 |
+
e.stopPropagation();
|
| 1441 |
+
}
|
| 1442 |
+
|
| 1443 |
+
function highlight() {
|
| 1444 |
+
dropArea.classList.add('dragover');
|
| 1445 |
+
}
|
| 1446 |
+
|
| 1447 |
+
function unhighlight() {
|
| 1448 |
+
dropArea.classList.remove('dragover');
|
| 1449 |
+
}
|
| 1450 |
+
|
| 1451 |
+
function handleDrop(e) {
|
| 1452 |
+
const dt = e.dataTransfer;
|
| 1453 |
+
const files = dt.files;
|
| 1454 |
+
handleFiles({ target: { files } });
|
| 1455 |
+
}
|
| 1456 |
+
|
| 1457 |
+
function handleFiles(e) {
|
| 1458 |
+
filesToUpload = [...e.target.files];
|
| 1459 |
+
renderUploadQueue();
|
| 1460 |
+
}
|
| 1461 |
+
|
| 1462 |
+
function renderUploadQueue() {
|
| 1463 |
+
uploadQueue.innerHTML = '';
|
| 1464 |
+
|
| 1465 |
+
if (filesToUpload.length === 0) {
|
| 1466 |
+
uploadQueue.innerHTML = `
|
| 1467 |
+
<tr>
|
| 1468 |
+
<td colspan="4" class="px-6 py-4 text-center text-gray-
|
| 1469 |
+
</html>
|
prompts.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
build a workout video archiver and editor app. where trainer can upload and edit his videos of exercises for every training. he can add tagging for workout and name of workout and training. then he could she it via whatsapp or email or link and he could view the videos by tag/workout/week/moth and sort chronologically or by tag
|