-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into tom/trends-cohort-breakdowns
- Loading branch information
Showing
74 changed files
with
1,302 additions
and
586 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+261 Bytes
(100%)
frontend/__snapshots__/scenes-app-sidepanels--side-panel-settings--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+87 Bytes
(100%)
frontend/__snapshots__/scenes-app-sidepanels--side-panel-settings--light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelStatus.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { IconCloud, IconExternal } from '@posthog/icons' | ||
import { LemonButton, Tooltip } from '@posthog/lemon-ui' | ||
import clsx from 'clsx' | ||
import { useActions, useValues } from 'kea' | ||
import { IconWithBadge } from 'lib/lemon-ui/icons' | ||
import { useState } from 'react' | ||
|
||
import { SidePanelPaneHeader } from '../components/SidePanelPaneHeader' | ||
import { sidePanelLogic } from '../sidePanelLogic' | ||
import { SidePanelDocsSkeleton } from './SidePanelDocs' | ||
import { sidePanelStatusLogic, STATUS_PAGE_BASE } from './sidePanelStatusLogic' | ||
|
||
export const SidePanelStatusIcon = (props: { className?: string }): JSX.Element => { | ||
const { status, statusPage } = useValues(sidePanelStatusLogic) | ||
|
||
let title = statusPage?.status.description | ||
if (statusPage?.status.description === 'All Systems Operational') { | ||
title = 'All systems operational' // Sentence-case Statuspage.io default message, which can't be changed | ||
} | ||
|
||
return ( | ||
<Tooltip title={title} placement="left"> | ||
<span {...props}> | ||
<IconWithBadge | ||
content={status !== 'operational' ? '!' : '✓'} | ||
status={status.includes('outage') ? 'danger' : status.includes('degraded') ? 'warning' : 'success'} | ||
> | ||
<IconCloud /> | ||
</IconWithBadge> | ||
</span> | ||
</Tooltip> | ||
) | ||
} | ||
|
||
export const SidePanelStatus = (): JSX.Element => { | ||
const { closeSidePanel } = useActions(sidePanelLogic) | ||
const [ready, setReady] = useState(false) | ||
|
||
return ( | ||
<> | ||
<SidePanelPaneHeader> | ||
<div className="flex-1" /> | ||
<LemonButton | ||
size="small" | ||
sideIcon={<IconExternal />} | ||
targetBlank | ||
// We can't use the normal `to` property as that is intercepted to open this panel :D | ||
onClick={() => { | ||
window.open(STATUS_PAGE_BASE, '_blank')?.focus() | ||
closeSidePanel() | ||
}} | ||
> | ||
Open in new tab | ||
</LemonButton> | ||
</SidePanelPaneHeader> | ||
<div className="relative flex-1 overflow-hidden"> | ||
<iframe | ||
src={STATUS_PAGE_BASE} | ||
title="Status" | ||
className={clsx('w-full h-full', !ready && 'hidden')} | ||
onLoad={() => setReady(true)} | ||
/> | ||
|
||
{!ready && <SidePanelDocsSkeleton />} | ||
</div> | ||
</> | ||
) | ||
} |
159 changes: 159 additions & 0 deletions
159
frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelStatusLogic.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers } from 'kea' | ||
import { loaders } from 'kea-loaders' | ||
import { FEATURE_FLAGS } from 'lib/constants' | ||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic' | ||
|
||
import { sidePanelStateLogic } from '../sidePanelStateLogic' | ||
import type { sidePanelStatusLogicType } from './sidePanelStatusLogicType' | ||
|
||
export type SPIndicator = 'none' | 'minor' | 'major' | ||
|
||
export type SPComponentStatus = 'operational' | 'degraded_performance' | 'partial_outage' | 'major_outage' | ||
|
||
export interface SPSummary { | ||
// page: SPPage | ||
components: SPComponent[] | ||
incidents: SPIncident[] | ||
scheduled_maintenances: any[] | ||
status: SPStatus | ||
} | ||
|
||
export interface SPComponent { | ||
id: string | ||
name: string | ||
status: SPComponentStatus | ||
created_at: Date | ||
updated_at: Date | ||
position: number | ||
description: null | string | ||
showcase: boolean | ||
start_date: Date | null | ||
group_id: null | string | ||
page_id: string | ||
group: boolean | ||
only_show_if_degraded: boolean | ||
components?: string[] | ||
} | ||
|
||
export interface SPIncident { | ||
id: string | ||
name: string | ||
status: string | ||
created_at: Date | ||
updated_at: Date | ||
monitoring_at: null | ||
resolved_at: null | ||
impact: string | ||
shortlink: string | ||
started_at: Date | ||
page_id: string | ||
incident_updates: SPIncidentUpdate[] | ||
components: SPComponent[] | ||
reminder_intervals: string | ||
} | ||
|
||
export interface SPIncidentUpdate { | ||
id: string | ||
status: string | ||
body: string | ||
incident_id: string | ||
created_at: Date | ||
updated_at: Date | ||
display_at: Date | ||
affected_components: SPAffectedComponent[] | ||
deliver_notifications: boolean | ||
custom_tweet: null | ||
tweet_id: null | ||
} | ||
|
||
export interface SPAffectedComponent { | ||
code: string | ||
name: string | ||
old_status: string | ||
new_status: string | ||
} | ||
|
||
export interface SPStatus { | ||
indicator: SPIndicator | ||
description: string | ||
} | ||
|
||
export const STATUS_PAGE_BASE = 'https://status.posthog.com' | ||
|
||
// NOTE: Test account with some incidents - ask @benjackwhite for access | ||
// export const STATUS_PAGE_BASE = 'https://posthogtesting.statuspage.io' | ||
|
||
// Map the hostname to relevant groups (found via the summary.json endpoint) | ||
const RELEVANT_GROUPS_MAP = { | ||
'us.posthog.com': ['41df083ftqt6', 'z0y6m9kyvy3j'], | ||
'eu.posthog.com': ['c4d9jd1jcx3f', 'nfknrn2bf3yz'], | ||
localhost: ['f58xx1143yvt', 't3rdjq2z0x7p'], // localhost has IDs for the test status page - that way we really only show it if local dev and overridden to use the other status page | ||
} | ||
|
||
export const REFRESH_INTERVAL = 60 * 1000 * 5 // 5 minutes | ||
|
||
export const sidePanelStatusLogic = kea<sidePanelStatusLogicType>([ | ||
path(['scenes', 'navigation', 'sidepanel', 'sidePanelStatusLogic']), | ||
connect({ | ||
values: [featureFlagLogic, ['featureFlags']], | ||
actions: [sidePanelStateLogic, ['openSidePanel', 'closeSidePanel']], | ||
}), | ||
|
||
actions({ | ||
loadStatusPage: true, | ||
}), | ||
|
||
reducers(() => ({ | ||
// Persisted copy to avoid flash effect on page load | ||
status: [ | ||
'operational' as SPComponentStatus, | ||
{ persist: true }, | ||
{ | ||
loadStatusPageSuccess: (_, { statusPage }) => { | ||
const relevantGroups = RELEVANT_GROUPS_MAP[window.location.hostname] | ||
if (!relevantGroups) { | ||
return 'operational' | ||
} | ||
|
||
return ( | ||
statusPage.components.find( | ||
({ group_id, status }) => relevantGroups.includes(group_id) && status !== 'operational' | ||
)?.status || 'operational' | ||
) | ||
}, | ||
loadStatusPageFailure: () => 'operational', | ||
}, | ||
], | ||
})), | ||
|
||
loaders(() => ({ | ||
statusPage: [ | ||
null as SPSummary | null, | ||
{ | ||
loadStatusPage: async () => { | ||
const response = await fetch(`${STATUS_PAGE_BASE}/api/v2/summary.json`) | ||
const data: SPSummary = await response.json() | ||
|
||
return data | ||
}, | ||
}, | ||
], | ||
})), | ||
|
||
listeners(({ actions, cache }) => ({ | ||
loadStatusPageSuccess: () => { | ||
clearTimeout(cache.timeout) | ||
cache.timeout = setTimeout(() => actions.loadStatusPage(), REFRESH_INTERVAL) | ||
}, | ||
})), | ||
|
||
afterMount(({ actions, values }) => { | ||
if (values.featureFlags[FEATURE_FLAGS.SIDEPANEL_STATUS]) { | ||
actions.loadStatusPage() | ||
} | ||
}), | ||
|
||
beforeUnmount(({ cache }) => { | ||
clearTimeout(cache.timeout) | ||
}), | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.