customer-module-frontend 1.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/context.md +306 -0
- package/.cursor/rules/guardrails.md +35 -0
- package/.env +1 -0
- package/.github/workflows/publish-to-npm-beta.yml +51 -0
- package/.github/workflows/publish-to-npm.yml +58 -0
- package/README.md +73 -0
- package/eslint.config.js +23 -0
- package/index.html +13 -0
- package/package.json +43 -0
- package/postcss-unwrap-layers.js +31 -0
- package/postcss.config.js +11 -0
- package/public/vite.svg +1 -0
- package/src/App.css +40 -0
- package/src/App.tsx +58 -0
- package/src/assets/accounts.svg +3 -0
- package/src/assets/at_the_rate.svg +10 -0
- package/src/assets/buildings.svg +3 -0
- package/src/assets/chat.svg +3 -0
- package/src/assets/close.svg +3 -0
- package/src/assets/contacts.svg +3 -0
- package/src/assets/conversation.svg +10 -0
- package/src/assets/customers.svg +10 -0
- package/src/assets/details.svg +3 -0
- package/src/assets/domain.svg +10 -0
- package/src/assets/edit.svg +15 -0
- package/src/assets/email.svg +3 -0
- package/src/assets/google.svg +8 -0
- package/src/assets/inbox.svg +0 -0
- package/src/assets/message.svg +3 -0
- package/src/assets/no-data.svg +9 -0
- package/src/assets/open_in_a_new_tab.svg +10 -0
- package/src/assets/phone.svg +3 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/search.svg +3 -0
- package/src/assets/search_typing.svg +4 -0
- package/src/assets/sm_contacts.svg +3 -0
- package/src/assets/sm_inbox.svg +3 -0
- package/src/assets/sm_slider.svg +3 -0
- package/src/assets/status-resolved.svg +3 -0
- package/src/assets/status-snoozed.svg +4 -0
- package/src/assets/status_open.svg +1 -0
- package/src/components/AccountContacts/index.tsx +107 -0
- package/src/components/AccountDetails/index.tsx +102 -0
- package/src/components/AccountsConversation/index.tsx +75 -0
- package/src/components/Avatar/constants.tsx +45 -0
- package/src/components/Avatar/index.tsx +42 -0
- package/src/components/BreadcrumbsSection/index.tsx +16 -0
- package/src/components/Card/index.tsx +31 -0
- package/src/components/Card/types.ts +10 -0
- package/src/components/ContactConversation/Converation.tsx +14 -0
- package/src/components/ContactConversation/index.tsx +81 -0
- package/src/components/ContactDetails/index.tsx +111 -0
- package/src/components/Contacts/EditContact.tsx +213 -0
- package/src/components/Contacts/constants/index.tsx +24 -0
- package/src/components/Contacts/index.tsx +171 -0
- package/src/components/ConversationBox/constants.tsx +99 -0
- package/src/components/ConversationBox/index.tsx +147 -0
- package/src/components/ConversationBox/types.ts +20 -0
- package/src/components/CustomersLayout/index.tsx +20 -0
- package/src/components/DetailsCard/index.tsx +31 -0
- package/src/components/DetailsCard/types.ts +10 -0
- package/src/components/EmptyData/NoDataFound.tsx +31 -0
- package/src/components/Header/index.tsx +55 -0
- package/src/components/Icon/index.tsx +93 -0
- package/src/components/Icon/types.ts +13 -0
- package/src/components/Listing/AccountTable.tsx +47 -0
- package/src/components/Listing/ContactTable.tsx +76 -0
- package/src/components/RightPanel/AccountPanel.tsx +123 -0
- package/src/components/RightPanel/ContactPanel.tsx +142 -0
- package/src/components/RightPanel/index.tsx +167 -0
- package/src/components/Search/SearchDialog.tsx +150 -0
- package/src/components/TabsSection/index.tsx +49 -0
- package/src/constants/index.tsx +645 -0
- package/src/hooks/useBreadcrumb.tsx +93 -0
- package/src/index.css +315 -0
- package/src/main.tsx +14 -0
- package/src/pages/AccountDetail.tsx +68 -0
- package/src/pages/Accounts.tsx +12 -0
- package/src/pages/ContactDetail.tsx +55 -0
- package/src/pages/Contacts.tsx +12 -0
- package/src/stores/count.tsx +17 -0
- package/src/types/index.ts +0 -0
- package/tailwind.config.js +179 -0
- package/tsconfig.app.json +36 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +31 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useParams } from 'react-router-dom';
|
|
3
|
+
import { accountData, contactsData } from '../../constants/index';
|
|
4
|
+
import accountsIcon from '../../assets/accounts.svg';
|
|
5
|
+
import contactsIcon from '../../assets/contacts.svg';
|
|
6
|
+
|
|
7
|
+
// Domain/Globe icon - using a simple SVG inline since we don't have a domain icon asset
|
|
8
|
+
const DomainIcon: React.FC = () => (
|
|
9
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10
|
+
<path d="M7 1.16667C3.78 1.16667 1.16667 3.78 1.16667 7C1.16667 10.22 3.78 12.8333 7 12.8333C10.22 12.8333 12.8333 10.22 12.8333 7C12.8333 3.78 10.22 1.16667 7 1.16667ZM7 11.6667C4.6 11.6667 2.66667 9.73333 2.66667 7.35H11.3333C11.3333 9.73333 9.4 11.6667 7 11.6667ZM2.66667 6.65C2.66667 4.26667 4.6 2.33333 7 2.33333C9.4 2.33333 11.3333 4.26667 11.3333 6.65H2.66667ZM1.16667 7C1.16667 4.26667 3.26667 2.1 6 2.01667V11.9833C3.26667 11.9 1.16667 9.73333 1.16667 7ZM8 11.9833V2.01667C10.7333 2.1 12.8333 4.26667 12.8333 7C12.8333 9.73333 10.7333 11.9 8 11.9833Z" fill="#64758B"/>
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
interface DetailRowProps {
|
|
15
|
+
icon: React.ReactNode;
|
|
16
|
+
label: string;
|
|
17
|
+
value: string | number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DetailRow: React.FC<DetailRowProps> = ({ icon, label, value }) => (
|
|
21
|
+
<div className="cm:flex cm:flex-col cm:items-start cm:relative cm:shrink-0 cm:w-full">
|
|
22
|
+
<div className="cm:flex cm:h-9 cm:items-center cm:relative cm:shrink-0 cm:w-full">
|
|
23
|
+
<div className="cm:flex cm:gap-2 cm:items-center cm:relative cm:shrink-0 cm:w-[100px]">
|
|
24
|
+
<div className="cm:relative cm:shrink-0 cm:w-3.5 cm:h-3.5 cm:flex cm:items-center cm:justify-center">
|
|
25
|
+
{icon}
|
|
26
|
+
</div>
|
|
27
|
+
<p className="cm:font-normal cm:leading-5 cm:text-sm cm:text-slate-text-subtle cm:w-[90px]">
|
|
28
|
+
{label}
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
<div className="cm:flex-1 cm:flex cm:gap-2 cm:h-full cm:items-center cm:min-w-0 cm:px-1 cm:py-0 cm:relative cm:shrink-0">
|
|
32
|
+
<p className="cm:font-medium cm:leading-5 cm:text-sm cm:text-slate-text-title cm:whitespace-nowrap">
|
|
33
|
+
{value}
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const AccountDetails: React.FC = () => {
|
|
41
|
+
const { accountId } = useParams<{ accountId: string }>();
|
|
42
|
+
|
|
43
|
+
const account = useMemo(() => {
|
|
44
|
+
if (!accountId) return null;
|
|
45
|
+
return accountData.data.results.find(
|
|
46
|
+
(acc: { id: number }) => acc.id.toString() === accountId
|
|
47
|
+
);
|
|
48
|
+
}, [accountId]);
|
|
49
|
+
|
|
50
|
+
const contactsCount = useMemo(() => {
|
|
51
|
+
if (!account) return 0;
|
|
52
|
+
return contactsData.data.results.filter(
|
|
53
|
+
(contact: { company?: { id: number } }) => contact.company?.id === account.id
|
|
54
|
+
).length;
|
|
55
|
+
}, [account]);
|
|
56
|
+
|
|
57
|
+
if (!account) {
|
|
58
|
+
return (
|
|
59
|
+
<div>
|
|
60
|
+
<p className="cm:text-sm cm:text-slate-text-subtle">Account not found</p>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const domain = account.domains?.[0]?.name || account.generated_name || 'N/A';
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="cm:flex cm:flex-col cm:gap-3 cm:items-stretch cm:relative cm:shrink-0 cm:w-full">
|
|
69
|
+
{/* Account Name Section */}
|
|
70
|
+
<div className="cm:flex cm:gap-3 cm:items-center cm:relative cm:shrink-0 cm:w-full">
|
|
71
|
+
{/* Account Icon with background */}
|
|
72
|
+
<div className="cm:relative cm:shrink-0">
|
|
73
|
+
<div className="cm:bg-slate-surface-subtle_150 cm:border cm:border-slate-border-light cm:rounded-full cm:w-7 cm:h-7 cm:flex cm:items-center cm:justify-center">
|
|
74
|
+
<div className="cm:w-3.5 cm:h-3.5 cm:flex cm:items-center cm:justify-center">
|
|
75
|
+
<img src={accountsIcon} alt="account" className="cm:w-3.5 cm:h-3.5" />
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<p className="cm:font-medium cm:leading-5 cm:text-sm cm:text-slate-text-title cm:whitespace-nowrap">
|
|
80
|
+
{account.name}
|
|
81
|
+
</p>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{/* Details Section */}
|
|
85
|
+
{/* <div className="cm:flex cm:flex-col cm:gap-0.5 cm:items-start cm:relative cm:shrink-0 cm:w-full"> */}
|
|
86
|
+
<DetailRow
|
|
87
|
+
icon={<DomainIcon />}
|
|
88
|
+
label="Domain"
|
|
89
|
+
value={domain}
|
|
90
|
+
/>
|
|
91
|
+
<DetailRow
|
|
92
|
+
icon={<img src={contactsIcon} alt="contacts" className="cm:w-3.5 cm:h-3.5" />}
|
|
93
|
+
label="Contacts"
|
|
94
|
+
value={contactsCount}
|
|
95
|
+
/>
|
|
96
|
+
{/* </div> */}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default AccountDetails;
|
|
102
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import ConversationBox from '../ConversationBox';
|
|
4
|
+
import type { ConversationBoxProps } from '../ConversationBox/types';
|
|
5
|
+
|
|
6
|
+
// Sample conversation data based on the Figma design
|
|
7
|
+
const conversations: Omit<ConversationBoxProps, 'onClick'>[] = [
|
|
8
|
+
{
|
|
9
|
+
type: 'chat',
|
|
10
|
+
status: 'open',
|
|
11
|
+
senderName: 'Blake Lively',
|
|
12
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
13
|
+
messagePreview: 'I am unable to access key features. The loading times are excessive, hindering my workflow. Can you please investigate?',
|
|
14
|
+
mailboxName: 'Sales',
|
|
15
|
+
assignedAgent: {
|
|
16
|
+
name: 'Alice Adams',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'email',
|
|
21
|
+
status: 'pending',
|
|
22
|
+
senderName: 'Ryan Reynolds',
|
|
23
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
24
|
+
heading: 'Issue with the app',
|
|
25
|
+
messagePreview: 'I am having trouble with the app. It keeps crashing and I am losing data. Can you please help?',
|
|
26
|
+
mailboxName: 'Finance',
|
|
27
|
+
assignedAgent: {
|
|
28
|
+
name: 'Andy Rue',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'chat',
|
|
33
|
+
status: 'pending',
|
|
34
|
+
senderName: 'Chris Pratt',
|
|
35
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
36
|
+
messagePreview: 'I am encountering persistent error messages when trying to save changes. This is impacting my productivity. Please assist.',
|
|
37
|
+
mailboxName: 'Sales',
|
|
38
|
+
assignedAgent: {
|
|
39
|
+
name: 'Kate Wills',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'email',
|
|
44
|
+
status: 'closed',
|
|
45
|
+
senderName: 'Zoe Saldana',
|
|
46
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
47
|
+
heading: "Can't log in to my account",
|
|
48
|
+
messagePreview: 'I am experiencing synchronization problems across devices. My data is not updating correctly. Could you resolve this?',
|
|
49
|
+
mailboxName: 'Finance',
|
|
50
|
+
assignedAgent: {
|
|
51
|
+
name: 'Kevin Smith',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const AccountsConversation: React.FC = () => {
|
|
57
|
+
const navigate = useNavigate();
|
|
58
|
+
|
|
59
|
+
const handleConversationClick = () => {
|
|
60
|
+
navigate('/conversation');
|
|
61
|
+
};
|
|
62
|
+
return (
|
|
63
|
+
<div className="cm:flex cm:flex-col cm:gap-2 cm:p-6 cm:w-full">
|
|
64
|
+
{conversations.map((conversation, index) => (
|
|
65
|
+
<ConversationBox
|
|
66
|
+
key={index}
|
|
67
|
+
{...conversation}
|
|
68
|
+
onClick={handleConversationClick}
|
|
69
|
+
/>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default AccountsConversation;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Generate a deterministic hash from a string
|
|
2
|
+
export const generateStringHash = (str: string): number => {
|
|
3
|
+
let hash = 0;
|
|
4
|
+
for (let i = 0; i < str.length; i++) {
|
|
5
|
+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
6
|
+
}
|
|
7
|
+
return hash;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// Color options for labels
|
|
11
|
+
export const colorOptions = [
|
|
12
|
+
"#5398cf", // light blue
|
|
13
|
+
"#d04b4f", // red
|
|
14
|
+
"#ac80b7", // violet
|
|
15
|
+
"#8789c5", // purple
|
|
16
|
+
"#e3692c", // orange
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// Generate a deterministic color for a label
|
|
20
|
+
export const getColorForLabel = (label: string | undefined): string => {
|
|
21
|
+
if (!label) return "#5398cf"; // light blue default
|
|
22
|
+
|
|
23
|
+
const hash = generateStringHash(label);
|
|
24
|
+
const index = Math.abs(hash) % colorOptions.length;
|
|
25
|
+
return colorOptions[index];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Size configuration
|
|
29
|
+
export const sizeConfig = {
|
|
30
|
+
small: {
|
|
31
|
+
size: "cm:w-3.5 cm:h-3.5", // 14x14
|
|
32
|
+
text: "cm:text-[8px]",
|
|
33
|
+
leading: "cm:leading-[12px]",
|
|
34
|
+
},
|
|
35
|
+
default: {
|
|
36
|
+
size: "cm:w-4 cm:h-4", // 16x16
|
|
37
|
+
text: "cm:text-[9px]",
|
|
38
|
+
leading: "cm:leading-[13.5px]",
|
|
39
|
+
},
|
|
40
|
+
large: {
|
|
41
|
+
size: "cm:w-7 cm:h-[18px]", // 28x18
|
|
42
|
+
text: "cm:text-sm",
|
|
43
|
+
leading: "cm:leading-[18px]",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { getColorForLabel, sizeConfig } from './constants';
|
|
3
|
+
|
|
4
|
+
export interface AvatarProps {
|
|
5
|
+
name: string;
|
|
6
|
+
size?: 'small' | 'default' | 'large';
|
|
7
|
+
className?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
labelName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const Avatar: React.FC<AvatarProps> = ({ name, size = 'default', className, color, labelName }) => {
|
|
13
|
+
// Get first letter of the name
|
|
14
|
+
const initial = name.charAt(0).toUpperCase() || 'A';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const assignedColor = useMemo(() =>
|
|
18
|
+
color ? color : getColorForLabel(labelName),
|
|
19
|
+
[color, labelName]);
|
|
20
|
+
|
|
21
|
+
const avatarData = useMemo(() => ({
|
|
22
|
+
backgroundColor: assignedColor,
|
|
23
|
+
}), [assignedColor]);
|
|
24
|
+
|
|
25
|
+
const sizeStyles = sizeConfig[size];
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
className={`cm:flex cm:flex-col cm:items-center cm:justify-center cm:rounded-full ${sizeStyles.size} cm:shrink-0 cm:relative ${className || ''}`}
|
|
30
|
+
style={avatarData}
|
|
31
|
+
>
|
|
32
|
+
<div className="cm:flex cm:flex-col cm:gap-0 cm:items-center cm:justify-center cm:w-full cm:h-full">
|
|
33
|
+
<p className={`cm:font-medium ${sizeStyles.leading} ${sizeStyles.text} cm:text-white cm:text-center cm:whitespace-nowrap`}>
|
|
34
|
+
{initial}
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default Avatar;
|
|
42
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Breadcrumb } from 'hiver-ui-kit-extended';
|
|
2
|
+
import { useBreadcrumb } from '../../hooks/useBreadcrumb';
|
|
3
|
+
|
|
4
|
+
const BreadcrumbsSection = () => {
|
|
5
|
+
|
|
6
|
+
const breadcrumbItems = useBreadcrumb();
|
|
7
|
+
console.log('breadcrumbItems', breadcrumbItems);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="cm:pb-2">
|
|
11
|
+
<Breadcrumb items={breadcrumbItems} />
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default BreadcrumbsSection;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { CardProps } from './types';
|
|
3
|
+
|
|
4
|
+
const Card: React.FC<CardProps> = ({
|
|
5
|
+
children,
|
|
6
|
+
header,
|
|
7
|
+
className = '',
|
|
8
|
+
onClick,
|
|
9
|
+
maxWidth = 'cm:max-w-[1000px]'
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
className={`cm:bg-slate-surface-white cm:border cm:border-slate-border-light cm:rounded-[12px] cm:relative cm:w-full ${maxWidth} ${className}`}
|
|
14
|
+
onClick={onClick}
|
|
15
|
+
>
|
|
16
|
+
<div className="cm:flex cm:flex-col cm:items-start cm:overflow-clip cm:relative cm:rounded-[inherit] cm:w-full">
|
|
17
|
+
{header && (
|
|
18
|
+
<div className="cm:border-b cm:border-slate-border-light cm:box-border cm:flex cm:h-12 cm:items-center cm:px-0 cm:pr-4 cm:py-0 cm:relative cm:shrink-0 cm:w-full">
|
|
19
|
+
{header}
|
|
20
|
+
</div>
|
|
21
|
+
)}
|
|
22
|
+
<div className="cm:box-border cm:flex cm:flex-col cm:items-start cm:px-0 cm:py-0 cm:relative cm:shrink-0 cm:w-full">
|
|
23
|
+
{children}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default Card;
|
|
31
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import RightPanel from "../RightPanel";
|
|
2
|
+
const Converation = () => {
|
|
3
|
+
return (
|
|
4
|
+
<div className="cm:flex cm:gap-4 cm:items-start cm:relative cm:shrink-0 cm:w-full cm:h-[100vh]">
|
|
5
|
+
<div className="cm:flex-1">dummy data</div>
|
|
6
|
+
|
|
7
|
+
<div className="cm:border-l cm:h-full cm:border-slate-border-light cm:w-[330px]">
|
|
8
|
+
<RightPanel />
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default Converation;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useNavigate, useLocation } from 'react-router-dom';
|
|
3
|
+
import ConversationBox from '../ConversationBox';
|
|
4
|
+
import type { ConversationBoxProps } from '../ConversationBox/types';
|
|
5
|
+
|
|
6
|
+
// Sample conversation data for contact - can be filtered by contact later
|
|
7
|
+
const conversations: Omit<ConversationBoxProps, 'onClick'>[] = [
|
|
8
|
+
{
|
|
9
|
+
type: 'chat',
|
|
10
|
+
status: 'open',
|
|
11
|
+
senderName: 'Blake Lively',
|
|
12
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
13
|
+
messagePreview: 'I am unable to access key features. The loading times are excessive, hindering my workflow. Can you please investigate?',
|
|
14
|
+
mailboxName: 'Sales',
|
|
15
|
+
assignedAgent: {
|
|
16
|
+
name: 'Alice Adams',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'email',
|
|
21
|
+
status: 'pending',
|
|
22
|
+
senderName: 'Ryan Reynolds',
|
|
23
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
24
|
+
heading: 'Issue with the app',
|
|
25
|
+
messagePreview: 'I am having trouble with the app. It keeps crashing and I am losing data. Can you please help?',
|
|
26
|
+
mailboxName: 'Finance',
|
|
27
|
+
assignedAgent: {
|
|
28
|
+
name: 'Andy Rue',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'chat',
|
|
33
|
+
status: 'pending',
|
|
34
|
+
senderName: 'Chris Pratt',
|
|
35
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
36
|
+
messagePreview: 'I am encountering persistent error messages when trying to save changes. This is impacting my productivity. Please assist.',
|
|
37
|
+
mailboxName: 'Sales',
|
|
38
|
+
assignedAgent: {
|
|
39
|
+
name: 'Kate Wills',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'email',
|
|
44
|
+
status: 'closed',
|
|
45
|
+
senderName: 'Zoe Saldana',
|
|
46
|
+
timestamp: 'Feb 4, 02:45 PM',
|
|
47
|
+
heading: "Can't log in to my account",
|
|
48
|
+
messagePreview: 'I am experiencing synchronization problems across devices. My data is not updating correctly. Could you resolve this?',
|
|
49
|
+
mailboxName: 'Finance',
|
|
50
|
+
assignedAgent: {
|
|
51
|
+
name: 'Kevin Smith',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const ContactConversation: React.FC = () => {
|
|
57
|
+
const navigate = useNavigate();
|
|
58
|
+
const location = useLocation();
|
|
59
|
+
|
|
60
|
+
console.log('Current route:', location.pathname);
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const handleConversationClick = () => {
|
|
64
|
+
navigate('/conversation');
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="cm:flex cm:flex-col cm:gap-2 cm:p-6 cm:w-full">
|
|
69
|
+
{conversations.map((conversation, index) => (
|
|
70
|
+
<ConversationBox
|
|
71
|
+
key={index}
|
|
72
|
+
{...conversation}
|
|
73
|
+
onClick={handleConversationClick}
|
|
74
|
+
/>
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default ContactConversation;
|
|
81
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useParams } from 'react-router-dom';
|
|
3
|
+
import { contactsData, accountData } from '../../constants/index';
|
|
4
|
+
import accountsIcon from '../../assets/accounts.svg';
|
|
5
|
+
import atTheRateIcon from '../../assets/at_the_rate.svg';
|
|
6
|
+
import phoneIcon from '../../assets/phone.svg';
|
|
7
|
+
import Avatar from '../Avatar';
|
|
8
|
+
import EditContact from '../Contacts/EditContact';
|
|
9
|
+
|
|
10
|
+
interface DetailRowProps {
|
|
11
|
+
icon: React.ReactNode;
|
|
12
|
+
label: string;
|
|
13
|
+
value: string | number;
|
|
14
|
+
valueColor?: 'default' | 'primary';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DetailRow: React.FC<DetailRowProps> = ({ icon, label, value, valueColor = 'default' }) => (
|
|
18
|
+
<div className="cm:flex cm:flex-col cm:items-start cm:relative cm:shrink-0 cm:w-full">
|
|
19
|
+
<div className="cm:flex cm:h-9 cm:items-center cm:relative cm:shrink-0 cm:w-full">
|
|
20
|
+
<div className="cm:flex cm:gap-2 cm:items-center cm:relative cm:shrink-0 cm:w-[100px]">
|
|
21
|
+
<div className="cm:relative cm:shrink-0 cm:w-3.5 cm:h-3.5 cm:flex cm:items-center cm:justify-center">
|
|
22
|
+
{icon}
|
|
23
|
+
</div>
|
|
24
|
+
<p className="cm:font-normal cm:leading-5 cm:text-sm cm:text-left cm:text-slate-text-subtle cm:w-[90px]">
|
|
25
|
+
{label}
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
28
|
+
<div className="cm:flex-1 cm:flex cm:gap-2 cm:h-full cm:items-center cm:min-w-0 cm:px-1 cm:py-0 cm:relative cm:shrink-0">
|
|
29
|
+
<p className={`cm:font-medium cm:leading-5 cm:text-sm cm:whitespace-nowrap ${
|
|
30
|
+
valueColor === 'primary'
|
|
31
|
+
? 'cm:text-primary-text-default'
|
|
32
|
+
: 'cm:text-slate-text-title'
|
|
33
|
+
}`}>
|
|
34
|
+
{value}
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const ContactDetails: React.FC = () => {
|
|
42
|
+
const { contactId } = useParams<{ contactId: string }>();
|
|
43
|
+
|
|
44
|
+
const contact = useMemo(() => {
|
|
45
|
+
if (!contactId) return null;
|
|
46
|
+
return contactsData.data.results.find(
|
|
47
|
+
(cont: { id: number }) => cont.id.toString() === contactId
|
|
48
|
+
);
|
|
49
|
+
}, [contactId]);
|
|
50
|
+
|
|
51
|
+
const account = useMemo(() => {
|
|
52
|
+
if (!contact?.company?.id) return null;
|
|
53
|
+
return accountData.data.results.find(
|
|
54
|
+
(acc: { id: number }) => acc.id === contact.company.id
|
|
55
|
+
);
|
|
56
|
+
}, [contact]);
|
|
57
|
+
|
|
58
|
+
if (!contact) {
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<p className="cm:text-sm cm:text-slate-text-subtle">Contact not found</p>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="cm:flex cm:flex-col cm:gap-[20px] cm:items-stretch cm:relative cm:shrink-0 cm:w-full">
|
|
68
|
+
{/* Contact Name Section */}
|
|
69
|
+
<div className="cm:flex cm:items-center cm:justify-between cm:px-0 cm:rounded-[6px] cm:shrink-0">
|
|
70
|
+
<div className="cm:flex cm:gap-3 cm:items-center cm:relative cm:shrink-0 cm:w-full">
|
|
71
|
+
{/* Contact Avatar */}
|
|
72
|
+
<Avatar
|
|
73
|
+
name={contact.name}
|
|
74
|
+
labelName={contact.name}
|
|
75
|
+
size="large"
|
|
76
|
+
className="cm:w-[28px] cm:h-[28px]"
|
|
77
|
+
/>
|
|
78
|
+
<p className="cm:font-medium cm:leading-5 cm:text-sm cm:text-slate-text-title cm:whitespace-nowrap">
|
|
79
|
+
{contact.name}
|
|
80
|
+
</p>
|
|
81
|
+
</div>
|
|
82
|
+
<EditContact />
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{/* Details Section */}
|
|
86
|
+
{/* <div className="cm:flex cm:flex-col cm:gap-0.5 cm:items-start cm:relative cm:shrink-0 cm:w-full"> */}
|
|
87
|
+
{account && (
|
|
88
|
+
<DetailRow
|
|
89
|
+
icon={<img src={accountsIcon} alt="account" className="cm:w-3.5 cm:h-3.5" />}
|
|
90
|
+
label="Account"
|
|
91
|
+
value={account.name}
|
|
92
|
+
/>
|
|
93
|
+
)}
|
|
94
|
+
<DetailRow
|
|
95
|
+
icon={<img src={atTheRateIcon} alt="email" className="cm:w-3.5 cm:h-3.5" />}
|
|
96
|
+
label="Email"
|
|
97
|
+
value={contact.email || 'N/A'}
|
|
98
|
+
valueColor="primary"
|
|
99
|
+
/>
|
|
100
|
+
<DetailRow
|
|
101
|
+
icon={<img src={phoneIcon} alt="phone" className="cm:w-3.5 cm:h-3.5" />}
|
|
102
|
+
label="Phone"
|
|
103
|
+
value={contact.phone_number || 'N/A'}
|
|
104
|
+
/>
|
|
105
|
+
{/* </div> */}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default ContactDetails;
|
|
111
|
+
|