diff --git a/frontend/src/components/payment/SubscriptionPlanCard.vue b/frontend/src/components/payment/SubscriptionPlanCard.vue index fbaa2744..687f119e 100644 --- a/frontend/src/components/payment/SubscriptionPlanCard.vue +++ b/frontend/src/components/payment/SubscriptionPlanCard.vue @@ -147,6 +147,7 @@ const MODEL_SCOPE_LABELS: Record = { } const modelScopeLabels = computed(() => { + if (platform.value !== 'antigravity') return [] const scopes = props.plan.supported_model_scopes if (!scopes || scopes.length === 0) return [] return scopes.map(s => MODEL_SCOPE_LABELS[s] || s) diff --git a/frontend/src/components/payment/__tests__/SubscriptionPlanCard.spec.ts b/frontend/src/components/payment/__tests__/SubscriptionPlanCard.spec.ts new file mode 100644 index 00000000..ebe695ee --- /dev/null +++ b/frontend/src/components/payment/__tests__/SubscriptionPlanCard.spec.ts @@ -0,0 +1,64 @@ +import { mount } from "@vue/test-utils"; +import { describe, expect, it } from "vitest"; +import { createI18n } from "vue-i18n"; +import SubscriptionPlanCard from "../SubscriptionPlanCard.vue"; + +const i18n = createI18n({ + legacy: false, + locale: "en", + fallbackWarn: false, + missingWarn: false, + messages: { + en: { + payment: { + days: "days", + models: "Models", + planCard: { + quota: "Quota", + rate: "Rate", + unlimited: "Unlimited", + }, + subscribeNow: "Subscribe now", + }, + }, + }, +}); + +const mountPlanCard = (groupPlatform: string) => + mount(SubscriptionPlanCard, { + props: { + plan: { + id: 1, + group_id: 10, + group_platform: groupPlatform, + name: "Pro", + price: 10, + amount: 1000, + features: [], + rate_multiplier: 1, + validity_days: 30, + validity_unit: "day", + supported_model_scopes: ["claude", "gemini_text", "gemini_image"], + is_active: true, + }, + }, + global: { plugins: [i18n] }, + }); + +describe("SubscriptionPlanCard", () => { + it("does not show Antigravity model scopes for OpenAI plans", () => { + const text = mountPlanCard("openai").text(); + + expect(text).not.toContain("Claude"); + expect(text).not.toContain("Gemini"); + expect(text).not.toContain("Imagen"); + }); + + it("shows model scopes for Antigravity plans", () => { + const text = mountPlanCard("antigravity").text(); + + expect(text).toContain("Claude"); + expect(text).toContain("Gemini"); + expect(text).toContain("Imagen"); + }); +}); diff --git a/frontend/src/views/admin/GroupsView.vue b/frontend/src/views/admin/GroupsView.vue index 753d52dd..f5d39a04 100644 --- a/frontend/src/views/admin/GroupsView.vue +++ b/frontend/src/views/admin/GroupsView.vue @@ -2865,6 +2865,7 @@ import { resetMessagesDispatchFormState, type MessagesDispatchMappingRow, } from "./groupsMessagesDispatch"; +import { normalizeSupportedModelScopesForPlatform } from "./groupsSupportedModelScopes"; const { t } = useI18n(); const appStore = useAppStore(); @@ -3710,6 +3711,10 @@ const handleCreateGroup = async () => { model_routing: convertRoutingRulesToApiFormat( createModelRoutingRules.value, ), + supported_model_scopes: normalizeSupportedModelScopesForPlatform( + createForm.platform, + createForm.supported_model_scopes, + ), messages_dispatch_model_config: createForm.platform === "openai" ? messagesDispatchFormStateToConfig({ @@ -3841,6 +3846,10 @@ const handleUpdateGroup = async () => { model_routing: convertRoutingRulesToApiFormat( editModelRoutingRules.value, ), + supported_model_scopes: normalizeSupportedModelScopesForPlatform( + editForm.platform, + editForm.supported_model_scopes, + ), messages_dispatch_model_config: editForm.platform === "openai" ? messagesDispatchFormStateToConfig({ diff --git a/frontend/src/views/admin/__tests__/groupsSupportedModelScopes.spec.ts b/frontend/src/views/admin/__tests__/groupsSupportedModelScopes.spec.ts new file mode 100644 index 00000000..182fafbd --- /dev/null +++ b/frontend/src/views/admin/__tests__/groupsSupportedModelScopes.spec.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { normalizeSupportedModelScopesForPlatform } from "../groupsSupportedModelScopes"; + +describe("normalizeSupportedModelScopesForPlatform", () => { + it("preserves model scopes for Antigravity groups", () => { + expect( + normalizeSupportedModelScopesForPlatform("antigravity", [ + "claude", + "gemini_text", + ]), + ).toEqual(["claude", "gemini_text"]); + }); + + it("returns an empty array for Antigravity groups without scopes", () => { + expect(normalizeSupportedModelScopesForPlatform("antigravity", undefined)).toEqual([]); + }); + + it("drops hidden model scopes for OpenAI groups", () => { + expect( + normalizeSupportedModelScopesForPlatform("openai", [ + "claude", + "gemini_text", + "gemini_image", + ]), + ).toEqual([]); + }); + + it("drops hidden model scopes for other non-Antigravity groups", () => { + expect(normalizeSupportedModelScopesForPlatform("claude", ["claude"])).toEqual([]); + }); +}); diff --git a/frontend/src/views/admin/groupsSupportedModelScopes.ts b/frontend/src/views/admin/groupsSupportedModelScopes.ts new file mode 100644 index 00000000..6f20e0d3 --- /dev/null +++ b/frontend/src/views/admin/groupsSupportedModelScopes.ts @@ -0,0 +1,7 @@ +export const normalizeSupportedModelScopesForPlatform = ( + platform: string, + scopes: string[] | undefined, +): string[] => { + if (platform !== "antigravity") return []; + return scopes ?? []; +};