Ripulisce profilo e tab, e mette Apri evento sull'ultima riga delle presenze.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,8 +7,8 @@ export type VoceSottosezione = {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
contenuto: ReactNode;
|
contenuto: ReactNode;
|
||||||
/** Se true, non ripete il titolo della tab sopra il contenuto (es. Classifica già nella barra). */
|
/** Se true, il pannello è a tutta larghezza (niente padding), es. barra filtri Classifica. */
|
||||||
nascondiTitolo?: boolean;
|
aTuttoLarghezza?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tween breve: evita molle + exit che tengono due pannelli in DOM insieme. */
|
/** Tween breve: evita molle + exit che tengono due pannelli in DOM insieme. */
|
||||||
@@ -19,9 +19,9 @@ const transizioneTab = { type: "tween" as const, duration: 0.16, ease: [0.25, 0.
|
|||||||
* pannello che mostra una sola sezione alla volta, cambiabile anche con
|
* pannello che mostra una sola sezione alla volta, cambiabile anche con
|
||||||
* swipe sul contenuto.
|
* swipe sul contenuto.
|
||||||
*
|
*
|
||||||
* `variante="sottolineatura"`: tab a testo con sottolineatura rossa e senza titolo
|
* `variante="sottolineatura"`: tab a testo con sottolineatura rossa (Squadra e Classifica
|
||||||
* ripetuto sotto la barra (Squadra e Classifica CSI). Default `pillole`: restano le
|
* CSI). Default `pillole`: restano le pill usate dal Profilo. Il titolo ripetuto sotto la
|
||||||
* pill e i titoli usati dal Profilo.
|
* barra non viene mai mostrato: l’etichetta è già nella tab.
|
||||||
*/
|
*/
|
||||||
export function BarraSottosezioni({
|
export function BarraSottosezioni({
|
||||||
voci,
|
voci,
|
||||||
@@ -135,11 +135,8 @@ export function BarraSottosezioni({
|
|||||||
initial={ridotto ? { opacity: 0 } : { opacity: 0, x: direzione.current * 20 }}
|
initial={ridotto ? { opacity: 0 } : { opacity: 0, x: direzione.current * 20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={ridotto ? { duration: 0.1 } : transizioneTab}
|
transition={ridotto ? { duration: 0.1 } : transizioneTab}
|
||||||
className={cn("touch-pan-y", voce.nascondiTitolo ? "px-0 pt-0 pb-4" : "px-5 py-4")}
|
className={cn("touch-pan-y", voce.aTuttoLarghezza ? "px-0 pt-0 pb-4" : "px-5 py-4")}
|
||||||
>
|
>
|
||||||
{voce.nascondiTitolo || sottolineatura ? null : (
|
|
||||||
<h2 className="mb-3 font-display-sm text-lg uppercase">{voce.label}</h2>
|
|
||||||
)}
|
|
||||||
{voce.contenuto}
|
{voce.contenuto}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,6 +54,36 @@ export function EventoCard({
|
|||||||
const isCompleanno = evento.tipo === "compleanno";
|
const isCompleanno = evento.tipo === "compleanno";
|
||||||
const passato = evento.data < dataOggi();
|
const passato = evento.data < dataOggi();
|
||||||
|
|
||||||
|
function ChipPresenza({ s }: { s: Stato }) {
|
||||||
|
const meta = statoMeta[s];
|
||||||
|
const attivo = stato === s;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={!io || salva.isPending || passato}
|
||||||
|
onClick={() => {
|
||||||
|
if (!io) return;
|
||||||
|
salva.mutate({
|
||||||
|
eventoId: evento.id,
|
||||||
|
giocatoreId: io.id,
|
||||||
|
stato: attivo ? null : s,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
aria-pressed={attivo}
|
||||||
|
className={cn(
|
||||||
|
// min-h-11: sono i controlli più toccati dell'app, sotto i
|
||||||
|
// 44px si sbaglia bersaglio.
|
||||||
|
"min-h-11 rounded-full border border-border px-3 text-xs font-semibold transition-all active:scale-95 disabled:opacity-50",
|
||||||
|
attivo
|
||||||
|
? cn(meta.className, "border-transparent shadow-card")
|
||||||
|
: "bg-background text-muted-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{meta.label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isCompleanno) {
|
if (isCompleanno) {
|
||||||
return (
|
return (
|
||||||
<Card as="article" className="flex items-center gap-3">
|
<Card as="article" className="flex items-center gap-3">
|
||||||
@@ -96,18 +126,6 @@ export function EventoCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{linkTo && (
|
|
||||||
<div className="mt-2 flex justify-end">
|
|
||||||
<Link
|
|
||||||
to={linkTo.to}
|
|
||||||
params={linkTo.params}
|
|
||||||
className="inline-flex min-h-11 items-center gap-1 rounded-full bg-primary px-4 text-xs font-bold uppercase tracking-wide text-primary-foreground transition-transform active:scale-95"
|
|
||||||
>
|
|
||||||
{linkTo.label} <ArrowRight className="h-3 w-3" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||||
<span className="inline-flex items-center gap-1">
|
<span className="inline-flex items-center gap-1">
|
||||||
<Clock className="h-3.5 w-3.5" /> {evento.ora}
|
<Clock className="h-3.5 w-3.5" /> {evento.ora}
|
||||||
@@ -123,37 +141,22 @@ export function EventoCard({
|
|||||||
|
|
||||||
<Barra percentuale={perc} altezza="h-1.5" trackClassName="mt-3" />
|
<Barra percentuale={perc} altezza="h-1.5" trackClassName="mt-3" />
|
||||||
|
|
||||||
{io ? (
|
{io || linkTo ? (
|
||||||
<div className="mt-4 flex flex-wrap gap-2">
|
<div className="mt-4 flex flex-wrap gap-2">
|
||||||
{stati.map((s) => {
|
{io ? stati.slice(0, -1).map((s) => <ChipPresenza key={s} s={s} />) : null}
|
||||||
const meta = statoMeta[s];
|
{/* Ultima riga: ultimo chip + Apri insieme, così non nasce una terza riga. */}
|
||||||
const attivo = stato === s;
|
<div className="flex w-full basis-full items-center gap-2">
|
||||||
return (
|
{io ? <ChipPresenza s={stati[stati.length - 1]!} /> : null}
|
||||||
<button
|
{linkTo ? (
|
||||||
key={s}
|
<Link
|
||||||
type="button"
|
to={linkTo.to}
|
||||||
disabled={salva.isPending || passato}
|
params={linkTo.params}
|
||||||
onClick={() =>
|
className="ml-auto inline-flex min-h-11 items-center gap-1 rounded-full bg-primary px-4 text-xs font-bold uppercase tracking-wide text-primary-foreground transition-transform active:scale-95"
|
||||||
salva.mutate({
|
|
||||||
eventoId: evento.id,
|
|
||||||
giocatoreId: io.id,
|
|
||||||
stato: attivo ? null : s,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
aria-pressed={attivo}
|
|
||||||
className={cn(
|
|
||||||
// min-h-11: sono i controlli più toccati dell'app, sotto i
|
|
||||||
// 44px si sbaglia bersaglio.
|
|
||||||
"min-h-11 rounded-full border border-border px-3 text-xs font-semibold transition-all active:scale-95 disabled:opacity-50",
|
|
||||||
attivo
|
|
||||||
? cn(meta.className, "border-transparent shadow-card")
|
|
||||||
: "bg-background text-muted-foreground",
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{meta.label}
|
{linkTo.label} <ArrowRight className="h-3 w-3" />
|
||||||
</button>
|
</Link>
|
||||||
);
|
) : null}
|
||||||
})}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
+2
-12
@@ -1,7 +1,7 @@
|
|||||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Flame, Camera, Trash2, Bell, LogOut, ShieldCheck, Bug, Lightbulb } from "lucide-react";
|
import { Flame, Trash2, Bell, LogOut, ShieldCheck, Bug, Lightbulb } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Card, PageHeader, StatTile, TeamLogo } from "@/components/crapp/ui-bits";
|
import { Card, PageHeader, StatTile, TeamLogo } from "@/components/crapp/ui-bits";
|
||||||
import { BarraSottosezioni } from "@/components/crapp/BarraSottosezioni";
|
import { BarraSottosezioni } from "@/components/crapp/BarraSottosezioni";
|
||||||
@@ -140,7 +140,6 @@ function Profilo() {
|
|||||||
<Reveal className="-mt-6 px-5">
|
<Reveal className="-mt-6 px-5">
|
||||||
<Card>
|
<Card>
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
<div className="relative">
|
|
||||||
<Avatar id={g.id} fallback={g.iniziali} className="h-20 w-20 text-2xl" bust={bust} />
|
<Avatar id={g.id} fallback={g.iniziali} className="h-20 w-20 text-2xl" bust={bust} />
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
@@ -149,15 +148,6 @@ function Profilo() {
|
|||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={onFile}
|
onChange={onFile}
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => inputRef.current?.click()}
|
|
||||||
className="absolute -bottom-1 -right-1 grid h-11 w-11 place-items-center rounded-full bg-accent-grad text-accent-foreground shadow-pop"
|
|
||||||
aria-label="Cambia foto"
|
|
||||||
>
|
|
||||||
<Camera className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 flex gap-2">
|
<div className="mt-4 flex gap-2">
|
||||||
<button
|
<button
|
||||||
@@ -181,7 +171,7 @@ function Profilo() {
|
|||||||
toast.error("Non sono riuscito a rimuovere l'immagine");
|
toast.error("Non sono riuscito a rimuovere l'immagine");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-secondary text-muted-foreground"
|
className="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-destructive/15 text-destructive"
|
||||||
aria-label="Rimuovi immagine"
|
aria-label="Rimuovi immagine"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="h-4 w-4" />
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ function Squadra() {
|
|||||||
{
|
{
|
||||||
id: "classifica-giocatori",
|
id: "classifica-giocatori",
|
||||||
label: "Classifica",
|
label: "Classifica",
|
||||||
nascondiTitolo: true,
|
aTuttoLarghezza: true,
|
||||||
contenuto: (
|
contenuto: (
|
||||||
<>
|
<>
|
||||||
<div className="flex min-w-0 items-center gap-2 border-b border-border bg-card px-5 py-2.5">
|
<div className="flex min-w-0 items-center gap-2 border-b border-border bg-card px-5 py-2.5">
|
||||||
|
|||||||
Reference in New Issue
Block a user