Skip to content

Commit

Permalink
✨ Add filters for labels and report type
Browse files Browse the repository at this point in the history
  • Loading branch information
foysalit committed Feb 6, 2024
1 parent 82a4441 commit c3c7c32
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 164 deletions.
1 change: 0 additions & 1 deletion app/actions/ModActionPanel/QuickAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,6 @@ function Form(
id="labels"
name="labels"
formId={FORM_ID}
subject={subject}
defaultLabels={currentLabels.filter(
(label) => !isSelfLabel(label),
)}
Expand Down
153 changes: 10 additions & 143 deletions components/common/labels/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,163 +1,25 @@
import { Fragment, useState, useEffect } from 'react'
import { useState } from 'react'
import Select from 'react-tailwindcss-select'
import { Disclosure, Transition } from '@headlessui/react'
import { LabelChip, LabelListEmpty } from './List'
import {
labelOptions,
displayLabel,
groupLabelList,
getLabelGroupInfo,
isSelfLabel,
buildAllLabelOptions,
unFlagSelfLabel,
} from './util'
import { classNames } from '@/lib/util'

const EMPTY_ARR = []
type SelectProps = React.ComponentProps<typeof Select>

// TODO: Probably redundant
export function LabelsGrid(props: LabelsProps) {
const {
id,
formId,
name,
className = '',
defaultLabels = EMPTY_ARR,
options = labelOptions,
disabled,
subject,
...others
} = props
const allOptions = buildAllLabelOptions(defaultLabels, options)
const [current, setCurrent] = useState<string[]>(defaultLabels)

// update the current list when the current labels prop changes
// default labels are an array of strings so passing that as dependency to the useEffect hook will
// cause the current state to change everytime some other prop changes. the string conversion shields
// us from that. only caveat is that it won't work when the labels don't change just their order is changed
const defaultLabelsKey = defaultLabels.join('_')
useEffect(() => {
setCurrent(defaultLabels)
}, [defaultLabelsKey, subject])

const handleCheckboxChange = (opt: string) => {
// don't allow self labels to be changed
if (isSelfLabel(opt)) return
setCurrent((prev) => {
if (prev.includes(opt)) {
return prev.filter((item) => item !== opt)
} else {
return [...prev, opt]
}
})
}

const groupedLabelList = groupLabelList(allOptions)
// Sort by number of labels in each group so that the lists of labels take less vertical space in the UI
const sortedLabelList = Object.values(groupedLabelList).sort(
(a, b) => b.labels?.length - a.labels?.length,
)

return (
<Disclosure as="div">
<Disclosure.Button
className={`${disabled ? '' : 'cursor-pointer'} ${className}`}
disabled={disabled}
{...others}
>
{!current.length && <LabelListEmpty>(click to add)</LabelListEmpty>}
{current.map((label) => {
const labelGroup = getLabelGroupInfo(unFlagSelfLabel(label))
return (
<LabelChip key={label} style={{ color: labelGroup.color }}>
{displayLabel(label)}
<input type="hidden" name={name} value={label} />
</LabelChip>
)
})}
</Disclosure.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Disclosure.Panel>
<div
className="flex flex-wrap flex-row gap-x-8 py-3 shadow-sm"
id={`${id}-staged-container`}
>
{sortedLabelList.map((group) => {
const groupTitle = group.strings.settings.en.name
return (
<div
key={`label_group_${groupTitle}`}
className={classNames('flex flex-col py-1 min-w-1/6')}
>
<p style={{ color: group.color }}>{groupTitle}</p>
{group.labels.map((opt, i) => {
const labelText = typeof opt === 'string' ? opt : opt.id
const cantChange = isSelfLabel(labelText)
return (
<div
className={classNames(
`flex flex-row pl-1`,
cantChange ? 'opacity-75' : '',
)}
key={`label_${labelText}`}
>
<div className="flex h-6 items-center">
<input
id={`${id}-${groupTitle}-opt-${i}`}
name={`${name}-staged`}
type="checkbox"
value={labelText}
disabled={cantChange}
checked={current.includes(labelText)}
onChange={() => handleCheckboxChange(labelText)}
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault() // make sure we don't submit the form
e.currentTarget.click() // simulate a click on the input
}
}}
/>
</div>
<label
htmlFor={`${id}-${groupTitle}-opt-${i}`}
className="ml-1 text-sm leading-6 font-medium text-gray-900"
>
{displayLabel(labelText)}
</label>
</div>
)
})}
</div>
)
})}
</div>
</Disclosure.Panel>
</Transition>
</Disclosure>
)
}

