Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ jobs:
- name: Run Redis explorer contract
run: yarn run test:redis-explorer

- name: Run CreateStream index mapping contract
run: yarn run test:create-stream-index

- name: Run result inspector contract
run: yarn run test:result-inspector

Expand Down
3 changes: 2 additions & 1 deletion chat2db-community-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"start:community:hot": "cross-env UMI_ENV=community cross-env APP_NAME=chat2db-community cross-env DISABLE_MFSU=true cross-env UMI_DEV_SERVER_COMPRESS=none cross-env PORT=8889 umi dev --public_path=/",
"start:offline:hot": "cross-env UMI_ENV=offline cross-env PORT=8888 umi dev",
"start:proxy:online": "cross-env HMR=none umi dev --webapp=true --public_path=http://localhost:8000/",
"ui": "yarn add @chat2db/ui"
"ui": "yarn add @chat2db/ui",
"test:create-stream-index": "tsx src/blocks/RedisAllData/CreateStream/mapDisplayedIndex.test.ts"
},
"dependencies": {
"@chat2db/ui": "^1.45.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import BaseTable, { BaseTableRef } from '@/components/BaseTable';
import { ToolbarBtn } from '@chat2db/ui';
import { Flex } from 'antd';
import { ActionType } from '@/constants/redis';
import { mapDisplayedIndexToValueListIndex } from './mapDisplayedIndex';
// import { v4 as uuid } from 'uuid';
import lodash from 'lodash';
import { StreamValue } from '@/typings/redis';
Expand Down Expand Up @@ -173,9 +174,8 @@ const CreateStream = forwardRef((props: IProps, ref: ForwardedRef<CreateStreamRe

// Handle cell edits.
const handleCellChange = (index, columnName, value) => {
// Count deleted rows before this index.
const deleteCount = valueList.slice(0, index + 1).filter((item) => item.action === ActionType.DELETE).length;
const realIndex = index + deleteCount;
const realIndex = mapDisplayedIndexToValueListIndex(valueList, index);
if (realIndex === -1) return;
setValueList((prevTableData) => {
const newData = [...prevTableData];
newData[realIndex][columnName] = value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import assert from 'node:assert/strict';
import { ActionType } from '@/constants/redis';
import { mapDisplayedIndexToValueListIndex } from './mapDisplayedIndex';

type Item = { action: ActionType; name: string };

const make = (action: ActionType, name: string): Item => ({ action, name });

// 1. No deleted rows — identity mapping
{
const list = [make(ActionType.ORIGINAL, 'a'), make(ActionType.ORIGINAL, 'b'), make(ActionType.ORIGINAL, 'c')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), 0);
assert.equal(mapDisplayedIndexToValueListIndex(list, 1), 1);
assert.equal(mapDisplayedIndexToValueListIndex(list, 2), 2);
assert.equal(mapDisplayedIndexToValueListIndex(list, 3), -1); // out of range
}

// 2. Deleted rows before the target — correct mapping
{
// valueList: [DELETE, DELETE, A, B]
// displayed: [A(0), B(1)]
const list = [make(ActionType.DELETE, 'd1'), make(ActionType.DELETE, 'd2'), make(ActionType.ORIGINAL, 'A'), make(ActionType.ORIGINAL, 'B')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), 2, 'first visible (A) should map to index 2');
assert.equal(mapDisplayedIndexToValueListIndex(list, 1), 3, 'second visible (B) should map to index 3');
assert.equal(mapDisplayedIndexToValueListIndex(list, 2), -1, 'out of range');
}

// 3. Deleted rows between visible rows — correct mapping
{
// valueList: [A, DELETE, B, DELETE, C]
// displayed: [A(0), B(1), C(2)]
const list = [make(ActionType.ORIGINAL, 'A'), make(ActionType.DELETE, 'd1'), make(ActionType.ORIGINAL, 'B'), make(ActionType.DELETE, 'd2'), make(ActionType.ORIGINAL, 'C')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), 0, 'A at index 0');
assert.equal(mapDisplayedIndexToValueListIndex(list, 1), 2, 'B at index 2');
assert.equal(mapDisplayedIndexToValueListIndex(list, 2), 4, 'C at index 4');
assert.equal(mapDisplayedIndexToValueListIndex(list, 3), -1, 'out of range');
}

// 4. Mixed ADD/UPDATE/ORIGINAL rows — all non-DELETE are visible
{
// valueList: [ADD, DELETE, UPDATE, ORIGINAL]
// displayed: [ADD(0), UPDATE(1), ORIGINAL(2)]
const list = [make(ActionType.ADD, 'new'), make(ActionType.DELETE, 'd'), make(ActionType.UPDATE, 'upd'), make(ActionType.ORIGINAL, 'orig')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), 0, 'ADD at index 0');
assert.equal(mapDisplayedIndexToValueListIndex(list, 1), 2, 'UPDATE at index 2');
assert.equal(mapDisplayedIndexToValueListIndex(list, 2), 3, 'ORIGINAL at index 3');
assert.equal(mapDisplayedIndexToValueListIndex(list, 3), -1, 'out of range');
}

// 5. Out-of-range displayed index — returns -1
{
const list = [make(ActionType.ORIGINAL, 'a'), make(ActionType.DELETE, 'd'), make(ActionType.ORIGINAL, 'b')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 2), -1, 'only 2 visible rows, index 2 is out of range');
assert.equal(mapDisplayedIndexToValueListIndex(list, -1), -1, 'negative index');
assert.equal(mapDisplayedIndexToValueListIndex(list, 100), -1, 'large out-of-range index');
}

// 6. All rows deleted — returns -1
{
const list = [make(ActionType.DELETE, 'a'), make(ActionType.DELETE, 'b')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), -1, 'no visible rows');
}

// 7. Single row — edge case
{
const list = [make(ActionType.ORIGINAL, 'only')];
assert.equal(mapDisplayedIndexToValueListIndex(list, 0), 0, 'single visible row');
assert.equal(mapDisplayedIndexToValueListIndex(list, 1), -1, 'out of range');
}

// 8. Empty list — returns -1
{
assert.equal(mapDisplayedIndexToValueListIndex([], 0), -1, 'empty list');
}

console.log('mapDisplayedIndex tests passed');
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ActionType } from '@/constants/redis';

interface ValueListItem {
action: ActionType;
[key: string]: unknown;
}

/**
* Maps a displayed (filtered) row index back to the valueList index.
*
* The displayed table filters out DELETE rows, so the displayed index
* must be mapped by counting non-DELETE rows (mirroring handleDelete's
* accumulation approach).
*
* @returns the valueList index, or -1 if the displayed index is out of range.
*/
export function mapDisplayedIndexToValueListIndex(
valueList: ValueListItem[],
displayedIndex: number,
): number {
let nonDeleteCount = 0;
for (let i = 0; i < valueList.length; i++) {
if (valueList[i].action !== ActionType.DELETE) {
if (nonDeleteCount === displayedIndex) {
return i;
}
nonDeleteCount++;
}
}
return -1;
}
Loading