diff --git a/frontend/app/components/ui/Icons/index.ts b/frontend/app/components/ui/Icons/index.ts
index 239193a3a..15ce844fe 100644
--- a/frontend/app/components/ui/Icons/index.ts
+++ b/frontend/app/components/ui/Icons/index.ts
@@ -434,6 +434,7 @@ export { default as No_dashboard } from './no_dashboard';
export { default as No_metrics_chart } from './no_metrics_chart';
export { default as No_metrics } from './no_metrics';
export { default as No_recordings } from './no_recordings';
+export { default as Orspot } from './orSpot';
export { default as Os_android } from './os_android';
export { default as Os_chrome_os } from './os_chrome_os';
export { default as Os_fedora } from './os_fedora';
diff --git a/frontend/app/components/ui/Icons/orSpot.tsx b/frontend/app/components/ui/Icons/orSpot.tsx
new file mode 100644
index 000000000..00dde0502
--- /dev/null
+++ b/frontend/app/components/ui/Icons/orSpot.tsx
@@ -0,0 +1,19 @@
+
+/* Auto-generated, do not edit */
+import React from 'react';
+
+interface Props {
+ size?: number | string;
+ width?: number | string;
+ height?: number | string;
+ fill?: string;
+}
+
+function Orspot(props: Props) {
+ const { size = 14, width = size, height = size, fill = '' } = props;
+ return (
+
+ );
+}
+
+export default Orspot;
diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx
index d5ee94fb3..fc1c24152 100644
--- a/frontend/app/components/ui/SVG.tsx
+++ b/frontend/app/components/ui/SVG.tsx
@@ -436,6 +436,7 @@ import {
No_metrics_chart,
No_metrics,
No_recordings,
+ Orspot,
Os_android,
Os_chrome_os,
Os_fedora,
@@ -557,7 +558,7 @@ import {
Zoom_in
} from './Icons'
-export type IconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | '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-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/Tor' | 'color/browser/applebot' | 'color/browser/chrome' | 'color/browser/chrome_mobile' | 'color/browser/chrome_mobile_ios' | 'color/browser/duck_duck_go' | 'color/browser/duckduckgo_mobile' | 'color/browser/edge' | 'color/browser/edge_mobile' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/firefox_ios' | 'color/browser/firefox_mobile' | 'color/browser/google' | 'color/browser/googlebot' | 'color/browser/huawei_browser' | 'color/browser/internet_explorer' | 'color/browser/miui_browser' | 'color/browser/mobile_safari' | 'color/browser/mobile_safari_ui' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/samsung_internet' | 'color/browser/uc_browser' | 'color/browser/unknown' | 'color/browser/whale' | 'color/browser/yandex_browser' | '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/other_phone' | '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/blackberry' | 'color/os/chrome_os' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/freebsd' | 'color/os/gnome' | 'color/os/ios' | 'color/os/linux' | 'color/os/linux_mint' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | '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' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | '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' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | '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-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | '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/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' | 'key' | 'keyboard' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | '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-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in';
+export type IconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | '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-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'color/apple' | 'color/browser/Tor' | 'color/browser/applebot' | 'color/browser/chrome' | 'color/browser/chrome_mobile' | 'color/browser/chrome_mobile_ios' | 'color/browser/duck_duck_go' | 'color/browser/duckduckgo_mobile' | 'color/browser/edge' | 'color/browser/edge_mobile' | 'color/browser/facebook' | 'color/browser/firefox' | 'color/browser/firefox_ios' | 'color/browser/firefox_mobile' | 'color/browser/google' | 'color/browser/googlebot' | 'color/browser/huawei_browser' | 'color/browser/internet_explorer' | 'color/browser/miui_browser' | 'color/browser/mobile_safari' | 'color/browser/mobile_safari_ui' | 'color/browser/opera' | 'color/browser/safari' | 'color/browser/samsung_internet' | 'color/browser/uc_browser' | 'color/browser/unknown' | 'color/browser/whale' | 'color/browser/yandex_browser' | '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/other_phone' | '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/blackberry' | 'color/os/chrome_os' | 'color/os/elementary' | 'color/os/fedora' | 'color/os/freebsd' | 'color/os/gnome' | 'color/os/ios' | 'color/os/linux' | 'color/os/linux_mint' | 'color/os/macos' | 'color/os/microsoft' | 'color/os/ubuntu' | 'color/os/unkown' | 'color/safari' | 'color/ubuntu' | 'color/us' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | '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' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | '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' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | '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-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | '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/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' | 'key' | 'keyboard' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'orSpot' | '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-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in';
interface Props {
name: IconNames;
@@ -1874,6 +1875,9 @@ const SVG = (props: Props) => {
// case 'no-recordings':
case 'no-recordings': return
;
+
+ case 'orSpot': return
;
+
// case 'os/android':
case 'os/android': return
;
diff --git a/frontend/app/duck/roles.js b/frontend/app/duck/roles.js
index 31d65cd79..d51d18f1f 100644
--- a/frontend/app/duck/roles.js
+++ b/frontend/app/duck/roles.js
@@ -18,7 +18,9 @@ const initialState = Map({
{ text: 'Dashboard', value: 'METRICS' },
{ text: 'Assist (Live)', value: 'ASSIST_LIVE' },
{ text: 'Assist (Call)', value: 'ASSIST_CALL' },
- { text: 'Feature Flags', value: 'FEATURE_FLAGS' }
+ { text: 'Feature Flags', value: 'FEATURE_FLAGS' },
+ { text: 'Spots', value: "SPOT" },
+ { text: 'Change Spot Visibility', value: 'SPOT_PUBLIC' }
]),
});
diff --git a/frontend/app/layout/Layout.tsx b/frontend/app/layout/Layout.tsx
index 92aa470d6..ca073ff9a 100644
--- a/frontend/app/layout/Layout.tsx
+++ b/frontend/app/layout/Layout.tsx
@@ -22,7 +22,7 @@ interface Props {
function Layout(props: Props) {
const { hideHeader, siteId } = props;
- const isPlayer = /\/(session|assist)\//.test(window.location.pathname);
+ const isPlayer = /\/(session|assist|view-spot)\//.test(window.location.pathname);
const { settingsStore } = useStore();
// const lastFetchedSiteIdRef = React.useRef
(null);
diff --git a/frontend/app/layout/SideMenu.tsx b/frontend/app/layout/SideMenu.tsx
index 34da77418..476a95296 100644
--- a/frontend/app/layout/SideMenu.tsx
+++ b/frontend/app/layout/SideMenu.tsx
@@ -112,6 +112,7 @@ function SideMenu(props: Props) {
[MENU.FEATURE_FLAGS]: () => withSiteId(routes.fflags(), siteId),
[MENU.PREFERENCES]: () => client(CLIENT_DEFAULT_TAB),
[MENU.USABILITY_TESTS]: () => withSiteId(routes.usabilityTesting(), siteId),
+ [MENU.SPOTS]: () => withSiteId(routes.spotsList(), siteId),
[PREFERENCES_MENU.ACCOUNT]: () => client(CLIENT_TABS.PROFILE),
[PREFERENCES_MENU.SESSION_LISTING]: () => client(CLIENT_TABS.SESSIONS_LISTING),
[PREFERENCES_MENU.INTEGRATIONS]: () => client(CLIENT_TABS.INTEGRATIONS),
diff --git a/frontend/app/layout/data.ts b/frontend/app/layout/data.ts
index 6ed58ec6b..ea458e3d5 100644
--- a/frontend/app/layout/data.ts
+++ b/frontend/app/layout/data.ts
@@ -53,6 +53,7 @@ export const enum MENU {
PREFERENCES = 'preferences',
SUPPORT = 'support',
EXIT = 'exit',
+ SPOTS = 'spots',
}
export const categories: Category[] = [
@@ -67,6 +68,13 @@ export const categories: Category[] = [
{ label: 'Notes', key: MENU.NOTES, icon: 'stickies' }
]
},
+ {
+ title: '',
+ key: 'spot',
+ items: [
+ { label: 'Spots', key: MENU.SPOTS, icon: 'orSpot' },
+ ]
+ },
{
title: '',
key: 'assist',
diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx
index d92f683fc..8d0872bec 100644
--- a/frontend/app/mstore/index.tsx
+++ b/frontend/app/mstore/index.tsx
@@ -21,6 +21,8 @@ import UxtestingStore from './uxtestingStore';
import TagWatchStore from './tagWatchStore';
import AiSummaryStore from "./aiSummaryStore";
import AiFiltersStore from "./aiFiltersStore";
+import SpotStore from "./spotStore";
+import LoginStore from "./loginStore";
export class RootStore {
dashboardStore: DashboardStore;
@@ -43,6 +45,8 @@ export class RootStore {
tagWatchStore: TagWatchStore;
aiSummaryStore: AiSummaryStore;
aiFiltersStore: AiFiltersStore;
+ spotStore: SpotStore;
+ loginStore: LoginStore;
constructor() {
this.dashboardStore = new DashboardStore();
@@ -65,6 +69,8 @@ export class RootStore {
this.tagWatchStore = new TagWatchStore();
this.aiSummaryStore = new AiSummaryStore();
this.aiFiltersStore = new AiFiltersStore();
+ this.spotStore = new SpotStore();
+ this.loginStore = new LoginStore();
}
initClient() {
diff --git a/frontend/app/mstore/loginStore.ts b/frontend/app/mstore/loginStore.ts
new file mode 100644
index 000000000..3845bd613
--- /dev/null
+++ b/frontend/app/mstore/loginStore.ts
@@ -0,0 +1,87 @@
+import { makeAutoObservable } from 'mobx';
+import { loginService } from "../services";
+
+const spotTokenKey = "___$or_spotToken$___"
+
+class LoginStore {
+ email = '';
+ password = '';
+ captchaResponse?: string;
+ spotJWT?: string;
+
+ constructor() {
+ makeAutoObservable(this);
+ const token = localStorage.getItem(spotTokenKey);
+ if (token) {
+ this.spotJWT = token;
+ }
+ }
+
+ getSpotJWT = async (): Promise => {
+ if (this.spotJwtPending) {
+ let tries = 0
+ return new Promise((resolve) => {
+ const interval = setInterval(() => {
+ if (!this.spotJwtPending && this.spotJWT) {
+ clearInterval(interval)
+ resolve(this.spotJWT)
+ }
+ if (tries > 50) {
+ clearInterval(interval)
+ resolve(null)
+ }
+ }, 100)
+ })
+ }
+ return this.spotJWT ?? null
+ }
+
+ setEmail = (email: string) => {
+ this.email = email;
+ }
+
+ setPassword = (password: string) => {
+ this.password = password;
+ }
+
+ setCaptchaResponse = (captchaResponse: string) => {
+ this.captchaResponse = captchaResponse;
+ }
+
+ setSpotJWT = (spotJWT: string) => {
+ this.spotJWT = spotJWT;
+ localStorage.setItem(spotTokenKey, spotJWT);
+ }
+
+ spotJwtPending = false
+ setSpotJwtPending = (pending: boolean) => {
+ this.spotJwtPending = pending
+ }
+
+ generateSpotJWT = async (onSuccess: (jwt:string) => void) => {
+ if (this.spotJwtPending) {
+ return
+ }
+ this.setSpotJwtPending(true)
+ try {
+ const resp = await loginService.spotLogin({
+ email: this.email,
+ password: this.password,
+ captchaResponse: this.captchaResponse
+ })
+ this.setSpotJWT(resp.jwt)
+ onSuccess(resp.jwt)
+ } catch (e) {
+ console.error(e)
+ } finally {
+ this.setSpotJwtPending(false)
+ }
+ }
+
+ invalidateSpotJWT = () => {
+ this.spotJWT = undefined
+ localStorage.removeItem(spotTokenKey)
+ }
+}
+
+export default LoginStore;
\ No newline at end of file
diff --git a/frontend/app/mstore/notesStore.ts b/frontend/app/mstore/notesStore.ts
index 686fe6558..bdea01bab 100644
--- a/frontend/app/mstore/notesStore.ts
+++ b/frontend/app/mstore/notesStore.ts
@@ -1,7 +1,5 @@
import { makeAutoObservable } from "mobx";
-
-
import { notesService } from "App/services";
import { Note, NotesFilter, WriteNote, iTag } from 'App/services/NotesService';
diff --git a/frontend/app/mstore/spotStore.ts b/frontend/app/mstore/spotStore.ts
new file mode 100644
index 000000000..f04c16dfe
--- /dev/null
+++ b/frontend/app/mstore/spotStore.ts
@@ -0,0 +1,166 @@
+import { makeAutoObservable } from 'mobx';
+
+import { spotService } from 'App/services';
+import { UpdateSpotRequest } from 'App/services/spotService';
+
+import { Spot } from './types/spot';
+
+export default class SpotStore {
+ isLoading: boolean = false;
+ spots: Spot[] = [];
+ currentSpot: Spot | null = null;
+ page: number = 1;
+ filter: 'all' | 'own' = 'all';
+ query: string = '';
+ total: number = 0;
+ limit: number = 9;
+ accessKey: string | undefined = undefined;
+ pubKey: { value: string; expiration: number } | null = null;
+ readonly order = 'desc';
+
+ constructor() {
+ makeAutoObservable(this);
+ }
+
+ clearCurrent = () => {
+ this.currentSpot = null;
+ this.pubKey = null;
+ this.accessKey = undefined;
+ };
+
+ withLoader(fn: () => Promise): Promise {
+ this.setLoading(true);
+ return fn().finally(() => {
+ this.setLoading(false);
+ });
+ }
+
+ setAccessKey(key: string) {
+ this.accessKey = key;
+ }
+
+ setSpots(spots: Spot[]) {
+ this.spots = spots;
+ }
+
+ setCurrentSpot(spot: Spot) {
+ this.currentSpot = spot;
+ }
+
+ setFilter(filter: 'all' | 'own') {
+ this.filter = filter;
+ }
+
+ setQuery(query: string) {
+ this.query = query;
+ }
+
+ setPage(page: number) {
+ this.page = page;
+ }
+
+ setLoading(loading: boolean) {
+ this.isLoading = loading;
+ }
+
+ setTotal(total: number) {
+ this.total = total;
+ }
+
+ async fetchSpots() {
+ const filters = {
+ page: this.page,
+ filterBy: this.filter,
+ query: this.query,
+ order: this.order,
+ limit: this.limit,
+ } as const;
+
+ const response = await this.withLoader(() =>
+ spotService.fetchSpots(filters)
+ );
+ this.setSpots(response.spots.map((spot: any) => new Spot(spot)));
+ this.setTotal(response.total);
+ }
+
+ async fetchSpotById(id: string) {
+ const response = await this.withLoader(() =>
+ spotService.fetchSpot(id, this.accessKey)
+ );
+
+ const spotInst = new Spot({ ...response.spot, id });
+ this.setCurrentSpot(spotInst);
+
+ return spotInst;
+ }
+
+ async addComment(spotId: string, comment: string, userName: string) {
+ await this.withLoader(async () => {
+ await spotService.addComment(spotId, { comment, userName });
+ const spot = this.currentSpot;
+ if (spot) {
+ spot.comments!.push({
+ text: comment,
+ user: userName,
+ createdAt: new Date().toISOString(),
+ });
+ this.setCurrentSpot(spot);
+ }
+ });
+ }
+
+ async deleteSpot(spotIds: string[]) {
+ await this.withLoader(() => spotService.deleteSpot(spotIds));
+ this.spots = this.spots.filter(
+ (spot) => spotIds.findIndex((s) => s === spot.spotId) === -1
+ );
+ this.total = this.total - spotIds.length;
+ await this.fetchSpots();
+ }
+
+ async updateSpot(spotId: string, data: UpdateSpotRequest) {
+ await this.withLoader(() => spotService.updateSpot(spotId, data));
+ if (data.name !== undefined) {
+ const updatedSpots = this.spots.map((s) => {
+ if (s.spotId === spotId) {
+ s.title = data.name!;
+ }
+ return s;
+ });
+ this.setSpots(updatedSpots);
+ }
+ }
+
+ async getVideo(id: string) {
+ return await this.withLoader(() => spotService.getVideo(id));
+ }
+
+ setPubKey(key: { value: string; expiration: number }) {
+ this.pubKey = key;
+ }
+
+ /**
+ * @param expiration - in seconds
+ * @param id - spot id string
+ * */
+ async generateKey(id: string, expiration: number) {
+ try {
+ const { key } = await this.withLoader(() =>
+ spotService.generateKey(id, expiration)
+ );
+ this.setPubKey(key);
+ return key;
+ } catch (e) {
+ console.error('couldnt generate pubkey')
+ }
+ }
+
+ async getPubKey(id: string) {
+ try {
+ const { key } = await this.withLoader(() => spotService.getKey(id));
+ this.setPubKey(key);
+ } catch (e) {
+ console.error('no pubkey', e)
+ }
+ }
+}
diff --git a/frontend/app/mstore/types/session.ts b/frontend/app/mstore/types/session.ts
index 0a855ea23..a5e6bcb1c 100644
--- a/frontend/app/mstore/types/session.ts
+++ b/frontend/app/mstore/types/session.ts
@@ -4,7 +4,7 @@ import { Duration } from 'luxon';
const HASH_MOD = 1610612741;
const HASH_P = 53;
-function hashString(s: string): number {
+export function hashString(s: string): number {
let mul = 1;
let hash = 0;
for (let i = 0; i < s.length; i++) {
diff --git a/frontend/app/mstore/types/spot.ts b/frontend/app/mstore/types/spot.ts
new file mode 100644
index 000000000..0b0ce5975
--- /dev/null
+++ b/frontend/app/mstore/types/spot.ts
@@ -0,0 +1,34 @@
+import { resentOrDate, shortDurationFromMs } from "App/date";
+import { makeAutoObservable } from "mobx";
+
+export class Spot {
+ thumbnail: string;
+ title: string;
+ createdAt: string;
+ user: string;
+ duration: string;
+ spotId: string;
+ mobURL?: string;
+ videoURL?: string;
+ streamFile?: string;
+ comments?: { user: string, text: string, createdAt: string }[] = []
+ /** public access key to add to url */
+ key?: { key: string, expirationDate: string } | null = null
+
+
+ constructor(data: Record) {
+ makeAutoObservable(this)
+ this.setAdditionalData(data)
+ this.comments = data.comments ?? [];
+ this.thumbnail = data.previewURL
+ this.title = data.name;
+ this.createdAt = resentOrDate(new Date(data.createdAt).getTime());
+ this.user = data.userEmail;
+ this.duration = shortDurationFromMs(data.duration);
+ this.spotId = data.id
+ }
+
+ setAdditionalData(data: Record) {
+ Object.assign(this, data)
+ }
+}
diff --git a/frontend/app/player-ui/FullScreenButton.tsx b/frontend/app/player-ui/FullScreenButton.tsx
index ff6a437c7..c0c663769 100644
--- a/frontend/app/player-ui/FullScreenButton.tsx
+++ b/frontend/app/player-ui/FullScreenButton.tsx
@@ -6,15 +6,16 @@ import { PlaySessionInFullscreenShortcut } from 'Components/Session_/Player/Cont
interface IProps {
size: number;
onClick: () => void;
- customClasses: string;
+ customClasses?: string;
+ noShortcut?: boolean;
}
-export function FullScreenButton({ size = 18, onClick }: IProps) {
+export function FullScreenButton({ size = 18, onClick, noShortcut }: IProps) {
return (
-
+ {!noShortcut ? : null}
Play In Fullscreen
}
diff --git a/frontend/app/routes.ts b/frontend/app/routes.ts
index ff29b4dbc..8179b27bd 100644
--- a/frontend/app/routes.ts
+++ b/frontend/app/routes.ts
@@ -53,6 +53,7 @@ export const setQueryParams = (location: Location, params: Record