Place des entreprises (#2034)

* WIP Place des entreprises

* Fix Emoji waning

* WIP Place des entreprises

* Ajout de PdE dans les CSP directive

* Ajout de l'option small sur la popover

* Style de l'iframe place des entreprises

* ⬆ Update react-aria button, dialog and overlays

* Amélioration du composant PlaceDesEntreprises

* Empêche l'event "click" apres un event "touch" sur mobile (fix #2020)

* Clean commentaire

* Fix des erreurs de dom

* Ajout des fonctions isProduction, isStaging et isDevelopment

* Ajout des url de staging place des entreprises

* Refacto du texte

* Ajout des traduction

* Ajout du staging de PdE dans le cors

* Modification du lien storybook EN

* Ajout de dépréciation sur les var d'env MODE, DEV et PROD

* Ajout du siret pour PdE dans Gérer

* Ajout d'un scrollToTop sur l'onLoad de l'iframe

* Fix css de la popover sur mobile

* Fix css des bar de pourcentage sur tablet
pull/2051/head
Jérémy Rialland 2022-03-15 13:12:27 +01:00 committed by GitHub
parent 00da2d25d1
commit 078e60b728
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 378 additions and 103 deletions

View File

@ -199,7 +199,7 @@ jobs:
- mon-entreprise : ${{ needs.deploy-context.outputs.fr_url }}
- mycompanyinfrance : ${{ needs.deploy-context.outputs.en_url }}
- storybook : ${{ needs.deploy-context.outputs.fr_url }}/dev/storybook/ or ${{ needs.deploy-context.outputs.en_url }}/dev/storybook/
- storybook : ${{ needs.deploy-context.outputs.fr_url }}/dev/storybook/ ([version EN](${{ needs.deploy-context.outputs.en_url }}/dev/storybook/))
e2e-test-preview:
env:

View File

@ -1,7 +1,7 @@
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self' mon-entreprise.fr; style-src 'self' 'unsafe-inline' mon-entreprise.zammad.com; connect-src 'self' *.sentry.io raw.githubusercontent.com *.xiti.com mon-entreprise.zammad.com api.recherche-entreprises.fabrique.social.gouv.fr geo.api.gouv.fr *.algolianet.com; form-action 'self' *.sibforms.com *.sentry.io mon-entreprise.zammad.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.xiti.com stonly.com code.jquery.com mon-entreprise.zammad.com polyfill.io; img-src 'self' data: *.xiti.com user-images.githubusercontent.com; frame-src 'self' https://www.youtube-nocookie.com https://codesandbox.io"
Content-Security-Policy = "default-src 'self' mon-entreprise.fr; style-src 'self' 'unsafe-inline' mon-entreprise.zammad.com; connect-src 'self' *.sentry.io raw.githubusercontent.com *.xiti.com mon-entreprise.zammad.com api.recherche-entreprises.fabrique.social.gouv.fr geo.api.gouv.fr *.algolianet.com; form-action 'self' *.sibforms.com *.sentry.io mon-entreprise.zammad.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.xiti.com stonly.com code.jquery.com mon-entreprise.zammad.com polyfill.io; img-src 'self' data: *.xiti.com user-images.githubusercontent.com; frame-src 'self' https://www.youtube-nocookie.com https://codesandbox.io https://place-des-entreprises.beta.gouv.fr https://reso-staging.osc-fr1.scalingo.io"
## Twemoji proxy for client privacy #1219
[[redirects]]

View File

@ -54,12 +54,12 @@
"@internationalized/number": "^3.0.3",
"@mui/material": "^5.0.4",
"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
"@react-aria/button": "^3.3.4",
"@react-aria/button": "^3.4.1",
"@react-aria/checkbox": "^3.2.3",
"@react-aria/dialog": "^3.1.4",
"@react-aria/dialog": "^3.1.6",
"@react-aria/i18n": "^3.3.2",
"@react-aria/numberfield": "^3.1.0",
"@react-aria/overlays": "^3.7.2",
"@react-aria/overlays": "^3.7.5",
"@react-aria/progress": "^3.1.3",
"@react-aria/radio": "^3.1.5",
"@react-aria/searchfield": "^3.2.0",
@ -75,6 +75,7 @@
"@sentry/tracing": "^6.18.1",
"algoliasearch": "^4.10.2",
"fuse.js": "^6.4.6",
"iframe-resizer": "^4.3.2",
"markdown-to-jsx": "^7.1.5",
"modele-social": "^0.6.0",
"publicodes": "^1.0.0-beta.32",
@ -111,7 +112,6 @@
"@storybook/addon-links": "^6.5.0-alpha.40",
"@storybook/react": "^6.5.0-alpha.40",
"@storybook/testing-library": "^0.0.9",
"@types/iframe-resizer": "^3.5.7",
"@types/ramda": "^0.26.43",
"@types/react": "^17.0.0",
"@types/react-color": "^3.0.1",

View File

@ -56,7 +56,7 @@ export default function CompanyDetails({
</>{' '}
</H3>
<InfoContainer>
<InfoContainer as="div">
{dateCreationUniteLegale && (
<div>
<Trans>Crée le</Trans>{' '}

View File

@ -32,10 +32,6 @@
flex: 1;
}
.distribution-chart__counterparts {
width: 35em;
}
@media not print {
@media (max-width: 600px) {
.distribution-chart__legend {

View File

@ -160,6 +160,7 @@ export default function PageFeedback({ customMessage }: PageFeedbackProps) {
title="Votre avis nous interesse"
isDismissable
onClose={() => setState({ showThanks: true, showForm: false })}
small
>
<Form />
</Popover>

View File

@ -14,6 +14,7 @@ export default function LegalNotice() {
</Link>
)}
title={t('legalNotice.title', 'Mentions légales')}
small
>
<H2>
<Trans i18nKey="legalNotice.editeur.title">Editeur</Trans>

View File

@ -0,0 +1,126 @@
import { lazy, ReactEventHandler, Suspense, useEffect, useRef } from 'react'
import styled from 'styled-components'
import Emoji from './utils/Emoji'
import { iframeResize } from 'iframe-resizer'
import { PopoverWithTrigger } from '@/design-system'
import { Button } from '@/design-system/buttons'
import { Loader } from '@/design-system/icons/Loader'
import { Body } from '@/design-system/typography/paragraphs'
import { Trans, useTranslation } from 'react-i18next'
import { isProduction } from '@/utils'
const Iframe = styled.iframe`
width: 1px;
min-width: 100%;
height: 80vh;
`
const IframeContainer = styled.div`
margin: 0 -3rem;
`
export const PlacesDesEntreprisesIframe = ({
src,
onLoad,
}: {
src: string
onLoad?: ReactEventHandler<HTMLIFrameElement>
}) => {
useEffect(() => {
iframeResize({}, '#pdeIframe')
}, [])
return (
<IframeContainer>
<Iframe
title="Formulaire de demande entreprise"
src={src}
frameBorder="0"
id="pdeIframe"
onLoad={onLoad}
/>
</IframeContainer>
)
}
const LazyIframe = lazy(async () => {
return import('./PlaceDesEntreprises').then(
({ PlacesDesEntreprisesIframe }) => ({
default: PlacesDesEntreprisesIframe,
})
)
})
const Container = styled.div`
display: flex;
justify-content: center;
`
const ButtonLabel = styled.span`
margin-left: 1rem;
`
export const PlacesDesEntreprisesButton = ({
pathname,
siret,
}: {
pathname: string
siret?: string
}) => {
const { t } = useTranslation()
const baseURL =
'https://' +
(isProduction()
? 'place-des-entreprises.beta.gouv.fr'
: 'reso-staging.osc-fr1.scalingo.io')
const url = new URL(baseURL + pathname)
const contentRef = useRef<HTMLDivElement>(null)
const scrollTo = (x: number, y: number) => {
contentRef.current?.scrollTo(x, y)
}
if (siret) {
url.searchParams.set('siret', siret)
}
return (
<Container>
<PopoverWithTrigger
title={t('Échanger avec un conseiller')}
trigger={(props) => (
<Button {...props} light size="XS">
<Emoji emoji="📞" />
<ButtonLabel>{t('Échanger avec un conseiller')}</ButtonLabel>
</Button>
)}
contentRef={contentRef}
>
<Body>
<Trans>
Décrivez votre projet ou votre problème en donnant quelques éléments
de contexte. Nous identifions, parmi lensemble des partenaires
publics et parapublics, le conseiller compétent pour votre demande.
Celui-ci vous contacte par téléphone sous 5 jours et vous accompagne
en fonction de votre situation.
</Trans>
</Body>
<Suspense
fallback={
<Container
css={`
height: 300px;
align-items: center;
`}
>
<Loader />
</Container>
}
>
<LazyIframe src={url.href} onLoad={() => scrollTo(0, 0)} />
</Suspense>
</PopoverWithTrigger>
</Container>
)
}

View File

@ -31,6 +31,7 @@ export default function SearchButton() {
</StyledLabel>
</StyledButton>
)}
small
>
{(closePopover) => (
<SearchRulesAndSimulators closePopover={closePopover} />

View File

@ -12,6 +12,7 @@ import styled from 'styled-components'
import { TrackingContext } from '../../ATInternetTracking'
import { useParamsFromSituation } from '../utils/useSearchParamsSimulationSharing'
import { ShareSimulationPopup } from './ShareSimulationPopup'
import { PlacesDesEntreprisesButton } from '../PlaceDesEntreprises'
export function useUrl() {
const language = useTranslation().i18n.language
@ -88,6 +89,7 @@ export default function ShareOrSaveSimulationBanner() {
</ButtonLabel>
</Button>
)}
small
>
<ShareSimulationPopup url={url} />
</PopoverWithTrigger>
@ -96,12 +98,7 @@ export default function ShareOrSaveSimulationBanner() {
{typeof window.print === 'function' && (
<Grid item xs={12} sm="auto">
<Button light size="XS" onPress={() => window.print()}>
<Emoji
css={`
margin-right: 1rem;
`}
emoji="🖨"
/>
<Emoji emoji="🖨" />
<ButtonLabel>
<Trans i18nKey="ExportSimulation.Banner">
Imprimer ou sauvegarder en PDF
@ -110,6 +107,10 @@ export default function ShareOrSaveSimulationBanner() {
</Button>
</Grid>
)}
<Grid item xs={12} sm="auto">
<PlacesDesEntreprisesButton pathname="/aide-entreprise/rh-mon-entreprise-urssaf-fr/theme/recrutement-formation#section-breadcrumbs" />
</Grid>
</Grid>
</>
)

View File

@ -12,6 +12,7 @@ export default function SeeAnswersButton() {
<Trans>Voir mes paramètres</Trans>
</Button>
)}
small
>
{(close) => <Answers onClose={close} />}
</PopoverWithTrigger>

View File

@ -14,6 +14,7 @@ export default function InscriptionBetaTesteur() {
<Link {...buttonProps}>Devenir beta-testeur</Link>
)}
title="Votre avis nous intéresse"
small
>
<img
src={FeedbackSvg}

View File

@ -33,6 +33,7 @@ export default function Privacy({ label }: { label?: string }) {
</Link>
)}
title={t('privacyContent.title', 'Données personnelles')}
small
>
<Trans i18nKey="privacyContent.texte">
<TrackPage chapter1="informations" name={'donnees_personnelles'} />

View File

@ -25,6 +25,6 @@ export default function Emoji({ emoji }: PropType) {
baseUrl: siteUrl + '/twemoji/2/',
ext: '.png',
}
: ({} as any)
: undefined
)
}

View File

@ -1,3 +1,4 @@
import { usePreventClickAfterTouch } from '@/hooks/usePreventClickAfterTouch'
import { Grid } from '@mui/material'
import { useButton } from '@react-aria/button'
import { useDialog } from '@react-aria/dialog'
@ -10,7 +11,7 @@ import {
usePreventScroll,
} from '@react-aria/overlays'
import { AriaDialogProps } from '@react-types/dialog'
import React, { useEffect, useRef, useState } from 'react'
import React, { RefObject, useEffect, useRef, useState } from 'react'
import styled, { css, keyframes, ThemeProvider } from 'styled-components'
import { Container } from './layout'
import { H2 } from './typography/heading'
@ -43,9 +44,10 @@ export default function Popover(
children: React.ReactNode
title?: string
small?: boolean
contentRef?: RefObject<HTMLDivElement>
}
) {
const { title, children } = props
const { title, children, small, contentRef } = props
// Handle interacting outside the dialog and pressing
// the Escape key to close the modal.
@ -70,6 +72,7 @@ export default function Popover(
},
closeButtonRef
)
usePreventClickAfterTouch(closeButtonRef)
const offsetTop = useIFrameOffset()
if (offsetTop === undefined) {
@ -82,7 +85,7 @@ export default function Popover(
<Underlay {...underlayProps}>
<Container>
<Grid container justifyContent="center">
<Grid item sm={10} md={8}>
<Grid item sm={small ? 10 : 12} md={small ? 8 : 12}>
<PopoverContainer
{...dialogProps}
{...modalProps}
@ -116,7 +119,7 @@ export default function Popover(
</CloseButton>
</CloseButtonContainer>
)}
<PopoverContent>
<PopoverContent ref={contentRef}>
{title && (
<H2 as="h1" {...titleProps}>
{title}
@ -153,20 +156,16 @@ const Underlay = styled.div`
z-index: 10;
background: rgba(255, 255, 255, 0.5);
animation: ${appear} 0.2s;
display: flex;
align-items: center;
@media (max-width: ${({ theme }) => theme.breakpointsWidth.sm}) {
align-items: flex-end;
}
`
const PopoverContainer = styled.div<{ offsetTop: number | null }>`
${({ offsetTop }) =>
offsetTop !== null
? css`
top: calc(${offsetTop}px + 2rem);
`
: css`
top: 10vh;
`}
position: relative;
max-height: calc(90vh - 1px);
max-height: 90vh;
background: ${({ theme }) => theme.colors.extended.grey[100]};
box-shadow: ${({ theme }) => theme.elevations[4]};
@ -179,8 +178,6 @@ const PopoverContainer = styled.div<{ offsetTop: number | null }>`
!offsetTop &&
css`
@media (max-width: ${theme.breakpointsWidth.sm}) {
top: calc(100vh - 100% - 1px);
max-height: calc(100vh - 1px);
margin: 0 -16px;
}
`}
@ -216,6 +213,5 @@ const CloseButton = styled.button`
const PopoverContent = styled.div`
overflow: auto;
padding: 0 ${({ theme }) => theme.spacings.xxl}
${({ theme }) => theme.spacings.md};
padding: 0 ${({ theme }) => theme.spacings.xxl + ' ' + theme.spacings.md};
`

View File

@ -2,7 +2,14 @@ import { useOverlayTrigger } from '@react-aria/overlays'
import { useOverlayTriggerState } from '@react-stately/overlays'
import { AriaButtonProps } from '@react-types/button'
import { Button } from '@/design-system/buttons'
import React, { ReactElement, Ref, useEffect, useMemo, useRef } from 'react'
import React, {
ReactElement,
Ref,
RefObject,
useEffect,
useMemo,
useRef,
} from 'react'
import { useLocation } from 'react-router'
import Popover from './Popover'
import { Link } from './typography/link'
@ -17,12 +24,16 @@ type PopoverWithTriggerProps = {
) => ReactElement<typeof Button> | ReactElement<typeof Link>
children: React.ReactNode | ((close: () => void) => React.ReactNode)
title?: string
small?: boolean
contentRef?: RefObject<HTMLDivElement>
}
export default function PopoverWithTrigger({
children,
title,
trigger,
small,
contentRef,
}: PopoverWithTriggerProps) {
const state = useOverlayTriggerState({})
const openButtonRef = useRef<HTMLButtonElement>(null)
@ -63,6 +74,8 @@ export default function PopoverWithTrigger({
onClose={() => state.close()}
isDismissable
role="dialog"
small={small}
contentRef={contentRef}
>
{typeof children === 'function'
? children(() => state.close())

View File

@ -3,7 +3,8 @@ import {
GenericButtonOrLinkProps,
useButtonOrLink,
} from '@/design-system/typography/link'
import React, { ForwardedRef, forwardRef } from 'react'
import { usePreventClickAfterTouch } from '@/hooks/usePreventClickAfterTouch'
import React, { ForwardedRef, forwardRef, useRef } from 'react'
import styled, { css } from 'styled-components'
type Size = 'XL' | 'MD' | 'XS'
@ -27,6 +28,9 @@ export const Button = forwardRef(function Button(
forwardedRef: ForwardedRef<HTMLAnchorElement | HTMLButtonElement>
) {
const buttonOrLinkProps = useButtonOrLink(ariaButtonProps, forwardedRef)
const buttonRef = useRef<HTMLButtonElement>(null)
usePreventClickAfterTouch(buttonRef)
return (
<StyledButton
{...buttonOrLinkProps}
@ -34,6 +38,7 @@ export const Button = forwardRef(function Button(
size={size}
$light={light}
color={color}
ref={buttonRef}
/>
)
})

View File

@ -46,6 +46,7 @@ export default function ButtonHelp({
</StyledButton>
)}
title={title}
small
>
{children}
</PopoverWithTrigger>

View File

@ -8,6 +8,7 @@ import {
useExternalLinkProps,
} from '@/design-system/typography/link'
import { Body } from '@/design-system/typography/paragraphs'
import { PropsOf } from '@emotion/react'
import React, { ReactHTML, useRef } from 'react'
import styled, { css, ThemeProvider } from 'styled-components'
@ -21,6 +22,7 @@ type CardProps = GenericCardProps & {
ctaLabel?: React.ReactNode
children: React.ReactNode
compact?: boolean
bodyAs?: PropsOf<typeof Body>['as']
}
export function Card({
@ -29,6 +31,7 @@ export function Card({
children,
ctaLabel,
compact = false,
bodyAs,
...ariaButtonProps
}: CardProps) {
const ref = useRef<HTMLAnchorElement | HTMLButtonElement>(null)
@ -49,7 +52,7 @@ export function Card({
width: 100%;
`}
>
<Body>{children}</Body>
<Body as={bodyAs}>{children}</Body>
</div>
{ctaLabel && (
// The button is not selectable with keyboard navigation because the whole card already is

View File

@ -0,0 +1,19 @@
import { RefObject, useEffect } from 'react'
/**
* Workaround for preventing clicks propagating
* to elements behind the button clicked
* Inspired from this issue https://github.com/adobe/react-spectrum/issues/1513
* @param buttonRef Ref of the button
*/
export const usePreventClickAfterTouch = (
buttonRef: RefObject<HTMLButtonElement>
) => {
useEffect(() => {
const button = buttonRef.current
const preventDefault = (e: HTMLElementEventMap['touchstart']) =>
e.preventDefault()
button?.addEventListener('touchstart', preventDefault)
}, [buttonRef])
}

View File

@ -63,6 +63,11 @@ Destinataire: Levied by
Devenir: Become
Déclenchement: Applicability
Découvrir: Discover
"Décrivez votre projet ou votre problème en donnant quelques éléments de contexte. Nous identifions, parmi lensemble des partenaires publics et parapublics, le conseiller compétent pour votre demande. Celui-ci vous contacte par téléphone sous 5 jours et vous accompagne en fonction de votre situation.":
Describe your project or your problem by giving some background information.
We identify, among all the public and parapublic partners, the competent
advisor for your request. He or she will contact you by phone within 5 days
and will accompany you according to your situation.
Démarches de création: Creation process checklist
Désactivée: Inactive
Détail annuel des cotisations: Annual detail of my contributions
@ -1566,30 +1571,36 @@ pages:
ogTitle: "Gross, net, net after-tax salary, total cost: the ultimate simulator
for employees and employers"
titre: "Gross / net salary: the Urssaf converter"
seo: <0>How is the net salary calculated?</0><1>At the job interview, the
employer usually proposes a "gross" remuneration. The amount announced
thus includes employee contributions, which are used to finance the
employee's social protection and which are deducted from the "net"
salary received by the employee.</1><2>You can use our simulator to
convert the <2>gross salary into net</2> salary. Simply enter the
advertised remuneration in the gross salary box. The simulation can be
refined by answering the various questions (fixed-term contract,
executive status, overtime, part-time, meal vouchers,
etc.).</2><3></3><4>Moreover, since 2019,<1> income tax</1> is deducted
at source. To do this, the Directorate General of Public Finance (DGFiP)
sends the employer the tax rate calculated from the employee's income
tax return. If this rate is unknown, for example in the first year of
employment, the employer uses the <4>neutral rate</4>.</4><5>How to
calculate the cost of hiring?</5><6>If you are looking to hire, you can
calculate the total cost of your employee's remuneration, as well as the
corresponding employer and employee contribution amounts. This enables
you to define the remuneration level by knowing the overall amount of
expense this represents for your company.</6><7>In addition to the
salary, our simulator takes into account the calculation of benefits in
kind (telephone, company car, etc.), as well as the compulsory health
insurance.</7><8>There are <2>deferred</2> hiring <2>aids</2> which are
not all taken into account by our simulator, you can find them on <6>the
official portal</6>.</8>
seo: "<0>How to calculate the net salary?</0><1>During the job interview, the
employer generally proposes a \"gross\" salary. This amount includes the
employee's social security contributions, which are deducted from the
\"net\" salary received by the employee.</1><2>You can use our simulator
to convert the <2>gross salary into net salary</2>: simply enter the
advertised salary in the gross salary box. The simulation can be refined
by answering different questions (fixed-term contract, executive status,
overtime, part-time work, meal vouchers, etc.).</2><3></3><4>In
addition, since 2019,<1>income tax</1> is deducted at source. To do
this, the Directorate General of Public Finance (DGFiP) sends the
employer the tax rate calculated from the employee's tax return. If this
rate is unknown, for example during a first year of activity, the
employer uses the <4>neutral rate</4>.</4><5>How to calculate the cost
of hiring?</5><6>If you are looking to hire, you can calculate the total
cost of your employee's remuneration, as well as the corresponding
employer and employee contributions. This allows you to define the level
of remuneration by knowing the overall amount of expense that this
represents for your company.</6><7>In addition to the salary, our
simulator takes into account the calculation of benefits in kind
(telephone, company car, etc.), as well as the mandatory health
insurance.</7><8>There are <2>deferred</2> hiring <2>aids</2> that are
not all taken into account by our simulator. You can find them on <6>the
official portal</6>.</8><9>Discuss your recruitment project with a
consultant</9><10>You want :<1><0>Be advised on the hiring aids
available for your recruitment</0><1>Find out about apprenticeships,
professionalization contracts, free jobs in priority neighborhoods, the
VTE, etc.</1><2>Find candidates</2><3>Recruiting a person with a
disability</3></1><2>Simple and fast public service: you are called back
by THE advisor who can help you.</2></10><11>Mobilized partners: Pôle
emploi, APEC, Cap Emploi, local missions...</11><12></12>"
shortname: Employee
title: Employee income simulator
title-employeur: Hiring Cost Simulator

View File

@ -29,6 +29,12 @@ Crée le: Crée le
Créer une: Créer une
Devenir: Devenir
Découvrir: Découvrir
"Décrivez votre projet ou votre problème en donnant quelques éléments de contexte. Nous identifions, parmi lensemble des partenaires publics et parapublics, le conseiller compétent pour votre demande. Celui-ci vous contacte par téléphone sous 5 jours et vous accompagne en fonction de votre situation.":
Décrivez votre projet ou votre problème en donnant quelques éléments de
contexte. Nous identifions, parmi lensemble des partenaires publics et
parapublics, le conseiller compétent pour votre demande. Celui-ci vous
contacte par téléphone sous 5 jours et vous accompagne en fonction de votre
situation.
En savoir plus: En savoir plus
Entreprise Individuelle: Entreprise Individuelle
Exonérations: Exonérations
@ -1240,7 +1246,16 @@ pages:
etc.), ainsi que la mutuelle santé obligatoire.</7><8>Il existe des
<2>aides différées</2> à l'embauche qui ne sont pas toutes prises en
compte par notre simulateur, vous pouvez les retrouver sur <6>le portail
officiel</6>.</8>"
officiel</6>.</8><9>Échanger avec un conseiller pour votre projet de
recrutement</9><10>Vous souhaitez :<1><0>Être conseillé(e) sur les aides
à l'embauche mobilisables pour votre recrutement</0><1>Vous informer sur
l'apprentissage, le contrat de professionnalisation, les emplois francs
en quartiers prioritaires, le VTE...</1><2>Trouver des
candidats</2><3>Recruter une personne en situation de
handicap</3></1><2>Service public simple et rapide : vous êtes
rappelé(e) par LE conseiller qui peut vous
aider.</2></10><11>Partenaires mobilisés : Pôle emploi, APEC, Cap
Emploi, missions locales...</11><12></12>"
shortname: Salarié
title: Simulateur de revenus pour salarié
title-employeur: Simulateur de coûts d'embauche

View File

@ -53,6 +53,7 @@ export default function EndBlock({ fields, missingValues }: EndBlockProps) {
Voir les champs manquants
</Button>
)}
small
>
<Ul>
{missingValues.map(
@ -127,6 +128,7 @@ export default function EndBlock({ fields, missingValues }: EndBlockProps) {
Générer la demande
</Button>
)}
small
>
<Body>
Afin dexaminer votre situation au regard des règlements

View File

@ -1,8 +1,5 @@
import { Grid } from '@mui/material'
import {
specifyIfAutoEntrepreneur,
specifyIfDirigeantMajoritaire,
} from '@/actions/existingCompanyActions'
import { specifyIfAutoEntrepreneur } from '@/actions/existingCompanyActions'
import CompanyDetails from '@/components/CompanyDetails'
import PageHeader from '@/components/PageHeader'
import { FromBottom } from '@/components/ui/animate'
@ -13,7 +10,7 @@ import { Container, Spacing } from '@/design-system/layout'
import Popover from '@/design-system/Popover'
import { H2 } from '@/design-system/typography/heading'
import { Link } from '@/design-system/typography/link'
import { Body, Intro } from '@/design-system/typography/paragraphs'
import { Intro } from '@/design-system/typography/paragraphs'
import { useContext, useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { Trans, useTranslation } from 'react-i18next'
@ -33,6 +30,7 @@ import { MobiliteCard } from './cards/MobiliteCard'
import { SecuriteSocialeCard } from './cards/SecuriteSocialeCard'
import forms from './forms.svg'
import growth from './growth.svg'
import { PlacesDesEntreprisesButton } from '@/components/PlaceDesEntreprises'
export type DirigeantOrNull = keyof SimulatorData | null
@ -222,6 +220,13 @@ export default function Gérer() {
<KbisCard dirigeant={dirigeantSimulateur} />
</Grid>
</Grid>
<Spacing lg />
<PlacesDesEntreprisesButton
pathname="/aide-entreprise/mon-entreprise-urssaf-fr"
siret={company.firstMatchingEtablissement.siret}
/>
</FromBottom>
</>
)
@ -264,6 +269,7 @@ export const CompanySection = ({ company }: CompanySectionProps) => {
<ScrollToTop />
<Popover
title={t('gérer.entreprise.auto', 'Êtes-vous auto-entrepreneur ?')}
small
>
<Grid container spacing={2}>
<Grid item>

View File

@ -27,6 +27,7 @@ export const ContinueWithCompany = ({ company }: ContinueWithCompanyProps) => {
compact
to={sitePaths.gérer.index}
data-testid="currently-selected-company"
bodyAs="div"
>
<CompanyDetails entreprise={company} />
</Card>

View File

@ -4,6 +4,7 @@ import Meta from '@/components/utils/Meta'
import { SitePathsContext } from '@/components/utils/SitePathsContext'
import useSearchParamsSimulationSharing from '@/components/utils/useSearchParamsSimulationSharing'
import useSimulationConfig from '@/components/utils/useSimulationConfig'
import { Spacing } from '@/design-system/layout'
import { H1 } from '@/design-system/typography/heading'
import { Intro } from '@/design-system/typography/paragraphs'
import { Evaluation } from 'publicodes'
@ -107,6 +108,8 @@ export default function PageData(props: PageDataProps) {
iframePath={privateIframe ? undefined : iframePath}
nextSteps={nextSteps}
/>
<Spacing lg />
</>
)}
</CurrentSimulatorDataProvider>

View File

@ -42,6 +42,9 @@ import PAMCHome from './PAMCHome'
import SalariéSimulation from './Salarié'
import { SASUSimulation } from './SASU'
import SchemeComparaisonPage from './SchemeComparaison'
import { Li, Ul } from '@/design-system/typography/list'
import { Strong } from '@/design-system/typography'
import { PlacesDesEntreprisesButton } from '@/components/PlaceDesEntreprises'
interface SimulatorsDataParams {
t?: TFunction<'translation', string>
@ -137,6 +140,32 @@ function getSimulatorsData({
</Link>
.
</Body>
<H2>Échanger avec un conseiller pour votre projet de recrutement</H2>
<Body as="div">
Vous souhaitez :
<Ul>
<Li>
Être conseillé(e) sur les aides à l'embauche mobilisables pour
votre recrutement
</Li>
<Li>
Vous informer sur l'apprentissage, le contrat de
professionnalisation, les emplois francs en quartiers
prioritaires, le VTE...
</Li>
<Li>Trouver des candidats</Li>
<Li>Recruter une personne en situation de handicap</Li>
</Ul>
<Strong>
Service public simple et rapide : vous êtes rappelé(e) par LE
conseiller qui peut vous aider.
</Strong>
</Body>
<Body>
Partenaires mobilisés : Pôle emploi, APEC, Cap Emploi, missions
locales...
</Body>
<PlacesDesEntreprisesButton pathname="/aide-entreprise/rh-mon-entreprise-urssaf-fr/theme/recrutement-formation#section-breadcrumbs" />
</Trans>
),
},

View File

@ -285,6 +285,7 @@ function EnSavoirPlusCSP() {
'pages.développeurs.iframe.csp-title',
'Intégration iframe et politique de sécurité de contenu'
)}
small
>
<Trans i18nKey="pages.développeurs.iframe.csp-1">
<Body>

View File

@ -1,18 +1,10 @@
import * as Sentry from '@sentry/react'
import { Integrations } from '@sentry/tracing'
import { getBranch, isProduction, isStaging } from './utils'
let branch: string | undefined = import.meta.env.VITE_GITHUB_REF?.split(
'/'
)?.slice(-1)?.[0]
const branch = getBranch()
if (branch === 'merge') {
branch = import.meta.env.VITE_GITHUB_HEAD_REF
}
const release =
branch && `${branch}-` + import.meta.env.VITE_GITHUB_SHA?.substring(0, 7)
if (branch && branch !== 'master') {
if (branch && isStaging()) {
// eslint-disable-next-line no-console
console.info(
` Vous êtes sur la branche : %c${branch}`,
@ -20,15 +12,10 @@ if (branch && branch !== 'master') {
)
}
// We use this variable to hide some features in production while keeping them
// in feature-branches. In case we do A/B testing with several branches served
// in production, we should add the public faced branch names in the test below.
// This is different from the import.meta.env.MODE in that a feature branch may
// be build in production mode (with the NODE_ENV) but we may still want to show
// or hide some features.
export const productionMode = ['master', 'next'].includes(branch ?? '')
const release =
branch && `${branch}-` + import.meta.env.VITE_GITHUB_SHA?.substring(0, 7)
if (productionMode) {
if (isProduction()) {
Sentry.init({
dsn: 'https://92bbc21937b24136a2fe1b1d922b000f@o548798.ingest.sentry.io/5745615',
integrations: [new Integrations.BrowserTracing()],

View File

@ -17,4 +17,17 @@ interface ImportMetaEnv {
VITE_COMPANY_SEARCH_HOST?: string
VITE_REDUX_TRACE?: string
/**
* @deprecated Use isProduction(), isStaging() or isDevelopment() from utils.ts instead
*/
MODE: string
/**
* @deprecated Use isDevelopment() from utils.ts instead
*/
DEV: boolean
/**
* @deprecated Use isProduction() from utils.ts instead
*/
PROD: boolean
}

View File

@ -1,3 +1,8 @@
interface Window {
parentIFrame?: any
}
// Types from @types/iframe-resizer are for V3 and we use V4
declare module 'iframe-resizer' {
export const iframeResize: (options: unknown, id: string) => void
}

View File

@ -93,3 +93,39 @@ export const getValueFrom = <
key: K
): Extract<T, { [k in K]?: unknown }>[K] | undefined =>
key in obj ? obj[key] : undefined
/**
* Return git branch name
* @returns string
*/
export const getBranch = () => {
let branch: string | undefined = import.meta.env.VITE_GITHUB_REF?.split(
'/'
)?.slice(-1)?.[0]
if (branch === 'merge') {
branch = import.meta.env.VITE_GITHUB_HEAD_REF
}
return branch ?? ''
}
/**
* We use this function to hide some features in production while keeping them
* in feature-branches. In case we do A/B testing with several branches served
* in production, we should add the public faced branch names in the test below.
* This is different from the import.meta.env.MODE in that a feature branch may
* be build in production mode (with the NODE_ENV) but we may still want to show
* or hide some features.
* @returns boolean
*/
export const isProduction = () =>
import.meta.env.PROD && ['master', 'next'].includes(getBranch())
/**
* Is a feature branche
* @returns boolean
*/
export const isStaging = () => import.meta.env.PROD && !isProduction()
export const isDevelopment = () => import.meta.env.DEV

View File

@ -2601,7 +2601,7 @@ __metadata:
languageName: node
linkType: hard
"@react-aria/button@npm:^3.3.4":
"@react-aria/button@npm:^3.4.1":
version: 3.4.1
resolution: "@react-aria/button@npm:3.4.1"
dependencies:
@ -2634,7 +2634,7 @@ __metadata:
languageName: node
linkType: hard
"@react-aria/dialog@npm:^3.1.4":
"@react-aria/dialog@npm:^3.1.6":
version: 3.1.6
resolution: "@react-aria/dialog@npm:3.1.6"
dependencies:
@ -2788,7 +2788,7 @@ __metadata:
languageName: node
linkType: hard
"@react-aria/overlays@npm:^3.7.2, @react-aria/overlays@npm:^3.7.5":
"@react-aria/overlays@npm:^3.7.5":
version: 3.7.5
resolution: "@react-aria/overlays@npm:3.7.5"
dependencies:
@ -5398,13 +5398,6 @@ __metadata:
languageName: node
linkType: hard
"@types/iframe-resizer@npm:^3.5.7":
version: 3.5.9
resolution: "@types/iframe-resizer@npm:3.5.9"
checksum: aa60f72832f72f788ea3029fe4cbfb572bb481b5a1bb9b50d13287cdef347d8e00e07916aa2c3765f7e39d4ec8701f64ddd9d27fe93ba7571a0b3bf06f1ecca4
languageName: node
linkType: hard
"@types/is-function@npm:^1.0.0":
version: 1.0.1
resolution: "@types/is-function@npm:1.0.1"
@ -12235,6 +12228,13 @@ __metadata:
languageName: node
linkType: hard
"iframe-resizer@npm:^4.3.2":
version: 4.3.2
resolution: "iframe-resizer@npm:4.3.2"
checksum: 762d934f4fbe7e4b76c0f4574c2a47ef0e3fc6c82564f512ec41e62b62501052d271fb8ca5d268535509c0088132493c0ac78312af0fe3f11bf65cd964dd11a0
languageName: node
linkType: hard
"ignore@npm:^4.0.3, ignore@npm:^4.0.6":
version: 4.0.6
resolution: "ignore@npm:4.0.6"
@ -17589,12 +17589,12 @@ __metadata:
"@internationalized/number": ^3.0.3
"@mui/material": ^5.0.4
"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"
"@react-aria/button": ^3.3.4
"@react-aria/button": ^3.4.1
"@react-aria/checkbox": ^3.2.3
"@react-aria/dialog": ^3.1.4
"@react-aria/dialog": ^3.1.6
"@react-aria/i18n": ^3.3.2
"@react-aria/numberfield": ^3.1.0
"@react-aria/overlays": ^3.7.2
"@react-aria/overlays": ^3.7.5
"@react-aria/progress": ^3.1.3
"@react-aria/radio": ^3.1.5
"@react-aria/searchfield": ^3.2.0
@ -17618,7 +17618,6 @@ __metadata:
"@storybook/addon-links": ^6.5.0-alpha.40
"@storybook/react": ^6.5.0-alpha.40
"@storybook/testing-library": ^0.0.9
"@types/iframe-resizer": ^3.5.7
"@types/ramda": ^0.26.43
"@types/react": ^17.0.0
"@types/react-color": ^3.0.1
@ -17648,6 +17647,7 @@ __metadata:
eslint-plugin-react-hooks: ^4.3.0
fuse.js: ^6.4.6
i18next-parser: ^6.0.0
iframe-resizer: ^4.3.2
isomorphic-fetch: ^2.2.1
markdown-to-jsx: ^7.1.5
modele-social: ^0.6.0