From 0f05fa76df83e1a0708697e3e7672e9318235144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Rialland?= Date: Tue, 13 Jun 2023 16:07:16 +0200 Subject: [PATCH] =?UTF-8?q?Am=C3=A9liore=20les=20Tag,=20termine=20le=20sty?= =?UTF-8?q?le=20des=20statuts=20disponibles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/source/components/StatusTag.tsx | 105 ++++++++++++++ site/source/components/layout/Header.tsx | 2 +- site/source/design-system/icons/index.tsx | 50 +++++-- .../design-system/tag/index.stories.tsx | 48 +++++++ site/source/design-system/tag/index.tsx | 117 +++++++++++---- site/source/design-system/theme.ts | 41 ++++-- site/source/design-system/types.ts | 9 ++ .../choix-du-statut/_components/Layout.tsx | 8 +- .../_components/Navigation.tsx | 5 +- .../_components/StatutsDisponibles.tsx | 136 +++++++++++++++--- .../choix-du-statut/détails-activité.tsx | 9 +- .../components/AllerPlusLoinRevenus.tsx | 35 ++--- .../components/DetailsRowCards.tsx | 4 +- .../components/Détails.tsx | 12 +- .../components/RevenuAprèsImpot.tsx | 6 +- .../components/StatusCard.tsx | 59 +------- site/source/types/utils.d.ts | 2 +- 17 files changed, 480 insertions(+), 168 deletions(-) create mode 100644 site/source/components/StatusTag.tsx create mode 100644 site/source/design-system/tag/index.stories.tsx create mode 100644 site/source/design-system/types.ts diff --git a/site/source/components/StatusTag.tsx b/site/source/components/StatusTag.tsx new file mode 100644 index 000000000..955754023 --- /dev/null +++ b/site/source/components/StatusTag.tsx @@ -0,0 +1,105 @@ +import { FC } from 'react' +import styled from 'styled-components' + +import { + CircleIcon, + HexagonIcon, + RhombusIcon, + SquareIcon, + TriangleIcon, +} from '@/design-system/icons' +import { Tag } from '@/design-system/tag' +import { Colors } from '@/design-system/theme' + +export const TAG_DATA = { + EI: { + color: 'independant', + longName: 'Entreprise individuelle', + shortName: 'Entreprise (EI)', + acronym: 'EI', + icon: TriangleIcon, + }, + AE: { + color: 'tertiary', + longName: 'Auto-entrepreneur', + shortName: 'Auto-entrepreneur', + acronym: 'AE', + icon: CircleIcon, + }, + SASU: { + color: 'secondary', + longName: 'Société par actions simplifiée unipersonnelle', + shortName: 'Société (SASU)', + acronym: 'SASU', + icon: HexagonIcon, + }, + SAS: { + color: 'secondary', + longName: 'Société par actions simplifiée', + shortName: 'Société (SAS)', + acronym: 'SAS', + icon: HexagonIcon, + }, + EURL: { + color: 'artisteAuteur', + longName: 'Entreprise unipersonnelle à responsabilité limitée', + shortName: 'Entreprise (EURL)', + acronym: 'EURL', + icon: RhombusIcon, + }, + SARL: { + color: 'artisteAuteur', + longName: 'Société à responsabilité limitée', + shortName: 'Société (SARL)', + acronym: 'SARL', + icon: RhombusIcon, + }, + association: { + color: 'primary', + longName: 'Association', + shortName: 'Association', + acronym: 'Assoc.', + icon: SquareIcon, + }, +} satisfies { + [key: string]: { + color: Colors + longName: string + shortName: string + acronym: string + icon: FC + } +} + +export type Status = keyof typeof TAG_DATA + +const StyledTag = styled(Tag)` + margin: 0 0.25rem; + + svg { + margin-right: 0.25rem; + width: 1rem; + height: 1rem; + } + svg.square-icon { + width: 0.8rem; + height: 0.8rem; + } +` + +interface StatusTagProps { + status: Status + text: 'acronym' | 'shortName' | 'longName' + showIcon?: boolean +} + +export const StatusTag = ({ status, text, showIcon }: StatusTagProps) => { + const Icon = TAG_DATA[status].icon + + return ( + + {showIcon && } + {TAG_DATA[status][text]} + + ) +} diff --git a/site/source/components/layout/Header.tsx b/site/source/components/layout/Header.tsx index c5ddbcc61..20d64671b 100644 --- a/site/source/components/layout/Header.tsx +++ b/site/source/components/layout/Header.tsx @@ -87,7 +87,7 @@ const StyledHeader = styled.div` column-gap: ${({ theme }) => theme.spacings.xs}; row-gap: ${({ theme }) => theme.spacings.md}; a { - height: 100%; + height: 3rem; display: flex; align-items: center; } diff --git a/site/source/design-system/icons/index.tsx b/site/source/design-system/icons/index.tsx index e3982078f..77aa6eb75 100644 --- a/site/source/design-system/icons/index.tsx +++ b/site/source/design-system/icons/index.tsx @@ -168,21 +168,6 @@ export const EditIcon = (props: HTMLAttributes) => ( ) -export const HexagonIcon = (props: HTMLAttributes) => ( - - - -) - export const TriangleIcon = (props: HTMLAttributes) => ( ) => ( ) +export const SquareIcon = (props: HTMLAttributes) => ( + + + +) + +export const RhombusIcon = styled(SquareIcon)` + transform: rotate(45deg); +` + +export const HexagonIcon = (props: HTMLAttributes) => ( + + + +) + export const CircleIcon = (props: HTMLAttributes) => ( = { + component: Tag, +} + +export default meta + +type Story = StoryObj + +export const Basic: Story = { + render: (args) => ( + <> + No props + Small + + Medium primary color + + Large red + + + With icon + +

+ Medium Tag with primary color in light mode and secondary color in dark + mode : +

+ + Example + +

Medium Tag with custom color in light and dark mode :

+ + Custom color + + + ), + args: {}, +} diff --git a/site/source/design-system/tag/index.tsx b/site/source/design-system/tag/index.tsx index 3efc35aa4..090bafe3f 100644 --- a/site/source/design-system/tag/index.tsx +++ b/site/source/design-system/tag/index.tsx @@ -1,47 +1,108 @@ import styled from 'styled-components' -import { baseTheme, getColorGroup } from '../theme' +import { Colors, getColorGroup, isColor } from '../theme' +import { KeysOfUnion, LG, MD, SM } from '../types' -export type TagType = keyof typeof baseTheme.colors.bases & - keyof typeof baseTheme.colors.extended & - keyof typeof baseTheme.colors.publics +type SizeProps = SM | MD | LG +type SizeKey = KeysOfUnion -type SizeType = 'sm' | 'md' | 'lg' +interface Color { + light: Colors | string + dark: Colors | string +} -const lightColors = ['grey'] +interface TagProps { + children?: React.ReactNode + className?: string + color?: Colors | Color | string +} -export const Tag = styled.div<{ $color?: TagType; $size?: SizeType }>` +export const Tag = ({ + children, + className, + color, + ...size +}: TagProps & Partial) => ( + + {children} + +) + +const StyledTag = styled.span<{ $color?: Color | string; $size: SizeKey }>` font-family: ${({ theme }) => theme.fonts.main}; - display: flex; + display: inline-flex; + vertical-align: middle; align-items: center; width: fit-content; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-weight: 500; + font-size: 0.75rem; + line-height: 1rem; + background-color: ${({ theme, $color }) => - $color - ? theme.colors[getColorGroup($color)][$color][ - lightColors.includes($color) ? 300 : 100 - ] - : theme.colors.bases.primary[100]}; + typeof $color === 'string' + ? isColor($color) + ? getColorGroup($color)?.[200] + : $color + : theme.darkMode + ? // darkmode + typeof $color?.dark === 'string' + ? isColor($color.dark) + ? getColorGroup($color.dark)?.[200] + : $color.dark + : theme.colors.extended.grey[600] + : // lightmode + typeof $color?.light === 'string' + ? isColor($color.light) + ? getColorGroup($color.light)?.[400] + : $color.light + : theme.colors.extended.grey[400]}; + color: ${({ theme, $color }) => - $color - ? theme.colors[getColorGroup($color)][$color][600] - : theme.colors.extended.grey[800]}; - font-size: ${({ $size }) => { - switch ($size) { - case 'sm': - return '0.75rem' - case 'md': - default: - return '1rem' - } - }}; + typeof $color === 'string' + ? isColor($color) + ? getColorGroup($color)?.[700] + : null + : theme.darkMode + ? // darkmode + typeof $color?.dark === 'string' + ? isColor($color.dark) + ? getColorGroup($color.dark)?.[700] + : null + : null + : // lightmode + typeof $color?.light === 'string' + ? isColor($color.light) + ? getColorGroup($color.light)?.[600] + : null + : null}; + svg { fill: ${({ theme, $color }) => - $color - ? theme.colors[getColorGroup($color)][$color][600] - : theme.colors.extended.grey[800]}; + typeof $color === 'string' + ? isColor($color) + ? getColorGroup($color)?.[700] + : null + : theme.darkMode + ? // darkmode + typeof $color?.dark === 'string' + ? isColor($color.dark) + ? getColorGroup($color.dark)?.[700] + : null + : null + : // lightmode + typeof $color?.light === 'string' + ? isColor($color.light) + ? getColorGroup($color.light)?.[600] + : null + : null}; } ` diff --git a/site/source/design-system/theme.ts b/site/source/design-system/theme.ts index 709b7667e..ad873fa98 100644 --- a/site/source/design-system/theme.ts +++ b/site/source/design-system/theme.ts @@ -1,6 +1,7 @@ import { Theme } from '@/types/styled' +import { Merge } from '@/types/utils' -import { TagType } from './tag' +import { KeysOfUnion } from './types' export const baseTheme = { colors: { @@ -185,17 +186,37 @@ export const baseTheme = { }, } -export type ColorGroups = Array +type ColorsType = typeof baseTheme.colors +type ColorGroups = Merge -export const getColorGroup = (color: TagType) => { - const colorGroups: ColorGroups = Object.keys(baseTheme.colors).map( - (colorGroup) => colorGroup as keyof typeof baseTheme.colors - ) +export type Colors = KeysOfUnion - return colorGroups.find( - (colorGroup: keyof typeof baseTheme.colors) => - !!baseTheme.colors[colorGroup]?.[color] - ) as keyof typeof baseTheme.colors +/** + * Check if a color is in the theme + * @param color + */ +export const isColor = (color: string): color is Colors => + Object.values(baseTheme.colors).some((val) => color in val) + +/** + * Get the color group of a color + * @example getColorGroup('error') => { 100: '#FDE8E9', 200: '#F9BCC0', 300: '#DB666E', 400: '#CB111D', 500: '#96050F', 600: '#52070C' } + * @param color + */ +export const getColorGroup = (color: T) => { + const colorGroup = Object.values(baseTheme.colors).find( + (val) => color in val + ) as ColorGroups | undefined + + if (colorGroup && color in colorGroup) { + return ( + (colorGroup[color as keyof ColorGroups] as Merge< + NonNullable + >) ?? null + ) + } + + return null } // We use the Grid from material-ui, we need to uniformise diff --git a/site/source/design-system/types.ts b/site/source/design-system/types.ts new file mode 100644 index 000000000..538daf76a --- /dev/null +++ b/site/source/design-system/types.ts @@ -0,0 +1,9 @@ +export type XXL = { xxl: true } +export type XL = { xl: true } +export type LG = { lg: true } +export type MD = { md: true } +export type SM = { sm: true } +export type XS = { xs: true } +export type XXS = { xxs: true } + +export type KeysOfUnion = T extends T ? keyof T : never diff --git a/site/source/pages/assistants/choix-du-statut/_components/Layout.tsx b/site/source/pages/assistants/choix-du-statut/_components/Layout.tsx index 2601d7879..531e9eb89 100644 --- a/site/source/pages/assistants/choix-du-statut/_components/Layout.tsx +++ b/site/source/pages/assistants/choix-du-statut/_components/Layout.tsx @@ -15,13 +15,11 @@ export default function Layout({ <>

{title}

- + {children} - - - - + + diff --git a/site/source/pages/assistants/choix-du-statut/_components/Navigation.tsx b/site/source/pages/assistants/choix-du-statut/_components/Navigation.tsx index 08b40da21..d91d2f087 100644 --- a/site/source/pages/assistants/choix-du-statut/_components/Navigation.tsx +++ b/site/source/pages/assistants/choix-du-statut/_components/Navigation.tsx @@ -119,7 +119,10 @@ const StyledNavigation = styled.div` padding: ${({ theme }) => theme.spacings.lg} 1rem; margin: 0 -1rem; bottom: 0; - background: ${({ theme }) => theme.colors.extended.grey[100]}; + background: ${({ theme }) => + theme.darkMode + ? theme.colors.extended.dark[800] + : theme.colors.extended.grey[100]}; z-index: 2; /* box-shadow: ${({ theme }) => theme.elevations[6]}; */ ` diff --git a/site/source/pages/assistants/choix-du-statut/_components/StatutsDisponibles.tsx b/site/source/pages/assistants/choix-du-statut/_components/StatutsDisponibles.tsx index 7597205ed..6a422362e 100644 --- a/site/source/pages/assistants/choix-du-statut/_components/StatutsDisponibles.tsx +++ b/site/source/pages/assistants/choix-du-statut/_components/StatutsDisponibles.tsx @@ -1,36 +1,136 @@ +import styled from 'styled-components' + import { DottedName } from '@/../../modele-social' +import { Status, StatusTag } from '@/components/StatusTag' import { useEngine } from '@/components/utils/EngineContext' +import { Message } from '@/design-system' import { H5 } from '@/design-system/typography/heading' import { Li, Ul } from '@/design-system/typography/list' import { SmallBody } from '@/design-system/typography/paragraphs' export default function StatutsDisponibles() { return ( - <> +
Statuts disponibles
Les statuts disponibles diffèrent en fonction de l'activité professionnelle que vous exercez -
    - - - - - - - - - - - -
- + + + + + + + + {/* + + + */} + + +
) } -function Statut({ statut }: { statut: DottedName }) { - const engine = useEngine() +const StyledMessage = styled(Message)` + padding-top: 2rem; + border: none; + border-radius: 0.5rem; + background: ${({ theme }) => + theme.darkMode + ? theme.colors.bases.primary[400] + : theme.colors.bases.primary[100]}; - return
  • {engine.getRule(statut).title}
  • + /* cut the top right corner */ + clip-path: polygon(0 100%, 0 0, calc(100% - 40px) 0, 100% 40px, 100% 100%); + + &:before { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 40px; + height: 40px; + background: ${({ theme }) => theme.colors.bases.primary[300]}; + border-bottom-left-radius: 0.5rem; + /* css blue triangle */ + clip-path: polygon( + -10px -10px, + 0 calc(100% + 10px), + calc(100% + 10px) calc(100% + 10px) + ); + } +` + +function Statut({ statut, status }: { statut: DottedName; status: Status }) { + const engine = useEngine() + const disabled = status === 'AE' + + return ( +
  • + {engine.getRule(statut).title} + +
  • + ) } + +const StyledSpan = styled.span`` + +const StyledUl = styled(Ul)` + ${Li} { + display: flex; + align-items: center; + justify-content: space-between; + background: ${({ theme }) => theme.colors.extended.grey[100]}; + border-radius: ${({ theme }) => theme.box.borderRadius}; + padding: ${({ theme }) => theme.spacings.sm}; + + ${StyledSpan} { + color: ${({ theme }) => theme.colors.bases.primary[700]}; + font-weight: bold; + } + + &.disabled { + background: ${({ theme }) => theme.colors.extended.grey[200]}; + ${StyledSpan} { + color: ${({ theme }) => theme.colors.extended.grey[600]}; + font-weight: bold; + text-decoration-line: line-through; + } + } + } +` diff --git a/site/source/pages/assistants/choix-du-statut/détails-activité.tsx b/site/source/pages/assistants/choix-du-statut/détails-activité.tsx index 60d54f850..b9a18b26a 100644 --- a/site/source/pages/assistants/choix-du-statut/détails-activité.tsx +++ b/site/source/pages/assistants/choix-du-statut/détails-activité.tsx @@ -117,7 +117,6 @@ function GuichetSelection({
    {getGuichetTitle(guichetEntry.label)}
    @@ -132,11 +131,9 @@ function GuichetSelection({ function GuichetSkeleton() { return ( - - - - - + + + ) } diff --git a/site/source/pages/simulateurs/comparaison-statuts/components/AllerPlusLoinRevenus.tsx b/site/source/pages/simulateurs/comparaison-statuts/components/AllerPlusLoinRevenus.tsx index c718d8479..d41c58ef7 100644 --- a/site/source/pages/simulateurs/comparaison-statuts/components/AllerPlusLoinRevenus.tsx +++ b/site/source/pages/simulateurs/comparaison-statuts/components/AllerPlusLoinRevenus.tsx @@ -9,13 +9,15 @@ import { SwitchInput } from '@/components/conversation/ChoicesInput' import { ExplicableRule } from '@/components/conversation/Explicable' import RuleInput from '@/components/conversation/RuleInput' import Value from '@/components/EngineValue' +import { StatusTag } from '@/components/StatusTag' import { useEngine } from '@/components/utils/EngineContext' import { Message } from '@/design-system' import { Button } from '@/design-system/buttons' import { Drawer } from '@/design-system/drawer' import { ArrowRightIcon, InfoIcon } from '@/design-system/icons' import { Grid, Spacing } from '@/design-system/layout' -import { Tag, TagType } from '@/design-system/tag' +import { Tag } from '@/design-system/tag' +import { Colors } from '@/design-system/theme' import { Tooltip } from '@/design-system/tooltip' import { Strong } from '@/design-system/typography' import { H2, H4, H5 } from '@/design-system/typography/heading' @@ -24,7 +26,6 @@ import { Body } from '@/design-system/typography/paragraphs' import { answerQuestion } from '@/store/actions/actions' import { useCasParticuliers } from '../contexts/CasParticuliers' -import { StatusTagIcon } from './StatusCard' const DOTTEDNAME_SOCIETE_IMPOT = 'entreprise . imposition' const DOTTEDNAME_SOCIETE_VERSEMENT_LIBERATOIRE = @@ -137,7 +138,7 @@ const AllerPlusLoinRevenus = ({ Type de structure - SASU + @@ -147,7 +148,7 @@ const AllerPlusLoinRevenus = ({ id="tooltip-ei-table" > - EI + @@ -155,7 +156,7 @@ const AllerPlusLoinRevenus = ({ - AE + @@ -175,7 +176,7 @@ const AllerPlusLoinRevenus = ({ Chiffre d'affaires - + Charges - + Cotisations - + - + - + - Impôts - + - + - + - + - + - + ` +const StyledTag = styled(Tag)` width: 100%; justify-content: center; font-size: 0.75rem; diff --git a/site/source/pages/simulateurs/comparaison-statuts/components/DetailsRowCards.tsx b/site/source/pages/simulateurs/comparaison-statuts/components/DetailsRowCards.tsx index cbbcf33b7..90f17ed0e 100644 --- a/site/source/pages/simulateurs/comparaison-statuts/components/DetailsRowCards.tsx +++ b/site/source/pages/simulateurs/comparaison-statuts/components/DetailsRowCards.tsx @@ -15,7 +15,9 @@ import { getBestOption, OptionType } from '../utils' import StatusCard from './StatusCard' const getStatusLabelsArray = (statusArray: OptionType[]) => - statusArray.map((statusOption) => statusOption.type) + statusArray.map( + (statusOption) => statusOption.type.toUpperCase() as 'SASU' | 'EI' | 'AE' + ) const getGridSizes = (statusArray: OptionType[]) => { return { sizeXs: 12, sizeLg: 4 * statusArray.length } diff --git a/site/source/pages/simulateurs/comparaison-statuts/components/Détails.tsx b/site/source/pages/simulateurs/comparaison-statuts/components/Détails.tsx index f0add8c8a..906bb576e 100644 --- a/site/source/pages/simulateurs/comparaison-statuts/components/Détails.tsx +++ b/site/source/pages/simulateurs/comparaison-statuts/components/Détails.tsx @@ -489,7 +489,7 @@ const Détails = ({ sa vie professionnelle. - + - + 1 € minimum - + Aucun @@ -664,17 +664,17 @@ const Détails = ({ - + Conjoint associé ou salarié - + Conjoint collaborateur ou salarié - + Conjoint collaborateur diff --git a/site/source/pages/simulateurs/comparaison-statuts/components/RevenuAprèsImpot.tsx b/site/source/pages/simulateurs/comparaison-statuts/components/RevenuAprèsImpot.tsx index f7630fd03..d9c259176 100644 --- a/site/source/pages/simulateurs/comparaison-statuts/components/RevenuAprèsImpot.tsx +++ b/site/source/pages/simulateurs/comparaison-statuts/components/RevenuAprèsImpot.tsx @@ -66,7 +66,7 @@ const RevenuAprèsImpot = ({ {status.map((statusString) => ( - - - {STATUS_DATA[statusString].label} - + ))} @@ -98,13 +72,6 @@ const StyledCardContainer = styled(CardContainer)` padding: 0; ` -const StyledTag = styled(Tag)` - display: inline-flex; - &:not(:last-child) { - margin-right: 0.5rem; - } -` - const StyledEmoji = styled(Emoji)` position: absolute; top: 0; @@ -138,23 +105,3 @@ const StyledBodyTooltip = styled(Body)` font-size: 0.75rem; margin: 0; ` - -export const StatusTagIcon = ({ - status, - ...props -}: { - status: 'sasu' | 'ei' | 'ae' - style?: { marginRight: string } -}) => { - switch (true) { - case status.includes('sasu'): - return - case status.includes('ei'): - return - case status.includes('ae'): - return - - default: - return null - } -} diff --git a/site/source/types/utils.d.ts b/site/source/types/utils.d.ts index 99cdc4591..b6e6dbdc3 100644 --- a/site/source/types/utils.d.ts +++ b/site/source/types/utils.d.ts @@ -1,5 +1,5 @@ /** - * Immutable type + * Immutable type, useful for satisfied typescript type checking * * source: https://github.com/cefn/lauf/blob/b982a09/modules/store/src/types/immutable.ts#L25 */