2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Accessibility,
|
|
|
|
|
ArrowLeft,
|
|
|
|
|
ArrowRight,
|
|
|
|
|
Baby,
|
|
|
|
|
BellRing,
|
|
|
|
|
Building,
|
|
|
|
|
Building2,
|
|
|
|
|
CircleDot,
|
|
|
|
|
CircleParking,
|
|
|
|
|
CirclePlus,
|
|
|
|
|
CircleX,
|
|
|
|
|
Cog,
|
|
|
|
|
Contact,
|
|
|
|
|
Cross,
|
|
|
|
|
Dot,
|
|
|
|
|
Euro,
|
|
|
|
|
FerrisWheel,
|
|
|
|
|
Film,
|
|
|
|
|
GraduationCap,
|
|
|
|
|
Heart,
|
|
|
|
|
Heater,
|
|
|
|
|
House,
|
|
|
|
|
Info,
|
|
|
|
|
LayoutDashboard,
|
|
|
|
|
type LucideProps,
|
|
|
|
|
Mail,
|
|
|
|
|
MessageSquareText,
|
|
|
|
|
MessagesSquare,
|
|
|
|
|
Milestone,
|
|
|
|
|
Paperclip,
|
|
|
|
|
Pointer,
|
|
|
|
|
Search,
|
|
|
|
|
ShoppingCart,
|
|
|
|
|
SlidersHorizontal,
|
|
|
|
|
Sofa,
|
|
|
|
|
Star,
|
|
|
|
|
Target,
|
|
|
|
|
ThermometerSun,
|
|
|
|
|
ThumbsUp,
|
|
|
|
|
Trees,
|
|
|
|
|
TriangleAlert,
|
|
|
|
|
User,
|
|
|
|
|
Users,
|
|
|
|
|
Wifi,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "lucide-react";
|
|
|
|
|
import type {
|
2025-08-28 18:27:07 +02:00
|
|
|
ComponentPropsWithoutRef,
|
|
|
|
|
ForwardRefExoticComponent,
|
|
|
|
|
RefAttributes,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "react";
|
2025-08-28 18:27:07 +02:00
|
|
|
import toast from "react-hot-toast";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipProvider,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "~/components/ui/tooltip";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
const iconOptions = [
|
2025-08-28 18:27:07 +02:00
|
|
|
"heater",
|
|
|
|
|
"error",
|
|
|
|
|
"warning",
|
|
|
|
|
"sofa",
|
|
|
|
|
"building",
|
|
|
|
|
"building2",
|
|
|
|
|
"wifi",
|
|
|
|
|
"accessibility",
|
|
|
|
|
"baby",
|
|
|
|
|
"thermometer-sun",
|
|
|
|
|
"circle-plus",
|
|
|
|
|
"circle-dot",
|
|
|
|
|
"dot",
|
|
|
|
|
"cog",
|
|
|
|
|
"check",
|
|
|
|
|
"search",
|
|
|
|
|
"contact",
|
|
|
|
|
"euro",
|
|
|
|
|
"dashboard",
|
|
|
|
|
"settings",
|
|
|
|
|
"user",
|
|
|
|
|
"users",
|
|
|
|
|
"messages",
|
|
|
|
|
"message",
|
|
|
|
|
"arrowR",
|
|
|
|
|
"arrowL",
|
|
|
|
|
"attachment",
|
|
|
|
|
"cart",
|
|
|
|
|
"star",
|
|
|
|
|
"house",
|
|
|
|
|
"heart",
|
|
|
|
|
"info",
|
|
|
|
|
"mail",
|
|
|
|
|
"school",
|
|
|
|
|
"cross",
|
|
|
|
|
"park",
|
|
|
|
|
"parking",
|
|
|
|
|
"film",
|
|
|
|
|
"milestone",
|
|
|
|
|
"entertainment",
|
|
|
|
|
"circle-help",
|
|
|
|
|
"bell",
|
|
|
|
|
"target",
|
|
|
|
|
"thumbs-up",
|
2025-08-04 17:45:44 +02:00
|
|
|
] as const;
|
|
|
|
|
|
|
|
|
|
export type IconType = (typeof iconOptions)[number];
|
|
|
|
|
type LucidePropsType = ForwardRefExoticComponent<
|
2025-08-28 18:27:07 +02:00
|
|
|
Omit<LucideProps, "ref"> & RefAttributes<SVGSVGElement>
|
2025-08-04 17:45:44 +02:00
|
|
|
>;
|
|
|
|
|
interface IconMatrixProps extends ComponentPropsWithoutRef<LucidePropsType> {
|
2025-08-28 18:27:07 +02:00
|
|
|
type: IconType;
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const IconMatrix = ({ type, ...rest }: IconMatrixProps) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
switch (type) {
|
|
|
|
|
case "heater":
|
|
|
|
|
return <Heater {...rest} />;
|
|
|
|
|
case "error":
|
|
|
|
|
return <CircleX {...rest} />;
|
|
|
|
|
case "warning":
|
|
|
|
|
return <TriangleAlert {...rest} />;
|
|
|
|
|
case "sofa":
|
|
|
|
|
return <Sofa {...rest} />;
|
|
|
|
|
case "building":
|
|
|
|
|
return <Building {...rest} />;
|
|
|
|
|
case "building2":
|
|
|
|
|
return <Building2 {...rest} />;
|
|
|
|
|
case "wifi":
|
|
|
|
|
return <Wifi {...rest} />;
|
|
|
|
|
case "accessibility":
|
|
|
|
|
return <Accessibility {...rest} />;
|
|
|
|
|
case "baby":
|
|
|
|
|
return <Baby {...rest} />;
|
|
|
|
|
case "thermometer-sun":
|
|
|
|
|
return <ThermometerSun {...rest} />;
|
|
|
|
|
case "circle-plus":
|
|
|
|
|
return <CirclePlus {...rest} />;
|
|
|
|
|
case "circle-dot":
|
|
|
|
|
return <CircleDot {...rest} />;
|
|
|
|
|
case "dot":
|
|
|
|
|
return <Dot {...rest} />;
|
|
|
|
|
case "cog":
|
|
|
|
|
return <Cog {...rest} />;
|
|
|
|
|
case "check":
|
|
|
|
|
return <CheckCircleIcon {...rest} />;
|
|
|
|
|
case "search":
|
|
|
|
|
return <Search {...rest} />;
|
|
|
|
|
case "contact":
|
|
|
|
|
return <Contact {...rest} />;
|
|
|
|
|
case "euro":
|
|
|
|
|
return <Euro {...rest} />;
|
|
|
|
|
case "dashboard":
|
|
|
|
|
return <LayoutDashboard {...rest} />;
|
|
|
|
|
case "settings":
|
|
|
|
|
return <SlidersHorizontal {...rest} />;
|
|
|
|
|
case "user":
|
|
|
|
|
return <User {...rest} />;
|
|
|
|
|
case "users":
|
|
|
|
|
return <Users {...rest} />;
|
|
|
|
|
case "messages":
|
|
|
|
|
return <MessagesSquare {...rest} />;
|
|
|
|
|
case "message":
|
|
|
|
|
return <MessageSquareText {...rest} />;
|
|
|
|
|
case "arrowR":
|
|
|
|
|
return <ArrowRight {...rest} />;
|
|
|
|
|
case "arrowL":
|
|
|
|
|
return <ArrowLeft {...rest} />;
|
|
|
|
|
case "attachment":
|
|
|
|
|
return <Paperclip {...rest} />;
|
|
|
|
|
case "cart":
|
|
|
|
|
return <ShoppingCart {...rest} />;
|
|
|
|
|
case "star":
|
|
|
|
|
return <Star {...rest} />;
|
|
|
|
|
case "house":
|
|
|
|
|
return <House {...rest} />;
|
|
|
|
|
case "heart":
|
|
|
|
|
return <Heart {...rest} />;
|
|
|
|
|
case "info":
|
|
|
|
|
return <Info {...rest} />;
|
|
|
|
|
case "mail":
|
|
|
|
|
return <Mail {...rest} />;
|
|
|
|
|
case "school":
|
|
|
|
|
return <GraduationCap {...rest} />;
|
|
|
|
|
case "cross":
|
|
|
|
|
return <Cross {...rest} />;
|
|
|
|
|
case "park":
|
|
|
|
|
return <Trees {...rest} />;
|
|
|
|
|
case "parking":
|
|
|
|
|
return <CircleParking {...rest} />;
|
|
|
|
|
case "film":
|
|
|
|
|
return <Film {...rest} />;
|
|
|
|
|
case "milestone":
|
|
|
|
|
return <Milestone {...rest} />;
|
|
|
|
|
case "entertainment":
|
|
|
|
|
return <FerrisWheel {...rest} />;
|
|
|
|
|
case "circle-help":
|
|
|
|
|
return <QuestionMarkCircleIcon {...rest} />;
|
|
|
|
|
case "bell":
|
|
|
|
|
return <BellRing {...rest} />;
|
|
|
|
|
case "target":
|
|
|
|
|
return <Target {...rest} />;
|
|
|
|
|
case "thumbs-up":
|
|
|
|
|
return <ThumbsUp {...rest} />;
|
2025-09-01 16:48:03 +02:00
|
|
|
default:
|
|
|
|
|
return <div>Icon not found</div>;
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const IconMatrixTooltip = () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<TooltipProvider delayDuration={300}>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger className="flex flex-row gap-1" type="button">
|
|
|
|
|
Lista Icone <Pointer className="size-4" />
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
|
|
|
|
<div className="grid grid-cols-4 gap-2">
|
|
|
|
|
{iconOptions.map((icon) => (
|
|
|
|
|
<button
|
2025-08-29 16:18:32 +02:00
|
|
|
className="bg-muted flex flex-col items-center rounded-xl p-2 hover:opacity-75"
|
|
|
|
|
key={icon}
|
2025-08-28 18:27:07 +02:00
|
|
|
onClick={async () => {
|
|
|
|
|
await navigator.clipboard.writeText(icon);
|
|
|
|
|
toast("Copiato", { icon: "📋" });
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
type="button"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
2025-08-29 16:18:32 +02:00
|
|
|
<IconMatrix className="size-6" type={icon} />
|
2025-08-28 18:27:07 +02:00
|
|
|
<span className="text-sm">{icon}</span>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</TooltipProvider>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
/*
|
|
|
|
|
type IRP_readOnly = {
|
|
|
|
|
readOnly: true;
|
|
|
|
|
value: number;
|
|
|
|
|
setValue?: never;
|
|
|
|
|
};
|
|
|
|
|
type IRP_dynamic = {
|
|
|
|
|
readOnly?: false;
|
|
|
|
|
value: number;
|
|
|
|
|
setValue: (value: number) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type IconRatingProps = {
|
|
|
|
|
maxRating?: number;
|
|
|
|
|
icon: IconType;
|
|
|
|
|
outline?: boolean;
|
|
|
|
|
zeroable?: boolean;
|
|
|
|
|
iconClassName?: string;
|
|
|
|
|
} & (IRP_readOnly | IRP_dynamic);
|
|
|
|
|
|
|
|
|
|
export const IconRatingComonent = ({
|
|
|
|
|
maxRating = 5,
|
|
|
|
|
value,
|
|
|
|
|
setValue,
|
|
|
|
|
icon,
|
|
|
|
|
outline = false,
|
|
|
|
|
zeroable = false,
|
|
|
|
|
iconClassName,
|
|
|
|
|
readOnly = false,
|
|
|
|
|
}: IconRatingProps) => {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex justify-center",
|
|
|
|
|
!readOnly &&
|
|
|
|
|
(outline
|
|
|
|
|
? "[&>*:has(~*:hover)]:stroke-yellow-500 [&>*:hover]:stroke-yellow-500"
|
|
|
|
|
: "[&>*:has(~*:hover)]:fill-yellow-500 [&>*:hover]:fill-yellow-500"),
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{Array.from({ length: maxRating }, (_, i) => (
|
|
|
|
|
<IconMatrix
|
|
|
|
|
type={icon}
|
|
|
|
|
key={i}
|
|
|
|
|
className={cn(
|
|
|
|
|
"size-10 px-1",
|
|
|
|
|
iconClassName,
|
|
|
|
|
outline ? "fill-none stroke-neutral-300" : "fill-neutral-300 stroke-none",
|
|
|
|
|
i < value
|
|
|
|
|
? outline
|
|
|
|
|
? "stroke-yellow-500"
|
|
|
|
|
: "fill-yellow-500"
|
|
|
|
|
: "",
|
|
|
|
|
)}
|
|
|
|
|
{...(!readOnly &&
|
|
|
|
|
setValue != undefined && {
|
|
|
|
|
onClick: () => {
|
|
|
|
|
if (zeroable && value == 1 && i == 0) {
|
|
|
|
|
setValue(0);
|
|
|
|
|
} else {
|
|
|
|
|
setValue(i + 1);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
})}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
*/
|
|
|
|
|
export const ShieldExclamationIcon = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
className,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
className?: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => (
|
2025-08-28 18:27:07 +02:00
|
|
|
<svg
|
|
|
|
|
className={className}
|
2025-08-29 16:18:32 +02:00
|
|
|
fill="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<title>Shield</title>
|
|
|
|
|
<path
|
|
|
|
|
clipRule="evenodd"
|
2025-08-29 16:18:32 +02:00
|
|
|
d="M11.484 2.17a.75.75 0 0 1 1.032 0 11.209 11.209 0 0 0 7.877 3.08.75.75 0 0 1 .722.515 12.74 12.74 0 0 1 .635 3.985c0 5.942-4.064 10.933-9.563 12.348a.749.749 0 0 1-.374 0C6.314 20.683 2.25 15.692 2.25 9.75c0-1.39.223-2.73.635-3.985a.75.75 0 0 1 .722-.516l.143.001c2.996 0 5.718-1.17 7.734-3.08ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75ZM12 15a.75.75 0 0 0-.75.75v.008c0 .414.336.75.75.75h.008a.75.75 0 0 0 .75-.75v-.008a.75.75 0 0 0-.75-.75H12Z"
|
|
|
|
|
fillRule="evenodd"
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</svg>
|
2025-08-04 17:45:44 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const QuestionMarkCircleIcon = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
className,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
className?: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => (
|
2025-08-28 18:27:07 +02:00
|
|
|
<svg
|
|
|
|
|
className={className}
|
2025-08-29 16:18:32 +02:00
|
|
|
fill="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<title>QuestionMark</title>
|
|
|
|
|
<path
|
|
|
|
|
clipRule="evenodd"
|
2025-08-29 16:18:32 +02:00
|
|
|
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm11.378-3.917c-.89-.777-2.366-.777-3.255 0a.75.75 0 0 1-.988-1.129c1.454-1.272 3.776-1.272 5.23 0 1.513 1.324 1.513 3.518 0 4.842a3.75 3.75 0 0 1-.837.552c-.676.328-1.028.774-1.028 1.152v.75a.75.75 0 0 1-1.5 0v-.75c0-1.279 1.06-2.107 1.875-2.502.182-.088.351-.199.503-.331.83-.727.83-1.857 0-2.584ZM12 18a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
|
|
|
|
|
fillRule="evenodd"
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</svg>
|
2025-08-04 17:45:44 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const HomeIcon = ({ className }: { className?: string }) => (
|
2025-08-28 18:27:07 +02:00
|
|
|
<svg
|
2025-08-29 16:18:32 +02:00
|
|
|
className={className}
|
2025-08-28 18:27:07 +02:00
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
2025-08-29 16:18:32 +02:00
|
|
|
strokeWidth={1.5}
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<title>Home</title>
|
|
|
|
|
<path
|
2025-08-29 16:18:32 +02:00
|
|
|
d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
|
2025-08-28 18:27:07 +02:00
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
2025-08-04 17:45:44 +02:00
|
|
|
);
|
|
|
|
|
const CheckCircleIcon = ({ className }: { className?: string }) => (
|
2025-08-28 18:27:07 +02:00
|
|
|
<svg
|
|
|
|
|
className={className}
|
2025-08-29 16:18:32 +02:00
|
|
|
fill="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<title>CheckCircle</title>
|
|
|
|
|
<path
|
|
|
|
|
clipRule="evenodd"
|
2025-08-29 16:18:32 +02:00
|
|
|
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
|
|
|
|
fillRule="evenodd"
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</svg>
|
2025-08-04 17:45:44 +02:00
|
|
|
);
|