diff --git a/backend/backend.log b/backend/backend.log index fec13a91..978ebf1e 100644 --- a/backend/backend.log +++ b/backend/backend.log @@ -7,37 +7,275 @@ @app.on_event("startup") INFO: Will watch for changes in these directories: ['/home/xsl/code/ocean_project_manager/backend'] INFO: Uvicorn running on http://0.0.0.0:8188 (Press CTRL+C to quit) -INFO: Started reloader process [6520] using WatchFiles -INFO: Started server process [6522] +INFO: Started reloader process [4044] using WatchFiles +INFO: Started server process [4143] INFO: Waiting for application startup. INFO: Application startup complete. -INFO: 127.0.0.1:38698 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:57886 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:57900 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:43792 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:47906 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:47906 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:57272 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:57288 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:60220 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:60230 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:49220 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:49232 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:43984 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:43998 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:35788 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:35788 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:56510 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:59614 - "POST /api/v1/auth/login HTTP/1.1" 200 OK -INFO: 127.0.0.1:39442 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK -INFO: 127.0.0.1:39456 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK -INFO: 127.0.0.1:39462 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK -INFO: 127.0.0.1:39476 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK -INFO: 127.0.0.1:39482 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity -INFO: 127.0.0.1:39486 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK -INFO: 127.0.0.1:39502 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity -INFO: 127.0.0.1:39506 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK -INFO: 127.0.0.1:39522 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity -INFO: 127.0.0.1:39530 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity -INFO: 127.0.0.1:33492 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK -INFO: 127.0.0.1:33496 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:58538 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:59360 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59362 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:56816 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:56828 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:56840 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:56842 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:56854 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:56862 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading... +INFO: Shutting down +INFO: Waiting for application shutdown. +INFO: Application shutdown complete. +INFO: Finished server process [4143] +INFO: Started server process [8844] +INFO: Waiting for application startup. +INFO: Application startup complete. +WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading... +INFO: Shutting down +INFO: Waiting for application shutdown. +INFO: Application shutdown complete. +INFO: Finished server process [8844] +INFO: Started server process [9834] +INFO: Waiting for application startup. +INFO: Application startup complete. +WARNING: WatchFiles detected changes in 'config/import-excel-data-v2.py'. Reloading... +INFO: Shutting down +INFO: Waiting for application shutdown. +INFO: Application shutdown complete. +INFO: Finished server process [9834] +INFO: Started server process [9977] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: 127.0.0.1:52704 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:52712 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:52714 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:52728 - "GET /api/v1/projects/statistics?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:52730 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:52744 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:55242 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55248 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55252 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK +INFO: 127.0.0.1:40794 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:40804 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40806 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:49642 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:49646 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:49662 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:36846 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:36848 - "GET /api/v1/projects/statistics HTTP/1.1" 401 Unauthorized +INFO: 127.0.0.1:57848 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:54960 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:54970 - "GET /api/v1/projects/statistics HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:42982 - "GET /api/v1/projects?limit=1 HTTP/1.1" 401 Unauthorized +INFO: 127.0.0.1:48152 - "GET /api/v1/projects?limit=1 HTTP/1.1" 200 OK +INFO: 127.0.0.1:36282 - "GET /api/v1/projects/statistics HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:36266 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53870 - "GET /api/v1/projects?page=1&page_size=1 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46572 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:46582 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:58766 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:53146 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:53150 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53166 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:53176 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:57068 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:57054 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:57084 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:57092 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:57096 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:57108 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50094 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:50104 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50120 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:50122 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50138 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50140 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50152 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50164 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50180 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:50182 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50192 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50202 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:47624 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK +INFO: 127.0.0.1:47640 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:47654 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:47668 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:47662 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47676 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40950 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:42386 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:42398 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46604 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:40432 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:40418 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40436 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:40440 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40442 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40450 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40480 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:40464 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:40486 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:40502 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:54970 - "POST /api/v1/auth/logout HTTP/1.1" 200 OK +INFO: 127.0.0.1:54980 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:54984 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:54992 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55000 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:54998 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:56704 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55252 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55260 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:39804 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:39806 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:39816 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:39822 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:56744 - "POST /api/v1/auth/login HTTP/1.1" 200 OK +INFO: 127.0.0.1:56760 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:46382 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46398 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50542 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50560 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50550 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:50570 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50584 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:50592 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:34382 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:34398 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:34394 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:34410 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:43320 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:43324 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55352 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55364 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55374 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55384 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55400 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55390 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59952 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:59962 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59966 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:59974 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46372 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46384 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46778 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK +INFO: 127.0.0.1:56132 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:56136 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:34614 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:34620 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41252 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41266 - "GET /api/v1/projects/514 HTTP/1.1" 200 OK +INFO: 127.0.0.1:44226 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:44218 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:44234 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:44246 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:45244 - "GET /api/v1/projects/517 HTTP/1.1" 200 OK +INFO: 127.0.0.1:45248 - "GET /api/v1/projects/517 HTTP/1.1" 200 OK +INFO: 127.0.0.1:48548 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:48546 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:48556 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:41330 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41336 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:41350 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:41360 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:41368 - "GET /api/v1/projects/statistics/basic?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:41374 - "GET /api/v1/projects/statistics/timeline?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 422 Unprocessable Entity +INFO: 127.0.0.1:41380 - "GET /api/v1/projects/statistics/group?group_by=engineering_type&time_field=signing_date&group_by_time=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:52902 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:53544 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:35154 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:35164 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:35166 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55172 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:55178 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:55194 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:59502 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:59504 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51184 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:35166 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:35174 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:35156 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:35186 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:35184 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:35198 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:50886 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:50902 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50914 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:50926 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50932 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50940 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:51086 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51100 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:51116 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:51132 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51140 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:51146 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:58160 - "GET /api/v1/projects/statistics/timeline?time_field=signing_date&group_by=month HTTP/1.1" 200 OK +INFO: 127.0.0.1:58176 - "GET /api/v1/projects/statistics/group?group_by=engineering_type HTTP/1.1" 200 OK +INFO: 127.0.0.1:58192 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51614 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51620 - "GET /api/v1/projects/statistics/basic HTTP/1.1" 200 OK +INFO: 127.0.0.1:51618 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:51624 - "GET /api/v1/projects?page=1&page_size=5 HTTP/1.1" 200 OK +INFO: 127.0.0.1:35502 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:35518 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:48470 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:45706 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:60248 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:48410 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53666 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:38252 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:38854 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:38862 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53224 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53234 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41768 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41770 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41772 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK +INFO: 127.0.0.1:41784 - "GET /api/v1/projects/518 HTTP/1.1" 200 OK +INFO: 127.0.0.1:44564 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:44578 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:60338 - "GET /api/v1/projects/515 HTTP/1.1" 200 OK +INFO: 127.0.0.1:60344 - "GET /api/v1/projects/515 HTTP/1.1" 200 OK +INFO: 127.0.0.1:51570 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:51578 - "GET /api/v1/projects?page=1&page_size=10 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47272 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47278 - "GET /api/v1/projects?page=1&page_size=10&keyword=31 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46604 - "GET /api/v1/projects?page=1&page_size=10&keyword=317 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46616 - "GET /api/v1/projects?page=1&page_size=10&keyword=3175 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46622 - "GET /api/v1/projects?page=1&page_size=10&keyword=317 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46634 - "GET /api/v1/projects?page=1&page_size=10&keyword=31 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46642 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK +INFO: 127.0.0.1:46654 - "GET /api/v1/projects?page=1&page_size=10&keyword=3 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59518 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59524 - "GET /api/v1/projects?page=1&page_size=10&keyword=22 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59534 - "GET /api/v1/projects?page=1&page_size=10&keyword=225 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59548 - "GET /api/v1/projects?page=1&page_size=10&keyword=2253 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59564 - "GET /api/v1/projects?page=1&page_size=10&keyword=225 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59570 - "GET /api/v1/projects?page=1&page_size=10&keyword=22 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59574 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:59590 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47084 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47096 - "GET /api/v1/projects?page=1&page_size=10&keyword=20 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47112 - "GET /api/v1/projects?page=1&page_size=10&keyword=202 HTTP/1.1" 200 OK +INFO: 127.0.0.1:47118 - "GET /api/v1/projects?page=1&page_size=10&keyword=2022 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50184 - "GET /api/v1/projects?page=1&page_size=10&keyword=20225 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50196 - "GET /api/v1/projects?page=1&page_size=10&keyword=202252 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50204 - "GET /api/v1/projects?page=1&page_size=10&keyword=20225 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50206 - "GET /api/v1/projects?page=1&page_size=10&keyword=2022 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50220 - "GET /api/v1/projects?page=1&page_size=10&keyword=202 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50222 - "GET /api/v1/projects?page=1&page_size=10&keyword=20 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50228 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:50240 - "GET /api/v1/projects?page=1&page_size=10&keyword=2 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53746 - "GET /api/v1/projects?page=1&page_size=10&keyword=t HTTP/1.1" 200 OK +INFO: 127.0.0.1:53756 - "GET /api/v1/projects?page=1&page_size=10&keyword=ti HTTP/1.1" 200 OK +INFO: 127.0.0.1:53764 - "GET /api/v1/projects?page=1&page_size=10&keyword=ti%27a HTTP/1.1" 200 OK +INFO: 127.0.0.1:53758 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao HTTP/1.1" 200 OK +INFO: 127.0.0.1:53780 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27s HTTP/1.1" 200 OK +INFO: 127.0.0.1:53786 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27sh HTTP/1.1" 200 OK +INFO: 127.0.0.1:53798 - "GET /api/v1/projects?page=1&page_size=10&keyword=tiao%27shi HTTP/1.1" 200 OK +INFO: 127.0.0.1:53814 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83%E8%AF%95 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53830 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53842 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E8%B0%83 HTTP/1.1" 200 OK +INFO: 127.0.0.1:53846 - "GET /api/v1/projects?page=1&page_size=10&keyword=l HTTP/1.1" 200 OK +INFO: 127.0.0.1:53858 - "GET /api/v1/projects?page=1&page_size=10&keyword=la HTTP/1.1" 200 OK +INFO: 127.0.0.1:53870 - "GET /api/v1/projects?page=1&page_size=10&keyword=lan HTTP/1.1" 200 OK +INFO: 127.0.0.1:53882 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E7%83%82 HTTP/1.1" 200 OK +INFO: 127.0.0.1:39108 - "GET /api/v1/projects?page=1&page_size=10&keyword=%E7%83%82 HTTP/1.1" 200 OK diff --git a/backend/config/import-excel-data-v2.py b/backend/config/import-excel-data-v2.py index c621fd75..ec802e17 100644 --- a/backend/config/import-excel-data-v2.py +++ b/backend/config/import-excel-data-v2.py @@ -20,7 +20,7 @@ from sqlalchemy.orm import declarative_base from sqlalchemy import text # 配置 -EXCEL_FILE = '../docs/example.xls' +EXCEL_FILE = '/home/xsl/code/ocean_project_manager/docs/example.xls' DATABASE_URL = "mysql+aiomysql://root:rootpassword@localhost:3306/project_manager?charset=utf8mb4" # 创建引擎 @@ -195,7 +195,7 @@ async def import_projects_from_sheet(sheet, sheet_name, admin_id): 'profit': parse_decimal_value(safe_get(39)), 'actual_profit': parse_decimal_value(safe_get(40)), 'cost_settlement_amount': parse_decimal_value(safe_get(41)), - 'cumulative_progress': parse_decimal_value(safe_get(42)), + 'cumulative_progress': (lambda x: x if x is None else min(x, Decimal('999.99')))(parse_decimal_value(safe_get(42))), 'receivable_amount': parse_decimal_value(safe_get(43)), 'invoice_amount': parse_decimal_value(safe_get(44)), 'actual_receipt_amount': parse_decimal_value(safe_get(45)), diff --git a/frontend/frontend.log b/frontend/frontend.log index 7cd6b059..081b7335 100644 --- a/frontend/frontend.log +++ b/frontend/frontend.log @@ -3,7 +3,7 @@ > vite - VITE v5.4.21 ready in 131 ms + VITE v5.4.21 ready in 145 ms ➜ Local: http://localhost:8189/ ➜ Network: use --host to expose diff --git a/frontend/public/test.html b/frontend/public/test.html new file mode 100644 index 00000000..70e0a47d --- /dev/null +++ b/frontend/public/test.html @@ -0,0 +1,45 @@ + + + + API测试 + + +

