diff --git a/README.md b/README.md
index b0400317536..b92c6dc5b7c 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
-- Ruby (MRI) 3.1.4
+- Ruby (MRI) 3.2.5
- Git 2.33+
- Redis 6.0+
- PostgreSQL 14.9+
diff --git a/app/assets/javascripts/behaviors/date_picker.js b/app/assets/javascripts/behaviors/date_picker.js
index 89f1ad9c89e..f49a27ba16a 100644
--- a/app/assets/javascripts/behaviors/date_picker.js
+++ b/app/assets/javascripts/behaviors/date_picker.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import Pikaday from 'pikaday';
-import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
+import { parsePikadayDate, toISODateFormat } from '~/lib/utils/datetime_utility';
export default function initDatePickers() {
$('.datepicker').each(function initPikaday() {
@@ -13,7 +13,7 @@ export default function initDatePickers() {
format: 'yyyy-mm-dd',
container: $datePicker.parent().get(0),
parse: (dateString) => parsePikadayDate(dateString),
- toString: (date) => pikadayToString(date),
+ toString: (date) => toISODateFormat(date),
onSelect(dateText) {
$datePicker.val(calendar.toString(dateText));
},
diff --git a/app/assets/javascripts/graphql_shared/constants.js b/app/assets/javascripts/graphql_shared/constants.js
index a081e29d3ea..fe3f94f6fde 100644
--- a/app/assets/javascripts/graphql_shared/constants.js
+++ b/app/assets/javascripts/graphql_shared/constants.js
@@ -35,3 +35,6 @@ export const TYPE_ORGANIZATION = 'Organizations::Organization';
export const TYPE_USERS_SAVED_REPLY = 'Users::SavedReply';
export const TYPE_WORKSPACE = 'RemoteDevelopment::Workspace';
export const TYPE_COMPLIANCE_FRAMEWORK = 'ComplianceManagement::Framework';
+
+export const QUERY_PARAM_START_CURSOR = 'start_cursor';
+export const QUERY_PARAM_END_CURSOR = 'end_cursor';
diff --git a/app/assets/javascripts/graphql_shared/utils.js b/app/assets/javascripts/graphql_shared/utils.js
index 828a18a240e..fb7f5124e6d 100644
--- a/app/assets/javascripts/graphql_shared/utils.js
+++ b/app/assets/javascripts/graphql_shared/utils.js
@@ -1,5 +1,6 @@
import { isArray } from 'lodash';
import Visibility from 'visibilityjs';
+import { QUERY_PARAM_START_CURSOR, QUERY_PARAM_END_CURSOR } from './constants';
/**
* Ids generated by GraphQL endpoints are usually in the format
@@ -162,3 +163,25 @@ export const etagQueryHeaders = (featureCorrelation, etagResource = '') => {
},
};
};
+
+export const calculateGraphQLPaginationQueryParams = ({
+ startCursor,
+ endCursor,
+ routeQuery: { start_cursor, end_cursor, ...routeQuery },
+}) => {
+ if (startCursor) {
+ return {
+ ...routeQuery,
+ [QUERY_PARAM_START_CURSOR]: startCursor,
+ };
+ }
+
+ if (endCursor) {
+ return {
+ ...routeQuery,
+ [QUERY_PARAM_END_CURSOR]: endCursor,
+ };
+ }
+
+ return routeQuery;
+};
diff --git a/app/assets/javascripts/issuable/issuable_form.js b/app/assets/javascripts/issuable/issuable_form.js
index 329b4c2781b..30edfa966f8 100644
--- a/app/assets/javascripts/issuable/issuable_form.js
+++ b/app/assets/javascripts/issuable/issuable_form.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import Pikaday from 'pikaday';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import Autosave from '~/autosave';
-import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
+import { parsePikadayDate, toISODateFormat } from '~/lib/utils/datetime_utility';
import { queryToObject, objectToQuery } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select';
import ZenMode from '~/zen_mode';
@@ -114,7 +114,7 @@ export default class IssuableForm {
format: 'yyyy-mm-dd',
container: $issuableDueDate.parent().get(0),
parse: (dateString) => parsePikadayDate(dateString),
- toString: (date) => pikadayToString(date),
+ toString: (date) => toISODateFormat(date),
onSelect: (dateText) => {
$issuableDueDate.val(calendar.toString(dateText));
if (this.autosaves.has('due_date')) this.autosaves.get('due_date').save();
diff --git a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
index 6e714025d80..2b3cdcb661e 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
@@ -33,7 +33,7 @@ export const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
* for UTC-8 timezone.
*
* @param {string|number|Date} date
- * @returns {Date|null|undefined}
+ * @returns {Date|null|undefined} A Date object in local time
*/
export const newDate = (date) => {
if (date === null) {
@@ -42,6 +42,7 @@ export const newDate = (date) => {
if (date === undefined) {
return undefined;
}
+ // Fix historical bug so we return a local time for `yyyy-mm-dd` date-only strings
if (typeof date === 'string' && DATE_ONLY_REGEX.test(date)) {
const parts = date.split('-');
const year = parseInt(parts[0], 10);
@@ -545,48 +546,6 @@ export const dateAtFirstDayOfMonth = (date) => new Date(cloneDate(date).setDate(
*/
export const datesMatch = (date1, date2) => differenceInMilliseconds(date1, date2) === 0;
-/**
- * A utility function which checks if two date ranges overlap.
- *
- * @param {Object} givenPeriodLeft - the first period to compare.
- * @param {Object} givenPeriodRight - the second period to compare.
- * @returns {Object} { overlap: number of days the overlap is present, overlapStartDate: the start date of the overlap in time format, overlapEndDate: the end date of the overlap in time format }
- * @throws {Error} Uncaught Error: Invalid period
- *
- * @example
- * getOverlappingDaysInPeriods(
- * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 13) },
- * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 14) }
- * ) => { daysOverlap: 2, overlapStartDate: 1610323200000, overlapEndDate: 1610496000000 }
- *
- */
-export const getOverlappingDaysInPeriods = (givenPeriodLeft = {}, givenPeriodRight = {}) => {
- const leftStartTime = new Date(givenPeriodLeft.start).getTime();
- const leftEndTime = new Date(givenPeriodLeft.end).getTime();
- const rightStartTime = new Date(givenPeriodRight.start).getTime();
- const rightEndTime = new Date(givenPeriodRight.end).getTime();
-
- if (!(leftStartTime <= leftEndTime && rightStartTime <= rightEndTime)) {
- throw new Error(__('Invalid period'));
- }
-
- const isOverlapping = leftStartTime < rightEndTime && rightStartTime < leftEndTime;
-
- if (!isOverlapping) {
- return { daysOverlap: 0 };
- }
-
- const overlapStartDate = Math.max(leftStartTime, rightStartTime);
- const overlapEndDate = rightEndTime > leftEndTime ? leftEndTime : rightEndTime;
- const differenceInMs = overlapEndDate - overlapStartDate;
-
- return {
- daysOverlap: Math.ceil(differenceInMs / MILLISECONDS_IN_DAY),
- overlapStartDate,
- overlapEndDate,
- };
-};
-
/**
* Mimics the behaviour of the rails distance_of_time_in_words function
* https://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-distance_of_time_in_words
@@ -634,21 +593,6 @@ export const approximateDuration = (seconds = 0) => {
return n__('1 day', '%d days', seconds < ONE_DAY_LIMIT ? 1 : days);
};
-/**
- * A utility function which helps creating a date object
- * for a specific date. Accepts the year, month and day
- * returning a date object for the given params.
- *
- * @param {Int} year the full year as a number i.e. 2020
- * @param {Int} month the month index i.e. January => 0
- * @param {Int} day the day as a number i.e. 23
- *
- * @return {Date} the date object from the params
- */
-export const dateFromParams = (year, month, day) => {
- return new Date(year, month, day);
-};
-
/**
* A utility function which computes a formatted 24 hour
* time string from a positive int in the range 0 - 24.
diff --git a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
index 3236a2c46d7..0e1ba0ef135 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
@@ -357,6 +357,19 @@ export const timeToHoursMinutes = (time = '') => {
return { hours, minutes };
};
+/**
+ * Converts a Date object to a date-only string in the ISO format `yyyy-mm-dd`
+ *
+ * @param {Date} date A Date object
+ * @returns {string} A string in the format `yyyy-mm-dd`
+ */
+export const toISODateFormat = (date) => {
+ const day = padWithZeros(date.getDate());
+ const month = padWithZeros(date.getMonth() + 1);
+ const year = date.getFullYear();
+ return `${year}-${month}-${day}`;
+};
+
/**
* This combines a date and a time and returns the computed Date's ISO string representation.
*
@@ -516,3 +529,15 @@ export const humanTimeframe = (startDate, dueDate) => {
}
return '';
};
+
+/**
+ * Formats seconds into a human readable value of elapsed time,
+ * optionally limiting it to hours.
+ * @param {Number} seconds Seconds to format
+ * @param {Boolean} limitToHours Whether or not to limit the elapsed time to be expressed in hours
+ * @return {String} Provided seconds in human readable elapsed time format
+ */
+export const formatTimeSpent = (seconds, limitToHours) => {
+ const negative = seconds < 0;
+ return (negative ? '- ' : '') + stringifyTime(parseSeconds(seconds, { limitToHours }));
+};
diff --git a/app/assets/javascripts/lib/utils/datetime/pikaday_utility.js b/app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
index 63542ddbb6a..4bd7b4199a1 100644
--- a/app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/pikaday_utility.js
@@ -1,5 +1,3 @@
-export const pad = (val, len = 2) => `0${val}`.slice(-len);
-
/**
* Formats dates in Pickaday
* @param {String} dateString Date in yyyy-mm-dd format
@@ -13,16 +11,3 @@ export const parsePikadayDate = (dateString) => {
return new Date(year, month, day);
};
-
-/**
- * Used `onSelect` method in pickaday
- * @param {Date} date UTC format
- * @return {String} Date formatted in yyyy-mm-dd
- */
-export const pikadayToString = (date) => {
- const day = pad(date.getDate());
- const month = pad(date.getMonth() + 1);
- const year = date.getFullYear();
-
- return `${year}-${month}-${day}`;
-};
diff --git a/app/assets/javascripts/lib/utils/datetime/time_spent_utility.js b/app/assets/javascripts/lib/utils/datetime/time_spent_utility.js
deleted file mode 100644
index 64c77bf1080..00000000000
--- a/app/assets/javascripts/lib/utils/datetime/time_spent_utility.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { stringifyTime, parseSeconds } from './date_format_utility';
-
-/**
- * Formats seconds into a human readable value of elapsed time,
- * optionally limiting it to hours.
- * @param {Number} seconds Seconds to format
- * @param {Boolean} limitToHours Whether or not to limit the elapsed time to be expressed in hours
- * @return {String} Provided seconds in human readable elapsed time format
- */
-export const formatTimeSpent = (seconds, limitToHours) => {
- const negative = seconds < 0;
- return (negative ? '- ' : '') + stringifyTime(parseSeconds(seconds, { limitToHours }));
-};
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 061ce96407e..cebac94b98f 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -2,5 +2,4 @@ export * from './datetime/timeago_utility';
export * from './datetime/date_format_utility';
export * from './datetime/date_calculation_utility';
export * from './datetime/pikaday_utility';
-export * from './datetime/time_spent_utility';
export * from './datetime/locale_dateformat';
diff --git a/app/assets/javascripts/organizations/groups_and_projects/components/app.vue b/app/assets/javascripts/organizations/groups_and_projects/components/app.vue
index e2f3f7a26ad..8beb6db1b5c 100644
--- a/app/assets/javascripts/organizations/groups_and_projects/components/app.vue
+++ b/app/assets/javascripts/organizations/groups_and_projects/components/app.vue
@@ -6,16 +6,15 @@ import GroupsView from '~/organizations/shared/components/groups_view.vue';
import ProjectsView from '~/organizations/shared/components/projects_view.vue';
import NewGroupButton from '~/organizations/shared/components/new_group_button.vue';
import NewProjectButton from '~/organizations/shared/components/new_project_button.vue';
-import { onPageChange } from '~/organizations/shared/utils';
+import { calculateGraphQLPaginationQueryParams } from '~/graphql_shared/utils';
import {
RESOURCE_TYPE_GROUPS,
RESOURCE_TYPE_PROJECTS,
- QUERY_PARAM_END_CURSOR,
- QUERY_PARAM_START_CURSOR,
SORT_DIRECTION_ASC,
SORT_DIRECTION_DESC,
SORT_ITEM_NAME,
} from '~/organizations/shared/constants';
+import { QUERY_PARAM_END_CURSOR, QUERY_PARAM_START_CURSOR } from '~/graphql_shared/constants';
import FilteredSearchAndSort from '~/groups_projects/components/filtered_search_and_sort.vue';
import {
RECENT_SEARCHES_STORAGE_KEY_GROUPS,
@@ -172,7 +171,9 @@ export default {
});
},
onPageChange(pagination) {
- this.pushQuery(onPageChange({ ...pagination, routeQuery: this.$route.query }));
+ this.pushQuery(
+ calculateGraphQLPaginationQueryParams({ ...pagination, routeQuery: this.$route.query }),
+ );
},
async userPreferencesUpdateMutate(input) {
try {
diff --git a/app/assets/javascripts/organizations/shared/constants.js b/app/assets/javascripts/organizations/shared/constants.js
index 7444ca70d90..8e871a8325d 100644
--- a/app/assets/javascripts/organizations/shared/constants.js
+++ b/app/assets/javascripts/organizations/shared/constants.js
@@ -30,9 +30,6 @@ export const FORM_FIELD_DESCRIPTION_VALIDATORS = [
),
];
-export const QUERY_PARAM_START_CURSOR = 'start_cursor';
-export const QUERY_PARAM_END_CURSOR = 'end_cursor';
-
export const SORT_DIRECTION_ASC = 'asc';
export const SORT_DIRECTION_DESC = 'desc';
diff --git a/app/assets/javascripts/organizations/shared/utils.js b/app/assets/javascripts/organizations/shared/utils.js
index f128c6bc336..220739492f8 100644
--- a/app/assets/javascripts/organizations/shared/utils.js
+++ b/app/assets/javascripts/organizations/shared/utils.js
@@ -4,12 +4,7 @@ import {
TIMESTAMP_TYPE_CREATED_AT,
TIMESTAMP_TYPE_UPDATED_AT,
} from '~/vue_shared/components/resource_lists/constants';
-import {
- SORT_CREATED_AT,
- SORT_UPDATED_AT,
- QUERY_PARAM_END_CURSOR,
- QUERY_PARAM_START_CURSOR,
-} from './constants';
+import { SORT_CREATED_AT, SORT_UPDATED_AT } from './constants';
const availableGroupActions = (userPermissions) => {
const baseActions = [];
@@ -52,28 +47,6 @@ export const formatGroups = (groups) =>
}),
);
-export const onPageChange = ({
- startCursor,
- endCursor,
- routeQuery: { start_cursor, end_cursor, ...routeQuery },
-}) => {
- if (startCursor) {
- return {
- ...routeQuery,
- [QUERY_PARAM_START_CURSOR]: startCursor,
- };
- }
-
- if (endCursor) {
- return {
- ...routeQuery,
- [QUERY_PARAM_END_CURSOR]: endCursor,
- };
- }
-
- return routeQuery;
-};
-
export const timestampType = (sortName) => {
const SORT_MAP = {
[SORT_CREATED_AT]: TIMESTAMP_TYPE_CREATED_AT,
diff --git a/app/assets/javascripts/organizations/show/components/groups_and_projects.vue b/app/assets/javascripts/organizations/show/components/groups_and_projects.vue
index 4b734d090a0..59a85808a84 100644
--- a/app/assets/javascripts/organizations/show/components/groups_and_projects.vue
+++ b/app/assets/javascripts/organizations/show/components/groups_and_projects.vue
@@ -4,16 +4,15 @@ import { isEqual } from 'lodash';
import { s__, __ } from '~/locale';
import GroupsView from '~/organizations/shared/components/groups_view.vue';
import ProjectsView from '~/organizations/shared/components/projects_view.vue';
-import { onPageChange } from '~/organizations/shared/utils';
+import { calculateGraphQLPaginationQueryParams } from '~/graphql_shared/utils';
import {
RESOURCE_TYPE_GROUPS,
RESOURCE_TYPE_PROJECTS,
- QUERY_PARAM_END_CURSOR,
- QUERY_PARAM_START_CURSOR,
SORT_CREATED_AT,
SORT_UPDATED_AT,
SORT_DIRECTION_DESC,
} from '~/organizations/shared/constants';
+import { QUERY_PARAM_END_CURSOR, QUERY_PARAM_START_CURSOR } from '~/graphql_shared/constants';
import { GROUPS_AND_PROJECTS_PER_PAGE } from '../constants';
import { buildDisplayListboxItem } from '../utils';
@@ -108,7 +107,9 @@ export default {
this.pushQuery({ display });
},
onPageChange(pagination) {
- this.pushQuery(onPageChange({ ...pagination, routeQuery: this.$route.query }));
+ this.pushQuery(
+ calculateGraphQLPaginationQueryParams({ ...pagination, routeQuery: this.$route.query }),
+ );
},
},
};
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index aab603c6769..8b23c36638b 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -7,7 +7,7 @@ import {
getDayName,
getDayDifference,
localeDateFormat,
- pikadayToString,
+ toISODateFormat,
newDate,
} from '~/lib/utils/datetime_utility';
import { n__, s__, __ } from '~/locale';
@@ -117,7 +117,7 @@ export default class ActivityCalendar {
date.setDate(date.getDate() + i);
const day = date.getDay();
- const count = timestamps[pikadayToString(date)] || 0;
+ const count = timestamps[toISODateFormat(date)] || 0;
// Create a new group array if this is the first day of the week
// or if is first object
diff --git a/app/assets/javascripts/projects/your_work/components/app.vue b/app/assets/javascripts/projects/your_work/components/app.vue
index 58961ec5e7e..343ce808824 100644
--- a/app/assets/javascripts/projects/your_work/components/app.vue
+++ b/app/assets/javascripts/projects/your_work/components/app.vue
@@ -3,9 +3,11 @@ import { GlTabs, GlTab, GlBadge, GlFilteredSearchToken } from '@gitlab/ui';
import { isEqual } from 'lodash';
import { __ } from '~/locale';
import { TIMESTAMP_TYPE_UPDATED_AT } from '~/vue_shared/components/resource_lists/constants';
+import { QUERY_PARAM_END_CURSOR, QUERY_PARAM_START_CURSOR } from '~/graphql_shared/constants';
import { numberToMetricPrefix } from '~/lib/utils/number_utils';
import { createAlert } from '~/alert';
import FilteredSearchAndSort from '~/groups_projects/components/filtered_search_and_sort.vue';
+import { calculateGraphQLPaginationQueryParams } from '~/graphql_shared/utils';
import { RECENT_SEARCHES_STORAGE_KEY_PROJECTS } from '~/filtered_search/recent_searches_storage_keys';
import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import { ACCESS_LEVEL_OWNER_INTEGER } from '~/access_level/constants';
@@ -133,6 +135,12 @@ export default {
isAscending() {
return this.sort.endsWith(SORT_DIRECTION_ASC);
},
+ startCursor() {
+ return this.$route.query[QUERY_PARAM_START_CURSOR];
+ },
+ endCursor() {
+ return this.$route.query[QUERY_PARAM_END_CURSOR];
+ },
},
methods: {
numberToMetricPrefix,
@@ -182,6 +190,11 @@ export default {
this.pushQuery({ sort, ...filters });
},
+ onPageChange(pagination) {
+ this.pushQuery(
+ calculateGraphQLPaginationQueryParams({ ...pagination, routeQuery: this.$route.query }),
+ );
+ },
},
};
@@ -201,7 +214,13 @@ export default {
-
+
{{ tab.text }}
diff --git a/app/assets/javascripts/projects/your_work/components/tab_view.vue b/app/assets/javascripts/projects/your_work/components/tab_view.vue
index ab44133e532..18bbfeda653 100644
--- a/app/assets/javascripts/projects/your_work/components/tab_view.vue
+++ b/app/assets/javascripts/projects/your_work/components/tab_view.vue
@@ -1,7 +1,8 @@
-
+
diff --git a/app/assets/javascripts/projects/your_work/graphql/queries/contributed_projects.query.graphql b/app/assets/javascripts/projects/your_work/graphql/queries/contributed_projects.query.graphql
index 29588e0948a..161c6da3457 100644
--- a/app/assets/javascripts/projects/your_work/graphql/queries/contributed_projects.query.graphql
+++ b/app/assets/javascripts/projects/your_work/graphql/queries/contributed_projects.query.graphql
@@ -1,12 +1,16 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
-query getContributedProjects {
+query getContributedProjects($first: Int, $last: Int, $before: String, $after: String) {
currentUser {
id
- contributedProjects {
+ contributedProjects(first: $first, last: $last, before: $before, after: $after) {
nodes {
...Project
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
}
diff --git a/app/assets/javascripts/projects/your_work/graphql/queries/inactive_projects.query.graphql b/app/assets/javascripts/projects/your_work/graphql/queries/inactive_projects.query.graphql
index 742ab50e4d3..99d4cb30186 100644
--- a/app/assets/javascripts/projects/your_work/graphql/queries/inactive_projects.query.graphql
+++ b/app/assets/javascripts/projects/your_work/graphql/queries/inactive_projects.query.graphql
@@ -1,9 +1,20 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
-query getInactiveProjects {
- projects(archived: ONLY, membership: true) {
+query getInactiveProjects($first: Int, $last: Int, $before: String, $after: String) {
+ projects(
+ archived: ONLY
+ membership: true
+ first: $first
+ last: $last
+ before: $before
+ after: $after
+ ) {
nodes {
...Project
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
diff --git a/app/assets/javascripts/projects/your_work/graphql/queries/membership_projects.query.graphql b/app/assets/javascripts/projects/your_work/graphql/queries/membership_projects.query.graphql
index e0b127d312e..f18ab0212b6 100644
--- a/app/assets/javascripts/projects/your_work/graphql/queries/membership_projects.query.graphql
+++ b/app/assets/javascripts/projects/your_work/graphql/queries/membership_projects.query.graphql
@@ -1,9 +1,13 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
-query getMembershipProjects {
- projects(membership: true) {
+query getMembershipProjects($first: Int, $last: Int, $before: String, $after: String) {
+ projects(membership: true, first: $first, last: $last, before: $before, after: $after) {
nodes {
...Project
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
diff --git a/app/assets/javascripts/projects/your_work/graphql/queries/personal_projects.query.graphql b/app/assets/javascripts/projects/your_work/graphql/queries/personal_projects.query.graphql
index fdfca493170..f4cc871f1f4 100644
--- a/app/assets/javascripts/projects/your_work/graphql/queries/personal_projects.query.graphql
+++ b/app/assets/javascripts/projects/your_work/graphql/queries/personal_projects.query.graphql
@@ -1,9 +1,13 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
-query getPersonalProjects {
- projects(personal: true) {
+query getPersonalProjects($first: Int, $last: Int, $before: String, $after: String) {
+ projects(personal: true, first: $first, last: $last, before: $before, after: $after) {
nodes {
...Project
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
diff --git a/app/assets/javascripts/projects/your_work/graphql/queries/starred_projects.query.graphql b/app/assets/javascripts/projects/your_work/graphql/queries/starred_projects.query.graphql
index f35bd137198..d2df5f2804a 100644
--- a/app/assets/javascripts/projects/your_work/graphql/queries/starred_projects.query.graphql
+++ b/app/assets/javascripts/projects/your_work/graphql/queries/starred_projects.query.graphql
@@ -1,12 +1,16 @@
+#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/graphql_shared/fragments/project.fragment.graphql"
-query getStarredProjects {
+query getStarredProjects($first: Int, $last: Int, $before: String, $after: String) {
currentUser {
id
- starredProjects {
+ starredProjects(first: $first, last: $last, before: $before, after: $after) {
nodes {
...Project
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
}
diff --git a/app/assets/javascripts/todos/components/filtered_search_tokens/group_token.vue b/app/assets/javascripts/todos/components/filtered_search_tokens/group_token.vue
new file mode 100644
index 00000000000..779e04bd0c9
--- /dev/null
+++ b/app/assets/javascripts/todos/components/filtered_search_tokens/group_token.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+ {{ activeTokenValue ? displayValue(activeTokenValue) : inputValue }}
+
+
+
+ {{ group.fullName }}
+
+
+
+
diff --git a/app/assets/javascripts/todos/components/filtered_search_tokens/project_token.vue b/app/assets/javascripts/todos/components/filtered_search_tokens/project_token.vue
new file mode 100644
index 00000000000..0db558b8f49
--- /dev/null
+++ b/app/assets/javascripts/todos/components/filtered_search_tokens/project_token.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+ {{ activeTokenValue ? displayValue(activeTokenValue) : inputValue }}
+
+
+
+ {{ project.name }}
+
+
+
+
diff --git a/app/assets/javascripts/todos/components/queries/search_groups.query.graphql b/app/assets/javascripts/todos/components/queries/search_groups.query.graphql
new file mode 100644
index 00000000000..dd6e97aa0f7
--- /dev/null
+++ b/app/assets/javascripts/todos/components/queries/search_groups.query.graphql
@@ -0,0 +1,8 @@
+query searchGroups($search: String) {
+ groups(search: $search, first: 20) {
+ nodes {
+ id
+ fullName
+ }
+ }
+}
diff --git a/app/assets/javascripts/todos/components/queries/search_projects.query.graphql b/app/assets/javascripts/todos/components/queries/search_projects.query.graphql
new file mode 100644
index 00000000000..a03719e3ead
--- /dev/null
+++ b/app/assets/javascripts/todos/components/queries/search_projects.query.graphql
@@ -0,0 +1,8 @@
+query searchTodosProjects($search: String) {
+ projects(search: $search, membership: true, first: 20) {
+ nodes {
+ id
+ name
+ }
+ }
+}
diff --git a/app/assets/javascripts/todos/components/todos_app.vue b/app/assets/javascripts/todos/components/todos_app.vue
index 07506b51612..00f2564f034 100644
--- a/app/assets/javascripts/todos/components/todos_app.vue
+++ b/app/assets/javascripts/todos/components/todos_app.vue
@@ -49,6 +49,7 @@ export default {
action: [],
sort: `${SORT_OPTIONS[0].value}_DESC`,
},
+ alert: null,
};
},
apollo: {
@@ -67,7 +68,7 @@ export default {
return nodes;
},
error(error) {
- createAlert({ message: s__('Todos|Something went wrong. Please try again.') });
+ this.alert = createAlert({ message: s__('Todos|Something went wrong. Please try again.') });
Sentry.captureException(error);
},
},
@@ -140,6 +141,7 @@ export default {
};
},
handleFiltersChanged(data) {
+ this.alert?.dismiss();
this.queryFilterValues = { ...data };
},
},
diff --git a/app/assets/javascripts/todos/components/todos_filter_bar.vue b/app/assets/javascripts/todos/components/todos_filter_bar.vue
index 17f8968d1ee..a9001550c72 100644
--- a/app/assets/javascripts/todos/components/todos_filter_bar.vue
+++ b/app/assets/javascripts/todos/components/todos_filter_bar.vue
@@ -1,8 +1,15 @@