cleanup
This commit is contained in:
parent
94b603b83c
commit
7de7d90b95
16 changed files with 15 additions and 660 deletions
|
|
@ -192,7 +192,7 @@ export const NewColumn = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => {
|
const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => {
|
||||||
const [newItemName, setNewItemName] = useState("");
|
const [newItemName, setNewItemName] = useState("");
|
||||||
const [newItemDescrizione, setNewItemDescrizione] = useState("");
|
const [newItemDescrizione, setNewItemDescrizione] = useState("");
|
||||||
const [newItemUserid, setNewItemUserid] = useState<UsersId | null>(null);
|
const [newItemUserid, setNewItemUserid] = useState<UsersId | null>(null);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { cn } from "~/lib/utils";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
export interface NumberInputProps
|
interface NumberInputProps
|
||||||
extends Omit<NumericFormatProps, "value" | "onValueChange"> {
|
extends Omit<NumericFormatProps, "value" | "onValueChange"> {
|
||||||
stepper?: number;
|
stepper?: number;
|
||||||
thousandSeparator?: string;
|
thousandSeparator?: string;
|
||||||
|
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
import { type AnimationSequence, useAnimate } from "framer-motion";
|
|
||||||
import {
|
|
||||||
type ComponentPropsWithoutRef,
|
|
||||||
forwardRef,
|
|
||||||
useImperativeHandle,
|
|
||||||
} from "react";
|
|
||||||
import { IconMatrix, type IconType } from "~/components/IconComponents";
|
|
||||||
import { Button } from "~/components/ui/button";
|
|
||||||
import { cn } from "~/lib/utils";
|
|
||||||
|
|
||||||
const randomNumberBetween = (min: number, max: number) => {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface AnimatedButtonRef {
|
|
||||||
sparkle: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AnimatedButtonProps extends ComponentPropsWithoutRef<typeof Button> {
|
|
||||||
sparklesRadius?: number;
|
|
||||||
sparklesNumber?: number;
|
|
||||||
sparklesClassName?: string;
|
|
||||||
sparklesIcon?: IconType;
|
|
||||||
sparklesScale?: [number, number];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AnimatedButton = forwardRef<
|
|
||||||
AnimatedButtonRef,
|
|
||||||
AnimatedButtonProps
|
|
||||||
>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
sparklesRadius = 100,
|
|
||||||
sparklesNumber = 20,
|
|
||||||
sparklesClassName = "fill-blue-500 stroke-transparent",
|
|
||||||
sparklesIcon = "star",
|
|
||||||
sparklesScale = [1.5, 2.5],
|
|
||||||
...props
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const [scope, animate] = useAnimate();
|
|
||||||
|
|
||||||
const Sparkles = () => {
|
|
||||||
const sparkles = Array.from({ length: sparklesNumber });
|
|
||||||
const sparklesAnimation: AnimationSequence = sparkles.map((_, index) => [
|
|
||||||
`.sparkle-${index}`,
|
|
||||||
{
|
|
||||||
opacity: 1,
|
|
||||||
scale: randomNumberBetween(sparklesScale[0], sparklesScale[1]),
|
|
||||||
x: randomNumberBetween(-sparklesRadius, sparklesRadius),
|
|
||||||
y: randomNumberBetween(-sparklesRadius, sparklesRadius),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
at: "<",
|
|
||||||
delay: index * 0.01,
|
|
||||||
duration: 0.4,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const sparklesFadeOut: AnimationSequence = sparkles.map((_, index) => [
|
|
||||||
`.sparkle-${index}`,
|
|
||||||
{
|
|
||||||
opacity: 0,
|
|
||||||
scale: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
at: "<",
|
|
||||||
duration: 0.3,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const sparklesReset: AnimationSequence = sparkles.map((_, index) => [
|
|
||||||
`.sparkle-${index}`,
|
|
||||||
{
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
duration: 0.000001,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
animate([
|
|
||||||
...sparklesReset,
|
|
||||||
["button", { scale: 0.8 }, { at: "<", duration: 0.1 }],
|
|
||||||
["button", { scale: 1 }, { duration: 0.1 }],
|
|
||||||
...sparklesAnimation,
|
|
||||||
["button", { duration: 0.000001 }],
|
|
||||||
...sparklesFadeOut,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
sparkle: Sparkles,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full" ref={scope}>
|
|
||||||
<Button
|
|
||||||
{...props}
|
|
||||||
aria-label="Animated Button"
|
|
||||||
className={cn("relative", props.className)}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
<span
|
|
||||||
aria-hidden
|
|
||||||
className="pointer-events-none absolute inset-0 z-10 block"
|
|
||||||
>
|
|
||||||
{Array.from({ length: sparklesNumber }).map((_, index) => (
|
|
||||||
<IconMatrix
|
|
||||||
className={cn(
|
|
||||||
`absolute top-1/2 left-1/2 opacity-0 sparkle-${index}`,
|
|
||||||
sparklesClassName,
|
|
||||||
)}
|
|
||||||
// biome-ignore lint/suspicious/noArrayIndexKey: <index is safe here>
|
|
||||||
key={index}
|
|
||||||
type={sparklesIcon}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
AnimatedButton.displayName = "AnimatedButton";
|
|
||||||
|
|
@ -311,75 +311,3 @@ export const PricingComponentConsulenza = ({
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PrezziShow = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { data: prezziario, isLoading } =
|
|
||||||
api.prezziario.getPrezziPerTipologiaAll.useQuery();
|
|
||||||
if (isLoading) return <LoadingPage />;
|
|
||||||
if (!prezziario) return null;
|
|
||||||
|
|
||||||
const pricingBreve = prezziario.transitorio.saldi.map((s) => ({
|
|
||||||
downPayment: prezziario.transitorio.acconto.prezzo_cent / 100,
|
|
||||||
secondPayment: s.prezzo_cent / 100,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const pricingStabile = prezziario.stabile.saldi.map((s) => ({
|
|
||||||
downPayment: prezziario.stabile.acconto.prezzo_cent / 100,
|
|
||||||
secondPayment: s.prezzo_cent / 100,
|
|
||||||
}));
|
|
||||||
return (
|
|
||||||
<div className="mx-auto flex w-full flex-col items-center justify-center gap-8 py-6 text-primary sm:max-w-5xl sm:flex-row">
|
|
||||||
<div className="flex w-full flex-col gap-2 text-start sm:w-2/5 sm:text-left">
|
|
||||||
<h1 className="font-bold text-3xl text-primary">
|
|
||||||
Quanto costa il servizio?
|
|
||||||
</h1>
|
|
||||||
<p className="text-lg text-muted-foreground">
|
|
||||||
Il nostro servizio è pensato per essere accessibile e conveniente, con
|
|
||||||
tariffe chiare e senza sorprese. Basate sul tipo di affitto che
|
|
||||||
desideri, le nostre tariffe sono suddivise in due categorie
|
|
||||||
principali: Affitto Transitorio e Affitto Stabile.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Tabs className="w-full sm:w-3/5" defaultValue="transitorio">
|
|
||||||
<TabsList className="flex h-auto w-full items-center justify-between">
|
|
||||||
<TabsTrigger className="w-full text-xl" value="transitorio">
|
|
||||||
Affitto Transitorio
|
|
||||||
</TabsTrigger>
|
|
||||||
<TabsTrigger className="w-full text-xl" value="stabile">
|
|
||||||
Affitto Stabile
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
<TabsContent value="transitorio">
|
|
||||||
<PricingComponent
|
|
||||||
className="border-transitorio bg-transitorio"
|
|
||||||
cta={t.pricing_cmp.cta_breve}
|
|
||||||
opzioni={[
|
|
||||||
t.pricing_cmp.breve_option1,
|
|
||||||
t.pricing_cmp.breve_option2,
|
|
||||||
t.pricing_cmp.breve_option3,
|
|
||||||
t.pricing_cmp.breve_option4,
|
|
||||||
t.pricing_cmp.breve_option5,
|
|
||||||
]}
|
|
||||||
pricingData={pricingBreve}
|
|
||||||
tipo={TipologiaPosizioneEnum.Transitorio}
|
|
||||||
title={t.prezzi.titolo_transitori}
|
|
||||||
/>
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent value="stabile">
|
|
||||||
<PricingComponent
|
|
||||||
className="border-stabile bg-stabile"
|
|
||||||
cta={t.pricing_cmp.cta_stabile}
|
|
||||||
opzioni={[
|
|
||||||
t.pricing_cmp.stabile_option1,
|
|
||||||
t.pricing_cmp.stabile_option2,
|
|
||||||
]}
|
|
||||||
pricingData={pricingStabile}
|
|
||||||
tipo={TipologiaPosizioneEnum.Stabile}
|
|
||||||
title={t.prezzi.titolo_stabile}
|
|
||||||
/>
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ export function SchedaAnnuncioStampabile({ data }: { data: Annunci }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SchedaAnnuncio({ data }: { data: Annunci }) {
|
function SchedaAnnuncio({ data }: { data: Annunci }) {
|
||||||
const companyAddress =
|
const companyAddress =
|
||||||
"Via Beata Giovanna, 1 a Bassano del Grappa (VI)\nTel. +39 0424529869\nEmail: arca@infoalloggi.it";
|
"Via Beata Giovanna, 1 a Bassano del Grappa (VI)\nTel. +39 0424529869\nEmail: arca@infoalloggi.it";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
import { add, differenceInDays } from "date-fns";
|
|
||||||
import { CalendarClock, CalendarDays, Clock, Hourglass } from "lucide-react";
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionContent,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionTrigger,
|
|
||||||
} from "~/components/ui/accordion";
|
|
||||||
import { Card, CardContent } from "~/components/ui/card";
|
|
||||||
import { Progress } from "~/components/ui/progress"; // Import the Progress component
|
|
||||||
import { useMediaQuery } from "~/hooks/use-media-query";
|
|
||||||
|
|
||||||
export const ServizioDuration = ({
|
|
||||||
decorrenza,
|
|
||||||
}: {
|
|
||||||
decorrenza: Date | null;
|
|
||||||
}) => {
|
|
||||||
const isDesktop = useMediaQuery("(min-width: 768px)");
|
|
||||||
if (!decorrenza) {
|
|
||||||
return null; // If decorrenza is null, do not render the component
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Accordion
|
|
||||||
collapsible
|
|
||||||
defaultValue={isDesktop ? "validity" : undefined}
|
|
||||||
type="single"
|
|
||||||
>
|
|
||||||
<AccordionItem value="validity">
|
|
||||||
<AccordionTrigger>
|
|
||||||
<div className="flex items-center gap-2 font-medium text-base">
|
|
||||||
<Clock className="size-6 text-primary" />
|
|
||||||
Validità servizio
|
|
||||||
</div>
|
|
||||||
</AccordionTrigger>
|
|
||||||
<AccordionContent>
|
|
||||||
<ServiceDurationDisplay
|
|
||||||
decorrenza={decorrenza}
|
|
||||||
recessoDays={14}
|
|
||||||
scadenza={add(decorrenza, { days: 60 })}
|
|
||||||
/>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ServiceDurationDisplayProps {
|
|
||||||
decorrenza: Date;
|
|
||||||
scadenza: Date;
|
|
||||||
recessoDays: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sleek, light-themed component for displaying service duration,
|
|
||||||
* including start date, end date, and trial period, with a progress bar.
|
|
||||||
*/
|
|
||||||
function ServiceDurationDisplay({
|
|
||||||
decorrenza,
|
|
||||||
scadenza,
|
|
||||||
recessoDays,
|
|
||||||
}: ServiceDurationDisplayProps) {
|
|
||||||
// Calculate remaining days
|
|
||||||
const diffDays = differenceInDays(scadenza, new Date()) + 1;
|
|
||||||
const remainingDaysText = diffDays >= 0 ? `${diffDays} giorni` : "Concluso";
|
|
||||||
|
|
||||||
const progressPercentage = ((60 - diffDays) / 60) * 100; // Assuming 60 days is the full duration for 100% progress
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card className="w-full rounded-xl p-2 shadow-lg sm:py-3">
|
|
||||||
<CardContent className="flex flex-col gap-5 px-1">
|
|
||||||
<div className="flex w-full flex-col justify-between gap-4 sm:flex-row sm:items-center">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<CalendarDays aria-hidden="true" className="h-6 w-6 text-primary" />
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground text-sm">Decorrenza</div>
|
|
||||||
<div className="font-semibold text-foreground text-lg">
|
|
||||||
{decorrenza.toLocaleDateString("it-IT", {
|
|
||||||
day: "2-digit",
|
|
||||||
month: "long",
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<CalendarClock
|
|
||||||
aria-hidden="true"
|
|
||||||
className="h-6 w-6 text-primary"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground text-sm">Scadenza</div>
|
|
||||||
<div className="font-semibold text-foreground text-lg">
|
|
||||||
{scadenza.toLocaleDateString("it-IT", {
|
|
||||||
day: "2-digit",
|
|
||||||
month: "long",
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<Hourglass aria-hidden="true" className="h-6 w-6 text-primary" />
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground text-sm">
|
|
||||||
Periodo di recesso
|
|
||||||
</div>
|
|
||||||
<div className="font-semibold text-foreground text-lg">
|
|
||||||
{recessoDays} giorni
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Progress Bar */}
|
|
||||||
<div className="pt-2">
|
|
||||||
<Progress
|
|
||||||
aria-label="Service duration progress"
|
|
||||||
className="h-2"
|
|
||||||
value={progressPercentage}
|
|
||||||
/>
|
|
||||||
<div className="mt-1 text-right text-muted-foreground text-sm">
|
|
||||||
{remainingDaysText}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -154,7 +154,7 @@ const ServizioInfos = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ServizioBasicActions = ({ className }: { className?: string }) => {
|
const ServizioBasicActions = ({ className }: { className?: string }) => {
|
||||||
const { servizio, isAdmin } = useServizio();
|
const { servizio, isAdmin } = useServizio();
|
||||||
|
|
||||||
const canUserEdit =
|
const canUserEdit =
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { cn } from "~/lib/utils";
|
|
||||||
import { run } from "~/utils/utils";
|
|
||||||
|
|
||||||
export function SubscriptionStatus({
|
|
||||||
status,
|
|
||||||
errorMsg,
|
|
||||||
reset,
|
|
||||||
}: {
|
|
||||||
status: "idle" | "connecting" | "pending" | "error";
|
|
||||||
errorMsg: string | undefined;
|
|
||||||
reset: () => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"rounded-full p-2 text-sm transition-colors",
|
|
||||||
run(() => {
|
|
||||||
switch (status) {
|
|
||||||
case "idle":
|
|
||||||
case "connecting":
|
|
||||||
return "bg-white text-gray-500 dark:bg-gray-900 dark:text-gray-400";
|
|
||||||
case "error":
|
|
||||||
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200";
|
|
||||||
case "pending":
|
|
||||||
return "bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-200";
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{run(() => {
|
|
||||||
switch (status) {
|
|
||||||
case "idle":
|
|
||||||
case "connecting":
|
|
||||||
// treat idle and connecting the same
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
Connecting...
|
|
||||||
{errorMsg && " (There are connection problems)"} {errorMsg}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "error":
|
|
||||||
// something went wrong
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
Error - <em>{errorMsg}</em>
|
|
||||||
<button
|
|
||||||
className="hover:underline"
|
|
||||||
onClick={() => {
|
|
||||||
reset();
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Try Again
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case "pending":
|
|
||||||
// we are polling for new messages
|
|
||||||
return <div>Connected - awaiting messages</div>;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { Slot } from "@radix-ui/react-slot";
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "~/lib/utils";
|
|
||||||
|
|
||||||
type TimelineContextProps = {
|
|
||||||
orientation: "horizontal" | "vertical";
|
|
||||||
};
|
|
||||||
|
|
||||||
const TimelineContext = React.createContext<TimelineContextProps | null>(null);
|
|
||||||
|
|
||||||
function useTimeline() {
|
|
||||||
const context = React.useContext(TimelineContext);
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useTimeline must be used within a <Timeline />.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineProps extends React.ComponentPropsWithoutRef<"ol"> {
|
|
||||||
orientation?: "horizontal" | "vertical";
|
|
||||||
}
|
|
||||||
|
|
||||||
function Timeline({
|
|
||||||
className,
|
|
||||||
orientation = "vertical",
|
|
||||||
...props
|
|
||||||
}: TimelineProps) {
|
|
||||||
return (
|
|
||||||
<TimelineContext value={{ orientation }}>
|
|
||||||
<ol
|
|
||||||
className={cn(
|
|
||||||
"flex",
|
|
||||||
orientation === "vertical" && "flex-col",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
//role="list"
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</TimelineContext>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
interface TimelineItemProps extends React.ComponentPropsWithoutRef<"li"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineItem({ className, asChild, ...props }: TimelineItemProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "li";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(
|
|
||||||
"flex gap-4",
|
|
||||||
orientation === "horizontal" && "flex-col",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-item"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineSeparatorProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineSeparator({
|
|
||||||
className,
|
|
||||||
asChild,
|
|
||||||
...props
|
|
||||||
}: TimelineSeparatorProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(
|
|
||||||
"flex items-center",
|
|
||||||
orientation === "vertical" && "flex-col",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-separator"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineDotProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
variant?: "default" | "outline";
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineDot({
|
|
||||||
variant = "default",
|
|
||||||
className,
|
|
||||||
asChild,
|
|
||||||
...props
|
|
||||||
}: TimelineDotProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(
|
|
||||||
"flex size-4 items-center justify-center empty:after:block empty:after:rounded-full empty:after:outline-current [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
orientation === "vertical" && "mt-1",
|
|
||||||
variant === "default" && "empty:after:size-2.5 empty:after:bg-current",
|
|
||||||
variant === "outline" && "empty:after:size-2 empty:after:outline",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-dot"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineConnectorProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineConnector({
|
|
||||||
className,
|
|
||||||
asChild,
|
|
||||||
...props
|
|
||||||
}: TimelineConnectorProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(
|
|
||||||
"flex-1 bg-border",
|
|
||||||
orientation === "vertical" && "my-2 w-0.5",
|
|
||||||
orientation === "horizontal" && "mx-2 h-0.5",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-connector"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineContentProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineContent({
|
|
||||||
className,
|
|
||||||
asChild,
|
|
||||||
...props
|
|
||||||
}: TimelineContentProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn(
|
|
||||||
"flex-1",
|
|
||||||
orientation === "vertical" && "pb-7 first:text-right last:text-left",
|
|
||||||
orientation === "horizontal" && "pr-7",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-content"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineTitleProps extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineTitle({ className, asChild, ...props }: TimelineTitleProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={className}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-title"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TimelineDescriptionProps
|
|
||||||
extends React.ComponentPropsWithoutRef<"div"> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TimelineDescription({
|
|
||||||
className,
|
|
||||||
asChild,
|
|
||||||
...props
|
|
||||||
}: TimelineDescriptionProps) {
|
|
||||||
const { orientation } = useTimeline();
|
|
||||||
const Comp = asChild ? Slot : "div";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
className={cn("text-[0.8em] text-muted-foreground", className)}
|
|
||||||
data-orientation={orientation}
|
|
||||||
data-slot="timeline-description"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Timeline,
|
|
||||||
TimelineItem,
|
|
||||||
TimelineSeparator,
|
|
||||||
TimelineDot,
|
|
||||||
TimelineConnector,
|
|
||||||
TimelineContent,
|
|
||||||
TimelineTitle,
|
|
||||||
TimelineDescription,
|
|
||||||
useTimeline,
|
|
||||||
};
|
|
||||||
|
|
@ -28,7 +28,7 @@ import type { UsersId } from "~/schemas/public/Users";
|
||||||
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
|
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
export type UploadCallback = (args: {
|
type UploadCallback = (args: {
|
||||||
storageIds: string[];
|
storageIds: string[];
|
||||||
userStorageIds: UsersStorageUserStorageId[];
|
userStorageIds: UsersStorageUserStorageId[];
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
|
||||||
|
|
@ -38,18 +38,3 @@ export const camereTesti = ({
|
||||||
}
|
}
|
||||||
return "N/A";
|
return "N/A";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTitoloTranslation = ({
|
|
||||||
locale,
|
|
||||||
titolo_it,
|
|
||||||
titolo_en,
|
|
||||||
}: {
|
|
||||||
locale: string;
|
|
||||||
titolo_it: string | null;
|
|
||||||
titolo_en: string | null;
|
|
||||||
}) => {
|
|
||||||
if (locale === "it") {
|
|
||||||
return titolo_it != null ? titolo_it : "";
|
|
||||||
}
|
|
||||||
return titolo_en != null ? titolo_en : "";
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,25 @@ import type { Nazioni } from "~/schemas/public/Nazioni";
|
||||||
import type { Provincie } from "~/schemas/public/Provincie";
|
import type { Provincie } from "~/schemas/public/Provincie";
|
||||||
|
|
||||||
export const ITALY_CATASTO_CODE = "0000";
|
export const ITALY_CATASTO_CODE = "0000";
|
||||||
export const isNazioneCatastoValid = (str: string) => {
|
const isNazioneCatastoValid = (str: string) => {
|
||||||
if (str.length !== 4) return false;
|
if (str.length !== 4) return false;
|
||||||
if (str === ITALY_CATASTO_CODE) return true;
|
if (str === ITALY_CATASTO_CODE) return true;
|
||||||
return /^Z[0-9]{3}$/.test(str);
|
return /^Z[0-9]{3}$/.test(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isComuneCatastoValid = (str: string) => {
|
const isComuneCatastoValid = (str: string) => {
|
||||||
if (str.length !== 4) return false;
|
if (str.length !== 4) return false;
|
||||||
return /^[A-Z][0-9]{3}$/.test(str);
|
return /^[A-Z][0-9]{3}$/.test(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getComuneFromCatasto = (
|
const getComuneFromCatasto = (
|
||||||
codice_catasto: string,
|
codice_catasto: string,
|
||||||
comuni_options: Comuni[],
|
comuni_options: Comuni[],
|
||||||
): Comuni | undefined => {
|
): Comuni | undefined => {
|
||||||
return comuni_options.find((c) => c.catasto === codice_catasto);
|
return comuni_options.find((c) => c.catasto === codice_catasto);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNazioneFromCatasto = (
|
const getNazioneFromCatasto = (
|
||||||
codice_catasto: string,
|
codice_catasto: string,
|
||||||
nazioni_options: Nazioni[],
|
nazioni_options: Nazioni[],
|
||||||
): Nazioni | undefined => {
|
): Nazioni | undefined => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { api } from "~/utils/api";
|
||||||
|
|
||||||
type UserViewContextType = UserWAnagrafica & { chatid: ChatsChatid | null };
|
type UserViewContextType = UserWAnagrafica & { chatid: ChatsChatid | null };
|
||||||
|
|
||||||
export const UserViewContext = createContext<UserViewContextType | null>(null);
|
const UserViewContext = createContext<UserViewContextType | null>(null);
|
||||||
|
|
||||||
export const UserViewProvider: FC<{ children: ReactNode; userId: UsersId }> = ({
|
export const UserViewProvider: FC<{ children: ReactNode; userId: UsersId }> = ({
|
||||||
children,
|
children,
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,26 @@ import {
|
||||||
updatePotenziali,
|
updatePotenziali,
|
||||||
} from "~/server/services/potenziali.service";
|
} from "~/server/services/potenziali.service";
|
||||||
|
|
||||||
export const zPotenzialiId = z.custom<PotenzialiId>(
|
const zPotenzialiId = z.custom<PotenzialiId>(
|
||||||
(val) => typeof val === "string" && val.length > 0,
|
(val) => typeof val === "string" && val.length > 0,
|
||||||
{ message: "Invalid PotenzialiId" },
|
{ message: "Invalid PotenzialiId" },
|
||||||
);
|
);
|
||||||
|
|
||||||
export const zNewPotenziali = z.custom<NewPotenziali>(
|
const zNewPotenziali = z.custom<NewPotenziali>(
|
||||||
(val) => {
|
(val) => {
|
||||||
return typeof val === "object" && val !== null;
|
return typeof val === "object" && val !== null;
|
||||||
},
|
},
|
||||||
{ message: "Invalid NewPotenziali" },
|
{ message: "Invalid NewPotenziali" },
|
||||||
);
|
);
|
||||||
|
|
||||||
export const zPotenzialiUpdate = z.custom<PotenzialiUpdate>(
|
const zPotenzialiUpdate = z.custom<PotenzialiUpdate>(
|
||||||
(val) => {
|
(val) => {
|
||||||
return typeof val === "object" && val !== null;
|
return typeof val === "object" && val !== null;
|
||||||
},
|
},
|
||||||
{ message: "Invalid PotenzialiUpdate" },
|
{ message: "Invalid PotenzialiUpdate" },
|
||||||
);
|
);
|
||||||
|
|
||||||
export const zPotenzialiGroupId = z.custom<PotenzialiGroupsId>(
|
const zPotenzialiGroupId = z.custom<PotenzialiGroupsId>(
|
||||||
(val) => typeof val === "string" && val.length > 0,
|
(val) => typeof val === "string" && val.length > 0,
|
||||||
{ message: "Invalid PotenzialiGroupsId" },
|
{ message: "Invalid PotenzialiGroupsId" },
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import type { Comuni, ComuniId } from "~/schemas/public/Comuni";
|
import type { ComuniId } from "~/schemas/public/Comuni";
|
||||||
import type { NazioniId } from "~/schemas/public/Nazioni";
|
import type { NazioniId } from "~/schemas/public/Nazioni";
|
||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
|
|
||||||
export type Comune = Pick<Comuni, "id" | "nome" | "sigla">;
|
|
||||||
export const getComuni = async () => {
|
export const getComuni = async () => {
|
||||||
try {
|
try {
|
||||||
return await db.selectFrom("comuni").selectAll().execute();
|
return await db.selectFrom("comuni").selectAll().execute();
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,3 @@ export async function comparePasswords({
|
||||||
export function generateSalt() {
|
export function generateSalt() {
|
||||||
return crypto.randomBytes(16).toString("hex").normalize();
|
return crypto.randomBytes(16).toString("hex").normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateTemporaryPassword() {
|
|
||||||
// Generate readable temporary password (e.g., "Abc12345")
|
|
||||||
return crypto.randomBytes(4).toString("hex");
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue