feat(email): adiciona templates React Email e melhora UI admin
Some checks failed
Some checks failed
- Cria 10 novos templates React Email (invite, password-reset, new-login, sla-warning, sla-breached, ticket-created, ticket-resolved, ticket-assigned, ticket-status, ticket-comment) - Adiciona envio de email ao criar convite de usuario - Adiciona security_invite em COLLABORATOR_VISIBLE_TYPES - Melhora tabela de equipe com badges de papel e colunas fixas - Atualiza TicketCard com nova interface de props - Remove botao de limpeza de dados antigos do admin 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8546a1feb1
commit
498b9789b5
17 changed files with 1422 additions and 190 deletions
|
|
@ -14,6 +14,18 @@ export type TicketCardData = {
|
|||
assigneeName?: string | null
|
||||
}
|
||||
|
||||
export type TicketCardProps = {
|
||||
ticketNumber: string
|
||||
ticketTitle: string
|
||||
status?: string | null
|
||||
priority?: string | null
|
||||
category?: string | null
|
||||
subcategory?: string | null
|
||||
companyName?: string | null
|
||||
requesterName?: string | null
|
||||
assigneeName?: string | null
|
||||
}
|
||||
|
||||
function badge(label: string, bg: string, color: string) {
|
||||
return (
|
||||
<span
|
||||
|
|
@ -76,7 +88,8 @@ function Row({ label, value }: { label: string; value: React.ReactNode }) {
|
|||
)
|
||||
}
|
||||
|
||||
export function TicketCard({ ticket }: { ticket: TicketCardData }) {
|
||||
/** @deprecated Use TicketCard with props instead */
|
||||
export function TicketCardLegacy({ ticket }: { ticket: TicketCardData }) {
|
||||
return (
|
||||
<Section
|
||||
style={{
|
||||
|
|
@ -100,3 +113,90 @@ export function TicketCard({ ticket }: { ticket: TicketCardData }) {
|
|||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
export function TicketCard(props: TicketCardProps) {
|
||||
const { ticketNumber, ticketTitle, status, priority, category, subcategory, companyName, requesterName, assigneeName } = props
|
||||
const categoryLabel = category && subcategory ? `${category} / ${subcategory}` : category ?? subcategory ?? null
|
||||
|
||||
return (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${EMAIL_COLORS.border}`,
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: "1px solid #f1f5f9" }}>
|
||||
<Text style={{ margin: 0, fontSize: "13px", fontWeight: 600, color: EMAIL_COLORS.textMuted }}>
|
||||
Chamado #{ticketNumber}
|
||||
</Text>
|
||||
<Text style={{ margin: "4px 0 0 0", fontSize: "16px", fontWeight: 700, color: EMAIL_COLORS.textPrimary }}>
|
||||
{ticketTitle}
|
||||
</Text>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
{status ? (
|
||||
<tr>
|
||||
<td style={{ paddingBottom: "10px", width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Status
|
||||
</td>
|
||||
<td style={{ paddingBottom: "10px" }}>{statusBadge(status)}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{priority ? (
|
||||
<tr>
|
||||
<td style={{ paddingBottom: "10px", width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Prioridade
|
||||
</td>
|
||||
<td style={{ paddingBottom: "10px" }}>{priorityBadge(priority)}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{categoryLabel ? (
|
||||
<tr>
|
||||
<td style={{ paddingBottom: "10px", width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Categoria
|
||||
</td>
|
||||
<td style={{ paddingBottom: "10px", color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>{categoryLabel}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{companyName ? (
|
||||
<tr>
|
||||
<td style={{ paddingBottom: "10px", width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Empresa
|
||||
</td>
|
||||
<td style={{ paddingBottom: "10px", color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>{companyName}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{requesterName ? (
|
||||
<tr>
|
||||
<td style={{ paddingBottom: "10px", width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Solicitante
|
||||
</td>
|
||||
<td style={{ paddingBottom: "10px", color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>{requesterName}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{assigneeName ? (
|
||||
<tr>
|
||||
<td style={{ width: "100px", color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500 }}>
|
||||
Responsavel
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>{assigneeName}</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
132
emails/invite-email.tsx
Normal file
132
emails/invite-email.tsx
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type InviteEmailProps = {
|
||||
inviterName: string
|
||||
roleName: string
|
||||
companyName?: string | null
|
||||
inviteUrl: string
|
||||
}
|
||||
|
||||
export default function InviteEmail(props: InviteEmailProps) {
|
||||
const { inviterName, roleName, companyName, inviteUrl } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Convite para o Sistema de Chamados" preview={`${inviterName} convidou voce para acessar o Sistema de Chamados Raven`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
}}
|
||||
>
|
||||
🎉
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Voce foi convidado!
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
<strong>{inviterName}</strong> convidou voce para acessar o Sistema de Chamados Raven.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${EMAIL_COLORS.border}`,
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: `1px solid #f1f5f9` }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "100px" }}>
|
||||
Funcao
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px", fontWeight: 600 }}>
|
||||
{roleName}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{companyName ? (
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "100px" }}>
|
||||
Empresa
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>
|
||||
{companyName}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={inviteUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Aceitar convite
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Se o botao nao funcionar, copie e cole esta URL no navegador:
|
||||
<br />
|
||||
<a href={inviteUrl} style={{ color: EMAIL_COLORS.primaryDark, textDecoration: "none" }}>
|
||||
{inviteUrl}
|
||||
</a>
|
||||
</Text>
|
||||
|
||||
<Text style={{ margin: "24px 0 0 0", fontSize: "13px", color: EMAIL_COLORS.textMuted, textAlign: "center", lineHeight: "1.6" }}>
|
||||
Este convite expira em 7 dias. Se voce nao esperava este convite, pode ignora-lo com seguranca.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
InviteEmail.PreviewProps = {
|
||||
inviterName: "Renan Oliveira",
|
||||
roleName: "Agente",
|
||||
companyName: "Paulicon",
|
||||
inviteUrl: "https://raven.rever.com.br/invite/abc123def456",
|
||||
} satisfies InviteEmailProps
|
||||
150
emails/new-login-email.tsx
Normal file
150
emails/new-login-email.tsx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import * as React from "react"
|
||||
import { Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type NewLoginEmailProps = {
|
||||
loginAt: string
|
||||
ipAddress: string
|
||||
userAgent: string
|
||||
location?: string | null
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
return new Intl.DateTimeFormat("pt-BR", {
|
||||
dateStyle: "long",
|
||||
timeStyle: "short",
|
||||
}).format(date)
|
||||
} catch {
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
export default function NewLoginEmail(props: NewLoginEmailProps) {
|
||||
const { loginAt, ipAddress, userAgent, location } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Novo acesso detectado" preview="Detectamos um novo acesso a sua conta">
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#fef3c7",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #f59e0b",
|
||||
}}
|
||||
>
|
||||
🔒
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Novo acesso detectado
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
Detectamos um novo acesso a sua conta. Se foi voce, pode ignorar este e-mail.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${EMAIL_COLORS.border}`,
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: `1px solid #f1f5f9` }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Data/Hora
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>
|
||||
{formatDate(loginAt)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: `1px solid #f1f5f9` }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Endereco IP
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px", fontFamily: "monospace" }}>
|
||||
{ipAddress}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{location ? (
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: `1px solid #f1f5f9` }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Localizacao
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "14px" }}>
|
||||
{location}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: EMAIL_COLORS.textMuted, fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Dispositivo
|
||||
</td>
|
||||
<td style={{ color: EMAIL_COLORS.textPrimary, fontSize: "13px" }}>
|
||||
{userAgent}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: "0", fontSize: "13px", color: EMAIL_COLORS.textMuted, textAlign: "center", lineHeight: "1.6" }}>
|
||||
Se voce nao reconhece este acesso, recomendamos que altere sua senha imediatamente.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
NewLoginEmail.PreviewProps = {
|
||||
loginAt: new Date().toISOString(),
|
||||
ipAddress: "192.168.1.100",
|
||||
userAgent: "Chrome 120.0 / Windows 11",
|
||||
location: "Sao Paulo, SP, Brasil",
|
||||
} satisfies NewLoginEmailProps
|
||||
81
emails/password-reset-email.tsx
Normal file
81
emails/password-reset-email.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type PasswordResetEmailProps = {
|
||||
resetUrl: string
|
||||
expiresIn?: string
|
||||
}
|
||||
|
||||
export default function PasswordResetEmail(props: PasswordResetEmailProps) {
|
||||
const { resetUrl, expiresIn = "1 hora" } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Redefinicao de senha" preview="Voce solicitou a redefinicao de sua senha">
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#fef3c7",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #f59e0b",
|
||||
}}
|
||||
>
|
||||
🔒
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Redefinir senha
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
Recebemos uma solicitacao para redefinir a senha da sua conta. Clique no botao abaixo para criar uma nova senha.
|
||||
</Text>
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={resetUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Redefinir senha
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Se o botao nao funcionar, copie e cole esta URL no navegador:
|
||||
<br />
|
||||
<a href={resetUrl} style={{ color: EMAIL_COLORS.primaryDark, textDecoration: "none" }}>
|
||||
{resetUrl}
|
||||
</a>
|
||||
</Text>
|
||||
|
||||
<Text style={{ margin: "24px 0 0 0", fontSize: "13px", color: EMAIL_COLORS.textMuted, textAlign: "center", lineHeight: "1.6" }}>
|
||||
Este link expira em {expiresIn}. Se voce nao solicitou esta redefinicao, pode ignorar este e-mail com seguranca.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
PasswordResetEmail.PreviewProps = {
|
||||
resetUrl: "https://raven.rever.com.br/redefinir-senha?token=abc123def456",
|
||||
expiresIn: "1 hora",
|
||||
} satisfies PasswordResetEmailProps
|
||||
151
emails/sla-breached-email.tsx
Normal file
151
emails/sla-breached-email.tsx
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type SlaBreachedEmailProps = {
|
||||
ticketNumber: string
|
||||
ticketTitle: string
|
||||
breachedAt: string
|
||||
ticketUrl: string
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
return new Intl.DateTimeFormat("pt-BR", {
|
||||
dateStyle: "long",
|
||||
timeStyle: "short",
|
||||
}).format(date)
|
||||
} catch {
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
export default function SlaBreachedEmail(props: SlaBreachedEmailProps) {
|
||||
const { ticketNumber, ticketTitle, breachedAt, ticketUrl } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="SLA estourado" preview={`Chamado #${ticketNumber} estourou o SLA`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#fee2e2",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #ef4444",
|
||||
}}
|
||||
>
|
||||
🚨
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
SLA estourado
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
O chamado abaixo excedeu o tempo de atendimento acordado e requer atencao imediata.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fef2f2",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid #fca5a5",
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: "1px solid #fecaca" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#991b1b", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Chamado
|
||||
</td>
|
||||
<td style={{ color: "#7f1d1d", fontSize: "14px", fontWeight: 600 }}>
|
||||
#{ticketNumber}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: "1px solid #fecaca" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#991b1b", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Titulo
|
||||
</td>
|
||||
<td style={{ color: "#7f1d1d", fontSize: "14px" }}>
|
||||
{ticketTitle}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#991b1b", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Estourado em
|
||||
</td>
|
||||
<td style={{ color: "#dc2626", fontSize: "14px", fontWeight: 700 }}>
|
||||
{formatDate(breachedAt)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: "#dc2626",
|
||||
color: "#ffffff",
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: "1px solid #b91c1c",
|
||||
}}
|
||||
>
|
||||
Atender agora
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Este chamado deve ser tratado com prioridade maxima.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
SlaBreachedEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
breachedAt: new Date().toISOString(),
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
} satisfies SlaBreachedEmailProps
|
||||
139
emails/sla-warning-email.tsx
Normal file
139
emails/sla-warning-email.tsx
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type SlaWarningEmailProps = {
|
||||
ticketNumber: string
|
||||
ticketTitle: string
|
||||
timeRemaining: string
|
||||
ticketUrl: string
|
||||
}
|
||||
|
||||
export default function SlaWarningEmail(props: SlaWarningEmailProps) {
|
||||
const { ticketNumber, ticketTitle, timeRemaining, ticketUrl } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Alerta de SLA" preview={`Chamado #${ticketNumber} esta proximo de estourar o SLA`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#fef3c7",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #f59e0b",
|
||||
}}
|
||||
>
|
||||
⚠
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Alerta de SLA
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
O chamado abaixo esta proximo de estourar o tempo de atendimento acordado.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fffbeb",
|
||||
borderRadius: "12px",
|
||||
border: "1px solid #fcd34d",
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: "1px solid #fde68a" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#92400e", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Chamado
|
||||
</td>
|
||||
<td style={{ color: "#78350f", fontSize: "14px", fontWeight: 600 }}>
|
||||
#{ticketNumber}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: "1px solid #fde68a" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#92400e", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Titulo
|
||||
</td>
|
||||
<td style={{ color: "#78350f", fontSize: "14px" }}>
|
||||
{ticketTitle}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ color: "#92400e", fontSize: "13px", fontWeight: 500, width: "120px" }}>
|
||||
Tempo restante
|
||||
</td>
|
||||
<td style={{ color: "#dc2626", fontSize: "14px", fontWeight: 700 }}>
|
||||
{timeRemaining}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Ver chamado
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Acesse o sistema para mais detalhes e acompanhe o status do chamado.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
SlaWarningEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
timeRemaining: "45 minutos",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
} satisfies SlaWarningEmailProps
|
||||
82
emails/ticket-assigned-email.tsx
Normal file
82
emails/ticket-assigned-email.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { TicketCard, type TicketCardProps } from "./_components/ticket-card"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type TicketAssignedEmailProps = TicketCardProps & {
|
||||
ticketUrl: string
|
||||
assigneeName: string
|
||||
}
|
||||
|
||||
export default function TicketAssignedEmail(props: TicketAssignedEmailProps) {
|
||||
const { ticketUrl, assigneeName, ...ticketProps } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Chamado atribuido" preview={`Chamado #${ticketProps.ticketNumber} foi atribuido a ${assigneeName}`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#e0f2fe",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #0ea5e9",
|
||||
}}
|
||||
>
|
||||
👤
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Chamado atribuido
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
O chamado foi atribuido a <strong>{assigneeName}</strong>.
|
||||
</Text>
|
||||
|
||||
<TicketCard {...ticketProps} />
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Ver chamado
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Voce recebera atualizacoes por e-mail quando houver novidades.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
TicketAssignedEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
status: "AWAITING_ATTENDANCE",
|
||||
priority: "HIGH",
|
||||
category: "Hardware",
|
||||
subcategory: "Desktop",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
assigneeName: "Weslei Magalhaes",
|
||||
} satisfies TicketAssignedEmailProps
|
||||
113
emails/ticket-comment-email.tsx
Normal file
113
emails/ticket-comment-email.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type TicketCommentEmailProps = {
|
||||
ticketNumber: string
|
||||
ticketTitle: string
|
||||
commenterName: string
|
||||
commentPreview: string
|
||||
ticketUrl: string
|
||||
}
|
||||
|
||||
export default function TicketCommentEmail(props: TicketCommentEmailProps) {
|
||||
const { ticketNumber, ticketTitle, commenterName, commentPreview, ticketUrl } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Novo comentario" preview={`${commenterName} comentou no chamado #${ticketNumber}`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#e0f2fe",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #0ea5e9",
|
||||
}}
|
||||
>
|
||||
💬
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Novo comentario
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
<strong>{commenterName}</strong> comentou no chamado <strong>#{ticketNumber}</strong>.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${EMAIL_COLORS.border}`,
|
||||
margin: "24px 0",
|
||||
}}
|
||||
>
|
||||
<table cellPadding="0" cellSpacing="0" role="presentation" style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px", borderBottom: `1px solid #f1f5f9` }}>
|
||||
<Text style={{ margin: 0, fontSize: "13px", fontWeight: 600, color: EMAIL_COLORS.textMuted }}>
|
||||
Chamado #{ticketNumber}
|
||||
</Text>
|
||||
<Text style={{ margin: "4px 0 0 0", fontSize: "14px", fontWeight: 600, color: EMAIL_COLORS.textPrimary }}>
|
||||
{ticketTitle}
|
||||
</Text>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: "16px 20px" }}>
|
||||
<Text style={{ margin: 0, fontSize: "13px", fontWeight: 600, color: EMAIL_COLORS.textMuted }}>
|
||||
Comentario
|
||||
</Text>
|
||||
<Text style={{ margin: "8px 0 0 0", fontSize: "14px", lineHeight: "1.6", color: EMAIL_COLORS.textPrimary }}>
|
||||
{commentPreview}
|
||||
</Text>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Ver e responder
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Clique no botao acima para ver o comentario completo e responder.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
TicketCommentEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
commenterName: "Weslei Magalhaes",
|
||||
commentPreview: "Ola! Ja verificamos o problema e parece ser relacionado ao driver da placa de video. Vou precisar de acesso remoto para fazer a correcao...",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
} satisfies TicketCommentEmailProps
|
||||
80
emails/ticket-created-email.tsx
Normal file
80
emails/ticket-created-email.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { TicketCard, type TicketCardProps } from "./_components/ticket-card"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type TicketCreatedEmailProps = TicketCardProps & {
|
||||
ticketUrl: string
|
||||
}
|
||||
|
||||
export default function TicketCreatedEmail(props: TicketCreatedEmailProps) {
|
||||
const { ticketUrl, ...ticketProps } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Novo chamado criado" preview={`Chamado #${ticketProps.ticketNumber} foi criado com sucesso`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#ecfdf5",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #10b981",
|
||||
}}
|
||||
>
|
||||
✅
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Chamado criado
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
Seu chamado foi registrado com sucesso e ja esta sendo processado pela nossa equipe.
|
||||
</Text>
|
||||
|
||||
<TicketCard {...ticketProps} />
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Acompanhar chamado
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Voce recebera atualizacoes por e-mail quando houver novidades.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
TicketCreatedEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
status: "PENDING",
|
||||
priority: "HIGH",
|
||||
category: "Hardware",
|
||||
subcategory: "Desktop",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
} satisfies TicketCreatedEmailProps
|
||||
121
emails/ticket-resolved-email.tsx
Normal file
121
emails/ticket-resolved-email.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { TicketCard, type TicketCardProps } from "./_components/ticket-card"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
|
||||
export type TicketResolvedEmailProps = TicketCardProps & {
|
||||
ticketUrl: string
|
||||
ratingUrl?: string | null
|
||||
resolution?: string | null
|
||||
}
|
||||
|
||||
export default function TicketResolvedEmail(props: TicketResolvedEmailProps) {
|
||||
const { ticketUrl, ratingUrl, resolution, ...ticketProps } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Chamado resolvido" preview={`Chamado #${ticketProps.ticketNumber} foi resolvido`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#ecfdf5",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #10b981",
|
||||
}}
|
||||
>
|
||||
🎉
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Chamado resolvido
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
Seu chamado foi marcado como resolvido. Confira os detalhes abaixo.
|
||||
</Text>
|
||||
|
||||
<TicketCard {...ticketProps} status="RESOLVED" />
|
||||
|
||||
{resolution ? (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${EMAIL_COLORS.border}`,
|
||||
margin: "24px 0",
|
||||
padding: "16px 20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ margin: "0 0 8px 0", fontSize: "13px", fontWeight: 600, color: EMAIL_COLORS.textMuted }}>
|
||||
Resolucao
|
||||
</Text>
|
||||
<Text style={{ margin: 0, fontSize: "14px", lineHeight: "1.6", color: EMAIL_COLORS.textPrimary }}>
|
||||
{resolution}
|
||||
</Text>
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Ver detalhes
|
||||
</Button>
|
||||
{ratingUrl ? (
|
||||
<Button
|
||||
href={ratingUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: "#0f172a",
|
||||
color: "#f8fafc",
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
marginLeft: "12px",
|
||||
}}
|
||||
>
|
||||
Avaliar atendimento
|
||||
</Button>
|
||||
) : null}
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Sua opiniao e importante! Avalie o atendimento para nos ajudar a melhorar.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
TicketResolvedEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
status: "RESOLVED",
|
||||
priority: "HIGH",
|
||||
category: "Hardware",
|
||||
subcategory: "Desktop",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
ratingUrl: "https://raven.rever.com.br/rate/abc123",
|
||||
resolution: "Problema resolvido apos atualizacao do driver da placa de video e reinicializacao do sistema.",
|
||||
} satisfies TicketResolvedEmailProps
|
||||
85
emails/ticket-status-email.tsx
Normal file
85
emails/ticket-status-email.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import * as React from "react"
|
||||
import { Button, Heading, Hr, Section, Text } from "@react-email/components"
|
||||
|
||||
import { RavenEmailLayout } from "./_components/layout"
|
||||
import { TicketCard, type TicketCardProps } from "./_components/ticket-card"
|
||||
import { EMAIL_COLORS } from "./_components/tokens"
|
||||
import { formatStatus } from "./_components/utils"
|
||||
|
||||
export type TicketStatusEmailProps = TicketCardProps & {
|
||||
ticketUrl: string
|
||||
previousStatus: string
|
||||
newStatus: string
|
||||
}
|
||||
|
||||
export default function TicketStatusEmail(props: TicketStatusEmailProps) {
|
||||
const { ticketUrl, previousStatus, newStatus, ...ticketProps } = props
|
||||
|
||||
return (
|
||||
<RavenEmailLayout title="Status atualizado" preview={`Chamado #${ticketProps.ticketNumber} mudou de ${formatStatus(previousStatus)} para ${formatStatus(newStatus)}`}>
|
||||
<Section style={{ textAlign: "center", margin: "24px 0" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "64px",
|
||||
height: "64px",
|
||||
backgroundColor: "#e0f2fe",
|
||||
borderRadius: "50%",
|
||||
lineHeight: "64px",
|
||||
fontSize: "28px",
|
||||
border: "1px solid #0ea5e9",
|
||||
}}
|
||||
>
|
||||
🔄
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Heading style={{ margin: "0 0 12px 0", fontSize: "26px", fontWeight: 700, color: EMAIL_COLORS.textPrimary, textAlign: "center" }}>
|
||||
Status atualizado
|
||||
</Heading>
|
||||
|
||||
<Text style={{ margin: "0 0 24px 0", fontSize: "15px", lineHeight: "1.7", color: EMAIL_COLORS.textSecondary, textAlign: "center" }}>
|
||||
O status do seu chamado foi alterado de <strong>{formatStatus(previousStatus)}</strong> para <strong>{formatStatus(newStatus)}</strong>.
|
||||
</Text>
|
||||
|
||||
<TicketCard {...ticketProps} status={newStatus} />
|
||||
|
||||
<Section style={{ textAlign: "center", margin: "32px 0" }}>
|
||||
<Button
|
||||
href={ticketUrl}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
backgroundColor: EMAIL_COLORS.primary,
|
||||
color: EMAIL_COLORS.primaryForeground,
|
||||
textDecoration: "none",
|
||||
borderRadius: "12px",
|
||||
padding: "14px 24px",
|
||||
fontWeight: 800,
|
||||
fontSize: "14px",
|
||||
border: `1px solid ${EMAIL_COLORS.primaryDark}`,
|
||||
}}
|
||||
>
|
||||
Ver chamado
|
||||
</Button>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: EMAIL_COLORS.border, margin: "24px 0" }} />
|
||||
|
||||
<Text style={{ margin: 0, fontSize: "12px", color: EMAIL_COLORS.textMuted, textAlign: "center" }}>
|
||||
Voce recebera atualizacoes por e-mail quando houver novidades.
|
||||
</Text>
|
||||
</RavenEmailLayout>
|
||||
)
|
||||
}
|
||||
|
||||
TicketStatusEmail.PreviewProps = {
|
||||
ticketNumber: "41025",
|
||||
ticketTitle: "Computador nao liga apos atualizacao",
|
||||
status: "AWAITING_ATTENDANCE",
|
||||
priority: "HIGH",
|
||||
category: "Hardware",
|
||||
subcategory: "Desktop",
|
||||
ticketUrl: "https://raven.rever.com.br/tickets/abc123",
|
||||
previousStatus: "PENDING",
|
||||
newStatus: "AWAITING_ATTENDANCE",
|
||||
} satisfies TicketStatusEmailProps
|
||||
Loading…
Add table
Add a link
Reference in a new issue