mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 13:39:22 +01:00
Toolbar Date Filter, remove react-hot-loader, fix dashboard date filter URL (#3586)
* dropdown and datepicker z-index
* remove dead code
* pass getPopupContainer to range picker
* make DateFilter's custom time range work in a shadow root
* close after selecting
* show date filter instead of date range
* skip updating url on date filter change in toolbar
* actually filter the dates
* fix flickering (always show map)
* refactor date filter component
* upgrade to webpack 5 and @posthog/react-rrweb-player module
* disconnect dashboardLogic from dateFilterLogic (remove broken url changes, etc)
* add bundle tracker
* remove react-hot-loader
* Revert "add bundle tracker"
This reverts commit e59bcfa8
* remove bundle tracker
* remove updatePath
* fix code quality complaints
* revert @cypress/webpack-preprocessor version
* try different cypress webpack plugin
* add crypto-browserify
* add crypto-browserify
* revert to webpack 4
* revert to webpack 4
* revert to webpack 4
* implement feedback and remove debug stuff
* move insights date filter logic out of lib/
* rename DateFilterComponent to DateFilter
* rename DateFilterCustom to DateFilterRange
* Remove hot from new code
This commit is contained in:
parent
37b11ac20b
commit
8c98e4f61d
@ -71,18 +71,18 @@ frontend/src/scenes/actions/ActionsTable.tsx(125,29)
|
||||
frontend/src/scenes/actions/ActionsTable.tsx(136,36)
|
||||
frontend/src/scenes/actions/ActionsTable.tsx(139,29)
|
||||
frontend/src/scenes/actions/ActionsTable.tsx(192,35)
|
||||
frontend/src/scenes/annotations/index.tsx(32,47)
|
||||
frontend/src/scenes/annotations/index.tsx(51,47)
|
||||
frontend/src/scenes/annotations/index.tsx(57,48)
|
||||
frontend/src/scenes/annotations/index.tsx(63,43)
|
||||
frontend/src/scenes/annotations/index.tsx(69,41)
|
||||
frontend/src/scenes/annotations/index.tsx(146,44)
|
||||
frontend/src/scenes/annotations/index.tsx(152,38)
|
||||
frontend/src/scenes/annotations/index.tsx(156,39)
|
||||
frontend/src/scenes/annotations/index.tsx(232,50)
|
||||
frontend/src/scenes/annotations/index.tsx(242,55)
|
||||
frontend/src/scenes/annotations/index.tsx(280,70)
|
||||
frontend/src/scenes/annotations/index.tsx(282,59)
|
||||
frontend/src/scenes/annotations/index.tsx(30,47)
|
||||
frontend/src/scenes/annotations/index.tsx(49,47)
|
||||
frontend/src/scenes/annotations/index.tsx(55,48)
|
||||
frontend/src/scenes/annotations/index.tsx(61,43)
|
||||
frontend/src/scenes/annotations/index.tsx(67,41)
|
||||
frontend/src/scenes/annotations/index.tsx(144,44)
|
||||
frontend/src/scenes/annotations/index.tsx(150,38)
|
||||
frontend/src/scenes/annotations/index.tsx(154,39)
|
||||
frontend/src/scenes/annotations/index.tsx(230,50)
|
||||
frontend/src/scenes/annotations/index.tsx(240,55)
|
||||
frontend/src/scenes/annotations/index.tsx(278,70)
|
||||
frontend/src/scenes/annotations/index.tsx(280,59)
|
||||
frontend/src/scenes/billing/Billing.tsx(6,24)
|
||||
frontend/src/scenes/billing/Billing.tsx(51,24)
|
||||
frontend/src/scenes/billing/Billing.tsx(53,61)
|
||||
@ -129,7 +129,7 @@ frontend/src/scenes/dashboard/DashboardItems.tsx(92,43)
|
||||
frontend/src/scenes/dashboard/Dashboards.tsx(27,42)
|
||||
frontend/src/scenes/dashboard/Dashboards.tsx(27,46)
|
||||
frontend/src/scenes/dashboard/Dashboards.tsx(56,46)
|
||||
frontend/src/scenes/events/Events.tsx(49,22)
|
||||
frontend/src/scenes/events/Events.tsx(47,22)
|
||||
frontend/src/scenes/events/EventsVolumeTable.tsx(53,14)
|
||||
frontend/src/scenes/events/EventsVolumeTable.tsx(55,22)
|
||||
frontend/src/scenes/events/EventsVolumeTable.tsx(63,29)
|
||||
@ -188,7 +188,7 @@ frontend/src/scenes/insights/InsightTabs/TrendTab/TrendTab.tsx(99,32)
|
||||
frontend/src/scenes/insights/InsightTabs/TrendTab/TrendTab.tsx(99,43)
|
||||
frontend/src/scenes/insights/InsightTabs/TrendTab/TrendTab.tsx(121,57)
|
||||
frontend/src/scenes/insights/insightCommandLogic.ts(10,41)
|
||||
frontend/src/scenes/instance/Licenses/index.tsx(63,31)
|
||||
frontend/src/scenes/instance/Licenses/index.tsx(61,31)
|
||||
frontend/src/scenes/instance/Licenses/logic.ts(38,17)
|
||||
frontend/src/scenes/instance/Licenses/logic.ts(43,5)
|
||||
frontend/src/scenes/instance/Licenses/logic.ts(47,71)
|
||||
@ -210,16 +210,12 @@ frontend/src/scenes/persons/Cohort.tsx(80,40)
|
||||
frontend/src/scenes/persons/Cohort.tsx(166,52)
|
||||
frontend/src/scenes/persons/Cohort.tsx(168,36)
|
||||
frontend/src/scenes/persons/CohortGroup.tsx(88,29)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(28,33)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(67,83)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(71,42)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(74,55)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(74,65)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(87,48)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(109,29)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(134,39)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(158,69)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(167,37)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(27,33)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(65,83)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(69,42)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(72,55)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(72,65)
|
||||
frontend/src/scenes/persons/Cohorts.tsx(85,48)
|
||||
frontend/src/scenes/persons/MergePerson.tsx(56,56)
|
||||
frontend/src/scenes/plugins/PluginImage.tsx(4,30)
|
||||
frontend/src/scenes/plugins/Repository.tsx(22,56)
|
||||
|
@ -4,7 +4,6 @@ module.exports = {
|
||||
'@babel/plugin-transform-runtime',
|
||||
'@babel/plugin-transform-react-jsx',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'react-hot-loader/babel',
|
||||
['babel-plugin-kea', { path: './frontend/src' }],
|
||||
],
|
||||
presets: ['@babel/preset-env', '@babel/typescript'],
|
||||
|
@ -17,7 +17,6 @@ import { triggerResizeAfterADelay } from 'lib/utils'
|
||||
import { useEscapeKey } from 'lib/hooks/useEscapeKey'
|
||||
import lgLogo from 'public/posthog-logo-white.svg'
|
||||
import smLogo from 'public/icon-white.svg'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import './Navigation.scss'
|
||||
import {
|
||||
IconCohorts,
|
||||
@ -138,8 +137,7 @@ function PinnedDashboards(): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
export const MainNavigation = hot(_MainNavigation)
|
||||
function _MainNavigation(): JSX.Element {
|
||||
export function MainNavigation(): JSX.Element {
|
||||
const { user } = useValues(userLogic)
|
||||
const { currentOrganization } = useValues(organizationLogic)
|
||||
const { menuCollapsed, toolbarModalOpen, pinnedDashboardsVisible } = useValues(navigationLogic)
|
||||
|
@ -22,7 +22,6 @@ import { guardPremiumFeature } from 'scenes/UpgradeModal'
|
||||
import { sceneLogic } from 'scenes/sceneLogic'
|
||||
import { CreateProjectModal } from 'scenes/project/CreateProjectModal'
|
||||
import { CreateOrganizationModal } from 'scenes/organization/CreateOrganizationModal'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { isMobile, platformCommandControlKey } from 'lib/utils'
|
||||
import { commandPaletteLogic } from 'lib/components/CommandPalette/commandPaletteLogic'
|
||||
import { Link } from 'lib/components/Link'
|
||||
@ -71,8 +70,7 @@ export function WhoAmI({ user }: { user: UserType }): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
export const TopNavigation = hot(_TopNavigation)
|
||||
export function _TopNavigation(): JSX.Element {
|
||||
export function TopNavigation(): JSX.Element {
|
||||
const {
|
||||
setMenuCollapsed,
|
||||
setChangelogModalOpen,
|
||||
|
@ -1,18 +1,23 @@
|
||||
import React, { useRef, useEffect, useState } from 'react'
|
||||
import { Select, DatePicker, Button } from 'antd'
|
||||
import { useValues, useActions } from 'kea'
|
||||
import moment from 'moment'
|
||||
import { dateFilterLogic } from './dateFilterLogic'
|
||||
import React, { useState } from 'react'
|
||||
import { Select } from 'antd'
|
||||
import moment, { Moment } from 'moment'
|
||||
import { dateMapping, isDate, dateFilterToText } from 'lib/utils'
|
||||
import { DateFilterRange } from 'lib/components/DateFilter/DateFilterRange'
|
||||
|
||||
interface Props {
|
||||
export interface DateFilterProps {
|
||||
defaultValue: string
|
||||
showCustom?: boolean
|
||||
bordered?: boolean
|
||||
makeLabel?: (key: string) => React.ReactNode
|
||||
style?: React.CSSProperties
|
||||
onChange?: () => void
|
||||
onChange?: (fromDate: string, toDate: string) => void
|
||||
disabled?: boolean
|
||||
getPopupContainer?: (props: any) => HTMLElement
|
||||
}
|
||||
|
||||
interface RawDateFilterProps extends DateFilterProps {
|
||||
dateFrom?: string | Moment
|
||||
dateTo?: string | Moment
|
||||
}
|
||||
|
||||
export function DateFilter({
|
||||
@ -23,12 +28,10 @@ export function DateFilter({
|
||||
disabled,
|
||||
makeLabel,
|
||||
onChange,
|
||||
}: Props): JSX.Element {
|
||||
const {
|
||||
dates: { dateFrom, dateTo },
|
||||
} = useValues(dateFilterLogic)
|
||||
|
||||
const { setDates } = useActions(dateFilterLogic)
|
||||
getPopupContainer,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
}: RawDateFilterProps): JSX.Element {
|
||||
const [rangeDateFrom, setRangeDateFrom] = useState(
|
||||
dateFrom && isDate.test(dateFrom as string) ? moment(dateFrom) : undefined
|
||||
)
|
||||
@ -42,10 +45,7 @@ export function DateFilter({
|
||||
}
|
||||
|
||||
function setDate(fromDate: string, toDate: string): void {
|
||||
setDates(fromDate, toDate)
|
||||
if (onChange) {
|
||||
onChange()
|
||||
}
|
||||
onChange?.(fromDate, toDate)
|
||||
}
|
||||
|
||||
function _onChange(v: string): void {
|
||||
@ -103,10 +103,12 @@ export function DateFilter({
|
||||
dropdownMatchSelectWidth={false}
|
||||
disabled={disabled}
|
||||
optionLabelProp={makeLabel ? 'label' : undefined}
|
||||
getPopupContainer={getPopupContainer}
|
||||
dropdownRender={(menu: React.ReactElement) => {
|
||||
if (dateRangeOpen) {
|
||||
return (
|
||||
<DatePickerDropdown
|
||||
<DateFilterRange
|
||||
getPopupContainer={getPopupContainer}
|
||||
onClick={dropdownOnClick}
|
||||
onDateFromChange={(date) => setRangeDateFrom(date)}
|
||||
onDateToChange={(date) => setRangeDateTo(date)}
|
||||
@ -140,83 +142,3 @@ export function DateFilter({
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
|
||||
function DatePickerDropdown(props: {
|
||||
onClickOutside: () => void
|
||||
onClick: (e: React.MouseEvent) => void
|
||||
onDateFromChange: (date: moment.Moment | undefined) => void
|
||||
onDateToChange: (date: moment.Moment | undefined) => void
|
||||
onApplyClick: () => void
|
||||
rangeDateFrom: string | moment.Moment | undefined
|
||||
rangeDateTo: string | moment.Moment | undefined
|
||||
}): JSX.Element {
|
||||
const dropdownRef = useRef<HTMLDivElement | null>(null)
|
||||
const [calendarOpen, setCalendarOpen] = useState(false)
|
||||
|
||||
const onClickOutside = (event: MouseEvent): void => {
|
||||
if ((!event.target || !dropdownRef.current?.contains(event.target as any)) && !calendarOpen) {
|
||||
props.onClickOutside()
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', onClickOutside)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', onClickOutside)
|
||||
}
|
||||
}, [calendarOpen])
|
||||
|
||||
return (
|
||||
<div ref={dropdownRef}>
|
||||
<a
|
||||
style={{
|
||||
margin: '0 1rem',
|
||||
color: 'rgba(0, 0, 0, 0.2)',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
href="#"
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<
|
||||
</a>
|
||||
<hr style={{ margin: '0.5rem 0' }} />
|
||||
<div style={{ padding: '0 1rem' }}>
|
||||
<label className="secondary">From date</label>
|
||||
<br />
|
||||
<DatePicker.RangePicker
|
||||
defaultValue={[
|
||||
props.rangeDateFrom
|
||||
? moment.isMoment(props.rangeDateFrom)
|
||||
? props.rangeDateFrom
|
||||
: moment(props.rangeDateFrom)
|
||||
: null,
|
||||
props.rangeDateTo
|
||||
? moment.isMoment(props.rangeDateTo)
|
||||
? props.rangeDateTo
|
||||
: moment(props.rangeDateTo)
|
||||
: null,
|
||||
]}
|
||||
onOpenChange={(open) => {
|
||||
setCalendarOpen(open)
|
||||
}}
|
||||
onChange={(dates) => {
|
||||
if (dates && dates.length === 2) {
|
||||
props.onDateFromChange(dates[0] || undefined)
|
||||
props.onDateToChange(dates[1] || undefined)
|
||||
}
|
||||
}}
|
||||
popupStyle={{ zIndex: 999999 }}
|
||||
/>
|
||||
<br />
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!props.rangeDateTo || !props.rangeDateFrom}
|
||||
style={{ marginTop: '1rem', marginBottom: '1rem' }}
|
||||
onClick={props.onApplyClick}
|
||||
>
|
||||
Apply filter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
110
frontend/src/lib/components/DateFilter/DateFilterRange.tsx
Normal file
110
frontend/src/lib/components/DateFilter/DateFilterRange.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import moment from 'moment'
|
||||
import { Button, DatePicker } from 'antd'
|
||||
|
||||
export function DateFilterRange(props: {
|
||||
onClickOutside: () => void
|
||||
onClick: (e: React.MouseEvent) => void
|
||||
onDateFromChange: (date: moment.Moment | undefined) => void
|
||||
onDateToChange: (date: moment.Moment | undefined) => void
|
||||
onApplyClick: () => void
|
||||
rangeDateFrom: string | moment.Moment | undefined
|
||||
rangeDateTo: string | moment.Moment | undefined
|
||||
getPopupContainer?: (props: any) => HTMLElement
|
||||
}): JSX.Element {
|
||||
const dropdownRef = useRef<HTMLDivElement | null>(null)
|
||||
const [calendarOpen, setCalendarOpen] = useState(false)
|
||||
|
||||
const onClickOutside = (event: MouseEvent): void => {
|
||||
const target = (event.composedPath?.()?.[0] || event.target) as HTMLElement
|
||||
|
||||
if (!target) {
|
||||
return
|
||||
}
|
||||
|
||||
const clickInPickerContainer = dropdownRef.current?.contains(target)
|
||||
const clickInDateDropdown = event
|
||||
.composedPath?.()
|
||||
?.find((e) => (e as HTMLElement)?.matches?.('.datefilter-datepicker'))
|
||||
|
||||
if (clickInPickerContainer && calendarOpen && target.tagName !== 'INPUT') {
|
||||
setCalendarOpen(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (!clickInPickerContainer && !clickInDateDropdown) {
|
||||
if (calendarOpen) {
|
||||
setCalendarOpen(false)
|
||||
} else {
|
||||
props.onClickOutside()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('mousedown', onClickOutside)
|
||||
return () => {
|
||||
window.removeEventListener('mousedown', onClickOutside)
|
||||
}
|
||||
}, [calendarOpen])
|
||||
|
||||
return (
|
||||
<div ref={dropdownRef}>
|
||||
<a
|
||||
style={{
|
||||
margin: '0 1rem',
|
||||
color: 'rgba(0, 0, 0, 0.2)',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
href="#"
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<
|
||||
</a>
|
||||
<hr style={{ margin: '0.5rem 0' }} />
|
||||
<div style={{ padding: '0 1rem' }}>
|
||||
<label className="secondary">From date</label>
|
||||
<br />
|
||||
<DatePicker.RangePicker
|
||||
dropdownClassName="datefilter-datepicker"
|
||||
getPopupContainer={props.getPopupContainer}
|
||||
defaultValue={[
|
||||
props.rangeDateFrom
|
||||
? moment.isMoment(props.rangeDateFrom)
|
||||
? props.rangeDateFrom
|
||||
: moment(props.rangeDateFrom)
|
||||
: null,
|
||||
props.rangeDateTo
|
||||
? moment.isMoment(props.rangeDateTo)
|
||||
? props.rangeDateTo
|
||||
: moment(props.rangeDateTo)
|
||||
: null,
|
||||
]}
|
||||
open={calendarOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (open) {
|
||||
setCalendarOpen(open)
|
||||
}
|
||||
}}
|
||||
onChange={(dates) => {
|
||||
if (dates && dates.length === 2) {
|
||||
props.onDateFromChange(dates[0] || undefined)
|
||||
props.onDateToChange(dates[1] || undefined)
|
||||
setCalendarOpen(false)
|
||||
}
|
||||
}}
|
||||
popupStyle={{ zIndex: 999999 }}
|
||||
/>
|
||||
<br />
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!props.rangeDateTo || !props.rangeDateFrom}
|
||||
style={{ marginTop: '1rem', marginBottom: '1rem' }}
|
||||
onClick={props.onApplyClick}
|
||||
>
|
||||
Apply filter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './DateFilter'
|
@ -1,4 +1,3 @@
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Layout } from 'antd'
|
||||
@ -23,8 +22,7 @@ function Toast(): JSX.Element {
|
||||
return <ToastContainer autoClose={8000} transition={Slide} position="top-right" />
|
||||
}
|
||||
|
||||
export const App = hot(_App)
|
||||
function _App(): JSX.Element | null {
|
||||
export function App(): JSX.Element | null {
|
||||
const { user } = useValues(userLogic)
|
||||
const { currentOrganization, currentOrganizationLoading } = useValues(organizationLogic)
|
||||
const { currentTeam, currentTeamLoading } = useValues(teamLogic)
|
||||
|
@ -7,7 +7,6 @@ import { eventsTableLogic } from 'scenes/events/eventsTableLogic'
|
||||
import api from 'lib/api'
|
||||
import { kea } from 'kea'
|
||||
import { Spin } from 'antd'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { EventsTable } from 'scenes/events'
|
||||
|
||||
let actionLogic = kea({
|
||||
@ -64,8 +63,7 @@ let actionLogic = kea({
|
||||
}),
|
||||
})
|
||||
|
||||
export const Action = hot(_Action)
|
||||
function _Action({ id }) {
|
||||
export function Action({ id }) {
|
||||
const fixedFilters = { action_id: id }
|
||||
|
||||
const { push } = useActions(router)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import React, { useState, useEffect, HTMLAttributes } from 'react'
|
||||
import { useValues, useActions } from 'kea'
|
||||
import { Table, Tag, Button, Modal, Input, DatePicker, Row, Spin, Menu, Dropdown } from 'antd'
|
||||
@ -16,8 +15,7 @@ import { AnnotationType } from '~/types'
|
||||
|
||||
const { TextArea } = Input
|
||||
|
||||
export const Annotations = hot(_Annotations)
|
||||
function _Annotations(): JSX.Element {
|
||||
export function Annotations(): JSX.Element {
|
||||
const { annotations, annotationsLoading, next, loadingNext } = useValues(annotationsTableLogic)
|
||||
const { loadAnnotations, updateAnnotation, deleteAnnotation, loadAnnotationsNext, restoreAnnotation } = useActions(
|
||||
annotationsTableLogic
|
||||
|
@ -8,15 +8,13 @@ import { DashboardHeader } from 'scenes/dashboard/DashboardHeader'
|
||||
import { DashboardItems } from 'scenes/dashboard/DashboardItems'
|
||||
import { dashboardsModel } from '~/models/dashboardsModel'
|
||||
import { HedgehogOverlay } from 'lib/components/HedgehogOverlay/HedgehogOverlay'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
shareToken?: string
|
||||
}
|
||||
|
||||
export const Dashboard = hot(_Dashboard)
|
||||
function _Dashboard({ id, shareToken }: Props): JSX.Element {
|
||||
export function Dashboard({ id, shareToken }: Props): JSX.Element {
|
||||
return (
|
||||
<BindLogic logic={dashboardLogic} props={{ id: parseInt(id), shareToken }}>
|
||||
<DashboardView id={id} shareToken={shareToken} />
|
||||
|
@ -25,16 +25,16 @@ import { FullScreen } from 'lib/components/FullScreen'
|
||||
import moment from 'moment'
|
||||
import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
|
||||
import { DashboardType } from '~/types'
|
||||
import { DateFilter } from 'lib/components/DateFilter'
|
||||
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
|
||||
|
||||
export function DashboardHeader(): JSX.Element {
|
||||
const { dashboard, draggingEnabled } = useValues(dashboardLogic)
|
||||
const { dashboard, draggingEnabled, filters: dashboardFilters } = useValues(dashboardLogic)
|
||||
const {
|
||||
addNewDashboard,
|
||||
renameDashboard,
|
||||
enableDragging,
|
||||
disableDragging,
|
||||
updateAndRefreshDashboard,
|
||||
setDates,
|
||||
refreshAllDashboardItems,
|
||||
} = useActions(dashboardLogic)
|
||||
const { dashboards, dashboardsLoading } = useValues(dashboardsModel)
|
||||
@ -82,7 +82,9 @@ export function DashboardHeader(): JSX.Element {
|
||||
<DateFilter
|
||||
defaultValue="Custom"
|
||||
showCustom
|
||||
onChange={updateAndRefreshDashboard}
|
||||
dateFrom={dashboardFilters?.date_from}
|
||||
dateTo={dashboardFilters?.date_to}
|
||||
onChange={setDates}
|
||||
makeLabel={(key) => (
|
||||
<>
|
||||
<CalendarOutlined />
|
||||
|
@ -7,14 +7,12 @@ import { Link } from 'lib/components/Link'
|
||||
import { PlusOutlined } from '@ant-design/icons'
|
||||
import { Table } from 'antd'
|
||||
import { PushpinFilled, PushpinOutlined, DeleteOutlined, AppstoreAddOutlined } from '@ant-design/icons'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { NewDashboard } from 'scenes/dashboard/NewDashboard'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { createdAtColumn, createdByColumn } from 'lib/components/Table'
|
||||
import { DashboardType } from '~/types'
|
||||
|
||||
export const Dashboards = hot(_Dashboards)
|
||||
function _Dashboards(): JSX.Element {
|
||||
export function Dashboards(): JSX.Element {
|
||||
const { dashboardsLoading } = useValues(dashboardsModel)
|
||||
const { deleteDashboard, unpinDashboard, pinDashboard, addDashboard } = useActions(dashboardsModel)
|
||||
const { setNewDashboardDrawer } = useActions(dashboardsLogic)
|
||||
|
@ -11,10 +11,9 @@ import { dashboardItemsModel } from '~/models/dashboardItemsModel'
|
||||
import { PATHS_VIZ, ACTIONS_LINE_GRAPH_LINEAR } from 'lib/constants'
|
||||
import { ViewType } from 'scenes/insights/insightLogic'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
import { dateFilterLogic } from 'lib/components/DateFilter/dateFilterLogic'
|
||||
|
||||
export const dashboardLogic = kea({
|
||||
connect: [dashboardsModel, dashboardItemsModel, eventUsageLogic, dateFilterLogic],
|
||||
connect: [dashboardsModel, dashboardItemsModel, eventUsageLogic],
|
||||
|
||||
key: (props) => props.id,
|
||||
|
||||
@ -32,9 +31,10 @@ export const dashboardLogic = kea({
|
||||
refreshDashboardItem: (id) => ({ id }),
|
||||
refreshAllDashboardItems: true,
|
||||
updateAndRefreshDashboard: true,
|
||||
setDates: (dateFrom, dateTo, reloadDashboard = true) => ({ dateFrom, dateTo, reloadDashboard }),
|
||||
}),
|
||||
|
||||
loaders: ({ props }) => ({
|
||||
loaders: ({ actions, props }) => ({
|
||||
allItems: [
|
||||
{},
|
||||
{
|
||||
@ -43,7 +43,7 @@ export const dashboardLogic = kea({
|
||||
const dashboard = await api.get(
|
||||
`api/dashboard/${props.id}/?${toParams({ share_token: props.shareToken })}`
|
||||
)
|
||||
dateFilterLogic.actions.setDates(dashboard.filters.date_from, dashboard.filters.date_to)
|
||||
actions.setDates(dashboard.filters.date_from, dashboard.filters.date_to, false)
|
||||
eventUsageLogic.actions.reportDashboardViewed(dashboard, !!props.shareToken)
|
||||
return dashboard
|
||||
} catch (error) {
|
||||
@ -64,6 +64,12 @@ export const dashboardLogic = kea({
|
||||
],
|
||||
}),
|
||||
reducers: ({ props }) => ({
|
||||
filters: [
|
||||
{ date_from: undefined, date_to: undefined },
|
||||
{
|
||||
setDates: (state, { dateFrom, dateTo }) => ({ ...state, date_from: dateFrom, date_to: dateTo }),
|
||||
},
|
||||
],
|
||||
allItems: {
|
||||
[dashboardItemsModel.actions.renameDashboardItemSuccess]: (state, { item }) => {
|
||||
return { ...state, items: state.items.map((i) => (i.id === item.id ? item : i)) }
|
||||
@ -335,12 +341,13 @@ export const dashboardLogic = kea({
|
||||
},
|
||||
updateAndRefreshDashboard: async (_, breakpoint) => {
|
||||
await breakpoint(200)
|
||||
const filters = {
|
||||
date_from: dateFilterLogic.values.dates.dateFrom,
|
||||
date_to: dateFilterLogic.values.dates.dateTo,
|
||||
actions.updateDashboard(values.filters)
|
||||
dashboardItemsModel.actions.refreshAllDashboardItems(values.filters)
|
||||
},
|
||||
setDates: ({ reloadDashboard }) => {
|
||||
if (reloadDashboard) {
|
||||
actions.updateAndRefreshDashboard()
|
||||
}
|
||||
actions.updateDashboard(filters)
|
||||
dashboardItemsModel.actions.refreshAllDashboardItems(filters)
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react'
|
||||
import { kea, useActions, useValues } from 'kea'
|
||||
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { Tabs } from 'antd'
|
||||
import { ActionsTable } from 'scenes/actions/ActionsTable'
|
||||
@ -35,8 +34,7 @@ const eventsLogic = kea<eventsLogicType>({
|
||||
}),
|
||||
})
|
||||
|
||||
export const ManageEvents = hot(_ManageEvents)
|
||||
function _ManageEvents({}): JSX.Element {
|
||||
export function ManageEvents(): JSX.Element {
|
||||
const { tab } = useValues(eventsLogic)
|
||||
const { setTab } = useActions(eventsLogic)
|
||||
|
||||
|
@ -12,10 +12,8 @@ import { EventName } from 'scenes/actions/EventName'
|
||||
import { eventToName, toParams } from 'lib/utils'
|
||||
import './EventsTable.scss'
|
||||
import { eventsTableLogic } from './eventsTableLogic'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
|
||||
export const EventsTable = hot(_EventsTable)
|
||||
function _EventsTable({ fixedFilters, filtersEnabled = true, pageKey }) {
|
||||
export function EventsTable({ fixedFilters, filtersEnabled = true, pageKey }) {
|
||||
const logic = eventsTableLogic({ fixedFilters, key: pageKey })
|
||||
const {
|
||||
properties,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { useValues, useActions } from 'kea'
|
||||
import { featureFlagLogic } from './featureFlagLogic'
|
||||
import { Table, Switch, Drawer, Button } from 'antd'
|
||||
@ -11,8 +10,7 @@ import { PageHeader } from 'lib/components/PageHeader'
|
||||
import PropertyFiltersDisplay from 'lib/components/PropertyFilters/PropertyFiltersDisplay'
|
||||
import { createdAtColumn, createdByColumn } from 'lib/components/Table'
|
||||
|
||||
export const FeatureFlags = hot(_FeatureFlags)
|
||||
function _FeatureFlags() {
|
||||
export function FeatureFlags() {
|
||||
const [openFeatureFlag, setOpenFeatureFlag] = useState(false)
|
||||
const logic = featureFlagLogic({ closeDrawer: () => setOpenFeatureFlag(false) })
|
||||
const { featureFlags, featureFlagsLoading } = useValues(logic)
|
||||
|
23
frontend/src/scenes/insights/DateFilter/DateFilter.tsx
Normal file
23
frontend/src/scenes/insights/DateFilter/DateFilter.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import { useValues, useActions } from 'kea'
|
||||
import { dateFilterLogic } from './dateFilterLogic'
|
||||
import { DateFilterProps, DateFilter as DateFilterComponent } from 'lib/components/DateFilter/DateFilter'
|
||||
|
||||
export function DateFilter(props: DateFilterProps): JSX.Element {
|
||||
const {
|
||||
dates: { dateFrom, dateTo },
|
||||
} = useValues(dateFilterLogic)
|
||||
const { setDates } = useActions(dateFilterLogic)
|
||||
|
||||
return (
|
||||
<DateFilterComponent
|
||||
{...props}
|
||||
dateFrom={dateFrom}
|
||||
dateTo={dateTo}
|
||||
onChange={(dateFrom, dateTo) => {
|
||||
setDates(dateFrom, dateTo)
|
||||
props.onChange?.(dateFrom, dateTo)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { kea } from 'kea'
|
||||
import { router } from 'kea-router'
|
||||
import { Moment } from 'moment'
|
||||
import { dateFilterLogicType } from 'lib/components/DateFilter/dateFilterLogicType'
|
||||
import { dateFilterLogicType } from 'scenes/insights/DateFilter/dateFilterLogicType'
|
||||
import { objectsEqual } from 'lib/utils'
|
||||
|
||||
interface UrlParams {
|
||||
@ -9,7 +9,7 @@ interface UrlParams {
|
||||
date_to?: string
|
||||
}
|
||||
|
||||
export const dateFilterLogic = kea<dateFilterLogicType<UrlParams, Moment>>({
|
||||
export const dateFilterLogic = kea<dateFilterLogicType<Moment>>({
|
||||
actions: () => ({
|
||||
setDates: (dateFrom: string | Moment | undefined, dateTo: string | Moment | undefined) => ({
|
||||
dateFrom,
|
@ -4,7 +4,7 @@ import { useActions, useMountedLogic, useValues, BindLogic } from 'kea'
|
||||
import { Loading } from 'lib/utils'
|
||||
import { SaveToDashboard } from 'lib/components/SaveToDashboard/SaveToDashboard'
|
||||
import moment from 'moment'
|
||||
import { DateFilter } from 'lib/components/DateFilter'
|
||||
import { DateFilter } from './DateFilter/DateFilter'
|
||||
import { IntervalFilter } from 'lib/components/IntervalFilter/IntervalFilter'
|
||||
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
@ -20,7 +20,6 @@ import {
|
||||
LIFECYCLE,
|
||||
FUNNEL_VIZ,
|
||||
} from 'lib/constants'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { annotationsLogic } from '~/lib/components/Annotations'
|
||||
import { router } from 'kea-router'
|
||||
|
||||
@ -101,8 +100,7 @@ const showComparePrevious = {
|
||||
[`${ViewType.PATHS}`]: false,
|
||||
}
|
||||
|
||||
export const Insights = hot(_Insights)
|
||||
function _Insights() {
|
||||
export function Insights() {
|
||||
useMountedLogic(insightCommandLogic)
|
||||
const [{ fromItem }] = useState(router.values.hashParams)
|
||||
const { clearAnnotationsToCreate } = useActions(annotationsLogic({ pageKey: fromItem }))
|
||||
|
@ -11,7 +11,7 @@ import { commandPaletteLogicType } from './commandPaletteLogicType'
|
||||
import { kea } from 'kea'
|
||||
import { compareFilterLogic } from 'lib/components/CompareFilter/compareFilterLogic'
|
||||
import { RiseOutlined } from '@ant-design/icons'
|
||||
import { dateFilterLogic } from 'lib/components/DateFilter/dateFilterLogic'
|
||||
import { dateFilterLogic } from 'scenes/insights/DateFilter/dateFilterLogic'
|
||||
import { dateMapping } from 'lib/utils'
|
||||
|
||||
const INSIGHT_COMMAND_SCOPE = 'insights'
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { Alert, Form, Button, Table, Input } from 'antd'
|
||||
import { licenseLogic } from './logic'
|
||||
import { useValues, useActions } from 'kea'
|
||||
@ -38,8 +37,7 @@ const columns = [
|
||||
},
|
||||
]
|
||||
|
||||
export const Licenses = hot(_Licenses)
|
||||
function _Licenses(): JSX.Element {
|
||||
export function Licenses(): JSX.Element {
|
||||
const [form] = Form.useForm()
|
||||
const { licenses, licensesLoading, error } = useValues(licenseLogic)
|
||||
const { createLicense } = useActions(licenseLogic)
|
||||
|
@ -1,7 +1,6 @@
|
||||
import './index.scss'
|
||||
|
||||
import React from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { Alert, Table, Tag, Card } from 'antd'
|
||||
import { systemStatusLogic } from './systemStatusLogic'
|
||||
import { useValues } from 'kea'
|
||||
@ -28,8 +27,7 @@ const columns = [
|
||||
},
|
||||
]
|
||||
|
||||
export const SystemStatus = hot(_Status)
|
||||
function _Status(): JSX.Element {
|
||||
export function SystemStatus(): JSX.Element {
|
||||
const { systemStatus, systemStatusLoading, error } = useValues(systemStatusLogic)
|
||||
return (
|
||||
<div className="system-status-scene">
|
||||
|
@ -3,15 +3,13 @@ import { useValues } from 'kea'
|
||||
import { Divider, Card } from 'antd'
|
||||
import { useAnchor } from 'lib/hooks/useAnchor'
|
||||
import { router } from 'kea-router'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { UpdateEmailPreferences } from './UpdateEmailPreferences'
|
||||
import { ChangePassword } from './ChangePassword'
|
||||
import { PersonalAPIKeys } from 'lib/components/PersonalAPIKeys'
|
||||
import { OptOutCapture } from './OptOutCapture'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
|
||||
export const MySettings = hot(_MySettings)
|
||||
function _MySettings(): JSX.Element {
|
||||
export function MySettings(): JSX.Element {
|
||||
const { location } = useValues(router)
|
||||
|
||||
useAnchor(location.hash)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useActions, useValues } from 'kea'
|
||||
import React from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { inviteSignupLogic, ErrorCodes } from './inviteSignupLogic'
|
||||
import { SceneLoading } from 'lib/utils'
|
||||
import './InviteSignup.scss'
|
||||
@ -178,8 +177,7 @@ function AuthenticatedAcceptInvite({ invite }: { invite: PrevalidatedInvite }):
|
||||
)
|
||||
}
|
||||
|
||||
export const InviteSignup = hot(_InviteSignup)
|
||||
function _InviteSignup(): JSX.Element {
|
||||
export function InviteSignup(): JSX.Element {
|
||||
const { invite, inviteLoading } = useValues(inviteSignupLogic)
|
||||
const { user } = useValues(userLogic)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import React from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { Button, Col, Collapse, Progress, Row, Switch } from 'antd'
|
||||
import {
|
||||
ProjectOutlined,
|
||||
@ -111,8 +110,7 @@ function OnboardingStep({
|
||||
)
|
||||
}
|
||||
|
||||
export const OnboardingSetup = hot(_OnboardingSetup)
|
||||
function _OnboardingSetup(): JSX.Element {
|
||||
export function OnboardingSetup(): JSX.Element {
|
||||
const {
|
||||
stepProjectSetup,
|
||||
stepInstallation,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useActions, useValues } from 'kea'
|
||||
import React from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { personalizationLogic } from './personalizationLogic'
|
||||
import { Row, Col, Button } from 'antd'
|
||||
import { RadioSelect } from 'lib/components/RadioSelect'
|
||||
@ -8,8 +7,7 @@ import { ROLES, PRODUCTS, IS_TECHNICAL } from './personalizationOptions'
|
||||
import { Link } from 'lib/components/Link'
|
||||
import './Personalization.scss'
|
||||
|
||||
export const Personalization = hot(_Personalization)
|
||||
function _Personalization(): JSX.Element {
|
||||
export function Personalization(): JSX.Element {
|
||||
const { personalizationData } = useValues(personalizationLogic)
|
||||
const { appendPersonalizationData, reportPersonalizationSkipped, reportPersonalization } = useActions(
|
||||
personalizationLogic
|
||||
|
@ -4,7 +4,6 @@ import { useValues, useActions } from 'kea'
|
||||
import { invitesLogic } from './invitesLogic'
|
||||
import { DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons'
|
||||
import { humanFriendlyDetailedTime } from 'lib/utils'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { OrganizationInviteType, UserNestedType } from '~/types'
|
||||
import { CopyToClipboardInline } from 'lib/components/CopyToClipboard'
|
||||
import { CreateInviteModalWithButton } from './CreateInviteModal'
|
||||
@ -46,8 +45,7 @@ function makeActionsComponent(
|
||||
)
|
||||
}
|
||||
}
|
||||
export const Invites = hot(_Invites)
|
||||
function _Invites(): JSX.Element {
|
||||
export function Invites(): JSX.Element {
|
||||
const { invites, invitesLoading } = useValues(invitesLogic)
|
||||
const { deleteInvite } = useActions(invitesLogic)
|
||||
|
||||
|
0
frontend/src/scenes/organization/Settings/index.js
Normal file
0
frontend/src/scenes/organization/Settings/index.js
Normal file
@ -1,6 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Button, Card, Input, Divider } from 'antd'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { UserType } from '~/types'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { Invites } from './Invites'
|
||||
@ -45,8 +44,7 @@ function DisplayName({ isRestricted }: RestrictedComponentProps): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
export const OrganizationSettings = hot(_OrganizationSettings)
|
||||
function _OrganizationSettings({ user }: { user: UserType }): JSX.Element {
|
||||
export function OrganizationSettings({ user }: { user: UserType }): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
|
@ -5,7 +5,6 @@ import { Tooltip, Table, Spin, Button, Input } from 'antd'
|
||||
import { ExportOutlined, DeleteOutlined, InfoCircleOutlined } from '@ant-design/icons'
|
||||
import { cohortsModel } from '../../models/cohortsModel'
|
||||
import { useValues, useActions, kea } from 'kea'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { PlusOutlined } from '@ant-design/icons'
|
||||
import { Cohort } from './Cohort'
|
||||
@ -51,8 +50,7 @@ const searchCohorts = (sources: CohortType[], search: string): CohortType[] => {
|
||||
.map((result) => result.item)
|
||||
}
|
||||
|
||||
export const Cohorts = hot(_Cohorts)
|
||||
function _Cohorts(): JSX.Element {
|
||||
export function Cohorts(): JSX.Element {
|
||||
const { cohorts, cohortsLoading } = useValues(cohortsModel)
|
||||
const { loadCohorts } = useActions(cohortsModel)
|
||||
const { openCohort } = useValues(cohortsUrlLogic)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Row, Tabs, Col, Card, Skeleton, Tag, Dropdown, Menu, Button, Popconfirm } from 'antd'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { SessionsView } from '../sessions/SessionsView'
|
||||
import { EventsTable } from 'scenes/events'
|
||||
import { useActions, useValues } from 'kea'
|
||||
@ -17,8 +16,7 @@ import { NewPropertyComponent } from './NewPropertyComponent'
|
||||
|
||||
const { TabPane } = Tabs
|
||||
|
||||
export const Person = hot(_Person)
|
||||
function _Person(): JSX.Element {
|
||||
export function Person(): JSX.Element {
|
||||
const [activeTab, setActiveTab] = useState('events')
|
||||
const [mergeModalOpen, setMergeModalOpen] = useState(false)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './Plugins.scss'
|
||||
import React, { useEffect } from 'react'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { PluginDrawer } from 'scenes/plugins/edit/PluginDrawer'
|
||||
import { RepositoryTab } from 'scenes/plugins/tabs/repository/RepositoryTab'
|
||||
import { InstalledTab } from 'scenes/plugins/tabs/installed/InstalledTab'
|
||||
@ -13,8 +12,7 @@ import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { PluginTab } from 'scenes/plugins/types'
|
||||
import { AdvancedTab } from 'scenes/plugins/tabs/advanced/AdvancedTab'
|
||||
|
||||
export const Plugins = hot(_Plugins)
|
||||
function _Plugins(): JSX.Element {
|
||||
export function Plugins(): JSX.Element {
|
||||
const { user } = useValues(userLogic)
|
||||
const { pluginTab } = useValues(pluginsLogic)
|
||||
const { setPluginTab } = useActions(pluginsLogic)
|
||||
|
@ -10,7 +10,6 @@ import { useAnchor } from 'lib/hooks/useAnchor'
|
||||
import { router } from 'kea-router'
|
||||
import { ReloadOutlined } from '@ant-design/icons'
|
||||
import { red } from '@ant-design/colors'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { ToolbarSettings } from './ToolbarSettings'
|
||||
import { CodeSnippet } from 'scenes/ingestion/frameworks/CodeSnippet'
|
||||
import { teamLogic } from 'scenes/teamLogic'
|
||||
@ -62,8 +61,7 @@ function DisplayName(): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
export const ProjectSettings = hot(_ProjectSettings)
|
||||
function _ProjectSettings(): JSX.Element {
|
||||
export function ProjectSettings(): JSX.Element {
|
||||
const { currentTeam } = useValues(teamLogic)
|
||||
const { resetToken } = useActions(teamLogic)
|
||||
const { location } = useValues(router)
|
||||
|
@ -1,14 +1,12 @@
|
||||
import React from 'react'
|
||||
import { SessionsView } from './SessionsView'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { SavedFiltersMenu } from 'scenes/sessions/filters/SavedFiltersMenu'
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { Divider } from 'antd'
|
||||
import { useValues } from 'kea'
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
|
||||
export const Sessions = hot(_Sessions)
|
||||
function _Sessions(): JSX.Element {
|
||||
export function Sessions(): JSX.Element {
|
||||
const { featureFlags } = useValues(featureFlagLogic)
|
||||
if (featureFlags['filter_by_session_props']) {
|
||||
return (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Player, PlayerRef, findCurrent } from 'posthog-react-rrweb-player'
|
||||
import { Player, PlayerRef, findCurrent } from '@posthog/react-rrweb-player'
|
||||
import { Card, Col, Input, Row, Skeleton, Tag } from 'antd'
|
||||
import {
|
||||
UserOutlined,
|
||||
@ -10,7 +10,6 @@ import {
|
||||
MobileOutlined,
|
||||
TabletOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Link } from 'lib/components/Link'
|
||||
import { colorForString } from 'lib/utils'
|
||||
@ -45,8 +44,7 @@ function DeviceIcon({ width }: { width: number }): JSX.Element {
|
||||
return <LaptopOutlined />
|
||||
}
|
||||
|
||||
export const SessionsPlay = hot(_SessionsPlay)
|
||||
function _SessionsPlay(): JSX.Element {
|
||||
export function SessionsPlay(): JSX.Element {
|
||||
const {
|
||||
session,
|
||||
sessionPlayerData,
|
||||
|
@ -1,3 +1,3 @@
|
||||
@import 'node_modules/rrweb/dist/rrweb.min';
|
||||
@import 'node_modules/rc-tooltip/assets/bootstrap';
|
||||
@import 'node_modules/posthog-react-rrweb-player/dist/index';
|
||||
@import 'node_modules/@posthog/react-rrweb-player/dist/index';
|
||||
|
@ -5,7 +5,7 @@ import { eventToName, toParams } from 'lib/utils'
|
||||
import { sessionsPlayLogicType } from './sessionsPlayLogicType'
|
||||
import { PersonType, SessionType } from '~/types'
|
||||
import moment from 'moment'
|
||||
import { EventIndex } from 'posthog-react-rrweb-player'
|
||||
import { EventIndex } from '@posthog/react-rrweb-player'
|
||||
import { sessionsTableLogic } from 'scenes/sessions/sessionsTableLogic'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
|
@ -2,12 +2,10 @@ import React from 'react'
|
||||
import { useValues } from 'kea'
|
||||
import { Elements } from '~/toolbar/elements/Elements'
|
||||
import { DraggableButton } from '~/toolbar/button/DraggableButton'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import { toolbarLogic } from '~/toolbar/toolbarLogic'
|
||||
import { Fade } from 'lib/components/Fade/Fade'
|
||||
|
||||
export const ToolbarContainer = hot(_ToolbarContainer)
|
||||
function _ToolbarContainer(): JSX.Element {
|
||||
export function ToolbarContainer(): JSX.Element {
|
||||
const { buttonVisible } = useValues(toolbarLogic)
|
||||
|
||||
return (
|
||||
|
@ -46,7 +46,7 @@ export function DraggableButton(): JSX.Element {
|
||||
savePosition={saveHeatmapPosition}
|
||||
>
|
||||
<div className="toolbar-block">
|
||||
<HeatmapStats buttonMode />
|
||||
<HeatmapStats />
|
||||
</div>
|
||||
</ButtonWindow>
|
||||
|
||||
|
@ -16,6 +16,7 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
|
||||
enableHeatmap: true,
|
||||
disableHeatmap: true,
|
||||
setShowHeatmapTooltip: (showHeatmapTooltip: boolean) => ({ showHeatmapTooltip }),
|
||||
setHeatmapFilter: (filter: Record<string, any>) => ({ filter }),
|
||||
},
|
||||
|
||||
reducers: {
|
||||
@ -42,18 +43,26 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
|
||||
setShowHeatmapTooltip: (_, { showHeatmapTooltip }) => showHeatmapTooltip,
|
||||
},
|
||||
],
|
||||
heatmapFilter: [
|
||||
{} as Record<string, any>,
|
||||
{
|
||||
setHeatmapFilter: (_, { filter }) => filter,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
loaders: {
|
||||
loaders: ({ values }) => ({
|
||||
events: [
|
||||
[] as ElementsEventType[],
|
||||
{
|
||||
resetEvents: () => [],
|
||||
getEvents: async ({ $current_url }: { $current_url: string }, breakpoint) => {
|
||||
const params = {
|
||||
const params: Record<string, any> = {
|
||||
properties: [{ key: '$current_url', value: $current_url }],
|
||||
temporary_token: toolbarLogic.values.temporaryToken,
|
||||
...values.heatmapFilter,
|
||||
}
|
||||
|
||||
const url = `${toolbarLogic.values.apiURL}api/element/stats/${encodeParams(params, '?')}`
|
||||
const response = await fetch(url)
|
||||
const results = await response.json()
|
||||
@ -73,7 +82,7 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
selectors: {
|
||||
elements: [
|
||||
@ -226,5 +235,8 @@ export const heatmapLogic = kea<heatmapLogicType<ElementsEventType, CountedHTMLE
|
||||
actions.setShowHeatmapTooltip(false)
|
||||
}
|
||||
},
|
||||
setHeatmapFilter: () => {
|
||||
actions.getEvents({ $current_url: currentPageLogic.values.href })
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -1,37 +1,29 @@
|
||||
import React from 'react'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Button, List, Space } from 'antd'
|
||||
import { List, Space, Spin } from 'antd'
|
||||
import { heatmapLogic } from '~/toolbar/elements/heatmapLogic'
|
||||
import { FireFilled, FireOutlined } from '@ant-design/icons'
|
||||
import { elementsLogic } from '~/toolbar/elements/elementsLogic'
|
||||
import { getShadowRootPopupContainer } from '~/toolbar/utils'
|
||||
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
|
||||
|
||||
interface HeatmapStatsProps {
|
||||
buttonMode?: boolean
|
||||
}
|
||||
|
||||
export function HeatmapStats({ buttonMode = false }: HeatmapStatsProps): JSX.Element {
|
||||
const { countedElements, clickCount, heatmapEnabled, heatmapLoading } = useValues(heatmapLogic)
|
||||
const { enableHeatmap, disableHeatmap } = useActions(heatmapLogic)
|
||||
export function HeatmapStats(): JSX.Element {
|
||||
const { countedElements, clickCount, heatmapEnabled, heatmapLoading, heatmapFilter } = useValues(heatmapLogic)
|
||||
const { setHeatmapFilter } = useActions(heatmapLogic)
|
||||
const { setHighlightElement, setSelectedElement } = useActions(elementsLogic)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!buttonMode ? (
|
||||
<div>
|
||||
<Button
|
||||
type={heatmapEnabled ? 'primary' : 'default'}
|
||||
onClick={heatmapEnabled ? disableHeatmap : enableHeatmap}
|
||||
loading={heatmapLoading}
|
||||
>
|
||||
{heatmapEnabled ? <FireFilled /> : <FireOutlined />}
|
||||
Enable Heatmap
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
{heatmapEnabled && !heatmapLoading ? (
|
||||
{heatmapEnabled ? (
|
||||
<>
|
||||
<div style={{ marginTop: buttonMode ? 0 : 20, marginBottom: 10 }}>
|
||||
<span style={{ borderBottom: '2px dashed hsla(230, 14%, 78%, 1)' }}>Last 7 days</span>
|
||||
<div style={{ marginTop: 0, marginBottom: 10 }}>
|
||||
<DateFilter
|
||||
defaultValue="Last 7 days"
|
||||
dateFrom={heatmapFilter.date_from}
|
||||
dateTo={heatmapFilter.date_to}
|
||||
onChange={(date_from, date_to) => setHeatmapFilter({ date_from, date_to })}
|
||||
getPopupContainer={getShadowRootPopupContainer}
|
||||
/>
|
||||
{heatmapLoading ? <Spin style={{ marginLeft: 8 }} /> : null}
|
||||
</div>
|
||||
<div style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
Found: {countedElements.length} elements / {clickCount} clicks!
|
||||
|
@ -20,9 +20,13 @@
|
||||
.ant-modal-wrap {
|
||||
z-index: 2147483030 !important;
|
||||
}
|
||||
.ant-tooltip {
|
||||
.ant-select-dropdown,
|
||||
.ant-picker-dropdown {
|
||||
z-index: 2147483031 !important;
|
||||
}
|
||||
.ant-tooltip {
|
||||
z-index: 2147483032 !important;
|
||||
}
|
||||
.ant-list-item {
|
||||
&:hover {
|
||||
background: #f0f0f0;
|
||||
|
21
package.json
21
package.json
@ -19,9 +19,9 @@
|
||||
"copy-scripts": "cp node_modules/posthog-js/dist/array.js* frontend/dist/; cp node_modules/rrweb/dist/rrweb.min.js frontend/dist/recorder.js",
|
||||
"test": "jest",
|
||||
"start": "concurrently -n WEBPACK,TYPEGEN -c blue,green \"yarn run start-http\" \"yarn run typegen:watch\"",
|
||||
"start-http": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack-dev-server --hotOnly",
|
||||
"start-https": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack-dev-server --hotOnly --https",
|
||||
"start-docker": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack-dev-server --hotOnly --host 0.0.0.0",
|
||||
"start-http": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack serve",
|
||||
"start-https": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack serve --https",
|
||||
"start-docker": "mkdir -p frontend/dist/ && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts && webpack serve --host 0.0.0.0",
|
||||
"build": "echo \"Building Webpack\" && NODE_ENV=production webpack --config webpack.config.js && cp -a frontend/public/* frontend/dist/ && npm run copy-scripts",
|
||||
"prettier": "prettier --write \"./frontend/src/**/*.{js,ts,tsx,json,yml,css,scss}\"",
|
||||
"prettier:check": "prettier --check \"./**/*.{js,ts,tsx,json,yml,css,scss}\"",
|
||||
@ -40,6 +40,7 @@
|
||||
"@babel/runtime": "^7.10.4",
|
||||
"@papercups-io/chat-widget": "^1.1.5",
|
||||
"@posthog/plugin-scaffold": "0.2.12",
|
||||
"@posthog/react-rrweb-player": "^1.1.0",
|
||||
"@posthog/simmerjs": "0.7.2-posthog.2",
|
||||
"@sentry/browser": "^6.0.4",
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
@ -62,14 +63,13 @@
|
||||
"moment": "^2.24.0",
|
||||
"posthog-js": "1.9.1",
|
||||
"posthog-js-lite": "^0.0.3",
|
||||
"posthog-react-rrweb-player": "^1.0.13",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-selector-shadow-dom": "0.8.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-draggable": "^4.2.0",
|
||||
"react-grid-layout": "^1.1.1",
|
||||
"react-markdown": "^5.0.2",
|
||||
"react-markdown": "^5.0.3",
|
||||
"react-monaco-editor": "^0.40.0",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-shadow": "^18.4.2",
|
||||
@ -115,8 +115,8 @@
|
||||
"eslint-plugin-react": "^7.20.3",
|
||||
"file-loader": "^6.1.0",
|
||||
"givens": "^1.3.6",
|
||||
"html-webpack-harddisk-plugin": "^1.0.1",
|
||||
"html-webpack-plugin": "^4.4.1",
|
||||
"html-webpack-harddisk-plugin": "^1.0.2",
|
||||
"html-webpack-plugin": "^4.5.2",
|
||||
"husky": "^4.3.0",
|
||||
"jest": "^26.6.3",
|
||||
"kea-typegen": "^0.5.1",
|
||||
@ -127,14 +127,13 @@
|
||||
"monaco-editor-webpack-plugin": "^2.0.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "^2.1.1",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"sass-loader": "^10.0.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^3.9.0",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.1.2"
|
||||
|
@ -85,11 +85,6 @@ function createEntry(entry) {
|
||||
types: path.resolve(__dirname, 'frontend', 'types'),
|
||||
public: path.resolve(__dirname, 'frontend', 'public'),
|
||||
cypress: path.resolve(__dirname, 'cypress'),
|
||||
...(process.env.NODE_ENV !== 'production'
|
||||
? {
|
||||
'react-dom': '@hot-loader/react-dom',
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
@ -182,19 +177,26 @@ function createEntry(entry) {
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, 'frontend', 'dist'),
|
||||
hot: true,
|
||||
host: webpackDevServerHost,
|
||||
port: 8234,
|
||||
stats: 'minimal',
|
||||
disableHostCheck: !!process.env.LOCAL_HTTPS,
|
||||
public: process.env.JS_URL ? new URL(process.env.JS_URL).host : `${webpackDevServerFrontendAddr}:8234`,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': '*',
|
||||
},
|
||||
},
|
||||
// add devServer config only to 'main' entry
|
||||
...(entry === 'main'
|
||||
? {
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, 'frontend', 'dist'),
|
||||
hot: true,
|
||||
host: webpackDevServerHost,
|
||||
port: 8234,
|
||||
stats: 'minimal',
|
||||
disableHostCheck: !!process.env.LOCAL_HTTPS,
|
||||
public: process.env.JS_URL
|
||||
? new URL(process.env.JS_URL).host
|
||||
: `${webpackDevServerFrontendAddr}:8234`,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': '*',
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
plugins: [
|
||||
new MonacoWebpackPlugin({
|
||||
languages: ['json', 'javascript'],
|
||||
|
Loading…
Reference in New Issue
Block a user