City Adventure Admin

Gestione percorsi turistici

Seleziona il backend Strapi da utilizzare
{{ loginError }}

Dashboard KPI

Filtri Andamento
Periodo attivo:
{{ dashboardPeriodLabel }}
Caricamento statistiche...

Caricamento statistiche in corso...

Contenuto

{{ stats.total_routes || 0 }}

Percorsi

{{ stats.total_nodes || 0 }}

POI / Nodi

{{ stats.total_users || 0 }}

Giocatori Tot.

Utenti e Attività

Utenti Attivi ({{ dashboardPeriodLabel }})

{{ dashboardPeriodStats.activeUsers }}

+{{ dashboardPeriodStats.newUsers }} nuovi utenti
{{ dashboardTrends.activeUsersDelta }}% {{ dashboardTrends.label }}
Attivi questa Settimana

{{ stats.active_users_week || 0 }}

+{{ stats.new_users_week || 0 }} nuovi
Attivi questo Mese

{{ stats.active_users_month || 0 }}

+{{ stats.new_users_month || 0 }} nuovi
Sessioni ({{ dashboardPeriodLabel }})

{{ dashboardPeriodStats.sessions }}

Totale storico: {{ stats.total_sessions || 0 }}
{{ dashboardTrends.sessionsDelta }}% {{ dashboardTrends.label }}
Trend Giornaliero Sessioni vs Utenti Attivi
{{ dashboardChartRangeLabel }}
Sessioni Utenti attivi Giorni (GG/MM) · Valori
Nessun dato sufficiente per costruire il grafico.

Engagement

{{ dashboardEngagementStats.avgSessionsPerUser }}

Sessioni Medie/Utente (periodo)

{{ dashboardEngagementStats.completionRate }}%

Tasso Completamento (periodo)

{{ dashboardEngagementStats.sessionsInPeriod }}

Sessioni nel periodo

Top 10 Percorsi Più Giocati (periodo)
Risultati filtrati: {{ filteredTopRoutes.length }}
# Percorso Categoria Lingua Partite Completati %
{{ index + 1 }} {{ route.name }} {{ route.category || 'N/A' }} {{ route.locale === 'it' ? '🇮🇹' : '🇬🇧' }} {{ route.plays }} {{ route.completions }} {{ getRouteCompletionPercent(route) }}%
Nessuna partita giocata ancora
Top 10 Giocatori Più Attivi (periodo)
Risultati filtrati: {{ filteredTopPlayers.length }}
# Username Partite
{{ index + 1 }} {{ player.username }}
{{ player.email }}
{{ player.games }}
Nessun dato disponibile

Gestione Città ({{ cities.length }})

Caricamento...
ID Nome Città Locale Percorsi Associati Coordinate Azioni
{{ city.id }} {{ city.attributes.Name }}
{{ formatDate(city.attributes.createdAt) }}
{{ city.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }} {{ getCityRouteCount(city) }} {{ city.attributes.latitude }}, {{ city.attributes.longitude }} N/A
Nessuna città trovata. Crea la tua prima città!

Gestione Percorsi ({{ filteredRoutes.length }})

