logs.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { useMemo, useState } from "react";
  2. import { useRecoilState } from "recoil";
  3. import {
  4. Box,
  5. Button,
  6. IconButton,
  7. MenuItem,
  8. Paper,
  9. Select,
  10. TextField,
  11. } from "@mui/material";
  12. import { Virtuoso } from "react-virtuoso";
  13. import { useTranslation } from "react-i18next";
  14. import {
  15. PlayCircleOutlineRounded,
  16. PauseCircleOutlineRounded,
  17. } from "@mui/icons-material";
  18. import { atomEnableLog, atomLogData } from "@/services/states";
  19. import { BaseEmpty, BasePage } from "@/components/base";
  20. import LogItem from "@/components/log/log-item";
  21. import { useCustomTheme } from "@/components/layout/use-custom-theme";
  22. const LogPage = () => {
  23. const { t } = useTranslation();
  24. const [logData, setLogData] = useRecoilState(atomLogData);
  25. const [enableLog, setEnableLog] = useRecoilState(atomEnableLog);
  26. const { theme } = useCustomTheme();
  27. const isDark = theme.palette.mode === "dark";
  28. const [logState, setLogState] = useState("all");
  29. const [filterText, setFilterText] = useState("");
  30. const filterLogs = useMemo(() => {
  31. return logData.filter((data) => {
  32. return (
  33. data.payload.includes(filterText) &&
  34. (logState === "all" ? true : data.type.includes(logState))
  35. );
  36. });
  37. }, [logData, logState, filterText]);
  38. return (
  39. <BasePage
  40. full
  41. title={t("Logs")}
  42. contentStyle={{ height: "100%" }}
  43. header={
  44. <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
  45. <IconButton
  46. size="small"
  47. color="inherit"
  48. onClick={() => setEnableLog((e) => !e)}
  49. >
  50. {enableLog ? (
  51. <PauseCircleOutlineRounded />
  52. ) : (
  53. <PlayCircleOutlineRounded />
  54. )}
  55. </IconButton>
  56. <Button
  57. size="small"
  58. variant="contained"
  59. onClick={() => setLogData([])}
  60. >
  61. {t("Clear")}
  62. </Button>
  63. </Box>
  64. }
  65. >
  66. <Box
  67. sx={{
  68. pt: 1,
  69. mb: 0.5,
  70. mx: "10px",
  71. height: "36px",
  72. display: "flex",
  73. alignItems: "center",
  74. }}
  75. >
  76. <Select
  77. size="small"
  78. autoComplete="off"
  79. value={logState}
  80. onChange={(e) => setLogState(e.target.value)}
  81. sx={{
  82. width: 120,
  83. height: 33.375,
  84. mr: 1,
  85. '[role="button"]': { py: 0.65 },
  86. }}
  87. >
  88. <MenuItem value="all">ALL</MenuItem>
  89. <MenuItem value="inf">INFO</MenuItem>
  90. <MenuItem value="warn">WARN</MenuItem>
  91. <MenuItem value="err">ERROR</MenuItem>
  92. </Select>
  93. <TextField
  94. hiddenLabel
  95. fullWidth
  96. size="small"
  97. autoComplete="off"
  98. spellCheck="false"
  99. variant="outlined"
  100. placeholder={t("Filter conditions")}
  101. value={filterText}
  102. onChange={(e) => setFilterText(e.target.value)}
  103. sx={{ input: { py: 0.65, px: 1.25 } }}
  104. />
  105. </Box>
  106. <Box
  107. height="calc(100% - 65px)"
  108. sx={{
  109. margin: "10px",
  110. borderRadius: "8px",
  111. bgcolor: isDark ? "#282a36" : "#ffffff",
  112. }}
  113. >
  114. {filterLogs.length > 0 ? (
  115. <Virtuoso
  116. initialTopMostItemIndex={999}
  117. data={filterLogs}
  118. itemContent={(index, item) => <LogItem value={item} />}
  119. followOutput={"smooth"}
  120. />
  121. ) : (
  122. <BaseEmpty text="No Logs" />
  123. )}
  124. </Box>
  125. </BasePage>
  126. );
  127. };
  128. export default LogPage;