export const LabelSelector = (props: LabelsProps) => {
const {
id,
formId,
name,
className = '',
defaultLabels = EMPTY_ARR,
options = labelOptions,
disabled,
subject,
...others
onChange,
} = props
const [selectedLabels, setSelectedLabels] = useState<SelectProps['value']>(
defaultLabels.map((label) => ({
Expand Down Expand Up @@ -186,6 +48,7 @@ export const LabelSelector = (props: LabelsProps) => {
<input
type="hidden"
name={name}
{...{ id, formId, disabled }}
value={
Array.isArray(selectedLabels)
? selectedLabels.map(({ label }) => label).join(',')
Expand All @@ -209,7 +72,12 @@ export const LabelSelector = (props: LabelsProps) => {
</li>
)
}}
onChange={(value) => setSelectedLabels(value)}
onChange={(value) => {
setSelectedLabels(value)
onChange?.(
Array.isArray(value) ? value.map(({ value }) => value) : [],
)
}}
/>
</>
)
Expand All @@ -220,8 +88,7 @@ type LabelsProps = {
formId: string
name: string
disabled?: boolean
className?: string
defaultLabels?: string[]
options?: string[]
subject?: string
onChange?: (labels: string[]) => void
}
69 changes: 68 additions & 1 deletion components/mod-event/EventList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import {
import { LoadMoreButton } from '@/common/LoadMoreButton'
import { ModEventItem } from './EventItem'
import { Dropdown } from '@/common/Dropdown'
import { MOD_EVENT_TITLES } from './constants'
import { MOD_EVENTS, MOD_EVENT_TITLES } from './constants'
import { ArchiveBoxXMarkIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
import { getSubjectTitle } from './helpers/subject'
import { useState } from 'react'
import { Checkbox, FormLabel, Input } from '@/common/forms'
import { ActionButton } from '@/common/buttons'
import { FunnelIcon as FunnelEmptyIcon } from '@heroicons/react/24/outline'
import { FunnelIcon as FunnelFilledIcon } from '@heroicons/react/24/solid'
import { reasonTypeOptions } from '@/reports/helpers/getType'
import Select from 'react-tailwindcss-select'
import { LabelSelector } from '@/common/labels/Grid'

const Header = ({
subjectTitle,
Expand Down Expand Up @@ -65,6 +68,9 @@ export const ModEventList = (
) => {
const {
types,
reportTypes,
addedLabels,
removedLabels,
includeAllUserRecords,
modEvents,
fetchMoreModEvents,
Expand Down Expand Up @@ -124,6 +130,9 @@ export const ModEventList = (
<EventFilterPanel
{...{
types,
reportTypes,
addedLabels,
removedLabels,
commentFilter,
toggleCommentFilter,
setCommentFilterKeyword,
Expand Down Expand Up @@ -180,6 +189,9 @@ export const ModEventList = (

const EventFilterPanel = ({
types,
reportTypes,
addedLabels,
removedLabels,
commentFilter,
createdBy,
subject,
Expand Down Expand Up @@ -405,6 +417,61 @@ const EventFilterPanel = ({
</FormLabel>
</div>
</div>
<div className="flex-row flex gap-2 mt-2">
{types.includes(MOD_EVENTS.LABEL) && (
<>
<FormLabel label="Added Labels" className="flex-1 max-w-sm">
<LabelSelector
id="addedLabels"
name="addedLabels"
formId=""
defaultLabels={[]}
onChange={(value) =>
changeListFilter({ field: 'addedLabels', value })
}
/>
</FormLabel>

<FormLabel label="Removed Labels" className="flex-1 max-w-sm">
<LabelSelector
id="removedLabels"
name="removedLabels"
formId=""
defaultLabels={[]}
onChange={(value) =>
changeListFilter({ field: 'removedLabels', value })
}
/>
</FormLabel>
</>
)}

{types.includes(MOD_EVENTS.REPORT) && (
<FormLabel label="Reason" htmlFor="reasonType" className="flex-1 max-w-sm">
<Select
isMultiple
isSearchable
primaryColor=""
value={reportTypes.map((value) => ({
value,
label: reasonTypeOptions[value],
}))}
options={Object.entries(reasonTypeOptions).map(
([value, label]) => ({
label,
value,
}),
)}
onChange={(value) =>
changeListFilter({
field: 'reportTypes',
value: Array.isArray(value) ? value.map((v) => v.value) : [],
})
}
/>
</FormLabel>
)}
</div>
<div>
<h5 className="text-gray-700 font-medium my-2">Sort Direction</h5>

Expand Down
Loading

0 comments on commit c3c7c32

Please sign in to comment.