Première implémentation des sous activités location meublée
parent
a39674c37a
commit
41593cc619
|
@ -5,11 +5,12 @@ import React, { useState, useContext } from 'react'
|
|||
import emoji from 'react-easy-emoji'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Animate from 'Ui/animate'
|
||||
import activités from './activités.yaml'
|
||||
import { flatActivités } from './reducers'
|
||||
import { createMarkdownDiv } from 'Engine/marked'
|
||||
import { StoreContext } from './StoreContext'
|
||||
import { NextButton } from './ActivitésSelection'
|
||||
import Exonérations from './Exonérations'
|
||||
import { MultiItemSelection } from './ActivitésSelection'
|
||||
|
||||
let allTrue = list => list && all(item => item === true)(list)
|
||||
|
||||
|
@ -23,8 +24,34 @@ export default withSitePaths(function LocationMeublée({
|
|||
state: { selectedActivities, activityAnswers },
|
||||
dispatch
|
||||
} = useContext(StoreContext),
|
||||
data = activités.find(({ titre }) => titre === title),
|
||||
answers = activityAnswers[title] || {}
|
||||
data = flatActivités.find(({ titre }) => titre === title)
|
||||
|
||||
if (data.activités) {
|
||||
return (
|
||||
<>
|
||||
<p>Sélectionnez un ou plusieurs types de location meublée</p>
|
||||
<MultiItemSelection
|
||||
{...{
|
||||
items: data.activités,
|
||||
selectedActivities,
|
||||
activityAnswers,
|
||||
dispatch,
|
||||
buttonAttributes: {
|
||||
currentActivité: title,
|
||||
action: () =>
|
||||
dispatch({
|
||||
type: 'UPDATE_ACTIVITY',
|
||||
title,
|
||||
data: { completed: true }
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
let answers = activityAnswers[title] || {}
|
||||
|
||||
return (
|
||||
<section>
|
||||
|
@ -101,22 +128,18 @@ export default withSitePaths(function LocationMeublée({
|
|||
</form>
|
||||
</>
|
||||
)}
|
||||
{JSON.stringify(data)}
|
||||
{JSON.stringify(answers)}
|
||||
<NextButton
|
||||
{...{
|
||||
activityAnswers,
|
||||
selectedActivities,
|
||||
disabled: incompleteActivity(data, answers),
|
||||
currentActivité: title,
|
||||
action :
|
||||
()=>
|
||||
dispatch({
|
||||
type: 'UPDATE_ACTIVITY',
|
||||
title,
|
||||
data: { ...answers, completed: true }
|
||||
})
|
||||
|
||||
currentActivité: title,
|
||||
action: () =>
|
||||
dispatch({
|
||||
type: 'UPDATE_ACTIVITY',
|
||||
title,
|
||||
data: { ...answers, completed: true }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Animate.fromBottom>
|
||||
|
|
|
@ -11,8 +11,10 @@ import { StoreContext } from './StoreContext'
|
|||
import { incompleteActivity } from './Activité'
|
||||
|
||||
export default (function ActivitésSelection() {
|
||||
let { state, dispatch } = useContext(StoreContext)
|
||||
let { selectedActivities } = state
|
||||
let {
|
||||
state: { selectedActivities, activityAnswers },
|
||||
dispatch
|
||||
} = useContext(StoreContext)
|
||||
|
||||
return (
|
||||
<Animate.fromBottom>
|
||||
|
@ -23,8 +25,87 @@ export default (function ActivitésSelection() {
|
|||
d'activité que vous exercez. Sélectionnez toutes les plateformes depuis
|
||||
lesquelles vous avez reçu de l'argent durant l'année.
|
||||
</p>
|
||||
<ul
|
||||
<MultiItemSelection
|
||||
{...{
|
||||
items: activités,
|
||||
selectedActivities,
|
||||
activityAnswers,
|
||||
dispatch
|
||||
}}
|
||||
/>
|
||||
</Animate.fromBottom>
|
||||
)
|
||||
})
|
||||
|
||||
export let MultiItemSelection = ({
|
||||
items,
|
||||
selectedActivities,
|
||||
activityAnswers,
|
||||
dispatch,
|
||||
buttonAttributes
|
||||
}) => (
|
||||
<>
|
||||
<ul css={multiCardSelectionStyle}>
|
||||
{items.map(({ titre, plateformes, icônes }) => {
|
||||
let selected = selectedActivities.includes(titre)
|
||||
return (
|
||||
<li
|
||||
className={classnames('ui__ card ', { selected })}
|
||||
key={titre}
|
||||
onClick={() => dispatch({ type: 'SELECT_ACTIVITY', titre })}>
|
||||
<div className="title">{titre}</div>
|
||||
{emoji(icônes)}
|
||||
<p>{plateformes.join(', ')}</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<p css="text-align: right">
|
||||
<NextButton
|
||||
{...{
|
||||
activityAnswers,
|
||||
selectedActivities,
|
||||
disabled: !selectedActivities.length,
|
||||
...buttonAttributes
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
|
||||
export let NextButton = withSitePaths(
|
||||
({
|
||||
sitePaths,
|
||||
activityAnswers,
|
||||
selectedActivities,
|
||||
disabled,
|
||||
currentActivité,
|
||||
action
|
||||
}) => {
|
||||
let nextActivity = without([currentActivité], selectedActivities).find(
|
||||
a => !activityAnswers[a].completed
|
||||
),
|
||||
to = nextActivity
|
||||
? sitePaths.économieCollaborative.activités.index + '/' + nextActivity
|
||||
: sitePaths.économieCollaborative.votreSituation
|
||||
|
||||
return (
|
||||
<Link
|
||||
css={`
|
||||
margin-top: 1rem;
|
||||
`}
|
||||
to={to}
|
||||
onClick={action}
|
||||
className={classnames('ui__ plain button', {
|
||||
disabled
|
||||
})}>
|
||||
Continuer
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
let multiCardSelectionStyle = `
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
flex-wrap: wrap;
|
||||
|
@ -64,62 +145,4 @@ export default (function ActivitésSelection() {
|
|||
|
||||
}
|
||||
}
|
||||
`}>
|
||||
{activités.map(({ titre, plateformes, icônes }) => {
|
||||
let selected = selectedActivities.includes(titre)
|
||||
return (
|
||||
<li
|
||||
className={classnames('ui__ card ', { selected })}
|
||||
key={titre}
|
||||
onClick={() => dispatch({ type: 'SELECT_ACTIVITY', titre })}>
|
||||
<div className="title">{titre}</div>
|
||||
{emoji(icônes)}
|
||||
<p>{plateformes.join(', ')}</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<p css="text-align: right">
|
||||
<NextButton
|
||||
{...{
|
||||
activityAnswers: state.activityAnswers,
|
||||
selectedActivities,
|
||||
disabled: !selectedActivities.length
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Animate.fromBottom>
|
||||
)
|
||||
})
|
||||
|
||||
export let NextButton = withSitePaths(
|
||||
({
|
||||
sitePaths,
|
||||
activityAnswers,
|
||||
selectedActivities,
|
||||
disabled,
|
||||
currentActivité,
|
||||
action
|
||||
}) => {
|
||||
let nextActivity = without([currentActivité], selectedActivities).find(
|
||||
a => !activityAnswers[a].completed
|
||||
),
|
||||
to = nextActivity
|
||||
? sitePaths.économieCollaborative.activités.index + '/' + nextActivity
|
||||
: sitePaths.économieCollaborative.votreSituation
|
||||
|
||||
return (
|
||||
<Link
|
||||
css={`
|
||||
margin-top: 1rem;
|
||||
`}
|
||||
to={to}
|
||||
onClick={action}
|
||||
className={classnames('ui__ plain button', {
|
||||
disabled
|
||||
})}>
|
||||
Continuer
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
)
|
||||
`
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
- titre: Location meublée
|
||||
- titre: Locations meublées
|
||||
icônes: 🏠 🛋
|
||||
explication: |
|
||||
Vous avez loué un logement meublé pour de courtes durées à une clientèle de passage qui n'y élit pas domicile (hors location de chambres d’hôtes et de meublé de tourisme)
|
||||
|
||||
explication: Vous avez loué un logement meublé pour de courtes durées
|
||||
plateformes:
|
||||
- Airbnb
|
||||
- Abritel
|
||||
- chambre d'hôte
|
||||
- tourisme classé
|
||||
|
||||
seuil pro: 23000
|
||||
exonérations:
|
||||
# https://www.pap.fr/bailleur/impots-taxes/location-meublee-quelle-fiscalite/a1787/location-meublee-les-exonerations
|
||||
- titre: Il s'agit d'une pièce dans ma résidence principale
|
||||
explication: |
|
||||
Vous donnez en location une ou plusieurs pièces de votre habitation principale : vous êtes exonéré d'impôt sous réserve que la ou les pièces louée(s) constituent la résidence principale du locataire ou sa résidence temporaire. Pour bénéficier de cette dernière condition, le locataire doit pouvoir justifier d'un contrat de travail saisonnier ou d'un CDD d'usage. Le prix de la location doit être fixé dans des limites raisonnables. Pour les baux conclus en 2019, le loyer ne doit pas dépasser 187 € par an et par mètre carré en Île-de-France et 138 € par mètre carré et par an dans les autres régions. Ces plafonds sont relevés au 1er janvier de chaque année.
|
||||
> Vous louez à Paris, dans votre résidence principale, une chambre de 18 m² à un étudiant. Pour être exonéré d'impôt, le loyer mensuel hors charges ne doit pas dépasser en 2019 (187 x 18) / 12 = 280 €.
|
||||
activités:
|
||||
- titre: Location meublée
|
||||
icônes: 🏠 🛋
|
||||
explication: Vous avez loué un logement meublé pour de courtes durées à une clientèle de passage qui n'y élit pas domicile. Ce n'est pas une chambre d’hôte ni un meublé de tourisme
|
||||
plateformes:
|
||||
- Airbnb
|
||||
- Abritel
|
||||
seuil pro: 23000
|
||||
exonérations:
|
||||
# https://www.pap.fr/bailleur/impots-taxes/location-meublee-quelle-fiscalite/a1787/location-meublee-les-exonerations
|
||||
- titre: Il s'agit d'une pièce dans ma résidence principale
|
||||
explication: |
|
||||
Vous donnez en location une ou plusieurs pièces de votre habitation principale : vous êtes exonéré d'impôt sous réserve que la ou les pièces louée(s) constituent la résidence principale du locataire ou sa résidence temporaire. Pour bénéficier de cette dernière condition, le locataire doit pouvoir justifier d'un contrat de travail saisonnier ou d'un CDD d'usage. Le prix de la location doit être fixé dans des limites raisonnables. Pour les baux conclus en 2019, le loyer ne doit pas dépasser 187 € par an et par mètre carré en Île-de-France et 138 € par mètre carré et par an dans les autres régions. Ces plafonds sont relevés au 1er janvier de chaque année.
|
||||
> Vous louez à Paris, dans votre résidence principale, une chambre de 18 m² à un étudiant. Pour être exonéré d'impôt, le loyer mensuel hors charges ne doit pas dépasser en 2019 (187 x 18) / 12 = 280 €.
|
||||
|
||||
# TODO implémenter ces autres possibilités de la location meublée
|
||||
autres possibilités:
|
||||
- titre: location de logement meublé de tourisme classé
|
||||
- titre: Location meublé de tourisme classé
|
||||
explication: Le bien doit avoir préalablement fait l’objet d’une déclaration auprès de la commune où il est situé. A l’issue de cette procédure, un numéro de déclaration (13 caractères) est délivré. Tout changement fait l'objet d'une nouvelle déclaration.
|
||||
icônes: 🏠 🛋⭐
|
||||
plateformes:
|
||||
- A PRECISER
|
||||
seuil pro: 23000
|
||||
- titre: location de chambre d'hôtes
|
||||
- titre: Location de chambre d'hôtes
|
||||
explication: Vous êtes loueur de chambres d’hôtes et remplissez les conditions prévues par le code du tourisme
|
||||
plateformes:
|
||||
- A PRECISER
|
||||
icônes: 🏠 🛋🧑
|
||||
seuil pro: 5268
|
||||
seuil déclaration: 760
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { without, map, pipe, mergeAll } from 'ramda'
|
||||
import data from './activités.yaml'
|
||||
import { without, map, pipe, mergeAll, unnest } from 'ramda'
|
||||
import activités from './activités.yaml'
|
||||
export let flatActivités = pipe(
|
||||
map(a => (a.activités ? [a, ...a.activités] : [a])),
|
||||
unnest
|
||||
)(activités)
|
||||
|
||||
let initialState = {
|
||||
selectedActivities: [],
|
||||
|
@ -10,9 +14,8 @@ let initialState = {
|
|||
: {}
|
||||
})),
|
||||
mergeAll
|
||||
)(data)
|
||||
)(flatActivités)
|
||||
}
|
||||
console.log(initialState)
|
||||
|
||||
let reducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
|
|
Loading…
Reference in New Issue