diff --git a/src/assets/icons/tabbar/grand-lyon.svg b/src/assets/icons/tabbar/grand-lyon.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e4c7c0794ba24b5782d7681d93f73630abb5464 --- /dev/null +++ b/src/assets/icons/tabbar/grand-lyon.svg @@ -0,0 +1,22 @@ +<svg width="101" height="23" viewBox="0 0 101 23" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M7.81215 12.9224C7.39405 13.427 6.88879 13.833 6.32645 14.1163C5.76411 14.3995 5.15621 14.5542 4.53892 14.5711C3.92164 14.588 3.30761 14.4667 2.7334 14.2146C2.15919 13.9625 1.63653 13.5846 1.19656 13.1034C0.0191753 11.7958 0 10.5266 0 7.28739C0 4.04822 0.0230104 2.77683 1.19656 1.46924C1.62081 1.00088 2.12406 0.630018 2.6775 0.377874C3.23094 0.125731 3.82371 -0.0027462 4.4219 -0.000207232C6.68846 -0.000207232 8.42769 1.65026 8.8323 4.18665H7.85434C7.47083 2.21461 6.1688 0.966645 4.42765 0.966645C3.97438 0.962018 3.5248 1.05754 3.10507 1.24766C2.68534 1.43777 2.30384 1.71868 1.98276 2.07405C1.05274 3.10905 0.981792 4.20794 0.981792 7.28739C0.981792 10.3668 1.05466 11.4742 1.97893 12.4986C2.29994 12.8541 2.68143 13.135 3.10117 13.3252C3.52092 13.5153 3.97053 13.6107 4.42382 13.606C4.93588 13.6092 5.44221 13.4864 5.90641 13.2463C6.3706 13.0062 6.78111 12.6548 7.10841 12.2175C7.66642 11.4529 7.88502 10.6075 7.88502 9.38081V8.09239H4.4219V7.12554H8.86298V9.46813C8.86298 10.9589 8.57343 12.0237 7.81215 12.9309" fill="white"/> +<path d="M17.0893 1.08766H13.2829V6.88451H17.0893C18.7384 6.88451 19.9349 5.95812 19.9349 3.98609C19.9349 2.01405 18.7384 1.08766 17.0893 1.08766ZM19.9157 14.4532L16.8342 7.85136H13.2829V14.4532H12.3049V0.127197H17.2158C19.3251 0.127197 20.9129 1.47525 20.9129 3.97118C20.9129 6.10081 19.7527 7.49358 17.9406 7.83645L21.0567 14.4596L19.9157 14.4532Z" fill="white"/> +<path d="M27.6724 1.56896L24.9725 10.0023H30.3724L27.6724 1.56896ZM31.8048 14.4511L30.6811 10.9691H24.6638L23.5401 14.4511H22.4893L27.2371 0.127197H28.1077L32.8556 14.4596L31.8048 14.4511Z" fill="white"/> +<path d="M43.5439 14.4508L36.0961 1.99248V14.4508H35.1182V0.118408H36.0961L43.5439 12.5384V0.118408H44.5238V14.4508H43.5439Z" fill="white"/> +<path d="M55.3753 2.19294C54.6332 1.34109 53.6361 1.08766 52.6025 1.08766H49.4846V13.4842H52.6006C53.6342 13.4842 54.6313 13.2223 55.3734 12.3768C56.405 11.2098 56.3322 8.6734 56.3322 7.0847C56.3322 5.49599 56.4031 3.3621 55.3734 2.18655L55.3753 2.19294ZM56.0618 13.1371C55.2948 13.989 54.1788 14.4447 52.8365 14.4447H48.5066V0.127197H52.8384C54.1807 0.127197 55.3024 0.591457 56.0637 1.43692C57.3696 2.88507 57.314 5.15951 57.314 7.09321C57.314 9.02692 57.3696 11.6932 56.0637 13.1435L56.0618 13.1371Z" fill="white"/> +<path d="M60.3035 14.4508V0.118408H62.8231V11.9549H68.6775V14.4508H60.3035Z" fill="white"/> +<path d="M74.202 8.57304V14.4508H71.7015V8.57304L67.8223 0.118408H70.5586L72.969 5.89822L75.3449 0.118408H78.0793L74.202 8.57304Z" fill="white"/> +<path d="M85.3987 3.26031C85.1955 3.01071 84.9458 2.81309 84.6671 2.68109C84.3883 2.54909 84.0871 2.48586 83.7841 2.49577C83.4787 2.48591 83.1749 2.54907 82.8934 2.68098C82.6118 2.81288 82.3591 3.01046 82.1523 3.26031C81.6633 3.86299 81.5368 4.53809 81.5368 7.28531C81.5368 10.0325 81.6633 10.7055 82.1523 11.3103C82.3592 11.56 82.6119 11.7575 82.8934 11.8894C83.175 12.0213 83.4787 12.0846 83.7841 12.0748C84.087 12.0846 84.3882 12.0213 84.667 11.8893C84.9457 11.7573 85.1954 11.5598 85.3987 11.3103C85.8877 10.7055 86.0335 10.0325 86.0335 7.28531C86.0335 4.53809 85.8896 3.86299 85.3987 3.26031ZM87.2722 13.0034C86.3373 14.0107 85.0858 14.5741 83.7832 14.5741C82.4805 14.5741 81.2291 14.0107 80.2942 13.0034C78.9902 11.5552 79.0247 9.76207 79.0247 7.28744C79.0247 4.81281 78.9902 3.02818 80.2942 1.57151C81.2291 0.564137 82.4805 0.000732422 83.7832 0.000732422C85.0858 0.000732422 86.3373 0.564137 87.2722 1.57151C88.5781 3.01966 88.5589 4.81068 88.5589 7.28744C88.5589 9.7642 88.5704 11.5552 87.2722 13.0034Z" fill="white"/> +<path d="M98.7506 14.4508L93.6403 5.65545V14.4508H91.1226V0.118408H93.37L98.4803 8.89461V0.118408H101V14.4508H98.7506Z" fill="white"/> +<path d="M85.6326 18.9751H85.6442C85.7043 18.8383 85.7987 18.7238 85.9161 18.6452C86.0334 18.5666 86.1687 18.5274 86.3057 18.5321C86.7659 18.5321 87.2338 18.7451 87.2338 20.14C87.2338 20.9365 87.2108 21.8543 86.275 21.8543C86.1485 21.8567 86.0236 21.8223 85.9126 21.7547C85.8017 21.6871 85.7085 21.5886 85.6422 21.4689H85.625V23.0001H85.1628V18.596H85.6307L85.6326 18.9751ZM86.7448 20.14C86.7448 19.6587 86.7448 18.9559 86.1581 18.9559C85.5713 18.9559 85.6346 19.8418 85.6346 20.3295C85.6346 20.7555 85.6576 21.4306 86.1753 21.4306C86.6931 21.4306 86.7448 20.9982 86.7448 20.1464V20.14Z" fill="white"/> +<path d="M53.4347 21.3281H53.4232C53.3722 21.4924 53.2736 21.6332 53.1427 21.7284C53.0119 21.8236 52.8562 21.8679 52.7003 21.8541C52.0905 21.8541 51.9141 21.4176 51.9141 20.8042C51.9141 19.8352 52.7693 19.7927 53.4232 19.8097C53.4347 19.3838 53.4405 18.9238 52.9381 18.9238C52.872 18.9179 52.8057 18.9292 52.7443 18.9568C52.6829 18.9844 52.6282 19.0275 52.5844 19.0827C52.5407 19.1379 52.5091 19.2037 52.4923 19.2748C52.4755 19.3459 52.4738 19.4204 52.4874 19.4924H51.9889C52.0119 18.7768 52.3532 18.5298 52.963 18.5298C53.7032 18.5298 53.8968 18.9557 53.8968 19.4924V21.03C53.8988 21.2846 53.918 21.5388 53.9544 21.7902H53.4424L53.4347 21.3281ZM52.382 20.8404C52.382 21.1492 52.5181 21.4282 52.8307 21.4282C53.1432 21.4282 53.4692 21.2323 53.4232 20.1994C52.9802 20.2079 52.382 20.1653 52.382 20.8426V20.8404Z" fill="white"/> +<path d="M61.8415 21.7905V19.5756C61.8415 19.2519 61.761 18.9559 61.3851 18.9559C61.3079 18.9573 61.2318 18.977 61.1621 19.0137C61.0923 19.0505 61.0304 19.1034 60.9805 19.1689C60.9083 19.312 60.8706 19.4734 60.8712 19.6374V21.7883H60.4072V19.2605C60.4072 19.0475 60.3957 18.8175 60.3784 18.596H60.8731V18.9943H60.8847C60.9435 18.8418 61.0451 18.7145 61.1743 18.6312C61.3035 18.548 61.4534 18.5132 61.6018 18.5321C61.7378 18.5209 61.8738 18.5537 61.9933 18.6267C62.1128 18.6996 62.2106 18.8096 62.2749 18.9431C62.3386 18.8061 62.4379 18.6936 62.5601 18.6203C62.6822 18.547 62.8214 18.5162 62.9595 18.5321C63.2624 18.5321 63.7457 18.6088 63.7457 19.3606V21.7905H63.2778V19.5756C63.2778 19.2519 63.1992 18.9559 62.8214 18.9559C62.7583 18.9448 62.6937 18.9498 62.6326 18.9705C62.5715 18.9911 62.5154 19.0269 62.4686 19.0752C62.3557 19.2349 62.2991 19.4347 62.3094 19.6374V21.7883L61.8415 21.7905Z" fill="white"/> +<path d="M67.1493 20.3593C67.1493 21.2516 67.366 21.4241 67.7245 21.4241C68.0429 21.4241 68.2097 21.1387 68.225 20.8172H68.737C68.737 21.5263 68.3382 21.8479 67.7341 21.8479C67.1301 21.8479 66.6411 21.6349 66.6411 20.2336C66.6411 19.2966 66.737 18.5299 67.7341 18.5299C68.5548 18.5299 68.76 19.0304 68.76 20.0867V20.3593H67.1493ZM68.271 19.9674C68.271 19.0368 68.0371 18.9239 67.6958 18.9239C67.4005 18.9239 67.155 19.0815 67.1493 19.9674H68.271ZM68.3861 17.2202L67.6402 18.1125H67.2567L67.7648 17.2202H68.3861Z" fill="white"/> +<path d="M71.9317 18.5959V17.9826L72.3996 17.7483V18.6001H73.0266V18.992H72.3996V20.947C72.3996 21.16 72.3996 21.4091 72.8272 21.4091C72.8617 21.4091 72.9346 21.3963 73.0381 21.3836V21.7818C72.8847 21.7946 72.7294 21.8329 72.576 21.8329C72.1311 21.8329 71.9317 21.62 71.9317 21.2643V18.9877H71.46V18.5959H71.9317Z" fill="white"/> +<path d="M76.5166 19.0814H76.5338C76.7256 18.5937 76.9672 18.5383 77.4217 18.5383V19.0771L77.297 19.058C77.2558 19.0502 77.2142 19.0459 77.1724 19.0452C76.6546 19.0452 76.5223 19.4818 76.5223 19.9119V21.7988H76.0544V18.6043H76.5223L76.5166 19.0814Z" fill="white"/> +<path d="M80.0793 20.2401C80.0793 19.303 80.1752 18.5364 81.1724 18.5364C82.1695 18.5364 82.2654 19.3094 82.2654 20.2401C82.2654 21.652 81.7802 21.8543 81.1724 21.8543C80.5645 21.8543 80.0793 21.652 80.0793 20.2401ZM81.7764 20.0271C81.7764 19.154 81.531 18.9623 81.1724 18.9623C80.8138 18.9623 80.5683 19.1518 80.5683 20.0271C80.5683 21.0642 80.6891 21.4391 81.1724 21.4391C81.6556 21.4391 81.7783 21.0557 81.7783 20.0186L81.7764 20.0271Z" fill="white"/> +<path d="M90.0909 20.2401C90.0909 19.303 90.1868 18.5364 91.184 18.5364C92.1811 18.5364 92.2789 19.3094 92.2789 20.2401C92.2789 21.652 91.7938 21.8543 91.184 21.8543C90.5742 21.8543 90.0909 21.652 90.0909 20.2401ZM91.788 20.0271C91.788 19.154 91.5426 18.9623 91.184 18.9623C90.8254 18.9623 90.5799 19.1518 90.5799 20.0271C90.5799 21.0642 90.6988 21.4391 91.184 21.4391C91.6691 21.4391 91.7899 21.0557 91.7899 20.0186L91.788 20.0271Z" fill="white"/> +<path d="M95.7747 21.7905H95.3068V17.2224H95.7727L95.7747 21.7905Z" fill="white"/> +<path d="M99.2224 20.3591C99.2224 21.2515 99.4391 21.424 99.7977 21.424C100.116 21.424 100.283 21.1386 100.298 20.817H100.81C100.81 21.5262 100.411 21.8477 99.8072 21.8477C99.2032 21.8477 98.7142 21.6348 98.7142 20.2335C98.7142 19.2965 98.8101 18.5298 99.8072 18.5298C100.628 18.5298 100.833 19.0302 100.833 20.0865V20.3591H99.2224ZM100.344 19.9673C100.344 19.0366 100.11 18.9238 99.7689 18.9238C99.4736 18.9238 99.2281 19.0814 99.2224 19.9673H100.344Z" fill="white"/> +<path d="M49.2296 21.7905H48.7617V17.2224H49.2296V21.7905Z" fill="white"/> +</svg> diff --git a/src/assets/png/badges/CHA00000003-0.png b/src/assets/png/badges/CHA00000003-0.png new file mode 100644 index 0000000000000000000000000000000000000000..bad09c3516c15277abd6b9adc44b4653ec40abb2 Binary files /dev/null and b/src/assets/png/badges/CHA00000003-0.png differ diff --git a/src/assets/png/badges/CHA00000003-1.png b/src/assets/png/badges/CHA00000003-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e16bba433ca3ece0ba70d5f0435443e57bcba00b Binary files /dev/null and b/src/assets/png/badges/CHA00000003-1.png differ diff --git a/src/components/ContentComponents/KonnectorViewer/picto-failure.png b/src/assets/png/picto/picto-failure.png similarity index 100% rename from src/components/ContentComponents/KonnectorViewer/picto-failure.png rename to src/assets/png/picto/picto-failure.png diff --git a/src/components/CommonKit/Button/StyledNoOauthButton.tsx b/src/components/CommonKit/Button/StyledAuthButton.tsx similarity index 86% rename from src/components/CommonKit/Button/StyledNoOauthButton.tsx rename to src/components/CommonKit/Button/StyledAuthButton.tsx index 0e4cc60f3cd74e00d458a58d8a63e0c9f50ec61c..ea79ad8cc2b10343e656e8549a690df3e748457e 100644 --- a/src/components/CommonKit/Button/StyledNoOauthButton.tsx +++ b/src/components/CommonKit/Button/StyledAuthButton.tsx @@ -42,6 +42,10 @@ const PrimaryButton = withStyles({ root: { background: 'var(--multiColorRadialGradient)', }, + disabled: { + background: 'var(--multiColorRadialGradient) !important', + opacity: '0.6', + }, label: { color: '#000000', fontWeight: 'bold', @@ -71,7 +75,7 @@ function MyButton(props: ButtonProps) { ) } -const StyledNoOauthButton: React.ComponentType<ButtonProps> = props => { +const StyledAuthButton: React.ComponentType<ButtonProps> = props => { return ( <> <MuiThemeProvider theme={theme}> @@ -81,8 +85,8 @@ const StyledNoOauthButton: React.ComponentType<ButtonProps> = props => { ) } -StyledNoOauthButton.defaultProps = { +StyledAuthButton.defaultProps = { color: 'primary', } -export default StyledNoOauthButton +export default StyledAuthButton diff --git a/src/components/CommonKit/Button/StyledButton.tsx b/src/components/CommonKit/Button/StyledButton.tsx index 645119f8931c0d59e320aa137be9de04eff400d3..f5c2c0e7bb6e2f0816440f98e9508d63854c2396 100644 --- a/src/components/CommonKit/Button/StyledButton.tsx +++ b/src/components/CommonKit/Button/StyledButton.tsx @@ -46,6 +46,10 @@ const PrimaryButton = withStyles({ color: '#000000', fontWeight: 'bold', }, + disabled: { + background: 'var(--multiColorRadialGradient) !important', + opacity: '0.6', + }, })(BaseButton) const SecondaryButton = withStyles({ diff --git a/src/components/CommonKit/Button/StyledOauthButton.tsx b/src/components/CommonKit/Button/StyledOauthButton.tsx deleted file mode 100644 index 9e3e6ebec877d7a900ecaf5aa700404893b6f067..0000000000000000000000000000000000000000 --- a/src/components/CommonKit/Button/StyledOauthButton.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react' -import MuiButton, { ButtonProps } from '@material-ui/core/Button' -import { - withStyles, - MuiThemeProvider, - createMuiTheme, -} from '@material-ui/core/styles' - -const theme = createMuiTheme({ - typography: { - useNextVariants: true, - }, - shape: { - borderRadius: 2, - }, - palette: { - primary: { - main: '#E3B82A', - }, - secondary: { - main: '#7B7B7B', - }, - }, -}) - -const BaseButton = withStyles({ - root: { - height: '5rem', - margin: '1.5rem 0 0 0', - padding: '0px 1px', - }, - label: { - textTransform: 'none', - fontFamily: 'Lato, sans-serif', - fontStyle: 'normal', - fontSize: '1rem', - lineHeight: '120%', - }, -})(MuiButton) - -const PrimaryButton = withStyles({ - root: { - background: 'var(--multiColorRadialGradient)', - }, - label: { - color: '#000000', - fontWeight: 'bold', - }, -})(BaseButton) - -const SecondaryButton = withStyles({ - root: { - border: '1px solid #121212', - }, - label: { - color: '#E0E0E0', - fontWeight: 'normal', - }, -})(BaseButton) - -function MyButton(props: ButtonProps) { - return props.color === 'secondary' ? ( - <SecondaryButton - fullWidth - color="secondary" - variant="outlined" - {...props} - /> - ) : ( - <PrimaryButton fullWidth color="primary" variant="contained" {...props} /> - ) -} - -const StyledOauthButton: React.ComponentType<ButtonProps> = props => { - return ( - <> - <MuiThemeProvider theme={theme}> - <MyButton {...props}>{props.children}</MyButton> - </MuiThemeProvider> - </> - ) -} - -StyledOauthButton.defaultProps = { - color: 'primary', -} - -export default StyledOauthButton diff --git a/src/components/ContainerComponents/Header/Header.tsx b/src/components/ContainerComponents/Header/Header.tsx index 4a480d809febb585e8ea728ba2f8a68477200f7c..e0d487f600c86ebdecc9bf87be06f7f6f2abc11f 100644 --- a/src/components/ContainerComponents/Header/Header.tsx +++ b/src/components/ContainerComponents/Header/Header.tsx @@ -4,7 +4,6 @@ import { history } from 'components/ContainerComponents/ViewContainer/ViewContai import { withClient, Client } from 'cozy-client' import { ScreenType } from 'enum/screen.enum' import { AppContext } from 'components/Contexts/AppContextProvider' -import useInstanceSettings from 'components/Hooks/userInstanceSettings' import BackArrowIcon from 'assets/icons/ico/back-arrow.svg' import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' @@ -29,7 +28,6 @@ const Header: React.FC<HeaderProps> = ({ }: HeaderProps) => { const header = useRef(null) const { screenType } = useContext(AppContext) - const { data: instanceSettings } = useInstanceSettings(client) const cozyBarHeight = 48 const headerBottomHeight = 8 diff --git a/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx index 92a2ae276ae3767009249713f57780cf75ec2c98..8be12a7ec3835b5dfed627bff6ad3143bb26e2b8 100644 --- a/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx @@ -37,10 +37,15 @@ const FinishedChallengeDetailsViewContainer: React.FC<FinishedChallengeDetailsVi const { screenType } = useContext(AppContext) async function importRightBadge(id: string, badgeStatus: number) { - // Les svg doivent être au format idchallenge-badgestate.svg - const importedBadge = await import( - /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` - ) + // Les png doivent être au format idchallenge-badgestate.png + const importedBadge = + id === 'CHA00000001' + ? await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-1.png` + ) + : await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` + ) setBadgeIcon(importedBadge.default) } @@ -111,20 +116,26 @@ const FinishedChallengeDetailsViewContainer: React.FC<FinishedChallengeDetailsVi </div> ) ) : null} - <div className="cp-win-badge-star"> - <img - className="cp-win-badge" - src={badgeIcon} - width={screenType === ScreenType.MOBILE ? 200 : 300} - ></img> - {challenge.badge === BadgeState.SUCCESS ? ( + {challenge.badge === BadgeState.SUCCESS ? ( + <div className="cp-win-badge-star"> + <img + className="cp-win-badge" + src={badgeIcon} + width={screenType === ScreenType.MOBILE ? 200 : 300} + ></img> <img className="cp-win-star" src={StarIcon} width={screenType === ScreenType.MOBILE ? 380 : 480} ></img> - ) : null} - </div> + </div> + ) : ( + <img + className="cp-win-badge" + src={badgeIcon} + width={screenType === ScreenType.MOBILE ? 200 : 300} + ></img> + )} <div className="cp-description text-16-bold"> {challenge.challengeType && challenge.challengeType.description} diff --git a/src/components/ContainerComponents/ViewContainer/LockedChallengeDetailsViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/LockedChallengeDetailsViewContainer.tsx index 2fcdca083b5a1e64801d7ea11476771b3050c898..36ee470b0237383ca94810b6ee5b4227c7bf15ca 100644 --- a/src/components/ContainerComponents/ViewContainer/LockedChallengeDetailsViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/LockedChallengeDetailsViewContainer.tsx @@ -65,7 +65,9 @@ const LockedChallengeDetailsViewContainer: React.FC<LockedChallengeDetailsViewPr width={screenType === ScreenType.MOBILE ? 180 : 220} ></img> <div className="cp-description text-16-bold"> - {t('CHALLENGE.LOCKED')} + {challenge.level < 900 + ? t('CHALLENGE.LOCKED') + : t('CHALLENGE.FULLY_LOCKED')} </div> </div> <div className="cp-valid-locked"> diff --git a/src/components/ContainerComponents/WelcomeModalContainer/WelcomeModalContainer.tsx b/src/components/ContainerComponents/WelcomeModalContainer/WelcomeModalContainer.tsx index 9acb933822ab33313210e9efe5152d0e4adb1669..c96bd78edca5cfaec7529b1652eec5936b6df271 100644 --- a/src/components/ContainerComponents/WelcomeModalContainer/WelcomeModalContainer.tsx +++ b/src/components/ContainerComponents/WelcomeModalContainer/WelcomeModalContainer.tsx @@ -3,7 +3,7 @@ import { withClient, Client } from 'cozy-client' import Modal from 'components/CommonKit/Modal/Modal' import StyledButton from 'components/CommonKit/Button/StyledButton' import { translate } from 'cozy-ui/react/I18n' -import useInstanceSettings from 'components/Hooks/userInstanceSettings' +import userInstanceSettings from 'components/Hooks/userInstanceSettings' interface WelcomeModalContainerProps { handleClose: () => void @@ -16,7 +16,7 @@ const WelcomeModalContainer: React.FC<WelcomeModalContainerProps> = ({ t, client, }: WelcomeModalContainerProps) => { - const { data: instanceSettings } = useInstanceSettings(client) + const { data: instanceSettings } = userInstanceSettings(client) return ( <React.Fragment> diff --git a/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx b/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx index ff67a7123da8c8b3d31e1909f4e8784fbc667969..9dd90d335fda5b9adbf18bf4369d962595f0e860 100644 --- a/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx +++ b/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx @@ -35,12 +35,42 @@ const FollowChallengeTimeline: React.FC<FollowChallengeTimelineViewProps> = ({ const startingDate = DateTime.fromISO(challenge.startingDate.toString()) const listOfChallengeDays = [] let i = 0 - while (challenge.challengeType.duration.days > i) { + if (challenge.challengeType.duration.days === 7) { + while (challenge.challengeType.duration.days > i) { + listOfChallengeDays.push({ + letter: startingDate + .plus({ days: i }) + .weekdayShort[0].toUpperCase(), + date: startingDate.plus({ days: i }).day, + }) + i++ + } + } else { + while (challenge.challengeType.duration.days >= i) { + listOfChallengeDays.push({ + letter: '', + date: startingDate.plus({ days: i }).toFormat('dd/MM'), + }) + i += 7 + } + } + return listOfChallengeDays + } else { + return [] + } + } + + const getListOfWeeks = () => { + if (challenge && challenge.challengeType) { + const startingDate = DateTime.fromISO(challenge.startingDate.toString()) + const listOfChallengeDays = [] + let i = 0 + while (challenge.challengeType.duration.days >= i) { listOfChallengeDays.push({ - letter: startingDate.plus({ days: i }).weekdayShort[0].toUpperCase(), - date: startingDate.plus({ days: i }).day, + letter: '', + date: startingDate.plus({ days: i }).toFormat('dd/MM'), }) - i++ + i += 7 } return listOfChallengeDays } else { @@ -58,36 +88,75 @@ const FollowChallengeTimeline: React.FC<FollowChallengeTimelineViewProps> = ({ return 0 } } else { - return 0 + if (challenge) { + return -Interval.fromDateTimes(DateTime.local(), viewingDate()).count( + 'days' + ) + } else { + return 0 + } } } + const generateTimelinePart = ( + index: number, + dayCount: number, + className: string + ) => { + return ( + <div + className={`${className} ${ + (index === 0 && dayCount > 0) || + (index === getListOfWeeks().length - 1 && dayCount < 0) + ? 'none' + : index * 7 < getListOfPastDays() + dayCount + ? 'past' + : 'futur' + }`} + ></div> + ) + } + + const monthChallenge = () => { + return getListOfWeeks().map((day, index) => ( + <div key={index} className="day-solo"> + <div className="day-line-label"> + {generateTimelinePart(index, 3, 'date-dash')} + {generateTimelinePart(index, 2, 'date-dash')} + {generateTimelinePart(index, 1, 'date-dash')} + {generateTimelinePart(index, 0, 'date-label')} + {generateTimelinePart(index, -1, 'date-dash')} + {generateTimelinePart(index, -2, 'date-dash')} + {generateTimelinePart(index, -3, 'date-dash')} + </div> + <div className="day-letter">{day.letter}</div> + <div className="day-date">{day.date}</div> + </div> + )) + } + + const weekChallenge = () => { + return getListOfDays().map((day, index) => ( + <div key={index} className="day-solo"> + <div className="day-line-label"> + {generateTimelinePart(index, 0, 'date-dash')} + {generateTimelinePart(index, 0, 'date-label')} + {generateTimelinePart(index + 1, 0, 'date-dash')} + </div> + <div className="day-letter">{day.letter}</div> + <div className="day-date">{day.date}</div> + </div> + )) + } + return ( <React.Fragment> <div className="list-of-days-duration"> - {getListOfDays().map((day, index) => ( - <div key={index} className="day-solo"> - <div className="day-line-label"> - <div - className={`date-dash ${ - index < getListOfPastDays() ? 'past' : 'futur' - }`} - ></div> - <div - className={`date-label ${ - index < getListOfPastDays() ? 'past' : 'futur' - }`} - ></div> - <div - className={`date-dash ${ - index + 1 < getListOfPastDays() ? 'past' : 'futur' - }`} - ></div> - </div> - <div className="day-letter">{day.letter}</div> - <div className="day-date">{day.date}</div> - </div> - ))} + {challenge && + challenge.challengeType && + challenge.challengeType.duration.days === 7 + ? weekChallenge() + : monthChallenge()} </div> </React.Fragment> ) diff --git a/src/components/ContentComponents/ChallengeList/ChallengeListItem.tsx b/src/components/ContentComponents/ChallengeList/ChallengeListItem.tsx index 6dfd81a72fa0729e4c3c77006c97c16e62958882..c533747de344be36a566cd18369ac85e98fc9061 100644 --- a/src/components/ContentComponents/ChallengeList/ChallengeListItem.tsx +++ b/src/components/ContentComponents/ChallengeList/ChallengeListItem.tsx @@ -33,10 +33,15 @@ const ChallengeListItem: React.FC<ChallengeListItemProps> = ({ const [badgeIcon, setBadgeIcon] = useState<any | null>(null) async function importRightBadge(id: string, badgeStatus: number) { - // Les svg doivent être au format idchallenge-badgestate.svg - const importedBadge = await import( - /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` - ) + // Les png doivent être au format idchallenge-badgestate.png + const importedBadge = + id === 'CHA00000001' + ? await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-1.png` + ) + : await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` + ) setBadgeIcon(importedBadge.default) } diff --git a/src/components/ContentComponents/ChallengeList/ChallengesList.tsx b/src/components/ContentComponents/ChallengeList/ChallengesList.tsx index 0a7180cc816246e789496e5f9c388114cf5da156..46c71e6a2a1b9d196c22ba3ec965492e0fbc1269 100644 --- a/src/components/ContentComponents/ChallengeList/ChallengesList.tsx +++ b/src/components/ContentComponents/ChallengeList/ChallengesList.tsx @@ -36,6 +36,7 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ const [openChallengeModal, setOpenChallengeModal] = useState(false) const [scroll, setScroll] = useState(0) const [paddingBottom, setPaddingBottom] = useState(0) + const [userLevel, setUserLevel] = useState<null | number>(null) const handleCloseClick = () => { setOngoingChallenge(null) @@ -87,8 +88,8 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ fluidTypes ) const dataAllUC = await challengeManager.getAllUserChallenges() - - if (subscribed && dataAllCT && dataAllUC) { + const levelOfUser = await challengeManager.getUserLevel() + if (subscribed && dataAllCT && dataAllUC && levelOfUser) { const ongoingChallengeTmp = dataAllUC.filter( challenge => challenge.state === ChallengeState.ONGOING )[0] @@ -96,7 +97,7 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ setChallengesType(dataAllCT) setUserChallenges(dataAllUC) setOngoingChallenge(ongoingChallengeTmp) - + setUserLevel(levelOfUser) if ( await challengeManager.isChallengeOver( ongoingChallengeTmp, @@ -122,10 +123,12 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ fluidTypes ) const dataAllUC = await challengeManager.getAllUserChallenges() - if (subscribed && dataAllCT && dataAllUC) { + const levelOfUser = await challengeManager.getUserLevel() + if (subscribed && dataAllCT && dataAllUC && levelOfUser) { setChallengesType(dataAllCT) setUserChallenges(dataAllUC) setRightChallengeInTheMiddle(dataAllCT, dataAllUC) + setUserLevel(levelOfUser) } } loadNewChallengesType() @@ -154,7 +157,10 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ style={{ paddingBottom: paddingBottom }} > {challengesType.map((challenge, index) => - index === challengesType.length - 1 && !ongoingChallenge ? ( + index === challengesType.length - 1 && + !ongoingChallenge && + userLevel === + challengesType[challengesType.length - 1].level ? ( <ChallengeListItem key={index} challenge={challenge} diff --git a/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx b/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx index 34bf9aae71eff40588526c2d70045939c801ebeb..b1456acf4026379b6d6a5219d18a65ece2cdee6d 100644 --- a/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx +++ b/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx @@ -29,10 +29,15 @@ const ChallengeModal: React.FC<ChallengeModalProps> = ({ const { screenType } = useContext(AppContext) async function importRightBadge(id: string, badgeStatus: number) { - // Les svg doivent être au format idchallenge-badgestate.svg - const importedBadge = await import( - /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` - ) + // Les png doivent être au format idchallenge-badgestate.png + const importedBadge = + id === 'CHA00000001' + ? await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-1.png` + ) + : await import( + /* webpackMode: "eager" */ `assets/png/badges/${id}-${badgeStatus}.png` + ) setBadgeIcon(importedBadge.default) } @@ -56,10 +61,20 @@ const ChallengeModal: React.FC<ChallengeModalProps> = ({ {t('CHALLENGE.CONGRATULATION')} </div> <div className="cm-result text-18-bold"> - <div>{t('CHALLENGE.RESULT_POSITIF')}</div> - <div className="cm-result-positif text-18-normal">{`${result} €`}</div> + {challengeId !== 'CHA00000001' ? ( + <> + <div>{t('CHALLENGE.RESULT_POSITIF')}</div> + <div className="cm-result-positif text-18-normal">{`${result} €`}</div> + </> + ) : null} </div> - <div className="cm-win-badge-star"> + <div + className={ + challengeId !== 'CHA00000001' + ? 'cm-win-badge-star' + : 'cm-win-badge-star --ecolyo-royal' + } + > <img className="cm-win-badge" src={badgeIcon} diff --git a/src/components/ContentComponents/ConsumptionNavigator/ActivateHalfHourLoad.tsx b/src/components/ContentComponents/ConsumptionNavigator/ActivateHalfHourLoad.tsx index a0735d1e3fa712383dc740ce0b1499533a9388fe..958cb0db6b07440c029edc66911ba14e62a6959a 100644 --- a/src/components/ContentComponents/ConsumptionNavigator/ActivateHalfHourLoad.tsx +++ b/src/components/ContentComponents/ConsumptionNavigator/ActivateHalfHourLoad.tsx @@ -1,6 +1,6 @@ import React from 'react' import { translate } from 'cozy-ui/react/I18n' -import StyledOauthButton from 'components/CommonKit/Button/StyledOauthButton' +import StyledAuthButton from 'components/CommonKit/Button/StyledAuthButton' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import iconEnedisLogo from 'assets/icons/visu/enedis-logo.svg' import Config from '../../../../config.json' @@ -16,7 +16,7 @@ const ActivateHalfHourLoad = ({ t }: ActivateHalfHourLoadProps) => { <div className="cta-box-header header-text text-16-normal"> {t('timestep.activate.enedis.info')} </div> - <StyledOauthButton + <StyledAuthButton className="cta-box-button" type="button" color="primary" @@ -30,7 +30,7 @@ const ActivateHalfHourLoad = ({ t }: ActivateHalfHourLoadProps) => { <div> {t('timestep.activate.enedis.label1')}</div> </div> </div> - </StyledOauthButton> + </StyledAuthButton> </div> ) } diff --git a/src/components/ContentComponents/Konnector/KonnectorForm.tsx b/src/components/ContentComponents/Konnector/KonnectorForm.tsx index cfd77ac8e05f1568b8dcd8081b8d07ef37cb5f95..e02336b889dd2542fbf802cfea2a5e30e5a0dbe9 100644 --- a/src/components/ContentComponents/Konnector/KonnectorForm.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorForm.tsx @@ -2,152 +2,49 @@ import React from 'react' import { translate } from 'cozy-ui/react/I18n' import IFluidConfig from 'services/IFluidConfig' +import { Konnector, Trigger } from 'doctypes' -import iconGRDFLogo from 'assets/icons/visu/grdf-logo.svg' -import iconEGLLogo from 'assets/icons/visu/egl-logo.svg' -import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' -import StyledButton from 'components/CommonKit/Button/StyledButton' -import StyledNoOauthButton from 'components/CommonKit/Button/StyledOauthButton' -import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import TrailingIcon from 'assets/icons/ico/trailing-icon.svg' +import KonnectorLoginForm from 'components/ContentComponents/Konnector/KonnectorLoginForm' +import KonnectorOAuthForm from 'components/ContentComponents/Konnector/KonnectorOAuthForm' interface KonnectorFormProps { fluidConfig: IFluidConfig - login: string - setLogin: Function - password: string - setPassword: Function - loading: boolean - error: string - handleSubmit: Function - t: Function + konnector: Konnector + account: Account | null + trigger: Trigger | null + handleSuccessForm: Function } const KonnectorForm: React.FC<KonnectorFormProps> = ({ fluidConfig, - login, - setLogin, - password, - setPassword, - loading, - error, - handleSubmit, - t, + konnector, + account, + trigger, + handleSuccessForm, }: KonnectorFormProps) => { - const konnectorName: string = fluidConfig.konnectorConfig.name - const konnectorType: string = fluidConfig.konnectorConfig.type - const siteLink: string = fluidConfig.siteLink + const oAuth: boolean = fluidConfig.konnectorConfig.oauth - function revealPassword(idInput: string) { - const input = document.getElementById(idInput) - if (input) { - if (input.getAttribute('type') === 'password') { - input.setAttribute('type', 'text') - } else { - input.setAttribute('type', 'password') - } - } + const handleSuccess = (_account: Account, _trigger: Trigger) => { + handleSuccessForm(_account, _trigger) } - const getIcon = (fluidName: string) => { - switch (fluidName) { - case 'grdf': - return iconGRDFLogo - case 'egl': - return iconEGLLogo - default: - return null - } - } - - const icon = getIcon(fluidConfig.name) - return ( - <form - className="form" - onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleSubmit(e)} - > - <div className="text-16-normal"> - {t('KONNECTORCONFIG.LABEL_FILLIN')} {konnectorName} - </div> - - <div className="form-group"> - <div className="form-message">{error}</div> - <input - id={'idFieldLogin' + konnectorType} - type="text" - className="form-control form-input" - aria-describedby="emailHelp" - placeholder="Adresse e-mail" - name="login" - onChange={(e: React.ChangeEvent<HTMLInputElement>) => - setLogin(e.target.value) - } - value={login} - ></input> - </div> - - <div className="form-group"> - <div className="form-message">{error}</div> - <input - id={'idFieldPassword' + konnectorType} - type="password" - className="form-control form-input" - aria-describedby="PasswordHelp" - placeholder="Mot de passe" - name="password" - onChange={(e: React.ChangeEvent<HTMLInputElement>) => - setPassword(e.target.value) - } - value={password} + <> + {!oAuth ? ( + <KonnectorLoginForm + fluidConfig={fluidConfig} + onSuccess={handleSuccess} + account={account} + trigger={trigger} /> - <span> - <StyledIconButton - icon={TrailingIcon} - className="form-trailing-icon" - size={22} - onClick={() => revealPassword('idFieldPassword' + konnectorType)} - /> - </span> - </div> - - <div className="knooauthform-button"> - <StyledNoOauthButton type="submit" color="primary" disabled={loading}> - <div className="nooauthform-button-content"> - <div className="nooauthform-button-content-icon"> - {icon && <StyledIcon icon={icon} size={48} />} - </div> - <div className="nooauthform-button-text text-18-bold"> - <div> - {t('nooauth.connection.text')}{' '} - {t('nooauth.' + fluidConfig.name + '.text')} - </div> - </div> - </div> - </StyledNoOauthButton> - </div> - <br /> - <div className="knooauthform-text text-16-bold"> - <div className="text-16-bold"> - {t('nooauth.creation.no_account')}{' '} - {t('nooauth.' + fluidConfig.name + '.text')} ? - </div> - <div className="text-16-normal"> - {t('nooauth.creation.text')}{' '} - {t('nooauth.' + fluidConfig.name + '.facture')} - </div> - </div> - - <StyledButton - type="button" - color="secondary" - disabled={loading} - onClick={() => window.open(siteLink, '_blank')} - > - {t('nooauth.creation.button_text')}{' '} - {t('nooauth.' + fluidConfig.name + '.short_text')} - </StyledButton> - </form> + ) : ( + <KonnectorOAuthForm + konnector={konnector} + siteLink={fluidConfig.siteLink} + onSuccess={handleSuccess} + /> + )} + </> ) } diff --git a/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx b/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8b0c24dea2d95ef6768c265a09c0e15898c8c297 --- /dev/null +++ b/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx @@ -0,0 +1,101 @@ +import React, { useEffect, useState } from 'react' +import { withClient, Client } from 'cozy-client' +import { translate } from 'cozy-ui/react/I18n' + +import { isKonnectorRunning } from 'cozy-harvest-lib/dist/helpers/triggers' +import { + KonnectorJob, + ERROR_EVENT, + LOGIN_SUCCESS_EVENT, + SUCCESS_EVENT, +} from 'cozy-harvest-lib/dist/models/KonnectorJob' +import { Trigger } from 'doctypes' + +import Lottie from 'react-lottie' +import * as loadingData from 'assets/anims/bounceloading.json' +import StyledButton from 'components/CommonKit/Button/StyledButton' + +const loadingOptions = { + loop: true, + autoplay: true, + animationData: loadingData, + rendererSettings: { + preserveAspectRatio: 'xMidYMid slice', + }, +} + +interface KonnectorLaunchProps { + trigger: Trigger + handleKonnectorLaunch: Function + client: Client + t: Function +} + +const KonnectorLaunch: React.FC<KonnectorLaunchProps> = ({ + trigger, + handleKonnectorLaunch, + client, + t, +}: KonnectorLaunchProps) => { + const [state, setState] = useState<string | null>(null) + + const callbackResponse = (_state: string) => { + setState(_state) + } + + const handleClick = () => { + handleKonnectorLaunch() + } + + useEffect(() => { + let subscribed = true + async function getData() { + if (subscribed && !isKonnectorRunning(trigger)) { + const konnectorJob = new KonnectorJob(client, trigger) + await konnectorJob.launch() + konnectorJob.jobWatcher.on(ERROR_EVENT, () => { + callbackResponse(ERROR_EVENT) + }) + konnectorJob.jobWatcher.on(LOGIN_SUCCESS_EVENT, () => { + callbackResponse(LOGIN_SUCCESS_EVENT) + }) + konnectorJob.jobWatcher.on(SUCCESS_EVENT, () => { + callbackResponse(SUCCESS_EVENT) + }) + } + } + getData() + return () => { + subscribed = false + } + }, []) + + return ( + <div className="klaunch-content"> + {!state ? ( + <> + <Lottie options={loadingOptions} height={50} width={50} /> + <div className="klaunch-content-text klaunch-content-text-center text-16-normal"> + <div>{t('KONNECTORCONFIG.PLZ_WAIT')}</div> + </div> + <div className="klaunch-content-text text-16-normal"> + <div>{t('KONNECTORCONFIG.LOADING_DATA')}</div> + </div> + </> + ) : ( + <> + <div className="klaunch-info-txt"> + {state === ERROR_EVENT + ? t('KONNECTORCONFIG.ERROR_DATA') + : t('KONNECTORCONFIG.SUCCESS')} + <StyledButton type="button" color="primary" onClick={handleClick}> + <div>{t('KONNECTORCONFIG.OK')}</div> + </StyledButton> + </div> + </> + )} + </div> + ) +} + +export default translate()(withClient(KonnectorLaunch)) diff --git a/src/components/ContentComponents/Konnector/KonnectorLoading.tsx b/src/components/ContentComponents/Konnector/KonnectorLoading.tsx deleted file mode 100644 index e502a959adf95d7626d29dad1a0c217b69925a6e..0000000000000000000000000000000000000000 --- a/src/components/ContentComponents/Konnector/KonnectorLoading.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { translate } from 'cozy-ui/react/I18n' -import Lottie from 'react-lottie' - -import * as loadingData from 'assets/anims/bounceloading.json' - -const loadingOptions = { - loop: true, - autoplay: true, - animationData: loadingData, - rendererSettings: { - preserveAspectRatio: 'xMidYMid slice', - }, -} - -interface KonnectorLoadingProps { - t: Function -} - -const KonnectorLoading: React.FC<KonnectorLoadingProps> = ({ - t, -}: KonnectorLoadingProps) => { - return ( - <div className="kload-content"> - <Lottie options={loadingOptions} height={50} width={50} /> - <div className="kload-content-text kload-content-text-center text-16-normal"> - <div>{t('KONNECTORCONFIG.PLZ_WAIT')}</div> - </div> - <div className="kload-content-text text-16-normal"> - <div>{t('KONNECTORCONFIG.LOADING_DATA')}</div> - </div> - </div> - ) -} - -export default translate()(KonnectorLoading) diff --git a/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx b/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2db7d009fe825f354b505285f1613b4e87bee92f --- /dev/null +++ b/src/components/ContentComponents/Konnector/KonnectorLoginForm.tsx @@ -0,0 +1,201 @@ +import React, { useState, useEffect } from 'react' +import { withClient, Client } from 'cozy-client' +import { translate } from 'cozy-ui/react/I18n' + +import IFluidConfig from 'services/IFluidConfig' + +import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' +import StyledButton from 'components/CommonKit/Button/StyledButton' +import TrailingIcon from 'assets/icons/ico/trailing-icon.svg' +import { ConnectionService } from 'services/connectionService' +import { AccountService } from 'services/accountService' +import { Account, AuthLoginData, Trigger } from 'doctypes' + +import iconGRDFLogo from 'assets/icons/visu/grdf-logo.svg' +import iconEGLLogo from 'assets/icons/visu/egl-logo.svg' +import StyledAuthButton from 'components/CommonKit/Button/StyledAuthButton' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' + +interface KonnectorLoginFormProps { + fluidConfig: IFluidConfig + onSuccess: Function + account: Account + trigger: Trigger + client: Client + t: Function +} + +const KonnectorLoginForm: React.FC<KonnectorLoginFormProps> = ({ + fluidConfig, + onSuccess, + account, + trigger, + client, + t, +}: KonnectorLoginFormProps) => { + const konnectorName: string = fluidConfig.konnectorConfig.name + const konnectorType: string = fluidConfig.konnectorConfig.type + const siteLink: string = fluidConfig.siteLink + + const [login, setLogin] = useState<string>('') + const [password, setPassword] = useState<string>('') + const [error, setError] = useState<string>('') + const [loading, setLoading] = useState<boolean>(false) + + const getIcon = (fluidName: string) => { + switch (fluidName) { + case 'grdf': + return iconGRDFLogo + case 'egl': + return iconEGLLogo + default: + return null + } + } + + const icon = getIcon(fluidConfig.name) + + function revealPassword(idInput: string) { + const input = document.getElementById(idInput) + if (input) { + if (input.getAttribute('type') === 'password') { + input.setAttribute('type', 'text') + } else { + input.setAttribute('type', 'password') + } + } + } + + const connect = async () => { + const connectionService = new ConnectionService( + client, + fluidConfig.konnectorConfig.slug, + login, + password + ) + const { account, trigger } = await connectionService.connectNewUser() + if (!trigger) { + setError(t('KONNECTORCONFIG.ERROR_ACCOUNT_CREATION')) + setLoading(false) + return null + } + onSuccess(account, trigger) + } + + const update = async () => { + const auth = { + login: login, + password: password, + } + account.auth = auth + const updatedAccount = await AccountService.updateAccount(client, account) + onSuccess(updatedAccount, trigger) + } + + const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault() + try { + setError('') + setLoading(true) + if (!login || !password) { + setError(t('KONNECTORCONFIG.ERROR_NO_LOGIN_PASSWORD')) + setLoading(false) + return null + } + if (!account) { + await connect() + } else { + await update() + } + } catch (error) { + setLoading(false) + } + } + + useEffect(() => { + if (account) { + const auth: AuthLoginData = account.auth + if (auth.login) { + setLogin(auth.login) + } + setError(t('KONNECTORCONFIG.ERROR_LOGIN_FAILED')) + } + }, []) + + return ( + <form + className="form" + onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleSubmit(e)} + > + {t('KONNECTORCONFIG.LABEL_FILLIN')} {konnectorName} + <div className="form-group"> + <input + id={'idFieldLogin' + konnectorType} + type="text" + className="form-control form-input" + aria-describedby="emailHelp" + placeholder="Adresse e-mail" + name="login" + onChange={(e: React.ChangeEvent<HTMLInputElement>) => + setLogin(e.target.value) + } + value={login} + ></input> + </div> + <div className="form-group"> + <input + id={'idFieldPassword' + konnectorType} + type="password" + className="form-control form-input" + aria-describedby="PasswordHelp" + placeholder="Mot de passe" + name="password" + onChange={(e: React.ChangeEvent<HTMLInputElement>) => + setPassword(e.target.value) + } + value={password} + /> + <span> + <StyledIconButton + icon={TrailingIcon} + className="form-trailing-icon" + size={22} + onClick={() => revealPassword('idFieldPassword' + konnectorType)} + /> + </span> + </div> + <div className="form-message">{error}</div> + <div className="kloginauthform-button"> + <StyledAuthButton type="submit" color="primary" disabled={loading}> + <div className="authform-button-content"> + <div className="authform-button-content-icon"> + {icon && <StyledIcon icon={icon} size={48} />} + </div> + <div className="kauthform-button-text text-18-bold"> + <div>{t('auth.' + fluidConfig.name + '.connect.label1')}</div> + <div>{t('auth.' + fluidConfig.name + '.connect.label2')}</div> + </div> + </div> + </StyledAuthButton> + </div> + <div className="kloginauthform-text text-16-bold"> + <div className="text-16-bold"> + {t('auth.' + fluidConfig.name + '.no_account.title')} + </div> + <div className="text-16-normal"> + {t('auth.' + fluidConfig.name + '.no_account.text')} + </div> + </div> + <StyledButton + type="button" + color="secondary" + disabled={loading} + onClick={() => window.open(siteLink, '_blank')} + > + {t('auth.' + fluidConfig.name + '.create_account')} + </StyledButton> + </form> + ) +} + +export default translate()(withClient(KonnectorLoginForm)) diff --git a/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx b/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cfe6ea899b5ee56d2abe54f461583039fd254c1c --- /dev/null +++ b/src/components/ContentComponents/Konnector/KonnectorNotFound.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { translate } from 'cozy-ui/react/I18n' + +import StyledButton from 'components/CommonKit/Button/StyledButton' + +interface KonnectorNotFoundProps { + konnectorSlug: string + t: Function +} + +const KonnectorNotFound: React.FC<KonnectorNotFoundProps> = ({ + konnectorSlug, + t, +}: KonnectorNotFoundProps) => { + const openKonnectorURL = () => { + // TODO - Use getstoreinstallationurl from client - https://docs.cozy.io/en/cozy-client/api/cozy-client/#getstoreinstallationurl-string + const hostname = window.location.origin.replace('ecolyo', 'store') + const url = hostname + '/#/discover/' + konnectorSlug + window.open(url, '_blank') + } + + return ( + <div className="knotfound"> + <div className="knotfound-text"> + {' '} + {t('KONNECTORCONFIG.NOT_INSTALLED')} + </div> + <div className="knotfound-button"> + <StyledButton type="button" color="primary" onClick={openKonnectorURL}> + {t('KONNECTORCONFIG.BTN_INSTALL')} + </StyledButton> + </div> + </div> + ) +} + +export default translate()(KonnectorNotFound) diff --git a/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx b/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx index 19381bb90596d91d588578eb669002210338c0b9..589d9c58b5eda0cc06ee470ca504ce2e573c5b20 100644 --- a/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorOAuthForm.tsx @@ -1,15 +1,19 @@ import React from 'react' +import { Client, withClient } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' -import { Konnector } from 'doctypes' +import { Konnector, Trigger } from 'doctypes' import OAuthForm from 'components/ContentComponents/OAuth/OAuthForm' import StyledButton from 'components/CommonKit/Button/StyledButton' +import { AccountService } from 'services/accountService' +import { TriggerService } from 'services/triggersService' interface KonnectorOAuthFormProps { konnector: Konnector siteLink: string onSuccess: Function loading: boolean + client: Client t: Function } @@ -18,22 +22,32 @@ const KonnectorOAuthForm: React.FC<KonnectorOAuthFormProps> = ({ siteLink, onSuccess, loading, + client, t, }: KonnectorOAuthFormProps) => { + const handleSuccess = async (accountId: string) => { + const account = await AccountService.getAccount(client, accountId) + if (!account) { + onSuccess(null, null) + } + const triggersServices = new TriggerService(client, account, konnector) + const trigger: Trigger = await triggersServices.createTrigger() + onSuccess(account, trigger) + } return ( <div className="koauthform"> <div className="koauthform-text text-16-normal"> - {t('oauth.connect.' + konnector.slug + '.info')} + {t('auth.' + konnector.slug + '.connect.info')} </div> <div className="koauthform-button"> - <OAuthForm konnector={konnector} onSuccess={onSuccess} /> + <OAuthForm konnector={konnector} onSuccess={handleSuccess} /> </div> <div className="koauthform-text text-16-bold"> <div className="text-16-bold"> - {t('oauth.no_account.' + konnector.slug + '.title')} + {t('auth.' + konnector.slug + '.no_account.title')} </div> <div className="text-16-normal"> - {t('oauth.no_account.' + konnector.slug + '.text')} + {t('auth.' + konnector.slug + '.no_account.text')} </div> </div> <div className="koauthform-button"> @@ -43,11 +57,11 @@ const KonnectorOAuthForm: React.FC<KonnectorOAuthFormProps> = ({ disabled={loading} onClick={() => window.open(siteLink, '_blank')} > - {t('oauth.create_account.' + konnector.slug)} + {t('auth.' + konnector.slug + '.create_account')} </StyledButton> </div> </div> ) } -export default translate()(KonnectorOAuthForm) +export default translate()(withClient(KonnectorOAuthForm)) diff --git a/src/components/ContentComponents/Konnector/KonnectorResult.tsx b/src/components/ContentComponents/Konnector/KonnectorResult.tsx index d6cf0f8708e150568496004999e21a9fac35de8c..c0b54a0a55371b89b2168b1ddac1bd80fc1f90c3 100644 --- a/src/components/ContentComponents/Konnector/KonnectorResult.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorResult.tsx @@ -1,42 +1,135 @@ -import React from 'react' +import React, { useState, useContext, useEffect } from 'react' +import { withClient, Client } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' +import { AppContext } from 'components/Contexts/AppContextProvider' + import StyledButton from 'components/CommonKit/Button/StyledButton' import StyledBlackSpinner from 'components/CommonKit/Spinner/StyledBlackSpinner' +import { Account, Trigger } from 'doctypes' +import { TriggerService } from 'services/triggersService' +import { AccountService } from 'services/accountService' + +import { isKonnectorRunning } from 'cozy-harvest-lib/dist/helpers/triggers' +import { + KonnectorJob, + ERROR_EVENT, + LOGIN_SUCCESS_EVENT, + SUCCESS_EVENT, +} from 'cozy-harvest-lib/dist/models/KonnectorJob' + interface KonnectorResultProps { - date: string - updating: boolean - errored: boolean - updateKonnector: (event: any) => void - deleteAccount: (event: any) => void + account: Account + handleJobState: Function + client: Client t: Function } const KonnectorResult: React.FC<KonnectorResultProps> = ({ - date, - updating, - errored, - updateKonnector, - deleteAccount, + account, + handleJobState, + client, t, }: KonnectorResultProps) => { + const [trigger, setTrigger] = useState<Trigger | null>(null) + const [updating, setUpdating] = useState<boolean>(false) + const [lastExecutionDate, setLastExecutionDate] = useState<string>('-') + const [status, setStatus] = useState<string>('') + + const context = useContext(AppContext) + + const updateState = async (trigger: Trigger) => { + const triggerState = await TriggerService.fetchTriggerState(client, trigger) + if (triggerState) { + setLastExecutionDate( + new Date(triggerState.last_execution).toLocaleString() + ) + setStatus(triggerState.status) + handleJobState(triggerState.status) + await context.refreshFluidTypes() + } + } + + const callbackResponse = async () => { + if (trigger) { + await updateState(trigger) + } + setUpdating(false) + } + + const updateKonnector = async () => { + setUpdating(true) + setStatus('') + setLastExecutionDate('-') + handleJobState('') + + if (trigger && !isKonnectorRunning(trigger)) { + const konnectorJob = new KonnectorJob(client, trigger) + await konnectorJob.launch() + konnectorJob.jobWatcher.on(ERROR_EVENT, () => { + callbackResponse() + }) + konnectorJob.jobWatcher.on(LOGIN_SUCCESS_EVENT, () => { + callbackResponse() + }) + konnectorJob.jobWatcher.on(SUCCESS_EVENT, () => { + callbackResponse() + }) + } + } + + const deleteAccount = async () => { + setUpdating(true) + try { + if (account) { + await AccountService.deleteAccount(client, account) + await context.refreshFluidTypes() + } + } catch (error) { + setUpdating(false) + } + } + + useEffect(() => { + let subscribed = true + async function getData() { + const _trigger = await TriggerService.fetchTriggerFromAccount( + client, + account + ) + if (subscribed && _trigger) { + setTrigger(_trigger) + await updateState(_trigger) + } + } + getData() + return () => { + subscribed = false + } + }, []) + return ( <div className="accordion-update-result"> <div className="accordion-update"> <div className={ - errored + status === 'errored' ? 'accordion-caption-red text-16-normal' : 'accordion-caption text-16-normal' } > {t('KONNECTORCONFIG.LABEL_UPDATEDAT')} </div> - <div>{date}</div> + <div>{lastExecutionDate}</div> </div> <div className="inline-buttons"> - <StyledButton type="button" color="primary" onClick={updateKonnector}> + <StyledButton + type="button" + color="primary" + onClick={updateKonnector} + disabled={updating} + > {updating ? ( <StyledBlackSpinner size="2em" /> ) : ( @@ -56,4 +149,4 @@ const KonnectorResult: React.FC<KonnectorResultProps> = ({ ) } -export default translate()(KonnectorResult) +export default translate()(withClient(KonnectorResult)) diff --git a/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx b/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx index 25c75055bcbecfff530f6a0b8af9e192599e245b..5f9c8ec19cfd61f79d1926303fa6fb8e3ca0e9a4 100644 --- a/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx +++ b/src/components/ContentComponents/KonnectorViewer/KonnectorViewer.tsx @@ -3,7 +3,6 @@ import { withClient, Client } from 'cozy-client' import { Konnector } from 'doctypes' import KonnectorService from 'services/konnectorService' -import KonnectorStatusService from 'services/konnectorStatusService' import KonnectorViewerCard from 'components/ContentComponents/KonnectorViewer/KonnectorViewerCard' import IFluidConfig from 'services/IFluidConfig' @@ -20,7 +19,7 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ isParam = false, }: KonnectorViewerProps) => { const [konnector, setKonnector] = useState<Konnector | null>(null) - const [lastTrigger, setLastTrigger] = useState<any>(null) + const [loaded, setLoaded] = useState<boolean>(false) useEffect(() => { let subscribed = true @@ -28,35 +27,15 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ client, fluidConfig.konnectorConfig.slug ) - const kss = new KonnectorStatusService(client) - async function getData() { const _konnector: Konnector = await konnectorService.fetchKonnector() - if (!_konnector || !_konnector.slug) { - throw new Error( - `Could not find konnector for ${fluidConfig.konnectorConfig.slug}` - ) - } if (subscribed && _konnector) { setKonnector(_konnector) } - - const allTriggers = await kss.getAllTriggers() - if (allTriggers) { - const lastTrigger = allTriggers - .filter( - trigger => - trigger.worker === 'konnector' && - trigger.message.konnector === fluidConfig.konnectorConfig.slug - ) - .sort((a, b) => (new Date(a) > new Date(b) ? 1 : -1)) - .shift() - if (subscribed && lastTrigger) { - setLastTrigger(lastTrigger) - } + if (subscribed) { + setLoaded(true) } } - getData() return () => { subscribed = false @@ -65,11 +44,10 @@ const KonnectorViewer: React.FC<KonnectorViewerProps> = ({ return ( <> - {!konnector ? null : ( + {!loaded ? null : ( <KonnectorViewerCard fluidConfig={fluidConfig} konnector={konnector} - trigger={lastTrigger} isParam={isParam} /> )} diff --git a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx b/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx index 5a196ab0d35e78ab2f0507750c083b9d339e5dbf..8ea145a2b6b44152e929e640653828e89e62aebd 100644 --- a/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx +++ b/src/components/ContentComponents/KonnectorViewer/KonnectorViewerCard.tsx @@ -1,11 +1,6 @@ -import React, { useState, useEffect, useRef, useContext } from 'react' -import { ConnectionService } from 'services/connectionService' -import { TriggerService } from 'services/triggersService' +import React, { useState, useEffect, useRef } from 'react' import { AccountService } from 'services/accountService' -import { AppContext } from 'components/Contexts/AppContextProvider' - -import { JobService, JobState } from 'services/jobsService' import { FluidType } from 'enum/fluid.enum' import { withClient, Client } from 'cozy-client' @@ -18,21 +13,21 @@ import chevronUp from 'assets/icons/ico/chevron-up.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' -import failurePicto from 'components/ContentComponents/KonnectorViewer/picto-failure.png' -import KonnectorStatusService from 'services/konnectorStatusService' +import failurePicto from 'assets/png/picto/picto-failure.png' import IFluidConfig from 'services/IFluidConfig' +import KonnectorNotFound from 'components/ContentComponents/Konnector/KonnectorNotFound' import KonnectorForm from 'components/ContentComponents/Konnector/KonnectorForm' -import KonnectorOAuthForm from 'components/ContentComponents/Konnector/KonnectorOAuthForm' -import KonnectorLoading from 'components/ContentComponents/Konnector/KonnectorLoading' import KonnectorResult from 'components/ContentComponents/Konnector/KonnectorResult' +import KonnectorLaunch from 'components/ContentComponents/Konnector/KonnectorLaunch' -import { Konnector, Trigger } from 'doctypes' +import { Konnector, Trigger, TriggerState } from 'doctypes' +import { JobState } from 'services/jobsService' +import { TriggerService } from 'services/triggersService' interface KonnectorViewerCardProps { fluidConfig: IFluidConfig konnector: Konnector - trigger: any | null client: Client isParam: boolean t: Function @@ -41,34 +36,32 @@ interface KonnectorViewerCardProps { const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ fluidConfig, konnector, - trigger, client, isParam, t, }: KonnectorViewerCardProps) => { - const [login, setLogin] = useState<string>('') - const [password, setPassword] = useState<string>('') - const [loading, setLoading] = useState<boolean>(false) - const [updating, setUpdating] = useState<boolean>(false) - const [konnectorAccountId, setAccountId] = useState<string>('') - const [isKonnectorAcc, setAccount] = useState<boolean>(false) + const [account, setAccount] = useState<Account | null>(null) + const [trigger, setTrigger] = useState<Trigger | null>(null) + const [triggerState, setTriggerState] = useState<TriggerState | null>(null) + const [shouldLaunch, setLaunch] = useState<boolean>(false) + const [setActive, setActiveState] = useState('') const [setHeight, setHeightState] = useState('0px') - const [updateTrigger, setUpdateTrigger] = useState<any>(null) - const [updateDate, setUpdateDate] = useState<string>( - new Date('0001-01-01T00:00:00Z').toDateString() - ) - // const [frequency, setFrequency] = useState<string>('daily') - const [error, setError] = useState<string>('') + const [jobState, setJobState] = useState<string>('') - const [launchedJob, setlaunchedJob] = useState<any>(null) + + const [isLoading, setLoading] = useState<boolean>(true) + const content: React.MutableRefObject<null> = useRef<null>(null) const type: string = fluidConfig.konnectorConfig.type const fluid: FluidType = getFuildType(type) const iconType = getPicto(fluid) - const context = useContext(AppContext) const iconAddType = isParam ? getParamPicto(fluid) : getAddPicto(fluid) + const loginFailed: boolean = + triggerState != null && + triggerState.last_error != undefined && + triggerState.last_error === 'LOGIN_FAILED' const toggleAccordion = () => { setActiveState(setActive === '' ? 'active' : '') @@ -82,260 +75,126 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ ) } - const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { - try { - e.preventDefault() - setError('') - setLoading(true) - if (!login || !password) { - setError('Please enter a login and password') - setLoading(false) - return null - } - setJobState(JobState.Running) - const connectionService = new ConnectionService( - client, - fluidConfig.konnectorConfig.slug, - login, - password - ) - const data = await connectionService.connectNewUser() - setLoading(false) - if (!data) { - setError('Error during the user connection') - setLoading(false) - setJobState(JobState.Errored) - return null - } - trigger = await KonnectorStatusService.getSingleTrigger( - client, - data.trigger_id - ) - setUpdateTrigger(trigger) - setlaunchedJob(data) - setUpdateDate(new Date(data.queued_at).toDateString()) - setAccountId(data.message.account) - setError('Connected') - setAccount(true) - return data - } catch (error) { - setLoading(false) - setError(error.message) - } + const getKonnectorStateMarkup = () => { + return jobState === JobState.Errored ? ( + <img className="state-icon" src={failurePicto}></img> + ) : ( + '' + ) } - const deleteAccount = async () => { - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - if (!account) { - setError('Error fetching account') - setLoading(false) - } - const delAccount = await AccountService.deleteAccount(client, account) - if (!delAccount) { - setError('Error during deletion account') - setLoading(false) - } - setError('') - setJobState('') - toggleAccordion() - trigger = null - setAccount(false) - await context.refreshFluidTypes() - } catch (error) { - setLoading(false) - setError(error.message) + const updateState = async (trigger: Trigger) => { + const triggerState = await TriggerService.fetchTriggerState(client, trigger) + if (triggerState) { + setTriggerState(triggerState) } } - const updateKonnector = async () => { - setError('') - setJobState('') - setUpdating(true) - try { - const account = await AccountService.getAccount( - client, - konnectorAccountId - ) - const triggersServices = new TriggerService(client, account, konnector) - if (updateTrigger !== null) { - triggersServices.setTrigger(updateTrigger) - } else { - triggersServices.setTrigger(trigger) - } - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - } catch (error) { - setUpdating(false) - setError(error.message) - } + const handleSuccessForm = (_account: Account, _trigger: Trigger) => { + setAccount(_account) + setTrigger(_trigger) + setLaunch(true) } - const initOauthAccount = async (oauthAccountId: string) => { - setError('') - setJobState('') - setLoading(true) - try { - const account = await AccountService.getAccount(client, oauthAccountId) - const triggersServices = new TriggerService(client, account, konnector) - const trigger: Trigger = await triggersServices.createTrigger() - if (!trigger) { - throw new Error(`Error during trigger creation`) - } - //Launch the creation trigger - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) - } - setUpdateTrigger(trigger) - setlaunchedJob(job) - setUpdateDate(new Date(job.queued_at).toDateString()) - setAccountId(job.message.account) - setError('Connected') - setAccount(true) - } catch (error) { - setLoading(false) - setError(error.message) + const handleKonnectorLaunch = async () => { + if (trigger) { + await updateState(trigger) } + setLaunch(false) } - const getIcon = (jobState: string) => { - switch (jobState) { - case JobState.Errored: - return failurePicto - // case JobState.Running: - // return pendingPicto - // case JobState.Done: - // return successPicto - default: - return '' - } - } - - const getKonnectorStateMarkup = (jobState: string) => { - const iconSrc = getIcon(jobState) - if (iconSrc === '') return '' - return <img className="state-icon" src={iconSrc}></img> - } - - const jobStateCallBack = async (state: string) => { - setJobState(state) - if (state !== JobState.Running) { - setLoading(false) - setUpdating(false) - setJobState(state) - context.refreshFluidTypes() - } + const handleJobState = (_jobState: JobState) => { + setJobState(_jobState) } useEffect(() => { let subscribed = true - async function getTriggerData() { - if (trigger || launchedJob) { - const jobService = new JobService(client) - let runningJob = null - - if (trigger) { - let triggerState = null - triggerState = TriggerService.fetchStateFromTrigger(trigger) - const konnectorAcc = TriggerService.fetchKonnectorAccountFromTrigger( - trigger + async function getData() { + setLoading(true) + if (konnector) { + const _account = await AccountService.getAccountByType( + client, + konnector.slug + ) + if (subscribed && _account) { + setAccount(_account) + const _trigger = await TriggerService.fetchTriggerFromAccount( + client, + _account ) - if (subscribed && konnectorAcc.konnector === konnector.slug) { - setAccountId(konnectorAcc.account) - setAccount(true) + if (subscribed && _trigger) { + setTrigger(_trigger) + await updateState(_trigger) } - if (subscribed && !launchedJob) setJobState(triggerState.status) - if (triggerState.status === JobState.Running) - runningJob = jobService.fetchJob(triggerState.last_executed_job_id) } - if (launchedJob) runningJob = launchedJob - if (runningJob) jobService.watch(runningJob, jobStateCallBack) } + setLoading(false) } - getTriggerData() + getData() return () => { subscribed = false } - }, [trigger, launchedJob]) + }, []) return ( <> - <div className={`accordion ${setActive}`}> - <div> - <div - className={`accordion-header ${setActive}`} - onClick={toggleAccordion} - > - <div className="accordion-icon"> - {!isKonnectorAcc ? ( - <StyledIcon className="icon" icon={iconAddType} size={49} /> - ) : ( - <StyledIcon className="icon" icon={iconType} size={49} /> - )} - </div> - <div className="state-picto"> - {getKonnectorStateMarkup(jobState)} - </div> - <div className="accordion-info"> - <div className="accordion-title text-16-normal"> - {!isKonnectorAcc - ? t('KONNECTORCONFIG.LABEL_CONNECTTO_' + FluidType[fluid]) - : t('FLUID.' + FluidType[fluid] + '.LABEL')} + {isLoading ? null : ( + <div className={`accordion ${setActive}`}> + <div> + <div + className={`accordion-header ${setActive}`} + onClick={toggleAccordion} + > + <div className="accordion-icon"> + {account && !loginFailed ? ( + <StyledIcon className="icon" icon={iconType} size={49} /> + ) : ( + <StyledIcon className="icon" icon={iconAddType} size={49} /> + )} + </div> + <div className="state-picto">{getKonnectorStateMarkup()}</div> + <div className="accordion-info"> + <div className="accordion-title text-16-normal"> + {account && !loginFailed + ? t('FLUID.' + FluidType[fluid] + '.LABEL') + : t('KONNECTORCONFIG.LABEL_CONNECTTO_' + FluidType[fluid])} + </div> </div> + <StyledIconButton icon={setActive ? chevronUp : chevronDown} /> </div> - <StyledIconButton icon={setActive ? chevronUp : chevronDown} /> - </div> - <div - ref={content} - style={{ maxHeight: `${setHeight}` }} - className={`accordion-content ${setActive}`} - > - {!isKonnectorAcc ? ( - !fluidConfig.konnectorConfig.oauth ? ( - <KonnectorForm - fluidConfig={fluidConfig} - login={login} - setLogin={setLogin} - password={password} - setPassword={setPassword} - loading={loading} - error={error} - handleSubmit={handleSubmit} + + <div + ref={content} + style={{ maxHeight: `${setHeight}` }} + className={`accordion-content ${setActive}`} + > + {!konnector ? ( + <KonnectorNotFound + konnectorSlug={fluidConfig.konnectorConfig.slug} + /> + ) : shouldLaunch && trigger ? ( + <KonnectorLaunch + trigger={trigger} + handleKonnectorLaunch={handleKonnectorLaunch} + /> + ) : account && !loginFailed ? ( + <KonnectorResult + account={account} + handleJobState={handleJobState} /> ) : ( - <KonnectorOAuthForm + <KonnectorForm + fluidConfig={fluidConfig} konnector={konnector} - siteLink={fluidConfig.siteLink} - onSuccess={initOauthAccount} - loading={loading} + account={account} + trigger={trigger} + handleSuccessForm={handleSuccessForm} /> - ) - ) : loading ? ( - <KonnectorLoading /> - ) : ( - <KonnectorResult - date={ - trigger - ? TriggerService.fetchDateFromTrigger(trigger) - : updateDate - } - updating={updating} - errored={jobState === JobState.Errored} - updateKonnector={updateKonnector} - deleteAccount={deleteAccount} - /> - )} + )} + </div> </div> </div> - </div> + )} </> ) } diff --git a/src/components/ContentComponents/Navbar/Navbar.tsx b/src/components/ContentComponents/Navbar/Navbar.tsx index 3b20f0257caa7a2e1603d396dd92f1dcc296f0e0..d4fc5e1f71849db51a41528f28859624ba317868 100644 --- a/src/components/ContentComponents/Navbar/Navbar.tsx +++ b/src/components/ContentComponents/Navbar/Navbar.tsx @@ -11,6 +11,7 @@ import ChallengeIconOffNotif from 'assets/icons/tabbar/defi/off-notif.svg' import ParameterIconOff from 'assets/icons/tabbar/parametre/off.svg' import ConsoIconOn from 'assets/icons/tabbar/conso/on.svg' import ParameterIconOn from 'assets/icons/tabbar/parametre/on.svg' +import logoGrandLyon from 'assets/icons/tabbar/grand-lyon.svg' interface NavbarProps { t: Function @@ -39,14 +40,10 @@ export const Navbar = ({ t }: NavbarProps) => { className="c-nav-link" activeClassName="is-active" > - <Icon - className="c-nav-icon off" - icon={ - challengeNotification - ? ChallengeIconOffNotif - : ChallengeIconOff - } - /> + {challengeNotification && ( + <div className="nb-challenge-notif">1</div> + )} + <Icon className="c-nav-icon off" icon={ChallengeIconOff} /> <Icon className="c-nav-icon on" icon={ChallengeIconOn} /> {t('Nav.challenges')} </NavLink> @@ -65,6 +62,7 @@ export const Navbar = ({ t }: NavbarProps) => { </li> </ul> </nav> + <Icon className="logo-grand-lyon" icon={logoGrandLyon} size={100} /> </aside> ) } diff --git a/src/components/ContentComponents/OAuth/OAuthForm.tsx b/src/components/ContentComponents/OAuth/OAuthForm.tsx index fb8b91c0fde9a58722dd04abcb34d7277793ab24..e05577f6e3ea10cfe9374612b2eff21121224941 100644 --- a/src/components/ContentComponents/OAuth/OAuthForm.tsx +++ b/src/components/ContentComponents/OAuth/OAuthForm.tsx @@ -6,13 +6,14 @@ import { Konnector } from 'doctypes' import { OAuthWindow } from 'cozy-harvest-lib/dist/components/OAuthWindow' import iconEnedisLogo from 'assets/icons/visu/enedis-logo.svg' -import StyledOauthButton from 'components/CommonKit/Button/StyledOauthButton' +import StyledAuthButton from 'components/CommonKit/Button/StyledAuthButton' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import StyledBlackSpinner from 'components/CommonKit/Spinner/StyledBlackSpinner' interface OAuthFormProps { konnector: Konnector onSuccess: Function + loginFailed: boolean client: Client t: Function } @@ -48,7 +49,7 @@ const OAuthForm: React.FC<OAuthFormProps> = ({ const isWaiting = status === WAITING return !konnector ? null : ( <> - <StyledOauthButton + <StyledAuthButton type="button" color="primary" onClick={handleSubmit} @@ -63,11 +64,11 @@ const OAuthForm: React.FC<OAuthFormProps> = ({ )} </div> <div className="oauthform-button-text text-18-bold"> - <div>{t('oauth.connect.' + konnector.slug + '.label1')}</div> - <div>{t('oauth.connect.' + konnector.slug + '.label2')}</div> + <div>{t('auth.' + konnector.slug + '.connect.label1')}</div> + <div>{t('auth.' + konnector.slug + '.connect.label2')}</div> </div> </div> - </StyledOauthButton> + </StyledAuthButton> {isWaiting && ( <OAuthWindow client={client} diff --git a/src/components/Hooks/userInstanceSettings.tsx b/src/components/Hooks/userInstanceSettings.tsx index f261ad4a50efe8b950724c10dbc7d435da31d67d..eb05ff8b973ea275f12537a16fe28ea3c88122d1 100644 --- a/src/components/Hooks/userInstanceSettings.tsx +++ b/src/components/Hooks/userInstanceSettings.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react' import get from 'lodash/get' import { Client } from 'cozy-client' -const useInstanceSettings = (client: Client) => { +const userInstanceSettings = (client: Client) => { const [settings, setSettings] = useState({}) const [fetchStatus, setFetchStatus] = useState('idle') @@ -12,9 +12,6 @@ const useInstanceSettings = (client: Client) => { const response = await client .getStackClient() .fetchJSON('GET', '/settings/instance') - // const response = await client.query( - // client.all('io.cozy.settings').getById('instance') - // ) setSettings(get(response, 'data.attributes'), {}) setFetchStatus('loaded') } catch (error) { @@ -30,4 +27,4 @@ const useInstanceSettings = (client: Client) => { } } -export default useInstanceSettings +export default userInstanceSettings diff --git a/src/db/challengeTypeData.json b/src/db/challengeTypeData.json index 7ab6a42b4ffd0e61aebefeb5fcdc7b213a649d9c..028b6669bd4a9523990f224d345dcd5be39eb5f1 100644 --- a/src/db/challengeTypeData.json +++ b/src/db/challengeTypeData.json @@ -48,10 +48,10 @@ { "_id": "CHA00000003", "type": 0, - "title": "Winter is leaving", - "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", + "title": "Méga Coques en stock", + "description": "Et si dans les 4 prochaines semaines vous réussissiez à consommer moins que dans les 4 dernières", "level": 3, - "duration": { "days": 7 }, + "duration": { "days": 28 }, "fluidTypes": [0, 1, 2], "relationships": { "availableEcogestures": { @@ -77,9 +77,9 @@ { "_id": "CHA00000004", "type": 0, - "title": "Mega Coques en stock", + "title": "Winter is leaving", "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", - "level": 4, + "level": 901, "duration": { "days": 7 }, "fluidTypes": [0, 1, 2], "relationships": { @@ -106,9 +106,9 @@ { "_id": "CHA00000005", "type": 0, - "title": "Giga Coques en stock", + "title": "Méga Winter is leaving", "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", - "level": 5, + "level": 902, "duration": { "days": 7 }, "fluidTypes": [0, 1, 2], "relationships": { diff --git a/src/doctypes/io-cozy-accounts.ts b/src/doctypes/io-cozy-accounts.ts index a0fc3b34a016566b417392d7547ce50ca140a91f..5969812143d8b01562adda58a08295cf2ee46d22 100644 --- a/src/doctypes/io-cozy-accounts.ts +++ b/src/doctypes/io-cozy-accounts.ts @@ -1,12 +1,21 @@ export const ACCOUNTS_DOCTYPE = 'io.cozy.accounts' +export type AuthLoginData = { + login: string + credentials_encrypted?: string + password?: string +} + +export type OAuthData = { + access_token: string + refresh_token: string + scope: string | null +} + export type Account = { _id: string account_type: string - auth: { - credentials_encrypted: string - login: string - } + auth: AuthLoginData | OAuthData identifier: string state?: string | null } diff --git a/src/doctypes/io-cozy-triggers.ts b/src/doctypes/io-cozy-triggers.ts index 1366e7b53412a70a493b260d4ad9ad5fb5b1a5c6..60e696f4ac1906bd6518670c2e709f1dffac04d1 100644 --- a/src/doctypes/io-cozy-triggers.ts +++ b/src/doctypes/io-cozy-triggers.ts @@ -3,7 +3,7 @@ export const TRIGGERS_DOCTYPE = 'io.cozy.triggers' export type Trigger = { _id: string type: string - workker: string + worker: string arguments: string message: { account: string @@ -11,12 +11,24 @@ export type Trigger = { } } +export type TriggerState = { + trigger_id: string + status: string + last_error?: string + last_executed_job_id: string + last_execution: string + last_failed_job_id: string + last_failure: string + last_manual_execution: string + last_manual_job_id: string +} + export function isTrigger(trigger: any): trigger is Trigger { return ( trigger && '_id' in trigger && 'type' in trigger && - 'workker' in trigger && + 'worker' in trigger && 'arguments' in trigger && 'message' in trigger ) diff --git a/src/locales/en.json b/src/locales/en.json index ce60b6198183a730e3ed0b55e0fb607a2970f1f4..ed3188e8c8f9b619f2c4d85870f3ba0c83dbbd40 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -121,7 +121,14 @@ "BTN_UPDATE": "Mettre à jour", "BTN_DELETE": "Supprimer", "LOADING_DATA": "Vos premières données seront disponibles dans quelques minutes et les prochaines données seront chargées automatiquement.", - "PLZ_WAIT": "Veuillez patienter..." + "PLZ_WAIT": "Veuillez patienter...", + "NOT_INSTALLED": "Le connecteur n'est pas installé. Veuillez l'installer en cliquant sur le bouton ci-dessous.", + "ERROR_NO_LOGIN_PASSWORD": "Identifiant et mot de passe requis", + "ERROR_ACCOUNT_CREATION": "Une erreur est survenue, veuillez essayer de nouveau.", + "ERROR_LOGIN_FAILED": "Identifiants invalides", + "SUCCESS": "Félicitations, vos premières données sont disponibles et les prochaines seront chargées automatiquement.", + "ERROR_DATA": "Une erreur est survenue pendant le rapatriement des données.", + "OK": "Ok" }, "INDICATOR": { "DISPLAY_OTHER_FLUID": "Voir", @@ -140,6 +147,7 @@ "ONGOING_CHALLENGE": "En cours", "ENDINGDATE_UNDEFINED": "Date non connue", "LOCKED": "Vous devez finir le défi précédent pour accéder à celui-ci", + "FULLY_LOCKED": "Ce défi sera disponible dans une prochaine mise à jour", "START": "Allons-y !", "NOT_NOW": "Pas maintenant !", "STOP": "Arrêter le défi", @@ -177,23 +185,43 @@ "WEAK": "1 nWh : économie faible" } }, - "oauth": { - "connect": { - "enedis": { - "info" : "En cliquant sur ce bouton, vous allez accéder à votre compte personnel Enedis où vous pourrez donner votre accord pour qu’Enedis nous transmette vos données.", + "auth": { + "enedis": { + "connect": { + "info" : "En cliquant sur ce bouton, vous accéderez à votre compte Enedis. Vous pourrez donner votre accord pour récupérer vos données de consommation électriques dans votre cloud personnel", "label1": "J'accède à mon", "label2": "espace client Enedis" - } - }, - "no_account" : { - "enedis" : { + }, + "no_account" : { "title": "Pas de compte Enedis ?", "text": "Vous pouvez le créer en vous munissant d'une facture d'élétricité." - } + }, + "create_account": "Je créé mon compte personnel Enedis" }, - "create_account": { - "enedis": "Je créé mon compte personnel Enedis" + "grdf" : { + "connect": { + "label1": "Je me connecte à ", + "label2": "mon compte GRDF" + }, + "no_account" : { + "title": "Pas de compte GRDF ?", + "text": "Vous pouvez le créer en vous munissant d'une facture de gaz." + }, + "create_account": "Je créé mon compte GRDF" }, + "egl" : { + "connect": { + "label1": "Je me connecte à ", + "label2": "mon compte Eau du Grand Lyon" + }, + "no_account" : { + "title": "Pas de compte Eau du Grand Lyon ?", + "text": "Vous pouvez le créer en vous munissant d'une facture d'eau." + }, + "create_account": "Je créé mon compte Eau du GL" + } + }, + "oauth": { "window": { "title": "OAuth" } @@ -206,25 +234,5 @@ "activateLink": "https://mon-compte-client.enedis.fr/" } } - }, - "nooauth": { - "connection" : { - "text" : "Je me connecte à mon compte" - }, - "creation" : { - "no_account" : "Pas de compte", - "text" : "Vous pouvez le créer en vous munissant d'une facture", - "button_text" : "Je créé mon compte" - }, - "grdf" : { - "text" : "GRDF", - "short_text" : "GRDF", - "facture" : "de gaz" - }, - "egl" : { - "text" : "Eau du Grand Lyon", - "short_text" : "Eau du GL", - "facture" : "d'eau" - } } } diff --git a/src/services/accountService.ts b/src/services/accountService.ts index 3732790237cdc495153cdc32865f62d50418ced2..74ac09a72071c57115686d02c6627c998e3e36d1 100644 --- a/src/services/accountService.ts +++ b/src/services/accountService.ts @@ -98,15 +98,26 @@ export class AccountService { } } + static updateAccount = async (client: Client, account: Account) => { + try { + const updatedAccount: Account = await accountsMutations( + client + ).updateAccount(account) + return updatedAccount + } catch (error) { + throw error + } + } + static getAccountByType = async (client: Client, type: string) => { try { const query = client .find('io.cozy.accounts') // eslint-disable-next-line @typescript-eslint/camelcase .where({ account_type: type }) - // .sortBy([{ 'cozyMetadata.updatedAt': 'desc' }]) .limitBy(1) - return await client.query(query) + const result = await client.query(query) + return result.data[0] ? result.data[0] : null } catch (error) { throw error } diff --git a/src/services/challengeDataManagerService.ts b/src/services/challengeDataManagerService.ts index c00456c30c927c433fa51d272d639818d8454b8e..6da8ff4b80846bb72f731101bafd4d876e1f866d 100644 --- a/src/services/challengeDataManagerService.ts +++ b/src/services/challengeDataManagerService.ts @@ -202,16 +202,14 @@ export default class ChallengeManager implements IChallengeManager { return 0 } - public async updateUserLevel(level?: number) { + public async updateUserLevel(level: number) { await this._client .query(this._client.find(USERPROFILE_DOCTYPE).limitBy(1)) .then(async ({ data }) => { const doc = data[0] - let actualLevel = 0 - if (level) { + let actualLevel = doc.level + if (level > actualLevel) { actualLevel = level - } else { - actualLevel = doc.level } await this._client.save({ ...doc, @@ -445,6 +443,16 @@ export default class ChallengeManager implements IChallengeManager { return challengeTypes } + public async getUserLevel() { + let userLevel + await this._client + .query(this._client.find(USERPROFILE_DOCTYPE).limitBy(1)) + .then(async ({ data }) => { + userLevel = data[0].level + }) + return userLevel + } + public async startChallenge( challenge: ChallengeType, fluidTypes: FluidType[], diff --git a/src/services/connectionService.ts b/src/services/connectionService.ts index 7108fedf68945dbeea026025100b04b65be2f58c..6fb4a3044b20f441b356c810728f4ea5757f4bbc 100644 --- a/src/services/connectionService.ts +++ b/src/services/connectionService.ts @@ -48,11 +48,15 @@ export class ConnectionService { if (!trigger) { throw new Error(`Error during trigger creation`) } - //Launch the creation trigger - const job = await triggersServices.launchTrigger() - if (!job) { - throw new Error(`Error during trigger launching`) + return { + account: account, + trigger: trigger, } - return job + //Launch the creation trigger + // const job = await triggersServices.launchTrigger() + // if (!job) { + // throw new Error(`Error during trigger launching`) + // } + // return job } } diff --git a/src/services/konnectorStatusService.ts b/src/services/konnectorStatusService.ts index 0b241f9f9152794bc6bee4ea6f2a1812677bef6f..8ab8c3bde63422f10d14660e413ac12406e8f0be 100644 --- a/src/services/konnectorStatusService.ts +++ b/src/services/konnectorStatusService.ts @@ -16,6 +16,7 @@ export default class KonnectorStatusService { return 'konnector status' } + // TODO - move to triggerservices async getTriggerbyKonnector(konnector: Konnector) { const query = this._client .find('io.cozy.triggers') @@ -120,13 +121,13 @@ export default class KonnectorStatusService { ), ]) const data: FluidType[] = [] - if (elecData && elecData.data && elecData.data[0]) { + if (elecData) { data.push(fluidConfig[FluidType.ELECTRICITY].fluidTypeId) } - if (gasData && gasData.data && gasData.data[0]) { + if (gasData) { data.push(fluidConfig[FluidType.GAS].fluidTypeId) } - if (waterData && waterData.data && waterData.data[0]) { + if (waterData) { data.push(fluidConfig[FluidType.WATER].fluidTypeId) } return data diff --git a/src/services/triggersService.ts b/src/services/triggersService.ts index 790798be45796c6c72294ba2c48fca4dfc4e0c60..1356ad79fe67302a8f15e22621535ce7c0bdbc5d 100644 --- a/src/services/triggersService.ts +++ b/src/services/triggersService.ts @@ -24,7 +24,7 @@ export class TriggerService { this._trigger = { _id: '', type: '', - workker: '', + worker: '', arguments: '', message: { account: '', @@ -65,14 +65,10 @@ export class TriggerService { 'TriggersServices : createTrigger - _triggerAttributes or _client not found' ) } - try { - this._trigger = await triggersMutations(this._client).createTrigger( - this._triggerAttributes - ) - return this._trigger - } catch (error) { - throw error - } + this._trigger = await triggersMutations(this._client).createTrigger( + this._triggerAttributes + ) + return this._trigger } setTrigger = (trigger: Trigger) => { @@ -110,4 +106,25 @@ export class TriggerService { trigger.current_state.last_execution ).toLocaleString(DateTime.DATETIME_MED) } + + static async fetchTriggerFromAccount(client: Client, account: Account) { + if (account == null) return null + const query = client + .find('io.cozy.triggers') + .where({ 'message.account': account._id }) + .sortBy([{ 'cozyMetadata.updatedAt': 'desc' }]) + .limitBy(1) + const result = await client.query(query) + return result.data[0] ? result.data[0] : null + } + + static async fetchTriggerState(client: Client, trigger: Trigger) { + if (trigger == null) return null + const triggerState = await client + .getStackClient() + .fetchJSON('GET', `/jobs/triggers/${trigger._id}`) + return triggerState.data.attributes.current_state + ? triggerState.data.attributes.current_state + : null + } } diff --git a/src/styles/components/_auth.scss b/src/styles/components/_auth.scss new file mode 100644 index 0000000000000000000000000000000000000000..7361443138f2eab3025c39a88ef28ec7b458d686 --- /dev/null +++ b/src/styles/components/_auth.scss @@ -0,0 +1,45 @@ +@import '../base/color'; +@import '../base/breakpoint'; + +.authform-button-content{ + display: flex; + justify-content: center; + align-items: center; + width: 100%; + .authform-button-text{ + display: flex; + flex-direction: column; + align-items: flex-start; + text-align: left; + margin-left: 1.25rem; + } +} + +.oauthform-button-content{ + display: flex; + justify-content: center; + align-items: center; + width: 100%; + .oauthform-button-text{ + display: flex; + flex-direction: column; + align-items: flex-start; + text-align: left; + margin-left: 1.25rem; + } +} + +.nooauthform-button-content{ + display: flex; + justify-content: center; + align-items: center; + .nooauthform-button-content-icon{ + margin: 0 1.375rem; + } + .nooauthform-button-text{ + display: flex; + flex-direction: column; + align-items: flex-start; + text-align: left; + } +} \ No newline at end of file diff --git a/src/styles/components/_challenges.scss b/src/styles/components/_challenges.scss index d602b791d94b350f581610598a4abb8b3fbf00d8..b369e632c4f9e0242d4bf5ce046e3cd7157cfa7e 100644 --- a/src/styles/components/_challenges.scss +++ b/src/styles/components/_challenges.scss @@ -238,8 +238,15 @@ position: absolute; top: 15px; @media #{$large-phone} { - top: 65px; - } + top: 65px; + } + &.--ecolyo-royal { + @extend .cp-content; + top: -38px; + @media #{$large-phone} { + top: 12px; + } + } .cm-win-badge { grid-column: 1; grid-row: 1; @@ -289,8 +296,11 @@ display: flex; flex-direction: row; justify-content: center; - margin-top: 1rem; + margin-top: 3rem; width: 100%; + @media #{$large-phone} { + margin-top: 1rem; + } .day-solo { display: flex; flex-direction: column; @@ -312,6 +322,9 @@ .futur { background-color: $grey-dark; } + .none { + opacity: 0; + } .date-label { width: 0.625rem; height: 0.625rem; @@ -343,6 +356,7 @@ display: flex; flex-direction: row; align-items: center; + justify-content: center; width: 100%; .pile-section { display: grid; @@ -469,7 +483,10 @@ max-width: 53rem; &.--ongoing { @extend .cp-valid; - width: 60%; + width: 25%; + @media #{$large-phone} { + width: 60%; + } } .cp-left-button { margin-right: 0.25rem; diff --git a/src/styles/components/_ecogesture.scss b/src/styles/components/_ecogesture.scss index b5900788c3578ae130b1d2bff715184dc220bc91..b36fb496e6ded9b15d7bedc4089839f310b02f61 100644 --- a/src/styles/components/_ecogesture.scss +++ b/src/styles/components/_ecogesture.scss @@ -54,7 +54,7 @@ display: flex; flex-direction: column; align-items: center; - justify-content: flex-end; + justify-content: space-around; &.ec-content-unlocked { padding: 0.4rem 0; } @@ -64,11 +64,6 @@ .ec-content-icon{ min-height: 50px; } - .ec-content-challenge-text { - display: flex; - flex: 1; - align-items: center; - } .ec-content-short-name { display: flex; flex: 1; diff --git a/src/styles/components/_form.scss b/src/styles/components/_form.scss index b783811df1eb23611db386fdd20d3a7eb792a389..09c504861eb784d6b31623b9210d03edb7ce7740 100644 --- a/src/styles/components/_form.scss +++ b/src/styles/components/_form.scss @@ -23,6 +23,7 @@ .form-group { display: flex; flex-direction: column; + margin: 1.5rem 0 0 0; .form-trailing-icon { float: right; position: relative; @@ -39,6 +40,10 @@ .form-message { color: $red-error; min-height: 1.25rem; + margin-top: 0.125rem; + } + .form-button { + margin-top: 0.125rem; } ::placeholder { color: $google-text-1; diff --git a/src/styles/components/_konnector.scss b/src/styles/components/_konnector.scss index 41704c852f6f11da08a06f696df59a76f3bb106e..489feeeea72d4ffa069d4e6530c6f1ff6073593c 100644 --- a/src/styles/components/_konnector.scss +++ b/src/styles/components/_konnector.scss @@ -44,6 +44,7 @@ } .state-picto { position: absolute; + display: flex; } } .accordion-update-result { @@ -111,54 +112,74 @@ } } +// KonnectorNotFound +.knotfound { + margin: 0 1.5rem; + @media #{$large-phone} { + margin: 0; + } + .knotfound-text { + color: $text-bright; + padding-top: 1rem; + } + .knotfound-button { + margin-bottom: 1rem; + } +} + // KonnectorOAuthForm -.koauthform{ +.koauthform { margin: 0 1.5rem; @media #{$large-phone} { margin: 0; } - .koauthform-text{ + .koauthform-text { color: $text-bright; padding-top: 1rem; } - .koauthform-button{ + .koauthform-button { margin-bottom: 1rem; } } -// KonnectorForm -.knooauthform{ +// KonnectorLoginForm +.kloginauthform{ margin: 0 1.5rem; @media #{$large-phone} { margin: 0; } - .knooauthform-text{ + .kloginauthform-text{ color: $text-bright; padding-top: 1rem; } - .knooauthform-button{ + .kloginauthform-button{ margin-bottom: 1rem; } } // KonnectorLoading -.kload-content { +.klaunch-content { margin: 0.5rem 1.5rem; @media #{$large-phone} { margin: 0.5rem 0; } - .kload-content-text { + .klaunch-content-text { color: $text-bright; margin: 1rem 0; } - .kload-content-text-center { + .klaunch-content-text-center { text-align: center; } + .klaunch-info-txt { + margin-top: 1.5rem; + } } + .state-icon { height: 22px; width: 22px; - margin-left: 32px; - margin-bottom: 40px; + position: absolute; + bottom: 8px; + left: 30px; } diff --git a/src/styles/components/_nav.scss b/src/styles/components/_nav.scss index ec1db7f555bc615166b4b8f493e8de82ff453579..d8e4e8189cba94b337741c0e5dc4fa6c28d7a617 100644 --- a/src/styles/components/_nav.scss +++ b/src/styles/components/_nav.scss @@ -17,6 +17,14 @@ @media #{$tablet} { background-color: $primary-color; } + .logo-grand-lyon { + position: absolute; + top: 50%; + left: 100px; + @media #{$tablet} { + display: none; + } + } } .c-nav-link { padding: 0 1rem; @@ -76,3 +84,24 @@ box-shadow: unset; } } + +.nb-challenge-notif { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + left: 42px; + bottom: 22px; + width: 1.25rem; + height: 1.25rem; + color: $dark-light; + border-radius: 50%; + border: 1px solid $dark-light; + z-index: 1; + background: $blue-radial-gradient; + font-size: 12px; + @media #{$tablet} { + left: 25px; + bottom: unset; + } +} diff --git a/src/styles/components/_nooauth.scss b/src/styles/components/_nooauth.scss deleted file mode 100644 index 3c418a4b553fd30597da402e6d05b9398d5927b9..0000000000000000000000000000000000000000 --- a/src/styles/components/_nooauth.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import '../base/color'; -@import '../base/breakpoint'; - -.nooauthform-button-content{ - display: flex; - justify-content: center; - align-items: center; - .nooauthform-button-content-icon{ - margin: 0 1.375rem; - } - .nooauthform-button-text{ - display: flex; - flex-direction: column; - align-items: flex-start; - text-align: left; - } -} \ No newline at end of file diff --git a/src/styles/components/_oauth.scss b/src/styles/components/_oauth.scss deleted file mode 100644 index 6e85346fac53804cb39dc80a2fa13607d6b555e0..0000000000000000000000000000000000000000 --- a/src/styles/components/_oauth.scss +++ /dev/null @@ -1,17 +0,0 @@ -@import '../base/color'; -@import '../base/breakpoint'; - -.oauthform-button-content{ - display: flex; - justify-content: center; - align-items: center; - .oauthform-button-content-icon{ - margin: 0 1.375rem; - } - .oauthform-button-text{ - display: flex; - flex-direction: column; - align-items: flex-start; - text-align: left; - } -} \ No newline at end of file diff --git a/src/styles/index.scss b/src/styles/index.scss index 3844047faac0fc9594ef183f76a91e9f57b1650e..79e0b9103d483cb5de60521f71f4276d6130e4f0 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -22,8 +22,7 @@ @import 'components/modal'; @import 'components/faq'; @import 'components/splash'; -@import 'components/oauth'; -@import 'components/nooauth'; +@import 'components/auth'; :root { --blue: #{$blue};