✨ Termine le formulaire de demande de mobilité en Europe
- Ajoute la possibilité de signer via un écran tactile - Améliore le parcours du formulaire - Prends en compte les retours ACOSS - Ajoute une possibilité d'intégration en iframe (non listée dans les intégrations officielles)pull/1086/head
parent
efe1068f6a
commit
bbdfa151c6
|
@ -52,6 +52,7 @@
|
|||
"react-redux": "^7.0.3",
|
||||
"react-router-dom": "^5.1.1",
|
||||
"react-router-hash-link": "^1.2.2",
|
||||
"react-signature-pad-wrapper": "^1.2.11",
|
||||
"react-spring": "=8.0.27",
|
||||
"react-syntax-highlighter": "^10.1.1",
|
||||
"react-transition-group": "^2.2.1",
|
||||
|
|
|
@ -32,6 +32,9 @@ export default function DateInput({
|
|||
if (+year < 1700) {
|
||||
return
|
||||
}
|
||||
if (year.length > 4) {
|
||||
return
|
||||
}
|
||||
if ([day, month, year].some(x => Number.isNaN(+x))) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import React, { useCallback } from 'react'
|
||||
import { debounce } from '../../utils'
|
||||
|
||||
export default function ParagrapheInput({
|
||||
onChange,
|
||||
dottedName,
|
||||
value,
|
||||
defaultValue,
|
||||
autoFocus
|
||||
}) {
|
||||
const debouncedOnChange = useCallback(debounce(1000, onChange), [])
|
||||
|
||||
return (
|
||||
<div className="step input">
|
||||
<textarea
|
||||
autoFocus={autoFocus}
|
||||
className="ui__"
|
||||
rows={6}
|
||||
style={{ resize: 'none' }}
|
||||
id={'step-' + dottedName}
|
||||
placeholder={(defaultValue?.nodeValue ?? defaultValue)?.replace(
|
||||
'\\n',
|
||||
'\n'
|
||||
)}
|
||||
onChange={({ target }) => {
|
||||
debouncedOnChange(`'${target.value.replace(/\n/g, '\\n')}'`)
|
||||
}}
|
||||
defaultValue={value?.replace('\\n', '\n')}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -12,6 +12,8 @@ import { useTranslation } from 'react-i18next'
|
|||
import { DottedName } from 'Rules'
|
||||
import DateInput from './DateInput'
|
||||
import TextInput from './TextInput'
|
||||
import SelectEuropeCountry from './select/SelectEuropeCountry'
|
||||
import ParagrapheInput from './ParagrapheInput'
|
||||
|
||||
type Value = string | number | object | boolean | null
|
||||
export type RuleInputProps<Name extends string = DottedName> = {
|
||||
|
@ -68,6 +70,8 @@ export default function RuleInput<Name extends string = DottedName>({
|
|||
}
|
||||
if (rule.API && rule.API === 'commune')
|
||||
return <SelectCommune {...commonProps} onSubmit={onSubmit} />
|
||||
if (rule.API && rule.API === 'pays européen')
|
||||
return <SelectEuropeCountry {...commonProps} onSubmit={onSubmit} />
|
||||
if (rule.API) throw new Error("Les seules API implémentées sont 'commune'")
|
||||
|
||||
if (rule.dottedName == 'contrat salarié . ATMP . taux collectif ATMP')
|
||||
|
@ -130,6 +134,9 @@ export default function RuleInput<Name extends string = DottedName>({
|
|||
if (rule.type === 'texte') {
|
||||
return <TextInput {...commonProps} />
|
||||
}
|
||||
if (rule.type === 'paragraphe') {
|
||||
return <ParagrapheInput {...commonProps} />
|
||||
}
|
||||
|
||||
return <Input {...commonProps} unit={unit} onSubmit={onSubmit} />
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import React from 'react'
|
||||
const STATES = [
|
||||
'Allemagne',
|
||||
'Autriche',
|
||||
'Belgique',
|
||||
'Bulgarie',
|
||||
'Chypre',
|
||||
'Croatie',
|
||||
'Danemark',
|
||||
'Espagne',
|
||||
'Estonie',
|
||||
'Finlande',
|
||||
'Grèce',
|
||||
'Hongrie',
|
||||
'Irlande',
|
||||
'Islande',
|
||||
'Italie',
|
||||
'Lettonie',
|
||||
'Liechtenstein',
|
||||
'Lituanie',
|
||||
'Luxembourg',
|
||||
'Malte',
|
||||
'Norvège',
|
||||
'Pays-Bas',
|
||||
'Pologne',
|
||||
'Portugal',
|
||||
'République Tchèque',
|
||||
'Roumanie',
|
||||
'Royaume-Uni',
|
||||
'Slovaquie',
|
||||
'Slovénie',
|
||||
'Suède',
|
||||
'Suisse'
|
||||
]
|
||||
|
||||
export default function SelectEuropeCountry({ value, onChange, onSubmit }) {
|
||||
return (
|
||||
<div>
|
||||
<select
|
||||
name="country"
|
||||
className="ui__"
|
||||
defaultValue={value?.slice(1, -1)}
|
||||
onChange={e => onChange(`'${e.target.value}'`)}
|
||||
>
|
||||
{STATES.map(state => (
|
||||
<option key={state} value={state}>
|
||||
{state}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -49,14 +49,14 @@
|
|||
.ui__.checkbox:hover svg {
|
||||
stroke: var(--color);
|
||||
}
|
||||
.ui__.checkbox-input:checked + .ui__.checkbox svg {
|
||||
.ui__.checkbox-input:checked + label .ui__.checkbox svg {
|
||||
stroke: var(--color);
|
||||
}
|
||||
.ui__.checkbox-input:checked + .ui__.checkbox svg path {
|
||||
.ui__.checkbox-input:checked + label .ui__.checkbox svg path {
|
||||
stroke-dashoffset: 60;
|
||||
transition: all 0.15s linear;
|
||||
}
|
||||
.ui__.checkbox-input:checked + .ui__.checkbox svg polyline {
|
||||
.ui__.checkbox-input:checked + label .ui__.checkbox svg polyline {
|
||||
stroke-dashoffset: 42;
|
||||
transition: all 0.1s linear;
|
||||
transition-delay: 0.075s;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react'
|
||||
import './index.css'
|
||||
|
||||
export default function Checkbox(props: React.ComponentProps<'input'>) {
|
||||
export default function Checkbox(
|
||||
props: React.ComponentProps<'input'> & { label?: string }
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
|
@ -10,11 +12,20 @@ export default function Checkbox(props: React.ComponentProps<'input'>) {
|
|||
style={{ display: 'none' }}
|
||||
{...props}
|
||||
/>
|
||||
<label htmlFor={props.id} className="ui__ checkbox" tabIndex={0}>
|
||||
<svg width="1em" height="1em" viewBox="0 0 18 18">
|
||||
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" />
|
||||
<polyline points="1 9 7 14 15 4" />
|
||||
</svg>
|
||||
<label
|
||||
htmlFor={props.id}
|
||||
tabIndex={0}
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
<div className="ui__ checkbox">
|
||||
<svg width="1em" height="1em" viewBox="0 0 18 18">
|
||||
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" />
|
||||
<polyline points="1 9 7 14 15 4" />
|
||||
</svg>
|
||||
</div>
|
||||
{'label' in props && (
|
||||
<span style={{ marginLeft: '0.6rem' }}>{props.label}</span>
|
||||
)}
|
||||
</label>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -196,18 +196,28 @@ pre.ui__.code > code {
|
|||
hyphens: none;
|
||||
}
|
||||
|
||||
input.ui__ {
|
||||
input.ui__,
|
||||
textarea.ui__,
|
||||
select.ui__ {
|
||||
padding: 0.4rem;
|
||||
width: 25rem;
|
||||
max-width: 100%;
|
||||
margin-bottom: 0.6rem;
|
||||
border: 1px solid var(--lighterTextColor);
|
||||
border-radius: 0.3rem;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
transition: border-color 0.1s;
|
||||
position: relative;
|
||||
font-family: inherit;
|
||||
}
|
||||
input.ui__ {
|
||||
width: 25rem;
|
||||
}
|
||||
textarea.ui__ {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input.ui__[inputmode='numeric'] {
|
||||
width: 10rem;
|
||||
}
|
||||
|
@ -217,7 +227,8 @@ input.ui__[type='date'] {
|
|||
input.ui__.suffixed {
|
||||
text-align: right;
|
||||
}
|
||||
input.ui__:focus {
|
||||
input.ui__:focus,
|
||||
select.ui__:focus {
|
||||
border-color: var(--color);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { iframeResizer } from 'iframe-resizer'
|
||||
import logoEnSvg from 'Images/logo-mycompany.svg'
|
||||
import logoFrSvg from 'Images/logo.svg'
|
||||
import marianneSvg from 'Images/marianne.svg'
|
||||
import urssafSvg from 'Images/urssaf.svg'
|
||||
|
||||
let script =
|
||||
|
@ -71,17 +70,10 @@ links.innerHTML = `
|
|||
<a href="https://www.urssaf.fr" target="_blank">
|
||||
<img
|
||||
style="height: 2.5rem; margin-right: 1rem"
|
||||
src="${process.env.FR_SITE.replace('${path}', '/' + urssafSvg)}"
|
||||
src="${urssafSvg}"
|
||||
alt="un service fourni par l'Urssaf"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://beta.gouv.fr" target="_blank">
|
||||
<img
|
||||
style="height: 2.5rem"
|
||||
src="${process.env.FR_SITE.replace('${path}', '/' + marianneSvg)}"
|
||||
alt="incubé par beta.gouv.fr (direction du numérique)"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
import { BlobProvider } from '@react-pdf/renderer'
|
||||
import Overlay from 'Components/Overlay'
|
||||
import Checkbox from 'Components/ui/Checkbox'
|
||||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import React, { useContext, useRef, useState, Suspense } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import SignaturePad from 'react-signature-pad-wrapper'
|
||||
import PDFDocument from './PDFDocument'
|
||||
|
||||
const IS_TOUCH_DEVICE = isOnTouchDevice()
|
||||
type SignaturePadInstance = {
|
||||
clear: () => void
|
||||
toDataURL: () => string
|
||||
}
|
||||
|
||||
export default function EndBlock({ fields, isMissingValues }) {
|
||||
const [isCertified, setCertified] = useState(false)
|
||||
const [place, setPlace] = useState<string>()
|
||||
const [showDownloadLink, toggleDownloadLink] = useState(false)
|
||||
|
||||
const { darkColor } = useContext(ThemeColorsContext)
|
||||
const signatureRef = useRef<SignaturePadInstance>()
|
||||
|
||||
if (isMissingValues) {
|
||||
return (
|
||||
<blockquote>
|
||||
<strong>Certains champs ne sont pas renseignés.</strong>
|
||||
<br />{' '}
|
||||
<small>
|
||||
Vous devez compléter l'intégralité du formulaire avant de pouvoir le
|
||||
signer et générer votre demande.
|
||||
</small>
|
||||
</blockquote>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h2>Déclaration sur l'honneur</h2>
|
||||
<Checkbox
|
||||
name="certified"
|
||||
id="certified"
|
||||
onChange={e => setCertified(e.target.checked)}
|
||||
checked={isCertified}
|
||||
label="Je certifie l’exactitude des informations communiquées ci-dessus."
|
||||
/>
|
||||
<p className="ui__ notice">
|
||||
L’auteur d’une fausse déclaration est passible d’une condamnation au
|
||||
titre de l’article 441-1 du code pénal.
|
||||
</p>
|
||||
|
||||
<label>
|
||||
<small>Fait à :</small>
|
||||
<br />
|
||||
<input
|
||||
type="text"
|
||||
className="ui__"
|
||||
value={place}
|
||||
onChange={e => setPlace(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
{IS_TOUCH_DEVICE && (
|
||||
<div>
|
||||
<small>
|
||||
Signez ci dessous en utilisant la totalité de l'espace :{' '}
|
||||
</small>
|
||||
<div
|
||||
css={`
|
||||
border: 1px solid var(--darkColor);
|
||||
border-radius: 0.3rem;
|
||||
position: relative;
|
||||
`}
|
||||
>
|
||||
<SignaturePad
|
||||
height={200}
|
||||
options={{ penColor: darkColor }}
|
||||
ref={signatureRef}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
css={`
|
||||
text-align: right;
|
||||
`}
|
||||
>
|
||||
<button
|
||||
className="ui__ simple small button"
|
||||
onClick={() => signatureRef.current?.clear()}
|
||||
>
|
||||
{emoji('🗑️')} Recommencer{' '}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<button
|
||||
className="ui__ cta plain button"
|
||||
disabled={!place || !isCertified}
|
||||
onClick={() => toggleDownloadLink(true)}
|
||||
>
|
||||
Générer la demande
|
||||
</button>
|
||||
</div>
|
||||
<p className="ui__ notice">
|
||||
<strong>Vie privée :</strong> aucune donnée n'est transmise à nos
|
||||
serveurs, la génération du formulaire se fait entièrement depuis votre
|
||||
navigateur.
|
||||
</p>
|
||||
{showDownloadLink && (
|
||||
<Overlay onClose={() => toggleDownloadLink(false)}>
|
||||
<h2>Votre demande de mobilité</h2>
|
||||
<p>
|
||||
Afin d’examiner votre situation au regard des règlements
|
||||
communautaires UE/EEE de Sécurité sociale (CE 883/2004), veuillez
|
||||
envoyer ce document à{' '}
|
||||
<a href="mailto:relations.internationales@urssaf.fr">
|
||||
relations.internationales@urssaf.fr
|
||||
</a>
|
||||
</p>
|
||||
<Suspense
|
||||
fallback={
|
||||
<blockquote>
|
||||
<small>Génération du pdf en cours...</small>
|
||||
</blockquote>
|
||||
}
|
||||
>
|
||||
<LazyBlobProvider
|
||||
document={
|
||||
<PDFDocument
|
||||
fields={fields}
|
||||
signatureURL={
|
||||
IS_TOUCH_DEVICE && signatureRef.current?.toDataURL()
|
||||
}
|
||||
place={place}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{({ url, loading, error }) =>
|
||||
error ? (
|
||||
<blockquote>
|
||||
<strong>Erreur lors de la génération du pdf</strong>
|
||||
<br />
|
||||
<small>
|
||||
Veuillez envoyer un mail à
|
||||
contact@mon-entreprise.beat.gouv.fr
|
||||
</small>
|
||||
</blockquote>
|
||||
) : loading ? (
|
||||
<blockquote>
|
||||
<small>Génération du pdf en cours...</small>
|
||||
</blockquote>
|
||||
) : (
|
||||
url && (
|
||||
<>
|
||||
{!IS_TOUCH_DEVICE && (
|
||||
<blockquote>
|
||||
<strong>
|
||||
N'oubliez pas de signer le document avant de
|
||||
l'envoyer
|
||||
</strong>
|
||||
</blockquote>
|
||||
)}
|
||||
<a
|
||||
href={url}
|
||||
className="ui__ cta plain button"
|
||||
download="demande-mobilité-europe.pdf"
|
||||
>
|
||||
Télécharger le fichier
|
||||
</a>
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
</LazyBlobProvider>
|
||||
</Suspense>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const LazyBlobProvider = React.lazy<typeof BlobProvider>(
|
||||
() =>
|
||||
new Promise(resolve =>
|
||||
setTimeout(() => resolve({ default: BlobProvider }), 300)
|
||||
)
|
||||
)
|
||||
|
||||
// From https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript/4819886#4819886
|
||||
function isOnTouchDevice() {
|
||||
const prefixes = ' -webkit- -moz- -o- -ms- '.split(' ')
|
||||
const mq = function(query) {
|
||||
return window.matchMedia(query).matches
|
||||
}
|
||||
if (
|
||||
'ontouchstart' in window ||
|
||||
('DocumentTouch' in window &&
|
||||
document instanceof (window as any).DocumentTouch)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
|
||||
// https://git.io/vznFH
|
||||
const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('')
|
||||
return mq(query)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import { StyleSheet, Text, View } from '@react-pdf/renderer'
|
||||
import { formatValue } from 'publicodes'
|
||||
import React from 'react'
|
||||
|
||||
export default function FieldsPDF({ fields }) {
|
||||
return fields.map(field => (
|
||||
<View style={styles.field} key={field.dottedName} wrap={false}>
|
||||
{field.type === 'groupe' ? (
|
||||
<>
|
||||
<Text style={styles.subtitle}>
|
||||
{field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.name}>
|
||||
{field.question ?? field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
{field.nodeValue != null && (
|
||||
<Text style={styles.value}>{formatValue(field)}</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
))
|
||||
}
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
fieldNumber: {
|
||||
opacity: 0.7
|
||||
},
|
||||
subtitle: {
|
||||
paddingTop: 10,
|
||||
fontFamily: 'Montserrat',
|
||||
fontSize: 16
|
||||
},
|
||||
field: {
|
||||
marginBottom: 12,
|
||||
lineHeight: 1.2
|
||||
},
|
||||
name: {
|
||||
fontSize: 11,
|
||||
marginBottom: 4,
|
||||
opacity: 0.7,
|
||||
fontFamily: 'Roboto'
|
||||
},
|
||||
value: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'Roboto'
|
||||
}
|
||||
})
|
|
@ -0,0 +1,131 @@
|
|||
import {
|
||||
Document,
|
||||
Font,
|
||||
Image,
|
||||
Page,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View
|
||||
} from '@react-pdf/renderer'
|
||||
import urssafPng from 'Images/destinataires/URSSAF.png'
|
||||
import React from 'react'
|
||||
import FieldsPDF, { styles as fieldStyles } from './FieldsPDF'
|
||||
import montserratUrl from './Montserrat-SemiBold.ttf'
|
||||
import robotoUrl from './Roboto-Regular.ttf'
|
||||
|
||||
export default function PDFDocument({ fields, signatureURL, place }) {
|
||||
return (
|
||||
<Document>
|
||||
<Page style={styles.body} wrap>
|
||||
<View style={styles.header}>
|
||||
<Image src={urssafPng} style={styles.logo} />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={styles.title}>
|
||||
{fields.find(({ dottedName }) => dottedName === 'détachement')
|
||||
? 'Demande de détachement en Europe'
|
||||
: "Demande d'activité transfrontalière simultanée en Europe"}
|
||||
</Text>
|
||||
<Text style={styles.texte}>
|
||||
Afin d’examiner votre situation au regard des règlements
|
||||
communautaires de Sécurité sociale (CE 883/2004 et CE 987/2009),
|
||||
veuillez envoyer ce document à relations.internationales@urssaf.fr
|
||||
</Text>
|
||||
</View>
|
||||
<FieldsPDF fields={fields} />
|
||||
<View style={styles.texte}>
|
||||
<View style={fieldStyles.field}>
|
||||
<Text style={fieldStyles.subtitle}>Déclaration sur l'honneur</Text>
|
||||
</View>
|
||||
<View style={fieldStyles.field}>
|
||||
<Text style={fieldStyles.value}>
|
||||
Je certifie l’exactitude des informations communiquées ci-dessus.
|
||||
</Text>
|
||||
</View>
|
||||
<View style={fieldStyles.field}>
|
||||
<Text style={fieldStyles.value}>
|
||||
Fait le{' '}
|
||||
{new Date()
|
||||
.toISOString()
|
||||
.split('T')[0]
|
||||
.replace(/([\d]{4})-([\d]{2})-([\d]{2})/, '$3/$2/$1')}{' '}
|
||||
à {place}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={fieldStyles.field}>
|
||||
<Text style={fieldStyles.name}>Signature :</Text>
|
||||
</View>
|
||||
{signatureURL ? (
|
||||
<Image src={signatureURL} style={styles.signature} />
|
||||
) : (
|
||||
<View style={styles.signatureBox} />
|
||||
)}
|
||||
</View>
|
||||
<View fixed style={styles.footer}>
|
||||
<Text>
|
||||
La loi n° 78-17 du 6 janvier 1978 relative à l’informatique, aux
|
||||
fichiers et aux libertés, s’applique aux réponses faites sur ce
|
||||
formulaire. Elle garantit un droit d’accès et de rectification pour
|
||||
les données vous concernant auprès de notre organisme.
|
||||
</Text>
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
)
|
||||
}
|
||||
|
||||
Font.register({
|
||||
family: 'Roboto',
|
||||
src: robotoUrl
|
||||
})
|
||||
|
||||
Font.register({
|
||||
family: 'Montserrat',
|
||||
src: montserratUrl
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
body: {
|
||||
paddingTop: 35,
|
||||
color: '#18457B',
|
||||
lineHeight: 1.5,
|
||||
paddingBottom: 65,
|
||||
paddingHorizontal: 35
|
||||
},
|
||||
header: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
marginBottom: 20
|
||||
},
|
||||
logo: {
|
||||
objectFit: 'scale-down',
|
||||
width: 100
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Montserrat'
|
||||
},
|
||||
texte: {
|
||||
fontFamily: 'Roboto',
|
||||
marginBottom: 12,
|
||||
fontSize: 14
|
||||
},
|
||||
signature: {
|
||||
objectFit: 'scale-down',
|
||||
maxWidth: 300
|
||||
},
|
||||
signatureBox: {
|
||||
height: 100
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
marginHorizontal: 35,
|
||||
paddingVertical: 5,
|
||||
opacity: 0.7,
|
||||
fontSize: 6
|
||||
}
|
||||
})
|
|
@ -0,0 +1,324 @@
|
|||
activité france . SIREN:
|
||||
note: 4.2
|
||||
type: texte
|
||||
|
||||
coordonnées assuré:
|
||||
titre: Vos coordonnées
|
||||
type: groupe
|
||||
formule: oui
|
||||
note: 1
|
||||
coordonnées assuré . nom:
|
||||
type: texte
|
||||
note: 1.2
|
||||
coordonnées assuré . prénoms:
|
||||
type: texte
|
||||
note: 1.3
|
||||
coordonnées assuré . nationalité:
|
||||
type: texte
|
||||
note: 1.6
|
||||
coordonnées assuré . numéro de sécurité sociale:
|
||||
description: Saisissez les 15 chiffres de votre numéro tel qu'il apparaît sur votre carte vitale par exemple.
|
||||
type: texte
|
||||
# API: numéro sécurité sociale
|
||||
note: 1.1
|
||||
coordonnées assuré . date de naissance:
|
||||
type: date
|
||||
note: 1.5
|
||||
coordonnées assuré . commune de naissance:
|
||||
API: commune
|
||||
note: 1.7
|
||||
|
||||
coordonnées assuré . domicile personnel:
|
||||
type: groupe
|
||||
formule: oui
|
||||
note: 1.8
|
||||
coordonnées assuré . domicile personnel . adresse:
|
||||
type: texte
|
||||
note: 1.9.1
|
||||
coordonnées assuré . domicile personnel . commune:
|
||||
API: commune
|
||||
note: 1.9.2 / 1.9.3
|
||||
coordonnées assuré . contact:
|
||||
type: groupe
|
||||
formule: oui
|
||||
coordonnées assuré . contact . email:
|
||||
type: texte
|
||||
coordonnées assuré . contact . téléphone:
|
||||
type: texte
|
||||
|
||||
activité france:
|
||||
note: 4 / 4.1.2
|
||||
type: groupe
|
||||
formule: oui
|
||||
titre: Votre activité indépendante en France
|
||||
|
||||
activité france . nom:
|
||||
note: 4.3
|
||||
titre: Nom de l'entreprise
|
||||
type: texte
|
||||
activité france . adresse:
|
||||
note: 4.4.1
|
||||
type: texte
|
||||
activité france . commune:
|
||||
API: commune
|
||||
note:: 4.4.3 / 4.4.4
|
||||
activité france . organisme urssaf:
|
||||
type: texte
|
||||
description: >
|
||||
Nom de l'organisme Urssaf dont vous relevez en france
|
||||
activité france . nature de l'activité:
|
||||
question: Quel est la nature de votre activité en France ?
|
||||
type: texte
|
||||
description: >-
|
||||
Saisissez une courte description de votre activité en France (ex: plombier, coach sportif, aménagement intérieur)
|
||||
|
||||
demande:
|
||||
titre: Votre demande
|
||||
formule: oui
|
||||
type: groupe
|
||||
|
||||
demande . pays unique:
|
||||
question: >
|
||||
Allez-vous exercer une activité non salariée dans un seul et unique pays ?
|
||||
|
||||
demande . infrastructure sauvegardée:
|
||||
applicable si: pays unique
|
||||
question: >
|
||||
Pendant votre travail en dehors de la France, l'infrastructure de votre
|
||||
entreprise en France reste-elle en place ?
|
||||
description: >-
|
||||
Par infrastructure, est entendu tout ce qui est nécessaire pour poursuivre votre travail en
|
||||
France après votre retour (local, matériel, etc.).
|
||||
|
||||
demande . activité semblable:
|
||||
applicable si: infrastructure sauvegardée
|
||||
question: >
|
||||
Allez-vous exercer cette mission dans un domaine d'activité semblable ?
|
||||
|
||||
demande . date de fin connue:
|
||||
applicable si: activité semblable
|
||||
question: Votre activité à l'étranger est-elle limitée dans le temps ?
|
||||
|
||||
demande . détachement possible:
|
||||
formule:
|
||||
toutes ces conditions:
|
||||
- date de fin connue
|
||||
- pays unique
|
||||
- infrastructure sauvegardée
|
||||
- activité semblable
|
||||
|
||||
détachement:
|
||||
note: 3.3
|
||||
titre: Demande de détachement
|
||||
applicable si: demande . détachement possible
|
||||
formule: oui
|
||||
type: groupe
|
||||
|
||||
détachement . pays:
|
||||
note: 4.4.2
|
||||
API: pays européen
|
||||
type: texte
|
||||
|
||||
détachement . date de début:
|
||||
type: date
|
||||
note: 2.2
|
||||
|
||||
détachement . date de fin:
|
||||
note: 2.2
|
||||
type: date
|
||||
|
||||
détachement . nature de l'activité:
|
||||
question: Quel sera la nature de votre activité pendant la période de détachement ?
|
||||
type: texte
|
||||
description: >-
|
||||
Saisissez une courte description de votre activité pendant le détachement (ex: plombier, coach sportif, aménagement intérieur)
|
||||
|
||||
détachement . base fixe:
|
||||
question: Connaissez vous l'adresse de votre activité à l'étranger ?
|
||||
note: 5.3
|
||||
|
||||
détachement . activité:
|
||||
applicable si: base fixe
|
||||
note: 5.2
|
||||
titre: Coordonnées de votre client / chantier / lieu d'activité à l'étranger
|
||||
type: groupe
|
||||
formule: oui
|
||||
détachement . activité . nom:
|
||||
type: texte
|
||||
détachement . activité . adresse:
|
||||
type: texte
|
||||
détachement . activité . ville:
|
||||
type: texte
|
||||
détachement . activité . code postal:
|
||||
type: texte
|
||||
détachement . commentaires additionnels:
|
||||
question: Souhaitez-vous partager d'autres informations pour l'instruction de votre dossier (adresses supplémentaires, commentaires, etc) ?
|
||||
détachement . commentaires additionnels . commentaires:
|
||||
type: paragraphe
|
||||
|
||||
activité transfrontalière simultanée:
|
||||
non applicable si: demande . détachement possible
|
||||
titre: Demande d'activité transfrontalière simultanée
|
||||
formule: oui
|
||||
type: groupe
|
||||
activité transfrontalière simultanée . salarié hors France:
|
||||
question: >
|
||||
Travaillez-vous en tant que salarié dans un autre pays ?
|
||||
|
||||
activité transfrontalière simultanée . activité salariée:
|
||||
applicable si: salarié hors France
|
||||
note: 3.8 / 4.1.2
|
||||
titre: Activité salariée à l'étranger
|
||||
type: groupe
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité salariée . pays:
|
||||
API: pays européen
|
||||
note: 5.1
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité salariée . profession:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité salariée . période:
|
||||
type: groupe
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité salariée . période . date de début du contrat:
|
||||
type: date
|
||||
activité transfrontalière simultanée . activité salariée . période . date de fin connue:
|
||||
question: Votre contrat salarié a-t'il une date de fin ?
|
||||
activité transfrontalière simultanée . activité salariée . période . date de fin:
|
||||
applicable si: date de fin connue
|
||||
type: date
|
||||
|
||||
activité transfrontalière simultanée . activité salariée . employeur:
|
||||
note: 5.1
|
||||
type: groupe
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité salariée . employeur . nom:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité salariée . employeur . adresse:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité salariée . employeur . ville:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité salariée . employeur . code postal:
|
||||
type: texte
|
||||
|
||||
activité transfrontalière simultanée . activité salariée . activité substantielle:
|
||||
question: >
|
||||
Le salaire de cette activité representera-t'il plus de 5% de vos revenus pour
|
||||
les 12 prochains mois ?
|
||||
|
||||
activité transfrontalière simultanée . activité salariée . activité indépendante additionnelle:
|
||||
question: >
|
||||
Exercez-vous également une activité non salariée à l'étranger ?
|
||||
|
||||
activité transfrontalière simultanée . part subtentielle France:
|
||||
non applicable si: activité salariée . activité substantielle
|
||||
applicable si:
|
||||
une de ces conditions:
|
||||
- activité salariée = non
|
||||
- activité salariée . activité indépendante additionnelle
|
||||
question: Est-ce qu'au moins 25% de votre activité professionnelle des 12 prochains mois aura lieu en France ?
|
||||
description: >-
|
||||
Ce pourcentage peut être celui du temps de travail ou du chiffre d'affaires
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée:
|
||||
applicable si:
|
||||
une de ces conditions:
|
||||
- activité salariée = non
|
||||
- activité salariée . activité indépendante additionnelle
|
||||
titre: Activités non salariées à l'étranger
|
||||
type: groupe
|
||||
formule: oui
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . nombre:
|
||||
question: Dans combien de pays autre que la France exercerez-vous une activité non salariée ?
|
||||
type: nombre
|
||||
contrôles:
|
||||
- si: nombre > 2
|
||||
message: >
|
||||
Ce formulaire ne permet pas de déclarer une activité dans plus de
|
||||
3 pays
|
||||
niveau: avertissement
|
||||
- si: nombre < 1
|
||||
message: >
|
||||
Vous devez déclarer un pays au moins
|
||||
niveau: avertissement
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . n°1:
|
||||
titre: activité non salarié n°1
|
||||
type: groupe
|
||||
applicable si: nombre >= 1
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . pays:
|
||||
API: pays européen
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . nom de l'entreprise:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . adresse:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . ville:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . code postal:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°1 . date de début d'activité:
|
||||
type: date
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . n°2:
|
||||
titre: activité non salarié n°2
|
||||
type: groupe
|
||||
applicable si: nombre >= 2
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . pays:
|
||||
API: pays européen
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . nom de l'entreprise:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . adresse:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . ville:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . code postal:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°2 . date de début d'activité:
|
||||
type: date
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . n°3:
|
||||
titre: activité non salarié n°3
|
||||
type: groupe
|
||||
applicable si: nombre >= 3
|
||||
formule: oui
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . pays:
|
||||
API: pays européen
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . nom de l'entreprise:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . adresse:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . ville:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . code postal:
|
||||
type: texte
|
||||
activité transfrontalière simultanée . activité non salariée . n°3 . date de début d'activité:
|
||||
type: date
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . pays centre d'intérêt:
|
||||
type: groupe
|
||||
formule: oui
|
||||
titre: Quel sera votre pays "centre d'intêret" pour les 12 prochains mois ?
|
||||
non applicable si:
|
||||
une de ces conditions:
|
||||
- part subtentielle France
|
||||
- activité salariée . activité substantielle
|
||||
applicable si: nombre >= 2
|
||||
description: >-
|
||||
Pour determiner votre pays "centre d'intêret", vous devez prendre en compte les critères suivants (au choix) :
|
||||
|
||||
- Le pays d'où découlera la majorité des revenus de vos activités / de votre chiffre d'affaires
|
||||
|
||||
- Le pays où vous réaliserez vos activités de manière habituelle / où vous passerez le plus de temps de travail
|
||||
|
||||
- En cas d'ambiguité pour les deux critères ci-dessus, le pays où sera situé le siège fixe de vos activités
|
||||
|
||||
activité transfrontalière simultanée . activité non salariée . pays centre d'intérêt . pays:
|
||||
API: pays européen
|
||||
titre: Pays "centre d'intêret"
|
||||
type: texte
|
|
@ -2,15 +2,17 @@ import RuleInput from 'Components/conversation/RuleInput'
|
|||
import * as Animate from 'Components/ui/animate'
|
||||
import InfoBulle from 'Components/ui/InfoBulle'
|
||||
import { Markdown } from 'Components/utils/markdown'
|
||||
import Engine from 'publicodes'
|
||||
import React, { useCallback, useState, Suspense } from 'react'
|
||||
import formulaire from './formulaire-détachement.yaml'
|
||||
import { usePersistingState } from 'Components/utils/persistState'
|
||||
import { hash } from '../../../../../utils'
|
||||
import Overlay from 'Components/Overlay'
|
||||
import { useDebounce } from 'Components/utils'
|
||||
import Engine from 'publicodes'
|
||||
import React, { Suspense, useCallback } from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
export default function FormulaireDétachementIndépendant() {
|
||||
import { hash } from '../../../../../utils'
|
||||
import formulaire from './formulaire-détachement.yaml'
|
||||
import { Explicable } from 'Components/conversation/Explicable'
|
||||
|
||||
const LazyEndBlock = React.lazy(() => import('./EndBlock'))
|
||||
|
||||
export default function formulaireMobilitéIndépendant() {
|
||||
const engine = new Engine(formulaire)
|
||||
return (
|
||||
<>
|
||||
|
@ -25,6 +27,19 @@ export default function FormulaireDétachementIndépendant() {
|
|||
</a>
|
||||
.
|
||||
</p>
|
||||
<blockquote>
|
||||
<p className="ui__ lead">
|
||||
<strong>Ce document nécessite votre signature {emoji('✍️')}</strong>
|
||||
</p>
|
||||
<p>
|
||||
Nous vous suggérons d'utiliser un appareil avec écran tactile pour
|
||||
compléter ce formulaire (téléphone, tablette, etc.).{' '}
|
||||
</p>
|
||||
<p>
|
||||
Autremenent, il vous faudra imprimer, signer et scanner le document
|
||||
généré.
|
||||
</p>
|
||||
</blockquote>
|
||||
<FormulairePublicodes engine={engine} />
|
||||
</>
|
||||
)
|
||||
|
@ -33,7 +48,12 @@ export default function FormulaireDétachementIndépendant() {
|
|||
const useFields = (engine: Engine<string>, fieldNames: Array<string>) => {
|
||||
const fields = fieldNames
|
||||
.map(name => engine.evaluate(name))
|
||||
.filter(node => node.isApplicable !== false && node.isApplicable !== null)
|
||||
.filter(
|
||||
node =>
|
||||
node.isApplicable !== false &&
|
||||
node.isApplicable !== null &&
|
||||
(node.question || node.type || node.API)
|
||||
)
|
||||
return fields
|
||||
}
|
||||
|
||||
|
@ -79,20 +99,21 @@ function FormulairePublicodes({ engine }) {
|
|||
`}
|
||||
>
|
||||
{field.question ? (
|
||||
<div
|
||||
<span
|
||||
css={`
|
||||
margin-top: 0.6rem;
|
||||
`}
|
||||
>
|
||||
{field.question}
|
||||
</div>
|
||||
</span>
|
||||
) : (
|
||||
<small>{field.title}</small>
|
||||
)}{' '}
|
||||
{field.description && (
|
||||
<InfoBulle>
|
||||
<Explicable>
|
||||
<h3>{field.title}</h3>
|
||||
<Markdown source={field.description} />
|
||||
</InfoBulle>
|
||||
</Explicable>
|
||||
)}
|
||||
<RuleInput
|
||||
dottedName={field.dottedName}
|
||||
|
@ -104,45 +125,9 @@ function FormulairePublicodes({ engine }) {
|
|||
)}
|
||||
</Animate.fromTop>
|
||||
))}
|
||||
<LazyPDFButton
|
||||
className="ui__ plain cta button"
|
||||
fields={fields}
|
||||
disabled={isMissingValues}
|
||||
>
|
||||
Générer la demande
|
||||
</LazyPDFButton>
|
||||
{isMissingValues && (
|
||||
<p className="ui__ notice">
|
||||
Vous devez compléter l'intégralité du formulaire pour générer la
|
||||
demande.{' '}
|
||||
</p>
|
||||
)}
|
||||
<Suspense fallback={null}>
|
||||
<LazyEndBlock fields={fields} isMissingValues={isMissingValues} />
|
||||
</Suspense>
|
||||
</Animate.fromTop>
|
||||
)
|
||||
}
|
||||
|
||||
const LazyPDFDownloadLink = React.lazy(() => import('./FormPDF'))
|
||||
function LazyPDFButton({ fields, className, disabled, children }) {
|
||||
const fieldsDebounced = useDebounce(fields.slice(1), 1000)
|
||||
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<button className={className} disabled>
|
||||
{children}
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<LazyPDFDownloadLink
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
fields={fieldsDebounced}
|
||||
fileName={'demande-détachement.pdf'}
|
||||
title={'Demande de mobilité en Europe'}
|
||||
description="Afin d’examiner votre situation au regard des règlements communautaires UE/EEE de Sécurité sociale (CE 883/2004), veuillez envoyer ce document à relations.internationales@urssaf.fr"
|
||||
>
|
||||
{children}
|
||||
</LazyPDFDownloadLink>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
import {
|
||||
Document,
|
||||
Page,
|
||||
Text,
|
||||
View,
|
||||
StyleSheet,
|
||||
Font,
|
||||
PDFViewer,
|
||||
PDFDownloadLink
|
||||
} from '@react-pdf/renderer'
|
||||
import React from 'react'
|
||||
import robotoUrl from './Roboto-Regular.ttf'
|
||||
import montserratUrl from './Montserrat-SemiBold.ttf'
|
||||
import { formatValue } from 'publicodes'
|
||||
import emoji from 'react-easy-emoji'
|
||||
|
||||
// function FormPDFViewer({ fields, title, description }) {
|
||||
// return (
|
||||
// <div
|
||||
// css={`
|
||||
// width: 100%;
|
||||
// padding-bottom: 141.4%;
|
||||
// position: relative;
|
||||
// `}
|
||||
// >
|
||||
// <PDFViewer
|
||||
// css={`
|
||||
// position: absolute;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// top: 0;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// `}
|
||||
// >
|
||||
// <FormPDF fields={fields} title={title} description={description} />
|
||||
// </PDFViewer>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
export default function FormPDFDownloadLink({
|
||||
fields,
|
||||
title,
|
||||
className,
|
||||
description,
|
||||
fileName,
|
||||
disabled,
|
||||
children
|
||||
}) {
|
||||
if (disabled) {
|
||||
return (
|
||||
<button disabled className={className}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<PDFDownloadLink
|
||||
document={
|
||||
<FormPDF fields={fields} title={title} description={description} />
|
||||
}
|
||||
fileName={fileName}
|
||||
className={className}
|
||||
>
|
||||
{({ blob, url, loading, error }) => children}
|
||||
</PDFDownloadLink>
|
||||
)
|
||||
// <PDFDownloadLink document={<MyDoc />} fileName="somename.pdf">
|
||||
// {({ blob, url, loading, error }) => (loading ? 'Loading document...' : 'Download now!')}
|
||||
// </PDFDownloadLink>
|
||||
}
|
||||
|
||||
function FormPDF({ fields, title, description }) {
|
||||
return (
|
||||
<Document>
|
||||
<Page style={styles.body}>
|
||||
<View>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
<Text style={styles.description}>{description}</Text>
|
||||
</View>
|
||||
{fields.map(field => (
|
||||
<View style={styles.field} key={field.dottedName} wrap={false}>
|
||||
{field.type === 'groupe' ? (
|
||||
<>
|
||||
<Text style={styles.subtitle}>
|
||||
{field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.name}>
|
||||
{field.question ?? field.title}{' '}
|
||||
{field.note && (
|
||||
<Text style={styles.fieldNumber}>({field.note})</Text>
|
||||
)}
|
||||
</Text>
|
||||
{field.nodeValue != null && (
|
||||
<Text style={styles.value}>{formatValue(field)}</Text>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</Page>
|
||||
</Document>
|
||||
)
|
||||
}
|
||||
|
||||
Font.register({
|
||||
family: 'Roboto',
|
||||
src: robotoUrl
|
||||
})
|
||||
|
||||
Font.register({
|
||||
family: 'Montserrat',
|
||||
src: montserratUrl
|
||||
})
|
||||
const styles = StyleSheet.create({
|
||||
body: {
|
||||
paddingTop: 35,
|
||||
color: '#18457B',
|
||||
lineHeight: 1.5,
|
||||
paddingBottom: 65,
|
||||
paddingHorizontal: 35
|
||||
},
|
||||
fieldNumber: {
|
||||
opacity: 0.7
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
marginBottom: 20,
|
||||
fontFamily: 'Montserrat'
|
||||
},
|
||||
description: {
|
||||
fontFamily: 'Roboto',
|
||||
marginBottom: 12,
|
||||
fontSize: 14
|
||||
},
|
||||
subtitle: {
|
||||
paddingTop: 10,
|
||||
fontFamily: 'Montserrat',
|
||||
fontSize: 16
|
||||
},
|
||||
field: {
|
||||
marginBottom: 12,
|
||||
lineHeight: 1.2
|
||||
},
|
||||
name: {
|
||||
fontSize: 11,
|
||||
marginBottom: 4,
|
||||
opacity: 0.7,
|
||||
fontFamily: 'Roboto'
|
||||
},
|
||||
value: {
|
||||
fontSize: 14,
|
||||
fontFamily: 'Roboto'
|
||||
}
|
||||
})
|
|
@ -1,212 +0,0 @@
|
|||
coordonnées assuré:
|
||||
titre: Vos coordonnées
|
||||
type: groupe
|
||||
formule: oui
|
||||
note: 1
|
||||
coordonnées assuré . nom:
|
||||
type: texte
|
||||
note: 1.2
|
||||
coordonnées assuré . prénoms:
|
||||
type: texte
|
||||
note: 1.3
|
||||
coordonnées assuré . nationalité:
|
||||
type: texte
|
||||
note: 1.6
|
||||
coordonnées assuré . date de naissance:
|
||||
type: date
|
||||
note: 1.5
|
||||
coordonnées assuré . commune de naissance:
|
||||
API: commune
|
||||
note: 1.7
|
||||
coordonnées assuré . numéro de sécurité sociale:
|
||||
type: texte
|
||||
note: 1.1
|
||||
coordonnées assuré . organisme urssaf:
|
||||
type: texte
|
||||
description: >
|
||||
Nom de l'organisme Urssaf dont vous relevez en france
|
||||
|
||||
coordonnées assuré . domicile personnel:
|
||||
type: groupe
|
||||
formule: oui
|
||||
note: 1.8
|
||||
coordonnées assuré . domicile personnel . adresse:
|
||||
type: texte
|
||||
note: 1.9.1
|
||||
coordonnées assuré . domicile personnel . commune:
|
||||
API: commune
|
||||
note: 1.9.2 / 1.9.3
|
||||
|
||||
activité france:
|
||||
note: 4 / 4.1.2
|
||||
type: groupe
|
||||
formule: oui
|
||||
titre: Votre activité indépendante en France
|
||||
activité france . SIREN:
|
||||
note: 4.2
|
||||
type: texte
|
||||
activité france . nom:
|
||||
note: 4.3
|
||||
titre: Nom de l'entreprise
|
||||
type: texte
|
||||
activité france . adresse:
|
||||
note: 4.4.1
|
||||
type: texte
|
||||
activité france . commune:
|
||||
API: commune
|
||||
note:: 4.4.3 / 4.4.4
|
||||
|
||||
demande:
|
||||
question: Quelle demande souhaitez-vous effectuer ?
|
||||
formule:
|
||||
une possibilité:
|
||||
choix obligatoire: oui
|
||||
possibilités:
|
||||
- détachement
|
||||
- activité transfrontalière simultanée
|
||||
|
||||
demande . détachement:
|
||||
note: 3.3
|
||||
type: groupe
|
||||
formule: oui
|
||||
applicable si: demande = 'détachement'
|
||||
description: >
|
||||
Vous vous détachez temporairement dans un autre état (pays) de l’union
|
||||
europeenne (ou eee ou suisse).
|
||||
|
||||
demande . détachement . infrastructure sauvegardée:
|
||||
question: >
|
||||
Pendant votre travail en dehors de la France, l'infrastructure de votre
|
||||
entreprise en France, qui est nécessaire pour poursuivre votre travail en
|
||||
France après votre retour, reste-elle en place ?
|
||||
|
||||
demande . détachement . activité identique:
|
||||
question: >
|
||||
L'activité que vous exercez temporairement dans un autre Etat membre
|
||||
est-elle la même que celle que vous exercez en France ?
|
||||
|
||||
demande . détachement . activité:
|
||||
titre: Activité non salarié dans le pays étranger
|
||||
type: groupe
|
||||
formule: oui
|
||||
demande . détachement . activité . nature:
|
||||
titre: Nature de l'activité
|
||||
type: texte
|
||||
applicable si: activité identique = non
|
||||
description: >
|
||||
La nature de l’activité pendant la période de détachement (ex: plombier)
|
||||
demande . détachement . activité . localisation:
|
||||
type: groupe
|
||||
formule: oui
|
||||
demande . détachement . activité . localisation . pays:
|
||||
type: texte
|
||||
demande . détachement . activité . localisation . adresse:
|
||||
type: texte
|
||||
demande . détachement . activité . localisation . code postal:
|
||||
type: texte
|
||||
demande . détachement . activité . localisation . commune:
|
||||
type: texte
|
||||
demande . détachement . période:
|
||||
type: groupe
|
||||
formule: oui
|
||||
titre: Période du détachement
|
||||
demande . détachement . période . date de début:
|
||||
type: date
|
||||
demande . détachement . période . date de fin:
|
||||
type: date
|
||||
|
||||
demande . activité transfrontalière simultanée:
|
||||
note: 3.4
|
||||
applicable si: demande = 'activité transfrontalière simultanée'
|
||||
type: groupe
|
||||
formule: oui
|
||||
description: >
|
||||
Vous êtes actif en tant qu'indépendant en France et travaillez également
|
||||
dans un ou plusieurs autres Etats membres de l'Espace Economique Européen
|
||||
|
||||
demande . activité transfrontalière simultanée . indépendant hors France:
|
||||
question: >
|
||||
Êtes-vous actif en tant qu'indépendant dans un pays autre que la France ?
|
||||
|
||||
demande . activité transfrontalière simultanée . pourcentage en France:
|
||||
applicable si: indépendant hors France
|
||||
question: >
|
||||
Est-ce que votre activité en France représente au moins 25% de votre
|
||||
temps de travail global et/ou de votre rémunération / revenu ?
|
||||
|
||||
demande . activité transfrontalière simultanée . nombre pays:
|
||||
unité: pays
|
||||
applicable si: indépendant hors France
|
||||
question: >
|
||||
Dans combien de pays (autre que la France) exercez vous une activité
|
||||
non salariée ?
|
||||
contrôles:
|
||||
- si: nombre pays > 3
|
||||
message: >
|
||||
Ce formulaire ne permet pas de déclarer une activité dans plus de
|
||||
3 pays
|
||||
niveau: avertissement
|
||||
- si: nombre pays < 1
|
||||
message: >
|
||||
Vous devez déclarer un pays au moins
|
||||
niveau: avertissement
|
||||
|
||||
demande . activité transfrontalière simultanée . pays 1:
|
||||
type: groupe
|
||||
applicable si: nombre pays >= 1
|
||||
formule: oui
|
||||
demande . activité transfrontalière simultanée . pays 1 . pays:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 1 . adresse:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 1 . code postal:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 1 . commune:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 1 . date de début:
|
||||
titre: date de début d'activité dans ce pays
|
||||
type: date
|
||||
demande . activité transfrontalière simultanée . pays 1 . pourcentage:
|
||||
question: >
|
||||
Est-ce que votre activité dans ce pays représente au moins 25% de votre
|
||||
temps de travail global et/ou de votre rémunération / revenu ?
|
||||
|
||||
demande . activité transfrontalière simultanée . pays 2:
|
||||
type: groupe
|
||||
applicable si: nombre pays >= 2
|
||||
formule: oui
|
||||
demande . activité transfrontalière simultanée . pays 2 . pays:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 2 . adresse:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 2 . code postal:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 2 . commune:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 2 . date de début:
|
||||
titre: date de début d'activité dans ce pays
|
||||
type: date
|
||||
demande . activité transfrontalière simultanée . pays 2 . pourcentage:
|
||||
question: >
|
||||
Est-ce que votre activité dans ce pays représente au moins 25% de votre
|
||||
temps de travail global et/ou de votre rémunération / revenu ?
|
||||
|
||||
demande . activité transfrontalière simultanée . pays 3:
|
||||
type: groupe
|
||||
applicable si: nombre pays >= 3
|
||||
formule: oui
|
||||
demande . activité transfrontalière simultanée . pays 3 . pays:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 3 . adresse:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 3 . code postal:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 3 . commune:
|
||||
type: texte
|
||||
demande . activité transfrontalière simultanée . pays 3 . date de début:
|
||||
titre: date de début d'activité dans ce pays
|
||||
type: date
|
||||
demande . activité transfrontalière simultanée . pays 3 . pourcentage:
|
||||
question: >
|
||||
Est-ce que votre activité dans ce pays représente au moins 25% de votre
|
||||
temps de travail global et/ou de votre rémunération / revenu ?
|
|
@ -45,7 +45,7 @@ const infereRégimeFromCompanyDetails = (company: Company | null) => {
|
|||
}
|
||||
|
||||
export default function SocialSecurity() {
|
||||
const { t } = useTranslation()
|
||||
const { t, i18n } = useTranslation()
|
||||
const company = useSelector(
|
||||
(state: RootState) => state.inFranceApp.existingCompany
|
||||
)
|
||||
|
@ -189,6 +189,22 @@ export default function SocialSecurity() {
|
|||
}
|
||||
`}
|
||||
>
|
||||
{régime === 'indépendant' &&
|
||||
i18n.language === 'fr' &&
|
||||
process.env.HEAD !== 'master' && (
|
||||
<Link
|
||||
className="ui__ interactive card button-choice lighter-bg"
|
||||
to={sitePaths.gérer.formulaireMobilité}
|
||||
>
|
||||
<Trans i18nKey="gérer.ressources.embaucher">
|
||||
<p>Exporter son activité en Europe</p>
|
||||
<p className="ui__ notice">
|
||||
Le formulaire pour effectuer une demande de mobilité en
|
||||
Europe (détachement ou pluriactivité)
|
||||
</p>
|
||||
</Trans>
|
||||
</Link>
|
||||
)}
|
||||
{!company?.isAutoEntrepreneur && (
|
||||
<Link
|
||||
className="ui__ interactive card button-choice lighter-bg"
|
||||
|
|
|
@ -8,7 +8,7 @@ import AideDéclarationIndépendant from './AideDéclarationIndépendant/index'
|
|||
import Embaucher from './Embaucher'
|
||||
import Home from './Home'
|
||||
import SécuritéSociale from './SécuritéSociale'
|
||||
import FormulaireDétachementIndépendant from './DétachementIndépendant'
|
||||
import formulaireMobilitéIndépendant from './DemandeMobilite'
|
||||
|
||||
export default function Gérer() {
|
||||
const sitePaths = useContext(SitePathsContext)
|
||||
|
@ -38,8 +38,8 @@ export default function Gérer() {
|
|||
/>
|
||||
<Route
|
||||
exact
|
||||
path={sitePaths.gérer.formulaireDétachement}
|
||||
component={FormulaireDétachementIndépendant}
|
||||
path={sitePaths.gérer.formulaireMobilité}
|
||||
component={formulaireMobilitéIndépendant}
|
||||
/>
|
||||
</Switch>
|
||||
</>
|
||||
|
|
|
@ -7,6 +7,7 @@ import SimulateurArtisteAuteur from '../Simulateurs/ArtisteAuteur'
|
|||
import SimulateurAssimiléSalarié from '../Simulateurs/AssimiléSalarié'
|
||||
import SimulateurAutoEntrepreneur from '../Simulateurs/AutoEntrepreneur'
|
||||
import SimulateurIndépendant from '../Simulateurs/Indépendant'
|
||||
import DemandeMobilite from '../Gérer/DemandeMobilite'
|
||||
import IframeFooter from './IframeFooter'
|
||||
import SimulateurEmbauche from './SimulateurEmbauche'
|
||||
|
||||
|
@ -42,6 +43,8 @@ export default function Iframes() {
|
|||
path="/iframes/simulateur-chomage-partiel"
|
||||
component={SimulateurChômagePartiel}
|
||||
/>
|
||||
<Route path="/iframes/demande-mobilite" component={DemandeMobilite} />
|
||||
|
||||
{inIframe() && <IframeFooter />}
|
||||
</div>
|
||||
</IsEmbeddedContext.Provider>
|
||||
|
|
|
@ -48,10 +48,8 @@ function IntegrationCustomizer() {
|
|||
margin: auto;
|
||||
|
||||
.ui__.left-side {
|
||||
width: 40%;
|
||||
padding-right: 25px;
|
||||
margin-right: 35px;
|
||||
border-right: 2px solid var(--lighterColor);
|
||||
width: 30%;
|
||||
padding-right: 1rem;
|
||||
|
||||
select {
|
||||
padding: 7px;
|
||||
|
@ -59,7 +57,8 @@ function IntegrationCustomizer() {
|
|||
}
|
||||
|
||||
.ui__.right-side {
|
||||
width: 60%;
|
||||
width: 70%;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
|
@ -127,14 +126,23 @@ function IntegrationCustomizer() {
|
|||
<h3>
|
||||
<Trans>Prévisualisation</Trans>
|
||||
</h3>
|
||||
<MemoryRouter
|
||||
key={currentModule}
|
||||
initialEntries={[`/iframes/${currentModule}`]}
|
||||
<div
|
||||
css={`
|
||||
background-color: white;
|
||||
border: 2px solid var(--lighterColor);
|
||||
border-radius: 0.3rem;
|
||||
padding: 1rem;
|
||||
`}
|
||||
>
|
||||
<ThemeColorsProvider color={color}>
|
||||
<Iframes />
|
||||
</ThemeColorsProvider>
|
||||
</MemoryRouter>
|
||||
<MemoryRouter
|
||||
key={currentModule}
|
||||
initialEntries={[`/iframes/${currentModule}`]}
|
||||
>
|
||||
<ThemeColorsProvider color={color}>
|
||||
<Iframes />
|
||||
</ThemeColorsProvider>
|
||||
</MemoryRouter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -45,7 +45,7 @@ const sitePathsFr = {
|
|||
embaucher: '/embaucher',
|
||||
sécuritéSociale: '/sécurité-sociale',
|
||||
déclarationIndépendant: '/aide-declaration-independants',
|
||||
formulaireDétachement: '/demande-détachement'
|
||||
formulaireMobilité: '/demande-mobilité'
|
||||
},
|
||||
simulateurs: {
|
||||
index: '/simulateurs',
|
||||
|
@ -95,7 +95,7 @@ const sitePathsEn = {
|
|||
embaucher: '/hiring',
|
||||
sécuritéSociale: '/social-security',
|
||||
déclarationIndépendant: '/declaration-aid-independent',
|
||||
formulaireDétachement: '/posting-demand'
|
||||
formulaireMobilité: '/posting-demand'
|
||||
},
|
||||
simulateurs: {
|
||||
index: '/simulators',
|
||||
|
|
|
@ -148,7 +148,7 @@ export function formatValue(
|
|||
return '-'
|
||||
}
|
||||
return typeof nodeValue === 'string'
|
||||
? capitalise0(nodeValue)
|
||||
? capitalise0(nodeValue.replace('\\n', '\n'))
|
||||
: typeof value === 'object' && 'API' in value && value.API === 'commune'
|
||||
? formatCommune(nodeValue as Commune)
|
||||
: typeof nodeValue === 'object'
|
||||
|
|
|
@ -16,7 +16,7 @@ const moo = require("moo");
|
|||
|
||||
const dateRegexp = `(?:(?:0?[1-9]|[12][0-9]|3[01])\\/)?(?:0?[1-9]|1[012])\\/\\d{4}`
|
||||
const letter = '[a-zA-Z\u00C0-\u017F€$%]';
|
||||
const letterOrNumber = '[a-zA-Z\u00C0-\u017F0-9\']';
|
||||
const letterOrNumber = '[a-zA-Z\u00C0-\u017F0-9\'°]';
|
||||
const word = `${letter}(?:[-']?${letterOrNumber}+)*`;
|
||||
const wordOrNumber = `(?:${word}|${letterOrNumber}+)`
|
||||
const words = `${word}(?:[\\s]?${wordOrNumber}+)*`
|
||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -847,6 +847,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.9.6":
|
||||
version "7.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.3.tgz#670d002655a7c366540c67f6fd3342cd09500364"
|
||||
integrity sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
||||
version "7.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
||||
|
@ -9992,6 +9999,17 @@ react-side-effect@^1.1.0:
|
|||
dependencies:
|
||||
shallowequal "^1.0.1"
|
||||
|
||||
react-signature-pad-wrapper@^1.2.11:
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/react-signature-pad-wrapper/-/react-signature-pad-wrapper-1.2.11.tgz#fb017012611e2e5c09b5210d35d56c1180f19137"
|
||||
integrity sha512-SPa5LtK6K9gvaAUx1w7Qek8U1GTeSv4mGWqZUpj7bDCzAKkM4g4XIXx6V89odV8VGVWHjjllRzkcFM6talmOsA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.6"
|
||||
prop-types "^15.7.2"
|
||||
react "^16.13.1"
|
||||
signature_pad "^2.3.2"
|
||||
throttle-debounce "^2.1.0"
|
||||
|
||||
react-smooth@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.5.tgz#94ae161d7951cdd893ccb7099d031d342cb762ad"
|
||||
|
@ -10927,6 +10945,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
signature_pad@^2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/signature_pad/-/signature_pad-2.3.2.tgz#ca7230021c89cedeead27b33d8d16ff254e5f04a"
|
||||
integrity sha512-peYXLxOsIY6MES2TrRLDiNg2T++8gGbpP2yaC+6Ohtxr+a2dzoaqWosWDY9sWqTAAk6E/TyQO+LJw9zQwyu5kA==
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6"
|
||||
|
@ -11672,6 +11695,11 @@ throat@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
|
||||
integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=
|
||||
|
||||
throttle-debounce@^2.1.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.2.1.tgz#fbd933ae6793448816f7d5b3cae259d464c98137"
|
||||
integrity sha512-i9hAVld1f+woAiyNGqWelpDD5W1tpMroL3NofTz9xzwq6acWBlO2dC8k5EFSZepU6oOINtV5Q3aSPoRg7o4+fA==
|
||||
|
||||
throttleit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
|
||||
|
|
Loading…
Reference in New Issue