From b9f6a267d56d3037b1cb651dd8680307fbbf389a Mon Sep 17 00:00:00 2001 From: Andrew Koreykin Date: Fri, 7 Feb 2025 19:02:54 +0400 Subject: [PATCH] refactor: labels --- .../src/subjects/stores/labels-store.tsx | 2 + .../src/subjects/views/labels/labels-form.tsx | 3 +- .../views/labels/project-labels-list.tsx | 7 +- .../views/labels/repo-labels-list.tsx | 7 +- .../pull-request-compare.tsx | 1 + .../pull-request-conversation.tsx | 3 +- ...l-store-with-project-label-values-data.ts} | 35 ++- .../labels/project-label-form-container.tsx | 41 +-- .../labels/project-labels-list-container.tsx | 18 +- .../project-general-settings-container.tsx | 6 +- .../{labels-store.tsx => labels-store.ts} | 4 +- .../hooks/use-pr-conversation-labels.ts | 24 +- .../pull-request/pull-request-compare.tsx | 1 + .../pull-request-conversation.tsx | 60 ++-- .../pull-request-data-provider.tsx | 5 +- .../labels/hooks/use-full-fill-label-store.ts | 29 +- .../use-get-repo-label-and-values-data.ts | 10 +- .../repo/labels/label-form-container.tsx | 39 +-- .../repo/labels/labels-list-container.tsx | 15 +- .../context/pull-request-data-provider.tsx | 5 +- packages/ui/locales/en/views.json | 11 +- packages/ui/locales/es/views.json | 11 +- packages/ui/locales/fr/views.json | 9 +- packages/ui/src/components/icon.tsx | 14 +- packages/ui/src/components/input.tsx | 17 +- packages/ui/src/components/label-marker.tsx | 90 ++++-- packages/ui/src/components/search-box.tsx | 32 +- packages/ui/src/components/select.tsx | 2 +- packages/ui/src/components/table.tsx | 8 +- packages/ui/src/utils/index.ts | 1 + .../labels/components/label-cell-content.tsx | 35 +-- .../label-form-color-and-name-group.tsx | 74 +++-- .../labels/components/labels-list-view.tsx | 53 ++-- .../ui/src/views/labels/label-form-page.tsx | 177 ++++++----- .../ui/src/views/labels/labels-list-page.tsx | 102 +++---- packages/ui/src/views/labels/types.ts | 2 + .../branch-selector/branch-selector.tsx | 19 +- .../compare/pull-request-compare-page.tsx | 9 +- .../labels/label-value-selector.tsx | 96 +++--- .../labels/pull-request-labels-header.tsx | 104 ++++--- .../labels/pull-request-labels-list.tsx | 20 +- .../pull-request-item-description.tsx | 17 +- .../components/pull-request-item-title.tsx | 53 ++-- .../components/pull-request-list.tsx | 50 ++- .../components/pull-request-side-bar.tsx | 13 +- .../conversation/pull-request-comment-box.tsx | 8 +- .../conversation/pull-request-filters.tsx | 8 +- .../pull-request-system-comments.tsx | 75 +++-- .../pull-request/pull-request-list-page.tsx | 7 +- .../secrets/new-secret/new-secret-form.tsx | 4 +- .../components/execution/execution-status.tsx | 4 +- .../components/execution/key-value-table.tsx | 4 +- .../pull-request/pull-request-filters.tsx | 87 ------ .../pull-request/pull-request-list.tsx | 284 ------------------ .../pull-request/pull-request-side-bar.tsx | 50 --- packages/views/src/index.ts | 3 - 56 files changed, 713 insertions(+), 1155 deletions(-) rename apps/gitness/src/pages-v2/project/labels/hooks/{use-get-project-label-and-values-data.ts => use-fill-label-store-with-project-label-values-data.ts} (85%) rename apps/gitness/src/pages-v2/project/stores/{labels-store.tsx => labels-store.ts} (92%) delete mode 100644 packages/views/src/components/pull-request/pull-request-filters.tsx delete mode 100644 packages/views/src/components/pull-request/pull-request-list.tsx delete mode 100644 packages/views/src/components/pull-request/pull-request-side-bar.tsx diff --git a/apps/design-system/src/subjects/stores/labels-store.tsx b/apps/design-system/src/subjects/stores/labels-store.tsx index 6bab5b985b..33c6695144 100644 --- a/apps/design-system/src/subjects/stores/labels-store.tsx +++ b/apps/design-system/src/subjects/stores/labels-store.tsx @@ -74,6 +74,7 @@ export const LabelsListStore: RepoLabelsListStore = { } ] }, + isLoading: false, getParentScopeLabels: false, @@ -82,6 +83,7 @@ export const LabelsListStore: RepoLabelsListStore = { addLabel: (_: ILabelType) => {}, deleteLabel: (_: string) => {}, + setIsLoading: (_: boolean) => {}, setValues: (_: Record) => {}, setRepoSpaceRef: (_: SetRepoSpaceRefProps) => {}, diff --git a/apps/design-system/src/subjects/views/labels/labels-form.tsx b/apps/design-system/src/subjects/views/labels/labels-form.tsx index bea68b06eb..9571b0c889 100644 --- a/apps/design-system/src/subjects/views/labels/labels-form.tsx +++ b/apps/design-system/src/subjects/views/labels/labels-form.tsx @@ -8,11 +8,10 @@ export const LabelsForm = () => { {}} onFormCancel={() => {}} error={''} - isDataLoading={false} /> ) } diff --git a/apps/design-system/src/subjects/views/labels/project-labels-list.tsx b/apps/design-system/src/subjects/views/labels/project-labels-list.tsx index 3b331e83d2..8ced6145bd 100644 --- a/apps/design-system/src/subjects/views/labels/project-labels-list.tsx +++ b/apps/design-system/src/subjects/views/labels/project-labels-list.tsx @@ -12,14 +12,13 @@ export const ProjectLabelsList = () => { return ( <> {}} - handleDeleteLabel={() => setOpenAlertDeleteDialog(true)} searchQuery={''} - setSearchQuery={() => {}} - isLoading={false} + setSearchQuery={noop} + labelsListViewProps={{ handleEditLabel: noop, handleDeleteLabel: () => setOpenAlertDeleteDialog(true) }} /> { return ( <> {}} - handleDeleteLabel={() => setOpenAlertDeleteDialog(true)} searchQuery={''} - setSearchQuery={() => {}} - isLoading={false} + setSearchQuery={noop} isRepository + labelsListViewProps={{ handleEditLabel: noop, handleDeleteLabel: () => setOpenAlertDeleteDialog(true) }} /> > = prop setSearchReviewersQuery={noop} jumpToDiff="" setJumpToDiff={noop} + editLabelsProps={{ to: '' }} {...props} /> ) diff --git a/apps/design-system/src/subjects/views/pull-request-conversation/pull-request-conversation.tsx b/apps/design-system/src/subjects/views/pull-request-conversation/pull-request-conversation.tsx index 1582ddb9d2..4f2474216c 100644 --- a/apps/design-system/src/subjects/views/pull-request-conversation/pull-request-conversation.tsx +++ b/apps/design-system/src/subjects/views/pull-request-conversation/pull-request-conversation.tsx @@ -175,7 +175,8 @@ const PullRequestConversation: FC = ({ state }) => searchLabelQuery: searchLabel, setSearchLabelQuery: noop, addLabel: noop, - removeLabel: noop + removeLabel: noop, + editLabelsProps: { to: '' } }} /> diff --git a/apps/gitness/src/pages-v2/project/labels/hooks/use-get-project-label-and-values-data.ts b/apps/gitness/src/pages-v2/project/labels/hooks/use-fill-label-store-with-project-label-values-data.ts similarity index 85% rename from apps/gitness/src/pages-v2/project/labels/hooks/use-get-project-label-and-values-data.ts rename to apps/gitness/src/pages-v2/project/labels/hooks/use-fill-label-store-with-project-label-values-data.ts index a1752ecc71..285df51678 100644 --- a/apps/gitness/src/pages-v2/project/labels/hooks/use-get-project-label-and-values-data.ts +++ b/apps/gitness/src/pages-v2/project/labels/hooks/use-fill-label-store-with-project-label-values-data.ts @@ -13,30 +13,35 @@ const isDataResponse = (value: LabelValuesResponseResultType): value is { key: s return 'data' in value } -export interface UseGetProjectLabelAndValuesDataProps { +export interface UseFillLabelStoreWithProjectLabelValuesDataProps { queryPage?: number query?: string enabled?: boolean } -export const useGetProjectLabelAndValuesData = ({ +export const useFillLabelStoreWithProjectLabelValuesData = ({ queryPage, query, enabled = true -}: UseGetProjectLabelAndValuesDataProps) => { +}: UseFillLabelStoreWithProjectLabelValuesDataProps) => { const space_ref = useGetSpaceURLParam() const [isLoadingValues, setIsLoadingValues] = useState(false) - const { labels: storeLabels, setLabels, setValues, setRepoSpaceRef, resetLabelsAndValues } = useLabelsStore() + const { + labels: storeLabels, + setLabels, + setValues, + setRepoSpaceRef, + resetLabelsAndValues, + setIsLoading + } = useLabelsStore() const { data: { body: labels } = {}, isLoading: isLoadingSpaceLabels } = useListSpaceLabelsQuery( { space_ref: space_ref ?? '', queryParams: { page: queryPage || 1, limit: 10, query: query ?? '' } }, - { - enabled - } + { enabled } ) /** @@ -123,13 +128,15 @@ export const useGetProjectLabelAndValuesData = ({ * Set space_ref to store */ useEffect(() => { - setRepoSpaceRef({ - space_ref: space_ref ?? '' - }) + setRepoSpaceRef({ space_ref: space_ref ?? '' }) }, [space_ref, setRepoSpaceRef]) - return { - isLoading: isLoadingSpaceLabels || isLoadingValues, - space_ref - } + /** + * Set loading state to store + */ + useEffect(() => { + setIsLoading(enabled ? isLoadingSpaceLabels : isLoadingValues) + }, [isLoadingSpaceLabels, isLoadingValues, setIsLoading, query, enabled]) + + return { space_ref } } diff --git a/apps/gitness/src/pages-v2/project/labels/project-label-form-container.tsx b/apps/gitness/src/pages-v2/project/labels/project-label-form-container.tsx index d31eec6944..1071053dfd 100644 --- a/apps/gitness/src/pages-v2/project/labels/project-label-form-container.tsx +++ b/apps/gitness/src/pages-v2/project/labels/project-label-form-container.tsx @@ -7,32 +7,14 @@ import { useRoutes } from '../../../framework/context/NavigationContext' import { useTranslationStore } from '../../../i18n/stores/i18n-store' import { PathParams } from '../../../RouteDefinitions' import { useLabelsStore } from '../stores/labels-store' -import { useGetProjectLabelAndValuesData } from './hooks/use-get-project-label-and-values-data' +import { useFillLabelStoreWithProjectLabelValuesData } from './hooks/use-fill-label-store-with-project-label-values-data' export const ProjectLabelFormContainer = () => { const routes = useRoutes() const { spaceId, labelId } = useParams() const navigate = useNavigate() - const { isLoading: isDataLoading, space_ref } = useGetProjectLabelAndValuesData({ - query: labelId, - enabled: !!labelId - }) - - const { - mutate, - isLoading, - error: createError - } = useSaveSpaceLabelMutation( - { - space_ref: space_ref ?? '' - }, - { - onSuccess: () => { - onFormCancel() - } - } - ) + const { space_ref } = useFillLabelStoreWithProjectLabelValuesData({ query: labelId, enabled: !!labelId }) const onFormCancel = () => { if (window.history.length > 1) { @@ -42,28 +24,27 @@ export const ProjectLabelFormContainer = () => { } } + const { + mutate, + isLoading: isSaving, + error: createError + } = useSaveSpaceLabelMutation({ space_ref: space_ref ?? '' }, { onSuccess: onFormCancel }) + const onSubmit = (data: CreateLabelFormFields) => { const { values, ...rest } = data - mutate({ - body: { - label: { - ...rest - }, - values - } - }) + mutate({ body: { label: { ...rest }, values } }) } return ( ) diff --git a/apps/gitness/src/pages-v2/project/labels/project-labels-list-container.tsx b/apps/gitness/src/pages-v2/project/labels/project-labels-list-container.tsx index 27ff3d5d65..5711f40897 100644 --- a/apps/gitness/src/pages-v2/project/labels/project-labels-list-container.tsx +++ b/apps/gitness/src/pages-v2/project/labels/project-labels-list-container.tsx @@ -10,7 +10,7 @@ import { useQueryState } from '../../../framework/hooks/useQueryState' import usePaginationQueryStateWithStore from '../../../hooks/use-pagination-query-state-with-store' import { useTranslationStore } from '../../../i18n/stores/i18n-store' import { useLabelsStore } from '../stores/labels-store' -import { useGetProjectLabelAndValuesData } from './hooks/use-get-project-label-and-values-data.ts' +import { useFillLabelStoreWithProjectLabelValuesData } from './hooks/use-fill-label-store-with-project-label-values-data.ts' export const ProjectLabelsList = () => { const navigate = useNavigate() @@ -24,7 +24,8 @@ export const ProjectLabelsList = () => { const { queryPage } = usePaginationQueryStateWithStore({ page, setPage }) const [query, setQuery] = useQueryState('query') - const { isLoading } = useGetProjectLabelAndValuesData({ queryPage, query }) + // To fetch labels/values and set isLoading state at useLabelsStore + useFillLabelStoreWithProjectLabelValuesData({ queryPage, query }) const handleOpenDeleteDialog = (identifier: string) => { setOpenAlertDeleteDialog(true) @@ -32,9 +33,7 @@ export const ProjectLabelsList = () => { } const { mutate: deleteSpaceLabel, isLoading: isDeletingSpaceLabel } = useDeleteSpaceLabelMutation( - { - space_ref: space_ref ?? '' - }, + { space_ref: space_ref ?? '' }, { onSuccess: (_data, variables) => { setOpenAlertDeleteDialog(false) @@ -48,22 +47,19 @@ export const ProjectLabelsList = () => { } const handleDeleteLabel = (identifier: string) => { - deleteSpaceLabel({ - key: identifier - }) + deleteSpaceLabel({ key: identifier }) } return ( <> { } // delete API call here - const deleteSpaceMutation = useDeleteSpaceMutation( + const { mutate: deleteSpaceMutation, isLoading } = useDeleteSpaceMutation( { space_ref: space?.path }, { onSuccess: ({ body: data }) => { @@ -65,7 +65,7 @@ export const ProjectGeneralSettingsPageContainer = () => { } ) - const handleDeleteProject = () => deleteSpaceMutation.mutate({}, {}) + const handleDeleteProject = () => deleteSpaceMutation({}) return ( <> @@ -85,7 +85,7 @@ export const ProjectGeneralSettingsPageContainer = () => { deleteFn={handleDeleteProject} type="Project" error={deleteError} - isLoading={deleteSpaceMutation.isLoading} + isLoading={isLoading} withForm useTranslationStore={useTranslationStore} /> diff --git a/apps/gitness/src/pages-v2/project/stores/labels-store.tsx b/apps/gitness/src/pages-v2/project/stores/labels-store.ts similarity index 92% rename from apps/gitness/src/pages-v2/project/stores/labels-store.tsx rename to apps/gitness/src/pages-v2/project/stores/labels-store.ts index 5a8f8b50a9..8a994682a7 100644 --- a/apps/gitness/src/pages-v2/project/stores/labels-store.tsx +++ b/apps/gitness/src/pages-v2/project/stores/labels-store.ts @@ -6,7 +6,7 @@ export const useLabelsStore = create(set => ({ labels: [], totalPages: 1, page: 1, - + isLoading: true, values: {}, space_ref: null, repo_ref: null, @@ -18,6 +18,8 @@ export const useLabelsStore = create(set => ({ deleteLabel: (key: string) => set(state => ({ labels: state.labels.filter(label => label.key !== key) })), setPage: (page: number) => set({ page }), + setIsLoading: (isLoading: boolean) => set({ isLoading }), + setValues: (values: LabelValuesType) => set({ values }), setRepoSpaceRef: ({ repo_ref, space_ref }: SetRepoSpaceRefProps) => set({ repo_ref, space_ref }), setGetParentScopeLabels: (getParentScopeLabels: boolean) => set({ getParentScopeLabels }), diff --git a/apps/gitness/src/pages-v2/pull-request/hooks/use-pr-conversation-labels.ts b/apps/gitness/src/pages-v2/pull-request/hooks/use-pr-conversation-labels.ts index 88ee3f4bbd..43ff03b4dd 100644 --- a/apps/gitness/src/pages-v2/pull-request/hooks/use-pr-conversation-labels.ts +++ b/apps/gitness/src/pages-v2/pull-request/hooks/use-pr-conversation-labels.ts @@ -25,11 +25,7 @@ export const usePrConversationLabels = ({ repoRef, prId, refetchData }: UsePrCon labels, values: labelsValues, refetchLabels - } = useGetRepoLabelAndValuesData({ - query: searchLabel, - inherited: true, - limit: 100 - }) + } = useGetRepoLabelAndValuesData({ query: searchLabel, inherited: true, limit: 100 }) const { data: { body: prLabels } = {}, refetch: refetchPRLabels } = useListLabelsQuery({ repo_ref: repoRef, @@ -44,23 +40,13 @@ export const usePrConversationLabels = ({ repoRef, prId, refetchData }: UsePrCon } const { mutate: addLabel } = useAssignLabelMutation( - { - repo_ref: repoRef, - pullreq_number: prId - }, - { - onSuccess: handleOnSuccess - } + { repo_ref: repoRef, pullreq_number: prId }, + { onSuccess: handleOnSuccess } ) const { mutate: removeLabel } = useUnassignLabelMutation( - { - repo_ref: repoRef, - pullreq_number: prId - }, - { - onSuccess: handleOnSuccess - } + { repo_ref: repoRef, pullreq_number: prId }, + { onSuccess: handleOnSuccess } ) const handleAddLabel = useCallback((body: HandleAddLabelType) => addLabel({ body }), [addLabel]) diff --git a/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx b/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx index cbc3032c82..d4e05aee49 100644 --- a/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx +++ b/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx @@ -605,6 +605,7 @@ export const CreatePullRequest = () => { PRLabels={labels} addLabel={handleAddLabel} removeLabel={handleDeleteLabel} + editLabelsProps={{ to: routes.toRepoLabels({ spaceId, repoId }) }} searchLabelQuery={searchLabel} setSearchLabelQuery={setSearchLabel} /> diff --git a/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx b/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx index 5d0ddb220f..f559ff78f3 100644 --- a/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx +++ b/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx @@ -227,33 +227,30 @@ export default function PullRequestConversationPage() { }, [sourceBranch, pullReqMetadata?.merged, pullReqMetadata?.closed]) useEffect(() => { - if (branchError) { - if (pullReqMetadata?.merged || pullReqMetadata?.closed) { - setShowRestoreBranchButton(true) - } else { - setShowDeleteBranchButton(false) - createBranch({ - repo_ref: repoRef, - body: { - name: pullReqMetadata?.source_branch || '', - target: pullReqMetadata?.source_sha, - bypass_rules: true, - dry_run_rules: true - } - }).then(res => { - if (res?.body?.rule_violations) { - const { checkIfBypassAllowed } = extractInfoFromRuleViolationArr(res.body?.rule_violations) - if (checkIfBypassAllowed) { - setShowRestoreBranchButton(true) - } else { - setShowRestoreBranchButton(false) - } - } else { - setShowRestoreBranchButton(true) - } - }) - } + if (!branchError) return + + if (pullReqMetadata?.merged || pullReqMetadata?.closed) { + return setShowRestoreBranchButton(true) } + + setShowDeleteBranchButton(false) + createBranch({ + repo_ref: repoRef, + body: { + name: pullReqMetadata?.source_branch || '', + target: pullReqMetadata?.source_sha, + bypass_rules: true, + dry_run_rules: true + } + }).then(res => { + if (res?.body?.rule_violations) { + const { checkIfBypassAllowed } = extractInfoFromRuleViolationArr(res.body?.rule_violations) + + return setShowRestoreBranchButton(checkIfBypassAllowed) + } + + setShowRestoreBranchButton(true) + }) }, [branchError]) const [activities, setActivities] = useState(activityData) @@ -348,16 +345,12 @@ export default function PullRequestConversationPage() { const handleAddReviewer = (id?: number) => { reviewerAddPullReq({ repo_ref: repoRef, pullreq_number: prId, body: { reviewer_id: id } }) - .then(() => { - refetchReviewers() - }) + .then(() => refetchReviewers()) .catch(error => setAddReviewerError(error.message)) } const handleDeleteReviewer = (id: number) => { reviewerDeletePullReq({ repo_ref: repoRef, pullreq_number: prId, pullreq_reviewer_id: id }) - .then(() => { - refetchReviewers() - }) + .then(() => refetchReviewers()) .catch(error => setRemoveReviewerError(error.message)) } @@ -653,7 +646,8 @@ export default function PullRequestConversationPage() { searchLabelQuery: searchLabel, setSearchLabelQuery: changeSearchLabel, addLabel: handleAddLabel, - removeLabel: handleRemoveLabel + removeLabel: handleRemoveLabel, + editLabelsProps: { to: routes.toRepoLabels({ spaceId, repoId }) } }} /> diff --git a/apps/gitness/src/pages-v2/pull-request/pull-request-data-provider.tsx b/apps/gitness/src/pages-v2/pull-request/pull-request-data-provider.tsx index 56f633c735..a21521849d 100644 --- a/apps/gitness/src/pages-v2/pull-request/pull-request-data-provider.tsx +++ b/apps/gitness/src/pages-v2/pull-request/pull-request-data-provider.tsx @@ -10,6 +10,7 @@ import { useListCommitsQuery, useListPullReqActivitiesQuery } from '@harnessio/code-service-client' +import { RepoRepositoryOutput } from '@harnessio/ui/views' import { useGetRepoRef } from '../../framework/hooks/useGetRepoPath' import { useGetSpaceURLParam } from '../../framework/hooks/useGetSpaceParam' @@ -92,7 +93,7 @@ const PullRequestDataProvider: React.FC { if (repoMetadata) { - setRepoMetadata(repoMetadata) + setRepoMetadata(repoMetadata as RepoRepositoryOutput) } }, [repoMetadata, setRepoMetadata]) useEffect(() => { @@ -119,7 +120,7 @@ const PullRequestDataProvider: React.FC { const repoId = useGetRepoId() - const { setLabels, setValues, setRepoSpaceRef, resetLabelsAndValues, getParentScopeLabels } = useLabelsStore() + const { setLabels, setValues, setRepoSpaceRef, resetLabelsAndValues, setIsLoading, getParentScopeLabels } = + useLabelsStore() - const { isLoading, space_ref, repo_ref, labels, values } = useGetRepoLabelAndValuesData({ - queryPage, - query, - enabled, - inherited: getParentScopeLabels - }) + const { + isLoading: isDataLoading, + space_ref, + repo_ref, + labels, + values + } = useGetRepoLabelAndValuesData({ queryPage, query, enabled, inherited: getParentScopeLabels }) /** * Resetting the store state for labels and values @@ -57,9 +59,12 @@ export const useFullFillLabelStore = ({ queryPage, query, enabled = true }: UseF setValues(values) }, [values, setValues]) - return { - isLoading, - space_ref, - repo_ref - } + /** + * Set loading state to store + */ + useEffect(() => { + setIsLoading(enabled && isDataLoading) + }, [isDataLoading, setIsLoading, query, enabled]) + + return { space_ref, repo_ref } } diff --git a/apps/gitness/src/pages-v2/repo/labels/hooks/use-get-repo-label-and-values-data.ts b/apps/gitness/src/pages-v2/repo/labels/hooks/use-get-repo-label-and-values-data.ts index 5819699931..b9cd5a2f69 100644 --- a/apps/gitness/src/pages-v2/repo/labels/hooks/use-get-repo-label-and-values-data.ts +++ b/apps/gitness/src/pages-v2/repo/labels/hooks/use-get-repo-label-and-values-data.ts @@ -42,14 +42,10 @@ export const useGetRepoLabelAndValuesData = ({ repo_ref: repo_ref ?? '', queryParams: { page: queryPage || 1, limit, query: query ?? '', inherited } }, - { - enabled - } + { enabled } ) - const labelsData = useMemo(() => { - return (labels || []) as ILabelType[] - }, [labels]) + const labelsData = useMemo(() => (labels || []) as ILabelType[], [labels]) /** * Get values for each label @@ -123,6 +119,6 @@ export const useGetRepoLabelAndValuesData = ({ repo_ref, labels: labelsData, values, - refetchLabels: refetchLabels + refetchLabels } } diff --git a/apps/gitness/src/pages-v2/repo/labels/label-form-container.tsx b/apps/gitness/src/pages-v2/repo/labels/label-form-container.tsx index 8ba547d439..0d5c584b53 100644 --- a/apps/gitness/src/pages-v2/repo/labels/label-form-container.tsx +++ b/apps/gitness/src/pages-v2/repo/labels/label-form-container.tsx @@ -14,52 +14,31 @@ export const RepoLabelFormContainer = () => { const { spaceId, repoId, labelId } = useParams() const navigate = useNavigate() - const { isLoading: isDataLoading, repo_ref } = useFullFillLabelStore({ - query: labelId, - enabled: !!labelId - }) + const { repo_ref } = useFullFillLabelStore({ query: labelId, enabled: !!labelId }) + + const onFormCancel = () => navigate(routes.toRepoLabels({ spaceId, repoId })) const { - mutate, - isLoading, + mutate: saveRepoLabel, + isLoading: isSaving, error: createError - } = useSaveRepoLabelMutation( - { - repo_ref - }, - { - onSuccess: () => { - onFormCancel() - } - } - ) - - const onFormCancel = () => { - navigate(routes.toRepoLabels({ spaceId, repoId })) - } + } = useSaveRepoLabelMutation({ repo_ref }, { onSuccess: onFormCancel }) const onSubmit = (data: CreateLabelFormFields) => { const { values, ...rest } = data - mutate({ - body: { - label: { - ...rest - }, - values - } - }) + saveRepoLabel({ body: { label: { ...rest }, values } }) } return ( ) diff --git a/apps/gitness/src/pages-v2/repo/labels/labels-list-container.tsx b/apps/gitness/src/pages-v2/repo/labels/labels-list-container.tsx index 906bacb1d3..9144724cbf 100644 --- a/apps/gitness/src/pages-v2/repo/labels/labels-list-container.tsx +++ b/apps/gitness/src/pages-v2/repo/labels/labels-list-container.tsx @@ -26,7 +26,7 @@ export const RepoLabelsList = () => { const [query, setQuery] = useQueryState('query') const { queryPage } = usePaginationQueryStateWithStore({ page, setPage }) - const { isLoading, space_ref, repo_ref } = useFullFillLabelStore({ queryPage, query }) + const { space_ref, repo_ref } = useFullFillLabelStore({ queryPage, query }) const handleOpenDeleteDialog = (identifier: string) => { setOpenAlertDeleteDialog(true) @@ -34,9 +34,7 @@ export const RepoLabelsList = () => { } const { mutate: deleteRepoLabel, isLoading: isDeletingRepoLabel } = useDeleteRepoLabelMutation( - { - repo_ref: repo_ref ?? '' - }, + { repo_ref: repo_ref ?? '' }, { onSuccess: (_data, variables) => { setOpenAlertDeleteDialog(false) @@ -46,9 +44,7 @@ export const RepoLabelsList = () => { ) const { mutate: deleteSpaceLabel, isLoading: isDeletingSpaceLabel } = useDeleteSpaceLabelMutation( - { - space_ref: space_ref ?? '' - }, + { space_ref: space_ref ?? '' }, { onSuccess: (_data, variables) => { setOpenAlertDeleteDialog(false) @@ -72,15 +68,14 @@ export const RepoLabelsList = () => { return ( <> { if (repoMetadata) { - setRepoMetadata(repoMetadata) + setRepoMetadata(repoMetadata as RepoRepositoryOutput) } }, [repoMetadata, setRepoMetadata]) useEffect(() => { @@ -135,7 +136,7 @@ const PullRequestDataProviderV1: React.FC>> -export interface IconProps { +export interface IconProps extends React.SVGProps { name: keyof typeof IconNameMap size?: number - height?: number - width?: number - className?: string } const Icon: React.FC = ({ name, size = 16, height, width, className }) => { const Component = IconNameMap[name] - return + return ( + + ) } export { Icon } diff --git a/packages/ui/src/components/input.tsx b/packages/ui/src/components/input.tsx index d84989b4ce..8ac195e02d 100644 --- a/packages/ui/src/components/input.tsx +++ b/packages/ui/src/components/input.tsx @@ -1,4 +1,4 @@ -import { forwardRef, Fragment, InputHTMLAttributes, ReactNode } from 'react' +import { forwardRef, InputHTMLAttributes, ReactNode } from 'react' import { Caption, ControlGroup, Icon, IconProps, Label, Message, MessageTheme } from '@/components' import { cn } from '@utils/cn' @@ -69,7 +69,7 @@ const BaseInputWithWrapper = forwardRef( }, ref ) => { - const isControlGroup = !!error || !!caption || !!label || !!wrapperClassName - const InputWrapper = isControlGroup ? ControlGroup : Fragment - const inputWrapperProps = isControlGroup - ? { - className: wrapperClassName - } - : {} - const InputComponent = customContent ? BaseInputWithWrapper : BaseInput const baseInputComp = ( @@ -167,7 +159,7 @@ const Input = forwardRef( } return ( - + {!!label && ( + ) } ) @@ -190,4 +182,3 @@ const Input = forwardRef( Input.displayName = 'Input' export { Input } -export type { InputProps } diff --git a/packages/ui/src/components/label-marker.tsx b/packages/ui/src/components/label-marker.tsx index 1dcf1595dc..62a8f6d51c 100644 --- a/packages/ui/src/components/label-marker.tsx +++ b/packages/ui/src/components/label-marker.tsx @@ -1,16 +1,20 @@ import { FC } from 'react' +import { Button, Icon } from '@/components' import { cn } from '@utils/cn' -import { ColorsEnum } from '@views/labels' +import { ColorsEnum, ILabelType, LabelType } from '@views/labels' export interface LabelMarkerProps { + type?: ILabelType['type'] + color: ILabelType['color'] label: string value?: string - color: ColorsEnum - inTable?: boolean + counter?: ILabelType['value_count'] + className?: string + onDelete?: () => void } -export const LabelMarkerColor = { +const LabelMarkerColor = { [ColorsEnum.RED]: 'bg-label-background-red text-label-foreground-red', [ColorsEnum.BLUE]: 'bg-label-background-blue text-label-foreground-blue', [ColorsEnum.GREEN]: 'bg-label-background-green text-label-foreground-green', @@ -25,23 +29,73 @@ export const LabelMarkerColor = { [ColorsEnum.MINT]: 'bg-label-background-mint text-label-foreground-mint', [ColorsEnum.LIME]: 'bg-label-background-lime text-label-foreground-lime' } +const deleteButtonColor = { + [ColorsEnum.RED]: 'text-label-foreground-red hover:opacity-60', + [ColorsEnum.BLUE]: 'text-label-foreground-blue hover:opacity-60', + [ColorsEnum.GREEN]: 'text-label-foreground-green hover:opacity-60', + [ColorsEnum.YELLOW]: 'text-label-foreground-yellow hover:opacity-60', + [ColorsEnum.PURPLE]: 'text-label-foreground-purple hover:opacity-60', + [ColorsEnum.PINK]: 'text-label-foreground-pink hover:opacity-60', + [ColorsEnum.VIOLET]: 'text-label-foreground-violet hover:opacity-60', + [ColorsEnum.INDIGO]: 'text-label-foreground-indigo hover:opacity-60', + [ColorsEnum.CYAN]: 'text-label-foreground-cyan hover:opacity-60', + [ColorsEnum.ORANGE]: 'text-label-foreground-orange hover:opacity-60', + [ColorsEnum.BROWN]: 'text-label-foreground-brown hover:opacity-60', + [ColorsEnum.MINT]: 'text-label-foreground-mint hover:opacity-60', + [ColorsEnum.LIME]: 'text-label-foreground-lime hover:opacity-60' +} + +export const LabelMarker: FC = ({ + type = LabelType.STATIC, + label, + value, + color, + counter, + onDelete, + className +}) => { + const isDynamic = type === LabelType.DYNAMIC + const isWithExtraContent = !!value || !!counter + const isWithDeleteButton = !!onDelete -export const LabelMarker: FC = ({ label, value, color, inTable = false }) => { return ( - - {label} - {!!value && ( - + + {label} + + {isWithExtraContent && ( + + {!!value && {value}} + + {!!counter && counter} + + )} + + + {isWithDeleteButton && ( + )} - + + {isDynamic && } + ) } diff --git a/packages/ui/src/components/search-box.tsx b/packages/ui/src/components/search-box.tsx index ac2bcba207..d9091ca334 100644 --- a/packages/ui/src/components/search-box.tsx +++ b/packages/ui/src/components/search-box.tsx @@ -1,14 +1,6 @@ -import React, { - ForwardedRef, - forwardRef, - ReactNode, - useCallback, - useEffect, - type ChangeEventHandler, - type InputHTMLAttributes -} from 'react' - -import { Icon, Input, Text } from '@/components' +import { ForwardedRef, forwardRef, ReactNode, useCallback, useEffect, type ChangeEventHandler } from 'react' + +import { Icon, Input, InputProps } from '@/components' import { cn } from '@utils/cn' import { noop } from 'lodash-es' @@ -28,8 +20,7 @@ enum TextSize { 'text-9xl' = 12 } -interface SearchBoxProps { - placeholder: string +interface SearchBoxProps extends InputProps { width?: 'full' | 'fixed' hasShortcut?: boolean hasSearchIcon?: boolean @@ -40,9 +31,6 @@ interface SearchBoxProps { onFocus?: () => void handleChange?: ChangeEventHandler showOnFocus?: boolean // New prop to control dialog appearance on focus - defaultValue?: InputHTMLAttributes['defaultValue'] - value?: InputHTMLAttributes['value'] - className?: string inputClassName?: string children?: ReactNode } @@ -65,7 +53,8 @@ const Root = forwardRef( showOnFocus = false, className, inputClassName = 'h-8', - children + children, + ...restInputProps }, ref: ForwardedRef ) => { @@ -115,17 +104,18 @@ const Root = forwardRef( return (
{hasSearchIcon && ( - + )} + {hasShortcut && !!shortcutLetter && (
- - {shortcutLetter} - + + {shortcutLetter}
)} >, 'defaultValue'>, 'dir'>, SelectPrimitive.SelectProps { label?: string diff --git a/packages/ui/src/components/table.tsx b/packages/ui/src/components/table.tsx index c86c15bca1..af76a418cf 100644 --- a/packages/ui/src/components/table.tsx +++ b/packages/ui/src/components/table.tsx @@ -43,7 +43,7 @@ TableHeader.displayName = 'TableHeader' const TableBody = forwardRef>( ({ className, ...props }, ref) => ( - + tr:hover]:bg-background-4', className)} {...props} /> ) ) TableBody.displayName = 'TableBody' @@ -57,11 +57,7 @@ TableFooter.displayName = 'TableFooter' const TableRow = forwardRef>( ({ className, ...props }, ref) => ( - + ) ) TableRow.displayName = 'TableRow' diff --git a/packages/ui/src/utils/index.ts b/packages/ui/src/utils/index.ts index 9c56149efa..cbcd9d099b 100644 --- a/packages/ui/src/utils/index.ts +++ b/packages/ui/src/utils/index.ts @@ -1 +1,2 @@ export * from './utils' +export * from './cn' diff --git a/packages/ui/src/views/labels/components/label-cell-content.tsx b/packages/ui/src/views/labels/components/label-cell-content.tsx index a61caf623f..7a15c3d8a7 100644 --- a/packages/ui/src/views/labels/components/label-cell-content.tsx +++ b/packages/ui/src/views/labels/components/label-cell-content.tsx @@ -1,45 +1,42 @@ import { FC } from 'react' import { Accordion, Icon, LabelMarker } from '@/components' -import { ILabelType, LabelValuesType } from '@/views' +import { ILabelType, LabelValueType } from '@/views' import { cn } from '@utils/cn' export interface LabelCellContentProps { label: ILabelType - values: LabelValuesType + values?: LabelValueType[] } export const LabelCellContent: FC = ({ label, values }) => { - const value = values?.[label.key] - const length = value ? value.length : 0 + const isWithValues = !!values?.length return ( - + -
- {!!length && ( +
+ {isWithValues && ( )} - + +
- - {!!value && - value.map(item => ( - + + {isWithValues && ( + + {values.map(item => ( + ))} - + + )} ) diff --git a/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx b/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx index 369f12d7ee..27079505d3 100644 --- a/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx +++ b/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx @@ -1,8 +1,8 @@ import { FC } from 'react' -import { UseFormRegister } from 'react-hook-form' -import { Button, Icon, Input, Select } from '@/components' -import { ColorsEnum, CreateLabelFormFields, TranslationStore } from '@/views' +import { Button, Icon, Input, InputProps, Select, SelectRootProps } from '@/components' +import { cn } from '@/utils' +import { ColorsEnum, TranslationStore } from '@/views' const SelectColorMarker = { [ColorsEnum.RED]: 'bg-label-foreground-red', @@ -21,62 +21,60 @@ const SelectColorMarker = { } interface LabelFormColorAndNameGroupProps { - name: string - colorValue: ColorsEnum - handleColorChange: (color: ColorsEnum) => void - colorError?: string - nameError?: string + className?: string isValue?: boolean useTranslationStore: () => TranslationStore - register: UseFormRegister - registerName: keyof CreateLabelFormFields handleDeleteValue?: () => void + selectProps?: SelectRootProps + inputProps?: InputProps } export const LabelFormColorAndNameGroup: FC = ({ - name, - colorValue, - handleColorChange, - colorError, - nameError, + className, isValue = false, useTranslationStore, - register, - registerName, - handleDeleteValue + handleDeleteValue, + selectProps, + inputProps }) => { const { t } = useTranslationStore() + const isWithDeleteButton = isValue && !!handleDeleteValue + return ( -
-
- - - {Object.values(ColorsEnum).map(color => ( - -
-
- {color} -
- - ))} - - -
+
+ + + {Object.values(ColorsEnum).map(color => ( + +
+
+ {color} +
+ + ))} + + + - {isValue && !!handleDeleteValue && ( + + {isWithDeleteButton && ( - - + +
- + + {(isAllowAddNewValue || !!values.length) && } {values.map(value => ( -
+
+ {label.selectedValueId === value.id && ( )} @@ -132,32 +132,24 @@ export const LabelValueSelector: FC = ({ ))} + {isAllowAddNewValue && !!label?.isCustom && !!values.length && } + {isAllowAddNewValue && !!label?.isCustom && ( -
- - -
- )} + - {!values.length && !label?.isCustom && ( -
- - {t('views:pullRequests.noLabels', 'No labels found')} -
+ + + + )} - {!values.length && !!label?.isCustom && !isAllowAddNewValue && ( -
- {t('views:pullRequests.addValue', 'Add new value')} -
+ {!values.length && !label?.isCustom && ( + + {t('views:pullRequests.labelNotFound', 'Label not found')} + )} diff --git a/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-header.tsx b/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-header.tsx index 0baa5348b2..66047e70bb 100644 --- a/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-header.tsx +++ b/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-header.tsx @@ -1,6 +1,7 @@ -import { useMemo, useState } from 'react' +import { useMemo, useRef, useState } from 'react' +import { LinkProps } from 'react-router-dom' -import { Button, DropdownMenu, Icon, LabelMarker, ScrollArea, SearchBox } from '@/components' +import { Button, DropdownMenu, Icon, LabelMarker, ScrollArea, SearchBox, StyledLink } from '@/components' import { useDebounceSearch } from '@/hooks' import { HandleAddLabelType, @@ -11,9 +12,18 @@ import { LabelValueType, TranslationStore } from '@/views' +import { debounce } from 'lodash-es' import { LabelValueSelector } from './label-value-selector' +const getSelectedValueId = (label?: LabelAssignmentType) => { + if (!!label && 'assigned_value' in label) { + return label.assigned_value?.id ?? undefined + } + + return undefined +} + export interface LabelsWithValueType extends ILabelType { values?: LabelValueType[] isCustom?: boolean @@ -26,17 +36,19 @@ interface LabelsHeaderProps { labelsValues: LabelValuesType selectedLabels: LabelAssignmentType[] addLabel?: (data: HandleAddLabelType) => void + editLabelsProps: LinkProps removeLabel?: (id: number) => void searchQuery?: string setSearchQuery?: (query: string) => void useTranslationStore: () => TranslationStore } -const LabelsHeader = ({ +export const LabelsHeader = ({ labelsList, labelsValues, selectedLabels, addLabel, + editLabelsProps, removeLabel, searchQuery, setSearchQuery, @@ -45,7 +57,7 @@ const LabelsHeader = ({ const { t } = useTranslationStore() const [labelWithValuesToShow, setLabelWithValuesToShow] = useState(null) - const { search, handleSearchChange } = useDebounceSearch({ + const { search, handleSearchChange, handleResetSearch } = useDebounceSearch({ handleChangeSearchValue: setSearchQuery, searchValue: searchQuery }) @@ -59,22 +71,15 @@ const LabelsHeader = ({ let res: LabelsWithValueType = { ...label, isSelected: !!selectedLabel, - selectedValueId: - !!selectedLabel && selectedLabel?.assigned_value ? selectedLabel?.assigned_value?.id || undefined : undefined + selectedValueId: getSelectedValueId(selectedLabel) } if (isCustom) { - res = { - ...res, - isCustom: true - } + res = { ...res, isCustom: true } } if (labelValues) { - res = { - ...res, - values: labelValues - } + res = { ...res, values: labelValues } } return res @@ -102,14 +107,18 @@ const LabelsHeader = ({ handleCloseValuesView() } - const handleCloseValuesView = () => { - setLabelWithValuesToShow(null) - } + const handleCloseValuesView = useRef( + debounce(() => { + setLabelWithValuesToShow(null) + handleResetSearch() + }, 300) + ).current return ( -
- {t('views:pullRequests.labels')} - +
+
{t('views:pullRequests.labels')}
+ + !isOpen && handleCloseValuesView()}> + event.preventDefault()} // Prevent focus on hidden content > - {labelWithValuesToShow ? ( + {labelWithValuesToShow && ( - ) : ( + )} + + {!labelWithValuesToShow && ( <> {!!setSearchQuery && ( <> @@ -144,28 +156,29 @@ const LabelsHeader = ({ value={search} handleChange={handleSearchChange} showOnFocus + hasSearchIcon />
)} - {labelsListWithValues.length ? ( + {!!labelsListWithValues.length && ( {labelsListWithValues?.map((label, idx) => ( -
-
- - {!!label.isCustom && } -
+
+ + {!!label?.description && ( {label.description} )} + {label.isSelected && ( )} @@ -173,19 +186,26 @@ const LabelsHeader = ({ ))} - ) : ( -
- - {t('views:pullRequests.noLabels', 'No labels found')} - -
)} + + {!labelsListWithValues.length && ( + + {t('views:pullRequests.noLabels', 'No labels found')} + + )} + + + +
+ {/* TODO: replace with StyledLink variant when its update is merged (https://github.com/harness/canary/pull/1134) */} + + {t('views:pullRequests.editLabels', 'Edit labels')} + +
)} -
+ ) } - -export { LabelsHeader } diff --git a/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-list.tsx b/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-list.tsx index 0e3e798c66..2d405b85c7 100644 --- a/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-list.tsx +++ b/packages/ui/src/views/repo/pull-request/components/labels/pull-request-labels-list.tsx @@ -1,26 +1,32 @@ import { FC } from 'react' -import { LabelMarker } from '@/components' +import { LabelMarker, LabelMarkerProps } from '@/components' import { PRListLabelType } from '@/views' import { cn } from '@utils/cn' +type LabelListLabel = PRListLabelType & Pick + interface LabelsListProps { - labels: PRListLabelType[] + labels: LabelListLabel[] className?: string } -const LabelsList: FC = ({ labels, className }) => { +export const LabelsList: FC = ({ labels, className }) => { if (!labels.length) { return No labels } return ( -
+
{labels.map(label => ( - + ))}
) } - -export { LabelsList } diff --git a/packages/ui/src/views/repo/pull-request/components/pull-request-item-description.tsx b/packages/ui/src/views/repo/pull-request/components/pull-request-item-description.tsx index e4230133de..89be46f5a1 100644 --- a/packages/ui/src/views/repo/pull-request/components/pull-request-item-description.tsx +++ b/packages/ui/src/views/repo/pull-request/components/pull-request-item-description.tsx @@ -1,7 +1,7 @@ import { FC } from 'react' import { Link, useResolvedPath } from 'react-router-dom' -import { Button, Icon, Text } from '@/components' +import { Button, Icon } from '@/components' interface PullRequestItemDescriptionProps { number: number @@ -29,7 +29,8 @@ export const PullRequestItemDescription: FC = ( return (

- {`#${number}`} opened {timestamp} by {author} + {`#${number}`} opened {timestamp} by{' '} + {author}

@@ -49,21 +50,17 @@ export const PullRequestItemDescription: FC = ( {sourceBranch && ( <> - - diff --git a/packages/ui/src/views/repo/pull-request/components/pull-request-item-title.tsx b/packages/ui/src/views/repo/pull-request/components/pull-request-item-title.tsx index 1991dfc735..32d3abc3e4 100644 --- a/packages/ui/src/views/repo/pull-request/components/pull-request-item-title.tsx +++ b/packages/ui/src/views/repo/pull-request/components/pull-request-item-title.tsx @@ -1,58 +1,45 @@ import { FC } from 'react' -import { Icon, StackedList } from '@/components' -import { PRListLabelType } from '@/views' +import { Icon } from '@/components' +import { PullRequestType } from '@/views' import { cn } from '@utils/cn' import { LabelsList } from '@views/repo/pull-request/components/labels' import { getPrState } from '../utils' -const Comments = ({ comments }: { comments: number }) => { - return ( -
- - {comments} -
- ) -} - interface PullRequestItemTitleProps { - merged?: number | null - isDraft?: boolean - state?: string - success: boolean - title: string - comments?: number - labels: PRListLabelType[] + pullRequest: PullRequestType } -export const PullRequestItemTitle: FC = ({ - success, - title, - labels, - state, - isDraft, - comments, - merged -}) => { +export const PullRequestItemTitle: FC = ({ pullRequest }) => { + const { name, labels, state, is_draft: isDraft, comments, merged } = pullRequest + const isSuccess = !!merged + return ( -
-
+
+
-

{title}

- {!!labels.length && } +

{name}

+ + {!!labels.length && }
- {!!comments && } right label secondary />} + + {!!comments && ( +
+ + {comments} +
+ )}
) } diff --git a/packages/ui/src/views/repo/pull-request/components/pull-request-list.tsx b/packages/ui/src/views/repo/pull-request/components/pull-request-list.tsx index 3e852161e3..d3b049a8a6 100644 --- a/packages/ui/src/views/repo/pull-request/components/pull-request-list.tsx +++ b/packages/ui/src/views/repo/pull-request/components/pull-request-list.tsx @@ -160,38 +160,24 @@ export const PullRequestList: FC = ({ {!!pullRequest.number && ( - <> - - ) - } - description={ - pullRequest.author && - typeof pullRequest.author === 'string' && ( - - ) - } - /> - + } + description={ + pullRequest.author && + typeof pullRequest.author === 'string' && ( + + ) + } + /> )} diff --git a/packages/ui/src/views/repo/pull-request/components/pull-request-side-bar.tsx b/packages/ui/src/views/repo/pull-request/components/pull-request-side-bar.tsx index 3ba85d95de..601ba0bfc1 100644 --- a/packages/ui/src/views/repo/pull-request/components/pull-request-side-bar.tsx +++ b/packages/ui/src/views/repo/pull-request/components/pull-request-side-bar.tsx @@ -1,4 +1,5 @@ import { FC } from 'react' +import { LinkProps } from 'react-router-dom' import { PrincipalType } from '@/types' import { @@ -38,11 +39,12 @@ export interface PullRequestSideBarProps { searchLabelQuery?: string setSearchLabelQuery?: (query: string) => void addLabel?: (data: HandleAddLabelType) => void + editLabelsProps: LinkProps removeLabel?: (id: number) => void useTranslationStore: () => TranslationStore } -const PullRequestSideBar: FC = ({ +export const PullRequestSideBar: FC = ({ usersList, reviewers = [], pullRequestMetadata, @@ -60,11 +62,12 @@ const PullRequestSideBar: FC = ({ searchLabelQuery, setSearchLabelQuery, addLabel, + editLabelsProps, removeLabel, useTranslationStore }) => { return ( -
+ <>
= ({ labelsValues={labelsValues} selectedLabels={PRLabels} addLabel={addLabel} + editLabelsProps={editLabelsProps} removeLabel={removeLabel} searchQuery={searchLabelQuery} setSearchQuery={setSearchLabelQuery} @@ -98,14 +102,13 @@ const PullRequestSideBar: FC = ({ /> ({ + onDelete: () => removeLabel?.(label.id), color: label?.assigned_value?.color || label.color, key: label.key, value: label?.assigned_value?.value || undefined }))} />
-
+ ) } - -export { PullRequestSideBar } 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 6bccd332f7..55a7987953 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 @@ -14,6 +14,7 @@ interface ToolbarItem { } export interface PullRequestCommentBoxProps { + className?: string onSaveComment: (comment: string) => void comment: string setComment: (comment: string) => void @@ -35,7 +36,8 @@ const TABS_KEYS = { } // TODO: will have to eventually implement a commenting and reply system similiar to gitness -const PullRequestCommentBox = ({ +export const PullRequestCommentBox = ({ + className, onSaveComment, currentUser, inReplyMode = false, @@ -138,7 +140,7 @@ const PullRequestCommentBox = ({ } return ( -
+
{!inReplyMode && !isEditMode && avatar}
) } - -export { PullRequestCommentBox } diff --git a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-filters.tsx b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-filters.tsx index 430973b37e..7cfaf1308d 100644 --- a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-filters.tsx +++ b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-filters.tsx @@ -1,4 +1,5 @@ import { DropdownMenu, Icon } from '@/components' +import { cn } from '@utils/cn' interface DropdownMenuComponentProps { items: T[] @@ -42,6 +43,7 @@ export interface PullRequestFilterProps { dateOrderSort: T setActivityFilter: (filter: T) => void setDateOrderSort: (sort: T) => void + className?: string } const PullRequestFilters = ({ @@ -50,11 +52,13 @@ const PullRequestFilters = ({ activityFilter, dateOrderSort, setActivityFilter, - setDateOrderSort + setDateOrderSort, + className }: PullRequestFilterProps) => { return ( -
+

Overview

+
diff --git a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-system-comments.tsx b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-system-comments.tsx index da47cc7d13..0848062fac 100644 --- a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-system-comments.tsx +++ b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-system-comments.tsx @@ -1,7 +1,7 @@ import { FC, useMemo } from 'react' import { useNavigate } from 'react-router-dom' -import { Avatar, CommitCopyActions, Icon, IconProps, LabelMarker, Layout, Text } from '@/components' +import { Avatar, CommitCopyActions, Icon, IconProps, LabelMarker, Layout } from '@/components' import { ColorsEnum, CommentItem, @@ -18,6 +18,12 @@ import { TypesPullReq } from '@views/repo/pull-request/pull-request.types' import PullRequestBranchBadge from './pull-request-branch-badge' import PullRequestTimelineItem, { TimelineItemProps } from './pull-request-timeline-item' +const labelActivityToTitleDict: Record = { + assign: 'added', + reassign: 'reassigned', + unassign: 'removed' +} + interface SystemCommentProps extends TypesPullReq { commentItems: CommentItem[] repoMetadataPath?: string @@ -35,9 +41,7 @@ const PullRequestSystemComments: FC = ({ }) => { const navigate = useNavigate() - const payloadMain = useMemo(() => { - return commentItems[0]?.payload - }, [commentItems]) + const payloadMain = useMemo(() => commentItems[0]?.payload, [commentItems]) const { header, @@ -45,10 +49,7 @@ const PullRequestSystemComments: FC = ({ }: Partial> & { header: TimelineItemProps['header'][number] } = useMemo(() => { - if (!payloadMain) - return { - header: {} - } + if (!payloadMain) return { header: {} } const handleNavigation = (url?: string) => { navigate(url || '') @@ -69,7 +70,8 @@ const PullRequestSystemComments: FC = ({ reviewer_type, label, label_color, - value + value, + value_color } = payload as GeneralPayload const openFromDraft = old_draft === true && new_draft === false @@ -80,7 +82,7 @@ const PullRequestSystemComments: FC = ({ return { header: { description: ( - + {merge_method === MergeStrategy.REBASE ? 'rebased changes from branch' : 'merged changes from'} = ({ return { header: { description: ( - <> - {openFromDraft || changedToDraft ? ( - - {changedToDraft ? 'marked pull request as draft' : 'opened pull request for review'} - - ) : ( - {`changed pull request state from ${old} to ${newData}`} - )} - + + {!!changedToDraft && 'This pull request is now a draft'} + {!!openFromDraft && 'This pull request is no longer a draft'} + {!changedToDraft && !openFromDraft && `changed pull request state from ${old} to ${newData}`} + ) }, icon: @@ -190,9 +188,9 @@ const PullRequestSystemComments: FC = ({ return { header: { description: ( - + changed title from {String(old)} to {String(newData)} - + ) }, icon: @@ -205,11 +203,11 @@ const PullRequestSystemComments: FC = ({ return { header: { description: ( - + {author?.id === mentionId ? 'removed their request for review' : `removed the request for review from ${mentionDisplayName}`} - + ) }, icon: @@ -223,13 +221,11 @@ const PullRequestSystemComments: FC = ({ return { header: { description: ( - - {reviewer_type === ReviewerAddActivity.SELF_ASSIGNED - ? 'self-requested a review' - : reviewer_type === ReviewerAddActivity.ASSIGNED - ? `assigned ${mentionDisplayName} as a reviewer` - : `requested a review from ${mentionDisplayName}`} - + + {reviewer_type === ReviewerAddActivity.SELF_ASSIGNED && 'self-requested a review'} + {reviewer_type === ReviewerAddActivity.ASSIGNED && `assigned ${mentionDisplayName} as a reviewer`} + {reviewer_type === ReviewerAddActivity.REQUESTED && `requested a review from ${mentionDisplayName}`} + ) }, icon: @@ -237,25 +233,22 @@ const PullRequestSystemComments: FC = ({ } case CommentType.LABEL_MODIFY: { - const labelType = type as LabelActivity + const labelType = payload?.type as LabelActivity return { header: { description: ( - - {labelType === LabelActivity.ASSIGN - ? 'applied' - : labelType === LabelActivity.RE_ASSIGN - ? 'reassigned' - : 'removed'}{' '} - label{' '} + + {labelType ? labelActivityToTitleDict[labelType] : 'modified'} - + label + ) }, icon: @@ -267,7 +260,7 @@ const PullRequestSystemComments: FC = ({ return { header: { - description: {String(type)} + description: {String(type)} } } } diff --git a/packages/ui/src/views/repo/pull-request/pull-request-list-page.tsx b/packages/ui/src/views/repo/pull-request/pull-request-list-page.tsx index 012b22dd36..c230ebf31a 100644 --- a/packages/ui/src/views/repo/pull-request/pull-request-list-page.tsx +++ b/packages/ui/src/views/repo/pull-request/pull-request-list-page.tsx @@ -133,9 +133,10 @@ const PullRequestList: FC = ({ {showTopBar && ( <> - -

Pull Requests

- +

+ Pull Requests +

+ + {/* NAME */} @@ -128,7 +128,7 @@ export function CreateSecretPage({ )} -
+
- ) -} - -interface DropdownMenuComponentProps { - items: T[] - selectedItem: T - onItemSelect: (item: T) => void -} - -const DropdownMenuComponent = ({ - items, - selectedItem, - onItemSelect -}: DropdownMenuComponentProps) => { - return ( - - - {}} /> - - - {items.map(item => ( - onItemSelect(item)}> - {item.label} - - ))} - - - ) -} - -interface FilterOption { - label: string - value: string -} - -export interface PullRequestFilterProps { - activityFilters: T[] - dateFilters: T[] - activityFilter: T - dateOrderSort: T - setActivityFilter: (filter: T) => void - setDateOrderSort: (sort: T) => void -} - -const PullRequestFilters = ({ - activityFilters, - dateFilters, - activityFilter, - dateOrderSort, - setActivityFilter, - setDateOrderSort -}: PullRequestFilterProps) => { - return ( -
- - Overview - -
- - -
-
- ) -} - -export { PullRequestFilters } diff --git a/packages/views/src/components/pull-request/pull-request-list.tsx b/packages/views/src/components/pull-request/pull-request-list.tsx deleted file mode 100644 index 04bfc46d4d..0000000000 --- a/packages/views/src/components/pull-request/pull-request-list.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { useMemo, useState } from 'react' - -import cx from 'classnames' - -import { Badge, cn, Icon, StackedList, Text } from '@harnessio/canary' - -import { IconType } from './interfaces' -import { getPrState } from './utils' - -interface PullRequestProps { - is_draft?: boolean - merged: number | null | undefined // TODO: Should merged really be all these?? - name: string | undefined - number?: number - sha?: string - author?: string - reviewRequired: boolean - tasks?: number - source_branch?: string - timestamp: string - comments?: number - state?: string - labels?: { - text: string - color: string - }[] -} - -interface PageProps { - pullRequests?: PullRequestProps[] - LinkComponent: React.ComponentType<{ to: string; children: React.ReactNode }> - closed_prs?: number - open_prs?: number -} - -const HeaderTitle = ({ - setHeaderFilter, - headerFilter, - closed_prs, - open_prs -}: { - setHeaderFilter: (state: string) => void - headerFilter: string - closed_prs?: number - open_prs?: number -}) => { - return ( -
-
setHeaderFilter('open')} - className={cx('flex items-center gap-2', { - 'text-white': headerFilter === 'open', - 'text-tertiary-background': headerFilter === 'closed' - })} - > - - - {open_prs} Open - -
-
setHeaderFilter('closed')} - className={cx('flex items-center gap-2', { - 'text-white': headerFilter === 'closed', - 'text-tertiary-background': headerFilter === 'open' - })} - > - - - {closed_prs} Closed - -
-
- ) -} - -const colorMapping: { [key: string]: { border: string; text: string; bg: string } } = { - mint: { border: 'border-emerald-400/20', text: 'text-emerald-300', bg: 'bg-emerald-400/10' }, - yellow: { border: 'border-orange-400/20', text: 'text-orange-400', bg: 'bg-orange-400/10' }, - red: { border: 'border-red-400/20', text: 'text-red-400', bg: 'bg-red-400/10' }, - blue: { border: 'border-blue-400/20', text: 'text-blue-300', bg: 'bg-blue-400/10' }, - purple: { border: 'border-purple-400/20', text: 'text-purple-300', bg: 'bg-purple-400/10' } -} - -const Title = ({ - success, - title, - labels, - state, - isDraft, - comments, - merged -}: { - merged?: number | null - isDraft?: boolean - state?: string - success: boolean - title: string - comments?: number - labels: { text: string; color: string }[] -}) => { - return ( -
-
- - - - {title} - - - {labels && - labels.map((l, l_idx) => { - const { border, text, bg } = colorMapping[l.color] || { border: '', text: '' } - return ( - -

{l.text}

-
- ) - })} -
- {comments && } right label secondary />} -
- ) -} - -const Description = ({ - reviewRequired, - number, - // tasks, - author, - source_branch, - timestamp -}: { - number: number - reviewRequired: boolean - tasks?: number - author: string - source_branch: string - timestamp: string -}) => { - return ( -
- {number &&
#{number}
} - {author && timestamp && ( - - opened {timestamp} by {author} - - )} - {typeof reviewRequired === 'boolean' && ( - - {reviewRequired ? 'Review required' : 'Draft'} - - )} - {/* TODO: where did tasks go?} - {/* {typeof tasks !== 'undefined' && tasks > 0 && ( -
- - - {tasks} task{tasks === 1 ? '' : 's'} - -
- )} */} - {source_branch && ( -
- - - {source_branch} - -
- )} -
- ) -} - -const Comments = ({ comments }: { comments: number }) => { - return ( -
- - - {comments} - -
- ) -} - -export function PullRequestList({ pullRequests, LinkComponent, open_prs, closed_prs }: PageProps) { - const [headerFilter, setHeaderFilter] = useState('open') - const filteredData = useMemo( - () => - pullRequests?.filter(pr => { - if (headerFilter === 'open') return pr.state === 'open' - if (headerFilter === 'closed') return pr.state !== 'open' || pr.merged !== null - return true - }), - [headerFilter] - ) - return ( - <> - {filteredData && filteredData.length > 0 && ( - - - - } - > - - {filteredData?.map((pullRequest, pullRequest_idx) => ( - - - {pullRequest.number && ( - <> - - ) - } - description={ - pullRequest.author && ( - - ) - } - /> - - )} - - - ))} - - )} - - ) -} diff --git a/packages/views/src/components/pull-request/pull-request-side-bar.tsx b/packages/views/src/components/pull-request/pull-request-side-bar.tsx deleted file mode 100644 index fe9b8e5bf2..0000000000 --- a/packages/views/src/components/pull-request/pull-request-side-bar.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { EnumPullReqReviewDecision, PullReqReviewDecision } from './interfaces' -import ReviewersHeader from './pull-request-reviewers-header' -import ReviewersList from './pull-request-reviewers-list' - -interface PullRequestSideBarProps { - reviewers?: { - reviewer?: { display_name?: string; id?: number } - review_decision?: EnumPullReqReviewDecision - sha?: string - }[] - processReviewDecision: ( - review_decision: EnumPullReqReviewDecision, - reviewedSHA?: string, - sourceSHA?: string - ) => EnumPullReqReviewDecision | PullReqReviewDecision.outdated - pullRequestMetadata?: { source_sha: string } - refetchReviewers: () => void - handleDelete: (id: number) => void - addReviewers?: (id?: number) => void - usersList?: { display_name?: string; id?: number; uid?: string }[] - currentUserId?: string -} - -const PullRequestSideBar = (props: PullRequestSideBarProps) => { - const { - usersList, - reviewers = [], - pullRequestMetadata, - processReviewDecision, - handleDelete, - addReviewers, - currentUserId - } = props - - return ( -
-
- - -
-
- ) -} - -export { PullRequestSideBar } diff --git a/packages/views/src/index.ts b/packages/views/src/index.ts index 3f7ed50fe9..39a76315a0 100644 --- a/packages/views/src/index.ts +++ b/packages/views/src/index.ts @@ -14,14 +14,11 @@ export * from './components/repo-clone/clone-repo-dialog' export * from './components/repo-import-form-component' export * from './components/no-data' -export * from './components/pull-request/pull-request-list' export * from './components/pull-request/pull-request-conversation-header' export * from './components/pull-request/pull-request-commits' export * from './components/pull-request/pull-request-overview' export * from './components/pull-request/pull-request-panel' export * from './components/pull-request/pull-request-comment-box' -export * from './components/pull-request/pull-request-filters' -export * from './components/pull-request/pull-request-side-bar' export * from './components/pull-request/pull-request-changes' export * from './components/pull-request/interfaces-store'