phone-number-card.tsx3.6 KBView on GitHub import { SettingsCard } from '@/modules/userSettings/components/settings-card';
import { Check, Copy, MessageSquare, Smartphone } from 'lucide-react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useTRPC } from '@/providers/query-provider';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
const CEDAR_CHANNELS = [
{ label: 'iMessage', number: '+13105971445', display: '+1 (310) 597-1445' },
{ label: 'SMS', number: '+14452854652', display: '+1 (445) 285-4652' },
{ label: 'WhatsApp', number: '+15559822517', display: '+1 (555) 982-2517' },
] as const;
function CopyableNumber({
label,
number,
display,
}: {
label: string;
number: string;
display: string;
}) {
const [copied, setCopied] = useState(false);
function handleCopy() {
navigator.clipboard.writeText(number);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
return (
<div className="flex items-center justify-between rounded-lg border px-3 py-2">
<div className="flex items-center gap-2">
<MessageSquare className="text-muted-foreground h-3.5 w-3.5" />
<span className="text-muted-foreground text-xs">{label}</span>
<span className="font-mono text-sm font-medium">{display}</span>
</div>
<Button variant="ghost" size="sm" className="h-7 cursor-pointer px-2" onClick={handleCopy}>
{copied ? (
<Check className="h-3.5 w-3.5 text-green-500" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
</Button>
</div>
);
}
export function PhoneNumberCard() {
const trpc = useTRPC();
const [phone, setPhone] = useState('');
const [isSaving, setIsSaving] = useState(false);
const { data } = useQuery(trpc.user.getPhoneNumber.queryOptions());
useEffect(() => {
if (data?.phoneNumber != null) setPhone(data.phoneNumber);
}, [data?.phoneNumber]);
const { mutateAsync: updatePhoneNumber } = useMutation(
trpc.user.updatePhoneNumber.mutationOptions(),
);
async function handleSave() {
setIsSaving(true);
try {
await updatePhoneNumber({ phoneNumber: phone });
} catch {
toast.error('Invalid phone number — use E.164 format, e.g. +14155551234');
} finally {
setIsSaving(false);
}
}
return (
<SettingsCard
title="Chat with Cedar on your phone"
description="Text Cedar from iMessage, SMS, or WhatsApp."
footer={
<Button onClick={handleSave} disabled={isSaving} className="cursor-pointer">
{isSaving ? 'Saving...' : 'Save Changes'}
</Button>
}
>
<div className="flex max-w-sm flex-col gap-4">
<div className="flex flex-col gap-2">
<p className="text-muted-foreground text-xs font-medium uppercase tracking-wide">
Cedar's numbers
</p>
{CEDAR_CHANNELS.map((ch) => (
<CopyableNumber key=[redacted] {...ch} />
))}
</div>
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-sm font-medium">
<Smartphone className="h-4 w-4" />
Your phone number
</div>
<Input
placeholder="+14155551234"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
<p className="text-muted-foreground text-xs">
E.164 format including country code. Save your number to link it to your Cedar account.
</p>
</div>
</div>
</SettingsCard>
);
}