mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-30 01:05:46 +00:00
Compare commits
10 Commits
coderabbit
...
worktree-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f6993744a | ||
|
|
d3077b231a | ||
|
|
0344fea717 | ||
|
|
e7df1f7c13 | ||
|
|
196f7baae0 | ||
|
|
82f6b12ac2 | ||
|
|
55e67ecc3b | ||
|
|
190e1442d9 | ||
|
|
9d72372ed6 | ||
|
|
75b47c7970 |
@@ -85,7 +85,10 @@ export class ComfyNodeSearchBox {
|
||||
}
|
||||
|
||||
async removeFilter(index: number) {
|
||||
await this.filterChips.nth(index).locator('.p-chip-remove-icon').click()
|
||||
await this.filterChips
|
||||
.nth(index)
|
||||
.getByRole('button', { name: 'Remove' })
|
||||
.click()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
89
browser_tests/tests/badgeVisual.spec.ts
Normal file
89
browser_tests/tests/badgeVisual.spec.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { expect } from '@playwright/test'
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe(
|
||||
'Badge visual regression',
|
||||
{ tag: ['@screenshot', '@ui'] },
|
||||
() => {
|
||||
async function dismissToasts(comfyPage: { page: Page }) {
|
||||
// Dismiss all toast notifications that may block interaction
|
||||
const toastCloseButtons = comfyPage.page.locator('.p-toast-close-button')
|
||||
const count = await toastCloseButtons.count()
|
||||
for (let i = 0; i < count; i++) {
|
||||
await toastCloseButtons
|
||||
.nth(i)
|
||||
.click()
|
||||
.catch(() => {})
|
||||
}
|
||||
if (count > 0) {
|
||||
await comfyPage.page
|
||||
.locator('.p-toast-message')
|
||||
.first()
|
||||
.waitFor({ state: 'hidden', timeout: 3000 })
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('SearchFilterChip badge', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.settings.setSetting(
|
||||
'Comfy.NodeSearchBoxImpl',
|
||||
'v1 (legacy)'
|
||||
)
|
||||
await dismissToasts(comfyPage)
|
||||
})
|
||||
|
||||
test('Single filter chip renders correctly', async ({ comfyPage }) => {
|
||||
await comfyPage.canvasOps.doubleClick()
|
||||
await comfyPage.searchBox.addFilter('CONDITIONING', 'Input Type')
|
||||
|
||||
const searchContainer = comfyPage.page.locator(
|
||||
'.comfy-vue-node-search-container'
|
||||
)
|
||||
await expect(searchContainer).toHaveScreenshot(
|
||||
'filter-chip-conditioning.png'
|
||||
)
|
||||
})
|
||||
|
||||
test('Multiple filter chips render correctly', async ({ comfyPage }) => {
|
||||
await comfyPage.canvasOps.doubleClick()
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.addFilter('CLIP', 'Output Type')
|
||||
|
||||
const searchContainer = comfyPage.page.locator(
|
||||
'.comfy-vue-node-search-container'
|
||||
)
|
||||
await expect(searchContainer).toHaveScreenshot(
|
||||
'filter-chips-multiple.png'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Node library tree badge', () => {
|
||||
test('Folder node count badge renders correctly', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await dismissToasts(comfyPage)
|
||||
|
||||
// Open the Node Library sidebar
|
||||
const sidebarButton = comfyPage.page.getByRole('button', {
|
||||
name: 'Node Library'
|
||||
})
|
||||
await sidebarButton.click()
|
||||
|
||||
// Wait for tree folders to load
|
||||
const sidebar = comfyPage.page.getByRole('complementary', {
|
||||
name: 'Sidebar'
|
||||
})
|
||||
await sidebar
|
||||
.getByRole('treeitem')
|
||||
.first()
|
||||
.waitFor({ state: 'visible', timeout: 10000 })
|
||||
|
||||
await expect(sidebar).toHaveScreenshot('node-library-tree-badges.png')
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -110,7 +110,7 @@ test.describe('Node search box', { tag: '@node' }, () => {
|
||||
async ({ comfyPage }) => {
|
||||
await comfyPage.canvasOps.disconnectEdge()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
await comfyPage.page.locator('.p-chip-remove-icon').click()
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler', {
|
||||
exact: true
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@ const mountActionbar = (showRunProgressBar: boolean) => {
|
||||
name: 'Panel',
|
||||
template: '<div><slot /></div>'
|
||||
},
|
||||
StatusBadge: true,
|
||||
Badge: true,
|
||||
ComfyRunButton: {
|
||||
name: 'ComfyRunButton',
|
||||
template: '<button type="button">Run</button>'
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<span class="text-sm font-normal tabular-nums">
|
||||
{{ activeJobsLabel }}
|
||||
</span>
|
||||
<StatusBadge
|
||||
<Badge
|
||||
v-if="activeJobsCount > 0"
|
||||
data-testid="active-jobs-indicator"
|
||||
variant="dot"
|
||||
@@ -104,7 +104,7 @@ import { computed, nextTick, ref, watch } from 'vue'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import QueueInlineProgress from '@/components/queue/QueueInlineProgress.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useQueueFeatureFlags } from '@/composables/queue/useQueueFeatureFlags'
|
||||
|
||||
116
src/components/common/Badge.stories.ts
Normal file
116
src/components/common/Badge.stories.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Badge from './Badge.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Badges/Badge',
|
||||
component: Badge,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
label: { control: 'text' },
|
||||
severity: {
|
||||
control: 'select',
|
||||
options: ['default', 'secondary', 'warn', 'danger', 'contrast']
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['label', 'dot', 'circle']
|
||||
}
|
||||
},
|
||||
args: {
|
||||
label: 'NEW',
|
||||
severity: 'default'
|
||||
}
|
||||
} satisfies Meta<typeof Badge>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
label: 'NEW',
|
||||
severity: 'secondary'
|
||||
}
|
||||
}
|
||||
|
||||
export const Warn: Story = {
|
||||
args: {
|
||||
label: 'NEW',
|
||||
severity: 'warn'
|
||||
}
|
||||
}
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
label: 'NEW',
|
||||
severity: 'danger'
|
||||
}
|
||||
}
|
||||
|
||||
export const Contrast: Story = {
|
||||
args: {
|
||||
label: 'NEW',
|
||||
severity: 'contrast'
|
||||
}
|
||||
}
|
||||
|
||||
export const Circle: Story = {
|
||||
args: {
|
||||
label: '3',
|
||||
variant: 'circle'
|
||||
}
|
||||
}
|
||||
|
||||
export const AllSeveritiesLabel: Story = {
|
||||
render: () => ({
|
||||
components: { Badge },
|
||||
template: `
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge label="NEW" severity="default" />
|
||||
<Badge label="NEW" severity="secondary" />
|
||||
<Badge label="NEW" severity="warn" />
|
||||
<Badge label="NEW" severity="danger" />
|
||||
<Badge label="NEW" severity="contrast" />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export const AllSeveritiesDot: Story = {
|
||||
render: () => ({
|
||||
components: { Badge },
|
||||
template: `
|
||||
<div class="flex items-center gap-2">
|
||||
<Badge variant="dot" severity="default" />
|
||||
<Badge variant="dot" severity="secondary" />
|
||||
<Badge variant="dot" severity="warn" />
|
||||
<Badge variant="dot" severity="danger" />
|
||||
<Badge variant="dot" severity="contrast" />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export const AllVariants: Story = {
|
||||
render: () => ({
|
||||
components: { Badge },
|
||||
template: `
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<Badge label="NEW" variant="label" />
|
||||
<span class="text-xs text-muted">label</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<Badge variant="dot" severity="danger" />
|
||||
<span class="text-xs text-muted">dot</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<Badge label="5" variant="circle" />
|
||||
<span class="text-xs text-muted">circle</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import { computed } from 'vue'
|
||||
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import { statusBadgeVariants } from './statusBadge.variants'
|
||||
import type { StatusBadgeVariants } from './statusBadge.variants'
|
||||
import { badgeVariants } from './badge.variants'
|
||||
import type { BadgeVariants } from './badge.variants'
|
||||
|
||||
const {
|
||||
label,
|
||||
@@ -13,14 +13,14 @@ const {
|
||||
class: className
|
||||
} = defineProps<{
|
||||
label?: string | number
|
||||
severity?: StatusBadgeVariants['severity']
|
||||
variant?: StatusBadgeVariants['variant']
|
||||
severity?: BadgeVariants['severity']
|
||||
variant?: BadgeVariants['variant']
|
||||
class?: string
|
||||
}>()
|
||||
|
||||
const badgeClass = computed(() =>
|
||||
cn(
|
||||
statusBadgeVariants({
|
||||
badgeVariants({
|
||||
severity,
|
||||
variant: variant ?? (label == null ? 'dot' : 'label')
|
||||
}),
|
||||
@@ -1,17 +1,28 @@
|
||||
<template>
|
||||
<Chip removable @remove="emit('remove', $event)">
|
||||
<Badge size="small" :class="semanticBadgeClass">
|
||||
{{ badge }}
|
||||
</Badge>
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-2xl bg-surface-700 py-0.5 pr-1 pl-2 text-xs"
|
||||
>
|
||||
<Badge :label="badge" :class="semanticBadgeClass" />
|
||||
{{ text }}
|
||||
</Chip>
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="$t('g.remove')"
|
||||
class="inline-flex cursor-pointer items-center justify-center rounded-full p-0.5 hover:bg-surface-600"
|
||||
@click="emit('remove', $event)"
|
||||
>
|
||||
<i
|
||||
class="icon-[lucide--x] size-3 text-muted-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Badge from 'primevue/badge'
|
||||
import Chip from 'primevue/chip'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
|
||||
export interface SearchFilter {
|
||||
text: string
|
||||
badge: string
|
||||
@@ -20,9 +31,9 @@ export interface SearchFilter {
|
||||
}
|
||||
|
||||
const semanticClassMap: Record<string, string> = {
|
||||
'i-badge': 'bg-green-500 text-white',
|
||||
'o-badge': 'bg-red-500 text-white',
|
||||
'c-badge': 'bg-blue-500 text-white',
|
||||
'i-badge': 'bg-green-500 text-[color:white]',
|
||||
'o-badge': 'bg-red-500 text-[color:white]',
|
||||
'c-badge': 'bg-blue-500 text-[color:white]',
|
||||
's-badge': 'bg-yellow-500'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import StatusBadge from './StatusBadge.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Common/StatusBadge',
|
||||
component: StatusBadge,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
label: { control: 'text' },
|
||||
severity: {
|
||||
control: 'select',
|
||||
options: ['default', 'secondary', 'warn', 'danger', 'contrast']
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['label', 'dot', 'circle']
|
||||
}
|
||||
},
|
||||
args: {
|
||||
label: 'Status',
|
||||
severity: 'default'
|
||||
}
|
||||
} satisfies Meta<typeof StatusBadge>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const Failed: Story = {
|
||||
args: {
|
||||
label: 'Failed',
|
||||
severity: 'danger'
|
||||
}
|
||||
}
|
||||
|
||||
export const Finished: Story = {
|
||||
args: {
|
||||
label: 'Finished',
|
||||
severity: 'contrast'
|
||||
}
|
||||
}
|
||||
|
||||
export const Dot: Story = {
|
||||
args: {
|
||||
label: undefined,
|
||||
variant: 'dot',
|
||||
severity: 'danger'
|
||||
}
|
||||
}
|
||||
|
||||
export const Circle: Story = {
|
||||
args: {
|
||||
label: '3',
|
||||
variant: 'circle'
|
||||
}
|
||||
}
|
||||
|
||||
export const AllSeverities: Story = {
|
||||
render: () => ({
|
||||
components: { StatusBadge },
|
||||
template: `
|
||||
<div class="flex items-center gap-2">
|
||||
<StatusBadge label="Default" severity="default" />
|
||||
<StatusBadge label="Secondary" severity="secondary" />
|
||||
<StatusBadge label="Warn" severity="warn" />
|
||||
<StatusBadge label="Danger" severity="danger" />
|
||||
<StatusBadge label="Contrast" severity="contrast" />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export const AllVariants: Story = {
|
||||
render: () => ({
|
||||
components: { StatusBadge },
|
||||
template: `
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<StatusBadge label="Label" variant="label" />
|
||||
<span class="text-xs text-muted">label</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<StatusBadge variant="dot" severity="danger" />
|
||||
<span class="text-xs text-muted">dot</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1">
|
||||
<StatusBadge label="5" variant="circle" />
|
||||
<span class="text-xs text-muted">circle</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Badge from 'primevue/badge'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
|
||||
@@ -61,8 +61,7 @@ describe('TreeExplorerTreeNode', () => {
|
||||
expect(wrapper.findComponent(EditableText).props('modelValue')).toBe(
|
||||
'Test Node'
|
||||
)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
expect(wrapper.findComponent(Badge).props()['value'].toString()).toBe('3')
|
||||
expect(wrapper.findComponent(Badge).props('label')).toBe('3')
|
||||
})
|
||||
|
||||
it('makes node label editable when renamingEditingNode matches', async () => {
|
||||
|
||||
@@ -23,9 +23,10 @@
|
||||
</span>
|
||||
<Badge
|
||||
v-if="showNodeBadgeText"
|
||||
:value="nodeBadgeText"
|
||||
:label="nodeBadgeText"
|
||||
severity="secondary"
|
||||
class="leaf-count-badge"
|
||||
:variant="nodeBadgeText.length > 1 ? 'label' : 'circle'"
|
||||
class="ml-2"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -38,7 +39,7 @@
|
||||
|
||||
<script setup lang="ts" generic="T">
|
||||
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview'
|
||||
import Badge from 'primevue/badge'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import { computed, inject, ref } from 'vue'
|
||||
|
||||
import EditableText from '@/components/common/EditableText.vue'
|
||||
@@ -146,9 +147,6 @@ if (props.node.droppable) {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.leaf-count-badge {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.node-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
28
src/components/common/badge.variants.ts
Normal file
28
src/components/common/badge.variants.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { VariantProps } from 'cva'
|
||||
import { cva } from 'cva'
|
||||
|
||||
export const badgeVariants = cva({
|
||||
base: 'inline-flex items-center justify-center rounded-full',
|
||||
variants: {
|
||||
/* eslint-disable better-tailwindcss/enforce-canonical-classes -- text-[color:*] prevents twMerge from clobbering color with font-size */
|
||||
severity: {
|
||||
default: 'bg-primary-background text-[color:white]',
|
||||
secondary: 'bg-secondary-background-hover text-[color:white]',
|
||||
warn: 'bg-warning-background text-[color:white]',
|
||||
danger: 'bg-destructive-background text-[color:white]',
|
||||
contrast: 'bg-base-foreground text-[color:var(--color-base-background)]'
|
||||
},
|
||||
/* eslint-enable better-tailwindcss/enforce-canonical-classes */
|
||||
variant: {
|
||||
label: 'h-3.5 px-1 text-xxxs font-semibold uppercase',
|
||||
dot: 'size-2',
|
||||
circle: 'size-3.5 text-xxxs font-semibold'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
severity: 'default',
|
||||
variant: 'label'
|
||||
}
|
||||
})
|
||||
|
||||
export type BadgeVariants = VariantProps<typeof badgeVariants>
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { VariantProps } from 'cva'
|
||||
import { cva } from 'cva'
|
||||
|
||||
export const statusBadgeVariants = cva({
|
||||
base: 'inline-flex items-center justify-center rounded-full',
|
||||
variants: {
|
||||
severity: {
|
||||
default: 'bg-primary-background text-base-foreground',
|
||||
secondary: 'bg-secondary-background text-base-foreground',
|
||||
warn: 'bg-warning-background text-base-background',
|
||||
danger: 'bg-destructive-background text-white',
|
||||
contrast: 'bg-base-foreground text-base-background'
|
||||
},
|
||||
variant: {
|
||||
label: 'h-3.5 px-1 text-xxxs font-semibold uppercase',
|
||||
dot: 'size-2',
|
||||
circle: 'size-3.5 text-xxxs font-semibold'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
severity: 'default',
|
||||
variant: 'label'
|
||||
}
|
||||
})
|
||||
|
||||
export type StatusBadgeVariants = VariantProps<typeof statusBadgeVariants>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Badge from 'primevue/badge'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import Column from 'primevue/column'
|
||||
import PrimeVue from 'primevue/config'
|
||||
@@ -125,13 +125,13 @@ describe('UsageLogsTable', () => {
|
||||
mockCustomerEventsService.getEventSeverity.mockImplementation((type) => {
|
||||
switch (type) {
|
||||
case EventType.CREDIT_ADDED:
|
||||
return 'success'
|
||||
return 'default'
|
||||
case EventType.ACCOUNT_CREATED:
|
||||
return 'info'
|
||||
return 'secondary'
|
||||
case EventType.API_USAGE_COMPLETED:
|
||||
return 'warning'
|
||||
return 'warn'
|
||||
default:
|
||||
return 'info'
|
||||
return 'secondary'
|
||||
}
|
||||
})
|
||||
mockCustomerEventsService.formatAmount.mockImplementation((amount) => {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<Column field="event_type" :header="$t('credits.eventType')">
|
||||
<template #body="{ data }">
|
||||
<Badge
|
||||
:value="customerEventService.formatEventType(data.event_type)"
|
||||
:label="customerEventService.formatEventType(data.event_type)"
|
||||
:severity="customerEventService.getEventSeverity(data.event_type)"
|
||||
/>
|
||||
</template>
|
||||
@@ -91,7 +91,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Badge from 'primevue/badge'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import Column from 'primevue/column'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Message from 'primevue/message'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Loader from '@/components/loader/Loader.vue'
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import type { AssetDownload } from '@/stores/assetDownloadStore'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
@@ -40,11 +40,11 @@ const isPending = computed(() => job.status === 'created')
|
||||
<i
|
||||
class="icon-[lucide--circle-alert] size-4 text-destructive-background"
|
||||
/>
|
||||
<StatusBadge :label="t('progressToast.failed')" severity="danger" />
|
||||
<Badge :label="t('progressToast.failed')" severity="danger" />
|
||||
</template>
|
||||
|
||||
<template v-else-if="isCompleted">
|
||||
<StatusBadge :label="t('progressToast.finished')" severity="contrast" />
|
||||
<Badge :label="t('progressToast.finished')" severity="contrast" />
|
||||
</template>
|
||||
|
||||
<template v-else-if="isRunning">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<span ref="textRef" class="min-w-0 truncate">
|
||||
<slot />
|
||||
</span>
|
||||
<StatusBadge
|
||||
<Badge
|
||||
v-if="badge !== undefined"
|
||||
:label="String(badge)"
|
||||
severity="contrast"
|
||||
@@ -33,7 +33,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import type { NavItemData } from '@/types/navTypes'
|
||||
|
||||
import NavIcon from './NavIcon.vue'
|
||||
|
||||
@@ -119,9 +119,10 @@
|
||||
@click.stop="handleSelect"
|
||||
>
|
||||
{{ $t('g.use') }}
|
||||
<StatusBadge
|
||||
<Badge
|
||||
v-if="isNewlyImported"
|
||||
severity="contrast"
|
||||
variant="dot"
|
||||
class="absolute -top-0.5 -right-0.5"
|
||||
/>
|
||||
</Button>
|
||||
@@ -137,7 +138,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import IconGroup from '@/components/button/IconGroup.vue'
|
||||
import MoreButton from '@/components/button/MoreButton.vue'
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import { showConfirmDialog } from '@/components/dialog/confirm/confirmDialog'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import AssetBadgeGroup from '@/platform/assets/components/AssetBadgeGroup.vue'
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"
|
||||
@click="$emit('stepClick', step.name)"
|
||||
>
|
||||
<StatusBadge
|
||||
<Badge
|
||||
:label="step.number"
|
||||
variant="circle"
|
||||
severity="contrast"
|
||||
@@ -67,7 +67,7 @@ import { computed } from 'vue'
|
||||
|
||||
import { vAutoAnimate } from '@formkit/auto-animate/vue'
|
||||
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import type { ComfyHubPublishStep } from '@/platform/workflow/sharing/composables/useComfyHubPublishWizard'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<span class="text-sm font-bold text-text-primary">
|
||||
{{ subscriptionTierName }}
|
||||
</span>
|
||||
<StatusBadge
|
||||
<Badge
|
||||
v-if="isCancelled"
|
||||
:label="$t('subscription.canceled')"
|
||||
severity="warn"
|
||||
@@ -365,7 +365,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import Badge from '@/components/common/Badge.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useBillingContext } from '@/composables/billing/useBillingContext'
|
||||
import { useBillingOperationStore } from '@/platform/workspace/stores/billingOperationStore'
|
||||
|
||||
@@ -216,15 +216,17 @@ describe('useCustomerEventsService', () => {
|
||||
|
||||
describe('getEventSeverity', () => {
|
||||
it('should return correct severity for known event types', () => {
|
||||
expect(service.getEventSeverity(EventType.CREDIT_ADDED)).toBe('success')
|
||||
expect(service.getEventSeverity(EventType.ACCOUNT_CREATED)).toBe('info')
|
||||
expect(service.getEventSeverity(EventType.CREDIT_ADDED)).toBe('default')
|
||||
expect(service.getEventSeverity(EventType.ACCOUNT_CREATED)).toBe(
|
||||
'secondary'
|
||||
)
|
||||
expect(service.getEventSeverity(EventType.API_USAGE_COMPLETED)).toBe(
|
||||
'warning'
|
||||
'warn'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return default severity for unknown event types', () => {
|
||||
expect(service.getEventSeverity('unknown_event')).toBe('info')
|
||||
expect(service.getEventSeverity('unknown_event')).toBe('secondary')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { AxiosError, AxiosResponse } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
import type { BadgeVariants } from '@/components/common/badge.variants'
|
||||
import { getComfyApiBaseUrl } from '@/config/comfyApi'
|
||||
import { d } from '@/i18n'
|
||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||
@@ -133,16 +134,18 @@ export const useCustomerEventsService = () => {
|
||||
return value
|
||||
}
|
||||
|
||||
function getEventSeverity(eventType: string) {
|
||||
function getEventSeverity(
|
||||
eventType: string
|
||||
): NonNullable<BadgeVariants['severity']> {
|
||||
switch (eventType) {
|
||||
case 'credit_added':
|
||||
return 'success'
|
||||
return 'default'
|
||||
case 'account_created':
|
||||
return 'info'
|
||||
return 'secondary'
|
||||
case 'api_usage_completed':
|
||||
return 'warning'
|
||||
return 'warn'
|
||||
default:
|
||||
return 'info'
|
||||
return 'secondary'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user