[questions][feat] add, delete question lists

pull/468/head
Jeff Sieu 3 years ago
parent 753593cd08
commit cf94cbce10

@ -1,48 +0,0 @@
import { Button, Dialog } from '@tih/ui';
export type CreateListDialogProps = {
onCancel: () => void;
onCreate: () => void;
show: boolean;
};
export default function CreateListDialog({
show,
onCancel,
onCreate,
}: CreateListDialogProps) {
return (
<Dialog
isShown={show}
primaryButton={undefined}
title="Create a new question list"
onClose={onCancel}>
<form className="mt-5 gap-2 sm:flex sm:items-center">
<div className="w-full sm:max-w-xs">
<input
className="focus:border-primary-500 focus:ring-primary-500 block w-full rounded-md border-slate-300 shadow-sm sm:text-sm"
id="listName"
name="listName"
placeholder="Enter list name"
type="text"
/>
</div>
<Button
display="inline"
label="Cancel"
size="md"
type="submit"
variant="tertiary"
onClick={onCancel}></Button>
<Button
display="inline"
label="Create"
size="md"
type="submit"
variant="primary"
onClick={onCreate}></Button>
</form>
</Dialog>
);
}

@ -0,0 +1,67 @@
import { useForm } from 'react-hook-form';
import { Button, Dialog, TextInput } from '@tih/ui';
import { useFormRegister } from '~/utils/questions/useFormRegister';
export type CreateListFormData = {
name: string;
};
export type CreateListDialogProps = {
onCancel: () => void;
onSubmit: (data: CreateListFormData) => Promise<void>;
show: boolean;
};
export default function CreateListDialog({
show,
onCancel,
onSubmit,
}: CreateListDialogProps) {
const {
register: formRegister,
handleSubmit,
formState: { isSubmitting },
} = useForm<CreateListFormData>();
const register = useFormRegister(formRegister);
return (
<Dialog
isShown={show}
primaryButton={undefined}
title="Create question list"
onClose={onCancel}>
<form
className="mt-5 gap-2 sm:flex sm:items-center"
onSubmit={handleSubmit(onSubmit)}>
<div className="w-full sm:max-w-xs">
<TextInput
id="listName"
isLabelHidden={true}
{...register('name')}
autoComplete="off"
label="Name"
placeholder="List name"
type="text"
/>
</div>
<Button
display="inline"
label="Cancel"
size="md"
type="submit"
variant="tertiary"
onClick={onCancel}
/>
<Button
display="inline"
isLoading={isSubmitting}
label="Create"
size="md"
type="submit"
variant="primary"
/>
</form>
</Dialog>
);
}