API测试页面

+ +
+ + + + \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5518d12c..6ccb3e90 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -5,6 +5,7 @@ import Layout from './components/Layout/Layout'; import Login from './pages/Login/Login'; import Dashboard from './pages/Dashboard/Dashboard'; import ProjectList from './pages/Projects/ProjectList'; +import ProjectDetail from './pages/Projects/ProjectDetail'; import UserList from './pages/Users/UserList'; import Statistics from './pages/Statistics/Statistics'; @@ -48,6 +49,14 @@ function App() { } /> + + + + } + /> { + const navigate = useNavigate(); const [statistics, setStatistics] = useState(null); const [recentProjects, setRecentProjects] = useState([]); const [loading, setLoading] = useState(true); @@ -14,13 +16,18 @@ const Dashboard = () => { try { setLoading(true); + console.log('开始获取仪表盘数据...'); + const [statsResponse, projectsResponse] = await Promise.all([ - authAPI.getStatistics(), + projectAPI.getStatistics(), projectAPI.getList({ page: 1, page_size: 5 }), ]); - setStatistics(statsResponse); - setRecentProjects(projectsResponse.items || []); + console.log('统计API响应:', statsResponse); + console.log('项目列表API响应:', projectsResponse); + + setStatistics(statsResponse.data || statsResponse); + setRecentProjects(projectsResponse.data?.items || projectsResponse.items || []); } catch (error) { console.error('Failed to fetch dashboard data:', error); } finally { @@ -44,17 +51,9 @@ const Dashboard = () => {
{statistics?.total_count || 0}
-
进行中
+
平均进度(%)
- {(statistics?.total_count || 0) - - (statistics?.completed_count || 0) - - (statistics?.cancelled_count || 0) || 0} -
-
-
-
已完成
-
- {statistics?.completed_count || 0} + {statistics?.avg_cumulative_progress?.toFixed(1) || '0.0'}
@@ -66,6 +65,12 @@ const Dashboard = () => { })}
+
+
回款完成率(%)
+
+ {statistics?.avg_receipt_completion_rate?.toFixed(1) || '0.0'} +
+
@@ -93,7 +98,7 @@ const Dashboard = () => { onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }} - onClick={() => window.location.href = `/projects/${project.id}`} + onClick={() => navigate(`/projects/${project.id}`)} >
{ + const { id } = useParams(); + const navigate = useNavigate(); + const [project, setProject] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchProjectDetail(); + }, [id]); + + const fetchProjectDetail = async () => { + try { + setLoading(true); + const result = await projectAPI.getDetail(id); + setProject(result.data || result); + } catch (error) { + console.error('Failed to fetch project detail:', error); + alert('获取项目详情失败'); + } finally { + setLoading(false); + } + }; + + if (loading) { + return
加载中...
; + } + + if (!project) { + return
项目不存在
; + } + + return ( +
+
+ +
+ +
+
+ 项目详情 +
+ +
+
+ +
+ {project.project_no || '-'} +
+
+ +
+ +
+ {project.name || '-'} +
+
+ +
+ +
+ {project.engineering_type || '-'} +
+
+ +
+ +
+ {project.project_department || '-'} +
+
+ +
+ +
+ {project.contract_amount ? Number(project.contract_amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '-'} +
+
+ +
+ +
+ {project.project_leader || '-'} +
+
+ +
+ +
+ {project.owner_unit || '-'} +
+
+ +
+ +
+ {project.owner_contact || '-'} +
+
+ +
+ +
+ {project.signing_date || '-'} +
+
+ +
+ +
+ {project.start_date || '-'} +
+
+ +
+ +
+ {project.planned_end_date || '-'} +
+
+ +
+ +
+ {project.actual_end_date || '-'} +
+
+ +
+ +
+ {project.cumulative_progress ? Number(project.cumulative_progress).toFixed(2) : '-'} +
+
+ +
+ +
+ {project.actual_receipt_amount ? Number(project.actual_receipt_amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '-'} +
+
+ +
+ +
+ {project.receipt_completion_rate ? Number(project.receipt_completion_rate).toFixed(2) : '-'} +
+
+ +
+ +
+ {project.status || '-'} +
+
+
+ + {project.payment_method && ( +
+ +
+ {project.payment_method} +
+
+ )} + + {project.problems && ( +
+ +
+ {project.problems} +
+
+ )} + + {project.suggestions && ( +
+ +
+ {project.suggestions} +
+
+ )} + + {project.remarks && ( +
+ +
+ {project.remarks} +
+
+ )} + +
+
+ 创建时间:{project.created_at} | 创建人:{project.created_by_name || '-'} +
+
+
+
+ ); +}; + +export default ProjectDetail; \ No newline at end of file diff --git a/frontend/src/pages/Projects/ProjectList.jsx b/frontend/src/pages/Projects/ProjectList.jsx index 2fb79a0a..045f7cf1 100644 --- a/frontend/src/pages/Projects/ProjectList.jsx +++ b/frontend/src/pages/Projects/ProjectList.jsx @@ -41,8 +41,9 @@ const ProjectList = ({ mode = 'list' }) => { ...filters, ...params, }); - setList(result.items || []); - setTotal(result.total || 0); + const data = result.data || result; + setList(data.items || []); + setTotal(data.total || 0); } catch (error) { console.error('Failed to fetch projects:', error); alert('获取项目列表失败'); @@ -72,7 +73,8 @@ const ProjectList = ({ mode = 'list' }) => { const handleEdit = async (id) => { try { - const project = await projectAPI.getDetail(id); + const result = await projectAPI.getDetail(id); + const project = result.data || result; setForm(project); setEditingId(id); setModalVisible(true); @@ -143,6 +145,23 @@ const ProjectList = ({ mode = 'list' }) => { }); }; + const handleChange = (e) => { + const { name, value } = e.target; + setForm({ ...form, [name]: value }); + }; + + const handleFilterChange = (e) => { + const { name, value } = e.target; + const newFilters = { ...filters }; + if (value) { + newFilters[name] = value; + } else { + delete newFilters[name]; + } + setFilters(newFilters); + fetchProjects(newFilters); + }; + const canEdit = (createdBy) => { return user.role === 'admin' || (user.role === 'market' && createdBy === user?.id); }; @@ -157,10 +176,13 @@ const ProjectList = ({ mode = 'list' }) => { {mode === 'create' ? '新建项目' : '项目列表'} -
+
{ }} /> + + + + + {user?.role === 'admin' && (
-
+
项目总数
{statistics?.total_count || 0}
-
总投资金额(万元)
+
平均进度(%)
- {(statistics?.total_investment || 0).toLocaleString('zh-CN', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })} + {Number(statistics?.avg_cumulative_progress || 0).toFixed(1)}
总合同金额(万元)
- {(statistics?.total_contract_amount || 0).toLocaleString('zh-CN', { + {Number(statistics?.total_contract_amount || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2, })}
-
总收款金额(万元)
+
回款完成率(%)
- {(statistics?.total_receipt_amount || 0).toLocaleString('zh-CN', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })} + {Number(statistics?.avg_receipt_completion_rate || 0).toFixed(1)}
@@ -336,16 +330,16 @@ const Statistics = () => { borderBottom: '1px solid #f0f0f0', }} > - - {item.month} - + + {item.month || item.day || item.year} + api.delete(`/projects/${id}`), - getStatistics: (params) => api.get('/projects/statistics', { params }), + getStatistics: (params) => api.get('/projects/statistics/basic', { params }), getGroupStatistics: (params) => api.get('/projects/statistics/group', { params }), diff --git a/frontend/src/styles/index.css b/frontend/src/styles/index.css index 773ee75a..ff2b96d2 100644 --- a/frontend/src/styles/index.css +++ b/frontend/src/styles/index.css @@ -48,7 +48,7 @@ body { background: #ffffff; border-right: 1px solid #e8e8e8; position: fixed; - left: 64px; + left: 0; top: 64px; bottom: 0; overflow-y: auto;