feat: add support for additional phone numbers and titles in user profiles
This commit is contained in:
parent
d600cac8ed
commit
e847aefe64
13 changed files with 218 additions and 23 deletions
5
apps/db/migrations/44_telefono_v2.up.sql
Normal file
5
apps/db/migrations/44_telefono_v2.up.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE IF EXISTS public.users
|
||||
ADD COLUMN IF NOT EXISTS extra_telefono_titolo text;
|
||||
|
||||
ALTER TABLE IF EXISTS public.users
|
||||
ADD COLUMN IF NOT EXISTS extra_telefono_numero text;
|
||||
|
|
@ -144,9 +144,20 @@ export const UserViewHeader = () => {
|
|||
<Link href={`https://wa.me/${data.telefono}`} target="_blank">
|
||||
<Button size="sm" variant="success">
|
||||
<WhatsAppIcon2 className="size-4 fill-white" />
|
||||
WhatsApp
|
||||
WhatsApp{data.extra_telefono_numero && ` (${data.nome})`}
|
||||
</Button>
|
||||
</Link>
|
||||
{data.extra_telefono_numero && (
|
||||
<Link
|
||||
href={`https://wa.me/${data.extra_telefono_numero}`}
|
||||
target="_blank"
|
||||
>
|
||||
<Button size="sm" variant="success">
|
||||
<WhatsAppIcon2 className="size-4 fill-white" />
|
||||
WhatsApp ({data.extra_telefono_titolo})
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{data.codice_fiscale ? (
|
||||
<IntestazioneMaker />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ import {
|
|||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuTrigger,
|
||||
} from "~/components/ui/context-menu";
|
||||
import { DataTable } from "~/components/ui/data-table";
|
||||
|
|
@ -161,7 +164,36 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
|||
<UserCog className="text-muted-foreground" /> Profilo
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
|
||||
{data.extra_telefono_numero ? (
|
||||
<ContextMenuSub>
|
||||
<ContextMenuSubTrigger className="flex w-full items-center gap-2">
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp
|
||||
</ContextMenuSubTrigger>
|
||||
<ContextMenuSubContent>
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
href={`https://wa.me/${data.telefono}`}
|
||||
target="_blank"
|
||||
>
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp ({data.nome})
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
href={`https://wa.me/${data.extra_telefono_numero}`}
|
||||
target="_blank"
|
||||
>
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp ({data.extra_telefono_titolo})
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSubContent>
|
||||
</ContextMenuSub>
|
||||
) : (
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
|
|
@ -172,6 +204,7 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
|||
WhatsApp
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ import {
|
|||
ContextMenuItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
} from "../ui/context-menu";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -66,10 +69,13 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
|||
|
||||
const tabledata = data.map((user) => {
|
||||
return {
|
||||
nome: user.nome,
|
||||
chatid: user.chatid,
|
||||
created_at: user.created_at,
|
||||
email: user.email,
|
||||
telefono: user.telefono,
|
||||
extra_telefono_numero: user.extra_telefono_numero,
|
||||
extra_telefono_titolo: user.extra_telefono_titolo,
|
||||
note: user.note,
|
||||
id: user.id,
|
||||
isAdmin: user.isAdmin ? "Admin" : "Utente",
|
||||
|
|
@ -190,6 +196,22 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
|||
title={columns_titles.telefono}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const { nome, telefono, extra_telefono_numero, extra_telefono_titolo } =
|
||||
row.original;
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<span>{telefono}</span>
|
||||
{extra_telefono_numero && (
|
||||
<>
|
||||
<span className="text-xs">{nome}</span>
|
||||
<span>{extra_telefono_numero}</span>
|
||||
<span className="text-xs">{extra_telefono_titolo}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
|
|
@ -361,6 +383,36 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
|||
<MessagesSquare /> Chat
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
{data.extra_telefono_numero ? (
|
||||
<ContextMenuSub>
|
||||
<ContextMenuSubTrigger className="flex w-full items-center gap-2">
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp
|
||||
</ContextMenuSubTrigger>
|
||||
<ContextMenuSubContent>
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
href={`https://wa.me/${data.telefono}`}
|
||||
target="_blank"
|
||||
>
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp ({data.nome})
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
href={`https://wa.me/${data.extra_telefono_numero}`}
|
||||
target="_blank"
|
||||
>
|
||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||
WhatsApp ({data.extra_telefono_titolo})
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
</ContextMenuSubContent>
|
||||
</ContextMenuSub>
|
||||
) : (
|
||||
<ContextMenuItem>
|
||||
<Link
|
||||
className="flex w-full items-center gap-2"
|
||||
|
|
@ -371,6 +423,8 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
|||
WhatsApp
|
||||
</Link>
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ import { api } from "~/utils/api";
|
|||
|
||||
type ProfileFromAccountProps = Pick<
|
||||
Users,
|
||||
"id" | "nome" | "cognome" | "email" | "telefono"
|
||||
| "id"
|
||||
| "nome"
|
||||
| "cognome"
|
||||
| "email"
|
||||
| "telefono"
|
||||
| "extra_telefono_numero"
|
||||
| "extra_telefono_titolo"
|
||||
> &
|
||||
Partial<Pick<Users, "isAdmin">>;
|
||||
|
||||
|
|
@ -30,6 +36,8 @@ export const ProfileFormAccount = ({
|
|||
cognome,
|
||||
email,
|
||||
telefono,
|
||||
extra_telefono_numero,
|
||||
extra_telefono_titolo,
|
||||
isAdmin,
|
||||
}: ProfileFromAccountProps) => {
|
||||
const profileFormSchema = z.object({
|
||||
|
|
@ -38,6 +46,8 @@ export const ProfileFormAccount = ({
|
|||
isAdmin: z.boolean().optional(),
|
||||
nome: z.string().nonempty("Inserisci un nome valido"),
|
||||
telefono: z.string().nonempty("Inserisci un numero di telefono"),
|
||||
extra_telefono_numero: z.string().optional(),
|
||||
extra_telefono_titolo: z.string().optional(),
|
||||
});
|
||||
|
||||
type ProfileFormValues = z.infer<typeof profileFormSchema>;
|
||||
|
|
@ -48,6 +58,8 @@ export const ProfileFormAccount = ({
|
|||
isAdmin: isAdmin,
|
||||
nome: nome || "",
|
||||
telefono,
|
||||
extra_telefono_numero: extra_telefono_numero || "",
|
||||
extra_telefono_titolo: extra_telefono_titolo || "",
|
||||
};
|
||||
const { locale, t } = useTranslation();
|
||||
z.config(z.locales[locale]());
|
||||
|
|
@ -78,6 +90,7 @@ export const ProfileFormAccount = ({
|
|||
data: {
|
||||
...fields,
|
||||
telefono: fields.telefono?.replace(/\s/g, ""),
|
||||
extra_telefono_numero: fields.extra_telefono_numero?.replace(/\s/g, ""),
|
||||
username: `${fields.cognome?.trim()} ${fields.nome?.trim()}`,
|
||||
isAdmin: fields.isAdmin || false,
|
||||
},
|
||||
|
|
@ -186,6 +199,57 @@ export const ProfileFormAccount = ({
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<DualInputLayout
|
||||
children1={
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="extra_telefono_numero"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex flex-wrap items-center gap-x-2">
|
||||
<FormLabel htmlFor="extra_telefono_numero">
|
||||
{t.profile.extra_telefono_numero}
|
||||
</FormLabel>
|
||||
<FormMessage />
|
||||
</div>
|
||||
|
||||
<FormControl>
|
||||
<PhoneInput
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
id="extra_telefono_numero"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
children2={
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="extra_telefono_titolo"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex flex-wrap items-center gap-x-2">
|
||||
<FormLabel htmlFor="extra_telefono_titolo">
|
||||
{t.profile.extra_telefono_titolo}
|
||||
</FormLabel>
|
||||
<FormMessage />
|
||||
</div>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder=""
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
id="extra_telefono_titolo"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{isAdmin !== undefined && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
|
|
|||
|
|
@ -1215,6 +1215,8 @@ The Stable Rent service, ideal for those with demonstrable work references and f
|
|||
email: "Email",
|
||||
nome: "Name",
|
||||
telefono: "Phone",
|
||||
extra_telefono_numero: "Additional phone",
|
||||
extra_telefono_titolo: "Additional phone contact",
|
||||
},
|
||||
proprietari: {
|
||||
contattaci: "Contact us",
|
||||
|
|
|
|||
|
|
@ -1213,6 +1213,8 @@ export const it: LangDict = {
|
|||
email: "Email",
|
||||
nome: "Nome",
|
||||
telefono: "Telefono",
|
||||
extra_telefono_numero: "Telefono aggiuntivo",
|
||||
extra_telefono_titolo: "Nominativo tel. aggiuntivo",
|
||||
},
|
||||
proprietari: {
|
||||
contattaci: "Contattaci",
|
||||
|
|
|
|||
|
|
@ -511,6 +511,8 @@ export type LangDict = {
|
|||
cognome: string;
|
||||
email: string;
|
||||
telefono: string;
|
||||
extra_telefono_numero: string;
|
||||
extra_telefono_titolo: string;
|
||||
aggiorna: string;
|
||||
aggiornato: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
|||
<ProfileFormAccount
|
||||
cognome={userData.cognome}
|
||||
email={userData.email}
|
||||
extra_telefono_numero={userData.extra_telefono_numero}
|
||||
extra_telefono_titolo={userData.extra_telefono_titolo}
|
||||
id={userData.id}
|
||||
isAdmin={userData.isAdmin}
|
||||
nome={userData.nome}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ const Dashboard: NextPageWithLayout = () => {
|
|||
<ProfileFormAccount
|
||||
cognome={userData.cognome}
|
||||
email={userData.email}
|
||||
extra_telefono_numero={userData.extra_telefono_numero}
|
||||
extra_telefono_titolo={userData.extra_telefono_titolo}
|
||||
id={userData.id}
|
||||
nome={userData.nome}
|
||||
telefono={userData.telefono}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ export default interface UsersTable {
|
|||
note: ColumnType<string | null, string | null, string | null>;
|
||||
|
||||
comms_enabled: ColumnType<boolean, boolean | undefined, boolean>;
|
||||
|
||||
extra_telefono_titolo: ColumnType<string | null, string | null, string | null>;
|
||||
|
||||
extra_telefono_numero: ColumnType<string | null, string | null, string | null>;
|
||||
}
|
||||
|
||||
export type Users = Selectable<UsersTable>;
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ export type UserWChatInfo = Pick<
|
|||
| "username"
|
||||
| "email"
|
||||
| "telefono"
|
||||
| "extra_telefono_numero"
|
||||
| "extra_telefono_titolo"
|
||||
| "isAdmin"
|
||||
| "isBlocked"
|
||||
| "created_at"
|
||||
|
|
@ -159,12 +161,16 @@ export type UserWChatInfo = Pick<
|
|||
export const getUsersWithChatInfoHandler = async (): Promise<
|
||||
UserWChatInfo[]
|
||||
> => {
|
||||
try {
|
||||
return await db
|
||||
.selectFrom("users")
|
||||
.select([
|
||||
"users.id",
|
||||
"users.nome",
|
||||
"users.username",
|
||||
"users.telefono",
|
||||
"users.extra_telefono_numero",
|
||||
"users.extra_telefono_titolo",
|
||||
"users.email",
|
||||
"users.isAdmin",
|
||||
"users.isBlocked",
|
||||
|
|
@ -193,4 +199,10 @@ export const getUsersWithChatInfoHandler = async (): Promise<
|
|||
.orderBy("users.id", "desc")
|
||||
.select(["users.id as id"])
|
||||
.execute();
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: `errore getUsersWithChatInfoHandler: ${(e as Error).message}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -125,6 +125,8 @@ export const getClientProfilo = async ({
|
|||
"cognome",
|
||||
"email",
|
||||
"telefono",
|
||||
"extra_telefono_numero",
|
||||
"extra_telefono_titolo",
|
||||
"created_at",
|
||||
"isAdmin",
|
||||
"isBlocked",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue