diff --git a/frontend/src/lib/components/EditableField/EditableField.tsx b/frontend/src/lib/components/EditableField/EditableField.tsx index 28cb2e4a247b7..0a83602cf5c5f 100644 --- a/frontend/src/lib/components/EditableField/EditableField.tsx +++ b/frontend/src/lib/components/EditableField/EditableField.tsx @@ -3,6 +3,7 @@ import './EditableField.scss' import { useMergeRefs } from '@floating-ui/react' import { IconPencil } from '@posthog/icons' import clsx from 'clsx' +import { useValues } from 'kea' import { useResizeObserver } from 'lib/hooks/useResizeObserver' import { IconMarkdown } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' @@ -12,6 +13,10 @@ import { pluralize } from 'lib/utils' import React, { useEffect, useLayoutEffect, useRef, useState } from 'react' import TextareaAutosize from 'react-textarea-autosize' +import { AvailableFeature } from '~/types' + +import { upgradeModalLogic } from '../UpgradeModal/upgradeModalLogic' + export interface EditableFieldProps { /** What this field stands for. */ name: string @@ -28,7 +33,7 @@ export interface EditableFieldProps { markdown?: boolean compactButtons?: boolean | 'xsmall' // The 'xsmall' is somewhat hacky, but necessary for 3000 breadcrumbs /** Whether this field should be gated behind a "paywall". */ - paywall?: boolean + paywallFeature?: AvailableFeature /** Controlled mode. */ mode?: 'view' | 'edit' onModeToggle?: (newMode: 'view' | 'edit') => void @@ -58,7 +63,7 @@ export function EditableField({ multiline = false, markdown = false, compactButtons = false, - paywall = false, + paywallFeature, mode, onModeToggle, editingIndication = 'outlined', @@ -68,6 +73,7 @@ export function EditableField({ saveButtonText = 'Save', notice, }: EditableFieldProps): JSX.Element { + const { guardAvailableFeature } = useValues(upgradeModalLogic) const [localIsEditing, setLocalIsEditing] = useState(mode === 'edit') const [localTentativeValue, setLocalTentativeValue] = useState(value) const [isDisplayTooltipNeeded, setIsDisplayTooltipNeeded] = useState(false) @@ -125,7 +131,7 @@ export function EditableField({ onModeToggle?.('view') } - const isEditing = !paywall && (mode === 'edit' || localIsEditing) + const isEditing = mode === 'edit' || localIsEditing const handleKeyDown = (e: React.KeyboardEvent): void => { if (isEditing) { @@ -156,123 +162,117 @@ export function EditableField({ style={style} ref={containerRef} > - -
- {isEditing ? ( - <> - {multiline ? ( - { - onChange?.(e.target.value) - setLocalTentativeValue(e.target.value) - }} - onBlur={saveOnBlur ? (localTentativeValue !== value ? save : cancel) : undefined} - onKeyDown={handleKeyDown} - placeholder={placeholder} - minLength={minLength} - maxLength={maxLength} - autoFocus={autoFocus} - ref={inputRef as React.RefObject} - /> - ) : ( - { +
+ {isEditing ? ( + <> + {multiline ? ( + { + onChange?.(e.target.value) + setLocalTentativeValue(e.target.value) + }} + onBlur={saveOnBlur ? (localTentativeValue !== value ? save : cancel) : undefined} + onKeyDown={handleKeyDown} + placeholder={placeholder} + minLength={minLength} + maxLength={maxLength} + autoFocus={autoFocus} + ref={inputRef as React.RefObject} + /> + ) : ( + { + guardAvailableFeature(paywallFeature, () => { onChange?.(e.target.value) setLocalTentativeValue(e.target.value) - }} - onBlur={saveOnBlur ? (localTentativeValue !== value ? save : cancel) : undefined} - onKeyDown={handleKeyDown} - placeholder={placeholder} - minLength={minLength} - maxLength={maxLength} - autoFocus={autoFocus} - ref={inputRef as React.RefObject} - /> - )} - {(!mode || !!onModeToggle) && ( -
- {markdown && ( - - - - - - )} - - Cancel - - - {saveButtonText} - -
- )} - - ) : ( - <> - {localTentativeValue && markdown ? ( - {localTentativeValue} - ) : ( - } + /> + )} + {(!mode || !!onModeToggle) && ( +
+ {markdown && ( + + + + + + )} + + Cancel + + - - {localTentativeValue || {placeholder}} - - - )} - {(!mode || !!onModeToggle) && ( -
- } - size={compactButtons ? 'small' : undefined} - onClick={() => { + {saveButtonText} + +
+ )} + + ) : ( + <> + {localTentativeValue && markdown ? ( + {localTentativeValue} + ) : ( + + + {localTentativeValue || {placeholder}} + + + )} + {(!mode || !!onModeToggle) && ( +
+ } + size={compactButtons ? 'small' : undefined} + onClick={() => { + guardAvailableFeature(paywallFeature, () => { setLocalIsEditing(true) onModeToggle?.('edit') - }} - data-attr={`edit-prop-${name}`} - disabled={paywall} - noPadding - /> -
- )} - - )} -
-
+ }) + }} + data-attr={`edit-prop-${name}`} + noPadding + /> +
+ )} + + )} +
{!isEditing && notice && ( diff --git a/frontend/src/lib/components/UpgradeModal/upgradeModalLogic.ts b/frontend/src/lib/components/UpgradeModal/upgradeModalLogic.ts index 9542ac6a208dc..51c6cf6fb670c 100644 --- a/frontend/src/lib/components/UpgradeModal/upgradeModalLogic.ts +++ b/frontend/src/lib/components/UpgradeModal/upgradeModalLogic.ts @@ -9,7 +9,7 @@ import { AvailableFeature } from '~/types' import type { upgradeModalLogicType } from './upgradeModalLogicType' export type GuardAvailableFeatureFn = ( - featureKey: AvailableFeature, + featureKey?: AvailableFeature, featureAvailableCallback?: () => void, options?: { guardOnCloud?: boolean @@ -60,6 +60,10 @@ export const upgradeModalLogic = kea([ (s) => [s.preflight, s.hasAvailableFeature], (preflight, hasAvailableFeature): GuardAvailableFeatureFn => { return (featureKey, featureAvailableCallback, options): boolean => { + if (!featureKey) { + featureAvailableCallback?.() + return true + } const { guardOnCloud = true, guardOnSelfHosted = true, diff --git a/frontend/src/scenes/actions/ActionEdit.tsx b/frontend/src/scenes/actions/ActionEdit.tsx index 9da6187358372..40623d01883fc 100644 --- a/frontend/src/scenes/actions/ActionEdit.tsx +++ b/frontend/src/scenes/actions/ActionEdit.tsx @@ -15,7 +15,6 @@ import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { compactNumber, uuid } from 'lib/utils' import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' -import { userLogic } from 'scenes/userLogic' import { tagsModel } from '~/models/tagsModel' import { ActionStepType, AvailableFeature } from '~/types' @@ -32,7 +31,6 @@ export function ActionEdit({ action: loadedAction, id }: ActionEditLogicProps): const { action, actionLoading, actionCount, actionCountLoading } = useValues(logic) const { submitAction, deleteAction } = useActions(logic) const { currentTeam } = useValues(teamLogic) - const { hasAvailableFeature } = useValues(userLogic) const { tags } = useValues(tagsModel) const slackEnabled = currentTeam?.slack_incoming_webhook @@ -96,7 +94,7 @@ export function ActionEdit({ action: loadedAction, id }: ActionEditLogicProps): className="action-description" compactButtons maxLength={600} // No limit on backend model, but enforce shortish description - paywall={!hasAvailableFeature(AvailableFeature.INGESTION_TAXONOMY)} + paywallFeature={AvailableFeature.INGESTION_TAXONOMY} /> )} diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx index 231b3516f4674..4c15d085d0317 100644 --- a/frontend/src/scenes/dashboard/DashboardHeader.tsx +++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx @@ -308,7 +308,11 @@ export function DashboardHeader(): JSX.Element | null { multiline name="description" markdown - value={dashboard.description || ''} + value={ + (hasAvailableFeature(AvailableFeature.TEAM_COLLABORATION) && + dashboard.description) || + '' + } placeholder="Description (optional)" onSave={(value) => updateDashboard({ id: dashboard.id, description: value, allowUndo: true }) @@ -316,7 +320,7 @@ export function DashboardHeader(): JSX.Element | null { saveOnBlur={true} compactButtons mode={!canEditDashboard ? 'view' : undefined} - paywall={!hasAvailableFeature(AvailableFeature.TEAM_COLLABORATION)} + paywallFeature={AvailableFeature.TEAM_COLLABORATION} /> )} {dashboard?.tags && ( diff --git a/frontend/src/scenes/data-management/definition/DefinitionView.tsx b/frontend/src/scenes/data-management/definition/DefinitionView.tsx index 93be7c2e0910c..f307d61f7d13a 100644 --- a/frontend/src/scenes/data-management/definition/DefinitionView.tsx +++ b/frontend/src/scenes/data-management/definition/DefinitionView.tsx @@ -17,7 +17,6 @@ import { definitionLogic, DefinitionLogicProps } from 'scenes/data-management/de import { EventDefinitionProperties } from 'scenes/data-management/events/EventDefinitionProperties' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { userLogic } from 'scenes/userLogic' import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { Query } from '~/queries/Query/Query' @@ -37,7 +36,6 @@ export function DefinitionView(props: DefinitionLogicProps = {}): JSX.Element { const { definition, definitionLoading, definitionMissing, hasTaxonomyFeatures, singular, isEvent, isProperty } = useValues(logic) const { deleteDefinition } = useActions(logic) - const { hasAvailableFeature } = useValues(userLogic) if (definitionLoading) { return @@ -146,7 +144,7 @@ export function DefinitionView(props: DefinitionLogicProps = {}): JSX.Element { className="definition-description" compactButtons maxLength={600} - paywall={!hasAvailableFeature(AvailableFeature.INGESTION_TAXONOMY)} + paywallFeature={AvailableFeature.INGESTION_TAXONOMY} /> )} {canEditInsight ? (