@@ -103,6 +103,23 @@ interface VFolderNameCellProps {
103103 * additionally redirects admins (project/domain/super) to that page.
104104 */
105105 disableProjectFolderActions ?: boolean ;
106+ /**
107+ * When true, the row-level "Deploy as service" action for model folders
108+ * is rendered disabled with a tooltip explaining that no deployment
109+ * presets are available. Computed once at the page level (via the page's
110+ * `useLazyLoadQuery` selecting `deploymentRevisionPresets(first: 0) { count }`)
111+ * and forwarded down through `VFolderNodes` so we don't fire one query
112+ * per row. The `VFolderDeployModal` retains a `null`-return fallback for
113+ * the same condition as defense in depth.
114+ *
115+ * TODO(needs-backend): the schema exposes
116+ * `modelCardAvailablePresets(scope: { modelCardId })`, but we have a
117+ * vfolder, not a model card, in this row. Either add a vfolder scope
118+ * (`VFolderAvailablePresetsScope`) or expose a vfolder→modelCard link
119+ * so we can narrow this check per row. Today this boolean reflects
120+ * "any preset exists in this project."
121+ */
122+ hasNoCompatiblePresets ?: boolean ;
106123}
107124
108125const VFolderNameCell : React . FC < VFolderNameCellProps > = ( {
@@ -113,6 +130,7 @@ const VFolderNameCell: React.FC<VFolderNameCellProps> = ({
113130 onDeleteForever,
114131 onStartServiceFallback,
115132 disableProjectFolderActions = false ,
133+ hasNoCompatiblePresets = false ,
116134} ) => {
117135 'use memo' ;
118136 const { t } = useTranslation ( ) ;
@@ -148,6 +166,10 @@ const VFolderNameCell: React.FC<VFolderNameCellProps> = ({
148166 key : 'start-service' ,
149167 title : t ( 'modelService.DeployAsService' ) ,
150168 icon : < BAIEndpointsIcon /> ,
169+ disabled : hasNoCompatiblePresets ,
170+ disabledReason : hasNoCompatiblePresets
171+ ? t ( 'data.folders.NoCompatibleDeploymentPresets' )
172+ : undefined ,
151173 onClick : ( ) => onStartServiceFallback ( vfolderId ) ,
152174 }
153175 : null ,
@@ -248,12 +270,29 @@ interface VFolderNodesProps extends Omit<
248270 * component. This prop is the V1-friendly stopgap.
249271 */
250272 disableProjectFolderActions ?: boolean ;
273+ /**
274+ * When true, the row-level "Deploy as service" action for model folders
275+ * is rendered disabled with a tooltip explaining that no deployment
276+ * presets are available. Derived at the page-level `useLazyLoadQuery`
277+ * from `deploymentRevisionPresets(first: 0) { count }` so we don't fire
278+ * one query per row. Defaults to `false` so existing call sites are
279+ * additive — page hosts opt in by passing the derived value.
280+ *
281+ * TODO(needs-backend): the schema exposes
282+ * `modelCardAvailablePresets(scope: { modelCardId })`, but we have a
283+ * vfolder, not a model card, in this row. Either add a vfolder scope
284+ * (`VFolderAvailablePresetsScope`) or expose a vfolder→modelCard link
285+ * so we can narrow this check per row. Today this boolean reflects
286+ * "any preset exists in this project."
287+ */
288+ hasNoCompatiblePresets ?: boolean ;
251289}
252290
253291const VFolderNodes : React . FC < VFolderNodesProps > = ( {
254292 vfoldersFrgmt,
255293 onRemoveRow,
256294 disableProjectFolderActions,
295+ hasNoCompatiblePresets = false ,
257296 ...tableProps
258297} ) => {
259298 const { t } = useTranslation ( ) ;
@@ -346,6 +385,7 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
346385 < VFolderNameCell
347386 vfolder = { vfolder }
348387 disableProjectFolderActions = { disableProjectFolderActions }
388+ hasNoCompatiblePresets = { hasNoCompatiblePresets }
349389 onShare = { ( ) => {
350390 vfolder ?. user === currentUser ?. uuid
351391 ? setInviteFolderId ( toLocalId ( vfolder ?. id ?? null ) )
0 commit comments