From 4db2762b0ffccdd5519e698a8c02f87b254a2e02 Mon Sep 17 00:00:00 2001 From: Junior Date: Sat, 14 Mar 2026 10:45:32 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20m=C3=B3dulo=20de=20intelig=C3=AAncia=20?= =?UTF-8?q?de=20mercado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front-end/app/composables/useInteligencia.ts | 49 +++++++++++ front-end/app/pages/inteligencia/index.vue | 85 ++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 front-end/app/composables/useInteligencia.ts create mode 100644 front-end/app/pages/inteligencia/index.vue diff --git a/front-end/app/composables/useInteligencia.ts b/front-end/app/composables/useInteligencia.ts new file mode 100644 index 0000000..086e954 --- /dev/null +++ b/front-end/app/composables/useInteligencia.ts @@ -0,0 +1,49 @@ +// front-end/app/composables/useInteligencia.ts +import { editais } from '~/data/mock/editais' +import { concorrentes } from '~/data/mock/concorrentes' + +export function useInteligencia() { + const totalEditais = editais.length + const vencidas = editais.filter(e => e.status === 'vencida') + const perdidas = editais.filter(e => e.status === 'perdida') + + const taxaVitoria = computed(() => + totalEditais > 0 ? Math.round((vencidas.length / totalEditais) * 100) : 0 + ) + + const valorGanho = computed(() => + vencidas.reduce((acc, e) => acc + e.valorEstimado, 0) + ) + + const ticketMedio = computed(() => + vencidas.length > 0 ? valorGanho.value / vencidas.length : 0 + ) + + const porModalidade = computed(() => { + const counts: Record = {} + for (const e of editais) { + if (!counts[e.modalidade]) counts[e.modalidade] = { total: 0, vitorias: 0 } + counts[e.modalidade].total++ + if (e.status === 'vencida') counts[e.modalidade].vitorias++ + } + return counts + }) + + const motivoPerda = computed(() => { + const motivos = [ + 'GOV - PERDIDO POR PREÇO', + 'GOV - PERDIDO POR DOCUMENTAÇÃO', + 'GOV - PERDIDO NO ALEATÓRIO', + 'GOV - DECLINADO POR REQUISITO TÉCNICO', + 'GOV - DECLINADO OUTROS', + 'Outro', + ] + return motivos.map((m, i) => ({ motivo: m, quantidade: [3, 1, 1, 1, 0, 1][i] ?? 0 })) + }) + + const concorrentesFrequentes = computed(() => + [...concorrentes].sort((a, b) => b.totalDisputas - a.totalDisputas).slice(0, 5) + ) + + return { taxaVitoria, valorGanho, ticketMedio, porModalidade, motivoPerda, concorrentesFrequentes, vencidas, perdidas } +} diff --git a/front-end/app/pages/inteligencia/index.vue b/front-end/app/pages/inteligencia/index.vue new file mode 100644 index 0000000..7d73fe3 --- /dev/null +++ b/front-end/app/pages/inteligencia/index.vue @@ -0,0 +1,85 @@ + + + + + +