From a51bc5a8a2d231e65315406e42d3aa52be4bf023 Mon Sep 17 00:00:00 2001 From: Surya Paolo Date: Tue, 2 Dec 2025 22:16:24 +0100 Subject: [PATCH] - aggiornamenti guida RIS, FAQ - Editor HTML aggiunto CSS e Script - Statistiche - CRISBalanceBar - Inizio Sync... (ma disattivato) --- _LIMBO/risospiegazione.html | 2291 +++++++++++++++++ src-pwa/custom-service-worker.js | 54 + src/components/CElemStat/CElemStat.scss | 63 +- src/components/CElemStat/CElemStat.vue | 25 +- .../CGridTableRec/CGridTableRec.scss | 10 + src/components/CGridTableRec/CGridTableRec.ts | 165 +- .../CGridTableRec/CGridTableRec.vue | 6 +- .../CMyCardService/CMyCardService.scss | 4 +- .../CMyCardService/CMyCardService.ts | 9 +- .../CMyCardService/CMyCardService.vue | 4 +- src/components/CMyChipList/CMyChipList.scss | 16 +- src/components/CMyCircuit/CMyCircuit.scss | 778 +++++- src/components/CMyCircuit/CMyCircuit.ts | 131 +- src/components/CMyEditElem/CMyEditElem.scss | 6 + src/components/CMyEditElem/CMyEditElem.vue | 27 +- src/components/CMyEditor/CMyEditor.scss | 33 + src/components/CMyEditor/CMyEditor.ts | 76 +- src/components/CMyEditor/CMyEditor.vue | 78 +- src/components/CMyElem/CMyElem.ts | 25 + src/components/CMyElem/CMyElem.vue | 9 +- src/components/CMyGroup/CMyGroup.scss | 12 +- src/components/CMyRecCard/CMyRecCard.scss | 48 +- src/components/CMyRecCard/CMyRecCard.ts | 8 + src/components/CMyRecCard/CMyRecCard.vue | 57 +- src/components/CMyUser/CMyUser.scss | 5 +- .../CRISBalanceBar/CRISBalanceBar.scss | 27 +- .../CRISBalanceBar/CRISBalanceBar.ts | 4 + .../CRISBalanceBar/CRISBalanceBar.vue | 8 +- src/components/CSignUp/CSignUp.scss | 4 +- src/components/CStatusReg/CStatusReg.ts | 2 +- src/components/CStatusReg/CStatusReg.vue | 532 ++-- .../CStatusReg/CStatusReg_Modern.scss | 243 ++ src/components/MyFooter/MyFooter.scss | 4 +- src/components/MyFooter/MyFooter.vue | 106 +- .../Riso_Home_Modern/Riso_Home_Modern.scss | 95 +- .../Riso_Home_Modern/Riso_Home_Modern.ts | 87 +- .../Riso_Home_Modern/Riso_Home_Modern.vue | 359 +-- src/js/storage.js | 71 +- src/pages/provaris1.vue | 289 +++ src/pages/provaris2.vue | 1118 ++++++++ src/pages/provaris3.vue | 676 +++++ src/root/nutriben/home/home.scss | 4 +- src/router/routesAdmin.ts | 33 + src/services/SyncService.js | 64 + src/services/idb.js | 68 + src/statics/lang/it.js | 67 +- src/store/CircuitStore.ts | 1316 +++++++++- src/store/Modules/fieldsTable.ts | 2 + src/store/Modules/tools.ts | 27 +- src/store/globalStore.ts | 66 +- src/views/login/signup/signup.vue | 2 +- src/views/user/mycircuit/mycircuit.ts | 2 +- src/views/user/mygroup/mygroup.ts | 2 +- 53 files changed, 8041 insertions(+), 1177 deletions(-) create mode 100644 _LIMBO/risospiegazione.html create mode 100644 src/components/CStatusReg/CStatusReg_Modern.scss create mode 100644 src/pages/provaris1.vue create mode 100644 src/pages/provaris2.vue create mode 100644 src/pages/provaris3.vue create mode 100644 src/services/SyncService.js create mode 100644 src/services/idb.js diff --git a/_LIMBO/risospiegazione.html b/_LIMBO/risospiegazione.html new file mode 100644 index 00000000..042fd64c --- /dev/null +++ b/_LIMBO/risospiegazione.html @@ -0,0 +1,2291 @@ + + + + + + + RISO - Domande Frequenti + + + + + + +
+
+
+
RISO +

Perché usare i RIS? (in breve)

+
+
+
+ +
+

Un sistema di scambio semplice, trasparente e orientato alla + comunità

+
+ + + + + +
+
+
+ account_balance_wallet +
+

100% Gratuito

+

Iscrizione e utilizzo completamente gratuiti

+
+ + + + +
+
+ people +
+

Parti da Subito

+

Bastano 2 persone per iniziare a scambiare

+
+ + + + +
+
+ trending_up +
+

Crei il Tuo Valore

+

Generi RIS offrendo beni o servizi

+
+ + + + +
+
+ verified +
+

Valore Garantito

+

1 RIS = 1 Euro nel circuito

+
+
+ + + + + +
+

Come Funziona

+
+
+
1
+
person_add
+

Ti iscrivi gratuitamente

+
+ + +
arrow_forward
+ + +
+
2
+
handshake
+

Offri beni o servizi

+
+ + +
arrow_forward
+ + +
+
3
+
swap_horiz
+

Scambi con RIS

+
+ + +
arrow_forward
+ + +
+
4
+
account_balance
+

Il sistema bilancia tutto

+
+
+
+ + + + + +
+

Equilibrio Automatico

+

Il sistema bilancia automaticamente crediti e debiti della + comunità

+ + +
+
+
add_circle
+
Chi ha dato
+
+
+ + +
+ compare_arrows +

Sempre in equilibrio

+
+ + +
+
remove_circle
+
Chi ha ricevuto
+
+
+
+
+ + + + + +
+

Tutti i Vantaggi

+ + +
+
+ store +
+

Economia Locale

+

Favorisce lo sviluppo delle attività nella tua zona

+
+
+ + + + +
+ favorite +
+

Relazioni Prima di Tutto

+

Gli scambi creano legami, non solo transazioni economiche +

+
+
+ + + + +
+ sync +
+

Scambi Semplificati

+

Sistema di pagamento alternativo facile da usare

+
+
+ + + + +
+ shield +
+

Valore Sicuro

+

1 RIS = 1 Euro, valore garantito nel circuito

+
+
+ + + + +
+ account_balance_wallet +
+

Autonomia Totale

+

Crei i RIS che ti servono, senza banche centrali

+
+
+ + + + +
+ balance +
+

Sistema Bilanciato

+

Debiti e crediti si equilibrano automaticamente

+
+
+ + + + +
+ euro_symbol +
+

Niente Euro Necessari

+

Scambi senza usare moneta tradizionale

+
+
+ + + + +
+ rocket_launch +
+

Stimola l'Attività

+

I limiti di saldo ti spingono a produrre e scambiare

+
+
+ + + + +
+ visibility +
+

Tutto Trasparente

+

Regole chiare e condivise da tutti

+
+
+ + + + +
+ block +
+

No Accumulo

+

Usa i RIS attivamente invece di tenerli fermi

+
+
+ + + + +
+ groups +
+

Crei Valore Tu

+

Nessuno eroga moneta dal nulla, la generi offrendo qualcosa +

+
+
+ + + + +
+ receipt_long +
+

Tracciabilità Totale

+

Tutti i movimenti sono tracciati e trasparenti

+
+
+
+
+ + + + + +
+

Il Sistema in Numeri

+
+
+
people
+
2+
+
Persone per iniziare
+
+ + +
+
payments
+
1:1
+
RIS = Euro nel circuito
+
+ + +
+
account_balance
+
0%
+
Costo di iscrizione
+
+
+
+
+ +
+ + + +
+
+ paid +
Quanto vale 1 RIS?
+ expand_more +
+
+
+

Il RIS è l'unità di conto delle Comunità Territoriali (CT) con funzione di moneta di scambio.

+ + +
+
+ 1 RIS + arrow_forward + 1 RIS +
+

Il RIS vale RIS, non è convertibile + in euro

+
+ + +
+
+ 1 RIS + + 1 € +
+

Valore di + riferimento per dare valore alle cose negli scambi

+
+ + +

Anche se 1 RIS non è convertibile in euro, usiamo l'euro come riferimento per facilitare gli + scambi. Se vendi una bicicletta a 50€ nel mercato normale, puoi venderla a 50 RIS nel circuito + RISO.

+
+
+
+ + + +
+
+ account_balance_wallet +
Come si crea la moneta autonomamente?
+ expand_more +
+
+
+

La moneta non è solo carta o metallo. È qualsiasi cosa accettata per gli scambi + in una comunità. Il credito funziona quando tutti si fidano che possa essere convertito in beni, + servizi o euro.

+ + +
+ info + Usiamo già il credito più spesso di quanto pensiamo! Ogni volta che dici "ti pago dopo" stai + creando un credito. +
+ + +

Esempio del Caffè: Vai al bar, prendi un caffè ma non hai soldi. Il barista + segna sul taccuino "mi devi 1 caffè". Hai appena creato un credito! Il barista può cedere questo + credito al giornalaio per comprare il giornale. Il giornalaio può cederlo a te per comprare la + tua farina. Cerchio chiuso, tutti hanno scambiato senza euro!

+ + +

RISO funziona esattamente così, ma invece del taccuino usa un'app che traccia automaticamente + tutti i crediti e debiti in modo trasparente.

+
+
+
+ + + +
+
+ groups +
Come funziona nella pratica? Esempio concreto
+ expand_more +
+
+
+
+
+ menu_book + La Storia di Francesca, Mario e Alice +
+ + + +
+
+
1
+
Mario Offre Giardinaggio a Francesca
+
+
+
+
+ person +
+
Francesca
+
-40 RIS
+
+ arrow_forward +
+
+ person +
+
Mario
+
+40 RIS
+
+
+

Mario sistema il giardino di Francesca per 40 RIS. Francesca non + ha + euro subito, ma accetta il "debito" di 40 RIS che Mario può usare con altri.

+
+ + + +
+
+
2
+
Mario Compra Lezioni da Alice
+
+
+
+
+ person +
+
Mario
+
+10 RIS
+
+ arrow_forward +
+
+ person +
+
Alice
+
+30 RIS
+
+
+

Mario usa 30 dei suoi RIS per comprare lezioni di yoga da + Alice. Ora Mario ha +10 RIS, Alice ha +30 RIS, Francesca ha -40 RIS.

+
+ + + +
+
+
3
+
Alice Compra Riparazioni da Francesca
+
+
+
+
+ person +
+
Alice
+
0 RIS
+
+ arrow_forward +
+
+ person +
+
Francesca
+
-10 RIS
+
+
+

Alice usa i suoi 30 RIS per pagare Francesca che le ripara il + computer. Ora Alice è a 0 RIS, Francesca ha -10 RIS (aveva -40, ha ricevuto 30).

+
+ + + +
+
+
4
+
Francesca Compra Assistenza da Mario
+
+
+
+
+ person +
+
Francesca
+
0 RIS
+
+ arrow_forward +
+
+ person +
+
Mario
+
0 RIS
+
+
+

Francesca usa i suoi ultimi 10 RIS per pagare Mario che lo aiuta + con le pratiche burocratiche. Tutti tornano a 0 RIS!

+
+ + +
+ verified + Cerchio chiuso perfettamente! Mario, Francesca e Alice hanno tutti + scambiato beni e servizi senza usare un solo euro. I RIS si sono creati e poi distrutti + automaticamente attraverso gli scambi. +
+
+
+
+
+ + + +
+
+ store +
Gli esercenti con partita IVA possono accettare RIS?
+ expand_more +
+
+
+

Sì! Gli esercenti possono offrire sconti fino al 30% e farsi pagare quella parte + in RIS.

+ + +
+

Esempio pratico:

+
+ Prezzo prodotto + 100 € +
+
+ Sconto 30% + -30 € +
+
+ Scontrino fiscale + 70 € +
+
+ Cliente paga + 70 € + 30 RIS +
+
+ + +
+ info + Consulta sempre il tuo commercialista per la corretta gestione fiscale della tua situazione + specifica. +
+ + +

Vantaggi per l'esercente:

+
    +
  • Attiri nuovi clienti che usano RIS
  • +
  • Fidelizzi i membri del circuito
  • +
  • Ottieni sia euro (per tasse) che RIS (per altri scambi)
  • +
+
+
+
+ + + +
+
+ person +
I privati cittadini possono accettare RIS?
+ expand_more +
+
+
+

Sì! Per scambi occasionali sotto una certa soglia annua, come già avviene con + gli euro.

+ + +
+
+
+ check_circle + Scambi OK +
+
+
+ check + Vicino ti aggiusta il rubinetto +
+
+ check + Vendi bici usata +
+
+ check + Dai ripetizioni occasionali +
+
+ check + Ortaggi dal tuo orto +
+
+
+ + +
+
+ warning + Verifica con Commercialista +
+
+
+ arrow_forward + Vendi regolarmente grandi quantità +
+
+ arrow_forward + Servizi professionali frequenti +
+
+ arrow_forward + Introiti continuativi +
+
+
+
+
+
+
+ + + +
+
+ currency_exchange +
Il RIS è convertibile in euro?
+ expand_more +
+
+
+

No, e questo è un vantaggio! Il RIS circola liberamente negli scambi senza + bisogno di conversione.

+ + +
+
+
+ account_balance_wallet +
+
Hai +50 RIS
+
+ arrow_forward +
+
+ shopping_cart +
+
Compri 30 RIS
+
+ arrow_forward +
+
+ check +
+
Hai +20 RIS
+
+
+ + +
+ lightbulb + Il RIS si usa direttamente, non si converte. È questo il suo punto di forza: è fatto per + circolare, non per essere trasformato in euro. +
+ + +

Unico caso di conversione: Se esci dal circuito con saldo positivo, chi ha + debiti ti ripaga in euro. Ma questo è un caso estremo, non il funzionamento normale.

+
+
+
+ + + +
+
+ receipt +
Posso pagare bollette e tasse con i RIS?
+ expand_more +
+
+
+
+ close + No, non è possibile. Bollette e tasse richiedono euro. +
+ + +

Ma ecco la strategia intelligente:

+ + +
+
+
+
+
Usa Euro Per
+
+
    +
  • Bollette (luce, gas, acqua)
  • +
  • Tasse e imposte
  • +
  • Affitto o mutuo
  • +
  • Assicurazioni
  • +
+
+ + +
+
+
R
+
Usa RIS Per
+
+
    +
  • Cibo (ortaggi, pane locale)
  • +
  • Servizi (riparazioni, lezioni)
  • +
  • Abbigliamento
  • +
  • Tempo libero
  • +
+
+
+ + +
+ trending_up + I tuoi euro vanno molto più lontano se usi i RIS per tutto il resto! +
+
+
+
+ + + +
+
+ help +
Se non ho euro, come posso ottenere RIS?
+ expand_more +
+
+
+
+ star + Questo è il punto rivoluzionario: Non hai bisogno di avere RIS per iniziare! +
+ + +
+
+
+ block + Sistema Euro +
+
+

Devi prima avere soldi → Lavori + settimane → Aspetti stipendio → Poi compri

+
+
+ + +
+
+ check_circle + Sistema RISO +
+
+

Compri SUBITO (vai in negativo) → + Ripaghi dopo offrendo beni/servizi

+
+
+
+ + +

Esempio Maria: Maria non ha euro ma sa cucire. Va da Giorgio e compra ortaggi + per 30 RIS (va a -30). Nei giorni dopo sistema vestiti per vari membri guadagnando 30 RIS. Torna + a zero. Ha mangiato oggi senza avere euro!

+ + +
+ lightbulb + La tua capacità di offrire qualcosa diventa potere d'acquisto immediato! +
+
+
+
+ + + +
+
+ compare +
Quali sono le differenze da altri sistemi di moneta alternativa?
+ expand_more +
+
+
+

