diff --git a/web/src/components/CreateAccessTokenDialog.tsx b/web/src/components/CreateAccessTokenDialog.tsx index 2e8d22dcb..8af13e509 100644 --- a/web/src/components/CreateAccessTokenDialog.tsx +++ b/web/src/components/CreateAccessTokenDialog.tsx @@ -8,12 +8,13 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { userServiceClient } from "@/grpcweb"; import useCurrentUser from "@/hooks/useCurrentUser"; import useLoading from "@/hooks/useLoading"; +import { UserAccessToken } from "@/types/proto/api/v1/user_service"; import { useTranslate } from "@/utils/i18n"; interface Props { open: boolean; onOpenChange: (open: boolean) => void; - onSuccess: () => void; + onSuccess: (created: UserAccessToken) => void; } interface State { @@ -72,7 +73,7 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) { try { requestState.setLoading(); - await userServiceClient.createUserAccessToken({ + const created = await userServiceClient.createUserAccessToken({ parent: currentUser.name, accessToken: { description: state.description, @@ -81,7 +82,7 @@ function CreateAccessTokenDialog({ open, onOpenChange, onSuccess }: Props) { }); requestState.setFinish(); - onSuccess(); + onSuccess(created); onOpenChange(false); } catch (error: any) { toast.error(error.details); diff --git a/web/src/components/Settings/AccessTokenSection.tsx b/web/src/components/Settings/AccessTokenSection.tsx index e66d233c0..8338aca76 100644 --- a/web/src/components/Settings/AccessTokenSection.tsx +++ b/web/src/components/Settings/AccessTokenSection.tsx @@ -29,14 +29,13 @@ const AccessTokenSection = () => { }); }, []); - const handleCreateAccessTokenDialogConfirm = async () => { - const prevTokensSet = new Set(userAccessTokens.map((token) => token.accessToken)); + const handleCreateAccessTokenDialogConfirm = async (created?: UserAccessToken) => { + // Refresh list to reflect server state and include stable fields like issuedAt/expiresAt const accessTokens = await listAccessTokens(currentUser.name); setUserAccessTokens(accessTokens); - const newToken = accessTokens.find((token) => !prevTokensSet.has(token.accessToken)); toast.success( t("setting.access-token-section.create-dialog.access-token-created", { - description: newToken?.description ?? t("setting.access-token-section.create-dialog.access-token-created-default"), + description: created?.description ?? t("setting.access-token-section.create-dialog.access-token-created-default"), }), ); }; @@ -56,9 +55,10 @@ const AccessTokenSection = () => { const confirmDeleteAccessToken = async () => { if (!deleteTarget) return; - const { name, accessToken, description } = deleteTarget; - await userServiceClient.deleteUserAccessToken({ name }); - setUserAccessTokens((prev) => prev.filter((token) => token.accessToken !== accessToken)); + const { name: tokenName, description } = deleteTarget; + await userServiceClient.deleteUserAccessToken({ name: tokenName }); + // Filter by stable resource name to avoid ambiguity with duplicate token strings + setUserAccessTokens((prev) => prev.filter((token) => token.name !== tokenName)); setDeleteTarget(undefined); toast.success(t("setting.access-token-section.access-token-deleted", { description })); }; @@ -108,7 +108,7 @@ const AccessTokenSection = () => {
{userAccessTokens.map((userAccessToken) => ( -