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 tabletpull/2051/head
parent
00da2d25d1
commit
078e60b728
|
@ -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:
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -56,7 +56,7 @@ export default function CompanyDetails({
|
|||
</>{' '}
|
||||
</H3>
|
||||
|
||||
<InfoContainer>
|
||||
<InfoContainer as="div">
|
||||
{dateCreationUniteLegale && (
|
||||
<div>
|
||||
<Trans>Crée le</Trans>{' '}
|
||||
|
|
|
@ -32,10 +32,6 @@
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.distribution-chart__counterparts {
|
||||
width: 35em;
|
||||
}
|
||||
|
||||
@media not print {
|
||||
@media (max-width: 600px) {
|
||||
.distribution-chart__legend {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 l’ensemble 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>
|
||||
)
|
||||
}
|
|
@ -31,6 +31,7 @@ export default function SearchButton() {
|
|||
</StyledLabel>
|
||||
</StyledButton>
|
||||
)}
|
||||
small
|
||||
>
|
||||
{(closePopover) => (
|
||||
<SearchRulesAndSimulators closePopover={closePopover} />
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@ export default function SeeAnswersButton() {
|
|||
<Trans>Voir mes paramètres</Trans>
|
||||
</Button>
|
||||
)}
|
||||
small
|
||||
>
|
||||
{(close) => <Answers onClose={close} />}
|
||||
</PopoverWithTrigger>
|
||||
|
|
|
@ -14,6 +14,7 @@ export default function InscriptionBetaTesteur() {
|
|||
<Link {...buttonProps}>Devenir beta-testeur</Link>
|
||||
)}
|
||||
title="Votre avis nous intéresse"
|
||||
small
|
||||
>
|
||||
<img
|
||||
src={FeedbackSvg}
|
||||
|
|
|
@ -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'} />
|
||||
|
|
|
@ -25,6 +25,6 @@ export default function Emoji({ emoji }: PropType) {
|
|||
baseUrl: siteUrl + '/twemoji/2/',
|
||||
ext: '.png',
|
||||
}
|
||||
: ({} as any)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
`
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -46,6 +46,7 @@ export default function ButtonHelp({
|
|||
</StyledButton>
|
||||
)}
|
||||
title={title}
|
||||
small
|
||||
>
|
||||
{children}
|
||||
</PopoverWithTrigger>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
}
|
|
@ -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 l’ensemble 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
|
||||
|
|
|
@ -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 l’ensemble 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 l’ensemble 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
|
||||
|
|
|
@ -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 d’examiner votre situation au regard des règlements
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
),
|
||||
},
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue