- altro aggiornamento restying

- Invio RIS aggiornato
- Eventi
- Home Page restyling
This commit is contained in:
Surya Paolo
2025-12-18 16:58:06 +01:00
parent 7c1946debe
commit d1a66ef4ea
38 changed files with 6551 additions and 5676 deletions

View File

@@ -35,7 +35,6 @@ export default defineConfig((ctx) => {
node: 'node20',
},
// ✅ AGGIUNTO: Importa automaticamente variables.scss ovunque
sassVariables: 'src/css/variables.scss',
vueRouterMode: 'history',

View File

@@ -190,6 +190,7 @@ export const shared_consts = {
PROFILE_COMPLETITION: 1510,
RISOHOME: 1600,
RISOHOME_MODERN: 1610,
RISOHOME_PAGFINALE: 1615,
PAGERIS: 1620,
CMYCIRCUITS: 1630,
CREA_VOLANTINO: 1700,
@@ -2122,6 +2123,11 @@ export const shared_consts = {
label: 'RISO Home Modern',
icon: 'fas fa-home',
},
{
value: 1615, // RISOHOME_PAGFINALE
label: 'RISO Home (Parte Finale))',
icon: 'fas fa-home',
},
{
value: 1620, // PAGERIS
label: 'Pagina RIS',

View File

@@ -67,6 +67,11 @@ export default defineComponent({
required: false,
default: false,
},
prop_compatto: {
type: Boolean,
required: false,
default: false,
},
finder: {
type: Boolean,
required: false,

View File

@@ -22,6 +22,7 @@
labelElemFind="trovati"
:choose_visutype="visuType"
:butt_modif_new="prop_modif"
:compatto="prop_compatto"
:noresultLabel="
t('grid.nosearchfound') + ' ' + (showMap ? t('grid.intheareamap') : '')
"

View File

@@ -1,3 +1,6 @@
// ========================================
// SHADOW VARIABLES
// ========================================
$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);
@@ -20,22 +23,23 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
margin-top: 3px;
}
.q-table td {
padding-left: 1px;
padding-right: 2px;
padding-top: 0;
padding-bottom: 0;
&__title {
font-size: 1rem;
}
}
.q-table {
td {
padding: 0 2px 0 1px;
&__title {
font-size: 1rem;
}
}
&__col {
font-size: 1rem;
color: gray;
}
&__top {
padding-top: 0 !important;
}
}
.newrec_fields {
@@ -51,10 +55,6 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
padding: 3px 6px !important;
}
.q-table__top {
padding-top: 0 !important;
}
// ========================================
// DIALOG & LAYOUT
// ========================================
@@ -88,6 +88,7 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
overflow-y: auto;
max-height: calc(100vh - 120px);
// Scrollbar styling
&::-webkit-scrollbar {
width: 6px;
}
@@ -148,15 +149,20 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
}
// ========================================
// INFINITE SCROLL
// ========================================
// ========================================
// GRID LAYOUT PER CARD
// GRID LAYOUT PER CARD (INFINITE SCROLL)
// ========================================
.q-infinite-scroll {
padding: 6px;
gap: 8px;
// Mobile: colonna singola
@media (max-width: $mobile-breakpoint) {
display: flex;
flex-direction: column;
padding: 5px;
gap: 6px;
}
// Desktop: layout a griglia 2 colonne
@media (min-width: $mobile-breakpoint) {
display: grid;
@@ -165,19 +171,11 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
padding: 8px;
}
// Desktop large: 3 colonne per schermi molto larghi
// Desktop large: mantiene 2 colonne con gap maggiore
@media (min-width: 1400px) {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
// Mobile: colonna singola
@media (max-width: $mobile-breakpoint) {
display: flex;
flex-direction: column;
padding: 5px;
gap: 6px;
}
}
// ========================================
@@ -209,6 +207,10 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
padding: 3px 6px;
}
}
// ========================================
// GRID CARD ITEM
// ========================================
.grid-card-item {
width: 100%;
background: rgba(255, 255, 255, 0.7);
@@ -216,14 +218,16 @@ $shadow-hover: 0 4px 12px rgba(25, 118, 210, 0.15);
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
padding: 8px;
@media (max-width: $mobile-breakpoint) {
width: 100%;
padding: 4px; // Ridotto su mobile
padding: 4px;
}
}
// ========================================
// UTILITY CLASSES
// ========================================
.fill-all-width {
width: 100%;
}

View File

@@ -40,6 +40,7 @@ import { CMyUser } from '../CMyUser';
import { CMyGroups } from '../CMyGroups';
import { CMyFieldDb } from '../CMyFieldDb';
import { CMyRecCard } from '../CMyRecCard';
import { CMyRecEventi } from '../CMyRecEventi';
import { CMyRecCatalog } from '../CMyRecCatalog';
import { CMyRecRaccoltaCataloghi } from '../CMyRecRaccoltaCataloghi';
import { CMapByTable } from '../CMapByTable';
@@ -176,6 +177,11 @@ export default defineComponent({
required: false,
default: '',
},
compatto: {
type: Boolean,
required: false,
default: false,
},
actionType: {
type: Number,
required: false,
@@ -381,6 +387,7 @@ export default defineComponent({
CMyGroups,
CMyUser,
CMyRecCard,
CMyRecEventi,
CMyRecCatalog,
CMyCardPopup,
CMyRecGrpCard,
@@ -1341,14 +1348,20 @@ export default defineComponent({
obj2.idSkill = idSkill;
filtersearch2.push(obj2);
}
} else if (item.table === toolsext.TABGOODS && item.value === costanti.FILTER_TUTTI) {
} else if (
item.table === toolsext.TABGOODS &&
item.value === costanti.FILTER_TUTTI
) {
const obj2: any = {};
if (idSectorGood > 0) {
// idSectorGood
obj2['sectorGood._id'] = idSectorGood;
filtersearch2.push(obj2);
}
} else if (item.table === toolsext.TABBACHECAS && item.value === costanti.FILTER_TUTTI) {
} else if (
item.table === toolsext.TABBACHECAS &&
item.value === costanti.FILTER_TUTTI
) {
const obj2: any = {};
if (idSectorBacheca > 0) {
// idSectorBacheca

View File

@@ -89,6 +89,8 @@
>
</CTitleBanner>
<div v-if="shared_consts.VERTIC_SHOW_GRID.includes(myvertical)">
<div
v-if="(prop_search || canEdit) && finder"
@@ -444,7 +446,29 @@
:style="heightcarousel ? `height: ${heightcarousel}` : ''"
v-intersection="onIntersection"
>
<div v-if="tablesel === shared_consts.TABLES_MYBACHECAS && compatto">
<div
v-for="(row, indexrow) in serverData.slice(0, 5)"
:key="row._id || indexrow"
:class="{
row: opt.rowclass,
'items-stretch': opt.rowclass,
}"
>
<CMyRecEventi
:table="tablesel"
:prop_myrec="row"
@cmdext="cmdExt"
:editOn="editOn"
:margin_right="margin_right"
:compatto="compatto"
>
</CMyRecEventi>
</div>
</div>
<q-carousel
v-else
swipeable
animated
:autoplay="autoplay"
@@ -507,6 +531,7 @@
@cmdext="cmdExt"
:editOn="editOn"
:margin_right="margin_right"
:compatto="compatto"
>
</CMyRecCard>
</q-carousel-slide>
@@ -523,10 +548,7 @@
<q-infinite-scroll
ref="myinfscroll"
v-else-if="
shared_consts.VERTIC_SHOW_GRID.includes(myvertical) &&
alreadymounting
"
v-else-if="shared_consts.VERTIC_SHOW_GRID.includes(myvertical) && alreadymounting"
:initial-index="0"
@load="onLoadScroll"
:offset="350"
@@ -631,6 +653,7 @@
@cmdext="cmdExt"
:editOn="editOn"
:margin_right="margin_right"
:compatto="compatto"
>
</CMyRecCard>
</div>
@@ -699,9 +722,7 @@
</q-infinite-scroll>
<q-table
v-else-if="
!shared_consts.VERTIC_SHOW_GRID.includes(myvertical) &&
serverData &&
mycolumns
!shared_consts.VERTIC_SHOW_GRID.includes(myvertical) && serverData && mycolumns
"
:grid="shared_consts.VERTIC_SHOW_GRID.includes(myvertical)"
:grid-header="
@@ -728,55 +749,38 @@
selection="single"
v-model:selected="selected"
>
<template
v-if="
!(
myvertical === costanti.VISUTABLE_SCHEDA_USER ||
myvertical === 2 ||
myvertical === costanti.VISUTABLE_SCHEDA_GROUP
)
"
v-slot:header="props"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th> </q-th>
<q-th />
<q-th
v-for="col in props.cols"
:key="col.name"
v-for="(col, index) in (props.cols || []).filter(
(c) => c !== undefined && c !== null
)"
:key="col.name || index"
:props="props"
class="text-italic text-weight-bold"
>
<span v-if="col && showColCheck(col, tools.TIPOVIS_SHOW_RECORD, true)">
{{ col.label }}
<span v-if="col">
<template
v-if="
!(
myvertical === costanti.VISUTABLE_SCHEDA_USER ||
myvertical === 2 ||
myvertical === costanti.VISUTABLE_SCHEDA_GROUP
) &&
showColCheck &&
showColCheck(col, tools.TIPOVIS_SHOW_RECORD, true)
"
>
{{ col.label }}
</template>
<template v-else-if="col.sortable !== undefined">
{{ col.label }}
</template>
</span>
</q-th>
</q-tr>
</template>
<template
v-else
v-slot:header="props"
>
<q-tr :props="props">
<q-th> </q-th>
<span
v-for="col in props.cols"
:key="col.name"
>
<q-th
v-if="col.sortable"
:key="col.name"
:props="props"
class="text-italic text-weight-bold"
>
<span>
{{ col.label }}
</span>
</q-th>
</span>
</q-tr>
</template>
<template
v-slot:top-right
v-if="tablesList || arrfilters || enableExport"

View File

@@ -1539,6 +1539,41 @@
label="Bottone Modifica"
@update:model-value="modifElem"
></q-toggle>
<q-toggle
v-model="myel.parambool5"
color="positive"
label="Compatto"
@update:model-value="modifElem"
></q-toggle>
<q-input
dense
label="Titolo Principale"
@update:model-value="modifElem"
v-model="myel.stiletit_str"
filled
v-on:keyup.enter="saveElem"
>
</q-input>
<q-input
dense
label="Icona Titolo Normale:"
@update:model-value="modifElem"
v-model="myel.stiletit_icon"
filled
v-on:keyup.enter="saveElem"
>
</q-input>
<q-input
dense
label="Link Principale: /..."
@update:model-value="modifElem"
v-model="myel.container2"
filled
v-on:keyup.enter="saveElem"
>
</q-input>
</div>
</div>
<div v-else-if="myel.type === shared_consts.ELEMTYPE.RACCOLTE_CATALOGHI"></div>

View File

@@ -1,44 +1,241 @@
// ========================================
// COLOR VARIABLES
// ========================================
$grayshadow: #555;
$textcol: blue;
$textcol_scuro: darkblue;
// Dark mode variants
$grayshadow-dark: #222;
$textcol-dark: #64b5f6;
$textcol_scuro-dark: #90caf9;
// ========================================
// TYPOGRAPHY BASE
// ========================================
p {
margin: 0 0 1.25rem;
//text-shadow: .125rem .125rem .25rem $grayshadow;
}
h4 {
font-size: 1.25rem;
line-height: 1.5;
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow;
.body--dark & {
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow-dark;
}
}
// ========================================
// HEADING STYLES
// ========================================
.text-h1,
h1 {
font-size: 3rem;
font-weight: bold;
line-height: 3rem;
letter-spacing: -0.01562em;
margin-bottom: 8px !important;
}
.text-h2 {
font-size: 3.75rem;
font-weight: 300;
line-height: 3.75rem;
letter-spacing: -0.00833em;
}
.text-weight-bold {
font-weight: 700;
}
.text-vers {
font-size: 0.75rem;
font-weight: 400;
line-height: 1.75rem;
letter-spacing: 0.00937em;
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow;
.body--dark & {
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow-dark;
}
}
// ========================================
// CARD & UTILITY
// ========================================
.mycard {
visibility: hidden;
}
.shadow {
text-shadow: 0.125rem 0.125rem 0.25rem $grayshadow;
.body--dark & {
text-shadow: 0.125rem 0.125rem 0.25rem $grayshadow-dark;
}
}
// ========================================
// LANDING PAGE - BACKGROUND & LAYOUT
// ========================================
.landing_background {
background: #000 url(/images/foto1.jpg) no-repeat 50% fixed;
background-size: cover
background: #000 url("/images/foto1.jpg") no-repeat 50% fixed;
background-size: cover;
}
.landing>section {
display: -webkit-box;
display: -ms-flexbox;
.landing > section {
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
//padding: 0 16px
> div {
position: relative;
width: 100%;
}
&.padding {
padding: 5.62rem 1rem;
}
&.padding_testo {
padding-top: 1.25rem;
padding-bottom: 1rem;
}
&.padding_gallery {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
}
}
// ========================================
// LANDING TOOLBAR
// ========================================
.landing__toolbar {
background: linear-gradient(180deg, #000, transparent);
padding: 0 !important;
.q-btn {
border-radius: 0 0 0.315rem 0.315rem;
align-self: stretch;
}
}
// ========================================
// LANDING HERO
// ========================================
.landing__hero {
min-height: 50vh;
}
.landing__header {
height: 18vh;
}
.landing__arrow {
bottom: 1.5rem;
opacity: 0.4;
}
.landing__front {
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.6) 15%);
}
.landing__logo {
width: 9.4rem;
height: 9.4rem;
margin-top: 1.315rem;
}
// ========================================
// LANDING FEATURES
// ========================================
.landing__features {
.q-icon {
font-size: 4rem;
}
h4,
h6 {
margin: 1rem 0;
}
p {
opacity: 0.7;
font-size: 1rem;
line-height: 1.5;
}
}
.feat-descr {
font-size: 1.15rem;
&:hover {
transition: opacity 0.5s ease-in-out;
opacity: 0.9;
}
}
// ========================================
// LANDING FOOTER
// ========================================
.landing__footer {
background: linear-gradient(180deg, rgba(0, 0, 0, 0.8) 95%, #fff);
padding: 4.5rem 1.25rem !important;
color: #9f9f9f;
.body--dark & {
background: linear-gradient(180deg, rgba(0, 0, 0, 0.9) 95%, #1a1a1a);
color: #b0b0b0;
}
.doc-link {
color: $textcol;
&:hover {
opacity: 0.8;
}
.body--dark & {
color: $textcol-dark;
}
}
}
.landing__footer-icons {
font-size: 1.75rem;
a {
margin: 0 0.5rem 0.5rem;
text-decoration: none;
outline: 0;
color: $textcol;
transition: color 0.28s;
&:hover {
color: $textcol_scuro;
}
.body--dark & {
color: $textcol-dark;
&:hover {
color: $textcol_scuro-dark;
}
}
}
}
// ========================================
// INTRO SECTION
// ========================================
.intro {
display: flex;
justify-content: space-between;
align-items: stretch;
/* flex-flow: row nowrap; */
padding: 1.25rem 0 1.25rem 0;
margin: .125rem;
padding: 1.25rem 0;
margin: 0.125rem;
* {
width: 100%;
@@ -47,10 +244,7 @@ h4 {
margin-right: auto;
}
&__associazione {
min-width: 350px;
}
&__associazione,
&__comeassociarsi {
min-width: 350px;
}
@@ -64,160 +258,20 @@ h4 {
font-size: 1rem;
}
.landing>section.padding {
padding: 5.62rem 1rem;
}
.landing>section.padding_testo {
padding-top: 1.25rem;
padding-bottom: 1rem;
}
.landing>section.padding_gallery {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
}
.landing>section>div {
position: relative;
width: 100%
}
// ========================================
// LAYOUT UTILITIES
// ========================================
.maxwidth1200 {
max-width: 1200px;
}
.landing__toolbar {
background: -webkit-gradient(linear, left top, left bottom, from(#000), to(transparent));
background: linear-gradient(180deg, #000, transparent);
padding: 0 !important
}
.landing__toolbar .q-btn {
border-radius: 0 0 .315rem .315rem;
-ms-flex-item-align: stretch;
align-self: stretch
}
.landing__hero {
min-height: 50vh
}
.landing__header {
height: 18vh
}
.landing__arrow {
bottom: 1.5rem;
opacity: .4
}
.landing__front {
background: -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(15%, rgba(0, 0, 0, .6)));
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, .6) 15%)
}
.landing__logo {
width: 9.40rem;
height: 9.40rem;
margin-top: 1.315rem;
//-webkit-animation: logo-rotate 240s linear infinite;
//animation: logo-rotate 240s linear infinite
}
.landing__features .q-icon {
font-size: 4rem
}
h4 {
line-height: 1.5;
text-shadow: .25rem .25rem .5rem $grayshadow;
}
.landing__features h4,
.landing__features h6 {
margin: 1rem 0
}
.landing__features p {
opacity: .7;
font-size: 1rem;
line-height: 1.5;
}
.landing__footer {
//background: -webkit-gradient(linear, left top, left bottom, color-stop(65%, rgba(0, 0, 0, .1)), to(#000));
background: linear-gradient(180deg, rgba(0, 0, 0, .8) 95%, #FFF);
padding-top: 4.5rem !important;
padding-bottom: 4.5rem !important;
padding-left: 1.25rem;
padding-right: 1.25rem;
color: #9f9f9f;
}
.icon_contact:hover {
color: blue;
border-color: white;
border-width: .0625rem;
}
.landing__footer .doc-link {
color: $textcol;
}
.landing__footer .doc-link:hover {
opacity: .8
}
.feat-descr {
font-size: 1.15rem;
}
.feat-descr:hover {
transition: opacity 0.5s ease-in-out;
opacity: 0.9;
}
.q-col-gutter-sm {
padding: 3.125rem 3.125rem;
//margin-left: -48px
padding: 3.125rem;
}
body.mobile .landing:before {
content: "";
position: fixed;
top: 0;
height: 100vh;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
//background: #000 url(/images/cover.jpg) 50%;
background-size: cover
}
/*
@-webkit-keyframes logo-rotate {
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}
@keyframes logo-rotate {
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}
*/
.home {
//background-color: rgb(250, 250, 250);
padding: 3.125rem;
display: flex;
//flex-wrap: nowrap;
flex-direction: column;
align-items: center;
justify-content: space-between;
@@ -227,86 +281,291 @@ body.mobile .landing:before {
margin: 3.125rem;
}
.shadow {
//color: white;
text-shadow: 0.125rem 0.125rem 0.25rem $grayshadow;
}
.text-h1,
h1 {
font-size: 3rem;
font-weight: bold;
line-height: 3rem;
letter-spacing: -.01562em;
margin-bottom: 8px !important;
}
.text-h2 {
font-size: 3.75rem;
font-weight: 300;
line-height: 3.75rem;
letter-spacing: -.00833em;
}
.text-weight-bold {
font-weight: 700;
}
.text-vers {
font-size: 0.75rem;
font-weight: 400;
line-height: 1.75rem;
letter-spacing: .00937em;
text-shadow: .25rem .25rem .5rem $grayshadow;
}
.landing__footer-icons {
font-size: 1.75rem
}
.landing__footer-icons a {
margin: 0 .5rem .5rem;
text-decoration: none;
outline: 0;
color: $textcol;
transition: color .28s
}
.landing__footer-icons a:hover {
color: $textcol_scuro;
}
.doc-img {
max-width: 100%;
}
.mylist {
background: #3fdaff;
padding-left: 1.25rem;
}
.clgutter {
margin-top: 1.25rem;
padding: .62rem;
// ========================================
// MOBILE BACKGROUND
// ========================================
body.mobile .landing::before {
content: "";
position: fixed;
top: 0;
height: 100vh;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background-size: cover;
}
// ========================================
// GALLERY & CAROUSEL
// ========================================
.carousel_img_3 {
//background-image: url(/images/cibo_sano.jpg);
background-size: cover !important;
background-position: 50% center !important;
background-repeat: no-repeat !important;
}
@media (max-width: 718px) {
// PER VERSIONE MOBILE
.custom-caption {
text-align: center;
padding: 0.75rem;
color: $textcol;
background-color: rgba(0, 0, 0, 0.3);
.body--dark & {
color: $textcol-dark;
background-color: rgba(0, 0, 0, 0.5);
}
}
.sfondo-grigio {
padding: 1rem;
color: $textcol;
background-color: rgba(0, 0, 0, 0.35);
.body--dark & {
color: $textcol-dark;
background-color: rgba(0, 0, 0, 0.5);
}
}
// ========================================
// CONTACTS
// ========================================
.mycontacts {
color: gray;
letter-spacing: 0.078rem;
.body--dark & {
color: #aaa;
}
}
.mycontacts_title {
text-shadow: 0.125rem 0.125rem 0.125rem #555;
font-weight: bold;
color: #999;
letter-spacing: 0.125rem;
.body--dark & {
text-shadow: 0.125rem 0.125rem 0.125rem #222;
color: #bbb;
}
}
.mycontacts_text {
color: #999;
letter-spacing: 0.093rem;
.body--dark & {
color: #bbb;
}
}
.icon_contact {
&:hover {
color: blue;
border-color: white;
border-width: 0.0625rem;
.body--dark & {
color: $textcol-dark;
}
}
}
// ========================================
// GUTTER & SPACING
// ========================================
.clgutter {
margin-top: 1.25rem;
padding: 0.62rem;
}
.mylist {
background: #3fdaff;
padding-left: 1.25rem;
.body--dark & {
background: #1a8a9e;
}
}
// ========================================
// IMAGE
// ========================================
.doc-img {
max-width: 100%;
}
// ========================================
// EDITOR STYLES
// ========================================
.clEditDiv {
border: 2px solid #c8c9cb;
padding: 2px;
.body--dark & {
border-color: #555;
}
}
.clEditNotActive {
background-color: #e6e6e6;
.body--dark & {
background-color: #3a3a3a;
}
}
.clEdit {
border: 2px solid #f69f09;
padding: 2px;
&:hover {
border-color: #11f609;
cursor: pointer;
}
}
.selectedElem {
border: 3px solid #b91111 !important;
}
.elemEdit {
margin: 3px;
padding: 3px;
text-align: center;
font-weight: bold;
&:hover {
border: 2px solid #11f609;
cursor: pointer;
}
}
.myElemBase {
margin-bottom: 16px;
}
// ========================================
// ALIGNMENT UTILITIES
// ========================================
.align_center {
text-align: center;
}
.align_right {
text-align: right;
}
.align_left {
text-align: left;
}
// ========================================
// FLEX UTILITIES
// ========================================
.flex3 {
display: flex;
justify-content: space-between;
background-color: green;
.body--dark & {
background-color: #1b5e20;
}
}
// ========================================
// CONTENT SECTION
// ========================================
.content-section {
background: white;
border-radius: $r-lg;
padding: $s-sm;
margin-bottom: $s-sm;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
.body--dark & {
background: #2d2d2d;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
&:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $s-sm;
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
.body--dark & {
border-bottom-color: rgba(255, 255, 255, 0.1);
}
}
}
// ========================================
// SECTION TITLE
// ========================================
.section-title {
font-size: 1.2rem;
font-weight: 700;
margin: 0;
display: flex;
align-items: center;
gap: $s-sm;
background: $gradient-primary;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
.q-icon {
background: $gradient-primary;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.body--dark & {
background: $gradient-primary;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
.q-icon {
background: $gradient-primary;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
// ========================================
// MOBILE RESPONSIVE
// ========================================
@media (max-width: 718px) {
.landing__hero {
text-align: center
text-align: center;
.text-h1,
h1 {
font-size: 2rem;
line-height: 2.05rem;
margin-bottom: 1.25rem;
}
}
.landing__header {
height: 7vh
height: 7vh;
}
.clgutter {
@@ -314,43 +573,42 @@ h1 {
padding: 0;
}
.landing__hero .text-h1,
h1 {
font-size: 2rem;
line-height: 2.05rem;
margin-bottom: 1.25rem
.landing > section {
&.padding {
padding: 2.5rem 1rem;
}
&.padding_testo {
padding-top: 1.25rem;
padding-bottom: 1rem;
}
&.padding_gallery {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
max-width: 800px;
> div {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
}
}
}
.landing>section.padding {
padding: 2.5rem 1rem;
}
.landing>section.padding_testo {
padding-top: 1.25rem;
padding-bottom: 1rem;
}
.landing>section.padding_gallery {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
max-width: 800px;
}
.landing>section.padding_gallery>div {
padding-top: 3.125rem;
padding-bottom: 5.625rem;
}
.landing__features h4,
.landing__features h6 {
margin: 1.25rem 0
.landing__features {
h4,
h6 {
margin: 1.25rem 0;
}
}
h4 {
line-height: 1.4;
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow;
.body--dark & {
text-shadow: 0.25rem 0.25rem 0.5rem $grayshadow-dark;
}
}
.landing .feature-item {
@@ -367,13 +625,11 @@ h1 {
}
.landing__hero-btns {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center
justify-content: center;
}
.q-col-gutter-sm {
padding: .625rem .315rem;
padding: 0.625rem 0.315rem;
}
.text-subtitle1 {
@@ -383,91 +639,4 @@ h1 {
.text-vers {
font-size: 0.6rem;
}
}
.custom-caption {
text-align: center;
padding: .75rem;
color: $textcol;
background-color: rgba(0, 0, 0, .3);
}
.sfondo-grigio {
padding: 1rem;
color: $textcol;
background-color: rgba(0, 0, 0, .35);
}
.mycontacts {
color: gray;
letter-spacing: 0.078rem;
}
.mycontacts_title {
text-shadow: 0.125rem 0.125rem 0.125rem #555;
font-weight: bold;
color: #999;
letter-spacing: 0.125rem;
}
.mycontacts_text {
color: #999;
letter-spacing: 0.093rem;
}
.clEditDiv {
border: #c8c9cb solid 2px;
padding: 2px;
}
.clEditNotActive {
background-color: #e6e6e6;
}
.clEdit {
border: #f69f09 solid 2px;
padding: 2px;
}
.clEdit:hover {
border: #11f609 solid 2px;
cursor: pointer;
}
.selectedElem {
border: #b91111 solid 3px !important;
}
.align_center {
text-align: center;
}
.align_right {
text-align: right;
}
.align_left {
text-align: left;
}
.flex3 {
display: flex;
justify-content: space-between;
background-color: green;
}
.elemEdit {
margin: 3px;
padding: 3px;
text-align: center;
font-weight: bold;
}
.elemEdit:hover {
border: #11f609 solid 2px;
cursor: pointer;
}
.myElemBase{
margin-bottom: 16px;
}

View File

@@ -38,6 +38,7 @@ import { HomeRiso } from '@/components/HomeRiso';
import mycircuits from '@/views/user/mycircuits/mycircuits.vue';
import PageRis from '@/components/pageris/pageris.vue';
import { Riso_Home_Modern } from '@/components/Riso_Home_Modern';
import { Riso_Home_ParteFinale } from '@/components/Riso_Home_ParteFinale';
import { InvitaAmico } from '@/components/InvitaAmico';
import { CMyVideoYoutube } from '@/components/CMyVideoYoutube';
import { editprofile } from '@/components/editprofile';
@@ -110,6 +111,7 @@ export default defineComponent({
InvitaAmico,
HomeRiso,
Riso_Home_Modern,
Riso_Home_ParteFinale,
CMyEditor,
mycircuits,
editprofile,

View File

@@ -109,6 +109,12 @@
>
<Riso_Home_Modern />
</div>
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.RISOHOME_PAGFINALE"
class="myElemBase"
>
<Riso_Home_ParteFinale />
</div>
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.IMGTITLE"
class="myElemBase"
@@ -137,15 +143,16 @@
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.CREA_VOLANTINO"
class="myElemBase"
>˚
<div
>
˚
<div
v-if="editOn"
class="elemEdit"
>
CREA POSTER VOLANTINI:
</div>
<EventPosterGenerator></EventPosterGenerator>
<EventPosterGenerator></EventPosterGenerator>
</div>
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.IMGPOSTER"
@@ -622,23 +629,48 @@
</div>
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.GRID_ORIZ"
class="myElemBase"
>
<div
v-if="editOn"
class="elemEdit"
<section
class="content-section"
>
Visualizzatore Tabelle
</div>
<CGridOriz
:table="myel.container"
:tipovisu="myel.number"
:prop_search="myel.parambool"
:finder="myel.parambool2"
:showMap="myel.parambool3"
:heightcarousel="myel.heightcarousel"
:prop_modif="myel.parambool4"
></CGridOriz>
<div
v-if="myel.stiletit_str"
class="section-header"
>
<h2 class="section-title">
<q-icon
v-if="myel.stiletit_icon"
:name="myel.stiletit_icon || 'event'"
/>
{{ myel.stiletit_str }}
</h2>
<q-btn
v-if="myel.container2"
flat
dense
round
icon="arrow_forward"
@click="naviga(myel.container2)"
/>
</div>
<div
v-if="editOn"
class="elemEdit"
>
Visualizzatore Tabelle
</div>
<CGridOriz
:table="myel.container"
:tipovisu="myel.number"
:prop_search="myel.parambool"
:finder="myel.parambool2"
:showMap="myel.parambool3"
:heightcarousel="myel.heightcarousel"
:prop_modif="myel.parambool4"
:prop_compatto="myel.parambool5"
></CGridOriz>
</section>
</div>
<div
v-else-if="myel.type === shared_consts.ELEMTYPE.SEARCHPRODUCT"
@@ -701,6 +733,7 @@
:showMap="false"
:prop_modif="tools.isAdmin()"
:enableExport="true"
:prop_compatto="myel.number === -1"
></CGridOriz>
</q-tab-panel>
@@ -784,6 +817,7 @@
:showMap="false"
:prop_modif="tools.isAdmin()"
:enableExport="true"
:prop_compatto="myel.number === -1"
></CGridOriz>
</q-tab-panel>

View File

@@ -4,7 +4,7 @@
.rec-card-wrapper {
margin: 3px auto;
padding: 0;
width: 100%; // Aggiungi questa riga
width: 100%;
@media (max-width: $mobile-breakpoint) {
margin: 2px auto;
@@ -12,19 +12,55 @@
&.is-even {
.modern-rec-card {
background: linear-gradient(135deg, rgba(49, 153, 239, 0.249) 0%, rgba(25, 118, 210, 0.03) 100%);
background: linear-gradient(
135deg,
rgba(49, 153, 239, 0.15) 0%,
rgba(25, 118, 210, 0.03) 100%
);
border-color: rgba(66, 165, 245, 0.12);
}
}
&.is-odd {
.modern-rec-card {
background: linear-gradient(135deg, rgba(38, 197, 218, 0.344) 0%, rgba(0, 150, 136, 0.03) 100%);
background: linear-gradient(
135deg,
rgba(38, 197, 218, 0.2) 0%,
rgba(0, 150, 136, 0.03) 100%
);
border-color: rgba(38, 198, 218, 0.12);
}
}
}
// Dark mode alternating colors
body.body--dark,
.body--dark {
.rec-card-wrapper {
&.is-even {
.modern-rec-card {
background: linear-gradient(
135deg,
rgba(49, 153, 239, 0.2) 0%,
rgba(25, 118, 210, 0.08) 100%
);
border-color: rgba(66, 165, 245, 0.2);
}
}
&.is-odd {
.modern-rec-card {
background: linear-gradient(
135deg,
rgba(38, 197, 218, 0.25) 0%,
rgba(0, 150, 136, 0.08) 100%
);
border-color: rgba(38, 198, 218, 0.2);
}
}
}
}
// ========================================
// CARD EVENTO (Calendar View)
// ========================================
@@ -32,14 +68,14 @@
display: flex;
gap: 6px;
margin-bottom: 6px;
background: white;
background: var(--app-bg-card);
border-radius: $border-radius;
overflow: hidden;
box-shadow: $shadow-md;
box-shadow: var(--app-shadow-md);
transition: box-shadow $transition-speed ease;
&:hover {
box-shadow: $shadow-hover;
box-shadow: var(--app-shadow-lg);
}
@media (max-width: $mobile-breakpoint) {
@@ -135,7 +171,7 @@
.user-avatar {
margin-top: 6px;
border: 2px solid rgba(255, 255, 255, 0.3);
box-shadow: $shadow-sm;
box-shadow: var(--app-shadow-sm);
@media (max-width: $mobile-breakpoint) {
margin-top: 5px;
@@ -148,8 +184,6 @@
flex: 1;
position: relative;
overflow: hidden;
// L'altezza viene gestita dinamicamente dal Vue
}
.event-image {
@@ -171,10 +205,10 @@
// CARD RECORD PRINCIPALE
// ========================================
.modern-rec-card {
background: white;
border: 1px solid rgba(0, 0, 0, 0.08);
background: var(--app-bg-card);
border: 1px solid var(--app-border-color);
border-radius: $border-radius;
box-shadow: $shadow-sm;
box-shadow: var(--app-shadow-sm);
padding: 6px;
transition: all $transition-speed ease;
margin-bottom: 3px;
@@ -182,7 +216,7 @@
overflow: hidden;
&:hover {
box-shadow: $shadow-md;
box-shadow: var(--app-shadow-md);
transform: translateY(-1px);
}
@@ -222,8 +256,8 @@
}
.record-avatar {
box-shadow: $shadow-sm;
border: 2px solid rgba(0, 0, 0, 0.05);
box-shadow: var(--app-shadow-sm);
border: 2px solid var(--app-border-color);
@media (max-width: $mobile-breakpoint) {
width: 48px !important;
@@ -269,22 +303,20 @@
.tag-chip {
height: 20px;
font-size: 0.9rem;
padding: 0 px;
padding: 0 6px;
border-radius: 4px;
box-shadow: none;
font-weight: 500;
// Categoria principale - più scura e intensa
&.sector {
background: linear-gradient(135deg, $primary-color, #1976d2);
color: white;
}
// Sottocategoria - stessa base ma più chiara
&.subsector {
background: linear-gradient(135deg, color.adjust($primary-color, $lightness: 10%), #42a5f5);
background: linear-gradient(135deg, $primary-light, #42a5f5);
color: white;
opacity: 0.9; // Opzionale: leggera trasparenza
opacity: 0.9;
}
@media (max-width: $mobile-breakpoint) {
@@ -315,7 +347,7 @@
.description-text {
font-size: 0.9375rem;
color: #2c3e50;
color: var(--app-text-primary);
font-weight: 500;
line-height: 1.4;
margin: 3px 0;
@@ -323,7 +355,6 @@
&.event-title {
font-weight: 600;
color: #1a1a1a;
}
@media (max-width: $mobile-breakpoint) {
@@ -333,7 +364,7 @@
}
// ========================================
// PREFERENCES ROW (dopo descrizione)
// PREFERENCES ROW
// ========================================
.preferences-row {
margin: 8px 0 6px 0;
@@ -357,11 +388,11 @@
.preference-icon-avatar {
cursor: pointer;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
box-shadow: var(--app-shadow-sm);
&:hover {
transform: translateY(-2px) scale(1.08);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
box-shadow: var(--app-shadow-md);
}
@media (max-width: $mobile-breakpoint) {
@@ -384,11 +415,11 @@
.user-name {
flex: 1 1 auto;
min-width: 0;
color: $mainColor;
color: var(--app-text-primary);
font-weight: 600;
font-size: 0.9rem;
@media (max-width: $mobile) {
@media (max-width: $mobile-breakpoint) {
font-size: 0.85rem;
}
}
@@ -398,8 +429,9 @@
white-space: nowrap;
margin-left: auto;
font-size: 0.85rem;
color: var(--app-text-secondary);
@media (max-width: $mobile) {
@media (max-width: $mobile-breakpoint) {
font-size: 0.75rem;
.stat-item {
@@ -434,8 +466,14 @@
}
}
// Dark mode attending badge
body.body--dark .attending-badge,
.body--dark .attending-badge {
background: rgba(33, 186, 69, 0.2);
}
.cities-text {
color: rgba(44, 62, 80, 0.7); // Grigio bluastro più caldo del grigio puro
color: var(--app-text-secondary);
font-size: 0.85rem;
display: flex;
margin-left: auto;
@@ -443,12 +481,13 @@
gap: 4px;
i {
color: rgba($mainColor, 0.6); // Icona location con colore primario attenuato
color: var(--app-text-muted);
}
.cities-text-bold {
font-weight: bold;
font-size: 1.1rem;
color: var(--app-text-primary);
@media (max-width: $mobile-breakpoint) {
font-size: 1rem;
@@ -484,11 +523,11 @@
.action-menu-btn {
width: 30px;
height: 30px;
color: $grey-color;
color: var(--app-text-muted);
transition: all $transition-speed ease;
&:hover {
background: rgba(0, 0, 0, 0.05);
background: var(--app-border-color);
color: $primary-color;
}
@@ -511,15 +550,15 @@
white-space: wrap;
overflow: hidden;
font-size: 0.8125rem;
color: $grey-color;
color: var(--app-text-muted);
}
.cardrec {
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: $shadow-sm;
border: 1px solid var(--app-border-color);
box-shadow: var(--app-shadow-sm);
border-radius: $border-radius;
padding: 6px;
background-color: white;
background-color: var(--app-bg-card);
margin: 3px;
@media (min-width: 500px) {
@@ -532,9 +571,16 @@
color: $primary-color;
}
// Dark mode text_title
body.body--dark .text_title,
.body--dark .text_title {
color: $primary-light;
}
.event_date {
font-style: italic;
font-size: 0.8125rem;
color: var(--app-text-secondary);
}
// ========================================
@@ -543,12 +589,16 @@
.q-separator {
margin: 3px 0;
opacity: 0.5;
background: var(--app-divider-color);
@media (max-width: $mobile-breakpoint) {
margin: 2px 0;
}
}
// ========================================
// CATEGORY HIERARCHY
// ========================================
.category-hierarchy {
display: flex;
flex-wrap: wrap;
@@ -558,23 +608,28 @@
.hierarchy-arrow {
opacity: 0.5;
font-size: 16px;
color: var(--app-text-muted);
}
@media (max-width: $mobile-breakpoint) {
gap: 0px;
}
}
// ========================================
// CATEGORIES DIALOG
// ========================================
.categories-dialog {
.dialog-header {
padding: 16px 20px;
background: var(--app-bg-card);
}
.dialog-content {
padding: 20px;
max-height: 60vh;
overflow-y: auto;
background: var(--app-bg-card);
}
.categories-grid {
@@ -590,9 +645,12 @@
}
}
// ========================================
// ANNUNCIO LOCATION
// ========================================
.annuncio-location {
font-size: 1rem;
color: #718096;
color: var(--app-text-secondary);
align-items: center;
gap: 4px;
@@ -601,35 +659,148 @@
}
}
// Il contenitore padre (q-item o card) deve avere position relative
// ========================================
// OVERLAY BUTTONS
// ========================================
.q-item,
.event-card {
position: relative;
}
// Bottone overlay fisso a destra
.action-menu-btn-overlay {
position: absolute !important;
top: 8px;
right: 0px;
transform: translateY(-50%);
z-index: 10;
// Sfondo semi-trasparente per visibilità
background: rgba(255, 255, 255, 0.8) !important;
// Ombra leggera
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
background: var(--app-bg-card) !important;
box-shadow: var(--app-shadow-sm);
transition: all $transition-speed ease;
&:hover {
background: rgba(255, 255, 255, 1) !important;
background: var(--app-bg-card-hover) !important;
box-shadow: var(--app-shadow-md);
}
}
// Alternativa: in alto a destra
.action-menu-btn-overlay--top {
position: absolute !important;
top: 4px;
right: 4px;
z-index: 10;
background: var(--app-bg-card) !important;
}
// ========================================
// DARK MODE SPECIFIC OVERRIDES
// ========================================
body.body--dark,
.body--dark {
// Event date column - leggermente più scuro in dark mode
.event-date-column {
background: linear-gradient(135deg, #1565c0, #1976d2);
}
// Tag chips
.tag-chip {
&.sector {
background: linear-gradient(135deg, #1565c0, #1976d2);
}
&.subsector {
background: linear-gradient(135deg, #1976d2, #2196f3);
}
}
// Time badges nel calendar
.time-start,
.time-end {
background: rgba(255, 255, 255, 0.15);
}
// User avatar border
.user-avatar {
border-color: rgba(255, 255, 255, 0.2);
}
// Record avatar
.record-avatar {
border-color: var(--app-border-color-strong);
}
// Preference icon avatar
.preference-icon-avatar {
&:hover {
box-shadow: var(--app-shadow-lg);
}
}
// Action menu button hover
.action-menu-btn {
&:hover {
background: var(--app-border-color-strong);
color: $primary-light;
}
}
}
// ========================================
// EVENT CARD - Theme Aware
// ========================================
.event-card {
display: flex;
align-items: center;
gap: $s-md;
// Background con gradiente viola - usa opacità che funziona in entrambi i temi
background: linear-gradient(
135deg,
rgba(102, 126, 234, 0.08) 0%,
rgba(118, 75, 162, 0.08) 100%
);
border: 2px solid rgba(102, 126, 234, 0.2);
border-radius: $r-md;
padding: $s-md;
min-width: 280px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
}
// ========================================
// EVENT DATE BADGE
// ========================================
.event-date {
display: flex;
flex-direction: column;
align-items: center;
background: $gradient-primary;
color: white;
border-radius: $r-sm;
padding: $s-sm;
min-width: 50px;
z-index: 1;
// Posizionamento assoluto (dalla seconda definizione)
position: absolute;
top: $s-sm;
left: $s-sm;
.event-day {
font-size: 1.5rem;
font-weight: 700;
line-height: 1;
}
.event-month {
font-size: 0.9rem;
font-weight: 600;
text-transform: uppercase;
}
}
}

View File

@@ -44,6 +44,11 @@ export default defineComponent({
required: false,
default: 0,
},
compatto: {
type: Boolean,
required: false,
default: false,
},
},
setup(props, { emit }) {

View File

@@ -65,7 +65,6 @@
/>
</div>
</div>
<!-- Card Record Principale -->
<q-item
v-if="myrec"
@@ -343,7 +342,11 @@
class="annuncio-location"
>
<span v-if="ind > 0"></span>
<span v-if="table === shared_consts.TABLES_MYHOSPS" class="cities-text-bold">{{ rec.comune }} ({{ rec.prov }})</span>
<span
v-if="table === shared_consts.TABLES_MYHOSPS"
class="cities-text-bold"
>{{ rec.comune }} ({{ rec.prov }})</span
>
<span v-else>{{ rec.comune }} ({{ rec.prov }})</span>
</span>
</div>

View File

@@ -0,0 +1,759 @@
// ========================================
// WRAPPER CARD - ALTERNATING COLORS
// ========================================
.rec-card-wrapper {
margin: 3px auto;
padding: 0;
width: 100%; // Aggiungi questa riga
@media (max-width: $mobile-breakpoint) {
margin: 2px auto;
}
&.is-even {
.modern-rec-card {
background: linear-gradient(135deg, rgba(49, 153, 239, 0.249) 0%, rgba(25, 118, 210, 0.03) 100%);
border-color: rgba(66, 165, 245, 0.12);
}
}
&.is-odd {
.modern-rec-card {
background: linear-gradient(135deg, rgba(38, 197, 218, 0.344) 0%, rgba(0, 150, 136, 0.03) 100%);
border-color: rgba(38, 198, 218, 0.12);
}
}
}
// ========================================
// CARD EVENTO (Calendar View)
// ========================================
.event-card {
display: flex;
gap: 6px;
margin-bottom: 6px;
background: white;
border-radius: $border-radius;
overflow: hidden;
box-shadow: $shadow-md;
transition: box-shadow $transition-speed ease;
&:hover {
box-shadow: $shadow-hover;
}
@media (max-width: $mobile-breakpoint) {
gap: 5px;
margin-bottom: 5px;
}
}
.event-date-column {
flex: 0 0 70px;
background: linear-gradient(135deg, $primary-color, #42a5f5);
padding: 6px 3px;
display: flex;
align-items: center;
justify-content: center;
@media (max-width: $mobile-breakpoint) {
flex: 0 0 65px;
padding: 5px 2px;
}
}
.date-stack {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
color: white;
text-align: center;
}
.day-of-week {
font-size: 0.625rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.9;
@media (max-width: $mobile-breakpoint) {
font-size: 0.5625rem;
}
}
.day-number {
font-size: 1.875rem;
font-weight: 700;
line-height: 1;
margin: 3px 0;
@media (max-width: $mobile-breakpoint) {
font-size: 1.625rem;
margin: 2px 0;
}
}
.month-name {
font-size: 0.8125rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
@media (max-width: $mobile-breakpoint) {
font-size: 0.75rem;
}
}
.time-start,
.time-end {
font-size: 0.6875rem;
font-weight: 500;
padding: 2px 5px;
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
margin-top: 2px;
@media (max-width: $mobile-breakpoint) {
font-size: 0.625rem;
padding: 1px 4px;
}
}
.date-separator {
font-size: 0.9375rem;
margin: 3px 0;
opacity: 0.8;
@media (max-width: $mobile-breakpoint) {
font-size: 0.875rem;
margin: 2px 0;
}
}
.user-avatar {
margin-top: 6px;
border: 2px solid rgba(255, 255, 255, 0.3);
box-shadow: $shadow-sm;
@media (max-width: $mobile-breakpoint) {
margin-top: 5px;
width: 36px !important;
height: 36px !important;
}
}
.event-image-container {
flex: 1;
position: relative;
overflow: hidden;
// L'altezza viene gestita dinamicamente dal Vue
}
.event-image {
width: 100%;
height: 100%;
cursor: pointer;
transition: transform $transition-speed ease;
&:hover {
transform: scale(1.03);
}
:deep(.q-img__image) {
object-fit: cover;
}
}
// ========================================
// CARD RECORD PRINCIPALE
// ========================================
.modern-rec-card {
background: white;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: $border-radius;
box-shadow: $shadow-sm;
padding: 6px;
transition: all $transition-speed ease;
margin-bottom: 3px;
position: relative;
overflow: hidden;
&:hover {
box-shadow: $shadow-md;
transform: translateY(-1px);
}
@media (max-width: $mobile-breakpoint) {
padding: 5px;
margin-bottom: 2px;
}
}
// ========================================
// AVATAR SECTION
// ========================================
.avatar-section {
position: relative;
padding: 0 !important;
margin-right: 6px;
@media (max-width: $mobile-breakpoint) {
margin-right: 5px;
}
}
.type-badge {
position: absolute;
top: -3px;
left: -3px;
z-index: 1;
font-size: 0.625rem;
padding: 2px 5px;
border-radius: 4px;
font-weight: 600;
@media (max-width: $mobile-breakpoint) {
font-size: 0.5625rem;
padding: 1px 4px;
}
}
.record-avatar {
box-shadow: $shadow-sm;
border: 2px solid rgba(0, 0, 0, 0.05);
@media (max-width: $mobile-breakpoint) {
width: 48px !important;
height: 48px !important;
}
:deep(.q-img) {
border-radius: 50%;
}
}
// ========================================
// CONTENT SECTION
// ========================================
.content-section {
padding: 0 6px 0 0 !important;
min-width: 0;
@media (max-width: $mobile-breakpoint) {
padding: 0 5px 0 0 !important;
}
}
.tags-row {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 3px;
margin-bottom: 3px;
.status-row {
display: flex;
gap: $s-xs;
flex-wrap: wrap;
}
@media (max-width: $mobile-breakpoint) {
gap: 2px;
margin-bottom: 2px;
}
}
.tag-chip {
height: 20px;
font-size: 0.9rem;
padding: 0 px;
border-radius: 4px;
box-shadow: none;
font-weight: 500;
// Categoria principale - più scura e intensa
&.sector {
background: linear-gradient(135deg, $primary-color, #1976d2);
color: white;
}
// Sottocategoria - stessa base ma più chiara
&.subsector {
background: linear-gradient(135deg, color.adjust($primary-color, $lightness: 10%), #42a5f5);
color: white;
opacity: 0.9; // Opzionale: leggera trasparenza
}
@media (max-width: $mobile-breakpoint) {
height: 19px;
font-size: 0.8rem;
padding: 0 4px;
}
:deep(.q-chip__content) {
padding: 0;
}
}
.status-badge {
font-size: 0.6875rem;
padding: 2px 5px;
border-radius: 4px;
display: inline-flex;
align-items: center;
gap: 3px;
@media (max-width: $mobile-breakpoint) {
font-size: 0.625rem;
padding: 2px 4px;
gap: 2px;
}
}
.description-text {
font-size: 0.9375rem;
color: $text-secondary; // Era #2c3e50
font-weight: 500;
line-height: 1.4;
margin: 3px 0;
word-break: break-word;
.body--dark & {
color: $text-secondary-dark;
}
&.event-title {
font-weight: 600;
color: $text-primary; // Era #1a1a1a
.body--dark & {
color: $text-primary-dark;
}
}
@media (max-width: $mobile-breakpoint) {
font-size: 0.875rem;
margin: 2px 0;
}
}
// ========================================
// PREFERENCES ROW (dopo descrizione)
// ========================================
.preferences-row {
margin: 8px 0 6px 0;
@media (max-width: $mobile-breakpoint) {
margin: 6px 0 4px 0;
}
}
.preferences-icons-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
@media (max-width: $mobile-breakpoint) {
gap: 5px;
}
}
.preference-icon-avatar {
cursor: pointer;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
&:hover {
transform: translateY(-2px) scale(1.08);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
}
@media (max-width: $mobile-breakpoint) {
width: 28px !important;
height: 28px !important;
.q-icon {
font-size: 16px !important;
}
}
}
.user-stats-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: $s-sm;
flex-wrap: wrap;
.user-name {
flex: 1 1 auto;
min-width: 0;
color: $mainColor;
font-weight: 600;
font-size: 0.9rem;
@media (max-width: $mobile) {
font-size: 0.85rem;
}
}
.stats-container {
flex: 0 0 auto;
white-space: nowrap;
margin-left: auto;
font-size: 0.85rem;
@media (max-width: $mobile) {
font-size: 0.75rem;
.stat-item {
margin-left: 4px;
.q-icon {
font-size: 12px !important;
}
}
}
}
}
.attending-badge {
display: inline-flex;
align-items: center;
gap: 3px;
color: $positive-color;
font-weight: 600;
font-size: 0.8125rem;
margin: 3px 0;
padding: 3px 6px;
background: rgba(33, 186, 69, 0.1);
border-radius: 6px;
width: fit-content;
@media (max-width: $mobile-breakpoint) {
font-size: 0.75rem;
margin: 2px 0;
padding: 2px 5px;
gap: 2px;
}
}
.cities-text {
color: rgba(44, 62, 80, 0.7); // Grigio bluastro più caldo del grigio puro
font-size: 0.85rem;
display: flex;
margin-left: auto;
flex-flow: wrap;
gap: 4px;
i {
color: rgba($mainColor, 0.6); // Icona location con colore primario attenuato
}
.cities-text-bold {
font-weight: bold;
font-size: 1.1rem;
@media (max-width: $mobile-breakpoint) {
font-size: 1rem;
}
}
}
.cities-contrib-row {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 8px;
flex-wrap: wrap;
@media (max-width: $mobile-breakpoint) {
gap: 6px;
}
}
// ========================================
// ACTIONS SECTION
// ========================================
.actions-section {
padding: 0 !important;
align-self: flex-start;
margin-left: 3px;
@media (max-width: $mobile-breakpoint) {
margin-left: 2px;
}
}
.action-menu-btn {
width: 30px;
height: 30px;
color: $grey-color;
transition: all $transition-speed ease;
&:hover {
background: rgba(0, 0, 0, 0.05);
color: $primary-color;
}
@media (max-width: $mobile-breakpoint) {
width: 28px;
height: 28px;
}
}
// ========================================
// LEGACY SUPPORT
// ========================================
.myflex {
display: flex;
flex: 1;
}
.text_user_city {
text-overflow: ellipsis;
white-space: wrap;
overflow: hidden;
font-size: 0.8125rem;
color: $grey-color;
}
.cardrec {
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: $shadow-sm;
border-radius: $border-radius;
padding: 6px;
background-color: white;
margin: 3px;
@media (min-width: 500px) {
margin: 2px;
padding: 5px;
}
}
.text_title {
color: $primary-color;
}
.event_date {
font-style: italic;
font-size: 0.8125rem;
}
// ========================================
// SEPARATOR
// ========================================
.q-separator {
margin: 3px 0;
opacity: 0.5;
@media (max-width: $mobile-breakpoint) {
margin: 2px 0;
}
}
.category-hierarchy {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 2px;
.hierarchy-arrow {
opacity: 0.5;
font-size: 16px;
}
@media (max-width: $mobile-breakpoint) {
gap: 0px;
}
}
.categories-dialog {
.dialog-header {
padding: 16px 20px;
}
.dialog-content {
padding: 20px;
max-height: 60vh;
overflow-y: auto;
}
.categories-grid {
display: flex;
flex-wrap: wrap;
gap: 2px;
}
.category-chip {
font-size: 1.1rem;
padding: 8px 12px;
font-weight: 500;
}
}
.annuncio-location {
font-size: 1rem;
color: #718096;
align-items: center;
gap: 4px;
&::before {
content: '📍';
}
}
// Il contenitore padre (q-item o card) deve avere position relative
.q-item,
.event-card {
position: relative;
}
// Bottone overlay fisso a destra
.action-menu-btn-overlay {
position: absolute !important;
top: 8px;
right: 0px;
transform: translateY(-50%);
z-index: 10;
// Sfondo semi-trasparente per visibilità
background: rgba(255, 255, 255, 0.8) !important;
// Ombra leggera
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
&:hover {
background: rgba(255, 255, 255, 1) !important;
}
}
// Alternativa: in alto a destra
.action-menu-btn-overlay--top {
position: absolute !important;
top: 4px;
right: 4px;
z-index: 10;
}
// ==========================================
// EVENT CARD
// ==========================================
.event-card {
position: relative;
display: flex;
align-items: center;
gap: $s-md;
background: $gradient-hover;
border: 2px solid rgba($primary-color, 0.2);
border-radius: $r-md;
padding: $s-md;
min-width: 280px;
cursor: pointer;
transition: all 0.3s ease;
.body--dark & {
background: $gradient-hover-dark;
border-color: rgba($primary-color-dark, 0.3);
}
// Hover state
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba($primary-color, 0.2);
border-color: rgba($primary-color, 0.4);
.body--dark & {
box-shadow: 0 4px 16px rgba($primary-color-dark, 0.3);
border-color: rgba($primary-color-dark, 0.5);
}
}
// ========== EVENT DATE BADGE ==========
.event-date {
position: absolute;
top: $s-sm;
left: $s-sm;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
background: $gradient-primary;
color: $text-on-primary;
border-radius: $r-sm;
padding: $s-sm;
min-width: 50px;
.body--dark & {
background: $gradient-primary-dark;
}
.event-day {
font-size: 1.5rem;
font-weight: 700;
line-height: 1;
}
.event-month {
font-size: 0.9rem;
font-weight: 600;
text-transform: uppercase;
}
}
// ========== EVENT INFO ==========
.event-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.event-title {
font-size: 1rem;
font-weight: 600;
color: $text-primary;
.body--dark & {
color: $text-primary-dark;
}
}
.event-location {
font-size: 1rem;
color: $text-muted;
display: flex;
align-items: center;
gap: 4px;
.body--dark & {
color: $text-muted-dark;
}
}
}
// ========== EVENT IMAGE ==========
.event-image {
width: 80px;
height: 80px;
border-radius: $r-sm;
flex-shrink: 0;
}
.event-image-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: $gradient-placeholder;
.body--dark & {
background: $gradient-placeholder-dark;
}
}
}

View File

@@ -0,0 +1,198 @@
import type { PropType } from 'vue';
import { defineComponent, onMounted, ref, watch, computed } from 'vue';
import { useUserStore } from '@store/UserStore';
import type { IUserFields } from 'model';
import { IImgGallery, IUserProfile } from 'model';
import { costanti } from '@costanti';
import { shared_consts } from '@/common/shared_vuejs';
import { fieldsTable } from '@store/Modules/fieldsTable';
import { tools } from '@tools';
import { toolsext } from '@store/Modules/toolsext';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { CMyCardPopup } from '@/components/CMyCardPopup';
import { useRouter } from 'vue-router';
import { useCalendarStore } from '@/store/CalendarStore';
import { useGlobalStore } from '@/store/globalStore';
export default defineComponent({
name: 'CMyRecEventi',
components: { CMyCardPopup },
emits: ['setCmd', 'cmdext'],
props: {
table: {
type: String,
required: true,
},
prop_myrec: {
type: Object as PropType<any>,
required: false,
default: null,
},
indexRow: {
type: Number,
required: false,
default: 0,
},
editOn: {
type: Boolean,
required: false,
default: false,
},
margin_right: {
type: Number,
required: false,
default: 0,
},
compatto: {
type: Boolean,
required: false,
default: false,
},
},
setup(props, { emit }) {
const userStore = useUserStore();
const calendarStore = useCalendarStore();
const globalStore = useGlobalStore();
const $q = useQuasar();
const { t } = useI18n();
const $router = useRouter();
const myrec = ref(<any>null);
const showPreferencesDialog = ref(false);
const visupage = ref(false);
const disabilita = computed(() => {
return props.table === shared_consts.TABLES_MYBACHECAS;
});
const arrSubSector = computed(() => {
return tools.getArrSubSector(props.table, myrec.value);
});
const arrSector = computed(() => {
return tools.getArrSector(props.table, myrec.value);
});
const getColorSubSector = computed(() => {
return arrSector.value && arrSector.value.length == 1 ? arrSector.value[0].color : 'primary'
})
watch(
() => props.prop_myrec,
(newval, oldval) => {
mounted();
}
);
function mounted() {
if (props.prop_myrec) {
myrec.value = props.prop_myrec;
}
}
function showBadge() {
if (shared_consts.TABLES_SHOW_ADTYPE.includes(props.table)) {
return true;
}
return false;
}
function getImgUser(profile: IUserFields) {
return userStore.getImgByProfile(profile);
}
function naviga(path: string) {
$router.push(path);
}
function setCmd(
$q: any,
cmd: number,
myusername: string,
value: any,
groupname: string
) {
emit('setCmd', $q, cmd, myusername, value, groupname);
}
function cmdExt(cmd: any, val1: any, val2: any) {
emit('cmdext', cmd, val1, val2);
}
function navigaExt(obj: any) {
cmdExt(costanti.CMD_SHOW_PAGE, null, obj);
//let link = shared_consts.getDirectoryByTable(props.table) + '/' + obj._id
//console.log('link', link)
//$router.push(link)
}
function getNameToShow(user: IUserFields, col = null) {
if (myrec.value.groupname) return myrec.value.groupname;
else return userStore.getNameToShow(user, col);
}
function isPartecipero() {
return (
props.table === shared_consts.TABLES_MYBACHECAS &&
calendarStore.isPartecipero(myrec.value._id, props.table)
);
}
function computedWidth() {
//const width = tools.getwidth($q) - 20;
//return `${Math.min(width, 600)}px`; // Limita la larghezza massima a 600px
const width = tools.getwidth($q);
// Limita la larghezza per evitare overflow
return `${Math.min(width - 40, 600)}px`;
//return '100%'; // Rimuovi la limitazione a 600px
}
function computedEventImageHeight() {
const width = tools.getwidth($q);
const isMobile = width < 768;
const cardWidth = Math.min(width - 20, 600);
if (isMobile) {
// Mobile: aspect ratio 16:9, minimo 200px
const calculatedHeight = Math.round((cardWidth - 70) * 0.5625);
return `${Math.max(calculatedHeight, 200)}px`;
} else {
// Desktop: altezza fissa ottimale 220px
return '220px';
}
}
onMounted(mounted);
return {
t,
myrec,
costanti,
getImgUser,
naviga,
navigaExt,
setCmd,
shared_consts,
userStore,
tools,
toolsext,
fieldsTable,
cmdExt,
visupage,
showBadge,
getNameToShow,
isPartecipero,
calendarStore,
disabilita,
globalStore,
computedWidth,
computedEventImageHeight,
arrSubSector,
arrSector,
getColorSubSector,
showPreferencesDialog,
};
},
});

View File

@@ -0,0 +1,104 @@
<template>
<div
v-if="myrec"
class="event-card"
@click="navigaExt(myrec)"
>
<q-img
:src="
myrec.photos
? tools.getFullFileName(myrec.photos, table, myrec.username, '')
: '/images/event-placeholder.png'
"
:alt="myrec.descr"
class="event-image"
@click="cmdExt(costanti.CMD_SHOW_PAGE, null, myrec)"
>
<template v-slot:error>
<div class="event-image-placeholder">
<q-icon
name="event"
size="2rem"
color="grey-5"
/>
</div>
</template>
</q-img>
<div class="event-date">
<span class="event-day">{{ tools.getstrDay(myrec.dateTimeStart) }}</span>
<span class="event-month">{{
tools.getstrMonth3Letters(myrec.dateTimeStart)
}}</span>
</div>
<div class="event-info">
<span class="event-title">{{ myrec.descr }}</span>
<span class="event-location">
<div class="cities-text">
<span
v-for="(rec, ind) of myrec.mycities"
:key="ind"
class="annuncio-location"
>
<span v-if="ind > 0"></span>
<span
v-if="table === shared_consts.TABLES_MYHOSPS"
class="cities-text-bold"
>{{ rec.comune }} ({{ rec.prov }})</span
>
<span v-else>{{ rec.comune }} ({{ rec.prov }})</span>
</span>
</div>
</span>
</div>
<q-icon name="chevron_right" size="lg" />
</div>
<!-- Categories Dialog -->
<q-dialog v-model="showPreferencesDialog">
<q-card
class="categories-dialog"
:style="{ minWidth: $q.screen.lt.sm ? '90vw' : '400px' }"
>
<q-card-section class="dialog-header">
<div class="text-h6">Categorie</div>
</q-card-section>
<q-separator />
<q-card-section class="dialog-content">
<div class="categories-grid">
<q-chip
v-for="(rec, ind) of myrec.preferences"
:key="ind"
class="category-chip shadow-3"
:style="`background-color: ${tools.getPreferenceById(rec).color}; opacity: 0.85; color: white;`"
>
<q-icon
:name="tools.getPreferenceById(rec).icon"
left
size="18px"
/>
{{ tools.getPreferenceById(rec).label }}
</q-chip>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
flat
label="Chiudi"
color="primary"
v-close-popup
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script lang="ts" src="./CMyRecEventi.ts"></script>
<style lang="scss" scoped>
@import './CMyRecEventi.scss';
</style>

View File

@@ -0,0 +1 @@
export { default as CMyRecEventi } from './CMyRecEventi.vue'

View File

@@ -367,7 +367,7 @@ export default defineComponent({
}
function update() {
console.log('update', props.value, props);
// console.log(' #### mounted myselect', props.options, 'arrvalue', myarrvalue.value)
let rec: any;
if (optionsreal.value) {

View File

@@ -35,7 +35,6 @@ const username = computed(() => {
</script>
<style lang="scss" scoped>
@import 'src/css/variables.scss';
.user-not-found-container {
min-height: calc(100vh - #{$headerHeight});

View File

@@ -378,6 +378,15 @@ export default defineComponent({
});
}
function invio_ris_caricamento() {
$q.loading.show({
message: 'Invio RIS in corso...',
spinnerColor: 'green',
backgroundColor: 'primary',
messageColor: 'white',
});
}
function fine_caricamento() {
$q.loading.hide();
}
@@ -531,7 +540,7 @@ export default defineComponent({
// ═══════════════════════════════════════════════════════════════
// METHODS - Send Mode
// ═══════════════════════════════════════════════════════════════
function sendCoin() {
async function sendCoin() {
const ok =
(to_user_real.value && to_user_real.value.username) ||
(props.to_group && props.to_group.groupname) ||
@@ -563,12 +572,55 @@ export default defineComponent({
myrecsendcoin.dest = to_user_real.value ? to_user_real.value.username : '';
if (myrecsendcoin) {
tools
.sendCoinsByCircuit($q, $router, circuitloaded.value, myrecsendcoin)
.then((ris: any) => {
const orig = myrecsendcoin.grouporig || myrecsendcoin.contoComOrig || '';
const dest =
myrecsendcoin.groupdest || myrecsendcoin.contoComDest || myrecsendcoin.dest;
// Seleziona il messaggio appropriato
const messageKey = orig
? 'circuit.question_sendcoinsto_from'
: 'circuit.question_sendcoinsto';
// Parametri comuni per i messaggi
const msgParams = {
coin: circuitloaded.value.symbol,
dest,
qty: myrecsendcoin.qty,
circuit: circuitloaded.value.name,
...(orig && { from: orig }),
};
// Mostra dialog di conferma
$q.dialog({
title: t('db.domanda'),
message: t(messageKey, msgParams),
ok: {
label: t('dialog.yes'),
color: 'primary',
push: true,
},
cancel: {
label: t('dialog.cancel'),
color: 'secondary',
},
})
.onOk(async () => {
invio_ris_caricamento();
const ris = await tools.sendCoinsByCircuit(
$q,
circuitloaded.value,
myrecsendcoin
);
if (ris) {
showpage.value = false;
}
fine_caricamento()
})
.onCancel(() => {
return false;
})
.onDismiss(() => {
return false;
});
}
}

View File

@@ -222,7 +222,7 @@
</q-expansion-item>
</q-list>
<div class="chart-wrapper">
<div v-if="tools.isAdmin()" class="chart-wrapper">
<CLineChart :mydata="datastat.reg_daily" :title="t('stat.reg_daily')"
color="blue" bordercolor="blue" :sum="true" :showMedia="true" />
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ import { defineComponent, ref, computed, watch, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { CRISBalanceBar } from '@/components/CRISBalanceBar';
import { tools } from '@tools';
import { useUserStore } from 'app/src/store';
import { useCircuitStore, useGlobalStore, useUserStore } from 'app/src/store';
const isTest = true; // Cambia a false in produzione
@@ -15,8 +15,8 @@ export default defineComponent({
// State
const showAnnunciDialog = ref(false);
const showBannScambio = ref(true);
const walletSectionOpen = ref(false);
const selectedCircuit = ref<'provinciale' | 'italia'>('provinciale');
const handshakesView = ref<'mine' | 'all'>('mine');
const transactionsView = ref<'mine' | 'all'>('mine');
@@ -60,12 +60,9 @@ export default defineComponent({
const lastTradeTime = ref('Mai');
const userStore = useUserStore()
const circuitStore = useCircuitStore()
const globalStore = useGlobalStore()
// Organizzazioni
const organizations = ref<any[]>([
// Esempio struttura:
// { initial: 'AS', name: 'Associazione Locale', membersCount: 45 }
]);
// Telegram
const telegramLinks = ref<any[]>([
@@ -78,6 +75,8 @@ export default defineComponent({
const hasOrganizations = computed(() => organizations.value.length > 0);
const hasTelegramLinks = computed(() => telegramLinks.value.length > 0);
const organizations = computed(() => globalStore.getMyGroups())
// ========== METODI DA IMPLEMENTARE ==========
// Dialog Annunci
@@ -193,7 +192,7 @@ export default defineComponent({
const openOrganization = (org: any) => {
// TODO: aprire dettaglio organizzazione
// $router.push(`/groups/${org.id}`)
$router.push(`/grp/${org.groupname}`)
};
const goToAllOrganizations = () => {
@@ -217,296 +216,6 @@ export default defineComponent({
// $router.push('/info')
};
const loadTestData = () => {
if (!isTest) return;
// Circuito Provinciale
circuits.value.provinciale = {
title: 'RIS Provinciale',
availableBalance: 50,
realBalance: -50,
trustLimit: -100,
maxAccumulation: 200,
totalTransactions: 15,
sentCount: 8,
receivedCount: 7,
uniqueMembers: 12,
lastTradeTime: '2 giorni fa',
recentTransactions: [
{ userInitial: 'M', description: 'Ortaggi freschi', time: '2h fa', amount: 15 },
{
userInitial: 'L',
description: 'Lezione chitarra',
time: '5h fa',
amount: -25,
},
{
userInitial: 'G',
description: 'Riparazione bici',
time: '1 giorno fa',
amount: 30,
},
],
};
// Circuito Italia
circuits.value.italia = {
title: 'RIS Italia',
availableBalance: -50,
realBalance: 150,
trustLimit: -200,
maxAccumulation: 400,
totalTransactions: 8,
sentCount: 3,
receivedCount: 5,
uniqueMembers: 7,
lastTradeTime: '5 giorni fa',
recentTransactions: [
{
userInitial: 'A',
description: 'Consulenza legale',
time: '3 giorni fa',
amount: -80,
},
{
userInitial: 'F',
description: 'Prodotti artigianali',
time: '4 giorni fa',
amount: 50,
},
{
userInitial: 'S',
description: 'Corso di cucina',
time: '5 giorni fa',
amount: -40,
},
],
};
// Transazioni community (tutti)
allTransactions.value = [
{
fromInitial: 'M',
toInitial: 'L',
fromName: 'Mario',
toName: 'Laura',
amount: 25,
time: '1h fa',
},
{
fromInitial: 'G',
toInitial: 'C',
fromName: 'Giovanni',
toName: 'Chiara',
amount: 40,
time: '2h fa',
},
{
fromInitial: 'P',
toInitial: 'A',
fromName: 'Paolo',
toName: 'Anna',
amount: 15,
time: '3h fa',
},
];
// Community
invitesSent.value = 7;
myHandshakes.value = [
{ userInitial: 'M', name: 'Mario Rossi', time: '2 giorni fa' },
{ userInitial: 'L', name: 'Laura Bianchi', time: '3 giorni fa' },
{ userInitial: 'G', name: 'Giovanni Verdi', time: '1 settimana fa' },
{ userInitial: 'C', name: 'Chiara Neri', time: '2 settimane fa' },
{ userInitial: 'P', name: 'Paolo Conti', time: '3 settimane fa' },
];
allHandshakes.value = [
{
user1Initial: 'A',
user2Initial: 'B',
user1Name: 'Anna',
user2Name: 'Bruno',
time: '1h fa',
},
{
user1Initial: 'C',
user2Initial: 'D',
user1Name: 'Carlo',
user2Name: 'Diana',
time: '3h fa',
},
{
user1Initial: 'E',
user2Initial: 'F',
user1Name: 'Elena',
user2Name: 'Franco',
time: '5h fa',
},
{
user1Initial: 'G',
user2Initial: 'H',
user1Name: 'Giulia',
user2Name: 'Hugo',
time: '1 giorno fa',
},
{
user1Initial: 'I',
user2Initial: 'L',
user1Name: 'Irene',
user2Name: 'Luca',
time: '2 giorni fa',
},
];
// Eventi test data con immagini
upcomingEventsCount.value = 7;
recentEvents.value = [
{
day: '15',
month: 'DIC',
title: 'Mercatino di Natale',
location: 'Roma Centro',
id: 1,
image: '',
},
{
day: '20',
month: 'DIC',
title: 'Corso di Panificazione',
location: 'Milano',
id: 2,
image: '',
},
{
day: '22',
month: 'DIC',
title: 'Scambio Semi e Piante',
location: 'Bologna',
id: 3,
image: '',
},
{
day: '28',
month: 'DIC',
title: 'Incontro Produttori Locali',
location: 'Firenze',
id: 4,
image: '',
},
{
day: '05',
month: 'GEN',
title: 'Festa della Comunità',
location: 'Torino',
id: 5,
image: '',
},
];
// Scambi test data
recentTrades.value = [
{
userInitial: 'M',
description: 'Scambio ortaggi freschi',
time: '2h fa',
amount: 15,
},
{
userInitial: 'L',
description: 'Lezione di chitarra',
time: '5h fa',
amount: -25,
},
{
userInitial: 'G',
description: 'Riparazione bicicletta',
time: '1 giorno fa',
amount: 30,
},
{
userInitial: 'A',
description: 'Pane fatto in casa',
time: '2 giorni fa',
amount: -10,
},
{
userInitial: 'S',
description: 'Consulenza informatica',
time: '3 giorni fa',
amount: 40,
},
];
// Statistiche test data
totalMembers.value = 342;
totalTrades.value = 1567;
totalEvents.value = 89;
// Organizzazioni test data
organizations.value = [
{ initial: 'AS', name: 'Associazione Scambio Locale', membersCount: 45, id: 1 },
{ initial: 'GP', name: 'Gruppo Produttori Bio', membersCount: 32, id: 2 },
{ initial: 'CE', name: 'Comunità Energetica', membersCount: 28, id: 3 },
{ initial: 'RT', name: 'Rete Tempo', membersCount: 56, id: 4 },
{ initial: 'OC', name: 'Orto Condiviso', membersCount: 23, id: 5 },
{ initial: 'LB', name: 'La Banca del Tempo', membersCount: 41, id: 6 },
{ initial: 'MP', name: 'Mercato Popolare', membersCount: 38, id: 7 },
{ initial: 'CC', name: 'Casa Comune', membersCount: 19, id: 8 },
{ initial: 'SF', name: 'Sharing Food', membersCount: 27, id: 9 },
{ initial: 'EA', name: 'Economia Alternativa', membersCount: 34, id: 10 },
];
// Telegram test data con immagini
telegramLinks.value = [
{
title: 'Canale Principale RISO',
url: 'https://t.me/riso_italia',
image: '/images/telegram-main.jpg',
},
{
title: 'Gruppo Scambi Locali',
url: 'https://t.me/riso_scambi',
image: '/images/telegram-scambi.jpg',
},
{
title: 'Eventi e Incontri',
url: 'https://t.me/riso_eventi',
image: '/images/telegram-eventi.jpg',
},
];
};
// Dati circuiti
const circuits = ref({
provinciale: {
availableBalance: 0,
realBalance: 0,
trustLimit: -100,
maxAccumulation: 200,
totalTransactions: 0,
sentCount: 0,
receivedCount: 0,
uniqueMembers: 0,
lastTradeTime: 'Mai',
recentTransactions: [] as any[],
title: 'RIS Provinciale',
},
italia: {
availableBalance: 0,
realBalance: 0,
trustLimit: -200,
maxAccumulation: 400,
totalTransactions: 0,
sentCount: 0,
receivedCount: 0,
uniqueMembers: 0,
lastTradeTime: 'Mai',
recentTransactions: [] as any[],
title: 'RIS Italia',
},
});
// Dati community
const invitesSent = ref(0);
const myHandshakes = ref<any[]>([]);
@@ -516,11 +225,6 @@ export default defineComponent({
const activeCircuitsCount = ref(2);
const totalCircuitsAvailable = ref(12);
// Computed per circuito corrente
const currentCircuitData = computed(() => {
return circuits.value[selectedCircuit.value];
});
// Metodi
const openTransaction = (tx: any) => {
// TODO: aprire dettaglio transazione
@@ -535,7 +239,7 @@ export default defineComponent({
// $router.push('/circuits')
};
loadTestData();
//loadTestData();
onMounted(() => {
showBannScambio.value = tools.getCookieBool('bann_scambio', true);
@@ -605,10 +309,7 @@ export default defineComponent({
uniqueMembers,
lastTradeTime,
goToCircuits,
selectedCircuit,
handshakesView,
circuits,
currentCircuitData,
invitesSent,
myHandshakes,
allHandshakes,
@@ -622,6 +323,8 @@ export default defineComponent({
transactionsView,
allTransactions,
userStore,
walletSectionOpen,
circuitStore,
};
},
});

View File

@@ -1,614 +1,168 @@
<template>
<div class="riso-modern-home">
<!-- Hero Section: Card Principali -->
<!-- Header -->
<header class="riso-header">
<div class="header-left">
<q-avatar
size="44px"
class="header-avatar"
>
<span class="header-avatar-text">
{{ (userStore.my.username || 'R').slice(0, 1).toUpperCase() }}
</span>
</q-avatar>
<div class="header-text">
<div class="header-greeting">
Ciao, <span class="u-strong">{{ userStore.my.username }}</span>
</div>
<div class="header-sub">Rete Italiana Scambio Orizzontale</div>
</div>
</div>
<div class="header-actions">
<q-btn
flat
round
dense
icon="help_outline"
class="header-action-btn"
aria-label="Guida"
@click="openGuide"
/>
</div>
</header>
<!-- Hero Section: Azioni principali -->
<section class="hero-cards">
<!-- Card Annunci -->
<!-- Annunci -->
<div
class="hero-card gradient-primary"
role="button"
tabindex="0"
v-ripple
@click="openAnnunciDialog"
@keyup.enter="openAnnunciDialog"
@keyup.space.prevent="openAnnunciDialog"
>
<q-icon
name="campaign"
size="2.5rem"
/>
<span class="card-title">Annunci</span>
<span class="card-subtitle">Beni, Servizi, Ospitalità</span>
<div class="hero-card-top">
<div class="hero-icon">
<q-icon
name="campaign"
size="1.6rem"
/>
</div>
<q-icon
name="chevron_right"
size="sm"
class="hero-chevron"
/>
</div>
<div class="hero-card-body">
<div class="card-title">Annunci</div>
<div class="card-subtitle">Beni · Servizi · Ospitalità</div>
</div>
</div>
<!-- Card Circuiti RIS -->
<!-- Circuiti -->
<div
class="hero-card gradient-success"
class="hero-card gradient-secondary"
role="button"
tabindex="0"
v-ripple
@click="goToCircuits"
@keyup.enter="goToCircuits"
@keyup.space.prevent="goToCircuits"
>
<q-icon
name="fas fa-coins"
size="2.5rem"
/>
<span class="card-title">Circuiti RIS</span>
<span class="card-subtitle">Gestisci i tuoi RIS</span>
<div class="hero-card-top">
<div class="hero-icon">
<q-img
src="/images/1ris_rosso_100.png"
width="1.6rem"
/>
</div>
<q-badge
class="hero-badge"
color="white"
text-color="primary"
rounded
>
{{ circuitStore.quantiCircuitiSonoDentro() }}
</q-badge>
</div>
<div class="hero-card-body">
<div class="card-title">Circuiti RIS</div>
<div class="card-subtitle">Saldi · Limiti · Movimenti</div>
</div>
</div>
<!-- Card Eventi -->
<!-- Eventi -->
<div
class="hero-card gradient-accent"
role="button"
tabindex="0"
v-ripple
@click="goToEvents"
@keyup.enter="goToEvents"
@keyup.space.prevent="goToEvents"
>
<q-icon
name="event"
size="2.5rem"
/>
<span class="card-title">Eventi</span>
<span class="card-subtitle">{{ upcomingEventsCount }} prossimi</span>
<div class="hero-card-top">
<div class="hero-icon">
<q-icon
name="event"
size="1.6rem"
/>
</div>
<!--<q-badge
class="hero-badge"
color="white"
text-color="primary"
rounded
>
{{ upcomingEventsCount }}
</q-badge>-->
</div>
<div class="hero-card-body">
<div class="card-title">Eventi</div>
<div class="card-subtitle">I prossimi nella rete</div>
</div>
</div>
<!-- Card Profilo -->
<!-- Profilo -->
<div
class="hero-card gradient-orange"
role="button"
tabindex="0"
v-ripple
@click="goToProfile"
@keyup.enter="goToProfile"
@keyup.space.prevent="goToProfile"
>
<q-icon
name="account_circle"
size="2.5rem"
/>
<span class="card-title">Il mio Profilo</span>
<span class="card-subtitle text-italic">({{ userStore.my.username }})</span>
</div>
</section>
<!-- Azioni RIS -->
<section class="ris-actions-section">
<div class="ris-actions-grid">
<q-btn
unelevated
rounded
class="ris-action-btn send-btn"
icon="arrow_upward"
label="Invia RIS"
@click="sendRIS"
/>
<q-btn
unelevated
rounded
class="ris-action-btn receive-btn"
icon="arrow_downward"
label="Ricevi RIS"
@click="receiveRIS"
/>
</div>
</section>
<!-- Wallet Card Dettagliata con 2 Circuiti -->
<section class="wallet-section">
<div class="wallet-card">
<div class="wallet-header">
<h2 class="section-title-white">
<q-icon name="fas fa-coins" />
I Tuoi Scambi in RIS
</h2>
<q-btn
flat
dense
round
icon="refresh"
text-color="white"
@click="refreshWallet"
/>
</div>
<!-- Saldi Entrambi Circuiti -->
<div class="circuits-balance-overview">
<div class="circuit-balance-mini">
<CRISBalanceBar
:current-balance="circuits.provinciale.realBalance"
:min-limit="circuits.provinciale.trustLimit"
:max-limit="circuits.provinciale.maxAccumulation"
:label="circuits.provinciale.title"
small
/>
</div>
<div class="circuit-balance-mini">
<CRISBalanceBar
:current-balance="circuits.italia.realBalance"
:min-limit="circuits.italia.trustLimit"
:max-limit="circuits.italia.maxAccumulation"
:label="circuits.italia.title"
small
/>
</div>
</div>
</div>
<div class="wallet-card">
<!-- Selettore Circuito -->
<div class="circuit-selector">
<div class="selector-label">
<span class="selector-emoji">👉🏻</span>
<span class="selector-text">Seleziona il Circuito</span>
</div>
<q-btn-toggle
v-model="selectedCircuit"
dense
no-caps
unelevated
rounded
toggle-color="white"
toggle-text-color="primary"
:options="[
{ label: 'RIS Provinciale', value: 'provinciale' },
{ label: 'RIS Italia', value: 'italia' },
]"
class="circuit-toggle"
/>
</div>
<!-- Statistiche transazioni del circuito selezionato -->
<div class="wallet-transactions">
<div class="transaction-stat total">
<div class="hero-card-top">
<div class="hero-icon">
<q-icon
name="repeat"
size="sm"
name="account_circle"
size="1.6rem"
/>
<span class="stat-number">{{ currentCircuitData.totalTransactions }}</span>
<span class="stat-label">Transazioni</span>
</div>
<div class="transaction-stat members">
<q-icon
name="people"
size="sm"
/>
<span class="stat-number">{{ currentCircuitData.uniqueMembers }}</span>
<span class="stat-label">Membri coinvolti</span>
</div>
<div class="transaction-stat sent">
<q-icon
name="arrow_upward"
size="sm"
/>
<span class="stat-number">{{ currentCircuitData.sentCount }}</span>
<span class="stat-label">Inviate</span>
</div>
<div class="transaction-stat received">
<q-icon
name="arrow_downward"
size="sm"
/>
<span class="stat-number">{{ currentCircuitData.receivedCount }}</span>
<span class="stat-label">Ricevute</span>
</div>
</div>
</div>
<div
v-if="showBannScambio"
class="avviso-card"
>
<div class="wallet-activity-focus">
<q-btn
flat
dense
round
icon="close"
class="close-btn"
@click="
showBannScambio = false;
tools.setCookie('bann_scambio', '0');
"
/>
<div class="activity-message">
<q-icon
name="people_outline"
size="lg"
/>
<span class="activity-text"
>Continua a scambiare con più persone per far crescere la tua
comunità!</span
>
</div>
</div>
</div>
</section>
<!-- Prossimi Eventi -->
<section
v-if="hasEvents"
class="content-section"
>
<div class="section-header">
<h2 class="section-title">
<q-icon name="event" />
Prossimi Eventi
</h2>
<q-btn
flat
dense
round
icon="arrow_forward"
@click="goToAllEvents"
/>
</div>
<div
v-for="(event, idx) in recentEvents"
:key="idx"
class="event-card"
@click="openEvent(event)"
>
<q-img
:src="event.image || '/images/event-placeholder.png'"
class="event-image"
ratio="1"
>
<template
v-if="!event.image"
v-slot:error
>
<div class="event-image-placeholder">
<q-icon
name="event"
size="2rem"
color="grey-5"
/>
</div>
</template>
</q-img>
<div class="event-date">
<span class="event-day">{{ event.day }}</span>
<span class="event-month">{{ event.month }}</span>
</div>
<div class="event-info">
<span class="event-title">{{ event.title }}</span>
<span class="event-location">{{ event.location }}</span>
</div>
<q-icon name="chevron_right" />
</div>
</section>
<!-- Organizzazioni -->
<section
v-if="hasOrganizations"
class="content-section"
>
<div class="section-header">
<h2 class="section-title">
<q-icon name="fas fa-users" />
Organizzazioni
</h2>
<q-btn
flat
dense
round
icon="arrow_forward"
@click="goToAllOrganizations"
/>
</div>
<div class="org-scroll">
<div
v-for="(org, idx) in organizations.slice(0, 10)"
:key="idx"
class="org-card-scroll"
@click="openOrganization(org)"
>
<q-avatar
size="50px"
color="blue-6"
text-color="white"
>
{{ org.initial }}
</q-avatar>
<span class="org-name">{{ org.name }}</span>
<span class="org-members">{{ org.membersCount }} membri</span>
</div>
</div>
</section>
<!-- Circuiti RIS Overview -->
<!--<section class="circuits-overview-section">
<div class="section-header">
<h2 class="section-title">
<q-icon name="account_balance" />
Circuiti RIS
</h2>
</div>
<div class="circuits-cards">
<div class="circuit-info-card">
<q-icon
name="check_circle"
color="positive"
size="lg"
/>
<span class="circuit-stat-number">{{ activeCircuitsCount }}</span>
<span class="circuit-stat-label">Circuiti Attivi</span>
</div>
<div class="circuit-info-card">
<q-icon
name="public"
color="primary"
size="lg"
/>
<span class="circuit-stat-number">{{ totalCircuitsAvailable }}</span>
<span class="circuit-stat-label">Circuiti Disponibili</span>
</div>
</div>
<q-btn
unelevated
rounded
class="view-all-btn"
label="Esplora tutti i Circuiti"
icon-right="arrow_forward"
color="primary"
@click="goToAllCircuits"
/>
</section>
-->
<!-- Community Actions -->
<section class="community-actions-section">
<h2 class="section-title">
<q-icon name="people" />
Community
</h2>
<div class="invite-banner">
<div class="invite-message">
<q-icon
name="campaign"
size="md"
color="positive"
/>
<div class="invite-text">
<span class="invite-main"
>Aiuta la community a crescere, parla di RISO ed invita le persone alla
App</span
>
<span class="invite-count"
>Hai già inviato <strong>{{ invitesSent }}</strong> inviti</span
>
</div>
</div>
</div>
<div class="community-actions-grid">
<q-btn
unelevated
rounded
class="community-action-btn"
icon="person_add"
label="Invita Amici"
color="positive"
@click="inviteFriend"
/>
<q-btn
unelevated
rounded
class="community-action-btn"
icon="list"
label="Lista Iscritti"
color="info"
@click="showMembers"
/>
</div>
</section>
<!-- Attività Community
<section class="community-section">
<div class="community-card">
<div class="community-header">
<h3 class="community-title">
<q-icon name="handshake" />
Strette di Mano
</h3>
<q-btn-toggle
v-model="handshakesView"
dense
no-caps
unelevated
rounded
name="chevron_right"
size="sm"
toggle-color="primary"
:options="[
{ label: 'Tue', value: 'mine' },
{ label: 'Generali', value: 'all' },
]"
class="hero-chevron"
/>
</div>
<div
v-if="handshakesView === 'mine'"
class="activity-list"
>
<div
v-for="(handshake, idx) in myHandshakes.slice(0, 5)"
:key="idx"
class="activity-item"
@click="openHandshake(handshake)"
>
<q-avatar
size="40px"
color="purple-6"
text-color="white"
>
{{ handshake.userInitial }}
</q-avatar>
<div class="activity-content">
<span class="activity-title">{{ handshake.name }}</span>
<span class="activity-time">Stretta di mano {{ handshake.time }}</span>
</div>
<q-icon
name="handshake"
color="purple-6"
/>
</div>
</div>
<div
v-if="handshakesView === 'all'"
class="activity-list"
>
<div
v-for="(handshake, idx) in allHandshakes.slice(0, 5)"
:key="idx"
class="activity-item"
@click="openHandshake(handshake)"
>
<div class="handshake-avatars">
<q-avatar
size="32px"
color="purple-6"
text-color="white"
>
{{ handshake.user1Initial }}
</q-avatar>
<q-avatar
size="32px"
color="purple-4"
text-color="white"
class="avatar-overlap"
>
{{ handshake.user2Initial }}
</q-avatar>
</div>
<div class="activity-content">
<span class="activity-title"
>{{ handshake.user1Name }} e {{ handshake.user2Name }}</span
>
<span class="activity-time">{{ handshake.time }}</span>
</div>
<q-icon
name="handshake"
color="purple-6"
/>
</div>
<div class="hero-card-body">
<div class="card-title">Il mio profilo</div>
<div class="card-subtitle text-italic">@{{ userStore.my.username }}</div>
</div>
</div>
-->
<!-- Ultimi Scambi -->
<!--<div class="community-card">
<div class="community-header">
<h3 class="community-title">
<q-icon name="swap_horiz" />
Ultimi Scambi
</h3>
<q-btn
flat
dense
round
icon="arrow_forward"
@click="goToAllTrades"
/>
</div>
<div class="activity-list">
<div
v-for="(trade, idx) in recentTrades.slice(0, 3)"
:key="idx"
class="activity-item"
@click="openTrade(trade)"
>
<q-avatar
size="40px"
color="primary"
text-color="white"
>
{{ trade.userInitial }}
</q-avatar>
<div class="activity-content">
<span class="activity-title">{{ trade.description }}</span>
<span class="activity-time">{{ trade.time }}</span>
</div>
<div class="activity-amount">
<span :class="trade.amount > 0 ? 'positive' : 'negative'">
{{ trade.amount > 0 ? '+' : '' }}{{ trade.amount }} RIS
</span>
</div>
</div>
</div>
</div>
</section>-->
<!-- Canali Telegram -->
<!--<section
v-if="hasTelegramLinks"
class="content-section"
>
<div class="section-header">
<h2 class="section-title">
<q-icon name="telegram" />
Unisciti ai Canali
</h2>
</div>
<div class="telegram-grid-modern">
<a
v-for="(link, idx) in telegramLinks.slice(0, 3)"
:key="idx"
:href="link.url"
target="_blank"
class="telegram-card-modern"
>
<q-img
v-if="link.image"
:src="link.image"
class="telegram-card-image"
ratio="16/9"
>
<div class="telegram-overlay">
<q-icon
name="telegram"
size="3rem"
color="white"
/>
</div>
</q-img>
<div
v-else
class="telegram-icon-wrapper"
>
<q-icon
name="telegram"
size="3rem"
color="white"
/>
</div>
<div class="telegram-content-modern">
<span class="telegram-title">{{ link.title }}</span>
<q-icon
name="open_in_new"
size="sm"
/>
</div>
</a>
</div>
</section>
-->
<!-- Footer Links -->
<section class="footer-section-modern">
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="help_outline"
label="FAQ"
color="primary"
@click="openFAQ"
/>
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="school"
label="Guida"
color="secondary"
@click="openGuide"
/>
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="support_agent"
label="Assistenza"
color="positive"
@click="openInfo"
/>
</section>
<!-- Dialog Annunci -->
@@ -619,65 +173,130 @@
>
<q-card class="annunci-dialog">
<q-card-section class="dialog-header">
<h3 class="dialog-title">Scegli Categoria</h3>
<q-btn
flat
round
dense
icon="close"
v-close-popup
/>
</q-card-section>
<q-card-section class="dialog-content">
<div class="annunci-options-mobile">
<div
class="annuncio-option gradient-indigo"
@click="goToGoods"
>
<q-icon
name="fas fa-tshirt"
size="2.5rem"
/>
<span class="option-title">Beni</span>
<span class="option-subtitle">Autoproduzioni, cibo</span>
</div>
<div
class="annuncio-option gradient-red"
@click="goToServices"
>
<q-icon
name="fas fa-house-user"
size="2.5rem"
/>
<span class="option-title">Servizi</span>
<span class="option-subtitle">Competenze, aiuti</span>
</div>
<div
class="annuncio-option gradient-lime"
@click="goToHospitality"
>
<q-icon
name="fas fa-bed"
size="2.5rem"
/>
<span class="option-title">Ospitalità</span>
<span class="option-subtitle">Ospitare viaggiatori</span>
</div>
<div class="annuncio-option gradient-teal">
<q-icon
name="directions_car"
size="2.5rem"
/>
<span class="option-title">Trasporti</span>
<span class="option-subtitle">Condivisione viaggi</span>
<span class="option-subtitle"> (IN ARRIVO...)</span>
</div>
<div class="dialog-title-row">
<h3 class="dialog-title">Scegli categoria</h3>
<q-btn
flat
round
dense
icon="close"
aria-label="Chiudi"
@click="showAnnunciDialog = false"
/>
</div>
<div class="dialog-subtitle">Apri la sezione giusta in un tap.</div>
</q-card-section>
<q-separator />
<q-list class="annunci-list">
<q-item
clickable
v-ripple
class="annunci-item gradient-green"
@click="goToGoods"
>
<q-item-section avatar>
<div class="annunci-icon">
<q-icon
name="fas fa-tshirt"
size="1.4rem"
/>
</div>
</q-item-section>
<q-item-section>
<q-item-label class="option-title">Beni</q-item-label>
<q-item-label
caption
class="option-subtitle"
>
Autoproduzioni · Cibo · Oggetti
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon name="chevron_right" />
</q-item-section>
</q-item>
<q-item
clickable
v-ripple
class="annunci-item gradient-red"
@click="goToServices"
>
<q-item-section avatar>
<div class="annunci-icon">
<q-icon
name="fas fa-house-user"
size="1.4rem"
/>
</div>
</q-item-section>
<q-item-section>
<q-item-label class="option-title">Servizi</q-item-label>
<q-item-label
caption
class="option-subtitle"
>
Competenze · Aiuti · Consulenze
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon name="chevron_right" />
</q-item-section>
</q-item>
<q-item
clickable
v-ripple
class="annunci-item gradient-lime"
@click="goToHospitality"
>
<q-item-section avatar>
<div class="annunci-icon">
<q-icon
name="fas fa-bed"
size="1.4rem"
/>
</div>
</q-item-section>
<q-item-section>
<q-item-label class="option-title">Ospitalità</q-item-label>
<q-item-label
caption
class="option-subtitle"
>
Ospitare · Viaggi · Accoglienza
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon name="chevron_right" />
</q-item-section>
</q-item>
<q-item
class="annunci-item gradient-blue disabled"
disable
>
<q-item-section avatar>
<div class="annunci-icon">
<q-icon
name="commute"
size="1.4rem"
/>
</div>
</q-item-section>
<q-item-section>
<q-item-label class="option-title">Trasporti</q-item-label>
<q-item-label
caption
class="option-subtitle"
>
In arrivo
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card>
</q-dialog>
</div>

View File

@@ -0,0 +1,375 @@
/* =========================================================
RISO Home (parte finale) — refresh estetico coerente
========================================================= */
.riso-modern-home {
padding: $s-md;
padding-bottom: calc(#{$s-md} + env(safe-area-inset-bottom));
max-width: 1200px;
margin: 0 auto;
min-height: 100vh;
display: flex;
flex-direction: column;
gap: $s-md;
}
/* ---- Section shell (glass card) ---- */
.content-section,
.community-actions-section,
.footer-section-modern {
border-radius: $r-lg;
padding: $s-md;
background: rgba(255, 255, 255, 0.72);
border: 1px solid rgba(255, 255, 255, 0.55);
box-shadow: 0 14px 40px rgba(16, 24, 40, 0.08);
backdrop-filter: blur(12px);
}
:global(body.body--dark) {
.content-section,
.community-actions-section,
.footer-section-modern {
background: rgba(16, 24, 40, 0.55);
border: 1px solid rgba(255, 255, 255, 0.10);
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35);
}
}
/* ---- Titles ---- */
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: $s-sm;
margin-bottom: $s-sm;
}
.section-title {
font-size: 1.12rem;
font-weight: 900;
margin: 0;
display: flex;
align-items: center;
gap: $s-sm;
letter-spacing: 0.01em;
/* gradient text */
background: $gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
.q-icon {
background: $gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
/* ---- Organizations: horizontal scroll ---- */
.org-scroll {
display: flex;
gap: $s-sm;
overflow-x: auto;
padding-bottom: 4px;
scroll-snap-type: x mandatory;
/* smoother */
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
height: 8px;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.12);
border-radius: 999px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
}
:global(body.body--dark) {
.org-scroll::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.16);
}
}
.org-card-scroll {
min-width: 190px;
max-width: 240px;
padding: $s-sm;
border-radius: $r-lg;
cursor: pointer;
scroll-snap-align: start;
display: grid;
grid-template-columns: 54px 1fr;
gap: $s-sm;
align-items: center;
background: rgba(255, 255, 255, 0.86);
border: 1px solid rgba(0, 0, 0, 0.06);
transition: transform 140ms ease, box-shadow 140ms ease, filter 140ms ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 16px 36px rgba(16, 24, 40, 0.14);
filter: saturate(1.04);
}
&:active {
transform: translateY(0px) scale(0.99);
}
&:focus-visible {
outline: 3px solid rgba(102, 126, 234, 0.35);
outline-offset: 2px;
}
.org-info {
min-width: 0;
display: grid;
gap: 2px;
}
.org-name {
font-weight: 900;
font-size: 0.95rem;
line-height: 1.15;
opacity: 0.92;
/* show full titles (no truncation) */
white-space: normal;
overflow: visible;
text-overflow: unset;
word-break: break-word;
}
.org-subtitle {
font-size: 0.82rem;
opacity: 0.72;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
:global(body.body--dark) {
.org-card-scroll {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.10);
&:hover {
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.40);
}
.org-subtitle {
opacity: 0.84;
}
}
}
.org-card-scroll.see-more-card {
grid-template-columns: 1fr;
place-items: center;
text-align: center;
background: $gradient-primary;
border: 1px solid rgba(255, 255, 255, 0.18);
color: white;
.see-more-circle {
width: 54px;
height: 54px;
border-radius: 18px;
display: grid;
place-items: center;
background: rgba(255, 255, 255, 0.18);
border: 1px solid rgba(255, 255, 255, 0.20);
margin-bottom: 6px;
}
.see-more-count {
font-weight: 900;
letter-spacing: 0.02em;
}
.org-name {
color: white;
}
}
/* Small helper text under scroll */
.org-scroll-hint {
display: flex;
align-items: center;
gap: $s-xs;
font-size: 0.86rem;
opacity: 0.72;
margin-top: $s-sm;
}
/* ---- Community invite banner ---- */
.invite-banner {
display: flex;
justify-content: space-between;
align-items: center;
gap: $s-sm;
padding: $s-md;
border-radius: $r-lg;
background: linear-gradient(135deg, rgba(34, 197, 94, 0.14), rgba(16, 185, 129, 0.10));
border: 1px solid rgba(34, 197, 94, 0.22);
}
:global(body.body--dark) {
.invite-banner {
background: linear-gradient(135deg, rgba(34, 197, 94, 0.18), rgba(16, 185, 129, 0.12));
border: 1px solid rgba(34, 197, 94, 0.22);
}
}
.invite-message {
display: flex;
align-items: flex-start;
gap: $s-sm;
.invite-text {
display: grid;
gap: 4px;
}
.invite-main {
font-weight: 800;
line-height: 1.25;
}
.invite-count {
font-size: 0.86rem;
opacity: 0.78;
}
}
/* ---- Telegram ---- */
.telegram-grid-modern {
display: grid;
grid-template-columns: 1fr;
gap: $s-md;
@media (min-width: 700px) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.telegram-card-modern {
border-radius: $r-lg;
overflow: hidden;
text-decoration: none;
display: block;
color: inherit;
position: relative;
transition: transform 140ms ease, box-shadow 140ms ease, filter 140ms ease;
background: rgba(255, 255, 255, 0.86);
border: 1px solid rgba(0, 0, 0, 0.06);
box-shadow: 0 10px 26px rgba(16, 24, 40, 0.10);
&:hover {
transform: translateY(-2px);
box-shadow: 0 16px 36px rgba(16, 24, 40, 0.14);
filter: saturate(1.03);
}
&:active {
transform: translateY(0px) scale(0.99);
}
&:focus-visible {
outline: 3px solid rgba(59, 130, 246, 0.25);
outline-offset: 2px;
}
}
:global(body.body--dark) {
.telegram-card-modern {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.10);
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.35);
&:hover {
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
}
}
}
.telegram-card-image {
border-radius: 0;
}
.telegram-overlay {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.32), rgba(14, 165, 233, 0.22));
}
.telegram-icon-wrapper {
height: 130px;
display: grid;
place-items: center;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.18), rgba(14, 165, 233, 0.12));
}
.telegram-card-content {
padding: $s-md;
display: grid;
gap: 4px;
.telegram-card-title {
font-weight: 900;
font-size: 1.02rem;
line-height: 1.2;
}
.telegram-card-subtitle {
font-size: 0.86rem;
opacity: 0.74;
line-height: 1.25;
}
}
/* ---- Footer ---- */
.footer-section-modern {
display: grid;
gap: $s-sm;
@media (min-width: 700px) {
grid-template-columns: repeat(3, minmax(0, 1fr));
align-items: stretch;
}
}
.footer-btn-modern {
width: 100%;
border-radius: 16px;
font-weight: 800;
letter-spacing: 0.01em;
text-transform: none;
background: rgba(255, 255, 255, 0.86);
border: 1px solid rgba(0, 0, 0, 0.06);
&:hover {
filter: saturate(1.03);
}
}
:global(body.body--dark) {
.footer-btn-modern {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.10);
}
}

View File

@@ -0,0 +1,614 @@
import { defineComponent, ref, computed, watch, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { CRISBalanceBar } from '@/components/CRISBalanceBar';
import { tools } from '@tools';
import { useGlobalStore, useUserStore } from 'app/src/store';
const isTest = true; // Cambia a false in produzione
export default defineComponent({
name: 'Riso_Home_ParteFinale',
components: { CRISBalanceBar },
setup() {
const $router = useRouter();
// State
const showAnnunciDialog = ref(false);
const showBannScambio = ref(true);
const walletSectionOpen = ref(false);
const selectedCircuit = ref<'provinciale' | 'italia'>('provinciale');
const handshakesView = ref<'mine' | 'all'>('mine');
const transactionsView = ref<'mine' | 'all'>('mine');
// Transazioni globali community
const allTransactions = ref<any[]>([]);
// Dati wallet (da collegare allo store)
const availableBalance = ref(0); // Saldo utilizzabile (include fido)
const realBalance = ref(0); // Saldo reale effettivo
const trustLimit = ref(-100); // Fido concesso (negativo)
const maxAccumulation = ref(200); // Massimo accumulo
const receivedCount = ref(0);
const sentCount = ref(0);
// Dati eventi (da collegare allo store)
const upcomingEventsCount = ref(0);
const recentEvents = ref<any[]>([
// Esempio struttura:
// { day: '15', month: 'DIC', title: 'Mercatino', location: 'Roma' }
]);
// Dati attività (da collegare allo store)
const recentTrades = ref<any[]>([
// Esempio struttura:
// { userInitial: 'M', description: 'Scambio completato', time: '2h fa', amount: 50 }
]);
const recentConnections = ref<any[]>([
// Esempio struttura:
// { userInitial: 'A', name: 'Mario Rossi', time: '1 giorno fa' }
]);
// Stats
const totalMembers = ref(0);
const totalTrades = ref(0);
const totalEvents = ref(0);
const totalTransactions = ref(0);
const uniqueMembers = ref(0);
const lastTradeTime = ref('Mai');
const userStore = useUserStore()
const globalStore = useGlobalStore()
// Telegram
const telegramLinks = ref<any[]>([
// Esempio struttura:
// { title: 'Canale Principale', url: 'https://t.me/...', image: '/path/to/img' }
]);
// Computed
const hasEvents = computed(() => recentEvents.value.length > 0);
const hasOrganizations = computed(() => organizations.value.length > 0);
const hasTelegramLinks = computed(() => telegramLinks.value.length > 0);
const organizations = computed(() => globalStore.getMyGroups())
// ========== METODI DA IMPLEMENTARE ==========
// Dialog Annunci
const openAnnunciDialog = () => {
showAnnunciDialog.value = true;
};
// Navigazione Annunci
const goToGoods = () => {
showAnnunciDialog.value = false;
$router.push('/goods')
};
const goToServices = () => {
showAnnunciDialog.value = false;
$router.push('/services')
};
const goToHospitality = () => {
showAnnunciDialog.value = false;
$router.push('/hosps')
};
const goToTransport = () => {
showAnnunciDialog.value = false;
// TODO: navigare a /transport (da creare?)
// $router.push('/transport')
};
// Hero Cards
const goToWallet = () => {
// TODO: navigare al portafoglio dettagliato
// $router.push('/wallet')
};
const goToCircuits = () => {
// TODO: navigare ai circuiti RIS
$router.push('/circuits')
};
const goToEvents = () => {
// TODO: navigare alla lista eventi
$router.push('/events')
};
const goToProfile = () => {
// TODO: navigare al profilo utente
$router.push('/my/' + userStore.my.username)
};
// Azioni Rapide
const sendRIS = () => {
// TODO: aprire dialog/pagina invio RIS
};
const receiveRIS = () => {
// TODO: aprire dialog/pagina ricezione RIS
};
const inviteFriend = () => {
// TODO: aprire dialog/pagina invito amico
};
const showMembers = () => {
// TODO: navigare alla lista iscritti
// $router.push('/members')
};
// Wallet
const refreshWallet = () => {
// TODO: ricaricare dati wallet dallo store/API
};
const goToTransactions = () => {
// TODO: navigare alla lista transazioni
// $router.push('/transactions')
};
// Eventi
const goToAllEvents = () => {
// TODO: navigare a tutti gli eventi
$router.push('/events')
};
const openEvent = (event: any) => {
// TODO: aprire dettaglio evento
// $router.push(`/events/${event.id}`)
};
// Community
const openTrade = (trade: any) => {
// TODO: aprire dettaglio scambio
};
const openConnection = (connection: any) => {
// TODO: aprire profilo utente connesso
};
const goToAllTrades = () => {
// TODO: navigare a lista completa scambi
// $router.push('/trades')
};
const goToAllConnections = () => {
// TODO: navigare a lista completa connessioni
// $router.push('/connections')
};
// Organizzazioni
const createOrganization = () => {
// TODO: aprire form creazione organizzazione
};
const openOrganization = (org: any) => {
// TODO: aprire dettaglio organizzazione
$router.push(`/grp/${org.groupname}`)
};
const goToAllOrganizations = () => {
// TODO: navigare a lista completa organizzazioni
$router.push('/groups')
};
// Footer
const openFAQ = () => {
// TODO: navigare a FAQ
$router.push('/faq_ris')
};
const openGuide = () => {
// TODO: navigare a guida
$router.push('/guida')
};
const openInfo = () => {
// TODO: navigare a info
// $router.push('/info')
};
const loadTestData = () => {
if (!isTest) return;
// Circuito Provinciale
circuits.value.provinciale = {
title: 'RIS Provinciale',
availableBalance: 50,
realBalance: -50,
trustLimit: -100,
maxAccumulation: 200,
totalTransactions: 15,
sentCount: 8,
receivedCount: 7,
uniqueMembers: 12,
lastTradeTime: '2 giorni fa',
recentTransactions: [
{ userInitial: 'M', description: 'Ortaggi freschi', time: '2h fa', amount: 15 },
{
userInitial: 'L',
description: 'Lezione chitarra',
time: '5h fa',
amount: -25,
},
{
userInitial: 'G',
description: 'Riparazione bici',
time: '1 giorno fa',
amount: 30,
},
],
};
// Circuito Italia
circuits.value.italia = {
title: 'RIS Italia',
availableBalance: -50,
realBalance: 150,
trustLimit: -200,
maxAccumulation: 400,
totalTransactions: 8,
sentCount: 3,
receivedCount: 5,
uniqueMembers: 7,
lastTradeTime: '5 giorni fa',
recentTransactions: [
{
userInitial: 'A',
description: 'Consulenza legale',
time: '3 giorni fa',
amount: -80,
},
{
userInitial: 'F',
description: 'Prodotti artigianali',
time: '4 giorni fa',
amount: 50,
},
{
userInitial: 'S',
description: 'Corso di cucina',
time: '5 giorni fa',
amount: -40,
},
],
};
// Transazioni community (tutti)
allTransactions.value = [
{
fromInitial: 'M',
toInitial: 'L',
fromName: 'Mario',
toName: 'Laura',
amount: 25,
time: '1h fa',
},
{
fromInitial: 'G',
toInitial: 'C',
fromName: 'Giovanni',
toName: 'Chiara',
amount: 40,
time: '2h fa',
},
{
fromInitial: 'P',
toInitial: 'A',
fromName: 'Paolo',
toName: 'Anna',
amount: 15,
time: '3h fa',
},
];
// Community
invitesSent.value = 7;
myHandshakes.value = [
{ userInitial: 'M', name: 'Mario Rossi', time: '2 giorni fa' },
{ userInitial: 'L', name: 'Laura Bianchi', time: '3 giorni fa' },
{ userInitial: 'G', name: 'Giovanni Verdi', time: '1 settimana fa' },
{ userInitial: 'C', name: 'Chiara Neri', time: '2 settimane fa' },
{ userInitial: 'P', name: 'Paolo Conti', time: '3 settimane fa' },
];
allHandshakes.value = [
{
user1Initial: 'A',
user2Initial: 'B',
user1Name: 'Anna',
user2Name: 'Bruno',
time: '1h fa',
},
{
user1Initial: 'C',
user2Initial: 'D',
user1Name: 'Carlo',
user2Name: 'Diana',
time: '3h fa',
},
{
user1Initial: 'E',
user2Initial: 'F',
user1Name: 'Elena',
user2Name: 'Franco',
time: '5h fa',
},
{
user1Initial: 'G',
user2Initial: 'H',
user1Name: 'Giulia',
user2Name: 'Hugo',
time: '1 giorno fa',
},
{
user1Initial: 'I',
user2Initial: 'L',
user1Name: 'Irene',
user2Name: 'Luca',
time: '2 giorni fa',
},
];
// Eventi test data con immagini
upcomingEventsCount.value = 7;
recentEvents.value = [
{
day: '15',
month: 'DIC',
title: 'Mercatino di Natale',
location: 'Roma Centro',
id: 1,
image: '',
},
{
day: '20',
month: 'DIC',
title: 'Corso di Panificazione',
location: 'Milano',
id: 2,
image: '',
},
{
day: '22',
month: 'DIC',
title: 'Scambio Semi e Piante',
location: 'Bologna',
id: 3,
image: '',
},
{
day: '28',
month: 'DIC',
title: 'Incontro Produttori Locali',
location: 'Firenze',
id: 4,
image: '',
},
{
day: '05',
month: 'GEN',
title: 'Festa della Comunità',
location: 'Torino',
id: 5,
image: '',
},
];
// Scambi test data
recentTrades.value = [
{
userInitial: 'M',
description: 'Scambio ortaggi freschi',
time: '2h fa',
amount: 15,
},
{
userInitial: 'L',
description: 'Lezione di chitarra',
time: '5h fa',
amount: -25,
},
{
userInitial: 'G',
description: 'Riparazione bicicletta',
time: '1 giorno fa',
amount: 30,
},
{
userInitial: 'A',
description: 'Pane fatto in casa',
time: '2 giorni fa',
amount: -10,
},
{
userInitial: 'S',
description: 'Consulenza informatica',
time: '3 giorni fa',
amount: 40,
},
];
// Statistiche test data
totalMembers.value = 342;
totalTrades.value = 1567;
totalEvents.value = 89;
// Telegram test data con immagini
telegramLinks.value = [
{
title: 'Canale Principale RISO',
url: 'https://t.me/riso_italia',
image: '/images/telegram-main.jpg',
},
{
title: 'Gruppo Scambi Locali',
url: 'https://t.me/riso_scambi',
image: '/images/telegram-scambi.jpg',
},
{
title: 'Eventi e Incontri',
url: 'https://t.me/riso_eventi',
image: '/images/telegram-eventi.jpg',
},
];
};
// Dati circuiti
const circuits = ref({
provinciale: {
availableBalance: 0,
realBalance: 0,
trustLimit: -100,
maxAccumulation: 200,
totalTransactions: 0,
sentCount: 0,
receivedCount: 0,
uniqueMembers: 0,
lastTradeTime: 'Mai',
recentTransactions: [] as any[],
title: 'RIS Provinciale',
},
italia: {
availableBalance: 0,
realBalance: 0,
trustLimit: -200,
maxAccumulation: 400,
totalTransactions: 0,
sentCount: 0,
receivedCount: 0,
uniqueMembers: 0,
lastTradeTime: 'Mai',
recentTransactions: [] as any[],
title: 'RIS Italia',
},
});
// Dati community
const invitesSent = ref(0);
const myHandshakes = ref<any[]>([]);
const allHandshakes = ref<any[]>([]);
// Circuiti overview
const activeCircuitsCount = ref(2);
const totalCircuitsAvailable = ref(12);
// Computed per circuito corrente
const currentCircuitData = computed(() => {
return circuits.value[selectedCircuit.value];
});
// Metodi
const openTransaction = (tx: any) => {
// TODO: aprire dettaglio transazione
};
const openHandshake = (handshake: any) => {
// TODO: aprire dettaglio stretta di mano
};
const goToAllCircuits = () => {
// TODO: navigare a lista circuiti
// $router.push('/circuits')
};
loadTestData();
onMounted(() => {
showBannScambio.value = tools.getCookieBool('bann_scambio', true);
// loadWalletData()
// loadRecentEvents()
// loadRecentActivity()
// loadStats()
// loadOrganizations()
// loadTelegramLinks()
});
return {
// State
showAnnunciDialog,
// Data
availableBalance,
realBalance,
trustLimit,
maxAccumulation,
receivedCount,
sentCount,
upcomingEventsCount,
recentEvents,
recentTrades,
recentConnections,
totalMembers,
totalTrades,
totalEvents,
organizations,
telegramLinks,
// Computed
hasEvents,
hasOrganizations,
hasTelegramLinks,
// Methods
openAnnunciDialog,
goToGoods,
goToServices,
goToHospitality,
goToTransport,
goToWallet,
goToEvents,
goToProfile,
sendRIS,
receiveRIS,
inviteFriend,
showMembers,
refreshWallet,
goToTransactions,
goToAllEvents,
openEvent,
openTrade,
openConnection,
goToAllTrades,
goToAllConnections,
createOrganization,
openOrganization,
goToAllOrganizations,
openFAQ,
openGuide,
openInfo,
totalTransactions,
uniqueMembers,
lastTradeTime,
goToCircuits,
selectedCircuit,
handshakesView,
circuits,
currentCircuitData,
invitesSent,
myHandshakes,
allHandshakes,
activeCircuitsCount,
totalCircuitsAvailable,
openTransaction,
openHandshake,
goToAllCircuits,
showBannScambio,
tools,
transactionsView,
allTransactions,
userStore,
walletSectionOpen,
};
},
});

View File

@@ -0,0 +1,290 @@
<template>
<q-page class="riso-modern-home riso-modern-home--tail">
<!-- Organizzazioni -->
<section
v-if="hasOrganizations"
class="content-section"
>
<div class="section-header">
<h2 class="section-title">
<q-icon name="fas fa-users" />
Organizzazioni
</h2>
<q-btn
flat
dense
round
icon="arrow_forward"
@click="goToAllOrganizations"
/>
</div>
<div
class="org-scroll"
v-if="Array.isArray(organizations) && organizations.length"
>
<div
v-for="(org, idx) in organizations.slice(0, 10)"
:key="idx"
class="org-card-scroll"
role="button"
tabindex="0"
v-ripple
@click="openOrganization(org)"
@keyup.enter="openOrganization(org)"
@keyup.space.prevent="openOrganization(org)"
>
<q-avatar size="50px">
<q-img
:src="userStore.getImgByGroup(org)"
:alt="org.title"
img-class="imgprofile"
height="50px"
/>
</q-avatar>
<span class="org-name">{{ org.title }}</span>
</div>
<!-- 🆕 Card "Vedi tutti" se ci sono più di 10 gruppi -->
<div
v-if="organizations.length > 10"
class="org-card-scroll see-more-card"
role="button"
tabindex="0"
v-ripple
@click="goToAllOrganizations"
@keyup.enter="goToAllOrganizations"
@keyup.space.prevent="goToAllOrganizations"
>
<div class="see-more-circle">
<span class="see-more-count">+{{ organizations.length - 10 }}</span>
</div>
<span class="org-name">Vedi tutti</span>
</div>
</div>
<!-- 🆕 Indicatore sotto lo scroll -->
<div
v-if="organizations.length > 10"
class="scroll-hint"
>
<q-icon
name="swipe"
size="xs"
/>
<span>Scorri per vedere tutti i {{ organizations.length }} gruppi</span>
</div>
</section>
<!-- Community Actions -->
<section class="community-actions-section">
<h2 class="section-title">
<q-icon name="people" />
Community
</h2>
<div class="invite-banner">
<div class="invite-message">
<q-icon
name="campaign"
size="md"
color="positive"
/>
<div class="invite-text">
<span class="invite-main"
>Aiuta la community a crescere, parla di RISO ed invita le persone alla
App</span
>
<span class="invite-count"
>Hai già inviato <strong>{{ invitesSent }}</strong> inviti</span
>
</div>
</div>
</div>
<div class="community-actions-grid">
<q-btn
unelevated
rounded
class="community-action-btn"
icon="person_add"
label="Invita Amici"
color="positive"
@click="inviteFriend"
/>
<q-btn
unelevated
rounded
class="community-action-btn"
icon="list"
label="Lista Iscritti"
color="info"
@click="showMembers"
/>
</div>
</section>
<!-- Canali Telegram -->
<!--<section
v-if="hasTelegramLinks"
class="content-section"
>
<div class="section-header">
<h2 class="section-title">
<q-icon name="telegram" />
Unisciti ai Canali
</h2>
</div>
<div class="telegram-grid-modern">
<a
v-for="(link, idx) in telegramLinks.slice(0, 3)"
:key="idx"
:href="link.url"
target="_blank"
class="telegram-card-modern" v-ripple rel="noopener">
<q-img
v-if="link.image"
:src="link.image"
class="telegram-card-image"
ratio="16/9"
>
<div class="telegram-overlay">
<q-icon
name="telegram"
size="3rem"
color="white"
/>
</div>
</q-img>
<div
v-else
class="telegram-icon-wrapper"
>
<q-icon
name="telegram"
size="3rem"
color="white"
/>
</div>
<div class="telegram-content-modern">
<span class="telegram-title">{{ link.title }}</span>
<q-icon
name="open_in_new"
size="sm"
/>
</div>
</a>
</div>
</section>
-->
<!-- Footer Links -->
<section class="footer-section-modern">
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="help_outline"
label="FAQ"
color="primary"
@click="openFAQ"
/>
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="school"
label="Guida"
color="secondary"
@click="openGuide"
/>
<q-btn
unelevated
rounded
class="footer-btn-modern"
icon="support_agent"
label="Assistenza"
color="positive"
@click="openInfo"
/>
</section>
<!-- Dialog Annunci -->
<q-dialog
v-model="showAnnunciDialog"
transition-show="slide-up"
transition-hide="slide-down"
>
<q-card class="annunci-dialog">
<q-card-section class="dialog-header">
<h3 class="dialog-title">Scegli Categoria</h3>
<q-btn
flat
round
dense
icon="close"
v-close-popup
/>
</q-card-section>
<q-card-section class="dialog-content">
<div class="annunci-options-mobile">
<div
class="annuncio-option gradient-indigo"
@click="goToGoods"
>
<q-icon
name="fas fa-tshirt"
size="2.5rem"
/>
<span class="option-title">Beni</span>
<span class="option-subtitle">Autoproduzioni, cibo</span>
</div>
<div
class="annuncio-option gradient-red"
@click="goToServices"
>
<q-icon
name="fas fa-house-user"
size="2.5rem"
/>
<span class="option-title">Servizi</span>
<span class="option-subtitle">Competenze, aiuti</span>
</div>
<div
class="annuncio-option gradient-lime"
@click="goToHospitality"
>
<q-icon
name="fas fa-bed"
size="2.5rem"
/>
<span class="option-title">Ospitalità</span>
<span class="option-subtitle">Ospitare viaggiatori</span>
</div>
<div class="annuncio-option gradient-teal">
<q-icon
name="directions_car"
size="2.5rem"
/>
<span class="option-title">Trasporti</span>
<span class="option-subtitle">Condivisione viaggi</span>
<span class="option-subtitle"> (IN ARRIVO...)</span>
</div>
</div>
</q-card-section>
</q-card>
</q-dialog>
</q-page>
</template>
<script lang="ts" src="./Riso_Home_ParteFinale.ts"></script>
<style lang="scss" scoped>
@import './Riso_Home_ParteFinale.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as Riso_Home_ParteFinale} from './Riso_Home_ParteFinale.vue'

View File

@@ -2,60 +2,6 @@
// CIRCUIT MANAGER - SCSS
// ========================================
// ========================================
// CSS Variables
// ========================================
:root {
--cm-bg-primary: #f5f7fa;
--cm-bg-card: #ffffff;
--cm-bg-card-hover: #f8fafc;
--cm-bg-dialog: #ffffff;
--cm-border: rgba(0, 0, 0, 0.08);
--cm-border-accent: rgba(102, 126, 234, 0.4);
--cm-text-primary: #1e293b;
--cm-text-secondary: #475569;
--cm-text-muted: #64748b;
--cm-text-hint: #94a3b8;
--cm-accent: #667eea;
--cm-accent-light: rgba(102, 126, 234, 0.1);
--cm-positive: #10b981;
--cm-negative: #ef4444;
--cm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
--cm-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
--cm-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
--cm-nav-bg: rgba(255, 255, 255, 0.98);
}
.body--dark {
--cm-bg-primary: #0f172a;
--cm-bg-card: #1e293b;
--cm-bg-card-hover: #334155;
--cm-bg-dialog: #1e293b;
--cm-border: rgba(255, 255, 255, 0.1);
--cm-border-accent: rgba(102, 126, 234, 0.5);
--cm-text-primary: #f8fafc;
--cm-text-secondary: #e2e8f0;
--cm-text-muted: #94a3b8;
--cm-text-hint: #64748b;
--cm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
--cm-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
--cm-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
--cm-nav-bg: rgba(15, 23, 42, 0.98);
}
// ========================================
// Variables
// ========================================
$gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
$gradient-send: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
$gradient-receive: linear-gradient(135deg, #10b981 0%, #059669 100%);
@@ -71,6 +17,7 @@ $space-lg: 16px;
$space-xl: 20px;
$space-xxl: 24px;
// ========================================
// Base
// ========================================

File diff suppressed because it is too large Load Diff

View File

@@ -12,15 +12,21 @@
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary : #1976D2;
$secondary : #26A69A;
$accent : #9C27B0;
// ==========================================
// QUASAR VARIABLES - THEMING UFFICIALE
// ==========================================
$dark : #1D1D1D;
$positive : #21BA45;
$negative : #C10015;
$info : #31CCEC;
$warning : #F2C037;
// ==========================================
// COLORI QUASAR (OBBLIGATORI)
// ==========================================
$primary : #1976d2;
$secondary : #26a69a;
$accent : #9c27b0;
$dark : #1d1d1d;
$dark-page : #121212;
$positive : #21ba45;
$negative : #c10015;
$info : #31ccec;
$warning : #f2c037;

460
src/css/variables.scss Executable file → Normal file
View File

@@ -2,37 +2,179 @@
// VARIABILI STILE RISO - Moderno e Minimalista
// ==========================================
// Colori principali - Palette moderna
// ==========================================
// CSS CUSTOM PROPERTIES PER THEMING DINAMICO
// Queste DEVONO essere qui per funzionare con Quasar
// ==========================================
:root {
// === BACKGROUNDS ===
--app-bg-primary: #f1f5f9;
--app-bg-secondary: #e2e8f0;
--app-bg-tertiary: #f8fafc;
--app-bg-card: #ffffff;
--app-bg-card-hover: #f8fafc;
--app-bg-input: #ffffff;
--app-bg-overlay: rgba(0, 0, 0, 0.5);
// === TEXT ===
--app-text-primary: #2c3e50;
--app-text-secondary: #555555;
--app-text-muted: #666666;
--app-text-disabled: #999999;
--app-text-inverse: #ffffff;
// === BORDERS ===
--app-border-color: rgba(0, 0, 0, 0.08);
--app-border-color-strong: rgba(0, 0, 0, 0.15);
--app-divider-color: rgba(0, 0, 0, 0.06);
// === SHADOWS ===
--app-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
--app-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
--app-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
--app-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
--app-shadow-xl: 0 12px 40px rgba(0, 0, 0, 0.15);
// === SCROLLBAR ===
--app-scrollbar-thumb: rgba(0, 0, 0, 0.15);
--app-scrollbar-thumb-hover: rgba(0, 0, 0, 0.25);
--app-scrollbar-track: transparent;
// === MISC ===
--app-backdrop-blur: blur(10px);
--background-even: rgba(211, 211, 211, 0.3);
--background-odd: #ffffff;
}
// ==========================================
// DARK MODE - CSS Custom Properties Override
// Usa html.body--dark per massima specificità
// ==========================================
html.body--dark,
body.body--dark,
.body--dark {
// === BACKGROUNDS ===
--app-bg-primary: #0a0a0a;
--app-bg-secondary: #121212;
--app-bg-tertiary: #1a1a1a;
--app-bg-card: #1e1e1e;
--app-bg-card-hover: #2a2a2a;
--app-bg-input: #2a2a2a;
--app-bg-overlay: rgba(0, 0, 0, 0.7);
// === TEXT ===
--app-text-primary: #e4e4e7;
--app-text-secondary: #a1a1aa;
--app-text-muted: #71717a;
--app-text-disabled: #52525b;
--app-text-inverse: #18181b;
// === BORDERS ===
--app-border-color: rgba(255, 255, 255, 0.1);
--app-border-color-strong: rgba(255, 255, 255, 0.2);
--app-divider-color: rgba(255, 255, 255, 0.08);
// === SHADOWS (più intense per dark mode) ===
--app-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
--app-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3);
--app-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
--app-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.6);
--app-shadow-xl: 0 12px 40px rgba(0, 0, 0, 0.7);
// === SCROLLBAR ===
--app-scrollbar-thumb: rgba(255, 255, 255, 0.2);
--app-scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
// === MISC ===
--background-even: rgba(45, 45, 45, 0.7);
--background-odd: #1e1e1e;
}
// Light mode
$text-primary: #1a1a1a;
$text-secondary: #2c3e50;
$text-muted: #6c757d;
$text-light: #8a9aa9;
// Dark mode
$text-primary-dark: #f5f5f5;
$text-secondary-dark: #b8c5d3;
$text-muted-dark: #8a9aa9;
$text-light-dark: #6c757d;
// ==========================================
// VARIABILI SCSS STATICHE (non cambiano con dark mode)
// ==========================================
// Colori principali
$mainStyle: #4975BA;
$mainColor: #3c4858;
// Variables
$primary-color: #1976d2;
// ==========================================
// COLOR VARIABLES - AGGIORNATE
// ==========================================
// Primary colors
$primary-color: #667eea;
$primary-color-dark: #7c93f0;
$primary-light: #42a5f5;
$primary-dark: #1565c0;
$accent-color: #26a69a;
// Text colors - Light mode
$text-primary: #1a202c;
$text-secondary: #2c3e50;
$text-muted: #718096;
$text-on-primary: #ffffff;
// Text colors - Dark mode
$text-primary-dark: #f7fafc;
$text-secondary-dark: #b8c5d3;
$text-muted-dark: #a0aec0;
// Placeholder gradients
$gradient-placeholder: linear-gradient(135deg, #e5e7eb 0%, #d1d5db 100%);
$gradient-placeholder-dark: linear-gradient(135deg, #374151 0%, #4b5563 100%);
$gray-shadow: #555;
$primary-accent: #667eea;
$success-color: #4caf50;
$warning-color: #f69f09;
$danger-color: #ff0000;
$secondary-color: #26a69a;
$positive-color: #21ba45;
$negative-color: #c10015;
$success-color: #21ba45;
$warning-color: #f2c037;
$info-color: #31ccec;
$orange-color: #ff9800;
// Card styles
$card-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
$card-shadow-hover: 0 4px 20px rgba(0, 0, 0, 0.12);
// Border radius
$border-radius: 16px;
$transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
$grey-text: #555;
$grey-color: #666;
$grey-light: #999;
$border-radius-sm: 12px;
$border-radius-lg: 24px;
$radius-sm: 8px;
$radius-md: 12px;
$radius-lg: 16px;
$radius-xl: 20px;
// Transitions
$transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
$transition-speed: 0.3s;
// Greys
$grey-text: #555;
$grey-color: #666;
$grey-light: #999;
// Spacing
$space-xs: 4px;
$space-sm: 8px;
$space-md: 12px;
@@ -40,29 +182,37 @@ $space-lg: 16px;
$space-xl: 20px;
$space-xxl: 24px;
// Gialli
$s-xs: 4px;
$s-sm: 8px;
$s-md: 12px;
$s-lg: 16px;
$s-xl: 20px;
$r-sm: 8px;
$r-md: 12px;
$r-lg: 16px;
$r-xl: 20px;
// Colori specifici
$yellow1: #f8ab1c;
$yellow2: rgb(221, 144, 35);
$yellow3: #f8d71c;
// Blu
$blue1: #4286f4;
$blue2: #a9dff5;
// Rossi e arancio
$red1: #c84242;
$orange1: #cf7219;
$rose1: #dd4587;
// Verdi
$green1: #5cb85c;
$green2: #CEE8DF;
$green3: #70BEB1;
$green4: #4c964c;
// Marroni
$brown1: #D99E7E;
// Export per JavaScript
:export {
mainStyle: $mainStyle;
red1: $red1;
@@ -76,19 +226,11 @@ $brown1: #D99E7E;
green3: $green3;
}
$s-xs: 4px;
$s-sm: 8px;
$s-md: 12px;
$s-lg: 16px;
$s-xl: 20px;
$r-sm: 8px;
$r-md: 12px;
$r-lg: 16px;
$r-xl: 20px;
// Gradienti
// ==========================================
// GRADIENTI - LIGHT MODE
// ==========================================
$gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
$gradient-secondary: linear-gradient(135deg, #a9dff5 0%, #3889da 100%);
$gradient-blue: linear-gradient(135deg, #3889da 0%, #4e57a7 100%);
$gradient-accent: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
$gradient-success: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
@@ -103,9 +245,208 @@ $gradient-card-light: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
$gradient-card-white: linear-gradient(to bottom, #ffffff 0%, #f8f9fa 100%);
$gradient-hover: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
// ==========================================
// GRADIENTI - DARK MODE
// ==========================================
// Versioni più scure e desaturate per dark mode
$gradient-primary-dark: linear-gradient(135deg, #4c5fd7 0%, #5c3a7e 100%);
$gradient-secondary-dark: linear-gradient(135deg, #2a4a5f 0%, #1e4976 100%);
$gradient-blue-dark: linear-gradient(135deg, #2563a8 0%, #3a4280 100%);
$gradient-accent-dark: linear-gradient(135deg, #c06cc9 0%, #c4404f 100%);
$gradient-success-dark: linear-gradient(135deg, #3584c9 0%, #00b8bf 100%);
$gradient-info-dark: linear-gradient(135deg, #5a9e9a 0%, #a8889a 100%);
$gradient-orange-dark: linear-gradient(135deg, #c45a0e 0%, #b8440a 100%);
$gradient-indigo-dark: linear-gradient(135deg, #434da8 0%, #4c5fd7 100%);
$gradient-red-dark: linear-gradient(135deg, #c44545 0%, #b84a7a 100%);
$gradient-lime-dark: linear-gradient(135deg, #5f9410 0%, #0a8a5f 100%);
$gradient-teal-dark: linear-gradient(135deg, #0e8a7e 0%, #048a9e 100%);
$gradient-card-dark: linear-gradient(135deg, #1e1e1e 0%, #2d2d2d 100%);
$gradient-card-elevated-dark: linear-gradient(to bottom, #2d2d2d 0%, #252525 100%);
$gradient-hover-dark: linear-gradient(135deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%);
// ==========================================
// SCALA GRIGI - Stile RISO pulito
// GRADIENTI AGGIUNTIVI DARK MODE
// ==========================================
// Per superfici e background
$gradient-surface-dark: linear-gradient(135deg, #121212 0%, #1a1a1a 100%);
$gradient-surface-elevated-dark: linear-gradient(135deg, #242424 0%, #2a2a2a 100%);
$gradient-overlay-dark: linear-gradient(135deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.8) 100%);
// ==========================================
// MIXIN PER GESTIONE GRADIENTI
// ==========================================
@mixin gradient-adaptive($light-gradient, $dark-gradient) {
background: $light-gradient;
.body--dark & {
background: $dark-gradient;
}
}
// ==========================================
// CLASSI UTILITY PER GRADIENTI
// ==========================================
.bg-gradient-primary {
background: $gradient-primary;
.body--dark & {
background: $gradient-primary-dark;
}
}
.bg-gradient-secondary {
background: $gradient-secondary;
.body--dark & {
background: $gradient-secondary-dark;
}
}
.bg-gradient-blue {
background: $gradient-blue;
.body--dark & {
background: $gradient-blue-dark;
}
}
.bg-gradient-accent {
background: $gradient-accent;
.body--dark & {
background: $gradient-accent-dark;
}
}
.bg-gradient-success {
background: $gradient-success;
.body--dark & {
background: $gradient-success-dark;
}
}
.bg-gradient-info {
background: $gradient-info;
.body--dark & {
background: $gradient-info-dark;
}
}
.bg-gradient-orange {
background: $gradient-orange;
.body--dark & {
background: $gradient-orange-dark;
}
}
.bg-gradient-indigo {
background: $gradient-indigo;
.body--dark & {
background: $gradient-indigo-dark;
}
}
.bg-gradient-red {
background: $gradient-red;
.body--dark & {
background: $gradient-red-dark;
}
}
.bg-gradient-lime {
background: $gradient-lime;
.body--dark & {
background: $gradient-lime-dark;
}
}
.bg-gradient-teal {
background: $gradient-teal;
.body--dark & {
background: $gradient-teal-dark;
}
}
.bg-gradient-card {
background: $gradient-card-light;
.body--dark & {
background: $gradient-card-dark;
}
}
.bg-gradient-card-white {
background: $gradient-card-white;
.body--dark & {
background: $gradient-card-elevated-dark;
}
}
.bg-gradient-hover {
background: $gradient-hover;
.body--dark & {
background: $gradient-hover-dark;
}
}
// ==========================================
// GRADIENTI PER TESTO
// ==========================================
.text-gradient-primary {
background: $gradient-primary;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
.body--dark & {
background: $gradient-primary-dark;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.text-gradient-accent {
background: $gradient-accent;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
.body--dark & {
background: $gradient-accent-dark;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.text-gradient-success {
background: $gradient-success;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
.body--dark & {
background: $gradient-success-dark;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
// ==========================================
// SCALA GRIGI
// ==========================================
$w255: rgb(255, 255, 255);
$w250: rgb(250, 250, 250);
@@ -139,7 +480,7 @@ $g20: rgb(20, 20, 20);
$g10: rgb(10, 10, 10);
$g0: rgb(0, 0, 0);
$ombre: rgba(10, 10, 10, 0.08); // Ridotta per stile più pulito
$ombre: rgba(10, 10, 10, 0.08);
// ==========================================
// TIPOGRAFIA
@@ -147,39 +488,28 @@ $ombre: rgba(10, 10, 10, 0.08); // Ridotta per stile più pulito
$mainFont: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
// ==========================================
// BREAKPOINT - Mobile first
// BREAKPOINTS
// ==========================================
$mini: "(max-width: 1000px)";
$desktop: "(min-width: 1001px)";
$tablet: "(min-width: 601px) and (max-width: 1000px)";
$mobile: "(max-width: 600px)";
$border-radius: 16px;
$border-radius-sm: 12px;
$border-radius-lg: 24px;
$mobile-breakpoint: 768px;
$mobile-footer-height: 80px;
$small-breakpoint: 600px;
$transition-speed: 0.3s;
// Shadows
$shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
$shadow-md: 0 4px 16px rgba(0, 0, 0, 0.12);
$shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.16);
$shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.12);
$mobile-breakpoint: 768px;
$mobile-footer-height: 80px;
$small-breakpoint: 600px;
// ==========================================
// DIMENSIONI HEADER - Più compatto
// DIMENSIONI
// ==========================================
$headerHeight: 52px; // Ridotto da 60px
$headerHeight: 52px;
$headerColor: #373F46;
// ==========================================
// ALTRE DIMENSIONI
// ==========================================
$Loadersize: 20px;
$boutonfont: 14px;
$boutonH: 20px;
@@ -187,7 +517,7 @@ $aside-w: 180px;
$contentSize: 170px;
// ==========================================
// MIXIN MODERNI
// MIXIN
// ==========================================
@mixin transition($args...) {
transition: $args;
@@ -240,17 +570,12 @@ $contentSize: 170px;
filter: $property;
}
// ==========================================
// NUOVI MIXIN STILE RISO
// ==========================================
@mixin card-shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08),
0 1px 3px rgba(0, 0, 0, 0.06);
box-shadow: var(--app-shadow-sm);
}
@mixin card-shadow-hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12),
0 2px 6px rgba(0, 0, 0, 0.08);
box-shadow: var(--app-shadow-md);
}
@mixin smooth-transition {
@@ -259,8 +584,14 @@ $contentSize: 170px;
@mixin glass-effect {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: var(--app-backdrop-blur);
-webkit-backdrop-filter: var(--app-backdrop-blur);
}
@mixin glass-effect-dark {
background: rgba(30, 30, 30, 0.8);
backdrop-filter: var(--app-backdrop-blur);
-webkit-backdrop-filter: var(--app-backdrop-blur);
}
// ==========================================
@@ -328,3 +659,16 @@ $contentSize: 170px;
font-weight: normal;
font-style: normal;
}
// Typography
$heading-primary-size-mobile: 1.25rem;
$heading-primary-size-desktop: 1.5rem;
$heading-primary-weight: 700;
$heading-primary-letter-spacing: -0.02em;
$heading-primary-line-height: 1.3;
// Breakpoints
$breakpoint-xs: 600px;
$breakpoint-sm: 768px;
$breakpoint-md: 1024px;
$breakpoint-lg: 1200px;

View File

@@ -163,6 +163,7 @@ export interface IMyElem {
parambool2?: boolean
parambool3?: boolean
parambool4?: boolean
parambool5?: boolean
number?: number
num2?: number
imgback?: string
@@ -195,6 +196,8 @@ export interface IMyElem {
elemsText?: IElemText[]
titleBanner?: string
classBanner?: string
stiletit_str?: string
stiletit_icon?: string
color?: string
rows?: any[]
columns?: any[]

View File

@@ -352,6 +352,12 @@ export const useCircuitStore = defineStore('CircuitStore', {
return false;
},
quantiCircuitiSonoDentro() {
const userStore = useUserStore();
return userStore.my.profile.mycircuits.length
},
updateListCircuit(visu: number, sortbyuser?: boolean) {
const userStore = useUserStore();

View File

@@ -800,6 +800,11 @@ export const colmyelems = [
label_trans: 'myelems.parambool4',
fieldtype: costanti.FieldType.boolean,
}),
AddCol({
name: 'parambool5',
label_trans: 'myelems.parambool5',
fieldtype: costanti.FieldType.boolean,
}),
AddCol({
name: 'number',
label_trans: 'myelems.number',

View File

@@ -7461,10 +7461,9 @@ export const tools = {
async sendCoinsByCircuit(
$q: any,
$router: any,
circuit: ICircuit,
sendcoinrec: ISendCoin
): Promise<void> {
): Promise<boolean> {
const userStore = useUserStore();
const notifStore = useNotifStore();
const { username } = userStore.my;
@@ -7473,83 +7472,48 @@ export const tools = {
const orig = sendcoinrec.grouporig || sendcoinrec.contoComOrig || '';
const dest = sendcoinrec.groupdest || sendcoinrec.contoComDest || sendcoinrec.dest;
// Parametri comuni per i messaggi
const msgParams = {
coin: circuit.symbol,
dest,
qty: sendcoinrec.qty,
circuit: circuit.name,
...(orig && { from: orig }), // Aggiunge 'from' solo se esiste
};
try {
const res = await userStore.setCircuitCmd(
$q,
t,
username,
sendcoinrec.circuitname,
shared_consts.CIRCUITCMD.SENDCOINS_REQ,
true,
sendcoinrec
);
// Seleziona il messaggio appropriato
const messageKey = orig
? 'circuit.question_sendcoinsto_from'
: 'circuit.question_sendcoinsto';
console.log('setCircuitCmd', res);
// Mostra dialog di conferma
return new Promise((resolve, reject) => {
$q.dialog({
title: t('db.domanda'),
message: t(messageKey, msgParams),
ok: {
label: t('dialog.yes'),
color: 'primary',
push: true,
},
cancel: {
label: t('dialog.cancel'),
color: 'secondary',
},
})
.onOk(async () => {
try {
const res = await userStore.setCircuitCmd(
$q,
t,
username,
sendcoinrec.circuitname,
shared_consts.CIRCUITCMD.SENDCOINS_REQ,
true,
sendcoinrec
);
if (!res?.cansend) {
tools.showNegativeNotif($q, res?.errormsg || t('errors.generic'));
return false;
}
console.log('setCircuitCmd', res);
// Aggiorna gli account utente se presenti
if (res.useraccounts?.length > 0) {
userStore.my.profile.useraccounts = res.useraccounts;
}
if (!res?.cansend) {
tools.showNegativeNotif($q, res?.errormsg || t('errors.generic'));
return resolve();
}
// Aggiorna dati
tools.updateMyData(res);
notifStore.updateNotification = true;
// Aggiorna gli account utente se presenti
if (res.useraccounts?.length > 0) {
userStore.my.profile.useraccounts = res.useraccounts;
}
// Aggiorna dati e naviga
tools.updateMyData(res);
notifStore.updateNotification = true;
tools.showPositiveNotif(
$q,
t('circuit.coins_sent', {
qty: sendcoinrec.qty,
symbol: circuit.symbol,
dest,
})
);
$router.push('/circuits');
resolve();
} catch (error) {
console.error('Errore invio coins:', error);
tools.showNegativeNotif($q, t('errors.generic'));
reject(error);
}
tools.showPositiveNotif(
$q,
t('circuit.coins_sent', {
qty: sendcoinrec.qty,
symbol: circuit.symbol,
dest,
})
.onCancel(() => resolve())
.onDismiss(() => resolve());
});
);
return true;
} catch (error) {
console.error('Errore invio coins:', error);
tools.showNegativeNotif($q, t('errors.generic'));
return false;
}
},
/* return await new Promise((resolve, reject) => {
resolve(false)

View File

@@ -3643,5 +3643,9 @@ export const useGlobalStore = defineStore('GlobalStore', {
async sendInviteEmail(email: string) {
// ...
},
getMyGroups() {
return this.mygroups.sort((a, b) => (a.title > b.title) ? 1 : -1);
}
},
});