first commit

This commit is contained in:
2026-07-16 20:48:43 -03:00
commit 5696cee264
1111 changed files with 322270 additions and 0 deletions
@@ -0,0 +1,168 @@
"use client";
import React, { useEffect, useState } from "react";
import classNames from "classnames";
import styles from "./PlanMetricsWidget.module.css";
import { ISubscriptionInfo } from "@core/Models/Subscription.Model";
import StorefrontOutlinedIcon from "@mui/icons-material/StorefrontOutlined";
import PeopleOutlineOutlinedIcon from "@mui/icons-material/PeopleOutlineOutlined";
import CalendarTodayOutlinedIcon from "@mui/icons-material/CalendarTodayOutlined";
import AssignmentIndOutlinedIcon from "@mui/icons-material/AssignmentIndOutlined";
import RepeatOutlinedIcon from "@mui/icons-material/RepeatOutlined";
import Button from "@core/app/components/Button/Button";
import { MP_SUBS_STATUS } from "@core/Models/Subscription.Model";
import dayjs from "dayjs";
export interface PlanMetricsWidgetProps {
subscription: ISubscriptionInfo;
metrics: {
organizationsCount: number;
employeesCount: number;
clientsCount: number;
appointmentsCount: number;
servicesCount: number;
repeatsCount: number;
};
onUpgrade: () => void;
onRenew?: () => void;
onCancel: () => void;
isOwner?: boolean;
}
const MetricRow = ({
icon,
title,
value,
maxValue,
}: {
icon: React.ReactNode;
title: string;
value: number;
maxValue: number;
}) => {
const [animatedWidth, setAnimatedWidth] = useState(0);
const isInfinite = maxValue === -1;
const formattedMax = isInfinite ? "∞" : maxValue;
const percentage = isInfinite ? 100 : Math.min(100, (value / maxValue) * 100);
let statusClass = styles.safe;
if (isInfinite) {
statusClass = styles.infinite;
} else {
if (percentage >= 100) {
statusClass = styles.danger;
} else if (percentage >= 80) {
statusClass = styles.warning;
}
}
useEffect(() => {
const timer = setTimeout(() => {
setAnimatedWidth(percentage);
}, 100);
return () => clearTimeout(timer);
}, [percentage]);
return (
<div className={styles.metricRow}>
<div className={styles.metricHeader}>
<div className={styles.metricTitleWrap}>
<span className={styles.metricIcon}>{icon}</span>
<span className={styles.metricTitle}>{title}</span>
</div>
<div className={styles.metricValue}>
<span className={styles.currentValue}>{value}</span>
<span className={styles.maxValue}>/ {formattedMax}</span>
</div>
</div>
<div className={styles.progressContainer}>
<div
className={classNames(styles.progressBar, statusClass)}
style={{ width: `${animatedWidth}%` }}
/>
</div>
</div>
);
};
export default function PlanMetricsWidget({
subscription,
metrics,
onUpgrade,
onCancel,
isOwner = true,
}: PlanMetricsWidgetProps) {
const isCancelled = subscription.mpStatus === MP_SUBS_STATUS.CANCELLED;
return (
<div className={styles.widgetContainer}>
<div className={styles.widgetHeader}>
<h3 className={styles.title}>Métricas del Plan {subscription.plan.name}</h3>
<span className={styles.subtitle}>Uso actual de tu suscripción</span>
</div>
<div className={styles.metricsList}>
<MetricRow
icon={<StorefrontOutlinedIcon fontSize="small" />}
title="Organizaciones"
value={metrics.organizationsCount}
maxValue={subscription.plan.limitOrganizations}
/>
<MetricRow
icon={<PeopleOutlineOutlinedIcon fontSize="small" />}
title="Clientes"
value={metrics.clientsCount}
maxValue={subscription.plan.limitClients}
/>
<MetricRow
icon={<CalendarTodayOutlinedIcon fontSize="small" />}
title="Turnos"
value={metrics.appointmentsCount}
maxValue={subscription.plan.limitAppointments}
/>
<MetricRow
icon={<AssignmentIndOutlinedIcon fontSize="small" />}
title="Colaboradores"
value={metrics.employeesCount}
maxValue={subscription.plan.limitEmployees}
/>
<MetricRow
icon={<RepeatOutlinedIcon fontSize="small" />}
title="Repeticiones"
value={metrics.repeatsCount || 0}
maxValue={subscription.plan.limitRepeats}
/>
</div>
{isOwner && (
<div className={styles.actionsContainer}>
<Button
color="primary"
text="Mejorar mi plan"
width="custom"
style={{ width: "100%", padding: "12px 0", borderRadius: "8px", fontWeight: "600" }}
onClick={onUpgrade}
/>
<div className={styles.statusFooter}>
{isCancelled ? (
<div className={styles.cancelledInfo}>
<p className={styles.cancelledText}>La suscripción ha sido cancelada</p>
<p className={styles.validUntilText}>
Vigente hasta el {dayjs(subscription.endDate).format("DD/MM/YYYY")}
</p>
</div>
) : (
<Button
color="link"
text="Cancelar subscripción"
onClick={onCancel}
width="custom"
style={{ margin: 0, padding: 0, fontSize: "13px", color: "#6b7280", minHeight: "auto" }}
/>
)}
</div>
</div>
)}
</div>
);
}