Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login table #614

Merged
merged 11 commits into from
Jan 24, 2025
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@ storybook-static
public/locales*

.VSCodeCounter

# editor backup/swap files
*.swp
*.bak
3 changes: 3 additions & 0 deletions public/static/locales/en/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@
"goToVice": "Go to analysis",
"instantLaunches": "Instant Launches",
"integratedBy": "Integrated by",
"ipAddress": "IP Address",
"launchAction": "Launch",
"launchAria": "launch",
"learnMore": "Learn More",
"login": "Login",
"loginTime": "Login Time",
"loginsTableError": "Unable to get recent logins.",
"loginSignUp": "Login or sign-up to access additional features.",
"newsFeed": "News",
"noAnalysesStats": "Looks like you haven't run any analyses yet.",
Expand Down
84 changes: 84 additions & 0 deletions src/components/dashboard/dashboardItem/LoginTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
*
* @author mian
*
* A table of recent logins for the user.
*
*/

import React from "react";
import { useQuery } from "react-query";

import { logins, LOGINS_QUERY_KEY } from "serviceFacades/users";
import ErrorTypographyWithDialog from "components/error/ErrorTypographyWithDialog";
import { useTranslation } from "i18n";
import {
formatDate,
getFormattedDistance,
} from "components/utils/DateFormatter";
import {
useTheme,
Skeleton,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from "@mui/material";

export default function LoginsTable() {
const { t } = useTranslation("dashboard");
const { t: i18nCommon } = useTranslation("common");

const theme = useTheme();
const { status, data, error } = useQuery({
queryKey: [LOGINS_QUERY_KEY],
queryFn: () => logins({ limit: 5 }),
});

if (status === "error") {
return (
<div style={{ padding: theme.spacing(1) }}>
<ErrorTypographyWithDialog
errorObject={error}
errorMessage={t("loginsTableError")}
/>
</div>
);
}
if (status === "loading") {
return <Skeleton variant="rectangular" height={200} />;
}
return (
<TableContainer component={Paper}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>{t("loginTime")}</TableCell>
<TableCell></TableCell>
<TableCell>{t("ipAddress")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data["logins"].map((row) => (
<TableRow>
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
<TableCell>
{i18nCommon("timestamp", {
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
timestamp: getFormattedDistance(
row["login_time"] / 1000
),
})}
</TableCell>
<TableCell>
({formatDate(row["login_time"])})
</TableCell>
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
<TableCell>{row["ip_address"]}</TableCell>
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
8 changes: 7 additions & 1 deletion src/components/dashboard/dashboardItem/ResourceUsageItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {

import DataConsumption from "./DataConsumption";
import AnalysesStats from "./AnalysesStats";
import LoginTable from "./LoginTable";
import CPUConsumption from "./CPUConsumption";
import ExternalLink from "components/utils/ExternalLink";
import ErrorTypographyWithDialog from "components/error/ErrorTypographyWithDialog";
Expand Down Expand Up @@ -145,11 +146,16 @@ export default function ResourceUsageItem(props) {
</Card>
</Grid>
)}
<Grid item xs={12} md={12}>
<Grid item xs={12} md={6}>
<Card>
<AnalysesStats />
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card>
<LoginTable />
</Card>
</Grid>
</Grid>
</>
)}
Expand Down
10 changes: 10 additions & 0 deletions src/server/api/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ export default function userRouter() {
})
);

logger.info("adding the GET /logins handler");
api.get(
"/logins",
auth.authnTokenMiddleware,
terrainHandler({
method: "GET",
pathname: "/secured/logins",
})
);

logger.info("adding the POST /preferences handler");
api.post(
"/preferences",
Expand Down
12 changes: 12 additions & 0 deletions src/serviceFacades/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const WEBHOOKS_TOPICS_QUERY_KEY = "fetchHookTopics";
const WEBHOOK_TEST_KEY = "testWebhook";
const USER_PORTAL_QUERY_KEY = "fetchUserPortalStatus";
const USER_PORTAL_DETAILS_QUERY_KEY = "fetchUserPortalDetails;";
const LOGINS_QUERY_KEY = "logins";

const getUserInfo = ({ userIds }) => {
const userQuery = userIds.join("&username=");
Expand All @@ -41,6 +42,15 @@ function bootstrap() {
});
}

function logins({ limit }) {
console.log(`limit: ${limit}`);
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
return callApi({
endpoint: `/api/logins?limit=${limit}`,
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
ianmcorvidae marked this conversation as resolved.
Show resolved Hide resolved
method: "GET",
credentials: "include",
});
}

function savePreferences({ preferences, webhooks }) {
let promises = [];

Expand Down Expand Up @@ -198,10 +208,12 @@ export {
WEBHOOKS_TOPICS_QUERY_KEY,
WEBHOOKS_TYPES_QUERY_KEY,
WEBHOOK_TEST_KEY,
LOGINS_QUERY_KEY,
getUserInfo,
getUserPortalDetails,
getUserProfile,
bootstrap,
logins,
savePreferences,
resetToken,
getRedirectURIs,
Expand Down
Loading