feat: finalizar Tablero CDC con multipais, Fulgencio y tarifario

This commit is contained in:
Isaac_Aracena
2026-06-16 14:23:30 -04:00
parent 8c3d18814d
commit 1fe2ffba80
17 changed files with 3903 additions and 638 deletions
+526
View File
@@ -0,0 +1,526 @@
export type TariffLevel = {
id: string;
label: string;
reference: string;
min?: number;
max?: number;
hint?: string;
};
export type TariffWorkType = {
id: string;
label: string;
shortLabel: string;
hourReference?: string;
levels: TariffLevel[];
};
export type TariffCatalogItem = {
id: string;
section: "grafico" | "estrategia";
category: string;
service: string;
notes?: string;
workTypes: TariffWorkType[];
};
export const TARIFF_SECTIONS = [
{ id: "grafico", label: "Tarifario Gráfico CDC" },
{ id: "estrategia", label: "Estrategia y Creatividad" },
] as const;
export const WORK_TYPE_DESIGN = "design_final_art";
export const WORK_TYPE_ADAPTATION = "adaptation_final_art";
export const WORK_TYPE_REFERENCE = "reference";
function level(
id: string,
label: string,
reference: string,
min?: number,
max?: number,
hint?: string,
): TariffLevel {
return { id, label, reference, min, max, hint };
}
function graphicWorkTypes({
designHour = "35 - 50",
adaptationHour = "30",
design,
adaptation,
}: {
designHour?: string;
adaptationHour?: string;
design: TariffLevel[];
adaptation?: TariffLevel[];
}): TariffWorkType[] {
const workTypes: TariffWorkType[] = [
{
id: WORK_TYPE_DESIGN,
label: "Diseño + Entregable Arte Final",
shortLabel: "Diseño + AF",
hourReference: designHour,
levels: design,
},
];
if (adaptation?.length) {
workTypes.push({
id: WORK_TYPE_ADAPTATION,
label: "Adaptación Arte Final",
shortLabel: "Adaptación AF",
hourReference: adaptationHour,
levels: adaptation,
});
}
return workTypes;
}
export const TARIFF_CATALOG: TariffCatalogItem[] = [
{
id: "pdv-materiales-basicos",
section: "grafico",
category: "Materiales de PDV estándar",
service:
"Cenefas, habladores, danglers, rompetráficos, afiches, backings, rollups, uniformes, piezas WhatsApp/web",
notes:
"Incluye diseño en alta resolución, troqueles y 3 rondas de cambios. Cambios adicionales pueden agregarse como Otros.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "50 - 80", 50, 80),
level("intermediate", "Intermedio", "100 - 150", 100, 150),
level("advanced", "Avanzado", "150 - 300", 150, 300),
],
adaptation: [
level("basic", "Básico", "30 - 50", 30, 50),
level("intermediate", "Intermedio", "60 - 80", 60, 80),
level("advanced", "Avanzado", "100 - 150", 100, 150),
],
}),
},
{
id: "pdv-promocion-pack",
section: "grafico",
category: "Promoción en PDV",
service: "Quick counter + roll up + mecánica de canje + uniforme (pack 3 a 5 piezas o más)",
notes:
"Montos por paquete de 3 a 5 piezas. Piezas o solicitudes extra deben agregarse como Otros.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "320 - 500", 320, 500),
level("intermediate", "Intermedio", "500 - 700", 500, 700),
level("advanced", "Avanzado", "800 - 1000", 800, 1000),
],
adaptation: [
level("basic", "Básico", "200 - 350", 200, 350),
level("intermediate", "Intermedio", "350 - 550", 350, 550),
level(
"advanced",
"Avanzado",
"600 - 800+",
600,
800,
"Puede superar el rango si el caso lo requiere.",
),
],
}),
},
{
id: "ooh-gigantografias",
section: "grafico",
category: "OOH y gigantografías",
service: "Vallas, mupies y gigantografías",
notes: "Los niveles están asociados a tamaño aproximado: 6m², más de 10m² y más de 20m².",
workTypes: graphicWorkTypes({
design: [
level("basic", "6m² aprox", "250 - 350", 250, 350),
level("intermediate", "Más 10m²", "400 - 650", 400, 650),
level("advanced", "Más de 20m²", "700 - 1100", 700, 1100),
],
adaptation: [
level("basic", "6m² aprox", "100 - 150", 100, 150),
level("intermediate", "Más 10m²", "200 - 300", 200, 300),
level("advanced", "Más de 20m²", "400 - 600", 400, 600),
],
}),
},
{
id: "stands-muebles-exhibidores",
section: "grafico",
category: "Stands, muebles y exhibidores",
service: "One Way, cabeceras de góndola, revestimiento de islas y displays básicos",
notes: "Incluye diseño en alta resolución, troqueles y 3 rondas de cambios.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "300 - 400", 300, 400),
level("intermediate", "Intermedio", "450 - 650", 450, 650),
level("advanced", "Avanzado", "700 - 1000", 700, 1000),
],
adaptation: [
level("basic", "Básico", "200 - 300", 200, 300),
level("intermediate", "Intermedio", "300 - 400", 300, 400),
level("advanced", "Avanzado", "450 - 600", 450, 600),
],
}),
},
{
id: "proyectos-especiales-muebles",
section: "grafico",
category: "Proyectos especiales",
service:
"Diseño de muebles u otros proyectos especiales con planos estructurales y troqueles desde cero",
notes:
"Precio por proyecto. Incluye diseño gráfico/estructural, planos, troqueles, conceptualización y 4 rondas de cambios.",
workTypes: graphicWorkTypes({
design: [level("project", "Precio único", "1000 - 1500", 1000, 1500)],
adaptation: [level("project", "Precio único", "600 - 850", 600, 850)],
}),
},
{
id: "stands-creativos-eventos",
section: "grafico",
category: "Proyectos especiales",
service:
"Stands creativos para ferias/eventos, montaje de eventos especiales y diseños de espacios especiales",
notes:
"Precio por proyecto. Incluye stand creativo, planos estructurales, visualizaciones 3D y 4 rondas de cambios.",
workTypes: graphicWorkTypes({
design: [level("project", "Precio único", "1500 - 3000", 1500, 3000)],
adaptation: [level("project", "Precio único", "800 - 1200", 800, 1200)],
}),
},
{
id: "empaque-simple",
section: "grafico",
category: "Diseño de empaques",
service: "Empaque simple",
notes: "Diseño gráfico original para empaques simples sin troqueles complejos.",
workTypes: graphicWorkTypes({
design: [level("project", "Precio único", "700 - 1000", 700, 1000)],
adaptation: [level("project", "Adaptación / SKU", "100 - 300", 100, 300)],
}),
},
{
id: "empaque-complejo",
section: "grafico",
category: "Diseño de empaques",
service: "Empaque complejo",
notes:
"Diseño gráfico y estructural para empaques con troqueles personalizados o formas no convencionales.",
workTypes: graphicWorkTypes({
design: [level("project", "Precio único", "1000 - 1500", 1000, 1500)],
adaptation: [level("project", "Adaptación / SKU", "100 - 300", 100, 300)],
}),
},
{
id: "brochures",
section: "grafico",
category: "Catálogos, brochures y folletos",
service: "Brochures",
notes: "Incluye diseño y 3 rondas de cambios.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "200 - 400", 200, 400),
level("intermediate", "Intermedio", "400 - 600", 400, 600),
level("advanced", "Avanzado", "600 - 800", 600, 800),
],
adaptation: [
level("basic", "Básico", "50 - 100", 50, 100),
level("intermediate", "Intermedio", "100 - 150", 100, 150),
level("advanced", "Avanzado", "150 - 350", 150, 350),
],
}),
},
{
id: "catalogos-folletos-pagina",
section: "grafico",
category: "Catálogos, brochures y folletos",
service: "Catálogos y folletos (costo por página)",
notes: "Costo por página. Incluye diseño y 3 rondas de cambios.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "30 - 60", 30, 60),
level("intermediate", "Intermedio", "60 - 90", 60, 90),
level("advanced", "Avanzado", "90 - 120", 90, 120),
],
adaptation: [
level("basic", "Básico", "15 - 30", 15, 30),
level("intermediate", "Intermedio", "30 - 60", 30, 60),
level("advanced", "Avanzado", "60 - 100", 60, 100),
],
}),
},
{
id: "web-landing",
section: "grafico",
category: "Páginas web",
service: "Landing page o sitio simple hasta 5 páginas",
notes:
"Sitio web básico con diseño estático, contenido del cliente y hasta 2 rondas de cambios.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Diseño web",
shortLabel: "Web",
hourReference: "35 - 50",
levels: [level("project", "Precio único", "800 - 1500", 800, 1500)],
},
],
},
{
id: "web-intermedio",
section: "grafico",
category: "Páginas web",
service: "Sitio corporativo intermedio de 6 a 15 páginas",
notes:
"Sitio mediano con páginas adicionales y diseño más personalizado. Hasta 3 rondas de cambios.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Diseño web",
shortLabel: "Web",
hourReference: "35 - 50",
levels: [level("project", "Precio único", "2000 - 5000", 2000, 5000)],
},
],
},
{
id: "web-avanzado",
section: "grafico",
category: "Páginas web",
service: "Sitio avanzado: más de 15 páginas, interactividad o integraciones complejas",
notes: "Incluye desarrollo a medida y hasta 5 rondas de cambios.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Diseño web",
shortLabel: "Web",
hourReference: "36 - 50",
levels: [level("project", "Precio único", "4000 - 8000", 4000, 8000)],
},
],
},
{
id: "retoques-fotograficos",
section: "grafico",
category: "Otros servicios",
service: "Retoques fotográficos",
notes: "Incluye 2 rondas de ajustes.",
workTypes: graphicWorkTypes({
design: [
level("basic", "Básico", "20 - 40", 20, 40),
level("intermediate", "Intermedio", "50 - 100", 50, 100),
level("advanced", "Avanzado", "150 - 300", 150, 300),
],
adaptation: undefined,
}),
},
{
id: "videos",
section: "grafico",
category: "Otros servicios",
service: "Videos",
notes: "Referencia por complejidad del video.",
workTypes: graphicWorkTypes({
designHour: "35 - 50",
design: [
level("basic", "Básico", "100 - 250", 100, 250),
level("intermediate", "Intermedio", "250 - 500", 250, 500),
level("advanced", "Avanzado", "500 - 1200", 500, 1200),
],
}),
},
{
id: "copy-puntual",
section: "grafico",
category: "Otros servicios",
service: "Copy puntual para ADS, tarjetas o comunicaciones sueltas",
notes: "Solo copy puntual. Si requiere concepto, presupuestar según proyecto/hora hombre.",
workTypes: graphicWorkTypes({
designHour: "1 - 25",
design: [
level("basic", "Básico", "100", 100, 100),
level("intermediate", "Intermedio", "100 - 200", 100, 200),
level("advanced", "Avanzado", "200 - 500", 200, 500),
],
}),
},
{
id: "render-sencillo",
section: "grafico",
category: "Otros servicios",
service: "Render sencillo de 1 solo plano por unidad",
notes: "No aplica para eventos donde hay que conceptualizar el ambiente total.",
workTypes: graphicWorkTypes({
designHour: "1 - 50",
design: [
level("basic", "Básico", "100", 100, 100),
level("intermediate", "Intermedio", "100 - 150", 100, 150),
level("advanced", "Avanzado", "150 - 300", 150, 300),
],
}),
},
{
id: "carnets",
section: "grafico",
category: "Otros servicios",
service: "Carnets",
notes:
"Referencia especial: diseño base + réplicas. El Director Creativo calcula y escribe el monto final manualmente.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Referencia manual",
shortLabel: "Manual",
levels: [
level("base", "Diseño base", "20 base", 20, 20),
level("replica", "Réplicas", "5 por réplica", 5, 5),
],
},
],
},
{
id: "identidad-basica",
section: "estrategia",
category: "Desarrollo de Identidad Gráfica para Marca",
service: "Desarrollo básico para pequeñas marcas o emprendimientos",
notes:
"Incluye logo, paleta, tipografía, identidad visual básica, hasta 2 conceptos y 3 rondas de cambios.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Básico", "2000 - 4000", 2000, 4000)],
},
],
},
{
id: "identidad-intermedia",
section: "estrategia",
category: "Desarrollo de Identidad Gráfica para Marca",
service: "Desarrollo intermedio para marcas medianas",
notes:
"Incluye nombre si aplica, logotipo, paleta, tipografía, key visuals y manual de marca detallado.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Intermedio", "4000 - 8000", 4000, 8000)],
},
],
},
{
id: "identidad-avanzada",
section: "estrategia",
category: "Desarrollo de Identidad Gráfica para Marca",
service: "Desarrollo avanzado para grandes marcas o redes",
notes:
"Identidad visual robusta con aplicaciones de marca, investigación, key visuals y manual exhaustivo.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Avanzado", "8000 - 15000 o más", 8000, 15000)],
},
],
},
{
id: "campana-key-visual-basica",
section: "estrategia",
category: "Desarrollo de Campaña Creativa",
service: "Concepto y Key Visual básico para campañas pequeñas",
notes:
"Concepto creativo básico, key visual, línea gráfica y eslogan con aplicaciones limitadas.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Básico", "2000 - 3000", 2000, 3000)],
},
],
},
{
id: "campana-creativa-intermedia",
section: "estrategia",
category: "Desarrollo de Campaña Creativa",
service: "Campaña creativa intermedia para lanzamientos medianos",
notes: "Concepto creativo completo, key visuals, eslogan y piezas para distintos medios.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Intermedio", "3000 - 5000", 3000, 5000)],
},
],
},
{
id: "campana-creativa-avanzada",
section: "estrategia",
category: "Desarrollo de Campaña Creativa",
service: "Campaña creativa avanzada para lanzamientos grandes",
notes:
"Desarrollo estratégico y creativo de campaña grande con insights, key visuals y múltiples piezas.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija",
shortLabel: "Fija",
levels: [level("project", "Avanzado", "5000 - 8000 o más", 5000, 8000)],
},
],
},
{
id: "consultoria-estrategica",
section: "estrategia",
category: "Desarrollo Estratégico y Conceptual",
service: "Consultoría estratégica y desarrollo de concepto",
notes:
"Investigación de mercado, análisis de competencia, concepto estratégico y alineación con objetivos del cliente.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Tarifa fija o por hora",
shortLabel: "Referencia",
levels: [
level("project", "Por proyecto", "1500 - 3000", 1500, 3000),
level("hour", "Por hora", "100 - 200 por hora", 100, 200),
],
},
],
},
{
id: "rondas-adicionales-estrategia",
section: "estrategia",
category: "Adicionales estratégicos",
service: "Rondas adicionales de cambios",
notes: "Referencia para trabajo adicional en estrategia o creatividad.",
workTypes: [
{
id: WORK_TYPE_REFERENCE,
label: "Por hora",
shortLabel: "Hora",
levels: [level("hour", "Por hora", "50 - 150 por hora", 50, 150)],
},
],
},
];
export function formatTariffRange(level?: TariffLevel | null) {
if (!level) return "";
return level.reference;
}
export function defaultTariffAmount(level?: TariffLevel | null) {
if (!level) return "";
if (typeof level.min === "number" && typeof level.max === "number") {
return String(level.min === level.max ? level.min : level.min);
}
return "";
}