@@ -145,6 +145,15 @@ const T: Record<string, Record<string, string>> = {
145145 fr : "Marquer comme lu" ,
146146 pt : "Marcar como lido" ,
147147 } ,
148+ mark_all_read : {
149+ it : "Segna tutti come letti" ,
150+ en : "Mark all as read" ,
151+ hu : "Összes megjelölése olvasottként" ,
152+ es : "Marcar todo como leído" ,
153+ de : "Alle als gelesen markieren" ,
154+ fr : "Tout marquer comme lu" ,
155+ pt : "Marcar tudo como lido" ,
156+ } ,
148157 reply : {
149158 it : "Rispondi →" ,
150159 en : "Reply →" ,
@@ -227,6 +236,15 @@ const KIND_LABEL_KEY: Record<PendingMessageKind, string> = {
227236 alert : "kind_alert" ,
228237} ;
229238
239+ // Alcuni messaggi salvati prima del fix in jht-notify-user contengono le
240+ // escape LETTERALI `\n`/`\t` (l'agente le scrive come separatori di paragrafo
241+ // in una stringa singola). pre-wrap rende solo i newline VERI, quindi senza
242+ // questa normalizzazione si vedrebbe il testo "\n\n". Difensivo e idempotente:
243+ // i newline gia' veri restano invariati.
244+ function normalizeBody ( s : string ) : string {
245+ return s . replace ( / \\ r \\ n | \\ n / g, "\n" ) . replace ( / \\ t / g, "\t" ) ;
246+ }
247+
230248export default function PendingMessagesCard ( { initialMessages } : Props ) {
231249 // L'array e' state interno: ack/reply rimuovono il messaggio dalla lista
232250 // senza dover ricaricare la pagina. Il polling per nuove notifiche verra'
@@ -273,6 +291,24 @@ export default function PendingMessagesCard({ initialMessages }: Props) {
273291 } ) ;
274292 }
275293
294+ async function handleAckAll ( ) {
295+ setError ( null ) ;
296+ startTransition ( async ( ) => {
297+ try {
298+ const res = await fetch ( `/api/pending-messages/ack-all` , {
299+ method : "POST" ,
300+ } ) ;
301+ if ( ! res . ok ) {
302+ const body = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
303+ throw new Error ( body . error ?? `HTTP ${ res . status } ` ) ;
304+ }
305+ setMessages ( [ ] ) ;
306+ } catch ( e ) {
307+ setError ( ( e as Error ) . message ) ;
308+ }
309+ } ) ;
310+ }
311+
276312 async function handleReply ( id : string ) {
277313 const reply = replyText . trim ( ) ;
278314 if ( ! reply ) {
@@ -304,9 +340,21 @@ export default function PendingMessagesCard({ initialMessages }: Props) {
304340 < div className = "mb-8" style = { { animation : "fade-in 0.35s ease both" } } >
305341 < div className = "flex items-center justify-between mb-4" >
306342 < span className = "section-label" > { tr ( "section_title" ) } </ span >
307- < span className = "text-[10px] font-semibold tracking-widest uppercase text-[var(--color-dim)]" >
308- { tr ( "unread" ) . replace ( "{n}" , String ( messages . length ) ) }
309- </ span >
343+ < div className = "flex items-center gap-3" >
344+ < span className = "text-[10px] font-semibold tracking-widest uppercase text-[var(--color-dim)]" >
345+ { tr ( "unread" ) . replace ( "{n}" , String ( messages . length ) ) }
346+ </ span >
347+ { messages . length > 1 && (
348+ < button
349+ type = "button"
350+ onClick = { handleAckAll }
351+ disabled = { pending }
352+ className = "text-[10px] font-semibold tracking-widest uppercase text-[var(--color-muted)] hover:text-[var(--color-bright)] transition-colors disabled:opacity-50"
353+ >
354+ { tr ( "mark_all_read" ) }
355+ </ button >
356+ ) }
357+ </ div >
310358 </ div >
311359
312360 { error && (
@@ -377,7 +425,7 @@ export default function PendingMessagesCard({ initialMessages }: Props) {
377425 className = "text-[12px] text-[var(--color-base)] m-0 mb-2 leading-relaxed"
378426 style = { { whiteSpace : "pre-wrap" } }
379427 >
380- { m . body }
428+ { normalizeBody ( m . body ) }
381429 </ p >
382430 { m . related_position_id && (
383431 < Link
0 commit comments