Skip to content

Commit

Permalink
Merge branch 'main' into feature/error-boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
jthrilly authored Oct 27, 2023
2 parents 21e4d4b + c8d9b39 commit 4b44c04
Show file tree
Hide file tree
Showing 33 changed files with 1,075 additions and 550 deletions.
55 changes: 55 additions & 0 deletions app/(dashboard)/dashboard/_components/ActiveProtocolSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import { useRouter } from 'next/navigation';
import { api } from '~/trpc/client';
import { Switch } from '~/components/ui/switch';

const ActiveProtocolSwitch = ({
initialData,
hash,
}: {
initialData: boolean;
hash: string;
}) => {
const utils = api.useUtils();
const router = useRouter();

const { data: isActive } = api.protocol.getActive.useQuery(hash, {
initialData,
});

const { mutateAsync: setActive } = api.protocol.setActive.useMutation({
async onMutate(variables) {
const { input: newState, hash } = variables;
await utils.protocol.getActive.cancel();

const previousState = utils.protocol.getActive.getData();

if (hash) {
utils.protocol.getActive.setData(hash, newState);
}

return previousState;
},
onError: (err, _newState, previousState) => {
utils.protocol.getActive.setData(hash, previousState);
// eslint-disable-next-line no-console
console.error(err);
},
onSuccess: () => {
router.refresh();
},
});

const handleCheckedChange = async () => {
await setActive({ input: !isActive, hash });
};

return (
<Switch
checked={isActive}
onCheckedChange={() => void handleCheckedChange()}
/>
);
};
export default ActiveProtocolSwitch;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import { MoreHorizontal } from 'lucide-react';
import { Button } from '~/components/ui/Button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import type { Row } from '@tanstack/react-table';
import { useState } from 'react';
import { DeleteInterviewsDialog } from '~/app/(dashboard)/dashboard/interviews/_components/DeleteInterviewsDialog';
import type { Interview } from '@prisma/client';

export const ActionsDropdown = ({ row }: { row: Row<Interview> }) => {
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [interviewToDelete, setInterviewToDelete] = useState<Interview[]>();

const handleDelete = (data: Interview) => {
setInterviewToDelete([data]);
setShowDeleteModal(true);
};

return (
<>
<DeleteInterviewsDialog
open={showDeleteModal}
setOpen={setShowDeleteModal}
interviewsToDelete={interviewToDelete || []}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleDelete(row.original)}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
);
};
48 changes: 2 additions & 46 deletions app/(dashboard)/dashboard/_components/InterviewsTable/Columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,9 @@

import { type ColumnDef } from '@tanstack/react-table';
import type { inferRouterOutputs } from '@trpc/server';
import { ActionsDropdown } from '~/components/DataTable/ActionsDropdown';
import { Checkbox } from '~/components/ui/checkbox';
import { DataTableColumnHeader } from '~/components/DataTable/ColumnHeader';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '~/components/ui/tooltip';
import { Settings } from 'lucide-react';
import { DropdownMenuItem } from '~/components/ui/dropdown-menu';

import { Button } from '~/components/ui/Button';
import Link from 'next/link';
import { Progress } from '~/components/ui/progress';
Expand All @@ -22,9 +14,7 @@ import type { Stage } from '@codaco/shared-consts';
type RouterOutput = inferRouterOutputs<AppRouter>;
type Interviews = RouterOutput['interview']['get']['all'][0];

export const InterviewColumns = (
handleDelete: (id: string) => Promise<void>,
): ColumnDef<Interviews>[] => [
export const InterviewColumns = (): ColumnDef<Interviews>[] => [
{
id: 'select',
header: ({ table }) => (
Expand Down Expand Up @@ -138,38 +128,4 @@ export const InterviewColumns = (
);
},
},
{
id: 'actions',
header: () => (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Settings />
</TooltipTrigger>
<TooltipContent>
<p>Delete an individual interview.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
),
cell: ({ row }) => {
return (
<ActionsDropdown
menuItems={[
{
label: 'Delete',
row,
component: (
<DropdownMenuItem
onClick={() => void handleDelete(row.original.id)}
>
Edit
</DropdownMenuItem>
),
},
]}
/>
);
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,20 @@ import { DataTable } from '~/components/DataTable/DataTable';
import { InterviewColumns } from '~/app/(dashboard)/dashboard/_components/InterviewsTable/Columns';
import { api } from '~/trpc/client';
import { type Interview } from '@prisma/client';

type InterviewWithoutNetwork = Omit<Interview, 'network'>;
import { ActionsDropdown } from '~/app/(dashboard)/dashboard/_components/InterviewsTable/ActionsDropdown';
import { useState } from 'react';
import { DeleteInterviewsDialog } from '../../interviews/_components/DeleteInterviewsDialog';

export const InterviewsTable = () => {
const interviews = api.interview.get.all.useQuery();

const { mutateAsync: deleteInterviews } = api.interview.delete.useMutation({
async onSuccess() {
await interviews.refetch();
},
});
const [interviewsToDelete, setInterviewsToDelete] = useState<Interview[]>();
const [showDeleteModal, setShowDeleteModal] = useState(false);

const handleDelete = async (id: string) => {
const result = await deleteInterviews([{ id }]);
if (result.error) throw new Error(result.error);
const handleDelete = (data: Interview[]) => {
setInterviewsToDelete(data);
setShowDeleteModal(true);
};

if (!interviews.data) {
return <div>Loading...</div>;
}
Expand All @@ -34,13 +31,19 @@ export const InterviewsTable = () => {
}));

return (
<DataTable
columns={InterviewColumns(handleDelete)}
data={convertedData}
filterColumnAccessorKey="id"
handleDeleteSelected={async (data: InterviewWithoutNetwork[]) => {
await deleteInterviews(data);
}}
/>
<>
<DeleteInterviewsDialog
open={showDeleteModal}
setOpen={setShowDeleteModal}
interviewsToDelete={interviewsToDelete || []}
/>
<DataTable
columns={InterviewColumns()}
data={convertedData}
filterColumnAccessorKey="id"
handleDeleteSelected={handleDelete}
actions={ActionsDropdown}
/>
</>
);
};
2 changes: 1 addition & 1 deletion app/(dashboard)/dashboard/_components/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function NavigationBar() {
Home
</NavButton>
<NavButton
href="/dashboard"
href="/dashboard/protocols"
isActive={pathname === '/dashboard/protocols'}
>
Protocols
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use client';

import { MoreHorizontal } from 'lucide-react';
import { Button } from '~/components/ui/Button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import type { Row } from '@tanstack/react-table';
import CopyButton from './CopyButton';
import { useState } from 'react';
import ParticipantModal from '~/app/(dashboard)/dashboard/participants/_components/ParticipantModal';
import type { ParticipantWithInterviews } from '~/shared/types';
import { DeleteParticipantsDialog } from '~/app/(dashboard)/dashboard/participants/_components/DeleteParticipantsDialog';

export const ActionsDropdown = ({
row,
data,
}: {
row: Row<ParticipantWithInterviews>;
data: ParticipantWithInterviews[];
}) => {
const [selectedParticipant, setSelectedParticipant] = useState<string | null>(
null,
);
const [showParticipantModal, setShowParticipantModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [participantToDelete, setParticipantToDelete] =
useState<ParticipantWithInterviews[]>();

const editParticipant = (identifier: string) => {
setSelectedParticipant(identifier);
setShowParticipantModal(true);
};

const handleDelete = (data: ParticipantWithInterviews) => {
setParticipantToDelete([data]);
setShowDeleteModal(true);
};

return (
<>
<ParticipantModal
open={showParticipantModal}
setOpen={setShowParticipantModal}
existingParticipants={data}
editingParticipant={selectedParticipant}
setEditingParticipant={setSelectedParticipant}
/>
<DeleteParticipantsDialog
open={showDeleteModal}
setOpen={setShowDeleteModal}
participantsToDelete={participantToDelete || []}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => editParticipant(row.original.identifier)}
>
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDelete(row.original)}>
Delete
</DropdownMenuItem>

<CopyButton text={`/interview/${row.original.id}`}>
Copy URL
</CopyButton>
</DropdownMenuContent>
</DropdownMenu>
</>
);
};
Loading

0 comments on commit 4b44c04

Please sign in to comment.