lodash#chain JavaScript Examples
The following examples show how to use
lodash#chain.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
getScopesFromEnrollments = (enrollments) =>
chain(enrollments)
.map(({ scopes }) =>
chain(scopes)
.omitBy((v) => !v)
.keys()
.value()
)
.flatten()
.uniq()
.value()
Example #2
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
dataProviderParametersToContactInfo = (parameters) =>
chain(parameters)
.map(({ label, ...rest }) =>
label.endsWith(' (Bac à sable)')
? { label: label.replace(' (Bac à sable)', ''), ...rest }
: { label, ...rest }
)
.reject(({ email }) => !email)
.reject(({ label }) => label.endsWith(' (Production)'))
.reject(({ label }) => label.endsWith(' (FC)'))
.groupBy(({ email }) => email)
.mapValues((params) => {
let labels = params.map(({ label }) => label);
if (labels.length > 4) {
labels = labels.slice(0, 4);
labels.push('etc.');
}
return labels.join(', ');
})
.toPairs()
.map(([email, label]) => ({ email, label }))
.value()
Example #3
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
getChangelogV1 = (diff) =>
chain(diff)
// { intitule: ['a', 'b'], contacts: [[{'name': 'c', email: 'd'}], [{'name': 'e', email: 'd'}]] }
.omit(['updated_at'])
.transform(flattenDiffTransformer, {})
// { intitule: ['a', 'b'], contacts.0.name: ['c', 'e'] }
.transform(changelogFormatTransformer, [])
// ['changement d’intitule de "a" en "b"', 'changement du nom du DPD de "c" en "d"']
.value()
Example #4
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
export function collectionWithKeyToObject(collection) {
return (
chain(collection)
// [{ id: 'a', attr1: 'a1', attr2: 'a2' }, { id: 'b', attr1: 'b1', attr2: 'b2' }]
.map(({ id, ...attributes }) => [id, attributes])
// [[ 'a', { attr1: 'a1', attr2: 'a2' }], ['b', { attr1: 'b1', attr2: 'b2' }]]
.fromPairs()
// { a: { attr1: 'a1', attr2: 'a2' }, b: { attr1: 'b1', attr2: 'b2' }}
.value()
);
}
Example #5
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
export function getErrorMessages(error) {
if (!isEmpty(error.response) && isObject(error.response.data)) {
return chain(error.response.data).values().flatten().value();
}
const errorMessageEnd =
'Merci de réessayer ultérieurement. ' +
'Vous pouvez également nous signaler cette erreur par mail à [email protected].';
if (!isEmpty(error.response) && error.message !== 'Network Error') {
return [
`Une erreur est survenue. Le code de l’erreur est ${error.response.status} (${error.response.statusText}). ${errorMessageEnd}`,
];
}
if (error.message === 'Network Error') {
return [
'Une erreur de connexion au serveur est survenue. ' +
'Merci de vérifier que vous êtes bien connecté à internet. ' +
'Si vous utilisez un réseau d’entreprise, merci de signaler cette erreur à ' +
'l’administrateur de votre réseau informatique. ' +
'Si le problème persiste, vous pouvez nous contacter par mail à ' +
'[email protected].',
];
}
console.error(error);
return [`Une erreur inconnue est survenue. ${errorMessageEnd}`];
}
Example #6
Source File: useNewTeamMembers.js From datapass with GNU Affero General Public License v3.0 | 6 votes |
useNewTeamMembers = ({
user,
team_members,
contactConfiguration,
}) =>
useMemo(
() =>
chain(contactConfiguration)
.keys()
.map((type) => {
const isMemberAlreadyInitialized = team_members.some(
({ type: t }) => t === type
);
if (isMemberAlreadyInitialized) {
return null;
}
const tmp_id = uniqueId(`tmp_`);
let newTeamMember = { type, tmp_id };
if (type === 'demandeur') {
newTeamMember = {
...newTeamMember,
email: user.email,
};
}
return newTeamMember;
})
.compact()
.value(),
[user, team_members, contactConfiguration]
)
Example #7
Source File: dataProvider.js From acy-dex-interface with MIT License | 5 votes |
export function useSwapSources({ from = FIRST_DATE_TS, to = NOW_TS, chainName = "arbitrum" } = {}) {
const query = `{
a: ${getSwapSourcesFragment(0, from, to)}
b: ${getSwapSourcesFragment(1000, from, to)}
c: ${getSwapSourcesFragment(2000, from, to)}
d: ${getSwapSourcesFragment(3000, from, to)}
e: ${getSwapSourcesFragment(4000, from, to)}
}`
const [graphData, loading, error] = useGraph(query, { chainName })
let total = 0
let data = useMemo(() => {
if (!graphData) {
return null
}
const {a, b, c, d, e} = graphData
let ret = chain([...a, ...b, ...c, ...d, ...e])
.groupBy(item => parseInt(item.timestamp / 86400) * 86400)
.map((values, timestamp) => {
let all = 0
const retItem = {
timestamp: Number(timestamp),
...values.reduce((memo, item) => {
const source = knownSwapSources[chainName][item.source] || item.source
if (item.swap != 0) {
const volume = item.swap / 1e30
memo[source] = memo[source] || 0
memo[source] += volume
all += volume
}
return memo
}, {})
}
retItem.all = all
return retItem
})
.sortBy(item => item.timestamp)
.value()
return ret
}, [graphData])
return [data, loading, error]
}
Example #8
Source File: resources.js From file-picker with Apache License 2.0 | 5 votes |
export function buildResource(resource) {
const ext = resource.type !== 'dir' ? _extName(resource.name) : ''
return {
type: resource.type === 'dir' ? 'folder' : resource.type,
// actual file id (string)
id: resource.fileInfo['{http://owncloud.org/ns}fileid'],
// temporary list id, to be used for view only and for uniqueness inside the list
viewId: uniqueId('file-'),
starred: resource.fileInfo['{http://owncloud.org/ns}favorite'] !== '0',
mdate: resource.fileInfo['{DAV:}getlastmodified'],
size: (function () {
if (resource.type === 'dir') {
return resource.fileInfo['{http://owncloud.org/ns}size']
} else {
return resource.fileInfo['{DAV:}getcontentlength']
}
})(),
extension: (function () {
return ext
})(),
name: path.basename(resource.name),
path: resource.name,
permissions: resource.fileInfo['{http://owncloud.org/ns}permissions'] || '',
etag: resource.fileInfo['{DAV:}getetag'],
sharePermissions:
resource.fileInfo['{http://open-collaboration-services.org/ns}share-permissions'],
shareTypes: (function () {
let shareTypes = resource.fileInfo['{http://owncloud.org/ns}share-types']
if (shareTypes) {
shareTypes = chain(shareTypes)
.filter(
(xmlvalue) =>
xmlvalue.namespaceURI === 'http://owncloud.org/ns' &&
xmlvalue.nodeName.split(':')[1] === 'share-type'
)
.map((xmlvalue) => parseInt(xmlvalue.textContent || xmlvalue.text, 10))
.value()
}
return shareTypes || []
})(),
privateLink: resource.fileInfo['{http://owncloud.org/ns}privatelink'],
owner: {
username: resource.fileInfo['{http://owncloud.org/ns}owner-id'],
displayName: resource.fileInfo['{http://owncloud.org/ns}owner-display-name']
},
canUpload: function () {
return this.permissions.indexOf('C') >= 0
},
canDownload: function () {
return this.type !== 'folder'
},
canBeDeleted: function () {
return this.permissions.indexOf('D') >= 0
},
canRename: function () {
return this.permissions.indexOf('N') >= 0
},
canShare: function () {
return this.permissions.indexOf('R') >= 0
},
canCreate: function () {
return this.permissions.indexOf('C') >= 0
},
isMounted: function () {
return this.permissions.indexOf('M') >= 0
},
isReceivedShare: function () {
return this.permissions.indexOf('S') >= 0
}
}
}
Example #9
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 5 votes |
getChangelogV2 = (diff) =>
chain(diff)
// { intitule: ['a', 'b'], team_members: { "_t": "a", "0": { 'name': ['c','e'], email: ['d'] } } }
.transform(flattenDiffTransformerV2Factory(), {})
// { intitule: ['a', 'b'], team_members.0.name: ['c', 'e'] , team_members.0.email: ['d'] }
.transform(changelogFormatTransformer, [])
// ['changement d’intitule de "a" en "b"', ...]
.value()
Example #10
Source File: Enrollment.js From datapass with GNU Affero General Public License v3.0 | 5 votes |
Enrollment = ({
id,
events,
target_api,
intitule,
description,
status,
onSelect,
}) => {
const handleClick = useCallback(
(e) => {
onSelect(target_api, id, e);
},
[id, target_api, onSelect]
);
const lastUpdateEvent = useMemo(() => {
return chain(events)
.sortBy('updated_at')
.filter(({ name }) => ['created', 'updated'].includes(name))
.last()
.value();
}, [events]);
return (
<div className="enrollment">
<div className="enrollment-header">
<div className="fs">
{DATA_PROVIDER_PARAMETERS[target_api]?.label}{' '}
{DATA_PROVIDER_PARAMETERS[target_api]?.icon && (
<div>
<img
src={`/images/${DATA_PROVIDER_PARAMETERS[target_api]?.icon}`}
alt={`logo ${DATA_PROVIDER_PARAMETERS[target_api]?.label}`}
/>
</div>
)}
</div>
<Tag type={STATUS_TO_BUTTON_TYPE[status]}>
{USER_STATUS_LABELS[status]}
</Tag>
</div>
<div className="enrollment-body">
<div className="title">
<div className="intitule">{intitule || 'Aucun intitulé'}</div>
{lastUpdateEvent && (
<div className="creation">
Dernière modification le{' '}
<b>{moment(lastUpdateEvent.created_at).format('L')}</b> par{' '}
<b>{lastUpdateEvent.user.email}</b>
</div>
)}
</div>
<p className="description">{description || 'Aucune description'}</p>
<ActivityFeedWrapper
events={events}
status={status}
target_api={target_api}
/>
<div className="enrollment-footer">
<div>
<b>N° {id}</b>
</div>
<Button large onClick={handleClick}>
Consulter
</Button>
</div>
</div>
</div>
);
}
Example #11
Source File: ActivityFeed.js From datapass with GNU Affero General Public License v3.0 | 5 votes |
render() {
const { showDetails } = this.state;
const { events } = this.props;
let eventsToDisplay = chain(events)
.sortBy('updated_at')
.reject(
({ name, diff }) => name === 'update' && isEmpty(getChangelog(diff))
)
.value();
if (!showDetails && events.length > 0) {
eventsToDisplay = [last(eventsToDisplay)];
}
return (
<div>
<div className="activity-head">
<Button
outline
icon="eye"
onClick={() => this.setState({ showDetails: !showDetails })}
>
{showDetails ? 'Cacher l’historique' : 'Voir l’historique'}
</Button>
</div>
{eventsToDisplay.map(
({
id,
comment,
name,
updated_at,
user: { email, given_name, family_name },
diff,
}) => (
<EventItem
key={id}
comment={comment}
name={name}
updated_at={updated_at}
email={email}
family_name={family_name}
given_name={given_name}
diff={diff}
/>
)
)}
</div>
);
}
Example #12
Source File: dataProvider.js From acy-dex-interface with MIT License | 5 votes |
export function useVolumesData({ from = FIRST_DATE_TS, to = NOW_TS, chainName = "arbitrum" } = {}) {
const PROPS = 'margin liquidation swap mint burn'.split(' ')
const feesQuery = `{
volumeStats(
first: 1000
orderBy: id
orderDirection: desc
where: { period: daily, id_gte: ${from}, id_lte: ${to} }
) {
id
margin
liquidation
swap
mint
burn
${chainName === "avalanche" ? "timestamp" : ""}
}
}`
let [feesData, loading, error] = useGraph(feesQuery, {
chainName
})
const feesChartData = useMemo(() => {
if (!feesData) {
return null
}
let chartData = sortBy(feesData.feeStats, 'id').map(item => {
const ret = { timestamp: item.timestamp || item.id };
PROPS.forEach(prop => {
if (item[prop]) {
ret[prop] = item[prop] / 1e30
}
})
ret.liquidation = item.liquidation / 1e30 - item.margin / 1e30
ret.all = PROPS.reduce((memo, prop) => memo + ret[prop], 0)
return ret
})
let cumulative = 0
const cumulativeByTs = {}
return chain(chartData)
.groupBy(item => item.timestamp)
.map((values, timestamp) => {
const all = sumBy(values, 'all')
cumulative += all
let movingAverageAll
const movingAverageTs = timestamp - MOVING_AVERAGE_PERIOD
if (movingAverageTs in cumulativeByTs) {
movingAverageAll = (cumulative - cumulativeByTs[movingAverageTs]) / MOVING_AVERAGE_DAYS
}
const ret = {
timestamp: Number(timestamp),
all,
cumulative,
movingAverageAll
}
PROPS.forEach(prop => {
ret[prop] = sumBy(values, prop)
})
cumulativeByTs[timestamp] = cumulative
return ret
})
.value()
.filter(item => item.timestamp >= from)
}, [feesData])
return [feesChartData, loading, error]
}
Example #13
Source File: dataProvider.js From acy-dex-interface with MIT License | 5 votes |
export function useFeesData({ from = FIRST_DATE_TS, to = NOW_TS, chainName = "arbitrum" } = {}) {
const PROPS = 'margin liquidation swap mint burn'.split(' ')
const feesQuery = `{
feeStats(
first: 1000
orderBy: id
orderDirection: desc
where: { period: daily, id_gte: ${from}, id_lte: ${to} }
) {
id
margin
marginAndLiquidation
swap
mint
burn
${chainName === "avalanche" ? "timestamp" : ""}
}
}`
let [feesData, loading, error] = useGraph(feesQuery, {
chainName
})
const feesChartData = useMemo(() => {
if (!feesData) {
return null
}
let chartData = sortBy(feesData.feeStats, 'id').map(item => {
const ret = { timestamp: item.timestamp || item.id };
PROPS.forEach(prop => {
if (item[prop]) {
ret[prop] = item[prop] / 1e30
}
})
ret.liquidation = item.marginAndLiquidation / 1e30 - item.margin / 1e30
ret.all = PROPS.reduce((memo, prop) => memo + ret[prop], 0)
return ret
})
let cumulative = 0
const cumulativeByTs = {}
return chain(chartData)
.groupBy(item => item.timestamp)
.map((values, timestamp) => {
const all = sumBy(values, 'all')
cumulative += all
let movingAverageAll
const movingAverageTs = timestamp - MOVING_AVERAGE_PERIOD
if (movingAverageTs in cumulativeByTs) {
movingAverageAll = (cumulative - cumulativeByTs[movingAverageTs]) / MOVING_AVERAGE_DAYS
}
const ret = {
timestamp: Number(timestamp),
all,
cumulative,
movingAverageAll
}
PROPS.forEach(prop => {
ret[prop] = sumBy(values, prop)
})
cumulativeByTs[timestamp] = cumulative
return ret
})
.value()
.filter(item => item.timestamp >= from)
}, [feesData])
return [feesChartData, loading, error]
}
Example #14
Source File: dataProvider.js From acy-dex-interface with MIT License | 5 votes |
export function useGambitPoolStats({ from, to, groupPeriod }) {
const [data, loading, error] = useGraph(`{
poolStats (
first: 1000,
where: { id_gte: ${from}, id_lte: ${to} }
orderBy: id
orderDirection: desc
) {
id,
usdgSupply,
BTC_usd,
ETH_usd,
BNB_usd,
USDC_usd,
USDT_usd,
BUSD_usd
}
}`, { subgraph: 'gkrasulya/gambit' })
const ret = useMemo(() => {
if (!data) {
return null
}
let ret = data.poolStats.map(item => {
return Object.entries(item).reduce((memo, [key, value]) => {
if (key === 'id') memo.timestamp = value
else if (key === 'usdgSupply') memo.usdgSupply = value / 1e18
else memo[key.substr(0, key.length - 4)] = value / 1e30
return memo
}, {})
})
ret = chain(ret)
.sortBy('timestamp')
.groupBy(item => Math.floor(item.timestamp / groupPeriod) * groupPeriod)
.map((values, timestamp) => {
return {
...values[values.length - 1],
timestamp
}
})
.value()
return fillPeriods(ret, { period: groupPeriod, from, to, interpolate: false, extrapolate: true })
}, [data])
return [ret, loading, error]
}
Example #15
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 4 votes |
ÉquipeSection = ({
initialContacts = {},
responsableTechniqueNeedsMobilePhone = false,
}) => {
const {
isUserEnrollmentLoading,
disabled,
onChange,
enrollment: { team_members = [] },
} = useContext(FormContext);
const { user } = useAuth();
const contactConfiguration = useMemo(() => {
const defaultInitialContacts = {
demandeur: {
header: 'Demandeur',
description: getDefaultDemandeurDescription(),
forceDisable: true,
},
responsable_traitement: {
header: 'Responsable de traitement',
description: getDefaultResponsableTraitementDescription(),
},
delegue_protection_donnees: {
header: 'Délégué à la protection des données',
description: getDefaultDelegueProtectionDonneesDescription(),
},
responsable_technique: {
header: 'Responsable technique',
description: getDefaultResponsableTechniqueDescription(
responsableTechniqueNeedsMobilePhone
),
displayMobilePhoneLabel: responsableTechniqueNeedsMobilePhone,
},
};
return chain(defaultInitialContacts)
.assign(initialContacts)
.omitBy((p) => !p)
.value();
}, [initialContacts, responsableTechniqueNeedsMobilePhone]);
const newTeamMembers = useNewTeamMembers({
user,
team_members,
contactConfiguration,
});
useEffect(() => {
if (!isUserEnrollmentLoading && !disabled && !isEmpty(newTeamMembers)) {
onChange({
target: {
name: 'team_members',
value: [...team_members, ...newTeamMembers],
},
});
}
}, [
isUserEnrollmentLoading,
disabled,
onChange,
team_members,
newTeamMembers,
]);
const displayIdForAdministrator = useMemo(
() => user && user.roles.includes('administrator'),
[user]
);
const updateWithUserInformation = useCallback(
(index) => {
if (team_members[index]?.email !== user.email) {
onChange({
target: {
name: `team_members[${index}].email`,
value: user.email,
},
});
}
if (team_members[index]?.given_name !== user.given_name) {
onChange({
target: {
name: `team_members[${index}].given_name`,
value: user.given_name,
},
});
}
if (team_members[index]?.family_name !== user.family_name) {
onChange({
target: {
name: `team_members[${index}].family_name`,
value: user.family_name,
},
});
}
if (team_members[index]?.phone_number !== user.phone_number) {
onChange({
target: {
name: `team_members[${index}].phone_number`,
value: user.phone_number,
},
});
}
if (team_members[index]?.job !== user.job) {
onChange({
target: {
name: `team_members[${index}].job`,
value: user.job,
},
});
}
},
[team_members, user, onChange]
);
useEffect(() => {
if (!isUserEnrollmentLoading && !disabled && !isEmpty(team_members)) {
const currentDemandeurIndex = team_members.findIndex(
({ type, email }) => type === 'demandeur' && email === user.email
);
if (currentDemandeurIndex !== -1) {
updateWithUserInformation(currentDemandeurIndex);
}
}
}, [
isUserEnrollmentLoading,
disabled,
team_members,
user,
updateWithUserInformation,
]);
const addTeamMemberFactory = (type) => {
const tmp_id = uniqueId(`tmp_`);
const newTeamMember = { type, tmp_id };
return () =>
onChange({
target: {
name: 'team_members',
value: [...team_members, newTeamMember],
},
});
};
const removeTeamMember = (index) => {
onChange({
target: {
name: 'team_members',
value: [
...team_members.slice(0, index),
...team_members.slice(index + 1),
],
},
});
};
return (
<ScrollablePanel scrollableId={SECTION_ID}>
<h2>{SECTION_LABEL}</h2>
<ExpandableQuote title="Comment renseigner la liste des contacts ?" large>
{Object.entries(contactConfiguration).map(([type, { description }]) => (
<p key={type}>{description}</p>
))}
</ExpandableQuote>
<CardContainer flex={false}>
{Object.entries(contactConfiguration).map(
([
type,
{
header,
forceDisable,
displayMobilePhoneLabel,
displayIndividualEmailLabel,
displayGroupEmailLabel,
contactByEmailOnly,
multiple,
},
]) => (
<React.Fragment key={type}>
{team_members
.filter(({ type: t }) => t === type)
.map(({ id, tmp_id, ...team_member }) => (
<Contact
heading={header}
key={id || tmp_id}
id={id}
index={findIndex(team_members, ({ id: i, tmp_id: t_i }) => {
if (id) {
// if id is defined match on id field
return i === id;
}
if (tmp_id) {
// if id is not defined and tmp_id is defined
// match on tmp_id
return t_i === tmp_id;
}
return false;
})}
{...team_member}
displayMobilePhoneLabel={displayMobilePhoneLabel}
displayIndividualEmailLabel={displayIndividualEmailLabel}
displayGroupEmailLabel={displayGroupEmailLabel}
contactByEmailOnly={contactByEmailOnly}
displayIdForAdministrator={displayIdForAdministrator}
disabled={forceDisable || disabled}
onChange={onChange}
onDelete={multiple && !id && removeTeamMember}
onUpdateWithUserInformation={updateWithUserInformation}
canUpdatePersonalInformation={
team_member.email === user.email &&
(team_member.given_name !== user.given_name ||
team_member.family_name !== user.family_name ||
team_member.phone_number !== user.phone_number ||
team_member.job !== user.job)
}
/>
))}
{!disabled && multiple && (
<AddCard
label={`ajouter un ${header.toLowerCase()}`}
onClick={addTeamMemberFactory(type)}
/>
)}
</React.Fragment>
)
)}
</CardContainer>
</ScrollablePanel>
);
}
Example #16
Source File: ActivityFeedWrapper.js From datapass with GNU Affero General Public License v3.0 | 4 votes |
ActivityFeedWrapper = ({ events, status, target_api }) => {
const [
majorityPercentileProcessingTimeInDays,
setMajorityPercentileTimeInDays,
] = useState(0);
useEffect(() => {
async function fetchStats() {
const {
data: { majority_percentile_processing_time_in_days },
} = await getCachedMajorityPercentileProcessingTimeInDays(target_api);
setMajorityPercentileTimeInDays(
majority_percentile_processing_time_in_days
);
}
if (status === 'submitted') {
fetchStats();
}
}, [status, target_api]);
const {
comment = '',
name: lastEventName = '',
updated_at = '',
user: { email = '', given_name, family_name } = {},
diff,
} = useMemo(
() => chain(events).sortBy('updated_at').last().value() || {},
[events]
);
if (
['draft', 'changes_requested'].includes(status) &&
['request_changes'].includes(lastEventName)
) {
return (
<Alert title={USER_STATUS_LABELS[status]} type="warning">
Votre demande d’habilitation est incomplète et requiert les
modifications suivantes :
<div style={{ margin: '1em 0' }}>
<EventItem
comment={comment}
name={lastEventName}
updated_at={updated_at}
email={email}
given_name={given_name}
family_name={family_name}
diff={diff}
/>
</div>
</Alert>
);
}
if (
['draft', 'changes_requested'].includes(status) &&
['create', 'update'].includes(lastEventName)
) {
return (
<Alert title={USER_STATUS_LABELS[status]}>
<p>
Votre demande d’habilitation est actuellement en cours d’édition.
Notre service juridique pourra la consulter lorsque vous cliquerez sur
"Soumettre la demande d’habilitation".
</p>
</Alert>
);
}
if (status === 'submitted' && majorityPercentileProcessingTimeInDays > 0) {
return (
<Alert title={USER_STATUS_LABELS[status]}>
La majorité des demandes d’habilitation des 6 derniers mois reçoivent
une réponse en moins de{' '}
<b>{majorityPercentileProcessingTimeInDays} jours</b>.
</Alert>
);
}
return null;
}
Example #17
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 4 votes |
DonneesSection = ({
DonneesDescription,
availableScopes = [],
AvailableScopesDescription,
accessModes,
enableFileSubmissionForScopeSelection = false,
}) => {
const {
disabled,
onChange,
enrollment: {
scopes = {},
data_recipients = '',
data_retention_period = '',
data_retention_comment = '',
additional_content = {},
documents = [],
documents_attributes = [],
},
} = useContext(FormContext);
useEffect(() => {
if (!isEmpty(availableScopes) && isEmpty(scopes)) {
onChange({
target: {
name: 'scopes',
value: zipObject(
availableScopes.map(({ value }) => value),
availableScopes.map(
({ required, checkedByDefault }) =>
!!required || !!checkedByDefault
)
),
},
});
}
});
const groupTitleScopesGroup = groupBy(
availableScopes,
(e) => e.groupTitle || 'default'
);
// {'a': true, 'b': false, 'c': true} becomes ['a', 'c']
const scopesAsArray = chain(scopes)
.omitBy((e) => !e)
.keys()
.value();
const availableScopesAsArray = availableScopes.map(({ value }) => value);
const outdatedScopes = difference(scopesAsArray, availableScopesAsArray);
const [isFileInputExpanded, setFileInputExpanded] = useState(
enableFileSubmissionForScopeSelection &&
!isEmpty(
documents.filter(
({ type }) => type === 'Document::ExpressionBesoinSpecifique'
)
)
);
useEffect(() => {
const hasDocument = !isEmpty(
documents.filter(
({ type }) => type === 'Document::ExpressionBesoinSpecifique'
)
);
if (
enableFileSubmissionForScopeSelection &&
!isFileInputExpanded &&
hasDocument
) {
setFileInputExpanded(true);
}
}, [enableFileSubmissionForScopeSelection, isFileInputExpanded, documents]);
return (
<ScrollablePanel scrollableId={SECTION_ID}>
<h2>Les données nécessaires</h2>
{DonneesDescription && (
<ExpandableQuote title="Comment choisir les données ?">
<DonneesDescription />
</ExpandableQuote>
)}
{AvailableScopesDescription && <AvailableScopesDescription />}
{!isEmpty(availableScopes) && (
<>
<h3>À quelles données souhaitez-vous avoir accès ?</h3>
{Object.keys(groupTitleScopesGroup).map((group) => (
<Scopes
key={group}
title={group === 'default' ? null : group}
scopes={groupTitleScopesGroup[group]}
selectedScopes={scopes}
disabledApplication={disabled}
handleChange={onChange}
/>
))}
{disabled && !isEmpty(outdatedScopes) && (
<Scopes
title="Les données suivantes ont été sélectionnées mais ne sont plus disponibles :"
scopes={outdatedScopes.map((value) => ({ value, label: value }))}
selectedScopes={zipObject(
outdatedScopes,
Array(outdatedScopes.length).fill(true)
)}
disabledApplication
handleChange={() => null}
/>
)}
</>
)}
{enableFileSubmissionForScopeSelection && (
<>
<ExpandableQuote title="J’ai une expression de besoin spécifique ?">
<p>
Les partenaires ayant convenu avec la DGFiP un périmètre de
données particulier peuvent rattacher leur expression de besoin
listant l’ensemble des données strictement nécessaires à leur cas
d’usage. Si vous n'avez pas encore contacté la DGFiP, vous pouvez
les joindre à l'adresse{' '}
<Link
inline
href="mailto:[email protected]?subject=Expression%20de%20besoin%20spécifique"
>
[email protected]
</Link>
</p>
<CheckboxInput
label="J’ai une expression de besoin spécifique"
value={isFileInputExpanded}
onChange={() => setFileInputExpanded(!isFileInputExpanded)}
disabled={disabled}
/>
</ExpandableQuote>
{isFileInputExpanded && (
<>
<FileInput
label="Joindre l’expression de besoin"
meta={
'Attention : seule l’expression de besoin en données ayant déjà été partagée avec la DGFiP peut être rattachée à votre habilitation.'
}
mimeTypes="*"
disabled={disabled}
uploadedDocuments={documents}
documentsToUpload={documents_attributes}
documentType={'Document::ExpressionBesoinSpecifique'}
onChange={onChange}
/>
</>
)}
</>
)}
{!isEmpty(accessModes) && (
<>
<h3>Comment souhaitez-vous y accéder ?</h3>
{accessModes.map(({ id, label }) => (
<CheckboxInput
key={id}
label={label}
name={`additional_content.${id}`}
value={additional_content[id] || false}
disabled={disabled}
onChange={onChange}
/>
))}
</>
)}
<h3>Comment seront traitées ces données personnelles ?</h3>
<TextInput
label="Destinataires des données"
placeholder={
'« agents instructeurs des demandes d’aides », « usagers des ' +
'services publics de la ville », etc.'
}
meta={
<Link
inline
newTab
href="https://www.cnil.fr/fr/definition/destinataire"
aria-label="Voir la définition CNIL du destinataire des données"
>
Plus d’infos
</Link>
}
name="data_recipients"
value={data_recipients}
disabled={disabled}
onChange={onChange}
required
/>
<NumberInput
label="Durée de conservation des données en mois"
meta={
<Link
inline
newTab
href="https://www.cnil.fr/fr/les-durees-de-conservation-des-donnees"
aria-label="Voir l’explication CNIL sur les durées de conservation des données"
>
Plus d’infos
</Link>
}
name="data_retention_period"
value={data_retention_period}
disabled={disabled}
onChange={onChange}
required
/>
{data_retention_period > 36 && (
<>
<Alert type="warning" title="Attention">
Cette durée excède la durée communément constatée (36 mois).
</Alert>
<TextInput
label="Veuillez justifier cette durée dans le champ ci-après :"
name="data_retention_comment"
value={data_retention_comment}
disabled={disabled}
onChange={onChange}
/>
</>
)}
</ScrollablePanel>
);
}
Example #18
Source File: index.js From datapass with GNU Affero General Public License v3.0 | 4 votes |
TextInputWithSuggestions = ({
label,
name,
options = [],
value,
disabled,
onChange,
required,
}) => {
// id will be set once when the component initially renders, but never again
// we generate an unique id prefixed by the field name
const [id] = useState(uniqueId(name));
const [suggestions, setSuggestions] = useState([]);
// from https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript
const normalize = (string = '') =>
string
.toLowerCase()
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '')
.replace(/[^\w\s]/gi, ' ');
useEffect(() => {
const newSuggestions = chain(options)
.map(({ id, label }) => ({
id,
label,
distance: levenshtein(normalize(value), normalize(label)).similarity,
}))
.sortBy(['distance'])
.reverse()
.filter(({ distance }) => distance > 0.25)
.take(10)
.value();
setSuggestions(newSuggestions);
}, [options, value]);
const [isDropDownOpen, setIsDropDownOpen] = useState(false);
const [activeSuggestion, setActiveSuggestion] = useState(null);
const closeDropDown = () => {
setIsDropDownOpen(false);
setActiveSuggestion(null);
};
const onKeyDown = (e) => {
if (e.key === 'Tab' && isDropDownOpen) {
e.preventDefault();
closeDropDown();
}
if (e.key === 'Enter' && !isDropDownOpen) {
e.preventDefault();
setIsDropDownOpen(true);
}
if (e.key === 'Escape' && isDropDownOpen) {
e.preventDefault();
closeDropDown();
}
if (e.key === 'ArrowDown' && !isDropDownOpen) {
e.preventDefault();
setIsDropDownOpen(true);
setActiveSuggestion(0);
}
if (e.key === 'ArrowDown' && isDropDownOpen && isNull(activeSuggestion)) {
e.preventDefault();
setActiveSuggestion(0);
}
if (e.key === 'ArrowDown' && isDropDownOpen && isNumber(activeSuggestion)) {
e.preventDefault();
setActiveSuggestion(min([activeSuggestion + 1, suggestions.length - 1]));
}
if (e.key === 'ArrowUp' && isDropDownOpen) {
e.preventDefault();
setActiveSuggestion(max([activeSuggestion - 1, 0]));
}
if (e.key === 'Enter' && isDropDownOpen) {
e.preventDefault();
onChange({
target: { name, value: suggestions[activeSuggestion]?.label },
});
closeDropDown();
}
};
const handleChange = (value) => {
closeDropDown();
onChange({ target: { name, value } });
};
return (
<div className="fr-input-group">
<label htmlFor={id}>
{label}
{required && ' *'}
</label>
<input
className="fr-input"
type="text"
onChange={onChange}
name={name}
id={id}
readOnly={disabled}
value={value}
required={required}
onKeyDown={onKeyDown}
onClick={() => setIsDropDownOpen(true)}
onInput={() => setIsDropDownOpen(true)}
/>
{!disabled && isDropDownOpen && !isEmpty(suggestions) && (
<Dropdown onOutsideClick={closeDropDown} fillWidth>
{suggestions.map(({ id, label }, index) => (
<div
key={id}
className={`datapass-text-input-suggestion ${
activeSuggestion === index
? 'datapass-text-input-active-suggestion'
: ''
}`}
onClick={() => handleChange(label)}
>
{label}
</div>
))}
</Dropdown>
)}
</div>
);
}
Example #19
Source File: diff.js From hivemind with Apache License 2.0 | 4 votes |
DiffAPI = async (req, res) => {
const { token } = req.headers
try {
const claims = await verifyIdToken(token)
const ukey = claims.uid
const userId = `users/${ukey}`
const { eid } = req.query
const cEvent = await compoundEvents.document(eid)
const { mid, event, fctime, lctime, nids, eids } = cEvent
if (await hasReadAccess(mid, userId)) {
let response, diff, nid, output, indexes, filteredNids, nKeys, path, timestamp
switch (req.method) {
case 'GET':
output = {}
switch (event) {
case 'created':
case 'restored':
output.v1 = {}
nid = nids.find((nid) => nid.startsWith('nodes/')) || mid
response = await rg.get('/history/show', {
path: `/n/${nid}`,
timestamp: lctime,
})
output.v2 = response.body[0]
break
case 'deleted':
indexes = chain(nids)
.map((nid, idx) =>
/^(nodes|mindmaps)\//.test(nid) ? idx : null
)
.reject(isNull)
.value()
output.v2 = indexes.map(() => ({}))
filteredNids = nids.filter((nid, idx) =>
indexes.includes(idx)
)
nKeys = {}
for (const nid of filteredNids) {
const [coll, key] = nid.split('/')
if (!nKeys[coll]) {
nKeys[coll] = []
}
nKeys[coll].push(key)
}
path = createNodeBracePath(nKeys)
timestamp = fctime - 0.0001
response = await rg.get('/history/show', { path, timestamp })
output.v1 = response.body
break
case 'updated':
nid = nids[0]
response = await rg.get('/history/show', {
path: `/n/${nid}`,
timestamp: lctime,
})
output.v2 = response.body[0]
diff = await getReversedDiff(nid, eids[0], fctime, lctime)
output.v1 = patch(diff, response.body[0])
}
return res.status(200).json(output)
}
} else {
return res.status(401).json({ message: 'Access Denied.' })
}
} catch (error) {
console.error(error.message, error.stack)
return res.status(401).json({ message: 'Access Denied.' })
}
}
Example #20
Source File: index.js From hivemind with Apache License 2.0 | 4 votes |
MindMapsAPI = async (req, res) => {
const { token } = req.headers
try {
const claims = await verifyIdToken(token)
const key = claims.uid
const userId = `users/${key}`
let mindmap, response, message, query, cursor, mindmaps, name
switch (req.method) {
case 'GET':
query = aql`
for v, e in 1
outbound ${userId}
graph 'mindmaps'
return { mindmap: v, access: e }
`
cursor = await db.query(query)
mindmaps = await cursor.all()
return res.status(200).json(mindmaps)
case 'POST':
name = req.body.name
mindmap = {
name,
isRoot: true,
title: name,
createdBy: userId,
}
response = await rg.post('/document/mindmaps', mindmap)
if (response.statusCode === 201) {
mindmap = response.body
const access = {
_from: `users/${key}`,
_to: mindmap._id,
access: 'admin',
}
response = await rg.post('/document/access', access)
if (response.statusCode === 201) {
message = 'Mindmap created.'
await recordCompoundEvent('created', userId, [mindmap])
} else {
message = response.body
await rg.delete(
'/history/purge',
{ path: `/n/${mindmap._id}` },
{ silent: true, deleteUserObjects: true }
)
}
} else {
message = response.body
}
return res.status(response.statusCode).json({ message })
case 'PATCH':
if (isEmpty(req.body._id)) {
return res.status(400).json({ message: 'Mindmap id is required' })
}
if (await hasWriteAccess(req.body._id, userId)) {
mindmap = chain(req.body)
.pick('audio', 'summary', 'content', '_rev', '_id', 'title', 'name')
.omitBy(isNil)
.value()
mindmap.lastUpdatedBy = userId
response = await rg.patch('/document/mindmaps', mindmap, {
keepNull: false,
ignoreRevs: false,
})
await recordCompoundEvent('updated', userId, [response.body])
return res.status(response.statusCode).json(response.body)
} else {
return res.status(401).json({ message: 'Access Denied.' })
}
}
} catch (error) {
console.error(error.message, error.stack)
return res.status(401).json({ message: 'Access Denied.' })
}
}
Example #21
Source File: index.js From hivemind with Apache License 2.0 | 4 votes |
MindMapAPI = async (req, res) => {
const { token } = req.headers
try {
const claims = await verifyIdToken(token)
const ukey = claims.uid
const userId = `users/${ukey}`
const { key, timestamp } = req.query
const id = `mindmaps/${key}`
let mindmap, query, cursor, edgeStartIdx
switch (req.method) {
case 'GET':
if (timestamp && (await hasReadAccess(id, userId))) {
const response = await rg.post(
'/history/traverse',
{
edges: { access: 'outbound', links: 'outbound' },
},
{
timestamp,
svid: id,
minDepth: 0,
maxDepth: Number.MAX_SAFE_INTEGER,
uniqueVertices: 'global',
returnPaths: false,
}
)
mindmap = response.body
edgeStartIdx = 0
} else {
query = aql`
for v, e, p in 1..${Number.MAX_SAFE_INTEGER}
any ${userId}
graph 'mindmaps'
options { uniqueVertices: 'global', bfs: true }
filter p.vertices[1]._id == ${id}
collect aggregate vertices = unique(v), edges = unique(e)
return { vertices, edges }
`
cursor = await db.query(query)
mindmap = await cursor.next()
edgeStartIdx = 1
}
if (get(mindmap, ['vertices', 'length'])) {
const meta = mindmap.vertices[0]
const access = mindmap.edges[0]
const vertices = [],
edges = []
for (let i = 0; i < mindmap.vertices.length; i++) {
vertices.push(mindmap.vertices[i])
}
for (let i = edgeStartIdx; i < mindmap.edges.length; i++) {
edges.push(mindmap.edges[i])
}
const userIds = chain(vertices)
.flatMap((v) => [v.createdBy, v.lastUpdatedBy])
.compact()
.uniq()
.value()
query = aql`
for u in users
filter u._id in ${userIds}
return keep(u, '_id', 'displayName', 'email')
`
cursor = await db.query(query)
const users = await cursor.all()
for (const v of vertices) {
for (const field of ['createdBy', 'lastUpdatedBy']) {
if (v[field]) {
const user = users.find((u) => u._id === v[field])
v[field] = user.displayName || user.email
}
}
}
const result = {
meta,
access,
elements: rg2cy([
{
type: 'vertex',
nodes: vertices,
},
{
type: 'edge',
nodes: edges,
},
]),
}
return res.status(200).json(result)
}
return res.status(401).json({ message: 'Access Denied.' })
default:
return res.status(405).json({ message: 'Method Not Allowed' })
}
} catch (error) {
console.error(error.message, error.stack)
return res.status(401).json({ message: 'Access Denied.' })
}
}