Differenza principale: Il RIS non è moneta "fiat" - ogni RIS rappresenta valore + reale produttivo.

+ + +
+
+
Altri Sistemi
+
+
+ check + Bonus monete all'iscrizione +
+
+ close + Valore basato solo su fiducia +
+
+ close + Rischio schema Ponzi +
+
+ close + Se chiude, perdi tutto +
+
+
+ + +
+
RISO
+
+
+ close + Niente bonus, parti da zero +
+
+ check + Ogni RIS = impegno reale +
+
+ check + Sempre bilanciato (+/- uguale) +
+
+ check + Credito garantito in euro +
+
+
+
+ + +
+ verified + Principio chiave: Ogni saldo positivo ha un corrispondente negativo. + Impossibile creare moneta dal nulla. +
+
+
+
+ + + +
+
+ exit_to_app +
Cosa succede se il circuito chiude o voglio uscire?
+ expand_more +
+
+
+ ```html +

Per capire se un sistema è affidabile, la domanda chiave da porsi è: se chiude domani, + tutti recuperano il + loro credito oppure qualcuno ci perde?

+

Il tuo credito è sempre garantito grazie al bilanciamento perfetto del sistema. +

+ + +
+

Esempio di chiusura:

+
+ Creditori +1000 RIS + compare_arrows + Debitori -1000 RIS +
+

Perfettamente bilanciato, sempre!

+
+ + +

Processo di chiusura:

+
    +
  1. Si cerca di compensare tutto con scambi diretti
  2. +
  3. Chi resta in negativo, se non riesce paga in euro
  4. +
  5. Chi ha crediti riceve euro
  6. +
+ + +
+ shield + Il tuo credito è sempre coperto da impegni reali di persone reali. È una garanzia matematica, + non una promessa vaga. +
+
+
+
+ + + +
+
+ savings +
Si può accumulare moneta? Ci sono limiti?
+ expand_more +
+
+
+

Il sistema scoraggia l'accumulo per incentivare la circolazione della moneta. +

+ + +
+

Limiti di saldo suggeriti:

+
+
-100
+
Zona Operativa
+
+200
+
+
+ Minimo -100 RIS + Massimo +200 RIS +
+
+ + +
+
+
+ trending_down + Vicino a -100? +
+

Offri servizi o beni per guadagnare RIS e + risalire

+
+ + +
+
+ trending_up + Vicino a +200? +
+

Spendi i RIS per tornare operativo e far + circolare la moneta

+
+
+ + +
+ info + Filosofia RISO: La moneta è uno strumento per scambiare, non ricchezza da + accumulare. La vera ricchezza è il benessere, le relazioni, il tempo libero. +
+
+
+
+ + + +
+
+ visibility +
Come funziona la trasparenza del sistema?
+ expand_more +
+
+
+

RISO rende pubblici tutti i dati del circuito in tempo reale. Puoi sempre + verificare che i conti tornano.

+ + +
+
+
+ account_balance + Totale Circolante +
+

Quanti RIS esistono nel circuito in questo + momento

+
+ + +
+
+ swap_horiz + Totale Transato +
+

Volume di scambi dall'inizio del circuito +

+
+ + +
+
+ people + Utenti Attivi +
+

Quanti hanno credito e quanti debito

+
+ + +
+
+ analytics + Distribuzione +
+

Come sono distribuiti i saldi tra i membri +

+
+
+ + +
+ verified_user + Garanzia matematica: Somma crediti = Somma debiti, SEMPRE. È impossibile che ci + sia squilibrio. +
+
+
+
+ + + +
+
+ sync_alt +
Cosa si può scambiare nei circuiti RISO?
+ expand_more +
+
+
+

Tutto ciò che i partecipanti concordano di scambiarsi! Non ci sono liste + predefinite.

+ + +
+
+
+ shopping_bag + Beni +
+
    +
  • Ortaggi e frutta
  • +
  • Prodotti artigianali
  • +
  • Pane e dolci
  • +
  • Abbigliamento usato
  • +
  • Mobili e oggetti
  • +
  • Libri e media
  • +
+
+ + +
+
+ build + Servizi +
+
    +
  • Riparazioni (idraulica, elettricità)
  • +
  • Lezioni (musica, lingue, sport)
  • +
  • Pulizie e manutenzioni
  • +
  • Ripetizioni scolastiche
  • +
  • Consulenze
  • +
  • Trasporti
  • +
+
+
+ + +

Come decidere il prezzo? Usa il riferimento euro (20€ → 20 RIS). Il mercato ti + darà feedback: se nessuno compra, abbassa; se vendi tutto subito, alza leggermente.

+
+
+
+ + + +
+
+ location_city +
Cosa sono le Comunità Territoriali?
+ expand_more +
+
+
+

Gruppi di persone vicine fisicamente che si incontrano regolarmente per + scambiare e costruire relazioni.

+ + +
+
+
+ diversity_3 + Incontri Reali +
+

Non virtuale - persone che si vedono faccia + a faccia e costruiscono fiducia

+
+ + +
+
+ storefront + Economia Locale +
+

La ricchezza resta nel territorio invece di + fuggire verso grandi corporation

+
+ + +
+
+ account_balance_wallet + Autonomia +
+

Ogni CT decide le proprie regole e gestisce + il proprio circuito

+
+
+ + +

Dimensioni ideali:

+
    +
  • 5-10 persone: Micro-CT, ottima per iniziare
  • +
  • 20-100 persone: Ideale - varietà + coesione
  • +
  • 100-500 persone: Grande - serve buona organizzazione
  • +
+
+
+
+ + + +
+
+ rocket_launch +
Come posso iniziare o creare un circuito?
+ expand_more +
+
+
+

Ci sono due modi principali per iniziare:

+ + +
+
+
1
+
Crea una Comunità
+
+

Coinvolgi 10-20 persone della tua zona + interessate a scambiare. Organizza un incontro informativo, spiega il sistema, iniziate + insieme. Più siete, più varietà di scambi potrete fare.

+
+ + +
+
+
2
+
Inizia da Solo
+
+

Parti con 1-2 persone fidate, fate i primi + scambi, poi invitate altri man mano che vedono che funziona. Crescita organica e solida.

+
+ + +
+ info + Iscriversi su riso.app è completamente gratuito. Non ci sono costi di adesione + o quote mensili! +
+ + + + open_in_new + Vai su riso.app per iniziare + +
+
+
+
+

+ Guarda il video di APPROFONDIMENTO per capire meglio come funziona il Sistema di Credito + Comunitario +

+
+ +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/src-pwa/custom-service-worker.js b/src-pwa/custom-service-worker.js index 7276e87a..c43ad753 100755 --- a/src-pwa/custom-service-worker.js +++ b/src-pwa/custom-service-worker.js @@ -117,8 +117,62 @@ self.addEventListener('activate', (event) => { ); }) ); + self.clients.claim(); // SERVE? OPPURE NO ? }); +const USASYNC = false; +// PER ATTIVARE IL SYNC TOGLI L'AREA COMMENTATA DI 'FETCH' QUI SOTTO .... +/* +// Strategia fetch +self.addEventListener('fetch', (event) => { + const { request } = event; + const url = new URL(request.url); + + // ============================================ + // IMPORTANTE: NON cachare API /sync o /loadsite + // ============================================ + if (url.pathname.includes('/api/') || + url.pathname.includes('/sync') || + url.pathname.includes('/loadsite')) { + // Lascia passare normalmente - IndexedDB gestisce cache + return; + } + + // ============================================ + // Cache Strategy per assets statici + // ============================================ + if (request.method === 'GET') { + event.respondWith( + caches.match(request).then((cachedResponse) => { + if (cachedResponse) { + return cachedResponse; + } + + return fetch(request).then((response) => { + // Non cachare se non è successo + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + // Clona e salva in cache + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(request, responseToCache); + }); + + return response; + }).catch(() => { + // Offline fallback + if (request.destination === 'document') { + return caches.match('/offline.html'); + } + }); + }) + ); + } +}); +*/ + console.log( ' [ VER-' + VITE_APP_VERSION + diff --git a/src/components/CElemStat/CElemStat.scss b/src/components/CElemStat/CElemStat.scss index 26047c77..48c488dc 100644 --- a/src/components/CElemStat/CElemStat.scss +++ b/src/components/CElemStat/CElemStat.scss @@ -5,6 +5,8 @@ $card-radius-mobile: 14px; $transition-smooth: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +$gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + .stat-card { position: relative; width: 100%; @@ -15,7 +17,7 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); padding: 0; border-radius: $card-radius-desktop; background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%); - box-shadow: + box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.02); transition: $transition-base; @@ -24,7 +26,7 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); &:hover { transform: translateY(-6px) scale(1.02); - box-shadow: + box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.03); @@ -108,7 +110,7 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); height: 64px; border-radius: 50%; background: linear-gradient(145deg, #ffffff, #f5f5f5); - box-shadow: + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.9); @@ -158,26 +160,26 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .stat-title { - font-size: 0.75rem; + font-size: 1rem; font-weight: 600; letter-spacing: 0.03em; line-height: 1.3; opacity: 0.85; text-transform: uppercase; margin: 4px 0; - color: #424242; + color: #718096; @media (max-width: 960px) { - font-size: 0.7rem; + font-size: 0.9rem; } @media (max-width: 718px) { - font-size: 0.65rem; + font-size: 0.8rem; margin: 2px 0; } @media (max-width: 480px) { - font-size: 0.6rem; + font-size: 0.8rem; letter-spacing: 0.02em; } } @@ -195,11 +197,14 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .stat-value { - font-size: 1.5rem; - font-weight: 700; - line-height: 1.1; letter-spacing: -0.03em; - color: #1a1a1a; + font-size: 2rem; + font-weight: 800; + background: $gradient-primary; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + line-height: 1; + padding-bottom: 2px; @media (max-width: 960px) { font-size: 1.375rem; @@ -223,7 +228,7 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); font-size: 0.625rem; font-weight: 600; color: white; - box-shadow: + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2), inset 0 1px 2px rgba(255, 255, 255, 0.2); backdrop-filter: blur(8px); @@ -276,12 +281,10 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); left: -50%; width: 200%; height: 200%; - background: linear-gradient( - 45deg, - transparent 30%, - rgba(255, 255, 255, 0.4) 50%, - transparent 70% - ); + background: linear-gradient(45deg, + transparent 30%, + rgba(255, 255, 255, 0.4) 50%, + transparent 70%); transform: translateX(-100%) translateY(-100%); transition: transform 0.8s ease; pointer-events: none; @@ -294,8 +297,8 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-radius: inherit; padding: 2px; background: linear-gradient(145deg, rgba(33, 150, 243, 0.3), rgba(156, 39, 176, 0.3)); - -webkit-mask: - linear-gradient(#fff 0 0) content-box, + -webkit-mask: + linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask-composite: xor; mask-composite: exclude; @@ -307,10 +310,13 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); // Animations @keyframes pulse { - 0%, 100% { + + 0%, + 100% { opacity: 0.3; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.05); @@ -318,9 +324,12 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } @keyframes bounce-subtle { - 0%, 100% { + + 0%, + 100% { transform: translateY(0); } + 50% { transform: translateY(-2px); } @@ -339,9 +348,11 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); opacity: 0; transform: scale(0.5) translateY(10px); } + 60% { transform: scale(1.1) translateY(-2px); } + 100% { opacity: 1; transform: scale(1) translateY(0); @@ -398,12 +409,12 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); .body--dark { .stat-card { background: linear-gradient(145deg, #1e1e1e 0%, #2a2a2a 100%); - box-shadow: + box-shadow: 0 10px 30px -5px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05); &:hover { - box-shadow: + box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.08); } @@ -411,7 +422,7 @@ $transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); .stat-icon-wrapper { background: linear-gradient(145deg, #2a2a2a, #1e1e1e); - box-shadow: + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.05); } diff --git a/src/components/CElemStat/CElemStat.vue b/src/components/CElemStat/CElemStat.vue index 783868f6..fbd6efa6 100644 --- a/src/components/CElemStat/CElemStat.vue +++ b/src/components/CElemStat/CElemStat.vue @@ -6,18 +6,16 @@
-
+
- -
- {{ title }} -
-
@@ -32,11 +30,19 @@ :style="{ backgroundColor: colBack }" >
- + +{{ value_today }} oggi
+ +
+ {{ title }} +
@@ -45,9 +51,8 @@ - + diff --git a/src/components/CGridTableRec/CGridTableRec.scss b/src/components/CGridTableRec/CGridTableRec.scss index 69c45567..b761cadf 100755 --- a/src/components/CGridTableRec/CGridTableRec.scss +++ b/src/components/CGridTableRec/CGridTableRec.scss @@ -6,6 +6,9 @@ $border-radius: 10px; $transition-speed: 0.3s; $mobile-breakpoint: 768px; +@use 'sass:color'; + + $shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06); $shadow-md: 0 2px 6px rgba(0, 0, 0, 0.08); $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15); @@ -219,9 +222,16 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15); } .grid-card-item { width: 100%; + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(8px); + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.9); + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); + padding: 8px; // Spazio tra carousel e card @media (max-width: $mobile-breakpoint) { width: 100%; + padding: 4px; // Ridotto su mobile } } diff --git a/src/components/CGridTableRec/CGridTableRec.ts b/src/components/CGridTableRec/CGridTableRec.ts index a979866e..99d7b517 100755 --- a/src/components/CGridTableRec/CGridTableRec.ts +++ b/src/components/CGridTableRec/CGridTableRec.ts @@ -1451,84 +1451,95 @@ export default defineComponent({ } function onRequest(myprops: any) { - const { page, rowsPerPage, rowsNumber, sortBy, descending } = myprops.pagination; - const myfilternow = myfilter.value; - const myfilterandnow = myfilterand.value; + try { + const { page, rowsPerPage, rowsNumber, sortBy, descending } = myprops.pagination; + const myfilternow = myfilter.value; + const myfilterandnow = myfilterand.value; - savefilter(); + savefilter(); - if (!mytable.value) { - startsearch.value = false; - return; - } - - // console.log('myfilterandnow', myfilterandnow, 'myfilterandnow', myfilterandnow) - - // console.log('onRequest', 'myfilter = ', myfilter.value) - - loading.value = true; - - spinner_visible.value = true; - - // update rowsCount with appropriate value - - // function all rows if "All" (0) is rowsel - const fetchCount = rowsPerPage === 0 ? rowsNumber : rowsPerPage; - - // calculate starting row of data - const startRow = (page - 1) * rowsPerPage; - const endRow = startRow + fetchCount; - - console.log('onRequest: startRow', startRow, 'endRow', endRow); - - serverData.value = []; - - // fetch data from "server" - return fetchFromServer( - startRow, - endRow, - myfilternow, - myfilterandnow, - sortBy, - descending - ).then((ris: any) => { - pagination.value.rowsNumber = getRowsNumberCount(); - - // clear out existing data and add new - if (!returnedData.value || returnedData.value.length === 0) { - serverData.value = []; - } else { - // if (serverData.length > 0) - // serverData.splice(0, serverData.length, ...returnedData) - // else - try { - serverData.value = [...returnedData.value]; - } catch (e) { - serverData.value = []; - } + if (!mytable.value) { + startsearch.value = false; + return; } - // console.log('serverData', serverData) + // console.log('myfilterandnow', myfilterandnow, 'myfilterandnow', myfilterandnow) - // don't forfunction to update local pagination object - pagination.value.page = page; - pagination.value.rowsPerPage = rowsPerPage; - //pagination.value.sortBy = getObjSort(sortBy, descending) - pagination.value.sortBy = sortBy; - // ordinam.value = sortBy - ordinam_desc.value = descending; - pagination.value.descending = descending; + // console.log('onRequest', 'myfilter = ', myfilter.value) - // console.log('pagination', pagination) + loading.value = true; - // ...and turn of loading indicator - loading.value = false; - spinner_visible.value = false; - changetable.value = false; - startsearch.value = false; + spinner_visible.value = true; - checkScrollPosition(); - }); + // update rowsCount with appropriate value + + // function all rows if "All" (0) is rowsel + const fetchCount = rowsPerPage === 0 ? rowsNumber : rowsPerPage; + + // calculate starting row of data + const startRow = (page - 1) * rowsPerPage; + const endRow = startRow + fetchCount; + + console.log('onRequest: startRow', startRow, 'endRow', endRow); + + if (page > 1) { + // Aggiungi senza duplicati + const existingIds = new Set(serverData.value.map((rec) => rec._id)); + const toadd = returnedData.value.filter((elem) => !existingIds.has(elem._id)); + serverData.value = [...serverData.value, ...toadd]; + } else { + serverData.value = [...returnedData.value]; + } + + // fetch data from "server" + return fetchFromServer( + startRow, + endRow, + myfilternow, + myfilterandnow, + sortBy, + descending + ).then((ris: any) => { + pagination.value.rowsNumber = getRowsNumberCount(); + + // clear out existing data and add new + if (!returnedData.value || returnedData.value.length === 0) { + serverData.value = []; + } else { + // if (serverData.length > 0) + // serverData.splice(0, serverData.length, ...returnedData) + // else + try { + serverData.value = [...returnedData.value]; + } catch (e) { + serverData.value = []; + } + } + + // console.log('serverData', serverData) + + // don't forfunction to update local pagination object + pagination.value.page = page; + pagination.value.rowsPerPage = rowsPerPage; + //pagination.value.sortBy = getObjSort(sortBy, descending) + pagination.value.sortBy = sortBy; + // ordinam.value = sortBy + ordinam_desc.value = descending; + pagination.value.descending = descending; + + // console.log('pagination', pagination) + + // ...and turn of loading indicator + loading.value = false; + spinner_visible.value = false; + changetable.value = false; + startsearch.value = false; + + checkScrollPosition(); + }); + } catch (e) { + console.error('Error onrequest', e); + } } function onUpdateData(index: number, myprops: any, done: any) { @@ -1768,6 +1779,20 @@ export default defineComponent({ } ); + watch( + () => searchList.value, + (to, from) => { + if ( + searchList.value && + !changetable.value && + !startsearch.value && + !loading.value + ) { + refresh(); + } + } + ); + /*watch(() => myfilterand.value, (newval, oldval) => { refresh() })*/ diff --git a/src/components/CGridTableRec/CGridTableRec.vue b/src/components/CGridTableRec/CGridTableRec.vue index d6351005..b2baab2d 100755 --- a/src/components/CGridTableRec/CGridTableRec.vue +++ b/src/components/CGridTableRec/CGridTableRec.vue @@ -513,7 +513,6 @@
recbook.userId === userStore.my._id && recbook.booked) + return arrbookings.value.some((recbook: IBookedEvent) => recbook.userId === userStore.my._id && recbook.booked); } function extraparams() { @@ -734,6 +734,7 @@ export default defineComponent({ ismounted, updatePart, numpart, + colmyUserPeople, } } }) diff --git a/src/components/CMyCardService/CMyCardService.vue b/src/components/CMyCardService/CMyCardService.vue index 1b22ea77..405e45f9 100644 --- a/src/components/CMyCardService/CMyCardService.vue +++ b/src/components/CMyCardService/CMyCardService.vue @@ -1041,7 +1041,7 @@
{{ $t('services.createdBy') }} {{ createdBy }}{{ myrec.createdBy }}
@@ -1522,7 +1522,7 @@ v-else-if="bookEventForm.booked" :label="getTitleBtnBooking()" color="primary" - @click="saveBookEvent()" + @click="saveBookEvent" :disable="!(bookEventpage.state === EState.Creating || hasModifiedBooking)" > diff --git a/src/components/CMyChipList/CMyChipList.scss b/src/components/CMyChipList/CMyChipList.scss index 02fc51a0..60f964b2 100755 --- a/src/components/CMyChipList/CMyChipList.scss +++ b/src/components/CMyChipList/CMyChipList.scss @@ -35,7 +35,7 @@ $mobile-breakpoint: 768px; // ======================================== .text-subtitle2 { &.text-primary { - background: linear-gradient(135deg, $primary-color, lighten($primary-color, 15%)); + background: linear-gradient(135deg, $primary-color, color.adjust($primary-color, $lightness: 15%)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -116,7 +116,7 @@ $mobile-breakpoint: 768px; // Colori con gradienti &[class*='bg-primary'], &[class*='bg-blue'] { - background: linear-gradient(135deg, $primary-color, lighten($primary-color, 10%)) !important; + background: linear-gradient(135deg, $primary-color, color.adjust($primary-color, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba($primary-color, 0.3); &:hover { @@ -127,7 +127,7 @@ $mobile-breakpoint: 768px; &[class*='bg-secondary'], &[class*='bg-teal'] { - background: linear-gradient(135deg, $secondary-color, lighten($secondary-color, 10%)) !important; + background: linear-gradient(135deg, $secondary-color, color.adjust($secondary-color, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba($secondary-color, 0.3); &:hover { @@ -138,7 +138,7 @@ $mobile-breakpoint: 768px; &[class*='bg-positive'], &[class*='bg-green'] { - background: linear-gradient(135deg, $positive-color, lighten($positive-color, 10%)) !important; + background: linear-gradient(135deg, $positive-color, color.adjust($positive-color, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba($positive-color, 0.3); &:hover { @@ -161,7 +161,7 @@ $mobile-breakpoint: 768px; &[class*='bg-warning'], &[class*='bg-orange'], &[class*='bg-amber'] { - background: linear-gradient(135deg, $warning-color, lighten($warning-color, 10%)) !important; + background: linear-gradient(135deg, $warning-color, color.adjust($warning-color, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba($warning-color, 0.3); &:hover { @@ -182,7 +182,7 @@ $mobile-breakpoint: 768px; } &[class*='bg-purple'] { - background: linear-gradient(135deg, #9c27b0, lighten(#9c27b0, 10%)) !important; + background: linear-gradient(135deg, #9c27b0, color.adjust(#9c27b0, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba(#9c27b0, 0.3); &:hover { @@ -192,7 +192,7 @@ $mobile-breakpoint: 768px; } &[class*='bg-pink'] { - background: linear-gradient(135deg, #e91e63, lighten(#e91e63, 10%)) !important; + background: linear-gradient(135deg, #e91e63, color.adjust(#e91e63, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba(#e91e63, 0.3); &:hover { @@ -203,7 +203,7 @@ $mobile-breakpoint: 768px; &[class*='bg-grey'], &[class*='bg-gray'] { - background: linear-gradient(135deg, #757575, lighten(#757575, 10%)) !important; + background: linear-gradient(135deg, #757575, color.adjust(#757575, $lightness: 10%)) !important; box-shadow: 0 2px 8px rgba(#757575, 0.3); &:hover { diff --git a/src/components/CMyCircuit/CMyCircuit.scss b/src/components/CMyCircuit/CMyCircuit.scss index b41e3954..8cb2e47a 100755 --- a/src/components/CMyCircuit/CMyCircuit.scss +++ b/src/components/CMyCircuit/CMyCircuit.scss @@ -3,113 +3,703 @@ flex: 1; } -.regulation-container { - :deep(.regulation-content) { - max-width: 900px; - margin: 0 auto; +// CSS MODERNO PER REGOLAMENTO - .reg-header { - text-align: center; - margin-bottom: 2rem; - padding-bottom: 1.5rem; - border-bottom: 2px solid #6b8e23; +// Variabili +$s-xs: 4px; +$s-sm: 8px; +$s-md: 12px; +$s-lg: 16px; +$s-xl: 24px; - h1 { - font-size: 2rem; - font-weight: 700; - color: #6b8e23; - margin: 0; - } +$r-sm: 8px; +$r-md: 12px; +$r-lg: 16px; + +$gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +$gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%); +$gradient-warning: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); + +.regulation-content { + max-width: 800px; + margin: 0 auto; + padding: $s-lg; + background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf3 100%); + min-height: 100vh; +} + +// Header +.reg-header { + background: $gradient-primary; + color: white; + padding: $s-xl; + border-radius: $r-lg; + text-align: center; + margin-bottom: $s-xl; + box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3); + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + top: -50%; + right: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); + } + + .header-icon { + margin-bottom: $s-md; + + svg { + width: 48px; + height: 48px; + color: white; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); + } + } + + h1 { + margin: 0; + font-size: 2rem; + font-weight: 800; + text-shadow: 0 2px 8px rgba(0,0,0,0.2); + } + + .circuit-badge { + display: inline-block; + background: rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + padding: $s-sm $s-lg; + border-radius: 20px; + margin-top: $s-md; + font-weight: 600; + border: 1px solid rgba(255,255,255,0.3); + } +} + +// Intro card +.intro-card { + background: white; + border-radius: $r-lg; + padding: $s-lg; + margin-bottom: $s-xl; + box-shadow: 0 4px 16px rgba(0,0,0,0.1); + display: flex; + gap: $s-lg; + align-items: flex-start; + + .intro-icon { + font-size: 2rem; + flex-shrink: 0; + } + + .intro-content { + h3 { + margin: 0 0 $s-sm 0; + color: #667eea; + font-size: 1.2rem; } - .reg-section { - margin-bottom: 2.5rem; - - .section-title { - font-size: 1.5rem; - font-weight: 600; - color: #556b2f; - margin-bottom: 1rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid rgba(107, 142, 35, 0.3); - } - - .section-content { - font-size: 1rem; - line-height: 1.7; - color: #333; - text-align: justify; - margin-bottom: 1rem; - - strong { - color: #6b8e23; - font-weight: 600; - } - } - - .section-list { - list-style: none; - padding-left: 0; - - scssli { - padding: 0.75rem 0 0.75rem 3rem; // aumenta da 2rem a 3rem - position: relative; - line-height: 1.6; - - &:before { - content: "•"; - position: absolute; - left: 1rem; // aumenta da 0.5rem a 1rem - color: #6b8e23; - font-size: 1.5rem; - line-height: 1.6; - } - } - } - - .highlight-box { - background: rgba(107, 142, 35, 0.08); - border-left: 4px solid #6b8e23; - padding: 1rem 1.5rem; - margin: 1rem 0; - border-radius: 4px; - - strong { - display: block; - margin-bottom: 0.5rem; - } - } - } - - .reg-footer { - margin-top: 3rem; - padding-top: 1.5rem; - border-top: 1px solid rgba(107, 142, 35, 0.3); - font-size: 0.95rem; - color: #666; + p { + margin: 0; + color: #64748b; + line-height: 1.6; } } } -// Responsive -@media (max-width: 768px) { - .regulation-container :deep(.regulation-content) { - .reg-header h1 { - font-size: 1.5rem; +// Sezioni +.reg-section { + background: white; + border-radius: $r-lg; + padding: $s-xl; + margin-bottom: $s-lg; + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + position: relative; + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 6px 20px rgba(0,0,0,0.12); + transform: translateY(-2px); + } + + .section-number { + position: absolute; + top: $s-lg; + right: $s-lg; + font-size: 3rem; + font-weight: 900; + color: rgba(102, 126, 234, 0.1); + line-height: 1; + } + + .section-header { + display: flex; + align-items: center; + gap: $s-md; + margin-bottom: $s-lg; + padding-bottom: $s-md; + border-bottom: 3px solid #f1f5f9; + + .section-icon { + font-size: 2rem; + flex-shrink: 0; } - .reg-section { - .section-title { - font-size: 1.25rem; - } + .section-title { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + color: #1e293b; + } + } - .section-content { - text-align: left; - } + .section-body { + .section-content { + line-height: 1.8; + color: #475569; + margin-bottom: $s-lg; - .section-list li { - padding-left: 1.5rem; + strong { + color: #667eea; + font-weight: 600; } } } +} + +// Info boxes +.info-box { + display: flex; + gap: $s-md; + padding: $s-lg; + border-radius: $r-md; + margin: $s-lg 0; + align-items: flex-start; + + &.primary { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); + border-left: 4px solid #667eea; + } + + &.success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + border-left: 4px solid #10b981; + } + + &.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%); + border-left: 4px solid #f59e0b; + } + + &.neutral { + background: rgba(100, 116, 139, 0.08); + border-left: 4px solid #64748b; + } + + .info-icon { + font-size: 1.5rem; + flex-shrink: 0; + } + + .info-text { + flex: 1; + line-height: 1.6; + color: #475569; + + strong { + display: block; + margin-bottom: $s-xs; + color: #1e293b; + } + } +} + +// Timeline +.timeline { + margin: $s-xl 0; + position: relative; + + &::before { + content: ''; + position: absolute; + left: 20px; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); + } + + .timeline-item { + display: flex; + gap: $s-lg; + margin-bottom: $s-lg; + position: relative; + + .timeline-dot { + width: 40px; + height: 40px; + border-radius: 50%; + background: $gradient-primary; + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + flex-shrink: 0; + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); + position: relative; + z-index: 1; + } + + .timeline-content { + flex: 1; + background: #f8fafc; + padding: $s-md; + border-radius: $r-md; + border: 1px solid #e2e8f0; + + strong { + display: block; + color: #667eea; + margin-bottom: $s-xs; + font-size: 0.95rem; + } + + p { + margin: 0; + color: #64748b; + font-size: 0.9rem; + line-height: 1.5; + } + } + } +} + +// Principle box +.principle-box { + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border: 2px solid #0ea5e9; + border-radius: $r-lg; + padding: $s-xl; + margin: $s-xl 0; + text-align: center; + + .principle-icon { + font-size: 3rem; + margin-bottom: $s-md; + } + + h4 { + margin: 0 0 $s-sm 0; + color: #0369a1; + font-size: 1.2rem; + } + + p { + margin: 0 0 $s-lg 0; + color: #0c4a6e; + } + + .balance-visual { + display: flex; + justify-content: center; + align-items: center; + gap: $s-xl; + margin-top: $s-lg; + + .balance-side { + display: flex; + flex-direction: column; + gap: $s-xs; + + &.positive .balance-value { + color: #10b981; + font-size: 3rem; + } + + &.negative .balance-value { + color: #ef4444; + font-size: 3rem; + } + + .balance-label { + font-size: 0.85rem; + font-weight: 600; + color: #64748b; + } + + .balance-value { + font-weight: 900; + } + } + + .balance-equal { + font-size: 2rem; + font-weight: 700; + color: #0369a1; + } + } +} + +// Cases grid +.cases-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: $s-md; + margin: $s-lg 0; + + .case-card { + background: #f8fafc; + border: 2px solid #e2e8f0; + border-radius: $r-md; + padding: $s-lg; + text-align: center; + transition: all 0.3s ease; + + &:hover { + border-color: #667eea; + transform: translateY(-4px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.2); + } + + .case-icon { + font-size: 2rem; + margin-bottom: $s-sm; + } + + h5 { + margin: 0 0 $s-xs 0; + color: #1e293b; + font-size: 1rem; + } + + p { + margin: 0; + font-size: 0.85rem; + color: #64748b; + line-height: 1.4; + } + } +} + +// Highlight box +.highlight-box { + display: flex; + gap: $s-md; + padding: $s-lg; + border-radius: $r-md; + margin: $s-lg 0; + align-items: flex-start; + + &.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(217, 119, 6, 0.15) 100%); + border: 2px solid #f59e0b; + } + + .highlight-icon { + font-size: 1.5rem; + flex-shrink: 0; + } + + .highlight-content { + flex: 1; + line-height: 1.6; + color: #78350f; + + strong { + display: block; + margin-bottom: $s-xs; + color: #92400e; + } + } +} + +// Role box +.role-box { + background: linear-gradient(135deg, #fefce8 0%, #fef3c7 100%); + border: 2px solid #fbbf24; + border-radius: $r-lg; + padding: $s-lg; + margin: $s-lg 0; + + .role-header { + display: flex; + align-items: center; + gap: $s-md; + margin-bottom: $s-lg; + padding-bottom: $s-md; + border-bottom: 2px solid #fde68a; + + .role-icon { + font-size: 1.5rem; + } + + h4 { + margin: 0; + color: #92400e; + font-size: 1.1rem; + } + } + + .tasks-list { + display: flex; + flex-direction: column; + gap: $s-sm; + + .task-item { + display: flex; + align-items: center; + gap: $s-md; + padding: $s-sm; + background: rgba(255,255,255,0.5); + border-radius: $r-sm; + transition: all 0.3s ease; + + &:hover { + background: rgba(255,255,255,0.8); + transform: translateX(4px); + } + + .task-bullet { + color: #10b981; + font-weight: 700; + font-size: 1.1rem; + flex-shrink: 0; + } + + span:last-child { + color: #78350f; + line-height: 1.4; + } + } + } +} + +// Platform card +.platform-card { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%); + border: 2px solid #667eea; + border-radius: $r-lg; + padding: $s-xl; + margin: $s-lg 0; + text-align: center; + + .platform-logo { + margin-bottom: $s-lg; + + .logo-text { + font-size: 2.5rem; + font-weight: 900; + background: $gradient-primary; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + .logo-domain { + font-size: 1.5rem; + color: #667eea; + font-weight: 600; + } + } + + p { + color: #475569; + margin-bottom: $s-lg; + line-height: 1.6; + } + + .platform-features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: $s-md; + + .feature { + display: flex; + flex-direction: column; + align-items: center; + gap: $s-xs; + padding: $s-md; + background: white; + border-radius: $r-md; + font-size: 0.85rem; + color: #64748b; + + .feature-icon { + font-size: 1.5rem; + } + } + } +} + +// Accounts grid +.accounts-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: $s-lg; + margin: $s-lg 0; + + .account-type { + padding: $s-xl; + border-radius: $r-lg; + text-align: center; + border: 3px solid; + + &.positive { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + border-color: #10b981; + } + + &.negative { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%); + border-color: #3b82f6; + } + + .account-icon { + font-size: 2.5rem; + margin-bottom: $s-md; + } + + h5 { + margin: 0 0 $s-sm 0; + color: #1e293b; + font-size: 1.1rem; + } + + p { + margin: 0; + color: #64748b; + font-size: 0.9rem; + line-height: 1.5; + } + } +} + +// Reference card +.reference-card { + background: linear-gradient(135deg, #ede9fe 0%, #ddd6fe 100%); + border: 2px solid #8b5cf6; + border-radius: $r-lg; + padding: $s-xl; + display: flex; + gap: $s-lg; + align-items: flex-start; + + .reference-icon { + font-size: 2rem; + flex-shrink: 0; + } + + .reference-content { + flex: 1; + + h4 { + margin: 0 0 $s-sm 0; + color: #6b21a8; + font-size: 1.2rem; + } + + p { + margin: 0 0 $s-md 0; + color: #7c3aed; + } + + .reference-link { + display: inline-flex; + align-items: center; + gap: $s-sm; + padding: $s-md $s-lg; + background: white; + color: #8b5cf6; + text-decoration: none; + border-radius: $r-md; + font-weight: 600; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(139, 92, 246, 0.2); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(139, 92, 246, 0.3); + } + + .link-arrow { + font-size: 1.2rem; + transition: transform 0.3s ease; + } + + &:hover .link-arrow { + transform: translateX(4px); + } + } + } +} + +// Footer +.reg-footer { + background: $gradient-primary; + color: white; + padding: $s-xl; + border-radius: $r-lg; + text-align: center; + margin-top: $s-xl; + box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3); + + .footer-icon { + font-size: 2rem; + margin-bottom: $s-md; + } + + p { + margin: 0; + font-size: 1.1rem; + font-weight: 600; + opacity: 0.95; + } +} + +// Responsive +@media (max-width: 600px) { + .regulation-content { + padding: $s-sm; + } + + .reg-header h1 { + font-size: 1.5rem; + } + + .reg-section { + padding: $s-lg; + + .section-number { + font-size: 2rem; + top: $s-sm; + right: $s-sm; + } + + .section-header .section-title { + font-size: 1.2rem; + } + } + + .timeline::before { + left: 15px; + } + + .timeline-item .timeline-dot { + width: 30px; + height: 30px; + font-size: 0.85rem; + } + + .cases-grid { + grid-template-columns: 1fr; + } + + .balance-visual { + flex-direction: column; + gap: $s-md !important; + } } \ No newline at end of file diff --git a/src/components/CMyCircuit/CMyCircuit.ts b/src/components/CMyCircuit/CMyCircuit.ts index e4bde04d..d271f271 100755 --- a/src/components/CMyCircuit/CMyCircuit.ts +++ b/src/components/CMyCircuit/CMyCircuit.ts @@ -72,7 +72,10 @@ export default defineComponent({ const circuit = ref(null); const account = computed(() => { if (groupnameSel.value) { - return userStore.getAccountGroupByCircuitId(circuit.value._id, groupnameSel.value); + return userStore.getAccountGroupByCircuitId( + circuit.value._id, + groupnameSel.value + ); } else { return circuit.value ? userStore.getAccountByCircuitId(circuit.value._id) : null; } @@ -164,133 +167,9 @@ export default defineComponent({ } // Trasforma il vecchio HTML in uno moderno - return transformRegulationHTML(name); + return circuitStore.transformRegulationHTML(name); } - function transformRegulationHTML(circuitName: string): string { - return ` -
-
-

