diff options
Diffstat (limited to 'app/assets/javascripts/crm/organizations/components/organizations_root.vue')
-rw-r--r-- | app/assets/javascripts/crm/organizations/components/organizations_root.vue | 225 |
1 files changed, 168 insertions, 57 deletions
diff --git a/app/assets/javascripts/crm/organizations/components/organizations_root.vue b/app/assets/javascripts/crm/organizations/components/organizations_root.vue index a165dd68603..155c8f00537 100644 --- a/app/assets/javascripts/crm/organizations/components/organizations_root.vue +++ b/app/assets/javascripts/crm/organizations/components/organizations_root.vue @@ -1,36 +1,54 @@ <script> -import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui'; import { s__, __ } from '~/locale'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants'; +import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue'; +import { + bodyTrClass, + initialPaginationState, +} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants'; +import { convertToSnakeCase } from '~/lib/utils/text_utility'; +import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME, organizationTrackViewsOptions } from '../../constants'; import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql'; +import getGroupOrganizationsCountByStateQuery from './graphql/get_group_organizations_count_by_state.query.graphql'; export default { components: { - GlAlert, GlButton, GlLoadingIcon, GlTable, + PaginatedTableWithSearchAndTabs, }, directives: { GlTooltip: GlTooltipDirective, }, - inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath'], + inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath', 'textQuery'], data() { return { - organizations: [], + organizations: { list: [] }, + organizationsCount: {}, error: false, + filteredByStatus: '', + pagination: initialPaginationState, + statusFilter: 'all', + searchTerm: this.textQuery, + sort: 'NAME_ASC', + sortDesc: false, }; }, apollo: { organizations: { - query() { - return getGroupOrganizationsQuery; - }, + query: getGroupOrganizationsQuery, variables() { return { groupFullPath: this.groupFullPath, + searchTerm: this.searchTerm, + state: this.statusFilter, + sort: this.sort, + firstPageSize: this.pagination.firstPageSize, + lastPageSize: this.pagination.lastPageSize, + prevPageCursor: this.pagination.prevPageCursor, + nextPageCursor: this.pagination.nextPageCursor, }; }, update(data) { @@ -40,19 +58,52 @@ export default { this.error = true; }, }, + organizationsCount: { + query: getGroupOrganizationsCountByStateQuery, + variables() { + return { + groupFullPath: this.groupFullPath, + searchTerm: this.searchTerm, + }; + }, + update(data) { + return data?.group?.organizationStateCounts; + }, + error() { + this.error = true; + }, + }, }, computed: { isLoading() { return this.$apollo.queries.organizations.loading; }, - canAdmin() { - return parseBoolean(this.canAdminCrmOrganization); + tbodyTrClass() { + return { + [bodyTrClass]: !this.loading && !this.isEmpty, + }; }, }, methods: { + errorAlertDismissed() { + this.error = false; + }, extractOrganizations(data) { const organizations = data?.group?.organizations?.nodes || []; - return organizations.slice().sort((a, b) => a.name.localeCompare(b.name)); + const pageInfo = data?.group?.organizations?.pageInfo || {}; + return { + list: organizations, + pageInfo, + }; + }, + fetchSortedData({ sortBy, sortDesc }) { + const sortingColumn = convertToSnakeCase(sortBy).toUpperCase(); + const sortingDirection = sortDesc ? 'DESC' : 'ASC'; + this.pagination = initialPaginationState; + this.sort = `${sortingColumn}_${sortingDirection}`; + }, + filtersChanged({ searchTerm }) { + this.searchTerm = searchTerm; }, getIssuesPath(path, value) { return `${path}?crm_organization_id=${value}`; @@ -60,6 +111,13 @@ export default { getEditRoute(id) { return { name: this.$options.EDIT_ROUTE_NAME, params: { id } }; }, + pageChanged(pagination) { + this.pagination = pagination; + }, + statusChanged({ filters, status }) { + this.statusFilter = filters; + this.filteredByStatus = status; + }, }, fields: [ { key: 'name', sortable: true }, @@ -83,60 +141,113 @@ export default { }, EDIT_ROUTE_NAME, NEW_ROUTE_NAME, + statusTabs: [ + { + title: __('Active'), + status: 'ACTIVE', + filters: 'active', + }, + { + title: __('Inactive'), + status: 'INACTIVE', + filters: 'inactive', + }, + { + title: __('All'), + status: 'ALL', + filters: 'all', + }, + ], + organizationTrackViewsOptions, + emptyArray: [], }; </script> <template> <div> - <gl-alert v-if="error" variant="danger" class="gl-mt-6" @dismiss="error = false"> - {{ $options.i18n.errorText }} - </gl-alert> - <div - class="gl-display-flex gl-align-items-baseline gl-flex-direction-row gl-justify-content-space-between gl-mt-6" + <paginated-table-with-search-and-tabs + :show-items="true" + :show-error-msg="false" + :i18n="$options.i18n" + :items="organizations.list" + :page-info="organizations.pageInfo" + :items-count="organizationsCount" + :status-tabs="$options.statusTabs" + :track-views-options="$options.organizationTrackViewsOptions" + :filter-search-tokens="$options.emptyArray" + filter-search-key="organizations" + @page-changed="pageChanged" + @tabs-changed="statusChanged" + @filters-changed="filtersChanged" + @error-alert-dismissed="errorAlertDismissed" > - <h2 class="gl-font-size-h2 gl-my-0"> - {{ $options.i18n.title }} - </h2> - <div - v-if="canAdmin" - class="gl-display-none gl-md-display-flex gl-align-items-center gl-justify-content-end" - > - <router-link :to="{ name: $options.NEW_ROUTE_NAME }"> - <gl-button variant="confirm" data-testid="new-organization-button"> + <template #header-actions> + <router-link v-if="canAdminCrmOrganization" :to="{ name: $options.NEW_ROUTE_NAME }"> + <gl-button + class="gl-my-3 gl-mr-5" + variant="confirm" + data-testid="new-organization-button" + > {{ $options.i18n.newOrganization }} </gl-button> </router-link> - </div> - </div> - <router-view /> - <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" /> - <gl-table - v-else - class="gl-mt-5" - :items="organizations" - :fields="$options.fields" - :empty-text="$options.i18n.emptyText" - show-empty - > - <template #cell(id)="{ value: id }"> - <gl-button - v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel" - class="gl-mr-3" - data-testid="issues-link" - icon="issues" - :aria-label="$options.i18n.issuesButtonLabel" - :href="getIssuesPath(groupIssuesPath, id)" - /> - <router-link :to="getEditRoute(id)"> - <gl-button - v-if="canAdmin" - v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel" - data-testid="edit-organization-button" - icon="pencil" - :aria-label="$options.i18n.editButtonLabel" - /> - </router-link> </template> - </gl-table> + + <template #title> + {{ $options.i18n.title }} + </template> + + <template #table> + <gl-table + :items="organizations.list" + :fields="$options.fields" + :busy="isLoading" + stacked="md" + :tbody-tr-class="tbodyTrClass" + sort-direction="asc" + :sort-desc.sync="sortDesc" + sort-by="createdAt" + show-empty + no-local-sorting + sort-icon-left + fixed + @sort-changed="fetchSortedData" + > + <template #cell(id)="{ value: id }"> + <gl-button + v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel" + class="gl-mr-3" + data-testid="issues-link" + icon="issues" + :aria-label="$options.i18n.issuesButtonLabel" + :href="getIssuesPath(groupIssuesPath, id)" + /> + <router-link :to="getEditRoute(id)"> + <gl-button + v-if="canAdminCrmOrganization" + v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel" + data-testid="edit-organization-button" + icon="pencil" + :aria-label="$options.i18n.editButtonLabel" + /> + </router-link> + </template> + + <template #table-busy> + <gl-loading-icon size="lg" color="dark" class="mt-3" /> + </template> + + <template #empty> + <span v-if="error"> + {{ $options.i18n.errorText }} + </span> + <span v-else> + {{ $options.i18n.emptyText }} + </span> + </template> + </gl-table> + </template> + </paginated-table-with-search-and-tabs> + <router-view /> </div> </template> |