fix: corrige tipo JSON para String no SQLite e acentuação nos textos

- Altera typePreferences e categoryPreferences de Json para String no Prisma
- Atualiza API de preferências para fazer parse/stringify de JSON
- Corrige todos os textos sem acentuação nos componentes de notificação

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-07 21:09:02 -03:00
parent f2c0298285
commit 7ecb4c1110
7 changed files with 104 additions and 89 deletions

View file

@ -462,11 +462,11 @@ model NotificationPreferences {
// Preferências por tipo de notificação (JSON) // Preferências por tipo de notificação (JSON)
// Ex: { "ticket_created": true, "ticket_resolved": true, "comment_public": false } // Ex: { "ticket_created": true, "ticket_resolved": true, "comment_public": false }
typePreferences Json @default("{}") typePreferences String @default("{}")
// Preferências por categoria de ticket (JSON) // Preferências por categoria de ticket (JSON)
// Ex: { "category_id_1": true, "category_id_2": false } // Ex: { "category_id_1": true, "category_id_2": false }
categoryPreferences Json @default("{}") categoryPreferences String @default("{}")
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt

View file

@ -80,10 +80,17 @@ export async function GET(_request: NextRequest) {
? Object.keys(NOTIFICATION_TYPES) ? Object.keys(NOTIFICATION_TYPES)
: COLLABORATOR_VISIBLE_TYPES : COLLABORATOR_VISIBLE_TYPES
// Parse JSON strings
const typePrefs: Record<string, boolean> = prefs!.typePreferences
? JSON.parse(prefs!.typePreferences as string)
: {}
const catPrefs = prefs!.categoryPreferences
? JSON.parse(prefs!.categoryPreferences as string)
: {}
// Monta resposta com configuração de cada tipo // Monta resposta com configuração de cada tipo
const typeConfigs = availableTypes.map((type) => { const typeConfigs = availableTypes.map((type) => {
const config = NOTIFICATION_TYPES[type as NotificationType] const config = NOTIFICATION_TYPES[type as NotificationType]
const typePrefs = prefs!.typePreferences as Record<string, boolean>
const enabled = typePrefs[type] ?? config.defaultEnabled const enabled = typePrefs[type] ?? config.defaultEnabled
return { return {
@ -104,7 +111,7 @@ export async function GET(_request: NextRequest) {
timezone: prefs.timezone, timezone: prefs.timezone,
digestFrequency: prefs.digestFrequency, digestFrequency: prefs.digestFrequency,
types: typeConfigs, types: typeConfigs,
categoryPreferences: prefs.categoryPreferences, categoryPreferences: catPrefs,
isStaff, isStaff,
}) })
} catch (error) { } catch (error) {
@ -194,11 +201,19 @@ export async function PUT(request: NextRequest) {
quietHoursEnd: quietHoursEnd ?? null, quietHoursEnd: quietHoursEnd ?? null,
timezone: timezone ?? "America/Sao_Paulo", timezone: timezone ?? "America/Sao_Paulo",
digestFrequency: digestFrequency ?? "immediate", digestFrequency: digestFrequency ?? "immediate",
typePreferences: validatedTypePrefs, typePreferences: JSON.stringify(validatedTypePrefs),
categoryPreferences: categoryPreferences ?? {}, categoryPreferences: JSON.stringify(categoryPreferences ?? {}),
}, },
}) })
} else { } else {
// Parse existing JSON strings
const existingTypePrefs = existingPrefs.typePreferences
? JSON.parse(existingPrefs.typePreferences as string)
: {}
const existingCatPrefs = existingPrefs.categoryPreferences
? JSON.parse(existingPrefs.categoryPreferences as string)
: {}
// Atualiza preferências existentes // Atualiza preferências existentes
await prisma.notificationPreferences.update({ await prisma.notificationPreferences.update({
where: { userId }, where: { userId },
@ -208,11 +223,11 @@ export async function PUT(request: NextRequest) {
quietHoursEnd: quietHoursEnd !== undefined ? quietHoursEnd : existingPrefs.quietHoursEnd, quietHoursEnd: quietHoursEnd !== undefined ? quietHoursEnd : existingPrefs.quietHoursEnd,
timezone: timezone ?? existingPrefs.timezone, timezone: timezone ?? existingPrefs.timezone,
digestFrequency: digestFrequency ?? existingPrefs.digestFrequency, digestFrequency: digestFrequency ?? existingPrefs.digestFrequency,
typePreferences: { typePreferences: JSON.stringify({
...(existingPrefs.typePreferences as Record<string, boolean>), ...existingTypePrefs,
...validatedTypePrefs, ...validatedTypePrefs,
}, }),
categoryPreferences: categoryPreferences ?? existingPrefs.categoryPreferences, categoryPreferences: JSON.stringify(categoryPreferences ?? existingCatPrefs),
}, },
}) })
} }

