mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
refactor: start on removing FF for subscription tiers (#7596)
## Summary Refactor: remove FF for subscription tier, remove legacy code for non subscription tier logic. ## Review Focus Preexisting cloud functionality impact. <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7596-refactor-start-on-removing-FF-for-subscription-tiers-2cc6d73d3650816bac3aef893e4f37cd) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -14,13 +14,7 @@
|
||||
class="p-1 text-amber-400"
|
||||
>
|
||||
<template #icon>
|
||||
<i
|
||||
:class="
|
||||
flags.subscriptionTiersEnabled
|
||||
? 'icon-[lucide--component]'
|
||||
: 'pi pi-dollar'
|
||||
"
|
||||
/>
|
||||
<i class="icon-[lucide--component]" />
|
||||
</template>
|
||||
</Tag>
|
||||
<div :class="textClass">
|
||||
@@ -36,7 +30,6 @@ import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { formatCreditsFromCents } from '@/base/credits/comfyCredits'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
|
||||
const { textClass, showCreditsOnly } = defineProps<{
|
||||
@@ -45,7 +38,6 @@ const { textClass, showCreditsOnly } = defineProps<{
|
||||
}>()
|
||||
|
||||
const authStore = useFirebaseAuthStore()
|
||||
const { flags } = useFeatureFlags()
|
||||
const balanceLoading = computed(() => authStore.isFetchingBalance)
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<!-- New Credits Design (default) -->
|
||||
<div v-if="useNewDesign" class="flex w-112 flex-col gap-8 p-8">
|
||||
<div class="flex w-112 flex-col gap-8 p-8">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4">
|
||||
<h1 class="text-2xl font-semibold text-base-foreground m-0">
|
||||
@@ -66,91 +65,32 @@
|
||||
@click="handleBuy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Legacy Design -->
|
||||
<div v-else class="flex w-96 flex-col gap-10 p-2">
|
||||
<div v-if="isInsufficientCredits" class="flex flex-col gap-4">
|
||||
<h1 class="my-0 text-2xl leading-normal font-medium">
|
||||
{{ $t('credits.topUp.insufficientTitle') }}
|
||||
</h1>
|
||||
<p class="my-0 text-base">
|
||||
{{ $t('credits.topUp.insufficientMessage') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Balance Section -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex w-full flex-col gap-2">
|
||||
<div class="text-base text-muted">
|
||||
{{ $t('credits.yourCreditBalance') }}
|
||||
</div>
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<UserCredit text-class="text-2xl" />
|
||||
<Button
|
||||
outlined
|
||||
severity="secondary"
|
||||
:label="$t('credits.topUp.seeDetails')"
|
||||
icon="pi pi-arrow-up-right"
|
||||
@click="handleSeeDetails"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Amount Input Section -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-sm text-muted"
|
||||
>{{ $t('credits.topUp.quickPurchase') }}:</span
|
||||
>
|
||||
<div class="grid grid-cols-[2fr_1fr] gap-2">
|
||||
<LegacyCreditTopUpOption
|
||||
v-for="amount in amountOptions"
|
||||
:key="amount"
|
||||
:amount="amount"
|
||||
:preselected="amount === preselectedAmountOption"
|
||||
/>
|
||||
|
||||
<LegacyCreditTopUpOption :amount="100" :preselected="false" editable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { computed, ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { creditsToUsd } from '@/base/credits/comfyCredits'
|
||||
import UserCredit from '@/components/common/UserCredit.vue'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
import CreditTopUpOption from './credit/CreditTopUpOption.vue'
|
||||
import LegacyCreditTopUpOption from './credit/LegacyCreditTopUpOption.vue'
|
||||
|
||||
interface CreditOption {
|
||||
credits: number
|
||||
description: string
|
||||
}
|
||||
|
||||
const {
|
||||
isInsufficientCredits = false,
|
||||
amountOptions = [5, 10, 20, 50],
|
||||
preselectedAmountOption = 10
|
||||
} = defineProps<{
|
||||
const { isInsufficientCredits = false } = defineProps<{
|
||||
isInsufficientCredits?: boolean
|
||||
amountOptions?: number[]
|
||||
preselectedAmountOption?: number
|
||||
}>()
|
||||
|
||||
const { flags } = useFeatureFlags()
|
||||
const { formattedRenewalDate } = useSubscription()
|
||||
// Use feature flag to determine design - defaults to true (new design)
|
||||
const useNewDesign = computed(() => flags.subscriptionTiersEnabled)
|
||||
|
||||
const { t } = useI18n()
|
||||
const authActions = useFirebaseAuthActions()
|
||||
@@ -202,8 +142,4 @@ const handleBuy = async () => {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSeeDetails = async () => {
|
||||
await authActions.accessBillingPortal()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-2">
|
||||
<Tag
|
||||
severity="secondary"
|
||||
icon="pi pi-wallet"
|
||||
rounded
|
||||
class="p-1 text-amber-400"
|
||||
/>
|
||||
<div v-if="editable" class="flex items-center gap-2">
|
||||
<InputNumber
|
||||
v-model="customAmount"
|
||||
:min="1"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
show-buttons
|
||||
:allow-empty="false"
|
||||
:highlight-on-focus="true"
|
||||
prefix="$"
|
||||
pt:pc-input-text:root="w-28"
|
||||
@blur="
|
||||
(e: InputNumberBlurEvent) =>
|
||||
(customAmount = clampUsd(Number(e.value)))
|
||||
"
|
||||
@input="
|
||||
(e: InputNumberInputEvent) =>
|
||||
(customAmount = clampUsd(Number(e.value)))
|
||||
"
|
||||
/>
|
||||
<span class="text-xs text-muted">{{ formattedCredits }}</span>
|
||||
</div>
|
||||
<div v-else class="flex flex-col leading-tight">
|
||||
<span class="text-xl font-semibold">{{ formattedCredits }}</span>
|
||||
<span class="text-xs text-muted">{{ formattedUsd }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressSpinner v-if="loading" class="h-8 w-8" />
|
||||
<Button
|
||||
v-else
|
||||
:severity="preselected ? 'primary' : 'secondary'"
|
||||
:outlined="!preselected"
|
||||
:label="$t('credits.topUp.buyNow')"
|
||||
@click="handleBuyNow"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
import type {
|
||||
InputNumberBlurEvent,
|
||||
InputNumberInputEvent
|
||||
} from 'primevue/inputnumber'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import Tag from 'primevue/tag'
|
||||
import { computed, onBeforeUnmount, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import {
|
||||
clampUsd,
|
||||
formatCreditsFromUsd,
|
||||
formatUsd
|
||||
} from '@/base/credits/comfyCredits'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
const authActions = useFirebaseAuthActions()
|
||||
const telemetry = useTelemetry()
|
||||
|
||||
const {
|
||||
amount,
|
||||
preselected,
|
||||
editable = false
|
||||
} = defineProps<{
|
||||
amount: number
|
||||
preselected: boolean
|
||||
editable?: boolean
|
||||
}>()
|
||||
|
||||
const customAmount = ref(amount)
|
||||
const didClickBuyNow = ref(false)
|
||||
const loading = ref(false)
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const displayUsdAmount = computed(() =>
|
||||
editable ? clampUsd(Number(customAmount.value)) : clampUsd(amount)
|
||||
)
|
||||
|
||||
const formattedCredits = computed(
|
||||
() =>
|
||||
`${formatCreditsFromUsd({
|
||||
usd: displayUsdAmount.value,
|
||||
locale: locale.value
|
||||
})} ${t('credits.credits')}`
|
||||
)
|
||||
|
||||
const formattedUsd = computed(
|
||||
() => `$${formatUsd({ value: displayUsdAmount.value, locale: locale.value })}`
|
||||
)
|
||||
|
||||
const handleBuyNow = async () => {
|
||||
const creditAmount = displayUsdAmount.value
|
||||
telemetry?.trackApiCreditTopupButtonPurchaseClicked(creditAmount)
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
await authActions.purchaseCredits(creditAmount)
|
||||
didClickBuyNow.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (didClickBuyNow.value) {
|
||||
// If clicked buy now, then returned back to the dialog and closed, fetch the balance
|
||||
void authActions.fetchBalance()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -138,15 +138,6 @@ vi.mock('@/composables/useExternalLink', () => ({
|
||||
}))
|
||||
}))
|
||||
|
||||
// Mock useFeatureFlags
|
||||
vi.mock('@/composables/useFeatureFlags', () => ({
|
||||
useFeatureFlags: vi.fn(() => ({
|
||||
flags: {
|
||||
subscriptionTiersEnabled: true
|
||||
}
|
||||
}))
|
||||
}))
|
||||
|
||||
// Mock useTelemetry
|
||||
vi.mock('@/platform/telemetry', () => ({
|
||||
useTelemetry: vi.fn(() => ({
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
formattedBalance
|
||||
}}</span>
|
||||
<i
|
||||
v-if="flags.subscriptionTiersEnabled"
|
||||
v-tooltip="{ value: $t('credits.unified.tooltip'), showDelay: 300 }"
|
||||
class="icon-[lucide--circle-help] cursor-help text-base text-muted-foreground mr-auto"
|
||||
/>
|
||||
@@ -147,7 +146,6 @@ import UserAvatar from '@/components/common/UserAvatar.vue'
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useExternalLink } from '@/composables/useExternalLink'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
|
||||
@@ -174,7 +172,6 @@ const {
|
||||
fetchStatus
|
||||
} = useSubscription()
|
||||
const subscriptionDialog = useSubscriptionDialog()
|
||||
const { flags } = useFeatureFlags()
|
||||
const { locale } = useI18n()
|
||||
|
||||
const formattedBalance = computed(() => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { LGraphBadge } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||
import { adjustColor } from '@/utils/colorUtil'
|
||||
|
||||
@@ -10,7 +9,6 @@ componentIconSvg.src =
|
||||
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='oklch(83.01%25 0.163 83.16)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15.536 11.293a1 1 0 0 0 0 1.414l2.376 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0zm-13.239 0a1 1 0 0 0 0 1.414l2.377 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414L6.088 8.916a1 1 0 0 0-1.414 0zm6.619 6.619a1 1 0 0 0 0 1.415l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.415l-2.377-2.376a1 1 0 0 0-1.414 0zm0-13.238a1 1 0 0 0 0 1.414l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z'/%3E%3C/svg%3E"
|
||||
|
||||
export const usePriceBadge = () => {
|
||||
const { flags } = useFeatureFlags()
|
||||
function updateSubgraphCredits(node: LGraphNode) {
|
||||
if (!node.isSubgraphNode()) return
|
||||
node.badges = node.badges.filter((b) => !isCreditsBadge(b))
|
||||
@@ -40,53 +38,26 @@ export const usePriceBadge = () => {
|
||||
|
||||
function isCreditsBadge(badge: LGraphBadge | (() => LGraphBadge)): boolean {
|
||||
const badgeInstance = typeof badge === 'function' ? badge() : badge
|
||||
if (flags.subscriptionTiersEnabled) {
|
||||
return badgeInstance.icon?.image === componentIconSvg
|
||||
} else {
|
||||
return badgeInstance.icon?.unicode === '\ue96b'
|
||||
}
|
||||
return badgeInstance.icon?.image === componentIconSvg
|
||||
}
|
||||
|
||||
const colorPaletteStore = useColorPaletteStore()
|
||||
function getCreditsBadge(price: string): LGraphBadge {
|
||||
const isLightTheme = colorPaletteStore.completedActivePalette.light_theme
|
||||
|
||||
if (flags.subscriptionTiersEnabled) {
|
||||
return new LGraphBadge({
|
||||
text: price,
|
||||
iconOptions: {
|
||||
image: componentIconSvg,
|
||||
size: 8
|
||||
},
|
||||
fgColor:
|
||||
colorPaletteStore.completedActivePalette.colors.litegraph_base
|
||||
.BADGE_FG_COLOR,
|
||||
bgColor: isLightTheme
|
||||
? adjustColor('#8D6932', { lightness: 0.5 })
|
||||
: '#8D6932'
|
||||
})
|
||||
} else {
|
||||
return new LGraphBadge({
|
||||
text: price,
|
||||
iconOptions: {
|
||||
unicode: '\ue96b',
|
||||
fontFamily: 'PrimeIcons',
|
||||
color: isLightTheme
|
||||
? adjustColor('#FABC25', { lightness: 0.5 })
|
||||
: '#FABC25',
|
||||
bgColor: isLightTheme
|
||||
? adjustColor('#654020', { lightness: 0.5 })
|
||||
: '#654020',
|
||||
fontSize: 8
|
||||
},
|
||||
fgColor:
|
||||
colorPaletteStore.completedActivePalette.colors.litegraph_base
|
||||
.BADGE_FG_COLOR,
|
||||
bgColor: isLightTheme
|
||||
? adjustColor('#8D6932', { lightness: 0.5 })
|
||||
: '#8D6932'
|
||||
})
|
||||
}
|
||||
return new LGraphBadge({
|
||||
text: price,
|
||||
iconOptions: {
|
||||
image: componentIconSvg,
|
||||
size: 8
|
||||
},
|
||||
fgColor:
|
||||
colorPaletteStore.completedActivePalette.colors.litegraph_base
|
||||
.BADGE_FG_COLOR,
|
||||
bgColor: isLightTheme
|
||||
? adjustColor('#8D6932', { lightness: 0.5 })
|
||||
: '#8D6932'
|
||||
})
|
||||
}
|
||||
return {
|
||||
getCreditsBadge,
|
||||
|
||||
@@ -13,7 +13,6 @@ export enum ServerFeatureFlag {
|
||||
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled',
|
||||
ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled',
|
||||
PRIVATE_MODELS_ENABLED = 'private_models_enabled',
|
||||
SUBSCRIPTION_TIERS_ENABLED = 'subscription_tiers_enabled',
|
||||
ONBOARDING_SURVEY_ENABLED = 'onboarding_survey_enabled'
|
||||
}
|
||||
|
||||
@@ -58,16 +57,6 @@ export function useFeatureFlags() {
|
||||
api.getServerFeature(ServerFeatureFlag.PRIVATE_MODELS_ENABLED, false)
|
||||
)
|
||||
},
|
||||
get subscriptionTiersEnabled() {
|
||||
// Check remote config first (from /api/features), fall back to websocket feature flags
|
||||
return (
|
||||
remoteConfig.value.subscription_tiers_enabled ??
|
||||
api.getServerFeature(
|
||||
ServerFeatureFlag.SUBSCRIPTION_TIERS_ENABLED,
|
||||
true // Default to true (new design)
|
||||
)
|
||||
)
|
||||
},
|
||||
get onboardingSurveyEnabled() {
|
||||
return (
|
||||
remoteConfig.value.onboarding_survey_enabled ??
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
import Button from 'primevue/button'
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
@@ -54,10 +53,7 @@ const emit = defineEmits<{
|
||||
|
||||
const { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } =
|
||||
useSubscription()
|
||||
const { flags } = useFeatureFlags()
|
||||
const shouldUseStripePricing = computed(
|
||||
() => isCloud && Boolean(flags.subscriptionTiersEnabled)
|
||||
)
|
||||
|
||||
const telemetry = useTelemetry()
|
||||
|
||||
const isLoading = ref(false)
|
||||
@@ -112,7 +108,7 @@ const stopPolling = () => {
|
||||
watch(
|
||||
[isAwaitingStripeSubscription, isActiveSubscription],
|
||||
([awaiting, isActive]) => {
|
||||
if (shouldUseStripePricing.value && awaiting && isActive) {
|
||||
if (isCloud && awaiting && isActive) {
|
||||
emit('subscribed')
|
||||
isAwaitingStripeSubscription.value = false
|
||||
}
|
||||
@@ -122,9 +118,6 @@ watch(
|
||||
const handleSubscribe = async () => {
|
||||
if (isCloud) {
|
||||
useTelemetry()?.trackSubscription('subscribe_clicked')
|
||||
}
|
||||
|
||||
if (shouldUseStripePricing.value) {
|
||||
isAwaitingStripeSubscription.value = true
|
||||
showSubscriptionDialog()
|
||||
return
|
||||
|
||||
@@ -16,9 +16,9 @@ import { useDialogService } from '@/services/dialogService'
|
||||
import type { components, operations } from '@/types/comfyRegistryTypes'
|
||||
import { useSubscriptionCancellationWatcher } from './useSubscriptionCancellationWatcher'
|
||||
|
||||
type CloudSubscriptionCheckoutResponse = {
|
||||
checkout_url: string
|
||||
}
|
||||
type CloudSubscriptionCheckoutResponse = NonNullable<
|
||||
operations['createCloudSubscriptionCheckout']['responses']['201']['content']['application/json']
|
||||
>
|
||||
|
||||
export type CloudSubscriptionStatusResponse = NonNullable<
|
||||
operations['GetCloudSubscriptionStatus']['responses']['200']['content']['application/json']
|
||||
|
||||
@@ -37,6 +37,5 @@ export type RemoteConfig = {
|
||||
model_upload_button_enabled?: boolean
|
||||
asset_update_options_enabled?: boolean
|
||||
private_models_enabled?: boolean
|
||||
subscription_tiers_enabled?: boolean
|
||||
onboarding_survey_enabled?: boolean
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { Component } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import type { SettingTreeNode } from '@/platform/settings/settingStore'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
@@ -36,7 +35,6 @@ export function useSettingUI(
|
||||
|
||||
const { shouldRenderVueNodes } = useVueFeatureFlags()
|
||||
const { isActiveSubscription } = useSubscription()
|
||||
const { flags } = useFeatureFlags()
|
||||
|
||||
const settingRoot = computed<SettingTreeNode>(() => {
|
||||
const root = buildTree(
|
||||
@@ -106,7 +104,6 @@ export function useSettingUI(
|
||||
|
||||
const shouldShowPlanCreditsPanel = computed(() => {
|
||||
if (!subscriptionPanel) return false
|
||||
if (!flags.subscriptionTiersEnabled) return true
|
||||
return isActiveSubscription.value
|
||||
})
|
||||
|
||||
|
||||
@@ -40,17 +40,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="isSubgraphNode" class="icon-[comfy--workflow] size-4" />
|
||||
<div
|
||||
v-if="isApiNode"
|
||||
:class="
|
||||
cn(
|
||||
'size-4',
|
||||
flags.subscriptionTiersEnabled
|
||||
? 'icon-[lucide--component]'
|
||||
: 'icon-[lucide--dollar-sign]'
|
||||
)
|
||||
"
|
||||
/>
|
||||
<div v-if="isApiNode" class="icon-[lucide--component] size-4" />
|
||||
|
||||
<!-- Node Title -->
|
||||
<div
|
||||
@@ -107,7 +97,6 @@ import EditableText from '@/components/common/EditableText.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import { st } from '@/i18n'
|
||||
import { LGraphEventMode, RenderShape } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
@@ -138,8 +127,6 @@ const emit = defineEmits<{
|
||||
'enter-subgraph': []
|
||||
}>()
|
||||
|
||||
const { flags } = useFeatureFlags()
|
||||
|
||||
// Error boundary implementation
|
||||
const renderError = ref<string | null>(null)
|
||||
const { toastErrorHandler } = useErrorHandling()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { describe, expect, vi } from 'vitest'
|
||||
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { LGraphBadge } from '@/lib/litegraph/src/LGraphBadge'
|
||||
import type { LGraphIcon } from '@/lib/litegraph/src/LGraphIcon'
|
||||
|
||||
import { subgraphTest } from '../../litegraph/subgraph/fixtures/subgraphFixtures'
|
||||
|
||||
@@ -17,23 +15,10 @@ vi.mock('@/stores/workspace/colorPaletteStore', () => ({
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/composables/useFeatureFlags', () => ({
|
||||
useFeatureFlags: () => ({
|
||||
flags: {
|
||||
subscriptionTiersEnabled: false // Test legacy badge behavior
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
const { updateSubgraphCredits } = usePriceBadge()
|
||||
const { updateSubgraphCredits, getCreditsBadge } = usePriceBadge()
|
||||
|
||||
const mockNode = new LGraphNode('mock node')
|
||||
const mockIcon: Partial<LGraphIcon> = { unicode: '\ue96b' }
|
||||
const badge: Partial<LGraphBadge> = {
|
||||
icon: mockIcon as LGraphIcon,
|
||||
text: '$0.05/Run'
|
||||
}
|
||||
mockNode.badges = [badge as LGraphBadge]
|
||||
mockNode.badges = [getCreditsBadge('$0.05/Run')]
|
||||
|
||||
function getBadgeText(node: LGraphNode): string {
|
||||
const badge = node.badges[0]
|
||||
|
||||
Reference in New Issue
Block a user