@ -8,13 +8,15 @@ import {
} from '@heroicons/react/24/outline';
import QuestionListCard from '~/components/questions/card/question/QuestionListCard';
import CreateListDialog from '~/components/questions/CreatListDialog';
import type { CreateListFormData } from '~/components/questions/CreateListDialog';
import CreateListDialog from '~/components/questions/CreateListDialog';
import DeleteListDialog from '~/components/questions/DeleteListDialog';
import { Button } from '~/../../../packages/ui/dist';
import { APP_TITLE } from '~/utils/questions/constants';
import { SAMPLE_QUESTION } from '~/utils/questions/constants';
import createSlug from '~/utils/questions/createSlug';
import { trpc } from '~/utils/trpc';
export const questions = [
SAMPLE_QUESTION,
@ -34,22 +36,40 @@ export const questions = [
SAMPLE_QUESTION,
];
export const lists = [
{ id: 1, name: 'list 1', questions },
{ id: 2, name: 'list 2', questions },
{ id: 3, name: 'list 3', questions },
{ id: 4, name: 'list 4', questions },
{ id: 5, name: 'list 5', questions },
];
export default function ListPage() {
const utils = trpc.useContext();
const { data: lists } = trpc.useQuery(['questions.lists.getListsByUser']);
const { mutateAsync: createList } = trpc.useMutation(
'questions.lists.create',
{
onSuccess: () => {
// TODO: Add optimistic update
utils.invalidateQueries(['questions.lists.getListsByUser']);
},
},
);
const { mutateAsync: deleteList } = trpc.useMutation(
'questions.lists.delete',
{
onSuccess: () => {
// TODO: Add optimistic update
utils.invalidateQueries(['questions.lists.getListsByUser']);
},
},
);
const [selectedList, setSelectedList] = useState(
(lists ?? []).length > 0 ? lists[0].id : '',
lists?.length ? lists[0].id : '',
);
const [showDeleteListDialog, setShowDeleteListDialog] = useState(false);
const [showCreateListDialog, setShowCreateListDialog] = useState(false);
const handleDeleteList = () => {
const [listIdToDelete, setListIdToDelete] = useState('');
const handleDeleteList = async (listId: string) => {
await deleteList({
id: listId,
});
setShowDeleteListDialog(false);
};
@ -57,7 +77,10 @@ export default function ListPage() {
setShowDeleteListDialog(false);
};
const handleCreateList = () => {
const handleCreateList = async (data: CreateListFormData) => {
await createList({
name: data.name,
});
setShowCreateListDialog(false);
};
@ -68,7 +91,7 @@ export default function ListPage() {
const listOptions = (
<>
<ul className="flex flex-1 flex-col divide-y divide-solid divide-slate-200">
{lists.map((list) => (
{(lists ?? []).map((list) => (
<li
key={list.id}
className={`flex items-center hover:bg-slate-50 ${
@ -107,7 +130,10 @@ export default function ListPage() {
: 'text-slate-900'
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
type="button"
onClick={() => setShowDeleteListDialog(true)}>
onClick={() => {
setShowDeleteListDialog(true);
setListIdToDelete(list.id);
}}>
Delete
</button>
)}
@ -134,7 +160,7 @@ export default function ListPage() {
</Head>
<main className="flex flex-1 flex-col items-stretch">
<div className="flex h-full flex-1">
<aside className="w-[300px] overflow-y-auto border-l bg-white py-4 lg:block">
<aside className="w-[300px] overflow-y-auto border-r bg-white py-4 lg:block">
<div className="mb-2 flex items-center justify-between">
<h2 className="px-4 text-xl font-semibold">My Lists</h2>
<div className="px-4">
@ -198,11 +224,13 @@ export default function ListPage() {
<DeleteListDialog
show={showDeleteListDialog}
onCancel={handleDeleteListCancel}
onDelete={handleDeleteList}></DeleteListDialog>
onDelete={() => {
handleDeleteList(listIdToDelete);
}}></DeleteListDialog>
<CreateListDialog
show={showCreateListDialog}
onCancel={handleCreateListCancel}
onCreate={handleCreateList}></CreateListDialog>
onSubmit={handleCreateList}></CreateListDialog>
</section>
</div>
</main>

@ -9,6 +9,7 @@ import { offersProfileRouter } from './offers/offers-profile-router';
import { protectedExampleRouter } from './protected-example-router';
import { questionsAnswerCommentRouter } from './questions-answer-comment-router';
import { questionsAnswerRouter } from './questions-answer-router';
import { questionListRouter } from './questions-list-router';
import { questionsQuestionCommentRouter } from './questions-question-comment-router';
import { questionsQuestionEncounterRouter } from './questions-question-encounter-router';
import { questionsQuestionRouter } from './questions-question-router';
@ -40,6 +41,7 @@ export const appRouter = createRouter()
.merge('resumes.comments.votes.user.', resumesCommentsVotesUserRouter)
.merge('questions.answers.comments.', questionsAnswerCommentRouter)
.merge('questions.answers.', questionsAnswerRouter)
.merge('questions.lists.', questionListRouter)
.merge('questions.questions.comments.', questionsQuestionCommentRouter)
.merge('questions.questions.encounters.', questionsQuestionEncounterRouter)
.merge('questions.questions.', questionsQuestionRouter)

@ -14,16 +14,16 @@ export const questionListRouter = createProtectedRouter()
include: {
question: true,
},
}
},
},
orderBy: {
createdAt: 'asc',
},
where: {
id: userId,
userId,
},
});
}
},
})
.query('getListById', {
input: z.object({
@ -38,7 +38,7 @@ export const questionListRouter = createProtectedRouter()
include: {
question: true,
},
}
},
},
orderBy: {
createdAt: 'asc',
@ -47,7 +47,7 @@ export const questionListRouter = createProtectedRouter()
id: userId,
},
});
}
},
})
.mutation('create', {
input: z.object({
@ -111,7 +111,7 @@ export const questionListRouter = createProtectedRouter()
},
});
if (listToDelete?.id !== userId) {
if (listToDelete?.userId !== userId) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'User have no authorization to record.',
@ -163,11 +163,12 @@ export const questionListRouter = createProtectedRouter()
async resolve({ ctx, input }) {
const userId = ctx.session?.user?.id;
const entryToDelete = await ctx.prisma.questionsListQuestionEntry.findUnique({
where: {
id: input.id,
},
});
const entryToDelete =
await ctx.prisma.questionsListQuestionEntry.findUnique({
where: {
id: input.id,
},
});
if (entryToDelete?.id !== userId) {
throw new TRPCError({
@ -176,7 +177,6 @@ export const questionListRouter = createProtectedRouter()
});
}
const listToAugment = await ctx.prisma.questionsList.findUnique({
where: {
id: entryToDelete.listId,
Loading…
Cancel
Save