- migliorata grafica statistiche valori

- corretto pagina profilo
This commit is contained in:
Surya Paolo
2025-11-03 01:47:21 +01:00
parent 7e156ca820
commit a164db8bf3
7 changed files with 600 additions and 191 deletions

View File

@@ -1,4 +1,5 @@
export const shared_consts = {
USER_ADMIN_CIRCUITS: ['surya1977', 'ElenaEspx'],
Accepted: {
CHECK_READ_GUIDELINES: {
value: 1,

View File

@@ -1,52 +1,435 @@
.my-card-stat {
// Variabili per consistenza
$card-radius-desktop: 20px;
$card-radius-tablet: 16px;
$card-radius-mobile: 14px;
$transition-smooth: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
$transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
.stat-card {
position: relative;
width: 100%;
max-width: 200px;
min-width: 120px;
padding: 1rem 1rem;
min-width: 150px;
height: 170px;
margin: 10px;
padding: 0;
border-radius: $card-radius-desktop;
background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%);
box-shadow:
0 10px 30px -5px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(0, 0, 0, 0.02);
transition: $transition-base;
overflow: hidden;
cursor: default;
&:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 20px 40px -8px rgba(0, 0, 0, 0.15),
0 0 0 1px rgba(0, 0, 0, 0.03);
.stat-icon {
transform: scale(1.15) rotate(-8deg);
}
.stat-icon-glow {
opacity: 1;
transform: scale(1.4);
}
.stat-shine {
transform: translateX(100%) translateY(-100%);
opacity: 0;
}
.stat-border {
opacity: 1;
}
}
&:active {
transform: translateY(-4px) scale(1);
}
@media (max-width: 960px) {
max-width: 180px;
min-width: 140px;
height: 160px;
margin: 8px;
border-radius: $card-radius-tablet;
}
box-shadow: none;
@media (max-width: 718px) {
// PER VERSIONE MOBILE
max-width: 150px;
padding: 0;
max-width: calc(33.333% - 12px);
min-width: 100px;
height: 145px;
margin: 6px;
border-radius: $card-radius-mobile;
}
@media (max-width: 480px) {
max-width: calc(33.333% - 10px);
min-width: 95px;
height: 140px;
margin: 5px;
}
}
.my-card-small-stat {
.stat-content {
position: relative;
z-index: 2;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
padding: 20px 14px;
text-align: center;
@media (max-width: 960px) {
padding: 18px 12px;
}
@media (max-width: 718px) {
padding: 16px 10px;
}
@media (max-width: 480px) {
padding: 14px 8px;
}
}
.stat-icon-wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 50%;
background: linear-gradient(145deg, #ffffff, #f5f5f5);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.08),
inset 0 1px 2px rgba(255, 255, 255, 0.9);
@media (max-width: 960px) {
width: 58px;
height: 58px;
}
@media (max-width: 718px) {
width: 52px;
height: 52px;
}
@media (max-width: 480px) {
width: 48px;
height: 48px;
}
}
.stat-icon-glow {
position: absolute;
width: 100%;
max-width: 60px;
min-width: 40px;
box-shadow: none;
@media (max-width: 718px) {
// PER VERSIONE MOBILE
max-width: 50px;
min-width: 40px;
}
height: 100%;
border-radius: 50%;
opacity: 0;
transform: scale(1);
transition: $transition-smooth;
pointer-events: none;
}
.stat-icon {
font-size: 32px !important;
transition: $transition-smooth;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
.text-h5-short {
line-height: 1.25rem !important;
@media (max-width: 960px) {
font-size: 29px !important;
}
@media (max-width: 718px) {
line-height: 1rem !important;
font-size: 26px !important;
}
@media (max-width: 480px) {
font-size: 24px !important;
}
}
.stat-title {
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.03em;
line-height: 1.3;
opacity: 0.85;
text-transform: uppercase;
margin: 4px 0;
color: #424242;
@media (max-width: 960px) {
font-size: 0.7rem;
}
@media (max-width: 718px) {
font-size: 0.65rem;
margin: 2px 0;
}
@media (max-width: 480px) {
font-size: 0.6rem;
letter-spacing: 0.02em;
}
}
.stat-value-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
@media (max-width: 718px) {
gap: 5px;
}
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
line-height: 1.1;
letter-spacing: -0.03em;
color: #1a1a1a;
@media (max-width: 960px) {
font-size: 1.375rem;
}
@media (max-width: 718px) {
font-size: 1.25rem;
}
@media (max-width: 480px) {
font-size: 1.125rem;
}
}
.stat-badge {
position: relative;
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 24px;
font-size: 0.625rem;
font-weight: 600;
color: white;
box-shadow:
0 3px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 2px rgba(255, 255, 255, 0.2);
backdrop-filter: blur(8px);
overflow: hidden;
@media (max-width: 960px) {
padding: 3px 9px;
font-size: 0.6rem;
}
@media (max-width: 718px) {
padding: 3px 8px;
font-size: 0.5625rem;
}
@media (max-width: 480px) {
padding: 2px 7px;
font-size: 0.5rem;
}
}
.stat-badge-pulse {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: 24px;
animation: pulse 2s ease-in-out infinite;
}
.stat-badge-icon {
margin-right: 4px;
z-index: 1;
animation: bounce-subtle 2s ease-in-out infinite;
@media (max-width: 718px) {
margin-right: 3px;
}
}
.stat-badge-text {
z-index: 1;
}
.stat-shine {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
45deg,
transparent 30%,
rgba(255, 255, 255, 0.4) 50%,
transparent 70%
);
transform: translateX(-100%) translateY(-100%);
transition: transform 0.8s ease;
pointer-events: none;
z-index: 3;
}
.stat-border {
position: absolute;
inset: 0;
border-radius: inherit;
padding: 2px;
background: linear-gradient(145deg, rgba(33, 150, 243, 0.3), rgba(156, 39, 176, 0.3));
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
z-index: 1;
}
// Animations
@keyframes pulse {
0%, 100% {
opacity: 0.3;
transform: scale(1);
}
50% {
opacity: 0.5;
transform: scale(1.05);
}
}
@keyframes bounce-subtle {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-2px);
}
}
.badge-pop-enter-active {
animation: badge-pop-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.badge-pop-leave-active {
animation: badge-pop-out 0.3s ease;
}
@keyframes badge-pop-in {
0% {
opacity: 0;
transform: scale(0.5) translateY(10px);
}
60% {
transform: scale(1.1) translateY(-2px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@keyframes badge-pop-out {
to {
opacity: 0;
transform: scale(0.8) translateY(5px);
}
}
// Small variant
.my-card-small-stat {
max-width: 110px;
min-width: 90px;
height: 130px;
.stat-content {
padding: 14px 10px;
}
.stat-icon-wrapper {
width: 44px;
height: 44px;
}
.stat-icon {
font-size: 24px !important;
}
.stat-title {
font-size: 0.65rem;
}
.stat-value {
font-size: 1.125rem;
}
.stat-badge {
padding: 3px 8px;
font-size: 0.5625rem;
}
@media (max-width: 718px) {
max-width: 95px;
min-width: 80px;
height: 120px;
}
}
// Dark mode support
.body--dark {
.stat-card {
background: linear-gradient(145deg, #1e1e1e 0%, #2a2a2a 100%);
box-shadow:
0 10px 30px -5px rgba(0, 0, 0, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.05);
&:hover {
box-shadow:
0 20px 40px -8px rgba(0, 0, 0, 0.5),
0 0 0 1px rgba(255, 255, 255, 0.08);
}
}
.stat-icon-wrapper {
background: linear-gradient(145deg, #2a2a2a, #1e1e1e);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 2px rgba(255, 255, 255, 0.05);
}
.stat-title {
color: #e0e0e0;
}
.stat-value {
color: #ffffff;
}
}
// Utility classes
.boldhigh {
font-weight: 700;
}
.fixed-size {
width: 160px;
/* Larghezza fissa */
height: 150px;
/* Altezza fissa */
display: flex;
/* Assicura che il contenuto sia centralizzato */
flex-direction: column;
/* Impila gli elementi verticalmente */
justify-content: center;
/* Centra verticalmente */
align-items: center;
/* Centra orizzontalmente */
margin: 4px;
// Classe mantenuta per compatibilità
}

View File

@@ -17,8 +17,23 @@ export default defineComponent({
components: {},
setup(props) {
function getGlowColor(classColor: string): string {
const colorMap: Record<string, string> = {
'text-red': 'rgba(244, 67, 54, 0.15)',
'text-orange': 'rgba(255, 152, 0, 0.15)',
'text-green': 'rgba(76, 175, 80, 0.15)',
'text-blue': 'rgba(33, 150, 243, 0.15)',
'text-indigo': 'rgba(63, 81, 181, 0.15)',
'text-blueviolet': 'rgba(138, 43, 226, 0.15)',
'text-purple': 'rgba(156, 39, 176, 0.15)',
'text-teal': 'rgba(0, 150, 136, 0.15)',
};
return colorMap[classColor] || 'rgba(0, 0, 0, 0.05)';
}
return {
tools
tools,
getGlowColor
}
},
})

View File

@@ -1,28 +1,47 @@
<template>
<q-card :class="myclass" :style="mystyle" class="fixed-size">
<div
:class="`column q-pa-sm text-center align-center ` + classColor"
style="align-items: center"
>
<div class="elem text-h6">
{{ title }}
</div>
<q-icon :name="icon" size="xl" :class="classColor + ` elem `" />
<div>
<div></div>
<q-card
:class="[myclass, 'stat-card']"
:style="mystyle"
>
<div :class="['stat-content', classColor]">
<!-- Icon con effetto hover e glow -->
<div class="stat-icon-wrapper">
<div class="stat-icon-glow" :style="{ backgroundColor: getGlowColor(classColor) }"></div>
<q-icon
:name="icon"
:class="['stat-icon', classColor]"
/>
</div>
<div class="elem full-width elem-value text-h5 boldhigh">
{{ mytextval }}
<q-badge
v-if="value_today > 0"
:style="`text-align: top;`"
:label="`+` + value_today + ` oggi`"
:color="colBack"
>
</q-badge>
<!-- Title -->
<div class="stat-title">
{{ title }}
</div>
<!-- Value con tipografia gerarchica -->
<div class="stat-value-container">
<div class="stat-value">
{{ mytextval }}
</div>
<!-- Badge oggi con micro-interazioni -->
<transition name="badge-pop">
<div
v-if="value_today > 0"
class="stat-badge"
:style="{ backgroundColor: colBack }"
>
<div class="stat-badge-pulse"></div>
<q-icon name="trending_up" size="12px" class="stat-badge-icon" />
<span class="stat-badge-text">+{{ value_today }} oggi</span>
</div>
</transition>
</div>
</div>
<!-- Decorative elements -->
<div class="stat-shine"></div>
<div class="stat-border"></div>
</q-card>
</template>

View File

@@ -187,130 +187,132 @@
v-if="visu === costanti.USER_CIRCUITS && tools.isUserOk()"
>
<q-item-label>
<div v-if="false">
<q-btn
rounded
:icon="
userStore.IsMyCircuitByName(circuit.name)
? `fas fa-ellipsis-h`
: `fas fa-user`
"
>
<q-menu>
<q-list
v-if="
!userStore.IsMyCircuitByName(circuit.name) &&
!userStore.IsAskedCircuitByName(circuit.name) &&
!userStore.IsRefusedCircuitByName(circuit.name)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
true,
circuitname
)
"
>
<q-item-section>{{
circuit.askManagerToEnter ? t('circuit.ask') : t('circuit.enter')
}}</q-item-section>
</q-item>
</q-list>
<q-list
v-else-if="
!userStore.IsMyCircuitByName(circuit.name) &&
userStore.IsAskedCircuitByName(circuit.name) &&
!userStore.IsRefusedCircuitByName(circuit.name)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
false,
circuit.name
)
"
>
<q-item-section>
{{ t('shared.refuse_ask') }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
false,
circuit.name
)
"
>
<q-item-section>
{{ t('shared.cancel_ask') }}
</q-item-section>
</q-item>
</q-list>
<q-list
v-else-if="
userStore.IsMyCircuitByName(circuit.name) && (saldo === 0 || !saldo)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REMOVE_FROM_MYLIST,
myusername(),
'',
circuit.name
)
"
>
<q-item-section>{{ t('circuit.exit') }}</q-item-section>
</q-item>
</q-list>
<q-list
v-if="userStore.isAdmin"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.DELETE,
myusername(),
'',
circuit.name
)
"
>
<q-item-section>{{ t('circuit.delete') }}</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<q-btn
rounded
:icon="
userStore.IsMyCircuitByName(circuit.name)
? `fas fa-ellipsis-h`
: `fas fa-user`
"
>
<q-menu>
<q-list
v-if="
!userStore.IsMyCircuitByName(circuit.name) &&
!userStore.IsAskedCircuitByName(circuit.name) &&
!userStore.IsRefusedCircuitByName(circuit.name)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
true,
circuitname
)
"
>
<q-item-section>{{
circuit.askManagerToEnter ? t('circuit.ask') : t('circuit.enter')
}}</q-item-section>
</q-item>
</q-list>
<q-list
v-else-if="
!userStore.IsMyCircuitByName(circuit.name) &&
userStore.IsAskedCircuitByName(circuit.name) &&
!userStore.IsRefusedCircuitByName(circuit.name)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
false,
circuit.name
)
"
>
<q-item-section>
{{ t('shared.refuse_ask') }}
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REQ,
myusername(),
false,
circuit.name
)
"
>
<q-item-section>
{{ t('shared.cancel_ask') }}
</q-item-section>
</q-item>
</q-list>
<q-list
v-else-if="
userStore.IsMyCircuitByName(circuit.name) && (saldo === 0 || !saldo)
"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.REMOVE_FROM_MYLIST,
myusername(),
'',
circuit.name
)
"
>
<q-item-section>{{ t('circuit.exit') }}</q-item-section>
</q-item>
</q-list>
<q-list
v-if="userStore.isAdmin"
style="min-width: 200px"
>
<q-item
clickable
v-close-popup
@click="
tools.setCmd(
$q,
shared_consts.CIRCUITCMD.DELETE,
myusername(),
'',
circuit.name
)
"
>
<q-item-section>{{ t('circuit.delete') }}</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<q-btn
v-if="fidoConcessoUtente !== ''"
v-if="fidoConcessoUtente !== '' && false"
icon="fas fa-house-user"
class="q-ml-sm"
:color="Number(fidoConcessoUtente) > 0 ? 'primary' : 'grey'"

