fix: scope storage type picker to the target organization
The storage create form fetched persistent-volume types once with no tenant scope, so in the platform-admin "All" view the dropdown listed every org's types and never re-scoped when the create-scope picker retargeted the form — letting an org with no cluster-access grant pick another org's types. The inline "add storage" sub-drawer in the instance create form had the same gap plus a redundant org picker that could retarget the storage away from the instance's org. Both surfaces now pin the storage-type list to the chosen org (via the X-Organization-Id header); the sub-drawer inherits the instance's scope and hides its own picker. Page/table listings stay unscoped so every row's type label still renders.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Form } from 'antd';
|
||||
import { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
||||
import { FormData, ListItem } from '../config/types';
|
||||
import Basic from './basic';
|
||||
|
||||
@@ -10,14 +11,67 @@ interface StorageFormProps {
|
||||
open: boolean;
|
||||
action: PageActionType;
|
||||
currentData?: ListItem | null;
|
||||
// Hide the create-scope org picker when the host already fixes the
|
||||
// tenant scope (e.g. the instance create form). Defaults to shown.
|
||||
showOrgScope?: boolean;
|
||||
// Fired when the create-scope picker (platform admin "All" view)
|
||||
// retargets the form to another org. Only emitted on genuine changes,
|
||||
// never on the initial mount, and never in builds where the picker
|
||||
// isn't mounted (the watched field stays undefined). Lets the parent
|
||||
// reload the org-scoped storage-type options.
|
||||
onScopeChange?: (orgId: number | null | undefined) => void;
|
||||
onFinish: (values: FormData) => Promise<void>;
|
||||
onFinishFailed?: (errorInfo: any) => void;
|
||||
}
|
||||
|
||||
const GPUServiceStorageForm: React.FC<StorageFormProps> = forwardRef(
|
||||
(props, ref) => {
|
||||
const { action, currentData, open, onFinish, onFinishFailed } = props;
|
||||
const {
|
||||
action,
|
||||
currentData,
|
||||
open,
|
||||
showOrgScope = true,
|
||||
onScopeChange,
|
||||
onFinish,
|
||||
onFinishFailed
|
||||
} = props;
|
||||
const [form] = Form.useForm<FormData>();
|
||||
// `organization_id` is owned by the create-scope picker slot; it only
|
||||
// exists/changes when a platform admin retargets the form. Watch it so
|
||||
// the initial/default scope can be propagated once the picker resolves it.
|
||||
const scopeOrgId = Form.useWatch('organization_id', form);
|
||||
const scopeInitRef = useRef(true);
|
||||
|
||||
// Stable wrapper so the effect / change handler always call the latest
|
||||
// callback without re-subscribing on the parent's fn identity.
|
||||
const orgScope = useMemoizedFn((orgId?: number | null) => {
|
||||
onScopeChange?.(orgId);
|
||||
});
|
||||
|
||||
// Genuine retarget by the create-scope picker: drop the stale type pick
|
||||
// (it may name a type the newly chosen org can't reference) and reload the
|
||||
// org-scoped list. Wired to the picker's own onChange, so it fires only on
|
||||
// a user selection — never on the picker's programmatic default.
|
||||
const handleOrgScopeChange = useMemoizedFn((orgId?: number | null) => {
|
||||
scopeInitRef.current = false;
|
||||
form.setFieldValue(['spec', 'type'], undefined);
|
||||
orgScope(orgId ?? null);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
// Re-arm the initial-scope propagation for the next open.
|
||||
scopeInitRef.current = true;
|
||||
return;
|
||||
}
|
||||
// Propagate the initial/default scope once the picker resolves it so the
|
||||
// parent loads the org-scoped type list, leaving any pre-filled type
|
||||
// intact. User-driven retargets go through handleOrgScopeChange instead.
|
||||
if (scopeInitRef.current && scopeOrgId != null) {
|
||||
scopeInitRef.current = false;
|
||||
orgScope(scopeOrgId);
|
||||
}
|
||||
}, [open, scopeOrgId, orgScope]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
@@ -56,7 +110,12 @@ const GPUServiceStorageForm: React.FC<StorageFormProps> = forwardRef(
|
||||
preserve={false}
|
||||
initialValues={{}}
|
||||
>
|
||||
<Basic action={action} open={open} />
|
||||
<Basic
|
||||
action={action}
|
||||
open={open}
|
||||
showOrgScope={showOrgScope}
|
||||
onOrgScopeChange={handleOrgScopeChange}
|
||||
/>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user