From e70c8c86bb255a8332748f775fff42b1a7bc8211 Mon Sep 17 00:00:00 2001 From: Timofei Ponomarev Date: Mon, 24 Feb 2025 18:11:23 +0100 Subject: [PATCH] refactoring + fix to design --- .../subjects/views/repo-branches/index.tsx | 2 +- .../views/repo-tags/repo-tags-list.tsx | 97 +++++++++- .../views/repo-tags/repo-tags-store.ts | 36 ++-- .../subjects/views/secrets/secret-input.tsx | 2 +- .../branch-selector-container.tsx | 5 +- .../components-v2/create-branch-dialog.tsx | 103 ++++++++++ .../src/pages-v2/repo/repo-branch-list.tsx | 7 + .../src/pages-v2/repo/repo-sidebar.tsx | 2 +- .../repo/repo-tags-list-container.tsx | 131 ++++++------- packages/ui/locales/en/views.json | 64 ++++--- packages/ui/locales/fr/views.json | 45 ++++- .../ui/src/components/alert/AlertTitle.tsx | 2 +- packages/ui/src/components/button.tsx | 2 +- .../chat/chat-empty-preview-wrapper.tsx | 2 +- .../components/chat/chat-preview-wrapper.tsx | 2 +- .../ui/src/components/commit-copy-actions.tsx | 8 +- packages/ui/src/components/dropdown-menu.tsx | 2 +- .../src/components/filters/filter-select.tsx | 4 +- .../filters/filters-bar/actions/sorts.tsx | 2 +- .../filters/filters-bar/filters-bar.tsx | 4 +- .../filters/triggers/filter-trigger.tsx | 4 +- .../ui/src/components/icon/icon-name-map.ts | 6 +- .../manage-navigation-search.tsx | 4 +- packages/ui/src/components/multi-select.tsx | 2 +- packages/ui/src/components/node-group.tsx | 2 +- .../components/warning-label.tsx | 2 +- packages/ui/src/components/sha-badge.tsx | 4 +- .../components/skeletons/skeleton-form.tsx | 4 +- .../components/skeletons/skeleton-list.tsx | 2 +- packages/ui/src/components/tabs.tsx | 4 +- .../theme-selector-v2/theme-dialog.tsx | 10 +- packages/ui/src/components/toast/use-toast.ts | 36 +++- .../lists-data-icons/no-data-tags-light.svg | 72 +++++++ .../ui/src/views/execution/console-logs.tsx | 4 +- .../label-form-color-and-name-group.tsx | 4 +- .../views/labels/components/label-marker.tsx | 2 +- .../profile-settings-general-page.tsx | 2 +- .../views/repo/components/branch-info-bar.tsx | 4 +- .../branch-selector-dropdown.tsx | 5 +- .../branch-selector-v2/branch-selector.tsx | 7 +- .../branch-selector/branch-selector.tsx | 2 +- .../repo/components/branch-selector/types.ts | 1 + .../commit-selector/commit-selector.tsx | 2 +- .../repo/components/list-control-bar.tsx | 2 +- packages/ui/src/views/repo/index.ts | 3 +- .../compare/pull-request-compare-page.tsx | 2 +- .../components/pull-request-header.tsx | 2 +- .../pull-request-item-description.tsx | 4 +- .../changes/pull-request-changes-filter.tsx | 2 +- .../conversation/pull-request-comment-box.tsx | 2 +- .../repo-branch/components/branch-list.tsx | 2 +- .../components/create-branch-dialog.tsx | 115 ++++++------ .../components/divergence-gauge.tsx | 2 +- .../repo-branch/repo-branch-list-view.tsx | 19 -- .../ui/src/views/repo/repo-branch/types.ts | 15 +- .../repo-commit-details-view.tsx | 4 +- .../views/repo/repo-list/repo-list-page.tsx | 2 +- .../repo-settings-general-rules.tsx | 4 +- .../repo-summary/components/summary-panel.tsx | 4 +- .../create-tag/create-tag-dialog.tsx | 134 +++++++++++++ .../repo-tags/components/create-tag/schema.ts | 20 ++ .../components/create-tags-dialog.tsx | 175 ----------------- .../repo-tags/components/repo-tags-list.tsx | 177 +++++++++++++----- .../repo/repo-tags/repo-tags-list-page.tsx | 102 +++------- .../ui/src/views/repo/repo-tags/types.tsx | 16 +- .../run-pipeline-drawer-content.tsx | 2 +- 66 files changed, 928 insertions(+), 589 deletions(-) create mode 100644 apps/gitness/src/components-v2/create-branch-dialog.tsx create mode 100644 packages/ui/src/icons/lists-data-icons/no-data-tags-light.svg create mode 100644 packages/ui/src/views/repo/repo-tags/components/create-tag/create-tag-dialog.tsx create mode 100644 packages/ui/src/views/repo/repo-tags/components/create-tag/schema.ts diff --git a/apps/design-system/src/subjects/views/repo-branches/index.tsx b/apps/design-system/src/subjects/views/repo-branches/index.tsx index 4763884eb0..41d1f62423 100644 --- a/apps/design-system/src/subjects/views/repo-branches/index.tsx +++ b/apps/design-system/src/subjects/views/repo-branches/index.tsx @@ -14,7 +14,7 @@ export function RepoBranchesView() { {}} useRepoBranchesStore={useRepoBranchesStore} useTranslationStore={useTranslationStore} isCreateBranchDialogOpen={isCreateBranchDialogOpen} diff --git a/apps/design-system/src/subjects/views/repo-tags/repo-tags-list.tsx b/apps/design-system/src/subjects/views/repo-tags/repo-tags-list.tsx index 0034d60733..d63e45906f 100644 --- a/apps/design-system/src/subjects/views/repo-tags/repo-tags-list.tsx +++ b/apps/design-system/src/subjects/views/repo-tags/repo-tags-list.tsx @@ -2,14 +2,40 @@ import { useCallback, useState } from 'react' import { noop, useTranslationStore } from '@utils/viewUtils.ts' -import { CreateTagDialog, IBranchSelectorStore, RepoTagsListView } from '@harnessio/ui/views' +import { DeleteAlertDialog } from '@harnessio/ui/components' +import { + BranchSelectorListItem, + BranchSelectorTab, + BranchSelectorV2, + CreateBranchDialog, + CreateTagDialog, + RepoTagsListView +} from '@harnessio/ui/views' -import { repoBranchesStore } from '../repo-branches/repo-branches-store' import { tagsStore } from './repo-tags-store' export const RepoTagsList = () => { const [openCreateTagDialog, setOpenCreateTagDialog] = useState(false) - const useRepoBranchesStore = useCallback((): IBranchSelectorStore => repoBranchesStore, []) + const [openCreateBranchDialog, setOpenCreateBranchDialog] = useState(false) + const [selectedTagInList, setSelectedTagInList] = useState(null) + const [preSelectedTab, setPreSelectedTab] = useState(BranchSelectorTab.BRANCHES) + + const [openDeleteTagDialog, setOpenDeleteTagDialog] = useState(false) + + const tags = tagsStore.tags + const branches = [ + { + id: 0, + name: 'branch', + sha: 'sha', + default: false, + timestamp: '', + user: { + name: '', + avatarUrl: '' + } + } + ] const useRepoTagsStore = useCallback(() => tagsStore, []) return ( @@ -17,23 +43,76 @@ export const RepoTagsList = () => { setOpenCreateTagDialog(true)} + openCreateBranchDialog={(selectedTagInList: BranchSelectorListItem) => { + setOpenCreateBranchDialog(true) + setSelectedTagInList(selectedTagInList) + setPreSelectedTab(BranchSelectorTab.TAGS) + }} searchQuery={''} setSearchQuery={noop} - onDeleteTag={noop} + onDeleteTag={() => setOpenDeleteTagDialog(true)} useRepoTagsStore={useRepoTagsStore} toCommitDetails={() => ''} - openCreateTagDialog={noop} + openCreateTagDialog={() => setOpenCreateTagDialog(true)} /> setOpenCreateTagDialog(false)} onSubmit={noop} - branchQuery={''} - setBranchQuery={noop} - useRepoBranchesStore={useRepoBranchesStore} isLoading={false} + error={''} + selectedBranchOrTag={null} + renderProp={() => ( + + )} + /> + setOpenCreateBranchDialog(false)} + selectedBranchOrTag={selectedTagInList} + onSubmit={async () => {}} + isCreatingBranch={false} + error={''} + renderProp={() => ( + + )} + /> + setOpenDeleteTagDialog(false)} + deleteFn={noop} + error={null} + type="tag" + identifier={undefined} + isLoading={false} + useTranslationStore={useTranslationStore} /> ) diff --git a/apps/design-system/src/subjects/views/repo-tags/repo-tags-store.ts b/apps/design-system/src/subjects/views/repo-tags/repo-tags-store.ts index 99c57cf43b..604270465d 100644 --- a/apps/design-system/src/subjects/views/repo-tags/repo-tags-store.ts +++ b/apps/design-system/src/subjects/views/repo-tags/repo-tags-store.ts @@ -4,23 +4,31 @@ export const tagsStore: RepoTagsStore = { tags: [ { name: 'v2.4.0', - sha: '947bcbe92a5d40aacf861269b6605226adda0daf', - is_annotated: false - }, - { - name: 'v2.3.1', - sha: '794bdb1c56d885ae02f799c6cce13942e8614516', - is_annotated: false + sha: 'dd6135add66c91c74b21aa7514a57fdc8d5dc320', + is_annotated: true, + title: 'v2.4.0', + message: 'v2.4.0', + tagger: { + identity: { + name: 'user', + email: 'user@gmail.com' + }, + when: '2025-04-07T10:08:00Z' + } }, { name: 'v2.3.0', - sha: '87f1c74cd8a14ea90635c56a627d5e99a4950c73', - is_annotated: false - }, - { - name: 'v2.25.1-debug', - sha: '3e7ecf16153e88919951725c3ecc69ff076c00b9', - is_annotated: false + sha: 'f067dc95f956b2b195f30635e6d6e74e774d2108', + is_annotated: true, + title: 'v2.3.0', + message: 'v2.3.0', + tagger: { + identity: { + name: 'user', + email: 'user@gmail.com' + }, + when: '2025-04-07T09:04:05Z' + } } ], page: 1, diff --git a/apps/design-system/src/subjects/views/secrets/secret-input.tsx b/apps/design-system/src/subjects/views/secrets/secret-input.tsx index 10f4d6029b..45755047c2 100644 --- a/apps/design-system/src/subjects/views/secrets/secret-input.tsx +++ b/apps/design-system/src/subjects/views/secrets/secret-input.tsx @@ -24,7 +24,7 @@ export const SecretInputExample = () => { }} onClear={() => setSelectedSecret(null)} renderValue={secret => secret.name} - className="max-w-xs mb-8" + className="mb-8 max-w-xs" /> void isBranchOnly?: boolean dynamicWidth?: boolean + preSelectedTab?: BranchSelectorTab } export const BranchSelectorContainer: React.FC = ({ selectedBranch, onSelectBranchorTag, isBranchOnly = false, - dynamicWidth = false + dynamicWidth = false, + preSelectedTab }) => { const repoRef = useGetRepoRef() const { spaceId, repoId } = useParams() @@ -95,6 +97,7 @@ export const BranchSelectorContainer: React.FC = ( onSelectBranch={onSelectBranchorTag} isBranchOnly={isBranchOnly} dynamicWidth={dynamicWidth} + preSelectedTab={preSelectedTab} /> ) } diff --git a/apps/gitness/src/components-v2/create-branch-dialog.tsx b/apps/gitness/src/components-v2/create-branch-dialog.tsx new file mode 100644 index 0000000000..b50b68e667 --- /dev/null +++ b/apps/gitness/src/components-v2/create-branch-dialog.tsx @@ -0,0 +1,103 @@ +import { useCallback, useEffect, useState } from 'react' + +import { useCreateBranchMutation } from '@harnessio/code-service-client' +import { useToastNotification } from '@harnessio/ui/components' +import { + BranchSelectorListItem, + BranchSelectorTab, + CreateBranchDialog as CreateBranchDialogComp, + CreateBranchFormFields +} from '@harnessio/ui/views' + +import { useGetRepoRef } from '../framework/hooks/useGetRepoPath' +import { useTranslationStore } from '../i18n/stores/i18n-store' +import { BranchSelectorContainer } from './branch-selector-container' + +interface CreateBranchDialogProps { + open: boolean + onClose: () => void + onSuccess?: () => void + preselectedBranchOrTag?: BranchSelectorListItem | null + preselectedTab?: BranchSelectorTab +} + +export const CreateBranchDialog = ({ + open, + onClose, + onSuccess, + preselectedBranchOrTag, + preselectedTab +}: CreateBranchDialogProps) => { + const repo_ref = useGetRepoRef() + + const { t } = useTranslationStore() + const [selectedBranchOrTag, setSelectedBranchOrTag] = useState(null) + + const [createdBranchName, setCreatedBranchName] = useState('') + + const selectBranchOrTag = useCallback((branchTagName: BranchSelectorListItem) => { + setSelectedBranchOrTag(branchTagName) + }, []) + + const { showToast } = useToastNotification({ + title: t('views:repos.branchCreated'), + description: t('views:repos.branchCreatedDescription', { name: createdBranchName }) + }) + + const { + mutateAsync: createBranch, + error: createBranchError, + isLoading: isCreatingBranch, + reset: resetBranchMutation + } = useCreateBranchMutation( + {}, + { + onSuccess: data => { + onClose() + setCreatedBranchName(data.body.name ?? '') + showToast() + onSuccess?.() + } + } + ) + + const handleCreateBranch = async (data: CreateBranchFormFields) => { + await createBranch({ + repo_ref, + body: { + ...data + } + }) + } + + useEffect(() => { + if (!open) { + resetBranchMutation() + } + }, [open, resetBranchMutation]) + + useEffect(() => { + if (preselectedBranchOrTag) { + setSelectedBranchOrTag(preselectedBranchOrTag) + } + }, [open, preselectedBranchOrTag]) + + return ( + ( + + )} + /> + ) +} diff --git a/apps/gitness/src/pages-v2/repo/repo-branch-list.tsx b/apps/gitness/src/pages-v2/repo/repo-branch-list.tsx index 8f9ddf39f9..2be327619d 100644 --- a/apps/gitness/src/pages-v2/repo/repo-branch-list.tsx +++ b/apps/gitness/src/pages-v2/repo/repo-branch-list.tsx @@ -13,6 +13,7 @@ import { import { DeleteAlertDialog } from '@harnessio/ui/components' import { CreateBranchFormFields, RepoBranchListView } from '@harnessio/ui/views' +import { CreateBranchDialog } from '../../components-v2/create-branch-dialog' import { useRoutes } from '../../framework/context/NavigationContext' import { useGetRepoRef } from '../../framework/hooks/useGetRepoPath' import { useQueryState } from '../../framework/hooks/useQueryState' @@ -191,6 +192,12 @@ export function RepoBranchesListPage() { setCreateBranchSearchQuery={setCreateBranchSearchQuery} /> + setCreateBranchDialogOpen(false)} + onSuccess={handleInvalidateBranchList} + /> + { const repo_ref = useGetRepoRef() - const { setTags, addTag, removeTag, page, setPage, setPaginationFromHeaders } = useRepoTagsStore() - const { setBranchList, setDefaultBranch, setSelectedBranchTag, branchList } = useRepoBranchesStore() + const { page, setPage, setPaginationFromHeaders, setTags } = useRepoTagsStore() const { spaceId, repoId } = useParams() const routes = useRoutes() const [query, setQuery] = useQueryState('query') - const [branchQuery, setBranchQuery] = useState('') const { queryPage } = usePaginationQueryStateWithStore({ page, setPage }) + const [selectedBranchOrTag, setSelectedBranchOrTag] = useState(null) + const [selectedTagInList, setSelectedTagInList] = useState(null) + const [preSelectedTab, setPreSelectedTab] = useState(BranchSelectorTab.BRANCHES) + const [openCreateTagDialog, setOpenCreateTagDialog] = useState(false) const [openCreateBranchDialog, setOpenCreateBranchDialog] = useState(false) const [deleteTagDialog, setDeleteTagDialog] = useState(false) const [deleteTagName, setDeleteTagName] = useState(null) - const { data: { body: repository } = {} } = useFindRepositoryQuery({ repo_ref: repo_ref }) + const [deleteError, setDeleteError] = useState(null) - const { data: { body: tagsList, headers } = {}, isLoading: isLoadingTags } = useListTagsQuery({ + const { + data: { body: tagsList, headers } = {}, + isLoading: isLoadingTags, + refetch: refetchTags + } = useListTagsQuery({ repo_ref: repo_ref, queryParams: { query: query ?? '', @@ -58,20 +62,17 @@ export const RepoTagsListContainer = () => { } }) - const { data: { body: branches } = {} } = useListBranchesQuery({ - queryParams: { - limit: 50, - query: branchQuery ?? '' - }, - repo_ref: repo_ref - }) - - const { mutate: createTag, isLoading: isCreatingTag } = useCreateTagMutation( + const { + mutate: createTag, + isLoading: isCreatingTag, + error: createTagError, + reset: resetCreateTagMutation + } = useCreateTagMutation( { repo_ref: repo_ref }, { - onSuccess: data => { + onSuccess: () => { setOpenCreateTagDialog(false) - addTag(data.body as CommitTagType) + refetchTags() } } ) @@ -81,16 +82,12 @@ export const RepoTagsListContainer = () => { { onSuccess: () => { setDeleteTagDialog(false) - removeTag(deleteTagName ?? '') - } - } - ) - - const { mutate: createBranch, error: createBranchError } = useCreateBranchMutation( - {}, - { - onSuccess: () => { - setOpenCreateBranchDialog(false) + setDeleteError(null) + refetchTags() + }, + onError: (error: DeleteTagErrorResponse) => { + const deleteErrorMsg = error?.message || 'An unknown error occurred.' + setDeleteError({ message: deleteErrorMsg }) } } ) @@ -101,30 +98,14 @@ export const RepoTagsListContainer = () => { } }, [tagsList, setTags]) - useEffect(() => { - if (branches) { - setBranchList(transformBranchList(branches, repository?.default_branch)) - } - }, [branches, setBranchList, repository]) - useEffect(() => { setPaginationFromHeaders( - parseInt(headers?.get(PageResponseHeader.xNextPage) || ''), - parseInt(headers?.get(PageResponseHeader.xPrevPage) || '') + parseInt(headers?.get(PageResponseHeader.xNextPage) ?? '0'), + parseInt(headers?.get(PageResponseHeader.xPrevPage) ?? '0') ) }, [headers, setPaginationFromHeaders]) - useEffect(() => { - const defaultBranch = branchList?.find(branch => branch.default) - setSelectedBranchTag({ - name: defaultBranch?.name || repository?.default_branch || '', - sha: defaultBranch?.sha || '', - default: true - }) - setDefaultBranch(repository?.default_branch ?? '') - }, [branchList, repository?.default_branch]) - - const onSubmit = (data: CreateTagFromFields) => { + const onSubmit = (data: CreateTagFormFields) => { createTag({ body: { ...data @@ -139,21 +120,26 @@ export const RepoTagsListContainer = () => { }) } - const handleCreateBranch = (data: CreateBranchFormFields) => { - createBranch({ - repo_ref, - body: { - ...data - } - }) - } + const selectBranchOrTag = useCallback((branchTagName: BranchSelectorListItem) => { + setSelectedBranchOrTag(branchTagName) + }, []) + + useEffect(() => { + if (!openCreateTagDialog) { + resetCreateTagMutation() + } + }, [openCreateTagDialog, resetCreateTagMutation]) return ( <> setOpenCreateBranchDialog(true)} + openCreateBranchDialog={(selectedTagInList: BranchSelectorListItem) => { + setOpenCreateBranchDialog(true) + setSelectedTagInList(selectedTagInList) + setPreSelectedTab(BranchSelectorTab.TAGS) + }} openCreateTagDialog={() => setOpenCreateTagDialog(true)} searchQuery={query} setSearchQuery={setQuery} @@ -169,25 +155,24 @@ export const RepoTagsListContainer = () => { open={openCreateTagDialog} onClose={() => setOpenCreateTagDialog(false)} onSubmit={onSubmit} - branchQuery={branchQuery} - setBranchQuery={setBranchQuery} - useRepoBranchesStore={useRepoBranchesStore} isLoading={isCreatingTag} + error={createTagError?.message} + selectedBranchOrTag={selectedBranchOrTag} + renderProp={() => ( + + )} /> setOpenCreateBranchDialog(false)} - useRepoBranchesStore={useRepoBranchesStore} - onSubmit={handleCreateBranch} - useTranslationStore={useTranslationStore} - handleChangeSearchValue={setBranchQuery} - error={createBranchError?.message} + preselectedBranchOrTag={selectedTagInList} + preselectedTab={preSelectedTab} /> setDeleteTagDialog(false)} deleteFn={onDeleteTag} - error={{ type: '', message: '' }} + error={deleteError} type="tag" identifier={deleteTagName ?? undefined} isLoading={isDeletingTag} diff --git a/packages/ui/locales/en/views.json b/packages/ui/locales/en/views.json index 9d95dbe248..0ba2666810 100644 --- a/packages/ui/locales/en/views.json +++ b/packages/ui/locales/en/views.json @@ -110,6 +110,14 @@ "viewRules": "View Rules", "browse": "Browse", "deleteBranch": "Delete Branch", + "createBranchDialog": { + "validation": { + "name": "Branch name is required", + "noSpaces": "Name cannot contain spaces", + "target": "Base branch is required", + "nameRegex": "Name must contain only letters, numbers, and the characters: - _ ." + } + }, "createBranchTitle": "Create a branch", "createBranchButton": "Create branch", "newBranch": "New branch", @@ -160,11 +168,25 @@ "repoTagDescriptionPlaceholder": "Enter tag description here", "creatingTagButton": "Creating tag...", "createTagButton": "Create tag", - "tag": "Tag", - "commit": "Commit", + "createTagDialog": { + "validation": { + "name": "Tag name is required", + "noSpaces": "Name cannot contain spaces", + "target": "Base branch is required", + "nameRegex": "Name must contain only letters, numbers, and the characters: - _ .", + "message": "Description is required" + }, + "name": "Name", + "target": "Target", + "message": "Message" + }, "createBranch": "Create branch", "viewFiles": "View Files", "deleteTag": "Delete tag", + "tag": "Tag", + "commit": "Commit", + "tagger": "Tagger", + "creationDate": "Creation date", "newTag": "New tag", "enableWebhookToggle": "Enable the webhook", "toggleDescription": "We will deliver event details when this hook is triggered", @@ -187,29 +209,8 @@ "deleteConnector": "Delete Connector", "deleteSecret": "Delete Secret", "status": "Status", - "tagger": "Tagger", - "creationDate": "Creation date", - "createBranchDialog": { - "validation": { - "name": "Branch name is required", - "nameRegex": "Name must contain only letters, numbers, and the characters: - _ .", - "noSpaces": "Name cannot contain spaces", - "target": "Base branch is required" - } - }, - "createTagDialog": { - "validation": { - "name": "Tag name is required", - "nameMax": "Name must be no longer than 40 characters", - "nameRegex": "Name must contain only letters, numbers, and the characters: - _ .", - "noSpaces": "Name cannot contain spaces", - "target": "Base branch is required", - "message": "Description is required" - }, - "name": "Name", - "target": "Target", - "message": "Message" - }, + "branchCreated": "Branch created", + "branchCreatedDescription": "Branch {{name}} created successfully", "renameBranch": "Rename Branch", "add": "Add", "users": "Users", @@ -251,6 +252,7 @@ "noReposProject": "There are no repositories in this project yet.", "createOrImportRepos": "Create new or import an existing repository.", "noTags": "No tags yet", + "noTagsDescription": "Your tags will appear here once they're created. Start creating tags to see your work organized.", "createNewTag": "Create new tag", "noWebhookExecution": "No webhook executions yet", "noWebhookExecutionsDescription": "Your webhook executions will appear here once they're completed. Trigger your webhook to see the results.", @@ -262,7 +264,6 @@ "noUsersDescription": "There are no users in this scope. Click on the button below to start adding them.", "noConnectorsProject": "There are no connectors in this project yet.", "noSecretsProject": "There are no secrets in this project yet.", - "noTagsDescription": "Your tags will appear here once they're created. Start creating tags to see your work organized.", "commit": "Commit", "noLabels": "No labels yet", "noLabelsDescription": "Use labels to organize, prioritize, and categorize tasks efficiently." @@ -524,10 +525,10 @@ "selectMember": "Select member", "selectRole": "Select role", "enterBranchName": "Enter branch name", - "createBranchError": "Branch name is required", - "enterTagName": "Enter a tag name here", - "tagName": "Name", "basedOn": "Based on", + "tagName": "Name", + "enterTagName": "Enter a tag name here", + "createBranchError": "Branch name is required", "select": "Select", "baseBranch": "Base branch", "selectBranchError": "Base branch is required", @@ -563,6 +564,11 @@ "exclude": "Exclude", "include": "Include" }, + "createTagDialog": { + "validation": { + "message": "Description is required" + } + }, "webhookData": { "branchCreated": "Branch created", "branchUpdated": "Branch updated", diff --git a/packages/ui/locales/fr/views.json b/packages/ui/locales/fr/views.json index fbb9c46073..03638afa20 100644 --- a/packages/ui/locales/fr/views.json +++ b/packages/ui/locales/fr/views.json @@ -110,6 +110,14 @@ "viewRules": "Voir les règles", "browse": "Browse", "deleteBranch": "Delete Branch", + "createBranchDialog": { + "validation": { + "name": "Le nom de la branche est requis", + "noSpaces": "Le nom ne peut pas contenir d'espaces", + "target": "La branche de base est requise", + "nameRegex": "Le nom ne doit contenir que des lettres, des chiffres et les caractères : - _ ." + } + }, "createBranchTitle": "Créer une branche", "createBranchButton": "Créer une branche", "newBranch": "Nouvelle branche", @@ -157,14 +165,28 @@ "editReadme": "Edit README.md", "openPullReq": "Requêtes de tirage ouvertes", "createTagTitle": "Create a tag", - "repoTagDescriptionPlaceholder": "Enter a description of this tag...", + "repoTagDescriptionPlaceholder": "Entrez la description de l'étiquette ici", "creatingTagButton": "Creating tag...", - "createTagButton": "Create tag", - "tag": "Tag", - "commit": "Commit", + "createTagButton": "Créer un tag", + "createTagDialog": { + "validation": { + "name": "Le nom du tag est requis", + "noSpaces": "Le nom ne peut pas contenir d'espaces", + "target": "La branche de base est requise", + "nameRegex": "Le nom ne doit contenir que des lettres, des chiffres et les caractères : - _ .", + "message": "La description est requise" + }, + "name": "Nom", + "target": "Cible", + "message": "Message" + }, "createBranch": "Create branch", "viewFiles": "View Files", "deleteTag": "Delete tag", + "tag": "Tag", + "commit": "Commit", + "tagger": "Tagger", + "creationDate": "Date de création", "newTag": "New tag", "enableWebhookToggle": "Activer le webhook", "toggleDescription": "Nous enverrons les détails lorsque ce webhook sera déclenché", @@ -184,8 +206,7 @@ "creatingWebhook": "Creating webhook...", "updateWebhook": "Update webhook", "createWebhook": "Create webhook", - "status": "Statut", - "tagger": "Tagger", + "status": "Status", "renameBranch": "Renommer la branche", "add": "Ajouter", "users": "Utilisateurs", @@ -226,7 +247,8 @@ "noRepos": "Pas encore de dépôts", "noReposProject": "Il n'y a pas encore de dépôts dans ce projet.", "createOrImportRepos": "Créer un nouveau dépôt ou importer un dépôt existant.", - "noTags": "There are no tags in this project yet.", + "noTags": "Il n'y a pas encore de tags dans ce projet.", + "noTagsDescription": "Vos tags apparaîtront ici une fois créés. Commencez à créer des tags pour organiser votre travail.", "createNewTag": "Create a new Tag.", "noWebhookExecution": "No webhook executions yet", "noWebhookExecutionsDescription": "Your webhook executions will appear here once they're completed. Trigger your webhook to see the results.", @@ -492,8 +514,10 @@ "selectMember": "", "selectRole": "", "enterBranchName": "Entrez le nom de la branche", + "basedOn": "Basé sur", + "tagName": "Nom", + "enterTagName": "Entrez un nom d'étiquette ici", "createBranchError": "Le nom de la branche est requis", - "enterTagName": "Enter tag name", "select": "Sélectionner", "baseBranch": "Branche de base", "selectBranchError": "La branche de base est requise", @@ -527,6 +551,11 @@ "edit": "Edit rule", "delete": "Delete rule" }, + "createTagDialog": { + "validation": { + "message": "Description is required" + } + }, "webhookData": { "branchCreated": "Branche créée", "branchUpdated": "Branche mise à jour", diff --git a/packages/ui/src/components/alert/AlertTitle.tsx b/packages/ui/src/components/alert/AlertTitle.tsx index e66048a61b..b9770747fb 100644 --- a/packages/ui/src/components/alert/AlertTitle.tsx +++ b/packages/ui/src/components/alert/AlertTitle.tsx @@ -7,7 +7,7 @@ export interface AlertTitleProps extends PropsWithChildren(({ className, children }, ref) => ( -
+
{children}
)) diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx index 9b5b2ea3c9..e196a670e2 100644 --- a/packages/ui/src/components/button.tsx +++ b/packages/ui/src/components/button.tsx @@ -15,7 +15,7 @@ const buttonVariants = cva( destructive: 'bg-button-background-danger-1 text-button-foreground-danger-1 hover:bg-button-background-danger-2', outline: - 'border border-cn-borders-2 bg-transparent text-cn-foreground-2 hover:border-cn-borders-6 hover:text-cn-foreground-1', + 'hover:border-cn-borders-6 border border-cn-borders-2 bg-transparent text-cn-foreground-2 hover:text-cn-foreground-1', secondary: 'bg-cn-background-3 text-cn-foreground-2 hover:bg-cn-background-3/80', tertiary: 'bg-cn-background-2 text-cn-foreground-2 hover:bg-cn-background-2/80', ghost: 'hover:bg-cn-background-12 hover:text-cn-foreground-1', diff --git a/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx b/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx index 5ef05c6dd9..7f48c3d60f 100644 --- a/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx +++ b/packages/ui/src/components/chat/chat-empty-preview-wrapper.tsx @@ -4,7 +4,7 @@ import { Chat } from '@/components' export const ChatEmptyPreviewWrapper: FC = () => { return ( -
+
diff --git a/packages/ui/src/components/chat/chat-preview-wrapper.tsx b/packages/ui/src/components/chat/chat-preview-wrapper.tsx index db39534b09..d39e9e8a57 100644 --- a/packages/ui/src/components/chat/chat-preview-wrapper.tsx +++ b/packages/ui/src/components/chat/chat-preview-wrapper.tsx @@ -19,7 +19,7 @@ const diffData = export const ChatPreviewWrapper: FC = () => { return ( -
+
{}} /> diff --git a/packages/ui/src/components/commit-copy-actions.tsx b/packages/ui/src/components/commit-copy-actions.tsx index 338d256348..8243eaa7a1 100644 --- a/packages/ui/src/components/commit-copy-actions.tsx +++ b/packages/ui/src/components/commit-copy-actions.tsx @@ -6,10 +6,12 @@ import copy from 'clipboard-copy' export const CommitCopyActions = ({ sha, - toCommitDetails + toCommitDetails, + className }: { sha: string toCommitDetails?: ({ sha }: { sha: string }) => string + className?: string }) => { const [copied, setCopied] = useState(false) const { navigate } = useRouterContext() @@ -30,10 +32,10 @@ export const CommitCopyActions = ({ } return ( - + )} - {isDynamic && } + {isDynamic && }
) } diff --git a/packages/ui/src/views/profile-settings/profile-settings-general-page.tsx b/packages/ui/src/views/profile-settings/profile-settings-general-page.tsx index d78b89a28a..fae34bea49 100644 --- a/packages/ui/src/views/profile-settings/profile-settings-general-page.tsx +++ b/packages/ui/src/views/profile-settings/profile-settings-general-page.tsx @@ -260,7 +260,7 @@ export const SettingsAccountGeneralPage: FC = ( - + = ({
)} - + {sourceBranch && ( <> diff --git a/packages/ui/src/views/repo/pull-request/details/components/changes/pull-request-changes-filter.tsx b/packages/ui/src/views/repo/pull-request/details/components/changes/pull-request-changes-filter.tsx index 42ad2a6bbc..9196d822b4 100644 --- a/packages/ui/src/views/repo/pull-request/details/components/changes/pull-request-changes-filter.tsx +++ b/packages/ui/src/views/repo/pull-request/details/components/changes/pull-request-changes-filter.tsx @@ -307,7 +307,7 @@ export const PullRequestChangesFilter: React.FC = {diff.addedLines != null && diff.addedLines > 0 && diff.deletedLines != null && - diff.deletedLines > 0 && } + diff.deletedLines > 0 && } {diff.deletedLines != null && diff.deletedLines > 0 && ( -{diff.deletedLines} )} diff --git a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-comment-box.tsx b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-comment-box.tsx index f08ccf019b..dfc285eef5 100644 --- a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-comment-box.tsx +++ b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-comment-box.tsx @@ -192,7 +192,7 @@ export const PullRequestCommentBox = ({ - {isFirst &&
} + {isFirst &&
} ) })} diff --git a/packages/ui/src/views/repo/repo-branch/components/branch-list.tsx b/packages/ui/src/views/repo/repo-branch/components/branch-list.tsx index 196b48e2b8..ca79f3bdea 100644 --- a/packages/ui/src/views/repo/repo-branch/components/branch-list.tsx +++ b/packages/ui/src/views/repo/repo-branch/components/branch-list.tsx @@ -93,7 +93,7 @@ export const BranchesList: FC = ({
{t('views:repos.behind', 'Behind')} -
+
{t('views:repos.ahead', 'Ahead')}
diff --git a/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx b/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx index 116182672d..d3e8a1a6c1 100644 --- a/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx +++ b/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx @@ -1,17 +1,31 @@ -import { useEffect } from 'react' +import { useCallback, useEffect } from 'react' import { useForm } from 'react-hook-form' -import { Alert, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Input } from '@/components' +import { Alert, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Input, Label } from '@/components' import { zodResolver } from '@hookform/resolvers/zod' -import { BranchSelector } from '@views/repo/components' +import { TranslationStore } from '@views/repo' import { z } from 'zod' import { CreateBranchDialogProps, CreateBranchFormFields } from '../types' -export const createBranchFormSchema = z.object({ - name: z.string().min(1, { message: 'Branch name is required' }), - target: z.string().min(1, { message: 'Base branch is required' }) -}) +export const createBranchFormSchema = (t: TranslationStore['t']) => + z.object({ + name: z + .string() + .trim() + .min(1, { message: t('views:repos.createBranchDialog.validation.name', 'Branch name is required') }) + .refine(data => !data.includes(' '), { + message: t('views:repos.createBranchDialog.validation.noSpaces', 'Name cannot contain spaces') + }), + target: z + .string() + .min(1, { message: t('views:repos.createBranchDialog.validation.target', 'Base branch is required') }) + }) + +const INITIAL_FORM_VALUES: CreateBranchFormFields = { + name: '', + target: '' +} export function CreateBranchDialog({ open, @@ -20,11 +34,10 @@ export function CreateBranchDialog({ isCreatingBranch, error, useTranslationStore, - useRepoBranchesStore, - handleChangeSearchValue + selectedBranchOrTag, + renderProp: BranchSelectorContainer }: CreateBranchDialogProps) { const { t } = useTranslationStore() - const { setSelectedBranchTag, defaultBranch } = useRepoBranchesStore() const { register, @@ -32,84 +45,66 @@ export function CreateBranchDialog({ setValue, reset, clearErrors, - formState: { errors, isValid, isSubmitSuccessful } + formState: { errors, isValid } } = useForm({ - resolver: zodResolver(createBranchFormSchema), + resolver: zodResolver(createBranchFormSchema(t)), mode: 'onChange', - defaultValues: { - name: '', - target: '' - } + defaultValues: INITIAL_FORM_VALUES }) - useEffect(() => { - if (isSubmitSuccessful) { - clearErrors() - reset() - setValue('name', '', { shouldValidate: false }) - setValue('target', defaultBranch || '', { shouldValidate: false }) - setSelectedBranchTag({ name: defaultBranch || '', sha: '' }) - onClose() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isSubmitSuccessful, open, onClose]) - - const handleClose = () => { + const resetForm = useCallback(() => { clearErrors() - setValue('name', '', { shouldValidate: false }) - setValue('target', defaultBranch || '', { shouldValidate: false }) - setSelectedBranchTag({ name: defaultBranch || '', sha: '' }) - handleChangeSearchValue('') - onClose() - } + reset(INITIAL_FORM_VALUES) + }, [clearErrors, reset]) + + const handleFormSubmit = async (data: CreateBranchFormFields) => { + await onSubmit(data) - const handleSelectChange = (fieldName: keyof CreateBranchFormFields, value: string) => { - setValue(fieldName, value, { shouldValidate: true }) + if (!error && !isCreatingBranch) { + handleClose() + } } useEffect(() => { - if (defaultBranch) { - setValue('target', defaultBranch, { shouldValidate: true }) - setSelectedBranchTag({ name: defaultBranch, sha: '' }) + if (open) { + resetForm() + + if (selectedBranchOrTag) { + setValue('target', selectedBranchOrTag.name, { shouldValidate: true }) + } } - }, [defaultBranch, setValue]) + }, [selectedBranchOrTag, setValue, open, resetForm]) + + const handleClose = () => { + resetForm() + onClose() + } return ( - {t('views:repos.createBranchTitle', 'Create a branch')} + {t('views:repos.createBranchTitle', 'Create a branch')} - +
- {/* TODO: Currently the search within BranchSelector is not working, we need to review the current passed states for it to work */} - { - handleSelectChange('target', value.name) - setSelectedBranchTag(value) - }} - setSearchQuery={handleChangeSearchValue} - buttonSize="md" - isBranchOnly - dynamicWidth - /> + + {BranchSelectorContainer()}
diff --git a/packages/ui/src/views/repo/repo-branch/components/divergence-gauge.tsx b/packages/ui/src/views/repo/repo-branch/components/divergence-gauge.tsx index de4b1afcc4..99518c93c9 100644 --- a/packages/ui/src/views/repo/repo-branch/components/divergence-gauge.tsx +++ b/packages/ui/src/views/repo/repo-branch/components/divergence-gauge.tsx @@ -35,7 +35,7 @@ export const DivergenceGauge = ({ behindAhead, className, useTranslationStore }: {t('views:repos.behind', 'behind')} -
+
{behindAhead.ahead ?? 0} diff --git a/packages/ui/src/views/repo/repo-branch/repo-branch-list-view.tsx b/packages/ui/src/views/repo/repo-branch/repo-branch-list-view.tsx index e9df206154..b52920d52d 100644 --- a/packages/ui/src/views/repo/repo-branch/repo-branch-list-view.tsx +++ b/packages/ui/src/views/repo/repo-branch/repo-branch-list-view.tsx @@ -6,22 +6,16 @@ import { useDebounceSearch } from '@hooks/use-debounce-search' import { cn } from '@utils/cn' import { BranchesList } from './components/branch-list' -import { CreateBranchDialog } from './components/create-branch-dialog' import { RepoBranchListViewProps } from './types' export const RepoBranchListView: FC = ({ isLoading, useRepoBranchesStore, useTranslationStore, - isCreateBranchDialogOpen, setCreateBranchDialogOpen, - onSubmit, - createBranchError, - isCreatingBranch, searchQuery, setSearchQuery, onDeleteBranch, - setCreateBranchSearchQuery, ...routingProps }) => { const { t } = useTranslationStore() @@ -89,19 +83,6 @@ export const RepoBranchListView: FC = ({ )} - { - setCreateBranchDialogOpen(false) - }} - useRepoBranchesStore={useRepoBranchesStore} - onSubmit={onSubmit} - isCreatingBranch={isCreatingBranch} - useTranslationStore={useTranslationStore} - error={createBranchError} - defaultBranch={defaultBranch} - handleChangeSearchValue={setCreateBranchSearchQuery} - /> ) } diff --git a/packages/ui/src/views/repo/repo-branch/types.ts b/packages/ui/src/views/repo/repo-branch/types.ts index 482d03e7b1..2f0be212eb 100644 --- a/packages/ui/src/views/repo/repo-branch/types.ts +++ b/packages/ui/src/views/repo/repo-branch/types.ts @@ -1,13 +1,13 @@ -import { Dispatch, SetStateAction } from 'react' +import { Dispatch, ReactNode, SetStateAction } from 'react' import { TranslationStore } from '@/views' import { z } from 'zod' import { PullRequestType } from '../pull-request/pull-request.types' -import { IBranchSelectorStore } from '../repo.types' +import { BranchSelectorListItem, IBranchSelectorStore } from '../repo.types' import { createBranchFormSchema } from './components/create-branch-dialog' -export type CreateBranchFormFields = z.infer +export type CreateBranchFormFields = z.infer> export interface BranchProps { id: number name: string @@ -60,7 +60,7 @@ export interface RepoBranchListViewProps extends Partial { useTranslationStore: () => TranslationStore isCreateBranchDialogOpen: boolean setCreateBranchDialogOpen: (isOpen: boolean) => void - onSubmit: (formValues: CreateBranchFormFields) => void + onSubmit: (formValues: CreateBranchFormFields) => Promise isCreatingBranch: boolean createBranchError?: string searchQuery: string | null @@ -77,11 +77,10 @@ interface Branch { export interface CreateBranchDialogProps { open: boolean onClose: () => void - onSubmit: (formValues: CreateBranchFormFields) => void + onSubmit: (formValues: CreateBranchFormFields) => Promise error?: string isCreatingBranch?: boolean useTranslationStore: () => TranslationStore - defaultBranch?: string - handleChangeSearchValue: Dispatch> - useRepoBranchesStore: () => IBranchSelectorStore + selectedBranchOrTag: BranchSelectorListItem | null + renderProp: () => ReactNode } diff --git a/packages/ui/src/views/repo/repo-commit-details/repo-commit-details-view.tsx b/packages/ui/src/views/repo/repo-commit-details/repo-commit-details-view.tsx index 79e41b6e66..813f802f16 100644 --- a/packages/ui/src/views/repo/repo-commit-details/repo-commit-details-view.tsx +++ b/packages/ui/src/views/repo/repo-commit-details/repo-commit-details-view.tsx @@ -47,7 +47,7 @@ export const RepoCommitDetailsView: FC = ({ {isVerified && ( <> - + {t('views:commits.verified', 'Verified')} @@ -73,7 +73,7 @@ export const RepoCommitDetailsView: FC = ({ {!showSidebar && } {showSidebar && ( - + )} diff --git a/packages/ui/src/views/repo/repo-list/repo-list-page.tsx b/packages/ui/src/views/repo/repo-list/repo-list-page.tsx index e7604dd116..952da73f87 100644 --- a/packages/ui/src/views/repo/repo-list/repo-list-page.tsx +++ b/packages/ui/src/views/repo/repo-list/repo-list-page.tsx @@ -104,7 +104,7 @@ const SandboxRepoListPage: FC = ({ {viewManagement.currentView && ( <> - + {viewManagement.currentView.name} )} diff --git a/packages/ui/src/views/repo/repo-settings/components/repo-settings-general-rules.tsx b/packages/ui/src/views/repo/repo-settings/components/repo-settings-general-rules.tsx index dbbcd0a928..731b4afee2 100644 --- a/packages/ui/src/views/repo/repo-settings/components/repo-settings-general-rules.tsx +++ b/packages/ui/src/views/repo/repo-settings/components/repo-settings-general-rules.tsx @@ -29,9 +29,9 @@ const Description: FC = ({ targetPatternsCount, rulesAppliedCo return (
{targetPatternsCount} {t('views:repos.targetPatterns', 'target patterns')} - + {rulesAppliedCount} {t('views:repos.rulesApplied', 'rules applied')} - + {bypassAllowed ? ( <> diff --git a/packages/ui/src/views/repo/repo-summary/components/summary-panel.tsx b/packages/ui/src/views/repo/repo-summary/components/summary-panel.tsx index 3ee40de967..738b8cb8de 100644 --- a/packages/ui/src/views/repo/repo-summary/components/summary-panel.tsx +++ b/packages/ui/src/views/repo/repo-summary/components/summary-panel.tsx @@ -47,7 +47,7 @@ const SummaryPanel: FC = ({ return ( <>
-
+
{title} @@ -76,7 +76,7 @@ const SummaryPanel: FC = ({ {!!description?.length && ( <> - + {description} diff --git a/packages/ui/src/views/repo/repo-tags/components/create-tag/create-tag-dialog.tsx b/packages/ui/src/views/repo/repo-tags/components/create-tag/create-tag-dialog.tsx new file mode 100644 index 0000000000..cd4d5aeaa8 --- /dev/null +++ b/packages/ui/src/views/repo/repo-tags/components/create-tag/create-tag-dialog.tsx @@ -0,0 +1,134 @@ +import { FC, ReactNode, useCallback, useEffect } from 'react' +import { useForm } from 'react-hook-form' + +import { Alert, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Input, Label, Textarea } from '@/components' +import { BranchSelectorListItem, TranslationStore } from '@/views/repo' +import { CreateTagFormFields, makeCreateTagFormSchema } from '@/views/repo/repo-tags/components/create-tag/schema' +import { zodResolver } from '@hookform/resolvers/zod' + +const INITIAL_FORM_VALUES: CreateTagFormFields = { + name: '', + target: '', + message: '' +} + +interface CreateTagDialogProps { + open: boolean + onClose: () => void + onSubmit: (data: CreateTagFormFields) => void + error?: string + useTranslationStore: () => TranslationStore + isLoading?: boolean + selectedBranchOrTag: BranchSelectorListItem | null + renderProp: () => ReactNode +} + +export const CreateTagDialog: FC = ({ + open, + onClose, + onSubmit, + error, + useTranslationStore, + isLoading, + selectedBranchOrTag, + renderProp: BranchSelectorContainer +}) => { + const { t } = useTranslationStore() + + const { + register, + handleSubmit, + setValue, + reset, + clearErrors, + formState: { errors } + } = useForm({ + resolver: zodResolver(makeCreateTagFormSchema(t)), + mode: 'onChange', + defaultValues: INITIAL_FORM_VALUES + }) + + const resetForm = useCallback(() => { + clearErrors() + reset(INITIAL_FORM_VALUES) + }, [clearErrors, reset]) + + useEffect(() => { + if (open) { + resetForm() + + if (selectedBranchOrTag) { + setValue('target', selectedBranchOrTag.name, { shouldValidate: true }) + } + } + }, [open, resetForm, selectedBranchOrTag, setValue]) + + const handleClose = () => { + resetForm() + onClose() + } + + return ( + + + + {t('views:repos.createTagTitle', 'Create a tag')} + + + +
+ +
+ +
+ + + {BranchSelectorContainer()} + +
+ +
+