Initial commit of dashboard
This commit is contained in:
parent
59d24aa206
commit
adc39db441
21 changed files with 797 additions and 20 deletions
1
.playwright-mcp/console-2026-06-14T14-30-38-925Z.log
Normal file
1
.playwright-mcp/console-2026-06-14T14-30-38-925Z.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
[ 2280ms] [ERROR] JSHandle@object @ http://localhost:4322/:305
|
||||
1
.playwright-mcp/console-2026-06-14T14-33-38-275Z.log
Normal file
1
.playwright-mcp/console-2026-06-14T14-33-38-275Z.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
[ 2268ms] [ERROR] JSHandle@object @ http://localhost:4322/:305
|
||||
0
.playwright-mcp/page-2026-06-14T14-30-39-017Z.yml
Normal file
0
.playwright-mcp/page-2026-06-14T14-30-39-017Z.yml
Normal file
0
.playwright-mcp/page-2026-06-14T14-31-46-468Z.yml
Normal file
0
.playwright-mcp/page-2026-06-14T14-31-46-468Z.yml
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-31-56-474Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-31-56-474Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
1
.playwright-mcp/page-2026-06-14T14-33-38-341Z.yml
Normal file
1
.playwright-mcp/page-2026-06-14T14-33-38-341Z.yml
Normal file
|
|
@ -0,0 +1 @@
|
|||
- paragraph [ref=e5]: Loading time tracking data...
|
||||
3
.playwright-mcp/page-2026-06-14T14-33-45-118Z.yml
Normal file
3
.playwright-mcp/page-2026-06-14T14-33-45-118Z.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- generic [ref=e6]:
|
||||
- heading "Error Loading Data" [level=2] [ref=e7]
|
||||
- paragraph [ref=e8]: ApexCharts is not defined
|
||||
BIN
.playwright-mcp/page-2026-06-14T14-33-48-605Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-33-48-605Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
3
.playwright-mcp/page-2026-06-14T14-46-48-666Z.yml
Normal file
3
.playwright-mcp/page-2026-06-14T14-46-48-666Z.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- generic [active] [ref=e1]:
|
||||
- paragraph [ref=e5]: Loading time tracking data...
|
||||
- img
|
||||
187
.playwright-mcp/page-2026-06-14T14-46-55-990Z.yml
Normal file
187
.playwright-mcp/page-2026-06-14T14-46-55-990Z.yml
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e6]:
|
||||
- heading "Time Tracking Dashboard" [level=1] [ref=e7]
|
||||
- generic [ref=e8]:
|
||||
- generic [ref=e9]:
|
||||
- generic [ref=e10]: 0m
|
||||
- generic [ref=e11]: Time Worked Today
|
||||
- generic [ref=e12]:
|
||||
- generic [ref=e13]: 6h 51m
|
||||
- generic [ref=e14]: Time Worked This Week
|
||||
- generic [ref=e15]:
|
||||
- generic [ref=e16]: 6h 51m
|
||||
- generic [ref=e17]: Total Time Tracked
|
||||
- generic [ref=e18]:
|
||||
- generic [ref=e19]: "6"
|
||||
- generic [ref=e20]: Tracked Items
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- heading "Time Worked - Last 2 Weeks" [level=2] [ref=e23]
|
||||
- generic [ref=e25]:
|
||||
- img [ref=e26]:
|
||||
- generic [ref=e29]:
|
||||
- generic "4.5" [ref=e30]
|
||||
- generic "4.0" [ref=e31]
|
||||
- generic "3.5" [ref=e32]
|
||||
- generic "3.0" [ref=e33]
|
||||
- generic "2.5" [ref=e34]
|
||||
- generic "2.0" [ref=e35]
|
||||
- generic "1.5" [ref=e36]
|
||||
- generic "1.0" [ref=e37]
|
||||
- generic "0.5" [ref=e38]
|
||||
- generic "0" [ref=e39]
|
||||
- generic [ref=e64]:
|
||||
- generic "06/02" [ref=e65]
|
||||
- generic "06/03" [ref=e66]
|
||||
- generic "06/04" [ref=e67]
|
||||
- generic "06/05" [ref=e68]
|
||||
- generic "06/06" [ref=e69]
|
||||
- generic "06/07" [ref=e70]
|
||||
- generic "06/08" [ref=e71]
|
||||
- generic "06/09" [ref=e72]
|
||||
- generic "06/10" [ref=e73]
|
||||
- generic "06/11" [ref=e74]
|
||||
- generic "06/12" [ref=e75]
|
||||
- generic "06/13" [ref=e76]
|
||||
- generic "06/14" [ref=e77]
|
||||
- generic [ref=e78]:
|
||||
- generic "Zoom In" [ref=e79] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Zoom Out" [ref=e80] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Selection Zoom" [ref=e81] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Panning" [ref=e82] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Reset Zoom" [ref=e83] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Menu" [ref=e84] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e85]:
|
||||
- heading "Time Worked - Last Month" [level=2] [ref=e86]
|
||||
- generic [ref=e88]:
|
||||
- img [ref=e89]:
|
||||
- generic [ref=e92]:
|
||||
- generic "4.5" [ref=e93]
|
||||
- generic "4.0" [ref=e94]
|
||||
- generic "3.5" [ref=e95]
|
||||
- generic "3.0" [ref=e96]
|
||||
- generic "2.5" [ref=e97]
|
||||
- generic "2.0" [ref=e98]
|
||||
- generic "1.5" [ref=e99]
|
||||
- generic "1.0" [ref=e100]
|
||||
- generic "0.5" [ref=e101]
|
||||
- generic "0" [ref=e102]
|
||||
- generic [ref=e143]:
|
||||
- generic "05/17" [ref=e144]
|
||||
- generic "05/19" [ref=e145]
|
||||
- generic "05/21" [ref=e146]
|
||||
- generic "05/23" [ref=e147]
|
||||
- generic "05/25" [ref=e148]
|
||||
- generic "05/27" [ref=e149]
|
||||
- generic "05/29" [ref=e150]
|
||||
- generic "05/31" [ref=e151]
|
||||
- generic "06/02" [ref=e152]
|
||||
- generic "06/04" [ref=e153]
|
||||
- generic "06/06" [ref=e154]
|
||||
- generic "06/08" [ref=e155]
|
||||
- generic "06/10" [ref=e156]
|
||||
- generic "06/12" [ref=e157]
|
||||
- generic "06/14" [ref=e158]
|
||||
- generic [ref=e159]:
|
||||
- generic "Zoom In" [ref=e160] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Zoom Out" [ref=e161] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Selection Zoom" [ref=e162] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Panning" [ref=e163] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Reset Zoom" [ref=e164] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Menu" [ref=e165] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]:
|
||||
- heading "Time by Issue" [level=2] [ref=e168]
|
||||
- generic [ref=e170]:
|
||||
- img [ref=e171]:
|
||||
- generic [ref=e173]:
|
||||
- generic [ref=e191]:
|
||||
- generic:
|
||||
- generic: 0.1h
|
||||
- generic: 0.0h
|
||||
- generic: 4.2h
|
||||
- generic: 1.3h
|
||||
- generic [ref=e198]:
|
||||
- generic "#11 - Inspect" [ref=e199]
|
||||
- generic "#..." [ref=e200]
|
||||
- generic "#3..." [ref=e201]
|
||||
- generic "#41..." [ref=e202]
|
||||
- generic [ref=e204]:
|
||||
- generic "5" [ref=e205]
|
||||
- generic "4" [ref=e206]
|
||||
- generic "4" [ref=e207]
|
||||
- generic "3" [ref=e208]
|
||||
- generic "3" [ref=e209]
|
||||
- generic "2" [ref=e210]
|
||||
- generic "2" [ref=e211]
|
||||
- generic "1" [ref=e212]
|
||||
- generic "1" [ref=e213]
|
||||
- generic "0" [ref=e214]
|
||||
- generic [ref=e215]:
|
||||
- generic "Menu" [ref=e216] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e217]:
|
||||
- heading "Time by Pull Request" [level=2] [ref=e218]
|
||||
- generic [ref=e220]:
|
||||
- img [ref=e221]:
|
||||
- generic [ref=e223]:
|
||||
- generic [ref=e242]:
|
||||
- generic:
|
||||
- generic: 0.4h
|
||||
- generic: 0.9h
|
||||
- generic [ref=e247]:
|
||||
- 'generic "#45 - PR für #34" [ref=e248]'
|
||||
- generic "#..." [ref=e249]
|
||||
- generic [ref=e251]:
|
||||
- generic "1.0" [ref=e252]
|
||||
- generic "0.9" [ref=e253]
|
||||
- generic "0.8" [ref=e254]
|
||||
- generic "0.7" [ref=e255]
|
||||
- generic "0.6" [ref=e256]
|
||||
- generic "0.5" [ref=e257]
|
||||
- generic "0.4" [ref=e258]
|
||||
- generic "0.3" [ref=e259]
|
||||
- generic "0.2" [ref=e260]
|
||||
- generic "0.1" [ref=e261]
|
||||
- generic "0.0" [ref=e262]
|
||||
- generic [ref=e263]:
|
||||
- generic "Menu" [ref=e264] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e265]:
|
||||
- heading "Time Distribution - Issues vs Pull Requests" [level=2] [ref=e266]
|
||||
- img [ref=e269]:
|
||||
- generic [ref=e271]:
|
||||
- generic [ref=e274] [cursor=pointer]: Issues
|
||||
- generic [ref=e277] [cursor=pointer]: Pull Requests
|
||||
- generic [ref=e282]:
|
||||
- generic: 81.2h
|
||||
- generic: 18.8h
|
||||
- img
|
||||
BIN
.playwright-mcp/page-2026-06-14T14-47-00-181Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-47-00-181Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
.playwright-mcp/page-2026-06-14T14-48-32-956Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-48-32-956Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
.playwright-mcp/page-2026-06-14T14-48-41-291Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T14-48-41-291Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
3
.playwright-mcp/page-2026-06-14T15-03-10-783Z.yml
Normal file
3
.playwright-mcp/page-2026-06-14T15-03-10-783Z.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
- generic [active] [ref=e1]:
|
||||
- paragraph [ref=e5]: Loading time tracking data...
|
||||
- img
|
||||
187
.playwright-mcp/page-2026-06-14T15-03-19-174Z.yml
Normal file
187
.playwright-mcp/page-2026-06-14T15-03-19-174Z.yml
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e6]:
|
||||
- heading "Time Tracking Dashboard" [level=1] [ref=e7]
|
||||
- generic [ref=e8]:
|
||||
- generic [ref=e9]:
|
||||
- generic [ref=e10]: 0m
|
||||
- generic [ref=e11]: Time Worked Today
|
||||
- generic [ref=e12]:
|
||||
- generic [ref=e13]: 6h 51m
|
||||
- generic [ref=e14]: Time Worked This Week
|
||||
- generic [ref=e15]:
|
||||
- generic [ref=e16]: 6h 51m
|
||||
- generic [ref=e17]: Time Worked This Month
|
||||
- generic [ref=e18]:
|
||||
- generic [ref=e19]: "6"
|
||||
- generic [ref=e20]: Tracked Items
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- heading "Time Worked - Last 2 Weeks" [level=2] [ref=e23]
|
||||
- generic [ref=e25]:
|
||||
- img [ref=e26]:
|
||||
- generic [ref=e29]:
|
||||
- generic "4.5" [ref=e30]
|
||||
- generic "4.0" [ref=e31]
|
||||
- generic "3.5" [ref=e32]
|
||||
- generic "3.0" [ref=e33]
|
||||
- generic "2.5" [ref=e34]
|
||||
- generic "2.0" [ref=e35]
|
||||
- generic "1.5" [ref=e36]
|
||||
- generic "1.0" [ref=e37]
|
||||
- generic "0.5" [ref=e38]
|
||||
- generic "0" [ref=e39]
|
||||
- generic [ref=e64]:
|
||||
- generic "06/02" [ref=e65]
|
||||
- generic "06/03" [ref=e66]
|
||||
- generic "06/04" [ref=e67]
|
||||
- generic "06/05" [ref=e68]
|
||||
- generic "06/06" [ref=e69]
|
||||
- generic "06/07" [ref=e70]
|
||||
- generic "06/08" [ref=e71]
|
||||
- generic "06/09" [ref=e72]
|
||||
- generic "06/10" [ref=e73]
|
||||
- generic "06/11" [ref=e74]
|
||||
- generic "06/12" [ref=e75]
|
||||
- generic "06/13" [ref=e76]
|
||||
- generic "06/14" [ref=e77]
|
||||
- generic [ref=e78]:
|
||||
- generic "Zoom In" [ref=e79] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Zoom Out" [ref=e80] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Selection Zoom" [ref=e81] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Panning" [ref=e82] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Reset Zoom" [ref=e83] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Menu" [ref=e84] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e85]:
|
||||
- heading "Time Worked - Last Month" [level=2] [ref=e86]
|
||||
- generic [ref=e88]:
|
||||
- img [ref=e89]:
|
||||
- generic [ref=e92]:
|
||||
- generic "4.5" [ref=e93]
|
||||
- generic "4.0" [ref=e94]
|
||||
- generic "3.5" [ref=e95]
|
||||
- generic "3.0" [ref=e96]
|
||||
- generic "2.5" [ref=e97]
|
||||
- generic "2.0" [ref=e98]
|
||||
- generic "1.5" [ref=e99]
|
||||
- generic "1.0" [ref=e100]
|
||||
- generic "0.5" [ref=e101]
|
||||
- generic "0" [ref=e102]
|
||||
- generic [ref=e143]:
|
||||
- generic "05/17" [ref=e144]
|
||||
- generic "05/19" [ref=e145]
|
||||
- generic "05/21" [ref=e146]
|
||||
- generic "05/23" [ref=e147]
|
||||
- generic "05/25" [ref=e148]
|
||||
- generic "05/27" [ref=e149]
|
||||
- generic "05/29" [ref=e150]
|
||||
- generic "05/31" [ref=e151]
|
||||
- generic "06/02" [ref=e152]
|
||||
- generic "06/04" [ref=e153]
|
||||
- generic "06/06" [ref=e154]
|
||||
- generic "06/08" [ref=e155]
|
||||
- generic "06/10" [ref=e156]
|
||||
- generic "06/12" [ref=e157]
|
||||
- generic "06/14" [ref=e158]
|
||||
- generic [ref=e159]:
|
||||
- generic "Zoom In" [ref=e160] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Zoom Out" [ref=e161] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Selection Zoom" [ref=e162] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Panning" [ref=e163] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Reset Zoom" [ref=e164] [cursor=pointer]:
|
||||
- img
|
||||
- generic "Menu" [ref=e165] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]:
|
||||
- heading "Time by Issue" [level=2] [ref=e168]
|
||||
- generic [ref=e170]:
|
||||
- img [ref=e171]:
|
||||
- generic [ref=e173]:
|
||||
- generic [ref=e191]:
|
||||
- generic:
|
||||
- generic: 0.1h
|
||||
- generic: 0.0h
|
||||
- generic: 4.2h
|
||||
- generic: 1.3h
|
||||
- generic [ref=e198]:
|
||||
- generic "#11 - Inspect" [ref=e199]
|
||||
- generic "#..." [ref=e200]
|
||||
- generic "#3..." [ref=e201]
|
||||
- generic "#41..." [ref=e202]
|
||||
- generic [ref=e204]:
|
||||
- generic "5" [ref=e205]
|
||||
- generic "4" [ref=e206]
|
||||
- generic "4" [ref=e207]
|
||||
- generic "3" [ref=e208]
|
||||
- generic "3" [ref=e209]
|
||||
- generic "2" [ref=e210]
|
||||
- generic "2" [ref=e211]
|
||||
- generic "1" [ref=e212]
|
||||
- generic "1" [ref=e213]
|
||||
- generic "0" [ref=e214]
|
||||
- generic [ref=e215]:
|
||||
- generic "Menu" [ref=e216] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e217]:
|
||||
- heading "Time by Pull Request" [level=2] [ref=e218]
|
||||
- generic [ref=e220]:
|
||||
- img [ref=e221]:
|
||||
- generic [ref=e223]:
|
||||
- generic [ref=e242]:
|
||||
- generic:
|
||||
- generic: 0.4h
|
||||
- generic: 0.9h
|
||||
- generic [ref=e247]:
|
||||
- 'generic "#45 - PR für #34" [ref=e248]'
|
||||
- generic "#..." [ref=e249]
|
||||
- generic [ref=e251]:
|
||||
- generic "1.0" [ref=e252]
|
||||
- generic "0.9" [ref=e253]
|
||||
- generic "0.8" [ref=e254]
|
||||
- generic "0.7" [ref=e255]
|
||||
- generic "0.6" [ref=e256]
|
||||
- generic "0.5" [ref=e257]
|
||||
- generic "0.4" [ref=e258]
|
||||
- generic "0.3" [ref=e259]
|
||||
- generic "0.2" [ref=e260]
|
||||
- generic "0.1" [ref=e261]
|
||||
- generic "0.0" [ref=e262]
|
||||
- generic [ref=e263]:
|
||||
- generic "Menu" [ref=e264] [cursor=pointer]:
|
||||
- img
|
||||
- generic:
|
||||
- generic "Download SVG"
|
||||
- generic "Download PNG"
|
||||
- generic "Download CSV"
|
||||
- generic [ref=e265]:
|
||||
- heading "Time Distribution - Issues vs Pull Requests" [level=2] [ref=e266]
|
||||
- img [ref=e269]:
|
||||
- generic [ref=e271]:
|
||||
- generic [ref=e274] [cursor=pointer]: Issues
|
||||
- generic [ref=e277] [cursor=pointer]: Pull Requests
|
||||
- generic [ref=e282]:
|
||||
- generic: 81.2h
|
||||
- generic: 18.8h
|
||||
- img
|
||||
BIN
.playwright-mcp/page-2026-06-14T15-03-23-572Z.png
Normal file
BIN
.playwright-mcp/page-2026-06-14T15-03-23-572Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
|
|
@ -1,5 +1,10 @@
|
|||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({});
|
||||
export default defineConfig({
|
||||
vite: {
|
||||
define: {
|
||||
'import.meta.env.ACCESS_TOKEN': JSON.stringify(process.env.ACCESS_TOKEN || ''),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
21
bun.lock
21
bun.lock
|
|
@ -4,6 +4,7 @@
|
|||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"apexcharts": "3.49.2",
|
||||
"astro": "^6.4.6",
|
||||
},
|
||||
},
|
||||
|
|
@ -225,8 +226,12 @@
|
|||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
|
||||
|
||||
"@yr/monotone-cubic-spline": ["@yr/monotone-cubic-spline@1.0.3", "", {}, "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"apexcharts": ["apexcharts@3.49.2", "", { "dependencies": { "@yr/monotone-cubic-spline": "^1.0.3", "svg.draggable.js": "^2.2.2", "svg.easing.js": "^2.0.0", "svg.filter.js": "^2.0.2", "svg.pathmorphing.js": "^0.1.3", "svg.resize.js": "^1.4.3", "svg.select.js": "^3.0.1" } }, "sha512-vBB8KgwfD9rSObA7s4kY2rU6DeaN67gTR3JN7r32ztgKVf8lKkdFQ6iUhk6oIHrV7W8PoHhr5EwKymn0z5Fz6A=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
|
@ -583,6 +588,20 @@
|
|||
|
||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"svg.draggable.js": ["svg.draggable.js@2.2.2", "", { "dependencies": { "svg.js": "^2.0.1" } }, "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw=="],
|
||||
|
||||
"svg.easing.js": ["svg.easing.js@2.0.0", "", { "dependencies": { "svg.js": ">=2.3.x" } }, "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA=="],
|
||||
|
||||
"svg.filter.js": ["svg.filter.js@2.0.2", "", { "dependencies": { "svg.js": "^2.2.5" } }, "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw=="],
|
||||
|
||||
"svg.js": ["svg.js@2.7.1", "", {}, "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="],
|
||||
|
||||
"svg.pathmorphing.js": ["svg.pathmorphing.js@0.1.3", "", { "dependencies": { "svg.js": "^2.4.0" } }, "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww=="],
|
||||
|
||||
"svg.resize.js": ["svg.resize.js@1.4.3", "", { "dependencies": { "svg.js": "^2.6.5", "svg.select.js": "^2.1.2" } }, "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw=="],
|
||||
|
||||
"svg.select.js": ["svg.select.js@3.0.1", "", { "dependencies": { "svg.js": "^2.6.5" } }, "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw=="],
|
||||
|
||||
"svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
|
||||
|
||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||
|
|
@ -661,6 +680,8 @@
|
|||
|
||||
"is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"svg.resize.js/svg.select.js": ["svg.select.js@2.1.2", "", { "dependencies": { "svg.js": "^2.2.5" } }, "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ=="],
|
||||
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"apexcharts": "3.49.2",
|
||||
"astro": "^6.4.6"
|
||||
}
|
||||
}
|
||||
|
|
@ -6,18 +6,29 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Astro Basics</title>
|
||||
<title>Time Tracking Dashboard</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html, body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0d0d1a;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
#app {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,364 @@
|
|||
---
|
||||
import Welcome from '../components/Welcome.astro';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
|
||||
// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
|
||||
// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
|
||||
const TOKEN = import.meta.env.PUBLIC_ACCESS_TOKEN || '';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Welcome />
|
||||
</Layout>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Time Tracking Dashboard</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/apexcharts@3.49.2/dist/apexcharts.css" />
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0; width: 100%; height: 100%;
|
||||
background: #0d0d1a; color: #e0e0e0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
#app { max-width: 1400px; margin: 0 auto; padding: 24px; }
|
||||
.dashboard { animation: fadeIn 0.5s ease-in; }
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
.title { font-size: 28px; font-weight: 600; color: #c4a7ff; margin-bottom: 24px; }
|
||||
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
|
||||
.stat-card { background: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.2); border-radius: 12px; padding: 20px; text-align: center; backdrop-filter: blur(10px); }
|
||||
.stat-value { font-size: 32px; font-weight: 700; color: #c4a7ff; margin-bottom: 4px; }
|
||||
.stat-label { font-size: 13px; color: #a0a0b8; text-transform: uppercase; letter-spacing: 0.5px; }
|
||||
.charts-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); gap: 16px; margin-bottom: 16px; }
|
||||
.chart-card { background: rgba(139, 92, 246, 0.08); border: 1px solid rgba(139, 92, 246, 0.15); border-radius: 12px; padding: 20px; backdrop-filter: blur(10px); }
|
||||
.chart-card.full-width { grid-column: 1 / -1; }
|
||||
.chart-title { font-size: 16px; font-weight: 500; color: #c4a7ff; margin-bottom: 16px; }
|
||||
.chart { width: 100%; min-height: 350px; }
|
||||
.loading { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 60vh; }
|
||||
.spinner { width: 48px; height: 48px; border: 4px solid rgba(139, 92, 246, 0.2); border-top-color: #c4a7ff; border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 16px; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.error { text-align: center; padding: 40px; }
|
||||
.error h2 { color: #ff6b6b; margin-bottom: 12px; }
|
||||
.error p { color: #a0a0b8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" data-token={TOKEN}>
|
||||
<div id="loading" class="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading time tracking data...</p>
|
||||
</div>
|
||||
<div id="dashboard" class="dashboard" style="display: none;">
|
||||
<h1 class="title">Time Tracking Dashboard</h1>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="time-today">-</div>
|
||||
<div class="stat-label">Time Worked Today</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="time-week">-</div>
|
||||
<div class="stat-label">Time Worked This Week</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="time-month">-</div>
|
||||
<div class="stat-label">Time Worked This Month</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="tracked-items">-</div>
|
||||
<div class="stat-label">Tracked Items</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<h2 class="chart-title">Time Worked - Last 2 Weeks</h2>
|
||||
<div id="chart-weekly" class="chart"></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<h2 class="chart-title">Time Worked - Last Month</h2>
|
||||
<div id="chart-monthly" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-grid">
|
||||
<div class="chart-card">
|
||||
<h2 class="chart-title">Time by Issue</h2>
|
||||
<div id="chart-issues" class="chart"></div>
|
||||
</div>
|
||||
<div class="chart-card">
|
||||
<h2 class="chart-title">Time by Pull Request</h2>
|
||||
<div id="chart-prs" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-card full-width">
|
||||
<h2 class="chart-title">Time Distribution - Issues vs Pull Requests</h2>
|
||||
<div id="chart-distribution" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="error" class="error" style="display: none;">
|
||||
<h2>Error Loading Data</h2>
|
||||
<p id="error-message">Failed to fetch time tracking data.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts@3.49.2/dist/apexcharts.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
var TOKEN = document.getElementById('app').getAttribute('data-token') || '';
|
||||
var API_BASE = 'https://bitfreedom.net/code/api/v1';
|
||||
|
||||
function formatDuration(seconds) {
|
||||
var h = Math.floor(seconds / 3600);
|
||||
var m = Math.floor((seconds % 3600) / 60);
|
||||
if (h > 0) return h + 'h ' + m + 'm';
|
||||
return m + 'm';
|
||||
}
|
||||
|
||||
function formatDurationDetailed(seconds) {
|
||||
var days = Math.floor(seconds / 86400);
|
||||
var h = Math.floor((seconds % 86400) / 3600);
|
||||
var m = Math.floor((seconds % 3600) / 60);
|
||||
if (days > 0) return days + 'd ' + h + 'h ' + m + 'm';
|
||||
if (h > 0) return h + 'h ' + m + 'm';
|
||||
return m + 'm';
|
||||
}
|
||||
|
||||
async function fetchTimes() {
|
||||
var since = getSinceDate();
|
||||
var url = API_BASE + '/user/times?access_token=' + TOKEN + '&since=' + since;
|
||||
var resp = await fetch(url);
|
||||
if (!resp.ok) throw new Error('API error: ' + resp.status);
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
function getTodayStart() {
|
||||
var now = new Date();
|
||||
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
||||
}
|
||||
|
||||
function getWeekStart() {
|
||||
var now = new Date();
|
||||
var day = now.getDay();
|
||||
var diff = now.getDate() - day + (day === 0 ? -6 : 1);
|
||||
var monday = new Date(now.setDate(diff));
|
||||
monday.setHours(0, 0, 0, 0);
|
||||
return monday.getTime();
|
||||
}
|
||||
|
||||
function getLast30Days() {
|
||||
var now = new Date();
|
||||
var monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
return { now: now.getTime(), monthAgo: monthAgo.getTime() };
|
||||
}
|
||||
|
||||
function getMonthStart() {
|
||||
var now = new Date();
|
||||
var monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
monthStart.setHours(0, 0, 0, 0);
|
||||
return monthStart.toISOString();
|
||||
}
|
||||
|
||||
function getSinceDate() {
|
||||
var now = new Date();
|
||||
var day = now.getDay();
|
||||
var diff = now.getDate() - day + (day === 0 ? -6 : 1);
|
||||
var monday = new Date(now.setDate(diff));
|
||||
monday.setHours(0, 0, 0, 0);
|
||||
return monday.toISOString();
|
||||
}
|
||||
|
||||
function getDayKey(dateStr) {
|
||||
var d = new Date(dateStr);
|
||||
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
function getDayLabel(dateStr) {
|
||||
var d = new Date(dateStr);
|
||||
return String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
function truncateTitle(t, max) {
|
||||
max = max || 40;
|
||||
return t.length > max ? t.substring(0, max) + '...' : t;
|
||||
}
|
||||
|
||||
function renderCharts(timesData) {
|
||||
var todayTotal = 0;
|
||||
var weekTotal = 0;
|
||||
var monthTotal = 0;
|
||||
var todayStart = getTodayStart();
|
||||
var weekStart = getWeekStart();
|
||||
var monthAgo = getLast30Days().monthAgo;
|
||||
var monthStart = new Date(new Date().getFullYear(), new Date().getMonth(), 1).getTime();
|
||||
|
||||
var dayMap = {};
|
||||
var issuesData = {};
|
||||
var prsData = {};
|
||||
var issuesTotal = 0;
|
||||
var prsTotal = 0;
|
||||
var trackedItems = new Set();
|
||||
|
||||
for (var i = 0; i < timesData.length; i++) {
|
||||
var entry = timesData[i];
|
||||
var timeSec = entry.time || 0;
|
||||
var created = new Date(entry.created).getTime();
|
||||
|
||||
if (created >= todayStart) todayTotal += timeSec;
|
||||
if (created >= weekStart) weekTotal += timeSec;
|
||||
if (created >= monthStart) monthTotal += timeSec;
|
||||
|
||||
var dayKey = getDayKey(entry.created);
|
||||
if (!dayMap[dayKey]) dayMap[dayKey] = 0;
|
||||
dayMap[dayKey] += timeSec;
|
||||
|
||||
if (entry.issue) {
|
||||
var itemId = entry.issue.pull_request ? 'pr_' + entry.issue.id : 'issue_' + entry.issue.id;
|
||||
trackedItems.add(itemId);
|
||||
var title = '#' + entry.issue.number + ' - ' + (entry.issue.title || 'Unknown');
|
||||
if (entry.issue.pull_request) {
|
||||
prsData[title] = (prsData[title] || 0) + timeSec;
|
||||
prsTotal += timeSec;
|
||||
} else {
|
||||
issuesData[title] = (issuesData[title] || 0) + timeSec;
|
||||
issuesTotal += timeSec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('time-today').textContent = formatDurationDetailed(todayTotal);
|
||||
document.getElementById('time-week').textContent = formatDurationDetailed(weekTotal);
|
||||
document.getElementById('time-month').textContent = formatDurationDetailed(monthTotal);
|
||||
document.getElementById('tracked-items').textContent = trackedItems.size;
|
||||
|
||||
var chartBase = {
|
||||
chart: { type: 'area', height: 350, background: 'transparent', toolbar: { show: true }, zoom: { enabled: true } },
|
||||
theme: { mode: 'dark' },
|
||||
stroke: { curve: 'smooth', width: 2 },
|
||||
dataLabels: { enabled: false },
|
||||
grid: { borderColor: 'rgba(139, 92, 246, 0.15)' },
|
||||
tooltip: { formatter: function(val) { return formatDuration(val * 3600); } }
|
||||
};
|
||||
|
||||
// Weekly chart (14 days)
|
||||
var weeklyData = [];
|
||||
var weeklyLabels = [];
|
||||
var now = new Date();
|
||||
for (var i = 13; i >= 0; i--) {
|
||||
var d = new Date(now);
|
||||
d.setDate(d.getDate() - i);
|
||||
var key = getDayKey(d.toISOString());
|
||||
weeklyLabels.push(String(d.getMonth() + 1).padStart(2, '0') + '/' + String(d.getDate()).padStart(2, '0'));
|
||||
weeklyData.push((dayMap[key] || 0) / 3600);
|
||||
}
|
||||
new ApexCharts(document.querySelector("#chart-weekly"), {
|
||||
chart: chartBase.chart,
|
||||
theme: chartBase.theme,
|
||||
xaxis: { type: 'category', labels: { formatter: function(val) { return weeklyLabels[parseInt(val) || 0]; }, style: { colors: '#a0a0b8', fontSize: '11px' } }, axisTicks: { show: true }, axisBorder: { show: true, color: 'rgba(139, 92, 246, 0.2)' } },
|
||||
yaxis: { labels: { formatter: function(val) { return val ? val.toFixed(1) : '0'; } } },
|
||||
series: [{ name: 'Hours', data: weeklyData }],
|
||||
stroke: chartBase.stroke,
|
||||
dataLabels: chartBase.dataLabels,
|
||||
grid: chartBase.grid,
|
||||
tooltip: chartBase.tooltip,
|
||||
colors: ['#7c3aed'],
|
||||
fill: { type: 'gradient', gradient: { shade: 'dark', gradientToColors: ['#8b5cf6'], shadeIntensity: 1, type: 'horizontal', opacityFrom: 0.6, opacityTo: 0.2, stops: [0, 100] } }
|
||||
}).render();
|
||||
|
||||
// Monthly chart (30 days)
|
||||
var monthlyData = [];
|
||||
var monthlyLabels = [];
|
||||
for (var i = 29; i >= 0; i--) {
|
||||
var d = new Date(now);
|
||||
d.setDate(d.getDate() - i);
|
||||
var key = getDayKey(d.toISOString());
|
||||
monthlyLabels.push(getDayLabel(d.toISOString()));
|
||||
monthlyData.push((dayMap[key] || 0) / 3600);
|
||||
}
|
||||
new ApexCharts(document.querySelector("#chart-monthly"), {
|
||||
chart: chartBase.chart,
|
||||
theme: chartBase.theme,
|
||||
xaxis: { type: 'category', labels: { formatter: function(val) { return monthlyLabels[parseInt(val) || 0]; }, style: { colors: '#a0a0b8', fontSize: '11px' } }, axisTicks: { show: true }, axisBorder: { show: true, color: 'rgba(139, 92, 246, 0.2)' } },
|
||||
yaxis: { labels: { formatter: function(val) { return val ? val.toFixed(1) : '0'; } } },
|
||||
series: [{ name: 'Hours', data: monthlyData }],
|
||||
stroke: chartBase.stroke,
|
||||
dataLabels: chartBase.dataLabels,
|
||||
grid: chartBase.grid,
|
||||
tooltip: chartBase.tooltip,
|
||||
colors: ['#7c3aed'],
|
||||
fill: { type: 'gradient', gradient: { shade: 'dark', gradientToColors: ['#a78bfa'], shadeIntensity: 1, type: 'horizontal', opacityFrom: 0.6, opacityTo: 0.2, stops: [0, 100] } }
|
||||
}).render();
|
||||
|
||||
// Issues chart - horizontal bar
|
||||
var issueTitles = Object.keys(issuesData);
|
||||
var issueHours = issueTitles.map(function(t) { return parseFloat((issuesData[t] / 3600).toFixed(2)); });
|
||||
new ApexCharts(document.querySelector("#chart-issues"), {
|
||||
chart: { type: 'bar', height: 350, background: 'transparent', toolbar: { show: true } },
|
||||
theme: { mode: 'dark' },
|
||||
series: [{ name: 'Hours', data: issueHours }],
|
||||
plotOptions: { bar: { horizontal: true, borderRadius: 4 } },
|
||||
xaxis: { categories: issueTitles.map(truncateTitle), labels: { style: { colors: '#a0a0b8', fontSize: '11px' } } },
|
||||
yaxis: { labels: { style: { colors: '#a0a0b8' } } },
|
||||
colors: ['#22c55e'],
|
||||
fill: { type: 'solid', opacity: 0.8 },
|
||||
grid: { borderColor: 'rgba(139, 92, 246, 0.15)' },
|
||||
dataLabels: { enabled: true, formatter: function(val) { return val.toFixed(1) + 'h'; }, style: { colors: ['#fff'] } },
|
||||
tooltip: { formatter: function(val) { return formatDuration(val * 3600); } },
|
||||
legend: { show: false }
|
||||
}).render();
|
||||
|
||||
// PRs chart - horizontal bar
|
||||
var prTitles = Object.keys(prsData);
|
||||
var prHours = prTitles.map(function(t) { return parseFloat((prsData[t] / 3600).toFixed(2)); });
|
||||
new ApexCharts(document.querySelector("#chart-prs"), {
|
||||
chart: { type: 'bar', height: 350, background: 'transparent', toolbar: { show: true } },
|
||||
theme: { mode: 'dark' },
|
||||
series: [{ name: 'Hours', data: prHours }],
|
||||
plotOptions: { bar: { horizontal: true, borderRadius: 4 } },
|
||||
xaxis: { categories: prTitles.map(truncateTitle), labels: { style: { colors: '#a0a0b8', fontSize: '11px' } } },
|
||||
yaxis: { labels: { style: { colors: '#a0a0b8' } } },
|
||||
colors: ['#3b82f6'],
|
||||
fill: { type: 'solid', opacity: 0.8 },
|
||||
grid: { borderColor: 'rgba(139, 92, 246, 0.15)' },
|
||||
dataLabels: { enabled: true, formatter: function(val) { return val.toFixed(1) + 'h'; }, style: { colors: ['#fff'] } },
|
||||
tooltip: { formatter: function(val) { return formatDuration(val * 3600); } },
|
||||
legend: { show: false }
|
||||
}).render();
|
||||
|
||||
// Distribution chart - donut
|
||||
new ApexCharts(document.querySelector("#chart-distribution"), {
|
||||
chart: { type: 'donut', height: 400, background: 'transparent' },
|
||||
theme: { mode: 'dark' },
|
||||
series: [parseFloat((issuesTotal / 3600).toFixed(1)), parseFloat((prsTotal / 3600).toFixed(1))],
|
||||
labels: ['Issues', 'Pull Requests'],
|
||||
colors: ['#22c55e', '#3b82f6'],
|
||||
fill: { type: 'solid', opacity: 0.9 },
|
||||
legend: { position: 'bottom', labels: { colors: '#a0a0b8' } },
|
||||
dataLabels: { formatter: function(val) { return val.toFixed(1) + 'h'; } },
|
||||
stroke: { width: 2, colors: ['#0d0d1a'] },
|
||||
tooltip: { formatter: function(val) { return formatDuration(val / 100 * (issuesTotal + prsTotal)); } }
|
||||
}).render();
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('error').style.display = 'flex';
|
||||
document.getElementById('error-message').textContent = msg;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
var timesData = await fetchTimes();
|
||||
renderCharts(timesData);
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('dashboard').style.display = 'block';
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
showError(err.message || 'Failed to fetch data');
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue