diff --git a/assets/src/components/apps/app/runbooks/runbook/display/DisplayBox.tsx b/assets/src/components/apps/app/runbooks/runbook/display/DisplayBox.tsx
index 835086d125..1f6e960e02 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/DisplayBox.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/DisplayBox.tsx
@@ -1,19 +1,36 @@
-import { Box } from 'grommet'
+import { Flex } from '@pluralsh/design-system'
import { recurse } from './misc'
const border = ({ borderSize, borderSide, border }) =>
borderSize || borderSide
- ? { side: borderSide, color: border, size: borderSize }
+ ? { [`border${borderSide}`]: `${border} ${borderSize}` }
: border
export function DisplayBox({ children, attributes }) {
return (
-
{recurse(children)}
-
+
+ )
+}
+
+// basically like a polyfill to convert grommet box attrs to regular flex syntax
+function grommetBoxAttrsToFlex(attrs: Record
) {
+ return Object.fromEntries(
+ Object.entries(attrs).map(([key, value]) => [
+ key === 'align'
+ ? 'alignItems'
+ : key === 'justify'
+ ? 'justifyContent'
+ : key === 'direction'
+ ? 'flexDirection'
+ : key,
+ value === 'start' ? 'flex-start' : value === 'end' ? 'flex-end' : value,
+ ])
)
}
diff --git a/assets/src/components/apps/app/runbooks/runbook/display/DisplayButton.tsx b/assets/src/components/apps/app/runbooks/runbook/display/DisplayButton.tsx
index dc7a6c2c98..3d81cbbdf8 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/DisplayButton.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/DisplayButton.tsx
@@ -1,6 +1,5 @@
import { Banner, Button } from '@pluralsh/design-system'
import { ApolloError } from 'apollo-boost'
-import { EXECUTE_RUNBOOK } from 'components/runbooks/queries'
import { useContext, useState } from 'react'
import { useMutation } from '@apollo/client'
import { useNavigate, useParams } from 'react-router-dom'
@@ -10,6 +9,7 @@ import { legacyUrl } from 'helpers/url'
import { ActionPortal } from '../Runbook'
import { DisplayContext } from '../RunbookDisplay'
+import { EXECUTE_RUNBOOK } from '../../queries'
function getButton({ primary, key, ...props }: any) {
return (
diff --git a/assets/src/components/apps/app/runbooks/runbook/display/DisplayGraph.tsx b/assets/src/components/apps/app/runbooks/runbook/display/DisplayGraph.tsx
index fddc7907c7..0132455433 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/DisplayGraph.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/DisplayGraph.tsx
@@ -1,10 +1,11 @@
-import { ValueFormats } from 'components/runbooks/utils'
import { Graph } from 'components/utils/Graph'
import { Div } from 'honorable'
import { useContext, useMemo } from 'react'
import { DisplayContext } from '../RunbookDisplay'
+import { ValueFormats } from './misc'
+
const convertVals = (v) =>
v.map(({ timestamp, value }) => ({
x: new Date(timestamp * 1000),
diff --git a/assets/src/components/apps/app/runbooks/runbook/display/DisplayLink.tsx b/assets/src/components/apps/app/runbooks/runbook/display/DisplayLink.tsx
index 569efe1bf6..e78365f194 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/DisplayLink.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/DisplayLink.tsx
@@ -1,18 +1,19 @@
-import { Anchor, Text } from 'grommet'
+import { useTheme } from 'styled-components'
import { recurse } from './misc'
export function DisplayLink({ value, attributes, children }) {
+ const theme = useTheme()
const val = value || attributes.value
return (
-
-
+
{val || recurse(children)}
-
-
+
+
)
}
diff --git a/assets/src/components/apps/app/runbooks/runbook/display/DisplayTable.tsx b/assets/src/components/apps/app/runbooks/runbook/display/DisplayTable.tsx
index d2d244ee39..e44d91c90b 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/DisplayTable.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/DisplayTable.tsx
@@ -1,11 +1,12 @@
import { Table } from '@pluralsh/design-system'
import { createColumnHelper } from '@tanstack/react-table'
-import { query } from 'components/runbooks/utils'
import { get } from 'lodash'
import { useContext, useMemo } from 'react'
import { DisplayContext } from '../RunbookDisplay'
+import { query } from './misc'
+
const columnHelper = createColumnHelper()
// Ignored column widths that are part of children object
diff --git a/assets/src/components/apps/app/runbooks/runbook/display/misc.tsx b/assets/src/components/apps/app/runbooks/runbook/display/misc.tsx
index 08826f1161..becea8baa3 100644
--- a/assets/src/components/apps/app/runbooks/runbook/display/misc.tsx
+++ b/assets/src/components/apps/app/runbooks/runbook/display/misc.tsx
@@ -1,6 +1,12 @@
-import { extract, query } from 'components/runbooks/utils'
import { useContext } from 'react'
+import jp from 'jsonpath'
+import { cpuFormat } from 'utils/kubernetes'
+
+import { filesize } from 'filesize'
+
+import { deepFetch } from 'utils/graphql'
+
import { DisplayContext } from '../RunbookDisplay'
import { DisplayAttachment } from './DisplayAttachment'
@@ -77,3 +83,18 @@ export function parse(struct, index, theme) {
return null
}
}
+
+export const query = (object, path) => jp.query(object, `$.${path}`)[0]
+
+export const ValueFormats = {
+ cpu: cpuFormat,
+ memory: filesize,
+}
+
+export function extract(data, doc) {
+ if (!doc) return data
+
+ const raw = deepFetch(data, doc)
+
+ return JSON.parse(raw)
+}
diff --git a/assets/src/components/builds/UpgradePoliciesList.tsx b/assets/src/components/builds/UpgradePoliciesList.tsx
index f957689713..79fd9c5cac 100644
--- a/assets/src/components/builds/UpgradePoliciesList.tsx
+++ b/assets/src/components/builds/UpgradePoliciesList.tsx
@@ -1,5 +1,5 @@
import { useQuery } from '@apollo/client'
-import { ArrowLeftIcon, Button, Card } from '@pluralsh/design-system'
+import { Accordion, ArrowLeftIcon, Button } from '@pluralsh/design-system'
import { UPGRADE_POLICIES } from 'components/graphql/builds'
import { Flex } from 'honorable'
import { isEmpty } from 'lodash'
@@ -23,10 +23,12 @@ export default function UpgradePoliciesList() {
return (
<>
{!isEmpty(upgradePolicies) ? (
-
{upgradePolicies.map((policy, i) => (
))}
-
+
) : (
'No upgrade policies available.'
)}
diff --git a/assets/src/components/builds/UpgradePolicy.tsx b/assets/src/components/builds/UpgradePolicy.tsx
index 16e8c29310..67a4d5b727 100644
--- a/assets/src/components/builds/UpgradePolicy.tsx
+++ b/assets/src/components/builds/UpgradePolicy.tsx
@@ -1,18 +1,17 @@
import { useMutation } from '@apollo/client'
import {
+ AccordionItem,
Button,
Chip,
- CollapseIcon,
TrashCanIcon,
} from '@pluralsh/design-system'
import { DELETE_POLICY, UPGRADE_POLICIES } from 'components/graphql/builds'
-import { Collapsible } from 'grommet'
import { Flex, P } from 'honorable'
-import { useState } from 'react'
+import { useTheme } from 'styled-components'
import { updateCache } from 'utils/graphql'
export default function UpgradePolicy({ policy, last = false }) {
- const [open, setOpen] = useState(false)
+ const theme = useTheme()
const [mutation] = useMutation(DELETE_POLICY, {
variables: { id: policy.id },
update: (cache, { data: { deleteUpgradePolicy } }) =>
@@ -28,92 +27,73 @@ export default function UpgradePolicy({ policy, last = false }) {
})
return (
- <>
- setOpen(!open)}
- padding="small"
- _hover={{ backgroundColor: 'fill-two-hover' }}
- >
+
-
+
+
+
+ {policy.name}
+
+
+ {policy.type?.toLowerCase()}
+
+
- {policy.name}
+ App bindings: {policy.target}
-
- {policy.type?.toLowerCase()}
-
-
- App bindings: {policy.target}
-
+
+ Weight: {policy.weight}
-
- Weight: {policy.weight}
-
-
-
+
-
-
+
+ }
+ onClick={() => mutation()}
>
- {policy.description || 'No description.'}
-
-
- }
- onClick={() => mutation()}
- >
- Delete
-
-
+ Delete
+
-
- >
+
+
)
}
diff --git a/assets/src/components/cd/logs/LogsCard.tsx b/assets/src/components/cd/logs/LogsCard.tsx
index 75a9543e6f..9998653d12 100644
--- a/assets/src/components/cd/logs/LogsCard.tsx
+++ b/assets/src/components/cd/logs/LogsCard.tsx
@@ -25,13 +25,7 @@ function doUpdate(prev, result) {
}
}
-export function LogsCard({
- serviceId,
- clusterId,
- query,
- addLabel,
- fullscreen = false,
-}: any) {
+export function LogsCard({ serviceId, clusterId, query, addLabel }: any) {
const [listRef, setListRef] = useState(null)
const [live, setLive] = useState(true)
const [loader, setLoader] = useState(null)
@@ -96,7 +90,6 @@ export function LogsCard({
}
onScroll={(arg) => setLive(!arg)}
addLabel={addLabel}
- fullscreen={fullscreen}
/>
) : (
diff --git a/assets/src/components/cd/utils/RepoKindSelector.tsx b/assets/src/components/cd/utils/RepoKindSelector.tsx
index 768c7debb1..2677b57fad 100644
--- a/assets/src/components/cd/utils/RepoKindSelector.tsx
+++ b/assets/src/components/cd/utils/RepoKindSelector.tsx
@@ -8,7 +8,7 @@ import {
TabListStateProps,
TabPanel,
} from '@pluralsh/design-system'
-import { useTheme } from 'styled-components'
+import styled, { useTheme } from 'styled-components'
import ProviderIcon from 'components/utils/Provider'
import { Provider } from 'generated/graphql-plural'
@@ -50,27 +50,13 @@ export function RepoKindSelector({
stateProps={tabListStateProps}
css={{
width: 'fit-content',
- position: 'relative',
borderRadius: theme.borderRadiuses.medium,
- '&::after': {
- pointerEvents: 'none',
- content: '""',
- outline: theme.borders.default,
- position: 'absolute',
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- outlineOffset: -1,
- borderRadius: theme.borderRadiuses.medium,
- },
+ outline: theme.borders.default,
+ outlineOffset: -1,
}}
>
-
@@ -82,12 +68,9 @@ export function RepoKindSelector({
color={theme.colors['icon-success']}
/>
)}
-
-
+
@@ -104,13 +87,10 @@ export function RepoKindSelector({
color={theme.colors['icon-success']}
/>
)}
-
+
{enableFlux ? (
-
@@ -122,7 +102,7 @@ export function RepoKindSelector({
color={theme.colors['icon-success']}
/>
)}
-
+
) : null}
)
}
+
+const SelectorSubtabSC = styled(SubTab)<{ $active?: boolean }>(
+ ({ theme, $active }) => ({
+ display: 'flex',
+ gap: theme.spacing.xsmall,
+ outline: $active ? theme.borders.input : undefined,
+ outlineOffset: -1,
+ '&:focus': { outline: theme.borders.input },
+ })
+)
diff --git a/assets/src/components/cluster/utils.tsx b/assets/src/components/cluster/utils.tsx
index 8f220a077f..b94c6b25b0 100644
--- a/assets/src/components/cluster/utils.tsx
+++ b/assets/src/components/cluster/utils.tsx
@@ -1,23 +1,3 @@
-import { Box, Text } from 'grommet'
-
-export function Container({ header, children, ...props }) {
- return (
-
-
- {header}
-
- {children}
-
- )
-}
-
export function roundToTwoPlaces(x: number) {
return roundTo(x, 2)
}
diff --git a/assets/src/components/incidents/AttachmentProvider.jsx b/assets/src/components/incidents/AttachmentProvider.jsx
deleted file mode 100644
index e7d69334ee..0000000000
--- a/assets/src/components/incidents/AttachmentProvider.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Box } from 'grommet'
-import React, { useContext, useState } from 'react'
-import { DndProvider, useDrop } from 'react-dnd'
-import { HTML5Backend, NativeTypes } from 'react-dnd-html5-backend'
-
-const DROP_BACKGROUND = 'rgba(255, 255, 255, 0.2)'
-const FILE_DROP_PROPS = {
- border: { color: 'focus', style: 'dashed', size: '2px' },
- background: DROP_BACKGROUND,
-}
-
-export const AttachmentContext = React.createContext({})
-
-export function Dropzone({ children }) {
- const { setAttachment } = useContext(AttachmentContext)
-
- const [{ canDrop, isOver }, drop] = useDrop({
- accept: [NativeTypes.FILE],
- drop: ({ files }) => setAttachment(files[0]),
- collect: (monitor) => ({
- isOver: monitor.isOver(),
- canDrop: monitor.canDrop(),
- }),
- })
- const dragActive = canDrop && isOver
-
- return (
-
- {children}
-
- )
-}
-
-export function AttachmentProvider({ children }) {
- const [attachment, setAttachment] = useState(null)
-
- return (
- // eslint-disable-next-line react/jsx-no-constructed-context-values
-
- {children}
-
- )
-}
diff --git a/assets/src/components/incidents/ClusterInformation.jsx b/assets/src/components/incidents/ClusterInformation.jsx
deleted file mode 100644
index 1831a328d0..0000000000
--- a/assets/src/components/incidents/ClusterInformation.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import {
- Box,
- Table,
- TableBody,
- TableCell,
- TableHeader,
- TableRow,
- Text,
-} from 'grommet'
-
-function NoInformation() {
- return (
-
- No cluster information present
-
- )
-}
-
-function Header() {
- return (
-
-
-
- Name
-
-
- Value
-
-
-
- )
-}
-
-const truncate = (str) =>
- str && str.length > 20 ? `${str.substring(0, 17)}...` : str
-
-function Row({ name, value }) {
- return (
-
-
- {name}
-
- {truncate(value)}
-
- )
-}
-
-export function ClusterInformation({ incident: { clusterInformation } }) {
- if (!clusterInformation) return
-
- const { __typename, ...info } = clusterInformation
-
- return (
-
-
-
-
- {Object.entries(info).map(([key, value]) => (
-
- ))}
-
-
-
- )
-}
diff --git a/assets/src/components/incidents/CreateIncident.jsx b/assets/src/components/incidents/CreateIncident.jsx
deleted file mode 100644
index ced3ec4fe2..0000000000
--- a/assets/src/components/incidents/CreateIncident.jsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import { useCallback, useContext, useEffect, useState } from 'react'
-import { Box, Text, TextInput } from 'grommet'
-import { Button, Check as Checkmark, SecondaryButton } from 'forge-core'
-import { Editable, Slate } from 'slate-react'
-
-import { useMutation, useQuery } from '@apollo/client'
-
-import { useEditor } from '../utils/hooks'
-import { plainDeserialize, plainSerialize } from '../../utils/slate'
-
-import { RepoIcon } from '../repos/Repositories'
-
-import { appendConnection, updateCache } from '../../utils/graphql'
-
-import { TagInput } from '../repos/Tags'
-
-import { SeveritySelect } from './Severity'
-import { CREATE_INCIDENT, INCIDENTS_Q, INSTALLATIONS_Q } from './queries'
-
-import { StatusSelector } from './IncidentStatus'
-
-import { IncidentContext } from './context'
-import { IncidentViewContext } from './Incidents'
-
-export function IncidentForm({
- attributes,
- setAttributes,
- statusEdit,
- children,
-}) {
- const [editorState, setEditorState] = useState(
- plainDeserialize(attributes.description || '')
- )
- const editor = useEditor()
- const setDescription = useCallback(
- (editorState) => {
- setEditorState(editorState)
- setAttributes({ ...attributes, description: plainSerialize(editorState) })
- },
- [setAttributes, attributes, setEditorState]
- )
-
- return (
-
-
-
-
- Title
-
-
- setAttributes({ ...attributes, title: value })
- }
- />
- {statusEdit && (
- setAttributes({ ...attributes, status })}
- />
- )}
-
- setAttributes({ ...attributes, severity })}
- />
-
-
-
-
-
-
-
-
- setAttributes({
- ...attributes,
- tags: [tag, ...(attributes.tags || [])],
- })
- }
- removeTag={(tag) =>
- setAttributes({
- ...attributes,
- tags: attributes.tags.filter((t) => t !== tag),
- })
- }
- />
- {children}
-
-
- )
-}
-
-export function RepoOption({ repo, selected, setRepository }) {
- return (
- setRepository(repo)}
- hoverIndicator="light-3"
- pad="small"
- focusIndicator={false}
- >
-
-
-
- {repo.name}
-
-
- {repo.description}
-
-
- {selected.id === repo.id && (
-
- )}
-
- )
-}
-
-export function RepositorySelect({ repository, setRepository }) {
- const { data } = useQuery(INSTALLATIONS_Q, {
- fetchPolicy: 'cache-and-network',
- })
-
- useEffect(() => {
- if (data && data.installations && !repository) {
- const installation = data.installations.edges[0]
-
- setRepository(installation && installation.node.repository)
- }
- }, [data, repository, setRepository])
- if (!data || !repository) return null
-
- return (
-
- {data.installations.edges.map(({ node: { repository: repo } }, i) => (
-
- ))}
-
- )
-}
-
-export function CreateIncident({ onCompleted }) {
- const { clusterInformation } = useContext(IncidentContext)
- const { sort, order, filters } = useContext(IncidentViewContext)
- const [repository, setRepository] = useState(null)
- const [attributes, setAttributes] = useState({
- title: '',
- description: '',
- severity: 4,
- tags: [],
- })
- const [mutation, { loading }] = useMutation(CREATE_INCIDENT, {
- variables: {
- repositoryId: repository && repository.id,
- attributes: {
- ...attributes,
- tags: attributes.tags.map((t) => ({ tag: t })),
- clusterInformation,
- },
- },
- update: (cache, { data: { createIncident } }) =>
- updateCache(cache, {
- query: INCIDENTS_Q,
- variables: {
- q: null,
- filters,
- order,
- sort,
- },
- update: (prev) => appendConnection(prev, createIncident, 'incidents'),
- }),
- onCompleted,
- })
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/assets/src/components/incidents/Editor.jsx b/assets/src/components/incidents/Editor.jsx
deleted file mode 100644
index 1d69107371..0000000000
--- a/assets/src/components/incidents/Editor.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import { useApolloClient } from '@apollo/client'
-
-import TypeaheadEditor from '../utils/TypeaheadEditor'
-
-// export function fetchUsers(client, query) {
-// if (!query) return
-
-// return client.query({
-// query: SEARCH_USERS,
-// variables: {q: query}
-// }).then(({data}) => {
-// return data.users.edges.map(edge => ({
-// key: edge.node.email,
-// value: mentionNode(edge.node),
-// suggestion: userSuggestion(edge.node)
-// }))
-// })
-// }
-
-// export function userSuggestion(user) {
-// return (
-//
-//
-//
-// {user.email}
-//
-//
-// {user.name}
-//
-//
-// )
-// }
-
-// const mentionNode = (user) => ({type: EntityType.MENTION, children: [{text: user.name + ' '}], user})
-
-// const PLUGINS = [
-// {trigger: /^@(\w+)$/, suggestions: fetchUsers}
-// ]
-
-export default function Editor({
- incidentId,
- editorState,
- editor,
- handlers,
- disableSubmit,
- setEditorState,
-}) {
- const client = useApolloClient()
-
- return (
- callback(client, query, incidentId)}
- onOpen={disableSubmit}
- handlers={handlers || []}
- setValue={setEditorState}
- style={{
- overflow: 'auto',
- fontFamily: 'Roboto',
- fontSize: '14px',
- width: '100%',
- maxHeight: '160px',
- paddingLeft: '10px',
- paddingTop: '3px',
- paddingBottom: '3px',
- }}
- />
- )
-}
diff --git a/assets/src/components/incidents/Emoji.jsx b/assets/src/components/incidents/Emoji.jsx
deleted file mode 100644
index 2bd44f54ef..0000000000
--- a/assets/src/components/incidents/Emoji.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import 'emoji-mart/css/emoji-mart.css'
-import emojiData from 'emoji-mart/data/google.json'
-import { NimblePicker } from 'emoji-mart'
-
-export function EmojiPicker(props) {
- return (
-
- )
-}
diff --git a/assets/src/components/incidents/File.jsx b/assets/src/components/incidents/File.jsx
deleted file mode 100644
index 18c7aca4bf..0000000000
--- a/assets/src/components/incidents/File.jsx
+++ /dev/null
@@ -1,244 +0,0 @@
-import { useState } from 'react'
-import { Box, Stack, Text } from 'grommet'
-import { FileIcon, defaultStyles } from 'react-file-icon'
-import { Download, HoveredBackground, Tooltip } from 'forge-core'
-import moment from 'moment'
-import { filesize } from 'filesize'
-
-import { download } from '../../utils/file'
-
-import { FileTypes } from './types'
-
-const extension = (file) => file.split('.').pop()
-
-function DownloadAffordance({ blob }) {
- return (
-
-
- {
- e.preventDefault()
- download(blob)
- }}
- border={{ color: 'light-3' }}
- >
-
-
-
- download
-
- )
-}
-
-function Image({ height, blob, filename }) {
- const [hover, setHover] = useState(false)
-
- return (
- setHover(true)}
- onMouseLeave={() => setHover(false)}
- >
-
-
- {hover && }
-
-
- )
-}
-
-function Video({ blob, filename }) {
- return (
-
- )
-}
-
-function MediaFile({ file }) {
- const { filename, mediaType } = file
-
- return (
-
-
-
- {filename}
-
-
-
- {mediaType === FileTypes.IMAGE ? (
-
- ) : (
-
- )}
-
-
- )
-}
-
-export function Icon({ size, name }) {
- const ext = extension(name)
- const styles = defaultStyles[ext] || {}
-
- return (
-
-
-
- )
-}
-
-const MEDIA_STYLES = { maxWidth: 50, maxHeight: 50 }
-
-export function FileEntry({ file }) {
- const [hover, setHover] = useState(false)
-
- return (
- setHover(true)}
- onMouseLeave={() => setHover(false)}
- >
-
-
-
- {file.mediaType === FileTypes.VIDEO ? (
-
- ) : file.mediaType === FileTypes.IMAGE ? (
-
- ) : (
-
- )}
-
-
- {file.filename}
-
-
- {filesize(file.filesize ?? 0)}
-
- {moment(file.insertedAt).fromNow()}
-
-
-
- {hover && }
-
-
- )
-}
-
-export function StandardFile({
- file: { filename, blob, insertedAt, ...file },
-}) {
- const [hover, setHover] = useState(false)
-
- return (
-
- setHover(true)}
- onMouseLeave={() => setHover(false)}
- onClick={() => download(blob)}
- border={hover ? { color: 'focus' } : { color: 'light-5' }}
- background="#fff"
- round="xsmall"
- align="center"
- direction="row"
- pad="small"
- gap="small"
- margin={{ vertical: 'xsmall' }}
- width="30%"
- >
-
-
- {filename}
-
-
- {filesize(file.filesize ?? 0)}
-
- {moment(insertedAt).fromNow()}
-
-
-
-
- )
-}
-
-export default function File({ file }) {
- switch (file.mediaType) {
- case FileTypes.IMAGE:
- case FileTypes.VIDEO:
- return
- default:
- return
- }
-}
diff --git a/assets/src/components/incidents/Followers.jsx b/assets/src/components/incidents/Followers.jsx
deleted file mode 100644
index 2c34a9bf4c..0000000000
--- a/assets/src/components/incidents/Followers.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useContext } from 'react'
-import { Box, Text } from 'grommet'
-import { Scroller } from 'forge-core'
-
-import { extendConnection } from '../../utils/graphql'
-import Avatar from '../utils/Avatar'
-
-import { PresenceContext, PresenceIndicator } from './Presence'
-
-function Follower({ follower: { user } }) {
- const { present } = useContext(PresenceContext)
- const isPresent = present[user.id]
-
- return (
- null}
- hoverIndicator="light-3"
- pad="small"
- >
-
-
- {user.name}
-
- {isPresent && }
-
- )
-}
-
-export function Followers({
- incident: {
- followers: { edges, pageInfo },
- },
- fetchMore,
-}) {
- return (
-
- (
-
- )}
- onLoadMore={() =>
- pageInfo.hasNextPage &&
- fetchMore({
- variables: { followerCursor: pageInfo.endCursor },
- updateQuery: (
- prev,
- {
- fetchMoreResult: {
- incident: { followers },
- },
- }
- ) => ({
- ...prev,
- incident: extendConnection(prev.incident, followers, 'followers'),
- }),
- })
- }
- />
-
- )
-}
diff --git a/assets/src/components/incidents/Incident.jsx b/assets/src/components/incidents/Incident.jsx
deleted file mode 100644
index 298f4544a2..0000000000
--- a/assets/src/components/incidents/Incident.jsx
+++ /dev/null
@@ -1,666 +0,0 @@
-import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
-import {
- Button,
- Close,
- Edit,
- File,
- Messages as MessagesI,
- ModalHeader,
- Scroller,
-} from 'forge-core'
-
-import { useMutation, useQuery, useSubscription } from '@apollo/client'
-
-import { useNavigate, useParams } from 'react-router-dom'
-
-import { Box, Layer, Text, TextInput } from 'grommet'
-
-import moment from 'moment'
-
-import { Editable, Slate } from 'slate-react'
-
-import { useSetBreadcrumbs } from '@pluralsh/design-system'
-
-import { CurrentUserContext } from '../login/CurrentUser'
-
-import { dateFormat } from '../../utils/date'
-
-import SmoothScroller from '../utils/SmoothScroller'
-
-import { extendConnection } from '../../utils/graphql'
-
-import { plainDeserialize, plainSerialize } from '../../utils/slate'
-
-import { useEditor } from '../utils/hooks'
-
-import { TagInput } from '../repos/Tags'
-
-import Avatar from '../utils/Avatar'
-
-import Markdown from './Markdown'
-import {
- DELETE_INCIDENT,
- INCIDENT_Q,
- INCIDENT_SUB,
- MESSAGE_SUB,
- UPDATE_INCIDENT,
-} from './queries'
-import { Severity } from './Severity'
-import { Status } from './IncidentStatus'
-import { MessageInput, MessageScrollContext } from './MessageInput'
-
-import { Message } from './Message'
-
-import { AttachmentProvider, Dropzone } from './AttachmentProvider'
-import { IncidentView } from './types'
-import { FileEntry } from './File'
-import { ViewSwitcher } from './ViewSwitcher'
-import { Sidebar } from './Sidebar'
-import { IncidentControls } from './IncidentControls'
-
-import { Postmortem } from './Postmortem'
-import { applyMessages } from './applicators'
-import { LastMessage } from './LastMessage'
-import { PresenceProvider } from './Presence'
-import { SlaTimer } from './SlaTimer'
-
-export const canEdit = ({ creator, owner }, { id }) =>
- (creator || {}).id === id || (owner || {}).id === id
-
-function EditButton({ editing, setEditing }) {
- return (
- setEditing(!editing)}
- >
- {editing ? (
-
- ) : (
-
- )}
-
- )
-}
-
-function Empty() {
- return (
-
-
- Get the conversation started
-
- )
-}
-
-function DeleteIncident({ incident }) {
- const [mutation, { loading }] = useMutation(DELETE_INCIDENT, {
- variables: { id: incident.id },
- })
-
- return (
-
- )
-}
-
-function IncidentHeader({
- incident,
- editable,
- editing,
- setEditing,
- mutation,
- attributes,
- setAttributes,
- updating,
-}) {
- const [editorState, setEditorState] = useState(
- plainDeserialize(incident.description || '')
- )
- const editor = useEditor()
- const setDescription = useCallback(
- (editorState) => {
- setEditorState(editorState)
- setAttributes({ ...attributes, description: plainSerialize(editorState) })
- },
- [setAttributes, attributes, setEditorState]
- )
-
- return (
-
-
-
-
- {incident.creator.name}
-
-
- created on {dateFormat(moment(incident.insertedAt))}
-
-
- {!editing && }
- {editing && (
-
-
- )}
- {editable && (
-
- )}
-
- {!editing && (
-
-
-
-
- {incident.tags.length > 0 && (
-
- {incident.tags.map(({ tag }) => (
-
- {tag}
-
- ))}
-
- )}
-
- )}
- {editing && (
-
-
-
-
-
-
-
-
- setAttributes({
- ...attributes,
- tags: [tag, ...(attributes.tags || [])],
- })
- }
- removeTag={(tag) =>
- setAttributes({
- ...attributes,
- tags: attributes.tags.filter((t) => t !== tag),
- })
- }
- />
-
-
- )}
-
- )
-}
-
-export function Messages({ incident, loading, fetchMore, subscribeToMore }) {
- const { setListRef, listRef } = useContext(MessageScrollContext)
- const {
- messages: {
- pageInfo: { hasNextPage, endCursor },
- edges,
- },
- } = incident
-
- useEffect(
- () =>
- subscribeToMore({
- document: MESSAGE_SUB,
- variables: { id: incident.id },
- updateQuery: (prev, { subscriptionData: { data } }) =>
- applyMessages(prev, data),
- }),
- [incident.id]
- )
-
- if (edges.length === 0) return
-
- return (
-
- e === 'end' ? (
-
- ) : (
-
- )
- }
- loading={loading}
- loadNextPage={() =>
- hasNextPage &&
- fetchMore({
- variables: { cursor: endCursor },
- updateQuery: (
- prev,
- {
- fetchMoreResult: {
- incident: { messages },
- },
- }
- ) => ({
- ...prev,
- incident: extendConnection(prev.incident, messages, 'messages'),
- }),
- })
- }
- hasNextPage={hasNextPage}
- />
- )
-}
-
-function NoFiles() {
- return (
-
-
- No files uploaded yet
-
- )
-}
-
-function Files({ incident, fetchMore }) {
- const {
- files: {
- pageInfo: { hasNextPage, endCursor },
- edges,
- },
- } = incident
-
- return (
-
- }
- mapper={({ node }, { node: next }) => (
-
- )}
- onLoadMore={() =>
- hasNextPage &&
- fetchMore({
- variables: { fileCursor: endCursor },
- updateQuery: (
- prev,
- {
- fetchMoreResult: {
- incident: { files },
- },
- }
- ) => ({
- ...prev,
- incident: extendConnection(prev.incident, files, 'files'),
- }),
- })
- }
- />
-
- )
-}
-
-function IncidentOwner({ incident: { owner } }) {
- return (
-
- Owner:
-
-
- {owner.email}
-
-
- )
-}
-
-const canDelete = (incident, me) =>
- me.id === incident.creator.id ||
- (incident.owner && incident.owner.id === me.id)
-
-function IncidentInner({
- incident,
- fetchMore,
- subscribeToMore,
- loading,
- editing,
- setEditing,
-}) {
- const [view, setView] = useState(IncidentView.MSGS)
- const [listRef, setListRef] = useState(null)
- const currentUser = useContext(CurrentUserContext)
- const editable = canEdit(incident, currentUser)
- const [attributes, setAttributes] = useState({
- description: incident.description,
- title: incident.title,
- tags: incident.tags.map(({ tag }) => tag),
- })
- const [mutation, { loading: updating }] = useMutation(UPDATE_INCIDENT, {
- variables: {
- id: incident.id,
- attributes: {
- ...attributes,
- tags: attributes.tags.map((tag) => ({ tag })),
- },
- },
- onCompleted: () => setEditing(false),
- })
-
- const refreshList = useCallback(() => {
- if (listRef) listRef.resetAfterIndex(0, true)
- }, [listRef])
-
- const returnToBeginning = useCallback(() => {
- listRef.scrollToItem(0)
- }, [listRef])
-
- return (
- // eslint-disable-next-line react/jsx-no-constructed-context-values
-
-
-
-
-
- mutation({ variables: { attributes: { severity } } })
- }
- />
- {!editing && (
-
- {incident.title}
-
- )}
- {editing && (
-
-
- setAttributes({ ...attributes, title: value })
- }
- />
-
- )}
- {incident.owner && }
- {incident.nextResponseAt && }
-
- mutation({ variables: { attributes: { status } } })
- }
- />
- {canDelete(incident, currentUser) && (
-
- )}
-
-
-
-
-
-
-
-
-
- {view === IncidentView.POST && (
-
- )}
- {view === IncidentView.FILES && (
-
- )}
- {view === IncidentView.MSGS && (
-
-
-
- )}
-
-
-
-
-
-
-
-
-
- )
-}
-
-export function Incident({ editing }) {
- const navigate = useNavigate()
- const [deleted, setDeleted] = useState(false)
- const { incidentId } = useParams()
- const [edit, setEdit] = useState(editing)
- const { data, loading, fetchMore, subscribeToMore } = useQuery(INCIDENT_Q, {
- variables: { id: incidentId },
- fetchPolicy: 'cache-and-network',
- })
-
- useSubscription(INCIDENT_SUB, {
- variables: { id: incidentId },
- onSubscriptionData: ({
- subscriptionData: {
- data: {
- incidentDelta: { delta },
- },
- },
- }) => delta === 'DELETE' && setDeleted(true),
- })
-
- const breadcrumbs = useMemo(
- () => [
- { url: '/incidents', label: 'incidents' },
- { url: `/incidents/${incidentId}`, label: incidentId },
- ],
- [incidentId]
- )
-
- useSetBreadcrumbs(breadcrumbs)
-
- if (!data) return null
-
- return (
-
- {deleted && (
-
-
-
-
- This incident was deleted
-
-
-
-
-
- )}
-
-
- )
-}
diff --git a/assets/src/components/incidents/IncidentControls.jsx b/assets/src/components/incidents/IncidentControls.jsx
deleted file mode 100644
index a563772e36..0000000000
--- a/assets/src/components/incidents/IncidentControls.jsx
+++ /dev/null
@@ -1,308 +0,0 @@
-import { useCallback, useState } from 'react'
-import { Anchor, Box, Layer, Text } from 'grommet'
-import {
- Button,
- Check as Checkmark,
- ModalHeader,
- Notification,
- Owner,
- SecondaryButton,
-} from 'forge-core'
-import { Zoom } from 'grommet-icons'
-import { useMutation } from '@apollo/client'
-
-import { Editable, Slate } from 'slate-react'
-
-import { plainDeserialize, plainSerialize } from '../../utils/slate'
-
-import { updateCache } from '../../utils/graphql'
-
-import { useEditor } from '../utils/hooks'
-
-import {
- ACCEPT_INCIDENT,
- COMPLETE_INCIDENT,
- FOLLOW,
- INCIDENT_Q,
- UNFOLLOW,
- ZOOM_MEETING,
-} from './queries'
-
-import { IncidentStatus } from './types'
-
-import { NotificationPreferences } from './NotificationPreferences'
-
-import { Attribute, Attributes } from './utils'
-
-function Control({ icon, onClick }) {
- return (
-
- {icon}
-
- )
-}
-
-function AcceptIncident({ incident: { id } }) {
- const [mutation] = useMutation(ACCEPT_INCIDENT, { variables: { id } })
-
- return (
- }
- onClick={mutation}
- />
- )
-}
-
-function CompleteIncident({ incident: { id } }) {
- const [open, setOpen] = useState(false)
- const [attributes, setAttributes] = useState({ content: '' })
- const [editorState, setEditorState] = useState(
- plainDeserialize(attributes.content)
- )
- const editor = useEditor()
- const setContent = useCallback(
- (editorState) => {
- setEditorState(editorState)
- setAttributes({ ...attributes, content: plainSerialize(editorState) })
- },
- [setAttributes, attributes, setEditorState]
- )
- const [mutation, { loading }] = useMutation(COMPLETE_INCIDENT, {
- variables: { id, attributes },
- onCompleted: () => setOpen(false),
- })
-
- return (
- <>
- }
- onClick={() => setOpen(true)}
- />
- {open && (
- setOpen(false)}
- >
-
-
-
-
-
-
-
-
-
- setOpen(false)}
- />
-
-
-
-
-
- )}
- >
- )
-}
-
-const followerPreferences = (follower) => {
- if (!follower) return { message: true, incidentUpdate: true, mention: true }
- const { message, incidentUpdate, mention } = follower.preferences
-
- return { message, incidentUpdate, mention }
-}
-
-function Follower({ incident: { id, follower } }) {
- const [open, setOpen] = useState(false)
- const [preferences, setPreferences] = useState(followerPreferences(follower))
- const [mutation, { loading }] = useMutation(FOLLOW, {
- variables: { id, attributes: { preferences } },
- update: (cache, { data: { followIncident } }) =>
- updateCache(cache, {
- query: INCIDENT_Q,
- variables: { id },
- update: ({ incident, ...prev }) => ({
- ...prev,
- incident: { ...incident, follower: followIncident },
- }),
- }),
- })
- const [unfollow, { loading: unfollowing }] = useMutation(UNFOLLOW, {
- variables: { id },
- update: (cache) =>
- updateCache(cache, {
- query: INCIDENT_Q,
- variables: { id },
- update: ({ incident, ...prev }) => ({
- ...prev,
- incident: { ...incident, follower: null },
- }),
- }),
- onCompleted: () => setOpen(false),
- })
-
- return (
- <>
- }
- onClick={() => setOpen(true)}
- />
- {open && (
- setOpen(false)}
- >
-
-
-
-
-
- {follower && (
-
- )}
-
-
-
-
-
- )}
- >
- )
-}
-
-function ZoomMeeting({ incident: { id, title } }) {
- const [open, setOpen] = useState(true)
- const [mutation, { data }] = useMutation(ZOOM_MEETING, {
- variables: { attributes: { incidentId: id, topic: title } },
- onCompleted: () => setOpen(true),
- })
-
- const meeting = data && data.createZoom
-
- return (
- <>
-
-
-
- {meeting && open && (
- setOpen(false)}
- >
-
-
-
-
-
-
-
-
- {meeting.password}
-
-
-
-
-
- )}
- >
- )
-}
-
-export function IncidentControls({ incident }) {
- return (
-
-
- {incident.status === IncidentStatus.RESOLVED && (
-
- )}
- {!incident.owner && }
-
-
- )
-}
diff --git a/assets/src/components/incidents/IncidentHistory.jsx b/assets/src/components/incidents/IncidentHistory.jsx
deleted file mode 100644
index 7725ba21ec..0000000000
--- a/assets/src/components/incidents/IncidentHistory.jsx
+++ /dev/null
@@ -1,184 +0,0 @@
-import { useMemo, useState } from 'react'
-import { Box, Collapsible, Text } from 'grommet'
-
-import moment from 'moment'
-import { Down, Next } from 'grommet-icons'
-import { sortBy } from 'lodash'
-import yaml from 'js-yaml'
-import ReactDiffViewer from 'react-diff-viewer'
-
-import Avatar from '../utils/Avatar'
-import { dateFormat } from '../../utils/date'
-import { extendConnection } from '../../utils/graphql'
-
-import { Action } from './types'
-
-function historyModifier(action) {
- switch (action) {
- case Action.CREATE:
- return 'created'
- case Action.EDIT:
- return 'updated'
- case Action.ACCEPT:
- return 'accepted'
- case Action.COMPLETE:
- return 'closed'
- case Action.SEVERITY:
- return 'updated severity'
- case Action.STATUS:
- return 'updated status'
- default:
- return ''
- }
-}
-
-const yamlDump = (val) => yaml.safeDump(val || {}, null, 2)
-
-function HistoryChanges({ changes }) {
- const { previous, next } = useMemo(() => {
- const sorted = sortBy(changes, ['key'])
- const prev = yamlDump(
- sorted.reduce((acc, { key, prev }) => ({ ...acc, [key]: prev }), {})
- )
- const next = yamlDump(
- sorted.reduce((acc, { key, next }) => ({ ...acc, [key]: next }), {})
- )
-
- return {
- previous: `...\n${prev}\n...`,
- next: `...\n${next}\n...`,
- }
- })
-
- return (
-
-
-
- )
-}
-
-function HistoryItem({ history: { action, actor, insertedAt, changes } }) {
- const [open, setOpen] = useState(false)
- const openable = action !== Action.CREATE
-
- return (
- <>
-
-
-
-
- {dateFormat(moment(insertedAt))}
-
-
-
- {historyModifier(action)}
-
-
-
- {openable && (
- setOpen(!open)}
- >
- {open ? : }
-
- )}
-
-
-
-
- >
- )
-}
-
-export function IncidentHistory({
- incident: {
- history: { edges, pageInfo },
- },
- fetchMore,
-}) {
- return (
-
- {edges.map(({ node }) => (
-
- ))}
- {pageInfo.hasNextPage && (
-
- fetchMore({
- variables: { historyCursor: pageInfo.endCursor },
- updateQuery: (
- prev,
- {
- fetchMoreResult: {
- incident: { history },
- },
- }
- ) => ({
- ...prev,
- incident: {
- ...prev.incident,
- history: extendConnection(
- prev.incident.history,
- history,
- 'history'
- ),
- },
- }),
- })
- }
- >
- load more...
-
- )}
-
- )
-}
diff --git a/assets/src/components/incidents/IncidentStatus.jsx b/assets/src/components/incidents/IncidentStatus.jsx
deleted file mode 100644
index aaf00ba564..0000000000
--- a/assets/src/components/incidents/IncidentStatus.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { useContext } from 'react'
-import { Box, Text } from 'grommet'
-import { Select } from 'forge-core'
-
-import { ThemeContext } from 'styled-components'
-
-import { normalizeColor } from 'grommet/utils'
-
-import { CurrentUserContext } from '../login/CurrentUser'
-
-import { IncidentStatus, StatusColorMap } from './types'
-import { canEdit } from './Incident'
-
-const normalize = (val) => val.replace('_', ' ')
-
-const textColor = (status) => StatusColorMap[status]
-
-const options = Object.keys(IncidentStatus).map((status) => ({
- value: status,
- label: normalize(status),
-}))
-
-export function StatusSelector({ status, setStatus }) {
- const theme = useContext(ThemeContext)
-
- return (
-
-
- >
+
+
)
}
diff --git a/assets/src/components/layout/NotificationsPanelOverlay.tsx b/assets/src/components/layout/NotificationsPanelOverlay.tsx
index f33b15db39..6567f23b1f 100644
--- a/assets/src/components/layout/NotificationsPanelOverlay.tsx
+++ b/assets/src/components/layout/NotificationsPanelOverlay.tsx
@@ -5,7 +5,6 @@ import { useTransition } from 'react-spring'
import styled from 'styled-components'
-import { useContentOverlay } from './Overlay'
import { NotificationsPanel } from './NotificationsPanel'
const PANEL_WIDTH = 480
@@ -60,8 +59,6 @@ export function NotificationsPanelOverlay({
const notificationsPanelRef = useRef