Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0582e6c
Initial plan
Copilot Jul 20, 2025
5370f07
Fix create-publisher modal background colors to use dark theme
Copilot Jul 20, 2025
5c2fba5
Add Storybook story for CreatePublisherModal to demonstrate fix
Copilot Jul 20, 2025
7fb2032
Merge branch 'main' into copilot/fix-147
snomiao Jul 23, 2025
764dbee
Merge branch 'main' into copilot/fix-147
snomiao Jul 26, 2025
08190d6
Merge branch 'main' into copilot/fix-147
snomiao Jul 26, 2025
53d4844
Merge branch 'main' into copilot/fix-147
snomiao Oct 20, 2025
fc6f2ef
fix: Update Storybook import to use nextjs-vite package
snomiao Oct 22, 2025
d5588ee
fix: Correct prop name in CreatePublisherModal stories
snomiao Oct 22, 2025
a9fe129
fix: Disable husky in CI auto-commit step
snomiao Oct 22, 2025
3d6bf7e
format: Apply prettier --fix changes
snomiao Oct 22, 2025
c96b3e3
refactor: Extract CreatePublisherFormContent component
snomiao Oct 22, 2025
e1f3da2
refactor: Update CreatePublisherModal to use CreatePublisherFormContent
snomiao Oct 22, 2025
be855c1
refactor: Update create publisher page to use CreatePublisherFormContent
snomiao Oct 22, 2025
2b39824
Merge branch 'main' into copilot/fix-147
snomiao Oct 25, 2025
6280fc2
fix: Correct import path in CreatePublisherModal stories
snomiao Oct 25, 2025
06d5341
format: Apply prettier --fix changes
snomiao Oct 25, 2025
a2037d3
Merge branch 'main' into copilot/fix-147
snomiao Oct 25, 2025
8085629
feat: Add Storybook story for create publisher page
snomiao Oct 25, 2025
874ac91
feat: Add title heading to create publisher page
snomiao Oct 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions components/pages/create.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Meta, StoryObj } from '@storybook/nextjs-vite'
import { Breadcrumb, Card } from 'flowbite-react'
import { HiHome } from 'react-icons/hi'
import CreatePublisherFormContent from '@/components/publisher/CreatePublisherFormContent'

const CreatePublisherPageLayout = () => {
const handleSuccess = (username: string) => {
console.log('Publisher created successfully:', username)
// In a real scenario, this would navigate to the publisher page
}

const handleCancel = () => {
console.log('Create publisher cancelled')
// In a real scenario, this would navigate back
}

return (
<div className="p-4 bg-gray-900 min-h-screen">
<div className="py-4">
<Breadcrumb>
<Breadcrumb.Item
href="/"
icon={HiHome}
onClick={(e) => {
e.preventDefault()
console.log('Navigate to home')
}}
className="dark"
>
Home
</Breadcrumb.Item>
<Breadcrumb.Item className="dark">
Create Publisher
</Breadcrumb.Item>
</Breadcrumb>
</div>

<section className="p-0">
<div className="flex items-center justify-center px-0 py-4 mx-auto">
<div className="w-full mx-auto shadow sm:max-w-lg">
<Card className="p-2 bg-gray-800 border border-gray-700 md:p-6 rounded-2xl">
<CreatePublisherFormContent
onSuccess={handleSuccess}
onCancel={handleCancel}
showTitle={true}
/>
</Card>
</div>
</div>
</section>
</div>
)
}

const meta: Meta<typeof CreatePublisherPageLayout> = {
title: 'Pages/Publishers/CreatePublisherPage',
component: CreatePublisherPageLayout,
parameters: {
layout: 'fullscreen',
},
}

export default meta
type Story = StoryObj<typeof CreatePublisherPageLayout>

export const Default: Story = {}
164 changes: 164 additions & 0 deletions components/publisher/CreatePublisherFormContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { isAxiosError } from 'axios'
import { Button, TextInput } from 'flowbite-react'
import { useRouter } from 'next/router'
import React, { useState } from 'react'
import { toast } from 'react-toastify'
import { useNextTranslation } from 'src/hooks/i18n'
import { customThemeTextInput } from 'utils/comfyTheme'
import { useCreatePublisher, useValidatePublisher } from '@/src/api/generated'

type CreatePublisherFormContentProps = {
onSuccess?: (username: string) => void
onCancel?: () => void
showTitle?: boolean
}

const CreatePublisherFormContent: React.FC<CreatePublisherFormContentProps> = ({
onSuccess,
onCancel,
showTitle = false,
}) => {
const { t } = useNextTranslation()
const router = useRouter()
const [username, setUsername] = useState('')
const [displayName, setDisplayName] = useState('')
const [publisherValidationError, setPublisherValidationError] = useState('')

const {
data,
isLoading: isLoadingValidation,
error,
} = useValidatePublisher({
username: username,
})
const createPublisherMutation = useCreatePublisher()

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()
createPublisherMutation.mutate(
{
data: {
id: username,
name: displayName,
},
},
{
onError: (error) => {
toast.error(
t('Could not create publisher. Please try again.')
)
},
onSuccess: () => {
toast.success('Publisher created successfully!')
if (onSuccess) {
onSuccess(username)
} else {
router.push(`/publishers/${username}`)
}
},
}
)
}

React.useEffect(() => {
if (isAxiosError(error)) {
setPublisherValidationError(error.response?.data?.message)
} else {
setPublisherValidationError('')
}
}, [error])

return (
<>
{showTitle && (
<h2 className="text-2xl font-bold text-white mb-4">
{t('Create Publisher')}
</h2>
)}
<p className="flex justify-center text-sm font-medium text-gray-400 ">
{t(
'Register a publisher to begin distributing custom nodes on Comfy.'
)}
</p>

<form
className="mt-4 space-y-4 lg:space-y-6"
onSubmit={handleSubmit}
>
<div>
<label className="block mb-1 text-xs font-bold text-white">
{t('Username')}
</label>
<TextInput
id="name"
placeholder={t('E.g. janedoe55')}
required
theme={customThemeTextInput}
type="text"
sizing="sm"
value={username}
onChange={(e) => setUsername(e.target.value)}
color={
!isLoadingValidation && publisherValidationError
? 'failure'
: 'success'
}
helperText={
<>
{isLoadingValidation && (
<>{t('Checking username...')}</>
)}
{!isLoadingValidation &&
publisherValidationError && (
<>
<span className="font-medium"></span>{' '}
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This empty span with only a className serves no purpose and should be removed. The space character following it can be achieved through other means if needed.

Suggested change
<span className="font-medium"></span>{' '}
{' '}

Copilot uses AI. Check for mistakes.
{publisherValidationError}
</>
)}
</>
}
/>
</div>
<div>
<label className="block mb-1 text-xs font-bold text-white">
{t('Display Name')}
</label>
<TextInput
sizing="sm"
theme={customThemeTextInput}
id="displayName"
className="border-gray-700 "
placeholder={t('E.g. Jane Doe')}
required
type="text"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
/>
</div>

<div className="flex center gap-4">
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The className 'center' is not a valid Tailwind CSS utility class. This should be 'justify-center' or 'items-center' depending on the intended alignment. Based on the context and similar patterns in the codebase, this should likely be 'justify-center'.

Suggested change
<div className="flex center gap-4">
<div className="flex justify-center gap-4">

Copilot uses AI. Check for mistakes.
<Button
type="button"
onClick={onCancel}
color="light"
className=" bg-gray-900"
>
<span className="text-white">{t('Cancel')}</span>
</Button>
<Button
type="submit"
color="blue"
size="sm"
disabled={
isLoadingValidation || !!publisherValidationError
}
>
{t('Create')}
</Button>
</div>
</form>
</>
)
}

export default CreatePublisherFormContent
41 changes: 41 additions & 0 deletions components/publisher/CreatePublisherModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
import CreatePublisherModal from './CreatePublisherModal'

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta: Meta<typeof CreatePublisherModal> = {
title: 'Components/Publisher/CreatePublisherModal',
component: CreatePublisherModal,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
open: { control: 'boolean' },
onCloseModal: { action: 'onCloseModal' },
onSuccess: { action: 'onSuccess' },
},
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
args: {
open: true,
onCloseModal: () => {},
},
}

export default meta
type Story = StoryObj<typeof meta>

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Default: Story = {
args: {
open: true,
},
}

export const Closed: Story = {
args: {
open: false,
},
}
Loading