diff --git a/app/client/src/git/ce/constants/messages.tsx b/app/client/src/git/ce/constants/messages.tsx
index 0978898423ff..432d0dcd4cea 100644
--- a/app/client/src/git/ce/constants/messages.tsx
+++ b/app/client/src/git/ce/constants/messages.tsx
@@ -1,5 +1,5 @@
export const OPS_MODAL = {
- TAB_RELEASE: "RELEASE",
+ TAB_RELEASE: "Release",
};
export const TAB_RELEASE = {
@@ -16,3 +16,8 @@ export const RELEASE_NOTES_INPUT = {
TITLE: "Release notes",
PLACEHOLDER: "Your release notes here",
};
+
+export const LATEST_COMMIT_INFO = {
+ LOADING_COMMIT_MESSAGE: "Fetching latest commit...",
+ NO_COMMIT_MESSAGE: "No commit message found",
+};
diff --git a/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.test.tsx b/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.test.tsx
index f5cfcf517c65..0e49e83a9079 100644
--- a/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.test.tsx
+++ b/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.test.tsx
@@ -4,91 +4,143 @@ import LatestCommitInfoView from "./LatestCommitInfoView";
import "@testing-library/jest-dom";
describe("LatestCommitInfoView", () => {
+ const currentTimestamp = Math.floor((Date.now() - 3600000) / 1000);
+
it("renders correctly with all props", () => {
- const { getByText } = render(
+ const { getByTestId } = render(
,
);
- expect(getByText("Initial commit")).toBeInTheDocument();
- expect(getByText("John Doe committed 2025-03-01")).toBeInTheDocument();
- expect(getByText("abc123")).toBeInTheDocument();
+ expect(getByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "Initial commit",
+ );
+ expect(getByTestId("t--git-latest-commit-commited-by")).toHaveTextContent(
+ "John Doe committed 1 hr ago",
+ );
+ expect(getByTestId("t--git-latest-commit-hash")).toHaveTextContent(
+ "abc123",
+ );
});
it("renders correctly with null authorName", () => {
- const { getByText } = render(
+ const { getByTestId, queryByTestId } = render(
,
);
- expect(getByText("Initial commit")).toBeInTheDocument();
- expect(getByText("- committed 2025-03-01")).toBeInTheDocument();
- expect(getByText("abc123")).toBeInTheDocument();
+ expect(
+ queryByTestId("t--git-latest-commit-commited-by"),
+ ).not.toBeInTheDocument();
+ expect(getByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "Initial commit",
+ );
});
it("renders correctly with null committedAt", () => {
- const { getByText } = render(
+ const { getByTestId } = render(
,
);
- expect(getByText("Initial commit")).toBeInTheDocument();
- expect(getByText("John Doe committed -")).toBeInTheDocument();
- expect(getByText("abc123")).toBeInTheDocument();
+ expect(getByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "Initial commit",
+ );
+ expect(getByTestId("t--git-latest-commit-commited-by")).toHaveTextContent(
+ "Committed by John Doe",
+ );
});
it("renders correctly with null hash", () => {
- const { getByText } = render(
+ const { getByTestId } = render(
,
);
- expect(getByText("Initial commit")).toBeInTheDocument();
- expect(getByText("John Doe committed 2025-03-01")).toBeInTheDocument();
- expect(getByText("-")).toBeInTheDocument();
+ expect(getByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "Initial commit",
+ );
+ expect(getByTestId("t--git-latest-commit-commited-by")).toHaveTextContent(
+ "John Doe committed 1 hr ago",
+ );
+ expect(getByTestId("t--git-latest-commit-hash")).toHaveTextContent("-");
});
it("renders correctly with null message", () => {
- const { getByText } = render(
+ const { getByTestId } = render(
,
);
- expect(getByText("John Doe committed 2025-03-01")).toBeInTheDocument();
- expect(getByText("abc123")).toBeInTheDocument();
+ expect(getByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "No commit message found",
+ );
+ expect(getByTestId("t--git-latest-commit-commited-by")).toHaveTextContent(
+ "John Doe committed 1 hr ago",
+ );
+ expect(getByTestId("t--git-latest-commit-hash")).toHaveTextContent(
+ "abc123",
+ );
});
it("renders correctly with all null props", () => {
- const { getByText } = render(
+ const { queryByTestId } = render(
,
);
- expect(getByText("- committed -")).toBeInTheDocument();
- expect(getByText("-")).toBeInTheDocument();
+ expect(
+ queryByTestId("t--git-latest-commit-commited-by"),
+ ).not.toBeInTheDocument();
+ expect(queryByTestId("t--git-latest-commit-message")).toHaveTextContent(
+ "No commit message found",
+ );
+ expect(queryByTestId("t--git-latest-commit-hash")).toHaveTextContent("-");
+ });
+
+ it("renders loading state correctly", () => {
+ const { getByTestId } = render(
+ ,
+ );
+
+ expect(getByTestId("t--git-latest-commit-loading")).toHaveTextContent(
+ "Fetching latest commit...",
+ );
});
});
diff --git a/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.tsx b/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.tsx
index fc6fb1ce671f..72a324087e2d 100644
--- a/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.tsx
+++ b/app/client/src/git/components/LatestCommitInfo/LatestCommitInfoView.tsx
@@ -1,6 +1,8 @@
-import { Flex, Icon, Text } from "@appsmith/ads";
+import { Flex, Icon, Spinner, Text } from "@appsmith/ads";
+import { LATEST_COMMIT_INFO } from "git/ee/constants/messages";
import React from "react";
import styled from "styled-components";
+import { howMuchTimeBeforeText } from "utils/helpers";
const Container = styled(Flex)`
border-radius: 4px;
@@ -9,8 +11,9 @@ const Container = styled(Flex)`
interface LatestCommitInfoViewProps {
authorName: string | null;
- committedAt: string | null;
+ committedAt: number | null;
hash: string | null;
+ isLoading: boolean;
message: string | null;
}
@@ -18,20 +21,55 @@ function LatestCommitInfoView({
authorName = null,
committedAt = null,
hash = null,
+ isLoading = false,
message = null,
}: LatestCommitInfoViewProps) {
+ const readableCommittedAt = committedAt
+ ? howMuchTimeBeforeText(new Date(committedAt * 1000).toString())
+ : null;
+
+ if (isLoading) {
+ return (
+
+
+ {LATEST_COMMIT_INFO.LOADING_COMMIT_MESSAGE}
+
+ );
+ }
+
return (
- {message}
-
- {authorName ?? "-"} committed {committedAt ?? "-"}
+
+ {message ?? {LATEST_COMMIT_INFO.NO_COMMIT_MESSAGE}}
+ {authorName && (
+
+ {authorName && !readableCommittedAt
+ ? `Committed by ${authorName}`
+ : null}
+ {authorName && readableCommittedAt
+ ? `${authorName} committed ${readableCommittedAt} ago`
+ : null}
+
+ )}
- {hash ?? "-"}
+
+ {hash ?? "-"}
+
diff --git a/app/client/src/git/components/LatestCommitInfo/index.tsx b/app/client/src/git/components/LatestCommitInfo/index.tsx
index ce04d0695e36..3361af46c6f1 100644
--- a/app/client/src/git/components/LatestCommitInfo/index.tsx
+++ b/app/client/src/git/components/LatestCommitInfo/index.tsx
@@ -1,16 +1,21 @@
import React from "react";
import LatestCommitInfoView from "./LatestCommitInfoView";
-import useLatestCommit from "git/hooks/useLatestCommit";
+import usePretag from "git/hooks/usePretag";
function LatestCommitInfo() {
- const { latestCommit } = useLatestCommit();
+ const { isPretagLoading, pretagResponse } = usePretag();
+
+ const commitHash = pretagResponse?.hash
+ ? pretagResponse.hash.slice(0, 7)
+ : null;
return (
);
}
diff --git a/app/client/src/git/components/OpsModal/OpsModalView.tsx b/app/client/src/git/components/OpsModal/OpsModalView.tsx
index a0f696a747f4..54bd0c80476a 100644
--- a/app/client/src/git/components/OpsModal/OpsModalView.tsx
+++ b/app/client/src/git/components/OpsModal/OpsModalView.tsx
@@ -15,6 +15,10 @@ import styled from "styled-components";
// import ReconnectSSHError from "../components/ReconnectSSHError";
import { GitOpsTab } from "git/constants/enums";
import noop from "lodash/noop";
+import isGitTaggingEnabled from "git/helpers/isGitTaggingEnabled";
+import type { GitArtifactDef } from "git/types";
+import TabRelease from "./TabRelease";
+import { OPS_MODAL } from "git/ee/constants/messages";
const StyledModalContent = styled(ModalContent)`
&&& {
@@ -27,6 +31,7 @@ const StyledModalContent = styled(ModalContent)`
`;
interface OpsModalViewProps {
+ artifactDef: GitArtifactDef | null;
fetchStatus: () => void;
isOpsModalOpen: boolean;
isProtectedMode: boolean;
@@ -36,6 +41,7 @@ interface OpsModalViewProps {
}
function OpsModalView({
+ artifactDef = null,
fetchStatus = noop,
isOpsModalOpen = false,
isProtectedMode = false,
@@ -43,6 +49,8 @@ function OpsModalView({
repoName = null,
toggleOpsModal = noop,
}: OpsModalViewProps) {
+ const isTaggingEnabled = isGitTaggingEnabled(artifactDef);
+
useEffect(
function fetchStatusOnMountEffect() {
if (isOpsModalOpen) {
@@ -91,10 +99,22 @@ function OpsModalView({
>
{createMessage(MERGE)}
+ {isTaggingEnabled && (
+
+ {OPS_MODAL.TAB_RELEASE}
+
+ )}
{opsModalTab === GitOpsTab.Deploy && }
{opsModalTab === GitOpsTab.Merge && }
+ {isTaggingEnabled && opsModalTab === GitOpsTab.Release && (
+
+ )}
{/* */}
diff --git a/app/client/src/git/components/OpsModal/TabRelease.tsx b/app/client/src/git/components/OpsModal/TabRelease/TabReleaseView.tsx
similarity index 61%
rename from app/client/src/git/components/OpsModal/TabRelease.tsx
rename to app/client/src/git/components/OpsModal/TabRelease/TabReleaseView.tsx
index b1ce504ac26e..a28bff3afb2e 100644
--- a/app/client/src/git/components/OpsModal/TabRelease.tsx
+++ b/app/client/src/git/components/OpsModal/TabRelease/TabReleaseView.tsx
@@ -3,7 +3,8 @@ import LatestCommitInfo from "git/components/LatestCommitInfo";
import ReleaseNotesInput from "git/components/ReleaseNotesInput";
import ReleaseVersionRadioGroup from "git/components/ReleaseVersionRadioGroup";
import { TAB_RELEASE } from "git/ee/constants/messages";
-import React, { useCallback, useState } from "react";
+import noop from "lodash/noop";
+import React, { useCallback, useEffect, useState } from "react";
import styled from "styled-components";
const Container = styled.div`
@@ -21,13 +22,42 @@ const StyledModalFooter = styled(ModalFooter)`
min-height: 52px;
`;
-function TabRelease() {
+interface TabReleaseProps {
+ fetchPretag: () => void;
+ createReleaseTag: (params: {
+ tag: string;
+ releaseNote: string;
+ commitSHA: string;
+ }) => void;
+ isCreateReleaseTagLoading: boolean;
+ latestCommitSHA: string | null;
+}
+
+function TabReleaseView({
+ createReleaseTag = noop,
+ fetchPretag = noop,
+ isCreateReleaseTagLoading = false,
+ latestCommitSHA = null,
+}: TabReleaseProps) {
const [releaseVersion, setReleaseVersion] = useState(null);
const [releaseNotes, setReleaseNotes] = useState(null);
const isReleaseDisabled = !releaseVersion || !releaseNotes;
- const handleClickOnRelease = useCallback(() => {}, []);
+ useEffect(
+ function fetchPretagOnInitEffect() {
+ fetchPretag();
+ },
+ [fetchPretag],
+ );
+
+ const handleClickOnRelease = useCallback(() => {
+ createReleaseTag({
+ tag: releaseVersion ?? "",
+ releaseNote: releaseNotes ?? "",
+ commitSHA: latestCommitSHA ?? "",
+ });
+ }, [createReleaseTag, latestCommitSHA, releaseNotes, releaseVersion]);
return (
<>
@@ -47,6 +77,7 @@ function TabRelease() {