mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-07 02:57:51 +00:00
fix: sync namespace member role updates
This commit is contained in:
parent
99ff863681
commit
0904f0feda
5 changed files with 86 additions and 4 deletions
|
|
@ -224,10 +224,10 @@ export function AddNamespaceMemberDialog({ slug, children }: AddNamespaceMemberD
|
|||
) : null}
|
||||
|
||||
<DialogFooter className="sm:justify-center sm:space-x-3">
|
||||
<Button variant="outline" onClick={() => handleOpenChange(false)}>
|
||||
<Button type="button" variant="outline" onClick={() => handleOpenChange(false)}>
|
||||
{t('dialog.cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleAddMember} disabled={addMemberMutation.isPending}>
|
||||
<Button type="button" onClick={handleAddMember} disabled={addMemberMutation.isPending}>
|
||||
{addMemberMutation.isPending ? t('members.addingMember') : t('members.addMember')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ export function NamespaceMembersPage() {
|
|||
<option value="ADMIN">{t('members.roleAdmin')}</option>
|
||||
</Select>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={roleValue === member.role || isSavingRole}
|
||||
|
|
@ -220,6 +221,7 @@ export function NamespaceMembersPage() {
|
|||
</td>
|
||||
<td className="p-4 text-right">
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
disabled={!canManageMembers || isOwner || isRemoving}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import type { SkillSummary, SkillDetail, SkillVersion, SkillVersionDetail, SkillFile, SearchParams, PagedResponse, PublishResult, Namespace, NamespaceMember, ManagedNamespace, CreateNamespaceRequest, NamespaceCandidateUser, NamespaceRole } from '@/api/types'
|
||||
import { fetchJson, fetchText, getCsrfHeaders, meApi, namespaceApi, promotionApi, skillLifecycleApi, WEB_API_PREFIX } from '@/api/client'
|
||||
import { appendNamespaceMember, replaceNamespaceMemberRole } from '@/shared/lib/namespace-member-cache'
|
||||
import { normalizeSearchQuery } from '@/shared/lib/search-query'
|
||||
|
||||
const PUBLISH_REQUEST_TIMEOUT_MS = 60_000
|
||||
|
|
@ -268,7 +269,11 @@ export function useAddNamespaceMember() {
|
|||
|
||||
return useMutation({
|
||||
mutationFn: addNamespaceMember,
|
||||
onSuccess: (_data, variables) => {
|
||||
onSuccess: (member, variables) => {
|
||||
queryClient.setQueryData<NamespaceMember[]>(
|
||||
['namespaces', variables.slug, 'members'],
|
||||
(currentMembers) => appendNamespaceMember(currentMembers, member),
|
||||
)
|
||||
invalidateNamespaceQueries(queryClient, variables.slug)
|
||||
},
|
||||
})
|
||||
|
|
@ -279,7 +284,11 @@ export function useUpdateNamespaceMemberRole() {
|
|||
|
||||
return useMutation({
|
||||
mutationFn: updateNamespaceMemberRole,
|
||||
onSuccess: (_data, variables) => {
|
||||
onSuccess: (member, variables) => {
|
||||
queryClient.setQueryData<NamespaceMember[]>(
|
||||
['namespaces', variables.slug, 'members'],
|
||||
(currentMembers) => replaceNamespaceMemberRole(currentMembers, variables.userId, member.role),
|
||||
)
|
||||
invalidateNamespaceQueries(queryClient, variables.slug)
|
||||
},
|
||||
})
|
||||
|
|
|
|||
41
web/src/shared/lib/namespace-member-cache.test.ts
Normal file
41
web/src/shared/lib/namespace-member-cache.test.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { appendNamespaceMember, replaceNamespaceMemberRole } from './namespace-member-cache'
|
||||
import type { NamespaceMember } from '@/api/types'
|
||||
|
||||
const baseMember = (overrides: Partial<NamespaceMember>): NamespaceMember => ({
|
||||
id: 1,
|
||||
userId: 'user-1',
|
||||
role: 'MEMBER',
|
||||
createdAt: '2026-03-17T00:00:00',
|
||||
...overrides,
|
||||
})
|
||||
|
||||
describe('appendNamespaceMember', () => {
|
||||
it('appends a newly added member with the returned role', () => {
|
||||
const members = [baseMember({})]
|
||||
const addedMember = baseMember({ id: 2, userId: 'user-2', role: 'ADMIN' })
|
||||
|
||||
expect(appendNamespaceMember(members, addedMember)).toEqual([
|
||||
members[0],
|
||||
addedMember,
|
||||
])
|
||||
})
|
||||
|
||||
it('replaces the existing member when the same user is returned again', () => {
|
||||
const members = [baseMember({ role: 'MEMBER' })]
|
||||
const updatedMember = baseMember({ role: 'ADMIN' })
|
||||
|
||||
expect(appendNamespaceMember(members, updatedMember)).toEqual([updatedMember])
|
||||
})
|
||||
})
|
||||
|
||||
describe('replaceNamespaceMemberRole', () => {
|
||||
it('updates the member role in the current list', () => {
|
||||
const members = [baseMember({}), baseMember({ id: 2, userId: 'user-2', role: 'MEMBER' })]
|
||||
|
||||
expect(replaceNamespaceMemberRole(members, 'user-2', 'ADMIN')).toEqual([
|
||||
members[0],
|
||||
{ ...members[1], role: 'ADMIN' },
|
||||
])
|
||||
})
|
||||
})
|
||||
30
web/src/shared/lib/namespace-member-cache.ts
Normal file
30
web/src/shared/lib/namespace-member-cache.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import type { NamespaceMember } from '@/api/types'
|
||||
|
||||
export function appendNamespaceMember(
|
||||
currentMembers: NamespaceMember[] | undefined,
|
||||
nextMember: NamespaceMember,
|
||||
) {
|
||||
const members = currentMembers ?? []
|
||||
const existingIndex = members.findIndex((member) => member.userId === nextMember.userId)
|
||||
|
||||
if (existingIndex === -1) {
|
||||
return [...members, nextMember]
|
||||
}
|
||||
|
||||
return members.map((member, index) => (index === existingIndex ? nextMember : member))
|
||||
}
|
||||
|
||||
export function replaceNamespaceMemberRole(
|
||||
currentMembers: NamespaceMember[] | undefined,
|
||||
userId: string,
|
||||
role: string,
|
||||
) {
|
||||
return (currentMembers ?? []).map((member) => (
|
||||
member.userId === userId
|
||||
? {
|
||||
...member,
|
||||
role,
|
||||
}
|
||||
: member
|
||||
))
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue