Skip to content

Commit

Permalink
Segment editing works
Browse files Browse the repository at this point in the history
  • Loading branch information
apata committed Nov 5, 2024
1 parent 6f053de commit 8c8bb1c
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 166 deletions.
28 changes: 21 additions & 7 deletions assets/js/dashboard/components/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
AppNavigationLink,
AppNavigationTarget
} from '../navigation/use-app-navigate'
import { NavigateOptions } from 'react-router-dom'

export const ToggleDropdownButton = forwardRef<
HTMLDivElement,
Expand Down Expand Up @@ -130,23 +131,36 @@ export const DropdownNavigationLink = ({
active,
className,
actions,
path,
params,
search,
navigateOptions,
onLinkClick,
...props
}: AppNavigationTarget & {
active?: boolean
children: ReactNode
className?: string
onClick?: () => void
actions?: ReactNode
}) => (
navigateOptions?: NavigateOptions
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
active?: boolean
onLinkClick?: () => void
actions?: ReactNode
}) => (
<div
className={classNames(
{ 'font-bold': !!active },
'flex items-center justify-between',
'px-4 py-2 text-sm leading-tight hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-900 dark:hover:text-gray-100',
className
)}
{...props}
>
<AppNavigationLink className="flex items-center justify-between w-full" {...props}>
<AppNavigationLink
className="flex items-center justify-between w-full"
path={path}
params={params}
search={search}
onClick={onLinkClick}
{...navigateOptions}
>
{children}
</AppNavigationLink>
{!!actions && actions}
Expand Down
4 changes: 2 additions & 2 deletions assets/js/dashboard/datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function ComparisonMenu({
<DropdownNavigationLink
active={query.comparison === ComparisonMode.custom}
search={(s) => s}
onClick={toggleCompareMenuCalendar}
onLinkClick={toggleCompareMenuCalendar}
>
{COMPARISON_MODES[ComparisonMode.custom]}
</DropdownNavigationLink>
Expand Down Expand Up @@ -250,7 +250,7 @@ function QueryPeriodsMenu({
key={label}
active={isActive({ site, query })}
search={search}
onClick={onClick || closeMenu}
onLinkClick={onClick || closeMenu}
>
{label}
{!!keyboardKey && <KeybindHint>{keyboardKey}</KeybindHint>}
Expand Down
4 changes: 2 additions & 2 deletions assets/js/dashboard/nav-menu/filter-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ export const FilterMenu = () => {
>
{opened && (
<DropdownMenuWrapper id="filter-menu" className="md:left-auto md:w-56">
<SegmentsList close={() => setOpened(false)} />
<SegmentsList closeList={() => setOpened(false)} />
<DropdownLinkGroup>
{filterListItems.map(({ modalKey, label }) => (
<DropdownNavigationLink
onClick={() => setOpened(false)}
onLinkClick={() => setOpened(false)}
active={false}
key={modalKey}
path={filterRoute.path}
Expand Down
71 changes: 13 additions & 58 deletions assets/js/dashboard/nav-menu/filters-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import { EllipsisHorizontalIcon, XMarkIcon } from '@heroicons/react/20/solid'
import classNames from 'classnames'
import React, { useRef, useState, useLayoutEffect, useEffect } from 'react'
import {
AppNavigationLink,
useAppNavigate
} from '../navigation/use-app-navigate'
import { AppNavigationLink } from '../navigation/use-app-navigate'
import { useOnClickOutside } from '../util/use-on-click-outside'
import {
DropdownMenuWrapper,
Expand All @@ -15,14 +12,8 @@ import {
import { FilterPillsList, PILL_X_GAP } from './filter-pills-list'
import { useQueryContext } from '../query-context'
import { SaveSegmentAction } from '../segments/segment-actions'
import {
EditingSegmentState,
isSegmentFilter,
parseApiSegmentData
} from '../segments/segments'
import { EditingSegmentState, isSegmentFilter } from '../segments/segments'
import { useLocation } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { useSiteContext } from '../site-context'

const SEE_MORE_GAP_PX = 16
const SEE_MORE_WIDTH_PX = 36
Expand Down Expand Up @@ -103,8 +94,6 @@ type VisibilityState = {
}

export const FiltersBar = () => {
const site = useSiteContext()
const navigate = useAppNavigate()
const containerRef = useRef<HTMLDivElement>(null)
const pillsRef = useRef<HTMLDivElement>(null)
const actionsRef = useRef<HTMLDivElement>(null)
Expand All @@ -114,48 +103,18 @@ export const FiltersBar = () => {
const { state: locationState } = useLocation() as {
state?: EditingSegmentState
}
const [editingSegmentId, setEditingSegmentId] = useState<number | null>(null)
const [editingSegment, setEditingSegment] = useState<
null | EditingSegmentState['editingSegment']
>(null)

useLayoutEffect(() => {
if (typeof locationState?.editingSegmentId === 'number') {
setEditingSegmentId(locationState.editingSegmentId)
}
}, [locationState])

const getSegment = useQuery({
enabled: editingSegmentId !== null,
queryKey: ['segments', editingSegmentId ?? 0],
queryFn: ({ queryKey: [_, id] }) => {
return fetch(
`/internal-api/${encodeURIComponent(site.domain)}/segments/${id}`,
{
method: 'GET',
headers: {
'content-type': 'application/json',
accept: 'application/json'
}
}
)
.then((res) => res.json())
.then((d) => ({
...d,
segment_data: parseApiSegmentData(d.segment_data)
}))
if (locationState?.editingSegment) {
setEditingSegment(locationState?.editingSegment)
}
})

useLayoutEffect(() => {
if (getSegment.data) {
navigate({
search: (s) => ({
...s,
filters: getSegment.data.segment_data.filters,
labels: getSegment.data.segment_data.labels
})
})
if (locationState?.editingSegment === null) {
setEditingSegment(null)
}
}, [getSegment.data])
console.log(editingSegmentId, getSegment.data)
}, [locationState?.editingSegment])

const [opened, setOpened] = useState(false)

Expand Down Expand Up @@ -257,25 +216,21 @@ export const FiltersBar = () => {
</ToggleDropdownButton>
)}
<ClearAction />
{editingSegmentId === null &&
{editingSegment === null &&
!query.filters.some((f) => isSegmentFilter(f)) && (
<>
<VerticalSeparator />
<SaveSegmentAction options={[{ type: 'create segment' }]} />
</>
)}
{editingSegmentId !== null && !!getSegment.data && (
{editingSegment !== null && (
<>
<VerticalSeparator />
<SaveSegmentAction
options={[
{
type: 'update segment',
segment: {
id: getSegment.data.id,
name: getSegment.data.name,
personal: getSegment.data.personal
}
segment: editingSegment
},
{ type: 'create segment' }
]}
Expand Down
17 changes: 11 additions & 6 deletions assets/js/dashboard/segments/segment-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
remapToApiFilters
} from '../util/filters'
import {
EditingSegmentState,
formatSegmentIdAsLabelKey,
parseApiSegmentData,
SavedSegment
Expand Down Expand Up @@ -84,7 +85,8 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {
filters,
labels
}
}
},
state: { editingSegment: null } as EditingSegmentState
})
close()
queryClient.invalidateQueries({ queryKey: ['segments'] })
Expand Down Expand Up @@ -144,7 +146,8 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {
filters,
labels
}
}
},
state: { editingSegment: null } as EditingSegmentState
})
close()
queryClient.invalidateQueries({ queryKey: ['segments'] })
Expand All @@ -161,9 +164,8 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {

const option = options.find((o) => o.type === modal)


return (
<div>
<div className="flex">
{options.map((o) => {
if (o.type === 'create segment') {
return (
Expand All @@ -172,7 +174,9 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {
className="whitespace-nowrap rounded font-medium text-sm leading-tight px-2 py-2 h-9 hover:text-indigo-700 dark:hover:text-indigo-500"
onClick={openCreateSegment}
>
Save segment
{options.find((o) => o.type === 'update segment')
? 'Save as new segment'
: 'Save as segment'}
</button>
)
}
Expand All @@ -190,6 +194,7 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {
})}
{modal === 'create segment' && (
<CreateSegmentModal
segment={options.find((o) => o.type === 'update segment')?.segment}
namePlaceholder={segmentNamePlaceholder}
close={close}
onSave={({ name, personal }) =>
Expand All @@ -207,7 +212,7 @@ export const SaveSegmentAction = ({ options }: { options: O[] }) => {
{option?.type === 'update segment' && (
<UpdateSegmentModal
segment={option.segment}
namePlaceholder={option.segment.name}
namePlaceholder={segmentNamePlaceholder}
close={close}
onSave={({ id, name, personal }) =>
patchSegment.mutate({
Expand Down
27 changes: 18 additions & 9 deletions assets/js/dashboard/segments/segment-modals.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
/** @format */

import React, { useRef, useState } from 'react'
import React, { useState } from 'react'
import ModalWithRouting from '../stats/modals/modal'
import classNames from 'classnames'
import { SavedSegment } from './segments'

export const CreateSegmentModal = ({
segment,
close,
onSave,
namePlaceholder
}: {
segment?: SavedSegment
close: () => void
onSave: (input: { name: string; personal: boolean }) => void
namePlaceholder: string
}) => {
const [name, setName] = useState(
segment?.name ? `Copy of ${segment.name}` : ''
)
const [personal, setPersonal] = useState(true)
const inputRef = useRef<HTMLInputElement>(null)

return (
<ModalWithRouting maxWidth="460px" className="p-6 min-h-fit" close={close}>
Expand All @@ -30,7 +34,9 @@ export const CreateSegmentModal = ({
</label>
<input
autoComplete="off"
ref={inputRef}
// ref={inputRef}
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={namePlaceholder}
id="name"
className="block mt-2 p-2 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500"
Expand Down Expand Up @@ -69,8 +75,9 @@ export const CreateSegmentModal = ({
<button
className="h-12 text-md font-medium py-2 px-3 rounded border"
onClick={() => {
const name = inputRef.current?.value.trim() || namePlaceholder
onSave({ name, personal })
const trimmedName = name.trim()
const saveableName = trimmedName.length ? trimmedName : namePlaceholder
onSave({ name: saveableName, personal })
}}
>
Save
Expand All @@ -91,8 +98,8 @@ export const UpdateSegmentModal = ({
segment: SavedSegment
namePlaceholder: string
}) => {
const [name, setName] = useState(segment.name)
const [personal, setPersonal] = useState<boolean>(segment.personal)
const inputRef = useRef<HTMLInputElement>(null)

return (
<ModalWithRouting maxWidth="460px" className="p-6 min-h-fit" close={close}>
Expand All @@ -107,7 +114,8 @@ export const UpdateSegmentModal = ({
</label>
<input
autoComplete="off"
ref={inputRef}
value={name}
onChange={(e) => setName(e.target.value)}
placeholder={namePlaceholder}
id="name"
className="block mt-2 p-2 w-full dark:bg-gray-900 dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-700 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500"
Expand Down Expand Up @@ -146,8 +154,9 @@ export const UpdateSegmentModal = ({
<button
className="h-12 text-md font-medium py-2 px-3 rounded border"
onClick={() => {
const name = inputRef.current?.value.trim() || namePlaceholder
onSave({ id: segment.id, name, personal })
const trimmedName = name.trim()
const saveableName = trimmedName.length ? trimmedName : namePlaceholder
onSave({ id: segment.id, name: saveableName, personal })
}}
>
Save
Expand Down
Loading

0 comments on commit 8c8bb1c

Please sign in to comment.