This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata.git


The following commit(s) were added to refs/heads/master by this push:
     new 58b8abc791 Support resource deletion (#522)
58b8abc791 is described below

commit 58b8abc79192997156ccee00f0695a3ad76f9bdf
Author: Ganning Xu <[email protected]>
AuthorDate: Wed Jun 18 07:14:56 2025 -0700

    Support resource deletion (#522)
    
    * support resource deletion
    * refactoring & error logging
    * support project deletion
---
 .../portal/src/components/add/AddGitUrl.tsx        |  70 +++---
 .../portal/src/components/add/AddProjectMaster.tsx | 133 +++++-----
 .../portal/src/components/add/DatasetSearch.tsx    | 196 +++++++--------
 .../portal/src/components/add/RepoSearch.tsx       | 179 +++++++------
 .../portal/src/components/home/ProjectCard.tsx     |  64 +++--
 .../portal/src/components/home/ResourceCard.tsx    | 128 +++++-----
 .../portal/src/components/notebooks/index.tsx      |  71 +++---
 .../components/projects/DeleteProjectButton.tsx    | 115 +++++++++
 .../components/resources/DeleteResourceButton.tsx  | 115 +++++++++
 .../src/components/resources/ResourceDetails.tsx   | 276 +++++++++++----------
 .../src/components/resources/ResourceTypeBadge.tsx |  33 +--
 .../portal/src/components/resources/index.tsx      |   1 -
 .../portal/src/interfaces/ProjectType.tsx          |   3 +-
 modules/research-framework/portal/src/lib/util.ts  |  13 +-
 .../research/service/config/AuthzTokenFilter.java  |   4 +-
 .../service/controller/ProjectController.java      |  14 +-
 .../service/controller/ResourceController.java     |   9 +-
 .../airavata/research/service/enums/StateEnum.java |  25 ++
 .../research/service/handlers/ProjectHandler.java  |  31 ++-
 .../research/service/handlers/ResourceHandler.java |  43 +++-
 .../research/service/model/entity/Project.java     |  15 ++
 .../research/service/model/entity/Resource.java    |  21 +-
 .../service/model/repo/ProjectRepository.java      |  15 ++
 .../service/model/repo/ResourceRepository.java     |   9 +-
 .../src/main/resources/application.yml             |   2 +-
 25 files changed, 992 insertions(+), 593 deletions(-)

diff --git a/modules/research-framework/portal/src/components/add/AddGitUrl.tsx 
b/modules/research-framework/portal/src/components/add/AddGitUrl.tsx
index 4ae21da8f4..79f2a2183a 100644
--- a/modules/research-framework/portal/src/components/add/AddGitUrl.tsx
+++ b/modules/research-framework/portal/src/components/add/AddGitUrl.tsx
@@ -1,15 +1,15 @@
-import { Button, Code, Input, Text, VStack } from "@chakra-ui/react";
-import { useState } from "react";
+import {Button, Code, Input, Text, VStack} from "@chakra-ui/react";
+import {useState} from "react";
 import yaml from "js-yaml";
-import { CreateResourceRequest } from 
"@/interfaces/Requests/CreateResourceRequest";
+import {CreateResourceRequest} from 
"@/interfaces/Requests/CreateResourceRequest";
 
 export const AddGitUrl = ({
-  nextStage,
-  createResourceRequest,
-  setCreateResourceRequest,
-  githubUrl,
-  setGithubUrl,
-}: {
+                            nextStage,
+                            createResourceRequest,
+                            setCreateResourceRequest,
+                            githubUrl,
+                            setGithubUrl,
+                          }: {
   nextStage: () => void;
   createResourceRequest: CreateResourceRequest;
   setCreateResourceRequest: (data: CreateResourceRequest) => void;
@@ -65,28 +65,34 @@ export const AddGitUrl = ({
   };
 
   return (
-    <VStack alignItems="flex-start">
-      <Text>Paste GitHub Url</Text>
-      <Text fontSize="sm" color="gray.500">
-        We&apos;ll pull the <Code>cybershuttle.yml</Code> file from the
-        repository to auto-populate the project fields.
-      </Text>
-      <Input
-        placeholder="https://github.com/username/repo.git";
-        value={githubUrl}
-        onChange={(e) => setGithubUrl(e.target.value)}
-        mt={2}
-      />
-      <Button
-        width="full"
-        loading={loadingPull}
-        onClick={onPullCybershuttleYml}
-        mt={4}
-        colorScheme="blue"
-        disabled={!githubUrl}
-      >
-        Pull cybershuttle.yml file
-      </Button>
-    </VStack>
+      <VStack alignItems="flex-start">
+        <Text>Paste GitHub Url</Text>
+        <Text fontSize="sm" color="gray.500">
+          We&apos;ll pull the <Code>cybershuttle.yml</Code> file from the
+          repository to auto-populate the project fields.
+        </Text>
+        <Input
+            placeholder="https://github.com/username/repo";
+            value={githubUrl}
+            onChange={(e) => {
+              let githubUrl = e.target.value;
+              if (githubUrl.endsWith(".git")) {
+                githubUrl = githubUrl.replace(".git", "");
+              }
+              setGithubUrl(githubUrl);
+            }}
+            mt={2}
+        />
+        <Button
+            width="full"
+            loading={loadingPull}
+            onClick={onPullCybershuttleYml}
+            mt={4}
+            colorScheme="blue"
+            disabled={!githubUrl}
+        >
+          Pull cybershuttle.yml file
+        </Button>
+      </VStack>
   );
 };
diff --git 
a/modules/research-framework/portal/src/components/add/AddProjectMaster.tsx 
b/modules/research-framework/portal/src/components/add/AddProjectMaster.tsx
index f28f2d85b0..2241c10c79 100644
--- a/modules/research-framework/portal/src/components/add/AddProjectMaster.tsx
+++ b/modules/research-framework/portal/src/components/add/AddProjectMaster.tsx
@@ -1,27 +1,19 @@
-import {
-  Box,
-  Button,
-  Container,
-  Field,
-  Heading,
-  Input,
-  VStack,
-} from "@chakra-ui/react";
-import { useState } from "react";
-import { useNavigate } from "react-router";
-import { FaArrowLeft } from "react-icons/fa";
-import { CreateProjectRequest } from 
"@/interfaces/Requests/CreateProjectRequest";
+import {Box, Button, Container, Field, Heading, Input, VStack,} from 
"@chakra-ui/react";
+import {useState} from "react";
+import {useNavigate} from "react-router";
+import {FaArrowLeft} from "react-icons/fa";
+import {CreateProjectRequest} from 
"@/interfaces/Requests/CreateProjectRequest";
 import RepoSearchInput from "./RepoSearch";
-import { DatasetSearchInput } from "./DatasetSearch";
+import {DatasetSearchInput} from "./DatasetSearch";
 import api from "@/lib/api";
-import { CONTROLLER } from "@/lib/controller";
-import { toaster } from "../ui/toaster";
-import { useAuth } from "react-oidc-context";
+import {CONTROLLER} from "@/lib/controller";
+import {toaster} from "../ui/toaster";
+import {useAuth} from "react-oidc-context";
 
 export const AddProjectMaster = () => {
   const navigate = useNavigate();
   const [createProjectRequest, setCreateProjectRequest] = useState(
-    {} as CreateProjectRequest
+      {} as CreateProjectRequest
   );
   const [loading, setLoading] = useState(false);
   const auth = useAuth();
@@ -39,8 +31,8 @@ export const AddProjectMaster = () => {
         return;
       }
       if (
-        !createProjectRequest.name ||
-        createProjectRequest.name.length === 0
+          !createProjectRequest.name ||
+          createProjectRequest.name.length === 0
       ) {
         toaster.create({
           title: "Error creating project",
@@ -49,8 +41,8 @@ export const AddProjectMaster = () => {
         });
         return;
       } else if (
-        !createProjectRequest.repositoryId ||
-        createProjectRequest.repositoryId.length === 0
+          !createProjectRequest.repositoryId ||
+          createProjectRequest.repositoryId.length === 0
       ) {
         toaster.create({
           title: "Error creating project",
@@ -81,58 +73,57 @@ export const AddProjectMaster = () => {
   };
 
   return (
-    <Container maxW="breakpoint-sm" mt={8}>
-      <Box maxW="breakpoint-sm" mx="auto">
-        <Heading
-          textAlign="center"
-          fontSize={{ base: "4xl", md: "5xl" }}
-          fontWeight="black"
-          lineHeight={1.2}
-        >
-          Add Project
-        </Heading>
-        <Button
-          onClick={() => {
-            navigate("/add");
-          }}
-          variant="ghost"
-          p={0}
-          mb={2}
-        >
-          <FaArrowLeft />
-          Back
-        </Button>
-      </Box>
+      <Container maxW="breakpoint-sm" mt={8}>
+        <Box maxW="breakpoint-sm" mx="auto">
+          <Heading
+              textAlign="center"
+              fontSize={{base: "4xl", md: "5xl"}}
+              fontWeight="black"
+              lineHeight={1.2}
+          >
+            Add Project
+          </Heading>
+          <Button
+              onClick={() => {
+                navigate("/add");
+              }}
+              variant="ghost"
+              p={0}
+              mb={2}
+          >
+            <FaArrowLeft/>
+            Back
+          </Button>
+        </Box>
 
-      <VStack gap={4} alignItems="flex-start">
-        <Field.Root>
-          <Field.Label>Project Name</Field.Label>
-          <Input
-            value={createProjectRequest.name}
-            onChange={(e) => {
-              setCreateProjectRequest({
-                ...createProjectRequest,
-                name: e.target.value,
-              });
-            }}
-            placeholder="Enter project name"
-          />
-        </Field.Root>
+        <VStack gap={4} alignItems="flex-start">
+          <Field.Root>
+            <Field.Label>Project Name</Field.Label>
+            <Input
+                value={createProjectRequest.name}
+                onChange={(e) => {
+                  setCreateProjectRequest({
+                    ...createProjectRequest,
+                    name: e.target.value,
+                  });
+                }}
+                placeholder="Enter project name"
+            />
+          </Field.Root>
 
-        <RepoSearchInput
-          createResourceRequest={createProjectRequest}
-          setCreateResourceRequest={setCreateProjectRequest}
-        />
+          <RepoSearchInput
+              createResourceRequest={createProjectRequest}
+              setCreateResourceRequest={setCreateProjectRequest}
+          />
 
-        <DatasetSearchInput
-          createResourceRequest={createProjectRequest}
-          setCreateResourceRequest={setCreateProjectRequest}
-        />
+          <DatasetSearchInput
+              setCreateResourceRequest={setCreateProjectRequest}
+          />
 
-        <Button onClick={handleSubmit} loading={loading} w="full">
-          Submit
-        </Button>
-      </VStack>
-    </Container>
+          <Button onClick={handleSubmit} loading={loading} w="full">
+            Submit
+          </Button>
+        </VStack>
+      </Container>
   );
 };
diff --git 
a/modules/research-framework/portal/src/components/add/DatasetSearch.tsx 
b/modules/research-framework/portal/src/components/add/DatasetSearch.tsx
index 5a8f8087d5..9df9f97bbc 100644
--- a/modules/research-framework/portal/src/components/add/DatasetSearch.tsx
+++ b/modules/research-framework/portal/src/components/add/DatasetSearch.tsx
@@ -1,38 +1,28 @@
-import { CreateProjectRequest } from 
"@/interfaces/Requests/CreateProjectRequest";
-import {
-  VStack,
-  Field,
-  Input,
-  Text,
-  Spinner,
-  Box,
-  HStack,
-} from "@chakra-ui/react";
-import { LuSearch } from "react-icons/lu";
-import { InputGroup } from "../ui/input-group";
-import { SetStateAction, useEffect, useState } from "react";
-import { DatasetResource } from "@/interfaces/ResourceType";
-import { ResourceTypeEnum } from "@/interfaces/ResourceTypeEnum";
-import { CONTROLLER } from "@/lib/controller";
+import {CreateProjectRequest} from 
"@/interfaces/Requests/CreateProjectRequest";
+import {Box, Field, HStack, Input, Spinner, Text, VStack,} from 
"@chakra-ui/react";
+import {LuSearch} from "react-icons/lu";
+import {InputGroup} from "../ui/input-group";
+import {SetStateAction, useEffect, useState} from "react";
+import {DatasetResource} from "@/interfaces/ResourceType";
+import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum";
+import {CONTROLLER} from "@/lib/controller";
 import api from "@/lib/api";
-import { FaCheck } from "react-icons/fa";
+import {FaCheck} from "react-icons/fa";
 
 export const DatasetSearchInput = ({
-  setCreateResourceRequest,
-  createResourceRequest,
-}: {
+                                     setCreateResourceRequest,
+                                   }: {
   setCreateResourceRequest: (
-    data: SetStateAction<CreateProjectRequest>
+      data: SetStateAction<CreateProjectRequest>
   ) => void;
-  createResourceRequest: CreateProjectRequest;
 }) => {
   const [datasetSearch, setDatasetSearch] = useState("");
 
   const [debounceTimeout, setDebounceTimeout] = useState<NodeJS.Timeout | 
null>(
-    null
+      null
   );
   const [selectedDatasets, setSelectedDatasets] = useState<DatasetResource[]>(
-    []
+      []
   );
   const [results, setResults] = useState<DatasetResource[]>([]);
   const [loading, setLoading] = useState(false);
@@ -78,88 +68,88 @@ export const DatasetSearchInput = ({
   }, [selectedDatasets]);
 
   return (
-    <VStack align="start" width="full" gap={2}>
-      <Field.Root>
-        <Field.Label>Dataset (select multiple)</Field.Label>
-        <InputGroup startElement={<LuSearch />} w="full">
-          <Input
-            value={datasetSearch}
-            onChange={(e) => setDatasetSearch(e.target.value)}
-            placeholder="Enter dataset name"
-          />
-        </InputGroup>
+      <VStack align="start" width="full" gap={2}>
+        <Field.Root>
+          <Field.Label>Dataset (select multiple)</Field.Label>
+          <InputGroup startElement={<LuSearch/>} w="full">
+            <Input
+                value={datasetSearch}
+                onChange={(e) => setDatasetSearch(e.target.value)}
+                placeholder="Enter dataset name"
+            />
+          </InputGroup>
 
-        {selectedDatasets.length > 0 && (
-          <Box mt={2} w="full">
-            <HStack gap={1}>
-              {selectedDatasets.map((res) => {
-                return (
-                  <Box
-                    key={res.id}
-                    borderWidth={1}
-                    borderRadius="md"
-                    p={2}
-                    cursor={"pointer"}
-                    _hover={{ bg: "red.200" }}
-                    onClick={() => {
-                      setSelectedDatasets((prev) =>
-                        prev.filter((item) => item.id !== res.id)
-                      );
-                    }}
-                  >
-                    <HStack>
-                      <FaCheck />
-                      <Text key={res.id} fontSize="sm">
-                        {res.name}
-                      </Text>
-                    </HStack>
-                  </Box>
-                );
-              })}
-            </HStack>
-          </Box>
-        )}
+          {selectedDatasets.length > 0 && (
+              <Box mt={2} w="full">
+                <HStack gap={1}>
+                  {selectedDatasets.map((res) => {
+                    return (
+                        <Box
+                            key={res.id}
+                            borderWidth={1}
+                            borderRadius="md"
+                            p={2}
+                            cursor={"pointer"}
+                            _hover={{bg: "red.200"}}
+                            onClick={() => {
+                              setSelectedDatasets((prev) =>
+                                  prev.filter((item) => item.id !== res.id)
+                              );
+                            }}
+                        >
+                          <HStack>
+                            <FaCheck/>
+                            <Text key={res.id} fontSize="sm">
+                              {res.name}
+                            </Text>
+                          </HStack>
+                        </Box>
+                    );
+                  })}
+                </HStack>
+              </Box>
+          )}
 
-        {loading && <Spinner size="sm" />}
+          {loading && <Spinner size="sm"/>}
 
-        {!loading && results.length > 0 && (
-          <Box mt={2} w="full">
-            <VStack align="start" gap={1}>
-              {results.map((res) => {
-                const isSelected = selectedDatasets.some(
-                  (item) => item.id === res.id
-                );
+          {!loading && results.length > 0 && (
+              <Box mt={2} w="full">
+                <VStack align="start" gap={1}>
+                  {results.map((res) => {
+                    const isSelected = selectedDatasets.some(
+                        (item) => item.id === res.id
+                    );
 
-                return (
-                  <Box
-                    key={res.id}
-                    bg={isSelected ? "green.200" : "white"}
-                    borderWidth={1}
-                    borderRadius="md"
-                    p={2}
-                    w="full"
-                    cursor={"pointer"}
-                    _hover={{ bg: "gray.100" }}
-                    onClick={() => {
-                      setSelectedDatasets((prev) => {
-                        if (isSelected) {
-                          return prev.filter((item) => item.id !== res.id);
-                        } else {
-                          return [...prev, res];
-                        }
-                      });
-                    }}
-                  >
-                    <Text key={res.id} fontSize="sm">
-                      {res.name}
-                    </Text>
-                  </Box>
-                );
-              })}
-            </VStack>
-          </Box>
-        )}
-      </Field.Root>
-    </VStack>
+                    return (
+                        <Box
+                            key={res.id}
+                            bg={isSelected ? "green.200" : "white"}
+                            borderWidth={1}
+                            borderRadius="md"
+                            p={2}
+                            w="full"
+                            cursor={"pointer"}
+                            _hover={{bg: "gray.100"}}
+                            onClick={() => {
+                              setSelectedDatasets((prev) => {
+                                if (isSelected) {
+                                  return prev.filter((item) => item.id !== 
res.id);
+                                } else {
+                                  return [...prev, res];
+                                }
+                              });
+                            }}
+                        >
+                          <Text key={res.id} fontSize="sm">
+                            {res.name}
+                          </Text>
+                        </Box>
+                    );
+                  })}
+                </VStack>
+              </Box>
+          )}
+        </Field.Root>
+      </VStack>
   );
 };
diff --git 
a/modules/research-framework/portal/src/components/add/RepoSearch.tsx 
b/modules/research-framework/portal/src/components/add/RepoSearch.tsx
index 4d96a94b6d..3f588a5d14 100644
--- a/modules/research-framework/portal/src/components/add/RepoSearch.tsx
+++ b/modules/research-framework/portal/src/components/add/RepoSearch.tsx
@@ -1,30 +1,21 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 "use client";
 
-import { useEffect, useState } from "react";
-import {
-  Box,
-  Button,
-  Field,
-  HStack,
-  Input,
-  Spinner,
-  Text,
-  VStack,
-} from "@chakra-ui/react";
-import { LuSearch } from "react-icons/lu";
-import { CreateProjectRequest } from 
"@/interfaces/Requests/CreateProjectRequest";
-import { InputGroup } from "../ui/input-group";
-import { CONTROLLER } from "@/lib/controller";
+import {useEffect, useState} from "react";
+import {Box, Button, Field, HStack, Input, Spinner, Text, VStack,} from 
"@chakra-ui/react";
+import {LuSearch} from "react-icons/lu";
+import {CreateProjectRequest} from 
"@/interfaces/Requests/CreateProjectRequest";
+import {InputGroup} from "../ui/input-group";
+import {CONTROLLER} from "@/lib/controller";
 import api from "@/lib/api";
-import { ResourceTypeEnum } from "@/interfaces/ResourceTypeEnum";
-import { RepositoryResource } from "@/interfaces/ResourceType";
-import { ResourceCard } from "../home/ResourceCard";
+import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum";
+import {RepositoryResource} from "@/interfaces/ResourceType";
+import {ResourceCard} from "../home/ResourceCard";
 
 const RepoSearchInput = ({
-  setCreateResourceRequest,
-  createResourceRequest,
-}: {
+                           setCreateResourceRequest,
+                           createResourceRequest,
+                         }: {
   setCreateResourceRequest: (data: CreateProjectRequest) => void;
   createResourceRequest: CreateProjectRequest;
 }) => {
@@ -32,10 +23,10 @@ const RepoSearchInput = ({
   const [results, setResults] = useState<any[]>([]);
   const [loading, setLoading] = useState(false);
   const [debounceTimeout, setDebounceTimeout] = useState<NodeJS.Timeout | 
null>(
-    null
+      null
   );
   const [selectedRepo, setSelectedRepo] = useState<RepositoryResource | null>(
-    null
+      null
   );
 
   useEffect(() => {
@@ -72,85 +63,85 @@ const RepoSearchInput = ({
 
   if (selectedRepo) {
     return (
-      <>
-        <Field.Root>
-          <HStack>
-            <Field.Label>Repository</Field.Label>
-            <Button
-              size="xs"
-              colorPalette="red"
-              variant="outline"
-              onClick={() => {
-                setSelectedRepo(null);
-                setCreateResourceRequest({
-                  ...createResourceRequest,
-                  repositoryId: "",
-                });
-                setRepoSearch("");
-              }}
-            >
-              Reset Selection
-            </Button>
-          </HStack>
-        </Field.Root>
+        <>
+          <Field.Root>
+            <HStack>
+              <Field.Label>Repository</Field.Label>
+              <Button
+                  size="xs"
+                  colorPalette="red"
+                  variant="outline"
+                  onClick={() => {
+                    setSelectedRepo(null);
+                    setCreateResourceRequest({
+                      ...createResourceRequest,
+                      repositoryId: "",
+                    });
+                    setRepoSearch("");
+                  }}
+              >
+                Reset Selection
+              </Button>
+            </HStack>
+          </Field.Root>
 
-        <ResourceCard size="sm" clickable={false} resource={selectedRepo} />
-      </>
+          <ResourceCard size="sm" resource={selectedRepo} deletable={false}/>
+        </>
     );
   }
 
   return (
-    <VStack align="start" width="full" gap={2}>
-      <Field.Root>
-        <Field.Label>Repository</Field.Label>
-        <InputGroup startElement={<LuSearch />} w="full">
-          <Input
-            value={repoSearch}
-            onChange={(e) => setRepoSearch(e.target.value)}
-            placeholder="Enter repository name"
-          />
-        </InputGroup>
-      </Field.Root>
+      <VStack align="start" width="full" gap={2}>
+        <Field.Root>
+          <Field.Label>Repository</Field.Label>
+          <InputGroup startElement={<LuSearch/>} w="full">
+            <Input
+                value={repoSearch}
+                onChange={(e) => setRepoSearch(e.target.value)}
+                placeholder="Enter repository name"
+            />
+          </InputGroup>
+        </Field.Root>
 
-      {loading && <Spinner size="sm" />}
+        {loading && <Spinner size="sm"/>}
 
-      {!loading && results.length > 0 && (
-        <Box mt={2} w="full">
-          <VStack align="start" gap={1}>
-            {results.map((res) => {
-              return (
-                <Box
-                  key={res.id}
-                  borderWidth={1}
-                  borderRadius="md"
-                  p={2}
-                  w="full"
-                  cursor={"pointer"}
-                  _hover={{ bg: "gray.100" }}
-                  onClick={() => {
-                    setSelectedRepo(res);
-                    setCreateResourceRequest({
-                      ...createResourceRequest,
-                      repositoryId: res.id,
-                    });
-                  }}
-                >
-                  <Text key={res.id} fontSize="sm">
-                    {res.name}
-                  </Text>
-                </Box>
-              );
-            })}
-          </VStack>
-        </Box>
-      )}
+        {!loading && results.length > 0 && (
+            <Box mt={2} w="full">
+              <VStack align="start" gap={1}>
+                {results.map((res) => {
+                  return (
+                      <Box
+                          key={res.id}
+                          borderWidth={1}
+                          borderRadius="md"
+                          p={2}
+                          w="full"
+                          cursor={"pointer"}
+                          _hover={{bg: "gray.100"}}
+                          onClick={() => {
+                            setSelectedRepo(res);
+                            setCreateResourceRequest({
+                              ...createResourceRequest,
+                              repositoryId: res.id,
+                            });
+                          }}
+                      >
+                        <Text key={res.id} fontSize="sm">
+                          {res.name}
+                        </Text>
+                      </Box>
+                  );
+                })}
+              </VStack>
+            </Box>
+        )}
 
-      {!loading && results.length === 0 && repoSearch !== "" && (
-        <Text fontSize="sm" color="gray.500">
-          No repositories found
-        </Text>
-      )}
-    </VStack>
+        {!loading && results.length === 0 && repoSearch !== "" && (
+            <Text fontSize="sm" color="gray.500">
+              No repositories found
+            </Text>
+        )}
+      </VStack>
   );
 };
 
diff --git 
a/modules/research-framework/portal/src/components/home/ProjectCard.tsx 
b/modules/research-framework/portal/src/components/home/ProjectCard.tsx
index d11ce5c968..243718b59f 100644
--- a/modules/research-framework/portal/src/components/home/ProjectCard.tsx
+++ b/modules/research-framework/portal/src/components/home/ProjectCard.tsx
@@ -1,34 +1,46 @@
-import { ProjectType } from "@/interfaces/ProjectType";
-import { Card, HStack, Text } from "@chakra-ui/react";
-import { StartSessionFromProjectButton } from 
"./StartSessionFromProjectButton";
+import {ProjectType} from "@/interfaces/ProjectType";
+import {Card, HStack, Text} from "@chakra-ui/react";
+import {StartSessionFromProjectButton} from "./StartSessionFromProjectButton";
+import {DeleteProjectButton} from 
"@/components/projects/DeleteProjectButton.tsx";
+import {useState} from "react";
+
+export const ProjectCard = ({project}: { project: ProjectType }) => {
+  const [hideCard, setHideCard] = useState(false);
+
+  const onDeleteSuccess = () => {
+    setHideCard(true);
+  }
 
-export const ProjectCard = ({ project }: { project: ProjectType }) => {
   return (
-    <Card.Root overflow="hidden" size="md" height="fit-content">
-      <Card.Body gap="2">
-        {/* Card Content */}
-        <HStack alignItems="center" justifyContent="space-between">
-          <Card.Title>{project.name}</Card.Title>
-          <Text color="gray.500">
-            {new Date(project.createdAt).toLocaleDateString()}
-          </Text>
-        </HStack>
+      <Card.Root overflow="hidden" size="md" height="fit-content" 
hidden={hideCard}>
+        <Card.Body gap="2">
+          {/* Card Content */}
+          <HStack alignItems="flex-start" justifyContent="space-between">
+            <Card.Title>
+              {project.name}
+              <Text color="gray.500" fontSize={'sm'}>
+                {new Date(project.createdAt).toLocaleDateString()}
+              </Text>
+            </Card.Title>
+
+            <DeleteProjectButton project={project} 
onSuccess={onDeleteSuccess}/>
+          </HStack>
 
-        <Text fontWeight="bold">
-          Repository:{" "}
-          <Text as="span" fontWeight="normal">
-            {project.repositoryResource.name}
+          <Text fontWeight="bold">
+            Repository:{" "}
+            <Text as="span" fontWeight="normal">
+              {project.repositoryResource.name}
+            </Text>
           </Text>
-        </Text>
-        <Text fontWeight="bold">
-          Datasets:{" "}
-          <Text as="span" fontWeight="normal">
-            {project.datasetResources.map((dataset) => dataset.name).join(", 
")}
+          <Text fontWeight="bold">
+            Datasets:{" "}
+            <Text as="span" fontWeight="normal">
+              {project.datasetResources.map((dataset) => dataset.name).join(", 
")}
+            </Text>
           </Text>
-        </Text>
 
-        <StartSessionFromProjectButton project={project} />
-      </Card.Body>
-    </Card.Root>
+          <StartSessionFromProjectButton project={project}/>
+        </Card.Body>
+      </Card.Root>
   );
 };
diff --git 
a/modules/research-framework/portal/src/components/home/ResourceCard.tsx 
b/modules/research-framework/portal/src/components/home/ResourceCard.tsx
index 7fe9b7064d..047ab3aa5e 100644
--- a/modules/research-framework/portal/src/components/home/ResourceCard.tsx
+++ b/modules/research-framework/portal/src/components/home/ResourceCard.tsx
@@ -2,49 +2,50 @@ import {ModelResource, Resource} from 
"@/interfaces/ResourceType";
 import {Tag} from "@/interfaces/TagType";
 import {isValidImaage, resourceTypeToColor} from "@/lib/util";
 import {Avatar, Badge, Box, Card, HStack, Image, Text,} from 
"@chakra-ui/react";
-import {Link} from "react-router";
 import {ResourceTypeBadge} from "../resources/ResourceTypeBadge";
 import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum";
 import {ModelCardButton} from "../models/ModelCardButton";
+import {DeleteResourceButton} from 
"@/components/resources/DeleteResourceButton.tsx";
+import {useState} from "react";
+import {Link} from 'react-router';
 
 export const ResourceCard = ({
                                resource,
-                               appendTypeToUrl = false,
-                               clickable = true,
                                size = "sm",
+                               deletable = true
                              }: {
   resource: Resource;
-  appendTypeToUrl?: boolean;
-  clickable?: boolean;
   size?: "sm" | "md" | "lg";
+  deletable?: boolean
 }) => {
+  const [hideCard, setHideCard] = useState(false);
   const author = resource.authors[0];
 
   const isValidImage = isValidImaage(resource.headerImage);
 
   resource.tags.sort((a, b) => a.value.localeCompare(b.value));
 
-  const linkTo = resource.id;
   const linkToWithType = `${resource.type}/${resource.id}`;
 
-  // Determine the link based on appendTypeToUrl
-  const link = appendTypeToUrl ? linkToWithType : linkTo;
+  const link = '/resources/' + linkToWithType;
+  const onDeleteSuccess = () => {
+    setHideCard(true);
+  }
+
   const content = (
       <Card.Root
           overflow="hidden"
           size={size}
-          _hover={{bg: resourceTypeToColor(resource.type) + ".100"}}
       >
         {isValidImage && (
             <Box position="relative" width="full">
-              {/* Badge in the upper-left */}
               <ResourceTypeBadge
                   type={resource.type}
                   position="absolute"
                   top="2"
                   left="2"
                   zIndex="1"
-                  boxShadow="md" // Optional: Shadow for visibility
+                  boxShadow="md"
               />
 
               {/* Full-width Image */}
@@ -58,58 +59,71 @@ export const ResourceCard = ({
             </Box>
         )}
 
-        <Card.Body gap="2">
-          {/* Card Content */}
-          <Card.Title>{resource.name}</Card.Title>
-          {!isValidImage && (
-              <Box>
-                <ResourceTypeBadge type={resource.type}/>
-              </Box>
-          )}
-          <HStack flexWrap="wrap">
-            {resource.tags.map((tag: Tag) => (
-                <Badge
-                    key={tag.id}
-                    size="md"
-                    rounded="md"
-                    colorPalette={resourceTypeToColor(resource.type)}
-                >
-                  {tag.value}
-                </Badge>
-            ))}
+        <Card.Header>
+          <HStack justifyContent={'space-between'} alignItems={'center'} 
flexWrap={'wrap'}>
+            <Card.Title>{resource.name}</Card.Title>
+            {deletable && <DeleteResourceButton resource={resource} 
onSuccess={onDeleteSuccess}/>}
           </HStack>
-          <Text color="fg.muted" lineClamp={2}>
-            {resource.description}
-          </Text>
-        </Card.Body>
+        </Card.Header>
 
-        <Card.Footer justifyContent="space-between" pt={4}>
-          {author && (
-              <HStack>
-                <Avatar.Root shape="full" size="sm">
-                  <Avatar.Fallback name={author}/>
-                  <Avatar.Image src={author}/>
-                </Avatar.Root>
+        <Link to={link} target={"_blank"}>
+          <Box
+              _hover={{bg: resourceTypeToColor(resource.type) + ".100"}}
+          >
+            <Card.Body gap="2">
+              {!isValidImage && (
+                  <Box>
+                    <ResourceTypeBadge type={resource.type}/>
+                  </Box>
+              )}
 
-                <Box>
-                  <Text fontWeight="bold">{author}</Text>
-                </Box>
+              {/* Card Content */}
+              <HStack flexWrap="wrap">
+                {resource.tags.map((tag: Tag) => (
+                    <Badge
+                        key={tag.id}
+                        size="md"
+                        rounded="md"
+                        colorPalette={resourceTypeToColor(resource.type)}
+                    >
+                      {tag.value}
+                    </Badge>
+                ))}
               </HStack>
-          )}
+              <Text color="fg.muted" lineClamp={2}>
+                {resource.description}
+              </Text>
+            </Card.Body>
+
+            <Card.Footer justifyContent="space-between" pt={4}>
+              {author && (
+                  <HStack>
+                    <Avatar.Root shape="full" size="sm">
+                      <Avatar.Fallback name={author}/>
+                      <Avatar.Image src={author}/>
+                    </Avatar.Root>
 
-          {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.MODEL && (
-              <ModelCardButton model={resource as ModelResource}/>
-          )}
-        </Card.Footer>
+                    <Box>
+                      <Text fontWeight="bold">{author}</Text>
+                    </Box>
+                  </HStack>
+              )}
+
+              {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.MODEL 
&& (
+                  <ModelCardButton model={resource as ModelResource}/>
+              )}
+            </Card.Footer>
+          </Box>
+        </Link>
       </Card.Root>
   );
 
-  if (clickable) {
-    return (
-        <Box>
-          <Link to={link}>{content}</Link>
-        </Box>
-    );
-  }
-  return <Box>{content}</Box>;
+  // if (clickable) {
+  //   return (
+  //       <Box hidden={hideCard}>
+  //         <Link to={link}>{content}</Link>
+  //       </Box>
+  //   );
+  // }
+  return <Box hidden={hideCard}>{content}</Box>;
 };
diff --git 
a/modules/research-framework/portal/src/components/notebooks/index.tsx 
b/modules/research-framework/portal/src/components/notebooks/index.tsx
index cb241577ea..a7af2a508e 100644
--- a/modules/research-framework/portal/src/components/notebooks/index.tsx
+++ b/modules/research-framework/portal/src/components/notebooks/index.tsx
@@ -1,13 +1,13 @@
-import { Container, Input, SimpleGrid } from "@chakra-ui/react";
-import { PageHeader } from "../PageHeader";
-import { InputGroup } from "../ui/input-group";
-import { LuSearch } from "react-icons/lu";
-import { useEffect, useState } from "react";
+import {Container, Input, SimpleGrid} from "@chakra-ui/react";
+import {PageHeader} from "../PageHeader";
+import {InputGroup} from "../ui/input-group";
+import {LuSearch} from "react-icons/lu";
+import {useEffect, useState} from "react";
 import api from "@/lib/api";
-import { NotebookResource } from "@/interfaces/ResourceType";
-import { ResourceCard } from "../home/ResourceCard";
-import { CONTROLLER } from "@/lib/controller";
-import { ResourceTypeEnum } from "@/interfaces/ResourceTypeEnum";
+import {NotebookResource} from "@/interfaces/ResourceType";
+import {ResourceCard} from "../home/ResourceCard";
+import {CONTROLLER} from "@/lib/controller";
+import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum";
 
 const getNotebooks = async () => {
   try {
@@ -38,34 +38,33 @@ const Notebooks = () => {
   }, []);
 
   return (
-    <>
-      <Container maxW="container.lg" mt={8}>
-        <PageHeader
-          title="Notebooks"
-          description="Create and manage your notebooks. From here, you can 
create new notebooks, view existing ones, and manage them."
-        />
-        <InputGroup endElement={<LuSearch />} w="100%" mt={4}>
-          <Input placeholder="Search" rounded="md" />
-        </InputGroup>
+      <>
+        <Container maxW="container.lg" mt={8}>
+          <PageHeader
+              title="Notebooks"
+              description="Create and manage your notebooks. From here, you 
can create new notebooks, view existing ones, and manage them."
+          />
+          <InputGroup endElement={<LuSearch/>} w="100%" mt={4}>
+            <Input placeholder="Search" rounded="md"/>
+          </InputGroup>
 
-        <SimpleGrid
-          columns={{ base: 1, md: 2, lg: 3 }}
-          mt={4}
-          gap={12}
-          justifyContent="space-around"
-        >
-          {notebooks.map((notebook: NotebookResource) => {
-            return (
-              <ResourceCard
-                resource={notebook}
-                key={notebook.id}
-                appendTypeToUrl={false}
-              />
-            );
-          })}
-        </SimpleGrid>
-      </Container>
-    </>
+          <SimpleGrid
+              columns={{base: 1, md: 2, lg: 3}}
+              mt={4}
+              gap={12}
+              justifyContent="space-around"
+          >
+            {notebooks.map((notebook: NotebookResource) => {
+              return (
+                  <ResourceCard
+                      resource={notebook}
+                      key={notebook.id}
+                  />
+              );
+            })}
+          </SimpleGrid>
+        </Container>
+      </>
   );
 };
 
diff --git 
a/modules/research-framework/portal/src/components/projects/DeleteProjectButton.tsx
 
b/modules/research-framework/portal/src/components/projects/DeleteProjectButton.tsx
new file mode 100644
index 0000000000..a9621d462c
--- /dev/null
+++ 
b/modules/research-framework/portal/src/components/projects/DeleteProjectButton.tsx
@@ -0,0 +1,115 @@
+import {Box, Button, CloseButton, Dialog, Input, Menu, Portal, Text, 
useDialog} from "@chakra-ui/react";
+import {BsThreeDots} from "react-icons/bs";
+import {FaTrash} from "react-icons/fa";
+import {useAuth} from "react-oidc-context";
+import {isProjectOwner} from "@/lib/util.ts";
+import {useState} from "react";
+import api from "@/lib/api.ts";
+import {CONTROLLER} from "@/lib/controller.ts";
+import {toaster} from "@/components/ui/toaster.tsx";
+import {ProjectType} from "@/interfaces/ProjectType.tsx";
+
+export const DeleteProjectButton = ({
+                                      project,
+                                      onSuccess,
+                                    }: {
+  project: ProjectType
+  onSuccess: () => void;
+}) => {
+  const dialog = useDialog();
+  const [deleteName, setDeleteName] = useState("");
+  const [deleteLoading, setDeleteLoading] = useState(false);
+  const auth = useAuth();
+  const isOwner = isProjectOwner(auth.user?.profile.email || "INVALID", 
project);
+  if (!isOwner || !auth.isAuthenticated) {
+    return null;
+  }
+
+  const handleDeleteProject = async () => {
+    setDeleteLoading(true);
+    try {
+      await api.delete(`${CONTROLLER.projects}/${project.id}`);
+      toaster.create({
+        title: "Project deleted",
+        description: project.name,
+        type: "success",
+      })
+      onSuccess();
+      dialog.setOpen(false);
+    } catch {
+      toaster.create({
+        title: "Error deleting session",
+        type: "error",
+      });
+    } finally {
+      setDeleteLoading(false)
+    }
+  }
+
+  return (
+      <>
+        <Menu.Root>
+          <Menu.Trigger _hover={{
+            cursor: 'pointer',
+          }}>
+            <BsThreeDots/>
+          </Menu.Trigger>
+          <Menu.Positioner>
+            <Menu.Content>
+              <Menu.Item value={"delete"}
+                         color="fg.error"
+                         _hover={{bg: "bg.error", color: "fg.error", cursor: 
"pointer"}}
+                         onClick={() => dialog.setOpen(true)}
+              >
+                <FaTrash/>
+                <Box flex="1">Delete</Box>
+
+              </Menu.Item>
+
+            </Menu.Content>
+          </Menu.Positioner>
+        </Menu.Root>
+
+        <Dialog.RootProvider size="sm" value={dialog}>
+          <Portal>
+            <Dialog.Backdrop/>
+            <Dialog.Positioner>
+              <Dialog.Content>
+                <Dialog.Header>
+                  <Dialog.Title>Delete Project</Dialog.Title>
+                </Dialog.Header>
+                <Dialog.Body>
+                  <Text color="gray.500">
+                    This action is irreversible. To confirm, please type:{" "}
+                    <b>{project.name}</b>.
+                  </Text>
+
+                  <Input
+                      mt={2}
+                      placeholder="Project name"
+                      value={deleteName}
+                      onChange={(e) => setDeleteName(e.target.value)}
+                  />
+                </Dialog.Body>
+                <Dialog.Footer>
+                  <Button
+                      width="100%"
+                      colorPalette="red"
+                      disabled={deleteName !== project.name || deleteLoading}
+                      loading={deleteLoading}
+                      onClick={handleDeleteProject}
+                  >
+                    Delete
+                  </Button>
+                </Dialog.Footer>
+                <Dialog.CloseTrigger asChild>
+                  <CloseButton size="sm"/>
+                </Dialog.CloseTrigger>
+              </Dialog.Content>
+            </Dialog.Positioner>
+          </Portal>
+        </Dialog.RootProvider>
+
+      </>
+  )
+}
\ No newline at end of file
diff --git 
a/modules/research-framework/portal/src/components/resources/DeleteResourceButton.tsx
 
b/modules/research-framework/portal/src/components/resources/DeleteResourceButton.tsx
new file mode 100644
index 0000000000..580c4f5051
--- /dev/null
+++ 
b/modules/research-framework/portal/src/components/resources/DeleteResourceButton.tsx
@@ -0,0 +1,115 @@
+import {Box, Button, CloseButton, Dialog, Input, Menu, Portal, Text, 
useDialog} from "@chakra-ui/react";
+import {BsThreeDots} from "react-icons/bs";
+import {FaTrash} from "react-icons/fa";
+import {Resource} from "@/interfaces/ResourceType.ts";
+import {useAuth} from "react-oidc-context";
+import {isResourceOwner} from "@/lib/util.ts";
+import {useState} from "react";
+import api from "@/lib/api.ts";
+import {CONTROLLER} from "@/lib/controller.ts";
+import {toaster} from "@/components/ui/toaster.tsx";
+
+export const DeleteResourceButton = ({
+                                       resource,
+                                       onSuccess,
+                                     }: {
+  resource: Resource
+  onSuccess: () => void;
+}) => {
+  const dialog = useDialog();
+  const [deleteName, setDeleteName] = useState("");
+  const [deleteLoading, setDeleteLoading] = useState(false);
+  const auth = useAuth();
+  const isOwner = isResourceOwner(auth.user?.profile.email || "INVALID", 
resource);
+  if (!isOwner || !auth.isAuthenticated) {
+    return null;
+  }
+
+  const handleDeleteResource = async () => {
+    setDeleteLoading(true);
+    try {
+      await api.delete(`${CONTROLLER.resources}/${resource.id}`);
+      toaster.create({
+        title: "Resource deleted",
+        description: resource.name,
+        type: "success",
+      })
+      onSuccess();
+      dialog.setOpen(false);
+    } catch {
+      toaster.create({
+        title: "Error deleting session",
+        type: "error",
+      });
+    } finally {
+      setDeleteLoading(false)
+    }
+  }
+
+  return (
+      <>
+        <Menu.Root>
+          <Menu.Trigger _hover={{
+            cursor: 'pointer',
+          }}>
+            <BsThreeDots/>
+          </Menu.Trigger>
+          <Menu.Positioner>
+            <Menu.Content>
+              <Menu.Item value={"delete"}
+                         color="fg.error"
+                         _hover={{bg: "bg.error", color: "fg.error", cursor: 
"pointer"}}
+                         onClick={() => dialog.setOpen(true)}
+              >
+                <FaTrash/>
+                <Box flex="1">Delete</Box>
+
+              </Menu.Item>
+
+            </Menu.Content>
+          </Menu.Positioner>
+        </Menu.Root>
+
+        <Dialog.RootProvider size="sm" value={dialog}>
+          <Portal>
+            <Dialog.Backdrop/>
+            <Dialog.Positioner>
+              <Dialog.Content>
+                <Dialog.Header>
+                  <Dialog.Title>Delete Resource</Dialog.Title>
+                </Dialog.Header>
+                <Dialog.Body>
+                  <Text color="gray.500">
+                    This action is irreversible. To confirm, please type:{" "}
+                    <b>{resource.name}</b>.
+                  </Text>
+
+                  <Input
+                      mt={2}
+                      placeholder="Resource name"
+                      value={deleteName}
+                      onChange={(e) => setDeleteName(e.target.value)}
+                  />
+                </Dialog.Body>
+                <Dialog.Footer>
+                  <Button
+                      width="100%"
+                      colorPalette="red"
+                      disabled={deleteName !== resource.name || deleteLoading}
+                      loading={deleteLoading}
+                      onClick={handleDeleteResource}
+                  >
+                    Delete
+                  </Button>
+                </Dialog.Footer>
+                <Dialog.CloseTrigger asChild>
+                  <CloseButton size="sm"/>
+                </Dialog.CloseTrigger>
+              </Dialog.Content>
+            </Dialog.Positioner>
+          </Portal>
+        </Dialog.RootProvider>
+
+      </>
+  )
+}
\ No newline at end of file
diff --git 
a/modules/research-framework/portal/src/components/resources/ResourceDetails.tsx
 
b/modules/research-framework/portal/src/components/resources/ResourceDetails.tsx
index feb844ee8e..80c5b17712 100644
--- 
a/modules/research-framework/portal/src/components/resources/ResourceDetails.tsx
+++ 
b/modules/research-framework/portal/src/components/resources/ResourceDetails.tsx
@@ -1,20 +1,20 @@
-import { useLocation, useNavigate, useParams } from "react-router";
+import {useLocation, useNavigate, useParams} from "react-router";
 import {
+  Avatar,
+  Badge,
+  Box,
+  Button,
   Container,
-  Spinner,
+  Heading,
   HStack,
-  Box,
-  Separator,
   Icon,
-  Text,
   Image,
-  Badge,
-  Heading,
-  Avatar,
-  Button,
+  Separator,
+  Spinner,
+  Text,
 } from "@chakra-ui/react";
-import { useEffect, useState } from "react";
-import { BiArrowBack } from "react-icons/bi";
+import {useEffect, useState} from "react";
+import {BiArrowBack} from "react-icons/bi";
 import api from "@/lib/api";
 import {
   DatasetResource,
@@ -23,15 +23,16 @@ import {
   RepositoryResource,
   Resource,
 } from "@/interfaces/ResourceType";
-import { Tag } from "@/interfaces/TagType";
-import { isValidImaage, resourceTypeToColor } from "@/lib/util";
-import { ResourceTypeBadge } from "./ResourceTypeBadge";
-import { ResourceTypeEnum } from "@/interfaces/ResourceTypeEnum";
-import { ModelSpecificBox } from "../models/ModelSpecificBox";
-import { NotebookSpecificDetails } from "../notebooks/NotebookSpecificDetails";
-import { RepositorySpecificDetails } from 
"../repositories/RepositorySpecificDetails";
-import { CONTROLLER } from "@/lib/controller";
-import { DatasetSpecificDetails } from "../datasets/DatasetSpecificDetails";
+import {Tag} from "@/interfaces/TagType";
+import {isValidImaage, resourceTypeToColor} from "@/lib/util";
+import {ResourceTypeBadge} from "./ResourceTypeBadge";
+import {ResourceTypeEnum} from "@/interfaces/ResourceTypeEnum";
+import {ModelSpecificBox} from "../models/ModelSpecificBox";
+import {NotebookSpecificDetails} from "../notebooks/NotebookSpecificDetails";
+import {RepositorySpecificDetails} from 
"../repositories/RepositorySpecificDetails";
+import {CONTROLLER} from "@/lib/controller";
+import {DatasetSpecificDetails} from "../datasets/DatasetSpecificDetails";
+import {DeleteResourceButton} from 
"@/components/resources/DeleteResourceButton.tsx";
 
 async function getResource(id: string) {
   const response = await api.get(`${CONTROLLER.resources}/${id}`);
@@ -39,10 +40,10 @@ async function getResource(id: string) {
 }
 
 const ResourceDetails = () => {
-  const { id } = useParams();
+  const {id} = useParams();
   const [resource, setResource] = useState<Resource | null>(null);
   const navigate = useNavigate();
-  const { state } = useLocation();
+  const {state} = useLocation();
 
   useEffect(() => {
     if (!id) return;
@@ -53,133 +54,142 @@ const ResourceDetails = () => {
 
       setResource(r);
     }
+
     getData();
   }, [id, state]);
 
-  if (!resource) return <Spinner />;
+  if (!resource) return <Spinner/>;
 
   const validImage = isValidImaage(resource.headerImage);
 
+  const goToResources = () => {
+    navigate(
+        "/resources?resourceTypes=REPOSITORY%2CNOTEBOOK%2CDATASET%2CMODEL"
+    )
+  }
+
+
   return (
-    <>
-      <Container maxW="breakpoint-lg" mx="auto" p={4} mt={16}>
-        <Box>
-          <Button
-            variant="plain"
-            p={0}
-            onClick={() =>
-              navigate(
-                
"/resources?resourceTypes=REPOSITORY%2CNOTEBOOK%2CDATASET%2CMODEL"
-              )
-            }
-          >
-            <HStack
-              alignItems="center"
-              mb={4}
-              display="inline-flex"
-              _hover={{
-                bg: "gray.300",
-              }}
-              p={1}
-              rounded="md"
+      <>
+        <Container maxW="breakpoint-lg" mx="auto" p={4} mt={16}>
+          <Box>
+            <Button
+                variant="plain"
+                p={0}
+                onClick={goToResources}
             >
-              <Icon>
-                <BiArrowBack />
-              </Icon>
-              Back
-            </HStack>
-          </Button>
-        </Box>
-
-        <HStack
-          alignItems={"flex-start"}
-          mb={4}
-          gap={8}
-          justifyContent="space-between"
-        >
+              <HStack
+                  alignItems="center"
+                  mb={4}
+                  display="inline-flex"
+                  _hover={{
+                    bg: "gray.300",
+                  }}
+                  p={1}
+                  rounded="md"
+              >
+                <Icon>
+                  <BiArrowBack/>
+                </Icon>
+                Back
+              </HStack>
+            </Button>
+          </Box>
+
+          <HStack
+              alignItems={"flex-start"}
+              mb={4}
+              gap={8}
+              justifyContent="space-between"
+          >
+            <Box w={'full'}>
+              <ResourceTypeBadge type={resource.type}/>
+
+              <HStack mt={2} justifyContent={'space-between'} 
alignItems={'center'} flexWrap={'wrap'}>
+                <Heading as="h1" size="4xl">
+                  {resource.name}
+                </Heading>
+
+                <DeleteResourceButton resource={resource} 
onSuccess={goToResources}/>
+              </HStack>
+
+              <HStack mt={2}>
+                {resource.tags.map((tag: Tag) => (
+                    <Badge
+                        key={tag.id}
+                        size="lg"
+                        rounded="md"
+                        colorPalette={resourceTypeToColor(resource.type)}
+                    >
+                      {tag.value}
+                    </Badge>
+                ))}
+              </HStack>
+
+              <HStack mt={8}>
+                {resource.authors.map((author: string) => {
+                  return (
+                      <HStack key={author}>
+                        <Avatar.Root shape="full" size="xl">
+                          <Avatar.Fallback name={author}/>
+                          <Avatar.Image src={author}/>
+                        </Avatar.Root>
+
+                        <Box>
+                          <Text fontWeight="bold">{author}</Text>
+                        </Box>
+                      </HStack>
+                  );
+                })}
+              </HStack>
+            </Box>
+
+            <Box>
+              {validImage && (
+                  <Image
+                      src={resource.headerImage}
+                      alt="Notebook Header"
+                      rounded="md"
+                      maxW="300px"
+                  />
+              )}
+            </Box>
+          </HStack>
+
+          <Separator my={6}/>
           <Box>
-            <ResourceTypeBadge type={resource.type} />
-            <Heading as="h1" size="4xl" mt={2}>
-              {resource.name}
+            <Heading fontWeight="bold" size="2xl">
+              About
             </Heading>
 
-            <HStack mt={2}>
-              {resource.tags.map((tag: Tag) => (
-                <Badge
-                  key={tag.id}
-                  size="lg"
-                  rounded="md"
-                  colorPalette={resourceTypeToColor(resource.type)}
-                >
-                  {tag.value}
-                </Badge>
-              ))}
-            </HStack>
-
-            <HStack mt={8}>
-              {resource.authors.map((author: string) => {
-                return (
-                  <HStack key={author}>
-                    <Avatar.Root shape="full" size="xl">
-                      <Avatar.Fallback name={author} />
-                      <Avatar.Image src={author} />
-                    </Avatar.Root>
-
-                    <Box>
-                      <Text fontWeight="bold">{author}</Text>
-                    </Box>
-                  </HStack>
-                );
-              })}
-            </HStack>
+            <Text>{resource.description}</Text>
           </Box>
 
+          <Separator my={8}/>
+
           <Box>
-            {validImage && (
-              <Image
-                src={resource.headerImage}
-                alt="Notebook Header"
-                rounded="md"
-                maxW="300px"
-              />
+            {(resource.type as ResourceTypeEnum) ===
+                ResourceTypeEnum.REPOSITORY && (
+                    <RepositorySpecificDetails
+                        repository={resource as RepositoryResource}
+                    />
+                )}
+
+            {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.DATASET 
&& (
+                <DatasetSpecificDetails dataset={resource as DatasetResource}/>
+            )}
+
+            {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.MODEL && 
(
+                <ModelSpecificBox model={resource as ModelResource}/>
             )}
+
+            {(resource.type as ResourceTypeEnum) ===
+                ResourceTypeEnum.NOTEBOOK && (
+                    <NotebookSpecificDetails notebook={resource as 
NotebookResource}/>
+                )}
           </Box>
-        </HStack>
-
-        <Separator my={6} />
-        <Box>
-          <Heading fontWeight="bold" size="2xl">
-            About
-          </Heading>
-
-          <Text>{resource.description}</Text>
-        </Box>
-
-        <Separator my={8} />
-
-        <Box>
-          {(resource.type as ResourceTypeEnum) ===
-            ResourceTypeEnum.REPOSITORY && (
-            <RepositorySpecificDetails
-              repository={resource as RepositoryResource}
-            />
-          )}
-
-          {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.DATASET && 
(
-            <DatasetSpecificDetails dataset={resource as DatasetResource} />
-          )}
-
-          {(resource.type as ResourceTypeEnum) === ResourceTypeEnum.MODEL && (
-            <ModelSpecificBox model={resource as ModelResource} />
-          )}
-
-          {(resource.type as ResourceTypeEnum) ===
-            ResourceTypeEnum.NOTEBOOK && (
-            <NotebookSpecificDetails notebook={resource as NotebookResource} />
-          )}
-        </Box>
-      </Container>
-    </>
+        </Container>
+      </>
   );
 };
 
diff --git 
a/modules/research-framework/portal/src/components/resources/ResourceTypeBadge.tsx
 
b/modules/research-framework/portal/src/components/resources/ResourceTypeBadge.tsx
index 49c7b8b0d9..9ca15e1316 100644
--- 
a/modules/research-framework/portal/src/components/resources/ResourceTypeBadge.tsx
+++ 
b/modules/research-framework/portal/src/components/resources/ResourceTypeBadge.tsx
@@ -1,26 +1,27 @@
-import { resourceTypeToColor } from "@/lib/util";
-import { Badge } from "@chakra-ui/react";
+import {resourceTypeToColor} from "@/lib/util";
+import {Badge} from "@chakra-ui/react";
 
 interface ResourceTypeBadgeProps {
   type: string;
+
   [key: string]: string | number | boolean; // Specify a more specific type 
for additional props
 }
 
 export const ResourceTypeBadge = ({
-  type,
-  ...props
-}: ResourceTypeBadgeProps) => {
+                                    type,
+                                    ...props
+                                  }: ResourceTypeBadgeProps) => {
   return (
-    <Badge
-      colorPalette={resourceTypeToColor(type)}
-      fontWeight="bold"
-      size="sm"
-      px="2"
-      py="1"
-      borderRadius="md"
-      {...props}
-    >
-      {type}
-    </Badge>
+      <Badge
+          colorPalette={resourceTypeToColor(type)}
+          fontWeight="bold"
+          size="xs"
+          px="2"
+          py="1"
+          borderRadius="md"
+          {...props}
+      >
+        {type}
+      </Badge>
   );
 };
diff --git 
a/modules/research-framework/portal/src/components/resources/index.tsx 
b/modules/research-framework/portal/src/components/resources/index.tsx
index 12c4efacbf..a86bd1b626 100644
--- a/modules/research-framework/portal/src/components/resources/index.tsx
+++ b/modules/research-framework/portal/src/components/resources/index.tsx
@@ -334,7 +334,6 @@ export const Resources = () => {
                   <ResourceCard
                       resource={resource}
                       key={resource.id}
-                      appendTypeToUrl={true}
                   />
               );
             })}
diff --git a/modules/research-framework/portal/src/interfaces/ProjectType.tsx 
b/modules/research-framework/portal/src/interfaces/ProjectType.tsx
index 94d0afb6d2..87a7c78774 100644
--- a/modules/research-framework/portal/src/interfaces/ProjectType.tsx
+++ b/modules/research-framework/portal/src/interfaces/ProjectType.tsx
@@ -1,4 +1,4 @@
-import { DatasetResource, RepositoryResource } from "./ResourceType";
+import {DatasetResource, RepositoryResource} from "./ResourceType";
 
 export interface ProjectType {
   id: string;
@@ -7,6 +7,7 @@ export interface ProjectType {
   datasetResources: DatasetResource[];
   createdAt: string;
   updatedAt: string;
+  ownerId: string;
 }
 
 export interface ProjectPostData {
diff --git a/modules/research-framework/portal/src/lib/util.ts 
b/modules/research-framework/portal/src/lib/util.ts
index bad255b39a..9cfb333adb 100644
--- a/modules/research-framework/portal/src/lib/util.ts
+++ b/modules/research-framework/portal/src/lib/util.ts
@@ -1,3 +1,6 @@
+import {Resource} from "@/interfaces/ResourceType.ts";
+import {ProjectType} from "@/interfaces/ProjectType.tsx";
+
 export const resourceTypeToColor = (type: string) => {
   if (type === "NOTEBOOK") {
     return "blue";
@@ -26,7 +29,15 @@ export const getGithubOwnerAndRepo = (url: string) => {
   if (match) {
     const owner = match[1];
     const repo = match[2].replace(/\.git$/, "");
-    return { owner, repo };
+    return {owner, repo};
   }
   return null;
+}
+
+export const isResourceOwner = (userEmail: string, resource: Resource) => {
+  return resource.authors.includes(userEmail);
+}
+
+export const isProjectOwner = (userEmail: string, project: ProjectType) => {
+  return project.ownerId.toLowerCase() === userEmail.toLowerCase();
 }
\ No newline at end of file
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/AuthzTokenFilter.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/AuthzTokenFilter.java
index f4a45cb5ba..0fcd374cf5 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/AuthzTokenFilter.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/AuthzTokenFilter.java
@@ -59,11 +59,11 @@ public class AuthzTokenFilter extends OncePerRequestFilter {
 
         if (request.getMethod().equalsIgnoreCase("POST")
                 || request.getMethod().equalsIgnoreCase("PUT")
-                || request.getMethod().equalsIgnoreCase("PATCH")) {
+                || request.getMethod().equalsIgnoreCase("PATCH")
+                || request.getMethod().equalsIgnoreCase("DELETE")) {
             return false; // mutation requests should be authenticated
         }
 
-        // TODO: ensure that only GET requests do not need auth
         return path.startsWith("/swagger")
                 || path.startsWith("/v2/api-docs")
                 || path.startsWith("/v3/api-docs")
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ProjectController.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ProjectController.java
index b36cba52f5..a29c37ced2 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ProjectController.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ProjectController.java
@@ -27,7 +27,13 @@ import 
org.apache.airavata.research.service.handlers.ProjectHandler;
 import org.apache.airavata.research.service.model.entity.Project;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/api/v1/rf/projects")
@@ -54,4 +60,10 @@ public class ProjectController {
     public ResponseEntity<Project> createProject(@RequestBody 
CreateProjectRequest createProjectRequest) {
         return 
ResponseEntity.ok(projectHandler.createProject(createProjectRequest));
     }
+
+    @DeleteMapping("/{projectId}")
+    @Operation(summary = "Delete project by id")
+    public ResponseEntity<Boolean> deleteProjectById(@PathVariable(value = 
"projectId") String projectId) {
+        return ResponseEntity.ok(projectHandler.deleteProject(projectId));
+    }
 }
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ResourceController.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ResourceController.java
index 06d414f91f..d5f2f696a1 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ResourceController.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/controller/ResourceController.java
@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PatchMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -100,12 +101,18 @@ public class ResourceController {
         return ResponseEntity.ok(resourceHandler.getAllTagsByPopularity());
     }
 
-    @Operation(summary = "Get dataset, notebook, or repository")
+    @Operation(summary = "Get dataset, notebook, repository, or model")
     @GetMapping(value = "/{id}")
     public ResponseEntity<Resource> getResource(@PathVariable(value = "id") 
String id) {
         return ResponseEntity.ok(resourceHandler.getResourceById(id));
     }
 
+    @Operation(summary = "Delete dataset, notebook, repository, or model")
+    @DeleteMapping(value = "/{id}")
+    public ResponseEntity<Boolean> deleteResource(@PathVariable(value = "id") 
String id) {
+        return ResponseEntity.ok(resourceHandler.deleteResourceById(id));
+    }
+
     @Operation(summary = "Get all resources")
     @GetMapping("/")
     public ResponseEntity<Page<Resource>> getAllResources(
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/StateEnum.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/StateEnum.java
new file mode 100644
index 0000000000..07586eacdd
--- /dev/null
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/StateEnum.java
@@ -0,0 +1,25 @@
+/**
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+package org.apache.airavata.research.service.enums;
+
+public enum StateEnum {
+    ACTIVE,
+    DELETED
+}
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ProjectHandler.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ProjectHandler.java
index 545a6f8938..9fca965303 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ProjectHandler.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ProjectHandler.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.airavata.research.service.dto.CreateProjectRequest;
 import org.apache.airavata.research.service.enums.ResourceTypeEnum;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.apache.airavata.research.service.model.UserContext;
 import org.apache.airavata.research.service.model.entity.DatasetResource;
 import org.apache.airavata.research.service.model.entity.Project;
@@ -52,7 +53,7 @@ public class ProjectHandler {
     }
 
     public Project findProject(String projectId) {
-        return projectRepository.findById(projectId).orElseThrow(() -> {
+        return projectRepository.findByIdAndState(projectId, 
StateEnum.ACTIVE).orElseThrow(() -> {
             LOGGER.error("Unable to find a Project with id: {}", projectId);
             return new EntityNotFoundException("Unable to find a Project with 
id: " + projectId);
         });
@@ -95,26 +96,46 @@ public class ProjectHandler {
 
         Set<DatasetResource> datasetResourcesSet = new 
HashSet<>(datasetResourcesList);
         project.setDatasetResources(datasetResourcesSet);
+        project.setState(StateEnum.ACTIVE);
         projectRepository.save(project);
         return project;
     }
 
     public List<Project> getAllProjects() {
-        return projectRepository.findAll();
+        return projectRepository.findALlByState(StateEnum.ACTIVE);
     }
 
     public List<Project> getAllProjectsByOwnerId(String ownerId) {
-        return projectRepository.findAllByOwnerIdOrderByCreatedAtDesc(ownerId);
+        return 
projectRepository.findAllByOwnerIdAndStateOrderByCreatedAtDesc(ownerId, 
StateEnum.ACTIVE);
+    }
+
+    public boolean deleteProject(String projectId) {
+        Optional<Project> optionalProject = 
projectRepository.findByIdAndState(projectId, StateEnum.ACTIVE);
+        if (optionalProject.isEmpty()
+                || StateEnum.DELETED.equals(optionalProject.get().getState())) 
{
+            throw new EntityNotFoundException("Unable to find a Project with 
id: " + projectId);
+        }
+
+        Project project = optionalProject.get();
+        String userId = UserContext.userId();
+        if (!project.getOwnerId().equalsIgnoreCase(userId)) {
+            throw new RuntimeException(
+                    String.format("User %s is not authorized to delete project 
with id: %s", userId, projectId));
+        }
+
+        project.setState(StateEnum.DELETED);
+        projectRepository.save(project);
+        return true;
     }
 
     public List<Project> findProjectsWithRepository(RepositoryResource 
repositoryResource) {
-        return 
projectRepository.findProjectsByRepositoryResource(repositoryResource);
+        return 
projectRepository.findProjectsByRepositoryResourceAndState(repositoryResource, 
StateEnum.ACTIVE);
     }
 
     public List<Project> findProjectsContainingDataset(DatasetResource 
datasetResource) {
         Set<DatasetResource> set = new HashSet<>();
         set.add(datasetResource);
 
-        return projectRepository.findProjectsByDatasetResourcesContaining(set);
+        return 
projectRepository.findProjectsByDatasetResourcesContainingAndState(set, 
StateEnum.ACTIVE);
     }
 }
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java
index 7de2ff38b1..475e2b7c18 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java
@@ -19,16 +19,19 @@
 */
 package org.apache.airavata.research.service.handlers;
 
+import jakarta.persistence.EntityNotFoundException;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.airavata.model.user.UserProfile;
 import org.apache.airavata.research.service.AiravataService;
 import org.apache.airavata.research.service.dto.CreateResourceRequest;
 import org.apache.airavata.research.service.dto.ModifyResourceRequest;
 import org.apache.airavata.research.service.dto.ResourceResponse;
 import org.apache.airavata.research.service.enums.ResourceTypeEnum;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.apache.airavata.research.service.enums.StatusEnum;
 import org.apache.airavata.research.service.model.UserContext;
 import org.apache.airavata.research.service.model.entity.RepositoryResource;
@@ -71,10 +74,9 @@ public class ResourceHandler {
             try {
                 UserProfile fetchedUser = 
airavataService.getUserProfile(authorId);
                 userSet.add(fetchedUser.getUserId());
-
             } catch (Exception e) {
                 LOGGER.error("Error while fetching user profile with the 
userId: {}", authorId, e);
-                throw new RuntimeException("Error while fetching user profile 
with the userId: " + authorId, e);
+                throw new EntityNotFoundException("Error while fetching user 
profile with the userId: " + authorId, e);
             }
         }
 
@@ -89,6 +91,7 @@ public class ResourceHandler {
         }
         resource.setAuthors(userSet);
         resource.setTags(tags);
+        resource.setState(StateEnum.ACTIVE);
     }
 
     public ResourceResponse createResource(Resource resource, ResourceTypeEnum 
type) {
@@ -118,7 +121,9 @@ public class ResourceHandler {
 
         resource.setName(createResourceRequest.getName());
         resource.setDescription(createResourceRequest.getDescription());
-        resource.setAuthors(createResourceRequest.getAuthors());
+        resource.setAuthors(createResourceRequest.getAuthors().stream()
+                .map(String::toLowerCase)
+                .collect(Collectors.toSet()));
         Set<org.apache.airavata.research.service.model.entity.Tag> tagsSet = 
new HashSet<>();
         for (String tag : createResourceRequest.getTags()) {
             org.apache.airavata.research.service.model.entity.Tag t =
@@ -142,10 +147,13 @@ public class ResourceHandler {
     public Resource modifyResource(ModifyResourceRequest resourceRequest) {
         Optional<Resource> resourceOp = 
resourceRepository.findById(resourceRequest.getId());
         if (resourceOp.isEmpty()) {
-            throw new RuntimeException("Resource not found");
+            throw new EntityNotFoundException("Resource not found");
         }
 
         Resource resource = resourceOp.get();
+        if (StateEnum.DELETED.equals(resource.getState())) {
+            throw new RuntimeException(String.format("Cannot modify deleted 
resource: %s", resource.getId()));
+        }
 
         // ensure that the user making the request is one of the current 
authors
         boolean found = false;
@@ -168,15 +176,38 @@ public class ResourceHandler {
 
     public Resource getResourceById(String id) {
         // Your logic to fetch the resource by ID
-        Optional<Resource> opResource = resourceRepository.findById(id);
+        Optional<Resource> opResource = 
resourceRepository.findByIdAndState(id, StateEnum.ACTIVE);
 
         if (opResource.isEmpty()) {
-            throw new RuntimeException("Resource not found: " + id);
+            throw new EntityNotFoundException("Resource not found: " + id);
         }
 
         return opResource.get();
     }
 
+    public boolean deleteResourceById(String id) {
+        Optional<Resource> opResource = 
resourceRepository.findByIdAndState(id, StateEnum.ACTIVE);
+
+        if (opResource.isEmpty()) {
+            throw new EntityNotFoundException("Resource not found: " + id);
+        }
+
+        Resource resource = opResource.get();
+
+        String userEmail = UserContext.userId();
+        if (!resource.getAuthors().contains(userEmail.toLowerCase())) {
+            String errorMsg = String.format(
+                    "User %s not authorized to delete resource: %s (%s), type: 
%s",
+                    userEmail, resource.getName(), id, 
resource.getType().toString());
+            LOGGER.error(errorMsg);
+            throw new RuntimeException(errorMsg);
+        }
+
+        resource.setState(StateEnum.DELETED);
+        resourceRepository.delete(resource);
+        return true;
+    }
+
     public Page<Resource> getAllResources(
             int pageNumber, int pageSize, List<Class<? extends Resource>> 
typeList, String[] tag, String nameSearch) {
         Pageable pageable = PageRequest.of(pageNumber, pageSize);
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Project.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Project.java
index 9614190b96..ca3d67ee87 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Project.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Project.java
@@ -22,6 +22,8 @@ package org.apache.airavata.research.service.model.entity;
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EntityListeners;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
 import jakarta.persistence.FetchType;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.Id;
@@ -32,6 +34,7 @@ import jakarta.persistence.ManyToOne;
 import java.time.Instant;
 import java.util.HashSet;
 import java.util.Set;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.hibernate.annotations.UuidGenerator;
 import org.springframework.data.annotation.CreatedDate;
 import org.springframework.data.annotation.LastModifiedDate;
@@ -72,6 +75,10 @@ public class Project {
     @LastModifiedDate
     private Instant updatedAt;
 
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private StateEnum state;
+
     public String getId() {
         return id;
     }
@@ -131,4 +138,12 @@ public class Project {
     public void setUpdatedAt(Instant updatedAt) {
         this.updatedAt = updatedAt;
     }
+
+    public StateEnum getState() {
+        return state;
+    }
+
+    public void setState(StateEnum state) {
+        this.state = state;
+    }
 }
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java
index d4a88ee6f9..97814f3163 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java
@@ -41,6 +41,7 @@ import java.util.HashSet;
 import java.util.Set;
 import org.apache.airavata.research.service.enums.PrivacyEnum;
 import org.apache.airavata.research.service.enums.ResourceTypeEnum;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.apache.airavata.research.service.enums.StatusEnum;
 import org.hibernate.annotations.UuidGenerator;
 import org.springframework.data.annotation.CreatedDate;
@@ -84,6 +85,10 @@ public abstract class Resource {
     @Enumerated(EnumType.STRING)
     private StatusEnum status;
 
+    @Column(nullable = false)
+    @Enumerated(EnumType.STRING)
+    private StateEnum state;
+
     @Column(nullable = false)
     @Enumerated(EnumType.STRING)
     private PrivacyEnum privacy;
@@ -146,19 +151,27 @@ public abstract class Resource {
         this.tags = tags;
     }
 
-    public org.apache.airavata.research.service.enums.StatusEnum getStatus() {
+    public StatusEnum getStatus() {
         return status;
     }
 
-    public void 
setStatus(org.apache.airavata.research.service.enums.StatusEnum status) {
+    public void setStatus(StatusEnum status) {
         this.status = status;
     }
 
-    public org.apache.airavata.research.service.enums.PrivacyEnum getPrivacy() 
{
+    public StateEnum getState() {
+        return state;
+    }
+
+    public void setState(StateEnum state) {
+        this.state = state;
+    }
+
+    public PrivacyEnum getPrivacy() {
         return privacy;
     }
 
-    public void 
setPrivacy(org.apache.airavata.research.service.enums.PrivacyEnum privacy) {
+    public void setPrivacy(PrivacyEnum privacy) {
         this.privacy = privacy;
     }
 
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ProjectRepository.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ProjectRepository.java
index adc9916013..0b6ff35a0b 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ProjectRepository.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ProjectRepository.java
@@ -20,7 +20,9 @@
 package org.apache.airavata.research.service.model.repo;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.apache.airavata.research.service.model.entity.DatasetResource;
 import org.apache.airavata.research.service.model.entity.Project;
 import org.apache.airavata.research.service.model.entity.RepositoryResource;
@@ -39,4 +41,17 @@ public interface ProjectRepository extends 
JpaRepository<Project, String> {
     List<Project> findAllByOwnerId(String ownerId);
 
     List<Project> findAllByOwnerIdOrderByCreatedAtDesc(String ownerId);
+
+    List<Project> findALlByState(StateEnum state);
+
+    List<Project> findProjectsByRepositoryResourceAndState(RepositoryResource 
repositoryResource, StateEnum state);
+
+    List<Project> findAllByOwnerIdAndStateOrderByCreatedAtDesc(String ownerId, 
StateEnum state);
+
+    List<Project> findProjectsByDatasetResourcesContainingAndState(
+            Set<DatasetResource> datasetResources, StateEnum state);
+
+    StateEnum State(StateEnum state);
+
+    Optional<Project> findByIdAndState(String id, StateEnum state);
 }
diff --git 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ResourceRepository.java
 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ResourceRepository.java
index 7036d0e814..e53058ab10 100644
--- 
a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ResourceRepository.java
+++ 
b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/repo/ResourceRepository.java
@@ -20,6 +20,8 @@
 package org.apache.airavata.research.service.model.repo;
 
 import java.util.List;
+import java.util.Optional;
+import org.apache.airavata.research.service.enums.StateEnum;
 import org.apache.airavata.research.service.model.entity.Resource;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -35,7 +37,7 @@ public interface ResourceRepository extends 
JpaRepository<Resource, String> {
             """
                     SELECT r
                     FROM #{#entityName} r
-                    WHERE TYPE(r) IN :types AND r.name LIKE CONCAT('%', 
:nameSearch, '%')
+                    WHERE TYPE(r) IN :types AND r.name LIKE CONCAT('%', 
:nameSearch, '%') AND r.state = 'ACTIVE'
                     ORDER BY r.name
                     """)
     Page<Resource> findAllByTypes(
@@ -51,6 +53,7 @@ public interface ResourceRepository extends 
JpaRepository<Resource, String> {
                     WHERE r.class IN :typeList
                       AND t.value IN :tags
                       AND LOWER(r.name) LIKE LOWER(CONCAT('%', :nameSearch, 
'%'))
+                      AND r.state = 'ACTIVE'
                     GROUP BY r
                     HAVING COUNT(DISTINCT t.value) = :tagCount
                     ORDER BY r.name
@@ -66,9 +69,11 @@ public interface ResourceRepository extends 
JpaRepository<Resource, String> {
             """
                     SELECT r
                     FROM Resource r
-                    WHERE TYPE(r) = :type
+                    WHERE TYPE(r) = :type AND r.state = 'ACTIVE'
                     AND LOWER(r.name) LIKE LOWER(CONCAT('%', :name, '%'))
                     """)
     List<Resource> findByTypeAndNameContainingIgnoreCase(
             @Param("type") Class<? extends Resource> type, @Param("name") 
String name);
+
+    Optional<Resource> findByIdAndState(String id, StateEnum state);
 }
diff --git 
a/modules/research-framework/research-service/src/main/resources/application.yml
 
b/modules/research-framework/research-service/src/main/resources/application.yml
index ceb9916601..e59360f7ce 100644
--- 
a/modules/research-framework/research-service/src/main/resources/application.yml
+++ 
b/modules/research-framework/research-service/src/main/resources/application.yml
@@ -55,7 +55,7 @@ spring:
       leak-detection-threshold: 20000
   jpa:
     hibernate:
-      ddl-auto: none
+      ddl-auto: update
     open-in-view: false
 
 springdoc:

Reply via email to