Linting and Formatting

This commit is contained in:
Kartik Datrika
2026-03-23 16:41:58 +05:30
parent 727a0728ee
commit 2c87a39733
175 changed files with 16535 additions and 11532 deletions

View File

@@ -1,15 +1,15 @@
import { useMemo } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPhone, faPhoneArrowDown, faCircleCheck, faEnvelope, faClock, faStars } from '@fortawesome/pro-duotone-svg-icons';
import { Avatar } from '@/components/base/avatar/avatar';
import { Badge } from '@/components/base/badges/badges';
import { SourceTag } from '@/components/shared/source-tag';
import { AgeIndicator } from '@/components/shared/age-indicator';
import { DispositionForm } from './disposition-form';
import { formatPhone, formatShortDate, getInitials } from '@/lib/format';
import type { Lead, LeadActivity, CallDisposition, Campaign } from '@/types/entities';
import { useMemo } from "react";
import { faCircleCheck, faClock, faEnvelope, faPhone, faPhoneArrowDown, faStars } from "@fortawesome/pro-duotone-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Avatar } from "@/components/base/avatar/avatar";
import { Badge } from "@/components/base/badges/badges";
import { AgeIndicator } from "@/components/shared/age-indicator";
import { SourceTag } from "@/components/shared/source-tag";
import { formatPhone, formatShortDate, getInitials } from "@/lib/format";
import type { CallDisposition, Campaign, Lead, LeadActivity } from "@/types/entities";
import { DispositionForm } from "./disposition-form";
type CallState = 'idle' | 'ringing' | 'active' | 'completed';
type CallState = "idle" | "ringing" | "active" | "completed";
interface IncomingCallCardProps {
callState: CallState;
@@ -21,57 +21,57 @@ interface IncomingCallCardProps {
}
const activityTypeIcons: Record<string, string> = {
CALL_MADE: 'phone',
CALL_RECEIVED: 'phone',
WHATSAPP_SENT: 'message',
WHATSAPP_RECEIVED: 'message',
SMS_SENT: 'message',
EMAIL_SENT: 'email',
EMAIL_RECEIVED: 'email',
NOTE_ADDED: 'note',
ASSIGNED: 'assign',
STATUS_CHANGE: 'status',
APPOINTMENT_BOOKED: 'calendar',
FOLLOW_UP_CREATED: 'clock',
CONVERTED: 'check',
MARKED_SPAM: 'alert',
DUPLICATE_DETECTED: 'alert',
CALL_MADE: "phone",
CALL_RECEIVED: "phone",
WHATSAPP_SENT: "message",
WHATSAPP_RECEIVED: "message",
SMS_SENT: "message",
EMAIL_SENT: "email",
EMAIL_RECEIVED: "email",
NOTE_ADDED: "note",
ASSIGNED: "assign",
STATUS_CHANGE: "status",
APPOINTMENT_BOOKED: "calendar",
FOLLOW_UP_CREATED: "clock",
CONVERTED: "check",
MARKED_SPAM: "alert",
DUPLICATE_DETECTED: "alert",
};
const ActivityIcon = ({ type }: { type: string }) => {
const iconType = activityTypeIcons[type] ?? 'note';
const baseClass = 'size-3.5 shrink-0 text-fg-quaternary';
const iconType = activityTypeIcons[type] ?? "note";
const baseClass = "size-3.5 shrink-0 text-fg-quaternary";
if (iconType === 'phone') return <FontAwesomeIcon icon={faPhone} className={baseClass} />;
if (iconType === 'email') return <FontAwesomeIcon icon={faEnvelope} className={baseClass} />;
if (iconType === 'clock') return <FontAwesomeIcon icon={faClock} className={baseClass} />;
if (iconType === 'check') return <FontAwesomeIcon icon={faCircleCheck} className={baseClass} />;
if (iconType === "phone") return <FontAwesomeIcon icon={faPhone} className={baseClass} />;
if (iconType === "email") return <FontAwesomeIcon icon={faEnvelope} className={baseClass} />;
if (iconType === "clock") return <FontAwesomeIcon icon={faClock} className={baseClass} />;
if (iconType === "check") return <FontAwesomeIcon icon={faCircleCheck} className={baseClass} />;
return <FontAwesomeIcon icon={faClock} className={baseClass} />;
};
const dispositionLabels: Record<CallDisposition, string> = {
APPOINTMENT_BOOKED: 'Appointment Booked',
FOLLOW_UP_SCHEDULED: 'Follow-up Needed',
INFO_PROVIDED: 'Info Provided',
NO_ANSWER: 'No Answer',
WRONG_NUMBER: 'Wrong Number',
CALLBACK_REQUESTED: 'Not Interested',
APPOINTMENT_BOOKED: "Appointment Booked",
FOLLOW_UP_SCHEDULED: "Follow-up Needed",
INFO_PROVIDED: "Info Provided",
NO_ANSWER: "No Answer",
WRONG_NUMBER: "Wrong Number",
CALLBACK_REQUESTED: "Not Interested",
};
export const IncomingCallCard = ({ callState, lead, activities, campaigns, onDisposition, completedDisposition }: IncomingCallCardProps) => {
if (callState === 'idle') {
if (callState === "idle") {
return <IdleState />;
}
if (callState === 'ringing') {
if (callState === "ringing") {
return <RingingState lead={lead} />;
}
if (callState === 'active' && lead !== null) {
if (callState === "active" && lead !== null) {
return <ActiveState lead={lead} activities={activities} campaigns={campaigns} onDisposition={onDisposition} />;
}
if (callState === 'completed') {
if (callState === "completed") {
return <CompletedState disposition={completedDisposition ?? null} />;
}
@@ -88,9 +88,7 @@ const IdleState = () => (
);
const RingingState = ({ lead }: { lead: Lead | null }) => {
const phoneDisplay = lead?.contactPhone?.[0]
? formatPhone(lead.contactPhone[0])
: '+91 98765 43210';
const phoneDisplay = lead?.contactPhone?.[0] ? formatPhone(lead.contactPhone[0]) : "+91 98765 43210";
return (
<div className="flex flex-col items-center justify-center rounded-2xl bg-brand-primary p-12 text-center">
@@ -100,12 +98,8 @@ const RingingState = ({ lead }: { lead: Lead | null }) => {
<FontAwesomeIcon icon={faPhoneArrowDown} className="size-12 text-fg-brand-primary" />
</div>
</div>
<span className="mb-1 text-xs font-bold uppercase tracking-wider text-brand-secondary">
Incoming Call
</span>
<span className="text-display-xs font-bold text-primary">
{phoneDisplay}
</span>
<span className="mb-1 text-xs font-bold tracking-wider text-brand-secondary uppercase">Incoming Call</span>
<span className="text-display-xs font-bold text-primary">{phoneDisplay}</span>
</div>
);
};
@@ -126,8 +120,8 @@ const ActiveState = ({
activities
.filter((a) => a.leadId === lead.id)
.sort((a, b) => {
const dateA = a.occurredAt ?? a.createdAt ?? '';
const dateB = b.occurredAt ?? b.createdAt ?? '';
const dateA = a.occurredAt ?? a.createdAt ?? "";
const dateB = b.occurredAt ?? b.createdAt ?? "";
return new Date(dateB).getTime() - new Date(dateA).getTime();
})
.slice(0, 3),
@@ -140,13 +134,11 @@ const ActiveState = ({
return campaign?.campaignName ?? null;
}, [campaigns, lead.campaignId]);
const firstName = lead.contactName?.firstName ?? '';
const lastName = lead.contactName?.lastName ?? '';
const fullName = `${firstName} ${lastName}`.trim() || 'Unknown Lead';
const initials = firstName && lastName ? getInitials(firstName, lastName) : 'UL';
const phoneDisplay = lead.contactPhone?.[0]
? formatPhone(lead.contactPhone[0])
: 'No phone';
const firstName = lead.contactName?.firstName ?? "";
const lastName = lead.contactName?.lastName ?? "";
const fullName = `${firstName} ${lastName}`.trim() || "Unknown Lead";
const initials = firstName && lastName ? getInitials(firstName, lastName) : "UL";
const phoneDisplay = lead.contactPhone?.[0] ? formatPhone(lead.contactPhone[0]) : "No phone";
const emailDisplay = lead.contactEmail?.[0]?.address ?? null;
return (
@@ -169,18 +161,14 @@ const ActiveState = ({
</div>
)}
<div className="mt-2 flex flex-wrap items-center gap-2">
{lead.leadSource !== null && (
<SourceTag source={lead.leadSource} size="sm" />
)}
{lead.leadSource !== null && <SourceTag source={lead.leadSource} size="sm" />}
{campaignName !== null && (
<Badge size="sm" color="brand">{campaignName}</Badge>
<Badge size="sm" color="brand">
{campaignName}
</Badge>
)}
</div>
{lead.interestedService !== null && (
<p className="mt-1.5 text-sm text-secondary">
Interested in: {lead.interestedService}
</p>
)}
{lead.interestedService !== null && <p className="mt-1.5 text-sm text-secondary">Interested in: {lead.interestedService}</p>}
{lead.createdAt !== null && (
<div className="mt-1 flex items-center gap-1.5 text-sm text-tertiary">
<span>Lead age:</span>
@@ -194,9 +182,7 @@ const ActiveState = ({
<div className="mt-4 rounded-xl bg-brand-primary p-4">
<div className="mb-2 flex items-center gap-1.5">
<FontAwesomeIcon icon={faStars} className="size-4 text-fg-brand-primary" />
<span className="text-xs font-bold uppercase tracking-wider text-brand-secondary">
AI Insight
</span>
<span className="text-xs font-bold tracking-wider text-brand-secondary uppercase">AI Insight</span>
</div>
{lead.aiSummary !== null ? (
<>
@@ -208,9 +194,7 @@ const ActiveState = ({
)}
</>
) : (
<p className="text-sm text-quaternary">
No AI insights available for this lead
</p>
<p className="text-sm text-quaternary">No AI insights available for this lead</p>
)}
</div>
@@ -221,14 +205,10 @@ const ActiveState = ({
<div className="flex flex-col gap-2">
{leadActivities.map((activity) => (
<div key={activity.id} className="flex items-start gap-2">
<ActivityIcon type={activity.activityType ?? 'NOTE_ADDED'} />
<span className="flex-1 text-xs text-secondary">
{activity.summary}
</span>
<ActivityIcon type={activity.activityType ?? "NOTE_ADDED"} />
<span className="flex-1 text-xs text-secondary">{activity.summary}</span>
<span className="shrink-0 text-xs text-quaternary">
{activity.occurredAt !== null
? formatShortDate(activity.occurredAt)
: ''}
{activity.occurredAt !== null ? formatShortDate(activity.occurredAt) : ""}
</span>
</div>
))}
@@ -240,7 +220,7 @@ const ActiveState = ({
</div>
{/* Right section: disposition form */}
<div className="w-full shrink-0 border-t border-secondary pt-4 lg:w-72 lg:border-l lg:border-t-0 lg:pl-6 lg:pt-0">
<div className="w-full shrink-0 border-t border-secondary pt-4 lg:w-72 lg:border-t-0 lg:border-l lg:pt-0 lg:pl-6">
<DispositionForm onSubmit={onDisposition} />
</div>
</div>
@@ -249,14 +229,16 @@ const ActiveState = ({
};
const CompletedState = ({ disposition }: { disposition: CallDisposition | null }) => {
const label = disposition !== null ? dispositionLabels[disposition] : 'Unknown';
const label = disposition !== null ? dispositionLabels[disposition] : "Unknown";
return (
<div className="flex flex-col items-center justify-center rounded-2xl bg-success-primary p-8 text-center">
<FontAwesomeIcon icon={faCircleCheck} className="mb-3 size-12 text-fg-success-primary" />
<h3 className="text-lg font-bold text-success-primary">Call Logged</h3>
{disposition !== null && (
<Badge size="md" color="success" className="mt-2">{label}</Badge>
<Badge size="md" color="success" className="mt-2">
{label}
</Badge>
)}
<p className="mt-2 text-sm text-tertiary">Returning to call desk...</p>
</div>