Skip to content

Commit

Permalink
Merge pull request #669 from alan-turing-institute/652-fix
Browse files Browse the repository at this point in the history
UI updates
  • Loading branch information
RichGriff authored Jan 23, 2025
2 parents 8492b1a + 58f3c77 commit 3b44765
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 54 deletions.
169 changes: 169 additions & 0 deletions next_frontend/components/cases/NodeAttributes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
'use client'

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 { updateAssuranceCase, updateAssuranceCaseNode } from '@/lib/case-helper'
import { useSession } from 'next-auth/react'
import { MinusIcon, PlusIcon } from 'lucide-react'

const formSchema = z.object({
assumption: z.string().optional(),
justification: z.string().optional()
})

interface NodeAttributesProps {
node: any;
actions: any
onClose: () => void
setUnresolvedChanges: Dispatch<SetStateAction<boolean>>
};

const NodeAttributes: React.FC<NodeAttributesProps> = ({
node,
actions,
onClose,
setUnresolvedChanges
}) => {
const { assuranceCase, setAssuranceCase } = useStore();
const { data: session } = useSession()
const [loading, setLoading] = useState<boolean>(false)
const [newAssumption, setNewAssumption] = useState<boolean>(false)
const [newJustification, setNewJustification] = useState<boolean>(false)

const { setSelectedLink, setAction } = actions

const reset = () => {
setSelectedLink(false)
setAction('')
}

const handleCancel = () => {
form.reset(); // Reset the form state
reset(); // Perform additional reset actions
};

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: node.data || {
assumption: '',
justification: ''
}
});

async function onSubmit(values: z.infer<typeof formSchema>) {
setLoading(true)
// Update item via api
const updateItem = {
assumption: values.assumption,
justification: values.justification,
}

const updated = await updateAssuranceCaseNode(node.type, node.data.id, session?.key ?? '', updateItem)

if(updated) {
// Assurance Case Update
const updatedAssuranceCase = await updateAssuranceCase(node.type, assuranceCase, updateItem, node.data.id, node)
if(updatedAssuranceCase) {
setAssuranceCase(updatedAssuranceCase)
setLoading(false)
onClose()
}
}
}

let readOnly = (assuranceCase.permissions === 'view' || assuranceCase.permissions === 'review') ? true : false

// useEffect(() => {
// form.watch((values, { name }) => {
// if (name === 'assumption') {
// setUnresolvedChanges(true);
// }
// });
// }, [form.watch, setUnresolvedChanges]);