View file

@ -5,8 +5,8 @@ import { NotificationPreferencesForm } from "@/components/settings/notification-
import { requireAuthenticatedSession } from "@/lib/auth-server" import { requireAuthenticatedSession } from "@/lib/auth-server"
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Preferencias de notificacao", title: "Preferências de notificação",
description: "Configure quais notificacoes por e-mail deseja receber.", description: "Configure quais notificações por e-mail deseja receber.",
} }
export default async function PortalNotificationSettingsPage() { export default async function PortalNotificationSettingsPage() {
@ -14,7 +14,7 @@ export default async function PortalNotificationSettingsPage() {
const role = (session.user.role ?? "").toLowerCase() const role = (session.user.role ?? "").toLowerCase()
const persona = (session.user.machinePersona ?? "").toLowerCase() const persona = (session.user.machinePersona ?? "").toLowerCase()
// Colaboradores e maquinas com persona de colaborador podem acessar // Colaboradores e máquinas com persona de colaborador podem acessar
const allowedRoles = new Set(["collaborator", "manager", "admin", "agent"]) const allowedRoles = new Set(["collaborator", "manager", "admin", "agent"])
const isMachinePersonaAllowed = role === "machine" && (persona === "collaborator" || persona === "manager") const isMachinePersonaAllowed = role === "machine" && (persona === "collaborator" || persona === "manager")
const allowed = allowedRoles.has(role) || isMachinePersonaAllowed const allowed = allowedRoles.has(role) || isMachinePersonaAllowed
@ -23,7 +23,7 @@ export default async function PortalNotificationSettingsPage() {
redirect("/portal") redirect("/portal")
} }
// Staff deve usar a pagina de configuracoes completa // Staff deve usar a página de configurações completa
const staffRoles = new Set(["admin", "manager", "agent"]) const staffRoles = new Set(["admin", "manager", "agent"])
if (staffRoles.has(role)) { if (staffRoles.has(role)) {
redirect("/settings/notifications") redirect("/settings/notifications")
@ -32,9 +32,9 @@ export default async function PortalNotificationSettingsPage() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h1 className="text-2xl font-semibold tracking-tight">Preferencias de notificacao</h1> <h1 className="text-2xl font-semibold tracking-tight">Preferências de notificação</h1>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Configure quais notificacoes por e-mail deseja receber sobre seus chamados. Configure quais notificações por e-mail deseja receber sobre seus chamados.
</p> </p>
</div> </div>
<NotificationPreferencesForm isPortal /> <NotificationPreferencesForm isPortal />

View file

@ -28,7 +28,7 @@ export default function RatePage() {
const [comment, setComment] = useState("") const [comment, setComment] = useState("")
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
// Se ja avaliou, mostra mensagem // Se já avaliou, mostra mensagem
if (alreadyRated && existingRating) { if (alreadyRated && existingRating) {
return ( return (
<div className="min-h-screen bg-background flex items-center justify-center p-4"> <div className="min-h-screen bg-background flex items-center justify-center p-4">
@ -37,9 +37,9 @@ export default function RatePage() {
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<CheckCircle className="h-16 w-16 text-emerald-500" /> <CheckCircle className="h-16 w-16 text-emerald-500" />
</div> </div>
<CardTitle>Chamado ja avaliado</CardTitle> <CardTitle>Chamado já avaliado</CardTitle>
<CardDescription> <CardDescription>
Voce ja avaliou este chamado anteriormente. Você avaliou este chamado anteriormente.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="text-center"> <CardContent className="text-center">
@ -54,7 +54,7 @@ export default function RatePage() {
))} ))}
</div> </div>
<p className="text-muted-foreground text-sm"> <p className="text-muted-foreground text-sm">
Sua avaliacao: {existingRating} estrela{existingRating > 1 ? "s" : ""} Sua avaliação: {existingRating} estrela{existingRating > 1 ? "s" : ""}
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -62,7 +62,7 @@ export default function RatePage() {
) )
} }
// Se acabou de avaliar, mostra formulario para comentario // Se acabou de avaliar, mostra formulário para comentário
if (submitted && rating > 0) { if (submitted && rating > 0) {
return ( return (
<div className="min-h-screen bg-background flex items-center justify-center p-4"> <div className="min-h-screen bg-background flex items-center justify-center p-4">
@ -71,9 +71,9 @@ export default function RatePage() {
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<CheckCircle className="h-16 w-16 text-emerald-500" /> <CheckCircle className="h-16 w-16 text-emerald-500" />
</div> </div>
<CardTitle>Obrigado pela avaliacao!</CardTitle> <CardTitle>Obrigado pela avaliação!</CardTitle>
<CardDescription> <CardDescription>
Sua opiniao e muito importante para nos. Sua opinião é muito importante para s.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6">
@ -89,10 +89,10 @@ export default function RatePage() {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="comment">Gostaria de deixar um comentario? (opcional)</Label> <Label htmlFor="comment">Gostaria de deixar um comentário? (opcional)</Label>
<Textarea <Textarea
id="comment" id="comment"
placeholder="Conte-nos mais sobre sua experiencia..." placeholder="Conte-nos mais sobre sua experiência..."
value={comment} value={comment}
onChange={(e) => setComment(e.target.value)} onChange={(e) => setComment(e.target.value)}
rows={4} rows={4}
@ -134,7 +134,7 @@ export default function RatePage() {
if (!response.ok) { if (!response.ok) {
const data = await response.json() const data = await response.json()
throw new Error(data.error || "Erro ao enviar comentario") throw new Error(data.error || "Erro ao enviar comentário")
} }
window.close() window.close()
@ -151,7 +151,7 @@ export default function RatePage() {
Enviando... Enviando...
</> </>
) : ( ) : (
"Enviar comentario" "Enviar comentário"
)} )}
</Button> </Button>
</div> </div>
@ -161,7 +161,7 @@ export default function RatePage() {
) )
} }
// Formulario de avaliacao // Formulário de avaliação
return ( return (
<div className="min-h-screen bg-background flex items-center justify-center p-4"> <div className="min-h-screen bg-background flex items-center justify-center p-4">
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
@ -174,7 +174,7 @@ export default function RatePage() {
</div> </div>
<CardTitle>Como foi o atendimento?</CardTitle> <CardTitle>Como foi o atendimento?</CardTitle>
<CardDescription> <CardDescription>
Sua avaliacao nos ajuda a melhorar nosso servico. Sua avaliação nos ajuda a melhorar nosso serviço.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6">
@ -206,12 +206,12 @@ export default function RatePage() {
<span>Excelente</span> <span>Excelente</span>
</div> </div>
{/* Comentario */} {/* Comentário */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="comment">Comentario (opcional)</Label> <Label htmlFor="comment">Comentário (opcional)</Label>
<Textarea <Textarea
id="comment" id="comment"
placeholder="Conte-nos mais sobre sua experiencia..." placeholder="Conte-nos mais sobre sua experiência..."
value={comment} value={comment}
onChange={(e) => setComment(e.target.value)} onChange={(e) => setComment(e.target.value)}
rows={4} rows={4}
@ -225,7 +225,7 @@ export default function RatePage() {
</div> </div>
)} )}
{/* Botao */} {/* Botão */}
<Button <Button
className="w-full" className="w-full"
size="lg" size="lg"
@ -243,7 +243,7 @@ export default function RatePage() {
if (!response.ok) { if (!response.ok) {
const data = await response.json() const data = await response.json()
throw new Error(data.error || "Erro ao enviar avaliacao") throw new Error(data.error || "Erro ao enviar avaliação")
} }
setSubmitted(true) setSubmitted(true)
@ -260,7 +260,7 @@ export default function RatePage() {
Enviando... Enviando...
</> </>
) : ( ) : (
"Enviar avaliacao" "Enviar avaliação"
)} )}
</Button> </Button>
</CardContent> </CardContent>

View file

@ -7,15 +7,15 @@ import { NotificationPreferencesForm } from "@/components/settings/notification-
import { requireAuthenticatedSession } from "@/lib/auth-server" import { requireAuthenticatedSession } from "@/lib/auth-server"
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Preferencias de notificacao", title: "Preferências de notificação",
description: "Configure quais notificacoes por e-mail deseja receber.", description: "Configure quais notificações por e-mail deseja receber.",
} }
export default async function NotificationSettingsPage() { export default async function NotificationSettingsPage() {
const session = await requireAuthenticatedSession() const session = await requireAuthenticatedSession()
const role = (session.user.role ?? "").toLowerCase() const role = (session.user.role ?? "").toLowerCase()
// Apenas staff pode acessar esta pagina // Apenas staff pode acessar esta página
const staffRoles = new Set(["admin", "manager", "agent"]) const staffRoles = new Set(["admin", "manager", "agent"])
if (!staffRoles.has(role)) { if (!staffRoles.has(role)) {
redirect("/portal/profile/notifications") redirect("/portal/profile/notifications")
@ -25,8 +25,8 @@ export default async function NotificationSettingsPage() {
<AppShell <AppShell
header={ header={
<SiteHeader <SiteHeader
title="Preferencias de notificacao" title="Preferências de notificação"
lead="Configure como e quando deseja receber notificacoes por e-mail" lead="Configure como e quando deseja receber notificações por e-mail"
/> />
} }
> >

View file

@ -114,7 +114,7 @@ export default function TicketViewPage() {
try { try {
window.location.href = ravenUrl window.location.href = ravenUrl
} catch { } catch {
// Protocolo nao suportado // Protocolo não suportado
} }
// Aguarda 2 segundos para ver se o protocolo foi aceito // Aguarda 2 segundos para ver se o protocolo foi aceito
@ -123,7 +123,7 @@ export default function TicketViewPage() {
document.body.removeChild(iframe) document.body.removeChild(iframe)
if (!protocolHandled) { if (!protocolHandled) {
// Protocolo nao foi aceito, carrega o ticket no navegador // Protocolo não foi aceito, carrega o ticket no navegador
setTryingRaven(false) setTryingRaven(false)
loadTicket() loadTicket()
} }
@ -153,10 +153,10 @@ export default function TicketViewPage() {
<Loader2 className="h-12 w-12 animate-spin mx-auto mb-4 text-primary" /> <Loader2 className="h-12 w-12 animate-spin mx-auto mb-4 text-primary" />
<h2 className="text-xl font-semibold mb-2">Abrindo no Raven...</h2> <h2 className="text-xl font-semibold mb-2">Abrindo no Raven...</h2>
<p className="text-muted-foreground text-sm"> <p className="text-muted-foreground text-sm">
Se o aplicativo Raven estiver instalado, ele abrira automaticamente. Se o aplicativo Raven estiver instalado, ele abrirá automaticamente.
</p> </p>
<p className="text-muted-foreground text-sm mt-2"> <p className="text-muted-foreground text-sm mt-2">
Caso contrario, o chamado sera exibido aqui em instantes. Caso contrário, o chamado será exibido aqui em instantes.
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -243,15 +243,15 @@ export default function TicketViewPage() {
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6">
{/* Informacoes */} {/* Informações */}
<div className="grid grid-cols-2 gap-4 text-sm"> <div className="grid grid-cols-2 gap-4 text-sm">
<div> <div>
<span className="text-muted-foreground">Solicitante</span> <span className="text-muted-foreground">Solicitante</span>
<p className="font-medium">{ticket.requester.name}</p> <p className="font-medium">{ticket.requester.name}</p>
</div> </div>
<div> <div>
<span className="text-muted-foreground">Responsavel</span> <span className="text-muted-foreground">Responsável</span>
<p className="font-medium">{ticket.assignee?.name ?? "Nao atribuido"}</p> <p className="font-medium">{ticket.assignee?.name ?? "Não atribuído"}</p>
</div> </div>
<div> <div>
<span className="text-muted-foreground">Criado em</span> <span className="text-muted-foreground">Criado em</span>
@ -274,17 +274,17 @@ export default function TicketViewPage() {
{/* Resumo */} {/* Resumo */}
{ticket.summary && ( {ticket.summary && (
<div> <div>
<h3 className="text-sm font-medium text-muted-foreground mb-2">Descricao</h3> <h3 className="text-sm font-medium text-muted-foreground mb-2">Descrição</h3>
<p className="text-sm whitespace-pre-wrap">{ticket.summary}</p> <p className="text-sm whitespace-pre-wrap">{ticket.summary}</p>
</div> </div>
)} )}
{/* Avaliacao */} {/* Avaliação */}
{ticket.rating && ( {ticket.rating && (
<div className="bg-muted/50 rounded-lg p-4"> <div className="bg-muted/50 rounded-lg p-4">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<CheckCircle className="h-4 w-4 text-emerald-500" /> <CheckCircle className="h-4 w-4 text-emerald-500" />
<span className="text-sm font-medium">Avaliacao</span> <span className="text-sm font-medium">Avaliação</span>
</div> </div>
<div className="flex items-center gap-1 mb-2"> <div className="flex items-center gap-1 mb-2">
{[1, 2, 3, 4, 5].map((star) => ( {[1, 2, 3, 4, 5].map((star) => (
@ -302,12 +302,12 @@ export default function TicketViewPage() {
</div> </div>
)} )}
{/* Comentarios */} {/* Comentários */}
{ticket.comments.length > 0 && ( {ticket.comments.length > 0 && (
<div> <div>
<h3 className="text-sm font-medium text-muted-foreground mb-4 flex items-center gap-2"> <h3 className="text-sm font-medium text-muted-foreground mb-4 flex items-center gap-2">
<MessageSquare className="h-4 w-4" /> <MessageSquare className="h-4 w-4" />
Ultimas atualizacoes Últimas atualizações
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
{ticket.comments.map((comment) => ( {ticket.comments.map((comment) => (

View file

@ -36,26 +36,26 @@ interface NotificationPreferencesFormProps {
isPortal?: boolean isPortal?: boolean
} }
// Agrupamento de tipos de notificacao // Agrupamento de tipos de notificação
const TYPE_GROUPS = { const TYPE_GROUPS = {
lifecycle: { lifecycle: {
label: "Ciclo de vida do chamado", label: "Ciclo de vida do chamado",
description: "Notificacoes sobre abertura, resolucao e mudancas de status", description: "Notificações sobre abertura, resolução e mudanças de status",
types: ["ticket_created", "ticket_assigned", "ticket_resolved", "ticket_reopened", "ticket_status_changed", "ticket_priority_changed"], types: ["ticket_created", "ticket_assigned", "ticket_resolved", "ticket_reopened", "ticket_status_changed", "ticket_priority_changed"],
}, },
communication: { communication: {
label: "Comunicacao", label: "Comunicação",
description: "Comentarios e respostas nos chamados", description: "Comentários e respostas nos chamados",
types: ["comment_public", "comment_response", "comment_mention"], types: ["comment_public", "comment_response", "comment_mention"],
}, },
sla: { sla: {
label: "SLA e alertas", label: "SLA e alertas",
description: "Alertas de prazo e metricas", description: "Alertas de prazo e métricas",
types: ["sla_at_risk", "sla_breached", "sla_daily_digest"], types: ["sla_at_risk", "sla_breached", "sla_daily_digest"],
}, },
security: { security: {
label: "Seguranca", label: "Segurança",
description: "Notificacoes de autenticacao e acesso", description: "Notificações de autenticação e acesso",
types: ["security_password_reset", "security_email_verify", "security_email_change", "security_new_login", "security_invite"], types: ["security_password_reset", "security_email_verify", "security_email_change", "security_new_login", "security_invite"],
}, },
} }
@ -74,20 +74,20 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
try { try {
const response = await fetch("/api/notifications/preferences") const response = await fetch("/api/notifications/preferences")
if (!response.ok) { if (!response.ok) {
throw new Error("Erro ao carregar preferencias") throw new Error("Erro ao carregar preferências")
} }
const data = await response.json() const data = await response.json()
setPreferences(data) setPreferences(data)
// Inicializa preferencias locais // Inicializa preferências locais
const typePrefs: Record<string, boolean> = {} const typePrefs: Record<string, boolean> = {}
data.types.forEach((t: NotificationType) => { data.types.forEach((t: NotificationType) => {
typePrefs[t.type] = t.enabled typePrefs[t.type] = t.enabled
}) })
setLocalTypePrefs(typePrefs) setLocalTypePrefs(typePrefs)
} catch (error) { } catch (error) {
console.error("Erro ao carregar preferencias:", error) console.error("Erro ao carregar preferências:", error)
toast.error("Nao foi possivel carregar suas preferencias") toast.error("Não foi possível carregar suas preferências")
} finally { } finally {
setLoading(false) setLoading(false)
} }
@ -112,13 +112,13 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
}) })
if (!response.ok) { if (!response.ok) {
throw new Error("Erro ao salvar preferencias") throw new Error("Erro ao salvar preferências")
} }
toast.success("Preferencias salvas com sucesso") toast.success("Preferências salvas com sucesso")
} catch (error) { } catch (error) {
console.error("Erro ao salvar preferencias:", error) console.error("Erro ao salvar preferências:", error)
toast.error("Nao foi possivel salvar suas preferencias") toast.error("Não foi possível salvar suas preferências")
} finally { } finally {
setSaving(false) setSaving(false)
} }
@ -153,7 +153,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
<Card> <Card>
<CardContent className="py-8"> <CardContent className="py-8">
<p className="text-center text-muted-foreground"> <p className="text-center text-muted-foreground">
Nao foi possivel carregar suas preferencias de notificacao. Não foi possível carregar suas preferências de notificação.
</p> </p>
<div className="flex justify-center mt-4"> <div className="flex justify-center mt-4">
<Button onClick={loadPreferences}>Tentar novamente</Button> <Button onClick={loadPreferences}>Tentar novamente</Button>
@ -163,7 +163,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
) )
} }
// Filtra tipos visiveis para o usuario // Filtra tipos visíveis para o usuário
const visibleTypes = preferences.types const visibleTypes = preferences.types
// Agrupa tipos por categoria // Agrupa tipos por categoria
@ -177,15 +177,15 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Configuracao global de e-mail */} {/* Configuração global de e-mail */}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
<Mail className="h-5 w-5" /> <Mail className="h-5 w-5" />
Notificacoes por e-mail Notificações por e-mail
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
Controle se deseja receber notificacoes por e-mail. Controle se deseja receber notificações por e-mail.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6">
@ -194,8 +194,8 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
<Label className="text-base">Receber e-mails</Label> <Label className="text-base">Receber e-mails</Label>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{preferences.emailEnabled {preferences.emailEnabled
? "Voce recebera notificacoes por e-mail conforme suas preferencias" ? "Você receberá notificações por e-mail conforme suas preferências"
: "Todas as notificacoes por e-mail estao desativadas"} : "Todas as notificações por e-mail estão desativadas"}
</p> </p>
</div> </div>
<Switch <Switch
@ -208,14 +208,14 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
<> <>
<Separator /> <Separator />
{/* Horario de silencio - apenas staff */} {/* Horário de silêncio - apenas staff */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Clock className="h-4 w-4 text-muted-foreground" /> <Clock className="h-4 w-4 text-muted-foreground" />
<Label className="text-base">Horario de silencio</Label> <Label className="text-base">Horário de silêncio</Label>
</div> </div>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Durante este periodo, notificacoes nao urgentes serao adiadas. Durante este período, notificações não urgentes serão adiadas.
</p> </p>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -229,7 +229,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
/> />
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label htmlFor="quietEnd" className="text-sm">as</Label> <Label htmlFor="quietEnd" className="text-sm">às</Label>
<Input <Input
id="quietEnd" id="quietEnd"
type="time" type="time"
@ -252,11 +252,11 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
<Separator /> <Separator />
{/* Frequencia de resumo - apenas staff */} {/* Frequência de resumo - apenas staff */}
<div className="space-y-4"> <div className="space-y-4">
<Label className="text-base">Frequencia de resumo</Label> <Label className="text-base">Frequência de resumo</Label>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
Como voce prefere receber as notificacoes nao urgentes. Como você prefere receber as notificações o urgentes.
</p> </p>
<Select <Select
value={preferences.digestFrequency} value={preferences.digestFrequency}
@ -267,7 +267,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="immediate">Imediato</SelectItem> <SelectItem value="immediate">Imediato</SelectItem>
<SelectItem value="daily">Resumo diario</SelectItem> <SelectItem value="daily">Resumo diário</SelectItem>
<SelectItem value="weekly">Resumo semanal</SelectItem> <SelectItem value="weekly">Resumo semanal</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
@ -277,19 +277,19 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
</CardContent> </CardContent>
</Card> </Card>
{/* Tipos de notificacao */} {/* Tipos de notificação */}
{preferences.emailEnabled && ( {preferences.emailEnabled && (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
<Bell className="h-5 w-5" /> <Bell className="h-5 w-5" />
Tipos de notificacao Tipos de notificação
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
Escolha quais tipos de notificacao deseja receber. Escolha quais tipos de notificação deseja receber.
{!preferences.isStaff && ( {!preferences.isStaff && (
<span className="block mt-1 text-amber-600"> <span className="block mt-1 text-amber-600">
Algumas notificacoes sao obrigatorias e nao podem ser desativadas. Algumas notificações são obrigatórias e o podem ser desativadas.
</span> </span>
)} )}
</CardDescription> </CardDescription>
@ -323,7 +323,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
</Label> </Label>
{notifType.required && ( {notifType.required && (
<Badge variant="secondary" className="ml-2 text-xs"> <Badge variant="secondary" className="ml-2 text-xs">
Obrigatorio Obrigatório
</Badge> </Badge>
)} )}
</div> </div>
@ -343,7 +343,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
</Card> </Card>
)} )}
{/* Botao de salvar */} {/* Botão de salvar */}
<div className="flex justify-end"> <div className="flex justify-end">
<Button onClick={savePreferences} disabled={saving}> <Button onClick={savePreferences} disabled={saving}>
{saving ? ( {saving ? (
@ -352,7 +352,7 @@ export function NotificationPreferencesForm({ isPortal = false }: NotificationPr
Salvando... Salvando...
</> </>
) : ( ) : (
"Salvar preferencias" "Salvar preferências"
)} )}
</Button> </Button>
</div> </div>