diff --git a/src/components/areas/AreasView.tsx b/src/components/areas/AreasView.tsx
index 40ff800..7e33cf3 100644
--- a/src/components/areas/AreasView.tsx
+++ b/src/components/areas/AreasView.tsx
@@ -40,6 +40,7 @@ export function AreasView() {
const [loadingList, setLoadingList] = useState(true);
const totalPages = Math.max(1, totalPaginas);
+ const isFiltering = filterNome.trim().length > 0;
useEffect(() => {
let cancelled = false;
@@ -262,7 +263,7 @@ export function AreasView() {
- {!loadingList && areasList.length === 0 && totalRegistros === 0 ? (
+ {!loadingList && totalRegistros === 0 && !isFiltering ? (
@@ -297,6 +298,12 @@ export function AreasView() {
Carregando...
+ ) : totalRegistros === 0 ? (
+
+
+ Nenhum resultado para o filtro {filterNome.trim() ? `"${filterNome.trim()}"` : "atual"}.
+
+
) : (
areasList.map((item) => (
diff --git a/src/components/audio/AudioView.tsx b/src/components/audio/AudioView.tsx
index 15315c7..4d9a905 100644
--- a/src/components/audio/AudioView.tsx
+++ b/src/components/audio/AudioView.tsx
@@ -14,6 +14,7 @@ import {
SelectValue,
} from "@/components/ui/select";
import { useToast } from "@/hooks/use-toast";
+import { audioGenerationService } from "@/services/audioGeneration";
const SUPPORTED_FORMATS = ['flac', 'm4a', 'mp3', 'mp4', 'mpeg', 'mpga', 'oga', 'ogg', 'wav', 'webm'];
const MAX_FILE_SIZE = 25 * 1024 * 1024; // 25 MB
@@ -186,19 +187,23 @@ export const AudioView = () => {
}, 2000);
};
- const handleDownloadAudio = (audio: GeneratedAudio) => {
- // In real implementation, download the actual audio file
- const a = document.createElement('a');
- a.href = audio.audioUrl;
- a.download = `audio_${audio.voiceLabel}_${new Date(audio.timestamp).getTime()}.mp3`;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
-
- toast({
- title: "Download iniciado",
- description: "O arquivo de áudio está sendo baixado.",
- });
+ const handleDownloadAudio = async (audio: GeneratedAudio) => {
+ const filename = `audio_${audio.voiceLabel}_${new Date(audio.timestamp).getTime()}.mp3`;
+ try {
+ await audioGenerationService.downloadAudioFile(audio.audioUrl, filename);
+ toast({
+ title: "Download iniciado",
+ description: "O arquivo de áudio está sendo baixado.",
+ });
+ } catch (error: unknown) {
+ const message =
+ error && typeof error === "object" && "message" in error ? String((error as { message: string }).message) : "Não foi possível baixar o áudio.";
+ toast({
+ title: "Erro ao baixar",
+ description: message,
+ variant: "destructive",
+ });
+ }
};
const handleDeleteAudio = (audioId: string) => {
@@ -482,10 +487,11 @@ export const AudioView = () => {
diff --git a/src/components/audio/GenerationView.tsx b/src/components/audio/GenerationView.tsx
index 8444f7f..793bf48 100644
--- a/src/components/audio/GenerationView.tsx
+++ b/src/components/audio/GenerationView.tsx
@@ -134,18 +134,23 @@ export const GenerationView = () => {
}
};
- const handleDownloadAudio = (audio: AudioRecord) => {
- const a = document.createElement('a');
- a.href = audio.audio_url;
- a.download = `audio_${audio.voice}_${new Date(audio.created_at).getTime()}.mp3`;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
-
- toast({
- title: "Download iniciado",
- description: "O arquivo de áudio está sendo baixado.",
- });
+ const handleDownloadAudio = async (audio: AudioRecord) => {
+ const filename = `audio_${audio.voice}_${new Date(audio.created_at).getTime()}.mp3`;
+ try {
+ await audioGenerationService.downloadAudioFile(audio.audio_url, filename);
+ toast({
+ title: "Download iniciado",
+ description: "O arquivo de áudio está sendo baixado.",
+ });
+ } catch (error: unknown) {
+ const message =
+ error && typeof error === "object" && "message" in error ? String((error as { message: string }).message) : "Não foi possível baixar o áudio.";
+ toast({
+ title: "Erro ao baixar",
+ description: message,
+ variant: "destructive",
+ });
+ }
};
const handleDeleteAudio = async (audioId: string) => {
@@ -290,10 +295,11 @@ export const GenerationView = () => {
diff --git a/src/components/chat/ChatMessage.tsx b/src/components/chat/ChatMessage.tsx
index ce829c4..c7a4007 100644
--- a/src/components/chat/ChatMessage.tsx
+++ b/src/components/chat/ChatMessage.tsx
@@ -1,4 +1,5 @@
import { Bot, User, Copy, File } from "lucide-react";
+import ReactMarkdown from "react-markdown";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { useState } from "react";
@@ -53,7 +54,7 @@ export const ChatMessage = ({ role, content, model, attachments }: ChatMessagePr
)}
-
+
{/* Attachments */}
{attachments && attachments.length > 0 && (
@@ -73,15 +74,19 @@ export const ChatMessage = ({ role, content, model, attachments }: ChatMessagePr
)}
-
- {content}
-
+ {isAssistant ? (
+
+ {content}
+
+ ) : (
+
{content}
+ )}
{showActions && (
diff --git a/src/components/parecer-juridico/ParecerJuridicoDetailView.tsx b/src/components/parecer-juridico/ParecerJuridicoDetailView.tsx
index 28ca26a..8474070 100644
--- a/src/components/parecer-juridico/ParecerJuridicoDetailView.tsx
+++ b/src/components/parecer-juridico/ParecerJuridicoDetailView.tsx
@@ -133,8 +133,9 @@ export function ParecerJuridicoDetailView() {
new Date(a.created_at.replace(" ", "T")).getTime() -
new Date(b.created_at.replace(" ", "T")).getTime()
);
+ const nonEmptyMessages = sorted.filter((m) => (m.content ?? "").trim().length > 0);
setMessages(
- sorted.map((m) => ({
+ nonEmptyMessages.map((m) => ({
id: m.id,
role: m.role,
content: m.content,
diff --git a/src/components/parecer-juridico/ParecerJuridicoFormView.tsx b/src/components/parecer-juridico/ParecerJuridicoFormView.tsx
index c366f11..90a325c 100644
--- a/src/components/parecer-juridico/ParecerJuridicoFormView.tsx
+++ b/src/components/parecer-juridico/ParecerJuridicoFormView.tsx
@@ -155,18 +155,32 @@ export function ParecerJuridicoFormView() {
-
+
setTituloParecer(e.target.value)}
placeholder="Ex.: Parecer sobre contrato de prestação de serviços"
className="w-full"
+ required
+ aria-required="true"
/>
-
+