Commit 36e1afbc authored by Paulo Avila's avatar Paulo Avila
Browse files

Inclusão de sistema de metas e produção

parent cfed0f56
import { Target } from "lucide-react";
import { getMetaInfo } from "@/lib/meta";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
/**
* Indicador da meta de subtarefas em aberto na tarefa-mãe.
* Verde = dentro da meta; vermelho = acima do teto; âmbar = abaixo do piso.
*/
export function MetaBadge({
metaType,
metaValue,
subtaskCount,
subtaskDone,
className,
}: {
metaType: string | null;
metaValue: number | null;
subtaskCount: number;
subtaskDone: number;
className?: string;
}) {
const info = getMetaInfo(metaType, metaValue, subtaskCount, subtaskDone);
if (!info) return null;
const styles = {
within: "bg-emerald-500 text-white hover:bg-emerald-500",
exceeded: "bg-destructive text-destructive-foreground hover:bg-destructive",
below: "bg-amber-500 text-white hover:bg-amber-500",
} as const;
return (
<Badge
title={info.label}
className={cn(
"gap-0.5 px-1.5 py-0 text-[10px] font-medium leading-4",
styles[info.state],
className
)}
>
<Target className="h-2.5 w-2.5 shrink-0" />
{info.shortLabel}
</Badge>
);
}
export type MetaKind = "teto" | "piso";
export type MetaState = "within" | "exceeded" | "below";
export interface MetaInfo {
kind: MetaKind;
state: MetaState;
/** true quando está dentro da meta. */
ok: boolean;
/** Subtarefas em aberto (não concluídas). */
open: number;
/** Valor da meta. */
target: number;
/** Texto completo (tooltip). */
label: string;
/** Texto curto para o badge ("8/10"). */
shortLabel: string;
}
/**
* Avalia a meta de subtarefas em aberto de uma tarefa-mãe recorrente.
* "Em aberto" = subtarefas não concluídas (total − concluídas).
* - teto: dentro quando em aberto ≤ meta; estoura acima.
* - piso: dentro quando em aberto ≥ meta; fica abaixo.
* Retorna null quando a tarefa não usa meta.
*/
export function getMetaInfo(
metaType: string | null,
metaValue: number | null,
subtaskCount: number,
subtaskDone: number
): MetaInfo | null {
if ((metaType !== "teto" && metaType !== "piso") || metaValue == null) {
return null;
}
const open = Math.max(0, subtaskCount - subtaskDone);
const target = metaValue;
if (metaType === "teto") {
const ok = open <= target;
return {
kind: "teto",
state: ok ? "within" : "exceeded",
ok,
open,
target,
label: ok
? `Dentro da meta: ${open} em aberto (teto ${target})`
: `Acima da meta: ${open} em aberto (teto ${target})`,
shortLabel: `${open}/${target}`,
};
}
const ok = open >= target;
return {
kind: "piso",
state: ok ? "within" : "below",
ok,
open,
target,
label: ok
? `Dentro da meta: ${open} em aberto (piso ${target})`
: `Abaixo da meta: ${open} em aberto (piso ${target})`,
shortLabel: `${open}/${target}`,
};
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment