Skip to content

Commit

Permalink
Merge branch 'master' into dw-data-warehouse-person-properties-2
Browse files Browse the repository at this point in the history
  • Loading branch information
EDsCODE authored Apr 2, 2024
2 parents 9dc5f30 + d790ade commit bbf2857
Show file tree
Hide file tree
Showing 183 changed files with 3,774 additions and 1,357 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/ci-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ jobs:
if: needs.changes.outputs.backend == 'true'
timeout-minutes: 10

name: Validate Django migrations
name: Validate Django and CH migrations
runs-on: depot-ubuntu-latest-4

steps:
Expand Down Expand Up @@ -232,6 +232,11 @@ jobs:
# run on initial setup where there is no data.
git diff --name-status origin/master..HEAD | grep "A\sposthog/migrations/" | awk '{print $2}' | grep -v migrations/0001_ | python manage.py test_migrations_are_safe
- name: Check CH migrations
run: |
# Same as above, except now for CH looking at files that were added in posthog/clickhouse/migrations/
git diff --name-status origin/master..HEAD | grep "A\sposthog/clickhouse/migrations/" | awk '{print $2}' | python manage.py test_ch_migrations_are_safe
django:
needs: changes
timeout-minutes: 30
Expand Down
50 changes: 50 additions & 0 deletions cypress/e2e/signup.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,56 @@ describe('Signup', () => {
cy.location('pathname').should('match', /\/verify_email\/[a-zA-Z0-9_.-]*/)
})

it('Can submit the signup form multiple times if there is a generic email set', () => {
cy.intercept('POST', '/api/signup/').as('signupRequest')

// Create initial account
const email = `new_user+generic_error_test@posthog.com`
cy.get('[data-attr=signup-email]').type(email).should('have.value', email)
cy.get('[data-attr=password]').type('12345678').should('have.value', '12345678')
cy.get('[data-attr=signup-start]').click()
cy.get('[data-attr=signup-name]').type('Alice Bob').should('have.value', 'Alice Bob')
cy.get('[data-attr=signup-submit]').click()

cy.wait('@signupRequest').then((interception) => {
expect(interception.request.body).to.have.property('first_name')
expect(interception.request.body.first_name).to.equal('Alice')
expect(interception.request.body).to.have.property('last_name')
expect(interception.request.body.last_name).to.equal('Bob')
})

cy.visit('/signup')

// Try to recreate account with same email- should fail
cy.get('[data-attr=signup-email]').type(email).should('have.value', email)
cy.get('[data-attr=password]').type('12345678').should('have.value', '12345678')
cy.get('[data-attr=signup-start]').click()
cy.get('[data-attr=signup-name]').type('Alice Bob').should('have.value', 'Alice Bob')
cy.get('[data-attr=signup-submit]').click()

cy.wait('@signupRequest').then((interception) => {
cy.get('.LemonBanner').should('contain', 'There is already an account with this email address.')
})

cy.get('[data-attr=signup-go-back]').click()

// Update email to generic email
const newEmail = `new_user+${Math.floor(Math.random() * 10000)}@posthog.com`
cy.get('[data-attr=signup-email]').clear().type(newEmail).should('have.value', newEmail)
cy.get('[data-attr=signup-start]').click()
cy.get('[data-attr=signup-submit]').click()

cy.wait('@signupRequest').then((interception) => {
expect(interception.request.body).to.have.property('first_name')
expect(interception.request.body.first_name).to.equal('Alice')
expect(interception.request.body).to.have.property('last_name')
expect(interception.request.body.last_name).to.equal('Bob')
})

// lazy regex for a guid
cy.location('pathname').should('match', /\/verify_email\/[a-zA-Z0-9_.-]*/)
})

