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

feat: Login UI improvements #1207

Merged
merged 11 commits into from
Oct 10, 2023
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,5 @@ To configure Keycloak:
- KEYCLOAK_ISSUER: http://localhost:8080/realms/master

[keycloak-admin]: http://localhost:8080/admin/master/console/#/

Remember to add NEXTAUTH_SECRET variable to your .env file!
3 changes: 1 addition & 2 deletions app/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import { DataSourceMenu } from "@/components/data-source-menu";
import Flex from "@/components/flex";
import { LanguageMenu } from "@/components/language-menu";
import { SOURCE_OPTIONS } from "@/domain/datasource/constants";

import LoginMenu from "./login-menu";
import { LoginMenu } from "@/login/components/login-menu";

const DEFAULT_HEADER_PROGRESS = 100;

Expand Down
6 changes: 3 additions & 3 deletions app/configurator/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { makeStyles } from "@mui/styles";
import clsx from "clsx";
import React from "react";

import { HEADER_HEIGHT } from "@/components/header";
import { DRAWER_WIDTH } from "@/configurator/components/drawer";

const useStyles = makeStyles((theme: Theme) => ({
Expand All @@ -28,10 +29,9 @@ const useStyles = makeStyles((theme: Theme) => ({
},
panelLayout: {
position: "fixed",
// FIXME replace 96px with actual header size
top: 96,
top: HEADER_HEIGHT,
width: "100%",
height: "calc(100vh - 96px)",
height: `calc(100vh - ${HEADER_HEIGHT}px)`,
display: "grid",
gridTemplateColumns: `${DRAWER_WIDTH}px minmax(22rem, 1fr)`,
gridTemplateRows: "auto minmax(0, 1fr)",
Expand Down
21 changes: 12 additions & 9 deletions app/db/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Config, Prisma, User } from "@prisma/client";

import { ChartConfig } from "@/configurator";
import { ChartConfig, ConfiguratorStatePublished } from "@/configurator";
import { migrateConfiguratorState } from "@/utils/chart-config/versioning";

import { createChartId } from "../utils/create-chart-id";
Expand Down Expand Up @@ -57,13 +57,16 @@ const ensureFiltersOrder = (chartConfig: ChartConfig) => {
};
};

type ChartJsonConfig = {
dataSet: string;
chartConfig: Prisma.JsonObject;
};

const parseDbConfig = (d: Config) => {
const data = d.data as ChartJsonConfig;
const parseDbConfig = (
d: Config
): {
id: number;
key: string;
data: ConfiguratorStatePublished;
created_at: Date;
user_id: number | null;
} => {
const data = d.data as ConfiguratorStatePublished;
const migratedData = migrateConfiguratorState(data);

return {
Expand All @@ -84,7 +87,7 @@ const parseDbConfig = (d: Config) => {
export const getConfig = async (key: string) => {
const config = await prisma.config.findFirst({
where: {
key: key,
key,
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Button, Typography } from "@mui/material";
import { getProviders, signIn, signOut, useSession } from "next-auth/react";
import { getProviders, signIn, useSession } from "next-auth/react";
import Link from "next/link";
import { useEffect, useState } from "react";

Expand All @@ -12,63 +12,49 @@ const useProviders = () => {
status: "loading",
data: undefined as Providers | undefined,
});

useEffect(() => {
const run = async () => {
const providers = await getProviders();
setState({ status: "loaded", data: providers });
};

run();
}, []);

return state;
};

function LoginMenu() {
export const LoginMenu = () => {
const { data: session, status: sessionStatus } = useSession();
const { data: providers, status: providersStatus } = useProviders();

if (sessionStatus === "loading" || providersStatus === "loading") {
return null;
}

if (!providers || !Object.keys(providers).length) {
return null;
}

return (
<Box sx={{ alignItems: "center", display: "flex" }}>
{session ? (
<>
<Typography variant="body2">
Signed in as{" "}
<Link href="/profile" legacyBehavior>
{session.user?.name}
</Link>{" "}
{" - "}
</Typography>
<Button
variant="text"
color="primary"
size="small"
onClick={async () => await signOut()}
>
Sign out
</Button>
</>
<Typography variant="body2">
<Link href="/profile" legacyBehavior>
{session.user?.name}
</Link>{" "}
</Typography>
) : (
<>
<Typography variant="body2">
Not signed in
{" - "}
</Typography>
<Button
variant="text"
color="primary"
size="small"
onClick={() => signIn("keycloak")}
>
Sign in
</Button>
</>
<Button
variant="text"
color="primary"
size="small"
onClick={() => signIn("keycloak")}
>
Sign in
</Button>
)}
</Box>
);
}

export default LoginMenu;
};
88 changes: 88 additions & 0 deletions app/login/components/profile-content-tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { TabContext, TabList, TabPanel } from "@mui/lab";
import { Box, Tab, Theme, Typography } from "@mui/material";
import { makeStyles } from "@mui/styles";
import clsx from "clsx";
import React from "react";

import { ParsedConfig } from "@/db/config";
import { ProfileVisualizationsTable } from "@/login/components/profile-tables";
import { useRootStyles } from "@/login/utils";
import useEvent from "@/utils/use-event";

const useStyles = makeStyles<Theme>((theme) => ({
section: {
marginTop: theme.spacing(6),
},
tabList: {
minHeight: "fit-content",

"& .MuiTabs-flexContainer": {
height: "fit-content",
},
},
tabPanel: {
padding: 0,
},
tabPanelContent: {
padding: theme.spacing(6),
},
tab: {
height: 48,
minHeight: 0,
padding: "0 1rem",
textTransform: "none",
},
}));

type ProfileContentTabsProps = {
userConfigs: ParsedConfig[];
};

export const ProfileContentTabs = (props: ProfileContentTabsProps) => {
const { userConfigs } = props;
const [value, setValue] = React.useState("Home");
const handleChange = useEvent((_: React.SyntheticEvent, v: string) => {
setValue(v);
});
const rootClasses = useRootStyles();
const classes = useStyles();

return (
<TabContext value={value}>
<Box className={clsx(rootClasses.section, classes.section)}>
<Box className={rootClasses.sectionContent}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<TabList className={classes.tabList} onChange={handleChange}>
{["Home", "My visualizations", "My favorite datasets"].map(
(d) => (
<Tab key={d} className={classes.tab} label={d} value={d} />
)
)}
</TabList>
</Box>
</Box>
</Box>
<TabPanel className={classes.tabPanel} value="Home">
<Box className={classes.tabPanelContent}>
<ProfileVisualizationsTable userConfigs={userConfigs} />
</Box>
</TabPanel>
<TabPanel className={classes.tabPanel} value="My visualizations">
<Box
className={classes.tabPanelContent}
sx={{ bgcolor: "background.paper" }}
>
<ProfileVisualizationsTable userConfigs={userConfigs} />
</Box>
</TabPanel>
<TabPanel className={classes.tabPanel} value="My favorite datasets">
<Box
className={classes.tabPanelContent}
sx={{ bgcolor: "background.paper" }}
>
<Typography variant="h2">My favorite datasets</Typography>
</Box>
</TabPanel>
</TabContext>
);
};
54 changes: 54 additions & 0 deletions app/login/components/profile-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Box, Button, Link, Theme, Typography } from "@mui/material";
import { makeStyles } from "@mui/styles";
import { User } from "@prisma/client";
import clsx from "clsx";
import { signOut } from "next-auth/react";

import { useRootStyles } from "@/login/utils";

const useStyles = makeStyles<Theme>((theme) => ({
section: {
paddingTop: theme.spacing(6),
backgroundColor: theme.palette.muted.main,
},
topRow: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
},
browseButton: {
width: "fit-content",
},
}));

type ProfileHeaderProps = {
user: User;
};

export const ProfileHeader = (props: ProfileHeaderProps) => {
const { user } = props;
const rootClasses = useRootStyles();
const classes = useStyles();

return (
<Box className={clsx(rootClasses.section, classes.section)}>
<Box className={rootClasses.sectionContent}>
<Box className={classes.topRow}>
<Typography variant="h1">{user.name}</Typography>
<Button component={Link} href="/browse">
Browse all datasets
</Button>
</Box>
<Button
className={classes.browseButton}
variant="text"
color="primary"
size="small"
onClick={async () => await signOut()}
>
Log out
</Button>
</Box>
</Box>
);
};
Loading