Skip to content

Commit e69896a

Browse files
cmullenxChristina MullenShreyas281299
authored
feat(cc-widgets): added new incomingtask and task ui based on figma design (webex#402)
Co-authored-by: Christina Mullen <chrmulle@cisco.com> Co-authored-by: Shreyas Sharma <shreyassharma9912@gmail.com>
1 parent 6ece5bd commit e69896a

File tree

17 files changed

+317
-361
lines changed

17 files changed

+317
-361
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"test:cc-widgets": "yarn workspaces foreach --all --exclude webex-widgets --exclude samples-cc-wc-app --exclude samples-cc-react-app run test:unit",
5252
"build:dev": "NODE_ENV=development yarn build",
5353
"build:prod": "NODE_ENV=production yarn build",
54-
"build": "NODE_OPTIONS=--max-old-space-size=4096 yarn workspaces foreach --all --parallel --topological --exclude samples-cc-react-app --exclude samples-cc-wc-app --exclude samples-meeting-app run build:src",
54+
"build": "NODE_OPTIONS=--max-old-space-size=4096 yarn workspaces foreach --all --topological --exclude samples-cc-react-app --exclude samples-cc-wc-app --exclude samples-meeting-app run build:src",
5555
"samples:build": "yarn workspaces foreach --all --parallel --include samples-cc-react-app --include samples-cc-wc-app --include samples-meeting-app run build:src && ./copy_to_docs.sh",
5656
"samples:serve": "http-server docs",
5757
"samples:serve-react": "yarn workspace samples-cc-react-app serve",

packages/contact-center/cc-components/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@
7575
"react": ">=18.3.1",
7676
"react-dom": ">=18.3.1"
7777
}
78-
}
78+
}

packages/contact-center/cc-widgets/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,4 @@
8686
"^.+\\.(css|less|scss)$": "babel-jest"
8787
}
8888
}
89-
}
89+
}

packages/contact-center/station-login/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@
7272
"^.+\\.(css|less|scss)$": "babel-jest"
7373
}
7474
}
75-
}
75+
}

packages/contact-center/store/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@
6464
"**/tests/**/*.tsx"
6565
]
6666
}
67-
}
67+
}

packages/contact-center/task/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,4 @@
7171
"^.+\\.(css|less|scss)$": "babel-jest"
7272
}
7373
}
74-
}
74+
}

packages/contact-center/task/src/IncomingTask/incoming-task.presentational.tsx

+17-186
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,30 @@
11
import React from 'react';
22
import {IncomingTaskPresentationalProps} from '../task.types';
3-
import {ButtonPill} from '@momentum-ui/react-collaboration';
4-
5-
const styles: {[key: string]: React.CSSProperties} = {
6-
box: {
7-
backgroundColor: '#ffffff',
8-
borderRadius: '8px',
9-
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
10-
padding: '20px',
11-
maxWidth: '800px',
12-
margin: '0 auto',
13-
},
14-
sectionBox: {
15-
padding: '10px',
16-
border: '1px solid #ddd',
17-
borderRadius: '8px',
18-
marginBottom: '20px',
19-
},
20-
fieldset: {
21-
border: '1px solid #ccc',
22-
borderRadius: '5px',
23-
padding: '10px',
24-
marginBottom: '20px',
25-
position: 'relative',
26-
},
27-
legendBox: {
28-
fontWeight: 'bold',
29-
color: '#0052bf',
30-
},
31-
container: {
32-
border: '1px solid #ccc',
33-
borderRadius: '8px',
34-
padding: '16px',
35-
width: '350px',
36-
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.2)',
37-
fontFamily: 'Arial, sans-serif',
38-
backgroundColor: '#ffffff',
39-
display: 'flex',
40-
flexDirection: 'column',
41-
},
42-
topSection: {
43-
display: 'flex',
44-
justifyContent: 'space-between',
45-
alignItems: 'center',
46-
},
47-
iconWrapper: {
48-
display: 'inline-block',
49-
backgroundColor: '#d4f8e8',
50-
borderRadius: '50%',
51-
width: '40px',
52-
height: '40px',
53-
justifyContent: 'center',
54-
alignItems: 'center',
55-
marginRight: '10px',
56-
},
57-
iconSvg: {
58-
width: '24px',
59-
height: '24px',
60-
color: '#146f5c',
61-
},
62-
callInfo: {
63-
margin: 0,
64-
fontSize: '1.2em',
65-
color: '#333',
66-
},
67-
aniText: {
68-
fontSize: '1.1em',
69-
fontWeight: 'bold',
70-
margin: '4px 0',
71-
color: '#146f5c',
72-
},
73-
buttonsWrapper: {
74-
display: 'flex',
75-
flexDirection: 'column',
76-
alignItems: 'flex-end',
77-
marginLeft: '16px',
78-
},
79-
answerButton: {
80-
padding: '8px 16px',
81-
border: 'none',
82-
borderRadius: '6px',
83-
fontSize: '0.9em',
84-
cursor: 'pointer',
85-
fontWeight: 'bold',
86-
backgroundColor: '#28a745',
87-
color: '#fff',
88-
marginBottom: '8px',
89-
},
90-
declineButton: {
91-
padding: '8px 16px',
92-
border: 'none',
93-
borderRadius: '6px',
94-
fontSize: '0.9em',
95-
cursor: 'pointer',
96-
fontWeight: 'bold',
97-
backgroundColor: '#dc3545',
98-
color: '#fff',
99-
},
100-
queueInfo: {
101-
fontSize: '0.9em',
102-
color: '#666',
103-
marginTop: '8px',
104-
},
105-
timeElapsed: {
106-
color: '#28a745',
107-
fontWeight: 'bold',
108-
},
109-
callDetails: {
110-
marginTop: '16px',
111-
fontSize: '0.9em',
112-
color: '#333',
113-
},
114-
detailItem: {
115-
margin: '4px 0',
116-
},
117-
detailLabel: {
118-
color: '#555',
119-
fontWeight: 'bold',
120-
},
121-
};
3+
import Task from '../Task';
1224

