@zumito-team/analytics-module 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/events/discord/MessageCreate.js +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +34 -4
  4. package/dist/models/ChannelMessageStats.d.ts +8 -0
  5. package/dist/models/ChannelMessageStats.js +31 -0
  6. package/dist/routes/AdminAnalytics.d.ts +1 -3
  7. package/dist/routes/AdminAnalytics.js +2 -23
  8. package/dist/routes/AdminAnalyticsCommands.d.ts +7 -0
  9. package/dist/routes/AdminAnalyticsCommands.js +34 -0
  10. package/dist/routes/AdminAnalyticsGrowth.d.ts +7 -0
  11. package/dist/routes/AdminAnalyticsGrowth.js +34 -0
  12. package/dist/routes/AdminAnalyticsMessages.d.ts +7 -0
  13. package/dist/routes/AdminAnalyticsMessages.js +32 -0
  14. package/dist/routes/UserPanelAnalytics.js +1 -17
  15. package/dist/routes/UserPanelAnalyticsCommands.d.ts +8 -0
  16. package/dist/routes/UserPanelAnalyticsCommands.js +48 -0
  17. package/dist/routes/UserPanelAnalyticsMessages.d.ts +8 -0
  18. package/dist/routes/UserPanelAnalyticsMessages.js +52 -0
  19. package/dist/routes/UserPanelAnalyticsVoice.d.ts +8 -0
  20. package/dist/routes/UserPanelAnalyticsVoice.js +53 -0
  21. package/dist/services/AnalyticsCollector.d.ts +5 -1
  22. package/dist/services/AnalyticsCollector.js +61 -3
  23. package/dist/views/admin-analytics-commands.ejs +55 -0
  24. package/dist/views/admin-analytics-growth.ejs +46 -0
  25. package/dist/views/admin-analytics-messages.ejs +30 -0
  26. package/dist/views/admin-analytics.ejs +38 -132
  27. package/dist/views/user-analytics-commands.ejs +55 -0
  28. package/dist/views/user-analytics-messages.ejs +47 -0
  29. package/dist/views/user-analytics-voice.ejs +47 -0
  30. package/dist/views/user-analytics.ejs +25 -166
  31. package/package.json +1 -1
@@ -0,0 +1,55 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground">Comandos</h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=7" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">7d</a>
6
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">30d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
11
+ <div class="card">
12
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Comandos por Dia</h3>
13
+ <canvas id="chartCommands" width="400" height="200"></canvas>
14
+ </div>
15
+ <div class="card">
16
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Top Comandos</h3>
17
+ <canvas id="chartTop" width="400" height="200"></canvas>
18
+ </div>
19
+ </div>
20
+ <% if (typeof slowestCommands !== 'undefined' && slowestCommands.length > 0) { %>
21
+ <div class="card mt-6">
22
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Comandos Mas Lentos (ms)</h3>
23
+ <canvas id="chartSlow" width="700" height="250"></canvas>
24
+ </div>
25
+ <% } %>
26
+ </div>
27
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
28
+ <script>
29
+ new Chart(document.getElementById('chartCommands'), {
30
+ type: 'bar',
31
+ data: {
32
+ labels: <%- JSON.stringify(commandsPerDay.map(v => v.date)) %>,
33
+ datasets: [{ label: 'Comandos', data: <%- JSON.stringify(commandsPerDay.map(v => v.count)) %>, backgroundColor: '#5865f2', borderRadius: 4 }]
34
+ },
35
+ options: { responsive: true, plugins: { legend: { display: false } } }
36
+ });
37
+ new Chart(document.getElementById('chartTop'), {
38
+ type: 'bar',
39
+ data: {
40
+ labels: <%- JSON.stringify(topCommands.map(v => v.command_name)) %>,
41
+ datasets: [{ label: 'Usos', data: <%- JSON.stringify(topCommands.map(v => v.usage_count)) %>, backgroundColor: topCommands.map((_, i) => 'rgba(88,101,242,' + Math.max(0.3, 1 - i * 0.08) + ')'), borderRadius: 4 }]
42
+ },
43
+ options: { indexAxis: 'y', responsive: true, plugins: { legend: { display: false } } }
44
+ });
45
+ <% if (typeof slowestCommands !== 'undefined' && slowestCommands.length > 0) { %>
46
+ new Chart(document.getElementById('chartSlow'), {
47
+ type: 'bar',
48
+ data: {
49
+ labels: <%- JSON.stringify(slowestCommands.map(v => v.command_name)) %>,
50
+ datasets: [{ label: 'ms', data: <%- JSON.stringify(slowestCommands.map(v => v.avgExecutionTimeMs)) %>, backgroundColor: '#fee75c', borderRadius: 4 }]
51
+ },
52
+ options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { title: { display: true, text: 'ms' } } } }
53
+ });
54
+ <% } %>
55
+ </script>
@@ -0,0 +1,46 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground">Crecimiento</h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">30d</a>
6
+ <a href="?days=60" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">60d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="grid grid-cols-3 gap-4 mb-6">
11
+ <div class="card text-center">
12
+ <div class="text-3xl font-bold text-discord-foreground"><%= totalGuilds %></div>
13
+ <div class="text-xs text-discord-foreground/60 mt-1">Servidores Totales</div>
14
+ </div>
15
+ <div class="card text-center">
16
+ <div class="text-3xl font-bold text-discord-foreground"><%= summary.totalMessages %></div>
17
+ <div class="text-xs text-discord-foreground/60 mt-1">Mensajes</div>
18
+ </div>
19
+ <div class="card text-center">
20
+ <div class="text-3xl font-bold text-discord-foreground"><%= summary.totalCommands %></div>
21
+ <div class="text-xs text-discord-foreground/60 mt-1">Comandos</div>
22
+ </div>
23
+ </div>
24
+ <div class="card">
25
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Servidores Activos por Dia</h3>
26
+ <canvas id="chartGrowth" width="700" height="300"></canvas>
27
+ </div>
28
+ </div>
29
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
30
+ <script>
31
+ new Chart(document.getElementById('chartGrowth'), {
32
+ type: 'line',
33
+ data: {
34
+ labels: <%- JSON.stringify(guildGrowth.map(v => v.date)) %>,
35
+ datasets: [{
36
+ label: 'Servidores',
37
+ data: <%- JSON.stringify(guildGrowth.map(v => v.guildCount)) %>,
38
+ borderColor: '#57f287',
39
+ backgroundColor: 'rgba(87,242,135,0.1)',
40
+ fill: true,
41
+ tension: 0.3,
42
+ }]
43
+ },
44
+ options: { responsive: true, plugins: { legend: { display: false } } }
45
+ });
46
+ </script>
@@ -0,0 +1,30 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground">Mensajes</h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=7" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">7d</a>
6
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">30d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="card">
11
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Mensajes por Dia (<%= daysBack %> dias)</h3>
12
+ <canvas id="chartMessages" width="700" height="300"></canvas>
13
+ </div>
14
+ </div>
15
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
16
+ <script>
17
+ new Chart(document.getElementById('chartMessages'), {
18
+ type: 'bar',
19
+ data: {
20
+ labels: <%- JSON.stringify(messagesPerDay.map(v => v.date)) %>,
21
+ datasets: [{
22
+ label: 'Mensajes',
23
+ data: <%- JSON.stringify(messagesPerDay.map(v => v.count)) %>,
24
+ backgroundColor: '#5865f2',
25
+ borderRadius: 4,
26
+ }]
27
+ },
28
+ options: { responsive: true, plugins: { legend: { display: false } } }
29
+ });
30
+ </script>
@@ -1,170 +1,76 @@
1
1
  <div class="p-4">
2
2
  <div class="flex items-center justify-between mb-6">
3
- <h1 class="text-2xl font-bold text-discord-foreground"><%= t('analytics.botStats') %></h1>
4
- <div class="flex gap-2">
5
- <button data-days="7" class="date-btn btn-primary text-sm px-3 py-1 rounded"><%= t('analytics.last7days') %></button>
6
- <button data-days="30" class="date-btn text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground"><%= t('analytics.last30days') %></button>
7
- <button data-days="90" class="date-btn text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground"><%= t('analytics.last90days') %></button>
8
- </div>
3
+ <h1 class="text-2xl font-bold text-discord-foreground">Analiticas</h1>
9
4
  </div>
