From 250eaf1eb67ebbd8a1efcbd65523d88d54a170bd Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 21 Feb 2022 16:41:04 +0100 Subject: [PATCH] feat(ui) - assist ui - wip --- frontend/app/components/Assist/Assist.tsx | 6 +- .../AssistActions/AssistActions.tsx | 43 +++---- .../components/AssistTabs/AssistTabs.tsx | 11 +- .../app/components/BugFinder/BugFinder.js | 14 +-- .../app/components/Header/SiteDropdown.js | 3 + .../components/Session_/PlayerBlockHeader.js | 45 ++++++-- .../SessionInfoItem/SessionInfoItem.tsx | 24 ++++ .../Session_/SessionInfoItem/index.ts | 1 + .../components/Session_/playerBlockHeader.css | 4 +- .../SessionItem/ErrorBars/ErrorBars.css | 3 + .../SessionItem/ErrorBars/ErrorBars.tsx | 21 ++-- .../shared/SessionItem/MetaItem/MetaItem.tsx | 22 ++++ .../shared/SessionItem/MetaItem/index.ts | 1 + .../MetaMoreButton/MetaMoreButton.tsx | 32 ++++++ .../SessionItem/MetaMoreButton/index.ts | 1 + .../shared/SessionItem/SessionItem.js | 107 +++++++++++------- .../SessionMetaList/SessionMetaList.tsx | 31 +---- frontend/app/components/ui/Avatar/Avatar.js | 5 +- .../components/ui/CountryFlag/CountryFlag.js | 30 ++--- .../components/ui/CountryFlag/countryFlag.css | 4 + .../components/ui/IconButton/IconButton.js | 4 + .../components/ui/IconButton/iconButton.css | 40 +++++++ .../ui/TextEllipsis/textEllipsis.css | 2 +- frontend/app/styles/main.css | 11 ++ 24 files changed, 325 insertions(+), 140 deletions(-) create mode 100644 frontend/app/components/Session_/SessionInfoItem/SessionInfoItem.tsx create mode 100644 frontend/app/components/Session_/SessionInfoItem/index.ts create mode 100644 frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.css create mode 100644 frontend/app/components/shared/SessionItem/MetaItem/MetaItem.tsx create mode 100644 frontend/app/components/shared/SessionItem/MetaItem/index.ts create mode 100644 frontend/app/components/shared/SessionItem/MetaMoreButton/MetaMoreButton.tsx create mode 100644 frontend/app/components/shared/SessionItem/MetaMoreButton/index.ts diff --git a/frontend/app/components/Assist/Assist.tsx b/frontend/app/components/Assist/Assist.tsx index 2180bfc8e..476b7ac5c 100644 --- a/frontend/app/components/Assist/Assist.tsx +++ b/frontend/app/components/Assist/Assist.tsx @@ -2,8 +2,10 @@ import React from 'react'; import LiveSessionList from 'Shared/LiveSessionList'; import LiveSessionSearch from 'Shared/LiveSessionSearch'; import cn from 'classnames' +import withPageTitle from 'HOCs/withPageTitle'; -export default function Assist() { +// @withPageTitle("Assist - OpenReplay") +function Assist() { return (
@@ -18,3 +20,5 @@ export default function Assist() {
) } + +export default withPageTitle("Assist - OpenReplay")(Assist); diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx index 8b2cf5245..d3390e47c 100644 --- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx +++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import { Popup, Icon } from 'UI' +import { Popup, Icon, IconButton } from 'UI' import { connect } from 'react-redux' import cn from 'classnames' import { toggleChatWindow } from 'Duck/sessions'; @@ -77,9 +77,28 @@ function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus const onCall = calling === CallingState.OnCall || calling === CallingState.Reconnecting const cannotCall = (peerConnectionStatus !== ConnectionStatus.Connected) || (isEnterprise && !hasPermission) + const remoteActive = remoteControlStatus === RemoteControlStatus.Enabled return (
+
+ {/* + { 'Remote Control' } */} + +
+
- - { onCall ? 'End Call' : 'Call' } + { onCall ? 'End Call' : 'Call' } */} +
} content={ cannotCall ? "You don’t have the permissions to perform this action." : `Call ${userId ? userId : 'User'}` } @@ -105,22 +125,7 @@ function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus inverted position="top right" /> -
- - { 'Remote Control' } -
+
{ onCall && callObject && }
diff --git a/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx index b7ecce906..5c1641f78 100644 --- a/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx +++ b/frontend/app/components/Assist/components/AssistTabs/AssistTabs.tsx @@ -15,16 +15,15 @@ const AssistTabs = (props: Props) => {
{props.userId && ( <> +
+ +
{props.userId}
+
setShowMenu(!showMenu)} > - More Live Sessions -
- by -
- -
{props.userId}
+ All Active Sessions
)} diff --git a/frontend/app/components/BugFinder/BugFinder.js b/frontend/app/components/BugFinder/BugFinder.js index 80f819ecd..0ebfa80bb 100644 --- a/frontend/app/components/BugFinder/BugFinder.js +++ b/frontend/app/components/BugFinder/BugFinder.js @@ -102,13 +102,13 @@ export default class BugFinder extends React.PureComponent { // }; // }); // // TODO should cache the response - props.fetchIntegrationVariables().then(() => { - defaultFilters[5] = { - category: 'Metadata', - type: 'custom', - keys: this.props.variables.map(({ key }) => ({ type: 'METADATA', key, label: key, icon: 'filters/metadata', isFilter: true })).toJS() - }; - }); + // props.fetchIntegrationVariables().then(() => { + // defaultFilters[5] = { + // category: 'Metadata', + // type: 'custom', + // keys: this.props.variables.map(({ key }) => ({ type: 'METADATA', key, label: key, icon: 'filters/metadata', isFilter: true })).toJS() + // }; + // }); props.fetchSessions(); props.resetFunnel(); diff --git a/frontend/app/components/Header/SiteDropdown.js b/frontend/app/components/Header/SiteDropdown.js index 37057866b..502b40085 100644 --- a/frontend/app/components/Header/SiteDropdown.js +++ b/frontend/app/components/Header/SiteDropdown.js @@ -10,6 +10,7 @@ import styles from './siteDropdown.css'; import cn from 'classnames'; import NewSiteForm from '../Client/Sites/NewSiteForm'; import { clearSearch } from 'Duck/search'; +import { fetchList as fetchIntegrationVariables } from 'Duck/customField'; @withRouter @connect(state => ({ @@ -21,6 +22,7 @@ import { clearSearch } from 'Duck/search'; pushNewSite, init, clearSearch, + fetchIntegrationVariables, }) export default class SiteDropdown extends React.PureComponent { state = { showProductModal: false } @@ -37,6 +39,7 @@ export default class SiteDropdown extends React.PureComponent { switchSite = (siteId) => { this.props.setSiteId(siteId); this.props.clearSearch(); + this.props.fetchIntegrationVariables(); } render() { diff --git a/frontend/app/components/Session_/PlayerBlockHeader.js b/frontend/app/components/Session_/PlayerBlockHeader.js index d78e115c8..e32fd2bb6 100644 --- a/frontend/app/components/Session_/PlayerBlockHeader.js +++ b/frontend/app/components/Session_/PlayerBlockHeader.js @@ -3,19 +3,21 @@ import { withRouter } from 'react-router-dom'; import { browserIcon, osIcon, deviceTypeIcon } from 'App/iconNames'; import { formatTimeOrDate } from 'App/date'; import { sessions as sessionsRoute, withSiteId } from 'App/routes'; -import { Icon, CountryFlag, IconButton, BackLink } from 'UI'; +import { Icon, CountryFlag, IconButton, BackLink, Popup } from 'UI'; import { toggleFavorite, setSessionPath } from 'Duck/sessions'; import cn from 'classnames'; import { connectPlayer } from 'Player'; import HeaderInfo from './HeaderInfo'; import SharePopup from '../shared/SharePopup/SharePopup'; import { fetchList as fetchListIntegration } from 'Duck/integrations/actions'; +import { countries } from 'App/constants'; import stl from './playerBlockHeader.css'; import Issues from './Issues/Issues'; import Autoplay from './Autoplay'; import AssistActions from '../Assist/components/AssistActions'; import AssistTabs from '../Assist/components/AssistTabs'; +import SessionInfoItem from './SessionInfoItem' const SESSIONS_ROUTE = sessionsRoute(); @@ -53,11 +55,13 @@ export default class PlayerBlockHeader extends React.PureComponent { this.props.fetchListIntegration('issues') } - getDimension = (width, height) => ( -
- { width || 'x' } { height || 'x' } -
- ); + getDimension = (width, height) => { + return width && height ? ( +
+ { width || 'x' } { height || 'x' } +
+ ) : Not Available; + } backHandler = () => { const { history, siteId, sessionPath } = this.props; @@ -85,6 +89,7 @@ export default class PlayerBlockHeader extends React.PureComponent { startedAt, userBrowser, userOs, + userOsVersion, userDevice, userBrowserVersion, userDeviceType, @@ -102,12 +107,13 @@ export default class PlayerBlockHeader extends React.PureComponent { return (
-
+
+ { _live && } -
+ {/*
{ formatTimeOrDate(startedAt) } { this.props.local === 'UTC' ? 'UTC' : ''} @@ -117,15 +123,33 @@ export default class PlayerBlockHeader extends React.PureComponent { - + */}
+
+ + )} + content={( +
+ } label={countries[userCountry]} value={ formatTimeOrDate(startedAt) } /> + + + +
+ )} + on="click" + position="top center" + hideOnScroll + /> +
+ { live && hasSessionsPath && (
this.props.setSessionPath('')}> This Session is Now Continuing Live
)} - { _live && } { _live && } { !_live && ( <> @@ -164,3 +188,4 @@ export default class PlayerBlockHeader extends React.PureComponent { ); } } + diff --git a/frontend/app/components/Session_/SessionInfoItem/SessionInfoItem.tsx b/frontend/app/components/Session_/SessionInfoItem/SessionInfoItem.tsx new file mode 100644 index 000000000..f89bfb9cd --- /dev/null +++ b/frontend/app/components/Session_/SessionInfoItem/SessionInfoItem.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Icon } from 'UI' +import cn from 'classnames' + +interface Props { + label: string, + icon?: string, + comp?: React.ReactNode, + value: string, + isLast?: boolean, +} +export default function SessionInfoItem(props: Props) { + const { label, icon, value, comp, isLast = false } = props + return ( +
+
+ { icon && } + { comp && comp } +
+
{label}
+
{value}
+
+ ) +} diff --git a/frontend/app/components/Session_/SessionInfoItem/index.ts b/frontend/app/components/Session_/SessionInfoItem/index.ts new file mode 100644 index 000000000..372f8dea8 --- /dev/null +++ b/frontend/app/components/Session_/SessionInfoItem/index.ts @@ -0,0 +1 @@ +export { default } from './SessionInfoItem'; \ No newline at end of file diff --git a/frontend/app/components/Session_/playerBlockHeader.css b/frontend/app/components/Session_/playerBlockHeader.css index d9934ef1e..764933a16 100644 --- a/frontend/app/components/Session_/playerBlockHeader.css +++ b/frontend/app/components/Session_/playerBlockHeader.css @@ -1,13 +1,13 @@ .header { height: 50px; border-bottom: solid thin $gray-light; - padding: 10px 15px; + padding: 0px 15px; background-color: white; } .divider { width: 1px; - height: 100%; + height: 49px; margin: 0 15px; background-color: $gray-light; } diff --git a/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.css b/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.css new file mode 100644 index 000000000..e88de9b3b --- /dev/null +++ b/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.css @@ -0,0 +1,3 @@ +.bar { + height: 2px; +} \ No newline at end of file diff --git a/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.tsx b/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.tsx index 64c7f91b4..31c233414 100644 --- a/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.tsx +++ b/frontend/app/components/shared/SessionItem/ErrorBars/ErrorBars.tsx @@ -1,5 +1,6 @@ import React from 'react' import cn from 'classnames' +import stl from './ErrorBars.css' const GOOD = 'Good' const LESS_CRITICAL = 'Less Critical' @@ -16,21 +17,23 @@ interface Props { } export default function ErrorBars(props: Props) { const { count = 2 } = props - const state = React.useCallback(() => getErrorState(count), [count])() + const state = React.useMemo(() => getErrorState(count), [count]) + const showSecondBar = (state === GOOD || state === LESS_CRITICAL || state === CRITICAL) + const showThirdBar = (state === GOOD || state === CRITICAL); const bgColor = { 'bg-red' : state === CRITICAL, 'bg-green' : state === GOOD, 'bg-red2' : state === LESS_CRITICAL } return ( -
+
-
- { (state === GOOD || state === LESS_CRITICAL || state === CRITICAL) &&
} - { (state === GOOD || state === CRITICAL) &&
} +
+ { showSecondBar &&
} + { showThirdBar &&
}
-
-
-
+
+
+
-
{state}
+
{state}
) } diff --git a/frontend/app/components/shared/SessionItem/MetaItem/MetaItem.tsx b/frontend/app/components/shared/SessionItem/MetaItem/MetaItem.tsx new file mode 100644 index 000000000..1d2df4b04 --- /dev/null +++ b/frontend/app/components/shared/SessionItem/MetaItem/MetaItem.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import cn from 'classnames' +import { TextEllipsis } from 'UI' + +interface Props { + className?: string, + label: string, + value?: string, +} +export default function MetaItem(props: Props) { + const { className = '', label, value } = props + return ( +
+ + + + + + +
+ ) +} diff --git a/frontend/app/components/shared/SessionItem/MetaItem/index.ts b/frontend/app/components/shared/SessionItem/MetaItem/index.ts new file mode 100644 index 000000000..f25b30c65 --- /dev/null +++ b/frontend/app/components/shared/SessionItem/MetaItem/index.ts @@ -0,0 +1 @@ +export { default } from './MetaItem'; \ No newline at end of file diff --git a/frontend/app/components/shared/SessionItem/MetaMoreButton/MetaMoreButton.tsx b/frontend/app/components/shared/SessionItem/MetaMoreButton/MetaMoreButton.tsx new file mode 100644 index 000000000..21a5f38f1 --- /dev/null +++ b/frontend/app/components/shared/SessionItem/MetaMoreButton/MetaMoreButton.tsx @@ -0,0 +1,32 @@ +import React from 'react' +import { Popup } from 'UI' +import MetaItem from '../MetaItem' + +interface Props { + list: any[], + maxLength: number, +} +export default function MetaMoreButton(props: Props) { + const { list, maxLength } = props + return ( + + + +{list.length - maxLength} More + +
+ ) } + content={ +
+ {list.slice(maxLength).map(({ label, value }, index) => ( + + ))} +
+ } + on="click" + position="top right" + hideOnScroll + /> + ) +} diff --git a/frontend/app/components/shared/SessionItem/MetaMoreButton/index.ts b/frontend/app/components/shared/SessionItem/MetaMoreButton/index.ts new file mode 100644 index 000000000..8a1f4733e --- /dev/null +++ b/frontend/app/components/shared/SessionItem/MetaMoreButton/index.ts @@ -0,0 +1 @@ +export { default } from './MetaMoreButton'; \ No newline at end of file diff --git a/frontend/app/components/shared/SessionItem/SessionItem.js b/frontend/app/components/shared/SessionItem/SessionItem.js index 7d39e8e4e..4956342f0 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.js +++ b/frontend/app/components/shared/SessionItem/SessionItem.js @@ -20,13 +20,15 @@ import Counter from './Counter' import { withRouter } from 'react-router-dom'; import SessionMetaList from './SessionMetaList'; import ErrorBars from './ErrorBars'; +import { assist as assistRoute, isRoute } from "App/routes"; + +const ASSIST_ROUTE = assistRoute(); const Label = ({ label = '', color = 'color-gray-medium'}) => (
{label}
) @connect(state => ({ timezone: state.getIn(['sessions', 'timezone']), - isAssist: state.getIn(['sessions', 'activeTab']).type === 'live', siteId: state.getIn([ 'user', 'siteId' ]), }), { toggleFavorite, setSessionPath }) @withRouter @@ -52,7 +54,8 @@ export default class SessionItem extends React.PureComponent { userDeviceType, userUuid, userNumericHash, - live + live, + metadata, }, timezone, onUserClick = () => null, @@ -61,71 +64,89 @@ export default class SessionItem extends React.PureComponent { } = this.props; const formattedDuration = durationFormatted(duration); const hasUserId = userId || userAnonymousId; + const isAssist = isRoute(ASSIST_ROUTE, this.props.location.pathname); + console.log('metadata', metadata); + + const _metaList = Object.keys(metadata).map(key => { + const value = metadata[key]; + return { label: key, value }; + }); + + console.log('SessionItem', _metaList); return (
-
-
-
+
+
+ {/*
*/} +
(!disableUser && !hasUserFilter && hasUserId) && onUserClick(userId, userAnonymousId)} > {userDisplayName}
-
30 Sessions
-
-
-
-
{formatTimeOrDate(startedAt, timezone) }
-
- {!live && ( -
- { eventsCount } - { eventsCount === 0 || eventsCount > 1 ? 'Events' : 'Event' } -
- )} - - -
{ live ? : formattedDuration }
-
-
-
-
- -
- {userBrowser} - - {userOs} - - {userDeviceType} +
(!disableUser && !hasUserFilter && hasUserId) && onUserClick(userId, userAnonymousId)} + > + 30 Sessions
-
- +
+
{formatTimeOrDate(startedAt, timezone) }
+
+ {!isAssist && ( + <> +
+ { eventsCount } + { eventsCount === 0 || eventsCount > 1 ? 'Events' : 'Event' } +
+
·
+ + )} +
{ live ? : formattedDuration }
+
+
+ {/*
*/} + +
+ + + +
·
+ + + +
·
+ + + +
+ {/*
*/} +
+ { !isAssist && ( +
+ +
+ )}
- {/* { live && } */} -
- -
-
- +
- + { isAssist && ( + + )}
); } diff --git a/frontend/app/components/shared/SessionItem/SessionMetaList/SessionMetaList.tsx b/frontend/app/components/shared/SessionItem/SessionMetaList/SessionMetaList.tsx index 3abc72041..96b082e96 100644 --- a/frontend/app/components/shared/SessionItem/SessionMetaList/SessionMetaList.tsx +++ b/frontend/app/components/shared/SessionItem/SessionMetaList/SessionMetaList.tsx @@ -1,6 +1,8 @@ import React from 'react' import { Popup } from 'UI' import cn from 'classnames' +import MetaItem from '../MetaItem'; +import MetaMoreButton from '../MetaMoreButton'; interface Props { className?: string, @@ -12,36 +14,11 @@ export default function SessionMetaList(props: Props) { return (
{metaList.slice(0, MAX_LENGTH).map(({ label, value }, index) => ( -
- {label} - {value} -
+ ))} {metaList.length > MAX_LENGTH && ( - - - +{metaList.length - MAX_LENGTH} More - -
- ) } - content={ -
- {metaList.slice(MAX_LENGTH).map(({ label, value }, index) => ( -
- {label} - {value} -
- ))} -
- } - on="click" - position="top right" - // className={ styles.popup } - hideOnScroll - /> + )}
) diff --git a/frontend/app/components/ui/Avatar/Avatar.js b/frontend/app/components/ui/Avatar/Avatar.js index b369c5e30..fd1ca884b 100644 --- a/frontend/app/components/ui/Avatar/Avatar.js +++ b/frontend/app/components/ui/Avatar/Avatar.js @@ -11,14 +11,15 @@ const ICON_LIST = ['icn_chameleon', 'icn_fox', 'icn_gorilla', 'icn_hippo', 'icn_ 'icn_wild1', 'icn_wild_bore'] -const Avatar = ({ className, width = "38px", height = "38px", iconSize = 26, seed }) => { +const Avatar = ({ isAssist = false, className, width = "38px", height = "38px", iconSize = 26, seed }) => { var iconName = avatarIconName(seed); return (
+ {isAssist &&
}
); }; diff --git a/frontend/app/components/ui/CountryFlag/CountryFlag.js b/frontend/app/components/ui/CountryFlag/CountryFlag.js index 0ba14c967..04cfaf38d 100644 --- a/frontend/app/components/ui/CountryFlag/CountryFlag.js +++ b/frontend/app/components/ui/CountryFlag/CountryFlag.js @@ -3,22 +3,26 @@ import { countries } from 'App/constants'; import { Popup } from 'UI'; import stl from './countryFlag.css'; -const CountryFlag = ({ country, className }) => { +const CountryFlag = React.memo(({ country, className, style = {}, label = false }) => { const knownCountry = !!country && country !== 'UN'; - const countryFlag = knownCountry ? country.toLowerCase() : ''; - const countryName = knownCountry ? countries[ country ] : 'Unknown Country'; + const countryFlag = knownCountry ? country.toLowerCase() : ''; + const countryName = knownCountry ? countries[ country ] : 'Unknown Country'; + return ( - - : { "N/A" } - } - content={ countryName } - inverted - size="tiny" - /> +
+ + :
{ "N/A" }
+ } + content={ countryName } + inverted + size="tiny" + /> + { knownCountry && label &&
{ countryName }
} +
); -} +}) CountryFlag.displayName = "CountryFlag"; diff --git a/frontend/app/components/ui/CountryFlag/countryFlag.css b/frontend/app/components/ui/CountryFlag/countryFlag.css index 29a5d880b..4cbc1f39b 100644 --- a/frontend/app/components/ui/CountryFlag/countryFlag.css +++ b/frontend/app/components/ui/CountryFlag/countryFlag.css @@ -1,4 +1,8 @@ .default { width: 22px !important; height: 14px !important; +} + +.label { + line-height: 0 !important; } \ No newline at end of file diff --git a/frontend/app/components/ui/IconButton/IconButton.js b/frontend/app/components/ui/IconButton/IconButton.js index eb708f21a..6aa9f3d5f 100644 --- a/frontend/app/components/ui/IconButton/IconButton.js +++ b/frontend/app/components/ui/IconButton/IconButton.js @@ -9,8 +9,10 @@ const IconButton = React.forwardRef(({ onClick, plain = false, shadow = false, + red = false, primary = false, primaryText = false, + redText = false, outline = false, loading = false, roundedOutline = false, @@ -40,7 +42,9 @@ const IconButton = React.forwardRef(({ [ stl.active ]: active, [ stl.shadow ]: shadow, [ stl.primary ]: primary, + [ stl.red ]: red, [ stl.primaryText ]: primaryText, + [ stl.redText ]: redText, [ stl.outline ]: outline, [ stl.circle ]: circle, [ stl.roundedOutline ]: roundedOutline, diff --git a/frontend/app/components/ui/IconButton/iconButton.css b/frontend/app/components/ui/IconButton/iconButton.css index 1685ca4d6..11b3dd51f 100644 --- a/frontend/app/components/ui/IconButton/iconButton.css +++ b/frontend/app/components/ui/IconButton/iconButton.css @@ -73,11 +73,41 @@ fill: white; } + & svg { + fill: white; + } + + & .label { + color: white !important; + } + &:hover { background-color: $teal-dark; } } + &.red { + background-color: $red; + box-shadow: 0 0 0 1px $red inset !important; + + & .icon { + fill: white; + } + + & svg { + fill: white; + } + + & .label { + color: white !important; + } + + &:hover { + background-color: $red; + filter: brightness(90%); + } + } + &.outline { box-shadow: 0 0 0 1px $teal inset !important; & .label { @@ -116,4 +146,14 @@ .primaryText .label { color: $teal !important; +} + +.redText { + & .label { + color: $red !important; + } + + & svg { + fill: $red; + } } \ No newline at end of file diff --git a/frontend/app/components/ui/TextEllipsis/textEllipsis.css b/frontend/app/components/ui/TextEllipsis/textEllipsis.css index 9baca35cc..9919f9160 100644 --- a/frontend/app/components/ui/TextEllipsis/textEllipsis.css +++ b/frontend/app/components/ui/TextEllipsis/textEllipsis.css @@ -1,7 +1,7 @@ .textEllipsis { text-overflow: ellipsis; overflow: hidden; - display: inline-block; + /* display: inline-block; */ white-space: nowrap; max-width: 100%; } \ No newline at end of file diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index fc366bc39..3339c4e48 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -123,4 +123,15 @@ &:hover { background-color: $active-blue; } +} + +.text-dotted-underline { + text-decoration: underline dotted !important; +} + +.divider { + width: 1px; + height: 49px; + margin: 0 15px; + background-color: $gray-light; } \ No newline at end of file