panels-origin/web-panel/components/WorkProgramGenerateDialog.vue
Cursor Agent dc510236c5
feat: programa de obra por proyecto (import, wizard, Gantt, vs costos)
Implementa el módulo completo de programa de obra vinculado al presupuesto:

- Schema PostgreSQL: work_programs, periods, concept_slots, partida_amounts, progress
- RPC: CRUD, import replace, generate wizard, recalculate partidas, vs-cost
- API: parser Excel Neodata (concepto Barra= + partida erogaciones), endpoints REST
- UI: página /programa-obra con cuadrículas, Gantt, comparativo programado/ejecutado
- Integración: menú, ficha proyecto, banner en presupuesto, permiso manage_budget

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-05 05:17:18 +00:00

59 lines
1.9 KiB
Vue

<template>
<Dialog v-model:visible="visibleModel" header="Generar programa automático" modal :style="{ width: '28rem' }">
<Message severity="info" :closable="false">
Crea un borrador distribuyendo los conceptos del presupuesto según la estrategia elegida.
</Message>
<FormField label="Estrategia" class="mt-3">
<Select v-model="strategy" :options="options" option-label="label" option-value="value" class="w-full" />
</FormField>
<Message v-if="error" severity="error" class="mt-3" :closable="false">{{ error }}</Message>
<template #footer>
<Button label="Cancelar" text @click="visibleModel = false" />
<Button label="Generar" icon="pi pi-bolt" :loading="busy" @click="run" />
</template>
</Dialog>
</template>
<script setup lang="ts">
import FormField from "~/components/padron/FormField.vue";
const props = defineProps<{ visible: boolean; projectId: number }>();
const emit = defineEmits<{ "update:visible": [boolean]; generated: [] }>();
const { api } = useApi();
const toast = useToast();
const visibleModel = computed({
get: () => props.visible,
set: (v) => emit("update:visible", v),
});
const strategy = ref("keyword");
const busy = ref(false);
const error = ref("");
const options = [
{ value: "keyword", label: "Por tipo de partida (preliminares → acabados)" },
{ value: "wbs", label: "Por orden WBS del presupuesto" },
{ value: "amount", label: "Por peso económico" },
];
async function run() {
if (!props.projectId) return;
busy.value = true;
error.value = "";
try {
await api(`/v1/projects/${props.projectId}/work-program/generate`, {
method: "POST",
json: { strategy: strategy.value },
});
toast.add({ severity: "success", summary: "Programa generado", life: 2500 });
emit("generated");
visibleModel.value = false;
} catch (err) {
error.value = err instanceof Error ? err.message : "No se pudo generar";
} finally {
busy.value = false;
}
}
</script>