You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/12/20 02:31:22 UTC

[camel-karavan] branch main updated: SHow correct file and project lastUpdates

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

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new 3f1f4fd  SHow correct file and project lastUpdates
3f1f4fd is described below

commit 3f1f4fd89c1b8f2930dad4e0d3aef4a9798573e6
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Dec 19 21:31:01 2022 -0500

    SHow correct file and project lastUpdates
---
 .../camel/karavan/api/ProjectFileResource.java     |  1 +
 .../org/apache/camel/karavan/model/GitRepo.java    | 49 ++++++++++++++++++++++
 .../apache/camel/karavan/model/GitRepoFile.java    | 37 ++++++++++++++++
 .../org/apache/camel/karavan/model/Project.java    | 15 ++++++-
 .../apache/camel/karavan/service/GitService.java   | 47 +++++++++++++++------
 .../camel/karavan/service/ImportService.java       | 32 +++++++-------
 .../camel/karavan/service/InfinispanService.java   |  1 -
 .../main/webui/src/projects/ProjectFilesTable.tsx  | 35 +---------------
 .../src/main/webui/src/projects/ProjectInfo.tsx    | 41 +++++++++++++-----
 .../src/main/webui/src/projects/ProjectModels.ts   |  2 +
 .../src/main/webui/src/projects/ProjectPage.tsx    | 14 ++++---
 .../main/webui/src/projects/ProjectPageToolbar.tsx |  5 ---
 12 files changed, 196 insertions(+), 83 deletions(-)

diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
index 747e34d..02dc7ad 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectFileResource.java
@@ -60,6 +60,7 @@ public class ProjectFileResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     public ProjectFile save(ProjectFile file) throws Exception {
+        file.setLastUpdate(Instant.now().toEpochMilli());
         infinispanService.saveProjectFile(file);
         return file;
     }
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepo.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepo.java
new file mode 100644
index 0000000..25d222f
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepo.java
@@ -0,0 +1,49 @@
+package org.apache.camel.karavan.model;
+
+import java.util.List;
+
+public class GitRepo {
+    private String name;
+    private String commitId;
+    private Long lastCommitTimestamp;
+    private List<GitRepoFile> files;
+
+    public GitRepo(String name, String commitId, Long lastCommitTimestamp, List<GitRepoFile> files) {
+        this.name = name;
+        this.commitId = commitId;
+        this.lastCommitTimestamp = lastCommitTimestamp;
+        this.files = files;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCommitId() {
+        return commitId;
+    }
+
+    public void setCommitId(String commitId) {
+        this.commitId = commitId;
+    }
+
+    public Long getLastCommitTimestamp() {
+        return lastCommitTimestamp;
+    }
+
+    public void setLastCommitTimestamp(Long lastCommitTimestamp) {
+        this.lastCommitTimestamp = lastCommitTimestamp;
+    }
+
+    public List<GitRepoFile> getFiles() {
+        return files;
+    }
+
+    public void setFiles(List<GitRepoFile> files) {
+        this.files = files;
+    }
+}
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepoFile.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepoFile.java
new file mode 100644
index 0000000..8505200
--- /dev/null
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/GitRepoFile.java
@@ -0,0 +1,37 @@
+package org.apache.camel.karavan.model;
+
+public class GitRepoFile {
+    private String name;
+    private Long lastCommitTimestamp;
+    private String body;
+
+    public GitRepoFile(String name, Long lastCommitTimestamp, String body) {
+        this.name = name;
+        this.lastCommitTimestamp = lastCommitTimestamp;
+        this.body = body;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Long getLastCommitTimestamp() {
+        return lastCommitTimestamp;
+    }
+
+    public void setLastCommitTimestamp(Long lastCommitTimestamp) {
+        this.lastCommitTimestamp = lastCommitTimestamp;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+}
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java
index 2626356..0325952 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java
@@ -3,6 +3,8 @@ package org.apache.camel.karavan.model;
 import org.infinispan.protostream.annotations.ProtoFactory;
 import org.infinispan.protostream.annotations.ProtoField;
 
+import java.time.Instant;
+
 public class Project {
     public static final String CACHE = "projects";
 
@@ -19,15 +21,18 @@ public class Project {
     String runtime;
     @ProtoField(number = 5)
     String lastCommit;
+    @ProtoField(number = 6)
+    Long lastCommitTimestamp;
 
 
     @ProtoFactory
-    public Project(String projectId, String name, String description, String runtime, String lastCommit) {
+    public Project(String projectId, String name, String description, String runtime, String lastCommit, Long lastCommitTimestamp) {
         this.projectId = projectId;
         this.name = name;
         this.description = description;
         this.runtime = runtime;
         this.lastCommit = lastCommit;
+        this.lastCommitTimestamp = lastCommitTimestamp;
     }
 
     public Project(String projectId, String name, String description, String runtime) {
@@ -35,6 +40,7 @@ public class Project {
         this.name = name;
         this.description = description;
         this.runtime = runtime;
+        this.lastCommitTimestamp = Instant.now().toEpochMilli();
     }
 
     public Project() {
@@ -80,4 +86,11 @@ public class Project {
         this.lastCommit = lastCommit;
     }
 
+    public Long getLastCommitTimestamp() {
+        return lastCommitTimestamp;
+    }
+
+    public void setLastCommitTimestamp(Long lastCommitTimestamp) {
+        this.lastCommitTimestamp = lastCommitTimestamp;
+    }
 }
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
index 13498ef..c52e00d 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/GitService.java
@@ -21,6 +21,8 @@ import io.quarkus.runtime.StartupEvent;
 import io.smallrye.mutiny.tuples.Tuple2;
 import io.vertx.core.Vertx;
 import org.apache.camel.karavan.model.GitConfig;
+import org.apache.camel.karavan.model.GitRepo;
+import org.apache.camel.karavan.model.GitRepoFile;
 import org.apache.camel.karavan.model.Project;
 import org.apache.camel.karavan.model.ProjectFile;
 import org.eclipse.jgit.api.*;
@@ -44,7 +46,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.time.LocalDate;
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Base64;
@@ -96,13 +98,16 @@ public class GitService {
     public Project commitAndPushProject(Project project) throws Exception {
         Project p = infinispanService.getProject(project.getProjectId());
         List<ProjectFile> files = infinispanService.getProjectFiles(project.getProjectId());
-        String commitId = commitAndPushProject(p, files);
+        RevCommit commit = commitAndPushProject(p, files);
+        String commitId = commit.getId().getName();
+        Long lastUpdate = commit.getCommitTime() * 1000L;
         p.setLastCommit(commitId);
+        p.setLastCommitTimestamp(lastUpdate);
         infinispanService.saveProject(p, false);
         return p;
     }
 
-    public String commitAndPushProject(Project project, List<ProjectFile> files) throws GitAPIException, IOException, URISyntaxException {
+    public RevCommit commitAndPushProject(Project project, List<ProjectFile> files) throws GitAPIException, IOException, URISyntaxException {
         LOGGER.info("Commit and push project " + project.getProjectId());
         GitConfig gitConfig = getGitConfig();
         CredentialsProvider cred = new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
@@ -121,10 +126,10 @@ public class GitService {
         }
         writeProjectToFolder(folder, project, files);
         addDeletedFilesToIndex(git, folder, project, files);
-        return commitAddedAndPush(git, gitConfig.getBranch(), cred, project.getProjectId()).getId().getName();
+        return commitAddedAndPush(git, gitConfig.getBranch(), cred, project.getProjectId());
     }
 
-    public List<Tuple2<String, Map<String, String>>> readProjectsFromRepository() {
+    public List<GitRepo> readProjectsFromRepository() {
         LOGGER.info("Read projects...");
         GitConfig gitConfig = getGitConfig();
         LOGGER.info("Read projects from repository " + gitConfig.getUri());
@@ -132,16 +137,25 @@ public class GitService {
         String uuid = UUID.randomUUID().toString();
         String folder = vertx.fileSystem().createTempDirectoryBlocking(uuid);
         LOGGER.infof("Temp folder created: %s", folder);
-        List<Tuple2<String, Map<String, String>>> result = new ArrayList<>();
+        List<GitRepo> result = new ArrayList<>();
         Git git = null;
         try {
             git = clone(folder, gitConfig.getUri(), gitConfig.getBranch(), cred);
             checkout(git, false, null, null, gitConfig.getBranch());
             List<String> projects = readProjectsFromFolder(folder);
-            projects.forEach(project -> {
-                Map<String, String> files = readProjectFilesFromFolder(folder, project);
-                result.add(Tuple2.of(project, files));
-            });
+            for (String project : projects) {
+                Map<String, String> filesRead = readProjectFilesFromFolder(folder, project);
+                List<GitRepoFile> files = new ArrayList<>(filesRead.size());
+                for (Map.Entry<String, String> entry : filesRead.entrySet()) {
+                    String name = entry.getKey();
+                    String body = entry.getValue();
+                    Tuple2<String, Integer> fileCommit = lastCommit(git, project + File.separator + name);
+                    files.add(new GitRepoFile(name, Integer.valueOf(fileCommit.getItem2()).longValue() * 1000, body));
+                }
+                Tuple2<String, Integer> commit = lastCommit(git, project);
+                GitRepo repo = new GitRepo(project, commit.getItem1(), Integer.valueOf(commit.getItem2()).longValue() * 1000, files);
+                result.add(repo);
+            }
             return result;
         } catch (RefNotFoundException e) {
             LOGGER.error("New repository");
@@ -187,10 +201,11 @@ public class GitService {
         vertx.fileSystem().readDirBlocking(repoFolder + File.separator + projectFolder).forEach(f -> {
             String[] filenames = f.split(File.separator);
             String filename = filenames[filenames.length -1];
-            if (!filename.startsWith(".") && !Files.isDirectory(Paths.get(f))) {
+            Path path = Paths.get(f);
+            if (!filename.startsWith(".") && !Files.isDirectory(path)) {
                 LOGGER.info("Importing file " + filename);
                 try {
-                    files.put(filename, Files.readString(Paths.get(f)));
+                    files.put(filename, Files.readString(path));
                 } catch (IOException e) {
                     LOGGER.error("Error during file read", e);
                 }
@@ -277,4 +292,12 @@ public class GitService {
         }
         checkoutCommand.call();
     }
+
+    private Tuple2<String, Integer> lastCommit(Git git, String path) throws GitAPIException {
+        Iterable<RevCommit> log = git.log().addPath(path).setMaxCount(1).call();
+        for (RevCommit commit : log) {
+            return Tuple2.of(commit.getId().getName(), commit.getCommitTime());
+        }
+        return null;
+    }
 }
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/ImportService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/ImportService.java
index ad3972c..a56d40a 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/service/ImportService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/ImportService.java
@@ -18,6 +18,8 @@ package org.apache.camel.karavan.service;
 
 import io.quarkus.vertx.ConsumeEvent;
 import io.smallrye.mutiny.tuples.Tuple2;
+import org.apache.camel.karavan.model.GitRepo;
+import org.apache.camel.karavan.model.GitRepoFile;
 import org.apache.camel.karavan.model.Project;
 import org.apache.camel.karavan.model.ProjectFile;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -54,25 +56,25 @@ public class ImportService {
     void importProjects(String data) {
         LOGGER.info("Import projects from Git");
         try {
-            List<Tuple2<String, Map<String, String>>> repo = gitService.readProjectsFromRepository();
-            repo.forEach(p -> {
+            List<GitRepo> repos = gitService.readProjectsFromRepository();
+            repos.forEach(repo -> {
                 Project project;
-                String folderName = p.getItem1();
+                String folderName = repo.getName();
                 if (folderName.equals(Project.NAME_TEMPLATES)) {
-                    project = new Project(Project.NAME_TEMPLATES, "Templates", "Templates", "quarkus", "");
+                    project = new Project(Project.NAME_TEMPLATES, "Templates", "Templates", "quarkus", repo.getCommitId(), repo.getLastCommitTimestamp());
                 } else if (folderName.equals(Project.NAME_KAMELETS)){
-                    project = new Project(Project.NAME_KAMELETS, "Custom Kamelets", "Custom Kamelets", "quarkus", "");
+                    project = new Project(Project.NAME_KAMELETS, "Custom Kamelets", "Custom Kamelets", "quarkus", repo.getCommitId(), repo.getLastCommitTimestamp());
                 } else {
-                    String propertiesFile = getPropertiesFile(p);
+                    String propertiesFile = getPropertiesFile(repo);
                     String projectName = getProjectName(propertiesFile);
                     String projectDescription = getProjectDescription(propertiesFile);
                     String runtime = getProjectRuntime(propertiesFile);
-                    project = new Project(folderName, projectName, projectDescription, runtime, "");
+                    project = new Project(folderName, projectName, projectDescription, runtime, repo.getCommitId(), repo.getLastCommitTimestamp());
                 }
                 infinispanService.saveProject(project, true);
 
-                p.getItem2().forEach((key, value) -> {
-                    ProjectFile file = new ProjectFile(key, value, folderName, Instant.now().toEpochMilli());
+                repo.getFiles().forEach(repoFile -> {
+                    ProjectFile file = new ProjectFile(repoFile.getName(), repoFile.getBody(), folderName, repoFile.getLastCommitTimestamp());
                     infinispanService.saveProjectFile(file);
                 });
             });
@@ -88,7 +90,7 @@ public class ImportService {
         try {
             Project kamelets  = infinispanService.getProject(Project.NAME_KAMELETS);
             if (kamelets == null) {
-                kamelets = new Project(Project.NAME_KAMELETS, "Custom Kamelets", "Custom Kamelets", "quarkus", "");
+                kamelets = new Project(Project.NAME_KAMELETS, "Custom Kamelets", "Custom Kamelets", "quarkus", "", Instant.now().toEpochMilli());
                 infinispanService.saveProject(kamelets, true);
                 gitService.commitAndPushProject(kamelets);
             }
@@ -103,7 +105,7 @@ public class ImportService {
         try {
             Project templates  = infinispanService.getProject(Project.NAME_TEMPLATES);
             if (templates == null) {
-                templates = new Project(Project.NAME_TEMPLATES, "Templates", "Templates", "quarkus", "");
+                templates = new Project(Project.NAME_TEMPLATES, "Templates", "Templates", "quarkus", "", Instant.now().toEpochMilli());
                 infinispanService.saveProject(templates, true);
 
                 codeService.getApplicationPropertiesTemplates().forEach((name, value) -> {
@@ -117,11 +119,11 @@ public class ImportService {
         }
     }
 
-    private String getPropertiesFile(Tuple2<String, Map<String, String>> p) {
+    private String getPropertiesFile(GitRepo repo) {
         try {
-            for (Map.Entry<String, String> e : p.getItem2().entrySet()){
-                if (e.getKey().equalsIgnoreCase("application.properties")) {
-                    return e.getValue();
+            for (GitRepoFile e : repo.getFiles()){
+                if (e.getName().equalsIgnoreCase("application.properties")) {
+                    return e.getBody();
                 }
             }
         } catch (Exception e) {
diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
index d86a37e..e7d4998 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java
@@ -147,7 +147,6 @@ public class InfinispanService {
         }
     }
     public void saveProjectFile(ProjectFile file) {
-        file.setLastUpdate(Instant.now().toEpochMilli());
         files.put(GroupedKey.create(file.getProjectId(), file.getName()), file);
     }
 
diff --git a/karavan-app/src/main/webui/src/projects/ProjectFilesTable.tsx b/karavan-app/src/main/webui/src/projects/ProjectFilesTable.tsx
index e185ae6..1fd609a 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectFilesTable.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectFilesTable.tsx
@@ -1,50 +1,19 @@
 import React from 'react';
 import {
     Badge,
-    Breadcrumb,
-    BreadcrumbItem,
     Button,
-    PageSection,
-    Text,
-    TextContent,
     Bullseye,
     EmptyState,
     EmptyStateVariant,
     EmptyStateIcon,
     Title,
-    ModalVariant,
-    Modal,
-    Flex,
-    FlexItem,
-    CodeBlockCode,
-    CodeBlock, Skeleton, Tabs, Tab
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
-import {MainToolbar} from "../MainToolbar";
-import {KaravanApi} from "../api/KaravanApi";
-import {getProjectFileType, Project, ProjectFile, ProjectFileTypes} from "./ProjectModels";
-import {CamelUi} from "../designer/utils/CamelUi";
-import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon";
+import {getProjectFileType, ProjectFile} from "./ProjectModels";
 import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
-import {KaravanDesigner} from "../designer/KaravanDesigner";
-import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
-import DownloadImageIcon from "@patternfly/react-icons/dist/esm/icons/image-icon";
-import FileSaver from "file-saver";
-import Editor from "@monaco-editor/react";
 import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
-import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
-import {CreateFileModal} from "./CreateFileModal";
-import {PropertiesEditor} from "./PropertiesEditor";
-import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel";
-import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi";
-import {KubernetesAPI} from "../designer/utils/KubernetesAPI";
-import {UploadModal} from "./UploadModal";
-import {ProjectInfo} from "./ProjectInfo";
-import {ProjectOperations} from "./ProjectOperations";
-import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
-import {ProjectPageToolbar} from "./ProjectPageToolbar";
+
 
 interface Props {
     files: ProjectFile[],
diff --git a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
index 15dfa65..a5af48f 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
@@ -11,10 +11,9 @@ import {
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {KaravanApi} from "../api/KaravanApi";
-import {DeploymentStatus, Project, PipelineStatus, CamelStatus, PodStatus} from "./ProjectModels";
+import {DeploymentStatus, Project, PipelineStatus, CamelStatus, PodStatus, ProjectFile} from "./ProjectModels";
 import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon";
 import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon";
-import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
 import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
 import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
 import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
@@ -23,6 +22,7 @@ import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon
 interface Props {
     project: Project,
     config: any,
+    files: ProjectFile[],
     showLog: (type: 'container' | 'pipeline', name: string, environment: string) => void
     deleteEntity: (type: 'pod' | 'deployment', name: string, environment: string) => void
 }
@@ -46,7 +46,6 @@ interface State {
 export class ProjectInfo extends React.Component<Props, State> {
 
     public state: State = {
-        project: this.props.project,
         podStatuses: [],
         isPushing: false,
         isBuilding: false,
@@ -167,13 +166,33 @@ export class ProjectInfo extends React.Component<Props, State> {
         </Tooltip>)
     }
 
-    getCommitPanel() {
-        const {project} = this.state;
+    getDate(lastUpdate: number):string {
+        if (lastUpdate) {
+            const date = new Date(lastUpdate);
+            return date.toDateString() + ' ' + date.toLocaleTimeString();
+        } else {
+            return "N/A"
+        }
+    }
+
+    needCommit() :boolean {
+        const {project, files} = this.props;
+        return files.filter(f => f.lastUpdate > project.lastCommitTimestamp).length > 0;
+    }
+
+    getLastUpdatePanel() {
+        const {project} = this.props;
+        const color = this.needCommit() ? "grey" : "green";
         return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
+            <Flex direction={{default:"row"}} justifyContent={{default: "justifyContentFlexStart"}}>
+                {project?.lastCommitTimestamp && project?.lastCommitTimestamp > 0 &&
+                    <FlexItem>
+                        <Label color={color}>{this.getDate(project?.lastCommitTimestamp)}</Label>
+                    </FlexItem>
+                }
                 <FlexItem>
                     <Tooltip content={project?.lastCommit} position={"right"}>
-                        <Badge>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Badge>
+                        <Label color={color}>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Label>
                     </Tooltip>
                 </FlexItem>
             </Flex>)
@@ -356,9 +375,9 @@ export class ProjectInfo extends React.Component<Props, State> {
                 <DescriptionListDescription>{project?.description}</DescriptionListDescription>
             </DescriptionListGroup>
             <DescriptionListGroup>
-                <DescriptionListTerm>Commit</DescriptionListTerm>
+                <DescriptionListTerm>Updated</DescriptionListTerm>
                 <DescriptionListDescription>
-                    {this.getCommitPanel()}
+                    {this.getLastUpdatePanel()}
                 </DescriptionListDescription>
             </DescriptionListGroup>
         </DescriptionList>)
@@ -395,11 +414,11 @@ export class ProjectInfo extends React.Component<Props, State> {
                     <Flex direction={{default: "row"}}
                           // style={{height: "200px"}}
                           justifyContent={{default: "justifyContentSpaceBetween"}}>
-                        <FlexItem flex={{default: "flex_1"}}>
+                        <FlexItem flex={{default: "flex_2"}}>
                             {this.getProjectDescription()}
                         </FlexItem>
                         <Divider orientation={{default: "vertical"}}/>
-                        <FlexItem flex={{default: "flex_2"}}>
+                        <FlexItem flex={{default: "flex_3"}}>
                             {this.getEnvPanel("dev")}
                         </FlexItem>
                     </Flex>
diff --git a/karavan-app/src/main/webui/src/projects/ProjectModels.ts b/karavan-app/src/main/webui/src/projects/ProjectModels.ts
index f7e8db0..5957fe9 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectModels.ts
+++ b/karavan-app/src/main/webui/src/projects/ProjectModels.ts
@@ -4,6 +4,7 @@ export class Project {
     description: string = '';
     runtime: string = '';
     lastCommit: string = '';
+    lastCommitTimestamp: number = 0;
 
     public constructor(projectId: string, name: string, description: string, runtime: string, lastCommit: string);
     public constructor(init?: Partial<Project>);
@@ -17,6 +18,7 @@ export class Project {
             this.description = args[2];
             this.runtime = args[3];
             this.lastCommit = args[4];
+            this.lastCommitTimestamp = args[5];
             return;
         }
     }
diff --git a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
index 4ff48cc..ec13a67 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
@@ -193,7 +193,7 @@ export class ProjectPage extends React.Component<Props, State> {
             setMode={mode => this.setState({mode: mode})}
             setCreateModalOpen={() => this.setState({isCreateModalOpen: true})}
             setUploadModalOpen={() => this.setState({isUploadModalOpen: true})}
-            onRefresh={() => this.onRefresh()}
+            onRefresh={this.onRefresh}
         />
     }
     
@@ -390,7 +390,7 @@ export class ProjectPage extends React.Component<Props, State> {
     }
 
     getProjectPanelFiles() {
-        const {tab, files} = this.state;
+        const {tab, files, project} = this.state;
         const isBuildIn = this.isBuildIn();
         return (
             <FlexItem>
@@ -403,7 +403,11 @@ export class ProjectPage extends React.Component<Props, State> {
                 }
                 {!isBuildIn &&
                     <PageSection padding={{default: "padding"}}>
-                        {tab === 'development' && <ProjectInfo project={this.props.project} config={this.props.config} deleteEntity={this.deleteEntity} showLog={this.showLogs}/>}
+                        {tab === 'development' && project && <ProjectInfo project={project}
+                                                               files={files}
+                                                               config={this.props.config}
+                                                               deleteEntity={this.deleteEntity}
+                                                               showLog={this.showLogs}/>}
                         {tab === 'development' && <ProjectFilesTable files={files}
                                                                      onOpenDeleteConfirmation={this.openDeleteConfirmation}
                                                                      onSelect={this.select}/>}
@@ -434,13 +438,13 @@ export class ProjectPage extends React.Component<Props, State> {
     }
 
     render() {
-        const {file, isDeleteModalOpen, fileToDelete, isUploadModalOpen, isCreateModalOpen} = this.state;
+        const {file, isDeleteModalOpen, fileToDelete, isUploadModalOpen, isCreateModalOpen, key} = this.state;
         const {project} = this.props;
         const types = this.isBuildIn()
             ? (this.isKameletsProject() ? ['KAMELET'] : ['JAVA'])
             : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name);
         return (
-            <PageSection className="kamelet-section project-page" padding={{default: 'noPadding'}}>
+            <PageSection key={key} className="kamelet-section project-page" padding={{default: 'noPadding'}}>
                 <PageSection className="tools-section" padding={{default: 'noPadding'}}>
                     <MainToolbar title={this.title()} tools={this.tools()}/>
                 </PageSection>
diff --git a/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx b/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
index f52123e..3b6db2d 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
@@ -76,11 +76,6 @@ export class ProjectPageToolbar extends React.Component<Props> {
                                 onChange={checked => this.props.setEditAdvancedProperties.call(this, checked)}
                             />
                         </FlexItem>}
-                        {/*<FlexItem>*/}
-                        {/*    <Tooltip content={project?.lastCommit} position={"right"}>*/}
-                        {/*        <Badge>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Badge>*/}
-                        {/*    </Tooltip>*/}
-                        {/*</FlexItem>*/}
                         <FlexItem>
                             <Tooltip content="Commit and push to git" position={"bottom"}>
                                 <Button isLoading={isPushing ? true : undefined} isSmall variant="secondary"