10
5
 
11
- <div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
12
- <div class="card text-center">
13
- <div class="text-2xl font-bold text-discord-foreground"><%= summary.totalGuilds %></div>
14
- <div class="text-xs text-discord-foreground/60 mt-1"><%= t('analytics.totalGuilds') %></div>
15
- </div>
6
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
16
7
  <div class="card text-center">
17
- <div class="text-2xl font-bold text-discord-foreground"><%= summary.totalMessages %></div>
18
- <div class="text-xs text-discord-foreground/60 mt-1"><%= t('analytics.messages') %></div>
8
+ <div class="text-2xl font-bold text-discord-primary"><%= summary.totalGuilds %></div>
9
+ <div class="text-xs text-discord-foreground/60 mt-1">Servidores</div>
19
10
  </div>
20
11
  <div class="card text-center">
21
- <div class="text-2xl font-bold text-discord-foreground"><%= summary.totalCommands %></div>
22
- <div class="text-xs text-discord-foreground/60 mt-1"><%= t('analytics.commands') %></div>
12
+ <div class="text-2xl font-bold text-discord-success"><%= summary.totalMessages %></div>
13
+ <div class="text-xs text-discord-foreground/60 mt-1">Mensajes</div>
23
14
  </div>
24
15
  <div class="card text-center">
25
- <div class="text-2xl font-bold text-discord-foreground"><%= summary.totalJoins %></div>
26
- <div class="text-xs text-discord-foreground/60 mt-1"><%= t('analytics.joins') %></div>
16
+ <div class="text-2xl font-bold text-discord-warning"><%= summary.totalCommands %></div>
17
+ <div class="text-xs text-discord-foreground/60 mt-1">Comandos</div>
27
18
  </div>
28
19
  <div class="card text-center">
29
20
  <div class="text-2xl font-bold text-discord-foreground"><%= Math.round(summary.totalVoiceMinutes) %></div>
30
- <div class="text-xs text-discord-foreground/60 mt-1"><%= t('analytics.voice') %> (min)</div>
21
+ <div class="text-xs text-discord-foreground/60 mt-1">Min. Voz</div>
31
22
  </div>
32
23
  </div>
33
24
 
34
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
25
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
35
26
  <div class="card">
36
- <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.guildGrowth') %></h3>
37
- <canvas id="chartGuildGrowth" width="400" height="200"></canvas>
27
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Crecimiento de Servidores</h3>
28
+ <canvas id="chartGrowth" width="400" height="200"></canvas>
38
29
  </div>
39
30
  <div class="card">
40
- <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.messagesPerDay') %></h3>
31
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Mensajes por Dia</h3>
41
32
  <canvas id="chartMessages" width="400" height="200"></canvas>
42
33
  </div>
43
- <div class="card">
44
- <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.commandsPerDay') %></h3>
45
- <canvas id="chartCommands" width="400" height="200"></canvas>
46
- </div>
47
- <div class="card">
48
- <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.topCommands') %></h3>
49
- <canvas id="chartTopCommands" width="400" height="200"></canvas>
50
- </div>
51
34
  </div>
52
-
53
- <% if (typeof slowestCommands !== 'undefined' && slowestCommands.length > 0) { %>
54
- <div class="card mb-6">
55
- <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.slowestCommands') %></h3>
56
- <canvas id="chartSlowestCommands" width="600" height="200"></canvas>
57
- </div>
58
- <% } %>
59
35
  </div>
60
36
 
61
37
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
62
38
  <script>
