• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CaptainFact / captain-fact-frontend / 20411409289

21 Dec 2025 02:42PM UTC coverage: 1.46% (-1.8%) from 3.249%
20411409289

push

github

Betree
iterate

26 of 1952 branches covered (1.33%)

Branch coverage included in aggregate %.

1 of 8 new or added lines in 4 files covered. (12.5%)

528 existing lines in 32 files now uncovered.

39 of 2500 relevant lines covered (1.56%)

0.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/components/Users/EditUserForm.jsx
1
import { useFormik } from 'formik'
2
import { AtSign, IdCard, Lock, Mail } from 'lucide-react'
3
import React, { useEffect } from 'react'
4
import { useTranslation } from 'react-i18next'
5
import { useHistory } from 'react-router-dom'
6

7
import { toast } from '@/hooks/use-toast'
8

9
import { updateUserInfo } from '../../API/http_api/current_user'
10
import { cleanStr } from '../../lib/clean_str'
11
import { validateUserForm } from '../../lib/user_validations'
12
import { useLoggedInUser } from '../LoggedInUser/UserProvider'
13
import { Button } from '../ui/button'
14
import { Input } from '../ui/input'
15
import { Label } from '../ui/label'
16

UNCOV
17
const EditUserForm = () => {
×
18
  const { t } = useTranslation('user')
×
UNCOV
19
  const history = useHistory()
×
20
  const { loggedInUser, loggedInUserLoading, isAuthenticated, updateLoggedInUser } =
UNCOV
21
    useLoggedInUser()
×
22

UNCOV
23
  useEffect(() => {
×
24
    // Redirect to user profile when not authenticated
UNCOV
25
    if (!loggedInUserLoading && !isAuthenticated) {
×
UNCOV
26
      history.push('/')
×
27
    }
28
  }, [loggedInUserLoading, isAuthenticated, history])
29

UNCOV
30
  const validateForm = (values) =>
×
UNCOV
31
    validateUserForm(t, values, {
×
32
      emailRequired: true,
33
      passwordRequired: false,
34
      includeUsername: true,
35
      includeName: true,
36
      includePasswordRepeat: true,
37
    })
38

UNCOV
39
  const formik = useFormik({
×
40
    initialValues: {
41
      username: loggedInUser?.username || '',
×
42
      name: loggedInUser?.name || '',
×
43
      email: loggedInUser?.email || '',
×
44
      password: '',
45
      passwordRepeat: '',
46
    },
47
    validate: validateForm,
48
    enableReinitialize: true,
49
    onSubmit: async (values) => {
50
      try {
×
UNCOV
51
        const user = await updateUserInfo(values)
×
UNCOV
52
        updateLoggedInUser(user)
×
UNCOV
53
        toast({
×
54
          variant: 'success',
55
          title: t('settingsUpdated'),
56
        })
57
      } catch (error) {
58
        // Handle form errors
UNCOV
59
        if (error && typeof error === 'object') {
×
UNCOV
60
          const formikErrors = {}
×
UNCOV
61
          Object.keys(error).forEach((key) => {
×
UNCOV
62
            formikErrors[key] = error[key]
×
63
          })
UNCOV
64
          formik.setErrors(formikErrors)
×
65
        }
66
      }
67
    },
68
  })
69

70
  const { values, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting, isValid } =
UNCOV
71
    formik
×
72

UNCOV
73
  return (
×
74
    <form className="edit-user-form form flex flex-col gap-2" onSubmit={handleSubmit}>
75
      {/* Username Field */}
76
      <div className="space-y-2">
77
        <Label htmlFor="username">{t('username')}</Label>
78
        <div className="relative">
79
          <Input
80
            id="username"
81
            name="username"
82
            type="text"
83
            placeholder={t('username')}
84
            value={values.username}
85
            onChange={handleChange}
86
            onBlur={handleBlur}
87
            className={`pl-10 ${touched.username && errors.username ? 'border-red-600' : ''}`}
×
88
          />
89
          <AtSign
90
            size={16}
91
            className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300"
92
          />
93
        </div>
94
        {touched.username && errors.username && (
×
95
          <span className="text-xs text-red-600 pl-1">{errors.username}</span>
96
        )}
97
      </div>
98

99
      {/* Name Field */}
100
      <div className="space-y-2">
101
        <Label htmlFor="name">
102
          {t('realName')} ({t('optional')})
103
        </Label>
104
        <div className="relative">
105
          <Input
106
            id="name"
107
            name="name"
108
            type="text"
109
            value={values.name}
110
            onChange={handleChange}
111
            onBlur={(e) => {
UNCOV
112
              handleChange({
×
113
                target: { name: 'name', value: cleanStr(e.target.value).trim() },
114
              })
UNCOV
115
              handleBlur(e)
×
116
            }}
117
            className={`pl-10 ${touched.name && errors.name ? 'border-red-600' : ''}`}
×
118
          />
119
          <IdCard
120
            size={16}
121
            className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300"
122
          />
123
        </div>
124
        {touched.name && errors.name && (
×
125
          <span className="text-xs text-red-600 pl-1">{errors.name}</span>
126
        )}
127
      </div>
128

129
      {/* Email Field */}
130
      <div className="space-y-2">
131
        <Label htmlFor="email">{t('email')}</Label>
132
        <div className="relative">
133
          <Input
134
            id="email"
135
            name="email"
136
            type="email"
137
            placeholder="example@example.com"
138
            value={values.email}
139
            onChange={handleChange}
140
            onBlur={handleBlur}
141
            className={`pl-10 ${touched.email && errors.email ? 'border-red-600' : ''}`}
×
142
          />
143
          <Mail
144
            size={16}
145
            className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300"
146
          />
147
        </div>
148
        {touched.email && errors.email && (
×
149
          <span className="text-xs text-red-600 pl-1">{errors.email}</span>
150
        )}
151
      </div>
152

153
      {/* Password Field */}
154
      <div className="space-y-2">
155
        <Label htmlFor="password">{t('passwordOptional')}</Label>
156
        <div className="relative">
157
          <Input
158
            id="password"
159
            name="password"
160
            type="password"
161
            value={values.password}
162
            onChange={handleChange}
163
            onBlur={handleBlur}
164
            className={`pl-10 ${touched.password && errors.password ? 'border-red-600' : ''}`}
×
165
          />
166
          <Lock
167
            size={16}
168
            className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300"
169
          />
170
        </div>
171
        {touched.password && errors.password && (
×
172
          <span className="text-xs text-red-600 pl-1">{errors.password}</span>
173
        )}
174
      </div>
175

176
      {/* Password Repeat Field */}
177
      <div className="space-y-2">
178
        <Label htmlFor="passwordRepeat">{t('repeatPassword')}</Label>
179
        <div className="relative">
180
          <Input
181
            id="passwordRepeat"
182
            name="passwordRepeat"
183
            type="password"
184
            value={values.passwordRepeat}
185
            onChange={handleChange}
186
            onBlur={handleBlur}
187
            className={`pl-10 ${touched.passwordRepeat && errors.passwordRepeat ? 'border-red-600' : ''}`}
×
188
          />
189
          <Lock
190
            size={16}
191
            className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300"
192
          />
193
        </div>
194
        {touched.passwordRepeat && errors.passwordRepeat && (
×
195
          <span className="text-xs text-red-600 pl-1">{errors.passwordRepeat}</span>
196
        )}
197
      </div>
198

199
      {/* Submit Button */}
200
      <div className="mt-6">
201
        <Button
202
          type="submit"
203
          disabled={!isValid || isSubmitting}
×
204
          loading={isSubmitting}
205
          variant="outline"
206
          className="w-full"
207
        >
208
          {t('main:actions.save')}
209
        </Button>
210
      </div>
211
    </form>
212
  )
213
}
214

215
export default EditUserForm
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc