@@ -31,6 +31,7 @@ import {
3131 type ChatFormData,
3232 useCreateChat,
3333 useDeleteChat,
34+ useRevealChatPassword,
3435 useUpdateChat,
3536} from '@/hooks/queries/chats'
3637import type { ChatDetail } from '@/hooks/queries/deployments'
@@ -41,6 +42,7 @@ import {
4142 getPasswordPlaceholder,
4243 hasExistingPassword,
4344 isPasswordRequired,
45+ shouldConfirmPasswordChange,
4446} from './utils'
4547
4648const logger = createLogger('ChatDeploy')
@@ -57,6 +59,7 @@ interface ChatDeployProps {
5759 onRefetchChat: () => Promise<void>
5860 chatSubmitting: boolean
5961 setChatSubmitting: (submitting: boolean) => void
62+ canRevealPassword: boolean
6063 onValidationChange?: (isValid: boolean) => void
6164 showDeleteConfirmation?: boolean
6265 setShowDeleteConfirmation?: (show: boolean) => void
@@ -97,6 +100,7 @@ export function ChatDeploy({
97100 onRefetchChat,
98101 chatSubmitting,
99102 setChatSubmitting,
103+ canRevealPassword,
100104 onValidationChange,
101105 showDeleteConfirmation: externalShowDeleteConfirmation,
102106 setShowDeleteConfirmation: externalSetShowDeleteConfirmation,
@@ -106,6 +110,7 @@ export function ChatDeploy({
106110}: ChatDeployProps) {
107111 const [imageUrl, setImageUrl] = useState<string | null>(null)
108112 const [internalShowDeleteConfirmation, setInternalShowDeleteConfirmation] = useState(false)
113+ const [showPasswordChangeConfirmation, setShowPasswordChangeConfirmation] = useState(false)
109114
110115 const showDeleteConfirmation =
111116 externalShowDeleteConfirmation !== undefined
@@ -213,9 +218,7 @@ export function ChatDeploy({
213218 }
214219 }, [existingChat, isLoadingChat])
215220
216- const handleSubmit = async (e?: React.FormEvent) => {
217- if (e) e.preventDefault()
218-
221+ const submitChat = async (passwordChangeConfirmed = false) => {
219222 if (chatSubmitting) return
220223
221224 setChatSubmitting(true)
@@ -227,14 +230,20 @@ export function ChatDeploy({
227230 try {
228231 if (!validateForm()) {
229232 newTab?.close()
230- setChatSubmitting(false)
231233 return
232234 }
233235
234236 if (!isIdentifierValid && formData.identifier !== existingChat?.identifier) {
235237 newTab?.close()
236238 setError('identifier', 'Please wait for identifier validation to complete')
237- setChatSubmitting(false)
239+ return
240+ }
241+
242+ if (
243+ !passwordChangeConfirmed &&
244+ shouldConfirmPasswordChange(Boolean(existingChat?.id), formData.authType, formData.password)
245+ ) {
246+ setShowPasswordChangeConfirmation(true)
238247 return
239248 }
240249
@@ -283,6 +292,11 @@ export function ChatDeploy({
283292 }
284293 }
285294
295+ const handleSubmit = async (event: React.FormEvent) => {
296+ event.preventDefault()
297+ await submitChat()
298+ }
299+
286300 const handleDelete = async () => {
287301 if (!existingChat || !existingChat.id) return
288302
@@ -306,6 +320,11 @@ export function ChatDeploy({
306320 }
307321 }
308322
323+ const handleConfirmPasswordChange = async () => {
324+ setShowPasswordChangeConfirmation(false)
325+ await submitChat(true)
326+ }
327+
309328 if (isLoadingChat) {
310329 return <LoadingSkeleton />
311330 }
@@ -404,6 +423,8 @@ export function ChatDeploy({
404423
405424 <AuthSelector
406425 key={`${existingChat?.id ?? 'new'}-${formInitCounter}`}
426+ chatId={existingChat?.id ?? null}
427+ canRevealPassword={canRevealPassword}
407428 authType={formData.authType}
408429 savedAuthType={existingChat?.authType as AuthType | undefined}
409430 password={formData.password}
@@ -445,6 +466,21 @@ export function ChatDeploy({
445466 </div>
446467 </form>
447468
469+ <ChipConfirmModal
470+ open={showPasswordChangeConfirmation}
471+ onOpenChange={setShowPasswordChangeConfirmation}
472+ srTitle='Change deployment password'
473+ title='Change deployment password?'
474+ text='Are you sure you want to change the password for this deployment?'
475+ confirm={{
476+ label: 'Change Password and Redeploy',
477+ onClick: handleConfirmPasswordChange,
478+ variant: 'primary',
479+ pending: chatSubmitting,
480+ pendingLabel: 'Updating...',
481+ }}
482+ />
483+
448484 <ChipConfirmModal
449485 open={showDeleteConfirmation}
450486 onOpenChange={setShowDeleteConfirmation}
@@ -617,6 +653,8 @@ function IdentifierInput({
617653}
618654
619655interface AuthSelectorProps {
656+ chatId: string | null
657+ canRevealPassword: boolean
620658 authType: AuthType
621659 /** The persisted mode of an existing chat, kept selectable even if newly disallowed. */
622660 savedAuthType?: AuthType
@@ -638,6 +676,8 @@ const AUTH_LABELS: Record<AuthType, string> = {
638676}
639677
640678function AuthSelector({
679+ chatId,
680+ canRevealPassword,
641681 authType,
642682 savedAuthType,
643683 password,
@@ -651,6 +691,7 @@ function AuthSelector({
651691}: AuthSelectorProps) {
652692 const [emailError, setEmailError] = useState('')
653693 const [invalidEmailItems, setInvalidEmailItems] = useState<TagItem[]>([])
694+ const revealPasswordMutation = useRevealChatPassword()
654695
655696 const emailsRef = useRef(emails)
656697 const invalidEmailItemsRef = useRef(invalidEmailItems)
@@ -756,9 +797,19 @@ function AuthSelector({
756797 value={password}
757798 onChange={onPasswordChange}
758799 disabled={disabled}
759- placeholder={getPasswordPlaceholder(hasExistingPassword )}
800+ placeholder={hasExistingPassword ? '' : getPasswordPlaceholder(false )}
760801 required={!hasExistingPassword}
802+ fetchCurrentPassword={
803+ canRevealPassword && chatId && hasExistingPassword
804+ ? () => revealPasswordMutation.mutateAsync({ chatId })
805+ : undefined
806+ }
761807 />
808+ {canRevealPassword && revealPasswordMutation.isError && (
809+ <p className='mt-[6.5px] text-[var(--text-error)] text-caption'>
810+ Failed to load the current password
811+ </p>
812+ )}
762813 <p className='mt-[6.5px] text-[var(--text-secondary)] text-xs'>
763814 {getPasswordHelperText(hasExistingPassword)}
764815 </p>
0 commit comments