diff --git a/web/src/features/responses/components/MediaFilesCell/MediaFilesCell.tsx b/web/src/features/responses/components/MediaFilesCell/MediaFilesCell.tsx index aaef00bac..3434654b1 100644 --- a/web/src/features/responses/components/MediaFilesCell/MediaFilesCell.tsx +++ b/web/src/features/responses/components/MediaFilesCell/MediaFilesCell.tsx @@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'; import { cn, getFileCategory } from '@/lib/utils'; import { ArrowDownTrayIcon, DocumentIcon, FilmIcon, MusicalNoteIcon, PhotoIcon } from '@heroicons/react/24/outline'; +import axios from 'axios'; import { useMemo } from 'react'; import ReactPlayer from 'react-player'; import { Attachment } from '../../models/common'; @@ -17,30 +18,27 @@ export default function MediaFilesCell({ attachment, className }: MediaFilesCell return getFileCategory(attachment.mimeType); }, [attachment.mimeType]); - // const handleDownload = async (e: React.MouseEvent) => { - // e.stopPropagation(); - // try { - // // throw new Error("uncomment this line to mock failure of API"); - // const response = await axios.get(attachment.presignedUrl, { - // responseType: 'blob', - // headers: { - // 'Access-Control-Allow-Origin': '*', - // }, - // }); + const handleDownload = async (e: React.MouseEvent) => { + e.stopPropagation(); + try { + const response = await axios.get(attachment.presignedUrl, { + responseType: 'blob', + headers: {}, + }); - // // Create download link - // const url = window.URL.createObjectURL(new Blob([response.data])); - // const link = document.createElement('a'); - // link.href = url; - // link.download = attachment.fileName; - // document.body.appendChild(link); - // link.click(); - // document.body.removeChild(link); - // window.URL.revokeObjectURL(url); - // } catch (error) { - // console.error('Download failed:', error); - // } - // }; + // Create download link + const url = window.URL.createObjectURL(new Blob([response.data])); + const link = document.createElement('a'); + link.href = url; + link.download = attachment.fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error('Download failed:', error); + } + }; const renderPreview = (isInDialog = false) => { const baseClasses = 'object-cover rounded-md transition-opacity duration-200'; @@ -116,7 +114,7 @@ export default function MediaFilesCell({ attachment, className }: MediaFilesCell return ( - +
{renderPreview(false)} @@ -147,14 +145,12 @@ export default function MediaFilesCell({ attachment, className }: MediaFilesCell
diff --git a/web/src/features/responses/components/ResponseExtraDataTable/ResponseExtraDataTable.tsx b/web/src/features/responses/components/ResponseExtraDataTable/ResponseExtraDataTable.tsx index 482209972..2cdd16f12 100644 --- a/web/src/features/responses/components/ResponseExtraDataTable/ResponseExtraDataTable.tsx +++ b/web/src/features/responses/components/ResponseExtraDataTable/ResponseExtraDataTable.tsx @@ -8,51 +8,60 @@ type ResponseExtraDataTableProps = { data: QuestionExtraData[]; }; +import { getFilteredRowModel, SortingState } from '@tanstack/react-table'; +import * as React from 'react'; + export function ResponseExtraDataTable({ columns, data }: ResponseExtraDataTableProps): FunctionComponent { + const [sorting, setSorting] = React.useState([]); + const table = useReactTable({ - columns, data, + columns, + onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + state: { + sorting, + }, }); - const rows = table.getRowModel().rows; - return ( - <> - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - - ); - })} - - ))} - - - - {rows.length > 0 ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - {flexRender(cell.column.columnDef.cell, cell.getContext())} - ))} +
+
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} + ))} + + )) + ) : ( + + + No results. + - )) - ) : ( - - - No results. - - - )} - -
- + )} + + + + ); } diff --git a/web/src/features/responses/utils/column-defs.tsx b/web/src/features/responses/utils/column-defs.tsx index 5a4f44f4c..5f31c8851 100644 --- a/web/src/features/responses/utils/column-defs.tsx +++ b/web/src/features/responses/utils/column-defs.tsx @@ -1,11 +1,3 @@ -import TableTagList from '@/components/table-tag-list/TableTagList'; -import { Badge } from '@/components/ui/badge'; -import { DataTableColumnHeader } from '@/components/ui/DataTable/DataTableColumnHeader'; -import { cn } from '@/lib/utils'; -import { ArrowTopRightOnSquareIcon, ChevronRightIcon } from '@heroicons/react/24/outline'; -import { Link } from '@tanstack/react-router'; -import { format } from 'date-fns'; - import { DateTimeFormat } from '@/common/formats'; import { CitizenReportFollowUpStatus, @@ -13,9 +5,18 @@ import { IncidentReportFollowUpStatus, QuickReportFollowUpStatus, } from '@/common/types'; +import TableTagList from '@/components/table-tag-list/TableTagList'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import type { RowData } from '@/components/ui/DataTable/DataTable'; +import { DataTableColumnHeader } from '@/components/ui/DataTable/DataTableColumnHeader'; +import { cn } from '@/lib/utils'; +import { ArrowTopRightOnSquareIcon, ChevronRightIcon } from '@heroicons/react/24/outline'; +import { Link } from '@tanstack/react-router'; import type { ColumnDef } from '@tanstack/react-table'; +import { format } from 'date-fns'; +import { ArrowDown, ArrowUp, ArrowUpDown } from 'lucide-react'; +import MediaFilesCell from '../components/MediaFilesCell/MediaFilesCell'; import { CitizenReportsAggregatedByForm, type CitizenReportByEntry } from '../models/citizen-report'; import { SubmissionType } from '../models/common'; import { @@ -27,7 +28,6 @@ import { IncidentReportByEntry, IncidentReportByForm, IncidentReportByObserver } import { type QuickReport } from '../models/quick-report'; import type { QuestionExtraData } from '../types'; import { mapIncidentCategory, mapIncidentReportLocationType, mapQuickReportLocationType } from './helpers'; -import MediaFilesCell from '../components/MediaFilesCell/MediaFilesCell'; export const formSubmissionsByEntryColumnDefs: ColumnDef[] = [ { @@ -518,35 +518,49 @@ export const formSubmissionsByFormColumnDefs: ColumnDef[] = [ { - header: ({ column }) => , - accessorFn: (row) => row.type, - id: 'type', - enableSorting: true, - enableGlobalFilter: true, - cell: ({ row }) =>
{row.original.type}
, - size: 80, // fixed width in px - minSize: 60, // minimum allowed width - maxSize: 120, // optional max width + accessorKey: 'type', + header: () =>
Type
, + cell: ({ row }) =>
{row.getValue('type')}
, + size: 80, + enableResizing: false, }, { - header: ({ column }) => , - accessorFn: (row) => row.timeSubmitted, - id: 'timeSubmitted', - enableSorting: true, - enableGlobalFilter: true, - cell: ({ row }) =>
{format(row.original.timeSubmitted, DateTimeFormat)}
, - size: 80, // fixed width in px - minSize: 60, // minimum allowed width - maxSize: 120, // optional max width + accessorKey: 'timeSubmitted', + header: ({ column }) => { + const isSorted = column.getIsSorted(); + + return ( + + ); + }, + cell: ({ row }) => { + const formatted = format(row.original.timeSubmitted, DateTimeFormat); + + return
{formatted}
; + }, + size: 80, + enableResizing: false, }, + { - header: ({ column }) => , id: 'preview', enableSorting: false, enableGlobalFilter: false, cell: ({ row }) => ( -
{row.original.type === 'Note' ? row.original.text : }
+
+ {row.original.type === 'Note' ? row.original.text : } +
), + size: 300, }, ];