feat: integrate consumption metrics into plan usage cycle tracking and UI display

This commit is contained in:
2026-07-18 22:02:48 -03:00
parent 0f76ef3118
commit 78ee86ac48
7 changed files with 31 additions and 4 deletions
@@ -58,7 +58,6 @@ export class PlanUsageCycleAdapterMongoose implements IPlanUsageCycleAdapter {
planId: data.planId, planId: data.planId,
cycleStart: data.cycleStart, cycleStart: data.cycleStart,
cycleEnd: data.cycleEnd, cycleEnd: data.cycleEnd,
appointmentsCount: 0,
creationDate: now, creationDate: now,
}, },
$set: { updateDate: now }, $set: { updateDate: now },
@@ -85,7 +84,6 @@ export class PlanUsageCycleAdapterMongoose implements IPlanUsageCycleAdapter {
planId: data.planId, planId: data.planId,
cycleStart: data.cycleStart, cycleStart: data.cycleStart,
cycleEnd: data.cycleEnd, cycleEnd: data.cycleEnd,
appointmentsCount: 0,
creationDate: now, creationDate: now,
}, },
$inc: { appointmentsCount: data.quantity }, $inc: { appointmentsCount: data.quantity },
@@ -49,6 +49,11 @@ export type SysAdminRecalculatePlanUsageCycleResult = {
planId: string; planId: string;
cycleStart: Date; cycleStart: Date;
cycleEnd: Date; cycleEnd: Date;
organizationsCount: number;
employeesCount: number;
servicesCount: number;
clientsCount: number;
repeatsCount: number;
appointmentsCount: number; appointmentsCount: number;
}; };
@@ -2,6 +2,7 @@ import { connect } from "mongoose";
import dayjs from "dayjs"; import dayjs from "dayjs";
import AppointmentList from "../../../Models/Appointments/Appointments"; import AppointmentList from "../../../Models/Appointments/Appointments";
import CompaniesList from "../../../Models/Companies/Companies"; import CompaniesList from "../../../Models/Companies/Companies";
import MetricsList from "../../../Models/Metrics/Metrics";
import PlanUsageCycleList from "../../../Models/PlanUsageCycle/PlanUsageCycle"; import PlanUsageCycleList from "../../../Models/PlanUsageCycle/PlanUsageCycle";
import { import {
SysAdminRecalculatePlanUsageCycleParams, SysAdminRecalculatePlanUsageCycleParams,
@@ -37,12 +38,20 @@ export class SysAdminPlanUsageCycleService {
throw new Error("No se pudo recalcular el uso del plan."); throw new Error("No se pudo recalcular el uso del plan.");
} }
await MetricsList.calculateMetrics({ userId: params.userId });
const metrics = await MetricsList.getMetrics(params.userId);
return { return {
userId: params.userId, userId: params.userId,
subscriptionId: String(updatedCycle.subscriptionId), subscriptionId: String(updatedCycle.subscriptionId),
planId: String(updatedCycle.planId), planId: String(updatedCycle.planId),
cycleStart: updatedCycle.cycleStart, cycleStart: updatedCycle.cycleStart,
cycleEnd: updatedCycle.cycleEnd, cycleEnd: updatedCycle.cycleEnd,
organizationsCount: metrics.organizationsCount,
employeesCount: metrics.employeesCount,
servicesCount: metrics.servicesCount,
clientsCount: metrics.clientsCount,
repeatsCount: metrics.repeatsCount || 0,
appointmentsCount: updatedCycle.appointmentsCount, appointmentsCount: updatedCycle.appointmentsCount,
}; };
} }
+5
View File
@@ -39,6 +39,11 @@ export type RecalculatePlanUsageCycleResult = {
planId: string; planId: string;
cycleStart: string; cycleStart: string;
cycleEnd: string; cycleEnd: string;
organizationsCount: number;
employeesCount: number;
servicesCount: number;
clientsCount: number;
repeatsCount: number;
appointmentsCount: number; appointmentsCount: number;
}; };
@@ -53,7 +53,7 @@ export const RecalculationToolsModal = ({ initialUserId = '', onClose }: { initi
<div style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid var(--glass-border)', borderRadius: '12px', padding: '1.25rem' }}> <div style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid var(--glass-border)', borderRadius: '12px', padding: '1.25rem' }}>
<div style={{ marginBottom: '1rem' }}> <div style={{ marginBottom: '1rem' }}>
<h3 style={{ fontSize: '1.1rem', fontWeight: '700', marginBottom: '0.35rem' }}>Recalcular uso del plan</h3> <h3 style={{ fontSize: '1.1rem', fontWeight: '700', marginBottom: '0.35rem' }}>Recalcular uso del plan</h3>
<p style={{ color: 'var(--text-muted)', fontSize: '0.9rem' }}>Recuenta las reservas del ciclo actual para el userId seleccionado.</p> <p style={{ color: 'var(--text-muted)', fontSize: '0.9rem' }}>Recuenta las reservas del ciclo actual y los contadores stock del userId seleccionado.</p>
</div> </div>
<form onSubmit={(e) => { e.preventDefault(); handleRecalculatePlanUsage(); }} style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap', alignItems: 'flex-end' }}> <form onSubmit={(e) => { e.preventDefault(); handleRecalculatePlanUsage(); }} style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap', alignItems: 'flex-end' }}>
@@ -82,6 +82,11 @@ export const RecalculationToolsModal = ({ initialUserId = '', onClose }: { initi
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: '0.75rem' }}> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: '0.75rem' }}>
<div><strong>Cycle Start</strong><br /><span style={{ color: 'var(--text-muted)' }}>{formatDateTime(usageResult.cycleStart)}</span></div> <div><strong>Cycle Start</strong><br /><span style={{ color: 'var(--text-muted)' }}>{formatDateTime(usageResult.cycleStart)}</span></div>
<div><strong>Cycle End</strong><br /><span style={{ color: 'var(--text-muted)' }}>{formatDateTime(usageResult.cycleEnd)}</span></div> <div><strong>Cycle End</strong><br /><span style={{ color: 'var(--text-muted)' }}>{formatDateTime(usageResult.cycleEnd)}</span></div>
<div><strong>Organizations</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.organizationsCount}</span></div>
<div><strong>Collaborators</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.employeesCount}</span></div>
<div><strong>Services</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.servicesCount}</span></div>
<div><strong>Clients</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.clientsCount}</span></div>
<div><strong>Repeats</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.repeatsCount}</span></div>
<div><strong>Appointments</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.appointmentsCount}</span></div> <div><strong>Appointments</strong><br /><span style={{ color: 'var(--success)', fontWeight: 700 }}>{usageResult.appointmentsCount}</span></div>
</div> </div>
</div> </div>
+5
View File
@@ -63,6 +63,11 @@ export type SysAdminRecalculatePlanUsageCycleResult = {
planId: string; planId: string;
cycleStart: string; cycleStart: string;
cycleEnd: string; cycleEnd: string;
organizationsCount: number;
employeesCount: number;
servicesCount: number;
clientsCount: number;
repeatsCount: number;
appointmentsCount: number; appointmentsCount: number;
}; };
@@ -44,7 +44,7 @@ const getLimitCards = (plan: ISubscriptionInfo["plan"], metrics: DashboardMetric
{ label: "Organizaciones", limit: plan.limitOrganizations, current: metrics?.organizationsCount }, { label: "Organizaciones", limit: plan.limitOrganizations, current: metrics?.organizationsCount },
{ label: "Colaboradores", limit: plan.limitEmployees, current: metrics?.employeesCount }, { label: "Colaboradores", limit: plan.limitEmployees, current: metrics?.employeesCount },
{ label: "Servicios", limit: plan.limitServices, current: metrics?.servicesCount }, { label: "Servicios", limit: plan.limitServices, current: metrics?.servicesCount },
{ label: "Turnos", limit: plan.limitAppointments, current: metrics?.appointmentsCount }, { label: "Turnos del último mes", limit: plan.limitAppointments, current: metrics?.appointmentsCount },
{ label: "Clientes", limit: plan.limitClients, current: metrics?.clientsCount }, { label: "Clientes", limit: plan.limitClients, current: metrics?.clientsCount },
{ label: "Repeticiones", limit: plan.limitRepeats, current: metrics?.repeatsCount }, { label: "Repeticiones", limit: plan.limitRepeats, current: metrics?.repeatsCount },
]; ];