Skip to content

Commit

Permalink
Merge pull request #525 from alan-turing-institute/ui-updates
Browse files Browse the repository at this point in the history
Some simple ui updates
  • Loading branch information
RichGriff authored Jul 3, 2024
2 parents 5c55456 + bd3791c commit debc8ef
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 34 deletions.
13 changes: 8 additions & 5 deletions next_frontend/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, Dispatch, SetStateAction } from 'react';
import { Button } from './ui/button';
import { ArrowLeft, Check, ChevronsUpDown, Copy, MessageSquareMore, Search, SearchIcon, X } from 'lucide-react';
import { ModeToggle } from './ui/theme-toggle';
Expand All @@ -10,9 +10,11 @@ import Link from 'next/link';
import { useReactFlow } from 'reactflow';
import SearchNodes from './common/SearchNodes';

interface HeaderProps {}
interface HeaderProps {
setOpen: Dispatch<SetStateAction<boolean>>
}

const Header = ({ }: HeaderProps) => {
const Header = ({ setOpen }: HeaderProps) => {
const { nodes, assuranceCase, setAssuranceCase } = useStore();
const router = useRouter();
const [editName, setEditName] = useState<boolean>(false);
Expand Down Expand Up @@ -95,7 +97,7 @@ const Header = ({ }: HeaderProps) => {
<Button variant={'ghost'} size={'icon'} onClick={() => router.push('/dashboard')} className='hover:bg-indigo-900/20 hover:dark:bg-gray-100/10 hover:text-white'>
<ArrowLeft className='w-4 h-4' />
</Button>
{editName ? (
{/* {editName ? (
<div className='flex justify-start items-center gap-4'>
<input
ref={inputRef}
Expand All @@ -114,7 +116,8 @@ const Header = ({ }: HeaderProps) => {
<p className='font-semibold' onClick={handleEditClick}>
{assuranceCase.name}
</p>
)}
)} */}
<p onClick={() => setOpen(true)} className='font-semibold hover:cursor-pointer'>{assuranceCase.name}</p>
</div>

<div className='flex justify-start items-center gap-4'>
Expand Down
5 changes: 4 additions & 1 deletion next_frontend/components/cases/CaseContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import { ReactFlowProvider } from 'reactflow';

import useStore from '@/data/store';
import { addHiddenProp } from '@/lib/case-helper';
import CaseDetails from './CaseDetails';

const CaseContainer = () => {
// const [assuranceCase, setAssuranceCase] = useState<any>()
const [loading, setLoading] = useState(true)
const { assuranceCase, setAssuranceCase } = useStore();
const [open, setOpen] = useState(false);

const params = useParams()
const { caseId } = params
Expand Down Expand Up @@ -66,8 +68,9 @@ const CaseContainer = () => {
) : (
assuranceCase ? (
<ReactFlowProvider>
<Header />
<Header setOpen={setOpen} />
<Flow />
<CaseDetails isOpen={open} setOpen={setOpen} />
</ReactFlowProvider>
) : (
<p>No Case Found</p>
Expand Down
68 changes: 68 additions & 0 deletions next_frontend/components/cases/CaseDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client'

import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'
import CaseSheet from '../ui/case-sheet'
import useStore from '@/data/store'
import CaseEditForm from './CaseEditForm'
import { AlertModal } from '../modals/alertModal'

interface CaseDetailsProps {
isOpen: boolean
setOpen: Dispatch<SetStateAction<boolean>>
}

const CaseDetails = ({ isOpen, setOpen }: CaseDetailsProps) => {
const [isMounted, setIsMounted] = useState(false);
const [loading, setLoading] = useState(false)
const [unresolvedChanges, setUnresolvedChanges] = useState(false)
const [alertOpen, setAlertOpen] = useState(false)

const { assuranceCase } = useStore();

useEffect(() => {
setIsMounted(true);
}, []);

if (!isMounted) {
return null;
}

const handleClose = () => {
setOpen(false)
setAlertOpen(false)
setUnresolvedChanges(false)
}

const onChange = (open: boolean) => {
if (unresolvedChanges) {
setAlertOpen(true)
} else {
handleClose()
}
};

return (
<CaseSheet
title={`Update Assurance Case`}
description={`Use this form to update your assurance case name and description.`}
isOpen={isOpen}
onClose={handleClose}
onChange={onChange}
>
<div className='my-6'>
<CaseEditForm onClose={handleClose} setUnresolvedChanges={setUnresolvedChanges}/>
<AlertModal
isOpen={alertOpen}
onClose={() => setAlertOpen(false)}
onConfirm={handleClose}
loading={loading}
message={'You have changes that have not been updated. Would you like to discard these changes?'}
confirmButtonText={'Yes, discard changes!'}
cancelButtonText={'No, keep editing'}
/>
</div>
</CaseSheet>
)
}

export default CaseDetails
132 changes: 132 additions & 0 deletions next_frontend/components/cases/CaseEditForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
'use client'

import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
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 { CloudFog, Loader, Loader2, LockIcon, LockKeyhole } from 'lucide-react'
import { getLayoutedElements } from '@/lib/layout-helper'
import { useLoginToken } from '@/hooks/useAuth'
import { findItemById, updateAssuranceCase, updateAssuranceCaseNode, caseItemDescription } from '@/lib/case-helper'

const formSchema = z.object({
name: z.string().min(2, {
message: "Name must be at least 2 characters.",
}).optional(),
description: z.string().min(2, {
message: "Description must be atleast 2 characters"
})
})

interface CaseEditFormProps {
onClose: () => void
setUnresolvedChanges: Dispatch<SetStateAction<boolean>>
};

const CaseEditForm: React.FC<CaseEditFormProps> = ({
onClose,
setUnresolvedChanges
}) => {
const { assuranceCase, setAssuranceCase } = useStore();
const [token] = useLoginToken();
const [loading, setLoading] = useState(false)

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: assuranceCase || {
name: '',
description: ''
}
});

async function onSubmit(values: z.infer<typeof formSchema>) {
setLoading(true)
const updateItem = {
name: values.name,
description: values.description
}

const url = `${process.env.NEXT_PUBLIC_API_URL}/api/cases/${assuranceCase.id}/`
const requestOptions: RequestInit = {
method: "PUT",
headers: {
Authorization: `Token ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(updateItem)
}
const response = await fetch(url, requestOptions);
if(!response.ok) {
console.log('Render a new error')
}

setLoading(false)
setAssuranceCase({ ...assuranceCase, name: updateItem.name, description: updateItem.description })
onClose()
}

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

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8 mt-6">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea rows={8} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className='flex justify-start items-center gap-3'>
<Button type="submit" className="bg-indigo-500 hover:bg-indigo-600 dark:text-white" disabled={loading}>
{loading ? (
<span className='flex justify-center items-center gap-2'><Loader2 className='w-4 h-4 animate-spin' />Updating...</span>
) : (
<span>Update</span>
)}
</Button>
</div>
</form>
</Form>
)
}

export default CaseEditForm
2 changes: 1 addition & 1 deletion next_frontend/components/cases/CaseList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const CaseList = ({ assuranceCases } : CaseListProps) => {
</button>
</div>
</div>
<div className='grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4 w-full'>
<div className='grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4 w-full'>
<button onClick={() => createCaseModal.onOpen()} className='group min-h-[420px]'>
<Card className='h-full flex justify-center items-center border-dashed group-hover:bg-indigo-500/10 transition-all'>
<CardContent className='flex flex-col justify-center items-center gap-2 py-20'>
Expand Down
46 changes: 27 additions & 19 deletions next_frontend/components/common/NodeEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,31 +221,33 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
onClose={handleClose}
onChange={onChange}
>
{node.type !== 'goal' && parentNode && (
<div className="mt-4 flex flex-col text-sm">
<div className="mb-2 flex justify-start items-center gap-2">
<p>Parent Description</p>
{toggleParentDescription ?
(
<Eye className="w-4 h-4" onClick={() => setToggleParentDescription(!toggleParentDescription)} />
) :
(
<EyeOff className="w-4 h-4" onClick={() => setToggleParentDescription(!toggleParentDescription)} />
)}
</div>
{toggleParentDescription && <p className="text-muted-foreground">{parentNode.data.short_description}</p>}
</div>
)}
{selectedLink ? (
<NewLinkForm node={node} linkType={linkToCreate} actions={{ setLinkToCreate, setSelectedLink, handleClose }} setUnresolvedChanges={setUnresolvedChanges} />
) : (
<>

{node.type !== 'goal' && parentNode && (
<div className="mt-6 flex flex-col text-sm">
<div className="mb-2 flex justify-start items-center gap-2">
<p>Parent Description</p>
{toggleParentDescription ?
(
<Eye className="w-4 h-4" onClick={() => setToggleParentDescription(!toggleParentDescription)} />
) :
(
<EyeOff className="w-4 h-4" onClick={() => setToggleParentDescription(!toggleParentDescription)} />
)}
</div>
{toggleParentDescription && <p className="text-muted-foreground">{parentNode.data.short_description}</p>}
</div>
)}

<EditForm node={node} onClose={handleClose} setUnresolvedChanges={setUnresolvedChanges} />

{/* Node specific form buttons */}
{node.type != 'context' && node.type != 'evidence' && (
<div className="flex flex-col justify-start items-start mt-8">
<h3 className="text-lg font-semibold mb-2">Link to {node.data.name}</h3>
<h3 className="text-lg font-semibold mb-2">Add New</h3>
<div className="flex flex-col justify-start items-center gap-4 w-full">
{node.type === 'goal' && (
<>
Expand Down Expand Up @@ -275,7 +277,7 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
{node.type === 'property' &&
<Select onValueChange={setSelectedClaimMove}> {/* Update state on change */}
<SelectTrigger>
<SelectValue placeholder="Select an element" />
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent>
{goal && (
Expand Down Expand Up @@ -330,7 +332,7 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
{node.type === 'evidence' &&
<Select onValueChange={setSelectedEvidenceMove}> {/* Update state on change */}
<SelectTrigger>
<SelectValue placeholder="Move to" />
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent>
{claims && claims.map((claim: any) => (
Expand All @@ -355,7 +357,13 @@ const NodeEdit = ({ node, isOpen, setEditOpen }: NodeEditProps) => {
</SelectContent>
</Select>
}
<Button className="bg-indigo-500 hover:bg-indigo-600 dark:text-white" onClick={handleMove}>Move</Button>
<Button
variant={"outline"}
// className="bg-indigo-500 hover:bg-indigo-600 dark:text-white"
onClick={handleMove}
>
Move
</Button>
</div>
</div>
) : null}
Expand Down
Loading

0 comments on commit debc8ef

Please sign in to comment.