Commit inicial - upload de todos os arquivos da pasta
This commit is contained in:
187
src/pages/entregaveis/components/EntregavelValuationCard.tsx
Normal file
187
src/pages/entregaveis/components/EntregavelValuationCard.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user