63
39
  (function() {
64
- const colors = {
65
- primary: '#5865f2',
66
- success: '#57f287',
67
- warning: '#fee75c',
68
- danger: '#ed4245',
69
- foreground: '#ffffff',
70
- foreground60: 'rgba(255,255,255,0.6)',
71
- dark100: '#36393f',
72
- dark200: '#2f3136',
73
- };
74
-
75
- Chart.defaults.color = colors.foreground60;
76
- Chart.defaults.borderColor = colors.dark100;
77
- Chart.defaults.plugins.legend.labels.color = colors.foreground60;
40
+ Chart.defaults.color = 'rgba(255,255,255,0.6)';
41
+ Chart.defaults.borderColor = '#36393f';
78
42
 
79
- const data = <%- JSON.stringify(chartData) %>;
43
+ const guildGrowth = <%- JSON.stringify(guildGrowth) %>;
44
+ const messages = <%- JSON.stringify(messagesPerDay) %>;
80
45
 
81
- function createLineChart(canvasId, label, values, keyX, keyY) {
82
- new Chart(document.getElementById(canvasId), {
83
- type: 'line',
84
- data: {
85
- labels: values.map(v => v[keyX]),
86
- datasets: [{
87
- label: label,
88
- data: values.map(v => v[keyY]),
89
- borderColor: colors.primary,
90
- backgroundColor: 'rgba(88,101,242,0.1)',
91
- fill: true,
92
- tension: 0.3,
93
- }]
94
- },
95
- options: { responsive: true, plugins: { legend: { display: false } } }
96
- });
97
- }
98
-
99
- function createBarChart(canvasId, label, values, keyX, keyY) {
100
- new Chart(document.getElementById(canvasId), {
101
- type: 'bar',
102
- data: {
103
- labels: values.map(v => v[keyX]),
104
- datasets: [{
105
- label: label,
106
- data: values.map(v => v[keyY]),
107
- backgroundColor: colors.primary,
108
- borderRadius: 4,
109
- }]
110
- },
111
- options: { responsive: true, plugins: { legend: { display: false } } }
112
- });
113
- }
114
-
115
- function createHorizontalBarChart(canvasId, label, values, keyX, keyY) {
116
- new Chart(document.getElementById(canvasId), {
117
- type: 'bar',
118
- data: {
119
- labels: values.map(v => v[keyX]),
120
- datasets: [{
121
- label: label,
122
- data: values.map(v => v[keyY]),
123
- backgroundColor: values.map((_, i) => {
124
- const alpha = 1 - (i * 0.08);
125
- return `rgba(88,101,242,${Math.max(0.3, alpha)})`;
126
- }),
127
- borderRadius: 4,
128
- }]
129
- },
130
- options: {
131
- indexAxis: 'y',
132
- responsive: true,
133
- plugins: { legend: { display: false } }
134
- }
135
- });
136
- }
137
-
138
- createLineChart('chartGuildGrowth', '<%= t("analytics.guildGrowth") %>', data.guildGrowth, 'date', 'guildCount');
139
- createBarChart('chartMessages', '<%= t("analytics.messagesPerDay") %>', data.messagesPerDay, 'date', 'count');
140
- createBarChart('chartCommands', '<%= t("analytics.commandsPerDay") %>', data.commandsPerDay, 'date', 'count');
141
- createHorizontalBarChart('chartTopCommands', '<%= t("analytics.topCommands") %>', data.topCommands, 'command_name', 'usage_count');
46
+ new Chart(document.getElementById('chartGrowth'), {
47
+ type: 'line',
48
+ data: {
49
+ labels: guildGrowth.map(v => v.date),
50
+ datasets: [{
51
+ label: 'Servidores',
52
+ data: guildGrowth.map(v => v.guildCount),
53
+ borderColor: '#57f287',
54
+ backgroundColor: 'rgba(87,242,135,0.1)',
55
+ fill: true,
56
+ tension: 0.3,
57
+ }]
58
+ },
59
+ options: { responsive: true, plugins: { legend: { display: false } } }
60
+ });
142
61
 
143
- <% if (typeof slowestCommands !== 'undefined' && slowestCommands.length > 0) { %>
144
- new Chart(document.getElementById('chartSlowestCommands'), {
62
+ new Chart(document.getElementById('chartMessages'), {
145
63
  type: 'bar',
146
64
  data: {
147
- labels: data.slowestCommands.map(v => v.command_name),
65
+ labels: messages.map(v => v.date),
148
66
  datasets: [{
149
- label: '<%= t("analytics.avgExecutionTime") %> (ms)',
150
- data: data.slowestCommands.map(v => v.avgExecutionTimeMs),
151
- backgroundColor: colors.warning,
67
+ label: 'Mensajes',
68
+ data: messages.map(v => v.count),
69
+ backgroundColor: '#5865f2',
152
70
  borderRadius: 4,
153
71
  }]
154
72
  },
155
- options: {
156
- responsive: true,
157
- plugins: { legend: { display: false } },
158
- scales: { y: { title: { display: true, text: 'ms' } } }
159
- }
160
- });
161
- <% } %>
162
-
163
- document.querySelectorAll('.date-btn').forEach(btn => {
164
- btn.addEventListener('click', () => {
165
- const days = btn.dataset.days;
166
- window.location.href = '/admin/analytics?days=' + days;
167
- });
73
+ options: { responsive: true, plugins: { legend: { display: false } } }
168
74
  });
169
75
  })();
170
76
  </script>
@@ -0,0 +1,55 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground"><%= t('analytics.commands') %></h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=7" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">7d</a>
6
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">30d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
11
+ <div class="card">
12
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.commandsPerDay') %></h3>
13
+ <canvas id="chartCommands" width="400" height="200"></canvas>
14
+ </div>
15
+ <div class="card">
16
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.topCommands') %></h3>
17
+ <canvas id="chartTop" width="400" height="200"></canvas>
18
+ </div>
19
+ </div>
20
+ <% if (slowestCommands && slowestCommands.length > 0) { %>
21
+ <div class="card mt-6">
22
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.slowestCommands') %></h3>
23
+ <canvas id="chartSlow" width="700" height="250"></canvas>
24
+ </div>
25
+ <% } %>
26
+ </div>
27
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
28
+ <script>
29
+ new Chart(document.getElementById('chartCommands'), {
30
+ type: 'bar',
31
+ data: {
32
+ labels: <%- JSON.stringify(commandsPerDay.map(v => v.date)) %>,
33
+ datasets: [{ label: '<%= t("analytics.commands") %>', data: <%- JSON.stringify(commandsPerDay.map(v => v.count)) %>, backgroundColor: '#5865f2', borderRadius: 4 }]
34
+ },
35
+ options: { responsive: true, plugins: { legend: { display: false } } }
36
+ });
37
+ new Chart(document.getElementById('chartTop'), {
38
+ type: 'bar',
39
+ data: {
40
+ labels: <%- JSON.stringify(topCommands.map(v => v.command_name)) %>,
41
+ datasets: [{ label: '<%= t("analytics.usageCount") %>', data: <%- JSON.stringify(topCommands.map(v => v.usage_count)) %>, backgroundColor: topCommands.map((_, i) => 'rgba(88,101,242,' + Math.max(0.3, 1 - i * 0.08) + ')'), borderRadius: 4 }]
42
+ },
43
+ options: { indexAxis: 'y', responsive: true, plugins: { legend: { display: false } } }
44
+ });
45
+ <% if (slowestCommands && slowestCommands.length > 0) { %>
46
+ new Chart(document.getElementById('chartSlow'), {
47
+ type: 'bar',
48
+ data: {
49
+ labels: <%- JSON.stringify(slowestCommands.map(v => v.command_name)) %>,
50
+ datasets: [{ label: 'ms', data: <%- JSON.stringify(slowestCommands.map(v => v.avgExecutionTimeMs)) %>, backgroundColor: '#fee75c', borderRadius: 4 }]
51
+ },
52
+ options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { title: { display: true, text: 'ms' } } } }
53
+ });
54
+ <% } %>
55
+ </script>
@@ -0,0 +1,47 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground"><%= t('analytics.messages') %></h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=7" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">7d</a>
6
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">30d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="card mb-6">
11
+ <div class="text-center py-4">
12
+ <div class="text-4xl font-bold text-discord-primary"><%= totalMessages %></div>
13
+ <div class="text-sm text-discord-foreground/60"><%= t('analytics.messages') %> (<%= daysBack %> dias)</div>
14
+ </div>
15
+ </div>
16
+ <div class="card mb-6">
17
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.messagesPerDay') %></h3>
18
+ <canvas id="chartMessages" width="700" height="300"></canvas>
19
+ </div>
20
+ <% if (typeof channelMessages !== 'undefined' && channelMessages.length > 0) { %>
21
+ <div class="card">
22
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground">Mensajes por Canal</h3>
23
+ <canvas id="chartChannels" width="700" height="250"></canvas>
24
+ </div>
25
+ <% } %>
26
+ </div>
27
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
28
+ <script>
29
+ new Chart(document.getElementById('chartMessages'), {
30
+ type: 'bar',
31
+ data: {
32
+ labels: <%- JSON.stringify(messagesPerDay.map(v => v.date)) %>,
33
+ datasets: [{ label: '<%= t("analytics.messages") %>', data: <%- JSON.stringify(messagesPerDay.map(v => v.count)) %>, backgroundColor: '#5865f2', borderRadius: 4 }]
34
+ },
35
+ options: { responsive: true, plugins: { legend: { display: false } } }
36
+ });
37
+ <% if (typeof channelMessages !== 'undefined' && channelMessages.length > 0) { %>
38
+ new Chart(document.getElementById('chartChannels'), {
39
+ type: 'bar',
40
+ data: {
41
+ labels: <%- JSON.stringify(channelMessages.map(v => v.channel_id)) %>,
42
+ datasets: [{ label: '<%= t("analytics.messages") %>', data: <%- JSON.stringify(channelMessages.map(v => v.message_count)) %>, backgroundColor: '#57f287', borderRadius: 4 }]
43
+ },
44
+ options: { responsive: true, plugins: { legend: { display: false } } }
45
+ });
46
+ <% } %>
47
+ </script>
@@ -0,0 +1,47 @@
1
+ <div class="p-4">
2
+ <div class="flex items-center justify-between mb-6">
3
+ <h1 class="text-2xl font-bold text-discord-foreground"><%= t('analytics.voice') %></h1>
4
+ <div class="flex gap-2">
5
+ <a href="?days=7" class="text-sm px-3 py-1 rounded bg-discord-primary text-white">7d</a>
6
+ <a href="?days=30" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">30d</a>
7
+ <a href="?days=90" class="text-sm px-3 py-1 rounded bg-discord-dark-100 text-discord-foreground/70 hover:text-discord-foreground">90d</a>
8
+ </div>
9
+ </div>
10
+ <div class="card mb-6">
11
+ <div class="text-center py-4">
12
+ <div class="text-4xl font-bold text-discord-primary"><%= Math.round(totalVoice) %> min</div>
13
+ <div class="text-sm text-discord-foreground/60"><%= t('analytics.voiceActivity') %></div>
14
+ </div>
15
+ </div>
16
+ <div class="card mb-6">
17
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.voiceActivity') %> (<%= daysBack %> dias)</h3>
18
+ <canvas id="chartVoice" width="700" height="300"></canvas>
19
+ </div>
20
+ <% if (channelVoice && channelVoice.length > 0) { %>
21
+ <div class="card">
22
+ <h3 class="text-lg font-semibold mb-4 text-discord-foreground"><%= t('analytics.perChannelVoice') %></h3>
23
+ <canvas id="chartChannels" width="700" height="250"></canvas>
24
+ </div>
25
+ <% } %>
26
+ </div>
27
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
28
+ <script>
29
+ new Chart(document.getElementById('chartVoice'), {
30
+ type: 'bar',
31
+ data: {
32
+ labels: <%- JSON.stringify(voicePerDay.map(v => v.date)) %>,
33
+ datasets: [{ label: 'min', data: <%- JSON.stringify(voicePerDay.map(v => v.count)) %>, backgroundColor: '#57f287', borderRadius: 4 }]
34
+ },
35
+ options: { responsive: true, plugins: { legend: { display: false } } }
36
+ });
37
+ <% if (channelVoice && channelVoice.length > 0) { %>
38
+ new Chart(document.getElementById('chartChannels'), {
39
+ type: 'bar',
40
+ data: {
41
+ labels: <%- JSON.stringify(channelVoice.map(v => v.channel_id)) %>,
42
+ datasets: [{ label: 'min', data: <%- JSON.stringify(channelVoice.map(v => v.total_minutes)) %>, backgroundColor: '#5865f2', borderRadius: 4 }]
43
+ },
44
+ options: { responsive: true, plugins: { legend: { display: false } } }
45
+ });
46
+ <% } %>
47
+ </script>