diff options
author | Michael Muré <batolettre@gmail.com> | 2021-04-23 00:16:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-23 00:16:57 +0200 |
commit | a8f3b55986982db5f7c3918acaba2c214c919d11 (patch) | |
tree | 19706066a71c32684979b019aa62525481ff4891 /webui/src/pages/identity | |
parent | 271ca35d19a9649f6d0512760aed70c8778822fc (diff) | |
parent | 774bae6d432c13cfa0de3ddc2f111927743243fe (diff) | |
download | git-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.tsx | 73 | ||||
-rw-r--r-- | webui/src/pages/identity/GetBugsByUser.graphql | 12 | ||||
-rw-r--r-- | webui/src/pages/identity/GetUserStatistic.graphql | 13 | ||||
-rw-r--r-- | webui/src/pages/identity/Identity.tsx | 147 | ||||
-rw-r--r-- | webui/src/pages/identity/IdentityQuery.tsx | 24 | ||||
-rw-r--r-- | webui/src/pages/identity/index.tsx | 1 |
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 + <Date date={bug.createdAt} /> + </Typography> + <Typography variant="subtitle2"> + Last edited + <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'; |