Compare commits
675 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fd45a00a3 | |||
| 36571b5cca | |||
| 7ad96db928 | |||
| 5ce9f108cd | |||
| 99bb737647 | |||
| e2ef540aac | |||
| 6e6119ffaf | |||
| 473edf7472 | |||
| 96b08352a0 | |||
| 7a45d19d2b | |||
| c73b08c1aa | |||
| b6e4347abb | |||
| adb9d2ccde | |||
| edd7f705ab | |||
| ea372ba0f2 | |||
| 779b722662 | |||
| 6bb8baf636 | |||
| 083f7b9c16 | |||
| 0eed896aec | |||
| e8ae224414 | |||
| fa3dcd8fd2 | |||
| 2c0c0a2632 | |||
| 501ee57430 | |||
| 0f6ec17fef | |||
| 112541275b | |||
| e08b879691 | |||
| 527351c232 | |||
| 0ed8da11e1 | |||
| f522e4389e | |||
| efb6527b9b | |||
| ba43bcddc7 | |||
| 3523cc95ee | |||
| c0d32cee12 | |||
| 3b6aa331db | |||
| fe7932e5ae | |||
| 05b64ac44c | |||
| 446e13ceba | |||
| 6570e28bbd | |||
| d146da7ab3 | |||
| ae06e730ad | |||
| f386b93dd2 | |||
| cebdf4d1d2 | |||
| e6738a1565 | |||
| ce9b8b6031 | |||
| 00eac45913 | |||
| 89719a8bbc | |||
| 901280363e | |||
| 6612f91047 | |||
| 5e7fabe1ce | |||
| e05aadd5c3 | |||
| 567a282dcf | |||
| 3eb5f6852f | |||
| 7ddab7d95c | |||
| 400340aaef | |||
| 5a43a2a9e1 | |||
| 69848dbe38 | |||
| ad40f73997 | |||
| fbdc460ee0 | |||
| e30c7f8daf | |||
| 9a87bc6627 | |||
| a2042e48e7 | |||
| cc02119938 | |||
| b2affbd87b | |||
| 0526286554 | |||
| 2b5e024bd0 | |||
| ed74d6f882 | |||
| ff2225a47b | |||
| 23832d2c4f | |||
| 655bce3fca | |||
| 1c33d5f7cf | |||
| 3737e34f32 | |||
| 36644308b8 | |||
| 080deea1e5 | |||
| a0caba4009 | |||
| 3c1deffbf1 | |||
| c07482e340 | |||
| cc80821658 | |||
| b0cbafdf44 | |||
| 6869085f6d | |||
| 9f8e61069c | |||
| 0b6b8451d1 | |||
| 17041758cb | |||
| ddfbd344f6 | |||
| 64d750fb9e | |||
| ebe9f62a8f | |||
| fd8cb20f52 | |||
| 7526059543 | |||
| d0bd5103cf | |||
| c9a3ae62ce | |||
| 7d2ca2bad3 | |||
| ab59d8872d | |||
| 4da821ce79 | |||
| 501af4b3f6 | |||
| 3c61032cf0 | |||
| af4e3207b4 | |||
| 3b835a4461 | |||
| f5a1fce6ac | |||
| 604b030045 | |||
| 9857a2363f | |||
| 892084cd41 | |||
| 47fca4b73a | |||
| d1c865dbd8 | |||
| f35b9d2037 | |||
| a4e039706d | |||
| 13f4790a36 | |||
| 829b1476d6 | |||
| 5c579cb592 | |||
| 2464d4b6bc | |||
| cb6729aa77 | |||
| d396fcc83a | |||
| d888c173f4 | |||
| 9d0f25984e | |||
| f2550a2df7 | |||
| 8f034b61de | |||
| 53ccf11d87 | |||
| fcfae5408b | |||
| 370e58494c | |||
| 9d5fbedb2c | |||
| cf7d4fc6bc | |||
| a960426a6d | |||
| 87862a26df | |||
| c29681cb7c | |||
| f01af4813e | |||
| de9a4f7b38 | |||
| b394f5fa5d | |||
| 9d9c777c93 | |||
| bddf254005 | |||
| 1a90223e22 | |||
| b963814067 | |||
| 921030d6c0 | |||
| 80e03c29e9 | |||
| 6cb252e031 | |||
| bfbb31ce25 | |||
| 849e0a5b18 | |||
| 9f497b83d5 | |||
| 344bdca41c | |||
| a05780ee39 | |||
| f7ac5e669f | |||
| b28ec325b8 | |||
| 6a6bfa3e39 | |||
| 753c990fc6 | |||
| 8069a54d1a | |||
| 9e2e58d45c | |||
| e7c59003cf | |||
| 8c435db886 | |||
| 2d41de8568 | |||
| a4a5096eb8 | |||
| d7128d7ddc | |||
| e06b23e73a | |||
| 90aefa56c2 | |||
| 6ceae61685 | |||
| d8033ec4ef | |||
| b4b9a0f5c4 | |||
| d4933d9535 | |||
| 024c34753c | |||
| d348f0d618 | |||
| 4662d0b30d | |||
| e6b873b861 | |||
| 88d0f8a222 | |||
| 15b94ea080 | |||
| e4218c708e | |||
| 772aa99c21 | |||
| e1c6729d04 | |||
| 4bd910fe6b | |||
| fe1b554dce | |||
| a75b4509a0 | |||
| e36330bcc2 | |||
| 51b36943ef | |||
| 78f80b4d7b | |||
| 65beb482c2 | |||
| ec7cab4b98 | |||
| 2089bd5c76 | |||
| 36cfa25d09 | |||
| 2394319605 | |||
| fb002efaec | |||
| 4b49371b08 | |||
| b5261ff528 | |||
| 61f03f4208 | |||
| d140bfeb38 | |||
| edb1ba680a | |||
| b3f927a46f | |||
| 288b8b1f51 | |||
| 416eb4129c | |||
| 613f0e755f | |||
| ed8562f95b | |||
| c95775194d | |||
| 783b65b5e9 | |||
| 38fef5c514 | |||
| e6286c97e4 | |||
| 55a2979814 | |||
| b5dc00eb7b | |||
| 813c085e3b | |||
| 550688a99e | |||
| 061e756237 | |||
| d6091fb496 | |||
| 52221edbee | |||
| 4cf4347611 | |||
| 60c9fa9b6f | |||
| 80c48533d7 | |||
| 3dfd5d2c89 | |||
| ba4118be38 | |||
| 9967eab3c5 | |||
| 861a0bf45f | |||
| 1b7089a694 | |||
| d6e345b2f3 | |||
| d720b08ccb | |||
| b773d04e4d | |||
| bd247c9ebd | |||
| e08c0f3605 | |||
| f86fcfe3d8 | |||
| 6629cd8156 | |||
| 5a29559ee7 | |||
| eab5e57e4b | |||
| 90ce3b1e6d | |||
| 97a438ac39 | |||
| a804655113 | |||
| 079cc18f5f | |||
| 18abd46de6 | |||
| 4728107d9a | |||
| 52f5c361d2 | |||
| efcd8002d5 | |||
| 2d9017bd5d | |||
| 7637ffd11c | |||
| b7fa1c403e | |||
| 48936e7150 | |||
| 3dcd837a9e | |||
| a859e045cd | |||
| 3a4637bc1a | |||
| a230f02005 | |||
| 3c4e06a84e | |||
| 0c8b1b5fe5 | |||
| 74b4de0543 | |||
| 730a61cddb | |||
| df7f3604f9 | |||
| 15e74aab9f | |||
| 7dd7071a0a | |||
| 8393f22ea6 | |||
| 985ed33346 | |||
| c357846ecf | |||
| 7685f304bf | |||
| 327d9d05c4 | |||
| f577733522 | |||
| 2a82a3758e | |||
| 80f7ef2da4 | |||
| 1ac0b286a9 | |||
| 20805afd31 | |||
| 47376ff7b7 | |||
| a787a1f870 | |||
| 2ca49a9566 | |||
| a4961638f2 | |||
| 583ab9480f | |||
| 73bf25904f | |||
| 5206050f6a | |||
| 5ca4abd2ac | |||
| 9bfc3ac023 | |||
| 088ad46a2f | |||
| d115add2ea | |||
| 9cdb2889d5 | |||
| 5cd1391ade | |||
| e74e915060 | |||
| e94f20ef78 | |||
| 25a335f2f0 | |||
| 26d243f658 | |||
| 3c0702022d | |||
| 9bfe4fb198 | |||
| 90b56c0471 | |||
| 28bb8e5299 | |||
| a18accb39c | |||
| 092b6cb655 | |||
| 24d733b4f7 | |||
| e3170e07dd | |||
| bda41ff48b | |||
| bf9be45efd | |||
| 323fe4b2c1 | |||
| 81938e714d | |||
| 6ab1b74342 | |||
| 1bedb3e467 | |||
| b1cd82c346 | |||
| a8df08e2f2 | |||
| 6b2a99caee | |||
| fbaf665ce9 | |||
| 4cfd8bde6f | |||
| b6e1a8b864 | |||
| 80eeb91f79 | |||
| 0063ef68f5 | |||
| eb6d281332 | |||
| ff0bb50414 | |||
| fb528ebc84 | |||
| a0504ec5fb | |||
| e40110a038 | |||
| 7512ed04e2 | |||
| 5c23dc39bf | |||
| 5488844845 | |||
| 33670ee1d6 | |||
| f569559f56 | |||
| d4083e4701 | |||
| ee808cffb2 | |||
| 99f1c4d117 | |||
| a54e85d359 | |||
| d10d03e6bb | |||
| 1d959f88f2 | |||
| 31d566a9c7 | |||
| 127e5e9d8d | |||
| c57b1402e9 | |||
| 3c759d3572 | |||
| 51deea06b8 | |||
| b9ba409c99 | |||
| 77d1f9954e | |||
| a3ad2c0030 | |||
| bc95306a15 | |||
| 45efd6b556 | |||
| 1ddbc65e14 | |||
| da90a3283a | |||
| efaf7ef6dd | |||
| 9999cc3417 | |||
| f931c25a9e | |||
| 0ac6abb5b5 | |||
| 79b8223c8c | |||
| 0633ece7ba | |||
| a474f0740f | |||
| 65e2ab90d2 | |||
| a7d31d6f4d | |||
| 042394d87d | |||
| 9d2fefe4ed | |||
| 3a08038da5 | |||
| 4cfad7bbca | |||
| cf8f8086d7 | |||
| 36f7d5698b | |||
| b0fd71ee0f | |||
| 0f8fe8cfd8 | |||
| c0e209ab67 | |||
| 6c091c3fac | |||
| 8a7b425e90 | |||
| 7ee7ab7e90 | |||
| ae603ba41d | |||
| 58c36dbf2d | |||
| d70ab851aa | |||
| de57d33a17 | |||
| 3caa419579 | |||
| fd9bc060cc | |||
| d705f062ed | |||
| a15a2ad831 | |||
| 4372c7a1e5 | |||
| d0dd09cce6 | |||
| db5670ffe2 | |||
| dd6e2cdf4b | |||
| 154fe260dc | |||
| 583e568085 | |||
| 3a137d4282 | |||
| 9c47f93e17 | |||
| 2b585216da | |||
| 77be2fd0c9 | |||
| 96ccc64eae | |||
| 9e9527686f | |||
| 6b0cb02afc | |||
| 4f5b03cdff | |||
| 63042e0e83 | |||
| 35ef8f28c0 | |||
| a9c3d382cf | |||
| de45ba36c6 | |||
| 7b227fb1f5 | |||
| b2e75da6f8 | |||
| 7aaf059e55 | |||
| 608283a6e6 | |||
| 9235571dcb | |||
| 5e806b6c64 | |||
| 989d215ef8 | |||
| 9daacb05d6 | |||
| b152cb5aea | |||
| 9ec2bf5cb6 | |||
| b8b18d5403 | |||
| cfc895b5dd | |||
| 287170ac96 | |||
| 6b636d0af3 | |||
| bd0e394d8a | |||
| 7f3764ae67 | |||
| 7363e9133b | |||
| 4d17137922 | |||
| fa24a9b292 | |||
| 4d637240aa | |||
| 2b617226f6 | |||
| 988440f5fd | |||
| 9d0d9f04ff | |||
| ab76048563 | |||
| 62e8f483c9 | |||
| 0ab872a2db | |||
| ed38d28fc5 | |||
| 2368f3f520 | |||
| 7a861a77d1 | |||
| f21bbadb5b | |||
| beab21d889 | |||
| c77ec3c9b0 | |||
| d8c289a6a8 | |||
| 77ba7c5f94 | |||
| 39baa0ab66 | |||
| 1c05c16041 | |||
| 62d13d66e5 | |||
| 1db0fab8e1 | |||
| 7e39a35997 | |||
| 8f97f706f0 | |||
| f5a6e86c61 | |||
| 9eeee67f03 | |||
| 314407806e | |||
| 9b196e3106 | |||
| c380260645 | |||
| 06d620ecba | |||
| ba41a79caa | |||
| 0c443bc341 | |||
| 9db04e1f5b | |||
| 1be8ac03fe | |||
| dfaeb05bf4 | |||
| 0b0a94e98b | |||
| 8878c1c57a | |||
| afe458cefd | |||
| bf4fa0dd30 | |||
| 9407dab844 | |||
| e34cc49a4f | |||
| 1de11320fd | |||
| dffd974ba6 | |||
| 7f70c3951e | |||
| d8b66eda0d | |||
| 42ad13bb93 | |||
| 1bc5a9c2c8 | |||
| c3517cdced | |||
| baf8c7cea0 | |||
| 3d4d90e46b | |||
| c46cddcdaf | |||
| c5e631064a | |||
| e5bdbe0bc8 | |||
| 14e216d448 | |||
| 1a7d811e8a | |||
| af478d49bc | |||
| 902665f0af | |||
| c042ae0ef7 | |||
| 988f179f14 | |||
| 77ad925b02 | |||
| d1a6aef77f | |||
| 2daa191b90 | |||
| b02dcb5baf | |||
| acc007ca81 | |||
| 142f673a17 | |||
| 40725cef90 | |||
| 389d2f4fac | |||
| d70d27299c | |||
| a6b8a7f555 | |||
| 518e5e75dc | |||
| 91b056274e | |||
| ce9dbdfb1c | |||
| 878335ef6d | |||
| 064979dd90 | |||
| 87d283763b | |||
| 5ae3750c3f | |||
| ebbb380c71 | |||
| f430471cd4 | |||
| 4bca554c4f | |||
| 61f547494b | |||
| 3c74d6e809 | |||
| 8045f38eac | |||
| 94dd549943 | |||
| ae47b6a838 | |||
| 9de0a394d7 | |||
| ce29a6438d | |||
| ef93e66968 | |||
| e32903baa5 | |||
| 753e76ccb1 | |||
| 7a523c50a0 | |||
| 7f5e323cd9 | |||
| b9e6c2ad21 | |||
| b42ad17f06 | |||
| ca8999a2d1 | |||
| bbe4a2eaa7 | |||
| ee6923c0a7 | |||
| b97ff9cc7c | |||
| 64f1c40594 | |||
| 13b55f50d7 | |||
| 2bbbcbae82 | |||
| 0b49b1a07b | |||
| eb08fffb3e | |||
| 10180345d3 | |||
| dbeb6bac94 | |||
| bdfa5d911c | |||
| cb71bd841c | |||
| 3d9a4fb063 | |||
| 736b83d8d3 | |||
| 20b7ac31f4 | |||
| 9a7f9c3993 | |||
| 13fb868117 | |||
| 360bd1d60f | |||
| 4a75e4de1e | |||
| 55247fde6b | |||
| 0b2dd9a546 | |||
| 69906acd22 | |||
| 985ef46648 | |||
| e0af97ac41 | |||
| 73b5f427cd | |||
| 9e1449e7fa | |||
| 007fdd2fac | |||
| dc62eac5e4 | |||
| b921d3eb46 | |||
| c50f6fe858 | |||
| 7e08ee6b6f | |||
| 29e1c2550f | |||
| a96f83dca5 | |||
| dc8687b794 | |||
| c216aec6c0 | |||
| 97bb5d3a75 | |||
| 956deb7dc4 | |||
| 512e5cf74f | |||
| 8951d83423 | |||
| 3a0e9c1938 | |||
| eb27aa74ab | |||
| 20bf564648 | |||
| bda3eb3302 | |||
| 3ad44318b1 | |||
| f703a2bd8f | |||
| 55c921c214 | |||
| 6188dc60a8 | |||
| fd4bad6477 | |||
| 7fa2bb1e10 | |||
| 3c5b4f4f24 | |||
| dbcb06cac8 | |||
| af417f1b3d | |||
| 7d918ad5ec | |||
| 569f7aac9f | |||
| 92df989fe3 | |||
| ba116d0919 | |||
| 6e553077d2 | |||
| d2a87c0daa | |||
| abfe12415f | |||
| 57dac37437 | |||
| 4df163eb30 | |||
| 3b5bb425a1 | |||
| bbe9970485 | |||
| 037424f103 | |||
| 6f4530c63c | |||
| 551b6af2d5 | |||
| 570c8482f6 | |||
| fe84754a0e | |||
| 8cc797f2ca | |||
| 825e658401 | |||
| 416758c178 | |||
| 398b2ab85a | |||
| 40e272ade1 | |||
| fb3809039f | |||
| 199bfb3ccb | |||
| 7f571b3917 | |||
| d4640944ee | |||
| 61891d7cd7 | |||
| 035137df0f | |||
| 7354b66453 | |||
| 652d3bac27 | |||
| b6c35ba444 | |||
| 9bb874ae2f | |||
| 7ce22883d2 | |||
| 2d6210c1e7 | |||
| 8f6c22d109 | |||
| 2c4bff2e7d | |||
| 4f66501811 | |||
| 3ad52a1e0b | |||
| b3c335fb25 | |||
| f1383d875e | |||
| 648517180a | |||
| 539e2d301f | |||
| dfe4a9aeef | |||
| ddae8c9b82 | |||
| 0f1762312f | |||
| b22c9f7fbd | |||
| daeb381f8b | |||
| f19d065c37 | |||
| 258ad73daa | |||
| d2eb717aab | |||
| 860b420608 | |||
| 3f18f70649 | |||
| b6ab0bfd2b | |||
| b17f156f38 | |||
| 9a333d58c4 | |||
| 58c34e6c47 | |||
| 1869fa4a07 | |||
| 8a16b8a47a | |||
| 212a2a1afc | |||
| e4c2b7fe2c | |||
| 788c58ecad | |||
| 4860438487 | |||
| ec8d55640d | |||
| 1ac2510ca3 | |||
| 53a7045e43 | |||
| b368f477ca | |||
| 87346e9ac5 | |||
| 8565f2142e | |||
| 24b957365f | |||
| 39a86b5a62 | |||
| 6febb3a654 | |||
| 409c396e68 | |||
| ecf6605eb5 | |||
| d88c50e3bb | |||
| a1f8618843 | |||
| d9820e49e0 | |||
| 8b876369c3 | |||
| 86aa81bdcc | |||
| 23c38245a3 | |||
| 48b8851657 | |||
| 2ed985518d | |||
| 9d06e89f5f | |||
| 3a49f735b6 | |||
| 69b1e087f9 | |||
| 980fc787bc | |||
| 39f33690dc | |||
| 0f81c9bab8 | |||
| ad98b0513a | |||
| f891479456 | |||
| fcc5bcad86 | |||
| 9c4364f082 | |||
| ee8de0330e | |||
| 95d906066f | |||
| b42d5abbb1 | |||
| 5f886f2a10 | |||
| bfcf73ae74 | |||
| 769d07440d | |||
| 435e6ad725 | |||
| f569ff237e | |||
| b85a4c9085 | |||
| b3b953196e | |||
| baa0cfa8c1 | |||
| 9b82a9cf59 | |||
| 39cd6cba2a | |||
| 7b1aee8a44 | |||
| e04465a751 | |||
| 09b586d94d | |||
| 48ac5c7d2c | |||
| 43d9a05bd6 | |||
| 68f44ab006 | |||
| 554c641c33 | |||
| 633e89628c | |||
| 5012b1e97a | |||
| 6d72c814d0 | |||
| 57d1e641a5 | |||
| 0b392222cf | |||
| e05f5c2b8f | |||
| 9a3bd880fb | |||
| 07b5e79d2b | |||
| c1e3c17504 | |||
| 354fefdae6 | |||
| ba4aba6a6d | |||
| f392062ece | |||
| 927eb81017 | |||
| ee4ef59ed3 | |||
| 063fb19dbd | |||
| 9169ee38ad | |||
| e68b61ce35 | |||
| 1f901b9a0a | |||
| 3abfff129c | |||
| 1267aa15b1 | |||
| 60e22554a8 | |||
| 72d98e057c | |||
| 0706fda67c | |||
| 1bdbd2f77d | |||
| 4daa99a020 | |||
| 89221a04ed | |||
| 765075e99b | |||
| 245f09b20f | |||
| d2649fdc16 | |||
| 2c08e9e68b | |||
| 5009409a51 | |||
| a8e4e739a2 | |||
| e1579a9d1d | |||
| 21510e7b71 | |||
| 59782cda43 | |||
| 6947ba6b33 | |||
| 71ad5427cb | |||
| c14de8e119 | |||
| 7e67bdcb45 | |||
| 5a0267e20f | |||
| aa3ef3456d | |||
| 03c01d842d | |||
| 502490ced1 |
@@ -1,3 +0,0 @@
|
||||
nextjs/node_modules/
|
||||
/node_modules/
|
||||
/nextjs/node_modules/
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/volume/
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
CLIENT=tt-app
|
||||
|
||||
DOCKERCOMPOSE := $(shell command -v docker-compose 2> /dev/null)
|
||||
USAGE := Usage: make ENV={prod|preprod|dev} {SYS=windows|linux} {docker-build|docker-start|docker-stop|docker-restart}
|
||||
|
||||
ifndef DOCKERCOMPOSE
|
||||
$(error docker-compose is not available)
|
||||
endif
|
||||
|
||||
ifndef ENV
|
||||
$(info Environment variable is not set)
|
||||
$(info $(USAGE))
|
||||
$(error Exiting)
|
||||
endif
|
||||
|
||||
SYS=linux
|
||||
|
||||
DC_OVERRIDE := $(shell test -e conf/docker-compose.override.yml && echo "--file conf/docker-compose.override.yml")
|
||||
DC_OVERRIDE_ENV := $(shell test -e conf/docker-compose.$(ENV).yml && echo "--file conf/docker-compose.$(ENV).yml")
|
||||
|
||||
ifeq ($(SYS),linux)
|
||||
DC=ENV=$(ENV) CLIENT=$(CLIENT) "$(DOCKERCOMPOSE)" --project-name "$(CLIENT)_$(ENV)" --file conf/docker-compose.yml $(DC_OVERRIDE) $(DC_OVERRIDE_ENV)
|
||||
else
|
||||
DC=ENV=$(ENV) CLIENT=$(CLIENT) "$(DOCKERCOMPOSE)" --project-name "$(CLIENT)_$(ENV)" --file conf/docker-compose.yml $(DC_OVERRIDE) $(DC_OVERRIDE_ENV)
|
||||
endif
|
||||
|
||||
|
||||
default:
|
||||
$(info $(USAGE))
|
||||
$(error Exiting)
|
||||
|
||||
docker-build:
|
||||
$(DC) build
|
||||
$(DC) up -d
|
||||
|
||||
docker-start:
|
||||
$(DC) start
|
||||
|
||||
docker-stop:
|
||||
$(DC) stop
|
||||
|
||||
docker-restart: docker-stop docker-start
|
||||
|
||||
docker-rm: docker-stop
|
||||
$(DC) rm
|
||||
|
||||
sys:
|
||||
@echo $(SYS)
|
||||
@echo $(DC)
|
||||
@@ -0,0 +1,28 @@
|
||||
## 1. Droits
|
||||
|
||||
mot de pass de depart (juste pour info, pas d'utilité)
|
||||
Jenkins : 8c628b7abfe242a397ac7fbedfd4de8c
|
||||
|
||||
Besoin d'effectuer la commande sudo chmod 666 /var/run/docker.sock dans le container Jenkins.
|
||||
docker exec -it --user root $(containerName) /bin/bash
|
||||
|
||||
docker exec -it -u 0 60344f0b414b bash
|
||||
|
||||
## 2. post install
|
||||
Installer yarn
|
||||
|
||||
## 3. run
|
||||
localhost:8080
|
||||
|
||||
>stop curent TTapp dev run to free 3000 port
|
||||
|
||||
### 1. login:password
|
||||
>sglatz:yVqSfBDRPp07
|
||||
|
||||
### 2. gitea password
|
||||
>tt-jenkins:
|
||||
|
||||
#### 2. As Kanye West said:
|
||||
|
||||
> We're living the future so
|
||||
> the present is our past.
|
||||
@@ -0,0 +1,17 @@
|
||||
version: '2'
|
||||
services:
|
||||
|
||||
jenkins:
|
||||
hostname: jenkins
|
||||
image: jenkinsci/blueocean
|
||||
ports:
|
||||
- '8080:8080'
|
||||
- '50000:50000'
|
||||
privileged: true
|
||||
volumes:
|
||||
# enable persistent volume (warning: make sure that the local jenkins_home folder is created)
|
||||
- '../volume/jenkins_data:/var/jenkins_home'
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
restart: always
|
||||
volumes:
|
||||
jenkins_data:
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"cypress": "^3.4.1"
|
||||
}
|
||||
}
|
||||
+1217
File diff suppressed because it is too large
Load Diff
-25
@@ -1,25 +0,0 @@
|
||||
SERVER_URL=http://localhost:80
|
||||
MONGO_URI=mongodb://declic:declic42@ds115653.mlab.com:15653/next-auth
|
||||
FACEBOOK_ID=2332071840142634
|
||||
FACEBOOK_SECRET=afb8bbca72bd8c517c64f81e998e4f12
|
||||
PROD_FACEBOOK_ID=1800752833559014
|
||||
PROD_FACEBOOK_SECRET=454de729b1e58f506f94c8b50ca5c011
|
||||
TT_LINKEDIN_ID=86m6hegwyj8lbo
|
||||
TT_LINKEDIN_SECRET=LIOibqDszsP2vQSr
|
||||
LINKEDIN_ID=77k1m8qx7w6ygj
|
||||
LINKEDIN_SECRET=mbcnVjyy2vqAZz1B
|
||||
GOOGLE_ID=
|
||||
GOOGLE_SECRET=
|
||||
TWITTER_KEY=
|
||||
TWITTER_SECRET=
|
||||
EMAIL_FROM=contact@application.bzh
|
||||
EMAIL_SERVER=mail.gandi.net
|
||||
EMAIL_PORT=587
|
||||
EMAIL_SECURE=true
|
||||
EMAIL_USERNAME=noreply@application.bzh
|
||||
EMAIL_PASSWORD=PWApwaPWApwa56!!!
|
||||
FIREBASE_API_KEY=AAAAHlF5rsI:APA91bHXvjGDJRLzVvqmBDPM1Et96Bw0Iuvc7EGe9wt4a_lSJijDlbZ3RT96seLDK-QM6Mf8qQ4dH6MI9iWt1_d3uzKG2bxr56zltglZ3Z9xU8gz2iJeRGmlOd0YdV43IumIVe6dIOr-
|
||||
RECAPTCHA_VERIFY_URL=https://www.google.com/recaptcha/api/siteverify
|
||||
RECAPTCHA_SECRET=6LcCvHcUAAAAAIG-_iR5AXqkwxINfgqxKZ_kG244
|
||||
MJ_APIKEY_PUBLIC=355697fac93fca865660734b4fdffaeb
|
||||
MJ_APIKEY_PRIVATE=ddecf75321ca89c09e561857b13534c8
|
||||
+6
-2
@@ -1,4 +1,8 @@
|
||||
|
||||
/node_modules/
|
||||
.next/
|
||||
.gitignore
|
||||
.env
|
||||
ecosystem.config.js
|
||||
yarn-error.log
|
||||
robots.txt
|
||||
/out/
|
||||
yarn.lock
|
||||
@@ -0,0 +1,18 @@
|
||||
FROM keymetrics/pm2:8-alpine
|
||||
|
||||
ENV TERM=xterm
|
||||
|
||||
|
||||
# RUN pm2 -V
|
||||
RUN apk --no-cache update \
|
||||
&& apk --no-cache add g++ make bash zlib-dev libpng-dev \
|
||||
&& rm -fr /var/cache/apk/*
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
RUN apk add --no-cache bash
|
||||
|
||||
RUN apk add --no-cache yarn
|
||||
|
||||
CMD cd /home/app/nextjs && yarn cache dir && yarn cache clean && yarn install && yarn build && pm2 startOrReload ecosystem.config.js --env production --no-daemon
|
||||
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
pipeline {
|
||||
agent {
|
||||
docker {
|
||||
image 'mhart/alpine-node:latest'
|
||||
args '-p 3000:3000'
|
||||
}
|
||||
// dockerfile true
|
||||
}
|
||||
environment {
|
||||
CI = 'true'
|
||||
}
|
||||
stages {
|
||||
stage('Prepare') {
|
||||
steps {
|
||||
// withEnv(['PATH+NODE=/usr/local/bin/node']) {
|
||||
// echo 'Prepare step started ... '
|
||||
// sh 'echo prefix = ~/.node >> ~/.npmrc'
|
||||
// sh 'whoami'
|
||||
// sh 'chown -R node:node /usr/local/lib/node_modules'
|
||||
sh 'cd /var/jenkins_home/workspace/tt-app_app_Saga/nextjs/'
|
||||
sh 'yarn install'
|
||||
// }
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
echo 'Build step started ... '
|
||||
sh '/var/jenkins_home/workspace/tt-app_app_Saga/nextjs/scripts/start.sh'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh '/var/jenkins_home/workspace/tt-app_app_Saga/nextjs/scripts/test.sh'
|
||||
}
|
||||
}
|
||||
stage('Response') {
|
||||
steps {
|
||||
echo 'Vous pouvez pull sur la production'
|
||||
sh '/var/jenkins_home/workspace/tt-app_app_Saga/nextjs/scripts/kill.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-2
@@ -2,16 +2,17 @@
|
||||
|
||||
## How to use
|
||||
|
||||
Download the example [or clone the repo](https://github.com/mui-org/material-ui):
|
||||
Download the example [or clone the repo](https://git.e-declic.net/talents-tube/app.git):
|
||||
|
||||
```bash
|
||||
git clone
|
||||
git clone
|
||||
```
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn build
|
||||
yarn dev
|
||||
```
|
||||
|
||||
@@ -24,3 +25,44 @@ yarn dev
|
||||
[Redux-Saga]
|
||||
https://redux-saga.js.org/docs/basics/ErrorHandling.html
|
||||
is a library that aims to make application side effect asynchronous things like data fetching and impure things like accessing the browser cache
|
||||
|
||||
## Integration
|
||||
|
||||
### Naming conventions
|
||||
|
||||
style classes name syntax : Camel case
|
||||
Try to name without too much precision
|
||||
|
||||
### Material UI
|
||||
|
||||
[components/atoms/theme.js] Const file
|
||||
[components/atoms/] Atomic components
|
||||
[components/organisms] General components
|
||||
[components/templates] Layouts components
|
||||
|
||||
## MEP
|
||||
|
||||
=================================
|
||||
Mise en PRE-production de l’application PWA
|
||||
=================================
|
||||
Aller dans la preproduction et git pull dans le répertoire /app (pas www)
|
||||
Possibilité de devoir git stash
|
||||
Attention de bien avoir le .env
|
||||
Restart docker preproduction (sudo make ENV=preprod docker-restart)
|
||||
sudo docker exec -it frontnginx.nginx bash
|
||||
nginx -t && nginx -s reload
|
||||
|
||||
## Test
|
||||
|
||||
### Cypress
|
||||
|
||||
[Gmail-tester]
|
||||
https://github.com/levz0r/gmail-tester
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
### Jenkins
|
||||
|
||||
> in case of emergency
|
||||
> make docker-restart ENV=dev
|
||||
> adduser jenkins root
|
||||
|
||||
+47
-27
@@ -1,63 +1,83 @@
|
||||
import {
|
||||
FETCH_ADS_REQUEST,
|
||||
FETCH_ADS_SUCCESS,
|
||||
FETCH_ADS_FAILURE,
|
||||
FETCH_AD_REQUEST,
|
||||
FETCH_AD_SUCCESS,
|
||||
FETCH_AD_FAILURE,
|
||||
FETCH_PROMOTED_ADS_REQUEST,
|
||||
FETCH_PROMOTED_ADS_SUCCESS,
|
||||
FETCH_PROMOTED_ADS_FAILURE,
|
||||
FETCH_JOBS_REQUEST,
|
||||
FETCH_JOBS_SUCCESS,
|
||||
FETCH_JOBS_FAILURE,
|
||||
FETCH_JOB_REQUEST,
|
||||
FETCH_JOB_SUCCESS,
|
||||
FETCH_JOB_FAILURE,
|
||||
FETCH_EMPHASIS_JOBS_REQUEST,
|
||||
FETCH_EMPHASIS_JOBS_SUCCESS,
|
||||
FETCH_EMPHASIS_JOBS_FAILURE,
|
||||
CLEAR_AD,
|
||||
COUNT_AD_VIEW,
|
||||
TOGGLE_PROMOTED_FILTER_ADS
|
||||
} from '../types'
|
||||
|
||||
//Ads stat count
|
||||
|
||||
export const countAdViewRequest = (id) => ({
|
||||
type: COUNT_AD_VIEW,
|
||||
id
|
||||
});
|
||||
|
||||
//Is ads promoted?
|
||||
|
||||
export const togglePromotedFilterRequest = () => ({
|
||||
type: TOGGLE_PROMOTED_FILTER_ADS
|
||||
});
|
||||
|
||||
//Ads list
|
||||
|
||||
export const fetchAdsRequest = (payload) => ({
|
||||
type: FETCH_ADS_REQUEST,
|
||||
export const fetchJobsRequest = (payload) => ({
|
||||
type: FETCH_JOBS_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchAdsSuccess = res => ({
|
||||
type: FETCH_ADS_SUCCESS,
|
||||
export const fetchJobsSuccess = res => ({
|
||||
type: FETCH_JOBS_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchAdsFailure = res => ({
|
||||
type: FETCH_ADS_FAILURE,
|
||||
export const fetchJobsFailure = res => ({
|
||||
type: FETCH_JOBS_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//Ads list
|
||||
|
||||
export const fetchPromotedAdsRequest = (payload) => ({
|
||||
type: FETCH_PROMOTED_ADS_REQUEST,
|
||||
export const fetchEmphasisJobsRequest = (payload) => ({
|
||||
type: FETCH_EMPHASIS_JOBS_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchPromotedAdsSuccess = res => ({
|
||||
type: FETCH_PROMOTED_ADS_SUCCESS,
|
||||
export const fetchEmphasisJobsSuccess = res => ({
|
||||
type: FETCH_EMPHASIS_JOBS_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchPromotedAdsFailure = res => ({
|
||||
type: FETCH_PROMOTED_ADS_FAILURE,
|
||||
export const fetchEmphasisJobsFailure = res => ({
|
||||
type: FETCH_EMPHASIS_JOBS_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//Single ad
|
||||
|
||||
export const fetchAdRequest = payload => ({
|
||||
type: FETCH_AD_REQUEST,
|
||||
export const fetchJobRequest = payload => ({
|
||||
type: FETCH_JOB_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchAdSuccess = res => ({
|
||||
type: FETCH_AD_SUCCESS,
|
||||
export const fetchJobSuccess = res => ({
|
||||
type: FETCH_JOB_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchAdFailure = res => ({
|
||||
type: FETCH_AD_FAILURE,
|
||||
export const fetchJobFailure = res => ({
|
||||
type: FETCH_JOB_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//clear ad
|
||||
export const clearAd = res => ({
|
||||
type: CLEAR_AD
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
POST_AD_ANSWER_REQUEST,
|
||||
POST_AD_ANSWER_FAILURE,
|
||||
POST_AD_ANSWER_SUCCESS,
|
||||
FETCH_JOBS_ANSWER_REQUEST,
|
||||
FETCH_JOBS_ANSWER_SUCCESS,
|
||||
FETCH_JOBS_ANSWER_FAILURE,
|
||||
FETCH_ANSWER_REQUEST,
|
||||
FETCH_ANSWER_SUCCESS,
|
||||
FETCH_ANSWER_FAILURE,
|
||||
POST_MESSAGE_ANSWER_REQUEST,
|
||||
POST_MESSAGE_ANSWER_FAILURE,
|
||||
POST_MESSAGE_ANSWER_SUCCESS,
|
||||
FETCH_ANSWER_MESAGES_REQUEST,
|
||||
FETCH_ANSWER_MESAGES_FAILURE,
|
||||
FETCH_ANSWER_MESAGES_SUCCESS,
|
||||
} from "../types";
|
||||
|
||||
export const postAdAnswerRequest = payload => ({
|
||||
type: POST_AD_ANSWER_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const postAdAnswerRequestFailure = errors => ({
|
||||
type: POST_AD_ANSWER_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const postAdAnswerRequestSuccess = res => ({
|
||||
type: POST_AD_ANSWER_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const postMessageAnswerRequest = payload => ({
|
||||
type: POST_MESSAGE_ANSWER_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const postMessageAnswerFailure = errors => ({
|
||||
type: POST_MESSAGE_ANSWER_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const postMessageAnswerSuccess = res => ({
|
||||
type: POST_MESSAGE_ANSWER_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
|
||||
export const fetchJobsAnsweredRequest = payload => ({
|
||||
type: FETCH_JOBS_ANSWER_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchJobsAnsweredSuccess = res => ({
|
||||
type: FETCH_JOBS_ANSWER_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
|
||||
export const fetchJobsAnsweredFailure = errors => ({
|
||||
type: FETCH_JOBS_ANSWER_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const fetchAnswerRequest = payload => ({
|
||||
type: FETCH_ANSWER_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchAnswerSuccess = res => ({
|
||||
type: FETCH_ANSWER_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchAnswerFailure = errors => ({
|
||||
type: FETCH_ANSWER_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const fetchAnswerMessagesRequest = payload => ({
|
||||
type: FETCH_ANSWER_MESAGES_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchAnswerMessagesSuccess = res => ({
|
||||
type: FETCH_ANSWER_MESAGES_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchAnswerMessagesFailure = errors => ({
|
||||
type: FETCH_ANSWER_MESAGES_FAILURE,
|
||||
errors
|
||||
});
|
||||
+64
-13
@@ -2,22 +2,73 @@ import {
|
||||
FETCH_COMPANIES_REQUEST,
|
||||
FETCH_COMPANIES_SUCCESS,
|
||||
FETCH_COMPANIES_FAILURE,
|
||||
FETCH_COMPANY_REQUEST,
|
||||
FETCH_COMPANY_SUCCESS,
|
||||
FETCH_COMPANY_FAILURE,
|
||||
FETCH_EMPHASIS_COMPANIES_REQUEST,
|
||||
FETCH_EMPHASIS_COMPANIES_SUCCESS,
|
||||
FETCH_EMPHASIS_COMPANIES_FAILURE,
|
||||
CLEAR_COMPANY,
|
||||
COUNT_COMPANY_VIEW,
|
||||
} from '../types'
|
||||
|
||||
|
||||
//COMPANIES list
|
||||
|
||||
export const fetchCompaniesRequest = (payload) => ({
|
||||
type: FETCH_COMPANIES_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchCompaniesSuccess = res => ({
|
||||
type: FETCH_COMPANIES_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchCompaniesFailure = res => ({
|
||||
type: FETCH_COMPANIES_FAILURE,
|
||||
res
|
||||
});
|
||||
type: FETCH_COMPANIES_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchCompaniesSuccess = res => ({
|
||||
type: FETCH_COMPANIES_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchCompaniesFailure = res => ({
|
||||
type: FETCH_COMPANIES_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//COMPANY single
|
||||
export const countCompanyViewRequest = (id) => ({
|
||||
type: COUNT_COMPANY_VIEW,
|
||||
id
|
||||
});
|
||||
|
||||
export const fetchCompanyRequest = payload => ({
|
||||
type: FETCH_COMPANY_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchCompanySuccess = res => ({
|
||||
type: FETCH_COMPANY_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchCompanyFailure = res => ({
|
||||
type: FETCH_COMPANY_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//EMPHASIS 'aka mise en avant'
|
||||
|
||||
export const fetchEmphasisCompaniesRequest = (payload) => ({
|
||||
type: FETCH_EMPHASIS_COMPANIES_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchEmphasisCompaniesSuccess = res => ({
|
||||
type: FETCH_EMPHASIS_COMPANIES_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchEmphasisCompaniesFailure = res => ({
|
||||
type: FETCH_EMPHASIS_COMPANIES_FAILURE,
|
||||
res
|
||||
});
|
||||
|
||||
//clear ad
|
||||
export const clearCompany = res => ({
|
||||
type: CLEAR_COMPANY
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
UPLOAD_MEDIA_REQUEST,
|
||||
UPLOAD_MEDIA_FAILURE,
|
||||
UPLOAD_MEDIA_SUCCESS,
|
||||
UPLOAD_MEDIA_PRES_REQUEST,
|
||||
UPLOAD_MEDIA_PRES_FAILURE,
|
||||
UPLOAD_MEDIA_PRES_SUCCESS,
|
||||
UPLOAD_MEDIA_CV_REQUEST,
|
||||
UPLOAD_MEDIA_CV_FAILURE,
|
||||
UPLOAD_MEDIA_CV_SUCCESS,
|
||||
|
||||
} from "../types";
|
||||
|
||||
export const uploadMedia = payload => ({
|
||||
type: UPLOAD_MEDIA_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const uploadMediaFailure = errors => ({
|
||||
type: UPLOAD_MEDIA_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const uploadMediaSuccess = res => ({
|
||||
type: UPLOAD_MEDIA_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const uploadMediaPres = payload => ({
|
||||
type: UPLOAD_MEDIA_PRES_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const uploadMediaPresFailure = errors => ({
|
||||
type: UPLOAD_MEDIA_PRES_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const uploadMediaPresSuccess = res => ({
|
||||
type: UPLOAD_MEDIA_PRES_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const uploadMediaCv = payload => ({
|
||||
type: UPLOAD_MEDIA_CV_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const uploadMediaCvFailure = errors => ({
|
||||
type: UPLOAD_MEDIA_CV_FAILURE,
|
||||
errors
|
||||
});
|
||||
|
||||
export const uploadMediaCvSuccess = res => ({
|
||||
type: UPLOAD_MEDIA_CV_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
|
||||
+127
-4
@@ -7,8 +7,45 @@ import {
|
||||
SUBSCRIBE_NEWSLETTER_FAILURE,
|
||||
REQUEST_REGISTER,
|
||||
REQUEST_REGISTER_SUCCESS,
|
||||
REQUEST_REGISTER_FAILURE
|
||||
REQUEST_REGISTER_FAILURE,
|
||||
ACCOUNT_CONFIRMATION_REQUEST,
|
||||
ACCOUNT_CONFIRMATION_SUCCESS,
|
||||
ACCOUNT_CONFIRMATION_FAILURE,
|
||||
SEND_CONFIRMATION_EMAIL_REQUEST,
|
||||
SEND_CONFIRMATION_EMAIL_SUCCESS,
|
||||
SEND_CONFIRMATION_EMAIL_FAILURE,
|
||||
UPDATE_USER_REQUEST,
|
||||
UPDATE_USER_SUCCESS,
|
||||
UPDATE_USER_FAILURE,
|
||||
RESET_PASSWORD_REQUEST,
|
||||
RESET_PASSWORD_SUCCESS,
|
||||
RESET_PASSWORD_FAILURE,
|
||||
SUBSCRIBE_USER_TO_PUSH_REQUEST,
|
||||
SUBSCRIBE_USER_TO_PUSH_SUCCESS,
|
||||
SUBSCRIBE_USER_TO_PUSH_FAILURE,
|
||||
FETCH_USER_SUBSCRIPTION_TO_PUSH_REQUEST,
|
||||
FETCH_USER_SUBSCRIPTION_TO_PUSH_SUCCESS,
|
||||
FETCH_USER_SUBSCRIPTION_TO_PUSH_FAILURE,
|
||||
UPDATE_USER_PICTURE_REQUEST,
|
||||
UPDATE_USER_PICTURE_SUCCESS,
|
||||
UPDATE_USER_PICTURE_FAILURE
|
||||
} from '../types'
|
||||
|
||||
|
||||
export const resetPasswordRequest = (payload) => ({
|
||||
type: RESET_PASSWORD_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const resetPasswordSuccess = () => ({
|
||||
type: RESET_PASSWORD_SUCCESS,
|
||||
});
|
||||
|
||||
export const resetPasswordFailure = (err) => ({
|
||||
type: RESET_PASSWORD_FAILURE,
|
||||
err
|
||||
});
|
||||
|
||||
export const fetchCurrentUserRequest = () => ({
|
||||
type: FETCH_CURRENT_USER_REQUEST
|
||||
});
|
||||
@@ -30,7 +67,7 @@ export const registerUserRequest = (payload) => ({
|
||||
|
||||
export const registerUserSuccess = () => ({
|
||||
type: REQUEST_REGISTER_SUCCESS,
|
||||
|
||||
|
||||
});
|
||||
|
||||
export const registerUserFailure = (err) => ({
|
||||
@@ -38,6 +75,20 @@ export const registerUserFailure = (err) => ({
|
||||
err
|
||||
});
|
||||
|
||||
export const sendConfirmationEmailRequest = (payload) => ({
|
||||
type: SEND_CONFIRMATION_EMAIL_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const sendConfirmationEmailSuccess = (payload) => ({
|
||||
type: SEND_CONFIRMATION_EMAIL_SUCCESS,
|
||||
});
|
||||
|
||||
export const sendConfirmationEmailFailure = (err) => ({
|
||||
type: SEND_CONFIRMATION_EMAIL_FAILURE,
|
||||
err
|
||||
});
|
||||
|
||||
export const subscribeToNewsletterRequest = (payload) => ({
|
||||
type: SUBSCRIBE_NEWSLETTER_REQUEST,
|
||||
payload
|
||||
@@ -47,6 +98,78 @@ export const subscribeToNewsletterSuccess = () => ({
|
||||
type: SUBSCRIBE_NEWSLETTER_SUCCESS,
|
||||
});
|
||||
|
||||
export const subscribeToNewsletterFailure = () => ({
|
||||
export const subscribeToNewsletterFailure = (err) => ({
|
||||
type: SUBSCRIBE_NEWSLETTER_FAILURE,
|
||||
});
|
||||
err
|
||||
});
|
||||
|
||||
export const requestConfirmationAccountRequest = (payload) => ({
|
||||
type: ACCOUNT_CONFIRMATION_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const requestConfirmationAccountSuccess = () => ({
|
||||
type: ACCOUNT_CONFIRMATION_SUCCESS,
|
||||
});
|
||||
|
||||
export const requestConfirmationAccountFailure = (err) => ({
|
||||
type: ACCOUNT_CONFIRMATION_FAILURE,
|
||||
err
|
||||
});
|
||||
|
||||
export const updateUserRequest = (payload) => ({
|
||||
type: UPDATE_USER_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const updateUserRequestSuccess = () => ({
|
||||
type: UPDATE_USER_SUCCESS,
|
||||
});
|
||||
|
||||
export const updateUserRequestFailure = (err) => ({
|
||||
type: UPDATE_USER_FAILURE,
|
||||
err
|
||||
});
|
||||
|
||||
export const subscribeUserToPushRequest = (payload) => ({
|
||||
type: SUBSCRIBE_USER_TO_PUSH_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const subscribeUserToPushSuccess = () => ({
|
||||
type: SUBSCRIBE_USER_TO_PUSH_SUCCESS,
|
||||
});
|
||||
|
||||
export const subscribeUserToPushFailure = (err) => ({
|
||||
type: SUBSCRIBE_USER_TO_PUSH_FAILURE,
|
||||
err
|
||||
});
|
||||
|
||||
export const fetchUserSubscriptionToPushRequest = (payload) => ({
|
||||
type: FETCH_USER_SUBSCRIPTION_TO_PUSH_REQUEST,
|
||||
payload
|
||||
});
|
||||
|
||||
export const fetchUserSubscriptionToPushSuccess = (res) => ({
|
||||
type: FETCH_USER_SUBSCRIPTION_TO_PUSH_SUCCESS,
|
||||
res
|
||||
});
|
||||
|
||||
export const fetchUserSubscriptionToPushFailure = (err) => ({
|
||||
type: FETCH_USER_SUBSCRIPTION_TO_PUSH_FAILURE,
|
||||
err
|
||||
});
|
||||
export const updateUserPictureRequest = (payload) => ({
|
||||
type: UPDATE_USER_PICTURE_REQUEST,
|
||||
payload
|
||||
})
|
||||
|
||||
export const updateUserPictureSuccess = (data) => ({
|
||||
type: UPDATE_USER_PICTURE_SUCCESS,
|
||||
data
|
||||
})
|
||||
|
||||
export const updateUserPictureFailure = (err) => ({
|
||||
type: UPDATE_USER_PICTURE_FAILURE,
|
||||
err
|
||||
})
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { withRouter } from 'next/router';
|
||||
import Link from 'next/link';
|
||||
import React, { Children } from 'react';
|
||||
|
||||
const ActiveLink = ({ router, children, ...props }) => {
|
||||
const child = Children.only(children);
|
||||
|
||||
let className = child.props.className || '';
|
||||
if (router.pathname === props.href && props.activeClassName) {
|
||||
className = `${className} ${props.activeClassName}`.trim();
|
||||
}
|
||||
|
||||
delete props.activeClassName;
|
||||
|
||||
return <Link {...props}>{React.cloneElement(child, { className })}</Link>;
|
||||
};
|
||||
|
||||
export default withRouter(ActiveLink);
|
||||
@@ -0,0 +1,84 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import VideoVimeo from './VideoVimeo'
|
||||
import ChatCardMessage from './ChatCardMessage'
|
||||
import _ from "lodash"
|
||||
import Ar from "lodash/array"
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
width: '100%',
|
||||
},
|
||||
userMessage: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
messageSender: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '300',
|
||||
color: theme.palette.primary.important,
|
||||
},
|
||||
messageContent: {
|
||||
fontWeight: '300',
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
messageDate: {
|
||||
fontSize: '0.8em',
|
||||
fontWeight: '300',
|
||||
color: theme.palette.primary.dark,
|
||||
padding: theme.spacing(2,0),
|
||||
},
|
||||
currentUserBubble: {
|
||||
background: theme.palette.primary.important,
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
currentMessageSender: {
|
||||
color: theme.palette.primary.important,
|
||||
},
|
||||
otherUserBubble: {
|
||||
background: theme.palette.primary.main,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
otherMessageSender: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
avatar: {
|
||||
width: '2.5em',
|
||||
borderRadius: '50%',
|
||||
margin: theme.spacing(0,1),
|
||||
},
|
||||
videoVimeo: {
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingBottom: '55%',
|
||||
},
|
||||
|
||||
},
|
||||
}));
|
||||
|
||||
export default function ChatCard(props) {
|
||||
const classes = useStyles();
|
||||
const userFirstName = useSelector(state => state.user.firstname);
|
||||
const userLastName = useSelector(state => state.user.lastname);
|
||||
const userPicture = useSelector(state => state.user.picture);
|
||||
const bubbleClass = (userFirstName === props.message.sender.firstname && userLastName === props.message.sender.lastname) ? classes.currentUserBubble : classes.otherUserBubble
|
||||
const messageSenderClass = (userFirstName === props.message.sender.firstname && userLastName === props.message.sender.lastname) ? classes.currentMessageSender : classes.otherMessageSender
|
||||
const bubbleSide = (userFirstName === props.message.sender.firstname && userLastName === props.message.sender.lastname) ? 'right' : 'left'
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ChatCardMessage side={bubbleSide} firstname={props.message.sender.firstname} lastname={props.message.sender.lastname} picture={userPicture} video={(props.message.video) ? true : false} message={(props.message.video) ? (<VideoVimeo classesOverride={ classes.videoVimeo } videoId={Ar.last(_.split(props.message.video.cdn_url, '/'))} />) : props.message.content} messageSenderClass={messageSenderClass} bubbleClass={bubbleClass}/>
|
||||
|
||||
<Typography
|
||||
className={classes.messageDate}
|
||||
align="center"
|
||||
>
|
||||
Envoyée le {(new Date(props.message.c_time)).toLocaleDateString("fr-FR")} à {(new Date(props.message.c_time)).toLocaleTimeString("fr-FR")}
|
||||
</Typography>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import React from 'react'
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import _ from "lodash"
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
userMessageLeft: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
userMessageRight: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
messageSender: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '300',
|
||||
color: theme.palette.primary.important,
|
||||
},
|
||||
messageContent: {
|
||||
fontWeight: '300',
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
userBubble: {
|
||||
width: 'auto',
|
||||
borderRadius: '5px',
|
||||
padding: theme.spacing(2),
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
userBubbleVideo: {
|
||||
minWidth: '60vw',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
minWidth: '45vw',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
minWidth: '30vw',
|
||||
},
|
||||
},
|
||||
currentUserBubble: {
|
||||
background: theme.palette.primary.main,
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
otherUserBubble: {
|
||||
background: theme.palette.primary.important,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
avatar: {
|
||||
width: '2.5em',
|
||||
height: '2.5em',
|
||||
borderRadius: '50%',
|
||||
margin: theme.spacing(0,1),
|
||||
},
|
||||
first: {
|
||||
order: 1,
|
||||
},
|
||||
second: {
|
||||
order: 2,
|
||||
}
|
||||
}));
|
||||
|
||||
export default function ChatCardMessage(props) {
|
||||
const { side, firstname, lastname, picture, message, video, messageSenderClass, bubbleClass } = props;
|
||||
const classes = useStyles();
|
||||
|
||||
const userBubble = (video) ? classes.userBubble +' '+ classes.userBubbleVideo : classes.userBubble
|
||||
return (
|
||||
<section className={(side==='left')? classes.userMessageLeft : classes.userMessageRight }>
|
||||
|
||||
{picture ?
|
||||
<img alt="talents-tube" src={`http://localhost/uploads/img/${picture}`} className={(side==='left')? classes.first+' '+classes.avatar : classes.second +' '+classes.avatar} />
|
||||
:
|
||||
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg' className={(side==='left')? classes.first +' '+ classes.avatar : classes.second+' '+classes.avatar } />
|
||||
}
|
||||
<div className={(side==='left')? classes.second : classes.first}>
|
||||
<Typography
|
||||
className={classes.messageSender +' '+ messageSenderClass}
|
||||
>
|
||||
{firstname + ' ' + lastname}
|
||||
</Typography>
|
||||
<div className={userBubble + ' ' + bubbleClass}>
|
||||
<Typography
|
||||
className={classes.messageContent}
|
||||
>
|
||||
{message}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import { Slide } from "@material-ui/core";
|
||||
|
||||
function getScrollY(scroller) {
|
||||
return scroller.pageYOffset !== undefined
|
||||
? scroller.pageYOffset
|
||||
: scroller.scrollTop !== undefined
|
||||
? scroller.scrollTop
|
||||
: (document.documentElement || document.body.parentNode || document.body)
|
||||
.scrollTop;
|
||||
}
|
||||
|
||||
const useHideOnScroll = options => {
|
||||
const { threshold, scroller } = options;
|
||||
|
||||
const scrollRef = React.useRef();
|
||||
const [hide, setHide] = React.useState(false);
|
||||
|
||||
const handleScroll = React.useCallback(() => {
|
||||
const scrollY = getScrollY(scroller || window);
|
||||
const prevScrollY = scrollRef.current;
|
||||
scrollRef.current = scrollY;
|
||||
|
||||
setHide(
|
||||
scrollY < prevScrollY
|
||||
? false
|
||||
: scrollY > prevScrollY &&
|
||||
scrollY > (threshold != null ? threshold : 100)
|
||||
? true
|
||||
: false
|
||||
);
|
||||
}, [scroller, threshold]);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, [handleScroll]);
|
||||
|
||||
return hide;
|
||||
};
|
||||
|
||||
export default function HideOnScroll(props) {
|
||||
const { children, threshold, scroller, ...other } = props;
|
||||
|
||||
const hide = useHideOnScroll({ threshold, scroller });
|
||||
|
||||
return (
|
||||
<Slide appear={false} direction="down" in={!hide} {...other}>
|
||||
{children}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
@@ -1,29 +1,52 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import SvgIcon from '@material-ui/core/SvgIcon';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import AssignmentIcon from '@material-ui/icons/Assignment';
|
||||
import RoomIcon from '@material-ui/icons/Room';
|
||||
import LocationOnIcon from '@material-ui/icons/LocationOn';
|
||||
import BusinessCenterIcon from '@material-ui/icons/BusinessCenter';
|
||||
import Business from '@material-ui/icons/Business';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
const variantIcon = {
|
||||
assignment: AssignmentIcon,
|
||||
room: RoomIcon,
|
||||
location_on: LocationOnIcon,
|
||||
business_center: BusinessCenterIcon,
|
||||
business: Business,
|
||||
};
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
height: '60vh',
|
||||
width: '100%',
|
||||
root: {
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
marginLeft: theme.spacing(3),
|
||||
marginRight: theme.spacing(3),
|
||||
},
|
||||
icon: {
|
||||
fontSize: '1.1em',
|
||||
},
|
||||
text: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '200',
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
class IconText extends React.Component {
|
||||
|
||||
render() {
|
||||
|
||||
const { classes, variant, text } = this.props;
|
||||
const Icon = variantIcon[variant];
|
||||
return (
|
||||
<Fragment>
|
||||
<SvgIcon>
|
||||
<path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z" />
|
||||
</SvgIcon>
|
||||
</Fragment>
|
||||
<Grid container className={classes.root}>
|
||||
<Grid item xs={1}>
|
||||
<Icon className={classes.icon} />
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<Typography className={classes.text}>{text}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +54,7 @@ class IconText extends React.Component {
|
||||
IconText.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(IconText);
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import AssignmentIcon from '@material-ui/icons/Assignment';
|
||||
import RoomIcon from '@material-ui/icons/Room';
|
||||
import LocationOnIcon from '@material-ui/icons/LocationOn';
|
||||
import BusinessCenterIcon from '@material-ui/icons/BusinessCenter';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Business from '@material-ui/icons/Business';
|
||||
|
||||
const variantIcon = {
|
||||
assignment: AssignmentIcon,
|
||||
room: RoomIcon,
|
||||
location_on: LocationOnIcon,
|
||||
business_center: BusinessCenterIcon,
|
||||
business: Business,
|
||||
};
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
icon: {
|
||||
fontSize: '1.1em',
|
||||
},
|
||||
text: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '200',
|
||||
},
|
||||
});
|
||||
|
||||
class IconTextInline extends React.Component {
|
||||
|
||||
render() {
|
||||
const { classes, variant, text } = this.props;
|
||||
const Icon = variantIcon[variant];
|
||||
return (
|
||||
<section className={classes.root}>
|
||||
<Icon className={classes.icon} />
|
||||
<Typography className={classes.text}>{text}</Typography>
|
||||
</section>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
IconTextInline.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(IconTextInline);
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import { JobPostingJsonLd } from 'next-seo';
|
||||
|
||||
const JobPosting = props => {
|
||||
return (
|
||||
|
||||
<JobPostingJsonLd
|
||||
datePosted="2020-01-06T03:37:40Z"
|
||||
description={props.job_description}
|
||||
hiringOrganization={{
|
||||
name: props.job_brand,
|
||||
sameAs: props.job_brand_link,
|
||||
}}
|
||||
jobLocation={{
|
||||
addressLocality: props.job_location,
|
||||
|
||||
}}
|
||||
title={props.job_title}
|
||||
validThrough="2020-01-06"
|
||||
applicantLocationRequirements="FR"
|
||||
/>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
JobPosting.propTypes = {
|
||||
// seo_title: PropTypes.string.isRequired,
|
||||
// seo_description: PropTypes.string.isRequired,
|
||||
// seo_url: PropTypes.string.isRequired,
|
||||
// image: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default JobPosting
|
||||
@@ -0,0 +1,113 @@
|
||||
import React, { Component, Fragment } from 'react'
|
||||
import { Snackbar } from '@material-ui/core';
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
|
||||
const styles = theme => ({
|
||||
close: {
|
||||
padding: theme.spacing.unit / 2,
|
||||
},
|
||||
});
|
||||
var EdeclicLib = null
|
||||
class Messager extends Component {
|
||||
state = {
|
||||
openSnack: false,
|
||||
title: '',
|
||||
body: '',
|
||||
link: '',
|
||||
tokenStopListener: null,
|
||||
messageStopListener: null,
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
EdeclicLib = (await import('./firebase-client.js')).default
|
||||
if (!EdeclicLib.messaging)
|
||||
return
|
||||
this.setState({ tokenStopListener: EdeclicLib.messaging.onTokenRefresh(this.RefreshToken) })
|
||||
this.setState({ messageStopListener: EdeclicLib.messaging.onMessage(this.MessageReceiver) })
|
||||
|
||||
}
|
||||
componentWillUnmount() {
|
||||
|
||||
if (process.browser) {
|
||||
if (!EdeclicLib.messaging)
|
||||
return
|
||||
if (!!this.state.tokenStopListener)
|
||||
this.state.tokenStopListener();
|
||||
if (!!this.state.messageStopListener)
|
||||
this.state.messageStopListener();
|
||||
}
|
||||
}
|
||||
|
||||
RefreshToken = () => {
|
||||
EdeclicLib.messaging.getToken().then(function (refreshedToken) {
|
||||
console.info('Token refreshed.');
|
||||
// Indicate that the new Instance ID token has not yet been sent to the
|
||||
// app server.
|
||||
EdeclicLib.setTokenSentToServer(false);
|
||||
// Send Instance ID token to app server.
|
||||
EdeclicLib.sendTokenToServer(refreshedToken);
|
||||
|
||||
}).catch(function (err) {
|
||||
console.warning('Unable to retrieve refreshed token ', err);
|
||||
|
||||
});
|
||||
}
|
||||
// Handle incoming messages. Called when:
|
||||
// - a message is received while the app has focus
|
||||
// - the user clicks on an app notification created by a service worker
|
||||
// `messaging.setBackgroundMessageHandler` handler.
|
||||
MessageReceiver = (payload) => {
|
||||
console.info('Message received in Messager. ', payload);
|
||||
|
||||
this.setState({
|
||||
openSnack: true,
|
||||
title: payload.notification.title,
|
||||
body: payload.notification.body,
|
||||
link: payload.notification.click_action
|
||||
});
|
||||
|
||||
}
|
||||
handleClose = () => {
|
||||
this.setState({ openSnack: false });
|
||||
|
||||
}
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={this.state.openSnack}
|
||||
autoHideDuration={6000}
|
||||
onClose={this.handleClose}
|
||||
ContentProps={{
|
||||
'aria-describedby': 'message-id',
|
||||
}}
|
||||
message={<span id="message-id">{this.state.title + " : " + this.state.body}</span>}
|
||||
action={[
|
||||
<a href={this.state.link}>Voir</a>,
|
||||
<IconButton
|
||||
key="close"
|
||||
aria-label="Close"
|
||||
color="inherit"
|
||||
className={classes.close}
|
||||
onClick={this.handleClose}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>,
|
||||
]}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default withStyles(styles)(Messager);
|
||||
@@ -0,0 +1,36 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
padding: theme.spacing(0, 4),
|
||||
},
|
||||
text: {
|
||||
fontFamily: 'Dosis',
|
||||
fontSize: '1,2em',
|
||||
fontWeight: '500',
|
||||
color: theme.palette.primary.main,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
function NoResult(props) {
|
||||
const { classes } = props;
|
||||
|
||||
return (
|
||||
<div className={ classes.container }>
|
||||
<Typography className={ classes.text }>
|
||||
{props.text}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NoResult.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withStyles(styles)(NoResult)
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Head from 'next/head'
|
||||
|
||||
const OgHead = props => {
|
||||
return (
|
||||
<Head>
|
||||
<title key="title">{props.seo_title}</title>
|
||||
<meta name="robots" content="noindex, nofollow"></meta>
|
||||
<meta name="description" kenoindexy="description" content={props.seo_description} />
|
||||
<meta name="keywords" content={props.seo_key}/>
|
||||
<meta key="og:url" property="og:url" content={props.seo_url} />
|
||||
<meta key="og:type" property="og:type" content="article" />
|
||||
<meta key="og:title" property="og:title" content={props.seo_title} />
|
||||
<meta
|
||||
key="og:description"
|
||||
property="og:description"
|
||||
content={props.seo_description}
|
||||
/>
|
||||
<meta name="keywords" content={props.seo_key}/>
|
||||
<meta key="og:image" property="og:image" content={props.image} />
|
||||
<meta name="twitter:card" content="summary"/>
|
||||
<meta name="twitter:site" content="@TalentsTube"/>
|
||||
<meta name="twitter:title" content={props.seo_title}/>
|
||||
<meta name="twitter:description" content={props.seo_title}/>
|
||||
<meta name="twitter:image" content={props.image}/>
|
||||
<meta http-equiv='cache-control' content='no-store,no-cache'></meta>
|
||||
<meta http-equiv='expires' content='0'></meta>
|
||||
<meta http-equiv='pragma' content='no-cache'></meta>
|
||||
<link rel="canonical" href={ props.seo_url} />
|
||||
|
||||
</Head>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
OgHead.propTypes = {
|
||||
// seo_title: PropTypes.string.isRequired,
|
||||
// seo_description: PropTypes.string.isRequired,
|
||||
// seo_url: PropTypes.string.isRequired,
|
||||
// image: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
export default OgHead
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
start: {
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
center: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
end: {
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
});
|
||||
|
||||
class RootContainer extends React.Component {
|
||||
|
||||
render() {
|
||||
const { classes, children, align } = this.props;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{(() => {
|
||||
switch(align) {
|
||||
case 'start':
|
||||
return <section className={classes.root + ' ' + classes.start}>{children}</section>;
|
||||
case 'center':
|
||||
return <section className={classes.root + ' ' + classes.center}>{children}</section>;
|
||||
case 'end':
|
||||
return <section className={classes.root + ' ' + classes.end}>{children}</section>;
|
||||
default:
|
||||
return <section className={classes.root}>{children}</section>;
|
||||
}
|
||||
})()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RootContainer.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
export default withStyles(styles)(RootContainer);
|
||||
@@ -0,0 +1,131 @@
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import clsx from 'clsx';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardHeader from '@material-ui/core/CardHeader';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { red } from '@material-ui/core/colors';
|
||||
import FavoriteIcon from '@material-ui/icons/Favorite';
|
||||
import ShareIcon from '@material-ui/icons/Share';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
width: '100%',
|
||||
},
|
||||
media: {
|
||||
height: 0,
|
||||
paddingTop: '56.25%', // 16:9
|
||||
},
|
||||
expand: {
|
||||
transform: 'rotate(0deg)',
|
||||
transition: theme.transitions.create('transform', {
|
||||
duration: theme.transitions.duration.shortest,
|
||||
}),
|
||||
width: '0.7em',
|
||||
height: '0.7em',
|
||||
fill: theme.palette.primary.dark,
|
||||
},
|
||||
expandOpen: {
|
||||
transform: 'rotate(180deg)',
|
||||
width: '0.7em',
|
||||
height: '0.7em',
|
||||
fill: theme.palette.primary.dark,
|
||||
},
|
||||
rootCard: {
|
||||
boxShadow: 'none !important',
|
||||
},
|
||||
rootCardAction: {
|
||||
margin: theme.spacing(1,0),
|
||||
'& button': {
|
||||
padding: theme.spacing(2,0),
|
||||
borderRadius: 0,
|
||||
}
|
||||
},
|
||||
rootCardContent: {
|
||||
padding: 0 + '!important',
|
||||
},
|
||||
containerCollapse: {
|
||||
position: 'relative',
|
||||
'&:before': {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
content: '""',
|
||||
background: 'linear-gradient(to top,rgba(255, 255, 255, 1) 8%, rgba(255, 255, 255, 0) 50%)',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
containerCollapseDefault: {
|
||||
position: 'relative',
|
||||
'&:before': {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
content: '""',
|
||||
background: ' rgba(255, 255, 255, 0)',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
displayMore: {
|
||||
color: theme.palette.primary.dark,
|
||||
fontWeight: '600',
|
||||
fontSize: '0.8rem',
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}));
|
||||
|
||||
export default function SimpleCollapse(props) {
|
||||
const classes = useStyles();
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
|
||||
function handleExpandClick() {
|
||||
setExpanded(!expanded);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={classes.card} classes={{ root: classes.rootCard }}>
|
||||
<Collapse in={expanded} timeout="auto" collapsedHeight={isWidthUp('sm', props.width) ? '28vh' : '30vh'} className={clsx(classes.containerCollapse, {
|
||||
[classes.containerCollapseDefault]: expanded,
|
||||
})}>
|
||||
<CardContent classes={{ root: classes.rootCardContent }}>
|
||||
{props.children}
|
||||
</CardContent>
|
||||
</Collapse>
|
||||
|
||||
<CardActions disableSpacing classes={{ root: classes.rootCardAction }}>
|
||||
|
||||
|
||||
<IconButton
|
||||
onClick={handleExpandClick}
|
||||
aria-expanded={expanded}
|
||||
aria-label="Afficher plus"
|
||||
> <Typography className={classes.displayMore}>
|
||||
{(!expanded) ? 'Afficher plus' : 'Afficher moins'}
|
||||
</Typography>
|
||||
<ExpandMoreIcon className={clsx(classes.expand, {
|
||||
[classes.expandOpen]: expanded,
|
||||
})} />
|
||||
</IconButton>
|
||||
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import Link from 'next/link';
|
||||
import clsx from 'clsx';
|
||||
import { loadCSS } from 'fg-loadcss';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { red } from '@material-ui/core/colors';
|
||||
import Icon from '@material-ui/core/Icon';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import ButtonGroup from '@material-ui/core/ButtonGroup';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
icon: {
|
||||
fontSize: '1em',
|
||||
},
|
||||
externalButton: {
|
||||
'& .MuiButtonGroup-groupedContained': {
|
||||
border: '0',
|
||||
},
|
||||
margin: theme.spacing(1,0),
|
||||
margin: theme.spacing(1,1.2),
|
||||
width: 'auto',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
minWidth: '20vw',
|
||||
width: '50%',
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
width: '45%',
|
||||
},
|
||||
},
|
||||
linkedinButton: {
|
||||
backgroundColor: theme.palette.authSocial.linkedin,
|
||||
padding: theme.spacing(1),
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.authSocial.linkedin,
|
||||
}
|
||||
},
|
||||
facebookButton: {
|
||||
backgroundColor: theme.palette.authSocial.facebook,
|
||||
padding: theme.spacing(1),
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.authSocial.facebook,
|
||||
}
|
||||
},
|
||||
linkedinIconButton: {
|
||||
backgroundColor: theme.palette.authSocial.linkedinDark,
|
||||
color: theme.palette.authSocial.linkedinDark,
|
||||
border: 0,
|
||||
},
|
||||
facebookIconButton: {
|
||||
backgroundColor: theme.palette.authSocial.facebookDark,
|
||||
color: theme.palette.authSocial.facebookDark,
|
||||
border: 0,
|
||||
},
|
||||
textButton: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
fontWeight: '400',
|
||||
color: theme.palette.secondary.main,
|
||||
textTransform: 'inherit',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
minWidth: '15vw',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SocialButtonsAuth(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
React.useEffect(() => {
|
||||
loadCSS(
|
||||
'https://use.fontawesome.com/releases/v5.1.0/css/all.css',
|
||||
document.querySelector('#font-awesome-css'),
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ButtonGroup variant="contained" onClick={props.linkedInAuth} className={classes.externalButton}>
|
||||
<Button className={classes.linkedinIconButton}>
|
||||
<Icon className={clsx(classes.icon, 'fab fa-linkedin-in')} color="secondary" />
|
||||
</Button>
|
||||
|
||||
<Button className={classes.textButton +' '+ classes.linkedinButton+ ' cypLinkedInConnexion'} >
|
||||
Connexion avec Linkedin
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<ButtonGroup variant="contained" onClick={props.facebookAuth} className={classes.externalButton}>
|
||||
<Button className={classes.facebookIconButton}>
|
||||
<Icon className={clsx(classes.icon, 'fab fa-facebook-f')} color="secondary" />
|
||||
</Button>
|
||||
|
||||
<Button className={classes.textButton +' '+ classes.facebookButton} >
|
||||
Connexion avec Facebook
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
export const payloadFromSubscription = function(subscription) {
|
||||
var key = subscription.getKey ? subscription.getKey('p256dh') : '';
|
||||
var auth = subscription.getKey ? subscription.getKey('auth') : '';
|
||||
// NOTE: p256dg and auth are encoded into std base64, NOT urlsafe base64
|
||||
return {
|
||||
endpoint: subscription.endpoint,
|
||||
keys: {
|
||||
p256dh: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '',
|
||||
auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : ''
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const urlB64ToUint8Array= function(base64String) {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4)
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/\-/g, '+')
|
||||
.replace(/_/g, '/')
|
||||
|
||||
const rawData = window.atob(base64)
|
||||
const outputArray = new Uint8Array(rawData.length)
|
||||
|
||||
for (let i = 0; i < rawData.length; ++i) {
|
||||
outputArray[i] = rawData.charCodeAt(i)
|
||||
}
|
||||
return outputArray
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
function redirect(params) {
|
||||
const { Router, ctx, location, status = 302 } = params
|
||||
|
||||
if (ctx.res) {
|
||||
// Seems to be the version used by zeit
|
||||
ctx.res.writeHead(status, {
|
||||
Location: location,
|
||||
// Add the content-type for SEO considerations
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
})
|
||||
ctx.res.end()
|
||||
return
|
||||
}
|
||||
|
||||
Router.replace(location)
|
||||
}
|
||||
|
||||
export default redirect
|
||||
@@ -0,0 +1,87 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import _ from "lodash"
|
||||
import Ar from "lodash/array"
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
video: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
paddingBottom: '56%', /* 16:9 58%*/
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 'calc(50% - 50vw)',
|
||||
width: '100vw',
|
||||
height: '100%',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
paddingBottom: '36%', /* 16:9 */
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 'calc(50% - 32vw)',
|
||||
width: '64vw',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
},
|
||||
videoPresentation: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
paddingBottom: '56%', /* 16:9 58%*/
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 'calc(50% - 50vw)',
|
||||
width: '100vw',
|
||||
height: '100%',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
paddingBottom: '56.5%', /* 16:9 */
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 'calc(50% - 32vw)',
|
||||
width: '64vw',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
export default function VideoVimeo(props) {
|
||||
|
||||
const {videoId, title, options, classesOverride, autoplay} = props;
|
||||
const classes = useStyles();
|
||||
let className = classes.video;
|
||||
if (classesOverride) {
|
||||
className = classes.videoPresentation;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<section className={className +' '+classesOverride}>
|
||||
<iframe className='player'
|
||||
src={"https://player.vimeo.com/video/"+ videoId + options+"?autoplay="+autoplay}
|
||||
frameBorder="0"
|
||||
title={title}
|
||||
webkitallowfullscreen="true" mozallowfullscreen="true" allowFullScreen></iframe>
|
||||
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
VideoVimeo.defaultProps = {
|
||||
options: "",
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
import React, { Component, Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from "react-redux";
|
||||
import { subscribeUserToPushRequest } from '../../actions/user'
|
||||
import getConfig from 'next/config'
|
||||
|
||||
|
||||
import { urlB64ToUint8Array, payloadFromSubscription } from './Utility'
|
||||
|
||||
var swRegistration = null
|
||||
|
||||
const WebPushContext = React.createContext()
|
||||
|
||||
function WebPushConsumer(props) {
|
||||
return (
|
||||
<WebPushContext.Consumer {...props}>
|
||||
{context => {
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
`Webpush.Consumer cannot be rendered outside the Webpush component`,
|
||||
)
|
||||
}
|
||||
return props.children(context)
|
||||
}}
|
||||
</WebPushContext.Consumer>
|
||||
)
|
||||
}
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
class WebPush extends Component {
|
||||
static Consumer = WebPushConsumer;
|
||||
|
||||
state = {
|
||||
on: false,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (swRegistration === null) {
|
||||
this.onRegisterServiceWorker()
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
console.log("isUserSubscribedToPush ::: " + nextProps.isUserSubscribedToPush);
|
||||
if (this.props.subscriveUserEnabled != nextProps.subscriveUserEnabled && nextProps.subscriveUserEnabled) {
|
||||
this.onSubscribeUser()
|
||||
}
|
||||
if (!!nextProps.isUserSubscribedToPush) {
|
||||
this.setState({ on: true })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onUpdateSubscriptionOnServer = (subscription) => {
|
||||
console.log("onUpdateSubscriptionOnServer:", subscription)
|
||||
var payload = payloadFromSubscription(subscription)
|
||||
console.log("payload:", JSON.stringify(payload))
|
||||
this.setState({ subscription: subscription })
|
||||
|
||||
this.props.subscribeUserToPushRequest(payload);
|
||||
|
||||
this.setState(({ on }) => ({ on: !on }))
|
||||
}
|
||||
|
||||
onSubscriptionFailed = (error) => {
|
||||
console.log("onSubscriptionFailed:", error)
|
||||
this.setState(({ on }) => ({ on: false }))
|
||||
}
|
||||
|
||||
toggleSubscription = () => {
|
||||
console.log(this.state.on)
|
||||
if (!this.state.on) {
|
||||
this.onSubscribeUser()
|
||||
}
|
||||
else {
|
||||
this.onUnSubscribeUser()
|
||||
}
|
||||
}
|
||||
|
||||
onSubscribeUser = () => {
|
||||
if (swRegistration == null) {
|
||||
return
|
||||
}
|
||||
|
||||
swRegistration.pushManager.getSubscription()
|
||||
.then((subscription) => {
|
||||
var isSubscribed = !(subscription === null)
|
||||
if (isSubscribed) {
|
||||
this.onUpdateSubscriptionOnServer(subscription)
|
||||
} else {
|
||||
const applicationServerKey = urlB64ToUint8Array(publicRuntimeConfig.applicationServerPublicKey)
|
||||
swRegistration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: applicationServerKey
|
||||
})
|
||||
.then((subscription) => {
|
||||
console.log('User is subscribed.')
|
||||
if (this.onUpdateSubscriptionOnServer) {
|
||||
this.onUpdateSubscriptionOnServer(subscription)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('Failed to subscribe the user: ', err)
|
||||
if (onSubscribeFailed) {
|
||||
this.onSubscriptionFailed(err)
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onUnSubscribeUser = () => {
|
||||
swRegistration.pushManager.getSubscription()
|
||||
|
||||
.then((subscription) => {
|
||||
if (subscription) {
|
||||
|
||||
return subscription.unsubscribe();
|
||||
}
|
||||
})
|
||||
.then(() =>
|
||||
this.setState(({ on }) => ({ on: false })))
|
||||
//TODO: facultatif update DB
|
||||
// .then(() => {
|
||||
// return fetch(//, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json'
|
||||
// },
|
||||
// body: JSON.stringify(subscription)
|
||||
// })
|
||||
// .then((response) => {
|
||||
// if (!response.ok) {
|
||||
// throw new Error('Bad status code from server.');
|
||||
// }
|
||||
// ///TODO bind UI
|
||||
// return true;
|
||||
// })
|
||||
|
||||
// })
|
||||
.catch((err) => {
|
||||
console.error('Failed to unsubscribe the user.', err);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
onRegisterServiceWorker = () => {
|
||||
navigator.serviceWorker.register('/static/js/Edeclic-service-worker.js')
|
||||
.then(function (swReg) {
|
||||
console.info('Service Worker is registered', swReg);
|
||||
|
||||
swRegistration = swReg;
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<WebPushContext.Provider
|
||||
value={{ on: this.state.on, toggleSubscription: this.toggleSubscription }}
|
||||
{...this.props}
|
||||
/>)
|
||||
}
|
||||
}
|
||||
|
||||
WebPush.propTypes = {
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
isUserSubscribedToPush: state.user.isUserSubscribedToPush
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { subscribeUserToPushRequest })(WebPush);
|
||||
@@ -0,0 +1,138 @@
|
||||
//var firebase = require("firebase");
|
||||
import firebase from "firebase/app"
|
||||
import 'firebase/database'
|
||||
import 'firebase/messaging'
|
||||
//based on
|
||||
//https://stackoverflow.com/questions/1479319/simplest-cleanest-way-to-implement-singleton-in-javascript
|
||||
class Singleton {
|
||||
// Properties & Methods
|
||||
messaging;
|
||||
fcmToken;
|
||||
|
||||
constructor() {
|
||||
console.log('firebase-client constructor')
|
||||
if (!Singleton.instance) {
|
||||
Singleton.instance = this
|
||||
}
|
||||
// Initialize object
|
||||
if (!firebase.messaging.isSupported()) {
|
||||
console.log('firebase.messaging not supported')
|
||||
return;
|
||||
}
|
||||
|
||||
let config = {
|
||||
apiKey: "AIzaSyArZab_4hUDgksQ4Dqdwv970JyRSlbGaKY",
|
||||
authDomain: "edeclicpwa.firebaseapp.com",
|
||||
databaseURL: "https://edeclicpwa.firebaseio.com",
|
||||
projectId: "edeclicpwa",
|
||||
storageBucket: "edeclicpwa.appspot.com",
|
||||
messagingSenderId: "130215947970"
|
||||
}
|
||||
firebase.initializeApp(config);
|
||||
|
||||
navigator.serviceWorker
|
||||
.register('/service-worker.js')
|
||||
.then((registration) => {
|
||||
firebase.messaging().useServiceWorker(registration);
|
||||
});
|
||||
this.messaging = firebase.messaging();
|
||||
this.messaging.usePublicVapidKey('BEiMSVYvblP5oKcIY7b90r-5xLk1QYDra1sSdZHdaynihcb3Hx9wdZdI9IUHmbqureB_-IpOFKpm7arBB93J3rk');
|
||||
console.log('FireBase Singleton Initialized')
|
||||
return Singleton.instance
|
||||
}
|
||||
|
||||
isSupported() {
|
||||
this.messaging.app.firebase_.messaging.isSupported()
|
||||
}
|
||||
|
||||
async requestPermission() {
|
||||
console.log('Requesting permission...');
|
||||
|
||||
return this.messaging.requestPermission().then(function () {
|
||||
console.log('Notification permission granted.');
|
||||
return true
|
||||
}).catch(function (err) {
|
||||
console.log('Unable to get permission to notify.', err);
|
||||
return false
|
||||
});
|
||||
}
|
||||
|
||||
async askForToken() {
|
||||
var res = await this.messaging.getToken().then(async (currentToken) => {
|
||||
if (currentToken) {
|
||||
console.log(currentToken)
|
||||
// this.fcmToken = currentToken;
|
||||
await this.sendTokenToServer(currentToken);
|
||||
return currentToken;
|
||||
}
|
||||
else {
|
||||
// Show permission request.
|
||||
console.log('No Instance ID token available. Request permission to generate one.');
|
||||
let hasPerm = await this.requestPermission()
|
||||
if (hasPerm)
|
||||
{
|
||||
debugger;
|
||||
return await this.askForToken()
|
||||
}
|
||||
else
|
||||
await this.setTokenSentToServer(false);
|
||||
}
|
||||
}).catch(async (err) => {
|
||||
console.log('An error occurred while retrieving token. ', err);
|
||||
// showToken('Error retrieving Instance ID token. ', err);
|
||||
await this.setTokenSentToServer(false);
|
||||
})
|
||||
console.log("Token : ")
|
||||
console.log(res);
|
||||
return res
|
||||
}
|
||||
|
||||
async deleteToken() {
|
||||
// Delete Instance ID token.
|
||||
// [START delete_token]
|
||||
this.messaging.getToken().then(async (currentToken) => {
|
||||
this.messaging.deleteToken(currentToken).then(async () => {
|
||||
console.log('Token deleted.');
|
||||
await this.setTokenSentToServer(false);
|
||||
// [START_EXCLUDE]
|
||||
// Once token is deleted update UI.
|
||||
//resetUI();
|
||||
// [END_EXCLUDE]
|
||||
}).catch((err) => {
|
||||
console.log('Unable to delete token. ', err);
|
||||
});
|
||||
// [END delete_token]
|
||||
}).catch((err) => {
|
||||
console.log('Error retrieving Instance ID token. ', err);
|
||||
//showToken('Error retrieving Instance ID token. ', err);
|
||||
});
|
||||
}
|
||||
|
||||
async sendTokenToServer(currentToken) {
|
||||
var tokenSent = await this.isTokenSentToServer()
|
||||
if (!tokenSent) {
|
||||
console.log('Sending token to server...');
|
||||
firebase.database().ref('subscription/' + currentToken).set({
|
||||
general: true
|
||||
});
|
||||
|
||||
this.setTokenSentToServer(true);
|
||||
} else {
|
||||
console.log('Token already sent to server so won\'t send it again ' +
|
||||
'unless it changes');
|
||||
}
|
||||
}
|
||||
|
||||
async isTokenSentToServer() {
|
||||
return window.localStorage.getItem('sentToServer') === '1';
|
||||
}
|
||||
|
||||
async setTokenSentToServer(sent) {
|
||||
window.localStorage.setItem('sentToServer', sent ? '1' : '0');
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new Singleton()
|
||||
Object.freeze(instance)
|
||||
|
||||
export default instance
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import SnackbarContent from '@material-ui/core/SnackbarContent';
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import ErrorIcon from '@material-ui/icons/Error';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
import CloseIcon from '@material-ui/icons/Close';
|
||||
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
||||
import WarningIcon from '@material-ui/icons/Warning';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import clsx from 'clsx';
|
||||
import { amber, green } from '@material-ui/core/colors';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles1 = makeStyles(theme => ({
|
||||
success: {
|
||||
backgroundColor: green[600],
|
||||
},
|
||||
error: {
|
||||
backgroundColor: theme.palette.error.dark,
|
||||
},
|
||||
info: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: amber[700],
|
||||
},
|
||||
icon: {
|
||||
fontSize: 20,
|
||||
},
|
||||
iconVariant: {
|
||||
opacity: 0.9,
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
message: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}));
|
||||
const variantIcon = {
|
||||
success: CheckCircleIcon,
|
||||
warning: WarningIcon,
|
||||
error: ErrorIcon,
|
||||
info: InfoIcon,
|
||||
};
|
||||
|
||||
function MySnackbarContentWrapper(props) {
|
||||
const classes = useStyles1();
|
||||
const { className, message, onClose, variant, ...other } = props;
|
||||
const Icon = variantIcon[variant];
|
||||
|
||||
return (
|
||||
<SnackbarContent
|
||||
className={clsx(classes[variant], className)}
|
||||
aria-describedby="client-snackbar"
|
||||
message={
|
||||
<span id="client-snackbar" className={classes.message}>
|
||||
<Icon className={clsx(classes.icon, classes.iconVariant)} />
|
||||
{message}
|
||||
</span>
|
||||
}
|
||||
action={[
|
||||
<IconButton key="close" aria-label="Close" color="inherit" onClick={onClose}>
|
||||
<CloseIcon className={classes.icon} />
|
||||
</IconButton>,
|
||||
]}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
|
||||
|
||||
});
|
||||
|
||||
class Snack extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={this.props.openSnack}
|
||||
autoHideDuration={7000}
|
||||
>
|
||||
<MySnackbarContentWrapper
|
||||
variant={this.props.variant}
|
||||
message={this.props.message}
|
||||
onClose={this.props.onClose} />
|
||||
</Snackbar>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Snack);
|
||||
|
||||
@@ -3,29 +3,184 @@ import red from '@material-ui/core/colors/red';
|
||||
|
||||
// Create a theme instance.
|
||||
const theme = createMuiTheme({
|
||||
|
||||
// COLORS
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#155EE4',
|
||||
main: '#1689FB',
|
||||
darkMain: '#155EE4',
|
||||
dark: '#1E2327',
|
||||
important: '#25D89A',
|
||||
conditional: '#D2D3D3',
|
||||
},
|
||||
secondary: {
|
||||
main: '#19857b',
|
||||
},
|
||||
flash: {
|
||||
main: '#25D89A',
|
||||
main: '#FFFFFF',
|
||||
dark: '#BBBCBD',
|
||||
important: '#FF5A5F',
|
||||
text: '#787B7D',
|
||||
},
|
||||
error: {
|
||||
main: red.A400,
|
||||
},
|
||||
background: {
|
||||
default: '#fff',
|
||||
offerlistitem: {
|
||||
title: '#1E2327',
|
||||
icon: '#BBBCBD',
|
||||
text: '#787B7D',
|
||||
},
|
||||
answer: {
|
||||
main: '#E8E8E8',
|
||||
},
|
||||
background: {
|
||||
default: '#ffffff',
|
||||
grid: '#FCFCFC',
|
||||
dark: '#1E2327',
|
||||
grey: '#F3F9FF',
|
||||
videotheque: '#54D7E6',
|
||||
blog: '#1689FB',
|
||||
recruteur: '#1E2327',
|
||||
},
|
||||
menu: {
|
||||
darkText: '#1E2327',
|
||||
lightText: '#F3F9FF',
|
||||
darkDivider: '#32383C',
|
||||
lightDivider: '#E2E2E2',
|
||||
backArrow: 'rgba(30, 35, 39, 0.4)',
|
||||
emailText: '#E8E8E9'
|
||||
},
|
||||
tags: {
|
||||
main: '#E7F3FF',
|
||||
},
|
||||
input: {
|
||||
main: '#F0F0F0',
|
||||
},
|
||||
authSocial: {
|
||||
facebook: '#3064B7',
|
||||
facebookDark: '#2C59A0',
|
||||
linkedin: '#0276B5',
|
||||
linkedinDark: '#025C8D',
|
||||
},
|
||||
social: {
|
||||
facebook: '#334F8D',
|
||||
twitter: '#1A97F0',
|
||||
linkedin: '#006CAC',
|
||||
mail: '#BBBCBD',
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//OVERRIDE
|
||||
overrides: {
|
||||
|
||||
MuiDrawer: {
|
||||
paperAnchorLeft: {
|
||||
right: '25%',
|
||||
backgroundColor: '#1E2327',
|
||||
},
|
||||
paperAnchorRight: {
|
||||
left: '25%',
|
||||
backgroundColor: '#1E2327',
|
||||
}
|
||||
},
|
||||
|
||||
MuiAppBar: {
|
||||
colorPrimary: {
|
||||
color: '#1689FB',
|
||||
backgroundColor: '#FFFFFF',
|
||||
},
|
||||
colorDefault: {
|
||||
boxShadow: 'none',
|
||||
backgroundColor: '#F8FBFF',
|
||||
}
|
||||
},
|
||||
|
||||
MuiPaper: {
|
||||
elevation1: {
|
||||
transition: 'box-shadow .25s',
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.2)',
|
||||
boxShadow: '0px 6px 16px rgba(30, 35, 39, 0.15)',
|
||||
transition: 'box-shadow 2s',
|
||||
'&:hover': {
|
||||
boxShadow: '0px 6px 16px rgba(35, 40, 45, 0.25)',
|
||||
transition: 'box-shadow 1.2s',
|
||||
}
|
||||
},
|
||||
elevation4: {
|
||||
transition: 'box-shadow .25s',
|
||||
boxShadow: '0px 6px 16px rgba(30, 35, 39, 0.15)',
|
||||
},
|
||||
},
|
||||
|
||||
MuiButton: {
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
boxShadow: '0px',
|
||||
},
|
||||
},
|
||||
|
||||
contained: {
|
||||
boxShadow: '0px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
boxShadow: '0px',
|
||||
},
|
||||
},
|
||||
outlined: {
|
||||
border: '2px solid',
|
||||
'&:hover': {
|
||||
backgroundColor: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
boxShadow: '0px',
|
||||
},
|
||||
},
|
||||
outlinedPrimary: {
|
||||
border: '2px solid',
|
||||
borderColor: '#1689FB',
|
||||
'&:hover': {
|
||||
border: '2px solid',
|
||||
},
|
||||
},
|
||||
outlinedSecondary: {
|
||||
border: '2px solid',
|
||||
borderColor: '#FFFFFF',
|
||||
'&:hover': {
|
||||
border: '2px solid',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
MuiButtonGroup: {
|
||||
contained: {
|
||||
boxShadow: 'inherit',
|
||||
},
|
||||
},
|
||||
|
||||
MuiLink: {
|
||||
underlineHover: {
|
||||
'&:hover': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
MuiCheckbox: {
|
||||
colorSecondary: {
|
||||
'&.Mui-checked': {
|
||||
color: "#1689FB",
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
MuiFilledInput: {
|
||||
underline: {
|
||||
'&:before, &:after': {
|
||||
display: 'none',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
import React, { useState, Component, Fragment } from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import { connect } from 'react-redux'
|
||||
import { updateUserRequest } from '../../actions/user'
|
||||
import getConfig from 'next/config'
|
||||
import Validator from "validator";
|
||||
import AccountProfile from '../organisms/AccountProfile';
|
||||
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
|
||||
import {
|
||||
Button,
|
||||
} from '@material-ui/core';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
width: '85%',
|
||||
},
|
||||
form: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '24px',
|
||||
paddingBottom: theme.spacing(8),
|
||||
marginBottom: theme.spacing(8),
|
||||
'&>div': {
|
||||
marginTop: theme.spacing(1.5),
|
||||
marginBottom: theme.spacing(1.5),
|
||||
}
|
||||
|
||||
},
|
||||
textField: {
|
||||
width: '85%',
|
||||
'&:disabled': {
|
||||
color: theme.palette.secondary.dark,
|
||||
},
|
||||
'& label': {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
'& fieldset:hover': {
|
||||
borderColor: theme.palette.primary.main + ' !important',
|
||||
},
|
||||
},
|
||||
textFieldSmall: {
|
||||
width: '48%',
|
||||
'&:disabled': {
|
||||
color: theme.palette.secondary.dark,
|
||||
},
|
||||
'& label': {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
'& fieldset:hover': {
|
||||
borderColor: theme.palette.primary.main + ' !important',
|
||||
},
|
||||
padding: '5px',
|
||||
},
|
||||
});
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
//https://xd.adobe.com/view/ba98331a-9145-4d67-7e27-25a36e519ef4-cca9/screen/a96779d7-bdb3-410a-8f19-e6987acb98ee/Candidature-Step-3
|
||||
class AccountEtape1 extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// This binding is necessary to make `this` work in the callback
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this)
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
file: null,
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email:'',
|
||||
phone: '',
|
||||
currentJob: '',
|
||||
address1:'',
|
||||
address2 : '',
|
||||
zipCode : '',
|
||||
city : '',
|
||||
country : '',
|
||||
linkedin: '',
|
||||
twitter: '',
|
||||
website: '',
|
||||
mediaId: null,
|
||||
companyId: null,
|
||||
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
|
||||
//console.log(this.refs.myTextField.input.value);
|
||||
//this.soumettre(e);
|
||||
|
||||
}
|
||||
|
||||
handleClose = (e, reason) => {
|
||||
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
|
||||
//setOpen(false);
|
||||
};
|
||||
|
||||
onBlur(e) {
|
||||
console.log("blur")
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
|
||||
this.soumettre(e.target.name, "unitaire");
|
||||
}
|
||||
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
this.setState({
|
||||
firstname: nextProps.user.firstname,
|
||||
lastname: nextProps.user.lastname,
|
||||
phone: nextProps.user.phone,
|
||||
email: nextProps.user.email,
|
||||
currentJob: nextProps.user.currentJob,
|
||||
address1 : nextProps.user.address1,
|
||||
address2 : nextProps.user.address2,
|
||||
zipCode : nextProps.user.zipCode,
|
||||
city : nextProps.user.city,
|
||||
country : nextProps.user.country,
|
||||
linkedin: nextProps.user.linkedin,
|
||||
twitter: nextProps.user.twitter,
|
||||
website: nextProps.user.website,
|
||||
companyId: nextProps.companyId,
|
||||
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
firstname: this.props.user.firstname,
|
||||
lastname: this.props.user.lastname,
|
||||
phone: this.props.user.phone,
|
||||
email: this.props.user.email,
|
||||
address1: this.props.user.address1,
|
||||
address2 : this.props.user.address2,
|
||||
zipCode : this.props.user.zipCode,
|
||||
city : this.props.user.city,
|
||||
country : this.props.user.country,
|
||||
currentJob: this.props.user.currentJob,
|
||||
linkedin: this.props.user.linkedin,
|
||||
twitter: this.props.user.twitter,
|
||||
website: this.props.user.website,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
onSubmit = (e) => {
|
||||
|
||||
e.preventDefault();
|
||||
console.log("submit")
|
||||
console.log(e.keyCode)
|
||||
|
||||
this.soumettre(e.target.name, "global")
|
||||
|
||||
}
|
||||
soumettre(champ, type){
|
||||
console.log("soumettre");
|
||||
console.log(champ);
|
||||
const errors = this.validate(this.state, champ, type);
|
||||
const success = {};
|
||||
this.setState({ errors });
|
||||
if (!this.state.errors || !this.state.errors.message){
|
||||
|
||||
success.message ="Profil mis à jour";
|
||||
this.setState({ success });
|
||||
const data = new FormData()
|
||||
data.append('firstname', this.state.firstname)
|
||||
data.append('lastname', this.state.lastname)
|
||||
data.append('phone', this.state.phone)
|
||||
//data.append('email', this.state.email)
|
||||
data.append('currentJob', this.state.currentJob)
|
||||
data.append('address1', this.state.address1)
|
||||
data.append('address2', this.state.address2)
|
||||
data.append('zipCode', this.state.zipCode)
|
||||
data.append('city', this.state.city)
|
||||
data.append('country', this.state.country)
|
||||
data.append('linkedin', this.state.linkedin)
|
||||
data.append('twitter', this.state.twitter)
|
||||
data.append('website', this.state.website)
|
||||
|
||||
//TODO:maybe add this two in a single transactionnal saga
|
||||
this.props.updateUserRequest(data)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
validate(data,champ, type){
|
||||
console.log("valdiate")
|
||||
console.log(champ);
|
||||
const errors = {};
|
||||
errors.message ="";
|
||||
//if (!Validator.isEmail(data.currentJob)) errors.message += "Le poste actuel doit etre un email\n";
|
||||
if (type=="global" || (champ=="firstname" && data.firstname=="")) errors.message += "Veuillez saisir votre prénom<br />";
|
||||
if (type=="global" || (champ=="lastname" && data.lastname=="")) errors.message += " Veuillez saisir saisir votre nom<br />";
|
||||
if (type=="global" || (champ=="phone" && data.phone=="")) errors.message += "Veuillez saisir un numéro de téléphone<br />";
|
||||
if (type=="global" || (champ=="currentJob" && data.currentJob=="")) errors.message += "Veuillez saisir votre intitulé de poste actuel ou poste recherché<br />";
|
||||
if (type=="global" || (champ=="zipCode" && (data.zipCode && data.zipCode!="") && !Validator.isPostalCode(data.zipCode, "FR"))) errors.message += "Le code postal n'est pas correct<br />";
|
||||
// if ((data.website && data.website!="") && !Validator.isURL(data.website)) errors.message += " Veuillez ajouter un site internet avec une url valide<br />";
|
||||
// if (!Validator.isMobilePhone(data.phone, "fr-FR") && !Validator.isNumeric(data.phone, "fr-FR")) errors.message += "Le téléphone n'est pas correct<br />";
|
||||
//console.log(errors.email);
|
||||
|
||||
return errors;
|
||||
};
|
||||
AfficherErreurs() {
|
||||
if (!this.state.errors || !this.state.errors.message){
|
||||
return "";
|
||||
}else if (this.state.errors.length != 0 && this.state.errors.message!="") {
|
||||
return (
|
||||
<Alert severity="error">
|
||||
{ ReactHtmlParser(this.state.errors.message)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
AfficherSuccess() {
|
||||
// if (this.state.success && this.state.success.length != 0 && this.state.success.message != "") {
|
||||
// return (
|
||||
// <Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
|
||||
// <Alert onClose={() => this.setState({open: false})} severity="success">
|
||||
// {ReactHtmlParser(this.state.success.message)}
|
||||
// </Alert>
|
||||
// </Snackbar>
|
||||
|
||||
|
||||
// );
|
||||
|
||||
// }
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
inputFilesystemChange = (e) => {
|
||||
|
||||
console.log("inputFilesystemChange");
|
||||
var file = e.target.files[0]
|
||||
this.setState({ picture: file })
|
||||
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
this.props.updateUserPictureRequest(data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
const { classes, user } = this.props
|
||||
const logoUrl = user.picture;
|
||||
function refresh() {
|
||||
document
|
||||
.location
|
||||
.reload(true);
|
||||
|
||||
}
|
||||
return (
|
||||
<section className={classes.container}>
|
||||
<form className={classes.form}
|
||||
autoComplete="off" focused="true" name="monForm" ref="formToSubmit">
|
||||
{/* {!!logoUrl && <Avatar className={classes.logo}
|
||||
classes={{ img: classes.MuiAvatarImg }}
|
||||
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl} alt="candidatLogo" />} */}
|
||||
|
||||
{/* {!!user.pictureLoading && <Loader />}
|
||||
{!!!user.pictureLoading && !!!logoUrl &&
|
||||
(<Fragment><input
|
||||
type="file" accept="image/*" capture="filesystem"
|
||||
className={classes.input}
|
||||
style={{ display: 'none' }}
|
||||
id="raised-button-file"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={this.inputFilesystemChange}
|
||||
/>
|
||||
<label htmlFor="raised-button-file">
|
||||
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
|
||||
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg'
|
||||
className={classes.iconButton} />
|
||||
<Typography align="left" className={classes.textButton}>
|
||||
J'<span className={classes.underlined}>importe</span> ma photo de profil
|
||||
</Typography>
|
||||
<KeyboardArrowRightIcon className={classes.InputKeyboardArrowRightIcon} />
|
||||
</Button>
|
||||
</label></Fragment>)
|
||||
} */}
|
||||
{this.AfficherErreurs()}
|
||||
{this.AfficherSuccess()}
|
||||
|
||||
|
||||
<TextField
|
||||
name="firstname"
|
||||
required
|
||||
id="outlined-required"
|
||||
label="Prénom"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.firstname}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="lastname"
|
||||
required
|
||||
id="outlined-required"
|
||||
label="Nom"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.lastname}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
disabled
|
||||
name="email"
|
||||
id="outlined"
|
||||
label="E-mail"
|
||||
required
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.email}
|
||||
onChange={this.handleChange}
|
||||
type="mail"
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="phone"
|
||||
id="outlined"
|
||||
label="Téléphone"
|
||||
required
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.phone ? this.state.phone: ''}
|
||||
onChange={this.handleChange}
|
||||
type="tel"
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<TextField
|
||||
name="currentJob"
|
||||
id="outlined"
|
||||
label="Poste actuel"
|
||||
required
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.currentJob && this.state.currentJob !='null' ? this.state.currentJob : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="address1"
|
||||
id="outlined"
|
||||
label="Adresse"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.address1 && this.state.address1 !='null' ? this.state.address1 : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="address2"
|
||||
id="outlined"
|
||||
label="Adresse complémentaire"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.address2 && this.state.address2 !='null' ? this.state.address2 : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
<div className={classes.container}>
|
||||
<TextField
|
||||
name="zipCode"
|
||||
id="outlined"
|
||||
label="Code postal"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.textFieldSmall }}
|
||||
variant="outlined"
|
||||
value={this.state.zipCode && this.state.zipCode !='null' ? this.state.zipCode : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="city"
|
||||
id="outlined"
|
||||
label="Ville"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.textFieldSmall }}
|
||||
variant="outlined"
|
||||
value={this.state.city && this.state.city !='null' ? this.state.city : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="country"
|
||||
id="outlined"
|
||||
label="Pays"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.textFieldSmall }}
|
||||
variant="outlined"
|
||||
value={this.state.country && this.state.country !='null' ? this.state.country : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
name="linkedin"
|
||||
id="outlined"
|
||||
label="Linkedin"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://www.linkedin.com/in/"
|
||||
value={this.state.linkedin && this.state.linkedin !='null' ? this.state.linkedin : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="twitter"
|
||||
id="outlined"
|
||||
label="Twitter"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://twitter.com/.."
|
||||
value={this.state.twitter && this.state.twitter !='null' ? this.state.twitter : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="website"
|
||||
id="outlined"
|
||||
label="Site internet"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://monsite.fr"
|
||||
value={this.state.website && this.state.website !='null' ? this.state.website : ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
|
||||
className={classes.button}>
|
||||
Enregistrer
|
||||
</Button>
|
||||
</form>
|
||||
{/* <div id="F" className={classes.foot}>
|
||||
<Button variant="contained" className={classes.button + ' cypButtonSendApplication'} onClick={this.onSubmit}>Mettre à jour!</Button>
|
||||
</div> */}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
user: state.user ,
|
||||
loading: !!state.user.loading,
|
||||
pictureLoading: !!state.user.pictureLoading,
|
||||
errors: state.user.errors || ownProps.errors,
|
||||
mediaId: state.media.message && state.media.message.mediaId,
|
||||
};
|
||||
}
|
||||
|
||||
export default withStyles(styles)(connect(mapStateToProps, { updateUserRequest })(AccountEtape1));
|
||||
@@ -0,0 +1,317 @@
|
||||
import React, {Fragment} from 'react'
|
||||
import {connect} from "react-redux";
|
||||
import {makeStyles} from '@material-ui/styles';
|
||||
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import {Button, Typography} from '@material-ui/core';
|
||||
import dynamic from 'next/dynamic'
|
||||
import {withStyles} from '@material-ui/core/styles'
|
||||
import {uploadMediaPres} from '../../actions/media'
|
||||
import {errorMessageSelector} from '../../reducers/media'
|
||||
import {fetchCompanyRequest} from '../../actions/companies'
|
||||
import {fetchCurrentUserRequest} from '../../actions/user'
|
||||
import VideoVimeo from '../atoms/VideoVimeo'
|
||||
import Router from 'next/router'
|
||||
import Loader from '../templates/loader'
|
||||
import Ar from "lodash/array"
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Container from '@material-ui/core/Container';
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
width: '100%',
|
||||
},
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
margin: theme.spacing(1),
|
||||
flexWrap: 'wrap',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
|
||||
recorderContainer: {
|
||||
//display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%'
|
||||
},
|
||||
rootButton: {
|
||||
textTransform: 'inherit'
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline'
|
||||
},
|
||||
textButton: {
|
||||
fontWeight: '700',
|
||||
fontSize: '1.15em',
|
||||
paddingLeft: theme.spacing(2)
|
||||
},
|
||||
videoPresentation: {
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
},
|
||||
conseilButton: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#25D89A",
|
||||
padding: theme.spacing(3, 2),
|
||||
margin: theme.spacing(1),
|
||||
width: "100%",
|
||||
textAlign: "right",
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
|
||||
'px rgba(0, 0, 0, 0.14)',
|
||||
'&:hover': {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#25D89A",
|
||||
}
|
||||
},
|
||||
importButton: {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
padding: theme.spacing(3, 2),
|
||||
margin: theme.spacing(1),
|
||||
width: "100%",
|
||||
textAlign: "right",
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
|
||||
'px rgba(0, 0, 0, 0.14)',
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
}
|
||||
},
|
||||
InputKeyboardArrowDownIconBlank: {
|
||||
fontSize: '2.5em',
|
||||
color: "#ffffff",
|
||||
textAlign: "right"
|
||||
},
|
||||
InputKeyboardArrowDownIcon: {
|
||||
fontSize: '2.5em',
|
||||
color: "#1689FB",
|
||||
textAlign: "right"
|
||||
}
|
||||
});
|
||||
const Recorder = dynamic(() => import ('../organisms/Recorder'), {ssr: false})
|
||||
class Etape2 extends React.Component {
|
||||
state = {
|
||||
loading: true,
|
||||
activeStep: 0,
|
||||
data: {},
|
||||
openSnack: false,
|
||||
errors: {},
|
||||
success : {}
|
||||
};
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
localStorage.setItem('loginRedirect', window.location)
|
||||
|
||||
if (!!!localStorage.getItem('ttJWT')) {
|
||||
Router.push("/Login")
|
||||
} else
|
||||
this.setState({loading: false, firstname: this.props.user.firstname, videoPres: this.props.user.videoPres, video: this.props.user.presentation_video_id})
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
|
||||
this.setState({
|
||||
loading: nextProps.loading,
|
||||
openSnack: !!!_.isEmpty(nextProps.errors),
|
||||
errors: !!!nextProps.errors
|
||||
? {
|
||||
errors: {
|
||||
message: ""
|
||||
}
|
||||
}
|
||||
: nextProps.errors,
|
||||
videoPres: nextProps.videoPres,
|
||||
video: nextProps.video
|
||||
})
|
||||
//window.location.reload(false);
|
||||
if (nextProps.media.status === 'success') {
|
||||
const success = {};
|
||||
success.message ="Vidéo mis à jour";
|
||||
this.setState({ success });
|
||||
this.setActiveStep(1);
|
||||
}
|
||||
}
|
||||
|
||||
setActiveStep = (step) => {
|
||||
this.setState({activeStep: step})
|
||||
}
|
||||
|
||||
inputFilesystemChange = (e) => {
|
||||
this.uploadMediaPres(e)
|
||||
}
|
||||
|
||||
inputCamRecorderChange = (e) => {
|
||||
this.uploadMediaPres(e)
|
||||
}
|
||||
|
||||
uploadMediaPres = (e) => {
|
||||
var file = e.target.files[0]
|
||||
|
||||
const data = new FormData()
|
||||
data.append('upload', file)
|
||||
//this.setState({ videoPres: videoPres })
|
||||
|
||||
this
|
||||
.props
|
||||
.uploadMediaPres(data)
|
||||
}
|
||||
|
||||
AfficherSuccess() {
|
||||
if (!this.state.success || !this.state.success.message) {
|
||||
|
||||
} else if (this.state.success.length != 0 && this.state.success.message != "") {
|
||||
return (
|
||||
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
|
||||
<Alert onClose={() => this.setState({open: false})} severity="success">
|
||||
{ReactHtmlParser(this.state.success.message)}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
handleClose = (event, reason) => {
|
||||
this.setState({openSnack: false});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {classes, company} = this.props;
|
||||
const {loading, activeStep} = this.state;
|
||||
let video;
|
||||
if (this.state.videoPres) {
|
||||
video = <VideoVimeo videoId={Ar.last(_.split(this.state.videoPres, '/'))} autoplay='1' classesOverride="videoPresentation"/>;
|
||||
} else {
|
||||
video = "";
|
||||
}
|
||||
return (
|
||||
<section className={classes.container}>
|
||||
<Fragment>
|
||||
{this.AfficherSuccess()}
|
||||
{ loading ? <Loader/> : <div></div> }
|
||||
|
||||
<div className={classes.root}>
|
||||
<Hidden xsUp>
|
||||
|
||||
</Hidden>
|
||||
<Container maxWidth="md">
|
||||
|
||||
<Grid container
|
||||
justify="center"
|
||||
alignItems="center"
|
||||
spacing={2}>
|
||||
<Grid item xs={12} sm={12} md={12} lg={6} lg={6}>
|
||||
<div className={classes.recorderContainer}>
|
||||
<Recorder uploader={this.props.uploadMediaPres} titreBouton="Remplacer ma vidéo"/>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={12} lg={6} xl={6}>
|
||||
<div>
|
||||
<label htmlFor="import-conseil">
|
||||
<Button
|
||||
component="span"
|
||||
className={classes.conseilButton}
|
||||
classes={{
|
||||
root: classes.rootButton}}
|
||||
onClick={() => Router.push(`/Conseils`)}>
|
||||
<img
|
||||
alt="talents-tube"
|
||||
src='/statique/images/conseils-talentstube.svg'
|
||||
className={classes.iconButton}/>
|
||||
<Typography align="left" className={classes.textButton}>
|
||||
<span className={classes.underlined}>Comment</span> créer ma vidéo
|
||||
</Typography>
|
||||
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIconBlank}/>
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
accept="video/*"
|
||||
className={classes.input + ' cypButtonImportVid'}
|
||||
style={{display: 'none'}}
|
||||
id="import-button-file"
|
||||
onChange={this.inputFilesystemChange}/>
|
||||
<label htmlFor="import-button-file">
|
||||
<Button
|
||||
component="span"
|
||||
className={classes.importButton}
|
||||
classes={{
|
||||
root: classes.rootButton}}>
|
||||
<img
|
||||
alt="talents-tube"
|
||||
src='/statique/images/Import.svg'
|
||||
className={classes.iconButton}/>
|
||||
<Typography align="left" className={classes.textButton}>
|
||||
J'<span className={classes.underlined}>importe</span> ma vidéo de réponse
|
||||
|
||||
</Typography>
|
||||
<KeyboardArrowDownIcon className={classes.InputKeyboardArrowDownIcon}/>
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</Grid>
|
||||
<Hidden xsUp>
|
||||
|
||||
</Hidden>
|
||||
|
||||
<Grid item xs={12} sm={12} md={12} lg={12} xl={12}>
|
||||
<div className={classes.videoPresentation}>
|
||||
{video}
|
||||
</div>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Container>
|
||||
</div>
|
||||
{/* <div className={classes.textContainer}>
|
||||
|
||||
</div> */}
|
||||
|
||||
</Fragment>
|
||||
</section>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Etape2.getInitialProps = function (context) {
|
||||
|
||||
const {companyId} = context.query
|
||||
const {res} = context
|
||||
//TODO :really needed ?
|
||||
context
|
||||
.store
|
||||
.dispatch(fetchCurrentUserRequest())
|
||||
|
||||
if (!!companyId)
|
||||
context.store.dispatch(fetchCompanyRequest(companyId))
|
||||
|
||||
return {}; //leave this please
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
||||
return {
|
||||
loading: !!state.media.loading,
|
||||
media: state.media,
|
||||
offre: state.ad,
|
||||
//errors: errorMessageSelector(state),
|
||||
user: state.user,
|
||||
company: state.company
|
||||
};
|
||||
}
|
||||
|
||||
export default withStyles(styles)(connect(mapStateToProps, {fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaPres})(Etape2));
|
||||
@@ -0,0 +1,257 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connect } from "react-redux";
|
||||
import { fetchCurrentUserRequest } from '../../actions/user'
|
||||
import { fetchCompanyRequest } from '../../actions/companies'
|
||||
import { uploadMediaCv } from '../../actions/media'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import { Button, Typography, Divider, Hidden } from '@material-ui/core';
|
||||
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import Loader from '../../components/templates/loader'
|
||||
import Snack from '../../components/atoms/snack';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import { Alert, AlertTitle } from '@material-ui/lab';
|
||||
import ReactHtmlParser from 'react-html-parser';
|
||||
import getConfig from 'next/config'
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
rootButton: {
|
||||
textTransform: 'inherit',
|
||||
},
|
||||
underlined: {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
textButton: {
|
||||
fontWeight: '700',
|
||||
fontSize: '1.15em',
|
||||
paddingLeft: theme.spacing(2),
|
||||
},
|
||||
InputKeyboardArrowDownIcon: {
|
||||
fontSize: '2.5em',
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
importButton: {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
padding: theme.spacing(3, 2),
|
||||
margin: theme.spacing(1),
|
||||
width: "370px",
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
|
||||
'px rgba(0, 0, 0, 0.14)',
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
class Etape3 extends React.Component {
|
||||
state = {
|
||||
loading: true,
|
||||
openSnack: false,
|
||||
cv:false,
|
||||
errors: {},
|
||||
success : {}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
localStorage.setItem('loginRedirect', window.location)
|
||||
|
||||
if(!!!localStorage.getItem('ttJWT'))
|
||||
{
|
||||
Router.push("/Login")
|
||||
}
|
||||
else
|
||||
this.setState({
|
||||
loading: false,
|
||||
cvLink: this.props.user.cv
|
||||
})
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
|
||||
this.setState({
|
||||
cvLink: nextProps.cvLink,
|
||||
loading: nextProps.loading,
|
||||
openSnack: !!!_.isEmpty(nextProps.errors),
|
||||
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
|
||||
})
|
||||
this.state.cvLink = nextProps.cvLink;
|
||||
if (nextProps.loading) {
|
||||
const success = {};
|
||||
success.message ="CV mis à jour";
|
||||
console.log("CV mis à jour!!!")
|
||||
if(this.state.cvLink) console.log(this.state.cvLink)
|
||||
this.setState({ success });
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
inputFilesystemChange = (e) => {
|
||||
this.uploadMediaCv(e)
|
||||
}
|
||||
|
||||
uploadMediaCv = (e) => {
|
||||
var file = e.target.files[0]
|
||||
|
||||
const data = new FormData()
|
||||
data.append('upload', file)
|
||||
|
||||
this.props.uploadMediaCv(data)
|
||||
}
|
||||
|
||||
AfficherSuccess() {
|
||||
if (!this.state.success || !this.state.success.message) {
|
||||
console.log("Erreur Upload CV");
|
||||
return "";
|
||||
} else if (this.state.success.length != 0 && this.state.success.message != "") {
|
||||
return (
|
||||
<Snackbar open={open} autoHideDuration={1000} onClose={() => this.setState({open: false})}>
|
||||
<Alert onClose={() => this.setState({open: false})} severity="success">
|
||||
{ReactHtmlParser(this.state.success.message)}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
AfficherLienCv() {
|
||||
console.log("afficher lien");
|
||||
if (this.state.cvLink !="") {
|
||||
|
||||
return (
|
||||
|
||||
<Fragment>
|
||||
<Hidden> </Hidden>
|
||||
<Hidden> </Hidden>
|
||||
|
||||
<Hidden> </Hidden>
|
||||
<Hidden> </Hidden>
|
||||
<Card variant="" style={{
|
||||
width:370+"px",
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1' +
|
||||
'px rgba(0, 0, 0, 0.14)',
|
||||
marginLeft : '10px',
|
||||
marginTop:'10px'
|
||||
|
||||
}}>
|
||||
<Hidden> </Hidden>
|
||||
<CardActions>
|
||||
<Button size="small">MON CV</Button>
|
||||
</CardActions>
|
||||
<CardContent>
|
||||
<Typography >
|
||||
<a href={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentCv + this.state.cvLink}
|
||||
target="_blank"
|
||||
style={{
|
||||
color:'#1689FB',
|
||||
'&:hover': {
|
||||
color: '#1689FB',
|
||||
},
|
||||
fontWeight: '700',
|
||||
fontSize: '1em',
|
||||
}}
|
||||
>
|
||||
Visionner mon CV
|
||||
</a>
|
||||
<br />
|
||||
{/* <a href="https://www.permatheque.fr/PDF/mansobufukuoka-larevolutiondunseulbrindepaille.pdf" target="_blank">lien</a> */}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
|
||||
</Card>
|
||||
</Fragment>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, company } = this.props;
|
||||
const { loading } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
{ loading ?
|
||||
<Loader /> :
|
||||
<Fragment>
|
||||
{this.AfficherSuccess()}
|
||||
|
||||
<input
|
||||
type="file"
|
||||
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document, .pdf, .odt"
|
||||
className={classes.input+' cypButtonImportVid'}
|
||||
style={{ display: 'none' }}
|
||||
id="import-button-file"
|
||||
onChange={this.inputFilesystemChange}
|
||||
/>
|
||||
<label htmlFor="import-button-file">
|
||||
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
|
||||
<img alt="talents-tube" src='/statique/images/Import-cv-candidat.svg' className={classes.iconButton} />
|
||||
<Typography className={classes.textButton}>
|
||||
J'<span className={classes.underlined}>importe</span> mon CV
|
||||
</Typography>
|
||||
<KeyboardArrowDownIcon className={ classes. InputKeyboardArrowDownIcon }/>
|
||||
</Button>
|
||||
|
||||
</label>
|
||||
<Hidden> </Hidden>
|
||||
{this.AfficherLienCv()}
|
||||
|
||||
</Fragment>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Etape3.getInitialProps = function (context) {
|
||||
|
||||
const { adId, companyId } = context.query
|
||||
const {res} = context
|
||||
//TODO :really needed ?
|
||||
context.store.dispatch(fetchCurrentUserRequest())
|
||||
|
||||
if(!!adId)
|
||||
context.store.dispatch(fetchJobRequest(adId))
|
||||
else if(!!companyId)
|
||||
context.store.dispatch(fetchCompanyRequest(companyId))
|
||||
|
||||
return {};//leave this please
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
// console.log("mapStateToProps")
|
||||
// console.log(state.loading)
|
||||
// console.log(state.user)
|
||||
// console.log(state.media.message)
|
||||
return {
|
||||
user: state.user ,
|
||||
cvLink : state.media.message,
|
||||
loading: !!state.media.loading,
|
||||
cv : true,
|
||||
//errors: errorMessageSelector(state),
|
||||
user: state.user,
|
||||
company: state.company
|
||||
};
|
||||
}
|
||||
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, fetchCompanyRequest, uploadMediaCv })(Etape3));
|
||||
@@ -0,0 +1,485 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
// import AccountStep from "../../components/organisms/AccountStep";
|
||||
import AccountEtape1 from '../organisms/AccountEtape1';
|
||||
import AccountEtape2 from '../organisms/AccountEtape2';
|
||||
import AccountEtape3 from '../organisms/AccountEtape3';
|
||||
import {withStyles} from '@material-ui/core/styles';
|
||||
// import Switch from '@material-ui/core/Switch';
|
||||
import PhoneIcon from '@material-ui/icons/Phone';
|
||||
import MailIcon from '@material-ui/icons/Mail';
|
||||
import PublicIcon from '@material-ui/icons/Public';
|
||||
import DuoIcon from '@material-ui/icons/Duo';
|
||||
import LinkedInIcon from '@material-ui/icons/LinkedIn';
|
||||
import Facebook from '@material-ui/icons/Facebook';
|
||||
// import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel
|
||||
// from '@material-ui/core/FormControlLabel'; import FormControl from
|
||||
// '@material-ui/core/FormControl';
|
||||
import {updateUserRequest, updateUserPictureRequest} from '../../actions/user'
|
||||
import {makeStyles} from '@material-ui/styles';
|
||||
import {connect} from 'react-redux'
|
||||
import getConfig from 'next/config'
|
||||
import BottomNavigation from '@material-ui/core/BottomNavigation';
|
||||
// import BottomNavigationAction from
|
||||
// '@material-ui/core/BottomNavigationAction';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
// import FavoriteIcon from '@material-ui/icons/Favorite'; import LocationOnIcon
|
||||
// from '@material-ui/icons/LocationOn';
|
||||
|
||||
import Box from '@material-ui/core/Box';
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
Avatar,
|
||||
Typography,
|
||||
Divider,
|
||||
Button,
|
||||
LinearProgress,
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
StepContent,
|
||||
StepButton,
|
||||
Paper,
|
||||
Grid
|
||||
} from '@material-ui/core';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
width: '100%'
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important
|
||||
},
|
||||
stickToBottom: {
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
bottom: 0
|
||||
}
|
||||
});
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {},
|
||||
details: {
|
||||
display: 'flex'
|
||||
},
|
||||
avatar: {
|
||||
marginLeft: 'auto',
|
||||
height: 110,
|
||||
width: 100,
|
||||
flexShrink: 0,
|
||||
flexGrow: 0
|
||||
},
|
||||
progress: {
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
information: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
[
|
||||
theme
|
||||
.breakpoints
|
||||
.up('md')
|
||||
]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-around',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
padding: theme.spacing(7, 7)
|
||||
},
|
||||
'& a': {
|
||||
textDecoration: 'none'
|
||||
}
|
||||
},
|
||||
texteBlanc: {
|
||||
color: "#fff"
|
||||
},
|
||||
uploadButton: {
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
button: {
|
||||
marginTop: theme.spacing(1),
|
||||
marginRight: theme.spacing(1)
|
||||
},
|
||||
actionsContainer: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
resetContainer: {
|
||||
padding: theme.spacing(3)
|
||||
}
|
||||
}));
|
||||
|
||||
function getSteps() {
|
||||
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
|
||||
}
|
||||
|
||||
function getStepContent(step) {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return <AccountEtape1 ></AccountEtape1>;
|
||||
case 1:
|
||||
return <AccountEtape2></AccountEtape2>;
|
||||
case 2:
|
||||
return <AccountEtape3></AccountEtape3>;
|
||||
default:
|
||||
return 'Unknown step';
|
||||
}
|
||||
}
|
||||
|
||||
const AccountProfile = props => {
|
||||
const {
|
||||
className,
|
||||
user,
|
||||
...rest
|
||||
} = props;
|
||||
const logoUrl = user.picture;
|
||||
const classes = useStyles();
|
||||
|
||||
// const user = { name: 'Laurent Laniau', city: 'Questembert', country:
|
||||
// 'France', timezone: '', avatar: '/images/avatars/avatar_11.png' };
|
||||
const {publicRuntimeConfig} = getConfig()
|
||||
const [activeStep,
|
||||
setActiveStep] = React.useState(0);
|
||||
const [completed,
|
||||
setCompleted] = React.useState(new Set());
|
||||
const [skipped,
|
||||
setSkipped] = React.useState(new Set());
|
||||
const [state,
|
||||
setState] = React.useState({checkedA: true, checkedB: true});
|
||||
const steps = getSteps();
|
||||
|
||||
const totalSteps = () => {
|
||||
return getSteps().length;
|
||||
};
|
||||
|
||||
const isStepOptional = (step) => {
|
||||
return step === 4;
|
||||
};
|
||||
|
||||
const handleSkip = () => {
|
||||
if (!isStepOptional(activeStep)) {
|
||||
// You probably want to guard against something like this it should never occur
|
||||
// unless someone's actively trying to break something.
|
||||
throw new Error("You can't skip a step that isn't optional.");
|
||||
}
|
||||
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||
setSkipped((prevSkipped) => {
|
||||
const newSkipped = new Set(prevSkipped.values());
|
||||
newSkipped.add(activeStep);
|
||||
return newSkipped;
|
||||
});
|
||||
};
|
||||
|
||||
const skippedSteps = () => {
|
||||
return skipped.size;
|
||||
};
|
||||
|
||||
const completedSteps = () => {
|
||||
return completed.size;
|
||||
};
|
||||
|
||||
const allStepsCompleted = () => {
|
||||
return completedSteps() === totalSteps() - skippedSteps();
|
||||
};
|
||||
|
||||
const isLastStep = () => {
|
||||
return activeStep === totalSteps() - 1;
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
const newActiveStep = isLastStep() && !allStepsCompleted()
|
||||
? // It's the last step, but not all steps have been completed
|
||||
// find the first step that has been completed
|
||||
steps.findIndex((step, i) => !completed.has(i))
|
||||
: activeStep + 1;
|
||||
|
||||
setActiveStep(newActiveStep);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
||||
};
|
||||
|
||||
const handleStep = (step) => () => {
|
||||
console.log("step");
|
||||
console.log(step);
|
||||
setActiveStep(step);
|
||||
};
|
||||
|
||||
const handleComplete = () => {
|
||||
const newCompleted = new Set(completed);
|
||||
newCompleted.add(activeStep);
|
||||
setCompleted(newCompleted);
|
||||
|
||||
/**
|
||||
* Sigh... it would be much nicer to replace the following if conditional with
|
||||
* `if (!this.allStepsComplete())` however state is not set when we do this,
|
||||
* thus we have to resort to not being very DRY.
|
||||
*/
|
||||
if (completed.size !== totalSteps() - skippedSteps()) {
|
||||
handleNext();
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0);
|
||||
setCompleted(new Set());
|
||||
setSkipped(new Set());
|
||||
};
|
||||
|
||||
const isStepSkipped = (step) => {
|
||||
return skipped.has(step);
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
setState({
|
||||
...state,
|
||||
[event.target.name]: event.target.checked
|
||||
});
|
||||
};
|
||||
|
||||
function isStepComplete(step) {
|
||||
return completed.has(step);
|
||||
}
|
||||
function refresh() {
|
||||
document
|
||||
.location
|
||||
.reload(true);
|
||||
console.log("refresh")
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
direction="row"
|
||||
justify="space-between"
|
||||
alignItems="flex-start">
|
||||
<Grid item lg={3} md={6} xl={4} xs={12}>
|
||||
<Card {...rest} className={clsx(classes.root, className)}>
|
||||
<CardContent>
|
||||
<div id="fb-root"></div>
|
||||
<script
|
||||
async
|
||||
defer
|
||||
crossorigin="anonymous"
|
||||
src="https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&version=v8.0&appId=2332071840142634&autoLogAppEvents=1"
|
||||
nonce="O28M4TTm"></script>
|
||||
<div className={classes.details}>
|
||||
<div>
|
||||
<Typography gutterBottom variant="h5">
|
||||
{user.lastname}
|
||||
{user.firstname}
|
||||
<a href="#" onClick={refresh}><RefreshIcon className={classes.menuButton}/></a>
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.locationText}
|
||||
color="textSecondary"
|
||||
variant="body1">
|
||||
<MailIcon className={classes.menuButton}/>
|
||||
: {user.email}<br/>
|
||||
<PhoneIcon className={classes.menuButton}/>
|
||||
: {user.phone}<br/>
|
||||
<LinkedInIcon className={classes.menuButton}/>
|
||||
: {(user.linkedin && user.linkedin != '' && user.linkedin != 'null')
|
||||
? <a href={user.linkedin} target="_blank">Lien sur mon linkendIn</a>
|
||||
: 'Non renseigné'}<br/>
|
||||
<PublicIcon className={classes.menuButton}/>
|
||||
: {(user.website && user.website != '' && user.website != 'null')
|
||||
? <a href={user.website} target="_blank">Lien sur mon site</a>
|
||||
: 'Non renseigné'}<br/>
|
||||
|
||||
|
||||
|
||||
</Typography>
|
||||
|
||||
</div>
|
||||
{/* <Avatar
|
||||
className={classes.avatar}
|
||||
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl}/> */}
|
||||
</div>
|
||||
<div>
|
||||
<Box m={3}/>
|
||||
<Divider/>
|
||||
<Box m={3}/>
|
||||
<Typography
|
||||
className={classes.locationText}
|
||||
color="textSecondary"
|
||||
variant="body1">
|
||||
|
||||
<DuoIcon className={classes.menuButton}/>
|
||||
: {(user.alias && user.alias != '' && user.alias != 'null')
|
||||
? <a
|
||||
href={publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias}
|
||||
target="_blank">Voir mon profil TalentsTube
|
||||
</a>
|
||||
: 'Non renseigné'}<br/>
|
||||
<div
|
||||
class="fb-share-button"
|
||||
data-href={publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias}
|
||||
data-layout="button_count"
|
||||
data-size="small">
|
||||
|
||||
<Facebook className={classes.menuButton}/>
|
||||
:
|
||||
<a
|
||||
target="_blank"
|
||||
href={"https://www.facebook.com/sharer/sharer.php?u=" + publicRuntimeConfig.cvThequeUrl + "talent/" + user.alias + "%2F&src=sdkpreparse"}
|
||||
class="fb-xfbml-parse-ignore"> Partager mon profil
|
||||
</a>
|
||||
</div>
|
||||
</Typography>
|
||||
</div>
|
||||
{/* <div className={classes.progress}>
|
||||
<Typography variant="body1">Etat du Profil: 70%</Typography>
|
||||
<LinearProgress value={70} variant="determinate"/>
|
||||
</div> */}
|
||||
|
||||
</CardContent>
|
||||
<Box m={2}/>
|
||||
<Divider/>
|
||||
<Box m={2}/>
|
||||
<div className={classes.information}>
|
||||
<Typography className={classes.texteBlanc} align="left"></Typography>
|
||||
</div>
|
||||
{/* <CardActions>
|
||||
<Button className={classes.uploadButton} color="primary" variant="text">
|
||||
Remplacer une photo de profil
|
||||
</Button>
|
||||
<Button variant="text">Supprimer photo de profil</Button>
|
||||
</CardActions> */}
|
||||
<Box m={1}/>
|
||||
<Divider/>
|
||||
{/* <CardContent>
|
||||
<Typography variant="body1"></Typography>
|
||||
<FormControlLabel
|
||||
value="end"
|
||||
control={< Switch checked = {
|
||||
state.checkedA
|
||||
}
|
||||
onChange = {
|
||||
handleChange
|
||||
}
|
||||
name = "checkedA" color = "primary" />}
|
||||
label="Je souhaite afficher mon profil sur la CV Vidéothèque Talents Tube"
|
||||
labelPlacement="start"/>
|
||||
|
||||
</CardContent> */}
|
||||
|
||||
|
||||
|
||||
<CardActions>
|
||||
|
||||
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
|
||||
{steps.map((label, index) => {
|
||||
const stepProps = {};
|
||||
const buttonProps = {};
|
||||
if (isStepOptional(index)) {
|
||||
buttonProps.optional = <Typography variant="caption">Optional</Typography>;
|
||||
}
|
||||
if (isStepSkipped(index)) {
|
||||
stepProps.completed = false;
|
||||
}
|
||||
return (
|
||||
<Step key={label} className={classes.actionsContainer} {...stepProps}>
|
||||
<StepButton
|
||||
onClick={handleStep(index)}
|
||||
completed={isStepComplete(index)}
|
||||
{...buttonProps}>
|
||||
{label}
|
||||
</StepButton>
|
||||
|
||||
</Step>
|
||||
);
|
||||
})}
|
||||
</Stepper>
|
||||
</CardActions>
|
||||
<Divider/>
|
||||
|
||||
<div>
|
||||
|
||||
<div>
|
||||
|
||||
<div>
|
||||
|
||||
{/* {isStepOptional(activeStep) && !completed.has(activeStep) && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleSkip}
|
||||
className={classes.button}
|
||||
>
|
||||
Skip
|
||||
</Button>
|
||||
)} */}
|
||||
|
||||
{/* {activeStep !== steps.length &&
|
||||
(completed.has(activeStep) ? (
|
||||
<Typography variant="caption" className={classes.completed}>
|
||||
Step {activeStep + 1} already completed
|
||||
</Typography>
|
||||
) : (
|
||||
<Button variant="contained" color="primary" onClick={handleComplete}>
|
||||
{completedSteps() === totalSteps() - 1 ? 'Finish' : 'Complete Step'}
|
||||
</Button>
|
||||
))} */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<Box m={2}/>
|
||||
<CardContent>
|
||||
<BottomNavigation className={classes.stickToBottom}>
|
||||
<Button
|
||||
disabled={activeStep === 0}
|
||||
onClick={handleBack}
|
||||
className={classes.button}>
|
||||
Etape précédente
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleNext}
|
||||
className={classes.button}>
|
||||
Etape suivante
|
||||
</Button>
|
||||
{/* <BottomNavigationAction label="Recents" icon={< RestoreIcon />}/>
|
||||
<BottomNavigationAction label="Favorites" icon={< FavoriteIcon />}/>
|
||||
<BottomNavigationAction label="Nearby" icon={< LocationOnIcon />}/> */}
|
||||
</BottomNavigation>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
</Grid>
|
||||
<Grid item lg={9} md={6} xl={8} xs={12}>
|
||||
{getStepContent(activeStep)}
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
AccountProfile.propTypes = {
|
||||
className: PropTypes.string
|
||||
};
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
user: state.user,
|
||||
loading: !!state.user.loading,
|
||||
errors: state.user.errors || ownProps.errors
|
||||
};
|
||||
}
|
||||
|
||||
export default withStyles(styles)(connect(mapStateToProps, {updateUserRequest})(AccountProfile))
|
||||
@@ -0,0 +1,218 @@
|
||||
import React, {Fragment} from 'react';
|
||||
import {Hidden} from '@material-ui/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import clsx from 'clsx';
|
||||
// import AccountStep from "../../components/organisms/AccountStep";
|
||||
import AccountEtape1 from './AccountEtape1';
|
||||
import AccountEtape2 from './AccountEtape2';
|
||||
import AccountEtape3 from './AccountEtape3';
|
||||
import {withStyles} from '@material-ui/core/styles';
|
||||
// import Switch from '@material-ui/core/Switch';
|
||||
import PhoneIcon from '@material-ui/icons/Phone';
|
||||
import MailIcon from '@material-ui/icons/Mail';
|
||||
import PublicIcon from '@material-ui/icons/Public';
|
||||
import DuoIcon from '@material-ui/icons/Duo';
|
||||
import LinkedInIcon from '@material-ui/icons/LinkedIn';
|
||||
import Facebook from '@material-ui/icons/Facebook';
|
||||
// import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel
|
||||
// from '@material-ui/core/FormControlLabel'; import FormControl from
|
||||
// '@material-ui/core/FormControl';
|
||||
import {updateUserRequest, updateUserPictureRequest} from '../../actions/user'
|
||||
import {makeStyles} from '@material-ui/styles';
|
||||
import {connect} from 'react-redux'
|
||||
import getConfig from 'next/config'
|
||||
import Router from 'next/router'
|
||||
|
||||
import BottomNavigation from '@material-ui/core/BottomNavigation';
|
||||
// import BottomNavigationAction from
|
||||
// '@material-ui/core/BottomNavigationAction';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
// import FavoriteIcon from '@material-ui/icons/Favorite'; import LocationOnIcon
|
||||
// from '@material-ui/icons/LocationOn';
|
||||
|
||||
import Box from '@material-ui/core/Box';
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
Avatar,
|
||||
Typography,
|
||||
Divider,
|
||||
Button,
|
||||
LinearProgress,
|
||||
Stepper,
|
||||
Step,
|
||||
StepLabel,
|
||||
StepContent,
|
||||
StepButton,
|
||||
Paper,
|
||||
Grid
|
||||
} from '@material-ui/core';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
width: '100%'
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important
|
||||
},
|
||||
stickToBottom: {
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
bottom: 0
|
||||
}
|
||||
});
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {},
|
||||
details: {
|
||||
display: 'flex'
|
||||
},
|
||||
avatar: {
|
||||
marginLeft: 'auto',
|
||||
height: 110,
|
||||
width: 100,
|
||||
flexShrink: 0,
|
||||
flexGrow: 0
|
||||
},
|
||||
progress: {
|
||||
marginTop: theme.spacing(2)
|
||||
},
|
||||
information: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
[
|
||||
theme
|
||||
.breakpoints
|
||||
.up('md')
|
||||
]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-around',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
padding: theme.spacing(7, 7)
|
||||
},
|
||||
'& a': {
|
||||
textDecoration: 'none'
|
||||
}
|
||||
},
|
||||
texteBlanc: {
|
||||
color: "#fff"
|
||||
},
|
||||
uploadButton: {
|
||||
marginRight: theme.spacing(2)
|
||||
},
|
||||
button: {
|
||||
marginTop: theme.spacing(1),
|
||||
marginRight: theme.spacing(1)
|
||||
},
|
||||
actionsContainer: {
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
resetContainer: {
|
||||
padding: theme.spacing(3)
|
||||
}
|
||||
}));
|
||||
|
||||
const AccountProfile = props => {
|
||||
const {
|
||||
className,
|
||||
user,
|
||||
...rest
|
||||
} = props;
|
||||
const logoUrl = user.picture;
|
||||
const classes = useStyles();
|
||||
|
||||
// const user = { name: 'Laurent Laniau', city: 'Questembert', country:
|
||||
// 'France', timezone: '', avatar: '/images/avatars/avatar_11.png' };
|
||||
const {publicRuntimeConfig} = getConfig()
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
direction="row"
|
||||
justify="space-between"
|
||||
alignItems="flex-start">
|
||||
|
||||
<Grid item lg={9} md={6} xl={8} xs={12}>
|
||||
<Fragment>
|
||||
<Hidden> </Hidden>
|
||||
<Hidden> </Hidden>
|
||||
|
||||
<Card
|
||||
variant="outlined"
|
||||
style={{
|
||||
width: 420 + "px",
|
||||
textAlign:"center"
|
||||
}}>
|
||||
<CardActions></CardActions>
|
||||
<CardContent>
|
||||
<Typography>
|
||||
Tu n'as pas tout dit dans ta vidéo?<br/>
|
||||
Pour mettre toutes les chances de ton côté,
|
||||
<b> dépose ton CV old school (format pdf, word)!</b>
|
||||
</Typography>
|
||||
</CardContent>
|
||||
|
||||
</Card>
|
||||
<Hidden> </Hidden>
|
||||
<Card
|
||||
variant="outlined"
|
||||
style={{
|
||||
width: 420 + "px",
|
||||
textAlign:"center"
|
||||
}}>
|
||||
<CardContent>
|
||||
<AccountEtape3></AccountEtape3>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Hidden> </Hidden>
|
||||
<Hidden> </Hidden>
|
||||
<Card
|
||||
variant="outlined"
|
||||
style={{
|
||||
width: 420 + "px",
|
||||
textAlign:"center"
|
||||
}}>
|
||||
<CardContent>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => Router.push(`/Candidatures`)}
|
||||
className={classes.button}>
|
||||
Voir mes candidatures
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Fragment>
|
||||
|
||||
</Grid>
|
||||
<Grid item lg={3} md={6} xl={4} xs={12}></Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
AccountProfile.propTypes = {
|
||||
className: PropTypes.string
|
||||
};
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
user: state.user,
|
||||
loading: !!state.user.loading,
|
||||
errors: state.user.errors || ownProps.errors
|
||||
};
|
||||
}
|
||||
|
||||
export default withStyles(styles)(connect(mapStateToProps, {updateUserRequest})(AccountProfile))
|
||||
@@ -0,0 +1,147 @@
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Stepper from '@material-ui/core/Stepper';
|
||||
import Step from '@material-ui/core/Step';
|
||||
import StepButton from '@material-ui/core/StepButton';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import AccountEtape1 from '../organisms/AccountEtape1';
|
||||
import AccountEtape2 from '../organisms/AccountEtape2';
|
||||
import AccountEtape3 from '../organisms/AccountEtape3';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
completed: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
instructions: {
|
||||
marginTop: theme.spacing(1),
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
function getSteps() {
|
||||
return ['Informations personnelles', 'Vidéo de présentation', 'Expériences professionnelles'];
|
||||
}
|
||||
|
||||
function getStepContent(step) {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return <AccountEtape1 ></AccountEtape1>;
|
||||
case 1:
|
||||
return <AccountEtape2></AccountEtape2>;
|
||||
case 2:
|
||||
return <AccountEtape3></AccountEtape3>;
|
||||
default:
|
||||
return 'Unknown step';
|
||||
}
|
||||
}
|
||||
|
||||
export default function HorizontalNonLinearStepper() {
|
||||
const classes = useStyles();
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
const [completed, setCompleted] = React.useState({});
|
||||
const steps = getSteps();
|
||||
|
||||
const totalSteps = () => {
|
||||
return steps.length;
|
||||
};
|
||||
|
||||
const completedSteps = () => {
|
||||
return Object.keys(completed).length;
|
||||
};
|
||||
|
||||
const isLastStep = () => {
|
||||
return activeStep === totalSteps() - 1;
|
||||
};
|
||||
|
||||
const allStepsCompleted = () => {
|
||||
return completedSteps() === totalSteps();
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
const newActiveStep =
|
||||
isLastStep() && !allStepsCompleted()
|
||||
? // It's the last step, but not all steps have been completed,
|
||||
// find the first step that has been completed
|
||||
steps.findIndex((step, i) => !(i in completed))
|
||||
: activeStep + 1;
|
||||
setActiveStep(newActiveStep);
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
||||
};
|
||||
|
||||
const handleStep = (step) => () => {
|
||||
setActiveStep(step);
|
||||
};
|
||||
|
||||
const handleComplete = () => {
|
||||
const newCompleted = completed;
|
||||
newCompleted[activeStep] = true;
|
||||
setCompleted(newCompleted);
|
||||
handleNext();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0);
|
||||
setCompleted({});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Stepper alternativeLabel nonLinear activeStep={activeStep}>
|
||||
{steps.map((label, index) => (
|
||||
<Step key={label}>
|
||||
<StepButton onClick={handleStep(index)} completed={completed[index]}>
|
||||
{label}
|
||||
</StepButton>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
<div>
|
||||
{allStepsCompleted() ? (
|
||||
<div>
|
||||
{/* <Typography className={classes.instructions}>
|
||||
Toutes les étapes sont terminées -
|
||||
</Typography>
|
||||
<Button onClick={handleReset}>Annuler</Button> */}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Typography className={classes.instructions}>{getStepContent(activeStep)}</Typography>
|
||||
<div>
|
||||
{/* <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
|
||||
Précédent
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleNext}
|
||||
className={classes.button}
|
||||
>
|
||||
Suivant
|
||||
</Button> */}
|
||||
{/* {activeStep !== steps.length &&
|
||||
(completed[activeStep] ? (
|
||||
<Typography variant="caption" className={classes.completed}>
|
||||
Step {activeStep + 1} already completed
|
||||
</Typography>
|
||||
) : (
|
||||
<Button variant="contained" color="primary" onClick={handleComplete}>
|
||||
{completedSteps() === totalSteps() - 1 ? 'Etape valide' : 'Processus terminé'}
|
||||
</Button>
|
||||
))} */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import PropTypes from 'prop-types';
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import Link from 'next/link';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardContent,
|
||||
CardActions,
|
||||
Divider,
|
||||
Grid,
|
||||
Button,
|
||||
TextField
|
||||
} from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {}
|
||||
}));
|
||||
|
||||
const AccountVideoPres = props => {
|
||||
const { className, ...rest } = props;
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Card
|
||||
{...rest}
|
||||
className={clsx(classes.root, className)}
|
||||
>
|
||||
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
AccountVideoPres.propTypes = {
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
export default AccountVideoPres;
|
||||
@@ -0,0 +1,328 @@
|
||||
import React, { Component, Fragment } from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { Button, Typography } from '@material-ui/core';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import { connect } from 'react-redux'
|
||||
import { postAdAnswerRequest } from '../../actions/answer'
|
||||
import { updateUserRequest, updateUserPictureRequest } from '../../actions/user'
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight';
|
||||
import Loader from './../templates/loader'
|
||||
import getConfig from 'next/config'
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
width: '100%',
|
||||
},
|
||||
form: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '24px',
|
||||
paddingBottom: theme.spacing(8),
|
||||
marginBottom: theme.spacing(8),
|
||||
'&>div': {
|
||||
marginTop: theme.spacing(1.5),
|
||||
marginBottom: theme.spacing(1.5),
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '0 calc(25vw + 24px)',
|
||||
},
|
||||
|
||||
},
|
||||
importContainer: {
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
textField: {
|
||||
width: '100%',
|
||||
'&:disabled': {
|
||||
color: theme.palette.secondary.dark,
|
||||
},
|
||||
'& label': {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
'& fieldset:hover': {
|
||||
borderColor: theme.palette.primary.main + ' !important',
|
||||
},
|
||||
},
|
||||
foot: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: '10',
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
padding: theme.spacing(1.2, 0),
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
|
||||
},
|
||||
rootButton: {
|
||||
textTransform: 'inherit',
|
||||
border: '1px solid',
|
||||
'&:hover': {
|
||||
border: '1px solid',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
},
|
||||
|
||||
importButton: {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
padding: theme.spacing(3, 2),
|
||||
margin: theme.spacing(1),
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.14)',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
InputKeyboardArrowRightIcon: {
|
||||
fontSize: '2.5em',
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
//https://xd.adobe.com/view/ba98331a-9145-4d67-7e27-25a36e519ef4-cca9/screen/a96779d7-bdb3-410a-8f19-e6987acb98ee/Candidature-Step-3
|
||||
class Answer extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// This binding is necessary to make `this` work in the callback
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
file: null,
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
phone: '',
|
||||
currentJob: '',
|
||||
linkedin: '',
|
||||
twitter: '',
|
||||
website: '',
|
||||
mediaId: null,
|
||||
companyId: null,
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value })
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
this.setState({
|
||||
firstname: nextProps.user.firstname,
|
||||
lastname: nextProps.user.lastname,
|
||||
phone: nextProps.user.phone,
|
||||
currentJob: nextProps.user.currentJob,
|
||||
linkedin: nextProps.user.linkedin,
|
||||
twitter: nextProps.user.twitter,
|
||||
website: nextProps.user.website,
|
||||
mediaId: nextProps.mediaId,
|
||||
companyId: nextProps.companyId,
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
firstname: this.props.user.firstname,
|
||||
lastname: this.props.user.lastname,
|
||||
phone: this.props.user.phone,
|
||||
currentJob: this.props.user.currentJob,
|
||||
linkedin: this.props.user.linkedin,
|
||||
twitter: this.props.user.twitter,
|
||||
website: this.props.user.website,
|
||||
})
|
||||
}
|
||||
|
||||
onSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const data = new FormData()
|
||||
|
||||
data.append('firstname', this.state.firstname)
|
||||
data.append('lastname', this.state.lastname)
|
||||
data.append('phone', this.state.phone)
|
||||
data.append('currentJob', this.state.currentJob)
|
||||
data.append('linkedin', this.state.linkedin)
|
||||
data.append('twitter', this.state.twitter)
|
||||
data.append('website', this.state.website)
|
||||
|
||||
//TODO:maybe add this two in a single transactionnal saga
|
||||
this.props.updateUserRequest(data)
|
||||
|
||||
this.props.postAdAnswerRequest(
|
||||
{
|
||||
adId: this.props.adId,
|
||||
companyId: this.props.companyId,
|
||||
mediaId: this.props.mediaId
|
||||
})
|
||||
}
|
||||
|
||||
inputFilesystemChange = (e) => {
|
||||
|
||||
console.log("inputFilesystemChange");
|
||||
var file = e.target.files[0]
|
||||
this.setState({ picture: file })
|
||||
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
this.props.updateUserPictureRequest(data)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, user } = this.props
|
||||
const logoUrl = user.picture;
|
||||
return (
|
||||
<section className={classes.container}>
|
||||
<form className={classes.form}
|
||||
autoComplete="off">
|
||||
{!!logoUrl && <Avatar className={classes.logo}
|
||||
classes={{ img: classes.MuiAvatarImg }}
|
||||
src={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.postedContentUrl + logoUrl} alt="candidatLogo" />}
|
||||
|
||||
{!!user.pictureLoading && <Loader />}
|
||||
{!!!user.pictureLoading && !!!logoUrl &&
|
||||
(<Fragment><input
|
||||
type="file" accept="image/*" capture="filesystem"
|
||||
className={classes.input}
|
||||
style={{ display: 'none' }}
|
||||
id="raised-button-file"
|
||||
multiple
|
||||
type="file"
|
||||
onChange={this.inputFilesystemChange}
|
||||
/>
|
||||
<label htmlFor="raised-button-file">
|
||||
<Button component="span" className={classes.importButton} classes={{ root: classes.rootButton }}>
|
||||
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg'
|
||||
className={classes.iconButton} />
|
||||
<Typography align="left" className={classes.textButton}>
|
||||
J'<span className={classes.underlined}>importe</span> ma photo de profil
|
||||
</Typography>
|
||||
<KeyboardArrowRightIcon className={classes.InputKeyboardArrowRightIcon} />
|
||||
</Button>
|
||||
</label></Fragment>)
|
||||
}
|
||||
<TextField
|
||||
name="lastname"
|
||||
required
|
||||
id="outlined-required"
|
||||
label="Nom"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.lastname}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="firstname"
|
||||
required
|
||||
id="outlined-required"
|
||||
label="Prénom"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.firstname}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="phone"
|
||||
id="outlined"
|
||||
label="Téléphone"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.phone}
|
||||
onChange={this.handleChange}
|
||||
type="tel"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
disabled
|
||||
id="outlined-required"
|
||||
label="E-mail"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={user.email}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="currentJob"
|
||||
id="outlined"
|
||||
label="Poste actuel"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
value={this.state.currentJob}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="linkedin"
|
||||
id="outlined"
|
||||
label="Linkedin"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://www.linkedin.com/in/"
|
||||
value={this.state.linkedin}
|
||||
onChange={this.handleChange}
|
||||
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="twitter"
|
||||
id="outlined"
|
||||
label="Twitter"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://twitter.com/.."
|
||||
value={this.state.twitter}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
name="website"
|
||||
id="outlined"
|
||||
label="Site internet"
|
||||
className={classes.textField}
|
||||
classes={{ root: classes.rootTextfield }}
|
||||
variant="outlined"
|
||||
placeholder="http://monsite.fr"
|
||||
value={this.state.website}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</form>
|
||||
<div id="F" className={classes.foot}>
|
||||
<Button variant="contained" className={classes.button + ' cypButtonSendApplication'} onClick={this.onSubmit}>Postuler</Button>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
user: state.user,
|
||||
loading: !!state.user.loading,
|
||||
pictureLoading: !!state.user.pictureLoading,
|
||||
errors: state.user.errors || ownProps.errors,
|
||||
mediaId: state.media.message && state.media.message.mediaId,
|
||||
};
|
||||
}
|
||||
|
||||
export default withStyles(styles)(connect(mapStateToProps, { postAdAnswerRequest, updateUserRequest, updateUserPictureRequest })(Answer))
|
||||
@@ -0,0 +1,263 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import Router from 'next/router'
|
||||
import getConfig from 'next/config'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import IconText from './../atoms/IconText';
|
||||
import KeyboardArrowleftIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
||||
import Link from 'next/link';
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
minHeight: '21vh',
|
||||
maxHeight: '25vh',
|
||||
width: '100%',
|
||||
margin: theme.spacing(1,0),
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
'& > div': {
|
||||
padding: '0 !important',
|
||||
},
|
||||
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '30%',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: '40%',
|
||||
},
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width: '100%',
|
||||
},
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: '1vh 0 1vh 0',
|
||||
},
|
||||
},
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
padding: theme.spacing(3, 2),
|
||||
width: '75%',
|
||||
},
|
||||
jobTitle: {
|
||||
fontSize: '1em',
|
||||
fontWeight: 'bold',
|
||||
color: theme.palette.offerlistitem.title,
|
||||
},
|
||||
companyTitle: {
|
||||
fontSize: '0.8em',
|
||||
fontWeight: '300',
|
||||
color: theme.palette.offerlistitem.title,
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
},
|
||||
cover: {
|
||||
position: 'relative',
|
||||
width: '125px',
|
||||
height: '100%',
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
},
|
||||
iconPlay: {
|
||||
fontSize: '2em',
|
||||
color: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
top: 'calc(50% - 0.5em)',
|
||||
left: 'calc(50% - 0.5em)',
|
||||
},
|
||||
iconText: {
|
||||
marginLeft: 0,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
iconTextIcon: {
|
||||
color: theme.palette.offerlistitem.icon,
|
||||
fontSize: '0.8em',
|
||||
},
|
||||
iconTextText: {
|
||||
color: theme.palette.offerlistitem.text,
|
||||
fontSize: '0.6em',
|
||||
paddingLeft: theme.spacing(1),
|
||||
},
|
||||
bottomCard: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '90%',
|
||||
},
|
||||
chip: {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
},
|
||||
icon: {
|
||||
color: theme.palette.secondary.dark,
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
|
||||
//TODO:shouldn't be here
|
||||
//must go in API
|
||||
const status = new Map([
|
||||
|
||||
["DRAFT"
|
||||
, "Brouillon"]
|
||||
,
|
||||
["TO_PUBLISH"
|
||||
, "En cours de validation"]
|
||||
,
|
||||
["VALIDATED"
|
||||
, "Validée"]
|
||||
,
|
||||
["PUBLISHED"
|
||||
, "Publiée"]
|
||||
,
|
||||
["CLOSED"
|
||||
, "Cloturée"]
|
||||
,
|
||||
["EXPIRED"
|
||||
, "Expirée"]
|
||||
,
|
||||
["SENDED"
|
||||
, "Envoyée"]
|
||||
,
|
||||
["DECLINED"
|
||||
, "Refusée"]
|
||||
,
|
||||
["NEW"
|
||||
, "Nouveau"]
|
||||
,
|
||||
["VIEWED"
|
||||
, "Visionnée"]
|
||||
,
|
||||
["NEGATIVE"
|
||||
, "Négatif"]
|
||||
,
|
||||
["POSITIVE"
|
||||
, "Positif"]
|
||||
,
|
||||
["INFOS"
|
||||
, "Demande d’informations"]
|
||||
,
|
||||
["CONTACTED"
|
||||
, "Demande d’informations"]
|
||||
,
|
||||
["CONTACTED.RDV"
|
||||
, "Convocation entretien"]
|
||||
|
||||
])
|
||||
class AnswerRowComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
|
||||
const { classes, rowData, company } = this.props;
|
||||
// company not used ?
|
||||
let companyR = company
|
||||
if (!!!company)
|
||||
companyR = rowData.ad.company;
|
||||
else
|
||||
companyR = company;
|
||||
|
||||
const imgUrl = (rowData.ad && rowData.ad.video && rowData.ad.video.thumbnails[0] && rowData.ad.video.thumbnails[0].sizes[4])
|
||||
? rowData.ad.video.thumbnails[0].sizes[4].link
|
||||
: publicRuntimeConfig.adListDefaultUrl
|
||||
const logoUrl = publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + companyR.logo
|
||||
|
||||
return (
|
||||
<Link href={`/Candidature/${rowData.id}`} >
|
||||
<Card className={classes.card} >
|
||||
{rowData.ad ? (
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={imgUrl}
|
||||
title={rowData.ad.title}
|
||||
>
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
</CardMedia>
|
||||
|
||||
<div className={classes.detailsContainer}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
className={classes.jobTitle}>
|
||||
{rowData.ad.title&&rowData.ad.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
className={classes.companyTitle}>
|
||||
{rowData.ad.company.brand_name&&rowData.ad.company.brand_name}
|
||||
</Typography>
|
||||
<div className={ classes.bottomCard }>
|
||||
<Chip size="small" label={status.get(rowData.status)} className={classes.chip} />
|
||||
<ArrowForwardIcon className={classes.icon} />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
):(
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + rowData.company.logo}
|
||||
title={rowData.company.brand_name}
|
||||
>
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
</CardMedia>
|
||||
|
||||
<div className={classes.detailsContainer}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
className={classes.jobTitle}>
|
||||
Candidature spontanée
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
className={classes.companyTitle}>
|
||||
{rowData.company.brand_name&&rowData.company.brand_name}
|
||||
</Typography>
|
||||
<div className={ classes.bottomCard }>
|
||||
<Chip size="small" label={status.get(rowData.status)} className={classes.chip} />
|
||||
<ArrowForwardIcon className={classes.icon} />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AnswerRowComponent.defaultProps = {
|
||||
company: {}
|
||||
}
|
||||
|
||||
AnswerRowComponent.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
rowData: PropTypes.object.isRequired,
|
||||
company: PropTypes.object
|
||||
};
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(AnswerRowComponent);
|
||||
@@ -0,0 +1,39 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import _ from "lodash"
|
||||
import Ar from "lodash/array"
|
||||
import VideoVimeo from '../atoms/VideoVimeo'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
width: '100%',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '500',
|
||||
},
|
||||
titleBorder: {
|
||||
'&:after': {
|
||||
content: '""',
|
||||
width: '15px',
|
||||
borderBottom: 'solid 2px',
|
||||
borderColor: theme.palette.primary.main + '!important',
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
video: {
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingBottom: '35%', /* 16:9 */
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
export default function CandidatureDetail(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<VideoVimeo classes={{ video: classes.video }} videoId={Ar.last(_.split(props.video.cdn_url, '/'))} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import React, { Fragment, useState, useCallback } from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import _ from "lodash"
|
||||
import ChatCard from '../atoms/ChatCard'
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { Button } from '@material-ui/core';
|
||||
import IconAdd from '@material-ui/icons/Add';
|
||||
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
card: {
|
||||
width: '100%',
|
||||
},
|
||||
wrapper: {
|
||||
minHeight: '100%',
|
||||
},
|
||||
contain: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '2vh 18vw',
|
||||
},
|
||||
},
|
||||
push: {
|
||||
height: '50px'
|
||||
},
|
||||
foot: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '2vh 18vw',
|
||||
},
|
||||
},
|
||||
chat: {
|
||||
|
||||
},
|
||||
filterForm: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: theme.palette.input.main,
|
||||
padding: theme.spacing(1),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: theme.spacing(3),
|
||||
},
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'inherit',
|
||||
borderRadius: '0',
|
||||
'& svg': {
|
||||
fontSize: '1.5rem',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
fontSize: '2rem',
|
||||
},
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
color: theme.palette.secondary.main,
|
||||
background: theme.palette.primary.important,
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: '50%',
|
||||
},
|
||||
video: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '11vh',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '500',
|
||||
},
|
||||
titleBorder: {
|
||||
'&:after': {
|
||||
content: '""',
|
||||
width: '15px',
|
||||
borderBottom: 'solid 2px',
|
||||
borderColor: theme.palette.primary.main + '!important',
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
function Chat(props) {
|
||||
const classes = useStyles();
|
||||
const [text, setText] = useState("");
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const messages = useSelector(state => state.answer.messages);
|
||||
const loadingMessage = useSelector(state => state.answer.loadingMessage);
|
||||
|
||||
const sendMessage = useCallback(
|
||||
(message) => dispatch({ type: 'POST_MESSAGE_ANSWER_REQUEST', payload: { answerId: props.answerId, message: message } }),
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const postMessage = () => {
|
||||
sendMessage(text);
|
||||
setText('');
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.contain}>
|
||||
{
|
||||
_.orderBy(messages, ['c_time'], ['asc']).map((message, index) => {
|
||||
return <ChatCard message={message} key={index}></ChatCard>
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="F" className={classes.foot}>
|
||||
<div className={classes.filterForm}>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={isWidthUp('md', props.width) ? 8 : 1}
|
||||
id="standard-search"
|
||||
name="chattext"
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
placeholder={"Message"}
|
||||
className={classes.textField}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
/>
|
||||
<Button onClick={()=>postMessage()}
|
||||
disabled={loadingMessage}
|
||||
variant="contained"
|
||||
className={classes.button}>
|
||||
<ArrowUpwardIcon className={classes.icon}/>
|
||||
</Button >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default withWidth()(Chat)
|
||||
@@ -6,10 +6,12 @@ import { withStyles } from '@material-ui/core/styles';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardHeader from '@material-ui/core/CardHeader';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import IconText from './../atoms/IconText';
|
||||
import KeyboardArrowleftIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import Link from 'next/link';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
|
||||
@@ -17,40 +19,57 @@ const styles = theme => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
height: '50vh',
|
||||
height: '67vh',
|
||||
width: '100%',
|
||||
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width : '30%',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width : '40%',
|
||||
},
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width : '100%',
|
||||
cursor: 'pointer',
|
||||
transform: 'translateY(-0px)',
|
||||
transition: 'transform 0.25s',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-15px)',
|
||||
transition: 'transform 0.5s, box-shadow 1.2s',
|
||||
'& .cardHover': {
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
},
|
||||
},
|
||||
|
||||
margin: '1vh 2vh 1vh 2vh',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: '1vh 0 1vh 0',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '30%',
|
||||
height: '53vh',
|
||||
},
|
||||
borderRadius: 0
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: '40%',
|
||||
height: '67vh',
|
||||
},
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
margin: theme.spacing(1.5),
|
||||
marginBottom: theme.spacing(3),
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
borderRadius: '4px',
|
||||
},
|
||||
details_container: {
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '8vh 0 0 0',
|
||||
width: '100%',
|
||||
paddingTop: theme.spacing(6),
|
||||
},
|
||||
company_title: {
|
||||
companyTitle: {
|
||||
textAlign: 'center',
|
||||
fontSize: '1em',
|
||||
fontWeight: 'normal',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: '500',
|
||||
minHeight: '5vh',
|
||||
padding: theme.spacing(0,1.5),
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexWrap: 'wrap',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
@@ -60,102 +79,108 @@ const styles = theme => ({
|
||||
height: '50%',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
position: 'relative',
|
||||
backgroundBlendMode: 'darken',
|
||||
backgroundColor: 'rgba(30,35,39,0.25)',
|
||||
transition: 'background-color 1s',
|
||||
},
|
||||
logo_container: {
|
||||
logoContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 100,
|
||||
height: 100,
|
||||
width: 85,
|
||||
height: 85,
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -66%)',
|
||||
border: 'solid 5px white',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 8px 6px -6px grey',
|
||||
boxShadow: '0px 6px 15px rgba(30, 35, 39, 0.10)',
|
||||
borderRadius: '6px',
|
||||
},
|
||||
iconTextContainer: {
|
||||
width: '100%',
|
||||
},
|
||||
iconText: {
|
||||
marginLeft: 0,
|
||||
padding: theme.spacing(2, 3),
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
iconTextIcon: {
|
||||
color: theme.palette.offerlistitem.icon,
|
||||
fontSize: '1em',
|
||||
},
|
||||
iconTextText: {
|
||||
color: theme.palette.offerlistitem.text,
|
||||
fontSize: '0.8em',
|
||||
textTransform: 'capitalize',
|
||||
paddingLeft: theme.spacing(1),
|
||||
},
|
||||
iconPlay: {
|
||||
fontSize: '3em',
|
||||
color: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
top: 'calc(50% - 0.5em)',
|
||||
left: 'calc(50% - 0.5em)',
|
||||
},
|
||||
logo: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
objectfit: 'contain',
|
||||
borderRadius: 0,
|
||||
objectFit: 'contain',
|
||||
borderRadius: '0px',
|
||||
},
|
||||
chip: {
|
||||
margin: '0 1vh 0 1vh',
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
class CompanyRowComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
const { classes, theme, rowData } = this.props;
|
||||
|
||||
const imgDefaultUrl = "https://www.talentstube.com/img/default_picture_company.svg"
|
||||
const imgDefaultUrl = "/static/images/default/default_picture_company.svg"
|
||||
|
||||
const imgUrl = (rowData.presentation_video && rowData.presentation_video.thumbnails[0] && rowData.presentation_video.thumbnails[0].sizes[4]) ? rowData.presentation_video.thumbnails[0].sizes[4].link : imgDefaultUrl
|
||||
const logoUrl = publicRuntimeConfig.logoRootUrl + rowData.logo
|
||||
const logoUrl = publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + rowData.logo
|
||||
let location = "non renseigné"
|
||||
if (rowData.addresses && rowData.addresses.billing && rowData.addresses.billing.city && rowData.addresses.billing.postcode)
|
||||
location = (rowData.addresses.billing.city.toLowerCase() + ', ' + rowData.addresses.billing.postcode);
|
||||
|
||||
const offre = (rowData.count_ads == 1) ? rowData.count_ads + ' offre' : rowData.count_ads + ' offres';
|
||||
const nbOffre = (rowData.count_ads > 0) ? offre : 'Candidature spontanée';
|
||||
|
||||
return (
|
||||
<Card onClick={() => Router.push(`/Company?id=${rowData.id}`)}>
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={imgUrl}
|
||||
title={rowData.title}
|
||||
/>
|
||||
<Link href={`/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}`} >
|
||||
<Card className={classes.card + ' cypCardCompany'}>
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover + ' cardHover'}
|
||||
image={imgUrl}
|
||||
title={rowData.brand_name}
|
||||
>
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
</CardMedia>
|
||||
<div className={classes.logoContainer}>
|
||||
<Avatar className={classes.logo} src={logoUrl} alt="companylogo" />
|
||||
</div>
|
||||
|
||||
<div className={classes.logo_container}>
|
||||
<Avatar className={classes.logo} src={logoUrl} />
|
||||
</div>
|
||||
|
||||
<div className={classes.details_container}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.company_title}>
|
||||
{rowData.business_name}
|
||||
</Typography>
|
||||
{/* <Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
align="center"
|
||||
className={classes.job_title}>
|
||||
{rowData.title}
|
||||
</Typography> */}
|
||||
</div>
|
||||
<div>
|
||||
{/* <Chip label={rowData.extra.contract_type} className={classes.chip} />
|
||||
<Chip label={rowData.location.gmap_address.address_components[0].long_name} className={classes.chip} /> */}
|
||||
</div>
|
||||
</CardContent>
|
||||
{/* <CardHeader />
|
||||
<CardContent>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={imgUrl}
|
||||
title={rowData.presentation_video&&rowData.presentation_video.title}
|
||||
/>
|
||||
<Typography gutterBottom component="h3"
|
||||
className={classes.details}>
|
||||
{rowData.brand_name}
|
||||
</Typography>
|
||||
<Typography gutterBottom component="h4"
|
||||
className={classes.details}>
|
||||
{rowData.website}
|
||||
</Typography>
|
||||
<Typography gutterBottom component="h5"
|
||||
className={classes.details}>
|
||||
{rowData.description}
|
||||
</Typography>
|
||||
</CardContent> */}
|
||||
</Card>
|
||||
<div className={classes.detailsContainer}>
|
||||
<Typography
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
className={classes.companyTitle}>
|
||||
{rowData.brand_name}
|
||||
</Typography>
|
||||
<div className={classes.iconTextContainer}>
|
||||
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={location} />
|
||||
</div>
|
||||
</div>
|
||||
<Chip color="primary" label={nbOffre} className={classes.chip} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,277 @@
|
||||
import Link from 'next/link'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { Fragment, Children } from 'react';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { Fragment } from 'react';
|
||||
import NewsLetterRegistration from './NewsLetterRegistration'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Button from '@material-ui/core/Button'
|
||||
|
||||
const PWAInstallSnack = dynamic(() => import('./PWAInstallSnack'), {
|
||||
ssr: false
|
||||
});
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
height: '10vh'
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
[theme.breakpoints.up('md')]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-around',
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
padding: theme.spacing(10, 18),
|
||||
},
|
||||
'& a': {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
padding: theme.spacing(0, 5),
|
||||
paddingBottom: theme.spacing(3.5),
|
||||
'&:first-child': {
|
||||
paddingTop: theme.spacing(4),
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: 0,
|
||||
'&:first-child': {
|
||||
paddingTop: 0,
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
containerRegion: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
padding: theme.spacing(3),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: theme.spacing(8, 0),
|
||||
},
|
||||
|
||||
},
|
||||
titleContainer: {
|
||||
color: theme.palette.primary.darkMain,
|
||||
fontSize: '1.5em',
|
||||
fontFamily: 'Dosis',
|
||||
fontWeight: '600',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
fontSize: '1.2em',
|
||||
paddingBottom: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
textContainer: {
|
||||
fontWeight: '300',
|
||||
fontSize: '0.9em',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
fontWeight: '300',
|
||||
fontSize: '0.8em',
|
||||
},
|
||||
},
|
||||
textContainerRegion: {
|
||||
fontWeight: '300',
|
||||
fontSize: '0.9em',
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
footerLogo: {
|
||||
width: '15vw',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '4vw',
|
||||
},
|
||||
},
|
||||
footerLogoTT: {
|
||||
width: '70%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingBottom: theme.spacing(1),
|
||||
width: '100%'
|
||||
},
|
||||
},
|
||||
containerReactApp: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
height: 'auto',
|
||||
paddingTop: theme.spacing(4),
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
height: '100vh',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
height: '35vh',
|
||||
padding: theme.spacing(6, 1),
|
||||
position: 'relative',
|
||||
},
|
||||
},
|
||||
titleReactApp: {
|
||||
fontFamily: 'Dosis',
|
||||
fontSize: '1.5em',
|
||||
fontWeight: '600',
|
||||
paddingTop: theme.spacing(4),
|
||||
paddingLeft: theme.spacing(4),
|
||||
paddingRight: theme.spacing(4),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
order: 1,
|
||||
width: '20%',
|
||||
textAlign: 'left',
|
||||
},
|
||||
},
|
||||
buttonReactApp: {
|
||||
textTransform: 'inherit',
|
||||
margin: theme.spacing(0, 8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
order: 3,
|
||||
margin: 'inherit',
|
||||
marginRight: '5vw',
|
||||
},
|
||||
},
|
||||
imageReactApp: {
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: 'auto',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '25%',
|
||||
order: 2,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
},
|
||||
},
|
||||
containerNewsletter: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: theme.spacing(6, 4),
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
function Footer(props) {
|
||||
const { classes } = props;
|
||||
return (
|
||||
<Fragment>
|
||||
{props.children}
|
||||
</Fragment>
|
||||
<div>
|
||||
<PWAInstallSnack >
|
||||
{({ initInstall }) =>
|
||||
<section
|
||||
className={classes.containerReactApp}
|
||||
>
|
||||
<Typography
|
||||
gutterBottom
|
||||
color="secondary"
|
||||
align="center"
|
||||
component="h3"
|
||||
className={classes.titleReactApp}
|
||||
>
|
||||
Installez l'application Talents Tube pour suivre vos candidatures
|
||||
</Typography>
|
||||
<Button
|
||||
aria-label='install'
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={initInstall}
|
||||
className={classes.buttonReactApp}
|
||||
>
|
||||
Installer l'application
|
||||
</Button>
|
||||
<img alt="télécharger-pwa" src='/static/images/newsletter/application.png' className={classes.imageReactApp} />
|
||||
</section>
|
||||
}
|
||||
</PWAInstallSnack>
|
||||
|
||||
<section className={classes.containerNewsletter}>
|
||||
<NewsLetterRegistration />
|
||||
</section>
|
||||
|
||||
<div className={classes.root}>
|
||||
<section className={classes.container}>
|
||||
<img alt="talents-tube" src='/static/images/logos/TalentsTube_logo-blanc.svg' className={classes.footerLogoTT} />
|
||||
<Link href="/Offres" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
Offre d'emploi
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/Entreprises" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
Entreprises
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/Help" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
Comment ça marche ?
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
</section>
|
||||
<section className={classes.container}>
|
||||
<Typography color="secondary" component="p" className={classes.titleContainer}>
|
||||
Informations
|
||||
</Typography>
|
||||
|
||||
<Link href="/Cgu" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
CGU
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/MentionsLegales" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
Mentions légales
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="/Confidentialite" >
|
||||
<a>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
Politique de confidentialité et Protection <br />des données
|
||||
</Typography>
|
||||
</a>
|
||||
</Link>
|
||||
</section>
|
||||
<section className={classes.container}>
|
||||
<Typography color="secondary" component="p" className={classes.titleContainer}>
|
||||
Rencontrons-nous
|
||||
</Typography>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
3, rue Louis de Broglie
|
||||
</Typography>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
56 000 Vannes
|
||||
</Typography>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
02.30.05.06.31
|
||||
</Typography>
|
||||
<Typography gutterBottom color="secondary" component="p" className={classes.textContainer}>
|
||||
contact@talentstube.com
|
||||
</Typography>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<section className={classes.containerRegion}>
|
||||
<Typography gutterBottom component="p" className={classes.textContainerRegion} align="center" >
|
||||
Entreprise soutenue par
|
||||
</Typography>
|
||||
<a href="https://www.bretagne.bzh/" target="_blank" className={classes.darkMenuItem} rel="noopener">
|
||||
<img alt="Région Bretagne" src='/static/images/logos/Logo-region.svg' className={classes.footerLogo} />
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
const NextButton = ({ hasNext, onClick, style, className, text }) => hasNext ? (
|
||||
<button type="button" onClick={onClick} style={style} className={className}>Suivant</button>
|
||||
) :
|
||||
null;
|
||||
|
||||
export default NextButton;
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
const PreviousButton = ({ hasPrevious, onClick, style, className, text }) => hasPrevious ? (
|
||||
<button type="button" onClick={onClick} style={style} className={className}>Précédent</button>
|
||||
) :
|
||||
null;
|
||||
|
||||
export default PreviousButton;
|
||||
@@ -1,43 +1,61 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Fragment, Children } from 'react';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { withStyles, getMuiTheme } from '@material-ui/core/styles';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
import Link from 'next/link'
|
||||
import Drawer from '@material-ui/core/Drawer';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import SendIcon from '@material-ui/icons/Send';
|
||||
import CodeIcon from '@material-ui/icons/Code';
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import FingerprintIcon from '@material-ui/icons/Fingerprint';
|
||||
import LocalBarIcon from '@material-ui/icons/LocalBar';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import HomeIcon from '@material-ui/icons/HomeOutlined';
|
||||
import OfferIcon from '@material-ui/icons/LocalOfferOutlined';
|
||||
import CompanyIcon from '@material-ui/icons/DomainOutlined';
|
||||
import HelpIcon from '@material-ui/icons/HelpOutline';
|
||||
import VideoIcon from '@material-ui/icons/OndemandVideoOutlined';
|
||||
import NotesIcon from '@material-ui/icons/NotesOutlined';
|
||||
import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount';
|
||||
import PersonIcon from '@material-ui/icons/Person';
|
||||
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
||||
import WorkOutlineIcon from '@material-ui/icons/WorkOutline';
|
||||
import AccountCircleOutlinedIcon from '@material-ui/icons/AccountCircleOutlined';
|
||||
import FolderSharedIcon from '@material-ui/icons/FolderShared';
|
||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
||||
import PermContactCalendar from '@material-ui/icons/PermContactCalendar';
|
||||
import VideoCam from '@material-ui/icons/Videocam';
|
||||
import SettingsIcon from '@material-ui/icons/Settings';
|
||||
import PowerSettingsNewIcon from '@material-ui/icons/PowerSettingsNew';
|
||||
import ArrowForward from '@material-ui/icons/ArrowForward';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import { ListItemText } from '@material-ui/core';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import Loader from './../templates/loader'
|
||||
import { connect } from 'react-redux'
|
||||
import { fetchCurrentUserRequest } from "../../actions/user";
|
||||
import { requestLogout } from "../../actions/auth";
|
||||
import Button from '@material-ui/core/Button';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { withRouter } from "next/router";
|
||||
import ActiveLink from "./../atoms/Activelink";
|
||||
import axios from 'axios';
|
||||
import setAuthorizationHeader from "../../data/setAuthorizationHeader";
|
||||
import getConfig from 'next/config'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
zIndex: '999',
|
||||
'& a': {
|
||||
textDecoration: 'none'
|
||||
}
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
height: '64px',
|
||||
},
|
||||
grow: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
bar: {
|
||||
},
|
||||
menuButton: {
|
||||
marginLeft: -12,
|
||||
marginRight: 20,
|
||||
@@ -46,40 +64,237 @@ const styles = theme => ({
|
||||
menuLogo: {
|
||||
cursor: 'pointer',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width:'200px'
|
||||
width: '200px'
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
width:'100px'
|
||||
width: '130px'
|
||||
},
|
||||
},
|
||||
title: {
|
||||
display: 'none',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
display: 'block',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
marginRight: 15,
|
||||
cursor: 'pointer'
|
||||
menuAvatar: {
|
||||
width: '2.5em',
|
||||
borderRadius: '50%',
|
||||
marginTop: theme.spacing(2),
|
||||
marginLeft: theme.spacing(2),
|
||||
},
|
||||
sectionDesktop: {
|
||||
display: 'none',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
display: 'flex',
|
||||
},
|
||||
menuUserName: {
|
||||
color: theme.palette.menu.emailText,
|
||||
fontSize: '1.3rem',
|
||||
fontWeight: '500',
|
||||
},
|
||||
sectionMobile: {
|
||||
menuUserEmail: {
|
||||
color: theme.palette.menu.emailText,
|
||||
fontSize: '0.9rem',
|
||||
fontWeight: '200',
|
||||
},
|
||||
darkDivider: {
|
||||
background: 'whitesmoke',
|
||||
opacity: '0.1',
|
||||
},
|
||||
List: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
height: '100%',
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
'&>a:first-child': {
|
||||
minHeight: '64px',
|
||||
height: '64px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
display: 'none',
|
||||
'&>a:first-child': {
|
||||
height: '64px',
|
||||
},
|
||||
'&>a:first-child li': {
|
||||
height: '64px',
|
||||
alignItems: 'center',
|
||||
},
|
||||
},
|
||||
},
|
||||
fullList: {
|
||||
width: 'auto',
|
||||
cursor: 'pointer'
|
||||
cursor: 'pointer',
|
||||
paddingBottom: 0
|
||||
},
|
||||
lightFullList: {
|
||||
height: '100vh',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
darkFullList: {
|
||||
backgroundColor: theme.palette.background.dark,
|
||||
},
|
||||
lightMenuItem: {
|
||||
color: theme.palette.menu.lightText,
|
||||
textDecoration: 'none',
|
||||
},
|
||||
darkMenuItem: {
|
||||
color: theme.palette.menu.darkText,
|
||||
textDecoration: 'none',
|
||||
},
|
||||
darkMenuItemSpecific: {
|
||||
'&:hover,&:active': {
|
||||
color: theme.palette.primary.main,
|
||||
}
|
||||
},
|
||||
links: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
height: '100%',
|
||||
},
|
||||
linkGroup: {
|
||||
padding: theme.spacing(1.2, 0),
|
||||
paddingLeft: 0,
|
||||
},
|
||||
videothequeLinkGroup: {
|
||||
backgroundColor: theme.palette.background.videotheque,
|
||||
},
|
||||
blogLinkGroup: {
|
||||
backgroundColor: theme.palette.background.blog,
|
||||
},
|
||||
recruteurLinkGroup: {
|
||||
backgroundColor: theme.palette.background.recruteur,
|
||||
},
|
||||
rootListItem: {
|
||||
padding: theme.spacing(1, 2),
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
rootListItemIcon: {
|
||||
padding: theme.spacing(1, 2),
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
ListItemUserConnected: {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
ListItemText: {
|
||||
fontSize: '1em',
|
||||
},
|
||||
icon: {
|
||||
paddingRight: theme.spacing(2.5),
|
||||
},
|
||||
iconAccount: {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
title: {
|
||||
color: theme.palette.secondary.main,
|
||||
fontSize: '1.5em'
|
||||
},
|
||||
email: {
|
||||
color: theme.palette.menu.emailText,
|
||||
fontSize: '0.9em'
|
||||
},
|
||||
button: {
|
||||
padding: theme.spacing(0.5, 1),
|
||||
textTransform: 'inherit',
|
||||
'& svg': {
|
||||
width: '0.7em',
|
||||
height: '0.7em',
|
||||
},
|
||||
},
|
||||
|
||||
active: {
|
||||
'& span, & svg': {
|
||||
color: theme.palette.primary.main + ' !important',
|
||||
},
|
||||
},
|
||||
|
||||
//DESKTOP PART
|
||||
|
||||
desktopMenuItem: {
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: theme.spacing(0.4),
|
||||
},
|
||||
'& span.MuiTypography-root': {
|
||||
fontSize: '0.8rem',
|
||||
fontWeight: '500',
|
||||
},
|
||||
},
|
||||
desktopMenuList: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: 0,
|
||||
},
|
||||
appBarDesktopSection: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
appBarDesktopSection1: {
|
||||
padding: theme.spacing(0, 2),
|
||||
height: '100%',
|
||||
borderRight: 'solid 1px whitesmoke',
|
||||
},
|
||||
appBarDesktopSection2: {
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
padding: theme.spacing(0, 2),
|
||||
'& ul': {
|
||||
width: '95%',
|
||||
},
|
||||
'& ul>div': {
|
||||
display: 'flex',
|
||||
},
|
||||
'& ul > div:nth-child(1) a span': {
|
||||
color: theme.palette.primary.dark,
|
||||
'&:hover, &:active': {
|
||||
color: theme.palette.primary.main,
|
||||
}
|
||||
},
|
||||
'& .MuiListItemText-root': {
|
||||
padding: theme.spacing(0, 1),
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: theme.spacing(0),
|
||||
},
|
||||
},
|
||||
'& a:last-child .MuiButtonBase-root span': {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
},
|
||||
appBarDesktopSection3: {
|
||||
padding: theme.spacing(0, 2),
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
'& a': {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
'& span, & svg': {
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
},
|
||||
toolbarDesktop: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
height: '64px',
|
||||
},
|
||||
desktopMenuButtonLogin: {
|
||||
background: theme.palette.primary.main,
|
||||
borderColor: theme.palette.primary.main,
|
||||
},
|
||||
rootListItemDesktop: {
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
iconDesktop: {
|
||||
paddingRight: theme.spacing(2.5),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingRight: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
let urlEnv;
|
||||
|
||||
class Header extends React.Component {
|
||||
state = {
|
||||
@@ -90,20 +305,34 @@ class Header extends React.Component {
|
||||
toggle: true
|
||||
};
|
||||
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
toggleDrawer = (side, open) => () => {
|
||||
this.setState({
|
||||
[side]: open,
|
||||
});
|
||||
};
|
||||
|
||||
onRedirectAccount = e => {
|
||||
urlEnv = (process.env.NODE_ENV !== 'production') ? '//localhost/app_dev.php' : process.env.SERVER_URL;
|
||||
e.preventDefault();
|
||||
var redirectAccountUrl = urlEnv + "/api/talent?bearer=" + localStorage.getItem("ttJWT");
|
||||
window.open(redirectAccountUrl);
|
||||
}
|
||||
|
||||
toggle = () => this.setState(state => ({ toggle: !state.toggle }))
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Drawer anchor="left" open={this.state.left} onClose={this.toggleDrawer('left', false)}>
|
||||
@@ -113,75 +342,69 @@ class Header extends React.Component {
|
||||
onClick={this.toggleDrawer('left', false)}
|
||||
onKeyDown={this.toggleDrawer('left', false)}
|
||||
>
|
||||
<div className={classes.fullList}>
|
||||
<List>
|
||||
<Link href="/" prefetch >
|
||||
<a><ListItem>
|
||||
<img alt="talents-tube" src='/static/images/logos/Logo-header.svg' className={classes.menuLogo} />
|
||||
<div className={classes.fullList + ' ' + classes.lightFullList}>
|
||||
<List className={classes.List}>
|
||||
|
||||
<Link href="/" >
|
||||
<a className={classes.darkMenuItem}><ListItem classes={{ root: classes.rootListItem }}>
|
||||
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} />
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
|
||||
|
||||
<Divider />
|
||||
<Link href="/" prefetch >
|
||||
<a><ListItem>
|
||||
<div className={classes.links}>
|
||||
<div className={classes.linkGroup}>
|
||||
<ActiveLink activeClassName={classes.active} href="/" >
|
||||
<a className={classes.darkMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<HomeIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Accueil'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</ActiveLink>
|
||||
<ActiveLink activeClassName={classes.active} href="/Offres" >
|
||||
<a className={classes.darkMenuItem + ' ' + classes.darkMenuItemSpecific}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<OfferIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary={`Offres d'emploi`}>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</ActiveLink>
|
||||
<ActiveLink activeClassName={classes.active} href="/Entreprises" >
|
||||
<a className={classes.darkMenuItem + ' ' + classes.darkMenuItemSpecific}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<CompanyIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Nos entreprises'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</ActiveLink>
|
||||
<ActiveLink activeClassName={classes.active} href="/Help" >
|
||||
<a className={classes.darkMenuItem + ' ' + classes.darkMenuItemSpecific}><ListItem classes={{ root: classes.rootListItem }}>
|
||||
<HelpIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Comment ça marche ?'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</ActiveLink>
|
||||
</div>
|
||||
<div>
|
||||
<div className={classes.blogLinkGroup}>
|
||||
<a href={publicRuntimeConfig.blogUrl} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<NotesIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Blog'>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
</a>
|
||||
</div>
|
||||
<div className={classes.recruteurLinkGroup}>
|
||||
<a href={publicRuntimeConfig.symphonyUrl } className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<SupervisorAccountIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Recruteurs'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ListItemText primary='Accueil'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/Offres" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary="Offres d'emploi">
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/Companies" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Entreprises'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/help" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Comment ça marche ?'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Divider />
|
||||
|
||||
<Link href="/" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='CV vidéothèque'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="https://blog.talentstube.com" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Blog'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/contact" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Contact'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Divider />
|
||||
|
||||
<Link href="https://www.talentstube.com/faire-offre-emploi-video" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Recruteurs'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
</div>
|
||||
</List>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
@@ -193,117 +416,235 @@ class Header extends React.Component {
|
||||
onClick={this.toggleDrawer('right', false)}
|
||||
onKeyDown={this.toggleDrawer('right', false)}
|
||||
>{!!this.props.loading &&
|
||||
(<div className="alert alert-info"><CircularProgress color="secondary" />Chargement...</div>
|
||||
(<Loader />
|
||||
)}
|
||||
{!!!this.props.loading && !!!this.props.fetched
|
||||
&&
|
||||
<div className={classes.fullList}>
|
||||
<div className={classes.fullList + ' ' + classes.darkFullList}>
|
||||
<List>
|
||||
<ListItem>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<Link href="/login" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Connexion'>
|
||||
</ListItemText>
|
||||
<Link href="/" >
|
||||
<a className={classes.darkMenuItem + ' cypFrontPage'}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-blanc.svg' className={classes.menuLogo} />
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
{/* TODO */}
|
||||
<Link href="/login" prefetch >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Inscription'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Divider />
|
||||
<Link href="https://www.talentstube.com/faire-offre-emploi-video" >
|
||||
<a><ListItem>
|
||||
|
||||
<ListItemText primary='Espace recruteurs'>
|
||||
<Divider light={true} />
|
||||
|
||||
<div className={classes.linkGroup}>
|
||||
<Link href="/Login" >
|
||||
<a className={classes.lightMenuItem + ' cypConnexion'}><ListItem classes={{ root: classes.rootListItemIcon }} >
|
||||
<PersonIcon className={classes.icon} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Connexion'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
{/* TODO */}
|
||||
<Link href="/Login?inscription=true" >
|
||||
<a className={classes.lightMenuItem + ' cypRegister'}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItem }}>
|
||||
<PersonAddIcon className={classes.icon} />
|
||||
<ListItemText primary='Inscription'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Divider light={true} />
|
||||
|
||||
<div className={classes.linkGroup}>
|
||||
<a href={publicRuntimeConfig.symphonyUrl + "/faire-offre-emploi-video"} className={classes.lightMenuItem}><ListItem classes={{ primary: classes.ListItemText }} classes={{ root: classes.rootListItemIcon }}>
|
||||
<WorkOutlineIcon className={classes.icon} />
|
||||
<ListItemText primary='Connexion recruteurs'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
</ListItem>
|
||||
</a>
|
||||
</div>
|
||||
</List>
|
||||
</div>}
|
||||
{!!!this.props.loading && !!this.props.fetched &&
|
||||
<Fragment>
|
||||
<ListItem>
|
||||
{this.props.user.firstname}
|
||||
{this.props.user.lastname}
|
||||
{this.props.user.email}
|
||||
{this.props.user.picture ?
|
||||
<img alt="talents-tube" src={publicRuntimeConfig.symphonyUrl +publicRuntimeConfig.postedContentUrl+ this.props.user.picture} className={classes.menuAvatar} />
|
||||
:
|
||||
<img alt="talents-tube" src='/static/images/default/account-avatar-default.svg' className={classes.menuAvatar} />
|
||||
}
|
||||
<ListItem classes={{ root: classes.rootListItem }} className={classes.ListItemUserConnected}>
|
||||
<Typography gutterBottom component="h1" className={classes.menuUserName}>
|
||||
{this.props.user.firstname + " " + this.props.user.lastname}
|
||||
</Typography>
|
||||
|
||||
<Typography gutterBottom component="h3" className={classes.menuUserEmail} >
|
||||
{this.props.user.email}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
|
||||
<Divider className={classes.darkDivider} />
|
||||
|
||||
{/* TODO:real links */}
|
||||
<Link href="/Candidatures" prefetch >
|
||||
<a><ListItem>
|
||||
<Link href="/Candidatures" >
|
||||
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<FolderSharedIcon className={classes.icon} />
|
||||
<ListItemText primary='Mes candidatures'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/help" prefetch >
|
||||
<a><ListItem>
|
||||
<Link href="/profileStep" >
|
||||
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<AccountCircleIcon className={classes.icon} />
|
||||
<ListItemText primary='Mon compte'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
<Link href="/help" prefetch >
|
||||
<a><ListItem>
|
||||
<ListItemText primary='Paramètres'>
|
||||
|
||||
<Link href="/Parametres" >
|
||||
<a className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<SettingsIcon className={classes.icon} />
|
||||
<ListItemText primary='Paramètres de notification'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
|
||||
<ListItem>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
onClick={() => this.props.requestLogout()}>Déconnexion</Button>
|
||||
</ListItem>
|
||||
<Link href="/" >
|
||||
<a onClick={() => {
|
||||
localStorage.removeItem("ttJWT");
|
||||
return this.props.requestLogout();
|
||||
}} className={classes.lightMenuItem}><ListItem classes={{ root: classes.rootListItemIcon }}>
|
||||
<PowerSettingsNewIcon className={classes.icon} />
|
||||
<ListItemText primary='Déconnexion'>
|
||||
</ListItemText>
|
||||
</ListItem></a>
|
||||
</Link>
|
||||
</Fragment>
|
||||
}
|
||||
</div>
|
||||
</Drawer>
|
||||
<AppBar position="static" className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<IconButton className={classes.menuButton} onClick={this.toggleDrawer('left', true)}
|
||||
color="inherit" aria-label="Open drawer">
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<div className={classes.grow} />
|
||||
|
||||
<img alt="talents-tube" src='/static/images/logos/Logo-header.svg' className={classes.menuLogo} />
|
||||
{isWidthUp('md', this.props.width) ?
|
||||
(<AppBar position="static" className={classes.appBar}>
|
||||
<Toolbar disableGutters={true} className={classes.toolbarDesktop}>
|
||||
<section className={classes.appBarDesktopSection + ' ' + classes.appBarDesktopSection1}>
|
||||
<Link href="/" >
|
||||
<a className={classes.darkMenuItem}>
|
||||
<ListItem classes={{ root: classes.rootListItemDesktop }}>
|
||||
<img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.desktopMenuLogo} />
|
||||
</ListItem>
|
||||
</a>
|
||||
</Link>
|
||||
</section>
|
||||
<section className={classes.appBarDesktopSection + ' ' + classes.appBarDesktopSection2}>
|
||||
<IconButton className={classes.menuButton} onClick={this.toggleDrawer('left', true)}
|
||||
color="inherit" aria-label="Open drawer">
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
|
||||
<div className={classes.grow} />
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="Profile"
|
||||
aria-controls={'primary-search-account-menu'}
|
||||
aria-haspopup="true"
|
||||
onClick={this.toggleDrawer('right', true)}
|
||||
color="inherit"
|
||||
>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<List className={classes.desktopMenuList}>
|
||||
<div>
|
||||
<ActiveLink activeClassName={classes.active} href="/Offres" >
|
||||
<a className={classes.desktopMenuItem}>
|
||||
<ListItemText primary="Offres d'emploi">
|
||||
</ListItemText>
|
||||
</a>
|
||||
</ActiveLink>
|
||||
<ActiveLink activeClassName={classes.active} href="/Entreprises" >
|
||||
<a className={classes.desktopMenuItem}>
|
||||
<ListItemText primary='Nos entreprises'>
|
||||
</ListItemText>
|
||||
</a>
|
||||
</ActiveLink>
|
||||
<ActiveLink activeClassName={classes.active} href="/Help" >
|
||||
<a className={classes.desktopMenuItem}>
|
||||
<ListItemText primary='Comment ça marche ?'>
|
||||
</ListItemText>
|
||||
</a>
|
||||
</ActiveLink>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{!!!this.props.loading && !!!this.props.fetched &&
|
||||
<Fragment>
|
||||
<Link href="/Login?inscription=true" >
|
||||
<a className={classes.desktopMenuItem}>
|
||||
<ListItem classes={{ root: classes.rootListItemDesktop }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
className={classes.button + ' ' + classes.desktopMenuButtonRegister}>
|
||||
<PersonAddIcon className={classes.iconDesktop} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Inscription'></ListItemText>
|
||||
</Button >
|
||||
</ListItem>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/Login" >
|
||||
<a className={classes.desktopMenuItem}>
|
||||
<ListItem classes={{ root: classes.rootListItemDesktop }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
className={classes.button + ' ' + classes.desktopMenuButtonLogin}>
|
||||
<PersonIcon className={classes.iconDesktop} />
|
||||
<ListItemText classes={{ primary: classes.ListItemText }} primary='Connexion'></ListItemText>
|
||||
</Button >
|
||||
</ListItem>
|
||||
</a>
|
||||
</Link>
|
||||
</Fragment>
|
||||
}
|
||||
{!!!this.props.loading && !!this.props.fetched &&
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="Profile"
|
||||
aria-controls={'primary-search-account-menu'}
|
||||
aria-haspopup="true"
|
||||
onClick={this.toggleDrawer('right', true)}
|
||||
className={classes.iconAccount + ' cypConnexionMenu'}
|
||||
>
|
||||
<AccountCircleOutlinedIcon />
|
||||
</IconButton>
|
||||
}
|
||||
</div>
|
||||
</List>
|
||||
</section>
|
||||
<section className={classes.appBarDesktopSection + ' ' + classes.appBarDesktopSection3}>
|
||||
|
||||
<a href={publicRuntimeConfig.symphonyUrl + "/faire-offre-emploi-video"} className={classes.desktopMenuItem}>
|
||||
<ListItemText primary='Espace recruteurs'>
|
||||
</ListItemText>
|
||||
<ArrowForward className={classes.icon} />
|
||||
</a>
|
||||
</section>
|
||||
</Toolbar>
|
||||
</AppBar>)
|
||||
:
|
||||
(
|
||||
<AppBar position="static" className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<IconButton className={classes.menuButton} onClick={this.toggleDrawer('left', true)}
|
||||
color="inherit" role="button">
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<div className={classes.grow} />
|
||||
<Link href="/" >
|
||||
<a><img alt="talents-tube" src='/statique/images/logos/TalentsTube_logo-color.svg' className={classes.menuLogo} /></a>
|
||||
</Link>
|
||||
<div className={classes.grow} />
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="Profile"
|
||||
aria-controls={'primary-search-account-menu'}
|
||||
aria-haspopup="true"
|
||||
onClick={this.toggleDrawer('right', true)}
|
||||
className={classes.iconAccount + ' cypConnexionMenu'}
|
||||
|
||||
>
|
||||
<AccountCircleOutlinedIcon />
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
)}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
user: state.user,
|
||||
@@ -311,5 +652,4 @@ function mapStateToProps(state) {
|
||||
loading: !!state.user.loading
|
||||
};
|
||||
}
|
||||
export default withStyles(styles)(connect(mapStateToProps, { fetchCurrentUserRequest, requestLogout })(Header));
|
||||
|
||||
export default (connect(mapStateToProps, { fetchCurrentUserRequest, requestLogout })(withStyles(styles)(withWidth()(Header))));
|
||||
@@ -2,22 +2,35 @@ import React, { Component, Fragment } from 'react'
|
||||
import GridList from '@material-ui/core/GridList';
|
||||
import GridListTile from '@material-ui/core/GridListTile';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import ArrowLeft from '@material-ui/icons/ArrowBackIosSharp';
|
||||
import ArrowRight from '@material-ui/icons/ArrowForwardIosSharp';
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
import Tile from './Tile';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
width: '100%',
|
||||
},
|
||||
rootGridList: {
|
||||
margin: '0 !important',
|
||||
},
|
||||
gridList: {
|
||||
position: 'relative',
|
||||
flexWrap: 'nowrap',
|
||||
scrollBehavior: 'smooth',
|
||||
width: '100%',
|
||||
// Promote the list into his own layer on Chrome. This cost memory but helps keeping high FPS.
|
||||
transform: 'translateZ(0)',
|
||||
height: '100%',
|
||||
marginBottom: theme.spacing(4),
|
||||
scrollbarWidth: 'none',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: 0,
|
||||
}
|
||||
},
|
||||
gridListTile: {
|
||||
display: 'flex',
|
||||
@@ -25,44 +38,114 @@ const styles = theme => ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100% !important',
|
||||
'&:first-child': {
|
||||
marginLeft: '3vw',
|
||||
//margin: '0 3vw',
|
||||
padding: '16px !important',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
margin: '0',
|
||||
paddingTop: '5vh',
|
||||
},
|
||||
},
|
||||
rootGridListTile: {
|
||||
margin: '3vw',
|
||||
|
||||
},
|
||||
tile: {
|
||||
width: '100% !important',
|
||||
overflow: 'visible !important',
|
||||
},
|
||||
anchor: {
|
||||
width: '100% !important',
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
right: '16px',
|
||||
top: '-200px',
|
||||
visibility: 'hidden',
|
||||
},
|
||||
anchorPrevious: {
|
||||
left: '-16px',
|
||||
},
|
||||
anchorNext: {
|
||||
left: '16px',
|
||||
},
|
||||
icon: {
|
||||
width: '0.9em',
|
||||
height: '0.9em',
|
||||
padding: theme.spacing(1.5),
|
||||
color: theme.palette.secondary.main,
|
||||
background: theme.palette.primary.dark,
|
||||
borderRadius: '50%',
|
||||
},
|
||||
dNone: {
|
||||
visibility: 'hidden',
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
class HorizontalScroller extends Component {
|
||||
render() {
|
||||
const { classes } = this.props
|
||||
state = {
|
||||
defaultIndex: 2,
|
||||
direction: '',
|
||||
}
|
||||
|
||||
const children =(tile)=> React.Children.map(this.props.children, child => {
|
||||
handlePrevious = () => {
|
||||
this.setState({ direction: 'previous' })
|
||||
|
||||
if (this.state.defaultIndex == 8 || this.state.defaultIndex == 5) {
|
||||
this.setState({ defaultIndex: this.state.defaultIndex - 5 })
|
||||
}
|
||||
if (this.state.defaultIndex == 3) {
|
||||
this.setState({ defaultIndex: this.state.defaultIndex - 3 })
|
||||
}
|
||||
else if (this.state.defaultIndex == 0) {
|
||||
this.setState({ defaultIndex: this.state.defaultIndex + 8 })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleNext = () => {
|
||||
this.setState({ direction: 'next' })
|
||||
if (this.state.defaultIndex == 6 || this.state.defaultIndex == 7 || this.state.defaultIndex == 8) {
|
||||
this.setState({ defaultIndex: 0 })
|
||||
}
|
||||
else if (this.state.defaultIndex == 0 || this.state.defaultIndex == 3) {
|
||||
this.setState({ defaultIndex: this.state.defaultIndex + 5 })
|
||||
}
|
||||
else if (this.state.defaultIndex < 6) {
|
||||
this.setState({ defaultIndex: this.state.defaultIndex + 3 })
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, type } = this.props
|
||||
const { defaultIndex } = this.state
|
||||
const children = (tile) => React.Children.map(this.props.children, child => {
|
||||
return React.cloneElement(child, {
|
||||
tile
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div> {/* Ne pas mettre de Fragment */}
|
||||
<GridList className={classes.gridList} cols={1.2}>
|
||||
{this.props.data.map(tile => (
|
||||
<GridListTile className={classes.gridListTile} classes={{ root: classes.rootGridListTile, tile: classes.tile }} key={tile.title}>
|
||||
<Tile rowData={tile}>
|
||||
{ children(tile) }
|
||||
<div className={classes.root}> {/* Ne pas mettre de Fragment */}
|
||||
<Hidden smDown>
|
||||
<a className={(defaultIndex < 3) ? classes.dNone : ''} onClick={this.handlePrevious} href={'#' + type + defaultIndex}><ArrowLeft classes={{ root: classes.icon }}></ArrowLeft></a>
|
||||
</Hidden>
|
||||
<GridList className={classes.gridList} cols={isWidthUp('md', this.props.width) ? 3 : isWidthUp('sm', this.props.width) ? 2.4 : 1.15} classes={{ root: classes.rootGridList }}>
|
||||
{this.props.data && this.props.data.map((tile, index) => (
|
||||
|
||||
<GridListTile className={classes.gridListTile} classes={{ root: classes.rootGridListTile, tile: classes.tile }} key={index}>
|
||||
<Tile rowData={tile} key={index}>
|
||||
{(this.state.direction == 'previous') ? (<a id={type + index} className={classes.anchor + ' ' + classes.anchorPrevious}></a>) : (<a id={type + index} className={classes.anchor + ' ' + classes.anchorNext}></a>)
|
||||
}
|
||||
{children(tile)}
|
||||
</Tile>
|
||||
</GridListTile>
|
||||
|
||||
))}
|
||||
</GridList>
|
||||
<Hidden smDown>
|
||||
<a className={(defaultIndex > 5) ? classes.dNone : ''} onClick={this.handleNext} href={'#' + type + defaultIndex}><ArrowRight classes={{ root: classes.icon }}></ArrowRight></a>
|
||||
</Hidden>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(HorizontalScroller)
|
||||
export default withStyles(styles)(withWidth()(HorizontalScroller))
|
||||
|
||||
@@ -1,34 +1,179 @@
|
||||
import React, { Component } from 'react'
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { connect } from "react-redux";
|
||||
import Validator from "validator";
|
||||
import { subscribeToNewsletterRequest } from "../../actions/user";
|
||||
import { Typography, Button } from '@material-ui/core';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
|
||||
import _ from "lodash";
|
||||
import SendIcon from '@material-ui/icons/Send';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Snack from '../atoms/snack'
|
||||
|
||||
const styles = theme => ({
|
||||
textField: {
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
},
|
||||
text: {
|
||||
fontSize: '1.2em',
|
||||
fontWeight: '600',
|
||||
paddingRight: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(3),
|
||||
'& span': {
|
||||
color: theme.palette.primary.main
|
||||
},
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: '25%',
|
||||
paddingBottom: 0,
|
||||
},
|
||||
},
|
||||
textField: {
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: 0,
|
||||
'& input': {
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
minWidth: '65vw',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
minWidth: 'inherit',
|
||||
},
|
||||
}
|
||||
},
|
||||
button: {
|
||||
padding: theme.spacing(2, 0),
|
||||
borderBottomLeftRadius: 0,
|
||||
borderTopLeftRadius: 0,
|
||||
boxShadown: '0px',
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
sendIcon: {
|
||||
fill: theme.palette.background.grey,
|
||||
},
|
||||
newsletterForm: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
margin: theme.spacing(0, 4),
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: 'max-content',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
class NewsLetterRegistration extends Component {
|
||||
state = {
|
||||
data: {
|
||||
email: "",
|
||||
},
|
||||
success: false,
|
||||
errors: { message: "" },
|
||||
loading: false,
|
||||
tabIndex: 0,
|
||||
openSnack: false
|
||||
};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
|
||||
this.setState({
|
||||
errors: !!!nextProps.errors ? { errors: { message: "" } } : nextProps.errors,
|
||||
loading: nextProps.loading,
|
||||
success: nextProps.success,
|
||||
openSnack: !_.isEmpty(nextProps.errors) || nextProps.success
|
||||
})
|
||||
}
|
||||
|
||||
onChange = e =>
|
||||
this.setState({
|
||||
data: { ...this.state.data, [e.target.name]: e.target.value }
|
||||
});
|
||||
|
||||
onSubmit = e => {
|
||||
e.preventDefault();
|
||||
const errors = this.validate(this.state.data);
|
||||
this.setState({ errors });
|
||||
if (Object.keys(errors).length === 0) {
|
||||
this.props.subscribeToNewsletterRequest(this.state.data)
|
||||
}
|
||||
};
|
||||
|
||||
submit = data => { this.props.login(data) }
|
||||
|
||||
validate = data => {
|
||||
const errors = {};
|
||||
if (!Validator.isEmail(data.email)) errors.email = "Invalid email";
|
||||
return errors;
|
||||
};
|
||||
|
||||
handleClose = e =>
|
||||
this.setState({
|
||||
openSnack: false
|
||||
});
|
||||
|
||||
render() {
|
||||
let {classes} = this.props
|
||||
const { classes } = this.props;
|
||||
const { data, success, errors, loading, openSnack } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography gutterBottom component="h3">
|
||||
Recevez les dernières nouvelles de Talents Tube par e-mail
|
||||
<section className={classes.root}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
component="h3"
|
||||
className={classes.text}
|
||||
>
|
||||
Recevez les dernières nouvelles de <span>Talents Tube</span> par e-mail
|
||||
</Typography>
|
||||
<TextField
|
||||
id="outlined-bare"
|
||||
className={classes.textField}
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
inputProps={{ 'aria-label': 'bare' }}
|
||||
/>
|
||||
<Button variant="outlined" onClick={() => alert("Salut, tu veux faire un tuto ?")}>Ok</Button>
|
||||
</div>
|
||||
<form className={classes.newsletterForm} onSubmit={this.onSubmit}>
|
||||
<TextField
|
||||
id="outlined-with-placeholder"
|
||||
disabled={loading}
|
||||
label="Newsletter"
|
||||
name="email"
|
||||
value={data.email}
|
||||
onChange={this.onChange}
|
||||
type="email"
|
||||
placeholder="exemple@gmail.com"
|
||||
className={classes.textField}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
margin="dense"
|
||||
variant="filled"
|
||||
/>
|
||||
<Button type="submit"
|
||||
name="registerToNewsletter"
|
||||
disabled={loading}
|
||||
variant="contained"
|
||||
className={classes.button}
|
||||
>
|
||||
<SendIcon className={classes.sendIcon}/>
|
||||
</Button >
|
||||
</form>
|
||||
<Snack name={success ? 'snackSuccess' : 'snackWarning'}
|
||||
variant={success ? 'success' : 'warning'}
|
||||
message={success ? 'Inscription validée.' : errors.message}
|
||||
openSnack={openSnack}
|
||||
onClose={this.handleClose} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(NewsLetterRegistration)
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
openSnack: !_.isEmpty(ownProps.errors),
|
||||
loading: !!state.user.loading,
|
||||
success: state.user.NewsLetterRegistrationSuccess,
|
||||
errors: state.user.errors || ownProps.errors
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default (connect(mapStateToProps, { subscribeToNewsletterRequest })(withStyles(styles)(NewsLetterRegistration)));
|
||||
|
||||
@@ -0,0 +1,489 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import Router from 'next/router'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import getConfig from 'next/config'
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import KeyboardArrowleftIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import IconText from '../atoms/IconText';
|
||||
import IconTextInline from './../atoms/IconTextInline';
|
||||
import SocialShare from './SocialShare';
|
||||
import SimpleCollapse from '../atoms/SimpleCollapse';
|
||||
import _ from "lodash/string";
|
||||
import Ar from "lodash/array";
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
import Link from 'next/link'
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import VideoVimeo from '../atoms/VideoVimeo'
|
||||
import OgHead from '../atoms/OgHead'
|
||||
import JobPosting from '../atoms/JobPosting'
|
||||
import Moment from 'react-moment';
|
||||
|
||||
const styles = theme => ({
|
||||
primary: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
root: {
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
paddingTop: theme.spacing(20),
|
||||
justifyContent: 'center',
|
||||
},
|
||||
container: {
|
||||
padding: theme.spacing(0, 3),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '0 30vw',
|
||||
},
|
||||
},
|
||||
headerContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
margin: theme.spacing(1.5, 0),
|
||||
},
|
||||
headerContainerDesktop: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-between',
|
||||
margin: theme.spacing(1.5, 0),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '16px 18vw',
|
||||
},
|
||||
'& button': {
|
||||
marginRight: 0,
|
||||
},
|
||||
},
|
||||
offerInfoContainer: {
|
||||
display: 'flex',
|
||||
},
|
||||
infoContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: theme.spacing(0, 1.5),
|
||||
'& a': {
|
||||
textDecoration: 'none',
|
||||
color: theme.palette.primary.dark,
|
||||
}
|
||||
},
|
||||
logoContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 60,
|
||||
height: 60,
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0px 6px 15px rgba(30, 35, 39, 0.10)',
|
||||
border: 'solid 5px white',
|
||||
borderRadius: '5px',
|
||||
|
||||
},
|
||||
IconTextContainer: {
|
||||
paddingTop: theme.spacing(1.5, 0),
|
||||
},
|
||||
IconTextContainerDesktop: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
},
|
||||
contentContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
margin: theme.spacing(1.5, 0),
|
||||
},
|
||||
tagsContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
},
|
||||
shareContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
discoverContainerCompany: {
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
paddingBottom: theme.spacing(2),
|
||||
'& section': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
},
|
||||
},
|
||||
discoverContainerImage: {
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
'& img': {
|
||||
width: '100%',
|
||||
borderRadius: '5px',
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.04), 0 1px 18px 0 rgba(0, 0, 0, 0.03), 0 3px 5px -1px rgba(0, 0, 0, 0.04)',
|
||||
'&:hover': {
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.15), 0 1px 18px 0 rgba(0, 0, 0, 0.10), 0 3px 5px -1px rgba(0, 0, 0, 0.15)',
|
||||
transition: 'box-shadow 2s',
|
||||
}
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
discoverContainerTerritory: {
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
paddingBottom: theme.spacing(10),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(8),
|
||||
},
|
||||
},
|
||||
discoverTerritoryContentContainer: {
|
||||
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
|
||||
},
|
||||
logo: {
|
||||
width: 'inherit',
|
||||
height: '100%',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '0.25em',
|
||||
},
|
||||
MuiAvatarImg: {
|
||||
objectFit: 'contain',
|
||||
},
|
||||
title: {
|
||||
fontSize: '1.3em',
|
||||
fontWeight: '500',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingTop: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
offreTitle: {
|
||||
[theme.breakpoints.up('md')]: {
|
||||
fontSize: '1.3em',
|
||||
fontWeight: '600',
|
||||
},
|
||||
},
|
||||
discoverTitle: {
|
||||
padding: theme.spacing(3, 0),
|
||||
},
|
||||
contentText: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '300',
|
||||
padding: theme.spacing(1, 0),
|
||||
whiteSpace: 'pre-line',
|
||||
textAlign: 'justify',
|
||||
},
|
||||
contentCondition: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '300',
|
||||
textTransform: 'uppercase',
|
||||
textAlign: 'center',
|
||||
padding: theme.spacing(1.5, 0),
|
||||
},
|
||||
contentShare: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
},
|
||||
companyName: {
|
||||
fontSize: '1.3em',
|
||||
fontWeight: '300',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
fontSize: '1em',
|
||||
},
|
||||
},
|
||||
iconText: {
|
||||
marginLeft: 0,
|
||||
justifyContent: 'flex-start',
|
||||
padding: theme.spacing(0.25, 0),
|
||||
},
|
||||
iconTextDesktop: {
|
||||
marginLeft: 0,
|
||||
justifyContent: 'flex-start',
|
||||
padding: theme.spacing(0.25, 0),
|
||||
width: 'inherit',
|
||||
},
|
||||
iconTextIcon: {
|
||||
color: theme.palette.offerlistitem.icon,
|
||||
fontSize: '1.3em',
|
||||
},
|
||||
iconTextText: {
|
||||
fontWeight: 'lighter',
|
||||
color: theme.palette.offerlistitem.text,
|
||||
fontSize: '1em',
|
||||
paddingLeft: theme.spacing(1),
|
||||
},
|
||||
iconTextTextDesktop: {
|
||||
fontWeight: 'lighter',
|
||||
color: theme.palette.offerlistitem.text,
|
||||
fontSize: '1em',
|
||||
paddingLeft: theme.spacing(1),
|
||||
width: '100%',
|
||||
},
|
||||
iconPlay: {
|
||||
fontSize: '3.5em',
|
||||
color: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
top: 'calc(50% - 0.5em)',
|
||||
left: 'calc(50% - 0.5em)',
|
||||
},
|
||||
tags: {
|
||||
color: theme.palette.primary.dark,
|
||||
fontWeight: '300',
|
||||
backgroundColor: theme.palette.tags.main,
|
||||
textTransform: 'capitalize',
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
shareIcons: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
chip: {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.secondary.important,
|
||||
padding: theme.spacing(1),
|
||||
margin: theme.spacing(1, 0),
|
||||
fontSize: '1em',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
marginRight: 'auto',
|
||||
},
|
||||
},
|
||||
videoOffre: {
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingBottom: '35%', /* 16:9 */
|
||||
},
|
||||
},
|
||||
videoTerritory: {
|
||||
paddingBottom: '56% !important',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
paddingBottom: '56% !important',
|
||||
}
|
||||
},
|
||||
territoryTitle: {
|
||||
fontWeight: '500',
|
||||
padding: theme.spacing(2),
|
||||
paddingBottom: 0,
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
territoryType: {
|
||||
fontWeight: '200',
|
||||
padding: theme.spacing(2),
|
||||
paddingTop: 0,
|
||||
background: theme.palette.background.default,
|
||||
borderBottomLeftRadius: '5px',
|
||||
borderBottomRightRadius: '5px',
|
||||
},
|
||||
})
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
|
||||
function OffreDetail(props) {
|
||||
let { classes, offre, isCandidature } = props;
|
||||
const logoUrl = (offre.company ? publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + offre.company.logo : "/static/images/defaultdefault_picture_company.svg/")
|
||||
const DefaultUrl = "/static/images/default/default_picture_company.svg"
|
||||
const imgUrl = (offre.company && offre.company.presentation_video && offre.company.presentation_video.thumbnails && offre.company.presentation_video.thumbnails[0] && offre.company.presentation_video.thumbnails[0].sizes[2]) ? offre.company.presentation_video.thumbnails[0].sizes[2].link : DefaultUrl
|
||||
const imgUrlDesktop = (offre.company && offre.company.presentation_video && offre.company.presentation_video.thumbnails && offre.company.presentation_video.thumbnails[0] && offre.company.presentation_video.thumbnails[0].sizes[4]) ? offre.company.presentation_video.thumbnails[0].sizes[4].link : DefaultUrl
|
||||
const socialShareUrl = typeof window !== 'undefined' ? window.location : publicRuntimeConfig.frontServerUrl + `/Offre/${offre.id}-${_.kebabCase(offre.title)}`;
|
||||
const entrepriseLink = (offre.company && `/Entreprise/${offre.company.id}-${_.kebabCase(offre.company.brand_name)}`);
|
||||
const territoryVideoUrlTab = (offre.territories && offre.territories[0] && offre.territories[0].territory) ? offre.territories[0].territory.presentation_video.cdn_url.split('/') : '';
|
||||
const territoryVideoId = territoryVideoUrlTab[2]
|
||||
let descriptionSEO = ""
|
||||
descriptionSEO = offre.company.brand_name + " recrute en vidéo un(e) "+offre.title
|
||||
offre.location.gmap_address.formatted_address ? descriptionSEO += ", "+offre.location.gmap_address.formatted_address : ""
|
||||
return (
|
||||
<Fragment>
|
||||
{/* {offre.public_tag.map((label, index) => {
|
||||
descriptionSEO += label+", "
|
||||
})} */}
|
||||
|
||||
<OgHead seo_title={offre.title}
|
||||
seo_description={descriptionSEO}
|
||||
seo_url={socialShareUrl}
|
||||
image={imgUrlDesktop}
|
||||
></OgHead>
|
||||
<JobPosting job_title={offre.title}
|
||||
job_description={offre.description}
|
||||
job_brand= {offre.company.brand_name && offre.company.brand_name}
|
||||
job_brand_link= {entrepriseLink}
|
||||
job_title = {offre.title}
|
||||
job_location = {offre.location.gmap_address.formatted_address}
|
||||
job_date = {offre.published_at}
|
||||
></JobPosting>
|
||||
{offre.video && (
|
||||
<Fragment>
|
||||
<Hidden smDown>
|
||||
<section className={classes.headerContainerDesktop}>
|
||||
<div className={classes.offerInfoContainer}>
|
||||
|
||||
<a href={entrepriseLink} >
|
||||
<div className={classes.logoContainer}>
|
||||
|
||||
<Avatar className={classes.logo} classes={{ img: classes.MuiAvatarImg }}
|
||||
src={logoUrl} alt={offre.company.brand_name && offre.company.brand_name} />
|
||||
|
||||
</div>
|
||||
</a>
|
||||
<div className={classes.infoContainer}>
|
||||
<Typography gutterBottom className={classes.offreTitle}>
|
||||
{offre.title} {/*- publié le <Moment format="DD/MM/YYYY" date={offre.published_at} /> */}
|
||||
|
||||
</Typography>
|
||||
<a href={entrepriseLink} >
|
||||
<Typography gutterBottom variant="subtitle1" className={classes.companyName}>
|
||||
{offre.company.brand_name ? offre.company.brand_name : ""}
|
||||
</Typography>
|
||||
</a>
|
||||
{offre.is_promoted && (
|
||||
<Chip label="Pourvue" className={classes.chip} />
|
||||
)}
|
||||
{/* ville */}
|
||||
</div>
|
||||
</div>
|
||||
{!!!offre.is_promoted && (
|
||||
<Link href={`/Reponse?adId=${offre.id}`}>
|
||||
<Button
|
||||
variant="contained"
|
||||
className={classes.button + ' cypButtonApply'}
|
||||
>Postuler</Button>
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
</Hidden>
|
||||
|
||||
<VideoVimeo {...isCandidature == 'true' ? 'classes={{ video: classes.videoOffre }}' : ''} videoId={Ar.last(_.split(offre.video.cdn_url, '/'))} autoplay='1' />
|
||||
|
||||
<div className={classes.container}>
|
||||
<Hidden mdUp>
|
||||
<section className={classes.headerContainer}>
|
||||
<div className={classes.logoContainer}>
|
||||
|
||||
<a href={entrepriseLink} >
|
||||
<Avatar className={classes.logo} classes={{ img: classes.MuiAvatarImg }}
|
||||
src={logoUrl} alt="companylogo" />
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div className={classes.infoContainer}>
|
||||
<Typography gutterBottom variant="h5" component="h1" className={classes.title}>
|
||||
{offre.title}
|
||||
</Typography>
|
||||
<a href={entrepriseLink} >
|
||||
<Typography gutterBottom variant="subtitle1" className={classes.text}>
|
||||
{offre.company.brand_name && offre.company.brand_name}
|
||||
</Typography>
|
||||
</a>
|
||||
{/* ville */}
|
||||
</div>
|
||||
</section>
|
||||
<section className={classes.IconTextContainer}>
|
||||
{offre.is_promoted && (<Chip label="Pourvue" className={classes.chip} />)}
|
||||
{offre.extra && (<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="assignment" text={offre.extra.contract_type} />)}
|
||||
{offre.location && offre.location.gmap_address && (<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={offre.location.gmap_address.formatted_address} />)}
|
||||
</section>
|
||||
</Hidden>
|
||||
|
||||
|
||||
<div className={classes.contentContainer}>
|
||||
<Typography gutterBottom className={classes.title}>
|
||||
Détails de l'offre d'emploi
|
||||
</Typography>
|
||||
<Hidden smDown>
|
||||
<section className={classes.IconTextContainerDesktop}>
|
||||
<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="assignment" text={offre.extra.contract_type} />
|
||||
<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={offre.location.gmap_address.formatted_address} />
|
||||
{offre.beneficiaire && (<IconTextInline classes={{ icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business" text={offre.beneficiaire} /> )}
|
||||
</section>
|
||||
</Hidden>
|
||||
<SimpleCollapse>
|
||||
<Typography gutterBottom className={classes.contentText}>
|
||||
{offre.description}
|
||||
</Typography>
|
||||
<section className={classes.tagsContainer}>
|
||||
{offre.public_tag.map((label, index) => {
|
||||
return (
|
||||
<Chip label={label} className={classes.tags} key={index} />
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
</SimpleCollapse>
|
||||
|
||||
{!offre.is_promoted && (
|
||||
<div className={classes.shareContainer}>
|
||||
<Link href={`/Reponse?adId=${offre.id}`}>
|
||||
<Button
|
||||
cyp="ButtonApply"
|
||||
variant="contained"
|
||||
className={classes.button}
|
||||
|
||||
>Postuler</Button>
|
||||
</Link>
|
||||
<Typography className={classes.contentCondition}>
|
||||
ou
|
||||
</Typography>
|
||||
<Typography gutterBottom className={classes.contentShare}>
|
||||
Partagez l'offre sur votre réseau
|
||||
</Typography>
|
||||
<section className={classes.ShareIcons}>
|
||||
<SocialShare url={socialShareUrl} />
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{offre.company.presentation_video && !offre.is_promoted && (
|
||||
<div className={classes.container + ' ' + classes.discoverContainerCompany}>
|
||||
<section>
|
||||
<Typography gutterBottom className={classes.title + ' ' + classes.discoverTitle} align="center">
|
||||
Découvrir l'entreprise <span className={classes.primary}>{offre.company.brand_name && offre.company.brand_name}</span>
|
||||
</Typography>
|
||||
|
||||
<a href={entrepriseLink}>
|
||||
<div className={classes.discoverContainerImage}>
|
||||
<img src={isWidthUp('sm', props.width) ? imgUrlDesktop : imgUrl} alt={offre.company.brand_name && offre.company.brand_name} />
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
</div>
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
{offre.territories && territoryVideoId && (
|
||||
<div className={classes.container + ' ' + classes.discoverContainerTerritory}>
|
||||
<section>
|
||||
<Typography gutterBottom className={classes.title + ' ' + classes.discoverTitle} align="center">
|
||||
Découvrir le <span className={classes.primary}>territoire</span>
|
||||
</Typography>
|
||||
<section className={classes.discoverTerritoryContentContainer}>
|
||||
<VideoVimeo classesOverride={classes.videoTerritory} videoId={territoryVideoId} autoplay='0' />
|
||||
<Typography className={classes.territoryTitle}>
|
||||
{(!!!offre.territories) ? offre.territories[0].territory.brand_name : ''}
|
||||
</Typography>
|
||||
<Typography className={classes.territoryType} gutterBottom>
|
||||
{(!!!offre.territories) ? offre.territories[0].territory.extra.terType : ''}
|
||||
</Typography>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
export default withStyles(styles)(withWidth()(OffreDetail));
|
||||
@@ -1,83 +1,113 @@
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import Router from 'next/router'
|
||||
import getConfig from 'next/config'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardHeader from '@material-ui/core/CardHeader';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import IconText from './../atoms/IconText';
|
||||
import KeyboardArrowleftIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import Link from 'next/link';
|
||||
import _ from 'lodash';
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
height: '50vh',
|
||||
minHeight: '21vh',
|
||||
maxHeight: '32vh',
|
||||
borderRadius: '5px',
|
||||
width: '100%',
|
||||
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0px 2px 4px rgba(30, 35, 39, 0.15)',
|
||||
transform: 'translateY(-0px)',
|
||||
transition: 'transform 0.25s, box-shadow ease-out',
|
||||
|
||||
'&:hover': {
|
||||
boxShadow: '0px 6px 16px rgba(30, 35, 39, 0.15)',
|
||||
transform: 'translateY(-5px)',
|
||||
transition: 'transform 0.5s, box-shadow 2s',
|
||||
'& .cardHover': {
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
},
|
||||
},
|
||||
'& > div': {
|
||||
padding: '0 !important',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width : '30%',
|
||||
minHeight: '15vh',
|
||||
maxHeight: '20vh',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width : '40%',
|
||||
},
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width : '100%',
|
||||
},
|
||||
|
||||
margin: '1vh 2vh 1vh 2vh',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: '1vh 0 1vh 0',
|
||||
},
|
||||
borderRadius: 0
|
||||
},
|
||||
details_container: {
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '10vh 0 0 0',
|
||||
alignItems: 'flex-start',
|
||||
padding: theme.spacing(2),
|
||||
width: '75%',
|
||||
// minHeight: '200px',
|
||||
},
|
||||
company_title: {
|
||||
fontSize: '1em',
|
||||
fontWeight: 'normal',
|
||||
textTransform: 'uppercase',
|
||||
jobTitle: {
|
||||
fontSize: '1.2em',
|
||||
fontWeight: 'bold',
|
||||
color: theme.palette.offerlistitem.title,
|
||||
},
|
||||
companyTitle: {
|
||||
fontSize: '1.2em',
|
||||
fontWeight: '300',
|
||||
color: theme.palette.offerlistitem.title,
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
},
|
||||
cover: {
|
||||
width: '100%',
|
||||
height: '50%',
|
||||
position: 'relative',
|
||||
width: '125px',
|
||||
height: '100%',
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundBlendMode: 'darken',
|
||||
backgroundColor: 'rgba(30,35,39,0.25)',
|
||||
transition: 'background-color 1s',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '35%',
|
||||
},
|
||||
},
|
||||
logo_container: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 100,
|
||||
height: 100,
|
||||
|
||||
iconPlay: {
|
||||
fontSize: '2em',
|
||||
color: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -66%)',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 8px 6px -6px grey',
|
||||
top: 'calc(50% - 0.5em)',
|
||||
left: 'calc(50% - 0.5em)',
|
||||
},
|
||||
logo: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
objectfit: 'contain',
|
||||
borderRadius: 0,
|
||||
iconText: {
|
||||
marginLeft: 0,
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
iconTextIcon: {
|
||||
color: theme.palette.offerlistitem.icon,
|
||||
fontSize: '1.2em',
|
||||
},
|
||||
iconTextText: {
|
||||
color: theme.palette.offerlistitem.text,
|
||||
fontSize: '1em',
|
||||
paddingLeft: theme.spacing(1),
|
||||
},
|
||||
chip: {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.secondary.important,
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -86,51 +116,63 @@ class OffreRowComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
|
||||
const { classes, rowData } = this.props;
|
||||
const { classes, rowData, company } = this.props;
|
||||
|
||||
let companyR = company
|
||||
if (!!!company)
|
||||
companyR = rowData.company;
|
||||
else
|
||||
companyR = company;
|
||||
|
||||
const imgUrl = (rowData.video && rowData.video.thumbnails && rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
|
||||
|
||||
const imgUrl = (rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
|
||||
const logoUrl = publicRuntimeConfig.logoRootUrl + rowData.company.logo
|
||||
//rowData._embedded.company.business_name
|
||||
return (
|
||||
<Link href={`/Offre/${rowData.id}-${_.kebabCase(rowData.title)}`} >
|
||||
<Card className={classes.card + ' cypCardOffre'} >
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={ classes.cover + ' cardHover'}
|
||||
image={imgUrl}
|
||||
title={rowData.title}
|
||||
>
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
</CardMedia>
|
||||
|
||||
<Card className={classes.card} onClick={() => Router.push(`/Offre?id=${rowData.id}`)}>
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={imgUrl}
|
||||
title={rowData.title}
|
||||
/>
|
||||
<div className={classes.detailsContainer}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
className={classes.jobTitle}>
|
||||
{rowData.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
className={classes.companyTitle}>
|
||||
{companyR.brand_name ? companyR.brand_name : "Non renseigné"}
|
||||
</Typography>
|
||||
<div>
|
||||
{rowData.is_promoted && (
|
||||
<Chip label="Pourvue" className={classes.chip} />
|
||||
)}
|
||||
|
||||
<div className={classes.logo_container}>
|
||||
<Avatar className={classes.logo} src={logoUrl} />
|
||||
</div>
|
||||
{!rowData.is_promoted && (
|
||||
<Fragment>
|
||||
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business_center" text={rowData.extra.contract_type} />
|
||||
<IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="location_on" text={rowData.location.gmap_address.formatted_address} />
|
||||
{rowData.beneficiaire ? <IconText classes={{ root: classes.iconText, icon: classes.iconTextIcon, text: classes.iconTextText }} variant="business" text={rowData.beneficiaire} /> : ""}
|
||||
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<div className={classes.details_container}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
align="center"
|
||||
className={classes.company_title}>
|
||||
{rowData.company.business_name}
|
||||
</Typography>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.job_title}>
|
||||
{rowData.title}
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Chip label={rowData.extra.contract_type} className={classes.chip} />
|
||||
<Chip label={rowData.location.gmap_address.address_components[0].long_name} className={classes.chip} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,85 +8,124 @@ import CardMedia from '@material-ui/core/CardMedia';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
|
||||
const styles = theme => ({
|
||||
Snackbar: {
|
||||
zIndex: 10001,
|
||||
},
|
||||
cardSnack: {
|
||||
maxWidth: 345,
|
||||
},
|
||||
media: {
|
||||
height: 140,
|
||||
}
|
||||
},
|
||||
askToInstallText: {
|
||||
fontSize: '1.4em',
|
||||
fontWeight: '500',
|
||||
fontFamily: 'Dosis',
|
||||
},
|
||||
installApp: {
|
||||
width: '100%',
|
||||
},
|
||||
installStepText: {
|
||||
fontSize: '1.2em',
|
||||
fontWeight: '500',
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
button: {
|
||||
color: theme.palette.primary.main,
|
||||
borderColor: theme.palette.primary.main,
|
||||
width: '50%',
|
||||
},
|
||||
buttonCompris: {
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
class PWAInstallSnack extends Component {
|
||||
state = {
|
||||
openSnack: false,
|
||||
buttonDisplay: true
|
||||
buttonDisplay: false
|
||||
}
|
||||
|
||||
isAvailable = () =>{
|
||||
console.log(this.buttonDisplay)
|
||||
isAvailable = () => {
|
||||
return this.state.buttonDisplay;
|
||||
}
|
||||
|
||||
initInstall = () =>
|
||||
{
|
||||
this.setState({openSnack: true})
|
||||
initInstall = () => {
|
||||
this.setState({ openSnack: true })
|
||||
}
|
||||
getStateAndHelpers() {
|
||||
return {
|
||||
on: this.state.openSnack,
|
||||
initInstall: this.initInstall,
|
||||
on: this.state.openSnack,
|
||||
initInstall: this.initInstall,
|
||||
}
|
||||
}
|
||||
}
|
||||
iOS() {
|
||||
|
||||
var iDevices = [
|
||||
'iPad Simulator',
|
||||
'iPhone Simulator',
|
||||
'iPod Simulator',
|
||||
'iPad',
|
||||
'iPhone',
|
||||
'iPod'
|
||||
'iPad Simulator',
|
||||
'iPhone Simulator',
|
||||
'iPod Simulator',
|
||||
'iPad',
|
||||
'iPhone',
|
||||
'iPod'
|
||||
];
|
||||
|
||||
|
||||
if (!!navigator.platform) {
|
||||
while (iDevices.length) {
|
||||
if (navigator.platform === iDevices.pop()){ return true; }
|
||||
}
|
||||
while (iDevices.length) {
|
||||
if (navigator.platform === iDevices.pop()) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BeforePromptInstallTrigger() {
|
||||
this.setState({ buttonDisplay: true })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (window !== undefined) {
|
||||
window.addEventListener('beforeinstallprompt', () => {
|
||||
this.BeforePromptInstallTrigger()
|
||||
})
|
||||
|
||||
if (!!window.deferredPrompt)
|
||||
this.setState({ buttonDisplay: true });
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
// don't show if we are in App
|
||||
if (window.navigator.standalone ||
|
||||
window.matchMedia('(display-mode: standalone)').matches)
|
||||
{
|
||||
console.log('standalone')
|
||||
this.setState({ openSnack: false, buttonDisplay: false })
|
||||
window.matchMedia('(display-mode: standalone)').matches) {
|
||||
this.setState({ openSnack: false, buttonDisplay: false })
|
||||
}
|
||||
else if(this.IsSafari())
|
||||
{
|
||||
console.log('safari')
|
||||
this.setState({ openSnack: false, buttonDisplay: false })
|
||||
// else if (window.deferredPrompt === null) {
|
||||
// this.setState({ openSnack: false, buttonDisplay: false })
|
||||
// }
|
||||
else if (this.IsSafari()) {
|
||||
this.setState({ openSnack: false, buttonDisplay: false })
|
||||
|
||||
if(this.iOS())
|
||||
this.setState({ openSnack: false, buttonDisplay: true })
|
||||
|
||||
if (this.iOS())
|
||||
{
|
||||
this.setState({ openSnack: false, buttonDisplay: true })}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
window.removeEventListener('beforeinstallprompt', () => {
|
||||
this.BeforePromptInstallTrigger()
|
||||
})
|
||||
}
|
||||
|
||||
//A2HS 'Add 2 Home Screen'
|
||||
PromptA2HS = (event, reason) => {
|
||||
|
||||
this.setState({ openSnack: false })
|
||||
console.log('prompted')
|
||||
if (window !== undefined) {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.promptInstall();
|
||||
}
|
||||
}
|
||||
@@ -105,38 +144,40 @@ class PWAInstallSnack extends Component {
|
||||
const { classes } = this.props;
|
||||
var safariSnack = (<Card className={classes.cardSnack}>
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image="/static/images/help/addToHomeFull.png"
|
||||
title="homeScreen"
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="h2">
|
||||
-> les PWA doivent encore s'installer manuellement sous IOS
|
||||
<Typography gutterBottom component="h2">
|
||||
Suivez les instructions ci -dessous :
|
||||
</Typography>
|
||||
<Typography gutterBottom align="center" component="h2" className={ classes.installStepText }>
|
||||
Etape 1 :
|
||||
</Typography>
|
||||
<img alt="installer-pwa" src='/static/images/help/safari-actions.png' className={classes.installApp} />
|
||||
<Typography gutterBottom align="center" component="h2" className={ classes.installStepText }>
|
||||
Etape 2 :
|
||||
</Typography>
|
||||
<img alt="installer-pwa" src='/static/images/help/safari-install-app.png' className={classes.installApp} />
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button name="compris" size="small" color="secondary" onClick={this.handleClose}>
|
||||
j'ai compris
|
||||
<Button name="compris" className={classes.buttonCompris} color="primary" onClick={this.handleClose}>
|
||||
j'ai compris
|
||||
</Button>
|
||||
</CardActions>
|
||||
</CardActions>
|
||||
</CardActionArea>
|
||||
|
||||
</Card>);
|
||||
|
||||
var usualSnack = (<Card className={classes.cardSnack}>
|
||||
<CardActionArea>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="h2">
|
||||
<Typography gutterBottom component="h2" className={ classes.askToInstallText }>
|
||||
Voulez-vous installer notre application ?
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions>
|
||||
<Button name='oui' size="small" color="secondary" onClick={this.PromptA2HS}>
|
||||
<Button className={classes.button} color="primary" name='oui' size="small" onClick={this.PromptA2HS}>
|
||||
oui
|
||||
</Button>
|
||||
<Button name='non' size="small" color="secondary" onClick={this.handleClose}>
|
||||
<Button className={classes.button} color="primary" name='non' size="small" onClick={this.handleClose}>
|
||||
non
|
||||
</Button>
|
||||
</CardActions>
|
||||
@@ -144,8 +185,9 @@ class PWAInstallSnack extends Component {
|
||||
|
||||
|
||||
|
||||
return <Fragment>
|
||||
return <Fragment>
|
||||
<Snackbar
|
||||
className={ classes.Snackbar }
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
open={this.state.openSnack}
|
||||
onClose={this.handleClose}
|
||||
@@ -154,9 +196,11 @@ class PWAInstallSnack extends Component {
|
||||
}}>
|
||||
{this.IsSafari() ? safariSnack : usualSnack}
|
||||
</Snackbar>
|
||||
{this.state.buttonDisplay && this.props.children(this.getStateAndHelpers())}
|
||||
</Fragment>
|
||||
|
||||
<Collapse in={this.state.buttonDisplay}>
|
||||
{this.state.buttonDisplay && this.props.children(this.getStateAndHelpers())}
|
||||
</Collapse>
|
||||
</Fragment>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import dynamic from 'next/dynamic'
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import axios from 'axios'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
import setAuthorizationHeader from "../../data/setAuthorizationHeader";
|
||||
import 'videojs/dist/video-js.css';
|
||||
import 'videojs-record/dist/css/videojs.record.css';
|
||||
@@ -16,26 +15,66 @@ const RTCRecorder = dynamic(() => import('RecordRTC'), {
|
||||
ssr: false
|
||||
})
|
||||
|
||||
const videoJsOptions = {
|
||||
controls: true,
|
||||
|
||||
fluid: true,
|
||||
plugins: {
|
||||
/*
|
||||
// wavesurfer section is only needed when recording audio-only
|
||||
wavesurfer: {
|
||||
src: 'live',
|
||||
waveColor: '#36393b',
|
||||
progressColor: 'black',
|
||||
debug: true,
|
||||
cursorWidth: 1,
|
||||
msDisplayMax: 20,
|
||||
hideScrollbar: true
|
||||
},
|
||||
*/
|
||||
record: {
|
||||
audio: true,
|
||||
video: true,
|
||||
maxLength: 600,
|
||||
//debug: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
margin: theme.spacing.unit,
|
||||
margin: theme.spacing(1),
|
||||
maxWidth: '400px', [theme.breakpoints.down('md')]: {
|
||||
maxWidth: `${100 - (theme.spacing.unit)}vw`
|
||||
maxWidth: `${100 - (theme.spacing(1))}vw`
|
||||
},
|
||||
sendContainer: {
|
||||
textAlign: 'center'
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
//borderRadius: '3px',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: 'auto',
|
||||
},
|
||||
|
||||
margin: theme.spacing.unit,
|
||||
alignSelf: 'flex-end'
|
||||
},
|
||||
}
|
||||
})
|
||||
class Recorder extends Component {
|
||||
|
||||
state={
|
||||
canSend:false
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
var record = (await import('videojs-record')).default
|
||||
// instantiate Video.js
|
||||
this.player = videojs(this.videoNode, this.props, () => {
|
||||
this.player = videojs(this.videoNode, videoJsOptions, () => {
|
||||
// print version information at startup
|
||||
var version_info = 'Using video.js ' + videojs.VERSION +
|
||||
' with videojs-record ' + videojs.getPluginVersion('record') +
|
||||
@@ -45,12 +84,14 @@ class Recorder extends Component {
|
||||
|
||||
// device is ready
|
||||
this.player.on('deviceReady', () => {
|
||||
console.log('device is ready!');
|
||||
console.info('device is ready!');
|
||||
this.setState({canSend:false});
|
||||
});
|
||||
|
||||
// user clicked the record button and started recording
|
||||
this.player.on('startRecord', () => {
|
||||
console.log('started recording!');
|
||||
this.setState({canSend:false});
|
||||
});
|
||||
|
||||
// user completed recording and stream is available
|
||||
@@ -58,19 +99,22 @@ class Recorder extends Component {
|
||||
// recordedData is a blob object containing the recorded data that
|
||||
// can be downloaded by the user, stored on server etc.
|
||||
console.log('finished recording: ', this.player.recordedData);
|
||||
this.setState({canSend:true});
|
||||
});
|
||||
|
||||
// error handling
|
||||
this.player.on('error', (element, error) => {
|
||||
console.warn(error);
|
||||
this.setState({canSend:false});
|
||||
});
|
||||
|
||||
this.player.on('deviceError', () => {
|
||||
console.error('device error:', this.player.deviceErrorCode);
|
||||
this.setState({canSend:false});
|
||||
});
|
||||
}
|
||||
|
||||
// destroy player on unmount
|
||||
//destroy player on unmount
|
||||
componentWillUnmount() {
|
||||
if (this.player) {
|
||||
this.player.dispose();
|
||||
@@ -78,39 +122,34 @@ class Recorder extends Component {
|
||||
}
|
||||
|
||||
sendBlob = () => {
|
||||
console.log('accessing recording: ', this.player.recordedData)
|
||||
// console.log('accessing recording: ', this.player.recordedData)
|
||||
var formData = new FormData();
|
||||
formData.append('upload', this.player.recordedData, "myupload");
|
||||
// formData.append('user', 7);//TODO: taking from auth
|
||||
|
||||
setAuthorizationHeader('eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NTk4OTE4ODIsImV4cCI6MTU1OTg5NTQ4Miwicm9sZXMiOlsiUk9MRV9UQUxFTlQiLCJST0xFX1VTRVIiXSwidXNlcm5hbWUiOiJ0aG9tYXMuYmVycm9kQGUtZGVjbGljLmNvbSJ9.dnXiifet5SIyZf1zOBnu6RvVqA6k-aT16O_YNy6ZAhRZUuQtqP21enNkug0KxLEFMy91gcj0gkGuETqMwcHBL_xkx7rJvb2dLoOo0yduZxJLzngWKroCZE152t-V5Edn2Gje3CHMGqTZLwjBowEgUTpgfyTE9IaJn-q2pizukWBzjmfmdz5mYFgoIQrsjThkPxRH8cNOTit0bj6SebjsCCsPb_jdM6rek03qtwELz2ABJaHJU1FiGGTLYtcF3k32AGlRwa5P9etKwQUrz0AL5q3lroKgbtwhxY3cKyRKAo-o68s3v12_T6R86WqEegOx6TyBb06GJhTcP1MIGBMMgNIm5GxSi0U0RvRDmLuFtsuZv5wBUZqkpth8Z_dK5H003k67XjLrYC4TnMhQXxUGV6Eji3lkCY5fOWbQjwXRO9R_rPCNSiPEZj0o_-ach0a4Y5nLhzVlnb3J0plpb4lwpN3D9t9FNfizKQ5VX9eLoozrzkI-apIbV-Z0is8i6j4yt7R24PR9dyreQXZ3-e_tEwu5UTKi-zYAaHaHrRgCSjWg4d6ozuyXQmzi64d1-Fnx6IkU_KtXtUYRVsZhQUvJpri2x1szLLM-Hqdsh2Ljuu5qDdJrHRKeG1gZeKWPfvZweZ3SWrfvlNZHKNWX-2pmEvz9sJdpsAO-NnmTEUVrpzY')
|
||||
|
||||
axios.post("http://localhost/app_dev.php/api/media",
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
})
|
||||
.then(res => console.log(res)).catch(err => console.log(err))
|
||||
|
||||
console.log('end send Blob')
|
||||
this.props.uploader(formData);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const { classes, titreBouton } = this.props;
|
||||
let titreButtonLabel= "Envoyer";
|
||||
|
||||
|
||||
if (titreBouton) {
|
||||
titreButtonLabel = titreBouton;
|
||||
console.log("titre");
|
||||
console.log(titreButtonLabel);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div data-vjs-player>
|
||||
<video id="myVideo" ref={node => this.videoNode = node} className="video-js vjs-default-skin" playsInline></video>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="submit" variant="outlined" color="secondary"
|
||||
<div className={classes.sendContainer}>
|
||||
{ this.state.canSend && <Button type="submit" variant="outlined" color="primary"
|
||||
className={classes.button}
|
||||
onClick={this.sendBlob}>
|
||||
Send
|
||||
</Button>
|
||||
{titreButtonLabel}
|
||||
</Button>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import React, {Fragment} from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { connect } from "react-redux";
|
||||
import Validator from "validator";
|
||||
import {FormLabel, FormHelperText,InputLabel, Input,TextField, FormGroup, FormControlLabel, Checkbox, Button, Typography, Link } from '@material-ui/core';
|
||||
|
||||
import { TextField, FormControlLabel, Checkbox, Button, Typography, Link } from '@material-ui/core';
|
||||
import Visibility from '@material-ui/icons/Visibility';
|
||||
import VisibilityOff from '@material-ui/icons/VisibilityOff';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { registerUserRequest, subscribeToNewsletterRequest } from "../../actions/user"
|
||||
import SocialButtonsAuth from '../atoms/SocialButtonsAuth';
|
||||
|
||||
const styles = theme => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
paddingTop: theme.spacing(4),
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
padding: '5vw 10vw',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
padding: '5vw 25vw',
|
||||
},
|
||||
},
|
||||
textField: {
|
||||
width: '100%'
|
||||
@@ -24,13 +31,75 @@ const styles = theme => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
input50: {
|
||||
width: '50%',
|
||||
formText: {
|
||||
padding: theme.spacing(0, 2),
|
||||
paddingTop: theme.spacing(3),
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '300',
|
||||
},
|
||||
checkboxError:{
|
||||
color:'red'
|
||||
}
|
||||
formLink: {
|
||||
paddingLeft: theme.spacing(1),
|
||||
fontWeight: '500',
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
checkbox: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
||||
'& .MuiFormControlLabel-label': {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '500',
|
||||
color: theme.palette.primary.dark,
|
||||
}
|
||||
},
|
||||
checkboxError: {
|
||||
'& .MuiFormControlLabel-label': {
|
||||
color: 'red'
|
||||
}
|
||||
},
|
||||
conditionalText: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
color: theme.palette.primary.conditional,
|
||||
textTransform: 'uppercase',
|
||||
|
||||
'&:before': {
|
||||
content: '""',
|
||||
flex: '1 1',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: theme.palette.primary.conditional,
|
||||
marginRight: '10px',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
'&:after': {
|
||||
content: '""',
|
||||
flex: '1 1',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: theme.palette.primary.conditional,
|
||||
marginLeft: '10px',
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(10),
|
||||
marginRight: theme.spacing(10),
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2),
|
||||
padding: theme.spacing(1.2, 1),
|
||||
borderRadius: '3px',
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: 'auto',
|
||||
},
|
||||
},
|
||||
buttonImportant: {
|
||||
marginTop: theme.spacing(6),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
});
|
||||
|
||||
class Register extends React.Component {
|
||||
@@ -43,9 +112,15 @@ class Register extends React.Component {
|
||||
checkNewsletter: false,
|
||||
checkCGU: false,
|
||||
},
|
||||
errors: {},
|
||||
errors: {
|
||||
email:null,
|
||||
password:null,
|
||||
firstname:null,
|
||||
lastname:null,
|
||||
checkCGU:null
|
||||
},
|
||||
loading: false,
|
||||
showPassword:false
|
||||
showPassword: false
|
||||
}
|
||||
|
||||
validate = data => {
|
||||
@@ -55,19 +130,17 @@ class Register extends React.Component {
|
||||
if (!!!data.firstname) errors.firstname = "Requis";
|
||||
if (!!!data.lastname) errors.lastname = "Requis";
|
||||
if (!data.checkCGU) errors.checkCGU = "Requis";
|
||||
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
onRegister = e => {
|
||||
e.preventDefault();
|
||||
const errors = this.validate(this.state.data);
|
||||
console.log('onSubmit')
|
||||
console.log(errors)
|
||||
|
||||
this.setState({ errors: errors });
|
||||
|
||||
if (Object.keys(errors).length === 0) {
|
||||
this.setState({ loading: true });
|
||||
|
||||
this.props.registerUserRequest(this.state.data)
|
||||
|
||||
@@ -77,7 +150,7 @@ class Register extends React.Component {
|
||||
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
this.setState({
|
||||
errors: nextProps.errors,
|
||||
loading: nextProps.loading
|
||||
@@ -88,7 +161,7 @@ class Register extends React.Component {
|
||||
data: { ...this.state.data, [e.target.name]: e.target.value }
|
||||
});
|
||||
handleCheckBoxChange = name => event => {
|
||||
this.setState({data:{ ...this.state.data, [name]: event.target.checked }});
|
||||
this.setState({ data: { ...this.state.data, [name]: event.target.checked } });
|
||||
};
|
||||
|
||||
handleChangePasswordVisibility = prop => event => {
|
||||
@@ -102,38 +175,34 @@ class Register extends React.Component {
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const { data, errors } = this.state;
|
||||
console.log('render errors');
|
||||
console.log(errors);
|
||||
console.log(this.state);
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
|
||||
<form className={classes.form}>
|
||||
<FormGroup row>
|
||||
<TextField
|
||||
value={data.firstname}
|
||||
className={classes.input50}
|
||||
onChange={this.onChange}
|
||||
id="prenom"
|
||||
name="firstname"
|
||||
label="Prénom"
|
||||
margin="normal"
|
||||
required
|
||||
error={!!errors.firstname}
|
||||
/>
|
||||
<TextField
|
||||
value={data.lastname}
|
||||
className={classes.input50}
|
||||
onChange={this.onChange}
|
||||
id="nom"
|
||||
name="lastname"
|
||||
label="Nom"
|
||||
margin="normal"
|
||||
required
|
||||
error={!!errors.lastname}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<TextField
|
||||
value={data.lastname}
|
||||
onChange={this.onChange}
|
||||
id="nom"
|
||||
name="lastname"
|
||||
label="Nom"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
required
|
||||
error={!!errors.lastname}
|
||||
/>
|
||||
<TextField
|
||||
value={data.firstname}
|
||||
onChange={this.onChange}
|
||||
id="prenom"
|
||||
name="firstname"
|
||||
label="Prénom"
|
||||
margin="normal"
|
||||
variant="outlined"
|
||||
required
|
||||
error={!!errors.firstname}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
value={data.email}
|
||||
@@ -143,80 +212,93 @@ class Register extends React.Component {
|
||||
label="Email"
|
||||
margin="normal"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
required
|
||||
error={!!errors.email}
|
||||
autoComplete="username email"
|
||||
/>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="adornment-password">Password *</InputLabel>
|
||||
<Input
|
||||
name="password"
|
||||
id="adornment-password"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
value={this.state.password}
|
||||
onChange={this.onChange}
|
||||
endAdornment={
|
||||
|
||||
|
||||
<TextField
|
||||
className={clsx(classes.margin, classes.textField)}
|
||||
label="Password"
|
||||
name="password"
|
||||
id="adornment-password"
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
value={this.state.password}
|
||||
onChange={this.onChange}
|
||||
error={!!errors.password}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton aria-label="Toggle password visibility" onClick={this.handleClickShowPassword}>
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="Toggle password visibility"
|
||||
onClick={this.handleClickShowPassword}
|
||||
>
|
||||
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
autoComplete="current-password"
|
||||
error={!!errors.password}
|
||||
/>
|
||||
</FormControl>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox value={data.checkNewsletter}
|
||||
<Checkbox
|
||||
id="cypCheckboxNewsletter"
|
||||
value={data.checkNewsletter}
|
||||
onChange={this.handleCheckBoxChange('checkNewsletter')}
|
||||
/>
|
||||
}
|
||||
className={classes.checkbox}
|
||||
label="Je souhaite recevoir la newsletter de Talents Tube"
|
||||
|
||||
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
name="checkCGU"
|
||||
className={!!errors.checkCGU&&classes.checkboxError}
|
||||
className={!!errors.checkCGU ? classes.checkbox + ' ' + classes.checkboxError : classes.checkbox}
|
||||
control={
|
||||
<Checkbox
|
||||
error={!!errors.checkCGU }
|
||||
<Checkbox
|
||||
id="cypCheckboxRegister"
|
||||
value={data.checkCGU}
|
||||
onChange={this.handleCheckBoxChange('checkCGU')}
|
||||
onChange={this.handleCheckBoxChange('checkCGU')} required
|
||||
/>
|
||||
|
||||
|
||||
}
|
||||
label="Je déclare avoir pris connaissance et accepte les termes et les CGU*"
|
||||
label="Je déclare avoir pris connaissance et accepte les termes et les CGU *"
|
||||
required
|
||||
/>
|
||||
<Button type="submit" variant="outlined" color="primary" onClick={this.onRegister}>
|
||||
<Button type="submit" variant="contained" className={classes.button + ' ' + classes.buttonImportant + ' cypButtonRegister'} onClick={this.onRegister}>
|
||||
S'inscrire
|
||||
</Button>
|
||||
|
||||
<Typography
|
||||
paragraph={true}
|
||||
align="center"
|
||||
className={classes.conditionalText}
|
||||
>Ou</Typography>
|
||||
|
||||
</form>
|
||||
<Button variant="outlined" color="primary" onClick={this.props.linkedInAuth}>
|
||||
Connexion avec Linkedin
|
||||
</Button>
|
||||
<Button variant="outlined" color="primary" onClick={this.props.facebookAuth}>
|
||||
Connexion avec Facebook
|
||||
</Button>
|
||||
<div className={classes.form}>
|
||||
|
||||
<SocialButtonsAuth linkedInAuth={this.props.linkedInAuth} facebookAuth={this.props.facebookAuth} />
|
||||
|
||||
<div classes={classes.form}>
|
||||
<Typography
|
||||
paragraph={true}
|
||||
align="center"
|
||||
className={classes.formText}
|
||||
>Je possède un compte Talents Tube ?
|
||||
<Link href={"/Login"} className={classes.link}>
|
||||
Se connecter
|
||||
<Link href={"/Login"} >
|
||||
<span className={classes.formLink}>
|
||||
Se connecter
|
||||
</span>
|
||||
</Link>
|
||||
</Typography>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -226,7 +308,13 @@ class Register extends React.Component {
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
loading: !!state.user.loading,
|
||||
errors: state.user.errors
|
||||
errors: !!state.user.errors ?state.user.errors: {
|
||||
email:null,
|
||||
password:null,
|
||||
firstname:null,
|
||||
lastname:null,
|
||||
checkCGU:null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import Link from 'next/link';
|
||||
import React, { Component } from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Plyr from 'react-plyr';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
|
||||
import VideoVimeo from '../atoms/VideoVimeo'
|
||||
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
@@ -20,21 +24,100 @@ const styles = theme => ({
|
||||
video: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
|
||||
'& .plyr__video-embed': {
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: '40vh',
|
||||
maxHeight: '40vh',
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
height: '60vh',
|
||||
},
|
||||
|
||||
},
|
||||
'& .plyr__video-wrapper.plyr__video-embed': {
|
||||
[theme.breakpoints.up('xs')]: {
|
||||
width: '240vw',
|
||||
transform: 'translateX(-55vw)',
|
||||
paddingBottom: '50% !important',
|
||||
},
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: 'auto',
|
||||
transform: 'inherit',
|
||||
},
|
||||
},
|
||||
'& .plyr__controls': {
|
||||
background: 'none'
|
||||
},
|
||||
},
|
||||
videoVimeo: {
|
||||
zIndex: '888',
|
||||
height: '65vh',
|
||||
paddingBottom: '0%',
|
||||
overflow: 'hidden',
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 'calc(50% - 125vw)',
|
||||
width: '250vw',
|
||||
height: '120vw',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
height: '48vw',
|
||||
paddingBottom: '0',
|
||||
'& iframe': {
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 100vw)',
|
||||
width: '200vw',
|
||||
height: '130%',
|
||||
},
|
||||
},
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
height: '48vw',
|
||||
},
|
||||
},
|
||||
text: {
|
||||
zIndex: '999',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
position: 'absolute',
|
||||
bottom: '17vh',
|
||||
left: '3vh',
|
||||
},
|
||||
plyr: {
|
||||
'.plyr--video .plyr__controls': {
|
||||
background: null
|
||||
}
|
||||
}
|
||||
textCompany: {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: 'rgba(30,35,39,0.9)',
|
||||
padding: theme.spacing(0.8, 2),
|
||||
marginBottom: 0,
|
||||
fontWeight: 'bold',
|
||||
fontSize: '1em',
|
||||
},
|
||||
textJob: {
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: 'rgba(30,35,39,0.5)',
|
||||
padding: theme.spacing(0.4, 2),
|
||||
marginBottom: 0,
|
||||
fontWeight: 'lighter',
|
||||
fontSize: '0.9em',
|
||||
},
|
||||
button: {
|
||||
position: 'absolute',
|
||||
bottom: '5vh',
|
||||
left: '3vh',
|
||||
padding: theme.spacing(1, 4),
|
||||
textTransform: 'inherit',
|
||||
zIndex: '999',
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const videoList = [{ vimeoId: '228345901', title: 'Eurofins', adTitle: 'CDI gestion', link: '/offre?id=22' },
|
||||
{ vimeoId: '230731700', title: 'MULTIPLAST', adTitle: 'CDI example', link: '/offre?id=24' },
|
||||
{ vimeoId: '232953887', title: 'Actual', adTitle: 'Chef général', link: '/offre?id=26' }]
|
||||
|
||||
const videoList = [
|
||||
{ vimeoId: '350927569', title: 'Les Vergers de Châteaubourg', adTitle: 'Conducteurs de ligne (H/F)', link: '/Offre/73-Les-Vergers-de-Châteaubourg-chateaubourg' },
|
||||
{ vimeoId: '350927551', title: 'Grand Ouest Étiquettes', adTitle: 'Conducteurs de machine d’impression', link: '/Offre/79-Grand-Ouest-Étiquettes-Saint-Brieuc' },
|
||||
{ vimeoId: '350927537', title: 'Fym Action', adTitle: 'Promoteur des ventes en alimentaire (H/F)', link: '/Offre/81-Fym-Action-Caudan' }
|
||||
]
|
||||
|
||||
class ShortVideoHooker extends Component {
|
||||
constructor(props) {
|
||||
@@ -54,7 +137,7 @@ class ShortVideoHooker extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
this.timer = setInterval(this.tick.bind(this), 500);
|
||||
this.timer = setInterval(this.tick.bind(this), 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -62,7 +145,7 @@ class ShortVideoHooker extends Component {
|
||||
}
|
||||
|
||||
tick() {
|
||||
// console.log(this.state)
|
||||
|
||||
if (new Date() - this.state.startDate >= 8000) {
|
||||
|
||||
if (this.state.i == 2)
|
||||
@@ -70,13 +153,13 @@ class ShortVideoHooker extends Component {
|
||||
else
|
||||
this.setState({ i: ++this.state.i, startDate: Date.now() })
|
||||
//Update Plyr
|
||||
if (this.plyrRef) {
|
||||
this.plyrRef.hideControls = true;
|
||||
this.plyrRef.displayDuration = false;
|
||||
this.plyrRef.updateVideoSource(videoList[this.state.i].vimeoId, 'vimeo')
|
||||
this.plyrRef.hideControls = true;
|
||||
this.plyrRef.displayDuration = false;
|
||||
}
|
||||
// if (this.plyrRef) {
|
||||
// this.plyrRef.hideControls = true;
|
||||
// this.plyrRef.displayDuration = false;
|
||||
// this.plyrRef.updateVideoSource(videoList[this.state.i].vimeoId, 'vimeo')
|
||||
// this.plyrRef.hideControls = true;
|
||||
// this.plyrRef.displayDuration = false;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,45 +167,42 @@ class ShortVideoHooker extends Component {
|
||||
render() {
|
||||
let { classes } = this.props;
|
||||
let videoId = videoList[0].vimeoId;
|
||||
// console.log(videoId)
|
||||
|
||||
return (
|
||||
<div className={classes.video}>
|
||||
{/* config at : https://github.com/xDae/react-plyr */}
|
||||
<Plyr
|
||||
//autoplay
|
||||
type="vimeo" // or "vimeo"
|
||||
videoId={videoId}
|
||||
//displayDuration={false}
|
||||
hideControls={true}
|
||||
clickToPlay={false}
|
||||
fullscreen={{ enabled: false, fallback: true, iosNative: false }}
|
||||
disableContextMenu={true}
|
||||
controls={null}
|
||||
ref={this.setPlyrRef}
|
||||
className={classes.plyr}
|
||||
/>
|
||||
<Typography gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.text}>
|
||||
{videoList[this.state.i].title}
|
||||
</Typography>
|
||||
{/* <VideoVimeo classesOverride={ classes.videoVimeo } videoId={videoId}
|
||||
options={`?byline=0&title=0&background=1&autoplay=1&mute=1`} />
|
||||
<section className={classes.text}>
|
||||
<Typography gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.textCompany}>
|
||||
{videoList[this.state.i].title}
|
||||
</Typography>
|
||||
|
||||
<Typography gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.text}>
|
||||
{videoList[this.state.i].adTitle}
|
||||
</Typography>
|
||||
<Button variant="outlined">
|
||||
Voir l'offre
|
||||
<Typography gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.textJob}>
|
||||
{videoList[this.state.i].adTitle}
|
||||
</Typography>
|
||||
</section>
|
||||
<Link href={videoList[this.state.i].link}>
|
||||
<Button
|
||||
name="Offre"
|
||||
className={classes.button}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
>
|
||||
Voir l'offre
|
||||
</Button>
|
||||
</Link> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default withStyles(styles)(ShortVideoHooker);
|
||||
export default withStyles(styles)(withWidth()(ShortVideoHooker))
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import clsx from 'clsx';
|
||||
import { loadCSS } from 'fg-loadcss';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { red } from '@material-ui/core/colors';
|
||||
import Icon from '@material-ui/core/Icon';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
iconHover: {
|
||||
margin: theme.spacing(2),
|
||||
'&:hover': {
|
||||
color: red[800],
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
margin: theme.spacing(1.5),
|
||||
padding: theme.spacing(1.5),
|
||||
borderRadius: '20px',
|
||||
fontSize: '1em',
|
||||
textAlign: 'center',
|
||||
},
|
||||
FacebookIcon: {
|
||||
backgroundColor: theme.palette.social.facebook,
|
||||
},
|
||||
TwitterIcon: {
|
||||
backgroundColor: theme.palette.social.twitter,
|
||||
},
|
||||
LinkedinIcon: {
|
||||
backgroundColor: theme.palette.social.linkedin,
|
||||
},
|
||||
MailIcon: {
|
||||
backgroundColor: theme.palette.social.mail,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function SocialShare(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
React.useEffect(() => {
|
||||
loadCSS(
|
||||
'https://use.fontawesome.com/releases/v5.1.0/css/all.css',
|
||||
document.querySelector('#font-awesome-css'),
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<a href={"https://www.facebook.com/sharer/sharer.php?u=" + encodeURIComponent(props.url)}>
|
||||
<Icon className={clsx(classes.icon, classes.FacebookIcon, 'fab fa-facebook-f')} color="secondary" />
|
||||
</a>
|
||||
<a href={"http://twitter.com/share?url=" + encodeURIComponent(props.url)}>
|
||||
<Icon className={clsx(classes.icon, classes.TwitterIcon, 'fab fa-twitter')} color="secondary" />
|
||||
</a>
|
||||
<a href={"https://www.linkedin.com/shareArticle?mini=true&url=" + encodeURIComponent(props.url + "&title=Talent's Tube")}>
|
||||
<Icon className={clsx(classes.icon, classes.LinkedinIcon, 'fab fa-linkedin-in')} color="secondary" />
|
||||
</a>
|
||||
{/* change because of https://github.com/zeit/next.js/blob/master/errors/invalid-href-passed.md */}
|
||||
<a href={"mailto:?subject=Offre d'emploi Talent's Tube&body=Regardez cette offre : " + props.url} >
|
||||
<Icon className={clsx(classes.icon, classes.MailIcon, 'fas fa-envelope')} color="secondary" />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,79 +5,80 @@ import getConfig from 'next/config'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardHeader from '@material-ui/core/CardHeader';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import KeyboardArrowleftIcon from '@material-ui/icons/PlayArrowRounded';
|
||||
import Link from 'next/link';
|
||||
import _ from 'lodash';
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
height: '60vh',
|
||||
height: '400px',
|
||||
width: '100%',
|
||||
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '30%',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: '40%',
|
||||
},
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width: '100%',
|
||||
cursor: 'pointer',
|
||||
transform: 'translateY(-0px)',
|
||||
transition: 'transform 0.25s',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-15px)',
|
||||
transition: 'transform 0.5s, box-shadow 1.2s',
|
||||
'& .cardHover': {
|
||||
backgroundColor: 'rgba(0,0,0,0)',
|
||||
},
|
||||
},
|
||||
|
||||
margin: '2vh 3vh 3vh 1vh',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
margin: '1vh 0 1vh 0',
|
||||
height: '530px',
|
||||
},
|
||||
[theme.breakpoints.up('md')]: {
|
||||
height: '404px',
|
||||
},
|
||||
borderRadius: '0.2em',
|
||||
},
|
||||
details_container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '8vh 0 0 0',
|
||||
},
|
||||
company_title: {
|
||||
fontSize: '1em',
|
||||
fontWeight: 'normal',
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
content: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
},
|
||||
cover: {
|
||||
width: '100%',
|
||||
height: '50%',
|
||||
height: '45%',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundBlendMode: 'darken',
|
||||
backgroundColor: 'rgba(30,35,39,0.25)',
|
||||
transition: 'background-color 1s',
|
||||
},
|
||||
logo_container: {
|
||||
logoContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 70,
|
||||
height: 70,
|
||||
width: 80,
|
||||
height: 80,
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
top: '45%',
|
||||
transform: 'translate(-50%, -66%)',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0 8px 6px -6px grey',
|
||||
border: 'solid 5px white',
|
||||
borderRadius: '0.5em',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: '0px 6px 15px rgba(30, 35, 39, 0.10)',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
iconPlay: {
|
||||
fontSize: '3em',
|
||||
color: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 0.5em)',
|
||||
top: 'calc(22.5% - 0.5em)',
|
||||
},
|
||||
logo: {
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
objectfit: 'contain',
|
||||
borderRadius: 'inherit',
|
||||
},
|
||||
chip: {
|
||||
margin: '0 1vh 0 1vh',
|
||||
@@ -90,28 +91,34 @@ const { publicRuntimeConfig } = getConfig()
|
||||
class Tile extends React.Component {
|
||||
|
||||
render() {
|
||||
const { classes, rowData, children, tileIndex } = this.props;
|
||||
const imgAdUrl = (rowData.video && rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
|
||||
const imgCompanyUrl = (rowData.presentation_video && rowData.presentation_video.thumbnails[0] && rowData.presentation_video.thumbnails[0].sizes[4]) ? rowData.presentation_video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
|
||||
const imgUrl = (rowData.video) ? imgAdUrl : imgCompanyUrl
|
||||
|
||||
const { classes, rowData, children } = this.props;
|
||||
const logoName = (rowData.company) ? rowData.company.logo : rowData.logo
|
||||
const logoUrl = publicRuntimeConfig.symphonyUrl + publicRuntimeConfig.cachedMediaUrl + logoName
|
||||
|
||||
const imgUrl = (rowData.video.thumbnails[0] && rowData.video.thumbnails[0].sizes[4]) ? rowData.video.thumbnails[0].sizes[4].link : publicRuntimeConfig.adListDefaultUrl
|
||||
const logoUrl = publicRuntimeConfig.logoRootUrl + rowData.company.logo
|
||||
//rowData._embedded.company.business_name
|
||||
|
||||
const router = (rowData.company) ? `/Offre/${rowData.id}-${_.kebabCase(rowData.title)}` : `/Entreprise/${rowData.id}-${_.kebabCase(rowData.brand_name)}}`
|
||||
|
||||
//rowData._embedded.company.brand_name
|
||||
return (
|
||||
<Card className={classes.card} onClick={() => Router.push(`/Offre?id=${rowData.id}`)}>
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={imgUrl}
|
||||
title={rowData.title}
|
||||
/>
|
||||
|
||||
<div className={classes.logo_container}>
|
||||
<Avatar className={classes.logo} src={logoUrl} />
|
||||
</div>
|
||||
{ children }
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Link href={router} >
|
||||
<Card cyp="CardOneOffers" className={classes.card}>
|
||||
<CardContent className={classes.content}>
|
||||
<CardMedia
|
||||
className={classes.cover + ' cardHover'}
|
||||
image={imgUrl}
|
||||
title={rowData.title}
|
||||
/>
|
||||
<KeyboardArrowleftIcon className={classes.iconPlay} />
|
||||
<div className={classes.logoContainer}>
|
||||
<Avatar className={classes.logo} src={logoUrl} alt={rowData.title}/>
|
||||
</div>
|
||||
{ children }
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +126,7 @@ class Tile extends React.Component {
|
||||
Tile.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
rowData: PropTypes.object.isRequired
|
||||
rowData: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(Tile);
|
||||
@@ -3,69 +3,88 @@ import PropTypes from "prop-types";
|
||||
import Router from 'next/router'
|
||||
import getConfig from 'next/config'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import IconText from './../atoms/IconText';
|
||||
|
||||
const styles = theme => ({
|
||||
details_container: {
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '8vh 0 0 0',
|
||||
justifyContent: 'space-between',
|
||||
position: 'absolute',
|
||||
top: '60%',
|
||||
width: '100%',
|
||||
minHeight: '35%',
|
||||
},
|
||||
company_title: {
|
||||
companyTitle: {
|
||||
fontSize: '1em',
|
||||
fontWeight: 'normal',
|
||||
textTransform: 'uppercase',
|
||||
paddingLeft: theme.spacing(1),
|
||||
paddingRight: theme.spacing(1),
|
||||
minHeight: '8vh',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
minHeight: 'inherit',
|
||||
},
|
||||
},
|
||||
chip: {
|
||||
height: '27px',
|
||||
padding: theme.spacing(1),
|
||||
margin: '0 1vh 0 1vh',
|
||||
},
|
||||
rootIconText: {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5, 0),
|
||||
color: theme.palette.secondary.dark,
|
||||
'& p ': {
|
||||
color: theme.palette.secondary.text,
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
class TileCompany extends React.Component {
|
||||
class CompanyOffre extends React.Component {
|
||||
|
||||
render() {
|
||||
|
||||
const { classes, tile } = this.props;
|
||||
|
||||
const offre = (tile.count_ads == 1) ? tile.count_ads + ' offre' : tile.count_ads + ' offres'
|
||||
const nbOffre = (tile.count_ads > 0) ? offre : 'Candidature spontanée'
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.details_container}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.company_title}>
|
||||
{tile.company.business_name}
|
||||
</Typography>
|
||||
<section className={classes.detailsContainer}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
align="center"
|
||||
className={classes.job_title}>
|
||||
{tile.title}
|
||||
className={classes.companyTitle}>
|
||||
{tile.brand_name ? tile.brand_name : "-"}
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Chip label={tile.extra.contract_type} className={classes.chip} />
|
||||
<Chip label={tile.location.gmap_address.address_components[0].long_name} className={classes.chip} />
|
||||
</div>
|
||||
|
||||
{tile.addresses && tile.addresses.billing && tile.addresses.billing.city &&
|
||||
<IconText classes={{ root: classes.rootIconText }} variant="location_on" text={tile.addresses.billing.city} />
|
||||
}
|
||||
{tile.title &&
|
||||
<IconText classes={{ root: classes.rootIconText }} variant="business_center" text={tile.title} />
|
||||
}
|
||||
{nbOffre &&
|
||||
<Chip color="primary" label={nbOffre} className={classes.chip} />
|
||||
}
|
||||
|
||||
</section>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TileCompany.propTypes = {
|
||||
CompanyOffre.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
tile: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(TileCompany);
|
||||
export default withStyles(styles, { withTheme: true })(CompanyOffre);
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import Router from 'next/router'
|
||||
import getConfig from 'next/config'
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Icon from '@material-ui/core/Icon';
|
||||
import IconText from './../atoms/IconText';
|
||||
|
||||
const styles = theme => ({
|
||||
details_container: {
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '8vh 0 0 0',
|
||||
justifyContent: 'space-between',
|
||||
position: 'absolute',
|
||||
top: '44%',
|
||||
width: '100%',
|
||||
},
|
||||
company_title: {
|
||||
fontSize: '0.8em',
|
||||
fontWeight: 'normal',
|
||||
companyTitle: {
|
||||
fontSize: '1em',
|
||||
fontWeight: '100',
|
||||
padding: theme.spacing(0, 2),
|
||||
marginTop: '4vh',
|
||||
minHeight: '6vh',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
minHeight: '3vh',
|
||||
},
|
||||
},
|
||||
job_title: {
|
||||
fontSize: '1em',
|
||||
marginLeft: theme.spacing(1),
|
||||
marginRight: theme.spacing(1),
|
||||
jobTitle: {
|
||||
fontSize: '1.2em',
|
||||
padding: theme.spacing(0, 2),
|
||||
minHeight: '8vh',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
minHeight: '50px',
|
||||
},
|
||||
},
|
||||
chip: {
|
||||
margin: '0 1vh 0 1vh',
|
||||
rootIconText: {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5, 2),
|
||||
color: theme.palette.secondary.dark,
|
||||
justifyContent: 'flex-start',
|
||||
'& p ': {
|
||||
color: theme.palette.secondary.text,
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -38,31 +51,41 @@ class TileOffre extends React.Component {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<section className={classes.details_container}>
|
||||
<section className={classes.detailsContainer} >
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h4"
|
||||
align="center"
|
||||
className={classes.company_title}>
|
||||
{tile.company.business_name}
|
||||
className={classes.companyTitle}>
|
||||
{tile.company.brand_name && tile.company.brand_name}
|
||||
</Typography>
|
||||
<Typography
|
||||
gutterBottom
|
||||
variant="headline"
|
||||
variant="h6"
|
||||
component="h3"
|
||||
align="center"
|
||||
className={classes.job_title}>
|
||||
{tile.title}
|
||||
align="left"
|
||||
className={classes.jobTitle}>
|
||||
{tile.title && tile.title}
|
||||
</Typography>
|
||||
</section>
|
||||
<section>
|
||||
<Chip label={tile.extra.contract_type} className={classes.chip} />
|
||||
<Chip label={tile.location.gmap_address.address_components[0].long_name} className={classes.chip} />
|
||||
<IconText />
|
||||
<Icon>add_circle</Icon>
|
||||
|
||||
{/*
|
||||
<IconText classes={{root: classes.rootIconText}} variant="business_center" text={tile.title} />
|
||||
*/}
|
||||
|
||||
|
||||
{tile.location && tile.location.gmap_address && tile.location.gmap_address.address_components && tile.location.gmap_address.address_components[0].long_name &&
|
||||
<IconText classes={{ root: classes.rootIconText }} variant="location_on" text={tile.location.gmap_address.address_components[0].long_name} />
|
||||
}
|
||||
{tile.extra && tile.extra.contract_type &&
|
||||
<IconText classes={{ root: classes.rootIconText }} variant="assignment" text={tile.extra.contract_type} />
|
||||
}
|
||||
|
||||
{tile.beneficiaire &&
|
||||
<IconText classes={{ root: classes.rootIconText }} variant="business" text={tile.beneficiaire} />
|
||||
}
|
||||
</section>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import BottomToolbar from '../organisms/BottomToolbar';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
height: '10vh'
|
||||
},
|
||||
});
|
||||
|
||||
function Footer(props) {
|
||||
const { classes } = props;
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
</Fragment>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Footer)
|
||||
@@ -1,15 +1,67 @@
|
||||
import Griddle, { utils } from "griddle-react";
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import OffreRow from "../organisms/OffreRow"
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { Button } from '@material-ui/core';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import GriddleCustomTable from "../organisms/GriddleCustomTableComponent";
|
||||
import GriddleCustomNextButton from "../organisms/GriddleCustomNextButton"
|
||||
import GriddleCustomPreviousButton from "../organisms/GriddleCustomPreviousButton"
|
||||
import Loader from './loader'
|
||||
import IconSearch from '@material-ui/icons/Search';
|
||||
import NoResult from './../atoms/NoResult';
|
||||
import _ from "lodash";
|
||||
|
||||
//insights
|
||||
//
|
||||
//https://codesandbox.io/s/pw9zy43v1m?module=%2FGrid.js
|
||||
|
||||
const styles = theme => ({
|
||||
textField: {
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: 0,
|
||||
'& input': {
|
||||
fontWeight: '300',
|
||||
color: theme.palette.primary.dark,
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
minWidth: '75vw',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
paddingLeft: theme.spacing(2),
|
||||
[theme.breakpoints.up('md')]: {
|
||||
minWidth: '85vw',
|
||||
},
|
||||
}
|
||||
},
|
||||
griddle: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'& .griddle-pagination': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
padding: theme.spacing(2, 0),
|
||||
boxShadown: '0px',
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
},
|
||||
filterForm: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: theme.palette.background.grey,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100% !important',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
justifyContent: 'space-evenly',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const CustomTableBody = ({ rowIds, Row, style, className }) => (
|
||||
<div style={style} className={className}>
|
||||
@@ -24,65 +76,145 @@ class Grid extends Component {
|
||||
success: false,
|
||||
data: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
pageSize: 12,
|
||||
recordCount: 0,
|
||||
filterText: ""
|
||||
promoted: false,
|
||||
filterText: "",
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
|
||||
this.setState({pageSize:this.props.limit})
|
||||
|
||||
this.props.dataFetcher(
|
||||
{
|
||||
offset: this.state.currentPage,
|
||||
limit: this.state.pageSize
|
||||
limit: this.props.limit
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
|
||||
this.setState({ data: nextProps.data });
|
||||
this.setState({ currentPage: nextProps.currentPage });
|
||||
this.setState({ recordCount: nextProps.recordCount });
|
||||
this.setState({ loading: nextProps.loading });
|
||||
this.setState({ limit: nextProps.limit });
|
||||
|
||||
|
||||
const { currentPage, pageSize, filterText, promoted } = this.state;
|
||||
|
||||
if (nextProps.promoted != this.state.promoted) {
|
||||
this.setState({ promoted: nextProps.promoted });
|
||||
this.props.dataFetcher(
|
||||
{
|
||||
offset: this.state.currentPage,
|
||||
limit: this.state.pageSize,
|
||||
keyword: filterText,
|
||||
isPromoted: nextProps.promoted
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
_onNext = () => {
|
||||
const { currentPage, pageSize, filterText } = this.state;
|
||||
console.debug(`-- _onNext -- ${0} -- ${1} -- ${2} --`, currentPage, pageSize, filterText)
|
||||
this.props.dataFetcher({ offset: currentPage * pageSize, limit: pageSize, keyword: filterText })
|
||||
const { currentPage, pageSize, filterText, promoted } = this.state;
|
||||
this.props.dataFetcher({ offset: currentPage * pageSize, limit: pageSize, keyword: filterText, isPromoted: promoted })
|
||||
};
|
||||
|
||||
_onPrevious = () => {
|
||||
const { currentPage, pageSize, filterText } = this.state;
|
||||
this.props.dataFetcher({ offset: (currentPage - 2) * pageSize + 1, limit: pageSize, keyword: filterText });
|
||||
const { currentPage, pageSize, filterText, promoted } = this.state;
|
||||
this.props.dataFetcher({ offset: (currentPage - 2) * pageSize + 1, limit: pageSize, keyword: filterText, isPromoted: promoted });
|
||||
};
|
||||
|
||||
_onGetPage = pageNumber => {
|
||||
const { pageSize, filterText } = this.state;
|
||||
this.props.dataFetcher({ offset: (pageNumber - 1) * pageSize + 1, limit: pageSize, keyword: filterText });
|
||||
const { pageSize, filterText, promoted } = this.state;
|
||||
this.props.dataFetcher({ offset: (pageNumber - 1) * pageSize + 1, limit: pageSize, keyword: filterText, isPromoted: promoted });
|
||||
};
|
||||
|
||||
_onFilter = filterText => {
|
||||
this.setState({ currentPage: 1 });
|
||||
const { currentPage, pageSize } = this.state;
|
||||
const { currentPage, pageSize, promoted } = this.state;
|
||||
this.setState({ filterText });
|
||||
this.props.dataFetcher({ offset: (currentPage - 1) * pageSize + 1, limit: pageSize, keyword: filterText });
|
||||
this.props.dataFetcher({ offset: (currentPage - 1) * pageSize + 1, limit: pageSize, keyword: filterText, isPromoted: promoted });
|
||||
};
|
||||
|
||||
searchChange = e => {
|
||||
this.setState({ filterText: e.target.value });
|
||||
}
|
||||
|
||||
searchSubmit = () => {
|
||||
const { currentPage, pageSize, promoted, filterText } = this.state;
|
||||
|
||||
this.props.dataFetcher({ offset: 1, limit: pageSize, keyword: filterText, isPromoted: promoted });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, placeholder } = this.props
|
||||
const style = {
|
||||
styles: {
|
||||
TableBody: { display: "flex", flexWrap: "wrap", justifyContent: "space-around" },
|
||||
TableBody: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-around'
|
||||
},
|
||||
Pagination: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingTop: '32px',
|
||||
},
|
||||
NextButton: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '600',
|
||||
fontFamily: 'Dosis',
|
||||
textTransform: 'inherit',
|
||||
marginLeft: '64px',
|
||||
marginRight: '64px',
|
||||
marginTop: '32px',
|
||||
marginBottom: '16px',
|
||||
padding: '9px 16px',
|
||||
borderRadius: '3px',
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid #E8E8E9',
|
||||
color: '#787B7D',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
PreviousButton: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '600',
|
||||
fontFamily: 'Dosis',
|
||||
textTransform: 'inherit',
|
||||
marginLeft: '64px',
|
||||
marginRight: '64px',
|
||||
marginTop: '32px',
|
||||
marginBottom: '16px',
|
||||
padding: '9px 16px',
|
||||
borderRadius: '3px',
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid #E8E8E9',
|
||||
color: '#787B7D',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
PageDropdown: {
|
||||
fontSize: '0.9em',
|
||||
fontWeight: '600',
|
||||
fontFamily: 'Dosis',
|
||||
display: 'none',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const { data, currentPage, pageSize, recordCount } = this.state;
|
||||
// console.debug("-- render -- ")
|
||||
// console.debug(data)
|
||||
const { filterText, data, currentPage, pageSize, recordCount, loading } = this.state;
|
||||
|
||||
const NewLayout = ({ Table, Pagination, Filter }) => (
|
||||
<div>
|
||||
<Filter />
|
||||
<Pagination />
|
||||
{/* <Filter /> */}
|
||||
<Table />
|
||||
<Pagination />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -103,39 +235,84 @@ class Grid extends Component {
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
<Griddle
|
||||
data={data}
|
||||
pageProperties={{
|
||||
currentPage,
|
||||
pageSize,
|
||||
recordCount
|
||||
}}
|
||||
components={{
|
||||
Layout: NewLayout,
|
||||
Row: this.props.CustomRowComponent,
|
||||
TableContainer: GriddleCustomTable,
|
||||
TableBody: CustomTableBody
|
||||
}}
|
||||
events={{
|
||||
onNext: this._onNext,
|
||||
onPrevious: this._onPrevious,
|
||||
onGetPage: this._onGetPage,
|
||||
onFilter: this._onFilter
|
||||
}}
|
||||
styleConfig={style}
|
||||
/>
|
||||
{this.props.searchable &&
|
||||
<div className={classes.filterForm}>
|
||||
<TextField
|
||||
id="standard-search"
|
||||
disabled={loading}
|
||||
name="search"
|
||||
value={filterText}
|
||||
onChange={this.searchChange}
|
||||
placeholder={placeholder}
|
||||
className={classes.textField}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
margin="dense"
|
||||
variant="filled"
|
||||
onKeyPress={(ev) => {
|
||||
if (ev.key === 'Enter') {
|
||||
this.searchSubmit()
|
||||
}
|
||||
}}
|
||||
type="search"
|
||||
/>
|
||||
<Button onClick={this.searchSubmit}
|
||||
disabled={loading}
|
||||
variant="contained"
|
||||
className={classes.button}>
|
||||
<IconSearch />
|
||||
</Button >
|
||||
</div>
|
||||
}
|
||||
{loading ?
|
||||
(<Loader />)
|
||||
: !_.isEmpty(data) ? (
|
||||
<Griddle
|
||||
className={classes.griddle}
|
||||
data={data}
|
||||
pageProperties={{
|
||||
currentPage,
|
||||
pageSize,
|
||||
recordCount,
|
||||
}}
|
||||
useCustomPagerComponent="true"
|
||||
components={{
|
||||
Layout: NewLayout,
|
||||
Row: this.props.CustomRowComponent,
|
||||
TableContainer: GriddleCustomTable,
|
||||
TableBody: CustomTableBody,
|
||||
PreviousButton: GriddleCustomPreviousButton,
|
||||
NextButton: GriddleCustomNextButton,
|
||||
}}
|
||||
events={{
|
||||
onNext: this._onNext,
|
||||
onPrevious: this._onPrevious,
|
||||
onGetPage: this._onGetPage,
|
||||
onFilter: this._onFilter
|
||||
}}
|
||||
styleConfig={style}
|
||||
/>
|
||||
) : (<NoResult text='Aucun résultat.' />)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Grid.defaultProps = {
|
||||
offset: 1,
|
||||
limit: 12,
|
||||
searchable: true
|
||||
}
|
||||
|
||||
Grid.propTypes = {
|
||||
dataFetcher: PropTypes.func.isRequired,
|
||||
CustomRowComponent: PropTypes.any.isRequired,
|
||||
data: PropTypes.any
|
||||
data: PropTypes.any,
|
||||
searchable: PropTypes.bool
|
||||
}
|
||||
|
||||
export default Grid;
|
||||
export default withStyles(styles)(Grid);
|
||||
|
||||
@@ -1,43 +1,25 @@
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import Header from '../organisms/Header'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
head: {
|
||||
width: '100%',
|
||||
|
||||
height: '56px',
|
||||
background: 'silver',
|
||||
height: '64px',
|
||||
background: theme.palette.secondary.main,
|
||||
position: 'fixed',
|
||||
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: '64px'
|
||||
}
|
||||
|
||||
zIndex: '1199',
|
||||
},
|
||||
foot: {
|
||||
height: '56px',
|
||||
height: '64px',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
top: '56px',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: '64px',
|
||||
top: '64px'
|
||||
},
|
||||
|
||||
top: '64px',
|
||||
},
|
||||
content: {
|
||||
top: '56px',
|
||||
height: 'calc(100vh - 112px)',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
top: '64px',
|
||||
height: 'calc(100vh - 128px)',
|
||||
},
|
||||
top: '64px',
|
||||
height: 'calc(100vh - 64px)',
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,7 +28,7 @@ function Layout(props) {
|
||||
const { classes } = props;
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<RootContainer>
|
||||
<div id="H" className={classes.head}>
|
||||
<div className={classes.inner}>
|
||||
<Header searchBar={true}/>
|
||||
@@ -56,7 +38,7 @@ function Layout(props) {
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</RootContainer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Router from 'next/router';
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import ArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
const styles = theme => ({
|
||||
inner: {
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 6px 10px 0 rgba(0, 0, 0, 0.04), 0 1px 10px 0 rgba(0, 0, 0, 0.04), 0 3px 5px -1px rgba(0, 0, 0, 0.04)',
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
head: {
|
||||
zIndex: '999',
|
||||
width: '100%',
|
||||
height: '64px',
|
||||
background: theme.palette.background.main,
|
||||
position: 'fixed',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: '64px'
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
fontSize: '2.5em',
|
||||
},
|
||||
title: {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
content: {
|
||||
top: '64px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
top: '64px',
|
||||
position: 'relative',
|
||||
},
|
||||
contentFixed: {
|
||||
height: 'calc(100vh - 64px)',
|
||||
},
|
||||
back: {
|
||||
zIndex: 10,
|
||||
border: 'none',
|
||||
padding: theme.spacing(0.2),
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
borderRadius: '50%',
|
||||
position: 'absolute',
|
||||
left: theme.spacing(1),
|
||||
},
|
||||
});
|
||||
|
||||
function LayoutBack(props) {
|
||||
|
||||
const { classes, height} = props;
|
||||
|
||||
//height fixed = height 100vh
|
||||
const content = (height == 'fixed') ? classes.content+' '+classes.contentFixed : classes.content
|
||||
return (
|
||||
<RootContainer align='center'>
|
||||
<div id="H" className={classes.head}>
|
||||
<div className={classes.inner}>
|
||||
<ButtonBase name='back'
|
||||
focusRipple
|
||||
className={classes.back}
|
||||
focusVisibleClassName={classes.focusVisible}
|
||||
onClick={() => Router.back()}>
|
||||
<ArrowLeft color="primary" classes={{ root: classes.icon }}></ArrowLeft>
|
||||
</ButtonBase>
|
||||
<Typography
|
||||
component="h1"
|
||||
align="center"
|
||||
className={classes.title}
|
||||
>
|
||||
{props.text}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div id="C" className={content}>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
</RootContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles)(LayoutBack)
|
||||
@@ -0,0 +1,120 @@
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import Header from '../organisms/Header'
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import IconAdd from '@material-ui/icons/Add';
|
||||
|
||||
import { Button } from '@material-ui/core';
|
||||
|
||||
const styles = theme => ({
|
||||
head: {
|
||||
width: '100%',
|
||||
height: '64px',
|
||||
background: theme.palette.secondary.main,
|
||||
position: 'fixed',
|
||||
zIndex: '999',
|
||||
},
|
||||
foot: {
|
||||
height: '64px',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
|
||||
},
|
||||
content: {
|
||||
top: '64px',
|
||||
height: 'calc(100vh - 64px)',
|
||||
position: 'relative',
|
||||
},
|
||||
filterForm: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: theme.palette.input.main,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: '100% !important',
|
||||
},
|
||||
textField: {
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: 0,
|
||||
'& input': {
|
||||
fontWeight: '300',
|
||||
color: theme.palette.primary.dark,
|
||||
backgroundColor: theme.palette.input.main,
|
||||
minWidth: '65vw',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
paddingLeft: theme.spacing(2),
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const chatSubmit = () => {
|
||||
|
||||
}
|
||||
|
||||
function LayoutCandidature(props) {
|
||||
|
||||
const { classes, children } = props;
|
||||
const [text, setText] = useState("");
|
||||
const [displayMessageBar, toggleMessageBar] = useState(false);
|
||||
|
||||
const handleChange = (v) => {
|
||||
toggleMessageBar(v)
|
||||
}
|
||||
const toggleDisplay = (v) => {
|
||||
toggleMessageBar(v)
|
||||
}
|
||||
return (
|
||||
<RootContainer>
|
||||
<div id="H" className={classes.head}>
|
||||
<div className={classes.inner}>
|
||||
<Header searchBar={true} />
|
||||
</div>
|
||||
</div>
|
||||
<div id="C" className={classes.content}>
|
||||
{
|
||||
React.Children.map(props.children, child => {
|
||||
return React.cloneElement(child, { toggleMessageBar: handleChange });
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
{displayMessageBar &&
|
||||
(<section id="F" className={classes.foot}>
|
||||
<div className={classes.filterForm}>
|
||||
<TextField
|
||||
id="standard-search"
|
||||
name="chattext"
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
placeholder={"Message"}
|
||||
className={classes.textField}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
// margin="dense"
|
||||
//variant="filled"
|
||||
onKeyPress={(ev) => {
|
||||
|
||||
if (ev.key === 'Enter') {
|
||||
// Do code here
|
||||
chatSubmit()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button onClick={chatSubmit}
|
||||
variant="contained"
|
||||
className={classes.button}>
|
||||
<IconAdd />
|
||||
</Button >
|
||||
</div>
|
||||
</section>)}
|
||||
</RootContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles)(LayoutCandidature)
|
||||
@@ -0,0 +1,129 @@
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from '@material-ui/core';
|
||||
import Router from 'next/router';
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import ArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
|
||||
import HideOnScroll from '../atoms/CollapsibleBack';
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import Header from '../organisms/Header'
|
||||
import {AppBar,CssBaseline,Container,Toolbar,IconButton,Typography} from "@material-ui/core";
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
|
||||
const styles = theme => ({
|
||||
RootContainer: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
maxWidth: 'inherit',
|
||||
},
|
||||
appBar: {
|
||||
backgroundColor: 'transparent',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
foot: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
padding: theme.spacing(1.2, 0),
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
|
||||
},
|
||||
content: {
|
||||
height: 'calc(100vh - 64px)',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: 'calc(100vh - 64px)',
|
||||
},
|
||||
position: 'relative',
|
||||
paddingBottom: '10vh',
|
||||
},
|
||||
back: {
|
||||
alignSelf: 'flex-start',
|
||||
position: 'absolute',
|
||||
zIndex: '999',
|
||||
color: 'white',
|
||||
backgroundColor: theme.palette.menu.backArrow,
|
||||
border: 'none',
|
||||
color: 'white',
|
||||
padding: theme.spacing(0.2),
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
display: 'inlineBlock',
|
||||
margin: theme.spacing(2),
|
||||
borderRadius: '50%',
|
||||
},
|
||||
icon: {
|
||||
fontSize: '2.5em',
|
||||
},
|
||||
rootButton: {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.important + '!important',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
},
|
||||
})
|
||||
|
||||
function LayoutCompany(props) {
|
||||
|
||||
const { classes, companyId } = props;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isWidthUp('sm', props.width) ?
|
||||
(<Header />)
|
||||
:
|
||||
(<HideOnScroll threshold={50}>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar>
|
||||
<ButtonBase name='back'
|
||||
focusRipple
|
||||
className={classes.back}
|
||||
focusVisibleClassName={classes.focusVisible}
|
||||
onClick={() => Router.back()}>
|
||||
<ArrowLeft classes={{ root: classes.icon }}></ArrowLeft>
|
||||
</ButtonBase>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</HideOnScroll>)
|
||||
}
|
||||
|
||||
<Container classes={{ root: classes.RootContainer }}>
|
||||
<RootContainer>
|
||||
<div id="C" className={classes.content}>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
<Hidden mdUp>
|
||||
<div id="F" className={classes.foot}>
|
||||
<Button
|
||||
variant="contained"
|
||||
className={classes.button}
|
||||
classes={{ root: classes.rootButton }}
|
||||
onClick={() => Router.push(`/Reponse?companyId=${companyId}`)}
|
||||
>
|
||||
Candidature spontanée</Button>
|
||||
</div>
|
||||
</Hidden>
|
||||
|
||||
</RootContainer>
|
||||
</Container>
|
||||
</React.Fragment>
|
||||
|
||||
)
|
||||
}
|
||||
LayoutCompany.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
company: PropTypes.object,
|
||||
};
|
||||
|
||||
export default withStyles(styles)(withWidth()(LayoutCompany))
|
||||
@@ -1,17 +1,15 @@
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Header from '../organisms/Header'
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import Footer from '../organisms/Footer';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
head: {
|
||||
width: '100%',
|
||||
|
||||
height: '56px',
|
||||
background: 'silver',
|
||||
zIndex: '999',
|
||||
height: '64px',
|
||||
background: theme.palette.secondary.main,
|
||||
position: 'fixed',
|
||||
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
@@ -20,16 +18,17 @@ const styles = theme => ({
|
||||
|
||||
},
|
||||
content: {
|
||||
top: '56px',
|
||||
height: 'calc(100vh - 56px)',
|
||||
background: theme.palette.background.grid,
|
||||
top: '64px',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
top: '64px',
|
||||
height: 'calc(100vh - 64px)',
|
||||
},
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
marginTop: theme.spacing(10),
|
||||
},
|
||||
});
|
||||
|
||||
function LayoutGrid(props) {
|
||||
@@ -37,25 +36,28 @@ function LayoutGrid(props) {
|
||||
const { classes } = props;
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<RootContainer>
|
||||
<div id="H" className={classes.head}>
|
||||
<div className={classes.inner}>
|
||||
<Header backLink={props.backLink}/>
|
||||
<Header backLink={props.backLink} />
|
||||
</div>
|
||||
</div>
|
||||
<div id="C" className={classes.content}>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="F" className={classes.footer}>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
</RootContainer>
|
||||
)
|
||||
}
|
||||
|
||||
LayoutGrid.defaultProps = {
|
||||
backLink:"/"
|
||||
backLink: "/"
|
||||
}
|
||||
LayoutGrid.propTypes = {
|
||||
backLink : PropTypes.string
|
||||
backLink: PropTypes.string
|
||||
}
|
||||
|
||||
export default withStyles(styles)(LayoutGrid)
|
||||
@@ -1,81 +1,115 @@
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Header from '../organisms/Header'
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from '@material-ui/core';
|
||||
import Router from 'next/router';
|
||||
import RootContainer from '../atoms/RootContainer'
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
|
||||
import ArrowLeft from '@material-ui/icons/keyboardArrowLeft';
|
||||
|
||||
import ArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
|
||||
import Header from '../organisms/Header'
|
||||
import withWidth, { isWidthUp } from '@material-ui/core/withWidth'
|
||||
import Hidden from '@material-ui/core/Hidden';
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
foot: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
width: '100%',
|
||||
position: 'fixed',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: '64px'
|
||||
},
|
||||
bottom: 0,
|
||||
padding: theme.spacing(1.2, 0),
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
boxShadow: '0px 0px 10px rgba(30, 35, 39, 0.10)',
|
||||
},
|
||||
content: {
|
||||
height: 'calc(100vh - 56px)',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
height: 'calc(100vh - 64px)',
|
||||
},
|
||||
contentDefault: {
|
||||
height: 'auto',
|
||||
position: 'relative',
|
||||
paddingBottom: '10vh',
|
||||
},
|
||||
contentProvided: {
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
|
||||
},
|
||||
back:
|
||||
{
|
||||
alignSelf: 'flex-start',
|
||||
position: 'absolute',
|
||||
zIndex:10,
|
||||
color : 'white',
|
||||
backgroundColor: 'grey',
|
||||
zIndex: '999',
|
||||
color: 'white',
|
||||
backgroundColor: theme.palette.menu.backArrow,
|
||||
border: 'none',
|
||||
color: 'white',
|
||||
padding: '10px',
|
||||
padding: theme.spacing(0.2),
|
||||
textAlign: 'center',
|
||||
textDecoration: 'none',
|
||||
display: 'inlineBlock',
|
||||
fontSize: '38px',
|
||||
margin:' 10px 16px',
|
||||
margin: theme.spacing(2),
|
||||
borderRadius: '50%',
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
fontSize: '2.5em',
|
||||
},
|
||||
rootButton: {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.primary.important + '!important',
|
||||
},
|
||||
},
|
||||
button: {
|
||||
textTransform: 'inherit',
|
||||
marginLeft: theme.spacing(8),
|
||||
marginRight: theme.spacing(8),
|
||||
padding: theme.spacing(1.2, 2),
|
||||
color: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.primary.important,
|
||||
},
|
||||
})
|
||||
|
||||
function Layout(props) {
|
||||
function LayoutOffre(props) {
|
||||
|
||||
const { classes } = props;
|
||||
const { classes, offreId, isProvided } = props;
|
||||
const contentClass = (!isProvided) ? classes.contentDefault : classes.contentProvided
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<ButtonBase name='back'
|
||||
focusRipple
|
||||
className={classes.back}
|
||||
focusVisibleClassName={classes.focusVisible}
|
||||
|
||||
onClick={() => Router.back()}>
|
||||
<ArrowLeft></ArrowLeft>
|
||||
</ButtonBase>
|
||||
<div id="C" className={classes.content}>
|
||||
<RootContainer>
|
||||
{isWidthUp('sm', props.width) ?
|
||||
(<Header />)
|
||||
:
|
||||
(<ButtonBase name='back'
|
||||
focusRipple
|
||||
className={classes.back}
|
||||
focusVisibleClassName={classes.focusVisible}
|
||||
onClick={() => Router.back()}>
|
||||
<ArrowLeft classes={{ root: classes.icon }}></ArrowLeft>
|
||||
</ButtonBase>)
|
||||
}
|
||||
<div id="C" className={contentClass}>
|
||||
{props.children}
|
||||
</div>
|
||||
<div id="F" className={classes.foot}>
|
||||
{/* <div className={classes.inner}>
|
||||
<div className={classes.grow} /> */}
|
||||
<Button variant="outlined" color="secondary" >Postuler</Button>
|
||||
{/* <div className={classes.grow} />
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<Hidden mdUp>
|
||||
{!isProvided && (
|
||||
<div id="F" className={classes.foot}>
|
||||
{/* <div className={classes.inner}>
|
||||
<div className={classes.grow} /> */}
|
||||
<Button
|
||||
variant="contained"
|
||||
className={classes.button + ' cypButtonApply'}
|
||||
classes={{ root: classes.rootButton}}
|
||||
onClick={() => Router.push(`/Reponse?adId=${offreId}`)}
|
||||
>
|
||||
Postuler</Button>
|
||||
{/* <div className={classes.grow} />
|
||||
</div> */}
|
||||
</div>
|
||||
)}
|
||||
</Hidden>
|
||||
|
||||
</RootContainer>
|
||||
)
|
||||
}
|
||||
LayoutOffre.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
offre: PropTypes.object,
|
||||
};
|
||||
|
||||
export default withStyles(styles)(Layout)
|
||||
export default withStyles(styles)(withWidth()(LayoutOffre))
|
||||
@@ -1,101 +1,28 @@
|
||||
import React from 'react'
|
||||
import React, { Fragment } from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
const styles = theme => ({
|
||||
loader: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontFamily: 'Dosis',
|
||||
fontWeight: '500',
|
||||
fontSize: '1em',
|
||||
padding: theme.spacing(6,0),
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
})
|
||||
|
||||
export default class extends React.Component {
|
||||
class Loader extends React.Component {
|
||||
render() {
|
||||
if (this.props.fullscreen) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<style jsx global>{`
|
||||
.circle-loader {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 50%;
|
||||
z-index: 100;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classes.loader + ' ' + 'alert alert-info'}><CircularProgress color="primary" />Chargement...</div>
|
||||
)
|
||||
|
||||
.circle-loader .circle {
|
||||
fill: transparent;
|
||||
stroke: rgba(0,0,0,0.2);
|
||||
stroke-width: 4px;
|
||||
animation: dash 2s ease infinite, rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dasharray: 1,95;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 85,95;
|
||||
stroke-dashoffset: -25;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 85,95;
|
||||
stroke-dashoffset: -93;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {transform: rotate(0deg); }
|
||||
100% {transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
<span className="circle-loader">
|
||||
<svg className="circle" width="60" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="30" cy="30" r="15"/>
|
||||
</svg>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<style jsx global>{`
|
||||
.circle-loader {
|
||||
display: block;
|
||||
text-center;
|
||||
padding-left: 50%;
|
||||
}
|
||||
|
||||
.circle-loader .circle {
|
||||
position: relative;
|
||||
left: -30px;
|
||||
fill: transparent;
|
||||
stroke: rgba(0,0,0,0.2);
|
||||
stroke-width: 4px;
|
||||
animation: dash 2s ease infinite, rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dasharray: 1,95;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 85,95;
|
||||
stroke-dashoffset: -25;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 85,95;
|
||||
stroke-dashoffset: -93;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {transform: rotate(0deg); }
|
||||
100% {transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
<span className="circle-loader">
|
||||
<svg className="circle" width="60" height="60" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="30" cy="30" r="15"/>
|
||||
</svg>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Loader)
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8000/",
|
||||
"env":{
|
||||
"nom":"toto",
|
||||
"prenom": "toto",
|
||||
"email":"thomasberrodedeclic42@gmail.com",
|
||||
"password": "123456"
|
||||
},
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
describe('RegisterTest', function() {
|
||||
before(function(){
|
||||
|
||||
cy.exec('yarn dropAndCreateDB',{failOnNonZeroExit: false});
|
||||
})
|
||||
it('Test if Register works', function() {
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('.cypRegister').click({ force: true })
|
||||
cy.url().should('contain', 'Login')
|
||||
cy.get('#nom').type(Cypress.env('nom'))
|
||||
cy.get('#prenom').type(Cypress.env('prenom'))
|
||||
cy.get('#emailRegister').type(Cypress.env('email'))
|
||||
cy.get('#adornment-password').type(Cypress.env('password'))
|
||||
cy.get('#cypCheckboxRegister').check()
|
||||
cy.get('.cypButtonRegister').click().wait(3000)
|
||||
cy.url().should('contain','VerifyEmail')
|
||||
cy.get('span').should('contain',Cypress.env('email'))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
// describe('ConfirmMailTest', function() {
|
||||
// it("Register Form: Email is delievered", function() {
|
||||
// const incoming_mailbox = `thomasberrodedeclic42@gmail.com`;
|
||||
// cy
|
||||
// .task("gmail:check", {
|
||||
// from: "noreply@talentstube.com",
|
||||
// to: incoming_mailbox,
|
||||
// subject: `Bienve nue ${incoming_mailbox} 🔑`
|
||||
// })
|
||||
// .then(email => {
|
||||
// assert.isNotNull(email, `Email was not found`);
|
||||
// });
|
||||
// });
|
||||
// })
|
||||
|
||||
describe("Email assertion:", () => {
|
||||
it("Look for an email with specific subject and link in email body", function() {
|
||||
// debugger; //Uncomment for debugger to work...
|
||||
cy.wait(5000)
|
||||
cy
|
||||
.task("gmail:get-messages", {
|
||||
options: {
|
||||
include_body: true,
|
||||
}
|
||||
})
|
||||
.then(emails => {
|
||||
const found_email = emails.find(email => {
|
||||
return (
|
||||
email.from.indexOf("noreply@talentstube.com") >= 0 &&
|
||||
email.subject.indexOf("Bienvenue thomasberrodedeclic42@gmail.com 🔑") >= 0
|
||||
);
|
||||
});
|
||||
assert.isNotNull(found_email, "Found email👍");
|
||||
const body = found_email.body.text;
|
||||
assert.isTrue(
|
||||
body.indexOf(
|
||||
"http://localhost:8000/ConfirmAccount/"
|
||||
) >= 0,
|
||||
"Found reset link👍"
|
||||
);
|
||||
|
||||
var startUrl = body.indexOf("http://");
|
||||
var endUrl = body.indexOf("*");
|
||||
var link = body.substring(startUrl, endUrl);
|
||||
|
||||
cy.visit(link);
|
||||
|
||||
|
||||
cy.get('p').should('contain','e-mail validé')
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
describe('Visit the whole site as anonymous', function() {
|
||||
it('visit the whole site to see if errors', function() {
|
||||
|
||||
cy.viewport(375, 667)
|
||||
|
||||
//Page d'accueil
|
||||
cy.visit('/')
|
||||
cy.get('p').should('contain', 'à la une')
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('span').should('contain', 'Connexion')
|
||||
cy.get('body').click('left')
|
||||
cy.get('button').first().click()
|
||||
cy.get('span').should('contain', 'Accueil')
|
||||
|
||||
//Page de Login
|
||||
cy.visit('/Login')
|
||||
cy.get('.cypButtonConnexion').should('exist')
|
||||
cy.get('.cypLinkedInConnexion').should('exist')
|
||||
|
||||
//Page d'offre(s)
|
||||
cy.visit('/Offres')
|
||||
cy.get('h1').should('contain', 'offres d\'emploi vidéo en cours')
|
||||
cy.get('.cypCardOffre').first().click()
|
||||
cy.get('.cypButtonApply').should('exist')
|
||||
cy.get('p').should('contain','Détails de l\'offre d\'emploi')
|
||||
cy.get('.player').should('exist')
|
||||
|
||||
//Page des entreprises
|
||||
cy.visit('/Entreprises')
|
||||
cy.get('.cypCardCompany').last().click()//could use .eq(2) instead of .last()
|
||||
cy.get('.player').should('exist')
|
||||
|
||||
//Help
|
||||
cy.visit('/Help')
|
||||
cy.get('h1').should('contain','Comment ça marche')
|
||||
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
describe('Wrong Login Test', function() {
|
||||
it('Teste la connexion d\'un utilisateur', function() {
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('.cypConnexion').click({ force: true })
|
||||
cy.url().should('contain', 'Login')
|
||||
cy.get('.cypInputEmail input').type('wrongmail@wrong.fr')
|
||||
cy.get('.cypInputPassword input').type('wrongpassword')
|
||||
cy.get('.cypButtonConnexion').click()
|
||||
cy.get('#client-snackbar').should('contain','Mauvais identifiants')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
describe('Apply to an offer', function() {
|
||||
it('Go to an offer and apply to it', function() {
|
||||
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('.cypConnexion').click({ force: true })
|
||||
cy.url().should('contain', 'Login')
|
||||
cy.get('.cypInputEmail input').type(Cypress.env('email'))
|
||||
cy.get('.cypInputPassword input').type(Cypress.env('password'))
|
||||
cy.get('.cypButtonConnexion').click().wait(3000)
|
||||
|
||||
|
||||
cy.get('.cypButtonOffers').click().wait(3000)
|
||||
cy.url().should('contain', 'Offres')
|
||||
cy.get('.cypCardOffre').first().click().wait(3000)
|
||||
cy.url().should('contain', 'Offre')
|
||||
cy.get('.cypButtonApply').first().click().wait(3000)
|
||||
cy.url().should('contain', 'Reponse')
|
||||
|
||||
cy.fixture('cypVid.mov', 'base64').then(fileContent => {
|
||||
cy.get('.cypButtonImportVid').invoke('show').upload(
|
||||
{ fileContent, fileName: 'cypVid.mov', mimeType: 'video/quicktime', encoding: 'base64' },
|
||||
{ subjectType: 'input', force: true }
|
||||
)}).wait(3000)
|
||||
cy.get('.cypButtonSendApplication').click().wait(3000)
|
||||
})
|
||||
|
||||
//Should verify if application is received
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
describe('ConnexionTest', function() {
|
||||
it('Test login', function() {
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('.cypConnexion').click({ force: true })
|
||||
cy.url().should('contain', 'Login')
|
||||
cy.get('.cypInputEmail input').type(Cypress.env('email'))
|
||||
cy.get('.cypInputPassword input').type(Cypress.env('password'))
|
||||
cy.get('.cypButtonConnexion').click().wait(3000)
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('h3').should('contain', Cypress.env('email') )
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
describe('VisitAsUser', function() {
|
||||
it('Test if user can access to his pages', function() {
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
cy.get('.cypConnexionMenu').click()
|
||||
cy.get('.cypConnexion').click({ force: true })
|
||||
cy.url().should('contain', 'Login')
|
||||
cy.get('.cypInputEmail input').type(Cypress.env('email'))
|
||||
cy.get('.cypInputPassword input').type(Cypress.env('password'))
|
||||
cy.get('.cypButtonConnexion').click().wait(3000)
|
||||
|
||||
cy.visit('/Candidatures')
|
||||
|
||||
|
||||
// cy.get('h1').should('contain', Cypress.env('Mes candidatures') )
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
describe('test_name', function() {
|
||||
|
||||
it('what_it_does', function() {
|
||||
|
||||
cy.viewport(1536, 674)
|
||||
|
||||
cy.visit('http://localhost:8000/')
|
||||
|
||||
cy.get('.MuiList-root > div > .jss56:nth-child(1) > .MuiListItemText-root > .MuiTypography-root').click()
|
||||
|
||||
cy.get('.MuiList-root > div > .jss56:nth-child(2) > .MuiListItemText-root > .MuiTypography-root').click()
|
||||
|
||||
cy.get('.MuiList-root > div > .jss56:nth-child(3) > .MuiListItemText-root > .MuiTypography-root').click()
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
{"installed":{"client_id":"438657734125-poo68tqp3dosq2od8udgipdqietbav0d.apps.googleusercontent.com","project_id":"tt-app-251309","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"m_zcFplh1LZlMHl6bwkBBFl8","redirect_uris":["http://localhost"],"javascript_origins":["http://localhost"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"access_token":"ya29.Glt0B7O-MEh7BOQ6yJyZY1628D2mH1lbVz7LaJfiUgKJJv5mt1DljeQsZU49z-5F9GYsWVn0E5ylYj2yWuGLGb2J0sZJjVUWMLYn333dRbk-0469E08Hmc3GkIOO","refresh_token":"1/42U4Ny2XVLbf3D3xO4S2bwyyX183RuqQ2kcBmND7hpr5japueYf6psdLEWzIFfCx","scope":"https://www.googleapis.com/auth/gmail.readonly","token_type":"Bearer","expiry_date":1567156006741}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
const gmail = require("gmail-tester");
|
||||
const path = require("path");
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
on("before:browser:launch", (browser = {}, args) => {
|
||||
if (browser.name === "chrome") {
|
||||
args.push("--remote-debugging-port=9221");
|
||||
return args;
|
||||
}
|
||||
});
|
||||
|
||||
// on("task", {
|
||||
// "gmail:check": async args => {
|
||||
// const { from, to, subject } = args;
|
||||
// const email = await gmail.check_inbox(
|
||||
// path.resolve(__dirname, "credentials.json"), // credentials.json is inside plugins/ directory.
|
||||
// path.resolve(__dirname, "gmail_token.json"), // gmail_token.json is inside plugins/ directory.
|
||||
// subject,
|
||||
// from,
|
||||
// to,
|
||||
// 10, // Poll interval (in seconds)
|
||||
// 30 // Maximum poll interval (in seconds). If reached, return null, indicating the completion of the task().
|
||||
// );
|
||||
// return email;
|
||||
// }
|
||||
// });
|
||||
|
||||
on("task", {
|
||||
"gmail:get-messages": async args => {
|
||||
const messages = await gmail.get_messages(
|
||||
path.resolve(__dirname, "credentials.json"),
|
||||
path.resolve(__dirname, "gmail_token.json"),
|
||||
args.options
|
||||
);
|
||||
return messages;
|
||||
}
|
||||
});
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
@@ -0,0 +1,46 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
import 'cypress-file-upload';
|
||||
|
||||
Cypress.Commands.add('login', () => {
|
||||
cy.viewport(375, 667)
|
||||
cy.visit('/')
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: 'http://localhost/api/login_check',
|
||||
body: {
|
||||
|
||||
username: 'cypress.tt.edeclic@gmail.com',
|
||||
password: 'qJxY4kovD13W'
|
||||
|
||||
}
|
||||
})
|
||||
.then((resp) => {
|
||||
window.localStorage.setItem('ttJWT', resp.body.token)
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
// before(() => {
|
||||
// cy.exec('yarn dropAndCreateDB');
|
||||
// })
|
||||
|
||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
||||
// returning false here prevents Cypress from
|
||||
// failing the test
|
||||
return false
|
||||
})
|
||||
|
||||
Binary file not shown.
+134
-27
@@ -1,47 +1,154 @@
|
||||
import axios from "axios";
|
||||
|
||||
import { fetchAnswerFailure } from "../actions/answer";
|
||||
import setAuthorizationHeader from "./setAuthorizationHeader";
|
||||
import Router from 'next/router';
|
||||
//axios.defaults.headers.common = { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
axios.defaults.baseURL = (process.env.NODE_ENV !== 'production') ? 'http://localhost/app_dev.php/' : ''
|
||||
axios.defaults.baseURL = (process.env.NODE_ENV !== 'production') ?
|
||||
'http://localhost/app_dev.php/' :
|
||||
process.env.SERVER_URL;
|
||||
|
||||
axios.interceptors.response.use(response => {
|
||||
return response;
|
||||
}, error => {
|
||||
if (error.response.status === 401) {
|
||||
localStorage.setItem('ttJWT', '');
|
||||
Router.push("/Login");
|
||||
}
|
||||
return error;
|
||||
});
|
||||
|
||||
export default {
|
||||
user: {
|
||||
login: credentials =>
|
||||
axios.post("/api/login_check", { username: credentials.email, password: credentials.password }).then(res => res.data),
|
||||
loginOauth: ({token,type}) =>
|
||||
axios.post("/api/login_check_oauth", {'oauth-token':token,'oauth-type':type}),
|
||||
fetchUser: () =>{
|
||||
console.log("axios /api/user")
|
||||
return axios.get("/api/user").then(res => {console.log(res); return res.data})},
|
||||
subscribeToNewsletter: (data) =>
|
||||
{
|
||||
console.log(data)
|
||||
return axios.post("http://localhost:3000/NewsletterSubscription", data).then(res => {console.log(res); return res})
|
||||
loginOauth: ({ token, type }) =>
|
||||
axios.post("/api/login_check_oauth", { 'oauth-token': token, 'oauth-type': type }),
|
||||
fetchUser: () => {
|
||||
return axios.get("/api/user").then(res => { return res.data })
|
||||
},
|
||||
register: (data) => {
|
||||
console.log(data)
|
||||
return axios.post("/api/register", { data}).then(res => res)
|
||||
subscribeToNewsletter: (data) => {
|
||||
return axios.post(process.env.FRONT_SERVER + "/NewsletterSubscription", data).then(res => { return res })
|
||||
},
|
||||
register: (payload) => {
|
||||
return axios.post("/api/register", payload).then(res => res)
|
||||
},
|
||||
sendconfirmationEmail: (data) => {
|
||||
return axios.post(process.env.FRONT_SERVER + "/sendConfirmationEmail", data).then(res => { return res })
|
||||
},
|
||||
confirmUserAccount: (payload) => {
|
||||
return axios.post("/api/confirmAccount", payload).then(res => res)
|
||||
},
|
||||
updateUser: (payload) => {
|
||||
return axios.post("/api/update_user", payload, {
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
}).then(res => res)
|
||||
},
|
||||
resetPassword: (payload) => {
|
||||
return axios.post("/api/reset_password", payload).then(res => res)
|
||||
},
|
||||
subscribeUserToPush: (payload) => {
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
return axios.post("/api/userSubscriptionToPush", payload).then(res => res)
|
||||
},
|
||||
fetchUserSubscriptionToPush: () => {
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
return axios.get(`/api/userSubscriptionToPush`).then(res => res)
|
||||
},
|
||||
uploadPicture: (payload) => {
|
||||
return axios.post("/api/update_profile_picture", payload, {
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
}).then(res => res)
|
||||
},
|
||||
},
|
||||
ads: {
|
||||
fetchAd: (id) => {
|
||||
console.debug("--- fetchAd")
|
||||
console.debug(id)
|
||||
return axios.get(`api/ads/${id}`).then(res => res.data)
|
||||
fetchJob: (id) => {
|
||||
return axios.get(`api/jobs/${id}`).then(res => res.data)
|
||||
},
|
||||
|
||||
|
||||
//offset is the index position of the first element to start requesting at
|
||||
// use 0|1 for isPromoted
|
||||
fetchAds: ({ limit = null, order = "", keyword = "", offset = 1, status='PUBLISHED', isPromoted=null}) => {
|
||||
console.debug("--- fetchAds" + offset, limit, order, keyword)
|
||||
return axios.get('api/ads', { params: { offset, limit, order, keyword, status, isPromoted } })
|
||||
fetchJobs: ({ limit = null, order = "", keyword = "", offset = 1, status = 'PUBLISHED', isPromoted = false, isEmphasis = false }) => {
|
||||
isPromoted = isPromoted !== null ? isPromoted * 1 : isPromoted;
|
||||
isEmphasis = isEmphasis !== null ? isEmphasis * 1 : isEmphasis;
|
||||
|
||||
return axios.get('api/jobs', { params: { offset, limit, order, keyword, status, isPromoted, isEmphasis } })
|
||||
.then(res => res.data)
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
companies: {
|
||||
fetchCompany: id =>
|
||||
axios.get(`api/companies/${id}`).then(res => res.data),
|
||||
fetchCompanies: (limit = "", order = "", keyword = "", offset = 1) =>
|
||||
axios.get('api/companies', { params: { offset, limit, order, keyword } }).then(res => res.data)
|
||||
|
||||
fetchCompanies: ({ limit = null, order = "", keyword = "", offset = 1, isEmphasis = null }) => {
|
||||
|
||||
isEmphasis = isEmphasis !== null ? isEmphasis * 1 : isEmphasis;
|
||||
return axios.get('api/companies', { params: { offset, limit, order, keyword, isEmphasis } }).then(res => res.data);
|
||||
}
|
||||
},
|
||||
answers: {
|
||||
postAnswer: (payload) => {
|
||||
return axios.post("/api/answers", { adId: payload.adId, mediaId: payload.mediaId, companyId: payload.companyId }).then(res => res)
|
||||
},
|
||||
fetchJobsAnswered: ({ limit = null, order = "", keyword = "", offset = 1 }) => {
|
||||
return axios.get(`api/answers`, { params: { offset, limit, order, keyword } }).then(res => res.data)
|
||||
},
|
||||
fetchAnswer: (payload) => {
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
return axios.get(`api/answers/${payload}`).then(res => res.data)
|
||||
},
|
||||
postMessage: (payload) => {
|
||||
return axios.post("/api/answers/message", { answerId: payload.answerId, message: payload.message }).then(res => res)
|
||||
},
|
||||
fetchAnswerMessages: (payload) => {
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
return axios.get(`api/answers/${payload}/messages`).then(res => res.data)
|
||||
},
|
||||
},
|
||||
medias: {
|
||||
uploadMedia: (payload) => {
|
||||
return axios.post("/api/media", payload, {
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
}).then(res => res)
|
||||
},
|
||||
uploadMediaPres: (payload) => {
|
||||
return axios.post("/api/mediaPres", payload, {
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
}).then(res => res)
|
||||
},
|
||||
uploadMediaCv: (payload) => {
|
||||
return axios.post("/api/mediaCv", payload, {
|
||||
headers: {
|
||||
'Content-Type': `multipart/form-data;`
|
||||
},
|
||||
withCredentials: true
|
||||
}).then(res => res)
|
||||
}
|
||||
},
|
||||
stats: {
|
||||
countAdView: id => {
|
||||
if (!!localStorage.ttJWT)
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
axios.get(`api/adAjaxRecStat/${id}`)
|
||||
},
|
||||
countCompanyView: id => {
|
||||
if (!!localStorage.ttJWT)
|
||||
setAuthorizationHeader(localStorage.ttJWT)
|
||||
axios.get(`api/companyAjaxRecStat/${id}`)
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
@@ -215,7 +215,7 @@ module.exports = () => {
|
||||
}
|
||||
})
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('Generated sign in link ' + url + ' for ' + email)
|
||||
console.info('Generated sign in link ' + url + ' for ' + email)
|
||||
}
|
||||
},
|
||||
// Credentials Sign In
|
||||
|
||||
@@ -26,7 +26,6 @@ require('dotenv').load()
|
||||
module.exports = () => {
|
||||
let providers = []
|
||||
|
||||
if (process.env.FACEBOOK_ID && process.env.FACEBOOK_SECRET) {
|
||||
providers.push({
|
||||
providerName: 'Facebook',
|
||||
providerOptions: {
|
||||
@@ -34,8 +33,8 @@ module.exports = () => {
|
||||
},
|
||||
Strategy: require('passport-facebook').Strategy,
|
||||
strategyOptions: {
|
||||
clientID: process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.FACEBOOK_SECRET,
|
||||
clientID: process.env.NODE_ENV === 'production' ? process.env.TT_FACEBOOK_ID : process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.NODE_ENV === 'production' ? process.env.TT_FACEBOOK_SECRET : process.env.FACEBOOK_SECRET,
|
||||
profileFields: ['id', 'displayName', 'email', 'link']
|
||||
},
|
||||
getProfile(profile) {
|
||||
@@ -47,9 +46,8 @@ module.exports = () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (process.env.LINKEDIN_ID && process.env.LINKEDIN_SECRET) {
|
||||
providers.push({
|
||||
providerName: 'Linkedin',
|
||||
providerOptions: {
|
||||
@@ -57,12 +55,11 @@ module.exports = () => {
|
||||
},
|
||||
Strategy: require('@sokratis/passport-linkedin-oauth2').Strategy,
|
||||
strategyOptions: {
|
||||
clientID: process.env.LINKEDIN_ID,
|
||||
clientSecret: process.env.LINKEDIN_SECRET,
|
||||
clientID: process.env.NODE_ENV === 'production' ? process.env.TT_LINKEDIN_ID : process.env.LINKEDIN_ID,
|
||||
clientSecret: process.env.NODE_ENV === 'production' ? process.env.TT_LINKEDIN_SECRET : process.env.LINKEDIN_SECRET,
|
||||
profileFields: ['r_emailaddress', 'r_basicprofile']
|
||||
},
|
||||
getProfile(profile) {
|
||||
console.log(profile);
|
||||
// Normalize profile into one with {id, name, email} keys
|
||||
return {
|
||||
id: profile.id,
|
||||
@@ -71,7 +68,7 @@ module.exports = () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return providers
|
||||
}
|
||||
+46
-45
@@ -4,47 +4,57 @@ const optimizedImages = require('next-optimized-images')
|
||||
const webpack = require("webpack")
|
||||
const withCSS = require('@ostai/next-css')
|
||||
const withOffline = require('next-offline')
|
||||
const Dotenv = require('dotenv-webpack');
|
||||
|
||||
const { ANALYZE, DEV } = process.env
|
||||
|
||||
module.exports = withPlugins([
|
||||
[withCSS],
|
||||
[optimizedImages, {
|
||||
/* config for next-optimized-images */
|
||||
}],
|
||||
[withOffline, {
|
||||
dontAutoRegisterSw: DEV ? true : false,
|
||||
workboxOpts: {
|
||||
globPatterns: ['static/**/*'],
|
||||
globDirectory: '.',
|
||||
module.exports = withPlugins(
|
||||
[
|
||||
[withCSS],
|
||||
[optimizedImages, {
|
||||
/* config for next-optimized-images */
|
||||
}],
|
||||
[withOffline, {
|
||||
dontAutoRegisterSw: DEV ? true : false,
|
||||
workboxOpts: {
|
||||
runtimeCaching: [
|
||||
// { urlPattern: /.*auth.*/, handler: 'networkOnly' },
|
||||
// { urlPattern: /.*account.*/, handler: 'networkOnly' },
|
||||
{ urlPattern: /^https?.*/, handler: 'networkFirst' },
|
||||
{ urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
|
||||
handler: "cacheFirst",
|
||||
options: {
|
||||
cacheName: "images",
|
||||
expiration: {
|
||||
maxEntries: 100
|
||||
}
|
||||
}}
|
||||
// { urlPattern: /.*auth.*/, handler: 'networkOnly' },
|
||||
// { urlPattern: /.*account.*/, handler: 'networkOnly' },
|
||||
{ urlPattern: /^https?.*/, handler: 'NetworkFirst' },
|
||||
{
|
||||
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
|
||||
handler: "CacheFirst",
|
||||
options: {
|
||||
cacheName: "images",
|
||||
expiration: {
|
||||
maxEntries: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
importScripts:
|
||||
['/static/js/firebase-messaging-sw.js',
|
||||
'/static/js/backgroundSync-sw.js'
|
||||
]
|
||||
},
|
||||
|
||||
}],
|
||||
|
||||
[{
|
||||
publicRuntimeConfig: {
|
||||
|
||||
adListDefaultUrl: "https://www.talentstube.com/img/default_picture_company.svg",
|
||||
logoRootUrl: 'https://www.talentstube.com/media/cache/profile_thumb/uploads/img/'
|
||||
}
|
||||
}]],
|
||||
['/statique/js/Edeclic-service-worker.js',
|
||||
'/statique/js/backgroundSync-sw.js'
|
||||
]
|
||||
},
|
||||
}],
|
||||
[{
|
||||
publicRuntimeConfig: {
|
||||
symphonyUrl: 'https://pp.talentstube.com',
|
||||
blogUrl: 'https://blog.talentstube.com',
|
||||
frontServerUrl: 'https://pp.app.talentstube.com',
|
||||
cvThequeUrl: 'https://cvtheque.talentstube.com/',
|
||||
adListDefaultUrl: '/statique/images/default/default_picture_company.svg',
|
||||
cachedMediaUrl: '/media/cache/profile_thumb/uploads/img/',
|
||||
postedContentUrl: '/uploads/img/',
|
||||
postedContentCv: '/uploads/cv/',
|
||||
ReCAPTCHA: {
|
||||
"siteKey": "6LdyqLwUAAAAADKI5uC7d5W9VqVgzd8VEmMU2Yi-"
|
||||
},
|
||||
applicationServerPublicKey: 'BNFyiyPiLH97Dgc0m1vXluUNaCN6y3Hkgd4YcqS2lVkP8my3tq2D5gM5g7cY6R4VxC7GljSzkLRu+k+oO+2TtjU='
|
||||
}
|
||||
}]
|
||||
],
|
||||
{
|
||||
webpack: (config, { isServer }) => {
|
||||
|
||||
@@ -68,20 +78,11 @@ module.exports = withPlugins([
|
||||
RecordRTC: "recordrtc"
|
||||
};
|
||||
|
||||
|
||||
config.resolve.alias = { ...config.resolve.alias, ...videojsAlias };
|
||||
config.plugins.push(videojsPlugin);
|
||||
|
||||
// config.module.rules.push(
|
||||
// {
|
||||
// test: /\.css$/,
|
||||
// loader: 'style-loader!css-loader',
|
||||
// include: [/node_modules/]
|
||||
// },
|
||||
config.plugins.push(new Dotenv());
|
||||
|
||||
// )
|
||||
|
||||
console.log(config);
|
||||
return config
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://app.talentstube.com/Candidature</loc>
|
||||
<lastmod>2019-09-17</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Cgu</loc>
|
||||
<lastmod>2019-09-17</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Confidentialite</loc>
|
||||
<lastmod>2019-11-04</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Entreprises</loc>
|
||||
<lastmod>2019-09-17</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Help</loc>
|
||||
<lastmod>2019-10-11</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Login</loc>
|
||||
<lastmod>2019-10-14</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/MentionsLegales</loc>
|
||||
<lastmod>2019-11-04</lastmod>
|
||||
</url>,<url>
|
||||
<loc>https://app.talentstube.com/Offres</loc>
|
||||
<lastmod>2019-10-16</lastmod>
|
||||
</url>
|
||||
</urlset>
|
||||
Generated
+16912
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user