feat: sistema completo de notificacoes por e-mail
Implementa sistema de notificacoes por e-mail com: - Notificacoes de ciclo de vida (abertura, resolucao, atribuicao, status) - Sistema de avaliacao de chamados com estrelas (1-5) - Deep linking via protocolo raven:// para abrir chamados no desktop - Tokens de acesso seguro para visualizacao sem login - Preferencias de notificacao configuraveis por usuario - Templates HTML responsivos com design tokens da plataforma - API completa para preferencias, tokens e avaliacoes Modelos Prisma: - TicketRating: avaliacoes de chamados - TicketAccessToken: tokens de acesso direto - NotificationPreferences: preferencias por usuario Turbopack como bundler padrao (Next.js 16) 🤖 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
cb6add1a4a
commit
f2c0298285
23 changed files with 4387 additions and 9 deletions
|
|
@ -8,7 +8,7 @@ use agent::{collect_inventory_plain, collect_profile, AgentRuntime, MachineProfi
|
|||
use chat::{ChatRuntime, ChatSession, ChatMessagesResponse, SendMessageResponse};
|
||||
use chrono::Local;
|
||||
use usb_control::{UsbPolicy, UsbPolicyResult};
|
||||
use tauri::{Emitter, Manager, WindowEvent};
|
||||
use tauri::{Emitter, Listener, Manager, WindowEvent};
|
||||
use tauri_plugin_store::Builder as StorePluginBuilder;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
|
|
@ -348,6 +348,81 @@ fn set_chat_minimized(app: tauri::AppHandle, ticket_id: String, minimized: bool)
|
|||
chat::set_chat_minimized(&app, &ticket_id, minimized)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Handler de Deep Link (raven://)
|
||||
// ============================================================================
|
||||
|
||||
/// Processa URLs do protocolo raven://
|
||||
/// Formatos suportados:
|
||||
/// - raven://ticket/{token} - Abre visualizacao do chamado
|
||||
/// - raven://chat/{ticketId}?token={token} - Abre chat do chamado
|
||||
/// - raven://rate/{token} - Abre avaliacao do chamado
|
||||
fn handle_deep_link(app: &tauri::AppHandle, url: &str) {
|
||||
log_info!("Processando deep link: {url}");
|
||||
|
||||
// Remove o prefixo raven://
|
||||
let path = url.trim_start_matches("raven://");
|
||||
|
||||
// Parse do path
|
||||
let parts: Vec<&str> = path.split('/').collect();
|
||||
|
||||
if parts.is_empty() {
|
||||
log_warn!("Deep link invalido: path vazio");
|
||||
return;
|
||||
}
|
||||
|
||||
match parts[0] {
|
||||
"ticket" => {
|
||||
if parts.len() > 1 {
|
||||
let token = parts[1].split('?').next().unwrap_or(parts[1]);
|
||||
log_info!("Abrindo ticket com token: {token}");
|
||||
|
||||
// Mostra a janela principal
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
|
||||
// Emite evento para o frontend navegar para o ticket
|
||||
let _ = app.emit("raven://deep-link/ticket", serde_json::json!({
|
||||
"token": token
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
"chat" => {
|
||||
if parts.len() > 1 {
|
||||
let ticket_id = parts[1].split('?').next().unwrap_or(parts[1]);
|
||||
log_info!("Abrindo chat do ticket: {ticket_id}");
|
||||
|
||||
// Abre janela de chat
|
||||
if let Err(e) = chat::open_chat_window(app, ticket_id) {
|
||||
log_error!("Falha ao abrir chat: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
"rate" => {
|
||||
if parts.len() > 1 {
|
||||
let token = parts[1].split('?').next().unwrap_or(parts[1]);
|
||||
log_info!("Abrindo avaliacao com token: {token}");
|
||||
|
||||
// Mostra a janela principal
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
|
||||
// Emite evento para o frontend navegar para avaliacao
|
||||
let _ = app.emit("raven://deep-link/rate", serde_json::json!({
|
||||
"token": token
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log_warn!("Deep link desconhecido: {path}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
|
|
@ -358,6 +433,7 @@ pub fn run() {
|
|||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.on_window_event(|window, event| {
|
||||
if let WindowEvent::CloseRequested { api, .. } = event {
|
||||
api.prevent_close();
|
||||
|
|
@ -372,6 +448,17 @@ pub fn run() {
|
|||
|
||||
log_info!("Raven iniciando...");
|
||||
|
||||
// Configura handler de deep link (raven://)
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
let handle = app.handle().clone();
|
||||
app.listen("deep-link://new-url", move |event| {
|
||||
let urls = event.payload();
|
||||
log_info!("Deep link recebido: {urls}");
|
||||
handle_deep_link(&handle, urls);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
setup_raven_autostart();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue