mejora semanal

This commit is contained in:
Carlos Narro
2026-03-20 04:31:17 +01:00
parent ed2d44fc13
commit 6eeb5f929c
2 changed files with 125 additions and 65 deletions

View File

@@ -195,13 +195,22 @@
data.weeklyResetText = resetText;
// Parsear "Se restablece vie, 4:59" para obtener día y hora
const resetMatch = resetText.match(/Se restablece\s+(\w+),?\s*([\d:]+)/i);
const resetMatch = resetText.match(/Se restablece\s+([a-záéíóúñü]+),?\s*([\d:]+)/i);
if (resetMatch) {
const dayAbbr = resetMatch[1].toLowerCase();
data.resetHour = resetMatch[2];
// Encontrar el índice del día
data.resetDayIndex = DAYS_ES.findIndex(d => d === dayAbbr || d.startsWith(dayAbbr.substring(0, 3)));
// Encontrar el índice del día (comparar primeras 3 letras sin acentos)
const normalize = s => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
const dayAbbrNorm = normalize(dayAbbr);
data.resetDayIndex = DAYS_ES.findIndex(d => {
const dNorm = normalize(d);
return dNorm === dayAbbrNorm || dNorm.startsWith(dayAbbrNorm.substring(0, 3)) || dayAbbrNorm.startsWith(dNorm.substring(0, 3));
});
console.log('[Claude Usage Tracker] Reset parsed:', dayAbbr, '->', data.resetDayIndex, 'hour:', data.resetHour);
} else {
console.log('[Claude Usage Tracker] Reset text no match:', resetText);
}
}
}
@@ -236,6 +245,7 @@
/**
* Calcula el desglose diario basado en los datos reales
* Usa HORAS transcurridas para mayor precisión
* Divide el día de reinicio en dos partes: inicio (después del reinicio) y fin (antes del reinicio)
*/
function calculateDailyUsage(pageData) {
const { weeklyUsage, resetDayIndex, resetHour } = pageData;
@@ -246,14 +256,16 @@
// Calcular el momento exacto del último reinicio
const [resetH, resetM] = (resetHour || '5:00').split(':').map(Number);
const resetHourDecimal = resetH + (resetM || 0) / 60;
// Encontrar la fecha del último reinicio
// Encontrar la fecha del último reinicio (usar effectiveResetDayIndex más abajo)
const effectiveResetIdx = resetDayIndex >= 0 ? resetDayIndex : 5;
let lastResetDate = new Date(now);
let daysSinceReset = todayIndex - resetDayIndex;
let daysSinceReset = todayIndex - effectiveResetIdx;
if (daysSinceReset < 0) daysSinceReset += 7;
if (daysSinceReset === 0) {
// Es el día de reinicio, ¿ya pasó la hora?
if (now.getHours() < resetH || (now.getHours() === resetH && now.getMinutes() < resetM)) {
if (currentHour < resetHourDecimal) {
daysSinceReset = 7; // Aún no ha reiniciado, usar el de la semana pasada
}
}
@@ -279,88 +291,126 @@
const avgUsagePerHour = hoursElapsed > 0 ? weeklyUsage / hoursElapsed : 0;
const idealUsagePerHour = 100 / totalWeekHours;
// La semana empieza el mismo día del reinicio (ej: viernes)
// El primer día es el día del reinicio, desde la hora de reinicio
const startDayIndex = resetDayIndex;
// Calcular la posición del día actual dentro de la semana de facturación
// Posición 0 = día del reinicio, posición 1 = día siguiente, etc.
let todayPositionInWeek = todayIndex - resetDayIndex;
if (todayPositionInWeek < 0) todayPositionInWeek += 7;
// Nombre del día de reinicio (con fallback, reutilizar effectiveResetIdx)
const effectiveResetDayIndex = effectiveResetIdx;
const resetDayName = DAYS_ES_DISPLAY[effectiveResetDayIndex] || 'Vie';
const resetDayFullName = DAYS_FULL_ES[effectiveResetDayIndex] || 'Viernes';
const dailyBreakdown = [];
for (let i = 0; i < 7; i++) {
const dayIndex = (startDayIndex + i) % 7;
const isToday = (i === todayPositionInWeek);
const isPast = (i < todayPositionInWeek);
const isFuture = (i > todayPositionInWeek);
// Estructura: 8 segmentos
// 0: Día reinicio (inicio) - desde resetHour hasta 24:00
// 1-6: Días completos (sáb, dom, lun, mar, mié, jue si reinicio es viernes)
// 7: Día reinicio (fin) - desde 00:00 hasta resetHour
for (let i = 0; i < 8; i++) {
let dayIndex, dayName, dayFullName, dayTotalHours, isResetDayStart, isResetDayEnd;
// Calcular cuántas horas de este día han transcurrido
if (i === 0) {
// Primer segmento: día de reinicio (parte después del reinicio)
dayIndex = effectiveResetDayIndex;
dayName = resetDayName;
dayFullName = resetDayFullName + ' (inicio)';
dayTotalHours = 24 - resetHourDecimal;
isResetDayStart = true;
isResetDayEnd = false;
} else if (i === 7) {
// Último segmento: día de reinicio (parte antes del reinicio)
dayIndex = effectiveResetDayIndex;
dayName = resetDayName;
dayFullName = resetDayFullName + ' (fin)';
dayTotalHours = resetHourDecimal;
isResetDayStart = false;
isResetDayEnd = true;
} else {
// Días intermedios (completos)
dayIndex = (effectiveResetDayIndex + i) % 7;
dayName = DAYS_ES_DISPLAY[dayIndex];
dayFullName = DAYS_FULL_ES[dayIndex];
dayTotalHours = 24;
isResetDayStart = false;
isResetDayEnd = false;
}
// Calcular posición del día actual
let segmentPosition;
if (todayIndex === effectiveResetDayIndex) {
// Hoy es el día de reinicio
if (currentHour >= resetHourDecimal) {
segmentPosition = 0; // Estamos en la parte de inicio
} else {
segmentPosition = 7; // Estamos en la parte de fin (antes del reinicio)
}
} else {
// Día normal
let pos = todayIndex - effectiveResetDayIndex;
if (pos <= 0) pos += 7;
segmentPosition = pos;
}
const isToday = (i === segmentPosition);
const isPast = (i < segmentPosition);
const isFuture = (i > segmentPosition);
// Calcular horas transcurridas en este segmento
let dayHoursElapsed = 0;
if (isPast) {
// Día completo ya pasado
if (i === 0) {
// Primer día (día del reinicio): desde hora de reinicio hasta medianoche
dayHoursElapsed = 24 - (resetH + (resetM || 0) / 60);
} else {
dayHoursElapsed = 24;
}
dayHoursElapsed = dayTotalHours;
} else if (isToday) {
if (i === 0) {
// Hoy es el día del reinicio: desde hora de reinicio hasta ahora
dayHoursElapsed = currentHour - (resetH + (resetM || 0) / 60);
// Hoy es el día de reinicio, parte inicio
dayHoursElapsed = currentHour - resetHourDecimal;
if (dayHoursElapsed < 0) dayHoursElapsed = 0;
} else if (i === 7) {
// Hoy es el día de reinicio, parte fin
dayHoursElapsed = currentHour;
} else {
// Día actual normal: desde medianoche hasta ahora
// Día normal
dayHoursElapsed = currentHour;
}
}
// Días futuros: dayHoursElapsed = 0
// Calcular horas totales del día (para el porcentaje de llenado)
let dayTotalHours = 24;
if (i === 0) {
// Primer día: solo cuenta desde la hora de reinicio
dayTotalHours = 24 - (resetH + (resetM || 0) / 60);
}
// Porcentaje de llenado del día (0-100%)
// Porcentaje de llenado del segmento (0-100%)
const dayFillPercent = dayTotalHours > 0 ? (dayHoursElapsed / dayTotalHours) * 100 : 0;
// Uso asignado a este día (promedio distribuido)
// Uso asignado a este segmento (promedio distribuido)
let usage = 0;
if (dayHoursElapsed > 0 && hoursElapsed > 0) {
// Distribuir el uso total proporcionalmente a las horas transcurridas
usage = (dayHoursElapsed / hoursElapsed) * weeklyUsage;
}
// Estado basado en si el uso del día está dentro del ideal
// Estado basado en si el uso está dentro del ideal
let status = 'pending';
if (dayHoursElapsed > 0) {
const idealForThisDay = (dayHoursElapsed / 24) * idealDailyPercent;
status = usage <= idealForThisDay * 1.1 ? 'good' :
usage <= idealForThisDay * 1.5 ? 'warning' : 'danger';
const idealForThisSegment = (dayHoursElapsed / totalWeekHours) * 100;
const actualForThisSegment = usage;
status = actualForThisSegment <= idealForThisSegment * 1.1 ? 'good' :
actualForThisSegment <= idealForThisSegment * 1.5 ? 'warning' : 'danger';
}
// Offset para el primer día (horas antes del reinicio que no cuentan)
const dayStartOffsetPercent = (i === 0) ? ((resetH + (resetM || 0) / 60) / 24) * 100 : 0;
// Offset visual (solo para el segmento de inicio del día de reinicio)
const dayStartOffsetPercent = isResetDayStart ? (resetHourDecimal / 24) * 100 : 0;
// Offset final (solo para el segmento de fin del día de reinicio)
const dayEndOffsetPercent = isResetDayEnd ? ((24 - resetHourDecimal) / 24) * 100 : 0;
dailyBreakdown.push({
dayIndex,
dayName: DAYS_ES_DISPLAY[dayIndex],
dayFullName: DAYS_FULL_ES[dayIndex],
dayName,
dayFullName,
isPast,
isToday,
isFuture,
isResetDay: i === 0,
isResetDayStart,
isResetDayEnd,
usage,
idealUsage: idealDailyPercent,
dayFillPercent, // % del día transcurrido (para llenar la barra)
idealUsage: (dayTotalHours / totalWeekHours) * 100,
dayFillPercent,
dayHoursElapsed,
dayTotalHours,
dayStartOffsetPercent, // % del día antes del reinicio (hueco a la izquierda)
dayStartOffsetPercent,
dayEndOffsetPercent,
status
});
}
@@ -410,7 +460,7 @@
container.id = 'claude-usage-tracker';
const usagePercent = pageData.weeklyUsage;
const resetDayName = DAYS_FULL_ES[pageData.resetDayIndex] || 'próximo reinicio';
const resetDayName = DAYS_FULL_ES[pageData.resetDayIndex >= 0 ? pageData.resetDayIndex : 5] || 'próximo reinicio';
const boostStatus = getBoostStatus();
container.innerHTML = `
@@ -434,21 +484,22 @@
<div class="weekly-progress-container">
${dailyData.dailyBreakdown.map(day => `
<div class="day-segment ${day.isToday ? 'today' : ''} ${day.isFuture ? 'future' : ''} ${day.isResetDay ? 'reset-day' : ''}">
<div class="day-segment ${day.isToday ? 'today' : ''} ${day.isFuture ? 'future' : ''} ${day.isResetDayStart ? 'reset-day-start' : ''} ${day.isResetDayEnd ? 'reset-day-end' : ''}">
<div class="day-tooltip">
${day.dayFullName}: ${day.usage.toFixed(1)}% usado
${day.isToday ? ` (${day.dayHoursElapsed.toFixed(1)}h transcurridas)` : day.isPast ? ' (completo)' : ''}
${day.isResetDay ? `<br>Reinicio a las ${pageData.resetHour}` : ''}
<br>Ideal para ${day.dayHoursElapsed.toFixed(0)}h: ${((day.dayHoursElapsed / 24) * day.idealUsage).toFixed(1)}%
${day.dayFullName}: ${day.usage.toFixed(1)}% usado (${day.dayTotalHours.toFixed(1)}h)
${day.isToday ? ` ${day.dayHoursElapsed.toFixed(1)}h transcurridas` : day.isPast ? ' completo' : ''}
${day.isResetDayStart ? `<br>🔄 Desde reinicio (${pageData.resetHour})` : ''}
${day.isResetDayEnd ? `<br>⏳ Hasta reinicio (${pageData.resetHour})` : ''}
</div>
<div class="day-bar">
${day.isResetDay ? `<div class="day-bar-offset" style="width: ${day.dayStartOffsetPercent}%"></div>` : ''}
<div class="day-bar-bg" style="left: ${day.dayStartOffsetPercent}%; width: ${(100 - day.dayStartOffsetPercent) * day.dayFillPercent / 100}%"></div>
${day.isResetDayStart ? `<div class="day-bar-offset start" style="width: ${day.dayStartOffsetPercent}%"></div>` : ''}
${day.isResetDayEnd ? `<div class="day-bar-offset end" style="left: ${100 - day.dayEndOffsetPercent}%; width: ${day.dayEndOffsetPercent}%"></div>` : ''}
<div class="day-bar-bg" style="left: ${day.dayStartOffsetPercent}%; width: ${day.dayFillPercent * (100 - day.dayStartOffsetPercent - day.dayEndOffsetPercent) / 100}%"></div>
<div class="day-bar-fill ${day.status === 'good' ? 'under-budget' : day.status === 'warning' ? 'on-track' : day.status === 'danger' ? 'over-budget' : ''}"
style="left: ${day.dayStartOffsetPercent}%; width: ${(100 - day.dayStartOffsetPercent) * day.dayFillPercent / 100}%">
style="left: ${day.dayStartOffsetPercent}%; width: ${day.dayFillPercent * (100 - day.dayStartOffsetPercent - day.dayEndOffsetPercent) / 100}%">
</div>
</div>
<span class="day-label">${day.dayName}</span>
<span class="day-label">${day.isResetDayStart ? day.dayName + '↓' : day.isResetDayEnd ? day.dayName + '↑' : day.dayName}</span>
</div>
`).join('')}
</div>