From 6bc58a7f68f455a51bce9ce9686781f7764d5814 Mon Sep 17 00:00:00 2001 From: Mohammad AbuAboud Date: Wed, 25 Dec 2024 00:34:02 +0000 Subject: [PATCH] chore: enhance project release ui --- .devcontainer/devcontainer.json | 5 +- .../app/routes/project-release/apply-plan.tsx | 77 +++++++++++---- .../create-release-dialog/index.tsx | 96 +++++++++++-------- .../operation-change.tsx | 6 +- .../project-release/download-button.tsx | 46 +++++---- .../src/app/routes/project-release/index.tsx | 49 +++++----- .../lib/project-release-api.ts | 7 +- 7 files changed, 176 insertions(+), 110 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ae62a39d11..d282b72015 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,11 +11,8 @@ "vscode": { // Add the IDs of extensions you want installed when the container is created. "extensions": [ - "dbaeumer.vscode-eslint", - "Angular.ng-template", "cipchk.cssrem", - "huizhou.githd", - "supermaven.supermaven" + "huizhou.githd" ] } }, diff --git a/packages/react-ui/src/app/routes/project-release/apply-plan.tsx b/packages/react-ui/src/app/routes/project-release/apply-plan.tsx index ee5842f081..990954f505 100644 --- a/packages/react-ui/src/app/routes/project-release/apply-plan.tsx +++ b/packages/react-ui/src/app/routes/project-release/apply-plan.tsx @@ -1,11 +1,20 @@ import { useMutation } from '@tanstack/react-query'; -import { useState } from 'react'; -import { DiffReleaseRequest } from '@activepieces/shared'; +import { t } from 'i18next'; +import { useState, ReactNode } from 'react'; + +import { Button, ButtonProps } from '@/components/ui/button'; import { INTERNAL_ERROR_TOAST, useToast } from '@/components/ui/use-toast'; +import { ConnectGitDialog } from '@/features/git-sync/components/connect-git-dialog'; +import { gitSyncHooks } from '@/features/git-sync/lib/git-sync-hooks'; import { projectReleaseApi } from '@/features/project-version/lib/project-release-api'; +import { authenticationSession } from '@/lib/authentication-session'; +import { + DiffReleaseRequest, + isNil, + ProjectReleaseType, +} from '@activepieces/shared'; + import { CreateReleaseDialog } from './create-release-dialog'; -import { Button, ButtonProps } from '@/components/ui/button'; -import { ReactNode } from 'react'; type ApplyButtonProps = ButtonProps & { request: DiffReleaseRequest; @@ -14,15 +23,33 @@ type ApplyButtonProps = ButtonProps & { defaultName?: string; }; -export const ApplyButton = ({ request, children, onSuccess, defaultName, ...props }: ApplyButtonProps) => { +export const ApplyButton = ({ + request, + children, + onSuccess, + defaultName, + ...props +}: ApplyButtonProps) => { const { toast } = useToast(); + const projectId = authenticationSession.getProjectId()!; + const { gitSync } = gitSyncHooks.useGitSync(projectId, !isNil(projectId)); const [dialogOpen, setDialogOpen] = useState(false); const [syncPlan, setSyncPlan] = useState(null); const [loadingRequestId, setLoadingRequestId] = useState(null); - const { mutate: loadSyncPlan, isPending: isLoadingApplyPlan } = useMutation({ - mutationFn: (request: DiffReleaseRequest) => projectReleaseApi.diff(request), + const { mutate: loadSyncPlan } = useMutation({ + mutationFn: (request: DiffReleaseRequest) => + projectReleaseApi.diff(request), onSuccess: (plan) => { + if (!plan.operations || plan.operations.length === 0) { + toast({ + title: t('No Changes Found'), + description: t('There are no differences to apply'), + variant: 'default', + }); + setLoadingRequestId(null); + return; + } setSyncPlan(plan); setDialogOpen(true); setLoadingRequestId(null); @@ -33,6 +60,9 @@ export const ApplyButton = ({ request, children, onSuccess, defaultName, ...prop }, }); + const [gitDialogOpen, setGitDialogOpen] = useState(false); + const showGitDialog = + isNil(gitSync) && request.type === ProjectReleaseType.GIT; const requestId = JSON.stringify(request); const isLoading = loadingRequestId === requestId; @@ -42,22 +72,35 @@ export const ApplyButton = ({ request, children, onSuccess, defaultName, ...prop {...props} loading={isLoading} onClick={() => { - setLoadingRequestId(requestId); - loadSyncPlan(request); + if (showGitDialog) { + setGitDialogOpen(true); + } else { + setLoadingRequestId(requestId); + loadSyncPlan(request); + } }} > {children} - {dialogOpen && ( - + ) : ( + dialogOpen && ( + + ) )} ); -}; \ No newline at end of file +}; diff --git a/packages/react-ui/src/app/routes/project-release/create-release-dialog/index.tsx b/packages/react-ui/src/app/routes/project-release/create-release-dialog/index.tsx index ea567ce2ed..4dba5130fe 100644 --- a/packages/react-ui/src/app/routes/project-release/create-release-dialog/index.tsx +++ b/packages/react-ui/src/app/routes/project-release/create-release-dialog/index.tsx @@ -4,11 +4,12 @@ import { t } from 'i18next'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import * as z from 'zod'; + import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { Dialog, DialogContent, - DialogDescription, DialogFooter, DialogHeader, DialogTitle, @@ -17,22 +18,20 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { INTERNAL_ERROR_TOAST, toast } from '@/components/ui/use-toast'; -import { ConnectGitDialog } from '@/features/git-sync/components/connect-git-dialog'; import { gitSyncHooks } from '@/features/git-sync/lib/git-sync-hooks'; import { projectReleaseApi } from '@/features/project-version/lib/project-release-api'; import { platformHooks } from '@/hooks/platform-hooks'; import { authenticationSession } from '@/lib/authentication-session'; -import { - ProjectSyncPlan, -} from '@activepieces/ee-shared'; -import { ProjectReleaseType } from '@activepieces/shared'; -import { Checkbox } from '@/components/ui/checkbox'; +import { ProjectSyncPlan } from '@activepieces/ee-shared'; +import { DiffReleaseRequest, ProjectReleaseType } from '@activepieces/shared'; + import { OperationChange } from './operation-change'; -type GitReleaseDialogProps = { +type CreateReleaseDialogProps = { open: boolean; setOpen: (open: boolean) => void; refetch: () => void; + diffRequest: DiffReleaseRequest; plan: ProjectSyncPlan | undefined; defaultName?: string; }; @@ -50,8 +49,8 @@ const CreateReleaseDialog = ({ refetch, plan, defaultName = '', -}: GitReleaseDialogProps) => { - const [isApplyingChanges, setIsApplyingChanges] = useState(false); + diffRequest, +}: CreateReleaseDialogProps) => { const { platform } = platformHooks.useCurrentPlatform(); const { gitSync } = gitSyncHooks.useGitSync( authenticationSession.getProjectId()!, @@ -66,54 +65,66 @@ const CreateReleaseDialog = ({ }, }); - const { mutate: applyChanges } = useMutation({ + const { mutate: applyChanges, isPending } = useMutation({ mutationFn: async () => { - setIsApplyingChanges(true); - if (gitSync) { - projectReleaseApi - .create({ + switch (diffRequest.type) { + case ProjectReleaseType.GIT: + if (!gitSync) { + throw new Error('Git sync is not connected'); + } + await projectReleaseApi.create({ name: form.getValues('name'), description: form.getValues('description'), selectedFlowsIds: Array.from(selectedChanges), repoId: gitSync.id, - type: ProjectReleaseType.GIT, - }) - .then(() => { - refetch(); - setOpen(false); - setIsApplyingChanges(false); + type: diffRequest.type, + }); + break; + case ProjectReleaseType.ROLLBACK: + await projectReleaseApi.create({ + name: form.getValues('name'), + description: form.getValues('description'), + selectedFlowsIds: Array.from(selectedChanges), + projectReleaseId: diffRequest.projectReleaseId, + type: diffRequest.type, }); + break; } }, + onSuccess: () => { + refetch(); + setOpen(false); + }, onError: (error) => { console.error(error); toast(INTERNAL_ERROR_TOAST); }, }); - const [selectedChanges, setSelectedChanges] = useState>(new Set(plan?.operations.map(op => op.flow.id) || [])); + const [selectedChanges, setSelectedChanges] = useState>( + new Set(plan?.operations.map((op) => op.flow.id) || []), + ); const handleSelectAll = (checked: boolean) => { if (!plan) return; setSelectedChanges( - new Set(checked ? plan.operations.map((op) => op.flow.id) : []) + new Set(checked ? plan.operations.map((op) => op.flow.id) : []), ); }; - if (!gitSync) { - return ; - } - return ( - { - if (newOpenState) { - form.reset({ - name: '', - description: '', - }); - } - setOpen(newOpenState); - }}> + { + if (newOpenState) { + form.reset({ + name: '', + description: '', + }); + } + setOpen(newOpenState); + }} + > {t('Create Git Release')} @@ -153,12 +164,13 @@ const CreateReleaseDialog = ({
-
@@ -173,8 +185,10 @@ const CreateReleaseDialog = ({ new Set( checked ? [...selectedChanges, operation.flow.id] - : [...selectedChanges].filter((id) => id !== operation.flow.id) - ) + : [...selectedChanges].filter( + (id) => id !== operation.flow.id, + ), + ), ); }} /> @@ -197,7 +211,7 @@ const CreateReleaseDialog = ({
)} @@ -41,7 +41,7 @@ export const OperationChange = React.memo( {renderDiffInfo( change.targetFlow.displayName, - + , )} )} @@ -50,7 +50,7 @@ export const OperationChange = React.memo( {renderDiffInfo( change.flow.displayName, - + , )} )} diff --git a/packages/react-ui/src/app/routes/project-release/download-button.tsx b/packages/react-ui/src/app/routes/project-release/download-button.tsx index 6fd70975ce..c749483472 100644 --- a/packages/react-ui/src/app/routes/project-release/download-button.tsx +++ b/packages/react-ui/src/app/routes/project-release/download-button.tsx @@ -1,6 +1,7 @@ import { useMutation } from '@tanstack/react-query'; -import { DownloadIcon } from 'lucide-react'; import { t } from 'i18next'; +import { DownloadIcon } from 'lucide-react'; + import { Button } from '@/components/ui/button'; import { Tooltip, @@ -13,25 +14,28 @@ import { ProjectRelease } from '@activepieces/shared'; export const DownloadButton = ({ release }: { release: ProjectRelease }) => { const { toast } = useToast(); - const { mutate: downloadProjectRelease, isPending: isDownloading } = useMutation({ - mutationFn: async ({ releaseId }: { releaseId: string }) => { - return await projectReleaseApi.export(releaseId); - }, - onSuccess: (data) => { - const blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${release.name || 'release'}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - }, - onError: () => { - toast(INTERNAL_ERROR_TOAST); - }, - }); + const { mutate: downloadProjectRelease, isPending: isDownloading } = + useMutation({ + mutationFn: async ({ releaseId }: { releaseId: string }) => { + return await projectReleaseApi.export(releaseId); + }, + onSuccess: (data) => { + const blob = new Blob([JSON.stringify(data)], { + type: 'application/json', + }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${release.name || 'release'}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, + onError: () => { + toast(INTERNAL_ERROR_TOAST); + }, + }); return (
@@ -50,4 +54,4 @@ export const DownloadButton = ({ release }: { release: ProjectRelease }) => {
); -}; \ No newline at end of file +}; diff --git a/packages/react-ui/src/app/routes/project-release/index.tsx b/packages/react-ui/src/app/routes/project-release/index.tsx index 21e2d83344..050b5fac1f 100644 --- a/packages/react-ui/src/app/routes/project-release/index.tsx +++ b/packages/react-ui/src/app/routes/project-release/index.tsx @@ -1,7 +1,8 @@ import { useQuery } from '@tanstack/react-query'; import { ColumnDef } from '@tanstack/react-table'; import { t } from 'i18next'; -import { Plus, ChevronDown, Undo2 } from 'lucide-react'; +import { Plus, ChevronDown, Undo2, GitBranch, RotateCcw } from 'lucide-react'; + import { Button } from '@/components/ui/button'; import { DataTable, RowDataWithActions } from '@/components/ui/data-table'; import { DataTableColumnHeader } from '@/components/ui/data-table/data-table-column-header'; @@ -19,13 +20,10 @@ import { } from '@/components/ui/tooltip'; import { projectReleaseApi } from '@/features/project-version/lib/project-release-api'; import { formatUtils } from '@/lib/utils'; -import { - ProjectRelease, - ProjectReleaseType, -} from '@activepieces/shared'; -import { DownloadButton } from './download-button'; -import { useState } from 'react'; +import { ProjectRelease, ProjectReleaseType } from '@activepieces/shared'; + import { ApplyButton } from './apply-plan'; +import { DownloadButton } from './download-button'; const ProjectReleasesPage = () => { const { data, isLoading, refetch } = useQuery({ @@ -33,9 +31,6 @@ const ProjectReleasesPage = () => { queryFn: () => projectReleaseApi.list(), }); - const [selectedRelease, setSelectedRelease] = useState(null); - const [isLoadingApplyPlan, setIsLoadingApplyPlan] = useState(false); - const columns: ColumnDef>[] = [ { accessorKey: 'name', @@ -45,6 +40,26 @@ const ProjectReleasesPage = () => { ), cell: ({ row }) =>
{row.original.name}
, }, + { + accessorKey: 'type', + accessorFn: (row) => row.type, + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const isGit = row.original.type === ProjectReleaseType.GIT; + return ( +
+ {isGit ? ( + + ) : ( + + )} + {isGit ? 'Git' : 'Rollback'} +
+ ); + }, + }, { accessorKey: 'created', accessorFn: (row) => row.created, @@ -71,13 +86,6 @@ const ProjectReleasesPage = () => {
{row.original.importedByUser?.email}
), }, - { - accessorKey: 'type', - accessorFn: (row) => row.type, - header: ({ column }) => ( - - ), - }, ]; return ( @@ -86,7 +94,7 @@ const ProjectReleasesPage = () => {
{t('Project Releases')}
- {t('View all history of imported project releases')} + {t('Track and manage your project version history and deployments')}
@@ -100,12 +108,10 @@ const ProjectReleasesPage = () => { setSelectedRelease(null)} >
@@ -136,8 +142,7 @@ const ProjectReleasesPage = () => { type: ProjectReleaseType.ROLLBACK, projectReleaseId: row.id, }} - defaultName={`Rollback ${row.name}`} - onClick={() => setSelectedRelease(row)} + defaultName={row.name} > diff --git a/packages/react-ui/src/features/project-version/lib/project-release-api.ts b/packages/react-ui/src/features/project-version/lib/project-release-api.ts index 0221ced963..cd4e566d40 100644 --- a/packages/react-ui/src/features/project-version/lib/project-release-api.ts +++ b/packages/react-ui/src/features/project-version/lib/project-release-api.ts @@ -1,11 +1,11 @@ import { api } from '@/lib/api'; +import { ProjectSyncPlan } from '@activepieces/ee-shared'; import { SeekPage, CreateProjectReleaseRequestBody, ProjectRelease, DiffReleaseRequest, } from '@activepieces/shared'; -import { ProjectSyncPlan } from '@activepieces/ee-shared'; export const projectReleaseApi = { async list() { @@ -21,6 +21,9 @@ export const projectReleaseApi = { return await api.post(`/v1/project-releases/${releaseId}/export`); }, async diff(request: DiffReleaseRequest) { - return await api.post(`/v1/project-releases/diff`, request); + return await api.post( + `/v1/project-releases/diff`, + request, + ); }, };