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() {