aboutsummaryrefslogtreecommitdiffstats
path: root/webui/src/pages/identity
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2021-04-23 00:16:57 +0200
committerGitHub <noreply@github.com>2021-04-23 00:16:57 +0200
commita8f3b55986982db5f7c3918acaba2c214c919d11 (patch)
tree19706066a71c32684979b019aa62525481ff4891 /webui/src/pages/identity
parent271ca35d19a9649f6d0512760aed70c8778822fc (diff)
parent774bae6d432c13cfa0de3ddc2f111927743243fe (diff)
downloadgit-bug-a8f3b55986982db5f7c3918acaba2c214c919d11.tar.gz
Merge pull request #605 from GlancingMind/upstream-12-allow-users-to-inspect-their-current-identity-details
WebUI: Add user profile
Diffstat (limited to 'webui/src/pages/identity')
-rw-r--r--webui/src/pages/identity/BugList.tsx73
-rw-r--r--webui/src/pages/identity/GetBugsByUser.graphql12
-rw-r--r--webui/src/pages/identity/GetUserStatistic.graphql13
-rw-r--r--webui/src/pages/identity/Identity.tsx147
-rw-r--r--webui/src/pages/identity/IdentityQuery.tsx24
-rw-r--r--webui/src/pages/identity/index.tsx1
6 files changed, 270 insertions, 0 deletions
diff --git a/webui/src/pages/identity/BugList.tsx b/webui/src/pages/identity/BugList.tsx
new file mode 100644
index 00000000..fbddb0fe
--- /dev/null
+++ b/webui/src/pages/identity/BugList.tsx
@@ -0,0 +1,73 @@
+import React from 'react';
+
+import { Card, Divider, Link, Typography } from '@material-ui/core';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import { makeStyles } from '@material-ui/core/styles';
+
+import Date from '../../components/Date';
+
+import { useGetBugsByUserQuery } from './GetBugsByUser.generated';
+
+const useStyles = makeStyles((theme) => ({
+ main: {
+ ...theme.typography.body2,
+ },
+ bugLink: {
+ ...theme.typography.button,
+ },
+ cards: {
+ backgroundColor: theme.palette.background.default,
+ color: theme.palette.info.contrastText,
+ padding: theme.spacing(1),
+ margin: theme.spacing(1),
+ },
+}));
+
+type Props = {
+ id: string;
+};
+
+function BugList({ id }: Props) {
+ const classes = useStyles();
+ const { loading, error, data } = useGetBugsByUserQuery({
+ variables: {
+ query: 'author:' + id + ' sort:creation',
+ },
+ });
+
+ if (loading) return <CircularProgress />;
+ if (error) return <p>Error: {error}</p>;
+ const bugs = data?.repository?.allBugs.nodes;
+
+ return (
+ <div className={classes.main}>
+ {bugs?.map((bug, index) => {
+ return (
+ <Card className={classes.cards} key={index}>
+ <Typography variant="overline" component="h2">
+ <Link
+ className={classes.bugLink}
+ href={'/bug/' + bug.id}
+ color={'inherit'}
+ >
+ {bug.title}
+ </Link>
+ </Typography>
+ <Divider />
+ <Typography variant="subtitle2">
+ Created&nbsp;
+ <Date date={bug.createdAt} />
+ </Typography>
+ <Typography variant="subtitle2">
+ Last edited&nbsp;
+ <Date date={bug.createdAt} />
+ </Typography>
+ </Card>
+ );
+ })}
+ {bugs?.length === 0 && <p>No authored bugs by this user found.</p>}
+ </div>
+ );
+}
+
+export default BugList;
diff --git a/webui/src/pages/identity/GetBugsByUser.graphql b/webui/src/pages/identity/GetBugsByUser.graphql
new file mode 100644
index 00000000..0f170dc1
--- /dev/null
+++ b/webui/src/pages/identity/GetBugsByUser.graphql
@@ -0,0 +1,12 @@
+query GetBugsByUser ($query: String){
+ repository {
+ allBugs(query: $query) {
+ nodes {
+ id
+ title
+ createdAt
+ lastEdit
+ }
+ }
+ }
+}
diff --git a/webui/src/pages/identity/GetUserStatistic.graphql b/webui/src/pages/identity/GetUserStatistic.graphql
new file mode 100644
index 00000000..318b860d
--- /dev/null
+++ b/webui/src/pages/identity/GetUserStatistic.graphql
@@ -0,0 +1,13 @@
+query GetUserStatistic($authorQuery: String!, $participantQuery: String!, $actionQuery: String!) {
+ repository {
+ authored: allBugs(query: $authorQuery) {
+ totalCount
+ },
+ participated: allBugs(query: $participantQuery) {
+ totalCount
+ }
+ actions: allBugs(query: $actionQuery) {
+ totalCount
+ }
+ }
+}
diff --git a/webui/src/pages/identity/Identity.tsx b/webui/src/pages/identity/Identity.tsx
new file mode 100644
index 00000000..5170eeea
--- /dev/null
+++ b/webui/src/pages/identity/Identity.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { Link as RouterLink } from 'react-router-dom';
+
+import { Link, Paper, Typography } from '@material-ui/core';
+import Avatar from '@material-ui/core/Avatar';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import Grid from '@material-ui/core/Grid';
+import { makeStyles } from '@material-ui/core/styles';
+import InfoIcon from '@material-ui/icons/Info';
+import MailOutlineIcon from '@material-ui/icons/MailOutline';
+
+import { IdentityFragment } from '../../components/Identity/IdentityFragment.generated';
+
+import { useGetUserStatisticQuery } from './GetUserStatistic.generated';
+
+const useStyles = makeStyles((theme) => ({
+ main: {
+ maxWidth: 1000,
+ margin: 'auto',
+ marginTop: theme.spacing(3),
+ },
+ content: {
+ padding: theme.spacing(0.5, 2, 2, 2),
+ wordWrap: 'break-word',
+ },
+ large: {
+ minWidth: 200,
+ minHeight: 200,
+ margin: 'auto',
+ maxWidth: '100%',
+ maxHeight: '100%',
+ },
+ heading: {
+ marginTop: theme.spacing(3),
+ },
+ header: {
+ ...theme.typography.h4,
+ wordBreak: 'break-word',
+ },
+ infoIcon: {
+ verticalAlign: 'bottom',
+ },
+}));
+
+type Props = {
+ identity: IdentityFragment;
+};
+const Identity = ({ identity }: Props) => {
+ const classes = useStyles();
+ const user = identity;
+
+ const { loading, error, data } = useGetUserStatisticQuery({
+ variables: {
+ authorQuery: 'author:' + user?.id,
+ participantQuery: 'participant:' + user?.id,
+ actionQuery: 'actor:' + user?.id,
+ },
+ });
+
+ if (loading) return <CircularProgress />;
+ if (error) return <p>Error: {error}</p>;
+ const statistic = data?.repository;
+ const authoredCount = statistic?.authored?.totalCount;
+ const participatedCount = statistic?.participated?.totalCount;
+ const actionCount = statistic?.actions?.totalCount;
+
+ return (
+ <main className={classes.main}>
+ <Paper elevation={3} className={classes.content}>
+ <Grid spacing={2} container direction="row">
+ <Grid xs={12} sm={4} className={classes.heading} item>
+ <Avatar
+ src={user?.avatarUrl ? user.avatarUrl : undefined}
+ className={classes.large}
+ >
+ {user?.displayName.charAt(0).toUpperCase()}
+ </Avatar>
+ </Grid>
+ <Grid xs={12} sm={4} item>
+ <section>
+ <h1 className={classes.header}>{user?.name}</h1>
+ <Typography variant="subtitle1">
+ Name: {user?.displayName ? user?.displayName : '---'}
+ </Typography>
+ <Typography variant="subtitle1">
+ Id (truncated): {user?.humanId ? user?.humanId : '---'}
+ <InfoIcon
+ titleAccess={user?.id ? user?.id : '---'}
+ className={classes.infoIcon}
+ />
+ </Typography>
+ {user?.email && (
+ <Typography
+ variant="subtitle1"
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ flexWrap: 'wrap',
+ }}
+ >
+ <MailOutlineIcon />
+ <Link href={'mailto:' + user?.email} color={'inherit'}>
+ {user?.email}
+ </Link>
+ </Typography>
+ )}
+ </section>
+ </Grid>
+ <Grid xs={12} sm={4} item>
+ <section>
+ <h1 className={classes.header}>Statistics</h1>
+ <Link
+ component={RouterLink}
+ to={`/?q=author%3A${user?.id}+sort%3Acreation`}
+ color={'inherit'}
+ >
+ <Typography variant="subtitle1">
+ Created {authoredCount} bugs.
+ </Typography>
+ </Link>
+ <Link
+ component={RouterLink}
+ to={`/?q=participant%3A${user?.id}+sort%3Acreation`}
+ color={'inherit'}
+ >
+ <Typography variant="subtitle1">
+ Participated to {participatedCount} bugs.
+ </Typography>
+ </Link>
+ <Link
+ component={RouterLink}
+ to={`/?q=actor%3A${user?.id}+sort%3Acreation`}
+ color={'inherit'}
+ >
+ <Typography variant="subtitle1">
+ Interacted with {actionCount} bugs.
+ </Typography>
+ </Link>
+ </section>
+ </Grid>
+ </Grid>
+ </Paper>
+ </main>
+ );
+};
+
+export default Identity;
diff --git a/webui/src/pages/identity/IdentityQuery.tsx b/webui/src/pages/identity/IdentityQuery.tsx
new file mode 100644
index 00000000..964a9bac
--- /dev/null
+++ b/webui/src/pages/identity/IdentityQuery.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+
+import CircularProgress from '@material-ui/core/CircularProgress';
+
+import { useGetUserByIdQuery } from '../../components/Identity/UserIdentity.generated';
+
+import Identity from './Identity';
+
+type Props = RouteComponentProps<{
+ id: string;
+}>;
+
+const UserQuery: React.FC<Props> = ({ match }: Props) => {
+ const { loading, error, data } = useGetUserByIdQuery({
+ variables: { userId: match.params.id },
+ });
+ if (loading) return <CircularProgress />;
+ if (error) return <p>Error: {error}</p>;
+ if (!data?.repository?.identity) return <p>404.</p>;
+ return <Identity identity={data.repository.identity} />;
+};
+
+export default UserQuery;
diff --git a/webui/src/pages/identity/index.tsx b/webui/src/pages/identity/index.tsx
new file mode 100644
index 00000000..06208687
--- /dev/null
+++ b/webui/src/pages/identity/index.tsx
@@ -0,0 +1 @@
+export { default } from './IdentityQuery';