1235
const IncomingTaskPresentational: React.FunctionComponent<IncomingTaskPresentationalProps> = (props) => {
1246
const {incomingTask, accept, decline, isBrowser} = props;
1257
if (!incomingTask) {
1268
return <></>; // hidden component
1279
}
12810

129-
const callAssociationDetails = incomingTask.data.interaction.callAssociatedDetails;
130-
const {ani, dn, virtualTeamName} = callAssociationDetails;
131-
const timeElapsed = ''; // TODO: Calculate time elapsed
132-
11+
const callAssociationDetails = incomingTask?.data?.interaction?.callAssociatedDetails;
12+
const ani = callAssociationDetails?.ani;
13+
const virtualTeamName = callAssociationDetails?.virtualTeamName;
14+
// rona timeout is not always available in the callAssociatedDetails object
15+
const ronaTimeout = callAssociationDetails?.ronaTimeout ? Number(callAssociationDetails?.ronaTimeout) : null;
16+
const startTimeStamp = incomingTask?.data?.interaction?.createdTimestamp;
13317
return (
134-
<div style={styles.box}>
135-
<section style={styles.sectionBox}>
136-
<fieldset style={styles.fieldset}>
137-
<legend style={styles.legendBox}>Incoming Task</legend>
138-
<div data-testid="incoming-task-presentational" style={styles.container}>
139-
{/* Top Section - Call Info with Phone Icon */}
140-
<div style={styles.topSection}>
141-
<div style={{display: 'flex', alignItems: 'center'}}>
142-
<span style={styles.iconWrapper}>
143-
<svg
144-
xmlns="http://www.w3.org/2000/svg"
145-
viewBox="0 0 24 24"
146-
fill="none"
147-
stroke="currentColor"
148-
strokeWidth="2"
149-
strokeLinecap="round"
150-
strokeLinejoin="round"
151-
style={styles.iconSvg}
152-
>
153-
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-2.73 19.5 19.5 0 0 1-6-6A19.86 19.86 0 0 1 3.08 4.18 2 2 0 0 1 5 2h3a2 2 0 0 1 2 1.72c.2 1.52.71 2.94 1.41 4.24a2 2 0 0 1-.45 2.31L9.91 11a16 16 0 0 0 6 6l1.73-1.05a2 2 0 0 1 2.31-.45 16.11 16.11 0 0 0 4.24 1.41A2 2 0 0 1 22 16.92z"></path>
154-
</svg>
155-
</span>
156-
<div>
157-
<h2 style={styles.callInfo}>Incoming Call</h2>
158-
<p data-testid="incoming-task-ani" style={styles.aniText}>
159-
{ani}
160-
</p>
161-
</div>
162-
</div>
163-
164-
{isBrowser && (
165-
<div style={styles.buttonsWrapper}>
166-
<ButtonPill style={styles.answerButton} onPress={accept}>
167-
Answer
168-
</ButtonPill>
169-
<ButtonPill style={styles.declineButton} onPress={decline}>
170-
Decline
171-
</ButtonPill>
172-
</div>
173-
)}
174-
</div>
175-
176-
{/* Queue and Timer Info */}
177-
<p style={styles.queueInfo}>
178-
{virtualTeamName} - <span style={styles.timeElapsed}>{timeElapsed}</span>
179-
</p>
180-
181-
{/* Call Details Section */}
182-
<div style={styles.callDetails}>
183-
<p style={styles.detailItem}>
184-
<strong style={styles.detailLabel}>Phone Number:</strong> {ani}
185-
</p>
186-
<p style={styles.detailItem}>
187-
<strong style={styles.detailLabel}>DNIS:</strong> {dn}
188-
</p>
189-
<p style={styles.detailItem}>
190-
<strong style={styles.detailLabel}>Queue Name:</strong> {virtualTeamName}
191-
</p>
192-
</div>
193-
</div>
194-
</fieldset>
195-
</section>
196-
</div>
18+
<Task
19+
title={ani}
20+
queue={virtualTeamName}
21+
isIncomingTask={true}
22+
isBrowser={isBrowser}
23+
acceptTask={accept}
24+
declineTask={decline}
25+
ronaTimeout={ronaTimeout}
26+
startTimeStamp={startTimeStamp}
27+
/>
19728
);
19829
};
19930

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import React from 'react';
2+
import {ButtonPill, ListItemBase, ListItemBaseSection, Text} from '@momentum-ui/react-collaboration';
3+
import {Avatar} from '@momentum-design/components/dist/react';
4+
import {PressEvent} from '@react-types/shared';
5+
import TaskTimer from '../TaskTimer';
6+
import './styles.scss';
7+
8+
interface TaskProps {
9+
title?: string;
10+
state?: string;
11+
startTimeStamp?: number;
12+
ronaTimeout?: number;
13+
selected?: boolean;
14+
isIncomingTask?: boolean;
15+
queue?: string;
16+
acceptTask?: (e: PressEvent) => void;
17+
declineTask?: (e: PressEvent) => void;
18+
isBrowser?: boolean;
19+
}
20+
21+
const Task: React.FC<TaskProps> = ({
22+
title,
23+
state,
24+
startTimeStamp,
25+
ronaTimeout,
26+
selected = false,
27+
isIncomingTask = false,
28+
queue,
29+
acceptTask,
30+
declineTask,
31+
isBrowser,
32+
}) => {
33+
const capitalizeFirstWord = (str: string) => {
34+
return str.replace(/^\s*(\w)/, (match, firstLetter) => firstLetter.toUpperCase());
35+
};
36+
37+
return (
38+
<ListItemBase className={`task-list-item ${selected ? 'task-list-item--selected' : ''}`}>
39+
<ListItemBaseSection position="start" className="task-list-item-start-section">
40+
<Avatar
41+
icon-name="handset-filled"
42+
className={`task-list-item-avatar ${selected ? 'task-list-item-avatar--selected' : ''}`}
43+
/>
44+
</ListItemBaseSection>
45+
46+
<ListItemBaseSection position="fill">
47+
<section className="task-details">
48+
{title && (
49+
<Text tagName="span" type={selected ? 'body-large-bold' : 'body-large-medium'} className="task-text">
50+
{title}
51+
</Text>
52+
)}
53+
54+
{state && !isIncomingTask && (
55+
<Text tagName="span" type="body-midsize-regular" className="task-text task-text--secondary">
56+
{capitalizeFirstWord(state)}
57+
</Text>
58+
)}
59+
60+
{queue && isIncomingTask && (
61+
<Text tagName="span" type="body-midsize-regular" className="task-text task-text--secondary">
62+
{capitalizeFirstWord(queue)}
63+
</Text>
64+
)}
65+
66+
{/* Handle Time should render if it's an incoming call without ronaTimeout OR if it's not an incoming call */}
67+
{(isIncomingTask && !ronaTimeout) || !isIncomingTask
68+
? startTimeStamp && (
69+
<Text tagName="span" type="body-midsize-regular" className="task-text task-text--secondary">
70+
Handle Time: {' '}
71+
<TaskTimer startTimeStamp={startTimeStamp} />
72+
</Text>
73+
)
74+
: null}
75+
76+
{/* Time Left should render if it's an incoming call with ronaTimeout */}
77+
{isIncomingTask && ronaTimeout && (
78+
<Text tagName="span" type="body-midsize-regular" className="task-text task-text--secondary">
79+
Time Left: {' '}
80+
<TaskTimer countdown={true} ronaTimeout={ronaTimeout} />
81+
</Text>
82+
)}
83+
</section>
84+
</ListItemBaseSection>
85+
86+
<ListItemBaseSection position="end">
87+
{isIncomingTask ? (
88+
<ButtonPill onPress={acceptTask} color="join" disabled={!isBrowser}>
89+
Ringing
90+
</ButtonPill>
91+
) : isBrowser ? (
92+
<ButtonPill onPress={declineTask} color="join">
93+
End
94+
</ButtonPill>
95+
) : null}
96+
</ListItemBaseSection>
97+
</ListItemBase>
98+
);
99+
};
100+
101+
export default Task;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
:root {
2+
--mds-color-theme-avatar-glass-normal: rgba(0, 0, 0, 0.07);
3+
}
4+
5+
@media (prefers-color-scheme: dark) {
6+
:root {
7+
--mds-color-theme-avatar-glass-normal: rgba(255, 255, 255, 0.07);
8+
}
9+
}
10+
11+
.task-list-item {
12+
display: flex;
13+
padding: 0.5rem 0.75rem;
14+
align-items: center;
15+
align-self: stretch;
16+
// have to override momentum default height for listitem to match the design
17+
height: 5rem !important;
18+
19+
mdc-avatar {
20+
--mdc-avatar-default-background-color: var(--mds-color-theme-avatar-glass-normal);
21+
--mdc-avatar-default-foreground-color: var(--mds-color-theme-indicator-stable);
22+
}
23+
}
24+
25+
.task-list-item--selected {
26+
background: var(--color-theme-background-primary-active, rgba(0, 0, 0, 0.11));
27+
28+
mdc-avatar {
29+
--mdc-avatar-default-background-color: var(--mds-color-theme-avatar-glass-active, rgba(255, 255, 255, 0.8));
30+
}
31+
}

0 commit comments

Comments
 (0)