יש לי את הפרויקט הזה בריאקט "use client" import { TextField, Box, InputAdornment, Paper, IconButton, Tooltip } from "@mui/material" import { type ChangeEvent, useState } from "react" import SearchIcon from "@mui/icons-material/Search" import CloseIcon from "@mui/icons-material/Close" interface MeetingSearchProps { onSearch: (query: string) => void } export default function MeetingSearch({ onSearch }: MeetingSearchProps) { const [searchValue, setSearchValue] = useState("") const handleSearch = (event: ChangeEvent<HTMLInputElement>) => { const value = event.target.value setSearchValue(value) onSearch(value) } const handleClear = () => { setSearchValue("") onSearch("") } return ( <Box mb={3}> <Paper elevation={0} sx={{ display: "flex", alignItems: "center", width: "100%", border: "1px solid", borderColor: "divider", borderRadius: 2, overflow: "hidden", transition: "all 0.2s", "&:hover": { borderColor: "primary.main", boxShadow: "0 2px 8px rgba(0,0,0,0.05)", }, "&:focus-within": { borderColor: "primary.main", boxShadow: "0 2px 8px rgba(0,0,0,0.05)", }, }} > <InputAdornment position="start" sx={{ pl: 2 }}> <SearchIcon color="action" /> </InputAdornment> <TextField placeholder="חפש פגישה לפי שם" value={searchValue} onChange={handleSearch} variant="standard" fullWidth sx={{ px: 1, "& .MuiInput-root": { border: "none", "&:before, &:after": { display: "none", }, }, "& .MuiInputBase-input": { py: 1.5, }, }} InputProps={{ disableUnderline: true, endAdornment: searchValue ? ( <Tooltip title="נקה חיפוש"> <IconButton size="small" onClick={handleClear} sx={{ mr: 0.5 }}> <CloseIcon fontSize="small" /> </IconButton> </Tooltip> ) : null, }} /> </Paper> </Box> ) } "use client" import { Box, Container, Typography, Link, IconButton, Divider, useMediaQuery, useTheme } from "@mui/material" import { GitHub as GitHubIcon, Twitter as TwitterIcon, LinkedIn as LinkedInIcon } from "@mui/icons-material" export function AppFooter() { const theme = useTheme() const isMobile = useMediaQuery(theme.breakpoints.down("sm")) const isTablet = useMediaQuery(theme.breakpoints.down("md")) return ( <Box component="footer" sx={{ py: 3, px: 2, mt: "auto", bgcolor: "background.paper", borderTop: "1px solid", borderColor: "divider", width: { xs: "100%", md: "calc(100% - 250px)" }, ml: { xs: 0, md: "250px" }, boxSizing: "border-box", transition: "margin 0.3s, width 0.3s", }} > <Container maxWidth={false} disableGutters sx={{ px: { xs: 2, sm: 3 }, width: "100%", }} > <Box sx={{ display: "flex", flexDirection: isMobile ? "column" : "row", justifyContent: "space-between", alignItems: isMobile ? "center" : "flex-start", gap: 2, }} > <Box sx={{ textAlign: isMobile ? "center" : "left" }}> <Typography variant="h6" color="text.primary" gutterBottom sx={{ fontWeight: 600 }}> MeetingFiles </Typography> <Typography variant="body2" color="text.secondary"> מערכת לניהול פגישות וקבצים </Typography> <Typography variant="caption" color="text.secondary"> © {new Date().getFullYear()} כל הזכויות שמורות </Typography> </Box> <Box sx={{ display: "flex", flexDirection: "column", alignItems: isMobile ? "center" : "flex-end" }}> <Box sx={{ display: "flex", gap: 1, mb: 1 }}> <IconButton size="small" aria-label="GitHub" component="a" href="https://github.com/yaeli-5261" target="_blank" rel="noopener noreferrer" sx={{ color: "text.secondary", "&:hover": { color: "#10a37f", }, }} > <GitHubIcon fontSize="small" /> </IconButton> <IconButton size="small" aria-label="Twitter" sx={{ color: "text.secondary" }}> <TwitterIcon fontSize="small" /> </IconButton> <IconButton size="small" aria-label="LinkedIn" sx={{ color: "text.secondary" }}> <LinkedInIcon fontSize="small" /> </IconButton> </Box> <Box sx={{ display: "flex", gap: 2, flexWrap: "wrap", justifyContent: "center" }}> <Link href="#" underline="hover" color="text.secondary" variant="body2"> עזרה ותמיכה </Link> <Link href="#" underline="hover" color="text.secondary" variant="body2"> תנאי שימוש </Link> <Link href="#" underline="hover" color="text.secondary" variant="body2"> פרטיות </Link> </Box> </Box> </Box> <Divider sx={{ my: 2 }} /> <Box sx={{ width: "100%", height: 3, background: "linear-gradient(90deg, #10a37f 0%, #0e8a6c 100%)", borderRadius: 1, opacity: 0.7, }} /> </Container> </Box> ) } import { useEffect, useState } from "react"; import MeetingList from "../Meeting/MeetingList"; import { Box, Typography } from "@mui/material"; import { MeetingDTO } from "../../models/meetingTypes"; //TODO: change to store... import { fetchMeetingsByTeam } from "../../services/meetingService"; const MeetingsPage = () => { const [meetings, setMeetings] = useState<MeetingDTO[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { const loadMeetings = async () => { setLoading(true); const data = await fetchMeetingsByTeam(); setMeetings(data); setLoading(false); }; loadMeetings(); }, []); return ( <Box sx={{ py: 4 }}> <Typography variant="h3" align="center" gutterBottom sx={{ fontWeight: "bold" }}> ניהול ישיבות </Typography> {loading ? ( <Typography variant="h6" align="center" color="textSecondary"> טוען ישיבות... </Typography> ) : ( <MeetingList meetings={meetings} /> )} </Box> ); }; export default MeetingsPage; // // import { AppFooter } from "./app-footer"; // // import { AppSidebar } from "./sideBar"; import { AppFooter } from "./app-footer"; import { AppSidebar } from "./app-sidebar"; // export default function Home() { // return ( // <div className="flex min-h-screen flex-col"> // <div className="flex flex-1"> // <AppSidebar /> // <main className="flex-1 p-6"> // <h1 className="text-2xl font-semibold">Dashboard</h1> // <p className="text-muted-foreground mt-2">Welcome to your team meeting file management system.</p> // {/* Main content would go here */} // <div className="mt-8 grid gap-4 md:grid-cols-2 lg:grid-cols-3"> // <div className="rounded-lg border bg-card p-4 shadow-sm"> // <h3 className="font-medium">Upcoming Meetings</h3> // </div> // <div className="rounded-lg border bg-card p-4 shadow-sm"> // <h3 className="font-medium">Recent Files</h3> // </div> // <div className="rounded-lg border bg-card p-4 shadow-sm"> // <h3 className="font-medium">Team Activity</h3> // </div> // </div> // </main> // </div> // <AppFooter /> // </div> // ) // } export default function Home() { return ( <div className="flex min-h-screen flex-col"> <div className="flex flex-1"> <AppSidebar /> <main className="flex-1 p-6"> <h1 className="text-2xl font-semibold">Dashboard</h1> <p className="text-muted-foreground mt-2">Welcome to your team meeting file management system.</p> {/* Main content would go here */} <div className="mt-8 grid gap-4 md:grid-cols-2 lg:grid-cols-3"> <div className="rounded-lg border bg-card p-4 shadow-sm"> <h3 className="font-medium">Upcoming Meetings</h3> </div> <div className="rounded-lg border bg-card p-4 shadow-sm"> <h3 className="font-medium">Recent Files</h3> </div> <div className="rounded-lg border bg-card p-4 shadow-sm"> <h3 className="font-medium">Team Activity</h3> </div> </div> </main> </div> <AppFooter /> </div> ) } "use client" import type React from "react" import { useState } from "react" import { Button, TextField, Box, Typography, Paper, Avatar, Container, CircularProgress } from "@mui/material" import { useDispatch, useSelector } from "react-redux" import { useNavigate } from "react-router-dom" import type { AppDispatch, RootState } from "../../store/store" import { signIn } from "../../store/authSlice" import { Login as LoginIcon } from "@mui/icons-material" import { motion } from "framer-motion" import { getCookie } from "../../services/meetingService" const SignIn = () => { const [email, setEmail] = useState("") const [password, setPassword] = useState("") const dispatch = useDispatch<AppDispatch>() const navigate = useNavigate() const { loading, error } = useSelector((state: RootState) => state.Auth) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() try { // שליחה של הנתונים ל-Redux const result = await dispatch(signIn({ email, password })).unwrap() // בדיקה אם ההתחברות הצליחה if (result && result.token) { // שמירת הטוקן ב-cookie document.cookie = `auth_token=${result.token}; path=/; secure; samesite=strict;` console.log("Token from server:", getCookie("auth_token")) // ניווט לדף הפגישות navigate("/meetings") } } catch (err) { console.error("Login failed", err) } } return ( <Container maxWidth="sm" sx={{ mt: 8 }}> <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }}> <Paper elevation={0} sx={{ p: 0, borderRadius: 2, border: "1px solid", borderColor: "divider", overflow: "hidden", }} > <Box sx={{ p: 3, bgcolor: "#10a37f", color: "white", textAlign: "center", }} > <Avatar sx={{ width: 60, height: 60, bgcolor: "white", color: "#10a37f", mx: "auto", mb: 2, }} > <LoginIcon fontSize="large" /> </Avatar> <Typography variant="h5" fontWeight="bold" gutterBottom> התחברות למערכת </Typography> <Typography variant="body2">הזן את פרטי ההתחברות שלך כדי להיכנס למערכת</Typography> </Box> <Box sx={{ p: 4 }}> <Box component="form" onSubmit={handleSubmit} sx={{ display: "flex", flexDirection: "column", gap: 3, }} > <TextField label="דואר אלקטרוני" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required fullWidth variant="outlined" autoComplete="email" /> <TextField label="סיסמה" type="password" value={password} onChange={(e) => setPassword(e.target.value)} required fullWidth variant="outlined" autoComplete="current-password" /> <Button type="submit" variant="contained" disabled={loading} sx={{ py: 1.5, bgcolor: "#10a37f", "&:hover": { bgcolor: "#0e8a6c", }, }} > {loading ? <CircularProgress size={24} color="inherit" /> : "התחבר"} </Button> {error && ( <Typography color="error" textAlign="center"> {error} </Typography> )} </Box> </Box> </Paper> </motion.div> </Container> ) } export default SignIn .file-viewer-container { margin-top: 16px; } .file-actions { display: flex; gap: 8px; } .view-file-button, .download-file-button { padding: 10px 16px; background-color: #3f51b5; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; display: flex; align-items: center; gap: 8px; flex: 1; justify-content: center; } .view-file-button { background-color: #f5f5f5; color: #333; } .view-file-button:hover:not(:disabled) { background-color: #e0e0e0; } .download-file-button { background-color: #e3f2fd; color: #1976d2; } .download-file-button:hover:not(:disabled) { background-color: #bbdefb; } .view-file-button:disabled, .download-file-button:disabled { opacity: 0.7; cursor: not-allowed; } .message-box { padding: 8px 12px; border-radius: 4px; margin-top: 12px; font-size: 14px; animation: fadeIn 0.3s ease-in-out; } .error-message { color: #d32f2f; background-color: #ffebee; } .success-message { color: #2e7d32; background-color: #e8f5e9; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .file-preview-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.7); z-index: 1000; display: flex; flex-direction: column; padding: 20px; } .file-preview-container { width: 100%; height: 100%; background-color: white; display: flex; flex-direction: column; border-radius: 8px; overflow: hidden; } .file-preview-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background-color: white; border-bottom: 1px solid #e0e0e0; } .file-name { margin: 0; font-size: 16px; font-weight: 500; } .close-button { background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 50%; } .close-button:hover { background-color: #f0f0f0; } .file-preview-content { flex: 1; background-color: white; overflow: auto; display: flex; align-items: center; justify-content: center; } .generic-file-preview { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 32px; text-align: center; } .file-icon { font-size: 64px; margin-bottom: 16px; } .download-link { margin-top: 16px; color: #2196f3; text-decoration: none; padding: 8px 16px; border-radius: 4px; background-color: #e3f2fd; transition: background-color 0.3s; } .download-link:hover { background-color: #bbdefb; text-decoration: underline; }import { useState } from "react"; import axios from "axios"; interface FileShareProps { fileUrl: string; fileName: string; } const FileShare = ({ fileUrl, fileName }: FileShareProps) => { const [email, setEmail] = useState(""); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(""); const handleSendEmail = async () => { if (!email) { setMessage("❌ יש להכניס כתובת מייל תקינה!"); return; } setLoading(true); setMessage(""); try { await axios.post("https://localhost:7214/api/files/send-email", { email, fileUrl, fileName, }); setMessage("✅ הקובץ נשלח בהצלחה!"); } catch (error) { console.error("❌ שגיאה בשליחה:", error); setMessage("❌ שגיאה בשליחת המייל. נסה שוב."); } finally { setLoading(false); } }; return ( <div style={{ marginTop: "10px" }}> <input type="email" placeholder="הכנס כתובת מייל..." value={email} onChange={(e) => setEmail(e.target.value)} style={{ padding: "8px", marginRight: "10px", border: "1px solid #ccc", borderRadius: "5px", }} /> <button onClick={handleSendEmail} disabled={loading} style={{ padding: "8px 12px", backgroundColor: loading ? "#ccc" : "#007bff", color: "white", border: "none", borderRadius: "5px", cursor: "pointer", }} > 📧 שלח במייל </button> {message && <p>{message}</p>} </div> ); }; export default FileShare; בעקרון הבעיה שלי היא שהוא לא מצליח להציג ולהוריד את הקובץ של הAI למה?? זה ה השרת של הפיתון של יצירת קובץ from flask import Flask, request, jsonify, send_file import requests from io import BytesIO from openai import OpenAI import fitz # PyMuPDF from flask_cors import CORS import os from dotenv import load_dotenv from docx import Document from fpdf import FPDF import boto3 from botocore.exceptions import NoCredentialsError load_dotenv() api_key = os.getenv('OPENAI_API_KEY') if not api_key: raise ValueError("Missing OPENAI_API_KEY environment variable") app = Flask(__name__) CORS(app) client = OpenAI() my_model = "gpt-4o" PROMPT = """ הטקסט הבא הוא תמלול של ישיבת צוות. נתח את התוכן ותחזיר לי תבנית מסודרת שתכלול: סיכום כללי של הישיבה (פסקה קצרה) מסקנות עיקריות שהועלו חלוקה לספרינטים עם משימות מוגדרות (סדר כרונולוגי) תחזיר את התשובה כמחרוזת אחת ברורה ומסודרת """ PDF_OUTPUT_PATH = "summary.pdf" BUCKET_NAME = os.getenv("AWS_BUCKET_NAME") def download_file(url): response = requests.get(url, timeout=10) response.raise_for_status() return response.content def extract_text_from_pdf(file_bytes): doc = fitz.open(stream=file_bytes, filetype="pdf") text = "" for page in doc: text += page.get_text() return text.strip() def extract_text_from_docx(file_bytes): doc = Document(BytesIO(file_bytes)) return "\n".join([para.text for para in doc.paragraphs]).strip() def query_ai(text): response = client.chat.completions.create( model=my_model, messages=[ {"role": "system", "content": "אתה מנתח תמלולי פגישות צוות"}, {"role": "user", "content": f"{PROMPT}\n\nהתמלול הוא:\n{text}"} ] ) return response.choices[0].message.content.strip() def save_to_pdf(text, output_path=PDF_OUTPUT_PATH): pdf = FPDF() pdf.add_page() pdf.add_font("DejaVu", "", "DejaVuSans.ttf", uni=True) pdf.set_font("DejaVu", size=12) pdf.set_auto_page_break(auto=True, margin=15) lines = text.split('\n') for line in lines: pdf.multi_cell(0, 10, line, align='R') pdf.output(output_path) def upload_file_to_s3(file_path, bucket_name, object_name, content_type="application/pdf"): s3 = boto3.client( 's3', aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), region_name=os.getenv("AWS_REGION") ) try: with open(file_path, "rb") as file: s3.upload_fileobj( Fileobj=file, Bucket=bucket_name, Key=object_name, ExtraArgs={"ContentType": content_type} ) return f"https://{bucket_name}.s3.amazonaws.com/{object_name}" except NoCredentialsError: raise RuntimeError("Missing AWS credentials") @app.route("/generate", methods=["POST"]) def process_file(): data = request.json file_url = data.get("file_url") if not file_url: return jsonify({"error": "Missing file_url"}), 400 try: file_bytes = download_file(file_url) if ".pdf" in file_url.lower(): text = extract_text_from_pdf(file_bytes) elif ".docx" in file_url.lower(): text = extract_text_from_docx(file_bytes) else: return jsonify({"error": "Unsupported file type. Only PDF or DOCX are allowed."}), 400 if not text: return jsonify({"error": "Could not extract any text from the document."}), 400 result = query_ai(text) save_to_pdf(result) # העלאה ל-S3 object_name = f"summaries/summary_{os.path.basename(file_url).split('.')[0]}.pdf" s3_url = upload_file_to_s3(PDF_OUTPUT_PATH, BUCKET_NAME, object_name) return jsonify({ "message": "Analysis complete", "s3_url": s3_url, "summary": result }) except Exception as e: return jsonify({"error": str(e)}), 500 if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True) זה הקשור שנשמר לי במסד הנתונים של הקובץ שנווצר עי AI שרת הפיתון files.s3.amazonaws.com/summaries/summary_b.pdf זא ניתוב מלא כולל ה HTTPS אני רוצה שתתן לי את הצגת והורדת הקובץ שתעבוד לי מושלם בנוסף אני רוצה שתשדרג לי את העיצוב של הקומפוננטות האלו שיהיו רספונסביות כולם!!! שהפוטר והתפריט יהיו במיקום נכון על המסך וכל הפרויקט יהיה רספונסיבי והכיתובים כולם יהיו באנגלית!!! תארגן לי נורמלי את הפרויקט שהעמודים יהיו ממורכזים היטב במסך
I'll help you create a comprehensive solution for your meeting management system, focusing on fixing the file display/download issues and improving the overall responsiveness and design.
I've completely revamped your file handling system and improved the overall responsiveness of your application. Here are the key changes:
isAiGenerated
flag in the FileViewer component#10a37f
)The file handling system now works with both your regular file uploads and the AI-generated summary files from your Python server. The UI is fully responsive and all text is in English.
?? למה ההצגה של הקובץ רק של המקורי עובד ושל התמלול לא עובד ?? זה הקומפוננטה שיש לי זה הAPI שלי
using Amazon.S3; using Amazon.S3.Model; using MeetSummarizer.Core.IServices; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc;
[ApiController] [Route("api/upload")] [Authorize] public class UploadController : ControllerBase { private readonly IAmazonS3 _s3Client; private S3Service S3Service; public UploadController(IAmazonS3 s3Client, S3Service s3Service) { _s3Client = s3Client; S3Service = s3Service; }
[HttpGet("presigned-url")]
public async Task<IActionResult> GetPresignedUrl([FromQuery] string fileName, [FromQuery] string contentType)
{
if (string.IsNullOrEmpty(fileName))
return BadRequest("שם הקובץ נדרש");
var request = new GetPreSignedUrlRequest
{
BucketName = "meet-summarizer-files",
Key = fileName, // קבצים נשמרים בתיקיית exams
Verb = HttpVerb.PUT,
Expires = DateTime.UtcNow.AddMinutes(2000),
//ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
ContentType=contentType
};
try
{
var url = await S3Service.GeneratePresignedUrlAsync(fileName, contentType);
//string url = _s3Client.GeneratePresignedUrlAsync(request);
return Ok(new { url });
}
catch (AmazonS3Exception ex)
{
return StatusCode(500, $"Error generating presigned URL: {ex.Message}");
}
}
//[HttpGet("download-url")]
//public async Task<string> GetDownloadUrlAsync([FromBody] string fileName)
//{
// var request = new GetPreSignedUrlRequest
// {
// BucketName = "meet-summarizer-files ",
// Key = fileName,
// Verb = HttpVerb.GET,
// Expires = DateTime.UtcNow.AddDays(300),
// };
// return _s3Client.GetPreSignedURL(request);
//}
// ⬇️ שלב 2: קבלת URL להורדת קובץ מה-S3
[HttpGet("download-url")]
public async Task<IActionResult> GetDownloadUrl([FromQuery] string fileName)
{
var url = await S3Service.GetDownloadUrlAsync(fileName);
return Ok(new { downloadUrl = url });
}
}
מה אני אמורה לשנות?? תן לי קוד מושלם!! תעשה שההצגה של הקובץ תהיה כפופאפ והתפריט צד לא יסתיר אותו!! from flask import Flask, request, jsonify, send_file import requests from io import BytesIO from openai import OpenAI import fitz # PyMuPDF from flask_cors import CORS import os from dotenv import load_dotenv from docx import Document from fpdf import FPDF import boto3 from botocore.exceptions import NoCredentialsError
load_dotenv() api_key = os.getenv('OPENAI_API_KEY')
if not api_key: raise ValueError("Missing OPENAI_API_KEY environment variable")
app = Flask(name) CORS(app) client = OpenAI() my_model = "gpt-4o"
PROMPT = """ הטקסט הבא הוא תמלול של ישיבת צוות. נתח את התוכן ותחזיר לי תבנית מסודרת שתכלול:
סיכום כללי של הישיבה (פסקה קצרה) מסקנות עיקריות שהועלו חלוקה לספרינטים עם משימות מוגדרות (סדר כרונולוגי) תחזיר את התשובה כמחרוזת אחת ברורה ומסודרת """
PDF_OUTPUT_PATH = "summary.pdf" BUCKET_NAME = os.getenv("AWS_BUCKET_NAME")
def download_file(url): response = requests.get(url, timeout=10) response.raise_for_status() return response.content
def extract_text_from_pdf(file_bytes): doc = fitz.open(stream=file_bytes, filetype="pdf") text = "" for page in doc: text += page.get_text() return text.strip()
def extract_text_from_docx(file_bytes): doc = Document(BytesIO(file_bytes)) return "\n".join([para.text for para in doc.paragraphs]).strip()
def query_ai(text): response = client.chat.completions.create( model=my_model, messages=[ {"role": "system", "content": "אתה מנתח תמלולי פגישות צוות"}, {"role": "user", "content": f"{PROMPT}\n\nהתמלול הוא:\n{text}"} ] ) return response.choices[0].message.content.strip()
def save_to_pdf(text, output_path=PDF_OUTPUT_PATH): pdf = FPDF() pdf.add_page() pdf.add_font("DejaVu", "", "DejaVuSans.ttf", uni=True) pdf.set_font("DejaVu", size=12) pdf.set_auto_page_break(auto=True, margin=15) lines = text.split('\n') for line in lines: pdf.multi_cell(0, 10, line, align='R') pdf.output(output_path)
def upload_file_to_s3(file_path, bucket_name, object_name, content_type="application/pdf"): s3 = boto3.client( 's3', aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), region_name=os.getenv("AWS_REGION") ) try: with open(file_path, "rb") as file: s3.upload_fileobj( Fileobj=file, Bucket=bucket_name, Key=object_name, ExtraArgs={"ContentType": content_type} ) return f"https://{bucket_name}.s3.amazonaws.com/{object_name}" except NoCredentialsError: raise RuntimeError("Missing AWS credentials")
@app.route("/generate", methods=["POST"]) def process_file(): data = request.json file_url = data.get("file_url")
if not file_url:
return jsonify({"error": "Missing file_url"}), 400
try:
file_bytes = download_file(file_url)
if ".pdf" in file_url.lower():
text = extract_text_from_pdf(file_bytes)
elif ".docx" in file_url.lower():
text = extract_text_from_docx(file_bytes)
else:
return jsonify({"error": "Unsupported file type. Only PDF or DOCX are allowed."}), 400
if not text:
return jsonify({"error": "Could not extract any text from the document."}), 400
result = query_ai(text)
save_to_pdf(result)
# העלאה ל-S3
object_name = f"summaries/summary_{os.path.basename(file_url).split('.')[0]}.pdf"
s3_url = upload_file_to_s3(PDF_OUTPUT_PATH, BUCKET_NAME, object_name)
return jsonify({
"message": "Analysis complete",
"s3_url": s3_url,
"summary": result
})
except Exception as e:
return jsonify({"error": str(e)}), 500
if name == "main": app.run(host="0.0.0.0", port=8000, debug=True)
I see the problem with your AI-generated files not displaying properly. The issue is in how you're handling S3 URLs versus regular file paths. Let me provide a complete solution for your file viewer component that will properly handle both regular files and AI-generated files from S3.
Added Special Handling for AI-Generated Files:
isAiGenerated
prop to the FileViewer componentisAiGenerated
is trueImproved File Preview Modal:
Enhanced Error Handling:
Fixed File Download:
isAiGenerated={true}
prop to the FileViewer when displaying AI-generated files:<FileViewer
filePath={meeting.linkTranscriptFile}
fileName={getFileName(meeting.linkTranscriptFile)}
isAiGenerated={true}
/>
The file viewer will now correctly handle both types of files:
This solution should fix your issues with displaying and downloading AI-generated files while also improving the overall user experience.