it('Can create user account with just a first name', () => {
cy.intercept('POST', '/api/signup/').as('signupRequest')

Expand Down
2 changes: 1 addition & 1 deletion ee/session_recordings/ai/error_clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def construct_response(df: pd.DataFrame, team: Team):
sample = rows.sample(n=1)[["session_id", "error"]].to_dict("records")[0]

date_series = (
df.groupby([df["timestamp"].dt.date])
rows.groupby([rows["timestamp"].dt.date])
.size()
.reindex(pd.date_range(end=date.today(), periods=7), fill_value=0)
)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions frontend/src/layout/navigation-3000/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import './Navigation.scss'

import clsx from 'clsx'
import { useMountedLogic, useValues } from 'kea'
import { useValues } from 'kea'
import { BillingAlertsV2 } from 'lib/components/BillingAlertsV2'
import { CommandBar } from 'lib/components/CommandBar/CommandBar'
import { FlaggedFeature } from 'lib/components/FlaggedFeature'
Expand All @@ -27,21 +27,23 @@ export function Navigation({
children: ReactNode
sceneConfig: SceneConfig | null
}): JSX.Element {
useMountedLogic(themeLogic)
const { theme } = useValues(themeLogic)
const { mobileLayout } = useValues(navigationLogic)
const { activeNavbarItem, mode } = useValues(navigation3000Logic)

if (mode !== 'full') {
return (
<div className="Navigation3000 flex-col">
// eslint-disable-next-line react/forbid-dom-props
<div className="Navigation3000 flex-col" style={theme?.mainStyle}>
{mode === 'minimal' ? <MinimalNavigation /> : null}
<main>{children}</main>
</div>
)
}

return (
<div className={clsx('Navigation3000', mobileLayout && 'Navigation3000--mobile')}>
// eslint-disable-next-line react/forbid-dom-props
<div className={clsx('Navigation3000', mobileLayout && 'Navigation3000--mobile')} style={theme?.mainStyle}>
<Navbar />
<FlaggedFeature flag={FEATURE_FLAGS.POSTHOG_3000_NAV}>
{activeNavbarItem && <Sidebar key={activeNavbarItem.identifier} navbarItem={activeNavbarItem} />}
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/layout/navigation-3000/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import { userLogic } from 'scenes/userLogic'

import { navigationLogic } from '~/layout/navigation/navigationLogic'
import { AccountPopoverOverlay } from '~/layout/navigation/TopBar/AccountPopover'
import { themeLogic } from '~/layout/navigation-3000/themeLogic'

import { navigation3000Logic } from '../navigationLogic'
import { KeyboardShortcut } from './KeyboardShortcut'
import { NavbarButton } from './NavbarButton'

export function Navbar(): JSX.Element {
const { theme } = useValues(themeLogic)
const { user } = useValues(userLogic)
const { isAccountPopoverOpen, systemStatusHealthy } = useValues(navigationLogic)
const { closeAccountPopover, toggleAccountPopover } = useActions(navigationLogic)
Expand All @@ -34,7 +36,11 @@ export function Navbar(): JSX.Element {
return (
<>
<nav className={clsx('Navbar3000', !isNavShown && 'Navbar3000--hidden')} ref={containerRef}>
<div className="Navbar3000__content">
<div
className="Navbar3000__content"
// eslint-disable-next-line react/forbid-dom-props
style={theme?.sidebarStyle}
>
<ScrollableShadows innerClassName="Navbar3000__top" direction="vertical">
{navbarItems.map((section, index) => (
<ul key={index}>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
SidePanelExports,
SidePanelExportsIcon,
} from '~/layout/navigation-3000/sidepanel/panels/exports/SidePanelExports'
import { themeLogic } from '~/layout/navigation-3000/themeLogic'
import { SidePanelTab } from '~/types'

import { SidePanelActivation, SidePanelActivationIcon } from './panels/activation/SidePanelActivation'
Expand Down Expand Up @@ -91,6 +92,7 @@ export const SIDE_PANEL_TABS: Record<
const DEFAULT_WIDTH = 512

export function SidePanel(): JSX.Element | null {
const { theme } = useValues(themeLogic)
const { visibleTabs, extraTabs } = useValues(sidePanelLogic)
const { selectedTab, sidePanelOpen, modalMode } = useValues(sidePanelStateLogic)
const { openSidePanel, closeSidePanel, setSidePanelAvailable } = useActions(sidePanelStateLogic)
Expand Down Expand Up @@ -170,6 +172,7 @@ export function SidePanel(): JSX.Element | null {
// eslint-disable-next-line react/forbid-dom-props
style={{
width: sidePanelOpenAndAvailable ? desiredWidth ?? DEFAULT_WIDTH : undefined,
...(theme?.sidebarStyle ?? {}),
}}
>
<Resizer {...resizerLogicProps} />
Expand Down
31 changes: 28 additions & 3 deletions frontend/src/layout/navigation-3000/themeLogic.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { actions, connect, events, kea, path, reducers, selectors } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { sceneLogic } from 'scenes/sceneLogic'
import { userLogic } from 'scenes/userLogic'

import type { themeLogicType } from './themeLogicType'
import { Theme, themes } from './themes'

export const themeLogic = kea<themeLogicType>([
path(['layout', 'navigation-3000', 'themeLogic']),
connect({
values: [userLogic, ['themeMode']],
values: [userLogic, ['themeMode'], featureFlagLogic, ['featureFlags']],
}),
actions({
syncDarkModePreference: (darkModePreference: boolean) => ({ darkModePreference }),
setTheme: (theme: string | null) => ({ theme }),
}),
reducers({
darkModeSystemPreference: [
Expand All @@ -19,11 +23,32 @@ export const themeLogic = kea<themeLogicType>([
syncDarkModePreference: (_, { darkModePreference }) => darkModePreference,
},
],
selectedTheme: [
null as string | null,
{ persist: true },
{
setTheme: (_, { theme }) => theme,
},
],
}),
selectors({
theme: [
(s) => [s.selectedTheme, s.featureFlags],
(selectedTheme, featureFlags): Theme | null => {
const flagVariant = featureFlags[FEATURE_FLAGS.THEME]
return (
(selectedTheme ? themes.find((theme) => theme.id === selectedTheme) : null) ??
(typeof flagVariant === 'string' ? themes.find((theme) => theme.id === flagVariant) : null) ??
null
)
},
],
isDarkModeOn: [
(s) => [s.themeMode, s.darkModeSystemPreference, sceneLogic.selectors.sceneConfig],
(themeMode, darkModeSystemPreference, sceneConfig) => {
(s) => [s.themeMode, s.darkModeSystemPreference, sceneLogic.selectors.sceneConfig, s.theme],
(themeMode, darkModeSystemPreference, sceneConfig, theme) => {
if (theme) {
return !!theme?.dark
}
// NOTE: Unauthenticated users always get the light mode until we have full support across onboarding flows
if (
sceneConfig?.layout === 'plain' ||
Expand Down
74 changes: 74 additions & 0 deletions frontend/src/layout/navigation-3000/themes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
export interface Theme {
id: string
name: string
dark?: boolean
sidebarStyle?: React.CSSProperties
mainStyle?: React.CSSProperties
boxStyle?: React.CSSProperties
}

export const themes: Theme[] = [
{
id: 'bleachedTom',
name: 'Hi, My name is Tom, and I am the light.',
dark: false,
sidebarStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)),url(https://www.shutterstock.com/image-photo/soft-wave-blue-ocean-on-600nw-396969259.jpg)',
},
mainStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)),url(https://pbs.twimg.com/profile_images/1237550450/mstom_400x400.jpg)',
},
boxStyle: {
background: 'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7))',
},
},
{
id: 'developers',
name: 'Developers, Developers, Developers, Developers',
dark: false,
sidebarStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)),url(https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExOWM2eDhveXRrNTJrdGZ5bmdhaGJrZWNqczFiZzUzMXF5aXc5azljNSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/2yuiXIlW8TwY2raAPB/giphy-downsized-large.gif)',
},
mainStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)),url(https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExa3ljdm5mczV3dnQza3lqY3E1czEyd3J0d3A4ZmtqbGE3a2JybTJlMyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l3q2zbskZp2j8wniE/giphy-downsized-large.gif)',
},
boxStyle: {
background: 'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7))',
},
},
{
id: 'livelaughhog',
name: 'Live, Laugh, Hog',
dark: false,
sidebarStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)), repeat url(https://i.imgur.com/okHCBbPl.png)',
},
mainStyle: {
background:
'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7)), repeat url(https://i.imgur.com/okHCBbPl.png)',
},
boxStyle: {
background: 'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7))',
},
},
{
id: 'herisson',
name: 'Live, Laugh, Hog',
dark: false,
mainStyle: {
background:
'linear-gradient(rgba(255,255,255,0.5),rgba(255,255,255,0.5)), url(https://i.imgur.com/RrGMCR6.jpeg)',
backgroundSize: 'contain',
backgroundPosition: 'center',
backgroundRepeat: 'repeat',
},
boxStyle: {
background: 'linear-gradient(rgba(255,255,255,0.7),rgba(255,255,255,0.7))',
},
},
]
3 changes: 3 additions & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,9 @@ const api = {
async update(id: PluginConfigTypeNew['id'], data: FormData): Promise<PluginConfigWithPluginInfoNew> {
return await new ApiRequest().pluginConfig(id).update({ data })
},
async create(data: FormData): Promise<PluginConfigWithPluginInfoNew> {
return await new ApiRequest().pluginConfigs().create({ data })
},
async list(): Promise<PaginatedResponse<PluginConfigTypeNew>> {
return await new ApiRequest().pluginConfigs().get()
},
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lib/components/Cards/InsightCard/InsightCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Paths } from 'scenes/paths/Paths'
import { RetentionContainer } from 'scenes/retention/RetentionContainer'
import { ActionsHorizontalBar, ActionsLineGraph, ActionsPie } from 'scenes/trends/viz'

import { themeLogic } from '~/layout/navigation-3000/themeLogic'
import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic'
import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode'
import { insightVizDataCollectionId, insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz'
Expand Down Expand Up @@ -259,6 +260,7 @@ function InsightCardInternal(
}: InsightCardProps,
ref: React.Ref<HTMLDivElement>
): JSX.Element {
const { theme } = useValues(themeLogic)
const insightLogicProps: InsightLogicProps = {
dashboardItemId: insight.short_id,
dashboardId: dashboardId,
Expand Down Expand Up @@ -298,6 +300,8 @@ function InsightCardInternal(
className={clsx('InsightCard border', highlighted && 'InsightCard--highlighted', className)}
data-attr="insight-card"
{...divProps}
// eslint-disable-next-line react/forbid-dom-props
style={{ ...(divProps?.style ?? {}), ...(theme?.boxStyle ?? {}) }}
ref={ref}
>
<BindLogic logic={insightLogic} props={insightLogicProps}>
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/lib/components/CompactList/CompactList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import './CompactList.scss'

import { useValues } from 'kea'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'

import { themeLogic } from '~/layout/navigation-3000/themeLogic'

import { EmptyMessage, EmptyMessageProps } from '../EmptyMessage/EmptyMessage'

interface CompactListProps {
Expand All @@ -23,8 +26,13 @@ export function CompactList({
emptyMessage,
renderRow,
}: CompactListProps): JSX.Element {
const { theme } = useValues(themeLogic)
return (
<div className="CompactList">
<div
className="CompactList"
// eslint-disable-next-line react/forbid-dom-props
style={theme?.boxStyle}
>
<div className="CompactList__header">
<h3 className="px-2 truncate" title={title}>
{title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ export const propertyFilterLogic = kea<propertyFilterLogicType>([

listeners(({ actions, props, values }) => ({
// Only send update if value is set to something
setFilter: async ({ property }, breakpoint) => {
await breakpoint(300)
setFilter: async ({ property }) => {
if (props.sendAllKeyUpdates || property?.value || (property?.key && property.type === 'hogql')) {
actions.update()
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export const FEATURE_FLAGS = {
SUBSCRIBE_FROM_PAYGATE: 'subscribe-from-paygate', // owner: #team-growth
REVERSE_PROXY_ONBOARDING: 'reverse-proxy-onboarding', // owner: @zlwaterfield
SESSION_REPLAY_MOBILE_ONBOARDING: 'session-replay-mobile-onboarding', // owner: #team-replay
THEME: 'theme', // owner: @aprilfools
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
Loading

0 comments on commit bbf2857

Please sign in to comment.