${circuitName}

-
- -
-

Costituzione e scopo Comunitario

-

- La Comunità Territoriale "RIS ${circuitName}" spontanea (da ora "la Comunità") costituisce un circuito di scambio - tra i partecipanti, ciascuno dei quali dovrà indicare i beni e servizi che offre alla Comunità stessa in RIS. - Il circuito funziona come dettagliato qui di seguito. -

-
- -
-

Circuito di scambio

-

- La Comunità ha avviato un sistema di scambio di beni e servizi tra utenti, in cui il valore delle transazioni - si misura utilizzando una unità di conto denominata RIS, convenzionalmente considerata pari a 1 euro. -

-

- Gli scambi tra utenti avvengono liberamente, dopo aver concordato il valore tra le parti. -

-

- Tutti gli utenti partecipanti al circuito iniziano con un conto vuoto, cioè pari a 0 RIS. -

-

- L'utente pagante che ha un conto RIS vuoto o con quantità non sufficiente a perfezionare lo scambio può utilizzare - la Fiducia Concessa andando in debito fino al limite massimo a lui permesso, denominato "fiducia concessa". - Un equivalente valore in RIS a credito sarà conseguentemente iscritto sul conto del ricevente. -

-

- In ogni momento nel circuito la somma di tutte le esposizioni in positivo saranno perfettamente bilanciate dalla - somma di tutte le esposizioni in negativo, dimostrando così che ciascun detentore di un saldo positivo abbia - garantita la solvibilità del proprio credito. Ciascun detentore di un saldo negativo concorda che la sua - esposizione funge da garanzia della quantità di RIS equivalenti circolanti nel circuito e che può essere chiamato - a regolarizzare con equivalente pagamento in euro *, entro un lasso di tempo ragionevolmente adeguato, nel caso in cui: -

