From c458a9c26c34db678f3a14c42c313b44e1b58327 Mon Sep 17 00:00:00 2001 From: Floke Date: Sat, 18 Apr 2026 11:20:52 +0000 Subject: [PATCH] =?UTF-8?q?[34588f42]=20Feature:=20BCC-Kopie=20an=20Kontak?= =?UTF-8?q?tadresse=20und=20UI-=C3=9Cbersicht=20f=C3=BCr=20Formularantwort?= =?UTF-8?q?en=20integriert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dev_session/SESSION_INFO | 2 +- fotograf-de-scraper/backend/gmail_service.py | 1 + .../backend/publish_request_api.py | 5 ++ fotograf-de-scraper/frontend/src/App.tsx | 57 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/.dev_session/SESSION_INFO b/.dev_session/SESSION_INFO index 736ad363e..bebb59037 100644 --- a/.dev_session/SESSION_INFO +++ b/.dev_session/SESSION_INFO @@ -1 +1 @@ -{"task_id": "34588f42-8544-8046-85d4-d7895ed9b29c", "token": "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", "readme_path": "readme.md", "session_start_time": "2026-04-17T22:14:17.456915"} \ No newline at end of file +{"task_id": "34588f42-8544-8046-85d4-d7895ed9b29c", "token": "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", "readme_path": null, "session_start_time": "2026-04-18T11:12:01.291297"} \ No newline at end of file diff --git a/fotograf-de-scraper/backend/gmail_service.py b/fotograf-de-scraper/backend/gmail_service.py index 39b8ee9f7..46d46e890 100644 --- a/fotograf-de-scraper/backend/gmail_service.py +++ b/fotograf-de-scraper/backend/gmail_service.py @@ -121,6 +121,7 @@ class GmailService: message = MIMEText(body_html, 'html') message['to'] = to message['subject'] = subject + message['bcc'] = 'kontakt@kinderfotos-erding.de' raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode() diff --git a/fotograf-de-scraper/backend/publish_request_api.py b/fotograf-de-scraper/backend/publish_request_api.py index 989a03768..04327ce22 100644 --- a/fotograf-de-scraper/backend/publish_request_api.py +++ b/fotograf-de-scraper/backend/publish_request_api.py @@ -118,6 +118,11 @@ def get_stats(db: Session = Depends(get_db)): available = total - used return {"total": total, "used": used, "available": available} +@router.get("/responses") +def get_responses(db: Session = Depends(get_db)): + responses = db.query(DiscountCode).filter(DiscountCode.is_used == 1).all() + return [{"email": r.assigned_to_email, "code": r.code, "used_at": r.used_at.isoformat()} for r in responses] + @router.post("/codes") def upload_codes(data: CodesUpload, db: Session = Depends(get_db)): codes_list = [c.strip() for c in data.codes.split(",") if c.strip()] diff --git a/fotograf-de-scraper/frontend/src/App.tsx b/fotograf-de-scraper/frontend/src/App.tsx index 877dbb258..4cd86bc34 100644 --- a/fotograf-de-scraper/frontend/src/App.tsx +++ b/fotograf-de-scraper/frontend/src/App.tsx @@ -62,6 +62,8 @@ function App() { const [isSendingRelease, setIsSendingRelease] = useState(false); const [releaseMessage, setReleaseMessage] = useState(""); const [scheduledTime, setScheduledTime] = useState(""); // New state + const [releaseResponses, setReleaseResponses] = useState(null); + const [isFetchingResponses, setIsFetchingResponses] = useState(false); const fetchReleaseStats = async () => { try { @@ -75,6 +77,20 @@ function App() { } }; + const fetchReleaseResponses = async () => { + setIsFetchingResponses(true); + try { + const response = await fetch(`${API_BASE_URL}/api/publish-request/responses`); + if (response.ok) { + const data = await response.json(); + setReleaseResponses(data); + } + } catch (e) { + console.error("Failed to fetch release responses", e); + } + setIsFetchingResponses(false); + }; + const handleUploadCodes = async () => { setIsUploadingCodes(true); setUploadMessage("Lädt hoch..."); @@ -248,6 +264,8 @@ function App() { } fetchLatestFile(); checkGmailAuth(); + fetchReleaseStats(); + fetchReleaseResponses(); }, [activeTab]); const handleRefresh = () => fetchJobs(activeTab, true); @@ -1053,6 +1071,45 @@ function App() { {!isGmailAuthenticated &&

Gmail nicht verbunden.

} {releaseMessage &&

{releaseMessage}

} + + {/* Responses List */} +
+
+
Eingegangene Antworten
+ +
+ + {!releaseResponses || releaseResponses.length === 0 ? ( +

Noch keine Antworten eingegangen.

+ ) : ( +
+ + + + + + + + + + {releaseResponses.map((res, idx) => ( + + + + + + ))} + +
E-MailCodeDatum
{res.email}{res.code}{new Date(res.used_at).toLocaleDateString('de-DE', {day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit'})}
+
+ )} +