@zendesk/retrace
Version:
define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API
243 lines • 13 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomerSidebar = void 0;
const react_1 = require("react");
const React = __importStar(require("react"));
const styled_components_1 = __importDefault(require("styled-components"));
const react_avatars_1 = require("@zendeskgarden/react-avatars");
const react_loaders_1 = require("@zendeskgarden/react-loaders");
const react_notifications_1 = require("@zendeskgarden/react-notifications");
const react_theming_1 = require("@zendeskgarden/react-theming");
const react_typography_1 = require("@zendeskgarden/react-typography");
const building_stroke_svg_1 = require("@zendeskgarden/svg-icons/src/16/building-stroke.svg");
const calendar_stroke_svg_1 = require("@zendeskgarden/svg-icons/src/16/calendar-stroke.svg");
const email_stroke_svg_1 = require("@zendeskgarden/svg-icons/src/16/email-stroke.svg");
const notes_stroke_svg_1 = require("@zendeskgarden/svg-icons/src/16/notes-stroke.svg");
const user_solo_stroke_svg_1 = require("@zendeskgarden/svg-icons/src/16/user-solo-stroke.svg");
const mockCustomers_1 = require("./mockCustomers");
const simulateLongTasks_1 = require("./simulateLongTasks");
const traceManager_1 = require("./traceManager");
const SidebarContainer = (0, styled_components_1.default)(react_notifications_1.Well) `
width: 320px;
height: fit-content;
margin-left: ${react_theming_1.DEFAULT_THEME.space.base * 4}px;
padding: ${react_theming_1.DEFAULT_THEME.space.base * 4}px;
`;
const CustomerSection = styled_components_1.default.div `
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-bottom: ${react_theming_1.DEFAULT_THEME.space.base * 4}px;
`;
const CustomerInfo = styled_components_1.default.div `
display: flex;
flex-direction: column;
gap: ${react_theming_1.DEFAULT_THEME.space.base * 2}px;
width: 100%;
`;
const InfoRow = styled_components_1.default.div `
display: flex;
align-items: center;
gap: ${react_theming_1.DEFAULT_THEME.space.base * 2}px;
padding: ${react_theming_1.DEFAULT_THEME.space.base * 2}px;
background-color: ${react_theming_1.PALETTE.grey[100]};
border-radius: ${react_theming_1.DEFAULT_THEME.borderRadii.md};
`;
const StatusBadge = (0, styled_components_1.default)(react_typography_1.Span) `
padding: ${react_theming_1.DEFAULT_THEME.space.base}px ${react_theming_1.DEFAULT_THEME.space.base * 2}px;
border-radius: ${react_theming_1.DEFAULT_THEME.borderRadii.sm};
font-size: ${react_theming_1.DEFAULT_THEME.fontSizes.xs};
font-weight: ${react_theming_1.DEFAULT_THEME.fontWeights.semibold};
text-transform: uppercase;
color: ${(props) => {
switch (props.status) {
case 'vip':
return react_theming_1.PALETTE.purple[700];
case 'active':
return react_theming_1.PALETTE.green[700];
case 'inactive':
return react_theming_1.PALETTE.grey[600];
default:
return react_theming_1.PALETTE.grey[600];
}
}};
background-color: ${(props) => {
switch (props.status) {
case 'vip':
return react_theming_1.PALETTE.purple[100];
case 'active':
return react_theming_1.PALETTE.green[100];
case 'inactive':
return react_theming_1.PALETTE.grey[200];
default:
return react_theming_1.PALETTE.grey[200];
}
}};
`;
const NotesSection = styled_components_1.default.div `
margin-top: ${react_theming_1.DEFAULT_THEME.space.base * 3}px;
padding: ${react_theming_1.DEFAULT_THEME.space.base * 3}px;
background-color: ${react_theming_1.PALETTE.blue[100]};
border-radius: ${react_theming_1.DEFAULT_THEME.borderRadii.md};
border-left: 4px solid ${react_theming_1.PALETTE.blue[600]};
`;
const CustomerSidebar = ({ ticketId, }) => {
const [customer, setCustomer] = (0, react_1.useState)(null);
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
const [error, setError] = (0, react_1.useState)(null);
// Use beacon for tracing
(0, traceManager_1.useBeacon)({
name: 'CustomerSidebar',
relatedTo: { ticketId },
renderedOutput: isLoading ? 'loading' : customer ? 'content' : 'error',
isIdle: !isLoading,
error: error ? new Error(error) : undefined,
attributes: {
customerId: customer?.id,
customerStatus: customer?.status,
},
});
(0, react_1.useEffect)(() => {
const loadCustomerData = async () => {
setIsLoading(true);
setError(null);
// Simulate network request with timing similar to the provided example
await new Promise((resolve) => {
setTimeout(resolve, 1_800 + Math.random() * 400);
});
// Simulate some CPU work
(0, simulateLongTasks_1.triggerLongTasks)({
minTime: 20,
maxTime: 50,
totalClusterDuration: 100,
});
try {
const customerData = (0, mockCustomers_1.getCustomerByTicketId)(ticketId);
if (!customerData) {
setError('Customer not found');
setCustomer(null);
}
else {
setCustomer(customerData);
}
}
catch {
setError('Failed to load customer data');
setCustomer(null);
}
finally {
setIsLoading(false);
}
};
void loadCustomerData();
}, [ticketId]);
if (isLoading) {
return (React.createElement(SidebarContainer, null,
React.createElement(react_typography_1.LG, { isBold: true, style: { marginBottom: react_theming_1.DEFAULT_THEME.space.base * 4 } }, "Customer Context"),
React.createElement(CustomerSection, null,
React.createElement(react_loaders_1.Skeleton, { height: "64px", width: "64px", style: { borderRadius: '50%' } }),
React.createElement(react_loaders_1.Skeleton, { height: "20px", width: "140px", style: { marginTop: react_theming_1.DEFAULT_THEME.space.base * 2 } }),
React.createElement(react_loaders_1.Skeleton, { height: "16px", width: "180px" })),
React.createElement("div", { style: {
display: 'flex',
flexDirection: 'column',
gap: react_theming_1.DEFAULT_THEME.space.base * 2,
} },
React.createElement(react_loaders_1.Skeleton, { height: "40px" }),
React.createElement(react_loaders_1.Skeleton, { height: "40px" }),
React.createElement(react_loaders_1.Skeleton, { height: "40px" }),
React.createElement(react_loaders_1.Skeleton, { height: "60px" }))));
}
if (error || !customer) {
return (React.createElement(SidebarContainer, null,
React.createElement(react_typography_1.LG, { isBold: true, style: { marginBottom: react_theming_1.DEFAULT_THEME.space.base * 4 } }, "Customer Context"),
React.createElement(react_notifications_1.Alert, { type: "error" },
React.createElement(react_notifications_1.Alert.Title, null, "Unable to load customer data"),
error ?? 'Customer information not available for this ticket.')));
}
return (React.createElement(SidebarContainer, null,
React.createElement(react_typography_1.LG, { isBold: true, style: { marginBottom: react_theming_1.DEFAULT_THEME.space.base * 4 } }, "Customer Context"),
React.createElement(CustomerSection, null,
React.createElement(react_avatars_1.Avatar, { size: "large", backgroundColor: react_theming_1.PALETTE.grey[600] },
React.createElement("img", { alt: customer.name, src: customer.avatar, style: { width: '100%', height: '100%', objectFit: 'cover' }, onError: (e) => {
// Fallback to icon if image fails to load
e.currentTarget.style.display = 'none';
e.currentTarget.parentElement.innerHTML = `
<svg width="32" height="32" viewBox="0 0 16 16" fill="currentColor">
<path d="M8 8a3 3 0 100-6 3 3 0 000 6zM8 9a5 5 0 00-5 5h10a5 5 0 00-5-5z"/>
</svg>
`;
} })),
React.createElement(react_typography_1.MD, { isBold: true, style: { marginTop: react_theming_1.DEFAULT_THEME.space.base * 2 } }, customer.name),
React.createElement(StatusBadge, { status: customer.status }, customer.status)),
React.createElement(CustomerInfo, null,
React.createElement(InfoRow, null,
React.createElement(email_stroke_svg_1.ReactComponent, { style: { color: react_theming_1.PALETTE.grey[600], flexShrink: 0 } }),
React.createElement("div", null,
React.createElement(react_typography_1.SM, { isBold: true, style: { color: react_theming_1.PALETTE.grey[600], display: 'block' } }, "Email"),
React.createElement(react_typography_1.Span, null, customer.email))),
React.createElement(InfoRow, null,
React.createElement(building_stroke_svg_1.ReactComponent, { style: { color: react_theming_1.PALETTE.grey[600], flexShrink: 0 } }),
React.createElement("div", null,
React.createElement(react_typography_1.SM, { isBold: true, style: { color: react_theming_1.PALETTE.grey[600], display: 'block' } }, "Company"),
React.createElement(react_typography_1.Span, null, customer.company))),
React.createElement(InfoRow, null,
React.createElement(calendar_stroke_svg_1.ReactComponent, { style: { color: react_theming_1.PALETTE.grey[600], flexShrink: 0 } }),
React.createElement("div", null,
React.createElement(react_typography_1.SM, { isBold: true, style: { color: react_theming_1.PALETTE.grey[600], display: 'block' } }, "Customer Since"),
React.createElement(react_typography_1.Span, null, customer.joined))),
React.createElement(InfoRow, null,
React.createElement(user_solo_stroke_svg_1.ReactComponent, { style: { color: react_theming_1.PALETTE.grey[600], flexShrink: 0 } }),
React.createElement("div", null,
React.createElement(react_typography_1.SM, { isBold: true, style: { color: react_theming_1.PALETTE.grey[600], display: 'block' } }, "Total Tickets"),
React.createElement(react_typography_1.Span, null, customer.ticketCount)))),
React.createElement(NotesSection, null,
React.createElement("div", { style: {
display: 'flex',
alignItems: 'center',
gap: react_theming_1.DEFAULT_THEME.space.base,
marginBottom: react_theming_1.DEFAULT_THEME.space.base * 2,
} },
React.createElement(notes_stroke_svg_1.ReactComponent, { style: { color: react_theming_1.PALETTE.blue[600] } }),
React.createElement(react_typography_1.SM, { isBold: true, style: { color: react_theming_1.PALETTE.blue[700] } }, "AGENT NOTES")),
React.createElement(react_typography_1.Span, { style: { color: react_theming_1.PALETTE.blue[800] } }, customer.notes))));
};
exports.CustomerSidebar = CustomerSidebar;
//# sourceMappingURL=CustomerSidebar.js.map