From 256b049e7dccd6345e41260d38e720d4617cc2b7 Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Wed, 28 May 2025 17:12:52 +0200 Subject: [PATCH] ui: embed player, some pdf fixes for kai --- .../app/components/Kai/components/ChatLog.tsx | 9 +++ .../app/components/Kai/components/ChatMsg.tsx | 35 +++++++-- .../components/Kai/components/EmbedPlayer.tsx | 75 +++++++++++++++++++ .../app/components/Session/ClipsPlayer.tsx | 5 +- .../Player/ClipPlayer/ClipPlayerContent.tsx | 5 +- .../Player/ClipPlayer/ClipPlayerControls.tsx | 4 +- .../Player/ClipPlayer/ClipPlayerHeader.tsx | 5 +- .../shared/SessionItem/PlayLink/PlayLink.tsx | 10 ++- .../shared/SessionItem/SessionItem.tsx | 56 +++++++++----- frontend/app/components/ui/Icons/index.ts | 5 ++ .../app/components/ui/Icons/play_fill_v2.tsx | 18 +++++ .../ui/Icons/play_fill_v2_assist.tsx | 18 +++++ frontend/app/components/ui/Icons/play_v2.tsx | 18 +++++ .../components/ui/Icons/play_v2_assist.tsx | 18 +++++ .../app/components/ui/Icons/played_v2.tsx | 18 +++++ frontend/app/components/ui/SVG.tsx | 22 +++++- frontend/app/layout/SideMenu.tsx | 65 ++++++++++++---- frontend/app/layout/data.ts | 12 ++- .../app/svg/icons/play-fill-v2-assist.svg | 6 ++ frontend/app/svg/icons/play-fill-v2.svg | 6 ++ frontend/app/svg/icons/play-v2-assist.svg | 6 ++ frontend/app/svg/icons/play-v2.svg | 6 ++ frontend/app/svg/icons/played-v2.svg | 6 ++ 23 files changed, 377 insertions(+), 51 deletions(-) create mode 100644 frontend/app/components/Kai/components/EmbedPlayer.tsx create mode 100644 frontend/app/components/ui/Icons/play_fill_v2.tsx create mode 100644 frontend/app/components/ui/Icons/play_fill_v2_assist.tsx create mode 100644 frontend/app/components/ui/Icons/play_v2.tsx create mode 100644 frontend/app/components/ui/Icons/play_v2_assist.tsx create mode 100644 frontend/app/components/ui/Icons/played_v2.tsx create mode 100644 frontend/app/svg/icons/play-fill-v2-assist.svg create mode 100644 frontend/app/svg/icons/play-fill-v2.svg create mode 100644 frontend/app/svg/icons/play-v2-assist.svg create mode 100644 frontend/app/svg/icons/play-v2.svg create mode 100644 frontend/app/svg/icons/played-v2.svg diff --git a/frontend/app/components/Kai/components/ChatLog.tsx b/frontend/app/components/Kai/components/ChatLog.tsx index e2b3d8a8d..76fccb584 100644 --- a/frontend/app/components/Kai/components/ChatLog.tsx +++ b/frontend/app/components/Kai/components/ChatLog.tsx @@ -5,6 +5,7 @@ import Ideas from './Ideas'; import { Loader } from 'UI'; import { kaiStore } from '../KaiStore'; import { observer } from 'mobx-react-lite'; +import EmbedPlayer from './EmbedPlayer'; function ChatLog({ projectId, @@ -21,6 +22,7 @@ function ChatLog({ chatTitle: string | null; onCancel: () => void; }) { + const [embedSession, setEmbedSession] = React.useState(null); const messages = kaiStore.messages; const loading = kaiStore.loadingChat; const chatRef = React.useRef(null); @@ -64,6 +66,12 @@ function ChatLog({ 'overflow-y-auto relative flex flex-col items-center justify-between w-full h-full pt-4' } > + {embedSession ? ( + setEmbedSession(null)} + /> + ) : null}
{messages.map((msg, index) => ( @@ -71,6 +79,7 @@ function ChatLog({ siteId={projectId} message={msg} chatTitle={chatTitle} + onReplay={(session) => setEmbedSession(session)} canEdit={ processingStage === null && msg.isUser && diff --git a/frontend/app/components/Kai/components/ChatMsg.tsx b/frontend/app/components/Kai/components/ChatMsg.tsx index 436d6ecb7..7a1d6d8dc 100644 --- a/frontend/app/components/Kai/components/ChatMsg.tsx +++ b/frontend/app/components/Kai/components/ChatMsg.tsx @@ -27,12 +27,14 @@ function ChatMsg({ canEdit, message, chatTitle, + onReplay, }: { message: Message; userName?: string; canEdit?: boolean; siteId: string; chatTitle: string | null; + onReplay: (session: any) => void; }) { const { t } = useTranslation(); const [metric, setMetric] = React.useState(null); @@ -73,7 +75,6 @@ function ChatMsg({ .then(async ({ jsPDF }) => { const doc = new jsPDF(); const blockWidth = 170; // mm - doc.addImage('/assets/img/logo-img.png', 20, 15, 30, 5); const content = bodyRef.current!.cloneNode(true) as HTMLElement; if (userPrompt) { const titleHeader = document.createElement('h2'); @@ -81,6 +82,18 @@ function ChatMsg({ titleHeader.style.marginBottom = '10px'; content.prepend(titleHeader); } + // insert logo /assets/img/logo-img.png + const logo = new Image(); + logo.src = '/assets/img/logo-img.png'; + logo.style.width = '130px'; + const container = document.createElement('div'); + container.style.display = 'flex'; + container.style.alignItems = 'center'; + container.style.justifyContent = 'center'; + container.style.marginBottom = '10mm'; + container.style.width = `${blockWidth}mm`; + container.appendChild(logo); + content.prepend(container); content.querySelectorAll('ul').forEach((ul) => { const frag = document.createDocumentFragment(); ul.querySelectorAll('li').forEach((li) => { @@ -123,9 +136,9 @@ function ChatMsg({ doc.save((chatTitle ?? 'document') + '.pdf'); }, // top, bottom, ?, left - margin: [5, 10, 20, 20], + margin: [10, 10, 20, 20], x: 0, - y: 15, + y: 0, // Target width width: blockWidth, // Window width for rendering @@ -172,7 +185,7 @@ function ChatMsg({ }, [metricData, chart_data]); return (
-
+
{message.sessions.map((session) => ( -
- +
+ { + e.preventDefault(); + e.stopPropagation(); + onReplay(session); + }} + slim + />
))}
diff --git a/frontend/app/components/Kai/components/EmbedPlayer.tsx b/frontend/app/components/Kai/components/EmbedPlayer.tsx new file mode 100644 index 000000000..6d4074f6d --- /dev/null +++ b/frontend/app/components/Kai/components/EmbedPlayer.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import cn from 'classnames'; +import { useStore } from 'App/mstore'; +import { Loader } from 'UI'; +import { observer } from 'mobx-react-lite'; +import MobileClipsPlayer from 'App/components/Session/MobileClipsPlayer'; +import ClipsPlayer from 'App/components/Session/ClipsPlayer'; +import Session from '@/types/session/session'; + +interface Clip { + sessionId: string | undefined; + range: [number, number]; + message: string; +} + +function EmbedPlayer({ + session, + onClose, +}: { + session: Session; + onClose: () => void; +}) { + const { projectsStore } = useStore(); + const clip = { + sessionId: session.sessionId, + range: [0, session.durationMs], + message: '', + }; + const { isMobile } = projectsStore; + + const onBgClick = (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + onClose(); + } + }; + return ( +
+
+ {isMobile ? ( + + ) : ( + + )} +
+
+ ); +} + +export default observer(EmbedPlayer); diff --git a/frontend/app/components/Session/ClipsPlayer.tsx b/frontend/app/components/Session/ClipsPlayer.tsx index e76ae46d5..82d3bcb0e 100644 --- a/frontend/app/components/Session/ClipsPlayer.tsx +++ b/frontend/app/components/Session/ClipsPlayer.tsx @@ -26,10 +26,11 @@ interface Props { autoplay: boolean; onClose?: () => void; isHighlight?: boolean; + isFull?: boolean; } function ClipsPlayer(props: Props) { - const { clip, currentIndex, isCurrent, onClose, isHighlight } = props; + const { clip, currentIndex, isCurrent, onClose, isHighlight, isFull } = props; const { sessionStore } = useStore(); const { prefetched } = sessionStore; const [windowActive, setWindowActive] = useState(!document.hidden); @@ -146,6 +147,7 @@ function ClipsPlayer(props: Props) { onClose={onClose} range={clip.range} session={session!} + isFull={isFull} /> ) : ( diff --git a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerContent.tsx b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerContent.tsx index ddf267669..cfaee07cf 100644 --- a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerContent.tsx +++ b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerContent.tsx @@ -19,13 +19,14 @@ interface Props { isHighlight?: boolean; message?: string; isMobile?: boolean; + isFull?: boolean; } function ClipPlayerContent(props: Props) { const playerContext = React.useContext(PlayerContext); const screenWrapper = React.useRef(null); const { time } = playerContext.store.get(); - const { range } = props; + const { range, isFull } = props; React.useEffect(() => { if (!playerContext.player) return; @@ -90,7 +91,7 @@ function ClipPlayerContent(props: Props) {
{props.message}
) : null} - +
); diff --git a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx index 6d2dc0f82..19ca0e96e 100644 --- a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx +++ b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx @@ -15,9 +15,11 @@ import { useTranslation } from 'react-i18next'; function ClipPlayerControls({ session, range, + isFull, }: { session: Session; range: [number, number]; + isFull?: boolean; }) { const { t } = useTranslation(); const { projectsStore } = useStore(); @@ -47,7 +49,7 @@ function ClipPlayerControls({
diff --git a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerHeader.tsx b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerHeader.tsx index 75d8b6951..b7fd37c9e 100644 --- a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerHeader.tsx +++ b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerHeader.tsx @@ -16,12 +16,13 @@ interface Props { range: [number, number]; onClose?: () => void; isHighlight?: boolean; + isFull?: boolean; } function ClipPlayerHeader(props: Props) { const { t } = useTranslation(); const { projectsStore } = useStore(); - const { session, range, onClose, isHighlight } = props; + const { session, range, onClose, isHighlight, isFull } = props; const { siteId } = projectsStore; const { message } = App.useApp(); @@ -33,7 +34,7 @@ function ClipPlayerHeader(props: Props) { }; return (
- {isHighlight ? : null} + {isHighlight && !isFull ? : null} diff --git a/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx b/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx index ce6b2f5c3..35a949510 100644 --- a/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx +++ b/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx @@ -10,7 +10,7 @@ import { Icon, Link } from 'UI'; import { useStore } from 'App/mstore'; const PLAY_ICON_NAMES = { - notPlayed: 'play-fill', + notPlayed: 'play-v2', played: 'play-circle-light', } as const; @@ -76,10 +76,14 @@ function PlayLink(props: Props) { rel={props.newTab ? 'noopener noreferrer' : undefined} >
- +
- +
); diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx index 23128a84f..f4591e2b2 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.tsx +++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx @@ -82,7 +82,8 @@ const PREFETCH_STATE = { function SessionItem(props: RouteComponentProps & Props) { const { location } = useHistory(); - const { settingsStore, sessionStore, searchStore, searchStoreLive } = useStore(); + const { settingsStore, sessionStore, searchStore, searchStoreLive } = + useStore(); const { timezone, shownTimezone } = settingsStore.sessionSettings; const { t } = useTranslation(); const [prefetchState, setPrefetched] = useState(PREFETCH_STATE.none); @@ -178,7 +179,7 @@ function SessionItem(props: RouteComponentProps & Props) { await sessionStore.getFirstMob(sessionId); setPrefetched(PREFETCH_STATE.fetched); } catch (e) { - setPrefetched(PREFETCH_STATE.none) + setPrefetched(PREFETCH_STATE.none); console.error('Error while prefetching first mob', e); } }, [prefetchState, live, isAssist, isMobile, sessionStore, sessionId]); @@ -247,13 +248,13 @@ function SessionItem(props: RouteComponentProps & Props) { ); }, [startedAt, timezone, userTimezone]); - const onMetaClick = (meta: { name: string, value: string }) => { + const onMetaClick = (meta: { name: string; value: string }) => { if (isAssist) { - searchStoreLive.addFilterByKeyAndValue(meta.name, meta.value) + searchStoreLive.addFilterByKeyAndValue(meta.name, meta.value); } else { searchStore.addFilterByKeyAndValue(meta.name, meta.value); } - } + }; return (
e.stopPropagation()} onMouseEnter={handleHover} @@ -293,13 +298,16 @@ function SessionItem(props: RouteComponentProps & Props) {
- {_metaList.length > 0 && ( - + {!slim && _metaList.length > 0 && ( + )}
)}
@@ -355,9 +368,12 @@ function SessionItem(props: RouteComponentProps & Props) {
-
+
)}
diff --git a/frontend/app/components/ui/Icons/index.ts b/frontend/app/components/ui/Icons/index.ts index 225deb1b0..976eeba73 100644 --- a/frontend/app/components/ui/Icons/index.ts +++ b/frontend/app/components/ui/Icons/index.ts @@ -416,9 +416,14 @@ export { default as Play_circle_bold } from './play_circle_bold'; export { default as Play_circle_light } from './play_circle_light'; export { default as Play_circle } from './play_circle'; export { default as Play_fill_new } from './play_fill_new'; +export { default as Play_fill_v2_assist } from './play_fill_v2_assist'; +export { default as Play_fill_v2 } from './play_fill_v2'; export { default as Play_fill } from './play_fill'; export { default as Play_hover } from './play_hover'; +export { default as Play_v2_assist } from './play_v2_assist'; +export { default as Play_v2 } from './play_v2'; export { default as Play } from './play'; +export { default as Played_v2 } from './played_v2'; export { default as Plug } from './plug'; export { default as Plus_circle } from './plus_circle'; export { default as Plus } from './plus'; diff --git a/frontend/app/components/ui/Icons/play_fill_v2.tsx b/frontend/app/components/ui/Icons/play_fill_v2.tsx new file mode 100644 index 000000000..abba72052 --- /dev/null +++ b/frontend/app/components/ui/Icons/play_fill_v2.tsx @@ -0,0 +1,18 @@ +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Play_fill_v2(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Play_fill_v2; diff --git a/frontend/app/components/ui/Icons/play_fill_v2_assist.tsx b/frontend/app/components/ui/Icons/play_fill_v2_assist.tsx new file mode 100644 index 000000000..1f28fb078 --- /dev/null +++ b/frontend/app/components/ui/Icons/play_fill_v2_assist.tsx @@ -0,0 +1,18 @@ +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Play_fill_v2_assist(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Play_fill_v2_assist; diff --git a/frontend/app/components/ui/Icons/play_v2.tsx b/frontend/app/components/ui/Icons/play_v2.tsx new file mode 100644 index 000000000..fea80e7e3 --- /dev/null +++ b/frontend/app/components/ui/Icons/play_v2.tsx @@ -0,0 +1,18 @@ +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Play_v2(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Play_v2; diff --git a/frontend/app/components/ui/Icons/play_v2_assist.tsx b/frontend/app/components/ui/Icons/play_v2_assist.tsx new file mode 100644 index 000000000..b22498527 --- /dev/null +++ b/frontend/app/components/ui/Icons/play_v2_assist.tsx @@ -0,0 +1,18 @@ +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Play_v2_assist(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Play_v2_assist; diff --git a/frontend/app/components/ui/Icons/played_v2.tsx b/frontend/app/components/ui/Icons/played_v2.tsx new file mode 100644 index 000000000..0f6e7fa6a --- /dev/null +++ b/frontend/app/components/ui/Icons/played_v2.tsx @@ -0,0 +1,18 @@ +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Played_v2(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Played_v2; diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx index 4cec3d10f..76baa3999 100644 --- a/frontend/app/components/ui/SVG.tsx +++ b/frontend/app/components/ui/SVG.tsx @@ -418,9 +418,14 @@ import { Play_circle_light, Play_circle, Play_fill_new, + Play_fill_v2_assist, + Play_fill_v2, Play_fill, Play_hover, + Play_v2_assist, + Play_v2, Play, + Played_v2, Plug, Plus_circle, Plus, @@ -490,7 +495,7 @@ import { Zoom_in } from './Icons' -export type IconNames = 'activity' | 'analytics' | 'anchor' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-up-short' | 'arrow-up' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'calendar' | 'call' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-right-fill' | 'chat-dots' | 'chat-left-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clock-history' | 'clock' | 'close' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/chrome' | 'color/browser/edge' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/google' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/unknown' | 'color/browser/whale' | 'color/chrome' | 'color/country/de' | 'color/country/fr' | 'color/country/gb' | 'color/country/in' | 'color/country/us' | 'color/de' | 'color/device/desktop' | 'color/device/mobile' | 'color/device/tablet' | 'color/device/unkown' | 'color/edge' | 'color/fedora' | 'color/firefox' | 'color/fr' | 'color/gb' | 'color/in' | 'color/issues/bad_request' | 'color/issues/click_rage' | 'color/issues/cpu' | 'color/issues/crash' | 'color/issues/custom' | 'color/issues/dead_click' | 'color/issues/errors' | 'color/issues/excessive_scrolling' | 'color/issues/js_exception' | 'color/issues/memory' | 'color/issues/missing_resource' | 'color/issues/mouse_thrashing' | 'color/issues/slow_page_load' | 'color/microsoft' | 'color/opera' | 'color/os/android' | 'color/os/apple' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/ios' | 'color/os/linux' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'cross' | 'cubes' | 'cursor-trash' | 'cypress' | 'dash' | 'dashboard-icn' | 'dashboards/circle-alert' | 'dashboards/cohort-chart' | 'dashboards/heatmap-2' | 'dashboards/user-journey' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dizzy' | 'door-closed' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'explosion' | 'export-pdf' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch-request' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/chevrons-up-down' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/screen' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel' | 'gear' | 'github' | 'graph-up' | 'grid-3x3' | 'grid-check' | 'grid' | 'hash' | 'headset' | 'history' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'icn_fetch-request' | 'icn_referrer' | 'icn_url' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/dynatrace' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'kai-mono' | 'kai' | 'kai_colored' | 'key' | 'keyboard' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-ul' | 'list' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'metadata-more' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'orIcn' | 'orSpot' | 'orspotOutline' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus' | 'prev1' | 'pulse' | 'puppeteer' | 'puzzle-piece' | 'puzzle' | 'pwright' | 'question-circle' | 'question-lg' | 'quotes' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo' | 'redux' | 'referrer' | 'remote-control' | 'resources-icon' | 'safe' | 'sandglass' | 'search' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'square-mouse-pointer' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table' | 'tags' | 'terminal' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-journey' | 'user-switch' | 'users' | 'vendors/graphql' | 'web-vitals' | 'wifi' | 'window-x' | 'window' | 'zoom-in'; +export type IconNames = 'activity' | 'analytics' | 'anchor' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-up-short' | 'arrow-up' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'calendar' | 'call' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-right-fill' | 'chat-dots' | 'chat-left-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clock-history' | 'clock' | 'close' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/chrome' | 'color/browser/edge' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/google' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/unknown' | 'color/browser/whale' | 'color/chrome' | 'color/country/de' | 'color/country/fr' | 'color/country/gb' | 'color/country/in' | 'color/country/us' | 'color/de' | 'color/device/desktop' | 'color/device/mobile' | 'color/device/tablet' | 'color/device/unkown' | 'color/edge' | 'color/fedora' | 'color/firefox' | 'color/fr' | 'color/gb' | 'color/in' | 'color/issues/bad_request' | 'color/issues/click_rage' | 'color/issues/cpu' | 'color/issues/crash' | 'color/issues/custom' | 'color/issues/dead_click' | 'color/issues/errors' | 'color/issues/excessive_scrolling' | 'color/issues/js_exception' | 'color/issues/memory' | 'color/issues/missing_resource' | 'color/issues/mouse_thrashing' | 'color/issues/slow_page_load' | 'color/microsoft' | 'color/opera' | 'color/os/android' | 'color/os/apple' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/ios' | 'color/os/linux' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'cross' | 'cubes' | 'cursor-trash' | 'cypress' | 'dash' | 'dashboard-icn' | 'dashboards/circle-alert' | 'dashboards/cohort-chart' | 'dashboards/heatmap-2' | 'dashboards/user-journey' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dizzy' | 'door-closed' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'explosion' | 'export-pdf' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch-request' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/chevrons-up-down' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/screen' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel' | 'gear' | 'github' | 'graph-up' | 'grid-3x3' | 'grid-check' | 'grid' | 'hash' | 'headset' | 'history' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'icn_fetch-request' | 'icn_referrer' | 'icn_url' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/dynatrace' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'kai-mono' | 'kai' | 'kai_colored' | 'key' | 'keyboard' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-ul' | 'list' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'metadata-more' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'orIcn' | 'orSpot' | 'orspotOutline' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill-v2-assist' | 'play-fill-v2' | 'play-fill' | 'play-hover' | 'play-v2-assist' | 'play-v2' | 'play' | 'played-v2' | 'plug' | 'plus-circle' | 'plus' | 'prev1' | 'pulse' | 'puppeteer' | 'puzzle-piece' | 'puzzle' | 'pwright' | 'question-circle' | 'question-lg' | 'quotes' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo' | 'redux' | 'referrer' | 'remote-control' | 'resources-icon' | 'safe' | 'sandglass' | 'search' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'square-mouse-pointer' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table' | 'tags' | 'terminal' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-journey' | 'user-switch' | 'users' | 'vendors/graphql' | 'web-vitals' | 'wifi' | 'window-x' | 'window' | 'zoom-in'; interface Props { name: IconNames; @@ -1753,15 +1758,30 @@ const SVG = (props: Props) => { // case 'play-fill-new': case 'play-fill-new': return ; + // case 'play-fill-v2-assist': + case 'play-fill-v2-assist': return ; + + // case 'play-fill-v2': + case 'play-fill-v2': return ; + // case 'play-fill': case 'play-fill': return ; // case 'play-hover': case 'play-hover': return ; + // case 'play-v2-assist': + case 'play-v2-assist': return ; + + // case 'play-v2': + case 'play-v2': return ; + case 'play': return ; + // case 'played-v2': + case 'played-v2': return ; + case 'plug': return ; diff --git a/frontend/app/layout/SideMenu.tsx b/frontend/app/layout/SideMenu.tsx index bf4803160..5b2de2054 100644 --- a/frontend/app/layout/SideMenu.tsx +++ b/frontend/app/layout/SideMenu.tsx @@ -29,6 +29,7 @@ import { spotOnlyCats, } from './data'; import { useTranslation } from 'react-i18next'; +import { tag } from '@/components/Session_/Inspector/inspector.css'; const { Text } = Typography; @@ -104,7 +105,7 @@ function SideMenu(props: Props) { modules.includes(MODULES.USABILITY_TESTS), item.isAdmin && !isAdmin, item.isEnterprise && !isEnterprise, - item.key === MENU.KAI && !hasAi + item.key === MENU.KAI && !hasAi, ].some((cond) => cond); return { ...item, hidden: isHidden }; @@ -118,7 +119,15 @@ function SideMenu(props: Props) { hidden: allItemsHidden, }; }); - }, [isAdmin, isEnterprise, isPreferencesActive, modules, spotOnly, siteId, i18n.language]); + }, [ + isAdmin, + isEnterprise, + isPreferencesActive, + modules, + spotOnly, + siteId, + i18n.language, + ]); const menuRoutes: any = { [MENU.EXIT]: () => @@ -216,7 +225,10 @@ function SideMenu(props: Props) { color={isActive ? 'teal' : 'black'} /> } - className={cn('!rounded-lg hover-fill-teal', isActive ? 'color-main' : 'color-black')} + className={cn( + '!rounded-lg hover-fill-teal', + isActive ? 'color-main' : 'color-black', + )} > {item.label} @@ -235,7 +247,10 @@ function SideMenu(props: Props) { /> } style={{ paddingLeft: '20px' }} - className={cn('!rounded-lg !pe-0', isActive ? 'color-main' : 'color-black')} + className={cn( + '!rounded-lg !pe-0', + isActive ? 'color-main' : 'color-black', + )} itemIcon={ item.leading ? ( {item.label} - - {t('Beta')} -
); @@ -285,7 +293,20 @@ function SideMenu(props: Props) { })} key={child.key} > - {child.label} +
+ {child.label} + {child.tag ? ( +
+ + {child.tag.label} + +
+ ) : null} +
))} @@ -301,7 +322,10 @@ function SideMenu(props: Props) { /> } style={{ paddingLeft: '20px' }} - className={cn('!rounded-lg hover-fill-teal', isActive ? 'color-main' : 'color-black')} + className={cn( + '!rounded-lg hover-fill-teal', + isActive ? 'color-main' : 'color-black', + )} itemIcon={ item.leading ? ( - {item.label} +
+ {item.label} + {item.tag ? ( +
+ + {item.tag.label} + +
+ ) : null} +
); })} diff --git a/frontend/app/layout/data.ts b/frontend/app/layout/data.ts index 96b998d90..30a7d785d 100644 --- a/frontend/app/layout/data.ts +++ b/frontend/app/layout/data.ts @@ -1,5 +1,5 @@ import { TFunction } from 'i18next'; -import { IconNames } from "../components/ui/SVG"; +import { IconNames } from '../components/ui/SVG'; import React from 'react'; export interface MenuItem { @@ -99,7 +99,15 @@ export const categories: (t: TFunction) => Category[] = (t) => [ title: '', key: 'kai', items: [ - { label: t('Kai'), key: MENU.KAI, icon: 'kai' }, + { + label: t('Kai'), + key: MENU.KAI, + icon: 'kai-mono', + tag: { + label: t('New'), + color: '#394DFE', + }, + }, ], }, { diff --git a/frontend/app/svg/icons/play-fill-v2-assist.svg b/frontend/app/svg/icons/play-fill-v2-assist.svg new file mode 100644 index 000000000..79ff10a89 --- /dev/null +++ b/frontend/app/svg/icons/play-fill-v2-assist.svg @@ -0,0 +1,6 @@ + + + + diff --git a/frontend/app/svg/icons/play-fill-v2.svg b/frontend/app/svg/icons/play-fill-v2.svg new file mode 100644 index 000000000..1c2c732ce --- /dev/null +++ b/frontend/app/svg/icons/play-fill-v2.svg @@ -0,0 +1,6 @@ + + + + diff --git a/frontend/app/svg/icons/play-v2-assist.svg b/frontend/app/svg/icons/play-v2-assist.svg new file mode 100644 index 000000000..a885bffe9 --- /dev/null +++ b/frontend/app/svg/icons/play-v2-assist.svg @@ -0,0 +1,6 @@ + + + + diff --git a/frontend/app/svg/icons/play-v2.svg b/frontend/app/svg/icons/play-v2.svg new file mode 100644 index 000000000..a9aa5f578 --- /dev/null +++ b/frontend/app/svg/icons/play-v2.svg @@ -0,0 +1,6 @@ + + + + diff --git a/frontend/app/svg/icons/played-v2.svg b/frontend/app/svg/icons/played-v2.svg new file mode 100644 index 000000000..58d7a97aa --- /dev/null +++ b/frontend/app/svg/icons/played-v2.svg @@ -0,0 +1,6 @@ + + + +