1212import { create } from '@bufbuild/protobuf' ;
1313import { Button } from 'components/redpanda-ui/components/button' ;
1414import { Checkbox } from 'components/redpanda-ui/components/checkbox' ;
15+ import { CopyButton } from 'components/redpanda-ui/components/copy-button' ;
1516import {
1617 Dialog ,
1718 DialogContent ,
@@ -30,9 +31,10 @@ import {
3031 SelectValue ,
3132} from 'components/redpanda-ui/components/select' ;
3233import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from 'components/redpanda-ui/components/tooltip' ;
33- import { Check , Copy , RefreshCw } from 'lucide-react' ;
34+ import { Text } from 'components/redpanda-ui/components/typography' ;
35+ import { RefreshCw } from 'lucide-react' ;
3436import { UpdateUserRequest_UserSchema , UpdateUserRequestSchema } from 'protogen/redpanda/api/dataplane/v1/user_pb' ;
35- import { useState } from 'react' ;
37+ import { useEffect , useState } from 'react' ;
3638import { getSASLMechanism , useUpdateUserMutationWithToast } from 'react-query/api/user' ;
3739import { toast } from 'sonner' ;
3840
@@ -51,28 +53,39 @@ interface ChangePasswordDialogProps {
5153}
5254
5355export function ChangePasswordDialog ( { open, userName, currentMechanism, onClose } : ChangePasswordDialogProps ) {
54- const [ newPassword , setNewPassword ] = useState ( '' ) ;
55- const [ confirmPassword , setConfirmPassword ] = useState ( '' ) ;
56+ const [ newPassword , setNewPassword ] = useState ( ( ) => generatePassword ( 24 , true ) ) ;
5657 const [ selectedMechanism , setSelectedMechanism ] = useState < 'SCRAM-SHA-256' | 'SCRAM-SHA-512' > (
5758 currentMechanism === 'SCRAM-SHA-256' || currentMechanism === 'SCRAM-SHA-512' ? currentMechanism : 'SCRAM-SHA-512'
5859 ) ;
5960 const [ error , setError ] = useState < string | null > ( null ) ;
60-
6161 const [ includeSpecialChars , setIncludeSpecialChars ] = useState ( true ) ;
62- const [ copied , setCopied ] = useState ( false ) ;
6362 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
6463
6564 const { mutateAsync : updateUser } = useUpdateUserMutationWithToast ( ) ;
6665
6766 const resetForm = ( ) => {
68- setNewPassword ( '' ) ;
69- setConfirmPassword ( '' ) ;
67+ setNewPassword ( generatePassword ( 24 , true ) ) ;
68+ setSelectedMechanism (
69+ currentMechanism === 'SCRAM-SHA-256' || currentMechanism === 'SCRAM-SHA-512' ? currentMechanism : 'SCRAM-SHA-512'
70+ ) ;
7071 setError ( null ) ;
7172 setIncludeSpecialChars ( true ) ;
72- setCopied ( false ) ;
7373 setIsSubmitting ( false ) ;
7474 } ;
7575
76+ useEffect ( ( ) => {
77+ if ( ! open ) {
78+ return ;
79+ }
80+ setNewPassword ( generatePassword ( 24 , true ) ) ;
81+ setSelectedMechanism (
82+ currentMechanism === 'SCRAM-SHA-256' || currentMechanism === 'SCRAM-SHA-512' ? currentMechanism : 'SCRAM-SHA-512'
83+ ) ;
84+ setError ( null ) ;
85+ setIncludeSpecialChars ( true ) ;
86+ setIsSubmitting ( false ) ;
87+ } , [ currentMechanism , open ] ) ;
88+
7689 const handleClose = ( ) => {
7790 resetForm ( ) ;
7891 onClose ( ) ;
@@ -81,17 +94,7 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
8194 const handleGenerate = ( ) => {
8295 const pwd = generatePassword ( 24 , includeSpecialChars ) ;
8396 setNewPassword ( pwd ) ;
84- setConfirmPassword ( pwd ) ;
8597 setError ( null ) ;
86- setCopied ( false ) ;
87- } ;
88-
89- const handleCopy = async ( ) => {
90- if ( newPassword ) {
91- await navigator . clipboard . writeText ( newPassword ) ;
92- setCopied ( true ) ;
93- setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
94- }
9598 } ;
9699
97100 const handleSubmit = async ( ) => {
@@ -107,11 +110,6 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
107110 setError ( 'Password should not exceed 64 characters' ) ;
108111 return ;
109112 }
110- if ( newPassword !== confirmPassword ) {
111- setError ( 'Passwords do not match' ) ;
112- return ;
113- }
114-
115113 setError ( null ) ;
116114 setIsSubmitting ( true ) ;
117115
@@ -138,19 +136,19 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
138136 return (
139137 < Dialog onOpenChange = { ( o ) => ! o && handleClose ( ) } open = { open } >
140138 < DialogContent className = "sm:max-w-md" >
141- < DialogHeader >
139+ < DialogHeader spacing = "loose" >
142140 < DialogTitle > Change Password</ DialogTitle >
143141 < DialogDescription asChild >
144142 < div className = "space-y-1" >
145143 < p > Set a new password for this user.</ p >
146- < p className = "font-mono text-foreground text-xs " > { userName } </ p >
144+ < p className = "font-mono text-base text-foreground " > { userName } </ p >
147145 </ div >
148146 </ DialogDescription >
149147 </ DialogHeader >
150148
151- < div className = "space-y-4 py-4 " >
149+ < div className = "space-y-4" >
152150 { /* Mechanism Selection */ }
153- < div className = "space-y-2 " >
151+ < div className = "space-y-3 " >
154152 < Label htmlFor = "mechanism" > SASL Mechanism</ Label >
155153 < Select
156154 onValueChange = { ( v ) => setSelectedMechanism ( v as 'SCRAM-SHA-256' | 'SCRAM-SHA-512' ) }
@@ -165,8 +163,8 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
165163 { saslMechanisms . map ( ( mech ) => (
166164 < SelectItem className = "py-2.5" key = { mech . id } value = { mech . id } >
167165 < div className = "flex flex-col gap-0.5" >
168- < span className = "font-mono text-sm " > { mech . name } </ span >
169- < span className = "text-muted-foreground text-xs " > { mech . description } </ span >
166+ < span className = "font-mono text-base " > { mech . name } </ span >
167+ < span className = "text-base text- muted-foreground leading-6 " > { mech . description } </ span >
170168 </ div >
171169 </ SelectItem >
172170 ) ) }
@@ -175,11 +173,11 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
175173 </ div >
176174
177175 { /* New Password */ }
178- < div className = "space-y-2 " >
179- < Label htmlFor = "new-password" > New Password </ Label >
180- < p className = "text-muted-foreground text-xs" >
181- Must be at least 8 characters and should not exceed 64 characters.
182- </ p >
176+ < div className = "space-y-3 " >
177+ < div className = "space-y-1" >
178+ < Label htmlFor = "new-password" > New Password </ Label >
179+ < Text variant = "muted" > Must be at least 8 characters and should not exceed 64 characters.</ Text >
180+ </ div >
183181 < div className = "flex gap-1.5" >
184182 < Input
185183 autoComplete = "new-password"
@@ -188,7 +186,6 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
188186 onChange = { ( e ) => {
189187 setNewPassword ( e . target . value ) ;
190188 setError ( null ) ;
191- setCopied ( false ) ;
192189 } }
193190 placeholder = "Enter new password"
194191 type = "password"
@@ -197,69 +194,29 @@ export function ChangePasswordDialog({ open, userName, currentMechanism, onClose
197194 < TooltipProvider >
198195 < Tooltip >
199196 < TooltipTrigger asChild >
200- < Button
201- className = "h-9 w-9 shrink-0"
202- onClick = { handleGenerate }
203- size = "icon"
204- type = "button"
205- variant = "outline"
206- >
197+ < Button className = "size-9" onClick = { handleGenerate } size = "icon" type = "button" variant = "outline" >
207198 < RefreshCw className = "size-4" />
208199 < span className = "sr-only" > Generate password</ span >
209200 </ Button >
210201 </ TooltipTrigger >
211202 < TooltipContent > Generate password</ TooltipContent >
212203 </ Tooltip >
213204 </ TooltipProvider >
214- < TooltipProvider >
215- < Tooltip >
216- < TooltipTrigger asChild >
217- < Button
218- className = "h-9 w-9 shrink-0"
219- disabled = { ! newPassword }
220- onClick = { handleCopy }
221- size = "icon"
222- type = "button"
223- variant = "outline"
224- >
225- { copied ? < Check className = "size-4 text-emerald-600" /> : < Copy className = "size-4" /> }
226- < span className = "sr-only" > Copy password</ span >
227- </ Button >
228- </ TooltipTrigger >
229- < TooltipContent > { copied ? 'Copied!' : 'Copy password' } </ TooltipContent >
230- </ Tooltip >
231- </ TooltipProvider >
205+ < CopyButton content = { newPassword } disabled = { ! newPassword } size = "icon" variant = "outline" />
232206 </ div >
233207 < div className = "flex items-center gap-2" >
234208 < Checkbox
235209 checked = { includeSpecialChars }
236210 id = "special-chars"
237211 onCheckedChange = { ( checked ) => setIncludeSpecialChars ( checked === true ) }
238212 />
239- < Label className = "font-normal text-sm " htmlFor = "special-chars" >
213+ < Label className = "font-normal text-base " htmlFor = "special-chars" >
240214 Generate with special characters
241215 </ Label >
242216 </ div >
243217 </ div >
244218
245- { /* Confirm Password */ }
246- < div className = "space-y-2" >
247- < Label htmlFor = "confirm-password" > Confirm Password</ Label >
248- < Input
249- autoComplete = "new-password"
250- className = "font-mono"
251- id = "confirm-password"
252- onChange = { ( e ) => {
253- setConfirmPassword ( e . target . value ) ;
254- setError ( null ) ;
255- } }
256- placeholder = "Confirm new password"
257- type = "password"
258- value = { confirmPassword }
259- />
260- </ div >
261-
262- { Boolean ( error ) && < p className = "text-destructive text-sm" > { error } </ p > }
219+ { Boolean ( error ) && < p className = "text-base text-destructive leading-6" > { error } </ p > }
263220 </ div >
264221
265222 < DialogFooter >
0 commit comments