return (
<div className='my-4 border-t'>
<div className='mt-4 font-medium text-muted-foreground text-sm'>
Please use this section to manage attributes for this element.
</div>

<div className='mt-4 flex justify-start items-center gap-2'>
{!node.data.assumption && (
<Button
variant={'outline'}
size={'sm'}
onClick={() => setNewAssumption(!newAssumption)}
>
{newAssumption ? <MinusIcon className='size-3 mr-2' /> : <PlusIcon className='size-3 mr-2' />}
Assumption
</Button>
)}
{!node.data.justification && node.type === 'strategy' && (
<Button
variant={'outline'}
size={'sm'}
onClick={() => setNewJustification(!newJustification)}
>
{newJustification ? <MinusIcon className='size-3 mr-2' /> : <PlusIcon className='size-3 mr-2' />}
Justification
</Button>
)}
</div>

<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 my-4">
{(node.data.assumption || newAssumption) && (
<FormField
control={form.control}
name="assumption"
render={({ field }) => (
<FormItem>
<FormLabel>Assumption</FormLabel>
<FormControl>
<Textarea placeholder="Type your assumption here." rows={5} {...field} readOnly={readOnly}/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
{node.type === 'strategy' && (node.data.justification || newJustification) && (
<FormField
control={form.control}
name="justification"
render={({ field }) => (
<FormItem>
<FormLabel>Justification</FormLabel>
<FormControl>
<Textarea placeholder="Type your justification here." rows={5} {...field} readOnly={readOnly} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<div className='flex justify-start items-center gap-3 pt-4'>
<Button variant={"outline"} onClick={handleCancel}>Cancel</Button>
<Button type="submit" disabled={loading} className="bg-indigo-500 hover:bg-indigo-600 dark:text-white">
{loading ? 'Saving...' : 'Update Attributes'}</Button>
</div>
</form>
</Form>
</div>
)
}

export default NodeAttributes
52 changes: 0 additions & 52 deletions next_frontend/components/common/EditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ const formSchema = z.object({
description: z.string().min(2, {
message: "Description must be atleast 2 characters"
}),
assumption: z.string().optional(),
justification: z.string().optional()
})

interface EditFormProps {
Expand All @@ -55,8 +53,6 @@ const EditForm: React.FC<EditFormProps> = ({
defaultValues: node.data || {
URL: '',
description: '',
assumption: '',
justification: ''
}
});

Expand All @@ -65,8 +61,6 @@ const EditForm: React.FC<EditFormProps> = ({
// Update item via api
const updateItem = {
short_description: values.description,
assumption: values.assumption,
justification: values.justification
}

if(node.type === 'evidence') {
Expand Down Expand Up @@ -123,52 +117,6 @@ const EditForm: React.FC<EditFormProps> = ({
</FormItem>
)}
/>
{node.type !== 'evidence' && (
<FormField
control={form.control}
name="assumption"
render={({ field }) => (
<FormItem>
<FormLabel className='flex justify-start items-center gap-2'>
Assumption
{readOnly && (
<span title='Read Only' className='flex justify-start items-center gap-2 text-xs text-muted-foreground py-2'><Lock className='w-3 h-3' /></span>
)}
</FormLabel>
<FormControl>
<Textarea
placeholder="" {...field}
readOnly={readOnly}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
{node.type !== 'evidence' && node.type === 'strategy' && (
<FormField
control={form.control}
name="justification"
render={({ field }) => (
<FormItem>
<FormLabel className='flex justify-start items-center gap-2'>
Justification
{readOnly && (
<span title='Read Only' className='flex justify-start items-center gap-2 text-xs text-muted-foreground py-2'><Lock className='w-3 h-3' /></span>
)}
</FormLabel>
<FormControl>
<Textarea
placeholder="" {...field}
readOnly={readOnly}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
{node.type === 'evidence' && (
<FormField
control={form.control}
Expand Down
14 changes: 12 additions & 2 deletions next_frontend/components/common/NodeEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Button } from "@/components/ui/button"
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import EditSheet from "../ui/edit-sheet";
import { BookOpenText, CloudFog, Eye, EyeOff, MessageCirclePlus, Move, Plus, PlusCircle, Trash2, Unplug } from "lucide-react"
import { BookOpenText, CloudFog, Eye, EyeOff, LibraryIcon, MessageCirclePlus, Move, Plus, PlusCircle, Trash2, Unplug } from "lucide-react"
import EditForm from "./EditForm";
import { Autour_One } from "next/font/google";
import { addEvidenceToClaim, addPropertyClaimToNested, createAssuranceCaseNode, deleteAssuranceCaseNode, listPropertyClaims, setNodeIdentifier, updateAssuranceCaseNode, caseItemDescription, updateAssuranceCase, removeAssuranceCaseNode, extractGoalsClaimsStrategies, findElementById, getChildrenHiddenStatus, findSiblingHiddenState, findParentNode, detachCaseElement } from "@/lib/case-helper";
Expand All @@ -26,6 +26,7 @@ import { ChatBubbleIcon } from "@radix-ui/react-icons";
import NodeComment from "../cases/NodeComments";
import { useSession } from "next-auth/react";
import NodeContext from "../cases/NodeContext";
import NodeAttributes from "../cases/NodeAttributes";

interface NodeEditProps {
node: Node | any
Expand Down Expand Up @@ -284,8 +285,14 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
Manage Context
</Button>
)}
{node.type !== 'evidence' && (
<Button variant={'outline'} onClick={() => setAction('attributes')} className="w-full">
<LibraryIcon className="w-4 h-4 mr-2"/>
Manage Attributes
</Button>
)}
{node.type !== 'context' && node.type !== 'evidence' && (
<Button variant={'outline'} onClick={() => setAction('new')} className="w-full"><PlusCircle className="w-4 h-4 mr-2"/>Add New</Button>
<Button variant={'outline'} onClick={() => setAction('new')} className="w-full"><PlusCircle className="w-4 h-4 mr-2"/>Add New Element</Button>
)}
{node.type !== 'context' && node.type !== 'evidence' && (
<Button variant={'outline'} onClick={() => setAction('existing')} className="w-full"><Unplug className="w-4 h-4 mr-2"/>Reattach Element(s)</Button>
Expand Down Expand Up @@ -503,6 +510,9 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
{action === 'context' && (
<NodeContext node={node} actions={{ setSelectedLink, handleClose, setAction }} setUnresolvedChanges={setUnresolvedChanges} />
)}
{action === 'attributes' && (
<NodeAttributes node={node} onClose={handleClose} actions={{ setSelectedLink, handleClose, setAction }} setUnresolvedChanges={setUnresolvedChanges} />
)}
<AlertModal
isOpen={deleteOpen}
onClose={() => setDeleteOpen(false)}
Expand Down

0 comments on commit 3b44765

Please sign in to comment.