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

EcrituresNumeriques / stylo / 13548057613

26 Feb 2025 03:57PM UTC coverage: 12.824% (+0.8%) from 12.024%
13548057613

push

github

web-flow
Merge pull request #1272 from EcrituresNumeriques/fix/739

170 of 317 branches covered (53.63%)

Branch coverage included in aggregate %.

74 of 172 new or added lines in 10 files covered. (43.02%)

1 existing line in 1 file now uncovered.

1235 of 10639 relevant lines covered (11.61%)

1.86 hits per line

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

0.0
/front/src/components/UserInfos.jsx
1
import React, { useState, useCallback } from 'react'
×
2
import { Check, Clipboard, Loader } from 'react-feather'
×
3
import { useTranslation } from 'react-i18next'
×
4
import { useSelector, useDispatch, shallowEqual } from 'react-redux'
×
5
import { CopyToClipboard } from 'react-copy-to-clipboard'
×
6
import { Helmet } from 'react-helmet'
×
7

8
import { useGraphQLClient } from '../helpers/graphQL'
×
9
import { updateUser } from './Credentials.graphql'
×
10

NEW
11
import { useSetAuthToken } from '../hooks/user.js'
×
12

13
import etv from '../helpers/eventTargetValue'
×
14
import styles from './credentials.module.scss'
×
15
import formStyles from './field.module.scss'
×
16
import Button from './Button'
×
17
import Field from './Field'
×
18
import TimeAgo from './TimeAgo.jsx'
×
19