-
    -
  • la Comunità, in accordo con il Gruppo Tecnico, deliberi il rientro di alcune posizioni con decisione motivata;
  • -
  • l'utente decida di uscire per motivi personali;
  • -
  • il circuito chiuda, per decisione deliberata o per motivi di forza maggiore.
  • -
-
- * Nota importante: - La regolarizzazione può avvenire con equivalente valore in beni e servizi, oppure, in ultima istanza, - con pagamento in euro. -
-
- -
-

Il Gruppo Tecnico

-

- La Comunità costituisce il Gruppo Tecnico di Gestione degli Scambi (da ora "Gruppo Tecnico"), partecipante esso - stesso agli scambi tramite uno o più delegati autorizzati. Il Gruppo Tecnico è l'organo che supporta la comunità - nella gestione degli scambi e quant'altro sia necessario all'ottimale svolgimento dello scopo comunitario. -

-

In particolare ha il compito di:

-
    -
  • approvare le richieste di adesione, dopo aver verificato che il richiedente faccia parte della Comunità;
  • -
  • tenere aggiornato l'elenco dei partecipanti al circuito;
  • -
  • stabilire la soglia di massimo scoperto e massimo attivo, collettivo e individuale;
  • -
  • valutare la sostenibilità di eventuali stanziamenti di finanziamenti;
  • -
  • introdurre tassazioni sulle transazioni o una tantum;
  • -
  • adottare o meno il Deperimento dei conti inoperosi (riferimento sistema Si.Cre.Na.C.C.) e modularne i parametri di applicazione;
  • -
  • deliberare la chiusura del circuito;
  • -
  • deliberare la destinazione delle riserve comunitarie depositate sui Conti della Comunità per: finanziare opere varie; - corrispondere salari o agire con interventi di sussistenza a partecipanti in stato di necessità; rilevare lo stato - debitorio all'interno del circuito di utenti in difficoltà economica, irreperibili o venuti a mancare; attuare - interventi ritenuti utili, necessari o meritevoli;
  • -
  • redigere, emendare ed adeguare alle eventuali necessità il presente Regolamento.
  • -
-

- Il Gruppo Tecnico è formato da candidati scelti dalla Comunità sulla base delle competenze e dovrà sottoporre - all'approvazione della Comunità stessa resoconti periodici delle sue attività. -

-
- -
-

Sistemi di contabilizzazione

-

- I conti RIS e le transazioni conseguenti agli scambi sono registrati attraverso la piattaforma riso.app, - dove i partecipanti comunicano i beni e servizi proposti alla Comunità. Tutti i partecipanti possono visualizzare - in ogni momento la situazione del circuito. -

-

- La Comunità, in accordo con il Gruppo Tecnico, può decidere di utilizzare altri strumenti di contabilizzazione, - che dovranno, in ogni caso, permettere l'inserimento delle singole transazioni effettuate tra gli utenti e la - visualizzazione da parte di ogni partecipante. Saranno dunque inseriti nel nuovo strumento tutti gli utenti - partecipanti con l'ultimo saldo rilevato al momento della transizione. -

-
- -
-

Conto Comunitario

-

- La Comunità può decidere di aprire uno o più Conti all'interno del circuito, che saranno gestiti dal Gruppo Tecnico, - allo scopo di interagire con i conti degli altri utenti e quindi recepire eventuali proventi da tassazioni o - finanziare iniziative autorizzate. I Conti Comunitari sono i conti della Comunità. -

-

- Un conto a credito rappresenta una riserva monetaria da re-immettere nel circuito per finanziare opere o attività - di beneficio comune; un conto a debito rappresenta il "debito pubblico" della Comunità. -

-

- I conti comunitari sono sottoposti alle stesse regole degli altri conti (conto vuoto, possibilità di scoperto di conto, ecc.). - Ogni operazione straordinaria viene deliberata appositamente, mentre l'ordinaria amministrazione può essere autorizzata - in via continuativa. -

-
- -
-

Riferimenti

-

- Per una migliore comprensione dei meccanismi che sottostanno allo scambio nel qui presentato circuito, si rimanda - alla lettura del testo disponibile gratuitamente su https://sicrenacc.info, da cui è stata tratta - libera ispirazione e condivisione dei principi generali del Sistema di Credito Naturale. -

