Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Open sidebar
SocialGouv
cdtn
code-du-travail-backoffice
Commits
5362e466
Unverified
Commit
5362e466
authored
May 05, 2020
by
Ivan Gabriele
Committed by
GitHub
May 05, 2020
Browse files
feat(app): improve answers management (#1019)
parent
5d046747
Changes
140
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
75 changed files
with
983 additions
and
3207 deletions
+983
-3207
.eslintrc
.eslintrc
+4
-1
CONTRIBUTING.md
CONTRIBUTING.md
+45
-10
db/migrations/20200430032517_switch_full_answers_view_to_left_join.sql
.../20200430032517_switch_full_answers_view_to_left_join.sql
+35
-0
db/migrations/knex/20200430032517_switch_full_answers_view_to_left_join.js
...x/20200430032517_switch_full_answers_view_to_left_join.js
+9
-0
db/seeds/dev/06-logs.js
db/seeds/dev/06-logs.js
+6
-6
db/snapshot_test_db.dump
db/snapshot_test_db.dump
+0
-0
package.json
package.json
+1
-1
packages/api/.dockerignore
packages/api/.dockerignore
+1
-0
packages/api/src/index.js
packages/api/src/index.js
+0
-3
packages/api/tsconfig.json
packages/api/tsconfig.json
+2
-1
packages/api/typings-env.d.ts
packages/api/typings-env.d.ts
+1
-0
packages/app/package.json
packages/app/package.json
+3
-8
packages/app/pages/admin/answers/edit.js
packages/app/pages/admin/answers/edit.js
+343
-355
packages/app/pages/admin/answers/index.js
packages/app/pages/admin/answers/index.js
+98
-69
packages/app/pages/admin/answers/print.js
packages/app/pages/admin/answers/print.js
+2
-3
packages/app/pages/admin/index.js
packages/app/pages/admin/index.js
+2
-2
packages/app/pages/admin/legal-references-migration-fix/index.js
...s/app/pages/admin/legal-references-migration-fix/index.js
+4
-3
packages/app/pages/admin/legal-references-migration/index.js
packages/app/pages/admin/legal-references-migration/index.js
+2
-1
packages/app/pages/admin/locations/edit.js
packages/app/pages/admin/locations/edit.js
+2
-3
packages/app/pages/admin/locations/new.js
packages/app/pages/admin/locations/new.js
+2
-2
packages/app/pages/admin/logs/index.js
packages/app/pages/admin/logs/index.js
+2
-2
packages/app/pages/admin/migrations/edit.js
packages/app/pages/admin/migrations/edit.js
+0
-4
packages/app/pages/admin/users/edit.js
packages/app/pages/admin/users/edit.js
+2
-3
packages/app/pages/admin/users/new.js
packages/app/pages/admin/users/new.js
+2
-2
packages/app/pages/answers/edit.js
packages/app/pages/answers/edit.js
+0
-9
packages/app/pages/answers/index.js
packages/app/pages/answers/index.js
+5
-13
packages/app/pages/answers/view.js
packages/app/pages/answers/view.js
+2
-12
packages/app/src/actions/answers.js
packages/app/src/actions/answers.js
+4
-1
packages/app/src/actions/comments.js
packages/app/src/actions/comments.js
+2
-4
packages/app/src/blocks/AdminAnswer/__tests__/index.test.js
packages/app/src/blocks/AdminAnswer/__tests__/index.test.js
+0
-180
packages/app/src/blocks/AdminAnswer/index.js
packages/app/src/blocks/AdminAnswer/index.js
+0
-126
packages/app/src/blocks/Answer/__tests__/index.test.js
packages/app/src/blocks/Answer/__tests__/index.test.js
+0
-115
packages/app/src/blocks/AnswerEditionContent/__tests__/__snapshots__/index.test.js.snap
...EditionContent/__tests__/__snapshots__/index.test.js.snap
+0
-488
packages/app/src/blocks/AnswerEditionContent/__tests__/index.test.js
...p/src/blocks/AnswerEditionContent/__tests__/index.test.js
+0
-27
packages/app/src/blocks/AnswerEditionHead/Actions.js
packages/app/src/blocks/AnswerEditionHead/Actions.js
+1
-1
packages/app/src/blocks/AnswerEditionHead/__tests__/Actions.test.js
...pp/src/blocks/AnswerEditionHead/__tests__/Actions.test.js
+0
-23
packages/app/src/blocks/AnswerEditionHead/__tests__/Tab.test.js
...es/app/src/blocks/AnswerEditionHead/__tests__/Tab.test.js
+0
-32
packages/app/src/blocks/AnswerEditionHead/__tests__/Tabs.test.js
...s/app/src/blocks/AnswerEditionHead/__tests__/Tabs.test.js
+0
-37
packages/app/src/blocks/AnswerEditionHead/__tests__/__snapshots__/index.test.js.snap
...werEditionHead/__tests__/__snapshots__/index.test.js.snap
+0
-277
packages/app/src/blocks/AnswerEditionHead/__tests__/index.test.js
.../app/src/blocks/AnswerEditionHead/__tests__/index.test.js
+0
-30
packages/app/src/blocks/AnswerEditionHead/index.js
packages/app/src/blocks/AnswerEditionHead/index.js
+1
-1
packages/app/src/blocks/AnswerEditionReferences/List.js
packages/app/src/blocks/AnswerEditionReferences/List.js
+0
-53
packages/app/src/blocks/AnswerEditionReferences/__tests__/List.test.js
...src/blocks/AnswerEditionReferences/__tests__/List.test.js
+0
-59
packages/app/src/blocks/AnswerEditionReferences/__tests__/index.test.js
...rc/blocks/AnswerEditionReferences/__tests__/index.test.js
+0
-217
packages/app/src/blocks/AnswerEditionReferences/index.js
packages/app/src/blocks/AnswerEditionReferences/index.js
+37
-218
packages/app/src/blocks/AnswerEditionTags/__tests__/index.test.js
.../app/src/blocks/AnswerEditionTags/__tests__/index.test.js
+0
-41
packages/app/src/blocks/AnswerEditionTags/index.js
packages/app/src/blocks/AnswerEditionTags/index.js
+0
-91
packages/app/src/components/AdminForm/__tests__/index.test.js
...ages/app/src/components/AdminForm/__tests__/index.test.js
+10
-13
packages/app/src/components/AdminForm/index.js
packages/app/src/components/AdminForm/index.js
+24
-49
packages/app/src/components/AdminForm/styles.js
packages/app/src/components/AdminForm/styles.js
+5
-1
packages/app/src/components/AdminIndex/__tests__/index.test.js
...ges/app/src/components/AdminIndex/__tests__/index.test.js
+0
-3
packages/app/src/components/AdminIndex/index.js
packages/app/src/components/AdminIndex/index.js
+5
-3
packages/app/src/components/Answer/index.js
packages/app/src/components/Answer/index.js
+106
-0
packages/app/src/components/Answer/index.style.js
packages/app/src/components/Answer/index.style.js
+54
-0
packages/app/src/components/LegalReferences/Tag.js
packages/app/src/components/LegalReferences/Tag.js
+4
-4
packages/app/src/components/LegalReferences/Tag.style.js
packages/app/src/components/LegalReferences/Tag.style.js
+10
-4
packages/app/src/components/LegalReferences/index.js
packages/app/src/components/LegalReferences/index.js
+7
-6
packages/app/src/components/LegalReferences/index.style.js
packages/app/src/components/LegalReferences/index.style.js
+7
-0
packages/app/src/components/Modal.js
packages/app/src/components/Modal.js
+1
-1
packages/app/src/components/Pagination.js
packages/app/src/components/Pagination.js
+0
-58
packages/app/src/components/Reference.js
packages/app/src/components/Reference.js
+0
-75
packages/app/src/components/Tags/Tag.js
packages/app/src/components/Tags/Tag.js
+0
-60
packages/app/src/components/Tags/__tests__/Tag.test.js
packages/app/src/components/Tags/__tests__/Tag.test.js
+0
-56
packages/app/src/components/Tags/__tests__/index.test.js
packages/app/src/components/Tags/__tests__/index.test.js
+0
-111
packages/app/src/components/Tags/index.js
packages/app/src/components/Tags/index.js
+0
-149
packages/app/src/components/__tests__/Pagination.test.js
packages/app/src/components/__tests__/Pagination.test.js
+0
-24
packages/app/src/constants.js
packages/app/src/constants.js
+10
-5
packages/app/src/elements/Button.js
packages/app/src/elements/Button.js
+17
-20
packages/app/src/elements/ContentTitle.js
packages/app/src/elements/ContentTitle.js
+13
-1
packages/app/src/elements/Field.js
packages/app/src/elements/Field.js
+7
-0
packages/app/src/elements/Hr.js
packages/app/src/elements/Hr.js
+3
-1
packages/app/src/elements/Icon.js
packages/app/src/elements/Icon.js
+7
-1
packages/app/src/elements/Idcc.js
packages/app/src/elements/Idcc.js
+38
-15
packages/app/src/elements/Input.js
packages/app/src/elements/Input.js
+29
-12
packages/app/src/elements/LoadingSpinner.js
packages/app/src/elements/LoadingSpinner.js
+6
-1
No files found.
.eslintrc
View file @
5362e466
...
...
@@ -27,7 +27,9 @@
},
{
"files": [
"packages/app/src/components/Answer/**/*.js",
"packages/app/src/components/LegalReferences/**/*.js",
"packages/app/src/elements/**/*.js",
"packages/app/src/templates/**/*.js"
],
"extends": ["@socialgouv/eslint-config-react-strict", "prettier"],
...
...
@@ -36,7 +38,7 @@
"node": true
},
"rules": {
"jest/no-disabled-tests": "
off
",
"jest/no-disabled-tests": "
error
",
"react/jsx-sort-props": "error",
"react/prop-types": "error"
}
...
...
@@ -66,6 +68,7 @@
"jest": true
},
"globals": {
"testClick": false,
"testRender": false,
"waitFor": false
}
...
...
CONTRIBUTING.md
View file @
5362e466
...
...
@@ -13,8 +13,10 @@ help make it even better than it is today!
-
[
Docker Compose
](
#docker-compose
)
-
[
Jest Watch
](
#jest-watch
)
-
[
Naming Guidelines
](
#naming-guidelines
)
-
[
API-related Actions
](
#api-related-actions
)
-
[
React Component Actions
](
#react-component-actions
)
-
[
API-related methods
](
#api-related-methods
)
-
[
React methods
](
#react-methods
)
-
[
Redux states
](
#redux-states
)
-
[
React variables
](
#react-variables
)
-
[
Commit Message Guidelines
](
#commit-message-guidelines
)
-
[
Revert
](
#revert
)
-
[
Type
](
#type
)
...
...
@@ -128,20 +130,53 @@ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo s
## Naming Guidelines
### API-related
Action
s
### API-related
method
s
This includes React
components
methods as well as Redux actions, action types and sagas:
This includes React methods as well as Redux actions, action types and sagas:
-
A
ny
`GET`
call-related method
should
start with the verb
**load**
.
-
A
ny
`POST`
call-related method
should
start with the verb
**create**
, or
**add**
if it targets a
-
A
ll
`GET`
call-related method
s must
start with the verb
**load**
.
-
A
ll
`POST`
call-related method
s must
start with the verb
**create**
, or
**add**
if it targets a
foreign entity (i.e.:
`addAnswerComment()`
).
-
A
ny
`PATCH`
call-related method
should
start with the verb
**update**
.
-
A
ny
`DELETE`
call-related method
should
start with the verb
**delete**
(or
**\_delete**
), or
-
A
ll
`PATCH`
call-related method
s must
start with the verb
**update**
.
-
A
ll
`DELETE`
call-related method
s must
start with the verb
**delete**
(or
**\_delete**
), or
**remove**
if it targets a foreign entity (i.e.:
`removeAnswerComment()`
).
### React Component Actions
### React methods
-
All the methods returning a JSX value should start with the verb
**render**
.
### Redux states
A common state should look like:
```
ts
interface
{
/** Single entity data (creation, edition) */
data
:
Object
|
null
;
error
:
Error
|
null
;
/** Is it fetching data? */
isLoading
:
boolean
;
/**
Total number of entities (listing)
@description
This represents the number of entities available on the API and should be higher than the list
length if there is more than one page.
*/
length
:
number
;
/** Multiple entities data (listing) */
list
:
Object
[];
/** Current page index (listing) */
pagesIndex
:
number
;
/** Total number of pages (listing) */
pagesLength
:
number
;
}
```
### React variables
-
Any method returning a JSX value should start with the verb
**render**
.
-
All the variables referencing a component must start with
**$**
(i.e.:
`<Button ref={node => this.$button = node}>`
).
---
...
...
db/migrations/20200430032517_switch_full_answers_view_to_left_join.sql
0 → 100644
View file @
5362e466
-------------------------------------- UP --------------------------------------
DROP
VIEW
api
.
full_answers
;
CREATE
VIEW
api
.
full_answers
AS
SELECT
answers
.
*
,
questions
.
index
AS
question_index
,
questions
.
value
AS
question_value
,
agreements
.
name
AS
agreement_name
,
agreements
.
idcc
AS
agreement_idcc
FROM
api
.
answers
answers
LEFT
JOIN
api
.
questions
questions
ON
questions
.
id
=
answers
.
question_id
LEFT
JOIN
api
.
agreements
agreements
ON
agreements
.
id
=
answers
.
agreement_id
;
GRANT
SELECT
ON
api
.
full_answers
TO
administrator
;
GRANT
SELECT
ON
api
.
full_answers
TO
contributor
;
------------------------------------- DOWN -------------------------------------
DROP
VIEW
api
.
full_answers
;
CREATE
VIEW
api
.
full_answers
AS
SELECT
answers
.
*
,
questions
.
index
AS
question_index
,
questions
.
value
AS
question_value
,
agreements
.
name
AS
agreement_name
,
agreements
.
idcc
AS
agreement_idcc
FROM
api
.
answers
answers
INNER
JOIN
api
.
questions
questions
ON
questions
.
id
=
answers
.
question_id
INNER
JOIN
api
.
agreements
agreements
ON
agreements
.
id
=
answers
.
agreement_id
;
GRANT
SELECT
ON
api
.
full_answers
TO
administrator
;
GRANT
SELECT
ON
api
.
full_answers
TO
contributor
;
db/migrations/knex/20200430032517_switch_full_answers_view_to_left_join.js
0 → 100644
View file @
5362e466
const
getMigrationQuery
=
require
(
"
../../../scripts/db/getMigrationQuery
"
);
exports
.
up
=
async
knex
=>
{
await
knex
.
raw
(
getMigrationQuery
(
"
20200430032517_switch_full_answers_view_to_left_join
"
).
up
());
};
exports
.
down
=
async
knex
=>
{
await
knex
.
raw
(
getMigrationQuery
(
"
20200430032517_switch_full_answers_view_to_left_join
"
).
down
());
};
db/seeds/dev/06-logs.js
View file @
5362e466
...
...
@@ -12,17 +12,17 @@ function getRandomIp() {
}
function
getRandomLog
()
{
const
ip
=
getRandomIp
();
const
action
=
ACTIONS
[
getRandomIntBetween
(
0
,
2
)];
const
url
=
"
/path
"
;
const
created_at
=
new
Date
(
Date
.
now
()
-
getRandomIntBetween
(
0
,
30
*
24
*
60
*
60
*
1000
));
const
ip
=
getRandomIp
();
const
method
=
ACTIONS
[
getRandomIntBetween
(
0
,
2
)];
const
path
=
"
/dummy-path
"
;
const
user_id
=
`00000000-0000-4000-8000-00000000040
${
getRandomIntBetween
(
1
,
5
)}
`
;
return
{
action
,
created_at
,
ip
,
url
,
method
,
path
,
user_id
,
};
}
...
...
@@ -32,7 +32,7 @@ exports.seed = async knex => {
const
logs
=
Array
.
from
({
length
:
1000
},
getRandomLog
);
await
knex
(
"
api
.logs
"
).
insert
(
logs
);
await
knex
(
"
public
.logs
"
).
insert
(
logs
);
global
.
spinner
.
succeed
(
`Logs generated.`
);
};
db/snapshot_test_db.dump
View file @
5362e466
No preview for this file type
package.json
View file @
5362e466
...
...
@@ -58,7 +58,7 @@
"
imgur
"
:
"
0.3.1
"
,
"
jest
"
:
"
25.4.0
"
,
"
ora
"
:
"
4.0.4
"
,
"
postgrester
"
:
"
1.
3.1
"
,
"
postgrester
"
:
"
1.
4.0
"
,
"
prettier
"
:
"
2.0.5
"
,
"
puppeteer
"
:
"
3.0.1
"
,
"
typescript
"
:
"
3.8.3
"
...
...
packages/api/.dockerignore
View file @
5362e466
...
...
@@ -3,6 +3,7 @@ __tests__/
node_modules/
tests/
*.d.ts
*.md
jest.config.js
...
...
packages/api/src/index.js
View file @
5362e466
// https://www.typescriptlang.org/docs/handbook/declaration-files/library-structures.html#dependencies-on-global-libraries
/// <reference types="@socialgouv/code-du-travail-backoffice__typings" />
const
http
=
require
(
"
http
"
);
const
httpProxy
=
require
(
"
http-proxy
"
);
const
log
=
require
(
"
@inspired-beings/log
"
);
...
...
packages/api/tsconfig.json
View file @
5362e466
{
"extends"
:
"../../tsconfig.json"
,
"compilerOptions"
:
{}
"compilerOptions"
:
{},
"include"
:
[
"typings-env.d.ts"
]
}
packages/ap
p/src/index.j
s
→
packages/ap
i/typings-env.d.t
s
View file @
5362e466
// https://www.typescriptlang.org/docs/handbook/declaration-files/library-structures.html#dependencies-on-global-libraries
/// <reference types="@socialgouv/code-du-travail-backoffice__typings" />
packages/app/package.json
View file @
5362e466
...
...
@@ -25,14 +25,14 @@
"koa-router"
:
"8.0.8"
,
"lodash.debounce"
:
"4.0.8"
,
"moment-timezone"
:
"0.5.28"
,
"next"
:
"9.3.
5
"
,
"next"
:
"9.3.
6
"
,
"next-cookies"
:
"2.0.3"
,
"next-redux-saga"
:
"4.1.2"
,
"next-redux-wrapper"
:
"5.0.0"
,
"numeral"
:
"2.0.6"
,
"password-generator"
:
"2.2.3"
,
"pg"
:
"8.0.3"
,
"postgrester"
:
"1.
3.1
"
,
"postgrester"
:
"1.
4.0
"
,
"prop-types"
:
"15.7.2"
,
"quill"
:
"1.3.7"
,
"ramda"
:
"0.27.0"
,
...
...
@@ -41,7 +41,6 @@
"react-dom"
:
"16.13.1"
,
"react-medixtor"
:
"0.1.0-alpha.16"
,
"react-onclickoutside"
:
"6.9.0"
,
"react-paginate"
:
"6.3.2"
,
"react-redux"
:
"7.2.0"
,
"react-select"
:
"3.1.0"
,
"react-table"
:
"6.11.5"
,
...
...
@@ -63,12 +62,10 @@
"unified"
:
"9.0.0"
},
"devDependencies"
:
{
"@babel/core"
:
"7.9.0"
,
"@testing-library/jest-dom"
:
"5.5.0"
,
"@testing-library/react"
:
"10.0.3"
,
"@types/ramda"
:
"0.27.4"
,
"@types/react-test-renderer"
:
"16.9.2"
,
"babel-eslint"
:
"10.1.0"
,
"babel-jest"
:
"25.4.0"
,
"dotenv"
:
"8.2.0"
,
"identity-obj-proxy"
:
"3.0.0"
,
"jest-emotion"
:
"10.0.32"
,
...
...
@@ -76,9 +73,7 @@
"nodemon"
:
"2.0.3"
,
"prettier"
:
"2.0.5"
,
"react-test-renderer"
:
"16.13.1"
,
"rimraf"
:
"3.0.2"
,
"snapshot-diff"
:
"0.7.0"
,
"uuid"
:
"8.0.0"
,
"zxcvbn"
:
"4.4.2"
}
}
packages/app/pages/admin/answers/edit.js
View file @
5362e466
This diff is collapsed.
Click to expand it.
packages/app/pages/admin/answers/index.js
View file @
5362e466
import
styled
from
"
@emotion/styled
"
;
import
debounce
from
"
lodash.debounce
"
;
import
Router
from
"
next/router
"
;
import
React
from
"
react
"
;
import
{
connect
}
from
"
react-redux
"
;
import
{
Flex
}
from
"
rebass
"
;
import
*
as
actions
from
"
../../../src/actions
"
;
import
AdminAnswerBlock
from
"
../../../src/blocks/AdminAnswer
"
;
import
Pagination
from
"
../../../src/components/Pagination
"
;
import
{
ANSWER_STATE_OPTIONS
}
from
"
../../../src/constants
"
;
import
Answer
from
"
../../../src/components/Answer
"
;
import
*
as
C
from
"
../../../src/constants
"
;
import
Button
from
"
../../../src/elements/Button
"
;
import
Checkbox
from
"
../../../src/elements/Checkbox
"
;
import
Input
from
"
../../../src/elements/Input
"
;
import
LoadingSpinner
from
"
../../../src/elements/LoadingSpinner
"
;
import
Select
from
"
../../../src/elements/Select
"
;
import
Title
from
"
../../../src/elements/Title
"
;
import
AdminMainLayout
from
"
../../../src/layouts/AdminMain
"
;
...
...
@@ -20,37 +21,32 @@ const Container = styled(Flex)`
flex-grow: 1;
margin: 0 1rem 1rem;
`
;
const
List
=
styled
(
Flex
)
`
flex-grow: 1;
padding-right: 1rem;
min-height: 0;
overflow-y: auto;
`
;
const
Top
=
styled
(
Flex
)
`
margin-bottom: 0.75rem;
const
ListSpinner
=
styled
(
Flex
)
`
margin-top: 1rem;
`
;
const
FiltersContainer
=
styled
(
Flex
)
`
border-bottom: solid 1px var(--color-border);
border-top: solid 1px var(--color-border);
padding: 0.5rem 0;
> * {
flex-grow: 0.25;
}
`
;
const
FilterSelect
=
styled
(
Select
)
`
margin-left: 1rem;
`
;
const
ActionsContainer
=
styled
(
Flex
)
`
background-color: var(--color-alice-blue);
border-bottom: solid 1px var(--color-border);
padding: 0.5rem 0.5rem 0.5rem 1rem;
`
;
const
Text
=
styled
.
p
`
margin-bottom: 0.5rem;
`
;
const
HelpText
=
styled
(
Text
)
`
font-size: 0.875rem;
`
;
export
class
AdminAnswersIndexPage
extends
React
.
Component
{
get
queryFilter
()
{
return
this
.
$queryFilter
!==
undefined
&&
this
.
$queryFilter
!==
null
...
...
@@ -58,6 +54,17 @@ export class AdminAnswersIndexPage extends React.Component {
:
""
;
}
constructor
(
props
)
{
super
(
props
);
this
.
isListeningListScroll
=
false
;
this
.
$list
=
null
;
this
.
onListScroll
=
this
.
onListScroll
.
bind
(
this
);
this
.
setQueryFilter
=
debounce
(
this
.
setQueryFilter
,
250
).
bind
(
this
);
}
componentDidMount
()
{
const
{
isGeneric
}
=
this
.
props
;
...
...
@@ -66,20 +73,41 @@ export class AdminAnswersIndexPage extends React.Component {
this
.
props
.
dispatch
(
actions
.
answers
.
setFilters
({
isGeneric
,
pageLength
:
10
,
pageLength
:
53
,
}),
);
}
componentDidUpdate
()
{
const
{
answers
}
=
this
.
props
;
if
(
this
.
$list
===
null
||
answers
.
isLoading
||
this
.
isListeningListScroll
)
return
;
this
.
$list
.
addEventListener
(
"
scroll
"
,
this
.
onListScroll
);
this
.
isListeningListScroll
=
true
;
}
onListScroll
()
{
const
{
answers
,
dispatch
}
=
this
.
props
;
if
(
answers
.
isLoading
||
answers
.
pagesIndex
>=
answers
.
pagesLength
-
1
)
return
;
const
scrollTopLimit
=
this
.
$list
.
scrollHeight
-
this
.
$list
.
clientHeight
-
1600
;
if
(
this
.
$list
.
scrollTop
>
scrollTopLimit
)
{
dispatch
(
actions
.
answers
.
load
(
answers
.
pagesIndex
+
1
));
}
}
getCheckableAnswerIds
()
{
const
{
answers
}
=
this
.
props
;
return
answers
.
list
.
map
(({
id
})
=>
id
).
filter
(
id
=>
!
answers
.
checked
.
includes
(
id
));
}
setAgreeementsFilter
(
selected
)
{
const
agreements
=
selected
!==
null
?
selected
:
[];
this
.
props
.
dispatch
(
actions
.
answers
.
setFilter
(
"
agreements
"
,
agreements
));
}
setPageFilter
({
selected
})
{
this
.
props
.
dispatch
(
actions
.
answers
.
setFilter
(
"
page
"
,
selected
));
}
setQuestionsFilter
(
selected
)
{
const
questions
=
selected
!==
null
?
selected
:
[];
this
.
props
.
dispatch
(
actions
.
answers
.
setFilter
(
"
questions
"
,
questions
));
...
...
@@ -99,8 +127,8 @@ export class AdminAnswersIndexPage extends React.Component {
}
checkAll
()
{
const
{
dispatch
,
answers
}
=
this
.
props
;
const
ids
=
answers
.
data
.
map
(({
id
})
=>
id
).
filter
(
id
=>
!
answers
.
checked
.
includes
(
id
)
);
const
{
dispatch
}
=
this
.
props
;
const
ids
=
this
.
getCheckableAnswerIds
(
);
dispatch
(
actions
.
answers
.
toggleCheck
(
ids
));
}
...
...
@@ -142,25 +170,34 @@ export class AdminAnswersIndexPage extends React.Component {
}
renderAnswersList
()
{
const
{
checked
,
data
,
isLoading
}
=
this
.
props
.
answers
;
if
(
isLoading
||
!
Array
.
isArray
(
data
))
{
return
<
HelpText
>
Chargement
…
<
/HelpText>
;
const
{
checked
,
list
,
isLoading
}
=
this
.
props
.
answers
;
if
(
list
.
length
===
0
)
{
return
(
<
List
alignItems
=
"
center
"
justifyContent
=
"
center
"
>
{
isLoading
?
<
LoadingSpinner
/>
:
T
.
ADMIN_ANSWERS_INFO_NO_DATA
}
<
/List
>
);
}
if
(
data
.
length
===
0
)
{
return
<
p
>
{
T
.
ADMIN_ANSWERS_INFO_NO_DATA
}
<
/p>
;
}
return
data
.
map
(
answer
=>
(
<
AdminAnswerBlock
data
=
{
answer
}
isChecked
=
{
checked
.
includes
(
answer
.
id
)}
key
=
{
answer
.
id
}
onCheck
=
{
this
.
check
.
bind
(
this
)}
onClick
=
{
this
.
editAnswer
.
bind
(
this
)}
/
>
));
return
(
<
List
flexDirection
=
"
column
"
ref
=
{
node
=>
(
this
.
$list
=
node
)}
>
{
list
.
map
(
answer
=>
(
<
Answer
data
=
{
answer
}
isChecked
=
{
checked
.
includes
(
answer
.
id
)}
key
=
{
answer
.
id
}
onCheck
=
{
this
.
check
.
bind
(
this
)}
onClick
=
{
this
.
editAnswer
.
bind
(
this
)}
/
>
))}
{
isLoading
&&
(
<
ListSpinner
alignItems
=
"
center
"
justifyContent
=
"
center
"
>
<
LoadingSpinner
/>
<
/ListSpinner
>
)}
<
/List
>
);
}
render
()
{
...
...
@@ -169,26 +206,25 @@ export class AdminAnswersIndexPage extends React.Component {
const
isLoading
=
isGeneric
?
answers
.
isLoading
:
agreements
.
isLoading
||
answers
.
isLoading
||
questions
.
isLoading
;
const
stateFilterAgreements
=
agreements
.
data
.
map
(({
id
,
idcc
,
name
})
=>
({
const
stateFilterAgreements
=
agreements
.
list
.
map
(({
id
,
idcc
,
name
})
=>
({
label
:
`[
${
idcc
}
]
${
name
}
`
,
value
:
id
,
}));
const
stateFilterQuestions
=
questions
.
data
.
map
(({
id
,
index
,
value
})
=>
({
const
stateFilterQuestions
=
questions
.
list
.
map
(({
id
,
index
,
value
})
=>
({
label
:
`
${
index
}
)
${
value
}
`
,
value
:
id
,
}));
const
stateActionOptions
=
ANSWER_STATE_OPTIONS
.
filter
(({
value
})
=>
value
!==
answers
.
state
);
return
(
<
AdminMainLayout
hasBareContent
>
<
Container
flexDirection
=
"
column
"
>
<
Top
alignItems
=
"
baseline
"
justifyContent
=
"
space-between
"
>
<
Flex
alignItems
=
"
center
"
justifyContent
=
"
space-between
"
>
<
Title
>
{
`Réponses
${
isGeneric
?
"
génériques
"
:
""
}
`
}
<
/Title
>
<
Button
disabled
=
{
isLoading
}
onClick
=
{
this
.
printAnswers
.
bind
(
this
)}
>
Imprimer
<
/Button
>
<
/
Top
>
<
/
Flex
>
{
/* Filters */
}
{
!
isGeneric
&&
(
...
...
@@ -196,34 +232,34 @@ export class AdminAnswersIndexPage extends React.Component {
<
Input
defaultValue
=
{
answers
.
filters
.
query
}
icon
=
"
search
"
onChange
=
{
this
.
setQueryFilter
.
bind
(
this
)
}
onChange
=
{
this
.
setQueryFilter
}
ref
=
{
node
=>
(
this
.
$queryFilter
=
node
)}
/
>
{
/* We must set the {instanceId} prop to avoid "Prop `id` did not match." warning. */
}
{
/* https://github.com/trezor/trezor-suite/issues/290#issuecomment-516349580 */
}
<
FilterSelect
<
Select
instanceId
=
"
statesFilter
"
isLoading
=
{
isLoading
}
isMulti
onChange
=
{
this
.
setStatesFilter
.
bind
(
this
)}
options
=
{
ANSWER_STATE_OPTIONS
}
options
=
{
C
.
ANSWER_STATE_OPTIONS
}
value
=
{
answers
.
filters
.
states
}
withMarginLeft
/>
<
Filter
Select
<
Select
instanceId
=
"
agreementsFilter
"
isLoading
=
{
isLoading
}
isLoading
=
{
agreements
.
isLoading
}
isMulti
onChange
=
{
this
.
setAgreeementsFilter
.
bind
(
this
)}
options
=
{
stateFilterAgreements
}
value
=
{
answers
.
filters
.
agreements
}
withMarginLeft
/>
<
Filter
Select
<
Select
instanceId
=
"
questionsFilter
"
isLoading
=
{
isLoading
}
isLoading
=
{
questions
.
isLoading
}
isMulti
onChange
=
{
this
.
setQuestionsFilter
.
bind
(
this
)}
options
=
{
stateFilterQuestions
}
value
=
{
answers
.
filters
.
questions
}
withMarginLeft
/>
<
/FiltersContainer
>
)}
...
...
@@ -231,37 +267,30 @@ export class AdminAnswersIndexPage extends React.Component {
{
/* Actions */
}
<
ActionsContainer
alignItems
=
"
center
"
justifyContent
=
"
space-between
"
>
<
Checkbox
i
con
=
{
answers
.
checked
.
length
>
0
?
"
check-square
"
:
"
square
"
}
isDisabled
=
{
isLoading
}
i
sChecked
=
{
answers
.
checked
.
length
>
0
}
isDisabled
=
{
isLoading
||
answers
.
list
.
length
===
0
}
onClick
=
{
answers
.
checked
.
length
>
0
?
this
.
uncheckAll
.
bind
(
this
)
:
this
.
checkAll
.
bind
(
this
)
}
/
>
<
Flex
>
<
Select
instanceId
=
"
stateAction
"
isDisabled
=
{
isLoading
||
answers
.
checked
.
length
===
0
}
isLoading
=
{
isLoading
}
options
=
{
stateActionOptions
}
options
=
{
C
.
ANSWER_STATE_OPTIONS
}
ref
=
{
node
=>
(
this
.
$newState
=
node
)}
/
>
<
Button
isDisabled
=
{
isLoading
||
answers
.
checked
.
length
===
0
}
onClick
=
{
this
.
setCheckedAnswersState
.
bind
(
this
)}
with
Left
Margin
withMargin
Left
>
{
`Appliquer (
${
answers
.
checked
.
length
}
)`
}
<
/Button
>
<
/Flex
>
<
/ActionsContainer
>
<
List
flexDirection
=
"
column
"
>
{
this
.
renderAnswersList
()}
<
/List
>
{
!
isLoading
&&
answers
.
pagesLength
>
0
&&
(