20
export default function UserInfos() {
×
21
  const dispatch = useDispatch()
×
22
  const { t } = useTranslation()
×
23
  const { query } = useGraphQLClient()
×
24
  const activeUser = useSelector((state) => state.activeUser, shallowEqual)
×
25
  const zoteroToken = useSelector((state) => state.activeUser.zoteroToken)
×
26
  const sessionToken = useSelector((state) => state.sessionToken)
×
27
  const [displayName, setDisplayName] = useState(activeUser.displayName)
×
28
  const [firstName, setFirstName] = useState(activeUser.firstName || '')
×
29
  const [lastName, setLastName] = useState(activeUser.lastName || '')
×
30
  const [institution, setInstitution] = useState(activeUser.institution || '')
×
31
  const [isSaving, setIsSaving] = useState(false)
×
32

33
  const updateActiveUserDetails = useCallback(
×
34
    (payload) =>
×
35
      dispatch({
×
36
        type: `UPDATE_ACTIVE_USER_DETAILS`,
×
37
        payload,
×
38
      }),
×
39
    []
×
40
  )
×
41

NEW
42
  const { link: linkZoteroAccount, unlink: unlinkZoteroAccount } =
×
NEW
43
    useSetAuthToken('zotero')
×
44

45
  const updateInfo = useCallback(
×
46
    async (e) => {
×
47
      e.preventDefault()
×
48
      setIsSaving(true)
×
49
      const variables = {
×
50
        user: activeUser._id,
×
51
        details: { displayName, firstName, lastName, institution },
×
52
      }
×
53
      const { updateUser: userDetails } = await query({
×
54
        query: updateUser,
×
55
        variables,
×
56
      })
×
57
      updateActiveUserDetails(userDetails)
×
58
      setIsSaving(false)
×
59
    },
×
60
    [activeUser._id, displayName, firstName, lastName, institution]
×
61
  )
×
62

63
  return (
×
64
    <>
×
65
      <Helmet>
×
66
        <title>{t('user.account.title')}</title>
×
67
      </Helmet>
×
68
      <section className={styles.section}>
×
69
        <h2>{t('user.account.title')}</h2>
×
70
        <form onSubmit={updateInfo} className={styles.form}>
×
71
          <Field
×
72
            id="displayNameField"
×
73
            label={t('user.account.displayName')}
×
74
            type="text"
×
75
            value={displayName}
×
76
            onChange={(e) => setDisplayName(etv(e))}
×
77
          />
×
78
          <Field
×
79
            id="firstNameField"
×
80
            label={t('user.account.firstName')}
×
81
            type="text"
×
82
            value={firstName}
×
83
            onChange={(e) => setFirstName(etv(e))}
×
84
          />
×
85
          <Field
×
86
            id="lastNameField"
×
87
            label={t('user.account.lastName')}
×
88
            type="text"
×
89
            value={lastName}
×
90
            onChange={(e) => setLastName(etv(e))}
×
91
          />
×
92
          <Field
×
93
            id="institutionField"
×
94
            label={t('user.account.institution')}
×
95
            type="text"
×
96
            value={institution}
×
97
            onChange={(e) => setInstitution(etv(e))}
×
98
          />
×
99
          <Field label="Zotero">
×
100
            <>
×
101
              {zoteroToken && (
×
102
                <div className={styles.zotero}>
×
NEW
103
                  <p>
×
NEW
104
                    {t('credentials.authentication.linkedService.description', {
×
NEW
105
                      service: 'Zotero',
×
NEW
106
                      token: zoteroToken,
×
NEW
107
                    })}
×
NEW
108
                  </p>
×
109
                  <Button
×
110
                    onClick={unlinkZoteroAccount}
×
NEW
111
                    type="button"
×
NEW
112
                    aria-label={t('credentials.authentication.unlinkLabel', {
×
NEW
113
                      service: 'zotero',
×
NEW
114
                    })}
×
115
                  >
NEW
116
                    {t('credentials.authentication.unlinkButton')}
×
NEW
117
                  </Button>
×
NEW
118
                </div>
×
119
              )}
NEW
120
              {!zoteroToken && (
×
NEW
121
                <div className={styles.zotero}>
×
NEW
122
                  <p>
×
NEW
123
                    {t(
×
NEW
124
                      'credentials.authentication.unlinkedService.description'
×
NEW
125
                    )}
×
NEW
126
                  </p>
×
127

NEW
128
                  <Button
×
NEW
129
                    onClick={linkZoteroAccount}
×
NEW
130
                    type="button"
×
NEW
131
                    aria-label={t('credentials.authentication.linkLabel', {
×
NEW
132
                      service: 'zotero',
×
NEW
133
                    })}
×
134
                  >
NEW
135
                    {t('credentials.authentication.linkButton')}
×
136
                  </Button>
×
137
                </div>
×
138
              )}
139
            </>
×
140
          </Field>
×
141
          <div className={formStyles.footer}>
×
142
            <Button primary={true} disabled={isSaving}>
×
143
              {isSaving ? <Loader /> : <Check />}
×
144
              Save changes
145
            </Button>
×
146
          </div>
×
147
        </form>
×
148
      </section>
×
149

150
      <section className={styles.section}>
×
151
        <div className={styles.info}>
×
152
          <Field label={t('user.account.email')}>
×
153
            <>{activeUser.email}</>
×
154
          </Field>
×
155
          {activeUser.username && (
×
156
            <Field label="Username">
×
157
              <>{activeUser.username}</>
×
158
            </Field>
×
159
          )}
160
          <Field label={t('user.account.authentication')}>
×
161
            <>
×
162
              {activeUser.authType === 'oidc'
×
163
                ? 'OpenID (External)'
×
164
                : 'Password'}
×
165
            </>
×
166
          </Field>
×
167
          <Field
×
168
            label={t('user.account.apiKey')}
×
169
            className={styles.apiKeyField}
×
170
          >
171
            <>
×
172
              <code
×
173
                className={styles.apiKeyValue}
×
174
                title={t('user.account.apiKeyValue', { token: sessionToken })}
×
175
              >
176
                {sessionToken}
×
177
              </code>
×
178
              <CopyToClipboard text={sessionToken}>
×
179
                <Button title={t('user.account.copyApiKey')} icon={true}>
×
180
                  <Clipboard />
×
181
                </Button>
×
182
              </CopyToClipboard>
×
183
            </>
×
184
          </Field>
×
185
          <Field label={t('user.account.id')}>
×
186
            <code>{activeUser._id}</code>
×
187
          </Field>
×
188
          <Field label={t('user.account.createdAt')}>
×
189
            <TimeAgo date={activeUser.createdAt} />
×
190
          </Field>
×
191
          <Field label={t('user.account.updatedAt')}>
×
192
            <TimeAgo date={activeUser.updatedAt} />
×
193
          </Field>
×
194
        </div>
×
195
      </section>
×
196
    </>
×
197
  )
198
}
×
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