mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-24 17:51:14 +02:00
use paginated table for change requests
This commit is contained in:
parent
1ab047859f
commit
8d44925eca
@ -1,285 +1,149 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import {
|
import { PaginatedTable } from 'component/common/Table';
|
||||||
SortableTableHeader,
|
import { createColumnHelper, useReactTable } from '@tanstack/react-table';
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableRow,
|
|
||||||
} from 'component/common/Table';
|
|
||||||
import { useSortBy, useTable } from 'react-table';
|
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
|
||||||
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
|
||||||
import { ChangeRequestStatusCell } from 'component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell';
|
import { ChangeRequestStatusCell } from 'component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestStatusCell';
|
||||||
import { AvatarCell } from 'component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/AvatarCell';
|
import { AvatarCell } from 'component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/AvatarCell';
|
||||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
||||||
import { GlobalChangeRequestTitleCell } from './GlobalChangeRequestTitleCell.js';
|
import { GlobalChangeRequestTitleCell } from './GlobalChangeRequestTitleCell.js';
|
||||||
import { FeaturesCell } from '../ProjectChangeRequests/ChangeRequestsTabs/FeaturesCell.js';
|
import { FeaturesCell } from '../ProjectChangeRequests/ChangeRequestsTabs/FeaturesCell.js';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag.js';
|
import { useUiFlag } from 'hooks/useUiFlag.js';
|
||||||
|
import { withTableState } from 'utils/withTableState';
|
||||||
|
import {
|
||||||
|
useChangeRequestsWithMockData as useChangeRequests,
|
||||||
|
type ChangeRequestItem
|
||||||
|
} from 'hooks/api/getters/useChangeRequests/useChangeRequests';
|
||||||
|
import {
|
||||||
|
encodeQueryParams,
|
||||||
|
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';
|
||||||
|
|
||||||
// Mock data with varied projects and change requests
|
const DEFAULT_PAGE_LIMIT = 25;
|
||||||
const mockChangeRequests = [
|
const columnHelper = createColumnHelper<ChangeRequestItem>();
|
||||||
{
|
|
||||||
id: 101,
|
|
||||||
title: 'Activate harpoons',
|
|
||||||
project: 'payment-service',
|
|
||||||
projectName: 'Payment Service',
|
|
||||||
features: [{ name: 'securePaymentFlow' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'alice', name: 'Alice Johnson', imageUrl: null },
|
|
||||||
createdAt: '2024-01-10T10:22:00Z',
|
|
||||||
environment: 'Production',
|
|
||||||
state: 'Review required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 102,
|
|
||||||
title: 'change request #102',
|
|
||||||
project: 'user-management',
|
|
||||||
projectName: 'User Management',
|
|
||||||
features: [{ name: 'enhancedValidation' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'bob', name: 'Bob Smith', 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: [{ name: 'newCheckoutUX' }, { name: 'paymentOptionsV2' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'carol', name: 'Carol Davis', imageUrl: null },
|
|
||||||
createdAt: '2024-01-10T12:30:00Z',
|
|
||||||
environment: 'Testing',
|
|
||||||
state: 'Review required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 104,
|
|
||||||
title: 'Update user permissions',
|
|
||||||
project: 'user-management',
|
|
||||||
projectName: 'User Management',
|
|
||||||
features: [
|
|
||||||
{ name: 'roleBasedAccess' },
|
|
||||||
{ name: 'permissionMatrix' },
|
|
||||||
{ name: 'adminDashboard' },
|
|
||||||
],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'david', name: 'David Wilson', imageUrl: null },
|
|
||||||
createdAt: '2024-01-09T16:45:00Z',
|
|
||||||
environment: 'Sandbox',
|
|
||||||
state: 'Review required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 105,
|
|
||||||
title: 'Deploy feature rollback',
|
|
||||||
project: 'analytics-platform',
|
|
||||||
projectName: 'Analytics Platform',
|
|
||||||
features: [
|
|
||||||
{ name: 'performanceTracking' },
|
|
||||||
{ name: 'realTimeAnalytics' },
|
|
||||||
{ name: 'customDashboards' },
|
|
||||||
{ name: 'dataExport' },
|
|
||||||
],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'eve', name: 'Eve Brown', 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: [{ name: 'emailTemplates' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'frank', name: 'Frank Miller', 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: [{ name: 'queryOptimization' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'grace', name: 'Grace Lee', 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: [{ name: 'pushNotifications' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'henry', name: 'Henry Chen', 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: [{ name: 'legacyPaymentGateway' }],
|
|
||||||
segments: [],
|
|
||||||
createdBy: { username: 'alice', name: 'Alice Johnson', 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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ChangeRequestsInner = () => {
|
const ChangeRequestsInner = () => {
|
||||||
const loading = false;
|
const stateConfig = {
|
||||||
|
offset: withDefault(NumberParam, 0),
|
||||||
|
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
|
||||||
|
sortBy: withDefault(StringParam, 'createdAt'),
|
||||||
|
sortOrder: withDefault(StringParam, 'desc'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const [tableState, setTableState] = useQueryParams(
|
||||||
|
stateConfig,
|
||||||
|
{ updateType: 'replaceIn' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
changeRequests: data,
|
||||||
|
total,
|
||||||
|
loading,
|
||||||
|
initialLoad,
|
||||||
|
} = useChangeRequests(
|
||||||
|
mapValues(encodeQueryParams(stateConfig, tableState), (value) =>
|
||||||
|
value ? `${value}` : undefined,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
columnHelper.accessor('title', {
|
||||||
id: 'Title',
|
id: 'Title',
|
||||||
Header: 'Title',
|
header: 'Title',
|
||||||
// todo (globalChangeRequestList): sort out width calculation. It's configured both here with a min width down in the inner cell?
|
meta: { width: '300px' },
|
||||||
width: 300,
|
cell: ({ getValue, row }) => (
|
||||||
canSort: true,
|
<GlobalChangeRequestTitleCell
|
||||||
accessor: 'title',
|
value={getValue()}
|
||||||
Cell: GlobalChangeRequestTitleCell,
|
row={row}
|
||||||
},
|
/>
|
||||||
{
|
),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('features', {
|
||||||
id: 'Updated feature flags',
|
id: 'Updated feature flags',
|
||||||
Header: 'Updated feature flags',
|
header: 'Updated feature flags',
|
||||||
canSort: false,
|
enableSorting: false,
|
||||||
accessor: 'features',
|
cell: ({
|
||||||
searchable: true,
|
getValue,
|
||||||
filterName: 'feature',
|
|
||||||
filterParsing: (values: Array<{ name: string }>) => {
|
|
||||||
return values?.map(({ name }) => name).join('\n') || '';
|
|
||||||
},
|
|
||||||
filterBy: (
|
|
||||||
row: { features: Array<{ name: string }> },
|
|
||||||
values: Array<string>,
|
|
||||||
) => {
|
|
||||||
return row.features.find((feature) =>
|
|
||||||
values
|
|
||||||
.map((value) => value.toLowerCase())
|
|
||||||
.includes(feature.name.toLowerCase()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Cell: ({
|
|
||||||
value,
|
|
||||||
row: {
|
row: {
|
||||||
original: { title, project },
|
original: { title, project },
|
||||||
},
|
},
|
||||||
}: any) => (
|
}) => {
|
||||||
<FeaturesCell project={project} value={value} key={title} />
|
const features = getValue();
|
||||||
|
// Convert string array to object array for FeaturesCell compatibility
|
||||||
|
const featureObjects = features.map((name: string) => ({ name }));
|
||||||
|
return (
|
||||||
|
<FeaturesCell
|
||||||
|
project={project}
|
||||||
|
value={featureObjects}
|
||||||
|
key={title}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor('createdBy', {
|
||||||
|
id: 'By',
|
||||||
|
header: 'By',
|
||||||
|
meta: { width: '180px', align: 'left' },
|
||||||
|
enableSorting: false,
|
||||||
|
cell: ({ getValue }) => (
|
||||||
|
<AvatarCell value={getValue()} />
|
||||||
),
|
),
|
||||||
},
|
}),
|
||||||
{
|
columnHelper.accessor('createdAt', {
|
||||||
Header: 'By',
|
id: 'Submitted',
|
||||||
accessor: 'createdBy',
|
header: 'Submitted',
|
||||||
maxWidth: 180,
|
meta: { width: '100px' },
|
||||||
canSort: false,
|
cell: ({ getValue }) => (
|
||||||
Cell: AvatarCell,
|
<TimeAgoCell value={getValue()} />
|
||||||
align: 'left',
|
),
|
||||||
searchable: true,
|
}),
|
||||||
filterName: 'by',
|
columnHelper.accessor('environment', {
|
||||||
filterParsing: (value: { username?: string }) =>
|
id: 'Environment',
|
||||||
value?.username || '',
|
header: 'Environment',
|
||||||
},
|
meta: { width: '100px' },
|
||||||
{
|
cell: ({ getValue }) => (
|
||||||
Header: 'Submitted',
|
<HighlightCell value={getValue()} />
|
||||||
accessor: 'createdAt',
|
),
|
||||||
maxWidth: 100,
|
}),
|
||||||
Cell: TimeAgoCell,
|
columnHelper.accessor('state', {
|
||||||
},
|
id: 'Status',
|
||||||
{
|
header: 'Status',
|
||||||
Header: 'Environment',
|
meta: { width: '170px' },
|
||||||
accessor: 'environment',
|
cell: ({ getValue, row }) => (
|
||||||
searchable: true,
|
<ChangeRequestStatusCell value={getValue()} row={row} />
|
||||||
maxWidth: 100,
|
),
|
||||||
Cell: HighlightCell,
|
}),
|
||||||
filterName: 'environment',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Status',
|
|
||||||
accessor: 'state',
|
|
||||||
searchable: true,
|
|
||||||
maxWidth: '170px',
|
|
||||||
Cell: ChangeRequestStatusCell,
|
|
||||||
filterName: 'status',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { headerGroups, rows, prepareRow, getTableProps, getTableBodyProps } =
|
const table = useReactTable(
|
||||||
useTable(
|
withTableState(tableState, setTableState, {
|
||||||
{
|
columns,
|
||||||
columns: columns as any[],
|
data,
|
||||||
data: mockChangeRequests,
|
}),
|
||||||
initialState: {
|
);
|
||||||
sortBy: [
|
|
||||||
{
|
const bodyLoadingRef = useLoading(loading);
|
||||||
id: 'createdAt',
|
|
||||||
desc: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
sortTypes,
|
|
||||||
autoResetHiddenColumns: false,
|
|
||||||
disableSortRemove: true,
|
|
||||||
autoResetSortBy: false,
|
|
||||||
defaultColumn: {
|
|
||||||
Cell: TextCell,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
useSortBy,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
isLoading={loading}
|
bodyClass='no-padding'
|
||||||
header={<PageHeader title='Change requests' />}
|
header={<PageHeader title='Change requests' />}
|
||||||
>
|
>
|
||||||
<Table {...getTableProps()}>
|
<div className={themeStyles.fullwidth}>
|
||||||
<SortableTableHeader headerGroups={headerGroups} />
|
<div ref={bodyLoadingRef}>
|
||||||
<TableBody {...getTableBodyProps()}>
|
<PaginatedTable tableInstance={table} totalItems={total} />
|
||||||
{rows.map((row) => {
|
</div>
|
||||||
prepareRow(row);
|
</div>
|
||||||
const { key, ...rowProps } = row.getRowProps();
|
|
||||||
return (
|
|
||||||
<TableRow hover key={key} {...rowProps}>
|
|
||||||
{row.cells.map((cell) => {
|
|
||||||
const { key, ...cellProps } =
|
|
||||||
cell.getCellProps();
|
|
||||||
return (
|
|
||||||
<TableCell key={key} {...cellProps}>
|
|
||||||
{cell.render('Cell')}
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,448 @@
|
|||||||
|
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',
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,122 @@
|
|||||||
|
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 };
|
Loading…
Reference in New Issue
Block a user