Visibili: {{ routes.length - getHiddenRoutesCount() }} Nascosti: {{ getHiddenRoutesCount() }} In uscita: {{ getScheduledRoutesCount() }}
Filtrato per città
Attenzione: Non ci sono percorsi in inglese nel database. Puoi crearli caricando CSV con locale 'en'.
Caricamento...
ID Nome Percorso Città Località Categoria Locale Stato Nodi Azioni
{{ route.id }} {{ getRouteName(route) }}
{{ formatDate(route.attributes.createdAt) }}
{{ getCityName(route) }} N/A {{ getRouteLocation(route) }} Nessuna {{ route.attributes.ROUTE_CATEGORY || 'N/A' }} {{ route.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}
{{ getVisibilityStatusLabel(route.attributes) }} {{ getReleaseStatusLabel(route.attributes) }} {{ getMissingTranslationLabel(route) }}
{{ getNodeCount(route) }}
Nessun percorso trovato con i filtri selezionati

Anteprima App

Come apparirà il contenuto nell'app Flutter, con i dati reali salvati su Strapi. È una ricostruzione grafica: non interagisce con GPS, scanner QR o notifiche.

Nessun POI su questo percorso.
In app gli enigmi del POI vengono mostrati uno dopo l'altro.
Diagnostica lista percorsi

Nell'app compaiono solo i percorsi visibili e non ancora in uscita. I record nascosti o futuri restano modificabili qui, ma non verrebbero mostrati.

{{ appPreviewVisibleRoutes.length }} visibili {{ appPreviewHiddenRoutes.length }} nascosti {{ appPreviewComingSoonRoutes.length }} in uscita
Da completare
  • {{ item }}
Nessuna mancanza rilevata per questa schermata.
{{ appPreviewError }}
{{ appPreviewClock }}
{{ appPreviewStrings.chooseRoute }}
Nascosto in app
"{{ (route.attributes.RouteName || '').toUpperCase() }}"
{{ getRouteLocation(route) }}
{{ route.attributes.locale === 'it' ? appPreviewStrings.languageIt : appPreviewStrings.languageEn }}
{{ appPreviewProgress(route) }}% {{ appPreviewStrings.completed }}
Icona percorso
{{ route.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}
{{ appPreviewStrings.availableFrom }} {{ formatDateOnly(route.attributes.ReleaseDate) }}
Nessun percorso da mostrare con i filtri attuali.
{{ appPreviewStrings.routeInfoTitle }}

{{ appPreviewRouteAttributes.RouteName }}

Immagine percorso

{{ appPreviewRouteAttributes.RouteDescription }}

{{ appPreviewStrings.routeLocationLabel }} {{ appPreviewCityName || '—' }}
{{ appPreviewStrings.routeLanguageLabel }} {{ appPreviewRouteAttributes.locale === 'it' ? appPreviewStrings.languageIt : appPreviewStrings.languageEn }}
{{ appPreviewStrings.availableFrom }} {{ formatDateOnly(appPreviewRouteAttributes.ReleaseDate) }}
{{ appPreviewStrings.startRoute }}
Seleziona un percorso per vederne il dettaglio.
{{ appPreviewStrings.riddleTitle }}
{{ appPreviewRiddle.reason }}
{{ appPreviewStrings.eventsTitle }}
Nascosto: l'app lo ignora
Main logo evento Logo sponsor evento

{{ appPreviewEventAttributes.EventName }}

{{ formatDateOnly(appPreviewEventAttributes.From) }} → {{ formatDateOnly(appPreviewEventAttributes.To) }}
{{ appPreviewStrings.eventCategory }} {{ appPreviewEventAttributes.EVENT_CATEGORY || 'N/A' }}
{{ appPreviewStrings.quizBehaviour }} {{ appPreviewEventAttributes.EVENT_QUIZ_BEHAVIOUR || 'INHERIT_FROM_ROUTE_SETTINGS' }}
{{ appPreviewStrings.scoreFields }} {{ getScoreValidationLabels(appPreviewEventAttributes).length || 0 }}
{{ appPreviewStrings.availableFrom }} {{ formatDateOnly(appPreviewEventAttributes.ReleaseDate) }}
{{ appPreviewStrings.startRoute }}
{{ label }}
Seleziona un evento per vederne l'anteprima.

Ricostruzione grafica a scopo di anteprima — non è l'app in esecuzione.

{{ selectedRoute.attributes.RouteName || 'Percorso' }}

ID: {{ selectedRoute.id }}

Nome: {{ selectedRoute.attributes.RouteName || 'N/A' }}

Località: {{ selectedRoute.attributes.RouteLocation || 'N/A' }}

Categoria: {{ selectedRoute.attributes.ROUTE_CATEGORY || 'N/A' }}

Locale: {{ selectedRoute.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}

Difficoltà: {{ selectedRoute.attributes.Difficulties || 'N/A' }}

Ha Quizzone: {{ selectedRoute.attributes.HasQuizzone ? 'Sì' : 'No' }}

Nodo di partenza fisso: {{ getFixedStartNodeLabel(selectedRoute.attributes) }}

Nodo di partenza fisso 2: {{ getSecondFixedStartNodeLabel(selectedRoute.attributes) }}

Forma percorso: {{ selectedRoute.attributes.RouteShape || 'CIRCULAR' }}

Verso di percorrenza: {{ selectedRoute.attributes.CircularDirection || 'FORWARD' }}

QR Route: {{ getQrStatusText(selectedRoute.attributes, false) }}

Icon URL: {{ selectedRoute.attributes.IconURL || 'N/A' }}

Stato in app: {{ getVisibilityStatusLabel(selectedRoute.attributes) }} {{ getReleaseStatusLabel(selectedRoute.attributes) }}

Creato: {{ formatDate(selectedRoute.attributes.createdAt) }}

Aggiornato: {{ formatDate(selectedRoute.attributes.updatedAt) }}

Descrizione
{{ selectedRoute.attributes.RouteDescription }}
Media del percorso

Usati dall'app: Icon (icona nell'angolo della card), RouteImage (immagine della card), MainLogo e SponsorLogo (branding). Se la route ha un evento, l'app usa i logo dell'evento.

{{ getMediaFieldLabel(field) }}

Nessun file

Elaborazione...
{{ getMediaError('route', selectedRoute.id, field) }}
Sponsor

Header: {{ selectedRoute.attributes.SponsorHeaderText }}

Testo: {{ selectedRoute.attributes.SponsorText }}

CTA Text: {{ selectedRoute.attributes.SponsorCallToActionText }}

CTA URL: {{ selectedRoute.attributes.SponsorCallToActionUrl }}

Usato quando la forma è CIRCULAR.
Se futura, in app la partita resta bloccata ("Disponibile dal ...").
Un percorso nascosto non compare nell'app.
Se impostato, indica il nodo di partenza fisso per la route.
Secondo punto di partenza alternativo.
Descrizione
Sponsor

Visualizzazione Geografica

Nodi del Percorso ({{ routeNodes.length }})

Caricamento...
Nessun nodo associato a questo percorso
# ID Titolo Mappa Quiz Coordinate Tipo Immagine Banner QR Locale Aneddoto Ordine
{{ index + 1 }} {{ node.id }} {{ node.attributes.Title }} Senza titolo Quiz Domanda - {{ node.attributes.MapCoordinates }} - {{ node.attributes.NodeType }} -
{{ getNodeListThumbnail(node.attributes).source }}
No
Banner
No
{{ getQrStatusText(node.attributes, false) }} {{ node.attributes.locale === 'it' ? '🇮🇹' : '🇬🇧' }} {{ node.attributes.Anecdote.substring(0, 100) }}{{ node.attributes.Anecdote.length > 100 ? '...' : '' }} Nessun aneddoto

{{ selectedNode.attributes.Title || 'POI' }}

{{ selectedNode.id }}

{{ selectedNode.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}

{{ selectedNode.attributes.Title }}

{{ selectedNode.attributes.NodeType }}

{{ selectedNode.attributes.Anecdote }}

{{ selectedNode.attributes.MapCoordinates || 'Nessuna coordinata' }}

{{ selectedNode.attributes.GeoLocalizationPolygon || 'Nessun poligono' }}

Formato KML: "lon,lat,alt" separati da spazi.
{{ getNodeMediaPopUp(selectedNode.attributes).type === 'video' ? 'VIDEO' : 'IMMAGINE' }}
{{ getNodeMediaPopUp(selectedNode.attributes).name }}

Nessun banner MediaPopUp associato a questo POI.

{{ selectedNode.attributes.Question || '(Nessuna domanda Quizzone)' }}

Usata solo se il percorso ha HasQuizzone = true
Anteprima dalla sorgente disponibile con priorita: Image del Node, poi media del Riddle, poi MediaPopUp.
{{ getNodeDisplayImage(selectedNode.attributes).name }} {{ getNodeDisplayImage(selectedNode.attributes).source }}

Nessuna immagine trovata su Node, Riddle o Banner.

{{ getQrStatusText(selectedNode.attributes, false) }} {{ selectedNode.attributes.QrEnabled ? 'QR abilitato' : 'QR disabilitato' }}
QR Code nodo

Token:

{{ selectedNode.attributes.QrToken || 'Nessun token generato' }}

Nessun QR code generato per questo nodo.
Enigmi ({{ nodeRiddles.length }})

ID: {{ riddle.id }}

{{ getRiddleCategoryLabel(riddle) }}

Tipo tecnico: {{ riddle.attributes.RiddleType || 'N/A' }}

Domanda Riddle: {{ getRiddleQuestion(riddle) }}

Soluzione: {{ riddle.attributes.Solution }}

Titolo Tutorial: {{ riddle.attributes.TutorialTitle }}

Testo Tutorial: {{ riddle.attributes.TutorialText }}

Immagine gioco Play (Riddle):

Fonte attiva in app: {{ getRiddleMediaSource(riddle) || 'Nessuna' }}. Priorita: HintURL > Media/ExtraMedia.

Sono valorizzati sia HintURL sia Media/ExtraMedia: in app verra mostrata HintURL.

Questa sezione modifica il campo Media del riddle (usato come fallback se HintURL non e valorizzata).

{{ getRiddleMedia(riddle).name }}

Nessuna media riddle associata nel campo Media.

Elaborazione...
{{ getRiddleImageError(riddle.id) }}
Risposte Riddle ({{ riddleAnswers.length }})
Estratte dal campo ExtraDetails del riddle

Risposta {{ idx + 1 }}: CORRETTA

{{ answer.text }}

Le risposte del riddle sono gestite nel campo ExtraDetails e non possono essere modificate qui.
Risposte Quiz ({{ quizAnswers.length }})
Usate se il percorso ha HasQuizzone = true

Risposta {{ idx + 1 }}: CORRETTA

Nessuna risposta trovata per questo POI

Carica Percorso da CSV

CSV bilingue richiede due percorsi separati: uno per IT e uno per EN
Crea Nuova Città IT
Crea Nuova Città EN
Seleziona prima la città italiana
Crea Nuovo Percorso IT
Inserisci il numero di POI che questo percorso conterrà
Seleziona prima la città inglese
Crea Nuovo Percorso EN
Insert the number of POIs this route will contain
Formato CSV Richiesto

Per CSV Singolo Percorso (IT o EN):

NOME, COORDINATE, ANEDDOTO, QUESTION, RISPOSTA 1 (CORRETTA), RISPOSTA 2, RISPOSTA 3, RISPOSTA 4

COORDINATE (opzionale): formato longitudine,latitudine (es: 14.42597,40.623016)

Per CSV Doppio Percorso (IT + EN):

NOME, COORDINATE, ANEDDOTO, QUESTION, RISPOSTA 1 (CORRETTA), RISPOSTA 2, RISPOSTA 3, RISPOSTA 4, ANEDDOTO, QUIZ, ANSWER 1 (CORRECT), ANSWER 2, ANSWER 3, ANSWER 4

Le prime 8 colonne sono per l'italiano, le seconde 7 per l'inglese

COORDINATE (opzionale): formato longitudine,latitudine (es: 14.42597,40.623016)

La prima risposta (RISPOSTA 1 / ANSWER 1) è sempre quella corretta
File Valido

{{ csvPreview.message }}

Nodi totali: {{ csvPreview.total_nodes }}

Anteprima primi 5 nodi
{{ idx + 1 }}. {{ node.title }}

{{ node.anecdote }}

{{ node.answers_count }} risposte
{{ uploadError }}
{{ uploadSuccess }}

Gestione Eventi ({{ filteredEvents.length }})

Caricamento...
ID Nome Descrizione Data Inizio Data Fine Locale Stato Creato
{{ event.id }} {{ event.attributes.EventName || 'Senza nome' }}
{{ event.attributes.EVENT_CATEGORY }}
{{ event.attributes.SponsorText.substring(0, 100) }}{{ event.attributes.SponsorText.length > 100 ? '...' : '' }} Nessuna descrizione {{ formatDate(event.attributes.From) }} - {{ formatDate(event.attributes.To) }} - {{ event.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}
{{ getVisibilityStatusLabel(event.attributes) }} {{ getReleaseStatusLabel(event.attributes) }}
{{ formatDate(event.attributes.createdAt) }}
Nessun evento trovato

Eventi e classifiche

Eventi in corso, storico e dati utenti utili al censimento.

Eventi totali

{{ eventLeaderboardData.summary.total_events || 0 }}

Eventi in corso

{{ eventLeaderboardData.summary.current_events || 0 }}

Utenti completati

{{ eventLeaderboardData.summary.total_completed_rows || 0 }}

Caricamento classifiche eventi in corso...

Eventi in corso
{{ filteredCurrentLeaderboardEvents.length }}
Nessun evento in corso trovato.
Eventi in corso compressi. Puoi riaprirli per passare rapidamente da un evento all'altro.

{{ selectedLeaderboardEvent.name }}

ID {{ selectedLeaderboardEvent.id }} · {{ selectedLeaderboardEvent.locale || 'N/A' }} · {{ selectedLeaderboardEvent.category || 'N/A' }}
{{ getLeaderboardStatusLabel(selectedLeaderboardEvent.status) }}
{{ formatDate(selectedLeaderboardEvent.from) }} → {{ formatDate(selectedLeaderboardEvent.to) }}
Partecipanti {{ selectedLeaderboardParticipants }}
Classificati {{ selectedLeaderboardCompletedParticipants }}
Locale {{ selectedLeaderboardEvent.locale || 'N/A' }}
Quizzone {{ selectedLeaderboardEvent.route_has_quizzone ? 'Presente' : 'Non previsto' }}
Percorso: {{ selectedLeaderboardEvent.current ? 'In corso' : 'Storico' }} Quizzone: {{ selectedLeaderboardEvent.route_has_quizzone ? 'Presente' : 'Non previsto' }} Step evento: {{ selectedLeaderboardEvent.event_nodes_count || 0 }}
Nessun utente classificato per questo evento.
# Username Nome Cognome Città Provincia Email Tel Dati salvati Percorso Quizzone POI completati Inizio Fine Tempo Score
{{ index + 1 }} {{ player.username }} {{ player.name }} {{ player.surname }} {{ player.city }} {{ player.province }} {{ player.email }} {{ player.tel }} {{ player.validation_form_saved ? 'Sì' : 'No' }} {{ player.route_completed ? 'Finito' : 'In corso' }} {{ player.quiz_available ? (player.quiz_completed ? 'Finito' : 'In corso') : 'N/D' }} {{ player.completed_poi_count ?? 0 }}
{{ formatDate(player.started_at) }}
Inizio gioco
{{ player.end_time ? formatDate(player.end_time) : 'In corso' }}
Fine gioco
{{ player.route_time_label || 'N/A' }}
{{ player.route_completed ? 'Tempo completamento' : 'Tempo trascorso' }}
{{ formatScore(player.total_score) }}
Seleziona un evento in corso o storico per visualizzare la classifica.

{{ selectedEvent.attributes.EventName || 'Evento' }}

ID: {{ selectedEvent.id }}

Nome: {{ selectedEvent.attributes.EventName }}

Categoria: {{ selectedEvent.attributes.EVENT_CATEGORY || 'N/A' }}

Locale: {{ selectedEvent.attributes.locale === 'it' ? '🇮🇹 IT' : '🇬🇧 EN' }}

Data Inizio: {{ selectedEvent.attributes.From ? formatDate(selectedEvent.attributes.From) : 'N/A' }}

Data Fine: {{ selectedEvent.attributes.To ? formatDate(selectedEvent.attributes.To) : 'N/A' }}

Stato in app: {{ getVisibilityStatusLabel(selectedEvent.attributes) }} {{ getReleaseStatusLabel(selectedEvent.attributes) }}

Difficoltà: {{ selectedEvent.attributes.Difficulty || 'N/A' }}

Comportamento Quiz: {{ selectedEvent.attributes.EVENT_QUIZ_BEHAVIOUR || 'INHERIT_FROM_ROUTE_SETTINGS' }}

Call To Action Text: {{ selectedEvent.attributes.CallToActionText || 'N/A' }}

Call To Action URL: {{ selectedEvent.attributes.CallToActionUrl }} N/A

Creato: {{ formatDate(selectedEvent.attributes.createdAt) }}

Aggiornato: {{ formatDate(selectedEvent.attributes.updatedAt) }}

Campi di convalida punteggio

Richiesti all'utente a fine evento prima di convalidare il punteggio.

{{ label }}

Nessun campo richiesto.

Media dell'evento

Il MainLogo e lo SponsorLogo dell'evento hanno priorità su quelli della route collegata.

{{ getMediaFieldLabel(field) }}

Nessun file

Elaborazione...
{{ getMediaError('event', selectedEvent.id, field) }}
Sponsor

Header: {{ selectedEvent.attributes.SponsorHeaderText }}

Testo: {{ selectedEvent.attributes.SponsorText }}

CTA Text: {{ selectedEvent.attributes.SponsorCallToActionText }}

CTA URL: {{ selectedEvent.attributes.SponsorCallToActionUrl }}

Vale anche per il percorso collegato: l'app usa la data piu' recente.
Un evento nascosto viene ignorato: la route resta un percorso normale.
Richiesti all'utente a fine evento.
Sponsor

Nodi dell'Evento ({{ eventNodes.length }})

Caricamento...
Nessun nodo associato a questo evento
# ID Titolo Quiz Coordinate Tipo Locale Aneddoto Ordine
{{ index + 1 }} {{ node.id }} {{ node.attributes.Title }} Senza titolo Quiz Domanda - {{ node.attributes.MapCoordinates }} - {{ node.attributes.NodeType }} - {{ node.attributes.locale === 'it' ? '🇮🇹' : '🇬🇧' }} {{ node.attributes.Anecdote.substring(0, 100) }}{{ node.attributes.Anecdote.length > 100 ? '...' : '' }} Nessun aneddoto