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

EcrituresNumeriques / stylo / 13547287365

26 Feb 2025 03:20PM UTC coverage: 12.024% (+0.3%) from 11.684%
13547287365

push

github

web-flow
Merge pull request #1255 from ggrossetie/fix-1171-hooks-renaming

157 of 305 branches covered (51.48%)

Branch coverage included in aggregate %.

67 of 264 new or added lines in 25 files covered. (25.38%)

10 existing lines in 4 files now uncovered.

1144 of 10515 relevant lines covered (10.88%)

1.83 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

NEW
8
import { useGraphQLClient } from '../helpers/graphQL'
×
9
import { updateUser } from './Credentials.graphql'
×
10
import etv from '../helpers/eventTargetValue'
×
11
import styles from './credentials.module.scss'
×
12
import formStyles from './field.module.scss'
×
13
import Button from './Button'
×
14
import Field from './Field'
×
15
import TimeAgo from './TimeAgo.jsx'
×
16

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

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

43
  const unlinkZoteroAccount = useCallback(async (event) => {
×
44
    event.preventDefault()
×
45

46
    const variables = { user: activeUser._id, details: { zoteroToken: null } }
×
NEW
47
    await query({ query: updateUser, variables })
×
48
    clearZoteroToken()
×
49
    setIsSaving(false)
×
50
  }, [])
×
51

52
  const updateInfo = useCallback(
×
53
    async (e) => {
×
54
      e.preventDefault()
×
55
      setIsSaving(true)
×
56
      const variables = {
×
57
        user: activeUser._id,
×
58
        details: { displayName, firstName, lastName, institution },
×
59
      }
×
NEW
60
      const { updateUser: userDetails } = await query({
×
61
        query: updateUser,
×
62
        variables,
×
63
      })
×
64
      updateActiveUserDetails(userDetails)
×
65
      setIsSaving(false)
×
66
    },
×
67
    [activeUser._id, displayName, firstName, lastName, institution]
×
68
  )
×
69

70
  return (
×
71
    <>
×
72
      <Helmet>
×
73
        <title>{t('user.account.title')}</title>
×
74
      </Helmet>
×
75
      <section className={styles.section}>
×
76
        <h2>{t('user.account.title')}</h2>
×
77
        <form onSubmit={updateInfo} className={styles.form}>
×
78
          <Field
×
79
            id="displayNameField"
×
80
            label={t('user.account.displayName')}
×
81
            type="text"
×
82
            value={displayName}
×
83
            onChange={(e) => setDisplayName(etv(e))}
×
84
          />
×
85
          <Field
×
86
            id="firstNameField"
×
87
            label={t('user.account.firstName')}
×
88
            type="text"
×
89
            value={firstName}
×
90
            onChange={(e) => setFirstName(etv(e))}
×
91
          />
×
92
          <Field
×
93
            id="lastNameField"
×
94
            label={t('user.account.lastName')}
×
95
            type="text"
×
96
            value={lastName}
×
97
            onChange={(e) => setLastName(etv(e))}
×
98
          />
×
99
          <Field
×
100
            id="institutionField"
×
101
            label={t('user.account.institution')}
×
102
            type="text"
×
103
            value={institution}
×
104
            onChange={(e) => setInstitution(etv(e))}
×
105
          />
×
106
          <Field label="Zotero">
×
107
            <>
×
108
              {zoteroToken && (
×
109
                <div className={styles.zotero}>
×
110
                  <div>
×
111
                    Linked with <code>{zoteroToken}</code> account.
×
112
                  </div>
×
113
                  <Button
×
114
                    title="Unlink this Zotero account"
×
115
                    onClick={unlinkZoteroAccount}
×
116
                  >
×
117
                    Unlink
118
                  </Button>
×
119
                </div>
×
120
              )}
121
              {!zoteroToken && <span>No linked account.</span>}
×
122
            </>
×
123
          </Field>
×
124
          <div className={formStyles.footer}>
×
125
            <Button primary={true} disabled={isSaving}>
×
126
              {isSaving ? <Loader /> : <Check />}
×
127
              Save changes
128
            </Button>
×
129
          </div>
×
130
        </form>
×
131
      </section>
×
132

133
      <section className={styles.section}>
×
134
        <div className={styles.info}>
×
135
          <Field label={t('user.account.email')}>
×
136
            <>{activeUser.email}</>
×
137
          </Field>
×
138
          {activeUser.username && (
×
139
            <Field label="Username">
×
140
              <>{activeUser.username}</>
×
141
            </Field>
×
142
          )}
143
          <Field label={t('user.account.authentication')}>
×
144
            <>
×
145
              {activeUser.authType === 'oidc'
×
146
                ? 'OpenID (External)'
×
147
                : 'Password'}
×
148
            </>
×
149
          </Field>
×
150
          <Field
×
151
            label={t('user.account.apiKey')}
×
152
            className={styles.apiKeyField}
×
153
          >
154
            <>
×
155
              <code
×
156
                className={styles.apiKeyValue}
×
157
                title={t('user.account.apiKeyValue', { token: sessionToken })}
×
158
              >
159
                {sessionToken}
×
160
              </code>
×
161
              <CopyToClipboard text={sessionToken}>
×
162
                <Button title={t('user.account.copyApiKey')} icon={true}>
×
163
                  <Clipboard />
×
164
                </Button>
×
165
              </CopyToClipboard>
×
166
            </>
×
167
          </Field>
×
168
          <Field label={t('user.account.id')}>
×
169
            <code>{activeUser._id}</code>
×
170
          </Field>
×
171
          <Field label={t('user.account.createdAt')}>
×
172
            <TimeAgo date={activeUser.createdAt} />
×
173
          </Field>
×
174
          <Field label={t('user.account.updatedAt')}>
×
175
            <TimeAgo date={activeUser.updatedAt} />
×
176
          </Field>
×
177
        </div>
×
178
      </section>
×
179
    </>
×
180
  )
181
}
×
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