Files
Frontend-Iasis/src/pages/entregaveis/components/EntregavelValuationCard.tsx

188 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { DollarSign } from 'lucide-react';
import { ContractItemType } from '../../../types/contract-item.types';
import { DELIVERABLE_TYPE_LABELS, DeliverableType } from '../../../constants/deliverable-type';
import type { DeliverableDetail } from '../../../types/deliverable.types';
interface Props {
deliverable: DeliverableDetail;
}
function formatCurrency(value: number): string {
return value.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
});
}
function formatNumber(value: number): string {
return value.toLocaleString('pt-BR', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
});
}
function resolveTimebox(deliverable: DeliverableDetail): {
value: number | null;
source: 'CONTRACT_ITEM' | 'DELIVERABLE' | null;
} {
if (deliverable.type === DeliverableType.MANUTENCAO) {
return { value: deliverable.timeboxManutencao ?? null, source: 'DELIVERABLE' };
}
const ci = deliverable.contractItem;
if (!ci) return { value: null, source: null };
switch (deliverable.type) {
case DeliverableType.DESCOBERTA:
return { value: ci.timeboxDescoberta, source: 'CONTRACT_ITEM' };
case DeliverableType.DESIGN:
return { value: ci.timeboxDesign, source: 'CONTRACT_ITEM' };
case DeliverableType.ARQUITETURA:
return { value: ci.timeboxArquitetura, source: 'CONTRACT_ITEM' };
case DeliverableType.CONSTRUCAO:
return { value: ci.timeboxConstrucao, source: 'CONTRACT_ITEM' };
default:
return { value: null, source: null };
}
}
export function EntregavelValuationCard({ deliverable }: Props) {
const ci = deliverable.contractItem;
const isSaas = ci?.itemType === ContractItemType.SAAS_LICENSE;
const { value: timebox, source } = resolveTimebox(deliverable);
const totalValueNumber = deliverable.totalValue != null ? Number(deliverable.totalValue) : null;
if (deliverable.type === DeliverableType.LICENCA) {
if (totalValueNumber == null || ci?.ustValue == null) {
return (
<ValuationWarning
title="Item de contrato sem valor da UST"
description="Configure o valor da UST no Item de Contrato vinculado para calcular o valor da licença."
/>
);
}
return (
<div className="rounded-lg border border-border bg-card p-5">
<div className="flex items-start gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-isis-blue/10">
<DollarSign className="h-5 w-5 text-isis-blue" />
</div>
<div className="flex flex-1 flex-col gap-2">
<div>
<p className="text-[11px] font-medium uppercase tracking-wider text-text-muted">
Valor Total
</p>
<p className="text-2xl font-bold text-isis-blue">
{formatCurrency(totalValueNumber)}
</p>
</div>
<p className="text-small text-text-secondary">
1 licença × R$ {formatNumber(ci.ustValue)}/unidade
</p>
</div>
</div>
</div>
);
}
if (isSaas) {
return (
<div className="rounded-lg border border-border bg-card p-5">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-warning/10">
<DollarSign className="h-5 w-5 text-warning" />
</div>
<div className="flex flex-1 flex-col gap-1">
<p className="text-[11px] font-medium uppercase tracking-wider text-text-muted">
Valor Total
</p>
<p className="text-2xl font-bold text-warning">R$ 0,00</p>
<span className="mt-1 inline-flex w-fit rounded-md bg-warning/20 px-2 py-0.5 text-xs font-medium text-warning">
Licença SaaS faturamento via OS Mãe
</span>
</div>
</div>
</div>
);
}
if (deliverable.type === DeliverableType.MANUTENCAO && deliverable.timeboxManutencao == null) {
return (
<ValuationWarning
title="Time-box de manutenção não informado"
description="Informe o time-box no formulário de edição do Entregável para calcular o valor."
/>
);
}
if (deliverable.type !== DeliverableType.MANUTENCAO && timebox == null) {
return (
<ValuationWarning
title={`Item de contrato sem time-box configurado para o tipo ${DELIVERABLE_TYPE_LABELS[deliverable.type]}`}
description="Configure o time-box correspondente no Item de Contrato vinculado."
/>
);
}
if (totalValueNumber == null || timebox == null || ci?.ustValue == null) {
return (
<div className="rounded-lg border border-border bg-card p-5">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-isis-blue/10">
<DollarSign className="h-5 w-5 text-isis-blue" />
</div>
<div>
<p className="text-[11px] font-medium uppercase tracking-wider text-text-muted">
Valor Total
</p>
<p className="text-body text-text-muted">Não valorado</p>
</div>
</div>
</div>
);
}
return (
<div className="rounded-lg border border-border bg-card p-5">
<div className="flex items-start gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-isis-blue/10">
<DollarSign className="h-5 w-5 text-isis-blue" />
</div>
<div className="flex flex-1 flex-col gap-2">
<div>
<p className="text-[11px] font-medium uppercase tracking-wider text-text-muted">
Valor Total
</p>
<p className="text-2xl font-bold text-isis-blue">{formatCurrency(totalValueNumber)}</p>
</div>
<p className="text-small text-text-secondary">
{formatNumber(timebox)}h × {deliverable.numWeeks} sem × R$ {formatNumber(ci.ustValue)}
/UST
</p>
<p className="text-xs text-text-muted">
Time-box{' '}
{source === 'DELIVERABLE'
? '(definido no Entregável)'
: '(definido no Item de Contrato)'}
</p>
</div>
</div>
</div>
);
}
function ValuationWarning({ title, description }: { title: string; description: string }) {
return (
<div className="rounded-lg border border-warning/30 bg-warning/10 p-5">
<div className="flex items-start gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-warning/20">
<DollarSign className="h-5 w-5 text-warning" />
</div>
<div>
<p className="text-body font-medium text-warning">{title}</p>
<p className="mt-1 text-small text-text-secondary">{description}</p>
</div>
</div>
</div>
);
}