feat: shop buy, forum replies, tickets, messages, sessions, and UX hardening
Deploy / release (push) Skipped
Deploy / deploy (push) Failing after 4m23s

Complete remaining product gaps: credit-based shop purchases, guild thread replies, help ticket detail/reply/close, offline message compose, sign-out-everywhere via JWT version, ads delete confirm, soft-fail feedback, rate limits, loading states, and single auth() in site layout.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 21:21:02 +02:00
1 parent ed7db6e048
commit 803e8f36c1
47 files changed
+2793 -358

No files matched your search

+174 -27
View File
@@ -112,7 +112,7 @@
},
"messages": {
"title": "Messaggi",
"subtitle": "Richieste di amicizia e messaggi offline in attesa per te",
"subtitle": "Richieste di amicizia, inbox offline e messaggi agli amici",
"requestsTitle": "Richieste di amicizia",
"requestsEmptySubtitle": "Nessuno ti ha ancora chiesto l'amicizia",
"requestsSubtitle": "{count, plural, one {# richiesta in sospeso} other {# richieste in sospeso}}",
@@ -124,18 +124,46 @@
"offlineEmptySubtitle": "La tua casella è vuota",
"offlineSubtitle": "{count, plural, one {# messaggio ricevuto} other {# messaggi ricevuti}}",
"offlineEmptyState": "Non hai messaggi offline.",
"composeTitle": "Invia messaggio offline",
"composeSubtitle": "Consegnato nel messenger dell'hotel quando l'amico è offline",
"composeEmptyFriends": "Aggiungi amici in hotel per inviare messaggi offline.",
"composeFriendLabel": "Amico",
"composeFriendPlaceholder": "Scegli un amico",
"composeMessageLabel": "Messaggio",
"composeMessagePlaceholder": "Scrivi il messaggio (max 500 caratteri)…",
"composeSend": "Invia messaggio",
"unknownUser": "Utente #{id}",
"success": {
"accepted": "Richiesta di amicizia accettata.",
"declined": "Richiesta di amicizia rifiutata."
"declined": "Richiesta di amicizia rifiutata.",
"sent": "Messaggio offline inviato."
},
"errors": {
"notFound": "Quella richiesta di amicizia non è stata trovata.",
"unauthorized": "Non puoi gestire questa richiesta di amicizia.",
"invalid": "Richiesta di amicizia non valida.",
"error": "Qualcosa è andato storto. Riprova."
"error": "Qualcosa è andato storto. Riprova.",
"notFriend": "Puoi scrivere solo agli amici.",
"emptyMessage": "Scrivi un messaggio.",
"rateLimited": "Troppi messaggi. Attendi un momento e riprova."
}
},
"sessions": {
"title": "Attività di accesso",
"subtitle": "Accessi recenti e tentativi falliti sul tuo account",
"recentTitle": "Accessi recenti",
"recentEmpty": "Nessun accesso registrato.",
"colWhen": "Quando",
"colIp": "IP",
"colDevice": "Dispositivo",
"failedTitle": "Tentativi falliti",
"failedSubtitle": "Password errata per il tuo username",
"unknownDevice": "Dispositivo sconosciuto",
"signOutEverywhereTitle": "Esci ovunque",
"signOutEverywhereDescription": "Termina tutte le sessioni del sito su ogni dispositivo. Dovrai accedere di nuovo anche qui.",
"signOutEverywhereButton": "Esci da tutte le sessioni",
"successSignedOutAll": "Tutte le sessioni sono state terminate."
},
"profile": {
"noMotto": "Nessun motto",
"statRank": "Ruolo",
@@ -237,7 +265,8 @@
"postComment": "Pubblica commento",
"toComment": "per lasciare un commento.",
"success": {
"posted": "Commento pubblicato."
"posted": "Commento pubblicato.",
"reaction": "Reazione aggiornata."
},
"errors": {
"empty": "Scrivi un commento prima di pubblicare.",
@@ -353,6 +382,35 @@
"error": "Qualcosa è andato storto. Riprova."
}
},
"guildForumThread": {
"backToForum": "← Torna al forum",
"unnamedGuild": "Gilda senza nome",
"noSubject": "(senza oggetto)",
"inGuildForum": "Forum di {guild}",
"pinned": "In evidenza",
"locked": "Bloccata",
"postsTitle": "Messaggi",
"postsSubtitle": "{count, plural, one {# messaggio} other {# messaggi}}",
"postsEmpty": "Nessun messaggio in questa discussione.",
"userNumber": "Utente #{id}",
"replyTitle": "Rispondi",
"replySubtitle": "Aggiungi un messaggio a questa discussione",
"replyPlaceholder": "Scrivi la tua risposta…",
"replySubmit": "Pubblica risposta",
"lockedHint": "Questa discussione è bloccata: non è possibile rispondere.",
"loginToReply": "Accedi per rispondere.",
"loginLink": "Accedi",
"success": {
"replied": "Risposta pubblicata."
},
"errors": {
"invalid": "Inserisci un messaggio.",
"notFound": "Discussione non trovata.",
"locked": "Questa discussione è bloccata.",
"ratelimit": "Stai pubblicando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"shop": {
"title": "Negozio",
"subtitle": "Ricarica il tuo account e accaparrati pacchetti esclusivi, valute e distintivi.",
@@ -361,7 +419,22 @@
"allPackages": "Tutti i pacchetti",
"packagesAvailable": "{count, plural, one {# pacchetto disponibile} other {# pacchetti disponibili}}",
"empty": "Al momento non ci sono pacchetti disponibili.",
"buy": "Acquista"
"buy": "Acquista",
"loginToBuy": "Accedi per acquistare",
"creditsHint": "I pacchetti si pagano con crediti di gioco. Ricarica prima se ti servono altri crediti.",
"topUpLink": "Ricarica crediti",
"priceCredits": "{credits} crediti (≈ {usd})",
"priceCreditsShort": "{credits} cr",
"success": {
"bought": "Pacchetto acquistato — le ricompense sono state aggiunte al tuo account.",
"boughtNamed": "Hai acquistato \"{name}\" — le ricompense sono state aggiunte al tuo account."
},
"errors": {
"invalid": "Questo pacchetto non è più disponibile.",
"credits": "Non hai abbastanza crediti per questo pacchetto.",
"ratelimit": "Stai acquistando troppo velocemente. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"topup": {
"title": "Ricarica con PayPal",
@@ -476,6 +549,36 @@
"error": "Qualcosa è andato storto. Riprova."
}
},
"helpTicketDetail": {
"backToList": "← I tuoi ticket",
"ticketId": "Ticket #{id}",
"statusOpen": "Aperto",
"statusClosed": "Chiuso",
"messagesTitle": "Conversazione",
"messagesSubtitle": "{count, plural, one {# messaggio} other {# messaggi}}",
"you": "Tu",
"userNumber": "Utente #{id}",
"replyTitle": "Rispondi",
"replySubtitle": "Invia un aggiornamento allo staff",
"replyPlaceholder": "Scrivi il tuo messaggio…",
"replySubmit": "Invia risposta",
"closeTitle": "Chiudi ticket",
"closeSubtitle": "Segna la richiesta come risolta se non ti serve più aiuto",
"closeSubmit": "Chiudi ticket",
"closedHint": "Questo ticket è chiuso. Apri un nuovo ticket se hai bisogno di altro aiuto.",
"success": {
"replied": "Risposta inviata.",
"closed": "Ticket chiuso."
},
"errors": {
"invalid": "Inserisci un messaggio.",
"notFound": "Ticket non trovato.",
"closedTicket": "Questo ticket è chiuso.",
"moderated": "Quel messaggio è stato bloccato dalla moderazione.",
"ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"applyStaff": {
"title": "Candidati per lo staff di {hotel}",
"subtitle": "Apriamo le candidature per lo staff di tanto in tanto. Se trovi una posizione che senti perfetta per te, non esitare a candidarti.",
@@ -486,7 +589,17 @@
"alreadyApplied": "Ti sei già candidato per {rank}",
"aboutYou": "Parlaci di te",
"aboutPlaceholder": "Raccontaci perché saresti un ottimo {rank}…",
"applyFor": "Candidati per {rank}"
"applyFor": "Candidati per {rank}",
"success": {
"submitted": "Candidatura inviata — lo staff la esaminerà a breve."
},
"errors": {
"empty": "Scrivi almeno 10 caratteri su di te.",
"invalid": "Candidatura non valida.",
"duplicate": "Ti sei già candidato per questa posizione.",
"ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"applyTeam": {
"title": "Candidati per il team di {hotel}",
@@ -496,7 +609,17 @@
"pending": "La tua candidatura è in attesa",
"aboutYou": "Parlaci di te",
"aboutPlaceholder": "Raccontaci perché saresti perfetto per {rank}…",
"applyFor": "Candidati per {rank}"
"applyFor": "Candidati per {rank}",
"success": {
"submitted": "Candidatura inviata — lo staff la esaminerà a breve."
},
"errors": {
"empty": "Scrivi almeno 10 caratteri su di te.",
"invalid": "Candidatura non valida.",
"duplicate": "Ti sei già candidato per questo team.",
"ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"radio": {
"title": "Radio",
@@ -658,7 +781,15 @@
"experiencePlaceholder": "Hai esperienze precedenti come DJ o conduttore?",
"motivationLabel": "Motivazione",
"motivationPlaceholder": "Perché vuoi entrare nel team della radio?",
"submitButton": "Invia candidatura"
"submitButton": "Invia candidatura",
"success": {
"submitted": "Candidatura inviata — ti ricontatteremo presto."
},
"errors": {
"invalid": "Compila tutti i campi obbligatori.",
"ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
},
"settings": {
"accountSettings": "Impostazioni account",
@@ -1129,6 +1260,13 @@
"delete": "Elimina",
"noAds": "Ancora nessun annuncio",
"confirmDelete": "Eliminare questo annuncio?",
"deleteSuccess": "Annuncio eliminato!",
"deleteError": "Eliminazione annuncio non riuscita.",
"adsCount": "Annunci totali",
"latest": "Ultimo",
"mostRecentAd": "Annuncio più recente",
"allAds": "Tutti gli annunci",
"colPreview": "Anteprima",
"colId": "ID",
"colName": "Nome",
"colImage": "Immagine",
@@ -2743,25 +2881,34 @@
}
},
"radioRequests": {
"formTitle": "Submit a request",
"formSubtitle": "Ask the DJ to play your favourite track",
"songLabel": "Song",
"songPlaceholder": "Title of the song",
"artistLabel": "Artist",
"artistPlaceholder": "Name of the artist (optional)",
"submitButton": "Submit request",
"loginToRequest": "Log in to submit a request.",
"latestTitle": "Recent requests",
"latestSubtitle": "The most recently requested songs",
"empty": "No requests yet.",
"colSong": "Song",
"colArtist": "Artist",
"colRequester": "Requester",
"colDate": "Date",
"colStatus": "Status",
"statusPending": "Pending",
"statusApproved": "Approved",
"statusPlayed": "Played"
"formTitle": "Invia una richiesta",
"formSubtitle": "Chiedi al DJ di passare la tua traccia preferita",
"songLabel": "Brano",
"songPlaceholder": "Titolo del brano",
"artistLabel": "Artista",
"artistPlaceholder": "Nome dell'artista (facoltativo)",
"submitButton": "Invia richiesta",
"loginToRequest": "Accedi per inviare una richiesta.",
"latestTitle": "Richieste recenti",
"latestSubtitle": "I brani richiesti più di recente",
"empty": "Ancora nessuna richiesta.",
"colSong": "Brano",
"colArtist": "Artista",
"colRequester": "Richiedente",
"colDate": "Data",
"colStatus": "Stato",
"statusPending": "In attesa",
"statusApproved": "Approvata",
"statusPlayed": "Passata",
"success": {
"posted": "Richiesta inviata."
},
"errors": {
"empty": "Inserisci il titolo del brano o l'artista.",
"invalid": "Richiesta non valida.",
"ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.",
"error": "Qualcosa è andato storto. Riprova."
}
}
}
}