From 360e06950e1f42b160ffdc1c065828a1b9775ba1 Mon Sep 17 00:00:00 2001 From: Richard Griffiths Date: Thu, 13 Jun 2024 16:12:39 +0100 Subject: [PATCH 1/7] identifiers working and chart reload on creation --- .../migrations/0016_auto_20240613_1028.py | 23 ++++++++ eap_backend/eap_api/urls.py | 14 +++-- eap_backend/eap_api/views.py | 52 ++++++++++++++++++- eap_backend/eap_backend/settings.py | 21 ++++---- next_frontend/.env.local | 4 +- next_frontend/components/cases/GoalNode.tsx | 16 ++++-- .../components/common/CreateForm.tsx | 5 +- .../components/common/NewLinkForm.tsx | 18 ++++--- next_frontend/lib/convert-case.ts | 2 + next_frontend/package-lock.json | 47 +++++++++++------ next_frontend/package.json | 1 + next_frontend/tailwind.config.ts | 6 ++- 12 files changed, 166 insertions(+), 43 deletions(-) create mode 100644 eap_backend/eap_api/migrations/0016_auto_20240613_1028.py diff --git a/eap_backend/eap_api/migrations/0016_auto_20240613_1028.py b/eap_backend/eap_api/migrations/0016_auto_20240613_1028.py new file mode 100644 index 00000000..1855fd3b --- /dev/null +++ b/eap_backend/eap_api/migrations/0016_auto_20240613_1028.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2024-06-13 10:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('eap_api', '0015_githubrepository'), + ] + + operations = [ + migrations.AlterField( + model_name='strategy', + name='long_description', + field=models.CharField(max_length=3000), + ), + migrations.AlterField( + model_name='strategy', + name='short_description', + field=models.CharField(max_length=1000), + ), + ] diff --git a/eap_backend/eap_api/urls.py b/eap_backend/eap_api/urls.py index 6dc47428..bbd68e87 100644 --- a/eap_backend/eap_api/urls.py +++ b/eap_backend/eap_api/urls.py @@ -16,8 +16,16 @@ path("goals//", views.goal_detail, name="goal_detail"), path("contexts/", views.context_list, name="context_list"), path("contexts//", views.context_detail, name="context_detail"), - path("propertyclaims/", views.property_claim_list, name="property_claim_list"), - path("comments//", views.comment_list, name="comment_list"), + path( + "propertyclaims/", + views.property_claim_list, + name="property_claim_list", + ), + path( + "comments//", + views.comment_list, + name="comment_list", + ), path( "comments//reply/", views.reply_to_comment, @@ -52,4 +60,4 @@ views.github_repository_list, name="github_repository_list", ), -] +] \ No newline at end of file diff --git a/eap_backend/eap_api/views.py b/eap_backend/eap_api/views.py index 9760978e..97ddb4d2 100644 --- a/eap_backend/eap_api/views.py +++ b/eap_backend/eap_api/views.py @@ -1,3 +1,5 @@ +from typing import Optional + from django.http import HttpResponse, JsonResponse from django.views.decorators.csrf import csrf_exempt from rest_framework import generics, status @@ -9,6 +11,7 @@ from .models import ( AssuranceCase, + CaseItem, Comment, Context, EAPGroup, @@ -38,6 +41,7 @@ filter_by_case_id, get_allowed_cases, get_allowed_groups, + get_case_id, get_case_permissions, get_json_tree, make_case_summary, @@ -47,7 +51,7 @@ @csrf_exempt -def user_list(request): +def user_list(request) -> Optional[HttpResponse]: """ List all users, or make a new user """ @@ -307,6 +311,7 @@ def context_list(request): if serializer.is_valid(): serializer.save() summary = make_summary(serializer.data) + update_identifiers(serializer.instance) return JsonResponse(summary, status=201) return JsonResponse(serializer.errors, status=400) return None @@ -339,6 +344,7 @@ def context_detail(request, pk): return JsonResponse(serializer.errors, status=400) elif request.method == "DELETE": context.delete() + update_identifiers(context) return HttpResponse(status=204) return None @@ -360,6 +366,7 @@ def property_claim_list(request): if serializer.is_valid(): serializer.save() summary = make_summary(serializer.data) + update_identifiers(serializer.instance) return JsonResponse(summary, status=201) return JsonResponse(serializer.errors, status=400) return None @@ -392,6 +399,7 @@ def property_claim_detail(request, pk): return JsonResponse(serializer.errors, status=400) elif request.method == "DELETE": claim.delete() + update_identifiers(claim) return HttpResponse(status=204) return None @@ -483,6 +491,7 @@ def strategies_list(request): if serializer.is_valid(): serializer.save() summary = make_summary(serializer.data) + update_identifiers(serializer.instance) return JsonResponse(summary, status=201) return JsonResponse(serializer.errors, status=400) return None @@ -510,6 +519,7 @@ def strategy_detail(request, pk): elif request.method == "DELETE": strategy.delete() + update_identifiers(strategy) return HttpResponse(status=204) return None @@ -606,3 +616,43 @@ def reply_to_comment(request, comment_id): return JsonResponse(serializer.data, status=status.HTTP_201_CREATED) return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +def update_identifiers(item: CaseItem): + + case_id: Optional[int] = get_case_id(item) + if case_id is None: + raise ValueError("Cannot obtain case id!") + + goal_id: int = TopLevelNormativeGoal.objects.get(assurance_case_id=case_id).pk + + for context_index, context in enumerate(Context.objects.filter(goal_id=goal_id)): + context.name = f"C{context_index + 1}" + context.save() + + for strategy_index, strategy in enumerate(Strategy.objects.filter(goal_id=goal_id)): + strategy.name = f"S{strategy_index + 1}" + strategy.save() + + parent_property_claims = PropertyClaim.objects.filter(property_claim_id=None) + current_case_claims = [ + claim for claim in parent_property_claims if get_case_id(claim) == case_id + ] + for property_claim_index, property_claim in enumerate(current_case_claims): + property_claim.name = f"P{property_claim_index + 1}" + property_claim.save() + update_property_claim_identifiers(property_claim) + + +def update_property_claim_identifiers(parent_property_claim: PropertyClaim): + child_property_claims = PropertyClaim.objects.filter( + property_claim_id=parent_property_claim.pk + ) + + if len(child_property_claims) == 0: + return + else: + for index, child_property_claim in enumerate(child_property_claims): + child_property_claim.name = f"{parent_property_claim.name}.{index + 1}" + child_property_claim.save() + update_property_claim_identifiers(child_property_claim) \ No newline at end of file diff --git a/eap_backend/eap_backend/settings.py b/eap_backend/eap_backend/settings.py index d1340648..616d4182 100644 --- a/eap_backend/eap_backend/settings.py +++ b/eap_backend/eap_backend/settings.py @@ -21,6 +21,8 @@ # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ +# TODO(cgavidia): Move DEBUG and SECRET_KEY to environment variables. + # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = "django-insecure-)@nls8m9den@jbfjkee2h343^=a8#jzq+@^nweds$s#%_1ia_g" @@ -32,7 +34,7 @@ GITHUB_CLIENT_SECRET = os.environ.get("GITHUB_CLIENT_SECRET") REDIRECT_URI = "http://localhost:3000/login" - +# TODO(cgavidia): This should also come from environment variables. ALLOWED_HOSTS = ["*"] @@ -40,11 +42,11 @@ INSTALLED_APPS = [ "eap_api.apps.ApiConfig", - "django.contrib.admin", + "django.contrib.admin", # TODO(cgavidia): Apparently, we don't need this. "django.contrib.auth", "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", + "django.contrib.sessions", # TODO(cgavidia): Apparently, we don't need this. + "django.contrib.messages", # TODO(cgavidia): Apparently, we don't need this. "django.contrib.staticfiles", "rest_framework", "rest_framework.authtoken", @@ -61,13 +63,13 @@ MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", - "django.middleware.common.CommonMiddleware", + "django.middleware.common.CommonMiddleware", # TODO(cgavidia): This line is duplicated. "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", # TODO(cgavidia): Apparently, we don't need this. "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", # TODO(cgavidia): Apparently, we don't need this. + "django.contrib.messages.middleware.MessageMiddleware", # TODO(cgavidia): Apparently, we don't need this. "django.middleware.clickjacking.XFrameOptionsMiddleware", "corsheaders.middleware.CorsMiddleware", ] @@ -104,6 +106,7 @@ REST_FRAMEWORK = { # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. + # TODO(cgavidia): Can we use IsAuthenticated instead? "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.AllowAny"], # "DEFAULT_PERMISSION_CLASSES": [ # "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly" @@ -210,4 +213,4 @@ ACCOUNT_SESSION_REMEMBER = True ACCOUNT_AUTHENTICATION_METHOD = "username" ACCOUNT_UNIQUE_EMAIL = False -ACCOUNT_UNIQUE_USERNAME = True +ACCOUNT_UNIQUE_USERNAME = True \ No newline at end of file diff --git a/next_frontend/.env.local b/next_frontend/.env.local index b7b9d84c..1de45caf 100644 --- a/next_frontend/.env.local +++ b/next_frontend/.env.local @@ -1,5 +1,5 @@ -# NEXT_PUBLIC_API_URL=http://localhost:8000 -NEXT_PUBLIC_API_URL=https://eap-backend.azurewebsites.net +NEXT_PUBLIC_API_URL=http://localhost:8000 +# NEXT_PUBLIC_API_URL=https://eap-backend.azurewebsites.net NEXT_PUBLIC_STORAGESASTOKEN=sv=2022-11-02&ss=bfqt&srt=c&sp=rwdlacupiytfx&se=2025-05-06T03:07:05Z&st=2024-05-05T19:07:05Z&spr=https&sig=vUQsHpRHQ%2BZAiE9k3riG4qmXJubVd0jMdbBFgM0fpuY%3D NEXT_PUBLIC_STORAGESOURCENAME=teamedia diff --git a/next_frontend/components/cases/GoalNode.tsx b/next_frontend/components/cases/GoalNode.tsx index 123ce9e2..a916dff8 100644 --- a/next_frontend/components/cases/GoalNode.tsx +++ b/next_frontend/components/cases/GoalNode.tsx @@ -8,17 +8,25 @@ import ToggleButton from './ToggleButton'; function GoalNode({ data, ...props }: NodeProps) { const [hidden, setHidden] = useState(true) + // Debug: Log the data.description value + console.log(data.description); + return ( -
+
-
+
{data.name}
-
{data.description}
+ {/* Ensure there is a value */} + {data.description ? ( +

{data.description}

+ ) : ( +

No description available.

+ )}
- +
{/* diff --git a/next_frontend/components/common/CreateForm.tsx b/next_frontend/components/common/CreateForm.tsx index f9d936ab..47682281 100644 --- a/next_frontend/components/common/CreateForm.tsx +++ b/next_frontend/components/common/CreateForm.tsx @@ -48,7 +48,7 @@ const CreateForm: React.FC = ({ onClose }) => { const identifier = await setNodeIdentifier(null, 'goal') const newGoal = { - "name": `G${identifier}`, + "name": `G1`, "short_description": values.description, "long_description": "N/A", "keywords": "N/A", @@ -70,8 +70,9 @@ const CreateForm: React.FC = ({ onClose }) => { goals: [ result.data ] } - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) onClose() + window.location.reload() } return ( diff --git a/next_frontend/components/common/NewLinkForm.tsx b/next_frontend/components/common/NewLinkForm.tsx index 167b219a..8a5220f5 100644 --- a/next_frontend/components/common/NewLinkForm.tsx +++ b/next_frontend/components/common/NewLinkForm.tsx @@ -82,8 +82,9 @@ const NewLinkForm: React.FC = ({ } // Update Assurance Case in state - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) reset() + window.location.reload() } /** Function used to handle creation of a strategy node linked to a goal */ @@ -121,8 +122,9 @@ const NewLinkForm: React.FC = ({ } // Update Assurance Case in state - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) reset() + window.location.reload() } /** Function used to create a property claim, whether its parent is a goal, strategy or another propery claim */ @@ -193,8 +195,9 @@ const NewLinkForm: React.FC = ({ }); // Update Assurance Case in state - setAssuranceCase(updatedAssuranceCase); + // setAssuranceCase(updatedAssuranceCase); reset() + window.location.reload() } } @@ -214,8 +217,9 @@ const NewLinkForm: React.FC = ({ ] } - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) reset() + window.location.reload() } if(node.type === 'goal') { @@ -235,8 +239,9 @@ const NewLinkForm: React.FC = ({ } // Update Assurance Case in state - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) reset() + window.location.reload() } } @@ -275,8 +280,9 @@ const NewLinkForm: React.FC = ({ ] } - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) reset() + window.location.reload() } const form = useForm>({ diff --git a/next_frontend/lib/convert-case.ts b/next_frontend/lib/convert-case.ts index dae80b9f..3efeb330 100644 --- a/next_frontend/lib/convert-case.ts +++ b/next_frontend/lib/convert-case.ts @@ -3,6 +3,8 @@ export const convertAssuranceCase = async (assuranceCase: any) => { let caseNodes: any[] = [], caseEdges: any[] = [] let identifier = 0 + console.log('Assurance Case', assuranceCase) + // Create nodes for each child array item const goals = assuranceCase.goals diff --git a/next_frontend/package-lock.json b/next_frontend/package-lock.json index d33a7560..f25b22b3 100644 --- a/next_frontend/package-lock.json +++ b/next_frontend/package-lock.json @@ -26,6 +26,7 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", + "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.10", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", @@ -291,9 +292,9 @@ } }, "node_modules/@azure/identity": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.0.tgz", - "integrity": "sha512-ve3aYv79qXOJ8wRxQ5jO0eIz2DZ4o0TyME4m4vlGV5YyePddVZ+pFMzusAMODNAflYAAv1cBIhKnd4xytmXyig==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.1.tgz", + "integrity": "sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.5.0", @@ -303,7 +304,7 @@ "@azure/core-util": "^1.3.0", "@azure/logger": "^1.0.0", "@azure/msal-browser": "^3.11.1", - "@azure/msal-node": "^2.6.6", + "@azure/msal-node": "^2.9.2", "events": "^3.0.0", "jws": "^4.0.0", "open": "^8.0.0", @@ -356,11 +357,11 @@ } }, "node_modules/@azure/msal-node": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.7.0.tgz", - "integrity": "sha512-wXD8LkUvHICeSWZydqg6o8Yvv+grlBEcmLGu+QEI4FcwFendbTEZrlSygnAXXSOCVaGAirWLchca35qrgpO6Jw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.9.2.tgz", + "integrity": "sha512-8tvi6Cos3m+0KmRbPjgkySXi+UQU/QiuVRFnrxIwt5xZlEEFa69O04RTaNESGgImyBBlYbo2mfE8/U8Bbdk1WQ==", "dependencies": { - "@azure/msal-common": "14.9.0", + "@azure/msal-common": "14.12.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -368,6 +369,14 @@ "node": ">=16" } }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "14.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.12.0.tgz", + "integrity": "sha512-IDDXmzfdwmDkv4SSmMEyAniJf6fDu3FJ7ncOjlxkDuT85uSnLEhZi3fGZpoR7T4XZpOMx9teM9GXBgrfJgyeBw==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@azure/storage-blob": { "version": "12.17.0", "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", @@ -2281,6 +2290,14 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/line-clamp": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", + "integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==", + "peerDependencies": { + "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + } + }, "node_modules/@tailwindcss/typography": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", @@ -3163,11 +3180,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4450,9 +4467,9 @@ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, diff --git a/next_frontend/package.json b/next_frontend/package.json index fd4cc322..0153fe33 100644 --- a/next_frontend/package.json +++ b/next_frontend/package.json @@ -27,6 +27,7 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", + "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.10", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", diff --git a/next_frontend/tailwind.config.ts b/next_frontend/tailwind.config.ts index 2532ce90..161b734d 100644 --- a/next_frontend/tailwind.config.ts +++ b/next_frontend/tailwind.config.ts @@ -82,7 +82,11 @@ const config = { }, }, }, - plugins: [require("tailwindcss-animate"), require('@tailwindcss/typography')], + plugins: [ + require("tailwindcss-animate"), + require('@tailwindcss/typography'), + require('@tailwindcss/line-clamp'), + ], } satisfies Config export default config From eaf220257b9d42671171403cbf4a1bb978c14b65 Mon Sep 17 00:00:00 2001 From: Richard Griffiths Date: Thu, 13 Jun 2024 16:14:47 +0100 Subject: [PATCH 2/7] Chart reload on node update --- next_frontend/components/common/EditForm.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/next_frontend/components/common/EditForm.tsx b/next_frontend/components/common/EditForm.tsx index 344db586..baf30817 100644 --- a/next_frontend/components/common/EditForm.tsx +++ b/next_frontend/components/common/EditForm.tsx @@ -72,7 +72,8 @@ const EditForm: React.FC = ({ // Assurance Case Update const updatedAssuranceCase = await updateAssuranceCase(node.type, assuranceCase, updateItem, node.data.id, node) if(updatedAssuranceCase) { - setAssuranceCase(updatedAssuranceCase) + // setAssuranceCase(updatedAssuranceCase) + window.location.reload() onClose() } } From 038d68373849df3b08432fc403260a82b6ffa115 Mon Sep 17 00:00:00 2001 From: Richard Griffiths Date: Thu, 13 Jun 2024 18:42:01 +0100 Subject: [PATCH 3/7] Moving elements --- .../components/cases/ContextNode.tsx | 2 +- .../components/cases/EvidenceNode.tsx | 2 +- next_frontend/components/cases/GoalNode.tsx | 2 +- .../components/cases/PropertyNode.tsx | 2 +- .../components/cases/StrategyNode.tsx | 2 +- next_frontend/components/common/NodeEdit.tsx | 109 ++++++++++++++++-- next_frontend/lib/case-helper.ts | 27 +++++ 7 files changed, 132 insertions(+), 14 deletions(-) diff --git a/next_frontend/components/cases/ContextNode.tsx b/next_frontend/components/cases/ContextNode.tsx index 4623bd26..c31e9e86 100644 --- a/next_frontend/components/cases/ContextNode.tsx +++ b/next_frontend/components/cases/ContextNode.tsx @@ -6,7 +6,7 @@ import { Handle, Position } from 'reactflow'; function ContextNode({ isConnectable, data }: any) { return ( -
+
diff --git a/next_frontend/components/cases/EvidenceNode.tsx b/next_frontend/components/cases/EvidenceNode.tsx index 2c2d9578..9c5fb3c5 100644 --- a/next_frontend/components/cases/EvidenceNode.tsx +++ b/next_frontend/components/cases/EvidenceNode.tsx @@ -6,7 +6,7 @@ import { Handle, Position } from 'reactflow'; function EvidenceNode({ data }: any) { return ( -
+
diff --git a/next_frontend/components/cases/GoalNode.tsx b/next_frontend/components/cases/GoalNode.tsx index a916dff8..c7ecd3ed 100644 --- a/next_frontend/components/cases/GoalNode.tsx +++ b/next_frontend/components/cases/GoalNode.tsx @@ -12,7 +12,7 @@ function GoalNode({ data, ...props }: NodeProps) { console.log(data.description); return ( -
+
diff --git a/next_frontend/components/cases/PropertyNode.tsx b/next_frontend/components/cases/PropertyNode.tsx index 607c14cd..a33788be 100644 --- a/next_frontend/components/cases/PropertyNode.tsx +++ b/next_frontend/components/cases/PropertyNode.tsx @@ -7,7 +7,7 @@ import ToggleButton from './ToggleButton'; function PropertyNode({ data, ...props}: NodeProps) { return ( -
+
diff --git a/next_frontend/components/cases/StrategyNode.tsx b/next_frontend/components/cases/StrategyNode.tsx index 0c90c26b..ea8719be 100644 --- a/next_frontend/components/cases/StrategyNode.tsx +++ b/next_frontend/components/cases/StrategyNode.tsx @@ -7,7 +7,7 @@ import ToggleButton from './ToggleButton'; function StrategyNode({ data, ...props }: NodeProps) { return ( -
+
diff --git a/next_frontend/components/common/NodeEdit.tsx b/next_frontend/components/common/NodeEdit.tsx index f963629a..d01ef0b0 100644 --- a/next_frontend/components/common/NodeEdit.tsx +++ b/next_frontend/components/common/NodeEdit.tsx @@ -7,15 +7,21 @@ import { CloudFog, Plus, Trash2 } from "lucide-react" import EditForm from "./EditForm"; import useStore from '@/data/store'; import { Autour_One } from "next/font/google"; -import { addEvidenceToClaim, addPropertyClaimToNested, createAssuranceCaseNode, deleteAssuranceCaseNode, setNodeIdentifier } from "@/lib/case-helper"; +import { addEvidenceToClaim, addPropertyClaimToNested, createAssuranceCaseNode, deleteAssuranceCaseNode, listPropertyClaims, setNodeIdentifier, updateAssuranceCaseNode } from "@/lib/case-helper"; import { useLoginToken } from "@/hooks/useAuth"; import NewLinkForm from "./NewLinkForm"; import { AlertModal } from "../modals/alertModal"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" interface NodeEditProps { node: Node | any isOpen: boolean - // onClose: () => void setEditOpen: Dispatch> } @@ -29,8 +35,18 @@ const NodeEdit = ({ node, isOpen, setEditOpen } : NodeEditProps ) => { const [alertOpen, setAlertOpen] = useState(false) const [loading, setLoading] = useState(false) + const [selectedClaimMove, setSelectedClaimMove] = useState(null); // State for selected strategy + const [selectedEvidenceMove, setSelectedEvidenceMove] = useState(null); // State for selected strategy + const [token] = useLoginToken(); + let claims, strategies = [] + + if(assuranceCase.goals[0]) { + strategies = assuranceCase.goals[0].strategies + claims = listPropertyClaims(assuranceCase.goals) + } + useEffect(() => { setIsMounted(true); }, []); @@ -72,6 +88,32 @@ const NodeEdit = ({ node, isOpen, setEditOpen } : NodeEditProps ) => { } }; + const handleMove = async () => { + // Add your move logic here + if (selectedClaimMove) { + console.log(`Move Property to Strategy with ID: ${selectedClaimMove}`); + const updateItem = { + strategy_id: selectedClaimMove, + } + const updated = await updateAssuranceCaseNode('property', node.data.id, token, updateItem) + if(updated) { + window.location.reload() + } + console.log('Something went wrong updating') + } + if (selectedEvidenceMove) { + console.log(`Move Evidence to Property Claim with ID: ${selectedEvidenceMove}`); + const updateItem = { + property_claim_id: [selectedEvidenceMove], + } + const updated = await updateAssuranceCaseNode('evidence', node.data.id, token, updateItem) + if(updated) { + window.location.reload() + } + console.log('Something went wrong updating') + } + } + return ( { {/* Node specific form buttons */} - {node.type != 'context' && ( + {node.type != 'context' && node.type != 'evidence' && (

Link to {node.data.name}

@@ -107,17 +149,66 @@ const NodeEdit = ({ node, isOpen, setEditOpen } : NodeEditProps ) => { )} - {node.type === 'evidence' && ( - <> - TODO: Property Links - - )}
)} + {/* Moving element options */} + {node.type === 'property' || node.type === 'evidence' ? ( +
+

Move {node.type}

+
+ {node.type === 'property' && + + } + {node.type === 'evidence' && + + } + +
+
+ ) : null} + {/* Handle Delete */} -
+