-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #624 from alan-turing-institute/437-element-comments
437 element comments
- Loading branch information
Showing
8 changed files
with
617 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import React, { Dispatch, SetStateAction, useState } from 'react' | ||
import { | ||
Form, | ||
FormControl, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage, | ||
} from "@/components/ui/form" | ||
import { z } from "zod" | ||
import { zodResolver } from "@hookform/resolvers/zod" | ||
import { useForm } from "react-hook-form" | ||
import { Textarea } from "../ui/textarea" | ||
import { Button } from '../ui/button' | ||
import useStore from '@/data/store'; | ||
import { useLoginToken } from '@/hooks/useAuth' | ||
import { addElementComment } from '@/lib/case-helper' | ||
|
||
const formSchema = z.object({ | ||
comment: z.string().min(2, { | ||
message: "Comment must be atleast 2 characters" | ||
}) | ||
}) | ||
|
||
interface CommentsFormProps { | ||
node: any | ||
}; | ||
|
||
const CommentsForm: React.FC<CommentsFormProps> = ({ node }: CommentsFormProps) => { | ||
const { assuranceCase, setAssuranceCase, nodeComments, setNodeComments } = useStore(); | ||
const [token] = useLoginToken(); | ||
const [loading, setLoading] = useState(false) | ||
|
||
const form = useForm<z.infer<typeof formSchema>>({ | ||
resolver: zodResolver(formSchema), | ||
defaultValues: { | ||
comment: '' | ||
} | ||
}); | ||
|
||
async function onSubmit(values: z.infer<typeof formSchema>) { | ||
console.log(values) | ||
setLoading(true) | ||
|
||
let newComment = { | ||
content: values.comment | ||
} as any | ||
|
||
let entity = null; | ||
switch (node.type) { | ||
case "context": | ||
entity = "contexts"; | ||
newComment.context = node.data.id | ||
break; | ||
case "strategy": | ||
entity = "strategies"; | ||
newComment.strategy = node.data.id | ||
break; | ||
case "property": | ||
entity = "propertyclaims"; | ||
newComment.property_claim = node.data.id | ||
break; | ||
case "evidence": | ||
entity = "evidence"; | ||
newComment.evidence = node.data.id | ||
break; | ||
default: | ||
entity = "goals"; | ||
newComment.goal = node.data.id | ||
break; | ||
} | ||
|
||
try { | ||
let url = `${process.env.NEXT_PUBLIC_API_URL}/api/${entity}/${node.data.id}/comments/`; | ||
|
||
const requestOptions: RequestInit = { | ||
method: "POST", | ||
headers: { | ||
Authorization: `Token ${token}`, | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(newComment) | ||
}; | ||
|
||
const response = await fetch(url, requestOptions); | ||
const result = await response.json() | ||
|
||
// **Update the comments as an array** | ||
const newCommentsList = [...nodeComments, result] | ||
|
||
setNodeComments(newCommentsList) | ||
|
||
// Clear form input | ||
form.setValue('comment', '') | ||
} catch (error) { | ||
console.log('Error', error) | ||
} finally { | ||
setLoading(false) | ||
} | ||
} | ||
|
||
return ( | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 mt-2 w-full"> | ||
<FormField | ||
control={form.control} | ||
name="comment" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel className='hidden'>New Comment</FormLabel> | ||
<FormControl> | ||
<Textarea placeholder="Type your comment here." rows={5} {...field} /> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<div className='flex justify-start items-center gap-3'> | ||
<Button type="submit" disabled={loading} className="bg-indigo-500 hover:bg-indigo-600 text-white"> | ||
{loading ? 'Adding...' : 'Add Comment'} | ||
</Button> | ||
</div> | ||
</form> | ||
</Form> | ||
) | ||
} | ||
|
||
export default CommentsForm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
'use client' | ||
|
||
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react' | ||
import { boolean, z } from "zod" | ||
import { zodResolver } from "@hookform/resolvers/zod" | ||
import { useForm } from "react-hook-form" | ||
import { Button } from "@/components/ui/button" | ||
import { | ||
Form, | ||
FormControl, | ||
FormDescription, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage, | ||
} from "@/components/ui/form" | ||
import { Textarea } from '../ui/textarea' | ||
import { useLoginToken } from '@/hooks/useAuth' | ||
import useStore from '@/data/store' | ||
import { updateElementComment } from '@/lib/case-helper' | ||
import { useToast } from '../ui/use-toast' | ||
|
||
type CommentsEditFormProps = { | ||
node: any | ||
comment: any | ||
setEdit: Dispatch<SetStateAction<boolean>> | ||
} | ||
|
||
const formSchema = z.object({ | ||
comment: z.string().min(2).max(500), | ||
}) | ||
|
||
const CommentsEditForm = ({ node, comment, setEdit } : CommentsEditFormProps ) => { | ||
const [token] = useLoginToken(); | ||
const { assuranceCase, setAssuranceCase, nodeComments, setNodeComments } = useStore() | ||
const [loading, setLoading] = useState<boolean>(false) | ||
const textareaRef = useRef<HTMLTextAreaElement | null>(null); // Ref for the textarea | ||
|
||
const { id: commentId, content } = comment | ||
const { toast } = useToast(); | ||
|
||
const form = useForm<z.infer<typeof formSchema>>({ | ||
resolver: zodResolver(formSchema), | ||
defaultValues: { | ||
comment: content, | ||
} | ||
}) | ||
|
||
async function onSubmit(values: z.infer<typeof formSchema>) { | ||
setLoading(true) | ||
|
||
const newComment = { | ||
content: values.comment | ||
} | ||
|
||
try { | ||
let url = `${process.env.NEXT_PUBLIC_API_URL}/api/comments/${commentId}/` | ||
|
||
const requestOptions: RequestInit = { | ||
method: "PUT", | ||
headers: { | ||
Authorization: `Token ${token}`, | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(newComment), | ||
}; | ||
const response = await fetch(url, requestOptions); | ||
|
||
if(!response.ok) { | ||
toast({ | ||
variant: 'destructive', | ||
title: 'Failed to update comment', | ||
description: 'Something went wrong trying to update the comment.', | ||
}); | ||
return | ||
} | ||
|
||
const updatedComment = await response.json(); | ||
|
||
// Find the index of the updated comment in the existing comments array | ||
const updatedComments = nodeComments.map((comment:any) => | ||
comment.id === updatedComment.id ? updatedComment : comment | ||
); | ||
|
||
setNodeComments(updatedComments); | ||
setEdit(false); | ||
} catch (error) { | ||
toast({ | ||
variant: 'destructive', | ||
title: 'Failed to update comment', | ||
description: 'Something went wrong trying to update the comment.', | ||
}); | ||
} finally { | ||
setLoading(false) | ||
} | ||
} | ||
|
||
// Function to adjust the textarea height dynamically | ||
const autoResizeTextarea = () => { | ||
if (textareaRef.current) { | ||
textareaRef.current.style.height = 'auto'; // Reset the height | ||
textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px'; // Set the height to match content | ||
} | ||
} | ||
|
||
// Resize the textarea when the content or the form loads | ||
useEffect(() => { | ||
autoResizeTextarea(); // Initial resize | ||
}, [form.watch('comment')]) // Re-run when the comment changes | ||
|
||
return ( | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> | ||
<FormField | ||
control={form.control} | ||
name="comment" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormControl> | ||
<Textarea | ||
placeholder="Type your message here." | ||
{...field} | ||
ref={(e) => { | ||
field.ref(e); // Integrate with react-hook-form | ||
textareaRef.current = e; // Set the local ref | ||
}} | ||
onInput={autoResizeTextarea} // Auto-resize on input | ||
style={{ overflow: 'hidden' }} // Hide scrollbars | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<div className='flex justify-end items-center gap-2'> | ||
<Button variant={'ghost'} className={'hover:bg-indigo-800/50'} onClick={() => setEdit(false)}>Cancel</Button> | ||
<Button type="submit" disabled={loading}> | ||
{loading ? 'Saving' : 'Save'} | ||
</Button> | ||
</div> | ||
</form> | ||
</Form> | ||
) | ||
} | ||
|
||
export default CommentsEditForm |
Oops, something went wrong.