Skip to content

Commit 7f04f6d

Browse files
✨ introduce specialists (#336)
This introduces the notion of some users being specialists in some subjects (aka tag labels). Specialists will have more permissions on questions and answers which are tagged with their specialties, though no permissions are introduced here. The specialties of a user are displayed on their profile. This does not introduce means to define specialties through the UI, this will come later. See also #334.
1 parent d0ac769 commit 7f04f6d

File tree

15 files changed

+462
-15
lines changed

15 files changed

+462
-15
lines changed

client/src/contexts/User/UserProvider.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, { useMemo } from 'react'
21
import { useQuery } from '@apollo/react-hooks'
2+
import React, { useMemo } from 'react'
33

44
import { useAuth } from '../Auth'
55

@@ -10,9 +10,11 @@ export const UserContext = React.createContext({})
1010
const UserProvider = ({ children }) => {
1111
const { isAuth } = useAuth()
1212

13-
const { data } = useQuery(GET_ME, { skip: !isAuth })
13+
const { data } = useQuery(GET_ME, {
14+
skip: !isAuth && process.env.REACT_APP_DISABLE_AUTH !== 'true'
15+
})
1416

15-
const value = useMemo(() => isAuth && data && data.me, [isAuth, data])
17+
const value = useMemo(() => data && data.me, [data])
1618

1719
return <UserContext.Provider value={value}>{children}</UserContext.Provider>
1820
}

client/src/scenes/UserProfile/UserProfile.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { alert, getIntl } from 'services'
77
import { Avatar, Button, Card, Modal, Loading, Input } from 'components'
88

99
import Logs from './components/Logs'
10+
import Specialties from './components/Specialties'
1011

1112
import { UPDATE_INDENTITY, DELETE_IDENTITY } from './queries'
1213

@@ -114,6 +115,7 @@ const UserProfile = ({ history }) => {
114115
</Button>
115116
</Card.Actions>
116117
</Card>
118+
<Specialties userId={user.id} />
117119
<Logs userId={user.id} />
118120
<Card>
119121
<Card.Text>
@@ -167,7 +169,7 @@ const UserProfile = ({ history }) => {
167169
}
168170

169171
UserProfile.translations = {
170-
fr: {
172+
en: {
171173
alert: {
172174
update_success: 'Your profile was successfully updated!',
173175
delete_success: 'Your personal data was succesfully deleted!'
@@ -204,7 +206,7 @@ UserProfile.translations = {
204206
button: 'I understand the consequences, delete my data'
205207
}
206208
},
207-
en: {
209+
fr: {
208210
alert: {
209211
update_success: 'Votre profil a été modifié avec succès !',
210212
delete_success: 'Vos données personnelles ont été supprimées avec succès !'

client/src/scenes/UserProfile/components/Logs/Logs.container.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { withRouter } from 'react-router'
22

3+
import { withError, withPagination } from 'components'
34
import { compose, unserialize } from 'helpers'
45
import { query } from 'services/apollo'
5-
import { withError, withPagination } from 'components'
66

77
import { meHistory } from './queries'
88

client/src/scenes/UserProfile/components/Logs/Logs.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import React from 'react'
21
import PropTypes from 'prop-types'
32
import { Link } from 'react-router-dom'
43

5-
import { getIntl } from 'services'
64
import { formatHistoryAction, nodeUrl } from 'helpers'
5+
import { getIntl } from 'services'
76

87
import { Loading } from 'components'
98
import Card, { CardText } from 'components/Card'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { withError } from 'components'
2+
import { compose } from 'helpers'
3+
import { query } from 'services/apollo'
4+
import { GET_SPECIALTIES } from './queries'
5+
6+
import Specialties from './Specialties'
7+
8+
export default compose(
9+
query(GET_SPECIALTIES, {
10+
parse: ({ me = {} }) => ({
11+
spe: me.specialties
12+
})
13+
}),
14+
withError()
15+
)(Specialties)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.specialty {
2+
display: flex;
3+
align-items: center;
4+
justify-content: center;
5+
width: fit-content;
6+
padding: 0.3rem;
7+
text-transform: capitalize;
8+
background-color: transparent;
9+
color: var(--primary-color);
10+
border: 1px solid var(--primary-color);
11+
border-radius: 4px;
12+
}
13+
14+
.specialty .material-icons {
15+
margin-right: 10px;
16+
font-size: 1rem;
17+
}
18+
19+
.emptyText {
20+
text-align: center;
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Card, { CardText } from 'components/Card'
2+
import PropTypes from 'prop-types'
3+
import { getIntl } from 'services'
4+
5+
import './Specialties.css'
6+
7+
const Specialties = ({ spe }) => {
8+
const intl = getIntl(Specialties)
9+
10+
return (
11+
<Card>
12+
<CardText className="specialties">
13+
<h2 style={{ marginBottom: '1rem' }}>{intl('title')}</h2>
14+
<hr />
15+
{spe && spe.length > 0 ? (
16+
spe.map(s => (
17+
<p key={s.name} className="specialty">
18+
<i className="material-icons">verified</i>
19+
{s.name}
20+
</p>
21+
))
22+
) : (
23+
<p className="emptyText">{intl('empty')}</p>
24+
)}
25+
</CardText>
26+
</Card>
27+
)
28+
}
29+
30+
Specialties.propTypes = {
31+
spe: PropTypes.array
32+
}
33+
34+
Specialties.translations = {
35+
en: {
36+
title: 'Specialties',
37+
empty: 'No specialties yet'
38+
},
39+
fr: {
40+
title: 'Spécialitées',
41+
empty: 'Pas encore de spécialitées'
42+
}
43+
}
44+
45+
export default Specialties
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './Specialties.container'
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import gql from 'graphql-tag'
2+
3+
export const GET_SPECIALTIES = gql`
4+
query {
5+
me {
6+
id
7+
specialties {
8+
id
9+
name
10+
}
11+
}
12+
}
13+
`

e2e/client.test.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,42 @@ const upsertConfig = /* GraphQL */ `mutation UpsertConfig{
3434
algoliaApiKey: "${process.env.ALGOLIA_API_KEY_ALL}"
3535
auth0Domain: "${process.env.AUTH0_DOMAIN}"
3636
auth0ClientId: "${process.env.AUTH0_CLIENT_ID}"
37-
tagCategories: {create: [{name: "agencies", order: 1, labels: {create: [{ name: "paris", order: 1 }, { name: "nantes", order: 2 }]}}, {name: "theme", order: 2, labels: {create: [{name: "tutorial", order: 1}, {name: "meta", order: 2}]}}]}
37+
tagCategories: {
38+
create: [
39+
{
40+
name: "services",
41+
order: 1,
42+
labels: {
43+
create: [
44+
{name: "payroll", order: 1}
45+
{name: "marketing", order: 2}
46+
{name: "ce", order: 3}
47+
{name: "sales", order: 4}
48+
]
49+
}
50+
},
51+
{
52+
name: "agencies",
53+
order: 2,
54+
labels: {
55+
create: [
56+
{ name: "paris", order: 1 }
57+
{ name: "nantes", order: 2 }
58+
]
59+
}
60+
},
61+
{
62+
name: "theme",
63+
order: 3,
64+
labels: {
65+
create: [
66+
{ name: "tutorial", order: 1 },
67+
{ name: "meta", order: 2 }
68+
]
69+
}
70+
}
71+
]
72+
}
3873
}
3974
update: {
4075
name: "${process.env.SERVICE_NAME}"
@@ -403,6 +438,16 @@ test('Should be able to search by text and tag', async ({ page }) => {
403438
await expect(page.getByText('Ceci est une réponse différente', { exact: true })).toBeVisible()
404439
})
405440

441+
test('Should see the marketing specialty on profile page', async ({ page }) => {
442+
await page.goto('/')
443+
await page.getByRole('img', { name: 'avatar' }).hover()
444+
await page
445+
.locator('a')
446+
.filter({ hasText: 'Profil' })
447+
.click()
448+
await expect(page.getByText('verifiedmarketing')).toBeVisible()
449+
})
450+
406451
test.afterEach(async () => {
407452
algolia.clearIndex({ prisma })
408453
})

0 commit comments

Comments
 (0)