mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
* fix(ui): show password verification for take ownership (PROJQUAY-9658) Co-authored-by: Claude <noreply@anthropic.com> * fix(ui): add fresh login verification to superuser operations (PROJQUAY-9658) Implemented password verification modal for sensitive superuser operations that require fresh login. Added fresh login handling to: - Delete organization - Rename organization - Delete user - Create user - Change user password - Change user email Also fixed UseCreateUser hook to pass error object instead of string to enable fresh login detection. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(ui): implement global fresh login with request queuing (PROJQUAY-9658) Replaced component-level fresh login handling with centralized axios interceptor approach. All failed requests due to fresh login requirement are now queued and automatically retried after password verification. - Add axios interceptor to catch 401 fresh_login_required globally - Implement request queuing mechanism for pending operations - Add global FreshLoginModal at app root level - Remove component-level useFreshLogin hook from all modals - Close operation modals immediately after submit (request queued if needed) - Add fresh login support for quota management operations * Add Cypress tests for fresh login flow (6 tests) * Address code rabbit reviews * fix(ui): handle fresh login password verification errors - Display error toast when wrong password entered in verification modal - Add centralized fresh login error filtering utility - Add Cypress tests for wrong password and retry scenarios --------- Co-authored-by: Claude <noreply@anthropic.com>
107 lines
3.0 KiB
TypeScript
107 lines
3.0 KiB
TypeScript
import {useMutation, useQueryClient} from '@tanstack/react-query';
|
|
import {useNavigate} from 'react-router-dom';
|
|
import {
|
|
deleteOrg,
|
|
renameOrganization,
|
|
takeOwnership,
|
|
} from 'src/resources/OrganizationResource';
|
|
|
|
export function useRenameOrganization({onSuccess, onError}) {
|
|
const queryClient = useQueryClient();
|
|
|
|
const renameOrganizationMutator = useMutation(
|
|
async ({orgName, newName}: {orgName: string; newName: string}) => {
|
|
return await renameOrganization(orgName, newName);
|
|
},
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
// Invalidate the main organizations list queries
|
|
queryClient.invalidateQueries([
|
|
'organization',
|
|
'superuser',
|
|
'organizations',
|
|
]);
|
|
queryClient.invalidateQueries(['organization', 'superuser', 'users']);
|
|
queryClient.invalidateQueries(['user']);
|
|
onSuccess(variables.orgName, variables.newName);
|
|
},
|
|
onError: (err) => {
|
|
onError(err);
|
|
},
|
|
},
|
|
);
|
|
|
|
return {
|
|
renameOrganization: async (orgName: string, newName: string) =>
|
|
renameOrganizationMutator.mutate({orgName, newName}),
|
|
isLoading: renameOrganizationMutator.isLoading,
|
|
};
|
|
}
|
|
|
|
export function useDeleteSingleOrganization({onSuccess, onError}) {
|
|
const queryClient = useQueryClient();
|
|
|
|
const deleteOrganizationMutator = useMutation(
|
|
async (orgName: string) => {
|
|
return await deleteOrg(orgName, true); // true for superuser
|
|
},
|
|
{
|
|
onSuccess: () => {
|
|
// Invalidate the main organizations list queries
|
|
queryClient.invalidateQueries([
|
|
'organization',
|
|
'superuser',
|
|
'organizations',
|
|
]);
|
|
queryClient.invalidateQueries(['organization', 'superuser', 'users']);
|
|
queryClient.invalidateQueries(['user']);
|
|
onSuccess();
|
|
},
|
|
onError: (err) => {
|
|
onError(err);
|
|
},
|
|
},
|
|
);
|
|
|
|
return {
|
|
deleteOrganization: async (orgName: string) =>
|
|
deleteOrganizationMutator.mutate(orgName),
|
|
isLoading: deleteOrganizationMutator.isLoading,
|
|
};
|
|
}
|
|
|
|
export function useTakeOwnership({onSuccess, onError}) {
|
|
const queryClient = useQueryClient();
|
|
const navigate = useNavigate();
|
|
|
|
const takeOwnershipMutator = useMutation(
|
|
async (namespace: string) => {
|
|
return await takeOwnership(namespace);
|
|
},
|
|
{
|
|
onSuccess: (data, namespace) => {
|
|
// Invalidate the main organizations list queries
|
|
queryClient.invalidateQueries([
|
|
'organization',
|
|
'superuser',
|
|
'organizations',
|
|
]);
|
|
queryClient.invalidateQueries(['organization', 'superuser', 'users']);
|
|
queryClient.invalidateQueries(['user']);
|
|
// Navigate to the organization page
|
|
navigate(`/organization/${namespace}`);
|
|
onSuccess();
|
|
},
|
|
onError: (err) => {
|
|
onError(err);
|
|
},
|
|
},
|
|
);
|
|
|
|
return {
|
|
takeOwnership: async (namespace: string) =>
|
|
takeOwnershipMutator.mutate(namespace),
|
|
isLoading: takeOwnershipMutator.isLoading,
|
|
};
|
|
}
|