-
-
- `; - } onMounted(mounted); return { diff --git a/src/components/CMyEditElem/CMyEditElem.scss b/src/components/CMyEditElem/CMyEditElem.scss index 869fb96b..6644febd 100755 --- a/src/components/CMyEditElem/CMyEditElem.scss +++ b/src/components/CMyEditElem/CMyEditElem.scss @@ -459,4 +459,10 @@ h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.scroll-input { + :deep(textarea) { + max-height: 400px; + overflow-y: auto; + } } \ No newline at end of file diff --git a/src/components/CMyEditElem/CMyEditElem.vue b/src/components/CMyEditElem/CMyEditElem.vue index 4b89e1bc..3c0cbc75 100755 --- a/src/components/CMyEditElem/CMyEditElem.vue +++ b/src/components/CMyEditElem/CMyEditElem.vue @@ -931,7 +931,6 @@ @update:model-value="modifElem" > - + + + + + +
diff --git a/src/components/CMyEditor/CMyEditor.scss b/src/components/CMyEditor/CMyEditor.scss index b892863a..880c601a 100755 --- a/src/components/CMyEditor/CMyEditor.scss +++ b/src/components/CMyEditor/CMyEditor.scss @@ -2,3 +2,36 @@ display: flex; flex: 1; } + + +// Wrapper per stili custom dell'editor +.cmyeditor.custom-styled { + // Gli stili verranno iniettati dinamicamente + + :deep(.q-editor__content) { + // Override di default se necessario + transition: all 0.3s ease; + } +} + +// Stili specifici per contenuto custom +[data-custom-editor] { + :deep(.q-editor__content) { + // Base styles che possono essere override + line-height: 1.6; + } +} + +.cmyeditor-wrapper.has-custom-styles { + :deep(.q-editor__content) { + // Force override di tutti gli stili di default + all: unset; + display: block; + width: 100%; + min-height: 10rem; + padding: 12px; + outline: none; + white-space: pre-wrap; + word-wrap: break-word; + } +} \ No newline at end of file diff --git a/src/components/CMyEditor/CMyEditor.ts b/src/components/CMyEditor/CMyEditor.ts index 3f252c63..8fa35cfd 100755 --- a/src/components/CMyEditor/CMyEditor.ts +++ b/src/components/CMyEditor/CMyEditor.ts @@ -1,7 +1,7 @@ import { tools } from '@tools'; import { CTitleBanner } from '../CTitleBanner'; -import { defineComponent, onMounted, ref, toRef, watch } from 'vue'; +import { defineComponent, onMounted, onUnmounted, ref, toRef, watch } from 'vue'; import { useQuasar } from 'quasar'; import { useI18n } from 'vue-i18n'; @@ -24,6 +24,11 @@ export default defineComponent({ required: false, default: '', }, + customStyles: { + type: String, + required: false, + default: '', + }, showButtons: { type: Boolean, required: false, @@ -62,6 +67,21 @@ export default defineComponent({ const myvalue = ref(''); const mycolor = ref(''); + const editorId = ref( + `editor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` + ); + const styleElement = ref(null); + + // Watch per applicare stili personalizzati + watch( + () => props.customStyles, + (newStyles) => { + if (newStyles && editorRef.value) { + applyCustomStyles(newStyles); + } + } + ); + const myfonts = ref({ arial: 'Arial', arial_black: 'Arial Black', @@ -127,6 +147,45 @@ export default defineComponent({ } ); + // Funzione per applicare stili custom + // ALTERNATIVA: Se vuoi applicare gli stili solo al contenuto dell'editor (più sicuro) + + function applyCustomStyles(styles: string) { + // console.log('Applying custom styles:', styles); + + // Rimuovi style precedente + if (styleElement.value && styleElement.value.parentNode) { + styleElement.value.parentNode.removeChild(styleElement.value); + } + + if (!styles) return; + + // Crea style element nel HEAD + styleElement.value = document.createElement('style'); + styleElement.value.setAttribute('type', 'text/css'); + styleElement.value.setAttribute('data-editor-styles', editorId.value); + + // CSS con selettori super specifici usando l'ID univoco + const css = ` + [data-editor-id="${editorId.value}"] .q-editor__content { + ${styles} + } + + [data-editor-id="${editorId.value}"] .q-editor__content * { + ${styles} + } + + /* Selettori per classi custom nel contenuto */ + [data-editor-id="${editorId.value}"] .q-editor__content .prova1 { + color: red !important; + } + `; + + styleElement.value.textContent = css; + document.head.appendChild(styleElement.value); + + // console.log('Style injected:', css); + } function getTextLength(html: string) { // Crea un elemento temporaneo per convertire HTML in testo const div = document.createElement('div'); @@ -197,6 +256,12 @@ export default defineComponent({ characterCount.value = getTextLength(myvalue.value); + if (props.customStyles) { + setTimeout(() => { + applyCustomStyles(props.customStyles); + }, 300); + } + if (props.startInCodeMode) { // Attiva modalità codice di default setTimeout(() => { @@ -231,6 +296,13 @@ export default defineComponent({ onMounted(mounted); + // Cleanup quando componente viene distrutto + onUnmounted(() => { + if (styleElement.value && styleElement.value.parentNode) { + styleElement.value.parentNode.removeChild(styleElement.value); + } + }); + return { myfonts, toolbarcomp, @@ -248,6 +320,8 @@ export default defineComponent({ showtools, characterCount, t, + applyCustomStyles, + editorId, }; }, }); diff --git a/src/components/CMyEditor/CMyEditor.vue b/src/components/CMyEditor/CMyEditor.vue index 9a7efdb2..c14e3e5c 100755 --- a/src/components/CMyEditor/CMyEditor.vue +++ b/src/components/CMyEditor/CMyEditor.vue @@ -15,9 +15,19 @@ v-close-popup > - - -
+ + +
- - + + - + - - -
Caratteri: {{ characterCount }} / {{ maxlength }}
+ +
+
+ Caratteri: {{ characterCount }} / {{ maxlength }} +
- + - + diff --git a/src/components/CStatusReg/CStatusReg_Modern.scss b/src/components/CStatusReg/CStatusReg_Modern.scss new file mode 100644 index 00000000..b2b3e0d7 --- /dev/null +++ b/src/components/CStatusReg/CStatusReg_Modern.scss @@ -0,0 +1,243 @@ +.status-reg-modern { + padding: 4px; + + @media (min-width: 1024px) { + padding: 8px; + } +} + +.title-banner { + padding: 4px; + + @media (min-width: 1024px) { + padding: 8px; + } +} + +// Stats wrapper +.stats-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + padding: 8px; + + @media (min-width: 1024px) { + gap: 12px; + padding: 12px; + } + + .stat-card { + min-width: 140px; + + @media (min-width: 768px) { + min-width: 160px; + } + } +} + +// Expansion list +.expansion-list { + background: transparent; + border: none; + margin: 8px 0; + + @media (min-width: 1024px) { + margin: 12px 0; + } + + .expansion-header { + padding: 8px 12px; + font-weight: 600; + + @media (min-width: 1024px) { + padding: 10px 16px; + } + } +} + +// Expansion card +.expansion-card { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + + .card-content { + padding: 8px; + + @media (min-width: 1024px) { + padding: 12px; + } + } +} + +// Info section +.info-section { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); + border-radius: 8px; + padding: 12px; + margin-bottom: 8px; + text-align: center; + + @media (min-width: 1024px) { + padding: 16px; + margin-bottom: 12px; + } + + .info-title { + font-weight: 700; + font-size: 16px; + color: #333; + margin-bottom: 4px; + } + + .info-text { + font-size: 13px; + color: #666; + line-height: 1.4; + + @media (min-width: 1024px) { + font-size: 14px; + } + } +} + +// User items +.user-item { + background: rgba(255, 255, 255, 0.8); + border-radius: 8px; + margin-bottom: 6px; + padding: 8px; + transition: all 0.2s ease; + + @media (min-width: 1024px) { + margin-bottom: 8px; + padding: 10px; + } + + &:hover { + background: rgba(102, 126, 234, 0.15); + transform: translateX(4px); + } + + &:last-child { + margin-bottom: 0; + } + + .user-name { + font-weight: 600; + font-size: 14px; + color: #333; + + @media (min-width: 1024px) { + font-size: 15px; + } + } + + .username-caption { + font-size: 12px; + color: #888; + + @media (min-width: 1024px) { + font-size: 13px; + } + } + + .date-label { + color: #888; + font-size: 12px; + + @media (min-width: 1024px) { + font-size: 13px; + } + } + + .access-time { + font-size: 11px; + color: #999; + font-style: italic; + + @media (min-width: 1024px) { + font-size: 12px; + } + } +} + +// Ranking items +.ranking-item { + .index_diffusore { + font-size: 14px; + font-weight: 700; + color: #667eea; + + @media (min-width: 1024px) { + font-size: 16px; + } + } + + .ranking-count { + font-size: 20px; + font-weight: 700; + color: #333; + + @media (min-width: 1024px) { + font-size: 24px; + } + } +} + +// Handshake items +.handshake-item { + display: flex; + align-items: center; + + .clDateStrette { + color: #888; + font-style: italic; + font-size: 11px; + margin-bottom: 4px; + + @media (min-width: 1024px) { + font-size: 12px; + } + } + + .handshake-friend { + text-align: right; + font-size: 13px; + color: #666; + + @media (min-width: 1024px) { + font-size: 14px; + } + } +} + +// Original styles preserved +.iscritto_da { + font-style: italic; + font-size: 11px; + color: #aaa; + + @media (min-width: 1024px) { + font-size: 12px; + } +} + +.iscritto_da_name { + color: #667eea !important; + font-weight: 600; +} + +// Chart wrapper +.chart-wrapper { + margin-top: 12px; + padding: 8px; + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(10px); + border-radius: 12px; + + @media (min-width: 1024px) { + margin-top: 16px; + padding: 12px; + } +} \ No newline at end of file diff --git a/src/components/MyFooter/MyFooter.scss b/src/components/MyFooter/MyFooter.scss index 7672bf8b..6475098e 100755 --- a/src/components/MyFooter/MyFooter.scss +++ b/src/components/MyFooter/MyFooter.scss @@ -42,7 +42,7 @@ } .img_in_tab{ - width: 52px; - height: 52px; + width: 32px; + height: 32px; margin: 4px !important; } \ No newline at end of file diff --git a/src/components/MyFooter/MyFooter.vue b/src/components/MyFooter/MyFooter.vue index 0c703575..1cfe0fa4 100755 --- a/src/components/MyFooter/MyFooter.vue +++ b/src/components/MyFooter/MyFooter.vue @@ -1,45 +1,87 @@ - + diff --git a/src/pages/provaris2.vue b/src/pages/provaris2.vue new file mode 100644 index 00000000..2891a341 --- /dev/null +++ b/src/pages/provaris2.vue @@ -0,0 +1,1118 @@ + + + + + diff --git a/src/pages/provaris3.vue b/src/pages/provaris3.vue new file mode 100644 index 00000000..7178c7bd --- /dev/null +++ b/src/pages/provaris3.vue @@ -0,0 +1,676 @@ + + + + + diff --git a/src/root/nutriben/home/home.scss b/src/root/nutriben/home/home.scss index ecf8d7f8..7b3c15ea 100755 --- a/src/root/nutriben/home/home.scss +++ b/src/root/nutriben/home/home.scss @@ -5,6 +5,8 @@ $text-color: #111827; $gray-dark: #4b5563; $bg-light: #f9fafb; +@use 'sass:color'; + @mixin center-flex { display: flex; justify-content: center; @@ -64,7 +66,7 @@ section.relative.overflow-hidden { color: white; &:hover { - background: darken($primary-color, 10%); + background: color.adjust($primary-color, $lightness: -10%); } } diff --git a/src/router/routesAdmin.ts b/src/router/routesAdmin.ts index 4753bdce..298a3c45 100644 --- a/src/router/routesAdmin.ts +++ b/src/router/routesAdmin.ts @@ -561,6 +561,39 @@ function getRoutesAd(site: ISites) { inmenu: false, infooter: false, }, + { + active: true, + order: 400, + path: '/provaris1', + materialIcon: 'fas fa-test', + name: 'mypages.provaris1', + component: () => import('@src/pages/provaris1.vue'), + meta: { }, + inmenu: false, + infooter: false, + }, + { + active: true, + order: 400, + path: '/provaris2', + materialIcon: 'fas fa-test', + name: 'mypages.provaris2', + component: () => import('@src/pages/provaris2.vue'), + meta: { }, + inmenu: false, + infooter: false, + }, + { + active: true, + order: 400, + path: '/provaris3', + materialIcon: 'fas fa-test', + name: 'mypages.provaris3', + component: () => import('@src/pages/provaris3.vue'), + meta: { }, + inmenu: false, + infooter: false, + }, { active: true, order: 2000, diff --git a/src/services/SyncService.js b/src/services/SyncService.js new file mode 100644 index 00000000..53a17489 --- /dev/null +++ b/src/services/SyncService.js @@ -0,0 +1,64 @@ +// src/services/SyncService.js +import syncIDB from './idb'; // Usa il wrapper sopra +import Api from './Api'; + +class SyncService { + constructor() { + this.syncTables = ['resps', 'workers', 'groups', 'mygroups', 'products', 'cart', 'orderscart']; + } + + async syncAll(idapp, tablesToSync, forceRefresh = false) { + const syncRequest = {}; + + for (const table of tablesToSync) { + const lastSync = forceRefresh ? 0 : await syncIDB.getLastSync(table); + syncRequest[table] = { lastSync }; + } + + console.log('🔄 Sync request:', syncRequest); + + try { + const response = await Api.SendReq( + `/sync/${idapp}`, + 'POST', + { tables: syncRequest } + ); + + if (response.status === 200 && response.data.success) { + await this.processServerData(response.data.data); + return { success: true }; + } + + } catch (error) { + console.error('Sync failed:', error); + return { success: false, error }; + } + } + + async processServerData(data) { + for (const [tableName, result] of Object.entries(data)) { + if (result.error) { + console.error(`❌ ${tableName}:`, result.error); + continue; + } + + const { data: records, timestamp, fullSync } = result; + + if (fullSync) { + await syncIDB.replaceTable(tableName, records); + } else { + await syncIDB.mergeRecords(tableName, records); + } + + await syncIDB.setLastSync(tableName, timestamp); + + console.log(`✓ ${tableName}: ${records.length} records (${fullSync ? 'full' : 'incremental'})`); + } + } + + async loadFromCache(tableName) { + return syncIDB.getTable(tableName); + } +} + +export default new SyncService(); diff --git a/src/services/idb.js b/src/services/idb.js new file mode 100644 index 00000000..47f545ac --- /dev/null +++ b/src/services/idb.js @@ -0,0 +1,68 @@ +// src/services/idb.js (nuovo, usa storage.js esistente) +import { idbKeyval } from '@/storage'; + +class SyncIDB { + constructor() { + this.tables = ['resps', 'workers', 'groups', 'mygroups', 'products', 'cart', 'orderscart']; + } + + // Sostituisci intera tabella + async replaceTable(tableName, records) { + await idbKeyval.clearalldata(tableName); + + for (const record of records) { + await idbKeyval.setdata(tableName, record); + } + } + + // Merge records modificati + async mergeRecords(tableName, records) { + for (const record of records) { + if (record.deleted === true) { + // Trova BOMID e cancella + const existing = await this.findByMongoId(tableName, record._id); + if (existing) { + await idbKeyval.deletedata(tableName, existing.BOMID); + } + } else { + // Controlla se esiste già + const existing = await this.findByMongoId(tableName, record._id); + + if (existing) { + // Aggiorna (mantieni BOMID) + record.BOMID = existing.BOMID; + await idbKeyval.setdata(tableName, record); + } else { + // Inserisci nuovo + await idbKeyval.setdata(tableName, record); + } + } + } + } + + // Trova record per _id MongoDB + async findByMongoId(tableName, mongoId) { + const allRecords = await idbKeyval.getalldata(tableName); + return allRecords.find(r => r._id === mongoId); + } + + // Ottieni tutti i record + async getTable(tableName) { + return await idbKeyval.getalldata(tableName); + } + + // Timestamp ultima sync + async getLastSync(tableName) { + const meta = await idbKeyval.getdata('metadata', `${tableName}_lastSync`); + return meta?.value || 0; + } + + async setLastSync(tableName, timestamp) { + await idbKeyval.setdata('metadata', { + BOMID: `${tableName}_lastSync`, + value: timestamp + }); + } +} + +export default new SyncIDB(); diff --git a/src/statics/lang/it.js b/src/statics/lang/it.js index 203f9b7d..a9e75166 100755 --- a/src/statics/lang/it.js +++ b/src/statics/lang/it.js @@ -1,5 +1,7 @@ const msg_it = { it: { + index: 'Indice', + years_of_exp: 'Anni Esperienza', words: { da: 'dal', a: 'al', @@ -490,50 +492,9 @@ const msg_it = { Commerciale: 'Commerciale', zoomeri: 'Zoomeri', grafico: 'Grafico', - /* sonomediatore: 'Quando diventi Meditore vieni contattato da un FACILITATORE, con lui devi:
    ' + - '
  1. Aprire la tua Gift Chat (tu come proprietario e il Facilitatore ' + - 'come amministratore) con questo nome:
    {nomenave}
  2. ' + - '
  3. Clicca sul nome della chat in alto -> Modifica -> Amministratori -> "Aggiungi Amministratore", seleziona il Facilitatore nell’elenco.
  4. ' + - '
  5. Devi configurare la chat in modo che chi entra vede anche i post precedenti (clicca sul nome della chat in alto, clicca su modifica, ' + - 'cambia la "cronologia per i nuovi membri" da nascosta a visibile.
  6. ' + - '
  7. Per trovare il link della Chat appena creata: clicca sul nome della chat in alto, clicca sulla Matita -> "Tipo di Gruppo" -> "invita nel gruppo tramite link", clicca su "copia link" e incollalo qui sotto, sulla casella "Link Gift Chat"
  8. ' + - '
  9. Invia il Link della Gift Chat a tutti i Donatori, cliccando sul bottone qui sotto.
', -*/ - sonomediatore: 'Quando sei MEDIATORE verrai contattato dai FACILITATORE AYNI tramite un messaggio sulla Chat AYNI BOT !', - superchat: 'Nota Bene: Non inviarci la ricevuta, non ci occorre. Attendi il messaggio di conferma da parte del Sognatore (sulla Chat AYNI BOT).
SOLO se hai problemi di PAGAMENTO, o ti manca la conferma del SOGNATORE (dopo aver atteso almeno 12 ore) o se vuoi essere SOSTITUITO, due Facilitatore ti aspettano per aiutarti sulla Chat:
Entra nella Gift Chat', - sonodonatore: '
  1. Quando sei in questa posizione, verrai invitato (tramite un messaggio su AYNI BOT) ad effettuare il Dono. Non sarà più necessario entrare in una Chat.
  2. ' - + '
  3. Avrai tempo 3 giorni per fare il Regalo (poi verrai sostituito), nella modalità di pagamento che troverai scritto sul messaggio in AYNI BOT .
', - sonodonatore_seconda_tessitura: '
  1. Qui tu sei Mediatore e anche Donatore, ma essendo la seconda Tessitura (il Ritorno), non avrai bisogno di effettuare nuovamente il dono
', - controlla_donatori: 'Controlla Lista Donatori', - link_chat: 'Link della Gift Chat Telegram', link_gruppo_telegram: 'Gruppo Telegram "RISO {prov}"', - tragitto: 'Tragitto', - nave: 'Nave', - data_partenza: 'Data
Partenza', - doni_inviati: 'Doni', - nome_dei_passaggi: 'Nome
dei Passaggi', - donatori: 'Donatori', - donatore: 'Donatore', - mediatore: 'Mediatore', - sognatore: 'Sognatore', - sognatori: 'SOGNATORI', - intermedio: 'INTERMEDIO', - pos2: 'Interm. 2', - pos3: 'Interm. 3', - pos5: 'Interm. 5', - pos6: 'Interm. 6', - gift_chat: 'Per entrare nella Gift Chat, clicca qui', - quando_eff_il_tuo_dono: 'Quando effettuare il Regalo', - entra_in_gift_chat: 'Entra in Gift Chat', - invia_link_chat: 'Invia il Link della Gift Chat ai Donatori', - inviare_msg_donatori: '5) Inviare messaggio ai Donatori', - msg_donatori_ok: 'Inviato messaggio ai Donatori', metodi_disponibili: 'Metodi Disponibili', importo: 'Importo', - effettua_il_dono: 'E\' arrivato il momento di Effettuare il proprio Dono al Sognatore
👉 {sognatore} 👈 !

' - + 'Inviare tramite PayPal a: {email}
' - + 'Aggiungere come messaggio la dicitura: Regalo
' - + 'ATTENZIONE IMPORTANTE: Scegliere l\'opzione
"INVIO DI DENARO A UN AMICO"
Cosi non pagherai delle commissioni extra!', paypal_me: '
2) Metodo Semplificato
Cliccare direttamente qui
' + 'si aprirà PayPal con l\'importo e il destinatario gia impostato.
' + 'Aggiungere come messaggio la dicitura: Regalo
' @@ -544,24 +505,8 @@ const msg_it = { qui_compariranno_le_info: 'Nel giorno della partenza della Nave, compariranno le informazioni del Sognatore', posizione: 'Posizione', come_inviare_regalo_con_paypal: 'Come Inviare il regalo tramite Paypal', - ho_effettuato_il_dono: 'Ho effettuato il Dono', - clicca_conferma_dono: 'Una volta inviato il Dono, lascia un commento al Sognatore e Clicca qui sotto per confermare che hai effettuato il tuo dono', - fatto_dono: 'Hai confermato che il dono è stato Inviato', - confermi_dono: 'Confermi che hai inviato il tuo Dono di 33€', - dono_ricevuto: 'Il tuo Dono è stato Ricevuto!', - dono_ricevuto_2: 'Ricevuto', - dono_ricevuto_3: 'Arrivato!', - confermi_dono_ricevuto: 'Confermi di aver ricevuto il Dono di 33€ da parte di {donatore}', - confermi_dono_ricevuto_msg: 'Confermato di aver ricevuto il Dono di 33€ da parte di {donatore}', - msg_bot_conferma: '{donatore} ha confermato di aver inviato il suo Dono di 33€ a {sognatore} (Commento: {commento})', - ricevuto_dono_ok: 'Hai confermato che il dono è stato Ricevuto', entra_in_lavagna: 'Entra sulla Tua Lavagna per vedere le Navi in Partenza', - doni_ricevuti: 'Doni Ricevuti', - doni_inviati_da_confermare: 'Doni Inviati (da confermare)', - doni_mancanti: 'Doni Mancanti', temporanea: 'Temporanea', - nave_provvisoria: 'Ti è stata assegnata una Nave TEMPORANEA.
E\'normale che vedrai variare la data di partenza, dovuto all\'aggiornamento della graduatoria dei passeggeri.', - ritessitura: 'RITESSITURA', }, reg: { title_abilita_circuito: 'Abilitazione Circuito', @@ -1280,6 +1225,9 @@ const msg_it = { date_updated: 'Ult. Aggiornamento', }, mypages: { + provaris1: 'Prova Ris 1', + provaris2: 'Prova Ris 2', + provaris3: 'Prova Ris 3', gestoreordinigas: 'Gestore Ordini GAS', testweb: 'Test Web', totaliordini: 'Totali Ordini GAS', @@ -1652,7 +1600,6 @@ const msg_it = { inviati: 'Inviati', ricevuti: 'Ricevuti', enablefido: 'Abilita la Fiducia', - regolamento: '
{nomecircuito}
Costituzione e scopo Comunitario
La Comunità Territoriale "RIS {nomecircuito}" spontanea (da ora “la Comunità”) costituisce un circuito di scambio tra i partecipanti, ciascuno dei quali dovrà indicare i beni e servizi che offre alla Comunità stessa in RIS. Il circuito funziona come dettagliato qui di seguito. 
Circuito di scambio
La Comunità ha avviato un sistema di scambio di beni e servizi tra utenti, in cui il valore delle transazioni si misura utilizzando una unità di conto denominata RIS, convenzionalmente considerata pari a 1 euro.
Gli scambi tra utenti avvengono liberamente, dopo aver concordato il valore tra le parti.
Tutti gli utenti partecipanti al circuito iniziano con un conto vuoto, cioè pari a 0 RIS.
L\’utente pagante che ha un conto RIS vuoto o con quantità non sufficiente a perfezionare lo scambio può utilizzare la Fiducia Concessa andando in debito fino al limite massimo a lui permesso, denominato "fiducia concessa". Un equivalente valore in RIS a credito sarà conseguentemente iscritto sul conto del ricevente.
In ogni momento nel circuito la somma di tutte le esposizioni in positivo saranno perfettamente bilanciate dalla somma di tutte le esposizioni in negativo, dimostrando così che ciascun detentore di un saldo positivo abbia garantita la solvibilità del proprio credito. Ciascun detentore di un saldo negativo concorda che la sua esposizione funge da garanzia della quantità di Ris equivalenti circolanti nel circuito e che può essere chiamato a regolarizzare con equivalente pagamento in euro *, entro un lasso di tempo ragionevolmente adeguato, nel caso in cui: la Comunità, in accordo con il Gruppo Tecnico, deliberi il rientro di alcune posizioni con decisione motivata; l’utente decida di uscire per motivi personali; il circuito chiuda, per decisione deliberata o per motivi di forza maggiore.

* La regolarizzazione può avvenire con equivalente valore in beni e servizi, oppure, in ultima istanza, con pagamento in euro.
Il Gruppo Tecnico
La Comunità costituisce il Gruppo Tecnico di Gestione degli Scambi (da ora “Gruppo Tecnico”), partecipante esso stesso agli scambi tramite uno o più delegati autorizzati. Il Gruppo Tecnico è l\’organo che supporta la comunità nella gestione degli scambi e quant\’altro sia necessario all\’ottimale svolgimento dello scopo comunitario. In particolare ha il compito di:
    • approvare le richieste di adesione, dopo aver verificato che il richiedente faccia parte della Comunità;
    • tenere aggiornato l\’elenco dei partecipanti al circuito;
    • stabilire la soglia di massimo scoperto e massimo attivo, collettivo e individuale;
    • valutare la sostenibilità di eventuali stanziamenti di finanziamenti;
    • introdurre tassazioni sulle transazioni o una tantum;
    • adottare o meno il Deperimento dei conti inoperosi (riferimento sistema Si.Cre.Na.C.C.) e modularne i parametri di applicazione;
    • deliberare la chiusura del circuito;
    • deliberare la destinazione delle riserve comunitarie depositate sui Conti della Comunità (vedi più avanti) per: finanziare opere varie; corrispondere salari o agire con interventi di sussistenza a partecipanti in stato di necessità; rilevare lo stato debitorio all\’interno del circuito di utenti in difficoltà economica, irreperibili o venuti a mancare; attuare interventi ritenuti utili, necessari o meritevoli;
    • redigere, emendare ed adeguare alle eventuali necessità il presente Regolamento.
Il Gruppo Tecnico è formato da candidati scelti dalla Comunità sulla base delle competenze e dovrà sottoporre all\’approvazione della Comunità stessa resoconti periodici delle sue attività. 
Sistemi di contabilizzazione
I conti RIS e le transazioni conseguenti agli scambi sono registrati attraverso la piattaforma riso.app, dove i partecipanti comunicano i beni e servizi proposti alla Comunità. Tutti i partecipanti possono visualizzare in ogni momento la situazione del circuito. 
La Comunità, in accordo con il Gruppo Tecnico, può decidere di utilizzare altri strumenti di contabilizzazione, che dovranno, in ogni caso, permettere l\’inserimento delle singole transazioni effettuate tra gli utenti e la visualizzazione da parte di ogni partecipante. Saranno dunque inseriti nel nuovo strumento tutti gli utenti partecipanti con l\’ultimo saldo rilevato al momento della transizione. 
Conto Comunitario
La Comunità può decidere di aprire uno o più Conti all\’interno del circuito, che saranno gestiti dal Gruppo Tecnico, allo scopo di interagire con i conti degli altri utenti e quindi recepire eventuali proventi da tassazioni o finanziare iniziative autorizzate. I Conti Comunitari sono i conti della Comunità. Un conto a credito rappresenta una riserva monetaria da re-immettere nel circuito per finanziare opere o attività di beneficio comune; un conto a debito rappresenta il “debito pubblico” della Comunità.
I conti comunitari sono sottoposti alle stesse regole degli altri conti (conto vuoto, possibilità di scoperto di conto, ecc.). Ogni operazione straordinaria viene deliberata appositamente, mentre l\’ordinaria amministrazione può essere autorizzata in via continuativa.
Riferimenti
Per una migliore comprensione dei meccanismi che sottostanno allo scambio nel qui presentato circuito, si rimanda alla lettura del testo disponibile gratuitamente su https://sicrenacc.info, da cui è stata tratta libera ispirazione e condivisione dei principi generali del Sistema di Credito Naturale.

', richieste_title: 'Qui compaiono le richieste in sospeso per abilitare la Fiducia ai seguenti membri:', per_uscire_dal_circuito_occorre_essere_a_zero: 'Prima di uscire da un Circuito, occorre avere saldo superiore o uguale a zero (0 {symbol}): se sei in negativo offri qualcosa in {symbol}, se sei in positivo accetta qualcosa in {symbol} per consumarli!', compila_il_tuo_link_per_ricevere_ris: 'Crea il tuo link da condividere con gli altri per farti inviare i RIS.
Opzionale: Se vuoi puoi inserire una descrizione oppure pre-impostare quanti RIS richiedere.', @@ -1728,7 +1675,7 @@ const msg_it = { friends: 'Iscritti', bookings: 'Prenotaz.', profile: 'Profilo', - circuits: 'Circuiti', + circuits: 'RIS', showViewCart: 'Carrello', showViewOrders: 'Ordini', }, @@ -1807,7 +1754,7 @@ const msg_it = { enableRegMultiChoice: 'Registratione a Scelta tra Email e Telegram', enableTokenExpired: 'Attiva Scadenza Token', enableDebugOn: 'Attiva Debug', - enabledRegNeedTelegram: 'Reg con Telegram', + enabledRegNeedTelegram: 'Registrati con Telegram', showViewGroups: 'Bott. Organizzazioni', showViewEventi: 'Bott. Eventi', showViewUsers: 'Bott. Users', diff --git a/src/store/CircuitStore.ts b/src/store/CircuitStore.ts index 47925873..38cdc917 100755 --- a/src/store/CircuitStore.ts +++ b/src/store/CircuitStore.ts @@ -1,30 +1,32 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; import type { IAccount, - ICircuit, ICircuitState, IGroupShort, IMyCircuit, IMyGroup, IUserFields, - IUserProfile + ICircuit, + ICircuitState, + IGroupShort, + IMyCircuit, + IMyGroup, + IUserFields, + IUserProfile, } from '@src/model'; -import { - IGlobalState -} from '@src/model' -import { tools } from '@tools' -import translate from '@src/globalroutines/util' +import { IGlobalState } from '@src/model'; +import { tools } from '@tools'; +import translate from '@src/globalroutines/util'; -import * as Types from '@src/store/Api/ApiTypes' -import { useGlobalStore } from '@store/globalStore' -import { serv_constants } from '@store/Modules/serv_constants' -import { Api } from '@api' -import { toolsext } from '@store/Modules/toolsext' -import { static_data } from '@src/db/static_data' -import { useUserStore } from '@store/UserStore' -import { useNotifStore } from '@store/NotifStore' +import * as Types from '@src/store/Api/ApiTypes'; +import { useGlobalStore } from '@store/globalStore'; +import { serv_constants } from '@store/Modules/serv_constants'; +import { Api } from '@api'; +import { toolsext } from '@store/Modules/toolsext'; +import { static_data } from '@src/db/static_data'; +import { useUserStore } from '@store/UserStore'; +import { useNotifStore } from '@store/NotifStore'; +import { shared_consts } from '@src/common/shared_vuejs'; +import { costanti } from '@costanti'; -import { shared_consts } from '@src/common/shared_vuejs' -import { costanti } from '@costanti' - -import globalroutines from '../globalroutines/index' +import globalroutines from '../globalroutines/index'; export const useCircuitStore = defineStore('CircuitStore', { state: (): ICircuitState => ({ @@ -36,161 +38,195 @@ export const useCircuitStore = defineStore('CircuitStore', { actions: { getRemainingCoinsToSend(account: IAccount) { - if (account) - return tools.roundDec2(account.saldo + account.fidoConcesso) - else - return 0 + if (account) return tools.roundDec2(account.saldo + account.fidoConcesso); + else return 0; }, getMaxCoinsToSend(account: IAccount) { - if (account) - return tools.roundDec2(account.qta_maxConcessa - account.saldo) - else - return 0 + if (account) return tools.roundDec2(account.qta_maxConcessa - account.saldo); + else return 0; }, getSaldoByCircuitId(circuitId: string): number { - const userStore = useUserStore() - const account = userStore.my.profile.useraccounts.find((rec: IAccount) => rec.circuitId === circuitId) - if (account) - return account.saldo - else - return 0 + const userStore = useUserStore(); + const account = userStore.my.profile.useraccounts.find( + (rec: IAccount) => rec.circuitId === circuitId + ); + if (account) return account.saldo; + else return 0; }, sonoDentroAlCircuitoNazionale() { - const userStore = useUserStore() + const userStore = useUserStore(); - const circNazionali: any = this.listcircuits.filter((circ: any) => circ.isCircItalia) + const circNazionali: any = this.listcircuits.filter( + (circ: any) => circ.isCircItalia + ); for (const circ of circNazionali) { - const trovato = userStore.my.profile.mycircuits.findIndex((mycirc: any) => mycirc.circuitname === circ.name) >= 0 + const trovato = + userStore.my.profile.mycircuits.findIndex( + (mycirc: any) => mycirc.circuitname === circ.name + ) >= 0; if (trovato) { - return true + return true; } } - return false - + return false; }, getCircuitoNazionale(arrCircuiti: any): any { - - const circNazionali: any = this.listcircuits.filter((circ: any) => circ.isCircItalia) + const circNazionali: any = this.listcircuits.filter( + (circ: any) => circ.isCircItalia + ); for (const circ of circNazionali) { - const reccirc = arrCircuiti.find((mycirc: any) => mycirc.circuitname === circ.name) + const reccirc = arrCircuiti.find( + (mycirc: any) => mycirc.circuitname === circ.name + ); if (reccirc) { - return reccirc + return reccirc; } } - return null - + return null; }, EDentroAlCircuitoItalia(user: IUserFields) { + const circNazionali: any = this.listcircuits.filter( + (circ: any) => circ.isCircItalia + ); - const circNazionali: any = this.listcircuits.filter((circ: any) => circ.isCircItalia) - - if (user && user.profile && user.profile.mycircuits && user.profile.mycircuits.length > 0) { + if ( + user && + user.profile && + user.profile.mycircuits && + user.profile.mycircuits.length > 0 + ) { for (const circ of circNazionali) { - const trovato = user.profile.mycircuits.findIndex((mycirc: any) => mycirc.circuitname === circ.name) >= 0 + const trovato = + user.profile.mycircuits.findIndex( + (mycirc: any) => mycirc.circuitname === circ.name + ) >= 0; if (trovato) { - return true + return true; } } } - return false - + return false; }, isCircuitNational(circuitname: string): boolean { - return this.listcircuits.findIndex((rec: ICircuit) => rec.name === circuitname && rec.isCircItalia) >= 0 + return ( + this.listcircuits.findIndex( + (rec: ICircuit) => rec.name === circuitname && rec.isCircItalia + ) >= 0 + ); }, getCircuitsNational(): any[] { - const userStore = useUserStore() - let arrcircnaz = this.listcircuits.filter((rec: ICircuit) => rec.isCircItalia) + const userStore = useUserStore(); + let arrcircnaz = this.listcircuits.filter((rec: ICircuit) => rec.isCircItalia); for (const circ of arrcircnaz) { if (userStore.my.profile.useraccounts) - circ.account = userStore.my.profile.useraccounts.find((rec: IAccount) => rec.circuitId === circ._id) + circ.account = userStore.my.profile.useraccounts.find( + (rec: IAccount) => rec.circuitId === circ._id + ); } - return arrcircnaz + return arrcircnaz; }, IsNationalAndNotEnterInLocal(circuitname: string): boolean { - const userStore = useUserStore() - - return this.isCircuitNational(circuitname) && userStore.my.profile.mycircuits.length <= 0 + const userStore = useUserStore(); + return ( + this.isCircuitNational(circuitname) && userStore.my.profile.mycircuits.length <= 0 + ); }, - getFidoConcessoByUsername(myuser: IUserFields, circuitId: string, username: string, groupname?: string): number|string { + getFidoConcessoByUsername( + myuser: IUserFields, + circuitId: string, + username: string, + groupname?: string + ): number | string { if (myuser && myuser.profile.useraccounts) { - const account = myuser.profile.useraccounts.find((rec: IAccount) => (rec.username === username || (rec.groupname === groupname && groupname !== '')) && rec.circuitId === circuitId) - return account ? account.fidoConcesso : 0 + const account = myuser.profile.useraccounts.find( + (rec: IAccount) => + (rec.username === username || + (rec.groupname === groupname && groupname !== '')) && + rec.circuitId === circuitId + ); + return account ? account.fidoConcesso : 0; } else { - return '' + return ''; } }, SonoDentroAdAlmeno1CircuitoConFido(): boolean { - const userStore = useUserStore() + const userStore = useUserStore(); if (userStore.my.profile.useraccounts) { - const accountsConFido = userStore.my.profile.useraccounts.filter((rec: IAccount) => rec.fidoConcesso > 0) + const accountsConFido = userStore.my.profile.useraccounts.filter( + (rec: IAccount) => rec.fidoConcesso > 0 + ); for (const account of accountsConFido) { - const mycircuit = this.listcircuits.find((circ: ICircuit) => circ._id === account.circuitId) + const mycircuit = this.listcircuits.find( + (circ: ICircuit) => circ._id === account.circuitId + ); if (mycircuit && !mycircuit.circuitiExtraProv) { - return true + return true; } } } - return false + return false; }, get1CircuitoConFido() { - const userStore = useUserStore() + const userStore = useUserStore(); if (userStore.my.profile.useraccounts) { - const accountsConFido = userStore.my.profile.useraccounts.filter((rec: IAccount) => rec.fidoConcesso > 0) + const accountsConFido = userStore.my.profile.useraccounts.filter( + (rec: IAccount) => rec.fidoConcesso > 0 + ); for (const account of accountsConFido) { - const mycircuit = this.listcircuits.find((circ: ICircuit) => circ._id === account.circuitId) + const mycircuit = this.listcircuits.find( + (circ: ICircuit) => circ._id === account.circuitId + ); if (mycircuit && !mycircuit.circuitiExtraProv) { - return mycircuit + return mycircuit; } } } - return null + return null; }, getCircuitClass(circuit: ICircuit) { if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE3_MONETA_ABILITATA) - return 'circuito_abilitato' + return 'circuito_abilitato'; else if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE2_ORGANIZZAZIONE) - return 'circuito_fase_2' + return 'circuito_fase_2'; else if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE1_CREAZIONE_GRUPPO) - return 'circuito_in_creazione' + return 'circuito_in_creazione'; }, - getColorCircuitClass(circuit: ICircuit) { if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE3_MONETA_ABILITATA) - return 'green' + return 'green'; else if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE2_ORGANIZZAZIONE) - return 'orange' + return 'orange'; else if (circuit.status === shared_consts.CIRCUIT_STATUS.FASE1_CREAZIONE_GRUPPO) - return 'red' + return 'red'; }, getCircuitByName(circuitname: string): ICircuit | undefined | null { - return this.listcircuits.find((rec: ICircuit) => rec.name === circuitname) + return this.listcircuits.find((rec: ICircuit) => rec.name === circuitname); }, getCircuitByProvinceAndCard(prov: string, card: string): ICircuit | undefined | null { @@ -199,11 +235,12 @@ export const useCircuitStore = defineStore('CircuitStore', { const idCircuit = globalStore.getIdCircuitToAssignByProv(prov); if (idCircuit) { - return this.getCircuitByCircuitId(idCircuit) + return this.getCircuitByCircuitId(idCircuit); } else { - return this.listcircuits.find((rec: ICircuit) => rec.strProv === prov && ((rec.card === card) || (!card))) + return this.listcircuits.find( + (rec: ICircuit) => rec.strProv === prov && (rec.card === card || !card) + ); } - }, getCircuitsByProvince(prov: string): ICircuit[] { @@ -211,9 +248,9 @@ export const useCircuitStore = defineStore('CircuitStore', { const idCircuit = globalStore.getIdCircuitToAssignByProv(prov); if (idCircuit) { - return this.listcircuits.filter((rec: ICircuit) => rec._id === idCircuit) + return this.listcircuits.filter((rec: ICircuit) => rec._id === idCircuit); } else { - return this.listcircuits.filter((rec: ICircuit) => rec.strProv === prov) + return this.listcircuits.filter((rec: ICircuit) => rec.strProv === prov); } }, @@ -222,75 +259,79 @@ export const useCircuitStore = defineStore('CircuitStore', { let arrcirc = filterarr.map((subArray: any) => { return subArray.name; - }) - if (arrcirc.length > 1) - return ['[Nessuno]', ...arrcirc] - else - return arrcirc + }); + if (arrcirc.length > 1) return ['[Nessuno]', ...arrcirc]; + else return arrcirc; }, getCircuitByCircuitId(circuitId: string): ICircuit | null | undefined { - return this.listcircuits.find((rec: ICircuit) => rec._id === circuitId) + return this.listcircuits.find((rec: ICircuit) => rec._id === circuitId); }, getNameByCircuitId(circuitId: string): string { const circuit = this.getCircuitByCircuitId(circuitId); - return circuit && circuit.name ? circuit.name : '' + return circuit && circuit.name ? circuit.name : ''; }, getCircuitsLabelValue(): any { - let arr = [] + let arr = []; for (const circ of this.listcircuits) { - arr.push({ label: circ.name, value: circ._id }) + arr.push({ label: circ.name, value: circ._id }); } - return arr + return arr; }, getCircuitByPath(circuitpath: string): ICircuit | null { - const ris = this.listcircuits.find((rec: ICircuit) => rec.path === circuitpath) - return ris ? ris : null + const ris = this.listcircuits.find((rec: ICircuit) => rec.path === circuitpath); + return ris ? ris : null; }, async loadCircuits() { return Api.SendReq('/users/circuits', 'POST', null) .then((res) => { - return res.data - }).catch((error) => { - return {} + return res.data; }) - + .catch((error) => { + return {}; + }); }, async aggiornaSaldo(circuitId: string, groupname: string) { - const userStore = useUserStore() - const notifStore = useNotifStore() + const userStore = useUserStore(); + const notifStore = useNotifStore(); - const username = userStore.my.username - const lastdr = notifStore.getLastDataRead(username) + const username = userStore.my.username; + const lastdr = notifStore.getLastDataRead(username); return Api.SendReq('/users/updatesaldo', 'POST', { circuitId, groupname, lastdr }) .then((res) => { - if (res.data.ris) - tools.updateMyData(res.data.ris) - }).catch((error) => { - return {} + if (res.data.ris) tools.updateMyData(res.data.ris); }) - + .catch((error) => { + return {}; + }); }, getCircuitsListByGroup(mygrp: IMyGroup): ICircuit[] { if (mygrp.mycircuits) - return this.listcircuits.filter((rec: ICircuit) => mygrp.mycircuits!.findIndex((circ: ICircuit) => circ.name === rec.name) >= 0) + return this.listcircuits.filter( + (rec: ICircuit) => + mygrp.mycircuits!.findIndex((circ: ICircuit) => circ.name === rec.name) >= 0 + ); - return [] + return []; }, IsAskedCircuitByNameAndGroup(name: string, groupname: string): boolean { - let circuit = this.listcircuits.find((circ: ICircuit) => circ.name === name) + let circuit = this.listcircuits.find((circ: ICircuit) => circ.name === name); if (circuit && circuit.req_groups) { - return circuit.req_groups?.findIndex((grp: IGroupShort) => grp.groupname === groupname) >= 0 + return ( + circuit.req_groups?.findIndex( + (grp: IGroupShort) => grp.groupname === groupname + ) >= 0 + ); } - return false + return false; }, getAccountByCircuitId(circuitId: string): any { @@ -300,23 +341,1048 @@ export const useCircuitStore = defineStore('CircuitStore', { return null*/ }, getAccountsListNameValue(): any[] { - let arr = [] + let arr = []; if (this.listaccounts) { for (const acc of this.listaccounts) { - let chi = acc.username ? acc.username : (acc.groupname ? 'Gruppo: ' + acc.groupname : 'Comunitario: ' + acc.contocom) + let chi = acc.username + ? acc.username + : acc.groupname + ? 'Gruppo: ' + acc.groupname + : 'Comunitario: ' + acc.contocom; if (acc.circuitId) { - chi = '[' + this.getNameByCircuitId(acc.circuitId) + '] ' + chi + chi = '[' + this.getNameByCircuitId(acc.circuitId) + '] ' + chi; } - arr.push({ label: chi, value: acc._id }) + arr.push({ label: chi, value: acc._id }); } } - return arr + return arr; }, getSortFieldsAvailable(): any[] { - return [{ label: 'Nome', value: 'name' }, { label: 'Iscritti', value: 'numMembers' }] + return [ + { label: 'Nome', value: 'name' }, + { label: 'Iscritti', value: 'numMembers' }, + ]; }, + getStypeRegulationHTML() { + return `/* CSS COMPLETO PER REGOLAMENTO - Pronto per essere salvato */ + +.regulation-content { + max-width: 800px; + margin: 0 auto; + padding: 16px; + background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf3 100%); + min-height: 100vh; +} + +/* Header */ +.reg-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 24px; + border-radius: 16px; + text-align: center; + margin-bottom: 24px; + box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3); + position: relative; + overflow: hidden; +} + +.reg-header::before { + content: ''; + position: absolute; + top: -50%; + right: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); +} + +.reg-header .header-icon { + margin-bottom: 12px; +} + +.reg-header .header-icon svg { + width: 48px; + height: 48px; + color: white; + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); +} + +.reg-header h1 { + margin: 0; + font-size: 2rem; + font-weight: 800; + text-shadow: 0 2px 8px rgba(0,0,0,0.2); +} + +.reg-header .circuit-badge { + display: inline-block; + background: rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + padding: 8px 16px; + border-radius: 20px; + margin-top: 12px; + font-weight: 600; + border: 1px solid rgba(255,255,255,0.3); +} + +/* Intro card */ +.intro-card { + background: white; + border-radius: 16px; + padding: 16px; + margin-bottom: 24px; + box-shadow: 0 4px 16px rgba(0,0,0,0.1); + display: flex; + gap: 16px; + align-items: flex-start; +} + +.intro-card .intro-icon { + font-size: 2rem; + flex-shrink: 0; +} + +.intro-card .intro-content h3 { + margin: 0 0 8px 0; + color: #667eea; + font-size: 1.2rem; +} + +.intro-card .intro-content p { + margin: 0; + color: #64748b; + line-height: 1.6; +} + +/* Sezioni */ +.reg-section { + background: white; + border-radius: 16px; + padding: 24px; + margin-bottom: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + position: relative; + transition: all 0.3s ease; +} + +.reg-section:hover { + box-shadow: 0 6px 20px rgba(0,0,0,0.12); + transform: translateY(-2px); +} + +.reg-section .section-number { + position: absolute; + top: 16px; + right: 16px; + font-size: 3rem; + font-weight: 900; + color: rgba(102, 126, 234, 0.1); + line-height: 1; +} + +.reg-section .section-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 3px solid #f1f5f9; +} + +.reg-section .section-header .section-icon { + font-size: 2rem; + flex-shrink: 0; +} + +.reg-section .section-header .section-title { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + color: #1e293b; +} + +.reg-section .section-body .section-content { + line-height: 1.8; + color: #475569; + margin-bottom: 16px; +} + +.reg-section .section-body .section-content strong { + color: #667eea; + font-weight: 600; +} + +/* Info boxes */ +.info-box { + display: flex; + gap: 12px; + padding: 16px; + border-radius: 12px; + margin: 16px 0; + align-items: flex-start; +} + +.info-box.primary { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); + border-left: 4px solid #667eea; +} + +.info-box.success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + border-left: 4px solid #10b981; +} + +.info-box.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%); + border-left: 4px solid #f59e0b; +} + +.info-box.neutral { + background: rgba(100, 116, 139, 0.08); + border-left: 4px solid #64748b; +} + +.info-box .info-icon { + font-size: 1.5rem; + flex-shrink: 0; +} + +.info-box .info-text { + flex: 1; + line-height: 1.6; + color: #475569; +} + +.info-box .info-text strong { + display: block; + margin-bottom: 4px; + color: #1e293b; +} + +/* Timeline */ +.timeline { + margin: 24px 0; + position: relative; +} + +.timeline::before { + content: ''; + position: absolute; + left: 20px; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); +} + +.timeline .timeline-item { + display: flex; + gap: 16px; + margin-bottom: 16px; + position: relative; +} + +.timeline .timeline-item .timeline-dot { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + flex-shrink: 0; + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); + position: relative; + z-index: 1; +} + +.timeline .timeline-item .timeline-content { + flex: 1; + background: #f8fafc; + padding: 12px; + border-radius: 12px; + border: 1px solid #e2e8f0; +} + +.timeline .timeline-item .timeline-content strong { + display: block; + color: #667eea; + margin-bottom: 4px; + font-size: 0.95rem; +} + +.timeline .timeline-item .timeline-content p { + margin: 0; + color: #64748b; + font-size: 0.9rem; + line-height: 1.5; +} + +/* Principle box */ +.principle-box { + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border: 2px solid #0ea5e9; + border-radius: 16px; + padding: 24px; + margin: 24px 0; + text-align: center; +} + +.principle-box .principle-icon { + font-size: 3rem; + margin-bottom: 12px; +} + +.principle-box h4 { + margin: 0 0 8px 0; + color: #0369a1; + font-size: 1.2rem; +} + +.principle-box p { + margin: 0 0 16px 0; + color: #0c4a6e; +} + +.principle-box .balance-visual { + display: flex; + justify-content: center; + align-items: center; + gap: 24px; + margin-top: 16px; +} + +.principle-box .balance-visual .balance-side { + display: flex; + flex-direction: column; + gap: 4px; +} + +.principle-box .balance-visual .balance-side.positive .balance-value { + color: #10b981; + font-size: 3rem; +} + +.principle-box .balance-visual .balance-side.negative .balance-value { + color: #ef4444; + font-size: 3rem; +} + +.principle-box .balance-visual .balance-side .balance-label { + font-size: 0.85rem; + font-weight: 600; + color: #64748b; +} + +.principle-box .balance-visual .balance-side .balance-value { + font-weight: 900; +} + +.principle-box .balance-visual .balance-equal { + font-size: 2rem; + font-weight: 700; + color: #0369a1; +} + +/* Cases grid */ +.cases-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 12px; + margin: 16px 0; +} + +.cases-grid .case-card { + background: #f8fafc; + border: 2px solid #e2e8f0; + border-radius: 12px; + padding: 16px; + text-align: center; + transition: all 0.3s ease; +} + +.cases-grid .case-card:hover { + border-color: #667eea; + transform: translateY(-4px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.2); +} + +.cases-grid .case-card .case-icon { + font-size: 2rem; + margin-bottom: 8px; +} + +.cases-grid .case-card h5 { + margin: 0 0 4px 0; + color: #1e293b; + font-size: 1rem; +} + +.cases-grid .case-card p { + margin: 0; + font-size: 0.85rem; + color: #64748b; + line-height: 1.4; +} + +/* Highlight box */ +.highlight-box { + display: flex; + gap: 12px; + padding: 16px; + border-radius: 12px; + margin: 16px 0; + align-items: flex-start; +} + +.highlight-box.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(217, 119, 6, 0.15) 100%); + border: 2px solid #f59e0b; +} + +.highlight-box .highlight-icon { + font-size: 1.5rem; + flex-shrink: 0; +} + +.highlight-box .highlight-content { + flex: 1; + line-height: 1.6; + color: #78350f; +} + +.highlight-box .highlight-content strong { + display: block; + margin-bottom: 4px; + color: #92400e; +} + +/* Role box */ +.role-box { + background: linear-gradient(135deg, #fefce8 0%, #fef3c7 100%); + border: 2px solid #fbbf24; + border-radius: 16px; + padding: 16px; + margin: 16px 0; +} + +.role-box .role-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 2px solid #fde68a; +} + +.role-box .role-header .role-icon { + font-size: 1.5rem; +} + +.role-box .role-header h4 { + margin: 0; + color: #92400e; + font-size: 1.1rem; +} + +.role-box .tasks-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.role-box .tasks-list .task-item { + display: flex; + align-items: center; + gap: 12px; + padding: 8px; + background: rgba(255,255,255,0.5); + border-radius: 8px; + transition: all 0.3s ease; +} + +.role-box .tasks-list .task-item:hover { + background: rgba(255,255,255,0.8); + transform: translateX(4px); +} + +.role-box .tasks-list .task-item .task-bullet { + color: #10b981; + font-weight: 700; + font-size: 1.1rem; + flex-shrink: 0; +} + +.role-box .tasks-list .task-item span:last-child { + color: #78350f; + line-height: 1.4; +} + +/* Platform card */ +.platform-card { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%); + border: 2px solid #667eea; + border-radius: 16px; + padding: 24px; + margin: 16px 0; + text-align: center; +} + +.platform-card .platform-logo { + margin-bottom: 16px; +} + +.platform-card .platform-logo .logo-text { + font-size: 2.5rem; + font-weight: 900; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.platform-card .platform-logo .logo-domain { + font-size: 1.5rem; + color: #667eea; + font-weight: 600; +} + +.platform-card p { + color: #475569; + margin-bottom: 16px; + line-height: 1.6; +} + +.platform-card .platform-features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 12px; +} + +.platform-card .platform-features .feature { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 12px; + background: white; + border-radius: 12px; + font-size: 0.85rem; + color: #64748b; +} + +.platform-card .platform-features .feature .feature-icon { + font-size: 1.5rem; +} + +/* Accounts grid */ +.accounts-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 16px; + margin: 16px 0; +} + +.accounts-grid .account-type { + padding: 24px; + border-radius: 16px; + text-align: center; + border: 3px solid; +} + +.accounts-grid .account-type.positive { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + border-color: #10b981; +} + +.accounts-grid .account-type.negative { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%); + border-color: #3b82f6; +} + +.accounts-grid .account-type .account-icon { + font-size: 2.5rem; + margin-bottom: 12px; +} + +.accounts-grid .account-type h5 { + margin: 0 0 8px 0; + color: #1e293b; + font-size: 1.1rem; +} + +.accounts-grid .account-type p { + margin: 0; + color: #64748b; + font-size: 0.9rem; + line-height: 1.5; +} + +/* Reference card */ +.reference-card { + background: linear-gradient(135deg, #ede9fe 0%, #ddd6fe 100%); + border: 2px solid #8b5cf6; + border-radius: 16px; + padding: 24px; + display: flex; + gap: 16px; + align-items: flex-start; +} + +.reference-card .reference-icon { + font-size: 2rem; + flex-shrink: 0; +} + +.reference-card .reference-content { + flex: 1; +} + +.reference-card .reference-content h4 { + margin: 0 0 8px 0; + color: #6b21a8; + font-size: 1.2rem; +} + +.reference-card .reference-content p { + margin: 0 0 12px 0; + color: #7c3aed; +} + +.reference-card .reference-content .reference-link { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background: white; + color: #8b5cf6; + text-decoration: none; + border-radius: 12px; + font-weight: 600; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(139, 92, 246, 0.2); +} + +.reference-card .reference-content .reference-link:hover { + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(139, 92, 246, 0.3); +} + +.reference-card .reference-content .reference-link .link-arrow { + font-size: 1.2rem; + transition: transform 0.3s ease; +} + +.reference-card .reference-content .reference-link:hover .link-arrow { + transform: translateX(4px); +} + +/* Footer */ +.reg-footer { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 24px; + border-radius: 16px; + text-align: center; + margin-top: 24px; + box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3); +} + +.reg-footer .footer-icon { + font-size: 2rem; + margin-bottom: 12px; +} + +.reg-footer p { + margin: 0; + font-size: 1.1rem; + font-weight: 600; + opacity: 0.95; +} + +/* Responsive */ +@media (max-width: 600px) { + .regulation-content { + padding: 8px; + } + + .reg-header h1 { + font-size: 1.5rem; + } + + .reg-section { + padding: 16px; + } + + .reg-section .section-number { + font-size: 2rem; + top: 8px; + right: 8px; + } + + .reg-section .section-header .section-title { + font-size: 1.2rem; + } + + .timeline::before { + left: 15px; + } + + .timeline .timeline-item .timeline-dot { + width: 30px; + height: 30px; + font-size: 0.85rem; + } + + .cases-grid { + grid-template-columns: 1fr; + } + + .principle-box .balance-visual { + flex-direction: column; + gap: 12px; + } +}`; + }, + + transformRegulationHTML(circuitName: string): string { + let str = ` +
+ +
+
+ + + + + +
+

Regolamento

+
${circuitName}
+
+ + +
+
ℹ️
+
+

Benvenuto nel Circuito RIS

+

Questo documento descrive le regole e i principi fondamentali del nostro sistema di scambio comunitario.

+
+
+ + +
+
01
+
+
🏛️
+

Costituzione e scopo Comunitario

+
+
+

+ La Comunità Territoriale "${circuitName}" spontanea (da ora "la Comunità") costituisce un circuito di scambio + tra i partecipanti, ciascuno dei quali dovrà indicare i beni e servizi che offre alla Comunità stessa in RIS. + Il circuito funziona come dettagliato qui di seguito. +

+
+
+ + +
+
02
+
+
🔄
+

Circuito di scambio

+
+
+ + +
+
💰
+
+ Cos'è il RIS? + Un'unità di conto per misurare il valore degli scambi, convenzionalmente pari a 1 euro. +
+
+ +

+ La Comunità ha avviato un sistema di scambio di beni e servizi tra utenti, in cui il valore delle transazioni + si misura utilizzando una unità di conto denominata RIS, convenzionalmente considerata pari a 1 euro. +

+ + +
+
+
1
+
+ Inizio +

Tutti gli utenti partono con 0 RIS

+
+
+
+
2
+
+ Accordo +

Le parti concordano liberamente il valore dello scambio

+
+
+
+
3
+
+ Transazione +

Il pagante può usare la "Fiducia Concessa" andando in debito

+
+
+
+
4
+
+ Credito +

Il ricevente ottiene RIS a credito sul proprio conto

+
+
+
+ + +
+
⚖️
+
+

Principio di Bilancio Perfetto

+

In ogni momento la somma dei crediti = somma dei debiti

+
+
+ Crediti + + +
+
=
+
+ Debiti + - +
+
+
+
+ +

+ Ciascun detentore di un saldo negativo concorda che la sua esposizione funge da garanzia della quantità + di RIS equivalenti circolanti nel circuito e che può essere chiamato a regolarizzare con equivalente + pagamento in euro *, entro un lasso di tempo ragionevolmente adeguato, nel caso in cui: +

+ +
+
+
📋
+
Delibera Comunitaria
+

Decisione motivata del Gruppo Tecnico

+
+
+
🚪
+
Uscita Volontaria
+

L'utente decide di uscire per motivi personali

+
+
+
+
Chiusura Circuito
+

Per decisione o forza maggiore

+
+
+ +
+
⚠️
+
+ Nota importante: + La regolarizzazione può avvenire con equivalente valore in beni e servizi, oppure, + in ultima istanza, con pagamento in euro. +
+
+
+
+ + +
+
03
+
+
👥
+

Il Gruppo Tecnico

+
+
+

+ La Comunità costituisce il Gruppo Tecnico di Gestione degli Scambi (da ora "Gruppo Tecnico"), + partecipante esso stesso agli scambi tramite uno o più delegati autorizzati. +

+ +
+
+
🎯
+

Compiti Principali

+
+
+
+ + Approvare richieste di adesione +
+
+ + Aggiornare l'elenco partecipanti +
+
+ + Stabilire soglie di scoperto e attivo +
+
+ + Valutare finanziamenti sostenibili +
+
+ + Gestire tassazioni sulle transazioni +
+
+ + Modulare il deperimento conti inoperosi +
+
+ + Deliberare chiusura circuito +
+
+ + Gestire riserve comunitarie +
+
+ + Emendare il Regolamento +
+
+
+ +
+
📊
+
+ Il Gruppo Tecnico sottopone periodicamente resoconti delle attività alla Comunità. +
+
+
+
+ + +
+
04
+
+
💻
+

Sistemi di contabilizzazione

+
+
+
+ +

Piattaforma ufficiale per la registrazione dei conti RIS e delle transazioni

+
+
+ 📝 + Registrazione transazioni +
+
+ 👁️ + Visualizzazione situazione +
+
+ 💬 + Comunicazione beni/servizi +
+
+
+ +

+ La Comunità, in accordo con il Gruppo Tecnico, può decidere di utilizzare altri strumenti + di contabilizzazione, che dovranno permettere l'inserimento delle transazioni e la visualizzazione + da parte di ogni partecipante. +

+
+
+ + +
+
05
+
+
🏦
+

Conto Comunitario

+
+
+

+ La Comunità può aprire uno o più Conti gestiti dal Gruppo Tecnico per interagire + con i conti degli altri utenti. +

+ +
+ + +
+ +
+
📌
+
+ I conti comunitari seguono le stesse regole degli altri conti (partenza a 0, possibilità di scoperto, ecc.) +
+
+
+
+ + +
+
06
+
+
📚
+

Riferimenti

+
+
+
+
🔗
+
+

Sistema di Credito Naturale

+

Per approfondimenti sui meccanismi dello scambio:

+ + sicrenacc.info + + +
+
+
+
+ + + +
+ `; + + const customCss = this.getStypeRegulationHTML(); + if (customCss) { + str = ` + + ${str} + `; + } + + return str; + }, }, -}) +}); diff --git a/src/store/Modules/fieldsTable.ts b/src/store/Modules/fieldsTable.ts index 7ee1f40c..eda4ade8 100755 --- a/src/store/Modules/fieldsTable.ts +++ b/src/store/Modules/fieldsTable.ts @@ -2324,6 +2324,7 @@ export const colmyUserGroup = [ required: false, showWhen: costanti.showWhen.NewRec + costanti.showWhen.InEdit, visible: false, + sortable: false, }), AddCol({ name: 'visibility', @@ -2508,6 +2509,7 @@ export const colmyGoods = [ required: false, showWhen: costanti.showWhen.NewRec + costanti.showWhen.InEdit, visible: false, + sortable: false, }), AddCol({ name: 'idShipping', diff --git a/src/store/Modules/tools.ts b/src/store/Modules/tools.ts index 29c12961..7ca82b14 100644 --- a/src/store/Modules/tools.ts +++ b/src/store/Modules/tools.ts @@ -4232,7 +4232,7 @@ export const tools = { }, sitename() { - return this.getappname() + return this.getappname(); }, getproc() { @@ -4681,9 +4681,9 @@ export const tools = { return def; } }, - getCookieBool(mytok: any, def?: any) { + getCookieBool(mytok: any, def?: any): boolean { const ris = Cookies.get(mytok); - if (ris === 'null') return def; + if (ris === 'null' || ris === undefined || ris === null) return def; return ris === '1' ? true : false; }, @@ -11430,14 +11430,29 @@ export const tools = { .replace(/[ \t]{2,}/g, ' '); // 2+ spazi → 1 spazio (MA non newline) }, }, // Nel tuo file tools.ts o dove hai le utility - convertHTMLForElement(htmlText: string, usacomeHTML?: boolean): string { + convertHTMLForElement( + htmlText: string, + usacomeHTML?: boolean, + customCss?: string + ): string { if (!htmlText) return ''; if (!usacomeHTML) { return htmlText; } - return this.converteSpaziMultipliIn1solo(htmlText); + let str = this.converteSpaziMultipliIn1solo(htmlText); + + if (customCss) { + str = ` + + ${str} + `; + } + + return str; }, convertinbspInSpazi(str: string) { @@ -11459,7 +11474,7 @@ export const tools = { }, isGruppoMacro() { - return this.getIdApp() === this.IDAPP_MACRO + return this.getIdApp() === this.IDAPP_MACRO; }, // FINE ! diff --git a/src/store/globalStore.ts b/src/store/globalStore.ts index 685d2db4..68e777aa 100644 --- a/src/store/globalStore.ts +++ b/src/store/globalStore.ts @@ -56,6 +56,8 @@ import { useCatalogStore } from './CatalogStore'; const stateConnDefault = 'online'; +const USASYNC = false; + async function getConfig(id: any) { return globalroutines('read', 'config', null, id); } @@ -1271,8 +1273,10 @@ export const useGlobalStore = defineStore('GlobalStore', { // console.log('loadAfterLogin') this.clearDataAfterLoginOnlyIfActiveConnection(); - await userStore.loadListaEditori(); - await userStore.loadListaReferenti(); + if (tools.isGruppoMacro()) { + await userStore.loadListaEditori(); + await userStore.loadListaReferenti(); + } await globalroutines('readall', 'config', null); @@ -2245,6 +2249,21 @@ export const useGlobalStore = defineStore('GlobalStore', { // User not exist !! } + + if (USASYNC) { + // ============================================ + // 3. CARICA TABELLE SYNC DA CACHE + // ============================================ + await this.loadSyncTablesFromCache(); + + // ============================================ + // 4. SYNC IN BACKGROUND (non blocca UI) + // ============================================ + if (res.data._syncTables && res.data._syncTables.length > 0) { + this.syncTablesInBackground(res.data._syncTables); + } + } + Products.init(); // const isLogged = localStorage.getItem(toolsext.localStorage.username) @@ -2306,6 +2325,49 @@ export const useGlobalStore = defineStore('GlobalStore', { return { ris: false, status }; }, + // ============================================ + // NUOVI METODI PER SYNC + // ============================================ + async loadSyncTablesFromCache() { + const SyncService = (await import('@src/services/SyncService')).default; + + // Carica da cache locale (veloce, mostra subito) + this.resps = (await SyncService.loadFromCache('resps')) || []; + this.workers = (await SyncService.loadFromCache('workers')) || []; + this.groups = (await SyncService.loadFromCache('groups')) || []; + this.mygroups = (await SyncService.loadFromCache('mygroups')) || []; + + const Products = useProducts(); + Products.products = (await SyncService.loadFromCache('products')) || []; + Products.cart = (await SyncService.loadFromCache('cart')) || { + items: [], + totalPrice: 0, + totalQty: 0, + userId: '', + }; + Products.orders = (await SyncService.loadFromCache('orderscart')) || []; + }, + + async syncTablesInBackground(syncTables: any) { + const SyncService = (await import('@src/services/SyncService')).default; + const idapp = tools.getEnv('VITE_APP_ID'); + + console.log('🔄 Sync in background:', syncTables); + + try { + // Sync tutte le tabelle indicate + const result = await SyncService.syncAll(idapp, syncTables); + + if (result.success) { + console.log('✓ Sync completato'); + // Ricarica dati aggiornati negli store + await this.loadSyncTablesFromCache(); + } + } catch (error) { + console.error('Sync background failed:', error); + } + }, + getProvinceByProv(provstr: string) { const recprov = this.provinces.find((rec: any) => rec.prov === provstr); diff --git a/src/views/login/signup/signup.vue b/src/views/login/signup/signup.vue index b4b0a388..7da0bae9 100755 --- a/src/views/login/signup/signup.vue +++ b/src/views/login/signup/signup.vue @@ -1,6 +1,6 @@