Skip to content

Commit

Permalink
✨ Add column selector and admin field protection
Browse files Browse the repository at this point in the history
  • Loading branch information
foysalit committed Dec 6, 2024
1 parent 602e706 commit 7628338
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 170 deletions.
4 changes: 2 additions & 2 deletions components/common/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ export const Dropdown = ({
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{/* z-30 value is important because we want all dropdowns to draw over other elements in the page and besides mobile menu, z-30 is the highest z-index we use in this codebase */}
{/* z-50 value is important because we want all dropdowns to draw over other elements in the page and besides mobile menu, z-40 is the highest z-index we use in this codebase */}
<Menu.Items
className={classNames(
rightAligned ? 'right-0' : '',
'absolute z-30 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-slate-800 py-1 shadow-lg dark:shadow-slate-900 ring-1 ring-black ring-opacity-5 focus:outline-none',
'absolute z-50 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-slate-800 py-1 shadow-lg dark:shadow-slate-900 ring-1 ring-black ring-opacity-5 focus:outline-none',
)}
>
{items.map((item) => (
Expand Down
4 changes: 2 additions & 2 deletions components/shell/CommandPalette/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ export const CommandPaletteRoot = ({
return (
<KBarProvider actions={staticActions}>
<KBarPortal>
{/* z-40 value is important because we want the cmd palette to be able above all panels and currently, the highest z-index we use is z-40 */}
<KBarPositioner className="p-2 bg-gray-900/80 flex items-center pb-4 z-40">
{/* z-50 value is important because we want the cmd palette to be able above all panels and currently, the highest z-index we use is z-50 */}
<KBarPositioner className="p-2 bg-gray-900/80 flex items-center pb-4 z-50">
<KBarAnimator className="w-full md:w-2/3 lg:w-1/2 w-max-[600px] overflow-hidden p-2 bg-white dark:bg-slate-800 rounded-xl">
<KBarSearch
defaultPlaceholder="Search by DID, bsky url or handle"
Expand Down
129 changes: 129 additions & 0 deletions components/workspace/ExportPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { ActionButton } from '@/common/buttons'
import { Popover, Transition } from '@headlessui/react'
import { useWorkspaceExport } from './hooks'
import { WorkspaceListData } from './useWorkspaceListData'
import { Checkbox, FormLabel, Input } from '@/common/forms'

export const WorkspaceExportPanel = ({
listData,
}: {
listData: WorkspaceListData
}) => {
const {
mutateAsync,
isLoading,
headers,
filename,
setFilename,
selectedColumns,
setSelectedColumns,
} = useWorkspaceExport()

const canDownload = selectedColumns.length > 0 && !isLoading && !!filename

return (
<Popover className="relative z-30">
{({ open, close }) => (
<>
<Popover.Button className="text-sm flex flex-row items-center z-20">
<ActionButton
size="sm"
appearance="outlined"
title="Export all users from workspace"
>
<span className="text-xs">
{isLoading ? 'Exporting...' : 'Export'}
</span>
</ActionButton>
</Popover.Button>

{/* Use the `Transition` component. */}
<Transition
show={open}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Popover.Panel className="absolute right-0 z-30 mt-1 flex w-screen max-w-max -translate-x-1/5 px-4">
<div className="w-fit-content flex-auto rounded bg-white dark:bg-slate-800 p-4 text-sm leading-6 shadow-lg dark:shadow-slate-900 ring-1 ring-gray-900/5">
<div className="">
<FormLabel label="Export File Name" className="mb-3" required>
<Input
type="text"
name="filename"
id="filename"
className="py-2 w-full"
placeholder="Enter the file name for your export"
required
value={filename}
onChange={(e) => setFilename(e.target.value)}
/>
</FormLabel>

<FormLabel
label="Select Columns to Export"
className="mb-1"
required
/>

{headers.map((fieldName) => {
return (
<Checkbox
key={fieldName}
value="true"
id={fieldName}
name={fieldName}
checked={selectedColumns.includes(fieldName)}
className="mb-2 flex items-center"
label={fieldName}
onChange={(e) =>
e.target.checked
? setSelectedColumns([
...selectedColumns,
fieldName,
])
: setSelectedColumns(
selectedColumns.filter(
(col) => col !== fieldName,
),
)
}
/>
)
})}
</div>
<p className="py-2 block max-w-lg text-gray-500 dark:text-gray-300 text-xs">
Note:{' '}
<i>
The exported file will only contain the selected columns.
<br />
When exporting from records (posts, profiles etc.) the
exported file will only contain the author account details
of the records.
</i>
</p>
<div className="flex flex-row mt-2 gap-2">
<ActionButton
size="xs"
appearance="outlined"
disabled={!canDownload}
onClick={() => {
mutateAsync(listData).then(() => close())
}}
>
<span className="text-xs">
{isLoading ? 'Downloading...' : 'Download'}
</span>
</ActionButton>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
)
}
2 changes: 1 addition & 1 deletion components/workspace/FilterSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const WorkspaceFilterSelector = ({
}

return (
<Popover className="relative z-20">
<Popover className="relative z-30">
{({ open }) => (
<>
<Popover.Button className="text-sm flex flex-row items-center z-20">
Expand Down
18 changes: 6 additions & 12 deletions components/workspace/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import {
import { SubjectOverview } from '@/reports/SubjectOverview'
import { ReviewStateIcon } from '@/subject/ReviewStateMarker'
import { PreviewCard } from '@/common/PreviewCard'
import { useWorkspaceExportMutation } from './hooks'
import {
WorkspaceListData,
WorkspaceListItemData,
} from './useWorkspaceListData'
import { ToolsOzoneModerationDefs } from '@atproto/api'
import { SubjectTag } from 'components/tags/SubjectTag'
import { ModerationLabel } from '@/common/labels'
import { WorkspaceExportPanel } from './ExportPanel'

interface WorkspaceListProps {
list: string[]
canExport: boolean
listData: WorkspaceListData
onRemoveItem: (item: string) => void
}
Expand All @@ -50,9 +51,11 @@ const getLangTagFromRecordValue = (
const WorkspaceList: React.FC<WorkspaceListProps> = ({
list,
listData,
canExport,
onRemoveItem,
}) => {
const groupedItems = groupSubjects(list)

return (
<div>
<div className="space-y-2">
Expand All @@ -64,7 +67,7 @@ const WorkspaceList: React.FC<WorkspaceListProps> = ({
items={items}
listData={listData}
onRemoveItem={onRemoveItem}
canExport={key === 'dids'}
canExport={canExport}
title={
GroupTitles[key] ||
`${key.charAt(0).toUpperCase()}${key.slice(1)}`
Expand Down Expand Up @@ -92,7 +95,6 @@ const ListGroup = ({
const checkboxesRef = useRef<(HTMLInputElement | null)[]>([])
const [detailShown, setDetailShown] = useState<string[]>([])
const areAllDetailShown = items.every((item) => detailShown.includes(item))
const exportMutation = useWorkspaceExportMutation()

// This ensures that when shift+clicking checkboxes, all checkboxes between the last interacted item are toggled
const handleChange = (
Expand Down Expand Up @@ -121,15 +123,7 @@ const ListGroup = ({
</h5>
<div className="flex gap-1">
{canExport && (
<ActionButton
size="sm"
appearance="outlined"
onClick={() => exportMutation.mutateAsync(items)}
>
<span className="text-xs">
{exportMutation.isLoading ? 'Exporting...' : 'Export'}
</span>
</ActionButton>
<WorkspaceExportPanel listData={listData} />
)}
<ActionButton
size="sm"
Expand Down
Loading

0 comments on commit 7628338

Please sign in to comment.