H-289: add Captain Skill management UI (#47)
* H-289: add Captain Skill management UI * H-289: guard Captain Skill assistant switches --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import CaptainSkills from 'dashboard/api/captain/skills';
|
||||
import { createStore } from '../storeFactory';
|
||||
|
||||
const withFlag = (mutations, flag, request) => async (context, payload) => {
|
||||
context.commit(mutations.SET_UI_FLAG, { [flag]: true });
|
||||
try {
|
||||
return await request(context, payload);
|
||||
} finally {
|
||||
context.commit(mutations.SET_UI_FLAG, { [flag]: false });
|
||||
}
|
||||
};
|
||||
|
||||
let latestListRequest = 0;
|
||||
|
||||
export default createStore({
|
||||
name: 'CaptainSkill',
|
||||
API: CaptainSkills,
|
||||
actions: mutations => ({
|
||||
get: async ({ commit }, params) => {
|
||||
const requestId = ++latestListRequest;
|
||||
commit(mutations.SET_UI_FLAG, { fetchingList: true });
|
||||
try {
|
||||
const { data } = await CaptainSkills.get(params);
|
||||
if (requestId === latestListRequest) {
|
||||
commit(mutations.SET, data.payload);
|
||||
commit(mutations.SET_META, data.meta);
|
||||
}
|
||||
return data.payload;
|
||||
} finally {
|
||||
if (requestId === latestListRequest) {
|
||||
commit(mutations.SET_UI_FLAG, { fetchingList: false });
|
||||
}
|
||||
}
|
||||
},
|
||||
detail: withFlag(mutations, 'fetchingItem', async (_, id) => {
|
||||
return (await CaptainSkills.show(id)).data;
|
||||
}),
|
||||
create: withFlag(
|
||||
mutations,
|
||||
'creatingItem',
|
||||
async ({ commit }, payload) => {
|
||||
const { data } = await CaptainSkills.create(payload);
|
||||
commit(mutations.UPSERT, data);
|
||||
return data;
|
||||
}
|
||||
),
|
||||
update: withFlag(
|
||||
mutations,
|
||||
'updatingItem',
|
||||
async ({ commit }, { id, ...payload }) => {
|
||||
const { data } = await CaptainSkills.update(id, payload);
|
||||
commit(mutations.UPSERT, data);
|
||||
return data;
|
||||
}
|
||||
),
|
||||
delete: withFlag(mutations, 'deletingItem', async ({ commit }, id) => {
|
||||
await CaptainSkills.delete(id);
|
||||
commit(mutations.DELETE, id);
|
||||
return id;
|
||||
}),
|
||||
bind: withFlag(mutations, 'updatingItem', (_, payload) =>
|
||||
CaptainSkills.bind(payload)
|
||||
),
|
||||
unbind: withFlag(mutations, 'updatingItem', (_, payload) =>
|
||||
CaptainSkills.unbind(payload)
|
||||
),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import CaptainSkillsAPI from 'dashboard/api/captain/skills';
|
||||
import store from './skills';
|
||||
|
||||
vi.mock('dashboard/api/captain/skills', () => ({
|
||||
default: {
|
||||
get: vi.fn(),
|
||||
bind: vi.fn(),
|
||||
unbind: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('captainSkills store', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('keeps only the latest assistant list response and loading state', async () => {
|
||||
let resolveA;
|
||||
let resolveB;
|
||||
CaptainSkillsAPI.get
|
||||
.mockReturnValueOnce(
|
||||
new Promise(resolve => {
|
||||
resolveA = resolve;
|
||||
})
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
new Promise(resolve => {
|
||||
resolveB = resolve;
|
||||
})
|
||||
);
|
||||
const commit = vi.fn();
|
||||
const requestA = store.actions.get({ commit }, { assistantId: 1 });
|
||||
const requestB = store.actions.get({ commit }, { assistantId: 2 });
|
||||
|
||||
resolveB({ data: { payload: ['B'], meta: { page: 2 } } });
|
||||
await requestB;
|
||||
resolveA({ data: { payload: ['A'], meta: { page: 1 } } });
|
||||
await requestA;
|
||||
|
||||
expect(commit).toHaveBeenCalledWith('SET_CAPTAINSKILL', ['B']);
|
||||
expect(commit).toHaveBeenCalledWith('SET_CAPTAINSKILL_META', {
|
||||
page: 2,
|
||||
});
|
||||
expect(commit).not.toHaveBeenCalledWith('SET_CAPTAINSKILL', ['A']);
|
||||
expect(commit).not.toHaveBeenCalledWith('SET_CAPTAINSKILL_META', {
|
||||
page: 1,
|
||||
});
|
||||
expect(
|
||||
commit.mock.calls.filter(
|
||||
([type, value]) =>
|
||||
type === 'SET_CAPTAINSKILL_UI_FLAG' &&
|
||||
value.fetchingList === false
|
||||
)
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it.each(['bind', 'unbind'])(
|
||||
'%s toggles the row loading state',
|
||||
async action => {
|
||||
CaptainSkillsAPI[action].mockResolvedValue({});
|
||||
const commit = vi.fn();
|
||||
const payload = { assistantId: 2, skillId: 3 };
|
||||
|
||||
await store.actions[action]({ commit }, payload);
|
||||
|
||||
expect(CaptainSkillsAPI[action]).toHaveBeenCalledWith(payload);
|
||||
expect(commit).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'SET_CAPTAINSKILL_UI_FLAG',
|
||||
{
|
||||
updatingItem: true,
|
||||
}
|
||||
);
|
||||
expect(commit).toHaveBeenLastCalledWith(
|
||||
'SET_CAPTAINSKILL_UI_FLAG',
|
||||
{
|
||||
updatingItem: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('restores loading state when binding fails', async () => {
|
||||
const error = new Error('conflict');
|
||||
CaptainSkillsAPI.bind.mockRejectedValue(error);
|
||||
const commit = vi.fn();
|
||||
|
||||
await expect(
|
||||
store.actions.bind({ commit }, { assistantId: 2, skillId: 3 })
|
||||
).rejects.toBe(error);
|
||||
expect(commit).toHaveBeenLastCalledWith('SET_CAPTAINSKILL_UI_FLAG', {
|
||||
updatingItem: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -59,6 +59,7 @@ import copilotMessages from './captain/copilotMessages';
|
||||
import captainScenarios from './captain/scenarios';
|
||||
import captainTools from './captain/tools';
|
||||
import captainCustomTools from './captain/customTools';
|
||||
import captainSkills from './captain/skills';
|
||||
import platform from './modules/platform';
|
||||
|
||||
const plugins = [];
|
||||
@@ -124,6 +125,7 @@ export default createStore({
|
||||
captainScenarios,
|
||||
captainTools,
|
||||
captainCustomTools,
|
||||
captainSkills,
|
||||
platform,
|
||||
},
|
||||
plugins,
|
||||
|
||||
Reference in New Issue
Block a user