page.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. 'use client';
  2. import './style.scss';
  3. import { useState, useEffect } from 'react';
  4. import { UAParser } from "ua-parser-js";
  5. import { LoginLogType } from '@/constants/common';
  6. import { fetchLoginLog } from '@/lib/api/account';
  7. import type { LoginLog } from '@/types/account/loginLog';
  8. import Loading from '@/app/component/Loading';
  9. import Pagination from '@/app/component/Pagination';
  10. import NavTabs from '../navTabs';
  11. export default function LoginLog()
  12. {
  13. const [error, setError] = useState<string>('');
  14. const [loading, setLoading] = useState<boolean>(true);
  15. const [page, setPage] = useState<number>(1);
  16. const [type, setType] = useState<LoginLogType>(LoginLogType.Today);
  17. const [total, setTotal] = useState<number>(0);
  18. const [logs, setLogs] = useState<LoginLog[]>([]);
  19. useEffect(() => {
  20. if (error) {
  21. alert(error);
  22. setError('');
  23. }
  24. }, [error]);
  25. useEffect(() => {
  26. setLoading(true);
  27. fetchLoginLog(type, page).then((res) => {
  28. if (!res.ok) {
  29. throw new Error(res.message!);
  30. }
  31. setTotal(res.data.total);
  32. setLogs(res.data.list);
  33. }).catch(err => {
  34. setError(err.message);
  35. }).finally(() => {
  36. setLoading(false);
  37. });
  38. }, [type, page]);
  39. useEffect(() => {
  40. setPage(1);
  41. }, [type]);
  42. const tabItems = [
  43. { label: "오늘", value: LoginLogType.Today },
  44. { label: "1주일", value: LoginLogType.Week },
  45. { label: "1개월", value: LoginLogType.Month },
  46. { label: "3개월", value: LoginLogType.QuarterYear },
  47. { label: "6개월", value: LoginLogType.HalfYear }
  48. ];
  49. return (
  50. <>
  51. <NavTabs />
  52. <div id="loginLog" >
  53. { loading && <Loading /> }
  54. <h1>로그인 기록</h1>
  55. <table className="table-auto max-2xl:w-full 2xl:w-[1000px]">
  56. <caption>
  57. <div className="grid grid-cols-[auto,1fr] gap-4 items-center">
  58. <div>
  59. 합계: {total}
  60. </div>
  61. <div id="loginTypeTab" className="justify-self-end">
  62. {tabItems.map((item, i) => (
  63. <button key={i} className={`flex-1 py-2 px-4 text-center text-sm font-medium
  64. ${type === item.value ? "border-b-2 border-blue-500 text-blue-500" : "text-gray-500"}
  65. `} onClick={() => setType(item.value)}>{item.label}</button>
  66. ))}
  67. </div>
  68. </div>
  69. </caption>
  70. <colgroup>
  71. <col width="20%"/>
  72. <col width="*"/>
  73. <col width="15%"/>
  74. <col width="15%"/>
  75. <col width="15%"/>
  76. </colgroup>
  77. <thead>
  78. <tr>
  79. <th>일시</th>
  80. <th>접속 IP</th>
  81. <th>접속 기기</th>
  82. <th>브라우저</th>
  83. <th>결과</th>
  84. </tr>
  85. </thead>
  86. <tbody>
  87. {logs.length > 0 ? (
  88. logs.map((log) => {
  89. const parser = new UAParser();
  90. parser.setUA(log.userAgent);
  91. const browser = `${parser.getBrowser().name ?? '알 수 없음'}`;
  92. const device = `${parser.getOS().name ?? '알 수 없음'}`;
  93. return (
  94. <tr key={log.id} className="hover:bg-gray-100">
  95. <td>{log.createdAt}</td>
  96. <td>{log.ipAddress}</td>
  97. <td>{device}</td>
  98. <td>{browser}</td>
  99. <td className={`border p-2 font-semibold ${
  100. log.success ? "text-green-500" : "text-red-500"
  101. }`}
  102. >
  103. {log.success ? "성공" : "실패"}
  104. </td>
  105. </tr>
  106. );
  107. })
  108. ) : (
  109. <tr>
  110. <td colSpan={5} className="border p-2 text-center text-gray-500">
  111. 기록이 없습니다.
  112. </td>
  113. </tr>
  114. )}
  115. </tbody>
  116. <tfoot>
  117. <tr>
  118. <td colSpan={5}>
  119. <Pagination total={total} page={page} perPage={20} onChange={setPage} />
  120. </td>
  121. </tr>
  122. </tfoot>
  123. </table>
  124. </div>
  125. </>
  126. );
  127. }