View File

@@ -3,7 +3,7 @@
<div v-if="visustat">
<CTitleBanner class="q-pa-xs" :title="$t('pages.status')" bgcolor="bg-primary" clcolor="text-white" mystyle=""
myclass="sfondo_gradiente_blu myshad" :canopen="true">
<div class="flex flex-center">
<div class="flex flex-center flex-wrap">
<CElemStat myclass="fixed-size" :title="$t('statusreg.reg')" icon="fas fa-users"
:value_today="datastat.num_reg_today" :mytextval="datastat.activeusers + ' su ' + tools.numtostr(datastat.num_reg)
" classColor="text-red" colBack="green">
@@ -35,17 +35,6 @@
<div class="q-pa-xs" v-if="datastat.num_part_accepted > 1">
<CCardStat :mytext="$t('stat.accepted')" :myval="datastat.num_part_accepted"></CCardStat>
<!--<CCardStat :mytext="$t('stat.modalita_pagamento')"
:myval="datastat.num_modalita_pagamento"></CCardStat>-->
<!--<CCardStat :mytext="$t('stat.requisiti')" :myval="datastat.num_requisiti"></CCardStat>-->
<!--<CCardStat :mytext="$t('stat.qualificati')" :myval="datastat.num_qualificati"></CCardStat>-->
<!--<CCardStat v-if="emailnonverif" :mytext="$t('stat.email_not_verif')" :myval="emailnonverif"
mycol="negative"></CCardStat>
<CCardStat v-if="telegnonattivi" :mytext="$t('stat.telegram_non_attivi')"
:myval="telegnonattivi"
mycol="negative"></CCardStat>
<CCardStat v-if="datastat.num_teleg_pending > 0" :mytext="$t('stat.telegram_pendenti')"
:myval="datastat.num_teleg_pending" mycol="negative"></CCardStat>-->
</div>
<q-list bordered>

View File

@@ -7705,7 +7705,7 @@ export const tools = {
let risultato = false;
if (userStore.isAdmin) {
if (userStore.isAdmin || shared_consts.USER_ADMIN_CIRCUITS.includes(userStore.my.username)) {
return true;
}