1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-24 17:51:14 +02:00

use real data

This commit is contained in:
Thomas Heartman 2025-09-22 13:27:49 +02:00
parent 4d9b400eea
commit 99e1b66fcd
No known key found for this signature in database
GPG Key ID: BD1F880DAED1EE78
7 changed files with 86 additions and 639 deletions

View File

@ -1,5 +1,4 @@
import type { VFC } from 'react';
import type { ChangeRequestType } from '../changeRequest.types';
import type { FC } from 'react';
import { Badge } from 'component/common/Badge/Badge';
import AccessTime from '@mui/icons-material/AccessTime';
import Check from '@mui/icons-material/Check';
@ -9,20 +8,27 @@ import ErrorIcon from '@mui/icons-material/Error';
import PauseCircle from '@mui/icons-material/PauseCircle';
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
import { useLocationSettings } from 'hooks/useLocationSettings';
import type {
ScheduledChangeRequest,
UnscheduledChangeRequest,
} from '../changeRequest.types';
interface IChangeRequestStatusBadgeProps {
changeRequest: ChangeRequestType | undefined;
export interface IChangeRequestStatusBadgeProps {
changeRequest:
| Pick<UnscheduledChangeRequest, 'state'>
| Pick<ScheduledChangeRequest, 'state' | 'schedule'>
| undefined;
}
const ReviewRequiredBadge: VFC = () => (
const ReviewRequiredBadge: FC = () => (
<Badge color='secondary' icon={<CircleOutlined fontSize={'small'} />}>
Review required
</Badge>
);
const DraftBadge: VFC = () => <Badge color='warning'>Draft</Badge>;
const DraftBadge: FC = () => <Badge color='warning'>Draft</Badge>;
export const ChangeRequestStatusBadge: VFC<IChangeRequestStatusBadgeProps> = ({
export const ChangeRequestStatusBadge: FC<IChangeRequestStatusBadgeProps> = ({
changeRequest,
}) => {
const { locationSettings } = useLocationSettings();

View File

@ -13,32 +13,34 @@ import { useUiFlag } from 'hooks/useUiFlag.js';
import { ChangeRequestFilters } from './ChangeRequestFilters.tsx';
import { withTableState } from 'utils/withTableState';
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import {
useChangeRequestsWithMockData as useChangeRequests,
type ChangeRequestItem
} from 'hooks/api/getters/useChangeRequests/useChangeRequests';
import {
encodeQueryParams,
useChangeRequestSearch,
DEFAULT_PAGE_LIMIT,
} from 'hooks/api/getters/useChangeRequestSearch/useChangeRequestSearch';
import type { ChangeRequestSearchItemSchema } from 'openapi';
import {
NumberParam,
StringParam,
withDefault,
useQueryParams,
} from 'use-query-params';
import mapValues from 'lodash.mapvalues';
import useLoading from 'hooks/useLoading';
import { styles as themeStyles } from 'component/common';
import { FilterItemParam } from 'utils/serializeQueryParams';
const DEFAULT_PAGE_LIMIT = 25;
const columnHelper = createColumnHelper<ChangeRequestItem>();
const columnHelper = createColumnHelper<ChangeRequestSearchItemSchema>();
const ChangeRequestsInner = () => {
const { user } = useAuthUser();
// Check URL parameters directly to avoid double fetching
const shouldApplyDefaults = useMemo(() => {
const urlParams = new URLSearchParams(window.location.search);
return !urlParams.has('createdBy') && !urlParams.has('requestedApprovalBy') && user;
return (
!urlParams.has('createdBy') &&
!urlParams.has('requestedApproverId') &&
user
);
}, [user]);
const stateConfig = {
@ -47,38 +49,37 @@ const ChangeRequestsInner = () => {
sortBy: withDefault(StringParam, 'createdAt'),
sortOrder: withDefault(StringParam, 'desc'),
createdBy: FilterItemParam,
requestedApprovalBy: FilterItemParam,
requestedApproverId: FilterItemParam,
};
// Apply initial defaults if needed
const initialState = shouldApplyDefaults ? {
createdBy: {
operator: 'IS' as const,
values: [user.id.toString()],
}
} : {};
const [tableState, setTableState] = useQueryParams(
stateConfig,
{ updateType: 'replaceIn' },
);
const initialState = shouldApplyDefaults
? {
createdBy: {
operator: 'IS' as const,
values: user ? [user.id.toString()] : [],
},
}
: {};
const [tableState, setTableState] = useQueryParams(stateConfig, {
updateType: 'replaceIn',
});
// Merge with initial state on first load only
const effectiveTableState = useMemo(() => ({
...initialState,
...tableState
}), [initialState, tableState]);
const effectiveTableState = useMemo(
() => ({
...initialState,
...tableState,
}),
[initialState, tableState],
);
const {
changeRequests: data,
total,
loading,
initialLoad,
} = useChangeRequests(
mapValues(encodeQueryParams(stateConfig, effectiveTableState), (value) =>
value ? `${value}` : undefined,
),
);
} = useChangeRequestSearch(effectiveTableState);
const columns = useMemo(
() => [
@ -87,8 +88,8 @@ const ChangeRequestsInner = () => {
header: 'Title',
meta: { width: '300px' },
cell: ({ getValue, row }) => (
<GlobalChangeRequestTitleCell
value={getValue()}
<GlobalChangeRequestTitleCell
value={getValue()}
row={row}
/>
),
@ -105,12 +106,14 @@ const ChangeRequestsInner = () => {
}) => {
const features = getValue();
// Convert string array to object array for FeaturesCell compatibility
const featureObjects = features.map((name: string) => ({ name }));
const featureObjects = features.map((name: string) => ({
name,
}));
return (
<FeaturesCell
project={project}
value={featureObjects}
key={title}
<FeaturesCell
project={project}
value={featureObjects}
key={title}
/>
);
},
@ -120,31 +123,26 @@ const ChangeRequestsInner = () => {
header: 'By',
meta: { width: '180px', align: 'left' },
enableSorting: false,
cell: ({ getValue }) => (
<AvatarCell value={getValue()} />
),
cell: ({ getValue }) => <AvatarCell value={getValue()} />,
}),
columnHelper.accessor('createdAt', {
id: 'Submitted',
header: 'Submitted',
meta: { width: '100px' },
cell: ({ getValue }) => (
<TimeAgoCell value={getValue()} />
),
cell: ({ getValue }) => <TimeAgoCell value={getValue()} />,
}),
columnHelper.accessor('environment', {
id: 'Environment',
header: 'Environment',
meta: { width: '100px' },
cell: ({ getValue }) => (
<HighlightCell value={getValue()} />
),
cell: ({ getValue }) => <HighlightCell value={getValue()} />,
}),
columnHelper.accessor('state', {
id: 'Status',
header: 'Status',
meta: { width: '170px' },
cell: ({ getValue, row }) => (
// @ts-expect-error (`globalChangeRequestList`) The schema (and query) needs to be updated
<ChangeRequestStatusCell value={getValue()} row={row} />
),
}),
@ -166,9 +164,9 @@ const ChangeRequestsInner = () => {
bodyClass='no-padding'
header={<PageHeader title='Change requests' />}
>
<ChangeRequestFilters
tableState={effectiveTableState}
setTableState={setTableState}
<ChangeRequestFilters
tableState={effectiveTableState}
setTableState={setTableState}
/>
<div className={themeStyles.fullwidth}>
<div ref={bodyLoadingRef}>
@ -189,4 +187,4 @@ export const ChangeRequests = () => {
}
return <ChangeRequestsInner />;
};
};

View File

@ -1,6 +1,7 @@
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { Link, styled, Typography } from '@mui/material';
import { Link as RouterLink, type LinkProps } from 'react-router-dom';
import { useProjectOverviewNameOrId } from 'hooks/api/getters/useProjectOverview/useProjectOverview';
type IGlobalChangeRequestTitleCellProps = {
value?: any;
@ -41,12 +42,12 @@ export const GlobalChangeRequestTitleCell = ({
id,
title,
project,
projectName,
features: featureChanges,
segments: segmentChanges,
} = original;
const projectName = useProjectOverviewNameOrId(project);
const totalChanges =
(featureChanges || []).length + (segmentChanges || []).length;
featureChanges?.length ?? 0 + segmentChanges?.length ?? 0;
const projectPath = `/projects/${project}`;
const crPath = `${projectPath}/change-requests/${id}`;

View File

@ -1,14 +1,18 @@
import type { VFC } from 'react';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
import { ChangeRequestStatusBadge } from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
import {
ChangeRequestStatusBadge,
type IChangeRequestStatusBadgeProps,
} from 'component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge';
import type { FC } from 'react';
interface IChangeRequestStatusCellProps {
value?: string | null; // FIXME: proper type
row: { original: ChangeRequestType };
row: {
original: IChangeRequestStatusBadgeProps['changeRequest'];
};
}
export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
export const ChangeRequestStatusCell: FC<IChangeRequestStatusCellProps> = ({
value,
row: { original },
}) => {

View File

@ -31,6 +31,12 @@ const fallbackData: ChangeRequestSearchResponseSchema = {
const SWR_CACHE_SIZE = 10;
const PATH = 'api/admin/search/change-requests?';
export type SearchChangeRequestsInput = {
[K in keyof SearchChangeRequestsParams]?:
| SearchChangeRequestsParams[K]
| null;
};
const createChangeRequestSearch = () => {
const internalCache: InternalCache = {};
@ -56,7 +62,7 @@ const createChangeRequestSearch = () => {
};
return (
params: SearchChangeRequestsParams,
params: SearchChangeRequestsInput,
options: SWRConfiguration = {},
cachePrefix: string = '',
): UseChangeRequestSearchOutput => {
@ -100,11 +106,13 @@ const createChangeRequestSearch = () => {
export const DEFAULT_PAGE_LIMIT = 25;
const getChangeRequestSearchFetcher = (params: SearchChangeRequestsParams) => {
const getChangeRequestSearchFetcher = (params: SearchChangeRequestsInput) => {
const urlSearchParams = new URLSearchParams(
Array.from(
Object.entries(params)
.filter(([_, value]) => !!value)
.filter(
(param): param is [string, string | number] => !!param[1],
)
.map(([key, value]) => [key, value.toString()]),
),
).toString();

View File

@ -1,448 +0,0 @@
import type { ChangeRequestItem } from './useChangeRequests';
// Mock data with varied projects and change requests (expanded to 35 items for pagination testing)
export const mockChangeRequests: ChangeRequestItem[] = [
{
id: 101,
title: 'Activate harpoons',
project: 'payment-service',
projectName: 'Payment Service',
features: ['securePaymentFlow'],
segments: [],
createdBy: { id: 1, username: 'alice', imageUrl: null },
createdAt: '2024-01-10T10:22:00Z',
environment: 'Production',
state: 'In review',
},
{
id: 102,
title: 'change request #102',
project: 'user-management',
projectName: 'User Management',
features: ['enhancedValidation'],
segments: [],
createdBy: { id: 2, username: 'bob', imageUrl: null },
createdAt: '2024-01-10T08:15:00Z',
environment: 'Production',
state: 'Approved',
},
{
id: 103,
title: 'Enable new checkout flow',
project: 'e-commerce-platform',
projectName: 'E-commerce Platform',
features: ['newCheckoutUX', 'paymentOptionsV2'],
segments: [],
createdBy: { id: 3, username: 'carol', imageUrl: null },
createdAt: '2024-01-10T12:30:00Z',
environment: 'Testing',
state: 'In review',
},
{
id: 104,
title: 'Update user permissions',
project: 'user-management',
projectName: 'User Management',
features: ['roleBasedAccess', 'permissionMatrix', 'adminDashboard'],
segments: [],
createdBy: { id: 4, username: 'david', imageUrl: null },
createdAt: '2024-01-09T16:45:00Z',
environment: 'Sandbox',
state: 'In review',
},
{
id: 105,
title: 'Deploy feature rollback',
project: 'analytics-platform',
projectName: 'Analytics Platform',
features: ['performanceTracking', 'realTimeAnalytics', 'customDashboards', 'dataExport'],
segments: [],
createdBy: { id: 5, username: 'eve', imageUrl: null },
createdAt: '2024-01-09T14:20:00Z',
environment: 'Sandbox',
state: 'Scheduled',
schedule: {
scheduledAt: '2024-01-12T09:46:51+05:30',
status: 'Pending',
},
},
{
id: 106,
title: 'change request #106',
project: 'notification-service',
projectName: 'Notification Service',
features: ['emailTemplates'],
segments: [],
createdBy: { id: 6, username: 'frank', imageUrl: null },
createdAt: '2024-01-08T11:00:00Z',
environment: 'Testing',
state: 'Approved',
},
{
id: 107,
title: 'Optimize database queries',
project: 'data-warehouse',
projectName: 'Data Warehouse',
features: ['queryOptimization'],
segments: [],
createdBy: { id: 7, username: 'grace', imageUrl: null },
createdAt: '2024-01-08T09:30:00Z',
environment: 'Testing',
state: 'Approved',
},
{
id: 108,
title: 'change request #108',
project: 'mobile-app',
projectName: 'Mobile App',
features: ['pushNotifications'],
segments: [],
createdBy: { id: 8, username: 'henry', imageUrl: null },
createdAt: '2024-01-07T15:20:00Z',
environment: 'Production',
state: 'Approved',
},
{
id: 109,
title: 'Archive legacy features',
project: 'payment-service',
projectName: 'Payment Service',
features: ['legacyPaymentGateway'],
segments: [],
createdBy: { id: 1, username: 'alice', imageUrl: null },
createdAt: '2024-01-07T13:10:00Z',
environment: 'Production',
state: 'Scheduled',
schedule: {
scheduledAt: '2024-01-12T09:46:51+05:30',
status: 'Failed',
reason: 'Mr Freeze',
},
},
// Additional 26 items to reach 35 total (ensuring >25 for pagination)
{
id: 110,
title: 'Enable dark mode',
project: 'design-system',
projectName: 'Design System',
features: ['darkTheme', 'themeToggle'],
segments: [],
createdBy: { id: 9, username: 'isabella', imageUrl: null },
createdAt: '2024-01-06T14:30:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 111,
title: 'API rate limiting',
project: 'api-gateway',
projectName: 'API Gateway',
features: ['rateLimiting', 'throttling'],
segments: ['premium-users'],
createdBy: { id: 10, username: 'jack', imageUrl: null },
createdAt: '2024-01-06T10:15:00Z',
environment: 'Production',
state: 'Approved',
},
{
id: 112,
title: 'Social media login',
project: 'auth-service',
projectName: 'Authentication Service',
features: ['googleLogin', 'facebookLogin', 'twitterLogin'],
segments: [],
createdBy: { id: 11, username: 'karen', imageUrl: null },
createdAt: '2024-01-05T16:45:00Z',
environment: 'Testing',
state: 'In review',
},
{
id: 113,
title: 'Real-time notifications',
project: 'notification-service',
projectName: 'Notification Service',
features: ['websocketNotifications', 'notificationCenter'],
segments: [],
createdBy: { id: 12, username: 'liam', imageUrl: null },
createdAt: '2024-01-05T12:00:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 114,
title: 'Multi-currency support',
project: 'payment-service',
projectName: 'Payment Service',
features: ['currencyConversion', 'multiCurrency'],
segments: ['international-users'],
createdBy: { id: 2, username: 'bob', imageUrl: null },
createdAt: '2024-01-04T09:30:00Z',
environment: 'Sandbox',
state: 'Rejected',
},
{
id: 115,
title: 'Advanced search filters',
project: 'e-commerce-platform',
projectName: 'E-commerce Platform',
features: ['advancedSearch', 'filterOptions', 'searchSuggestions'],
segments: [],
createdBy: { id: 13, username: 'mia', imageUrl: null },
createdAt: '2024-01-03T14:15:00Z',
environment: 'Testing',
state: 'Applied',
},
{
id: 116,
title: 'Machine learning recommendations',
project: 'recommendation-engine',
projectName: 'Recommendation Engine',
features: ['mlRecommendations', 'userBehaviorTracking'],
segments: ['active-users'],
createdBy: { id: 14, username: 'noah', imageUrl: null },
createdAt: '2024-01-02T11:20:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 117,
title: 'Video streaming optimization',
project: 'media-service',
projectName: 'Media Service',
features: ['videoOptimization', 'adaptiveBitrate'],
segments: [],
createdBy: { id: 15, username: 'olivia', imageUrl: null },
createdAt: '2024-01-01T08:45:00Z',
environment: 'Production',
state: 'Scheduled',
schedule: {
scheduledAt: '2024-01-15T10:00:00+05:30',
status: 'Pending',
},
},
{
id: 118,
title: 'Inventory management system',
project: 'inventory-service',
projectName: 'Inventory Service',
features: ['stockTracking', 'lowStockAlerts', 'automaticReordering'],
segments: [],
createdBy: { id: 16, username: 'peter', imageUrl: null },
createdAt: '2023-12-31T15:30:00Z',
environment: 'Testing',
state: 'In review',
},
{
id: 119,
title: 'Customer support chatbot',
project: 'customer-service',
projectName: 'Customer Service',
features: ['aiChatbot', 'ticketRouting'],
segments: ['support-tier-1'],
createdBy: { id: 17, username: 'quinn', imageUrl: null },
createdAt: '2023-12-30T13:15:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 120,
title: 'Geolocation services',
project: 'location-service',
projectName: 'Location Service',
features: ['geoLocation', 'addressAutocomplete'],
segments: ['mobile-users'],
createdBy: { id: 18, username: 'rachel', imageUrl: null },
createdAt: '2023-12-29T10:00:00Z',
environment: 'Sandbox',
state: 'Cancelled',
},
{
id: 121,
title: 'Blockchain integration',
project: 'crypto-service',
projectName: 'Crypto Service',
features: ['blockchainIntegration', 'cryptoWallet'],
segments: ['crypto-users'],
createdBy: { id: 19, username: 'sam', imageUrl: null },
createdAt: '2023-12-28T16:20:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 122,
title: 'Voice recognition',
project: 'ai-service',
projectName: 'AI Service',
features: ['voiceRecognition', 'speechToText'],
segments: [],
createdBy: { id: 20, username: 'taylor', imageUrl: null },
createdAt: '2023-12-27T12:45:00Z',
environment: 'Testing',
state: 'Approved',
},
{
id: 123,
title: 'Performance monitoring',
project: 'monitoring-service',
projectName: 'Monitoring Service',
features: ['performanceMetrics', 'alerting', 'dashboards'],
segments: [],
createdBy: { id: 21, username: 'uma', imageUrl: null },
createdAt: '2023-12-26T09:10:00Z',
environment: 'Production',
state: 'Applied',
},
{
id: 124,
title: 'Content moderation',
project: 'content-service',
projectName: 'Content Service',
features: ['autoModeration', 'contentFiltering'],
segments: ['user-generated-content'],
createdBy: { id: 22, username: 'victor', imageUrl: null },
createdAt: '2023-12-25T14:30:00Z',
environment: 'Testing',
state: 'In review',
},
{
id: 125,
title: 'Email marketing automation',
project: 'marketing-service',
projectName: 'Marketing Service',
features: ['emailCampaigns', 'marketingAutomation'],
segments: ['marketing-subscribers'],
createdBy: { id: 23, username: 'wendy', imageUrl: null },
createdAt: '2023-12-24T11:20:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 126,
title: 'Data backup and recovery',
project: 'backup-service',
projectName: 'Backup Service',
features: ['automaticBackup', 'dataRecovery'],
segments: [],
createdBy: { id: 24, username: 'xavier', imageUrl: null },
createdAt: '2023-12-23T08:15:00Z',
environment: 'Production',
state: 'Scheduled',
schedule: {
scheduledAt: '2024-01-20T02:00:00+05:30',
status: 'Pending',
},
},
{
id: 127,
title: 'Load balancing optimization',
project: 'infrastructure',
projectName: 'Infrastructure',
features: ['loadBalancer', 'trafficDistribution'],
segments: [],
createdBy: { id: 25, username: 'yara', imageUrl: null },
createdAt: '2023-12-22T15:45:00Z',
environment: 'Production',
state: 'Approved',
},
{
id: 128,
title: 'Calendar integration',
project: 'calendar-service',
projectName: 'Calendar Service',
features: ['calendarSync', 'eventReminders'],
segments: [],
createdBy: { id: 26, username: 'zoe', imageUrl: null },
createdAt: '2023-12-21T13:30:00Z',
environment: 'Testing',
state: 'In review',
},
{
id: 129,
title: 'Document collaboration',
project: 'document-service',
projectName: 'Document Service',
features: ['realTimeEditing', 'documentSharing'],
segments: ['business-users'],
createdBy: { id: 3, username: 'carol', imageUrl: null },
createdAt: '2023-12-20T10:15:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 130,
title: 'Mobile app security',
project: 'mobile-app',
projectName: 'Mobile App',
features: ['biometricAuth', 'appSecurityScanning'],
segments: ['mobile-users'],
createdBy: { id: 27, username: 'adam', imageUrl: null },
createdAt: '2023-12-19T16:00:00Z',
environment: 'Testing',
state: 'Approved',
},
{
id: 131,
title: 'Analytics dashboard v2',
project: 'analytics-platform',
projectName: 'Analytics Platform',
features: ['advancedDashboard', 'customCharts', 'dataVisualization'],
segments: ['analytics-users'],
createdBy: { id: 5, username: 'eve', imageUrl: null },
createdAt: '2023-12-18T12:20:00Z',
environment: 'Development',
state: 'In review',
},
{
id: 132,
title: 'Microservices migration',
project: 'architecture',
projectName: 'Architecture',
features: ['microservicesArchitecture', 'serviceDecomposition'],
segments: [],
createdBy: { id: 28, username: 'blake', imageUrl: null },
createdAt: '2023-12-17T09:45:00Z',
environment: 'Development',
state: 'Draft',
},
{
id: 133,
title: 'API versioning strategy',
project: 'api-gateway',
projectName: 'API Gateway',
features: ['apiVersioning', 'backwardCompatibility'],
segments: ['api-consumers'],
createdBy: { id: 10, username: 'jack', imageUrl: null },
createdAt: '2023-12-16T14:10:00Z',
environment: 'Testing',
state: 'Applied',
},
{
id: 134,
title: 'User onboarding flow',
project: 'user-management',
projectName: 'User Management',
features: ['onboardingWizard', 'userTutorials'],
segments: ['new-users'],
createdBy: { id: 29, username: 'claire', imageUrl: null },
createdAt: '2023-12-15T11:35:00Z',
environment: 'Production',
state: 'Scheduled',
schedule: {
scheduledAt: '2024-01-25T09:00:00+05:30',
status: 'Suspended',
reason: 'Resource constraints',
},
},
{
id: 135,
title: 'Feature flag management',
project: 'feature-management',
projectName: 'Feature Management',
features: ['featureFlags', 'gradualRollout'],
segments: ['beta-testers'],
createdBy: { id: 30, username: 'derek', imageUrl: null },
createdAt: '2023-12-14T08:25:00Z',
environment: 'Production',
state: 'Approved',
},
];

View File

@ -1,122 +0,0 @@
import { useMemo } from 'react';
import { createPaginatedHook } from '../usePaginatedData/usePaginatedData.js';
import { mockChangeRequests } from './mockChangeRequests.js';
// Type for change request data based on schema
export interface ChangeRequestItem {
id: number;
title: string;
environment: string;
project: string;
projectName?: string; // Not in schema but used by GlobalChangeRequestTitleCell
createdBy: {
id: number;
username?: string | null;
imageUrl?: string | null;
};
createdAt: string;
features: string[];
segments: string[];
state: 'Draft' | 'Approved' | 'In review' | 'Applied' | 'Scheduled' | 'Rejected' | 'Cancelled';
schedule?: {
scheduledAt: string;
status: 'Pending' | 'Failed' | 'Suspended';
reason?: string;
};
}
export interface ChangeRequestsResponse {
changeRequests: ChangeRequestItem[];
total: number;
}
// Sort function that mimics server-side sorting
const sortData = (data: ChangeRequestItem[], sortBy: string, sortOrder: string) => {
return [...data].sort((a, b) => {
let aValue: any;
let bValue: any;
switch (sortBy) {
case 'title':
aValue = a.title.toLowerCase();
bValue = b.title.toLowerCase();
break;
case 'createdAt':
aValue = new Date(a.createdAt).getTime();
bValue = new Date(b.createdAt).getTime();
break;
case 'environment':
aValue = a.environment.toLowerCase();
bValue = b.environment.toLowerCase();
break;
case 'state':
aValue = a.state.toLowerCase();
bValue = b.state.toLowerCase();
break;
case 'createdBy':
aValue = a.createdBy.username?.toLowerCase() || '';
bValue = b.createdBy.username?.toLowerCase() || '';
break;
default:
return 0;
}
if (aValue < bValue) {
return sortOrder === 'asc' ? -1 : 1;
}
if (aValue > bValue) {
return sortOrder === 'asc' ? 1 : -1;
}
return 0;
});
};
// Pagination function
const paginateData = (data: ChangeRequestItem[], offset: number, limit: number) => {
return data.slice(offset, offset + limit);
};
// Create the hook using the established pattern but with custom mock logic
export const useChangeRequests = createPaginatedHook<ChangeRequestsResponse>(
{ changeRequests: [], total: 0 },
'', // No API endpoint - we'll override the fetcher
);
// Override the created hook to add custom mock data logic
const originalUseChangeRequests = useChangeRequests;
export const useChangeRequestsWithMockData = (
params: Record<string, any> = {},
dynamicPrefixKey: string = '',
options: any = {},
) => {
// Extract parameters with defaults
const offset = Number(params?.offset) || 0;
const limit = Number(params?.limit) || 25;
const sortBy = params?.sortBy || 'createdAt';
const sortOrder = params?.sortOrder || 'desc';
const mockData = useMemo(() => {
// Apply sorting (simulating server-side sorting)
const sortedData = sortData(mockChangeRequests, sortBy, sortOrder);
// Apply pagination (simulating server-side pagination)
const paginatedData = paginateData(sortedData, offset, limit);
return {
changeRequests: paginatedData,
total: mockChangeRequests.length,
};
}, [offset, limit, sortBy, sortOrder]);
// Simulate loading and return mock data
return {
...mockData,
loading: false,
initialLoad: false,
error: null,
};
};
// For now, use the mock version. Later, replace with the real API version
export { useChangeRequestsWithMockData as useChangeRequestsSearch };