Compare commits
884 Commits
feature/fl
...
master
Author | SHA1 | Date |
---|---|---|
김장현 | 12610e03f8 | 21 minutes ago |
kyw546 | e4efb940ac | 27 minutes ago |
김지은 | a4081ead4a | 20 hours ago |
김지은 | 6b93fe2466 | 20 hours ago |
김지은 | 729845ad83 | 20 hours ago |
김장현 | f8beb8ed98 | 20 hours ago |
김지은 | ebfe6e9fcb | 21 hours ago |
이준희 | f5cc90fea7 | 2 months ago |
junh_eee(이준희) | 9b73b58e5b | 2 months ago |
이준희 | 6ba21bf69f | 2 months ago |
김지은 | e9fdf3e9ea | 10 months ago |
김지은 | f161ce7f7c | 10 months ago |
김지은 | 8e246de085 | 10 months ago |
김지은 | 72638e9f5a | 10 months ago |
김지은 | ecd66d0b8e | 10 months ago |
김지은 | b7e3e7a322 | 10 months ago |
김지은 | 993c06de17 | 10 months ago |
김지은 | c3c6ebe3ca | 10 months ago |
김지은 | af942b57c2 | 11 months ago |
김지은 | c9f9d651b0 | 11 months ago |
김지은 | 4fdfa2f665 | 11 months ago |
김지은 | 294d9ed93b | 11 months ago |
이준희 | 6578cb5f8d | 11 months ago |
이준희 | eb49879d45 | 11 months ago |
이준희 | 95f4be94a4 | 11 months ago |
이준희 | 1f04c6d59d | 11 months ago |
이준희 | 2bbf1294f9 | 11 months ago |
hhjk00 | 45cf2bfd12 | 11 months ago |
hhjk00 | b6bed9cfc4 | 11 months ago |
이준희 | 8fb4f11851 | 11 months ago |
김장현 | 7a50181d88 | 11 months ago |
이준희 | 79deb90758 | 11 months ago |
이준희 | 3629520881 | 11 months ago |
kyw546 | a93275dde0 | 11 months ago |
kyw546 | fc99f04e27 | 11 months ago |
김지은 | 259a65b420 | 11 months ago |
김지은 | 1b2f05464e | 11 months ago |
김지은 | 9152deff65 | 11 months ago |
김지은 | 9085249189 | 11 months ago |
김지은 | 5bfa8013c7 | 11 months ago |
junh_eee(이준희) | e77df057ed | 11 months ago |
kyw546 | 74f7a91bbe | 11 months ago |
kyw546 | 074b16db9f | 11 months ago |
kyw546 | 7a9470040d | 11 months ago |
kyw546 | 12e2da75d3 | 11 months ago |
kyw546 | f8a30eb1dc | 11 months ago |
kyw546 | 5ae982130e | 11 months ago |
kyw546 | c01e0894a2 | 11 months ago |
kyw546 | 66ab0ba594 | 11 months ago |
김장현 | 4812519fcb | 11 months ago |
김장현 | a802aed33d | 11 months ago |
hhjk00 | 24182b65a8 | 11 months ago |
hhjk00 | 9469d774bc | 11 months ago |
junh_eee(이준희) | 0caccde9fd | 11 months ago |
hhjk00 | 9d358d7361 | 11 months ago |
hhjk00 | b9d1f9eba8 | 11 months ago |
hhjk00 | 87997130e2 | 11 months ago |
hhjk00 | 2f2d30c524 | 11 months ago |
hhjk00 | ec1f3de597 | 1 year ago |
hhjk00 | 8e7691b7b7 | 1 year ago |
junh_eee(이준희) | 70e22656f0 | 1 year ago |
junh_eee(이준희) | 25f9433fdd | 1 year ago |
junh_eee(이준희) | 2dbf064211 | 1 year ago |
junh_eee(이준희) | 528988bff4 | 1 year ago |
junh_eee(이준희) | ca6465f9f1 | 1 year ago |
junh_eee(이준희) | 4c99822a38 | 1 year ago |
junh_eee(이준희) | df47548969 | 1 year ago |
junh_eee(이준희) | 01764adb94 | 1 year ago |
junh_eee(이준희) | 060a25c841 | 1 year ago |
junh_eee(이준희) | 650314091b | 1 year ago |
김장현 | da8b4d1079 | 1 year ago |
김장현 | 8ef3f93d45 | 1 year ago |
김장현 | 741cabf151 | 1 year ago |
이준희 | 113c4f87d6 | 1 year ago |
이준희 | 8b64938b0d | 1 year ago |
이준희 | 28b925b967 | 1 year ago |
이준희 | d7014cab22 | 1 year ago |
이준희 | 58928b91c9 | 1 year ago |
이준희 | 3b463398c9 | 1 year ago |
이준희 | 1c9cc24468 | 1 year ago |
sanguu516(박상현) | b28040e93a | 1 year ago |
sanguu516(박상현) | e0631f680f | 1 year ago |
sanguu516(박상현) | 4ed385d2a0 | 1 year ago |
이준희 | d577f38a0e | 1 year ago |
이준희 | e832f77d8a | 1 year ago |
이준희 | 00b571b854 | 1 year ago |
이준희 | 54d67b70e5 | 1 year ago |
sanguu516(박상현) | a108456653 | 1 year ago |
이준희 | 91993115a0 | 1 year ago |
이준희 | 2f10b1d0ab | 1 year ago |
sanguu516(박상현) | 6b2b81ed6f | 1 year ago |
이준희 | 966ccc2c03 | 1 year ago |
이준희 | 8e725dffe5 | 1 year ago |
이준희 | ca1cac98f7 | 1 year ago |
이준희 | 0fc59bb201 | 1 year ago |
이준희 | 23cbfcac2a | 1 year ago |
이준희 | d516bdce6e | 1 year ago |
이준희 | 539640f7fb | 1 year ago |
김장현 | 5f51da325c | 1 year ago |
이준희 | ff0a7d36ba | 1 year ago |
김장현 | 6a405c2da7 | 1 year ago |
김장현 | e70568596a | 1 year ago |
김장현 | e71e45525f | 1 year ago |
이준희 | d75ba1acf3 | 1 year ago |
이준희 | 0eb7a4b5d8 | 1 year ago |
이준희 | 0cdd8abbd9 | 1 year ago |
이준희 | 4a23b23f07 | 1 year ago |
이준희 | b35f8781e4 | 1 year ago |
이준희 | 7d2ec253ea | 1 year ago |
이준희 | 58e6cdf6a7 | 1 year ago |
sanguu516(박상현) | 505867d88b | 1 year ago |
이준희 | f35dc31116 | 1 year ago |
이준희 | 3ce9d538f7 | 1 year ago |
이준희 | d4954f3a20 | 1 year ago |
이준희 | 9ebc1da082 | 1 year ago |
qkr7828(박재우) | c38542b10e | 1 year ago |
qkr7828(박재우) | 50613a4175 | 1 year ago |
이준희 | da590a3911 | 1 year ago |
이준희 | a98eeb158f | 1 year ago |
이준희 | 6e1c6a71d8 | 1 year ago |
이준희 | 0bd71b7062 | 1 year ago |
지대한 | 4b371fb10c | 1 year ago |
지대한 | 84d755c0c7 | 1 year ago |
이준희 | 0cb2f2e16f | 1 year ago |
이학준 | 0490d3bc9f | 1 year ago |
이학준 | 17e577addd | 1 year ago |
이학준 | e9d104b53f | 1 year ago |
김장현 | eb3a9088ef | 1 year ago |
김장현 | 7d68dc66d8 | 2 years ago |
김장현 | b23a4f088a | 2 years ago |
sanguu(박상현) | 776a030ead | 2 years ago |
sanguu(박상현) | 2e61e3342c | 2 years ago |
김장현 | a718a753d9 | 2 years ago |
김장현 | cf223f008e | 2 years ago |
김장현 | 18ce2d5bf1 | 2 years ago |
이준희 | f1f2abd0a8 | 2 years ago |
이준희 | 42ccc06b66 | 2 years ago |
김장현 | 44e50d5530 | 2 years ago |
김장현 | dfeff86260 | 2 years ago |
김장현 | 0347e828f6 | 2 years ago |
이준희 | 36b9bfc4b4 | 2 years ago |
이준희 | c04af0d147 | 2 years ago |
sanguu(박상현) | 9413d00ee7 | 2 years ago |
김지은 | 4f58eb15d1 | 2 years ago |
김지은 | 56602c29bb | 2 years ago |
이준희 | 60eb9c4a7c | 2 years ago |
이준희 | ba0745970e | 2 years ago |
김장현 | 4ecf185a1c | 2 years ago |
김장현 | 09695f949a | 2 years ago |
김장현 | 69c4bd7e33 | 2 years ago |
김지은 | 4f7f181e91 | 2 years ago |
김지은 | dd3a3c5193 | 2 years ago |
김지은 | 40db62284a | 2 years ago |
김장현 | 44c76043d4 | 2 years ago |
이준희 | 5390b02ab0 | 2 years ago |
김지은 | 5cb37a1d00 | 2 years ago |
김지은 | a8f5434e1b | 2 years ago |
sanguu(박상현) | c09776fcad | 2 years ago |
sanguu(박상현) | 0b00931ae6 | 2 years ago |
이준희 | 20e3ab63e5 | 2 years ago |
이준희 | 4bd71f2795 | 2 years ago |
sanguu(박상현) | 6f798b0e0a | 2 years ago |
sanguu(박상현) | 07311fe086 | 2 years ago |
이준희 | 920619489b | 2 years ago |
이준희 | 93b398b34d | 2 years ago |
김장현 | 9b0b429d3c | 2 years ago |
김장현 | b9f01387f5 | 2 years ago |
김장현 | 53dc995d30 | 2 years ago |
김장현 | cbcf629fb2 | 2 years ago |
김장현 | 638bf7ae86 | 2 years ago |
김장현 | 34535b3299 | 2 years ago |
김장현 | c017f3ec0d | 2 years ago |
김장현 | f30d18a00e | 2 years ago |
sanguu(박상현) | 01402693d1 | 2 years ago |
sanguu(박상현) | 4ab42795cf | 2 years ago |
김장현 | 879687d12f | 2 years ago |
김장현 | 8c44e026ad | 2 years ago |
이준희 | ff960b2ca1 | 2 years ago |
sanguu(박상현) | 43bd3cc74e | 2 years ago |
이학준 | a1bb1ebf2d | 2 years ago |
이준희 | e4ca0f824f | 2 years ago |
이준희 | 47c99496db | 2 years ago |
이준희 | 3717a626e0 | 2 years ago |
김장현 | 7c3b9485c9 | 2 years ago |
이준희 | c28a973086 | 2 years ago |
이준희 | 2b03eaa548 | 2 years ago |
김장현 | cbac155875 | 2 years ago |
이준희 | 92829c456f | 2 years ago |
김장현 | af44093e3a | 2 years ago |
sanguu(박상현) | c10702a07b | 2 years ago |
sanguu(박상현) | 8e4806cea7 | 2 years ago |
이준희 | 4c926d176a | 2 years ago |
이준희 | 987ce1071a | 2 years ago |
이준희 | a69d4aa297 | 2 years ago |
sanguu(박상현) | 39583e996c | 2 years ago |
sanguu(박상현) | f85a666041 | 2 years ago |
sanguu(박상현) | 61350bec65 | 2 years ago |
이준희 | 38ce25e63c | 2 years ago |
sanguu(박상현) | 84d1ee36dc | 2 years ago |
sanguu(박상현) | ed22a32973 | 2 years ago |
이준희 | 6dcfde9cdc | 2 years ago |
이준희 | 949342a4a4 | 2 years ago |
이준희 | 84eca90e02 | 2 years ago |
이준희 | a3999eb9be | 2 years ago |
이학준 | b5dae4ba85 | 2 years ago |
sanguu(박상현) | 787c673145 | 2 years ago |
이준희 | 8b52d5405c | 2 years ago |
sanguu(박상현) | fc4fb6e88d | 2 years ago |
sanguu(박상현) | 41fcb639ad | 2 years ago |
김장현 | 8f010a234f | 2 years ago |
이준희 | ee28c647f1 | 2 years ago |
sanguu(박상현) | 95273c9cb6 | 2 years ago |
sanguu(박상현) | a19558c838 | 2 years ago |
sanguu(박상현) | aa4488bc0c | 2 years ago |
sanguu(박상현) | 64b0225019 | 2 years ago |
이준희 | 54d75ab9b5 | 2 years ago |
이준희 | 8787db9dbd | 2 years ago |
김장현 | 89c4153905 | 2 years ago |
이준희 | 81de717f2c | 2 years ago |
이준희 | 1a8ec3e880 | 2 years ago |
김장현 | 7392a76854 | 2 years ago |
김장현 | 2127aaa316 | 2 years ago |
이준희 | 0f164c1191 | 2 years ago |
이준희 | c78e8683e3 | 2 years ago |
김장현 | 33aa24b01f | 2 years ago |
김장현 | 5c1090a461 | 2 years ago |
sanguu(박상현) | 702600c2fe | 2 years ago |
sanguu(박상현) | 80816006f5 | 2 years ago |
이학준 | 97d60e75a4 | 2 years ago |
이준희 | 204af06df5 | 2 years ago |
김장현 | b270144384 | 2 years ago |
김장현 | d49b6682f5 | 2 years ago |
이준희 | 08db9210d2 | 2 years ago |
sanguu(박상현) | 761b8aa50e | 2 years ago |
sanguu(박상현) | 523ef1cdf0 | 2 years ago |
김장현 | 4d00b8d811 | 2 years ago |
김장현 | 24b7bdeb64 | 2 years ago |
이준희 | fc4d6296ae | 2 years ago |
이준희 | 82e8bcf14d | 2 years ago |
sanguu(박상현) | 9ec6caaac9 | 2 years ago |
sanguu(박상현) | a5c22b3c31 | 2 years ago |
sanguu(박상현) | c6210dcc40 | 2 years ago |
김장현 | 5af1866075 | 2 years ago |
김장현 | bcea695ea8 | 2 years ago |
김장현 | 3c9772c20f | 2 years ago |
김장현 | 4258801c0f | 2 years ago |
김장현 | 043c461a3c | 2 years ago |
이준희 | b3af91ee74 | 2 years ago |
junh_eee(이준희) | 2fece0871c | 2 years ago |
sanguu(박상현) | e2dc230204 | 2 years ago |
sanguu(박상현) | d406c2c966 | 2 years ago |
sanguu(박상현) | ecbc7ae710 | 2 years ago |
노승철 | b4efd1bf37 | 2 years ago |
junh_eee(이준희) | 7327afeeba | 2 years ago |
junh_eee(이준희) | 9b9e2c6cef | 2 years ago |
김지은 | 6f8b4a30ef | 2 years ago |
김지은 | 1c4c5a07b1 | 2 years ago |
sanguu(박상현) | ec0e70bd18 | 2 years ago |
sanguu(박상현) | f9a20bbceb | 2 years ago |
sanguu(박상현) | e86484f495 | 2 years ago |
sanguu(박상현) | 5bcc58761e | 2 years ago |
sanguu(박상현) | 850e78aa11 | 2 years ago |
sanguu(박상현) | 6a73c15d5e | 2 years ago |
김장현 | ec2c6ad83c | 2 years ago |
김장현 | e960209157 | 2 years ago |
sanguu(박상현) | f2d441e6be | 2 years ago |
sanguu(박상현) | 6877a53807 | 2 years ago |
junh_eee(이준희) | cf8aa1f906 | 2 years ago |
junh_eee(이준희) | fdcb6369f9 | 2 years ago |
김장현 | fa1c085340 | 2 years ago |
김장현 | b7c3c492e5 | 2 years ago |
junh_eee(이준희) | b468eae781 | 2 years ago |
junh_eee(이준희) | aa73d25bb9 | 2 years ago |
sanguu(박상현) | e7fbd6a1d5 | 2 years ago |
김장현 | 5caf38cdc1 | 2 years ago |
김장현 | 962b7ce8f0 | 2 years ago |
sanguu(박상현) | d6c3c5d525 | 2 years ago |
김장현 | 96520dc7c7 | 2 years ago |
junh_eee(이준희) | d4e3c38e8b | 2 years ago |
junh_eee(이준희) | 4812ebb941 | 2 years ago |
김지은 | 0b0e99287e | 2 years ago |
junh_eee(이준희) | 2f2ce66637 | 2 years ago |
김장현 | b7705009b4 | 2 years ago |
junh_eee(이준희) | b5749a90e1 | 2 years ago |
junh_eee(이준희) | 41575ea027 | 2 years ago |
junh_eee(이준희) | e4173f1a07 | 2 years ago |
김장현 | eee7bff44e | 2 years ago |
junh_eee(이준희) | 5b01356ddf | 2 years ago |
junh_eee(이준희) | d9bf9e80a5 | 2 years ago |
junh_eee(이준희) | ec1b3dfcfe | 2 years ago |
sanguu516 | cc96fca561 | 2 years ago |
kimjh(김장현) | 9c3d149571 | 2 years ago |
junh_eee(이준희) | 10774342ec | 2 years ago |
junh_eee(이준희) | 08c9d0a863 | 2 years ago |
junh_eee(이준희) | c6c9b62d2a | 2 years ago |
sanguu(박상현) | 918734155d | 2 years ago |
junh_eee(이준희) | 510bd6e6ec | 2 years ago |
junh_eee(이준희) | e1b91129fb | 2 years ago |
junh_eee(이준희) | 1ca0a4fe2b | 2 years ago |
junh_eee(이준희) | 576cc97878 | 2 years ago |
junh_eee(이준희) | 1a4cf2f089 | 2 years ago |
junh_eee(이준희) | 78358f3b79 | 2 years ago |
junh_eee(이준희) | d331e72c60 | 2 years ago |
junh_eee(이준희) | 9b891e458a | 2 years ago |
junh_eee(이준희) | 3107fc2a9d | 2 years ago |
junh_eee(이준희) | c31531ac6a | 2 years ago |
junh_eee(이준희) | 19562bd493 | 2 years ago |
이학준 | 397cd6c87f | 2 years ago |
이학준 | 23c4ca779d | 2 years ago |
sanguu(박상현) | 55284da4b1 | 2 years ago |
sanguu(박상현) | 055537bad9 | 2 years ago |
sanguu(박상현) | ce69940b22 | 2 years ago |
kimjh(김장현) | 2454a34556 | 2 years ago |
sanguu(박상현) | 14f7de6bb3 | 2 years ago |
sanguu(박상현) | 8dd0bf94b7 | 2 years ago |
sanguu(박상현) | ed8f9e0df3 | 2 years ago |
junh_eee(이준희) | 28264d9356 | 2 years ago |
junh_eee(이준희) | 1a9fd70fb0 | 2 years ago |
kimjh(김장현) | c741a9d394 | 2 years ago |
kimjh(김장현) | 6158bfdac5 | 2 years ago |
kimjh(김장현) | 7ece8c8892 | 2 years ago |
junh_eee(이준희) | 341ce941ee | 2 years ago |
junh_eee(이준희) | b07ab6de60 | 2 years ago |
sanguu(박상현) | 32232482d6 | 2 years ago |
sanguu(박상현) | 9911914b1c | 2 years ago |
sanguu(박상현) | 2df1b0c394 | 2 years ago |
sanguu(박상현) | 56c8d0dc1e | 2 years ago |
sanguu(박상현) | d243f9ffcc | 2 years ago |
kimjh(김장현) | 2ec149a740 | 2 years ago |
junh_eee(이준희) | e53a98f4c4 | 2 years ago |
junh_eee(이준희) | f9dc332ec3 | 2 years ago |
junh_eee(이준희) | 3f6cd77b2d | 2 years ago |
junh_eee(이준희) | 28e93c9b40 | 2 years ago |
kimjh(김장현) | 46b67f3fce | 2 years ago |
kimjh(김장현) | a473e81a5f | 2 years ago |
junh_eee(이준희) | 0f193747b9 | 2 years ago |
junh_eee(이준희) | ca4f648a70 | 2 years ago |
kimjh(김장현) | 3f0b4080c5 | 2 years ago |
sanguu(박상현) | bc638fff00 | 2 years ago |
sanguu(박상현) | d2b7c79d0e | 2 years ago |
junh_eee(이준희) | 676c88f170 | 2 years ago |
kimjh(김장현) | 8e8b69e5bc | 2 years ago |
kimjh(김장현) | deea4b0955 | 2 years ago |
sanguu | 377d992bc9 | 2 years ago |
sanguu | bb703cc3cd | 2 years ago |
sanguu | 664cd0d812 | 2 years ago |
sanguu | da76d238b5 | 2 years ago |
junh_eee(이준희) | b525c931ec | 2 years ago |
sanguu | 7685796ca6 | 2 years ago |
sanguu | 52623f0a36 | 2 years ago |
sanguu | b173d7f431 | 2 years ago |
junh_eee(이준희) | 80bddc52c7 | 2 years ago |
junh_eee(이준희) | e29c4f31e6 | 2 years ago |
kimjh(김장현) | 104ce781c8 | 2 years ago |
kimjh(김장현) | 8850cccb0a | 2 years ago |
이학준 | 6d5f989025 | 2 years ago |
김지은 | daef2ba808 | 2 years ago |
김지은 | 4272b3f339 | 2 years ago |
sanguu | d03f26ac25 | 2 years ago |
sanguu | e4fcf02592 | 2 years ago |
junh_eee(이준희) | a53d0c0ee1 | 2 years ago |
junh_eee(이준희) | 15aabbbc97 | 2 years ago |
이학준 | bb7f0ce4cc | 2 years ago |
junh_eee(이준희) | 2fe8ce51b4 | 2 years ago |
sanguu | 7b49dda605 | 2 years ago |
이학준 | 036521062d | 2 years ago |
junh_eee(이준희) | 0d58fa74cb | 2 years ago |
sanguu | b239d854a2 | 2 years ago |
sanguu | 04278d4b62 | 2 years ago |
junh_eee(이준희) | fc77d6aea3 | 2 years ago |
junh_eee(이준희) | 1940d42853 | 2 years ago |
junh_eee(이준희) | 3b9743587c | 2 years ago |
junh_eee(이준희) | bd6d3ca9c7 | 2 years ago |
junh_eee(이준희) | 9309e49d1e | 2 years ago |
junh_eee(이준희) | 377d0b345e | 2 years ago |
junh_eee(이준희) | bb5cee72d6 | 2 years ago |
junh_eee(이준희) | 5b1ab4d77e | 2 years ago |
junh_eee(이준희) | 55b6ce23b3 | 2 years ago |
sanguu | 0e4897234a | 2 years ago |
sanguu | 5051058100 | 2 years ago |
이학준 | 8eaf2d1cea | 2 years ago |
sanguu | 31fdfa8af4 | 2 years ago |
sanguu | 5781e58856 | 2 years ago |
kimjh(김장현) | 8ca4bb6d7f | 2 years ago |
sanguu | 11519948cd | 2 years ago |
sanguu | 5cf240bc14 | 2 years ago |
sanguu | 3a07338bae | 2 years ago |
sanguu | 5bf5f6d88c | 2 years ago |
junh_eee(이준희) | f9f7245691 | 2 years ago |
junh_eee(이준희) | 34f6d688f3 | 2 years ago |
junh_eee(이준희) | 55e1971b6a | 2 years ago |
sanguu | 5f8d17fcb5 | 2 years ago |
sanguu | 56f8f32a9c | 2 years ago |
sanguu | f689d2b8ca | 2 years ago |
junh_eee(이준희) | 6efedefd95 | 2 years ago |
junh_eee(이준희) | acd20600f1 | 2 years ago |
이학준 | eabe7b66c8 | 2 years ago |
이학준 | 8016684803 | 2 years ago |
junh_eee(이준희) | eaa7b6f818 | 2 years ago |
이학준 | 1227ef3f9b | 2 years ago |
junh_eee(이준희) | acb1db2c96 | 2 years ago |
junh_eee(이준희) | d139e76be8 | 2 years ago |
sanguu | 942b2b5033 | 2 years ago |
sanguu | 5109745c3b | 2 years ago |
sanguu | 6b41302ec8 | 2 years ago |
김지은 | c23747b2de | 2 years ago |
김지은 | 3881b84206 | 2 years ago |
kimjh(김장현) | dbae305928 | 2 years ago |
이학준 | d7ff0f5c8a | 2 years ago |
이학준 | 37396c09f0 | 2 years ago |
junh_eee(이준희) | a20eec41cf | 2 years ago |
junh_eee(이준희) | f9483eef6c | 2 years ago |
junh_eee(이준희) | dbc74b9060 | 2 years ago |
이학준 | cf39431e8f | 2 years ago |
이학준 | d8ba3bc2d2 | 2 years ago |
이학준 | c5bfab5e9a | 2 years ago |
sanguu | ab9a040546 | 2 years ago |
sanguu | daa7fc256d | 2 years ago |
이학준 | 1f008393c6 | 2 years ago |
이학준 | d8707546af | 2 years ago |
junh_eee(이준희) | e72d2b7543 | 2 years ago |
junh_eee(이준희) | 12a19d0496 | 2 years ago |
이학준 | 73f7c7c5a6 | 2 years ago |
이학준 | b6b9c4c2fb | 2 years ago |
junh_eee(이준희) | 1cb2169c04 | 2 years ago |
junh_eee(이준희) | e7271ce927 | 2 years ago |
sanguu | ca49af45a3 | 2 years ago |
sanguu | f794f11d0e | 2 years ago |
sanguu | f466a28454 | 2 years ago |
sanguu | 51dc2aa1c6 | 2 years ago |
이학준 | 0f37321946 | 2 years ago |
이학준 | dad9d6c99c | 2 years ago |
sanguu | df0daacbb9 | 2 years ago |
노승철 | ab3148d0d0 | 2 years ago |
노승철 | e000ad9e3d | 2 years ago |
이학준 | b8ed78f225 | 2 years ago |
이학준 | 3d13a41734 | 2 years ago |
노승철 | 6321a8a35c | 2 years ago |
kimjh(김장현) | af0c8d48a8 | 2 years ago |
junh_eee(이준희) | 1339446648 | 2 years ago |
sanguu | 418558e3e8 | 2 years ago |
sanguu | e97b7bf636 | 2 years ago |
sanguu | 1761e16358 | 2 years ago |
노승철 | 986c70b4c7 | 2 years ago |
sanguu | a93aed7a99 | 2 years ago |
sanguu | 084488a379 | 2 years ago |
kimjh(김장현) | 7aefa9da5c | 2 years ago |
junh_eee(이준희) | b56edf7892 | 2 years ago |
이학준 | 00ef030c2c | 2 years ago |
sanguu | 5b025dd6f1 | 2 years ago |
sanguu | 92927ab528 | 2 years ago |
qkr7828(박재우) | 29d32f183a | 2 years ago |
노승철 | 68342ebdf5 | 2 years ago |
kimjh(김장현) | 692301a478 | 2 years ago |
kimjh(김장현) | 3c8929b3c1 | 2 years ago |
sanguu | aeccc0af81 | 2 years ago |
sanguu | f158921a56 | 2 years ago |
junh_eee(이준희) | 007e3970ea | 2 years ago |
junh_eee(이준희) | a8315b6bd2 | 2 years ago |
노승철 | e3eb8fa4a8 | 2 years ago |
junh_eee(이준희) | 9d72100712 | 2 years ago |
노승철 | dc7ff52dc5 | 2 years ago |
sanguu | 5f5f4c1715 | 2 years ago |
이학준 | 0f192ed55a | 2 years ago |
kimjh(김장현) | 19f316ad88 | 2 years ago |
kimjh(김장현) | 4536d9445d | 2 years ago |
이학준 | d3e8c470f5 | 2 years ago |
kimjh(김장현) | d63b598b6e | 2 years ago |
junh_eee(이준희) | 82f3910f33 | 2 years ago |
junh_eee(이준희) | b11d8b6b33 | 2 years ago |
kimjh(김장현) | dd349bdf85 | 2 years ago |
노승철 | 017b1590ab | 2 years ago |
junh_eee(이준희) | 09c2615f8c | 2 years ago |
kimjh(김장현) | 3950e2f388 | 2 years ago |
sanguu | df2a65b096 | 2 years ago |
sanguu | 9c92ae6d72 | 2 years ago |
kimjh(김장현) | a803bf3d92 | 2 years ago |
김지은 | db4559636c | 2 years ago |
김지은 | eeec5d3ea9 | 2 years ago |
junh_eee(이준희) | 3f18266da1 | 2 years ago |
sanguu | a1cb57a5a8 | 2 years ago |
sanguu | 6cc80c321e | 2 years ago |
노승철 | 59c6213a1a | 2 years ago |
노승철 | 65febcda13 | 2 years ago |
junh_eee(이준희) | f568021390 | 2 years ago |
junh_eee(이준희) | adb9cccdf6 | 2 years ago |
이학준 | b6cfc6c471 | 2 years ago |
kimjh(김장현) | 233de2a08d | 2 years ago |
junh_eee(이준희) | 55a8ed1035 | 2 years ago |
sanguu | ef4b84d943 | 2 years ago |
sanguu | f024bc5542 | 2 years ago |
kimjh(김장현) | 7a9de5d68d | 2 years ago |
sanguu | 7c12d07654 | 2 years ago |
sanguu | c7bc82c6cc | 2 years ago |
junh_eee(이준희) | ff90ec7a15 | 2 years ago |
sanguu | c4366f82df | 2 years ago |
sanguu | 165b07b10e | 2 years ago |
sanguu | 08fa07dbed | 2 years ago |
김지은 | c9d16bdf36 | 2 years ago |
김지은 | 6c6104590c | 2 years ago |
김지은 | 8cd7ccfcbe | 2 years ago |
junh_eee(이준희) | 2311d2e857 | 2 years ago |
junh_eee(이준희) | 96775e8bc0 | 2 years ago |
junh_eee(이준희) | bc210c8740 | 2 years ago |
junh_eee(이준희) | a46f0dc2b7 | 2 years ago |
sanguu | af32b6d60b | 2 years ago |
sanguu | 6c8ed958f4 | 2 years ago |
junh_eee(이준희) | 560404a80b | 2 years ago |
junh_eee(이준희) | 02831824e9 | 2 years ago |
sanguu | cfad9d66ce | 2 years ago |
sanguu | 74f7bc6d7d | 2 years ago |
sanguu | 2bd1ca6877 | 2 years ago |
sanguu | 55df2a6b8e | 2 years ago |
junh_eee(이준희) | 500ff2426b | 2 years ago |
junh_eee(이준희) | 27a7409f39 | 2 years ago |
kimjh(김장현) | 58562a3b45 | 2 years ago |
kimjh(김장현) | 4cf6533571 | 2 years ago |
노승철 | ef8d22f43d | 2 years ago |
노승철 | e50c143c83 | 2 years ago |
kimjh(김장현) | de3ea4350d | 2 years ago |
kimjh(김장현) | 5caa6d8702 | 2 years ago |
kimjh(김장현) | 27da351ca5 | 2 years ago |
sanguu | 7b04365e79 | 2 years ago |
junh_eee(이준희) | 028d503d93 | 2 years ago |
junh_eee(이준희) | 83d2cb7019 | 2 years ago |
김지은 | c4680bc62f | 2 years ago |
김지은 | 84570e91b9 | 2 years ago |
junh_eee(이준희) | dea899820a | 2 years ago |
junh_eee(이준희) | 518437ca87 | 2 years ago |
sanguu | af7357690b | 2 years ago |
sanguu | de6a83c526 | 2 years ago |
김장현 | ffc7ed7d2d | 2 years ago |
junh_eee(이준희) | f134b8c1b2 | 2 years ago |
junh_eee(이준희) | 55c76b66d4 | 2 years ago |
junh_eee(이준희) | c2e680ecae | 2 years ago |
junh_eee(이준희) | f408817704 | 2 years ago |
sanguu | ad389eab4b | 2 years ago |
sanguu | 996883b2e8 | 2 years ago |
sanguu | 70e294e674 | 2 years ago |
junh_eee(이준희) | b0dbbcf727 | 2 years ago |
김지은 | bf28d909f6 | 2 years ago |
김지은 | 543c771bd8 | 2 years ago |
김장현 | 40e229e76c | 2 years ago |
sanguu | d6c9f0bfca | 2 years ago |
sanguu | 42e597d006 | 2 years ago |
junh_eee | 54bf26fd2f | 2 years ago |
junh_eee | 0833f63b5c | 2 years ago |
junh_eee | 8124409aa7 | 2 years ago |
노승철 | 68c6f8e2d6 | 2 years ago |
노승철 | 6689844381 | 2 years ago |
노승철 | 782fe28950 | 2 years ago |
김지은 | 664162ab94 | 2 years ago |
김지은 | c16ce6fc36 | 2 years ago |
sanguu | 807b3badbd | 2 years ago |
sanguu | 78ae4def1d | 2 years ago |
sanguu | 54833333c3 | 2 years ago |
junh_eee | 41daf50064 | 2 years ago |
노승철 | bd2e38e32d | 2 years ago |
sanguu | e077c84a16 | 2 years ago |
sanguu | cf97cad28c | 2 years ago |
sanguu | 02f0d37540 | 2 years ago |
sanguu | 3114c1c6f1 | 2 years ago |
junh_eee | c72ba8aeb0 | 2 years ago |
junh_eee | d44de2fdc8 | 2 years ago |
sanguu | d1e329d6c5 | 2 years ago |
sanguu | f515687a4e | 2 years ago |
junh_eee | 866cab0cb8 | 2 years ago |
junh_eee | aa5010a8b2 | 2 years ago |
김장현 | 11544552b1 | 2 years ago |
김장현 | 896cd88753 | 2 years ago |
이학준 | 67daac7d2d | 2 years ago |
이학준 | d78fed8bf5 | 2 years ago |
김장현 | 2d1e6e73e7 | 2 years ago |
junh_eee | 213912fb52 | 2 years ago |
junh_eee | f860874ca4 | 2 years ago |
김장현 | e77e7c08ba | 2 years ago |
김장현 | b57cbe7dae | 2 years ago |
junh_eee | 55f4224989 | 2 years ago |
sanguu | 44c24a1ad2 | 2 years ago |
sanguu | 499334d4b8 | 2 years ago |
김장현 | 4387559ba7 | 2 years ago |
junh_eee | add80872f1 | 2 years ago |
junh_eee | 998abfede5 | 2 years ago |
junh_eee | 8d82d9b999 | 2 years ago |
junh_eee | 31251e6e94 | 2 years ago |
김장현 | 7bc37488b0 | 2 years ago |
sanguu | 3afc1316fb | 2 years ago |
sanguu | 411f575fce | 2 years ago |
김장현 | af8b0cebb4 | 2 years ago |
이학준 | b180f4be82 | 2 years ago |
이학준 | 67d3ab9e9d | 2 years ago |
sanguu | ece33747a2 | 2 years ago |
sanguu | c5491bf0c3 | 2 years ago |
sanguu | 029a0d530f | 2 years ago |
이학준 | 93bac9a2ee | 2 years ago |
sanguu | 141ffc7f58 | 2 years ago |
sanguu | 5112ea5bf6 | 2 years ago |
김장현 | 19f51348c1 | 2 years ago |
김장현 | 9514eebc7a | 2 years ago |
sanguu | 69ce35a54d | 2 years ago |
junh_eee | e0bdf03455 | 2 years ago |
이학준 | 271d8b8157 | 2 years ago |
김장현 | f978ff57ce | 2 years ago |
sanguu | 39228fbacc | 2 years ago |
sanguu | c56e68d705 | 2 years ago |
김장현 | a8a74f7ad6 | 2 years ago |
junh_eee | 47a7560050 | 2 years ago |
김장현 | 38b58e3760 | 2 years ago |
sanguu | 8a9de8b5ec | 2 years ago |
이학준 | df88bb3e75 | 2 years ago |
junh_eee | b2a0ac97cd | 2 years ago |
junh_eee | 1f40cf01ad | 2 years ago |
junh_eee | cefa35620e | 2 years ago |
sanguu | efbc1b21b1 | 2 years ago |
sanguu | 27c7cbda39 | 2 years ago |
qkr7828(박재우) | d87e1f4289 | 2 years ago |
노승철 | bb0c718b81 | 2 years ago |
노승철 | 5f60400824 | 2 years ago |
이학준 | b7e66aac92 | 2 years ago |
junh_eee | 87c36227b2 | 2 years ago |
이학준 | ecb58a8cb6 | 2 years ago |
김장현 | f107e0d0b7 | 2 years ago |
김장현 | 15135966af | 2 years ago |
김장현 | 027d2aa041 | 2 years ago |
노승철 | 08d3b79d22 | 2 years ago |
노승철 | 594ac97525 | 2 years ago |
김장현 | 2e1cfb2991 | 2 years ago |
이학준 | 2fb5078733 | 2 years ago |
김장현 | 86418f7147 | 2 years ago |
junh_eee | 0a30a6261a | 2 years ago |
junh_eee | 6c36bde827 | 2 years ago |
junh_eee | 01b9c76533 | 2 years ago |
junh_eee | 070c9eec76 | 2 years ago |
junh_eee | d5aabdceff | 2 years ago |
junh_eee | c675fd8b8b | 2 years ago |
junh_eee | d79a91a756 | 2 years ago |
sanguu | 188e42f830 | 2 years ago |
junh_eee | b550025ad6 | 2 years ago |
junh_eee | 07dfda5dee | 2 years ago |
지대한 | 3e8f4de6f9 | 2 years ago |
노승철 | cf1605994a | 2 years ago |
이학준 | 16a383af7c | 2 years ago |
sanguu | 273152193c | 2 years ago |
이학준 | 5b7703d1e1 | 2 years ago |
이학준 | 410ac8c14c | 2 years ago |
junh_eee | 36dd00b7c1 | 2 years ago |
sanguu | ccd26b0fa6 | 2 years ago |
sanguu | 896c88d972 | 2 years ago |
junh_eee | 7c2323aa1d | 2 years ago |
junh_eee | 31cf8c7369 | 2 years ago |
qkr7828(박재우) | 5bd0ab93f4 | 2 years ago |
qkr7828(박재우) | ea7903a0d2 | 2 years ago |
qkr7828(박재우) | f930f85b86 | 2 years ago |
junh_eee | a24dc74a0a | 2 years ago |
김지은 | dbd2a164ac | 2 years ago |
김지은 | 2fd750ba65 | 2 years ago |
qkr7828(박재우) | 7dc1c60e4f | 2 years ago |
이학준 | 80f4d4e5f9 | 2 years ago |
이학준 | 6ec3f2100a | 2 years ago |
이학준 | 7d07f2a42b | 2 years ago |
이학준 | 4c4e98772c | 2 years ago |
qkr7828(박재우) | 13b282be13 | 2 years ago |
qkr7828(박재우) | 0ef03ea5ba | 2 years ago |
qkr7828(박재우) | bb4273c47f | 2 years ago |
이학준 | 90d0b28bed | 2 years ago |
junh_eee | cbd27bc72c | 2 years ago |
junh_eee | 018305e4e8 | 2 years ago |
junh_eee | f099da397d | 2 years ago |
이학준 | 4c3b253156 | 2 years ago |
이학준 | 22a80784d4 | 2 years ago |
노승철 | 65fa74cd0a | 2 years ago |
junh_eee | 0734a0c5a8 | 2 years ago |
sanguu | ea99b9fae9 | 2 years ago |
sanguu | 9f18339f9c | 2 years ago |
이학준 | 38c79891e9 | 2 years ago |
sanguu | b78be0e6bf | 2 years ago |
sanguu | 6dfb068fe2 | 2 years ago |
junh_eee | 1f6e565c22 | 2 years ago |
junh_eee | a8e05fd7e8 | 2 years ago |
junh_eee | febd1c49da | 2 years ago |
이학준 | 3b605f92b0 | 2 years ago |
노승철 | 46119a9b85 | 2 years ago |
이학준 | 6207984c23 | 2 years ago |
sanguu | e26a5ffe41 | 2 years ago |
이학준 | 2ba501fe8d | 2 years ago |
노승철 | 1944891357 | 2 years ago |
sanguu | 5229b2f50d | 2 years ago |
이학준 | 04f45676a2 | 2 years ago |
이학준 | 0c6dec7453 | 2 years ago |
이학준 | 655d2ddae7 | 2 years ago |
노승철 | 5d3f539b67 | 2 years ago |
이학준 | d8fbc13b84 | 2 years ago |
이학준 | f0985be945 | 2 years ago |
sanguu | 58982bb786 | 2 years ago |
노승철 | 0608588f33 | 2 years ago |
노승철 | d83648f51b | 2 years ago |
junh_eee | 2d433a5985 | 2 years ago |
junh_eee | a863528473 | 2 years ago |
sanguu | 2954517ed1 | 2 years ago |
김지은 | 7256cae224 | 2 years ago |
qkr7828(박재우) | ee44f99271 | 2 years ago |
qkr7828(박재우) | 171fcce7e7 | 2 years ago |
qkr7828(박재우) | c3ffdecbe1 | 2 years ago |
qkr7828(박재우) | d8231de8e5 | 2 years ago |
junh_eee | dfeaf9bf71 | 2 years ago |
노승철 | 1568d86f31 | 2 years ago |
qkr7828(박재우) | 624ea69593 | 2 years ago |
junh_eee | 6dd56e451f | 2 years ago |
junh_eee | 71834edf41 | 2 years ago |
junh_eee | af8d907cb0 | 2 years ago |
노승철 | 5b90f465f2 | 2 years ago |
junh_eee | 177b895294 | 2 years ago |
김지은 | 2a2e3e1e55 | 2 years ago |
김지은 | 25a4665621 | 2 years ago |
sanguu | 22de8668d6 | 2 years ago |
sanguu | f077600239 | 2 years ago |
junh_eee | f5ed16ab8f | 2 years ago |
sanguu | f4e4c7e8a4 | 2 years ago |
sanguu | 05bcd217fc | 2 years ago |
김지은 | 03efeb3393 | 2 years ago |
김지은 | 23e18b5535 | 2 years ago |
sanguu | 33e23bb63f | 2 years ago |
sanguu | add3171194 | 2 years ago |
이학준 | 32c1ce4fc8 | 2 years ago |
sanguu | 1ffe1edf8f | 2 years ago |
이학준 | 9c557764ec | 2 years ago |
sanguu | 34480e6a9a | 2 years ago |
junh_eee | 579aafc1fd | 2 years ago |
junh_eee | c97a6d8386 | 2 years ago |
junh_eee | b866bbfda1 | 2 years ago |
노승철 | 15bf3b8458 | 2 years ago |
노승철 | 8fbc78a2a4 | 2 years ago |
sanguu | 20909f89f6 | 2 years ago |
이학준 | baecc69ddb | 2 years ago |
junh_eee | cc3043d5df | 2 years ago |
junh_eee | 2c44da2035 | 2 years ago |
junh_eee | 175785a4b8 | 2 years ago |
이학준 | e7cf5dc304 | 2 years ago |
이학준 | 8ad1baffc9 | 2 years ago |
junh_eee | 55137ef50f | 2 years ago |
junh_eee | ef90d79e62 | 2 years ago |
junh_eee | b37e1541f1 | 2 years ago |
sanguu | b2651f24c9 | 2 years ago |
이학준 | b68e492dce | 2 years ago |
이학준 | 1e5c460798 | 2 years ago |
이학준 | ff75f29ca6 | 2 years ago |
junh_eee | df8b02d245 | 2 years ago |
junh_eee | e5b7f88a4e | 2 years ago |
노승철 | f1702ed0e1 | 2 years ago |
노승철 | f54a728b4d | 2 years ago |
junh_eee | 113ea9e4b4 | 2 years ago |
junh_eee | 13a5925776 | 2 years ago |
junh_eee | 24bcaa1eb8 | 2 years ago |
sanguu | da62397996 | 2 years ago |
sanguu | 08471a29e9 | 2 years ago |
junh_eee | c1ae711fcb | 2 years ago |
junh_eee | 53db791739 | 2 years ago |
김지은 | c0377ce151 | 2 years ago |
김지은 | c671d07a39 | 2 years ago |
junh_eee(이준희) | 2e4ce5c265 | 2 years ago |
sanguu | 5a73f05b78 | 2 years ago |
sanguu | eda5624a31 | 2 years ago |
sanguu | 511892feb6 | 2 years ago |
노승철 | a5f45373be | 2 years ago |
sanguu | 24aebfb0c8 | 2 years ago |
sanguu | b17e9d0023 | 2 years ago |
김지은 | 18432acfd7 | 2 years ago |
김지은 | cf0a655782 | 2 years ago |
sanguu | bcb0bd70c8 | 2 years ago |
sanguu | 7ae9857197 | 2 years ago |
junh_eee(이준희) | d704986d08 | 2 years ago |
junh_eee(이준희) | 1b54296044 | 2 years ago |
junh_eee(이준희) | eb6cc2e017 | 2 years ago |
노승철 | 43f60ebc86 | 2 years ago |
노승철 | 726904e591 | 2 years ago |
노승철 | 34c3fe8d37 | 2 years ago |
junh_eee(이준희) | 089d8a2ab3 | 2 years ago |
junh_eee(이준희) | 13576be1f6 | 2 years ago |
sanguu | f2208c0d84 | 2 years ago |
sanguu | 921298a2c8 | 2 years ago |
노승철 | fb45ed1e64 | 2 years ago |
노승철 | 32239fd1f5 | 2 years ago |
junh_eee(이준희) | 56d13d79fb | 2 years ago |
junh_eee(이준희) | c785763167 | 2 years ago |
junh_eee(이준희) | 75c29bb3c9 | 2 years ago |
sanguu | 123f89dd9b | 2 years ago |
sanguu | 0c08f2ca77 | 2 years ago |
이학준 | 634dbc9941 | 2 years ago |
sanguu | ba326dc76c | 2 years ago |
junh_eee(이준희) | 5dd8542aba | 2 years ago |
노승철 | 9c672e8262 | 2 years ago |
junh_eee(이준희) | d93b5b45d2 | 2 years ago |
junh_eee(이준희) | 89da1d0767 | 2 years ago |
노승철 | 0d8da961e0 | 2 years ago |
junh_eee(이준희) | 2b4f6799ba | 2 years ago |
이학준 | 26e5c69511 | 2 years ago |
이학준 | cbb3865981 | 2 years ago |
이학준 | 4005975317 | 2 years ago |
junh_eee(이준희) | ace5b2fe8e | 2 years ago |
junh_eee(이준희) | 9fd3d0cfb2 | 2 years ago |
junh_eee(이준희) | 3384ac5758 | 2 years ago |
지대한 | 7a540fdd24 | 2 years ago |
junh_eee(이준희) | 094e8d0b6e | 2 years ago |
junh_eee(이준희) | 416621f7b5 | 2 years ago |
노승철 | 7e47bba166 | 2 years ago |
junh_eee(이준희) | 062f9f8403 | 2 years ago |
junh_eee(이준희) | 0a8160af3f | 2 years ago |
김지은 | aa577d54af | 2 years ago |
김지은 | 4f61d66ff8 | 2 years ago |
김지은 | 5960c39f05 | 2 years ago |
김지은 | d31f901028 | 2 years ago |
김지은 | 97d19baa42 | 2 years ago |
노승철 | 15a6459ce6 | 2 years ago |
노승철 | f66335f6e5 | 2 years ago |
sanguu | ee9f1c82a1 | 2 years ago |
sanguu | 9dba7752e8 | 2 years ago |
노승철 | a78e5ccc04 | 2 years ago |
노승철 | afc35200a9 | 2 years ago |
노승철 | c0c2f14eaa | 2 years ago |
junh_eee(이준희) | fce8356398 | 2 years ago |
junh_eee(이준희) | dd318874d9 | 2 years ago |
이학준 | 3978b0f4c2 | 2 years ago |
이학준 | 5c01500b19 | 2 years ago |
sanguu | 9dbc07e11f | 2 years ago |
sanguu | 5415a36754 | 2 years ago |
junh_eee(이준희) | 218decdb3f | 2 years ago |
노승철 | 6abe69bc54 | 2 years ago |
노승철 | dfd96b1e36 | 2 years ago |
sanguu | 76309ab08d | 2 years ago |
노승철 | 37fc28ac07 | 2 years ago |
노승철 | 1c0d344aaa | 2 years ago |
노승철 | a751b15dcd | 2 years ago |
qkr7828(박재우) | df14112e48 | 2 years ago |
qkr7828(박재우) | 4d8839c888 | 2 years ago |
노승철 | 8c07769e76 | 2 years ago |
노승철 | 345139f44c | 2 years ago |
qkr7828(박재우) | a04a10a79c | 2 years ago |
qkr7828(박재우) | 0483a4950a | 2 years ago |
qkr7828(박재우) | efd968e718 | 2 years ago |
qkr7828(박재우) | aa6b739657 | 2 years ago |
qkr7828(박재우) | 0580ddea21 | 2 years ago |
junh_eee(이준희) | d78111716c | 2 years ago |
junh_eee(이준희) | 0201ad9c80 | 2 years ago |
qkr7828(박재우) | 8eb9e0112f | 2 years ago |
qkr7828(박재우) | d7ddc505eb | 2 years ago |
지대한 | 01b96dbd22 | 2 years ago |
지대한 | b7cbea9fc5 | 2 years ago |
지대한 | 3240e863d5 | 2 years ago |
노승철 | e737ba5eae | 2 years ago |
노승철 | f0c56c5b24 | 2 years ago |
junh_eee(이준희) | 9cf985a5c7 | 2 years ago |
junh_eee(이준희) | c41fc9641a | 2 years ago |
노승철 | 088ce6a265 | 2 years ago |
노승철 | 4065868d43 | 2 years ago |
junh_eee(이준희) | c2a9394725 | 2 years ago |
sanguu | bf835e03cc | 2 years ago |
sanguu | 2f493bee77 | 2 years ago |
sanguu | 8e811ef843 | 2 years ago |
junh_eee(이준희) | c025fc37fd | 2 years ago |
노승철 | 17ad69c591 | 2 years ago |
노승철 | e169cdb4d6 | 2 years ago |
노승철 | 377bb78479 | 2 years ago |
junh_eee(이준희) | 271e567280 | 2 years ago |
노승철 | 6dbc64678c | 2 years ago |
노승철 | aaca772b46 | 2 years ago |
노승철 | 203ca30607 | 2 years ago |
노승철 | bc2a22fffb | 2 years ago |
노승철 | 62d7bbb0a8 | 2 years ago |
노승철 | 9f444eb962 | 2 years ago |
노승철 | f1203f217a | 2 years ago |
노승철 | 911bec8583 | 2 years ago |
노승철 | 51a5d5f61c | 2 years ago |
노승철 | 831eac7bc3 | 2 years ago |
노승철 | 95394c60cc | 2 years ago |
노승철 | 4e846ae075 | 2 years ago |
노승철 | af230f9df6 | 2 years ago |
노승철 | 0e548907cc | 2 years ago |
지대한 | 9f86f9b172 | 2 years ago |
노승철 | 34566fab96 | 2 years ago |
노승철 | 02828e33e5 | 2 years ago |
지대한 | 1b453cb829 | 2 years ago |
qkr7828(박재우) | 4c41fc5203 | 2 years ago |
qkr7828(박재우) | ffb4d14ed7 | 2 years ago |
qkr7828(박재우) | 47ad91a0a5 | 2 years ago |
@ -1,3 +1,20 @@
|
||||
REACT_APP_HOST = http://localhost:8080/ |
||||
REACT_APP_WS_HOST = ws://localhost:8081/ws |
||||
# REACT_APP_HOST = http://192.168.0.30:8080/ |
||||
# REACT_APP_WS_HOST = ws://192.168.0.30:8081/ws |
||||
|
||||
# REACT_APP_IMAGE_HOST = https://palnet-file.s3.ap-northeast-2.amazonaws.com/ |
||||
|
||||
REACT_APP_HOST = http://pav.palntour.com:8080/ |
||||
REACT_APP_WS_HOST = ws://pav.palntour.com:8081/ws |
||||
REACT_APP_IMAGE_HOST = https://palnet-file.s3.ap-northeast-2.amazonaws.com/ |
||||
|
||||
# Naver Search API HOST |
||||
NAVER_SEARCH_API_HOST = https://openapi.naver.com/v1/search/local.json |
||||
|
||||
#windy API KEY |
||||
REACT_WINDY_KEY = wf6Lkn1MAtqcgyW78xbceFHBz6ccsUo8 |
||||
|
||||
# API KEY |
||||
NAVER_APP_CLIENT_KEY = WGEct3bJhQC0pyMsP_GK |
||||
NAVER_APP_SECRET_KEY = Q4K4OtUYol |
||||
|
||||
REACT_APP_MAPBOX_TOKEN = pk.eyJ1IjoianVuaGVlZSIsImEiOiJjbGxnNWVhc3IweDJsM2dvYmI1ZXg2MGljIn0.EmSS1ocpPJv2ZaduQHmz_Q |
||||
|
@ -1,3 +1,14 @@
|
||||
REACT_APP_HOST = http://control-api.palntour.com/ |
||||
REACT_APP_WS_HOST = ws://control-websocket.palntour.com/ws |
||||
REACT_APP_HOST = http://pav.palntour.com:8080/ |
||||
REACT_APP_WS_HOST = ws://pav.palntour.com:8081/ws |
||||
REACT_APP_IMAGE_HOST = https://palnet-file.s3.ap-northeast-2.amazonaws.com/ |
||||
|
||||
# Naver Search API HOST |
||||
NAVER_SEARCH_API_HOST = https://openapi.naver.com/v1/search/local.json |
||||
|
||||
#windy API KEY |
||||
REACT_WINDY_KEY = 8ynJ7tneZjmRxLmHQPGaDPGsGabgQhkC |
||||
|
||||
# API KEY |
||||
NAVER_APP_CLIENT_KEY = WGEct3bJhQC0pyMsP_GK |
||||
NAVER_APP_SECRET_KEY = Q4K4OtUYol |
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 15 KiB |
@ -1,6 +1,6 @@
|
||||
// ** Router Import
|
||||
import Router from './router/Router' |
||||
import Router from './router/Router'; |
||||
if (process.env.NODE_ENV === 'production') console.log = function () {}; |
||||
const App = props => <Router />; |
||||
|
||||
const App = props => <Router /> |
||||
|
||||
export default App |
||||
export default App; |
||||
|
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 459 KiB |
@ -0,0 +1,512 @@
|
||||
import { useState, useEffect } from 'react'; |
||||
import { useSelector, useDispatch } from 'react-redux'; |
||||
import { |
||||
InputGroup, |
||||
InputGroupAddon, |
||||
InputGroupText, |
||||
Col, |
||||
TabPane, |
||||
FormGroup, |
||||
Input, |
||||
Button, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter |
||||
} from 'reactstrap'; |
||||
import { User, Lock } from 'react-feather'; |
||||
import '../../../assets/css/custom.css'; |
||||
import { findAPI } from '../../../modules/account/find/apis/findApi'; |
||||
import { accountApi } from '../../../modules/account/register/apis/accountApi'; |
||||
import { updatePwAction } from '../../../modules/account/find/actions/findAction'; |
||||
|
||||
export const AccountFindPassword = props => { |
||||
const dispatch = useDispatch(); |
||||
const [activeTab, setActiveTab] = useState('2'); |
||||
|
||||
// ** Function to toggle tabs
|
||||
const toggle = tab => setActiveTab(tab); |
||||
//modal
|
||||
const [confirmModal, setConfirmModal] = useState(false); |
||||
const [saveModal, setSaveModal] = useState(false); |
||||
const [modal, setModal] = useState({ |
||||
isOpen: false, |
||||
title: '', |
||||
desc: '', |
||||
color: '' |
||||
}); |
||||
|
||||
//state값
|
||||
const { idResult, userId, pwResult, udResult } = useSelector( |
||||
state => state.findState |
||||
); |
||||
|
||||
//param으로 넘기기 위한 값
|
||||
const [inputId, setInputId] = useState(''); |
||||
const [inputHpno, setInputHpno] = useState(''); |
||||
const [inputCrtfy, setInputCrtfy] = useState(''); |
||||
const [inputNewPw, setInputNewPw] = useState(''); |
||||
const [inputNewPwCk, setInputNewPwCk] = useState(''); |
||||
|
||||
//3분 시간 및 인증
|
||||
const [minutes_Counter, setMinutes_Counter] = useState('03'); |
||||
const [seconds_Counter, setSeconds_Counter] = useState('00'); |
||||
const [timer, setTimer] = useState(null); |
||||
const [isRunning, setIsRunning] = useState(false); |
||||
const [sendCount, setSendCount] = useState(0); |
||||
const [isCrtfy, setIsCrtfy] = useState(false); |
||||
|
||||
useEffect(() => { |
||||
setIsRunning(true); |
||||
if (isRunning) clearInterval(timer); |
||||
return () => { |
||||
clearInterval(timer); |
||||
setIsRunning(false); |
||||
}; |
||||
}, []); |
||||
|
||||
const handlerSend = async () => { |
||||
if (!inputId) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '아이디를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputHpno) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '휴대폰 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (sendCount >= 3) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '인증번호가 발송은 3회까지만 가능합니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
if (inputHpno.length < 11) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '올바른 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
const res = await findAPI.sendForPw({ userId: inputId, hpno: inputHpno }); |
||||
if (res?.data.code === -1) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '가입되지 않은 회원정보입니다. 다시 확인해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
} else if (res?.data.code === 0) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '인증번호가 발송되었습니다.', |
||||
color: 'modal-primary' |
||||
}); |
||||
timeStart(); |
||||
} |
||||
}; |
||||
|
||||
const timeStart = () => { |
||||
if (isRunning) { |
||||
clearInterval(timer); |
||||
} |
||||
|
||||
setIsRunning(true); |
||||
setIsCrtfy(false); |
||||
|
||||
setSendCount(sendCount + 1); |
||||
timerStart(180); |
||||
}; |
||||
|
||||
const timerStart = count => { |
||||
let minutes, seconds; |
||||
|
||||
const timer = setInterval(() => { |
||||
setIsRunning(true); |
||||
minutes = parseInt(count / 60, 10); |
||||
seconds = parseInt(count % 60, 10); |
||||
minutes = minutes < 10 ? '0' + minutes : minutes; |
||||
seconds = seconds < 10 ? '0' + seconds : seconds; |
||||
setMinutes_Counter(minutes); |
||||
setSeconds_Counter(seconds); |
||||
|
||||
//타이머 끝
|
||||
if (--count < 0) { |
||||
clearInterval(timer); |
||||
setIsRunning(false); |
||||
// setValue('crtfyNo', '');
|
||||
// setCrtfyNo('');
|
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 만료', |
||||
desc: '인증번호가 만료되었습니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
} |
||||
}, 1000); |
||||
|
||||
setTimer(timer); |
||||
|
||||
return () => { |
||||
clearInterval(timer); |
||||
}; |
||||
}; |
||||
|
||||
const handlerConfirm = async () => { |
||||
if (!inputId) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '아이디를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputHpno) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '휴대폰 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputCrtfy) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
const res = await accountApi.crtfyhpConfirm(inputHpno, inputCrtfy); |
||||
if (!res.data.result) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증번호가 잘못되었습니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} else { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증되었습니다.', |
||||
color: 'modal-primary' |
||||
}); |
||||
setIsCrtfy(true); |
||||
setIsRunning(false); |
||||
} |
||||
}; |
||||
|
||||
const handlerUpdatePw = async () => { |
||||
if (!inputNewPw || !inputNewPwCk) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '비밀번호 변경', |
||||
desc: '비밀번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
const reg = |
||||
/^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[@$!%*#?&])[A-Za-z0-9@$!%*#?&]{8,20}$/; |
||||
if (!reg.test(inputNewPw) || !reg.test(inputNewPwCk)) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '비밀번호 변경', |
||||
desc: '8자 이상, 20자 미만 영문자/숫자/특수문자(@$!%*#?&) 조합하여 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (inputNewPw !== inputNewPwCk) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '비밀번호 변경', |
||||
desc: '비밀번호가 일치하지 않습니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (inputNewPw === inputNewPwCk && inputNewPw && inputNewPwCk) { |
||||
dispatch( |
||||
updatePwAction.request({ |
||||
userId: inputId, |
||||
hpno: inputHpno, |
||||
newPw: inputNewPw |
||||
}) |
||||
); |
||||
setSaveModal(!saveModal); |
||||
} |
||||
}; |
||||
|
||||
let id = ''; |
||||
let hpno = ''; |
||||
let crtfyhp = ''; |
||||
let newpw = ''; |
||||
let newpwck = ''; |
||||
const handlerChange = e => { |
||||
const { name, value } = e.target; |
||||
|
||||
if (name == 'userId') { |
||||
id = value; |
||||
setInputId(id); |
||||
} else if (name == 'hpno') { |
||||
const regex = /^[0-9]{0,11}$/; |
||||
if (regex.test(value)) { |
||||
hpno = value; |
||||
setInputHpno(hpno); |
||||
} |
||||
} else if (name == 'crtfyhpNo') { |
||||
const regex = /^[0-9]{0,6}$/; |
||||
if (regex.test(value)) { |
||||
crtfyhp = value; |
||||
setInputCrtfy(crtfyhp); |
||||
} |
||||
} else if (name == 'newPw') { |
||||
const regex = /^[A-Za-z0-9@$!%*#?&]{0,20}$/; |
||||
if (regex.test(value)) { |
||||
newpw = value; |
||||
setInputNewPw(newpw); |
||||
} |
||||
} else if (name == 'newPwCk') { |
||||
const regex = /^[A-Za-z0-9@$!%*#?&]{0,20}$/; |
||||
if (regex.test(value)) { |
||||
newpwck = value; |
||||
setInputNewPwCk(newpwck); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// const handlerClose = () => {
|
||||
// setModal({ ...modal, isOpen: !modal.isOpen });
|
||||
// setConfirmModal({ ...confirmModal, isOpen: !confirmModal.isOpen});
|
||||
// props.handlerClose();
|
||||
// }
|
||||
|
||||
return ( |
||||
<> |
||||
<TabPane tabId='2'> |
||||
<FormGroup className='form-label-group position-relative has-icon-left'> |
||||
<InputGroup className=''> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<User size={14} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='text' |
||||
id='userId' |
||||
name='userId' |
||||
placeholder='아이디' |
||||
value={inputId} |
||||
onChange={handlerChange} |
||||
/> |
||||
</InputGroup> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<div className='input-btn'> |
||||
<Col md='3' xs='12'> |
||||
<Input type='select' placeholder='+(국가번호)'> |
||||
{/* <option>+82</option> */} |
||||
<option value={'+82'}>대한민국(+82)</option> |
||||
<option value={'+81'}>일본(+81)</option> |
||||
<option value={'+86'}>중국(+86)</option> |
||||
</Input> |
||||
</Col> |
||||
<Col md='6' xs='12'> |
||||
<Input |
||||
type='number' |
||||
id='hpno' |
||||
name='hpno' |
||||
placeholder='01012345678' |
||||
value={inputHpno} |
||||
onChange={handlerChange} |
||||
/> |
||||
</Col> |
||||
<Col md='3' xs='12'> |
||||
<Button color='primary' type='button' onClick={handlerSend}> |
||||
인증번호 발송 |
||||
</Button> |
||||
</Col> |
||||
</div> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<div className='input-btn time-span'> |
||||
<Col md='9' xs='12' className='timeInput'> |
||||
<Input |
||||
type='number' |
||||
id='crtfyhpNo' |
||||
name='crtfyhpNo' |
||||
placeholder='인증번호 입력' |
||||
value={inputCrtfy} |
||||
onChange={handlerChange} |
||||
{...(sendCount > 0 ? {} : { disabled: true })} |
||||
/> |
||||
{/* <span className='time'>남은시간 : 3:00</span> */} |
||||
<span className={!isRunning || isCrtfy ? 'time d-none' : 'time'}> |
||||
남은시간 {minutes_Counter}:{seconds_Counter} |
||||
</span> |
||||
</Col> |
||||
<Col md='3' xs='12'> |
||||
<Button |
||||
color='primary' |
||||
type='button' |
||||
onClick={handlerConfirm} |
||||
{...(isRunning ? {} : { disabled: true })} |
||||
> |
||||
인증번호 확인 |
||||
</Button> |
||||
</Col> |
||||
</div> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<div className='full-btn-2n vertically-centered-modal'> |
||||
{/* <div className='full-btn-2n vertically-centered-confirmModal'> */} |
||||
<Button |
||||
color='secondary' |
||||
type='button' |
||||
onClick={props.handlerClose} |
||||
> |
||||
취소 |
||||
</Button> |
||||
<Button |
||||
color='primary' |
||||
type='button' |
||||
onClick={() => setConfirmModal(!confirmModal)} |
||||
{...(isCrtfy ? {} : { disabled: true })} |
||||
> |
||||
확인 |
||||
</Button> |
||||
|
||||
<Modal |
||||
isOpen={confirmModal} |
||||
toggle={() => setConfirmModal(!confirmModal)} |
||||
className='modal-dialog-centered user-search-modal' |
||||
> |
||||
<ModalHeader toggle={() => setConfirmModal(!confirmModal)}> |
||||
비밀번호 확인 |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
<span className='etc-txt'> |
||||
새로운 비밀번호로 변경해 주세요. |
||||
</span> |
||||
<FormGroup className='form-label-group position-relative has-icon-left'> |
||||
<InputGroup> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<Lock size={14} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='password' |
||||
id='newPw' |
||||
name='newPw' |
||||
placeholder='새로운 비밀번호' |
||||
value={inputNewPw} |
||||
onChange={handlerChange} |
||||
/> |
||||
</InputGroup> |
||||
</FormGroup> |
||||
<FormGroup className='form-label-group position-relative has-icon-left mb-0'> |
||||
<InputGroup> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<Lock size={14} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='password' |
||||
id='newPwCk' |
||||
name='newPwCk' |
||||
placeholder='새로운 비밀번호 확인' |
||||
value={inputNewPwCk} |
||||
onChange={handlerChange} |
||||
/> |
||||
</InputGroup> |
||||
</FormGroup> |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' type='button' onClick={handlerUpdatePw}> |
||||
저장 |
||||
</Button> |
||||
|
||||
<div className='vertically-centered-modal'> |
||||
<Modal |
||||
isOpen={saveModal} |
||||
toggle={() => setSaveModal(!saveModal)} |
||||
modalClassName='modal-primary' |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader toggle={() => setSaveModal(!saveModal)}> |
||||
비밀번호 변경 |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
변경이 완료되었습니다. 다시 로그인해 주세요. |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={props.handlerClose}> |
||||
로그인 |
||||
</Button>{' '} |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</FormGroup> |
||||
|
||||
<div className='vertically-centered-modal'> |
||||
<Modal |
||||
isOpen={modal.isOpen} |
||||
toggle={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
modalClassName={modal.color} |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader |
||||
toggle={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
> |
||||
{modal.title} |
||||
</ModalHeader> |
||||
<ModalBody>{modal.desc}</ModalBody> |
||||
<ModalFooter> |
||||
<Button |
||||
color='danger' |
||||
onClick={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
> |
||||
확인 |
||||
</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</TabPane> |
||||
</> |
||||
); |
||||
}; |
@ -0,0 +1,88 @@
|
||||
import { useState } from "react"; |
||||
import { Form, InputGroup, InputGroupAddon, InputGroupText, Card, CardBody, Row, Col, Nav, NavItem, NavLink, TabContent, TabPane, Alert, FormGroup, Input, Label, Button, |
||||
Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap' |
||||
import classnames from 'classnames'; |
||||
import { |
||||
User, |
||||
Info, |
||||
Share, |
||||
X, |
||||
Lock, |
||||
Edit, |
||||
Trash2, |
||||
Image, |
||||
UserCheck, |
||||
Clipboard, |
||||
Settings, |
||||
AlertTriangle |
||||
} from 'react-feather'; |
||||
import { AccountFindUserId } from "./AccountFindUserId"; |
||||
import { AccountFindPassword } from "./AccountFindPassword"; |
||||
import { AccountFindUserIdContainer } from "../../../containers/account/find/AccountFindUserIdContainer"; |
||||
import { AccountFindPasswordContainer } from "../../../containers/account/find/AccountFindPasswordContainer"; |
||||
import { useHistory } from 'react-router-dom' |
||||
|
||||
export const AccountFindTab = props => { |
||||
const [activeTab, setActiveTab] = useState('2'); |
||||
const history = useHistory(); |
||||
|
||||
const toggle = tab => setActiveTab(tab) |
||||
|
||||
// const toggle = tab => {
|
||||
// if (activeTab !== tab) setActiveTab(tab);
|
||||
// };
|
||||
|
||||
const handlerClose = () => { |
||||
history.push(`/account/login`) |
||||
} |
||||
|
||||
return( |
||||
<> |
||||
<div className='card-body-tab-menu'> |
||||
<Nav pills> |
||||
<NavItem> |
||||
{/* <NavLink active={activeTab === '1'} onClick={() => toggle('1')}> */} |
||||
<NavLink |
||||
className={classnames({ active: activeTab === '1' })} |
||||
onClick={() => { |
||||
toggle('1'); |
||||
}} |
||||
> |
||||
<User size={14} /> |
||||
<span className='align-middle d-none d-sm-block'>아이디 찾기</span> |
||||
</NavLink> |
||||
</NavItem> |
||||
<NavItem> |
||||
{/* <NavLink active={activeTab === '2'} onClick={() => toggle('2')}> */} |
||||
<NavLink |
||||
className={classnames({active: activeTab === '2'})} |
||||
onClick={() => { |
||||
toggle('2'); |
||||
}} |
||||
> |
||||
<Lock size={14} /> |
||||
<span className='align-middle d-none d-sm-block'>비밀번호 찾기</span> |
||||
</NavLink> |
||||
</NavItem> |
||||
</Nav> |
||||
</div> |
||||
<Row className='app-user-edit'> |
||||
<Col sm='12'> |
||||
<Card> |
||||
<CardBody className='card-body-tab-cont'> |
||||
<TabContent activeTab={activeTab}> |
||||
<AccountFindUserIdContainer |
||||
handlerClose={handlerClose} |
||||
/> |
||||
<AccountFindPasswordContainer
|
||||
handlerClose={handlerClose} |
||||
/> |
||||
</TabContent> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
</> |
||||
) |
||||
|
||||
} |
@ -0,0 +1,420 @@
|
||||
import { useState, useEffect, useMemo } from 'react'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
|
||||
import { |
||||
Form, |
||||
InputGroup, |
||||
InputGroupAddon, |
||||
InputGroupText, |
||||
Col, |
||||
TabPane, |
||||
FormGroup, |
||||
Input, |
||||
Button, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter, |
||||
FormFeedback |
||||
} from 'reactstrap'; |
||||
import { User } from 'react-feather'; |
||||
|
||||
import '../../../assets/css/custom.css'; |
||||
import { |
||||
findUserIdAction, |
||||
sendForIdAction |
||||
} from '../../../modules/account/find/actions/findAction'; |
||||
import { accountApi } from '../../../modules/account/register/apis/accountApi'; |
||||
import { findAPI } from '../../../modules/account/find/apis/findApi'; |
||||
|
||||
export const AccountFindUserId = props => { |
||||
const dispatch = useDispatch(); |
||||
const [activeTab, setActiveTab] = useState('2'); |
||||
|
||||
// ** Function to toggle tabs
|
||||
const toggle = tab => setActiveTab(tab); |
||||
|
||||
//modal
|
||||
const [confirmModal, setConfirmModal] = useState(false); |
||||
const [modal, setModal] = useState({ |
||||
isOpen: false, |
||||
title: '', |
||||
desc: '', |
||||
color: '' |
||||
}); |
||||
|
||||
//state값
|
||||
const { idResult, userId, pwResult, udResult } = useSelector( |
||||
state => state.findState |
||||
); |
||||
|
||||
//param으로 넘기기 위한 값
|
||||
const [inputName, setInputName] = useState(''); |
||||
const [inputHpno, setInputHpno] = useState(''); |
||||
const [inputCrtfy, setInputCrtfy] = useState(''); |
||||
|
||||
//3분 시간 및 인증
|
||||
const [minutes_Counter, setMinutes_Counter] = useState('03'); |
||||
const [seconds_Counter, setSeconds_Counter] = useState('00'); |
||||
const [timer, setTimer] = useState(null); |
||||
const [isRunning, setIsRunning] = useState(false); |
||||
const [sendCount, setSendCount] = useState(0); |
||||
const [isCrtfy, setIsCrtfy] = useState(false); |
||||
|
||||
useEffect(() => { |
||||
setIsRunning(true); |
||||
if (isRunning) clearInterval(timer); |
||||
return () => { |
||||
clearInterval(timer); |
||||
setIsRunning(false); |
||||
}; |
||||
}, []); |
||||
|
||||
const handlerSend = async () => { |
||||
if (!inputName) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '이름을 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputHpno) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '휴대폰 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (sendCount >= 3) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '인증번호가 발송은 3회까지만 가능합니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
if (inputHpno.length < 11) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '올바른 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
const res = await findAPI.sendForId({ |
||||
memberName: inputName, |
||||
hpno: inputHpno |
||||
}); |
||||
if (res?.data.code === -1) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '가입되지 않은 회원정보입니다. 다시 확인해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
} else if (res?.data.code === 0) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 발송', |
||||
desc: '인증번호가 발송되었습니다.', |
||||
color: 'modal-primary' |
||||
}); |
||||
timeStart(); |
||||
} |
||||
}; |
||||
|
||||
const timeStart = () => { |
||||
if (isRunning) { |
||||
clearInterval(timer); |
||||
} |
||||
|
||||
setIsRunning(true); |
||||
setIsCrtfy(false); |
||||
|
||||
setSendCount(sendCount + 1); |
||||
timerStart(180); |
||||
}; |
||||
|
||||
const timerStart = count => { |
||||
let minutes, seconds; |
||||
|
||||
const timer = setInterval(() => { |
||||
setIsRunning(true); |
||||
minutes = parseInt(count / 60, 10); |
||||
seconds = parseInt(count % 60, 10); |
||||
minutes = minutes < 10 ? '0' + minutes : minutes; |
||||
seconds = seconds < 10 ? '0' + seconds : seconds; |
||||
setMinutes_Counter(minutes); |
||||
setSeconds_Counter(seconds); |
||||
|
||||
//타이머 끝
|
||||
if (--count < 0) { |
||||
clearInterval(timer); |
||||
setIsRunning(false); |
||||
// setValue('crtfyNo', '');
|
||||
// setCrtfyNo('');
|
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 만료', |
||||
desc: '인증번호가 만료되었습니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
} |
||||
}, 1000); |
||||
|
||||
setTimer(timer); |
||||
|
||||
return () => { |
||||
clearInterval(timer); |
||||
}; |
||||
}; |
||||
|
||||
const handlerConfirm = async () => { |
||||
if (!inputHpno) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '휴대폰 번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputName) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '이름을 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!inputCrtfy) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증번호를 입력해 주세요.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
const res = await accountApi.crtfyhpConfirm(inputHpno, inputCrtfy); |
||||
if (!res.data.result) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증번호가 잘못되었습니다.', |
||||
color: 'modal-danger' |
||||
}); |
||||
return; |
||||
} else { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '인증번호 인증', |
||||
desc: '인증되었습니다.', |
||||
color: 'modal-primary' |
||||
}); |
||||
setIsCrtfy(true); |
||||
setIsRunning(false); |
||||
} |
||||
}; |
||||
|
||||
let memberName = ''; |
||||
let hpno = ''; |
||||
let crtfyhp = ''; |
||||
const handlerChange = e => { |
||||
const { name, value } = e.target; |
||||
|
||||
if (name == 'memberName') { |
||||
const regex = /^[ㄱ-ㅎ|ㅏ-ㅣ|가-힣|a-z]{0,10}$/; |
||||
if (regex.test(value)) { |
||||
memberName = value; |
||||
setInputName(memberName); |
||||
} |
||||
} else if (name == 'hpno') { |
||||
const regex = /^[0-9]{0,11}$/; |
||||
if (regex.test(value)) { |
||||
hpno = value; |
||||
setInputHpno(hpno); |
||||
} |
||||
} else if (name == 'crtfyhpNo') { |
||||
const regex = /^[0-9]{0,6}$/; |
||||
if (regex.test(value)) { |
||||
crtfyhp = value; |
||||
setInputCrtfy(crtfyhp); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
//모든 인증 완료 후 확인버튼
|
||||
const handlerFindId = () => { |
||||
dispatch( |
||||
findUserIdAction.request({ memberName: inputName, hpno: inputHpno }) |
||||
); |
||||
setConfirmModal(!confirmModal); |
||||
}; |
||||
|
||||
return ( |
||||
<> |
||||
<TabPane tabId='1'> |
||||
<FormGroup className='form-label-group position-relative has-icon-left'> |
||||
<InputGroup className=''> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<User size={14} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='text' |
||||
id='memberName' |
||||
name='memberName' |
||||
placeholder='이름' |
||||
value={inputName} |
||||
onChange={handlerChange} |
||||
/> |
||||
</InputGroup> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<div className='input-btn'> |
||||
<Col md='3' xs='12'> |
||||
<Input |
||||
type='select' |
||||
id='cntryCd' |
||||
name='cntryCd' |
||||
placeholder='+(국가번호)' |
||||
> |
||||
{/* <option>+82</option> */} |
||||
<option value={'+82'}>대한민국(+82)</option> |
||||
<option value={'+81'}>일본(+81)</option> |
||||
<option value={'+86'}>중국(+86)</option> |
||||
</Input> |
||||
</Col> |
||||
<Col md='6' xs='12'> |
||||
<Input |
||||
type='number' |
||||
id='hpno' |
||||
name='hpno' |
||||
placeholder='01012345678' |
||||
value={inputHpno} |
||||
onChange={handlerChange} |
||||
/> |
||||
</Col> |
||||
<Col md='3' xs='12'> |
||||
<Button color='primary' type='button' onClick={handlerSend}> |
||||
인증번호 발송 |
||||
</Button> |
||||
</Col> |
||||
</div> |
||||
</FormGroup> |
||||
|
||||
<FormGroup> |
||||
<div className='input-btn'> |
||||
<Col md='9' xs='12' className='timeInput'> |
||||
<Input |
||||
type='number' |
||||
id='crtfyhpNo' |
||||
name='crtfyhpNo' |
||||
placeholder='인증번호 입력' |
||||
value={inputCrtfy} |
||||
onChange={handlerChange} |
||||
{...(sendCount > 0 ? {} : { disabled: true })} |
||||
/> |
||||
<span className={!isRunning || isCrtfy ? 'time d-none' : 'time'}> |
||||
남은시간 {minutes_Counter}:{seconds_Counter} |
||||
</span> |
||||
</Col> |
||||
<Col md='3' xs='12'> |
||||
<Button |
||||
color='primary' |
||||
type='button' |
||||
onClick={handlerConfirm} |
||||
{...(isRunning ? {} : { disabled: true })} |
||||
> |
||||
인증번호 확인 |
||||
</Button> |
||||
</Col> |
||||
</div> |
||||
</FormGroup> |
||||
<FormGroup> |
||||
<div className='full-btn-2n vertically-centered-confirmModal'> |
||||
<Button |
||||
color='secondary' |
||||
type='button' |
||||
onClick={props.handlerClose} |
||||
> |
||||
취소 |
||||
</Button> |
||||
<Button |
||||
color='primary' |
||||
type='button' |
||||
onClick={handlerFindId} |
||||
{...(isCrtfy ? {} : { disabled: true })} |
||||
> |
||||
확인 |
||||
</Button> |
||||
|
||||
<Modal |
||||
isOpen={confirmModal} |
||||
toggle={() => setConfirmModal(!confirmModal)} |
||||
modalClassName='modal-primary' |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader toggle={() => setConfirmModal(!confirmModal)}> |
||||
아이디 확인 |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
회원님의 아이디는 |
||||
<br /> |
||||
<span className='user-search-id'> |
||||
{userId?.data.userId} |
||||
</span>{' '} |
||||
입니다. |
||||
{/* 회원님의 아이디는<br/><span className='user-search-id'>{result?.data.userId}</span> 입니다. */} |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={props.handlerClose}> |
||||
로그인 |
||||
</Button>{' '} |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</FormGroup> |
||||
|
||||
<div className='vertically-centered-modal'> |
||||
<Modal |
||||
isOpen={modal.isOpen} |
||||
toggle={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
modalClassName={modal.color} |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader |
||||
toggle={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
> |
||||
{modal.title} |
||||
</ModalHeader> |
||||
<ModalBody>{modal.desc}</ModalBody> |
||||
<ModalFooter> |
||||
<Button |
||||
color='danger' |
||||
onClick={() => setModal({ ...modal, isOpen: !modal.isOpen })} |
||||
> |
||||
확인 |
||||
</Button>{' '} |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</TabPane> |
||||
</> |
||||
); |
||||
}; |
@ -0,0 +1,324 @@
|
||||
// ** React Imports
|
||||
import { useState, useEffect } from 'react'; |
||||
|
||||
import { |
||||
Card, |
||||
CardBody, |
||||
Row, |
||||
Col, |
||||
Nav, |
||||
NavItem, |
||||
NavLink, |
||||
TabContent, |
||||
TabPane, |
||||
Alert, |
||||
FormGroup, |
||||
Form, |
||||
Input, |
||||
Label, |
||||
Button, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter |
||||
} from 'reactstrap'; |
||||
|
||||
// ** Styles
|
||||
import '../../../assets/css/custom.css'; |
||||
//import {UserPageState,UserPageData,initResponseUserPageData} from '../../../../modules/account/login/models/authModel';
|
||||
//const [detailData, setDetailData] = useState(userPage.UserPageData);
|
||||
import { useHistory } from 'react-router-dom'; |
||||
|
||||
const AccountMypageForm = ({ |
||||
userInfo, |
||||
handlerSmsSend, |
||||
isCrtfy, |
||||
seconds_Counter, |
||||
handler, |
||||
minutes_Counter, |
||||
isRunning, |
||||
formModal, |
||||
handlerUpdate, |
||||
modal, |
||||
handlerSmsConfirm, |
||||
handlerChange, |
||||
handlerInput, |
||||
inputHpno, |
||||
inputCrtfy, |
||||
okinput, |
||||
|
||||
handlerWidthrow |
||||
}) => { |
||||
const history = useHistory(); |
||||
|
||||
function handleUseHitory() { |
||||
history.push('/'); |
||||
} |
||||
|
||||
return ( |
||||
<Row className='app-user-edit'> |
||||
<Col sm='12'> |
||||
<Card> |
||||
<CardBody className='card-body-tab-cont'> |
||||
<TabPane tabId='1'> |
||||
<div className='search-info-box'> |
||||
{userInfo ? ( |
||||
<Row> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='memberName'>이름</Label> |
||||
<Input |
||||
type='text' |
||||
id='memberName' |
||||
bsSize='sm' |
||||
placeholder='' |
||||
value={userInfo.memberName || ''} |
||||
disabled |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='brithdyDate'>생년월일</Label> |
||||
<Input |
||||
type='text' |
||||
id='brithdyDate' |
||||
bsSize='sm' |
||||
placeholder='' |
||||
value={userInfo.brthdyDate || ''} |
||||
disabled |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='cenderCD'>성별</Label> |
||||
<Input |
||||
type='select' |
||||
name='select' |
||||
bsSize='sm' |
||||
id='cenderCD' |
||||
value={userInfo.genderCd || ''} |
||||
disabled |
||||
> |
||||
<option>남자</option> |
||||
<option>여자</option> |
||||
</Input> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='cntryCD'>국가코드</Label> |
||||
<Input |
||||
type='select' |
||||
name='select' |
||||
bsSize='sm' |
||||
id='cntryCD' |
||||
> |
||||
<option>대한민국</option> |
||||
</Input> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='userId'>ID</Label> |
||||
<Input |
||||
type='text' |
||||
id='userId' |
||||
bsSize='sm' |
||||
placeholder='' |
||||
value={userInfo.userId || ''} |
||||
disabled |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='email'>E-mail</Label> |
||||
<Input |
||||
type='text' |
||||
id='email' |
||||
name='email' |
||||
bsSize='sm' |
||||
placeholder='' |
||||
defaultValue={userInfo.email} |
||||
onChange={e => handlerInput(e)} |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label className='form-label' for='clncd'> |
||||
휴대폰 번호 |
||||
</Label> |
||||
<div className='user-phone-btn'> |
||||
<Button.Ripple |
||||
type='button' |
||||
color='primary' |
||||
onClick={handler} |
||||
> |
||||
<span className='d-sm-inline-block'>변경</span> |
||||
</Button.Ripple> |
||||
</div> |
||||
<Input |
||||
type='number' |
||||
name='clncd' |
||||
id='hpno' |
||||
bsSize='sm' |
||||
placeholder='' |
||||
disabled |
||||
value={!okinput ? userInfo.hpno || '' : inputHpno} |
||||
/> |
||||
<div> |
||||
<Modal |
||||
isOpen={formModal} |
||||
toggle={handler} |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader toggle={handler}> |
||||
휴대폰번호 변경 |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
<div className='modal-box'> |
||||
<p className='ti'>휴대폰 번호 변경</p> |
||||
<p className='txt'> |
||||
변경할 휴대폰 번호로 인증을 완료해 주세요. |
||||
</p> |
||||
</div> |
||||
<Row> |
||||
<Col className='list-input' md='12'> |
||||
<FormGroup> |
||||
<Row className='input-inline-btn'> |
||||
<Col md='8'> |
||||
<Label |
||||
className='form-label' |
||||
for='hpno' |
||||
> |
||||
휴대폰 번호 |
||||
</Label> |
||||
<Input |
||||
type='number' |
||||
name='hpno' |
||||
id='hpno' |
||||
bsSize='sm' |
||||
value={inputHpno} |
||||
onChange={handlerChange} |
||||
placeholder='01012345678' |
||||
/> |
||||
</Col> |
||||
<Col md='4' xs='12'> |
||||
{/* 발송 버튼을 누르면 남은시간 d-none를 빼주세여~ 그럼나타나여~ */} |
||||
<span |
||||
className={ |
||||
!isRunning || isCrtfy |
||||
? 'time d-none' |
||||
: 'time' |
||||
} |
||||
> |
||||
남은시간 {minutes_Counter}: |
||||
{seconds_Counter} |
||||
</span> |
||||
<Button.Ripple |
||||
type='button' |
||||
color='secondary' |
||||
onClick={handlerSmsSend} |
||||
> |
||||
<span className='d-sm-inline-block'> |
||||
인증번호 발송 |
||||
</span> |
||||
</Button.Ripple> |
||||
</Col> |
||||
</Row> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='12'> |
||||
<FormGroup> |
||||
<Row className='input-inline-btn'> |
||||
<Col md='8'> |
||||
<Label |
||||
className='form-label' |
||||
for='crtfyNo' |
||||
> |
||||
인증번호 |
||||
</Label> |
||||
<Input |
||||
type='number' |
||||
name='crtfyhpNo' |
||||
id='crtfyhpNo' |
||||
bsSize='sm' |
||||
value={inputCrtfy} |
||||
onChange={handlerChange} |
||||
{...(isRunning |
||||
? {} |
||||
: { disabled: true })} |
||||
/> |
||||
</Col> |
||||
<Col md='4' xs='12'> |
||||
<Button.Ripple |
||||
type='button' |
||||
color='secondary' |
||||
onClick={handlerSmsConfirm} |
||||
{...(isRunning |
||||
? {} |
||||
: { disabled: true })} |
||||
> |
||||
<span className='d-sm-inline-block'> |
||||
인증하기 |
||||
</span> |
||||
</Button.Ripple> |
||||
</Col> |
||||
</Row> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={handler}> |
||||
확인 |
||||
</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
</div> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
) : ( |
||||
<></> |
||||
)} |
||||
</div> |
||||
<div className='d-flex justify-content-between'> |
||||
<div> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
size='sm' |
||||
onClick={handlerUpdate} |
||||
> |
||||
저장 |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
color='danger' |
||||
size='sm' |
||||
onClick={handleUseHitory} |
||||
> |
||||
취소 |
||||
</Button.Ripple> |
||||
</div> |
||||
<div> |
||||
<Button.Ripple |
||||
color='danger' |
||||
size='sm' |
||||
onClick={() => handlerWidthrow()} |
||||
> |
||||
회원탈퇴 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
</TabPane> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
); |
||||
}; |
||||
export default AccountMypageForm; |
@ -0,0 +1,143 @@
|
||||
// ** React Imports
|
||||
import { useState, useEffect } from 'react' |
||||
import { useSelector, useDispatch } from 'react-redux' |
||||
import { |
||||
Card, CardBody, Row, Col, Nav, NavItem, NavLink, TabContent, TabPane, Alert, FormGroup, FormFeedback, Form, Input, Label, Button, |
||||
Modal, ModalHeader, ModalBody, ModalFooter |
||||
} from 'reactstrap' |
||||
// ** Styles
|
||||
import '../../../assets/css/custom.css'; |
||||
import { pwUpdateAction, pwCheckAction } from '../../../modules/account/login/actions/authAction'; |
||||
import { useHistory } from 'react-router-dom'; |
||||
import { ErrorModal } from '../../modal/ErrorModal'; |
||||
|
||||
|
||||
const AccountMypagePwForm = ({ activeTab }) => { |
||||
const { result } = useSelector(state => state.UserPageState) |
||||
const [userPswd, setuserPswd] = useState(''); |
||||
const [inputs, setInputs] = useState({ |
||||
newPswd: '', |
||||
newPswdConfirm: '' |
||||
}); |
||||
const { newPswd, newPswdConfirm } = inputs; |
||||
|
||||
const [resultOk, setresultOk] = useState(true); |
||||
const [btnOk, setbtnOk] = useState(false); |
||||
const [modal, setModal] = useState({ |
||||
isOpen: false, |
||||
title: '', |
||||
desc: '' |
||||
}); |
||||
|
||||
useEffect(() => { |
||||
if (result) { |
||||
if (result.errorCode) { |
||||
setresultOk(true) |
||||
setbtnOk(false) |
||||
} else { |
||||
setresultOk(false) |
||||
setbtnOk(true) |
||||
} |
||||
} |
||||
}, [result]) |
||||
|
||||
useEffect(() => { |
||||
setresultOk(true) |
||||
setbtnOk(false) |
||||
}, [activeTab]) |
||||
|
||||
|
||||
const dispatch = useDispatch(); |
||||
|
||||
const history = useHistory(); |
||||
|
||||
const onChange = (e) => { |
||||
setuserPswd(e.target.value); |
||||
}; |
||||
|
||||
const onKeyPress = e => { |
||||
if (e.key == 'Enter') { |
||||
pwok(); |
||||
} |
||||
}; |
||||
const onChanges = (e) => { |
||||
const { value, name } = e.target; |
||||
setInputs({ |
||||
...inputs, |
||||
[name]: value |
||||
}); |
||||
}; |
||||
|
||||
function handleUseHitory() { |
||||
history.push('/'); |
||||
} |
||||
|
||||
function pwSubmit() { |
||||
const reg_pw = |
||||
/^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[@$!%*#?&])[A-Za-z0-9@$!%*#?&]{8,20}$/; |
||||
if (!reg_pw.test(inputs.newPswd && inputs.newPswdConfirm)) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '필수값 입력 오류', |
||||
desc: '8 자 이상, 20 자 미만 영문자/숫자/특수문자(@$!%*#?&) 조합하여 입력해 주세요.', |
||||
}); |
||||
} |
||||
else if (inputs.newPswd != inputs.newPswdConfirm) { |
||||
setModal({ |
||||
isOpen: true, |
||||
title: '필수값 입력 오류', |
||||
desc: '비밀번호가 일치하지 않습니다.', |
||||
}); |
||||
} |
||||
else { |
||||
dispatch(pwUpdateAction.request(inputs)); |
||||
} |
||||
} |
||||
|
||||
function pwok() { |
||||
dispatch(pwCheckAction.request(userPswd)); |
||||
} |
||||
|
||||
return ( |
||||
<Card> |
||||
<CardBody className='card-body-tab-cont'> |
||||
<TabPane tabId='2'> |
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' md='3' sm='12'> |
||||
<FormGroup> |
||||
<Label for='userPswd'>기존 비밀번호</Label> |
||||
<Input type='password' readOnly={btnOk} onKeyPress={onKeyPress} id='userPswd' onChange={onChange} value={userPswd} name="userPswd" bsSize='sm' autoComplete='off' placeholder='' /> |
||||
</FormGroup> |
||||
</Col> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple className='mr-1' color='primary' onClick={pwok} size='sm'>확인</Button.Ripple> |
||||
</div> |
||||
|
||||
<Col className='list-input' md='3' sm='12' style={{ display: resultOk ? 'none' : 'inline' }} > |
||||
<FormGroup> |
||||
<Label for='newPswd' >새로운 비밀번호</Label> |
||||
<Input type='password' id='newPswd' onChange={onChanges} value={newPswd} name="newPswd" bsSize='sm' autoComplete='off' placeholder='' /> |
||||
</FormGroup> |
||||
</Col> |
||||
<Col className='list-input' md='3' sm='12' style={{ display: resultOk ? 'none' : 'inline' }}> |
||||
<FormGroup> |
||||
<Label for='newPswdConfirm'>새로운 비밀번호 확인</Label> |
||||
<Input type='password' id='newPswdConfirm' onChange={onChanges} value={newPswdConfirm} name="newPswdConfirm" bsSize='sm' autoComplete='off' placeholder='' /> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple className='mr-1' color='primary' onClick={pwSubmit} disabled={resultOk} size='sm'>저장</Button.Ripple> |
||||
<Button.Ripple color='danger' size='sm' onClick={handleUseHitory}>취소</Button.Ripple> |
||||
</div> |
||||
</TabPane> |
||||
</CardBody> |
||||
<ErrorModal modal={modal} setModal={setModal} /> |
||||
</Card > |
||||
) |
||||
} |
||||
|
||||
export default AccountMypagePwForm; |
||||
|
@ -1,62 +1,61 @@
|
||||
import { GridDatabase } from '../../../components/crud/grid/GridDatatable'; |
||||
import { |
||||
Row, |
||||
Col, |
||||
Table, |
||||
Badge, |
||||
UncontrolledDropdown, |
||||
DropdownMenu, |
||||
DropdownItem, |
||||
DropdownToggle, |
||||
Card, |
||||
CardHeader, |
||||
CardBody, |
||||
CardTitle, |
||||
CardSubtitle, |
||||
ButtonGroup, |
||||
Button, |
||||
Input, |
||||
CustomInput, |
||||
FormGroup |
||||
} from 'reactstrap'; |
||||
import { Card, Spinner } from 'reactstrap'; |
||||
import { ExcelExportButton } from '../../crud/excel/ExcelExportButton'; |
||||
import { useSelector } from 'react-redux'; |
||||
|
||||
export const AnalysisHistoryGrid = props => { |
||||
const { loading } = useSelector(state => state.loadingReducer); |
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.count}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<ExcelExportButton |
||||
filename={'비행이력.csv'} |
||||
data={props.data ? props.data : []} |
||||
headers={props.excelHeaders} |
||||
/> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
{/* <div className='no-dataTable'> |
||||
// <div className='pal-card-box'>
|
||||
// <Row>
|
||||
// <Col>
|
||||
<> |
||||
<div className='mt-2 cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.total}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<ExcelExportButton |
||||
filename={'비행이력.csv'} |
||||
data={props.data ? props.data : []} |
||||
headers={props.excelHeaders} |
||||
/> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
{/* <div className='no-dataTable'> |
||||
표시할 데이터가 없습니다. |
||||
</div> */} |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
/> |
||||
{loading ? ( |
||||
<div className='grid-loading'> |
||||
<div> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
</div> |
||||
</Card> |
||||
) : null} |
||||
|
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
total={props.total} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
handlerPageChange={props.handlerPageChange} |
||||
paginationPerPage={props.paginationPerPage} |
||||
paginationRowsPerPageOptions={props.paginationRowsPerPageOptions} |
||||
page={props.page} |
||||
/> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
// </Col>
|
||||
// </Row>
|
||||
// </div>
|
||||
); |
||||
}; |
||||
|
@ -0,0 +1,123 @@
|
||||
import moment from 'moment'; |
||||
import { useEffect, useState, useRef, useCallback } from 'react'; |
||||
import { useSelector } from 'react-redux'; |
||||
import { Spinner } from 'reactstrap'; |
||||
|
||||
export const AnalysimuationInfo = props => { |
||||
const [target, setTarget] = useState(null); |
||||
|
||||
const { loading } = useSelector(state => state.loadingReducer); |
||||
|
||||
const onIntersect = useCallback( |
||||
([entry], observer) => { |
||||
if (entry.isIntersecting) { |
||||
if (props.data?.filter(i => i === undefined).length <= 0) { |
||||
props.handlerPageList(); |
||||
} |
||||
} |
||||
}, |
||||
[props.handlerPageList] |
||||
); |
||||
|
||||
useEffect(() => { |
||||
let observer; |
||||
if (target) { |
||||
observer = new IntersectionObserver(onIntersect, { |
||||
threshold: 0.4 |
||||
}); |
||||
observer.observe(target); |
||||
} |
||||
return () => observer && observer.disconnect(); |
||||
}, [target, props.handlerPageList]); |
||||
|
||||
return ( |
||||
<div className='layer-content drone-list'> |
||||
{!props.data ? ( |
||||
<> |
||||
<div className='no-dataTable'>표시할 데이터가 없습니다.</div> |
||||
{loading && ( |
||||
<div |
||||
style={{ |
||||
display: 'flex', |
||||
justifyContent: 'center', |
||||
flexDirection: 'column', |
||||
alignItems: 'center' |
||||
}} |
||||
> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
)} |
||||
</> |
||||
) : ( |
||||
<> |
||||
{props.data |
||||
?.filter(i => i !== undefined) |
||||
.map(item => { |
||||
// item.idntfNum,
|
||||
// ':::',
|
||||
// item.idntfNum.indexOf(props.filterId)
|
||||
// );
|
||||
|
||||
if (item.idntfNum) { |
||||
return ( |
||||
<div |
||||
className={`layer-content-list ${ |
||||
item.cntrlId === props.cntrlId ? 'on' : '' |
||||
}`}
|
||||
onClick={() => { |
||||
props.handlerDetail(item.cntrlId); |
||||
}} |
||||
key={Math.random()} |
||||
> |
||||
<dl> |
||||
<dt> |
||||
<div className='list-left-txt'>식별번호</div> |
||||
<div className='list-right-txt'>{item.idntfNum}</div> |
||||
</dt> |
||||
<dt> |
||||
<div className='list-left-txt'>일자</div> |
||||
<div className='list-right-txt'> |
||||
{moment(item.cntrlStDt).format('YYYY년MM월DD일')} |
||||
</div> |
||||
</dt> |
||||
<dt> |
||||
<div className='list-left-txt'>시작 위치</div> |
||||
<div className='list-right-txt'>{item.stArea}</div> |
||||
</dt> |
||||
<dt> |
||||
<div className='list-left-txt'>시작/종료 시간</div> |
||||
<div className='list-right-txt'> |
||||
{moment(item.cntrlStDt).format('HH:mm')} |
||||
{' '}/{' '} |
||||
{moment(item.cntrlEndDt).format('HH:mm')} |
||||
</div> |
||||
</dt> |
||||
{/* <dt> |
||||
<div className='list-left-txt'>총 비행거리/시간</div> |
||||
<div className='list-right-txt'>ddd</div> |
||||
</dt> */} |
||||
</dl> |
||||
</div> |
||||
); |
||||
} |
||||
})} |
||||
{loading && ( |
||||
<div |
||||
style={{ |
||||
display: 'flex', |
||||
justifyContent: 'center', |
||||
flexDirection: 'column', |
||||
alignItems: 'center' |
||||
}} |
||||
> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
)} |
||||
<div ref={setTarget} /> |
||||
</> |
||||
)} |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,132 @@
|
||||
import React, { useEffect, useState } from 'react'; |
||||
import { Button, Card, Col, Row, Spinner } from 'reactstrap'; |
||||
import { GridDatabase } from '../../../crud/grid/GridDatatable'; |
||||
import { Link } from 'react-router-dom'; |
||||
import { selectableRowsComponent } from '../../../crud/grid/selectableRowsComponent'; |
||||
import { useSelector } from 'react-redux'; |
||||
|
||||
const FlightPlanAprvGrid = ({ |
||||
data, |
||||
handleChangeSelected, |
||||
handleClickAprv, |
||||
paginationPerPage, |
||||
pagination, |
||||
paginationRowsPerPageOptions, |
||||
handlerPageChange, |
||||
total, |
||||
isMyGroup |
||||
}) => { |
||||
const { loading } = useSelector(state => state.loadingReducer); |
||||
|
||||
const columns = [ |
||||
{ |
||||
id: 'planSno', |
||||
name: '번호', |
||||
cell: (row, i) => <div>{i + 1}</div> |
||||
}, |
||||
{ |
||||
id: 'fltPurpose', |
||||
name: '비행목적', |
||||
minWidth: '150px', |
||||
cell: (row, i) => <div>{row.fltPurpose}</div> |
||||
}, |
||||
{ |
||||
id: 'fltMethod', |
||||
name: '비행방식', |
||||
minWidth: '200px', |
||||
cell: row => { |
||||
const displayName = |
||||
(row.areaList && |
||||
row.areaList.length > 0 && |
||||
row.areaList[0].fltMethod) || |
||||
'-'; |
||||
return <div>{displayName}</div>; |
||||
} |
||||
}, |
||||
{ |
||||
id: 'schFltStDt', |
||||
name: '출발일', |
||||
minWidth: '200px', |
||||
cell: row => <div>{row.schFltStDt}</div> |
||||
}, |
||||
{ id: 'aprvlYn', name: '승인여부', cell: row => <div>{row.aprvlYn}</div> }, |
||||
{ |
||||
id: 'moveDetail', |
||||
name: '상세보기', |
||||
cell: row => { |
||||
return ( |
||||
<Link |
||||
to={`/basis/flight/plan/detail/${row.planSno}?type=aprv`} |
||||
size='sm' |
||||
> |
||||
상세보기 |
||||
</Link> |
||||
); |
||||
} |
||||
} |
||||
]; |
||||
|
||||
return ( |
||||
<> |
||||
<div className='mt-2 cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>비행계획서 신청 목록</h4> |
||||
<span className='search-case'> |
||||
검색결과 총 {!!data ? data.length : 0}건 |
||||
</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={handleClickAprv('aprv')} |
||||
{...(isMyGroup ? {} : { disabled: true })} |
||||
> |
||||
승인 |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
style={{ marginLeft: 10 }} |
||||
onClick={handleClickAprv('notAprov')} |
||||
{...(isMyGroup ? {} : { disabled: true })} |
||||
> |
||||
미승인 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
{loading ? ( |
||||
<div className='grid-loading'> |
||||
<div> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
</div> |
||||
) : null} |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={data} |
||||
columns={columns} |
||||
count={!!data ? data.length : 0} |
||||
// pagination={props.pagination}
|
||||
selectableRows |
||||
selectableRowsComponent={selectableRowsComponent} |
||||
onSelectedRowsChange={handleChangeSelected} |
||||
pagination={pagination} |
||||
paginationPerPage={paginationPerPage} |
||||
paginationRowsPerPageOptions={paginationRowsPerPageOptions} |
||||
handlerPageChange={handlerPageChange} |
||||
total={total} |
||||
/> |
||||
{/* 검색된 데이터가 없습니다. */} |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanAprvGrid; |
@ -0,0 +1,27 @@
|
||||
import React from 'react'; |
||||
import { GridDatabase } from '../../../crud/grid/GridDatatable'; |
||||
import { Card, Button } from 'reactstrap'; |
||||
|
||||
const FlightPlanAprvGroupGrid = ({ data, count, columns, pagination, paginationPerPage, paginationRowsPerPageOptions }) => { |
||||
return ( |
||||
<> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>그룹 목록</h4> |
||||
<span className='search-case'>검색결과 총 {count}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'></div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase data={data} count={count} columns={columns} |
||||
pagination={pagination} paginationPerPage={paginationPerPage} paginationRowsPerPageOptions={paginationRowsPerPageOptions} /> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanAprvGroupGrid; |
@ -0,0 +1,174 @@
|
||||
import React, { useState } from 'react'; |
||||
import { Button, Card, CardBody, Col, CustomInput, Row } from 'reactstrap'; |
||||
import { Search } from 'react-feather'; |
||||
import Flatpickr from 'react-flatpickr'; |
||||
import moment from 'moment'; |
||||
|
||||
const FlightPlanAprvSearch = ({ |
||||
searchData, |
||||
handleChangeSearchData, |
||||
handleSearch |
||||
}) => { |
||||
const { schFltStDt, schFltEndDt, aprvlYn } = searchData; |
||||
const initCheckState = { |
||||
all: aprvlYn == 'A', |
||||
yes: aprvlYn == 'Y' || aprvlYn == 'A', |
||||
no: aprvlYn == 'N' || aprvlYn == 'A' |
||||
}; |
||||
const [checkState, setCheckState] = useState(initCheckState); |
||||
const handleClickSearch = e => { |
||||
handleSearch(searchData); |
||||
}; |
||||
const handleChangeInput = (dates, value, config) => { |
||||
if (dates.length === 2) { |
||||
const schFltStDt = moment(dates[0]).format('YYYY-MM-DD HH:mm:ss'); |
||||
const schFltEndDt = moment(dates[1]) |
||||
.set({ h: 23, m: 59, s: 59 }) |
||||
.format('YYYY-MM-DD HH:mm:ss'); |
||||
handleChangeSearchData({ schFltStDt, schFltEndDt }); |
||||
} |
||||
}; |
||||
const handleChangeCheckbox = e => { |
||||
const { name, value, checked } = e.target; |
||||
let val; |
||||
switch (value) { |
||||
case 'A': |
||||
val = checked ? 'A' : ''; |
||||
handleChangeSearchData({ [name]: val }); |
||||
setCheckState({ |
||||
all: checked, |
||||
yes: checked, |
||||
no: checked |
||||
}); |
||||
break; |
||||
case 'Y': |
||||
if (checked && checkState.no) val = 'A'; |
||||
else if (checked && !checkState.no) val = 'Y'; |
||||
else if (!checked && checkState.no) val = 'N'; |
||||
else if (!checked && !checkState.no) val = ''; |
||||
handleChangeSearchData({ [name]: val }); |
||||
setCheckState(prevState => ({ |
||||
all: prevState.no && checked, |
||||
yes: checked, |
||||
no: prevState.no |
||||
})); |
||||
break; |
||||
case 'N': |
||||
if (checked && checkState.yes) val = 'A'; |
||||
else if (checked && !checkState.yes) val = 'N'; |
||||
else if (!checked && checkState.yes) val = 'Y'; |
||||
else if (!checked && !checkState.yes) val = ''; |
||||
handleChangeSearchData({ [name]: val }); |
||||
setCheckState(prevState => ({ |
||||
all: prevState.yes && checked, |
||||
yes: prevState.yes, |
||||
no: checked |
||||
})); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
}; |
||||
return ( |
||||
<div> |
||||
<Row> |
||||
<Col> |
||||
<div className='mt-2 cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>검색조건</h4> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={handleClickSearch} |
||||
> |
||||
<Search size={16} /> |
||||
검색 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<Card> |
||||
<CardBody className='pal-card-body'> |
||||
<div className='search-cont'> |
||||
<dl> |
||||
<dt> |
||||
<div className='search-box'> |
||||
<div className='search-list-ti'>신청일</div> |
||||
<div className='search-list'> |
||||
<div className='search-list-cont'> |
||||
<Row> |
||||
<Col className='list-input' xl='4' md='6' sm='12'> |
||||
<div className='d-flex align-items-center calendar-flat'> |
||||
<Flatpickr |
||||
id='searchDate' |
||||
value={[schFltStDt, schFltEndDt]} |
||||
options={{ |
||||
mode: 'range' |
||||
// defaultDate: [
|
||||
// props.params.stDate,
|
||||
// props.params.endDate
|
||||
// ]
|
||||
}} |
||||
onChange={handleChangeInput} |
||||
className='form-control flat-picker bg-transparent border-0 shadow-none' |
||||
/> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</dt> |
||||
|
||||
<dt> |
||||
<div className='search-box'> |
||||
<div className='search-list-ti'>승인여부</div> |
||||
<div className='search-list'> |
||||
<div className='search-list-cont'> |
||||
<CustomInput |
||||
inline |
||||
type='checkbox' |
||||
id='exampleCustomCheckbox' |
||||
label='전체' |
||||
name='aprvlYn' |
||||
value='A' |
||||
// checked={true}
|
||||
checked={checkState.all} |
||||
onChange={handleChangeCheckbox} |
||||
/> |
||||
<CustomInput |
||||
inline |
||||
type='checkbox' |
||||
id='exampleCustomCheckbox2' |
||||
label='승인' |
||||
name='aprvlYn' |
||||
value='Y' |
||||
checked={checkState.yes} |
||||
onChange={handleChangeCheckbox} |
||||
/> |
||||
<CustomInput |
||||
inline |
||||
type='checkbox' |
||||
id='exampleCustomCheckbox3' |
||||
label='미승인' |
||||
name='aprvlYn' |
||||
value='N' |
||||
checked={checkState.no} |
||||
onChange={handleChangeCheckbox} |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanAprvSearch; |
@ -0,0 +1,131 @@
|
||||
import React from 'react'; |
||||
import {Button, Card, CardBody, Col, CustomInput, Row, FormGroup, Input, Label} from 'reactstrap'; |
||||
import {Search} from 'react-feather'; |
||||
import {GridDatabase} from '../../../crud/grid/GridDatatable'; |
||||
import '../../../../assets/css/custom.css'; |
||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||
|
||||
const FlightPlanArcrft = ({ arcrftList, handleSelectArcrft, onClickEvent, name, returnArcrft,onChange }) => { |
||||
const columns = [ |
||||
{id: 'groupNm', name: '그룹 명', cell: row => (<div>{row.groupNm}</div>)}, |
||||
{id: 'arcrftModelNm', name: '모델 명', cell: row => (<div>{row.arcrftModelNm}</div>)}, |
||||
{id: 'idntfTypeCd', name: '종류', cell: row => (<div>{row.arcrftTypeCd}</div>)}, |
||||
{id: 'ownerNm', name: '소유자 명', cell: row => (<div>{row.ownerNm}</div>)}, |
||||
{id: 'idntfNum', name: '식별코드', cell: row => (<div>{row.idntfNum}</div>)}, |
||||
{ |
||||
id: 'selectPilot', name: '선택', cell: row => { |
||||
return <Button.Ripple color='primary' size='sm' onClick={() => { |
||||
handleSelectArcrft(row.idntfNum)
|
||||
} |
||||
}>선택</Button.Ripple>; |
||||
}
|
||||
} |
||||
]; |
||||
|
||||
return ( |
||||
<> |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div |
||||
className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>검색조건</h4> |
||||
</div> |
||||
{arcrftList?( |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={onClickEvent} |
||||
> |
||||
<Search size={16}/> |
||||
검색 |
||||
</Button.Ripple> |
||||
</div> |
||||
) |
||||
: |
||||
( |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={onClickEvent} |
||||
disabled |
||||
> |
||||
<Search size={16}/> |
||||
검색 |
||||
</Button.Ripple> |
||||
</div> |
||||
) |
||||
} |
||||
</div> |
||||
<Card> |
||||
<CardBody className='pal-card-body'> |
||||
<div className='search-cont'> |
||||
<dl>
|
||||
<dt>
|
||||
<div className='search-box'> |
||||
<div className='search-list-ti'>소유자 명</div> |
||||
<div className='search-list'> |
||||
<div className='search-list-cont'> |
||||
<Row> |
||||
<Col className='list-input' xl='4' md='4' sm='12'> |
||||
<FormGroup className='form-label-group'> |
||||
<Input |
||||
type='text' |
||||
id='ownerNm' |
||||
name='ownerNm' |
||||
value={name} |
||||
onChange={onChange} |
||||
bsSize='sm' |
||||
// onKeyPress={props.onKeyPress}
|
||||
placeholder='소유자 명을 입력하세요' |
||||
/> |
||||
<Label for='test'>소유자 명</Label> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
|
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>기체 목록</h4> |
||||
<span className='search-case'>검색결과 총 {!!returnArcrft ? returnArcrft.length : 0}건</span> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase
|
||||
data={returnArcrft} |
||||
count={returnArcrft ? returnArcrft.length : 0} |
||||
columns={columns} |
||||
// pagination={props.pagination}
|
||||
/> |
||||
{/* 검색된 데이터가 없습니다. */} |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</> |
||||
) |
||||
|
||||
} |
||||
|
||||
export default FlightPlanArcrft; |
@ -0,0 +1,266 @@
|
||||
import React, { useEffect, useState } from 'react'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
import { object } from 'prop-types'; |
||||
import { |
||||
Card, |
||||
CardBody, |
||||
Col, |
||||
FormGroup, |
||||
Label, |
||||
Row, |
||||
Button, |
||||
InputGroupAddon, |
||||
InputGroup, |
||||
Input, |
||||
Table |
||||
} from 'reactstrap'; |
||||
const FlightPlanAreaDetailForm = ({ |
||||
handleSave, |
||||
handleClose, |
||||
handleChange, |
||||
handleBufferList, |
||||
data, |
||||
mapControl, |
||||
isDone, |
||||
isDisabled |
||||
}) => { |
||||
const coordList = data ? data[0].coordList : null; |
||||
const dispatch = useDispatch(); |
||||
|
||||
|
||||
|
||||
return ( |
||||
<Card className='mb-0'> |
||||
<CardBody> |
||||
<Row> |
||||
<Col> |
||||
<Card> |
||||
<CardBody className='pal-card-body'> |
||||
<div className='search-cont search-info pd-0'> |
||||
<div className='cont-ti mb-1 d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>비행 구역 상세 정보</h4> |
||||
</div> |
||||
</div> |
||||
|
||||
<dl> |
||||
<dt> |
||||
<div className='search-info-ti d-flex justify-content-between'> |
||||
<h4 className='ti'>좌표 정보</h4> |
||||
</div> |
||||
|
||||
<div className='search-info-box'> |
||||
<Row> |
||||
{coordList ? ( |
||||
coordList.map((coord, idx) => { |
||||
const latlon = coord.lat + ' / ' + coord.lon; |
||||
|
||||
return ( |
||||
<Col |
||||
key={idx} |
||||
className='list-input' |
||||
lg={6} |
||||
md={6} |
||||
sm={12} |
||||
> |
||||
<FormGroup> |
||||
<Label for='test'> |
||||
<span className='necessary'>*</span>좌표{' '} |
||||
{idx + 1}{' '} |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
name='coord' |
||||
bsSize='sm' |
||||
placeholdeer='' |
||||
readOnly |
||||
value={latlon} |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
); |
||||
}) |
||||
) : ( |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup> |
||||
<Label for='test'> |
||||
<span className='necessary'>*</span>좌표 1 |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
name='coord' |
||||
bsSize='sm' |
||||
placeholdeer='' |
||||
readOnly |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
)} |
||||
</Row> |
||||
</div> |
||||
</dt> |
||||
|
||||
<dt> |
||||
<div className='search-info-ti d-flex justify-content-between'> |
||||
<h4 className='ti'>기타 정보</h4> |
||||
</div> |
||||
|
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup className='m_ft'> |
||||
<div className='m_ft_box'> |
||||
<Label for='test'> |
||||
<span className='necessary'>*</span>반경(m) |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
id='bufferZone' |
||||
name='bufferZone' |
||||
bsSize='sm' |
||||
disabled={ |
||||
isDisabled || isDone |
||||
? true |
||||
: coordList[0].lat && |
||||
data[0].areaType && |
||||
data[0].areaType !== 'POLYGON' |
||||
? false |
||||
: true |
||||
} |
||||
placeholder='반경' |
||||
value={ |
||||
data[0].bufferZone ? data[0].bufferZone : '' |
||||
} |
||||
onChange={e => { |
||||
const { name, value } = e.target; |
||||
handleChange({ |
||||
name, |
||||
value |
||||
}); |
||||
}} |
||||
/> |
||||
</div> |
||||
<div className='m_ft_box'> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
size='sm' |
||||
disabled={ |
||||
isDisabled || isDone |
||||
? true |
||||
: coordList[0].lat && |
||||
data[0].areaType && |
||||
data[0].areaType !== 'POLYGON' |
||||
? false |
||||
: true |
||||
} |
||||
onClick={() => handleBufferList()} |
||||
> |
||||
적용 |
||||
</Button.Ripple> |
||||
</div> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup> |
||||
<Label for='test'> |
||||
<span className='necessary'>*</span>고도(ft) |
||||
</Label> |
||||
|
||||
<Input |
||||
type='text' |
||||
id='fltElev' |
||||
name='fltElev' |
||||
bsSize='sm' |
||||
placeholder='고도' |
||||
value={data[0].fltElev ? data[0].fltElev : ''} |
||||
onChange={e => { |
||||
const { name, value } = e.target; |
||||
handleChange({ |
||||
name, |
||||
value |
||||
}); |
||||
}} |
||||
disabled={isDisabled || isDone} |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup> |
||||
<Label for='test'> |
||||
<span className='necessary'>*</span>비행 방식 |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
id='fltMethod' |
||||
name='fltMethod' |
||||
bsSize='sm' |
||||
placeholder='비행 방식' |
||||
value={ |
||||
data[0].fltMethod ? data[0].fltMethod : '' |
||||
} |
||||
onChange={e => { |
||||
const { name, value } = e.target; |
||||
handleChange({ |
||||
name, |
||||
value |
||||
}); |
||||
}} |
||||
disabled={isDisabled || isDone} |
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
|
||||
<div className='d-flex align-items-center mt-2'> |
||||
{isDisabled || isDone ? ( |
||||
<Button.Ripple |
||||
type='submit' |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handleSave()} |
||||
disabled={isDisabled || isDone} |
||||
> |
||||
등록 |
||||
</Button.Ripple> |
||||
) : ( |
||||
<Button.Ripple |
||||
type='submit' |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handleSave()} |
||||
disabled={!coordList[0].lat ? true : false} |
||||
> |
||||
등록 |
||||
</Button.Ripple> |
||||
)} |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handleClose()} |
||||
> |
||||
닫기 |
||||
</Button.Ripple> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
</CardBody> |
||||
</Card> |
||||
); |
||||
} |
||||
|
||||
export default FlightPlanAreaDetailForm; |
@ -1,115 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react'; |
||||
import classnames from 'classnames'; |
||||
import { |
||||
Card, |
||||
CardBody, |
||||
Col,
|
||||
FormGroup, |
||||
FormFeedback, |
||||
Input, |
||||
Label, |
||||
Row,
|
||||
Button, |
||||
Form |
||||
} from 'reactstrap'; |
||||
|
||||
|
||||
const FlightPlanAreaForm = (props) => {
|
||||
|
||||
return (
|
||||
<Card> |
||||
<CardBody>
|
||||
<div className='search-cont search-info pd-0'> |
||||
<div className='cont-ti mb-1 d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>상세 정보</h4> |
||||
</div> |
||||
<div className='final'> |
||||
{/* {props.type === 'update' ? ( |
||||
<span>최종 수정일자 : {props.updateDt}</span> |
||||
) : null} */} |
||||
</div> |
||||
</div> |
||||
|
||||
<dl> |
||||
<dt>
|
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup> |
||||
<Label for='test'> |
||||
<span className='necessary'></span>반경(m) |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
id='radius' |
||||
name='radius'
|
||||
size='sm' |
||||
placeholder=''
|
||||
innerRef={props.data}
|
||||
/> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='search-info-box'> |
||||
<Row> |
||||
<Col className='list-input' lg={6} md={6} sm={12}> |
||||
<FormGroup className='m_ft'> |
||||
<div className='m_ft_box'> |
||||
<Label for='test'> |
||||
<span className='necessary'></span>고도(m/ft) |
||||
</Label> |
||||
<Input |
||||
type='text' |
||||
id='altitude_m' |
||||
name='altitude_m'
|
||||
size='sm' |
||||
placeholder='m'
|
||||
innerRef={props.data}
|
||||
/> |
||||
</div> |
||||
<div className='m_ft_box'>
|
||||
<Input |
||||
type='text' |
||||
id='altitude' |
||||
name='altitude'
|
||||
size='sm' |
||||
placeholder='ft'
|
||||
innerRef={props.data}
|
||||
/> |
||||
</div> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
|
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
size='sm' |
||||
onClick={ |
||||
props.submit |
||||
} |
||||
> |
||||
확인 |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='danger' |
||||
size='sm' |
||||
onClick={() =>
|
||||
props.setModal({ ...props.modal, isOpen: !props.modal.isOpen }) |
||||
} |
||||
> |
||||
취소 |
||||
</Button.Ripple> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
) |
||||
} |
||||
|
||||
export default FlightPlanAreaForm; |
@ -1,78 +1,443 @@
|
||||
import React, { useEffect, useState } from 'react'; |
||||
import { |
||||
Card, |
||||
CardBody, |
||||
Button |
||||
Card, |
||||
CardBody, |
||||
Button, |
||||
Input, |
||||
InputGroup, |
||||
InputGroupAddon, |
||||
InputGroupText, |
||||
Modal, |
||||
ModalHeader, |
||||
ModalBody, |
||||
ModalFooter |
||||
} from 'reactstrap'; |
||||
import { Search } from 'react-feather'; |
||||
import { useDispatch, useSelector } from 'react-redux'; |
||||
import { FeatureAirZone } from '../../../map/naver/feature/FeatureAirZone'; |
||||
import { drawTypeChangeAction } from '../../../../modules/control/map/actions/controlMapActions'; |
||||
import { FlightPlanDraw_init } from '../../../map/naver/draw/FlightPlanDraw'; |
||||
import { initFlightBas } from '../../../../modules/basis/flight/models/basisFlightModel'; |
||||
import { |
||||
AREA_COORDINATE_LIST_SAVE, |
||||
AREA_DETAIL_INIT |
||||
} from '../../../../modules/basis/flight/actions/basisFlightAction'; |
||||
import { flightPlanAPI } from '../../../../modules/basis/flight/apis/basisFlightApi'; |
||||
import { WeatherContainer } from '../../../../containers/basis/flight/plan/WeatherContainer'; |
||||
import gimpo from '../../../map/geojson/airportAirArea.json'; |
||||
const FlightPlanAreaMap = props => { |
||||
const dispatch = useDispatch(); |
||||
const naver = window.naver; |
||||
const airArea = props.airArea; |
||||
const mapControl = useSelector(state => state.controlMapReducer); |
||||
const { areaCoordList } = useSelector(state => state.flightState); |
||||
|
||||
const [map, setMap] = useState(); |
||||
const [isMapLoad, setIsMapLoad] = useState(false); |
||||
const [mode, setMode] = useState(); |
||||
const [mapAreaCoordList, setMapAreaCoordList] = useState( |
||||
initFlightBas.initDetail.areaList |
||||
); |
||||
|
||||
const [query, setQuery] = useState(''); |
||||
const [searchRes, setSearchRes] = useState([]); |
||||
const [isSearch, setIsSearch] = useState(false); |
||||
|
||||
const [dragSize, setDragSize] = useState(70); |
||||
const [pastDragCircle, setDragCircle] = useState([]); |
||||
|
||||
const [number, setNumber] = useState(0); |
||||
|
||||
const [formModal, setFormModal] = useState(false); |
||||
|
||||
const [coordCenter, setCoordCenter] = useState([]); // 격자 센터값
|
||||
|
||||
let gimPofeatures = gimpo.features; |
||||
|
||||
useEffect(() => { |
||||
NaverMapInit(); |
||||
|
||||
const coords = []; |
||||
gimPofeatures.map(air => { |
||||
coords.push({ |
||||
lat: |
||||
(Math.max( |
||||
air.geometry.coordinates[0][0][1], |
||||
air.geometry.coordinates[0][1][1], |
||||
air.geometry.coordinates[0][2][1], |
||||
air.geometry.coordinates[0][3][1] |
||||
) + |
||||
Math.min( |
||||
air.geometry.coordinates[0][0][1], |
||||
air.geometry.coordinates[0][1][1], |
||||
air.geometry.coordinates[0][2][1], |
||||
air.geometry.coordinates[0][3][1] |
||||
)) / |
||||
2, |
||||
lng: |
||||
(Math.max( |
||||
air.geometry.coordinates[0][0][0], |
||||
air.geometry.coordinates[0][1][0], |
||||
air.geometry.coordinates[0][2][0], |
||||
air.geometry.coordinates[0][3][0] |
||||
) + |
||||
Math.min( |
||||
air.geometry.coordinates[0][0][0], |
||||
air.geometry.coordinates[0][1][0], |
||||
air.geometry.coordinates[0][2][0], |
||||
air.geometry.coordinates[0][3][0] |
||||
)) / |
||||
2, |
||||
airspace: air.properties.airspace |
||||
}); |
||||
}); |
||||
setCoordCenter(coords); |
||||
|
||||
const FlightPlanAreaMap = (props) => { |
||||
const naver = window.naver; |
||||
const airArea = props.airArea; |
||||
|
||||
const [map, setMap] = useState(); |
||||
const [isMapLoad, setIsMapLoad] = useState(false); |
||||
|
||||
useEffect(() => { |
||||
NaverMapInit(); |
||||
}, []); |
||||
useEffect(() => {
|
||||
setIsMapLoad(true); |
||||
}, [airArea]); |
||||
|
||||
const NaverMapInit = () => { |
||||
const mapOptions = { |
||||
center: new naver.maps.LatLng(36.56793936069445, 127.85101412107547), |
||||
zoom: 10, |
||||
zoomControl: true, |
||||
mapTypeId: naver.maps.MapTypeId.HYBRID, |
||||
zoomControlOptions: { |
||||
position: naver.maps.Position.TOP_LEFT, |
||||
|
||||
style: naver.maps.ZoomControlStyle.SMALL |
||||
} |
||||
};
|
||||
|
||||
setMap(new naver.maps.Map('map', mapOptions));
|
||||
return () => { |
||||
dispatch(AREA_DETAIL_INIT()); |
||||
}; |
||||
}, []); |
||||
|
||||
useEffect(() => { |
||||
pastDragCircle.forEach(c => c.setRadius(dragSize)); |
||||
}, [dragSize]); |
||||
|
||||
return ( |
||||
<Card className='mb-0'> |
||||
<CardBody>
|
||||
<div className='search-cont search-info pd-0'> |
||||
<div className='cont-ti mb-1 d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>지도 영역</h4> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
useEffect(() => { |
||||
setIsMapLoad(true); |
||||
}, [airArea]); |
||||
|
||||
useEffect(() => { |
||||
coordCenter.map((val, idx) => { |
||||
const position = new naver.maps.LatLng( |
||||
val.lat.toFixed(6), |
||||
val.lng.toFixed(6) |
||||
); |
||||
const marker = new naver.maps.Marker({ |
||||
position: position, |
||||
map: map, |
||||
icon: { |
||||
content: `<div style="color:#000000;font-size:10px;">${val.airspace}</div>`, |
||||
size: new naver.maps.Size(0, 0), |
||||
origin: new naver.maps.Point(0, 0), |
||||
anchor: new naver.maps.Point(7, 5), |
||||
align: 'center' |
||||
} |
||||
}); |
||||
}); |
||||
}, [coordCenter]); |
||||
useEffect(() => { |
||||
ModeInit(); |
||||
}, [mapControl.drawType]); |
||||
|
||||
useEffect(() => { |
||||
if (areaCoordList) { |
||||
if ( |
||||
areaCoordList[0].coordList[0].lat !== 0 && |
||||
areaCoordList[0].coordList[0].lon !== 0 |
||||
) { |
||||
if (number === 0) { |
||||
if (map) { |
||||
map.setCenter( |
||||
new naver.maps.LatLng( |
||||
areaCoordList[0].coordList[0].lat, |
||||
areaCoordList[0].coordList[0].lon |
||||
) |
||||
); |
||||
setNumber(number + 1); |
||||
} |
||||
} |
||||
setMapAreaCoordList(areaCoordList); |
||||
} |
||||
} |
||||
}, [areaCoordList, map, number]); |
||||
|
||||
const ModeInit = () => { |
||||
setMode(mapControl.drawType); |
||||
}; |
||||
|
||||
const NaverMapInit = () => { |
||||
const bufferzoom = {}; |
||||
if (areaCoordList) { |
||||
if ( |
||||
areaCoordList[0].bufferZone >= 0 && |
||||
areaCoordList[0].bufferZone < 2000 |
||||
) { |
||||
bufferzoom.bufferzoom = 13; |
||||
} else if ( |
||||
areaCoordList[0].bufferZone >= 2000 && |
||||
areaCoordList[0].bufferZone < 5000 |
||||
) { |
||||
bufferzoom.bufferzoom = 12; |
||||
} else if ( |
||||
areaCoordList[0].bufferZone >= 5000 && |
||||
areaCoordList[0].bufferZone <= 9000 |
||||
) { |
||||
bufferzoom.bufferzoom = 11; |
||||
} else { |
||||
bufferzoom.bufferzoom = 10; |
||||
} |
||||
} |
||||
const mapOptions = { |
||||
// center: new naver.maps.LatLng(36.56793936069445, 127.85101412107547),
|
||||
center: new naver.maps.LatLng(37.558522, 126.793722), |
||||
zoom: !areaCoordList ? 11 : bufferzoom.bufferzoom, |
||||
zoomControl: true, |
||||
mapTypeId: naver.maps.MapTypeId.NORMAL, |
||||
zoomControlOptions: { |
||||
position: naver.maps.Position.RIGHT_CENTER, |
||||
style: naver.maps.ZoomControlStyle.SMALL |
||||
} |
||||
}; |
||||
const mapp = new naver.maps.Map('map', mapOptions); |
||||
// naver.maps.Event.addListener(mapp, 'idle', function () {
|
||||
// // console.log(dragSize);
|
||||
// });
|
||||
naver.maps.Event.addListener(mapp, 'zoom_changed', function () { |
||||
let radius = 17920; |
||||
for (let i = 6; i < 22; i++) { |
||||
if (i == mapp.zoom) { |
||||
setDragSize(radius); |
||||
} |
||||
radius = radius / 2; |
||||
} |
||||
}); |
||||
setMap(mapp); |
||||
}; |
||||
|
||||
const handlerDrawType = val => { |
||||
dispatch(drawTypeChangeAction(val)); |
||||
}; |
||||
|
||||
const handleInitCoordinates = () => { |
||||
const init = initFlightBas.initDetail.areaList.concat(); |
||||
dispatch(AREA_COORDINATE_LIST_SAVE(init)); |
||||
}; |
||||
|
||||
const handleCoordinates = areaInfo => { |
||||
const initAreaList = initFlightBas.initDetail.areaList.concat(); |
||||
|
||||
<div id="map" style={{ width: '100%', height: '50vh'}}></div> |
||||
|
||||
|
||||
{isMapLoad ? (
|
||||
<FeatureAirZone map={map} naver={naver} features={airArea.features} />
|
||||
) : null} |
||||
|
||||
|
||||
|
||||
<div className='d-flex align-items-center mt-2'> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary'
|
||||
> |
||||
Line |
||||
</Button.Ripple> |
||||
<Button.Ripple
|
||||
color='primary'
|
||||
> |
||||
Circle |
||||
</Button.Ripple> |
||||
const coordList = []; |
||||
|
||||
// radius = 10;
|
||||
areaInfo.coordinates.forEach((c, i) => { |
||||
const coord = Object.assign({}, initFlightBas['coord']); |
||||
coord.lat = c.lat; |
||||
coord.lon = c.lon; |
||||
|
||||
coordList.push(coord); |
||||
}); |
||||
|
||||
// initAreaList[0].bufferZone = areaInfo.bufferZone;
|
||||
// initAreaList[0].areaType = areaInfo.areaType;
|
||||
|
||||
const areaList = initAreaList.map((area, i) => { |
||||
return { |
||||
...area, |
||||
bufferZone: areaInfo.bufferZone, |
||||
areaType: areaInfo.areaType, |
||||
coordList: coordList |
||||
}; |
||||
}); |
||||
|
||||
// dispatch(AREA_COORDINATE_LIST_SAVE(areaList))
|
||||
setMapAreaCoordList(areaList); |
||||
}; |
||||
|
||||
const handleSearch = async () => { |
||||
const res = await flightPlanAPI.searchArea({ query: query }); |
||||
setIsSearch(true); |
||||
setSearchRes(res.data.items); |
||||
}; |
||||
|
||||
const handleChange = e => { |
||||
const { name, value } = e.target; |
||||
|
||||
if (name == 'searchInput') { |
||||
setQuery(value); |
||||
} |
||||
}; |
||||
|
||||
const handleCoord = (mapx, mapy) => { |
||||
let utmk = naver.maps.TransCoord.fromTM128ToUTMK( |
||||
naver.maps.Point(mapx, mapy) |
||||
); |
||||
let latlng = naver.maps.TransCoord.fromUTMKToLatLng(utmk); |
||||
|
||||
setIsSearch(false); |
||||
|
||||
let options = { |
||||
duration: 800, |
||||
easing: 'easeOutCubic' |
||||
}; |
||||
map.morph(latlng, 16, options); |
||||
}; |
||||
|
||||
const handleEnter = e => { |
||||
if (e.key == 'Enter') { |
||||
handleSearch(); |
||||
} |
||||
}; |
||||
const handler = () => { |
||||
setFormModal(!formModal); |
||||
}; |
||||
return ( |
||||
<Card className='mb-0'> |
||||
<CardBody> |
||||
<div className='search-cont search-info pd-0'> |
||||
<div className='cont-ti mb-1 d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>지도 영역</h4> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div style={{ position: 'relative' }}> |
||||
<div id='map' style={{ width: '100%', height: '60vh' }}> |
||||
{map ? ( |
||||
<FlightPlanDraw_init |
||||
map={map} |
||||
naver={naver} |
||||
mode={mode} |
||||
areaCoordList={mapAreaCoordList} |
||||
// areaCoordList={areaCoordList}
|
||||
handleCoordinates={handleCoordinates} |
||||
handleInitCoordinates={handleInitCoordinates} |
||||
handleConfirm={props.handleConfirm} |
||||
isDone={props.isDone} |
||||
isDisabled={props.isDisabled} |
||||
dragSize={dragSize} |
||||
pastDragCircle={pastDragCircle} |
||||
setDragCircle={setDragCircle} |
||||
/> |
||||
) : null} |
||||
|
||||
<div className='d-flex search-comp'> |
||||
<div className=''> |
||||
<InputGroup className='search-feather'> |
||||
<InputGroupAddon addonType='prepend'> |
||||
<InputGroupText> |
||||
<Search size={14} onClick={handleSearch} /> |
||||
</InputGroupText> |
||||
</InputGroupAddon> |
||||
<Input |
||||
type='text' |
||||
id='searchInput' |
||||
name='searchInput' |
||||
bsSize='sm' |
||||
autoComplete='off' |
||||
placeholder='검색명을 입력하세요.' |
||||
onChange={handleChange} |
||||
onKeyPress={handleEnter} |
||||
/> |
||||
</InputGroup> |
||||
<div className='search-result-comp'> |
||||
<ul> |
||||
{searchRes?.length !== 0 && isSearch ? ( |
||||
searchRes?.map((prev, idx) => { |
||||
let title = prev.title.replaceAll('<b>', ''); |
||||
title = title.replaceAll('</b>', ''); |
||||
|
||||
return ( |
||||
<li |
||||
key={prev.mapx + prev.mapy} |
||||
onClick={() => handleCoord(prev.mapx, prev.mapy)} |
||||
> |
||||
<a> |
||||
<div className='search-result'> |
||||
<div className='title'> |
||||
<span> |
||||
<strong>{title}</strong> |
||||
</span> |
||||
</div> |
||||
<div className='address'> |
||||
{/* <span>{prev.address}</span> */} |
||||
<span>{prev.roadAddress}</span> |
||||
</div> |
||||
</div> |
||||
</a> |
||||
</li> |
||||
); |
||||
}) |
||||
) : ( |
||||
<></> |
||||
)} |
||||
</ul> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
) |
||||
} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
{isMapLoad ? ( |
||||
<FeatureAirZone map={map} naver={naver} features={airArea.features} /> |
||||
) : null} |
||||
|
||||
<div className='d-flex align-items-center mt-2'> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handlerDrawType('LINE')} |
||||
disabled={props.isDisabled || props.isDone} |
||||
> |
||||
WayPoint |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handlerDrawType('CIRCLE')} |
||||
disabled={props.isDisabled || props.isDone} |
||||
> |
||||
Circle |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
className='mr-1' |
||||
color='primary' |
||||
onClick={e => handlerDrawType('POLYGON')} |
||||
disabled={props.isDisabled || props.isDone} |
||||
> |
||||
Polygon |
||||
</Button.Ripple> |
||||
<Button.Ripple |
||||
color='primary' |
||||
className='mr-1' |
||||
onClick={e => handlerDrawType('RESET')} |
||||
// {...props.test? (
|
||||
// {}
|
||||
// ):(
|
||||
// {disabled:false}
|
||||
// )}
|
||||
disabled={props.isDisabled || props.isDone} |
||||
> |
||||
초기화 |
||||
</Button.Ripple> |
||||
{areaCoordList ? ( |
||||
areaCoordList[0].coordList[0].lat ? ( |
||||
<Button.Ripple color='primary' onClick={handler}> |
||||
날씨 |
||||
</Button.Ripple> |
||||
) : null |
||||
) : ( |
||||
<></> |
||||
)} |
||||
</div> |
||||
<Modal |
||||
isOpen={formModal} |
||||
toggle={handler} |
||||
className='modal-dialog-centered' |
||||
> |
||||
<ModalHeader toggle={handler}>날씨 정보</ModalHeader> |
||||
<ModalBody> |
||||
<WeatherContainer mapAreaCoordList={mapAreaCoordList} /> |
||||
</ModalBody> |
||||
<ModalFooter> |
||||
<Button color='primary' onClick={handler}> |
||||
확인 |
||||
</Button> |
||||
</ModalFooter> |
||||
</Modal> |
||||
</CardBody> |
||||
</Card> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanAreaMap; |
||||
export default FlightPlanAreaMap; |
||||
|
@ -1,49 +0,0 @@
|
||||
import { useState } from 'react'; |
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; |
||||
import FlightPlanAreaContainer from '../../../../containers/basis/flight/plan/FlightPlanAreaContainer'; |
||||
|
||||
export const FlightPlanAreaModal = props => {
|
||||
|
||||
const [onSubmit, setOnSubmit] = useState(false); |
||||
|
||||
return ( |
||||
<div className='vertically-centered-modal'> |
||||
<Modal |
||||
isOpen={props.modal.isOpen} |
||||
toggle={() => |
||||
props.setModal({ ...props.modal, isOpen: !props.modal.isOpen }) |
||||
}
|
||||
className='modal-dialog-centered modal-xl' |
||||
> |
||||
<ModalHeader |
||||
toggle={() => |
||||
props.setModal({ ...props.modal, isOpen: !props.modal.isOpen }) |
||||
} |
||||
> |
||||
{props.modal.title} |
||||
</ModalHeader> |
||||
<ModalBody> |
||||
<FlightPlanAreaContainer |
||||
modal={props.modal} |
||||
setModal={props.setModal}
|
||||
onSubmit={onSubmit}
|
||||
setOnSubmit={setOnSubmit}
|
||||
/> |
||||
</ModalBody> |
||||
{/* <ModalFooter> |
||||
<Button |
||||
type="submit" |
||||
color='primary' |
||||
// onClick={() =>
|
||||
// // props.setModal({ ...props.modal, isOpen: !props.modal.isOpen })
|
||||
// setOnSubmit(true)
|
||||
// }
|
||||
outline |
||||
> |
||||
저장 |
||||
</Button> |
||||
</ModalFooter> */} |
||||
</Modal> |
||||
</div> |
||||
); |
||||
}; |
@ -1,66 +1,143 @@
|
||||
import React from 'react'; |
||||
import { |
||||
Row, |
||||
Col, |
||||
Table, |
||||
Badge, |
||||
UncontrolledDropdown, |
||||
DropdownMenu, |
||||
DropdownItem, |
||||
DropdownToggle, |
||||
Card, |
||||
CardHeader, |
||||
CardBody, |
||||
CardTitle, |
||||
CardSubtitle, |
||||
ButtonGroup, |
||||
Button, |
||||
Input, |
||||
CustomInput, |
||||
FormGroup |
||||
} from 'reactstrap'; |
||||
import { Button, Card, Col, Row, Spinner } from 'reactstrap'; |
||||
import { GridDatabase } from '../../../crud/grid/GridDatatable'; |
||||
import { Redirect } from 'react-router-dom'; |
||||
import { Link, useHistory } from 'react-router-dom'; |
||||
import { useSelector } from 'react-redux'; |
||||
|
||||
const FlightPlanGrid = ({ |
||||
movePage, |
||||
planListData, |
||||
pagination, |
||||
paginationPerPage, |
||||
paginationRowsPerPageOptions, |
||||
isMyGroup, |
||||
user, |
||||
handlerPageChange, |
||||
total |
||||
}) => { |
||||
const { loading } = useSelector(state => state.loadingReducer); |
||||
const columns = [ |
||||
// { id: 'planSno', name: '번호', cell: (row, i) => <div>{i + 1}</div> },
|
||||
{ |
||||
id: 'fltPurpose', |
||||
name: '비행목적', |
||||
cell: row => <div>{row.fltPurpose}</div> |
||||
}, |
||||
{ |
||||
id: 'fltMethod', |
||||
name: '비행방식', |
||||
minWidth: '150px', |
||||
cell: row => { |
||||
const displayName = |
||||
(row.areaList && |
||||
row.areaList.length > 0 && |
||||
row.areaList[0].fltMethod) || |
||||
'-'; |
||||
return <div>{displayName}</div>; |
||||
} |
||||
}, |
||||
{ |
||||
id: 'schFltStDt', |
||||
name: '출발일', |
||||
minWidth: '200px', |
||||
cell: row => <div>{row.schFltStDt}</div> |
||||
}, |
||||
{ id: 'aprvlYn', name: '승인여부', cell: row => <div>{row.aprvlYn}</div> }, |
||||
{ |
||||
id: 'moveDetail', |
||||
name: '상세보기', |
||||
cell: row => { |
||||
return ( |
||||
<Link |
||||
to={`/basis/flight/plan/detail/${row.planSno}?type=plan`} |
||||
size='sm' |
||||
> |
||||
상세보기 |
||||
</Link> |
||||
); |
||||
} |
||||
}, |
||||
{ |
||||
id: 'ORG', |
||||
name: 'ORG', |
||||
cell: row => <div>zzzz</div> |
||||
}, |
||||
{ |
||||
id: 'ETD', |
||||
name: 'ETD', |
||||
cell: row => <div>zzzz</div> |
||||
}, |
||||
{ |
||||
id: 'ATD', |
||||
name: 'ATD', |
||||
cell: row => <div>zzzz</div> |
||||
}, |
||||
{ |
||||
id: 'DES', |
||||
name: 'DES', |
||||
cell: row => <div>zzzz</div> |
||||
}, |
||||
{ |
||||
id: 'ETA', |
||||
name: 'ETA', |
||||
cell: row => <div>zzzz</div> |
||||
}, |
||||
{ |
||||
id: 'ATA', |
||||
name: 'ATA', |
||||
cell: row => <div>zzzz</div> |
||||
} |
||||
]; |
||||
|
||||
const FlightPlanGrid = (props) => { |
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
{/* <h4>{"비행 계획"} 목록</h4> */} |
||||
<h4>{"비행계획서 신청"} 목록</h4> |
||||
<span className='search-case'>검색결과 총 0건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={props.moveFlightPlan} |
||||
> |
||||
{/* 계획서 생성 */} |
||||
비행계획서 신청 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
<> |
||||
<div className='mt-2 cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>비행계획서 신청 목록</h4> |
||||
<span className='search-case'> |
||||
검색결과 총 {!!planListData ? planListData.length : 0}건 |
||||
</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={movePage} |
||||
{...(isMyGroup ? {} : { disabled: true })} |
||||
> |
||||
비행계획서 신청 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
{loading ? ( |
||||
<div className='grid-loading'> |
||||
<div> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
</div> |
||||
) : null} |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
// data={props.data}
|
||||
count={0} |
||||
// columns={props.columns}
|
||||
// pagination={props.pagination}
|
||||
/> |
||||
{/* 검색된 데이터가 없습니다. */} |
||||
</div> |
||||
</Card> |
||||
data={planListData} |
||||
columns={columns} |
||||
count={!!planListData ? planListData.length : 0} |
||||
pagination={pagination} |
||||
paginationPerPage={paginationPerPage} |
||||
paginationRowsPerPageOptions={paginationRowsPerPageOptions} |
||||
handlerPageChange={handlerPageChange} |
||||
total={total} |
||||
/> |
||||
{/* 검색된 데이터가 없습니다. */} |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
) |
||||
} |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanGrid; |
||||
export default FlightPlanGrid; |
||||
|
@ -0,0 +1,105 @@
|
||||
import React from 'react'; |
||||
import { GridDatabase } from '../../../crud/grid/GridDatatable'; |
||||
import { Card, Button } from 'reactstrap'; |
||||
|
||||
const FlightPlanGroupGrid = ({ |
||||
data, |
||||
count, |
||||
selectGroup, |
||||
handlerGroupCancel, |
||||
handleGroupSelect, |
||||
paginationRowsPerPageOptions, |
||||
pagination |
||||
}) => { |
||||
const columns = [ |
||||
{ |
||||
// id: 'groupNm',
|
||||
name: '그룹 명', |
||||
selector: row => row.groupNm, |
||||
minWidth: '102px', |
||||
sortable: true, |
||||
cell: row => <div>{row.groupNm}</div> |
||||
}, |
||||
{ |
||||
// id: 'groupId',
|
||||
name: '그룹 코드', |
||||
selector: row => row.groupId, |
||||
minWidth: '102px', |
||||
sortable: true, |
||||
cell: row => <div>{row.groupId}</div> |
||||
}, |
||||
{ |
||||
name: '생성일시', |
||||
selector: row => row?.createDt, |
||||
sortable: true, |
||||
cell: row => { |
||||
return row?.createDt ? row.createDt : '-'; |
||||
} |
||||
}, |
||||
{ |
||||
name: '가입일시', |
||||
selector: row => row?.joinDt, |
||||
sortable: true, |
||||
cell: row => { |
||||
return row?.joinDt ? row.joinDt : '-'; |
||||
} |
||||
}, |
||||
{ |
||||
sortable: true, |
||||
cell: row => { |
||||
return selectGroup?.groupId === row?.groupId ? ( |
||||
<Button.Ripple |
||||
color='danger' |
||||
className='badge badge-danger' |
||||
onClick={() => handlerGroupCancel()} |
||||
> |
||||
선택취소 |
||||
</Button.Ripple> |
||||
) : ( |
||||
<Button.Ripple |
||||
color='primary' |
||||
className='badge badge-primary' |
||||
onClick={() => { |
||||
handleGroupSelect({ |
||||
groupId: row?.groupId, |
||||
groupNm: row?.groupNm, |
||||
groupAuthCd: row?.groupAuthCd, |
||||
myGroupAuthCd: row?.myGroupAuthCd |
||||
}); |
||||
}} |
||||
> |
||||
상세보기 |
||||
</Button.Ripple> |
||||
); |
||||
} |
||||
} |
||||
]; |
||||
|
||||
return ( |
||||
<> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>그룹 목록</h4> |
||||
<span className='search-case'>검색결과 총 {count}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'></div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
data={data} |
||||
count={count} |
||||
columns={columns} |
||||
pagination={pagination} |
||||
paginationPerPage={5} |
||||
paginationRowsPerPageOptions={paginationRowsPerPageOptions} |
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default FlightPlanGroupGrid; |
@ -0,0 +1,29 @@
|
||||
import { useState } from 'react'; |
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; |
||||
import FlightPlanAreaContainer from '../../../../containers/basis/flight/plan/FlightPlanAreaContainer'; |
||||
|
||||
export const FlightPlanModal = ({isOpen, title, description, type, handleModal}) => {
|
||||
|
||||
return ( |
||||
<div className='vertically-centered-modal'> |
||||
<Modal |
||||
isOpen={isOpen} |
||||
toggle={() => |
||||
handleModal(({target: type, isOpen: false})) |
||||
}
|
||||
className='modal-dialog-centered modal-xl' |
||||
> |
||||
<ModalHeader |
||||
toggle={() => |
||||
handleModal(({target: type, isOpen: false})) |
||||
} |
||||
> |
||||
{title} |
||||
</ModalHeader> |
||||
<ModalBody className='pal-modal-body'> |
||||
{description} |
||||
</ModalBody> |
||||
</Modal> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,157 @@
|
||||
import React from 'react'; |
||||
import {Button, Card, CardBody, Col, CustomInput, Row, FormGroup, Input, Label} from 'reactstrap'; |
||||
import {Search} from 'react-feather'; |
||||
import {GridDatabase} from '../../../crud/grid/GridDatatable'; |
||||
import { AiOutlineSearch } from 'react-icons/ai'; |
||||
import '../../../../assets/css/custom.css'; |
||||
import '@styles/react/libs/tables/react-dataTable-component.scss'; |
||||
|
||||
const FlightPlanPilot = ({ handleSelectPilot, returnPilot, onClickEvent, onChange,name}) => { |
||||
|
||||
const columns = [ |
||||
{id: 'groupNm', name: '그룹 명', cell: row => (<div>{row.groupNm}</div>)}, |
||||
{id: 'memberName', name: '성명', cell: row => (<div>{row.memberName}</div>)}, |
||||
{id: 'hpno', name: '핸드폰 번호', cell: row => (<div>{row.hpno}</div>)}, |
||||
{id: 'email', name: '이메일', cell: row => (<div>{row.email}</div>)}, |
||||
{ |
||||
id: 'selectPilot', name: '선택', cell: row => { |
||||
return <Button.Ripple color='primary' size='sm' onClick={() => {
|
||||
handleSelectPilot(row.cstmrSno)
|
||||
} |
||||
}>선택</Button.Ripple>; |
||||
}
|
||||
} |
||||
]; |
||||
return ( |
||||
<> |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div |
||||
className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>검색조건</h4> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={onClickEvent} |
||||
> |
||||
<Search size={16}/> |
||||
검색 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<Card> |
||||
<CardBody className='pal-card-body'> |
||||
<div className='search-cont'> |
||||
<dl>
|
||||
<dt>
|
||||
<div className='search-box'> |
||||
<div className='search-list-ti'>성명</div> |
||||
<div className='search-list'> |
||||
<div className='search-list-cont'> |
||||
<Row> |
||||
<Col className='list-input' xl='4' md='4' sm='12'> |
||||
<FormGroup className='form-label-group' > |
||||
<Input |
||||
type='text' |
||||
id='memberName' |
||||
name='memberName' |
||||
value = {name} |
||||
onChange = {onChange} |
||||
bsSize='sm' |
||||
// onKeyPress={props.onKeyPress}
|
||||
placeholder='성명을 입력하세요' |
||||
/> |
||||
<Label for='test'>성명</Label> |
||||
</FormGroup> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
|
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
<div className='pal-card-box'>
|
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>조종사 목록</h4> |
||||
<span className='search-case'>검색결과 총 {!!returnPilot ? returnPilot.length : 0}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'></div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase
|
||||
data={returnPilot} |
||||
count={returnPilot ? returnPilot.length : 0} |
||||
columns={columns} |
||||
// pagination={props.pagination}
|
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
{/* {inputVal !== '' ? ( |
||||
<div className='pal-card-box'>
|
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>조종사 목록</h4> |
||||
<span className='search-case'>검색결과 총 {!!returnPilot ? [returnPilot].length : 0}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'></div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase
|
||||
data={[returnPilot]} |
||||
count={returnPilot ? [returnPilot].length : 0} |
||||
columns={columns} |
||||
// pagination={props.pagination}
|
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
):( |
||||
<div className='pal-card-box'>
|
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>조종사 목록</h4> |
||||
<span className='search-case'>검색결과 총 {!!pilotList ? pilotList.length : 0}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'></div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase
|
||||
data={pilotList} |
||||
count={pilotList ? pilotList.length : 0} |
||||
columns={columns} |
||||
// pagination={props.pagination}
|
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</div> |
||||
)} */} |
||||
</> |
||||
) |
||||
|
||||
} |
||||
|
||||
export default FlightPlanPilot; |
@ -0,0 +1,408 @@
|
||||
import { |
||||
Table |
||||
} from 'reactstrap'; |
||||
import { |
||||
Search, |
||||
Compass, |
||||
Sun, |
||||
CloudLightning, |
||||
Cloud, |
||||
CloudRain, |
||||
CloudDrizzle, |
||||
CloudSnow, |
||||
Navigation2 |
||||
} from 'react-feather'; |
||||
|
||||
|
||||
export function TodayWeather({ todayData }) { |
||||
todayData?.fcstTime?.sort([compareFunction]); |
||||
const dayres = todayData?.filter(dayData => { |
||||
switch (dayData?.fcstTime) { |
||||
case "0200": |
||||
case "0500": |
||||
case "0500": |
||||
case "0800": |
||||
case "1100": |
||||
case "1400": |
||||
case "1700": |
||||
case "2000": |
||||
case "2300": |
||||
return { ...dayData?.todayData } |
||||
} |
||||
}); |
||||
// 배열.reduce((누적값, 현잿값, 인덱스, 요소) => { return 결과 }, 초깃값);
|
||||
const groupValues = dayres.reduce((acc, current) => { |
||||
acc[current.fcstTime] = acc[current.fcstTime] || []; |
||||
acc[current.fcstTime].push({ category: current.category, fcstValue: current.fcstValue }); |
||||
return acc; |
||||
}, {}); |
||||
// 위에서 만든 객체를 key로 돌려서 새로운 객체 return
|
||||
const groups = Object.keys(groupValues).map((key) => { |
||||
return { fcstTime: key, category: groupValues[key] }; |
||||
}); |
||||
|
||||
return ( |
||||
<> |
||||
{dayres.length > 0 ? ( |
||||
|
||||
<Table responsive> |
||||
<thead> |
||||
<tr> |
||||
<th>시각</th> |
||||
<th>날씨</th> |
||||
<th>기온</th> |
||||
<th>풍향</th> |
||||
<th>풍속</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{groups.map((i, index) => { |
||||
return ( |
||||
<tr key={index}> |
||||
<td >{i.fcstTime.substring(0, 2)}시</td> |
||||
{i.category[4].fcstValue == "0" ? |
||||
<td> |
||||
{i.category[3].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{i.category[4].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{i.category[0].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${i.category[1]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{i.category[2].fcstValue} m/s</td> |
||||
</tr> |
||||
) |
||||
})} |
||||
</tbody> |
||||
</Table> |
||||
) : |
||||
null} |
||||
</> |
||||
) |
||||
} |
||||
|
||||
|
||||
export function TomorrowWeahter({ tomorrowData }) { |
||||
tomorrowData?.fcstTime?.sort([compareFunction]); |
||||
const dayres = tomorrowData?.filter(dayData => { |
||||
switch (dayData?.fcstTime) { |
||||
case "0200": |
||||
case "0500": |
||||
case "0500": |
||||
case "0800": |
||||
case "1100": |
||||
case "1400": |
||||
case "1700": |
||||
case "2000": |
||||
case "2300": |
||||
return { ...dayData?.tomorrowData } |
||||
} |
||||
}); |
||||
return ( |
||||
<Table responsive> |
||||
<thead> |
||||
<tr> |
||||
<th>시각</th> |
||||
<th>날씨</th> |
||||
<th>기온</th> |
||||
<th>풍향</th> |
||||
<th>풍속</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>{dayres[0]?.fcstTime.substring(0, 2)}시</td> |
||||
{dayres[3].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[3].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[4].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[0].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[1]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[2].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[5].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[8].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[8].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[9].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[5].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[6]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[7].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[10].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[13].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[13].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[14].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[10].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[11]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[12].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[15].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[18].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[18].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[19].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[15].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[16]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[17].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[20].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[23].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[23].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[24].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[20].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[21]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[22].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[25].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[28].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[28].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[29].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[25].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[26]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[27].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[30].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[33].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[33].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[34].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[30].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[31]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[32].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[35].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[38].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[38].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[39].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[35].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[36]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[37].fcstValue} m/s</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
) |
||||
} |
||||
export function AfterTomorrowWeahter({ afterData }) { |
||||
afterData?.fcstTime?.sort([compareFunction]); |
||||
const dayres = afterData?.filter(dayData => { |
||||
switch (dayData?.fcstTime) { |
||||
case "0200": |
||||
case "0500": |
||||
case "0500": |
||||
case "0800": |
||||
case "1100": |
||||
case "1400": |
||||
case "1700": |
||||
case "2000": |
||||
case "2300": |
||||
return { ...dayData?.afterData } |
||||
} |
||||
}); |
||||
|
||||
return ( |
||||
<Table responsive> |
||||
<thead> |
||||
<tr> |
||||
<th>시각</th> |
||||
<th>날씨</th> |
||||
<th>기온</th> |
||||
<th>풍향</th> |
||||
<th>풍속</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>{dayres[0].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[3].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[3].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[4].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[0].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[1]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[2].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[5].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[8].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[8].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[9].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[5].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[6]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[7].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[10].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[13].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[13].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[14].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[10].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[11]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[12].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[15].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[18].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[18].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[19].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[15].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[16]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[17].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[20].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[23].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[23].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[24].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[20].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[21]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[22].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[25].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[28].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[28].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[29].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[25].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[26]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[27].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[30].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[33].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[33].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[34].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[30].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[31]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[32].fcstValue} m/s</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{dayres[35].fcstTime.substring(0, 2)}시</td> |
||||
{dayres[38].fcstValue != "0" ? |
||||
<td> |
||||
{dayres[38].fcstValue == "1" ? <Sun /> : <Cloud />} |
||||
</td> |
||||
: <td> |
||||
{dayres[39].fcstValue == "3" ? <CloudSnow /> : <CloudRain />} |
||||
</td> |
||||
} |
||||
<td>{dayres[35].fcstValue}℃</td> |
||||
<td> |
||||
<Navigation2 style={{ transform: `rotate(${dayres[36]?.fcstValue}deg)` }} /> |
||||
</td> |
||||
<td>{dayres[37].fcstValue} m/s</td> |
||||
</tr> |
||||
</tbody> |
||||
</Table> |
||||
) |
||||
} |
@ -0,0 +1,344 @@
|
||||
import { useRef, useState } from 'react'; |
||||
import { GridDatabase } from '@src/components/crud/grid/GridDatatable'; |
||||
import { Row, Col, Card, Spinner } from 'reactstrap'; |
||||
import FlightScheduleRealTime from './FlightScheduleRealTime'; |
||||
import { useSelector } from 'react-redux'; |
||||
import moment from 'moment'; |
||||
|
||||
/** |
||||
* 비행 전 : B |
||||
* 비행 중 : F |
||||
* 비행 완료 : S |
||||
*/ |
||||
function FlightScheduleGrid() { |
||||
const timeRef = useRef(null); |
||||
|
||||
const { scheduleList } = useSelector(state => state.flightState); |
||||
const { loading } = useSelector(state => state.loadingReducer); |
||||
|
||||
const fillZero = (width, str) => { |
||||
return str.length >= width |
||||
? str |
||||
: new Array(width - str.length + 1).join('0') + str; //남는 길이만큼 0으로 채움
|
||||
}; |
||||
|
||||
const columns = [ |
||||
{ |
||||
name: '그룹 명', |
||||
selector: row => row.groupNm, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return row.groupNm; |
||||
} |
||||
}, |
||||
{ |
||||
name: '기체 소유자', |
||||
selector: row => row.ownerNm, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return row.ownerNm; |
||||
} |
||||
}, |
||||
{ |
||||
name: '기체 식별번호', |
||||
selector: row => row.idntfNum, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return row.idntfNum; |
||||
} |
||||
}, |
||||
{ |
||||
name: '출발지', |
||||
selector: row => row.startAddress, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return row.startAddress; |
||||
} |
||||
}, |
||||
{ |
||||
name: '비행 시작 시간', |
||||
selector: row => row.schFltStDt, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return moment(row.schFltStDt).format('YYYY-MM-DD HH:mm'); |
||||
} |
||||
}, |
||||
{ |
||||
name: '도착지', |
||||
selector: row => row.endAddress, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return row.endAddress; |
||||
} |
||||
}, |
||||
{ |
||||
name: '비행 종료 시간', |
||||
selector: row => row.schFltEndDt, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
return moment(row.schFltEndDt).format('YYYY-MM-DD HH:mm'); |
||||
} |
||||
}, |
||||
{ |
||||
name: '총 예상 비행시간', |
||||
selector: row => row.groupNm, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
const endDate = moment(row.schFltEndDt); |
||||
const startDate = moment(row.schFltStDt); |
||||
|
||||
const hours = Math.floor( |
||||
moment.duration(endDate.diff(startDate)).asHours() |
||||
); |
||||
|
||||
const minute = |
||||
moment.duration(endDate.diff(startDate)).asMinutes() % 60; |
||||
|
||||
return `${fillZero(2, String(hours))} : ${fillZero(2, String(minute))}`; |
||||
} |
||||
}, |
||||
{ |
||||
name: '상태', |
||||
selector: row => row.statusCd, |
||||
minWidth: '150px', |
||||
sortable: true, |
||||
cell: row => { |
||||
switch (row.statusCd) { |
||||
case 'B': |
||||
return '비행전'; |
||||
case 'F': |
||||
return '비행중'; |
||||
default: |
||||
return '비행완료'; |
||||
} |
||||
} |
||||
} |
||||
]; |
||||
|
||||
const conditionalRowStyles = [ |
||||
{ |
||||
when: row => row.statusCd === 'S', |
||||
classNames: ['secondary'] |
||||
}, |
||||
{ |
||||
when: row => { |
||||
if (row.statusCd !== 'S') { |
||||
// 현재시간
|
||||
const currTime = |
||||
timeRef?.current?.lastChild?.data |
||||
.replace(/\:/g, '') |
||||
.substring(0, 4) || ''; |
||||
|
||||
// 현재날짜
|
||||
const currDay = Number(moment().format('YYYYMMDD')); |
||||
const currDateTime = moment(); |
||||
|
||||
// 데이터 시작날짜
|
||||
// const itemStartDay = Number(moment(row.schFltStDt).format('YYYYMMDD'));
|
||||
// 데이터 종료날짜
|
||||
const itemEndDay = Number(moment(row.schFltEndDt).format('YYYYMMDD')); |
||||
|
||||
const itemStartTime = moment(row.schFltStDt).format('HHmm'); |
||||
const itemEndTime = moment(row.schFltEndDt); |
||||
const diffTime = |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 > |
||||
-0 |
||||
? moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 |
||||
: Math.ceil( |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % |
||||
60 |
||||
); |
||||
|
||||
// 현재날짜 - 종료날짜
|
||||
if (currDay - itemEndDay > 0) { |
||||
return false; |
||||
} |
||||
if (row.statusCd === 'B') { |
||||
if ( |
||||
currTime - itemStartTime >= 10 && |
||||
currTime - itemStartTime <= 19 |
||||
) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
if (row.statusCd === 'F') { |
||||
if (diffTime >= 10 && diffTime <= 19) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
}, |
||||
classNames: ['yellow'] |
||||
}, |
||||
{ |
||||
when: row => { |
||||
if (row.statusCd !== 'S') { |
||||
// 현재시간
|
||||
const currTime = timeRef.current.lastChild.data |
||||
.replace(/\:/g, '') |
||||
.substring(0, 4); |
||||
// 현재날짜
|
||||
const currDay = Number(moment().format('YYYYMMDD')); |
||||
const currDateTime = moment(); |
||||
|
||||
// 데이터 시작날짜
|
||||
// const itemStartDay = Number(moment(row.schFltStDt).format('YYYYMMDD'));
|
||||
// 데이터 종료날짜
|
||||
const itemEndDay = Number(moment(row.schFltEndDt).format('YYYYMMDD')); |
||||
|
||||
const itemStartTime = moment(row.schFltStDt).format('HHmm'); |
||||
const itemEndTime = moment(row.schFltEndDt); |
||||
const diffTime = |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 > |
||||
-0 |
||||
? moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 |
||||
: Math.ceil( |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % |
||||
60 |
||||
); |
||||
|
||||
// 현재날짜 - 종료날짜
|
||||
if (currDay - itemEndDay > 0) { |
||||
return false; |
||||
} |
||||
// 비행전 체크
|
||||
if (row.statusCd === 'B') { |
||||
if ( |
||||
currTime - itemStartTime >= 20 && |
||||
currTime - itemStartTime <= 29 |
||||
) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
// 비행중 체크
|
||||
if (row.statusCd === 'F') { |
||||
if (diffTime >= 20 && diffTime <= 29) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
}, |
||||
classNames: ['warning'] |
||||
}, |
||||
{ |
||||
when: row => { |
||||
if (row.statusCd !== 'S') { |
||||
// 현재시간
|
||||
const currTime = timeRef.current.lastChild.data |
||||
.replace(/\:/g, '') |
||||
.substring(0, 4); |
||||
// 현재날짜
|
||||
const currDay = Number(moment().format('YYYYMMDD')); |
||||
const currDateTime = moment(); |
||||
// 데이터 시작날짜
|
||||
// const itemStartDay = Number(moment(row.schFltStDt).format('YYYYMMDD'));
|
||||
// 데이터 종료날짜
|
||||
const itemEndDay = Number(moment(row.schFltEndDt).format('YYYYMMDD')); |
||||
|
||||
const itemStartTime = moment(row.schFltStDt).format('HHmm'); |
||||
const itemEndTime = moment(row.schFltEndDt); |
||||
const diffTime = |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 > |
||||
-0 |
||||
? moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % 60 |
||||
: Math.ceil( |
||||
moment.duration(currDateTime.diff(itemEndTime)).asMinutes() % |
||||
60 |
||||
); |
||||
|
||||
// 현재날짜 - 종료날짜
|
||||
if (currDay - itemEndDay > 0) { |
||||
return true; |
||||
} |
||||
// 비행전 체크
|
||||
if (row.statusCd === 'B') { |
||||
if (currTime - itemStartTime >= 30) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
// 비행중 체크
|
||||
if (row.statusCd === 'F') { |
||||
if (currDay - itemEndDay < 0) { |
||||
return false; |
||||
} |
||||
if (diffTime > 30) { |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
} |
||||
return false; |
||||
}, |
||||
classNames: ['danger'] |
||||
} |
||||
]; |
||||
|
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div |
||||
style={{ |
||||
display: 'flex', |
||||
justifyContent: 'space-between', |
||||
alignItems: 'center', |
||||
width: '100%' |
||||
}} |
||||
> |
||||
<div style={{ display: 'flex', alignItems: 'center' }}> |
||||
<h4>비행운항 스케줄 목록</h4> |
||||
<span className='search-case'> |
||||
검색결과 총 {scheduleList.length}건 |
||||
</span> |
||||
</div> |
||||
<FlightScheduleRealTime ref={timeRef} /> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
{loading ? ( |
||||
<div className='grid-loading'> |
||||
<div> |
||||
<Spinner color='primary' /> |
||||
<span>Loading...</span> |
||||
</div> |
||||
</div> |
||||
) : null} |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={scheduleList} |
||||
count={scheduleList.length} |
||||
columns={columns} |
||||
pagination={false} |
||||
conditionalRowStyles={conditionalRowStyles} |
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default FlightScheduleGrid; |
@ -0,0 +1,22 @@
|
||||
import { useState, forwardRef } from 'react'; |
||||
import moment from 'moment'; |
||||
import useInterval from '@src/utility/hooks/useInterval'; |
||||
|
||||
const KR_TIME_DIFF = 9 * 60 * 60 * 1000; |
||||
|
||||
const FlightScheduleRealTime = forwardRef(({}, ref) => { |
||||
const [currTime, setCurrTime] = useState(new Date()); |
||||
const utc = currTime.getTime() + currTime.getTimezoneOffset() * 60 * 1000; |
||||
const kr_curr = new Date(utc + KR_TIME_DIFF); |
||||
|
||||
useInterval(() => { |
||||
setCurrTime(new Date()); |
||||
setTime(moment(new Date()).format('HH:mm:ss')); |
||||
}, [1000]); |
||||
|
||||
const [time, setTime] = useState(moment(kr_curr).format('HH:mm:ss')); |
||||
|
||||
return <div ref={ref}>현재시간 : {time.toString()}</div>; |
||||
}); |
||||
|
||||
export default FlightScheduleRealTime; |
@ -0,0 +1,92 @@
|
||||
import { useEffect, useState } from 'react'; |
||||
import { Row, Col, Button, Card, CardBody } from 'reactstrap'; |
||||
import { Search, Calendar } from 'react-feather'; |
||||
import moment from 'moment'; |
||||
import Flatpickr from 'react-flatpickr'; |
||||
import { FLIGHT_SCHEDULE_LIST } from '@src/modules/basis/flight/actions/basisFlightAction'; |
||||
import { useDispatch } from 'react-redux'; |
||||
import useInterval from '@src/utility/hooks/useInterval'; |
||||
|
||||
function FlightScheduleSearch() { |
||||
const dispatch = useDispatch(); |
||||
|
||||
const [date, setDate] = useState( |
||||
moment().subtract('day').format('YYYY-MM-DD') |
||||
); |
||||
const [isSearch, setIsSearch] = useState(false); |
||||
|
||||
useEffect(() => { |
||||
setIsSearch(false); |
||||
}, [date]); |
||||
|
||||
useInterval(() => { |
||||
if (isSearch) { |
||||
dispatch(FLIGHT_SCHEDULE_LIST.request(date)); |
||||
} |
||||
}, [10000]); |
||||
|
||||
const handlerChangeDate = val => { |
||||
setDate(moment(val[0]).format('YYYY-MM-DD')); |
||||
}; |
||||
|
||||
const handlerSearch = () => { |
||||
setIsSearch(true); |
||||
dispatch(FLIGHT_SCHEDULE_LIST.request(date)); |
||||
}; |
||||
|
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>검색조건</h4> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple color='primary' size='sm' onClick={handlerSearch}> |
||||
<Search size={16} /> |
||||
검색 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<Card> |
||||
<CardBody className='pal-card-body'> |
||||
<div className='search-cont'> |
||||
<dl> |
||||
<dt> |
||||
<div className='search-box'> |
||||
<div className='search-list-ti'>일자</div> |
||||
<div className='search-list'> |
||||
<div className='search-list-cont'> |
||||
<Row> |
||||
<Col className='list-input' xl='4' md='6' sm='12'> |
||||
<div className='d-flex align-items-center calendar-flat'> |
||||
<Flatpickr |
||||
placeholder='날짜를 선택해주세요' |
||||
id='searchDate' |
||||
value={date} |
||||
options={{ |
||||
mode: 'single' |
||||
}} |
||||
onChange={handlerChangeDate} |
||||
className='form-control flat-picker bg-transparent border-0 shadow-none' |
||||
/> |
||||
<Calendar size={14} /> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</dt> |
||||
</dl> |
||||
</div> |
||||
</CardBody> |
||||
</Card> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default FlightScheduleSearch; |
@ -1,61 +1,33 @@
|
||||
import { GridDatabase } from '../../crud/grid/GridDatatable'; |
||||
import { |
||||
Row, |
||||
Col, |
||||
Table, |
||||
Badge, |
||||
UncontrolledDropdown, |
||||
DropdownMenu, |
||||
DropdownItem, |
||||
DropdownToggle, |
||||
Card, |
||||
CardHeader, |
||||
CardBody, |
||||
CardTitle, |
||||
CardSubtitle, |
||||
ButtonGroup, |
||||
Button, |
||||
Input, |
||||
CustomInput, |
||||
FormGroup |
||||
} from 'reactstrap'; |
||||
import { Card } from 'reactstrap'; |
||||
import { ExcelExportButton } from '../../crud/excel/ExcelExportButton'; |
||||
|
||||
export const BasisGroupApprovalGrid = props => { |
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.count}건</span> |
||||
</div> |
||||
{/* <div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={props.handlerGroupCreate} |
||||
> |
||||
그룹생성 |
||||
</Button.Ripple> |
||||
</div> */} |
||||
<> |
||||
<div className='mt-2 cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.total}건</span> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
paginationPerPage={props.paginationPerPage} |
||||
paginationRowsPerPageOptions={props.paginationRowsPerPageOptions} |
||||
handlerPageChange={props.handlerPageChange} |
||||
total={props.total} |
||||
/> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
@ -1,61 +1,30 @@
|
||||
import { GridDatabase } from '../../crud/grid/GridDatatable'; |
||||
import { |
||||
Row, |
||||
Col, |
||||
Table, |
||||
Badge, |
||||
UncontrolledDropdown, |
||||
DropdownMenu, |
||||
DropdownItem, |
||||
DropdownToggle, |
||||
Card, |
||||
CardHeader, |
||||
CardBody, |
||||
CardTitle, |
||||
CardSubtitle, |
||||
ButtonGroup, |
||||
Button, |
||||
Input, |
||||
CustomInput, |
||||
FormGroup |
||||
} from 'reactstrap'; |
||||
import { ExcelExportButton } from '../../crud/excel/ExcelExportButton'; |
||||
import { Card, Row, Col, Button } from 'reactstrap'; |
||||
|
||||
export const BasisGroupGrid = props => { |
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.count}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={props.handlerGroupCreate} |
||||
> |
||||
그룹생성 |
||||
</Button.Ripple> |
||||
</div> |
||||
<> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.count}건</span> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
paginationPerPage={props.paginationPerPage} |
||||
paginationRowsPerPageOptions={props.paginationRowsPerPageOptions} |
||||
/> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
); |
||||
}; |
||||
|
@ -0,0 +1,46 @@
|
||||
import { GridDatabase } from '../../crud/grid/GridDatatable'; |
||||
import { Card, Row, Col, Button } from 'reactstrap'; |
||||
|
||||
export const BasisGroupMyGrid = props => { |
||||
return ( |
||||
<div className='pal-card-box'> |
||||
<Row> |
||||
<Col> |
||||
<> |
||||
<div className='cont-ti d-flex justify-content-between align-items-sm-center align-items-start flex-sm-row'> |
||||
<div> |
||||
<h4>{props.title} 목록</h4> |
||||
<span className='search-case'>검색결과 총 {props.count}건</span> |
||||
</div> |
||||
<div className='d-flex align-items-center'> |
||||
<Button.Ripple |
||||
color='primary' |
||||
size='sm' |
||||
onClick={props.handlerGroupCreate} |
||||
> |
||||
그룹생성 |
||||
</Button.Ripple> |
||||
</div> |
||||
</div> |
||||
<div className='invoice-list-wrapper'> |
||||
<Card> |
||||
{/* <div className='grid-loading'> |
||||
<div><Spinner color='primary' /><span>Loading...</span></div> |
||||
</div> */} |
||||
<div className='invoice-list-dataTable'> |
||||
<GridDatabase |
||||
title={'비행이력'} |
||||
data={props.data} |
||||
count={props.count} |
||||
columns={props.columns} |
||||
pagination={props.pagination} |
||||
/> |
||||
</div> |
||||
</Card> |
||||
</div> |
||||
</> |
||||
</Col> |
||||
</Row> |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,24 @@
|
||||
import { CustomInput, Input } from 'reactstrap'; |
||||
import React from 'react'; |
||||
|
||||
export const selectableRowsComponent = React.forwardRef( |
||||
({ onClick, ...rest }, ref) => { |
||||
let c = new Date().getTime() + Math.random(); |
||||
return ( |
||||
<div |
||||
className='custom-checkbox custom-control' |
||||
style={{ paddingLeft: 0 }} |
||||
> |
||||
<input |
||||
className='custom-control-input' |
||||
id={c} |
||||
type='checkbox' |
||||
ref={ref} |
||||
onClick={onClick} |
||||
{...rest} |
||||
/> |
||||
<label className='custom-control-label' htmlFor={c} /> |
||||
</div> |
||||
); |
||||
} |
||||
); |