diff --git a/src/components/SshKeys/StyledKeys.tsx b/src/components/SshKeys/StyledKeys.tsx index 689807be..204dac24 100644 --- a/src/components/SshKeys/StyledKeys.tsx +++ b/src/components/SshKeys/StyledKeys.tsx @@ -7,6 +7,11 @@ export const StyledKeys = styled.div` flex: 4; align-self: center; } + label.fingerprint { + @media ${bp.wide_ultraWide} { + flex-grow: 3; + } + } label.type, div.type { flex: 2; @@ -16,11 +21,23 @@ export const StyledKeys = styled.div` div.name { flex: 1.8; } - label.created, div.created { flex: 2; } + label.last-used, + div.last-used { + flex: 2; + } + label.last-used { + @media ${bp.wide_ultraWide} { + flex-grow: 3; + padding-left: 0px !important; + } + @media screen and (min-width: 1400px) and (max-width: 1600px) { + flex-grow: 3.5; + } + } .header { padding-right: calc(15% + 8px); @@ -93,6 +110,9 @@ export const StyledKeys = styled.div` &.created { align-self: center; } + &.last-used { + align-self: center; + } &.name { overflow-wrap: break-word; } @@ -120,6 +140,18 @@ export const StyledKeys = styled.div` width: 50%; } } + + &.last-used { + word-break: break-word; + overflow: hidden; + text-overflow: ellipsis; + @media ${bp.wideUp} { + width: 45%; + } + @media ${bp.extraWideUp} { + width: 50%; + } + } } &:hover { @@ -135,6 +167,11 @@ export const StyledKeys = styled.div` border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; } + + .anticon.anticon-info-circle { + vertical-align: top; + margin-left: 4px; + } } } `; diff --git a/src/components/SshKeys/index.js b/src/components/SshKeys/index.js index 196581a5..5e30c965 100644 --- a/src/components/SshKeys/index.js +++ b/src/components/SshKeys/index.js @@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react'; import { Mutation } from 'react-apollo'; import Skeleton from 'react-loading-skeleton'; -import { Col, Modal, Row, Space, notification } from 'antd'; +import InfoCircleTwoTone from '@ant-design/icons/InfoCircleTwoTone'; +import { Col, Modal, Row, Space, Tooltip, notification } from 'antd'; import Button from 'components/Button'; import DeleteConfirm from 'components/DeleteConfirm'; import DeleteUserSSHPublicKey from 'lib/mutation/DeleteUserSSHPublicKey'; @@ -49,6 +50,51 @@ const SshKeys = ({ me: { id, email, sshKeys: keys }, loading, handleRefetch }) = }); }; + const tooltipDateTime = (created, lastUsed) => { + return ( + <> + + Created: {moment.utc(created).local().format('DD MMM YYYY, HH:mm:ss (Z)')} + +
+ + Last Used: {moment.utc(lastUsed).local().format('DD MMM YYYY, HH:mm:ss (Z)')} + + + ); + }; + + const lastUsedTimeframe = dateTime => { + if (!dateTime) { + return 'Never'; + } + + const lastUsed = moment.utc(dateTime).local().format('DD MMM YYYY, HH:mm:ss (Z)'); + const now = moment(); + const dayDiff = now.diff(lastUsed, 'days'); + + if (dayDiff === 0) { + return 'Today'; + } else if (dayDiff === 1) { + return 'Yesterday'; + } else if (dayDiff < 7) { + return `${dayDiff} days ago`; + } + + const weekDiff = now.diff(lastUsed, 'weeks'); + if (weekDiff < 4) { + return `${weekDiff} week${weekDiff > 1 ? 's' : ''} ago`; + } + + const monthDiff = now.diff(lastUsed, 'months'); + if (monthDiff < 12) { + return `${monthDiff} month${monthDiff > 1 ? 's' : ''} ago`; + } + + const yearDiff = now.diff(lastUsed, 'years'); + return `${yearDiff} year${yearDiff > 1 ? 's' : ''} ago`; + }; + useEffect(() => { setIsLoading(loading); }, [loading]); @@ -59,14 +105,14 @@ const SshKeys = ({ me: { id, email, sshKeys: keys }, loading, handleRefetch }) = - + {/**/} + {isLoading ? ( ) : (
{!keys?.length &&
No SSH keys
} - {keys && keys.map(key => (
@@ -75,8 +121,20 @@ const SshKeys = ({ me: { id, email, sshKeys: keys }, loading, handleRefetch }) =
{key.keyType}
{key.keyFingerprint}
-
- {moment.utc(key.created).local().format('DD MMM YYYY, HH:mm:ss (Z)')} +
+
+ Created: {lastUsedTimeframe(key.created)} + + + +
+
+ Last used: {lastUsedTimeframe(key.lastUsed)} +
diff --git a/src/lib/query/Me.js b/src/lib/query/Me.js index ffca22c8..c5a6c10c 100644 --- a/src/lib/query/Me.js +++ b/src/lib/query/Me.js @@ -14,6 +14,7 @@ export default gql` keyValue created keyFingerprint + lastUsed } } } diff --git a/src/lib/variables.js b/src/lib/variables.js index 6eaa7a7a..8bb16c7b 100644 --- a/src/lib/variables.js +++ b/src/lib/variables.js @@ -5,6 +5,7 @@ const BP_TABLET = 768; const BP_DESKTOP = 960; const BP_WIDE = 1200; const BP_EXTRAWIDE = 1400; +const BP_ULTRAWIDE = 1800; export const color = { black: '#1a1a1a', @@ -51,6 +52,7 @@ export const bp = { xs_small_extrawide: `all and (min-width: ${BP_XS / 16}em) and (max-width: ${(BP_EXTRAWIDE - 1) / 16}em)`, desktop_extrawide: `all and (min-width: ${BP_DESKTOP / 16}em) and (max-width: ${(BP_EXTRAWIDE - 1) / 16}em)`, wide_extraWide: `all and (min-width: ${BP_WIDE / 16}em) and (max-width: ${(BP_EXTRAWIDE - 1) / 16}em)`, + wide_ultraWide: `all and (min-width: ${BP_WIDE / 16}em) and (max-width: ${(BP_ULTRAWIDE - 1) / 16}em)`, }; export const pxToRem = pxValue => `${pxValue / 16}rem`; diff --git a/src/static/images/information.svg b/src/static/images/information.svg new file mode 100644 index 00000000..89f2bb37 --- /dev/null +++ b/src/static/images/information.svg @@ -0,0 +1 @@ + \ No newline at end of file