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">
|
<Link href={`https://wa.me/${data.telefono}`} target="_blank">
|
||||||
<Button size="sm" variant="success">
|
<Button size="sm" variant="success">
|
||||||
<WhatsAppIcon2 className="size-4 fill-white" />
|
<WhatsAppIcon2 className="size-4 fill-white" />
|
||||||
WhatsApp
|
WhatsApp{data.extra_telefono_numero && ` (${data.nome})`}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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 ? (
|
{data.codice_fiscale ? (
|
||||||
<IntestazioneMaker />
|
<IntestazioneMaker />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ import {
|
||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
ContextMenuLabel,
|
ContextMenuLabel,
|
||||||
|
ContextMenuSub,
|
||||||
|
ContextMenuSubContent,
|
||||||
|
ContextMenuSubTrigger,
|
||||||
ContextMenuTrigger,
|
ContextMenuTrigger,
|
||||||
} from "~/components/ui/context-menu";
|
} from "~/components/ui/context-menu";
|
||||||
import { DataTable } from "~/components/ui/data-table";
|
import { DataTable } from "~/components/ui/data-table";
|
||||||
|
|
@ -161,17 +164,47 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => {
|
||||||
<UserCog className="text-muted-foreground" /> Profilo
|
<UserCog className="text-muted-foreground" /> Profilo
|
||||||
</Link>
|
</Link>
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
|
{data.extra_telefono_numero ? (
|
||||||
<ContextMenuItem>
|
<ContextMenuSub>
|
||||||
<Link
|
<ContextMenuSubTrigger className="flex w-full items-center gap-2">
|
||||||
className="flex w-full items-center gap-2"
|
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||||
href={`https://wa.me/${data.telefono}`}
|
WhatsApp
|
||||||
target="_blank"
|
</ContextMenuSubTrigger>
|
||||||
>
|
<ContextMenuSubContent>
|
||||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
<ContextMenuItem>
|
||||||
WhatsApp
|
<Link
|
||||||
</Link>
|
className="flex w-full items-center gap-2"
|
||||||
</ContextMenuItem>
|
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"
|
||||||
|
href={`https://wa.me/${data.telefono}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||||
|
WhatsApp
|
||||||
|
</Link>
|
||||||
|
</ContextMenuItem>
|
||||||
|
)}
|
||||||
</ContextMenuContent>
|
</ContextMenuContent>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ import {
|
||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
ContextMenuLabel,
|
ContextMenuLabel,
|
||||||
ContextMenuSeparator,
|
ContextMenuSeparator,
|
||||||
|
ContextMenuSub,
|
||||||
|
ContextMenuSubContent,
|
||||||
|
ContextMenuSubTrigger,
|
||||||
} from "../ui/context-menu";
|
} from "../ui/context-menu";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -66,10 +69,13 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
||||||
|
|
||||||
const tabledata = data.map((user) => {
|
const tabledata = data.map((user) => {
|
||||||
return {
|
return {
|
||||||
|
nome: user.nome,
|
||||||
chatid: user.chatid,
|
chatid: user.chatid,
|
||||||
created_at: user.created_at,
|
created_at: user.created_at,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
telefono: user.telefono,
|
telefono: user.telefono,
|
||||||
|
extra_telefono_numero: user.extra_telefono_numero,
|
||||||
|
extra_telefono_titolo: user.extra_telefono_titolo,
|
||||||
note: user.note,
|
note: user.note,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
isAdmin: user.isAdmin ? "Admin" : "Utente",
|
isAdmin: user.isAdmin ? "Admin" : "Utente",
|
||||||
|
|
@ -190,6 +196,22 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
||||||
title={columns_titles.telefono}
|
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",
|
accessorKey: "email",
|
||||||
|
|
@ -361,16 +383,48 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => {
|
||||||
<MessagesSquare /> Chat
|
<MessagesSquare /> Chat
|
||||||
</Link>
|
</Link>
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
<ContextMenuItem>
|
{data.extra_telefono_numero ? (
|
||||||
<Link
|
<ContextMenuSub>
|
||||||
className="flex w-full items-center gap-2"
|
<ContextMenuSubTrigger className="flex w-full items-center gap-2">
|
||||||
href={`https://wa.me/${data.telefono}`}
|
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||||
target="_blank"
|
WhatsApp
|
||||||
>
|
</ContextMenuSubTrigger>
|
||||||
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
<ContextMenuSubContent>
|
||||||
WhatsApp
|
<ContextMenuItem>
|
||||||
</Link>
|
<Link
|
||||||
</ContextMenuItem>
|
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"
|
||||||
|
href={`https://wa.me/${data.telefono}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<WhatsAppIcon2 className="size-4 fill-muted-foreground stroke-0" />
|
||||||
|
WhatsApp
|
||||||
|
</Link>
|
||||||
|
</ContextMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<ContextMenuSeparator />
|
<ContextMenuSeparator />
|
||||||
<ContextMenuItem>
|
<ContextMenuItem>
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,13 @@ import { api } from "~/utils/api";
|
||||||
|
|
||||||
type ProfileFromAccountProps = Pick<
|
type ProfileFromAccountProps = Pick<
|
||||||
Users,
|
Users,
|
||||||
"id" | "nome" | "cognome" | "email" | "telefono"
|
| "id"
|
||||||
|
| "nome"
|
||||||
|
| "cognome"
|
||||||
|
| "email"
|
||||||
|
| "telefono"
|
||||||
|
| "extra_telefono_numero"
|
||||||
|
| "extra_telefono_titolo"
|
||||||
> &
|
> &
|
||||||
Partial<Pick<Users, "isAdmin">>;
|
Partial<Pick<Users, "isAdmin">>;
|
||||||
|
|
||||||
|
|
@ -30,6 +36,8 @@ export const ProfileFormAccount = ({
|
||||||
cognome,
|
cognome,
|
||||||
email,
|
email,
|
||||||
telefono,
|
telefono,
|
||||||
|
extra_telefono_numero,
|
||||||
|
extra_telefono_titolo,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
}: ProfileFromAccountProps) => {
|
}: ProfileFromAccountProps) => {
|
||||||
const profileFormSchema = z.object({
|
const profileFormSchema = z.object({
|
||||||
|
|
@ -38,6 +46,8 @@ export const ProfileFormAccount = ({
|
||||||
isAdmin: z.boolean().optional(),
|
isAdmin: z.boolean().optional(),
|
||||||
nome: z.string().nonempty("Inserisci un nome valido"),
|
nome: z.string().nonempty("Inserisci un nome valido"),
|
||||||
telefono: z.string().nonempty("Inserisci un numero di telefono"),
|
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>;
|
type ProfileFormValues = z.infer<typeof profileFormSchema>;
|
||||||
|
|
@ -48,6 +58,8 @@ export const ProfileFormAccount = ({
|
||||||
isAdmin: isAdmin,
|
isAdmin: isAdmin,
|
||||||
nome: nome || "",
|
nome: nome || "",
|
||||||
telefono,
|
telefono,
|
||||||
|
extra_telefono_numero: extra_telefono_numero || "",
|
||||||
|
extra_telefono_titolo: extra_telefono_titolo || "",
|
||||||
};
|
};
|
||||||
const { locale, t } = useTranslation();
|
const { locale, t } = useTranslation();
|
||||||
z.config(z.locales[locale]());
|
z.config(z.locales[locale]());
|
||||||
|
|
@ -78,6 +90,7 @@ export const ProfileFormAccount = ({
|
||||||
data: {
|
data: {
|
||||||
...fields,
|
...fields,
|
||||||
telefono: fields.telefono?.replace(/\s/g, ""),
|
telefono: fields.telefono?.replace(/\s/g, ""),
|
||||||
|
extra_telefono_numero: fields.extra_telefono_numero?.replace(/\s/g, ""),
|
||||||
username: `${fields.cognome?.trim()} ${fields.nome?.trim()}`,
|
username: `${fields.cognome?.trim()} ${fields.nome?.trim()}`,
|
||||||
isAdmin: fields.isAdmin || false,
|
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 && (
|
{isAdmin !== undefined && (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
|
||||||
|
|
@ -1215,6 +1215,8 @@ The Stable Rent service, ideal for those with demonstrable work references and f
|
||||||
email: "Email",
|
email: "Email",
|
||||||
nome: "Name",
|
nome: "Name",
|
||||||
telefono: "Phone",
|
telefono: "Phone",
|
||||||
|
extra_telefono_numero: "Additional phone",
|
||||||
|
extra_telefono_titolo: "Additional phone contact",
|
||||||
},
|
},
|
||||||
proprietari: {
|
proprietari: {
|
||||||
contattaci: "Contact us",
|
contattaci: "Contact us",
|
||||||
|
|
|
||||||
|
|
@ -1213,6 +1213,8 @@ export const it: LangDict = {
|
||||||
email: "Email",
|
email: "Email",
|
||||||
nome: "Nome",
|
nome: "Nome",
|
||||||
telefono: "Telefono",
|
telefono: "Telefono",
|
||||||
|
extra_telefono_numero: "Telefono aggiuntivo",
|
||||||
|
extra_telefono_titolo: "Nominativo tel. aggiuntivo",
|
||||||
},
|
},
|
||||||
proprietari: {
|
proprietari: {
|
||||||
contattaci: "Contattaci",
|
contattaci: "Contattaci",
|
||||||
|
|
|
||||||
|
|
@ -511,6 +511,8 @@ export type LangDict = {
|
||||||
cognome: string;
|
cognome: string;
|
||||||
email: string;
|
email: string;
|
||||||
telefono: string;
|
telefono: string;
|
||||||
|
extra_telefono_numero: string;
|
||||||
|
extra_telefono_titolo: string;
|
||||||
aggiorna: string;
|
aggiorna: string;
|
||||||
aggiornato: string;
|
aggiornato: string;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,8 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
|
||||||
<ProfileFormAccount
|
<ProfileFormAccount
|
||||||
cognome={userData.cognome}
|
cognome={userData.cognome}
|
||||||
email={userData.email}
|
email={userData.email}
|
||||||
|
extra_telefono_numero={userData.extra_telefono_numero}
|
||||||
|
extra_telefono_titolo={userData.extra_telefono_titolo}
|
||||||
id={userData.id}
|
id={userData.id}
|
||||||
isAdmin={userData.isAdmin}
|
isAdmin={userData.isAdmin}
|
||||||
nome={userData.nome}
|
nome={userData.nome}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ const Dashboard: NextPageWithLayout = () => {
|
||||||
<ProfileFormAccount
|
<ProfileFormAccount
|
||||||
cognome={userData.cognome}
|
cognome={userData.cognome}
|
||||||
email={userData.email}
|
email={userData.email}
|
||||||
|
extra_telefono_numero={userData.extra_telefono_numero}
|
||||||
|
extra_telefono_titolo={userData.extra_telefono_titolo}
|
||||||
id={userData.id}
|
id={userData.id}
|
||||||
nome={userData.nome}
|
nome={userData.nome}
|
||||||
telefono={userData.telefono}
|
telefono={userData.telefono}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,10 @@ export default interface UsersTable {
|
||||||
note: ColumnType<string | null, string | null, string | null>;
|
note: ColumnType<string | null, string | null, string | null>;
|
||||||
|
|
||||||
comms_enabled: ColumnType<boolean, boolean | undefined, boolean>;
|
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>;
|
export type Users = Selectable<UsersTable>;
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,8 @@ export type UserWChatInfo = Pick<
|
||||||
| "username"
|
| "username"
|
||||||
| "email"
|
| "email"
|
||||||
| "telefono"
|
| "telefono"
|
||||||
|
| "extra_telefono_numero"
|
||||||
|
| "extra_telefono_titolo"
|
||||||
| "isAdmin"
|
| "isAdmin"
|
||||||
| "isBlocked"
|
| "isBlocked"
|
||||||
| "created_at"
|
| "created_at"
|
||||||
|
|
@ -159,12 +161,16 @@ export type UserWChatInfo = Pick<
|
||||||
export const getUsersWithChatInfoHandler = async (): Promise<
|
export const getUsersWithChatInfoHandler = async (): Promise<
|
||||||
UserWChatInfo[]
|
UserWChatInfo[]
|
||||||
> => {
|
> => {
|
||||||
|
try {
|
||||||
return await db
|
return await db
|
||||||
.selectFrom("users")
|
.selectFrom("users")
|
||||||
.select([
|
.select([
|
||||||
"users.id",
|
"users.id",
|
||||||
|
"users.nome",
|
||||||
"users.username",
|
"users.username",
|
||||||
"users.telefono",
|
"users.telefono",
|
||||||
|
"users.extra_telefono_numero",
|
||||||
|
"users.extra_telefono_titolo",
|
||||||
"users.email",
|
"users.email",
|
||||||
"users.isAdmin",
|
"users.isAdmin",
|
||||||
"users.isBlocked",
|
"users.isBlocked",
|
||||||
|
|
@ -193,4 +199,10 @@ export const getUsersWithChatInfoHandler = async (): Promise<
|
||||||
.orderBy("users.id", "desc")
|
.orderBy("users.id", "desc")
|
||||||
.select(["users.id as id"])
|
.select(["users.id as id"])
|
||||||
.execute();
|
.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",
|
"cognome",
|
||||||
"email",
|
"email",
|
||||||
"telefono",
|
"telefono",
|
||||||
|
"extra_telefono_numero",
|
||||||
|
"extra_telefono_titolo",
|
||||||
"created_at",
|
"created_at",
|
||||||
"isAdmin",
|
"isAdmin",
|
||||||
"isBlocked",
|
"isBlocked",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue