DatabasePasswordInput.tsx92 lines · main
| 1 | import { UseFormReturn } from 'react-hook-form' |
| 2 | import { FormControl, FormField } from 'ui' |
| 3 | import { Input } from 'ui-patterns/DataInputs/Input' |
| 4 | import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' |
| 5 | |
| 6 | import { DATABASE_PASSWORD_REGEX } from './ProjectCreation.constants' |
| 7 | import { CreateProjectForm } from './ProjectCreation.schema' |
| 8 | import { SpecialSymbolsCallout } from './SpecialSymbolsCallout' |
| 9 | import Panel from '@/components/ui/Panel' |
| 10 | import { PasswordStrengthBar } from '@/components/ui/PasswordStrengthBar' |
| 11 | import { passwordStrength } from '@/lib/password-strength' |
| 12 | import { generateStrongPassword } from '@/lib/project' |
| 13 | |
| 14 | interface DatabasePasswordInputProps { |
| 15 | form: UseFormReturn<CreateProjectForm> |
| 16 | } |
| 17 | |
| 18 | const updatePasswordStrength = async (form: UseFormReturn<CreateProjectForm>, value: string) => { |
| 19 | try { |
| 20 | const { warning, message, strength } = await passwordStrength(value) |
| 21 | form.setValue('dbPassStrength', strength, { shouldValidate: false, shouldDirty: false }) |
| 22 | form.setValue('dbPassStrengthMessage', message ?? '', { |
| 23 | shouldValidate: false, |
| 24 | shouldDirty: false, |
| 25 | }) |
| 26 | form.setValue('dbPassStrengthWarning', warning ?? '', { |
| 27 | shouldValidate: false, |
| 28 | shouldDirty: false, |
| 29 | }) |
| 30 | |
| 31 | form.trigger('dbPass') |
| 32 | } catch (error) { |
| 33 | console.error(error) |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | export const DatabasePasswordInput = ({ form }: DatabasePasswordInputProps) => { |
| 38 | // [Refactor] DB Password could be a common component used in multiple pages with repeated logic |
| 39 | async function generatePassword() { |
| 40 | const password = generateStrongPassword() |
| 41 | form.setValue('dbPass', password) |
| 42 | |
| 43 | updatePasswordStrength(form, password) |
| 44 | } |
| 45 | |
| 46 | return ( |
| 47 | <Panel.Content> |
| 48 | <FormField |
| 49 | control={form.control} |
| 50 | name="dbPass" |
| 51 | render={({ field }) => { |
| 52 | const isInvalidDatabasePassword = |
| 53 | field.value.length > 0 && !field.value.match(DATABASE_PASSWORD_REGEX) |
| 54 | |
| 55 | return ( |
| 56 | <FormItemLayout |
| 57 | label="Database password" |
| 58 | layout="horizontal" |
| 59 | description={ |
| 60 | <> |
| 61 | {isInvalidDatabasePassword && <SpecialSymbolsCallout />} |
| 62 | <PasswordStrengthBar |
| 63 | passwordStrengthScore={form.getValues('dbPassStrength')} |
| 64 | password={field.value} |
| 65 | passwordStrengthMessage={form.getValues('dbPassStrengthMessage')} |
| 66 | generateStrongPassword={generatePassword} |
| 67 | /> |
| 68 | </> |
| 69 | } |
| 70 | > |
| 71 | <FormControl> |
| 72 | <Input |
| 73 | copy={field.value.length > 0} |
| 74 | type="password" |
| 75 | placeholder="Type in a strong password" |
| 76 | {...field} |
| 77 | autoComplete="off" |
| 78 | onChange={async (event) => { |
| 79 | const newValue = event.target.value |
| 80 | field.onChange(event) |
| 81 | |
| 82 | updatePasswordStrength(form, newValue) |
| 83 | }} |
| 84 | /> |
| 85 | </FormControl> |
| 86 | </FormItemLayout> |
| 87 | ) |
| 88 | }} |
| 89 | /> |
| 90 | </Panel.Content> |
| 91 | ) |
| 92 | } |