diff --git a/frontend/src/components/site-finder/analysis/ForecastExportButtons.tsx b/frontend/src/components/site-finder/analysis/ForecastExportButtons.tsx
index 4cf9979e..d5f9b403 100644
--- a/frontend/src/components/site-finder/analysis/ForecastExportButtons.tsx
+++ b/frontend/src/components/site-finder/analysis/ForecastExportButtons.tsx
@@ -11,12 +11,17 @@
* Backend (live on prod):
* GET /api/v1/parcels/{cad}/forecast/export?format=md → text/markdown attachment
* GET /api/v1/parcels/{cad}/forecast/export?format=json → application/json attachment
+ * GET /api/v1/parcels/{cad}/forecast/export?format=docx → Word (.docx) attachment
+ * GET /api/v1/parcels/{cad}/forecast/export?format=pptx → PowerPoint (.pptx) attachment
* GET /api/v1/parcels/{cad}/forecast/export?format=tg → text/plain inline (TG snippet)
* No forecast run → 404 → inline «Отчёт ещё не готов» (no crash, no console spam).
+ *
+ * docx/pptx are binary zip-based OOXML — they go through the SAME response.blob()
+ * path as md/json (never response.text(), which would corrupt the archive).
*/
import { useEffect, useRef, useState } from "react";
-import { Download, Copy, Check } from "lucide-react";
+import { Download, Copy, Check, FileText, Presentation } from "lucide-react";
import { API_BASE_URL } from "@/lib/api";
import { triggerDownload } from "@/lib/download";
@@ -25,7 +30,9 @@ interface Props {
cad: string;
}
-type ExportFormat = "md" | "json";
+// Binary OOXML formats (docx/pptx) share md/json's blob download path; only the
+// query param + filename extension differ, so one discriminant covers them all.
+type ExportFormat = "md" | "json" | "docx" | "pptx";
const NOT_READY_MSG = "Отчёт ещё не готов";
const GENERIC_ERR_MSG = "Не удалось получить отчёт";
@@ -37,7 +44,7 @@ function sanitizeCad(cad: string): string {
return cad.replace(/:/g, "_");
}
-function exportUrl(cad: string, format: "md" | "json" | "tg"): string {
+function exportUrl(cad: string, format: ExportFormat | "tg"): string {
return `${API_BASE_URL}/api/v1/parcels/${encodeURIComponent(
cad,
)}/forecast/export?format=${format}`;
@@ -136,6 +143,8 @@ export function ForecastExportButtons({ cad }: Props) {
const mdLoading = downloadingFormat === "md";
const jsonLoading = downloadingFormat === "json";
+ const docxLoading = downloadingFormat === "docx";
+ const pptxLoading = downloadingFormat === "pptx";
const anyLoading = downloadingFormat !== null || tgLoading;
return (
@@ -176,6 +185,26 @@ export function ForecastExportButtons({ cad }: Props) {
onClick={() => void handleDownload("json")}
icon={}
/>
+
+ void handleDownload("docx")}
+ icon={}
+ />
+
+ void handleDownload("pptx")}
+ icon={}
+ />
{error && (