@drodil/backstage-plugin-qeta 1.20.0 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,11 @@ import omitBy from 'lodash/omitBy';
5
5
  import isEmpty from 'lodash/isEmpty';
6
6
  import { qetaRouteRef, tagRouteRef, questionRouteRef, userRouteRef, askRouteRef } from '@drodil/backstage-plugin-qeta-react';
7
7
  import useAsync from 'react-use/lib/useAsync';
8
- import { makeStyles, Box, Grid, FormGroup, FormControlLabel, Checkbox, FormControl, FormLabel, RadioGroup, TextField, Radio, Tooltip, Chip, useTheme, Card, CardContent, Typography, Divider, Select, MenuItem, Button, Collapse, TableRow, TableCell, ButtonGroup, TableContainer, Table, TableHead, TableBody, TablePagination, SvgIcon, ListItem, ListItemAvatar, Avatar, ListItemText, List, Container } from '@material-ui/core';
9
- import { catalogApiRef, entityRouteRef, useEntityPresentation, EntityRefLink } from '@backstage/plugin-catalog-react';
8
+ import { makeStyles, Box, Grid, FormGroup, FormControlLabel, Checkbox, FormControl, FormLabel, RadioGroup, TextField, Radio, Tooltip, Chip, useTheme, Card, CardContent, Typography, Avatar, Divider, Select, MenuItem, Button, Collapse, TableRow, TableCell, ButtonGroup, TableContainer, Table, TableHead, TableBody, TablePagination, SvgIcon, ListItem, ListItemAvatar, ListItemText, List, Container } from '@material-ui/core';
9
+ import { catalogApiRef, useEntityPresentation, entityRouteRef, EntityRefLink } from '@backstage/plugin-catalog-react';
10
10
  import { trimEnd, compact } from 'lodash';
11
11
  import { useSearchParams, useNavigate } from 'react-router-dom';
12
- import React, { useEffect, useMemo } from 'react';
12
+ import React, { useEffect, useRef, useState, useMemo } from 'react';
13
13
  import useDebounce from 'react-use/lib/useDebounce';
14
14
  import { Autocomplete, Pagination, Alert } from '@material-ui/lab';
15
15
  import { stringifyEntityRef, getCompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';
@@ -469,7 +469,7 @@ const qetaPlugin = createPlugin({
469
469
  const QetaPage = qetaPlugin.provide(
470
470
  createRoutableExtension({
471
471
  name: "QetaPage",
472
- component: () => import('./index-0917d370.esm.js').then((m) => m.HomePage),
472
+ component: () => import('./index-3bec4f6e.esm.js').then((m) => m.HomePage),
473
473
  mountPoint: qetaRouteRef
474
474
  })
475
475
  );
@@ -478,7 +478,7 @@ const QuestionTableCard = qetaPlugin.provide(
478
478
  name: "QuestionsTableCard",
479
479
  title: "Q&A",
480
480
  description: "Shows Q&A questions",
481
- components: () => import('./index-a1512692.esm.js'),
481
+ components: () => import('./index-db684599.esm.js'),
482
482
  layout: {
483
483
  height: { minRows: 6 },
484
484
  width: { minColumns: 6 }
@@ -606,6 +606,29 @@ const useStyles$1 = makeStyles((theme) => {
606
606
  width: "calc(100% - 70px)",
607
607
  minHeight: "160px"
608
608
  },
609
+ questionListItemStats: {
610
+ width: "80px",
611
+ textAlign: "right",
612
+ marginRight: "26px",
613
+ display: "inline-block",
614
+ verticalAlign: "top",
615
+ paddingTop: "10px"
616
+ },
617
+ questionListItemContent: {
618
+ display: "inline-block",
619
+ width: "calc(100% - 120px)"
620
+ },
621
+ questionListItemAuthor: {
622
+ display: "inline",
623
+ float: "right"
624
+ },
625
+ questionListItemAvatar: {
626
+ display: "inline-flex",
627
+ marginRight: "0.25rem",
628
+ fontSize: "1rem",
629
+ maxWidth: "1rem",
630
+ maxHeight: "1rem"
631
+ },
609
632
  answerCardContent: {
610
633
  display: "inline-block",
611
634
  width: "calc(100% - 70px)"
@@ -713,6 +736,43 @@ const useBasePath = () => {
713
736
  const { pathname } = new URL(url, base);
714
737
  return trimEnd(pathname, "/");
715
738
  };
739
+ const useEntityAuthor = (entity) => {
740
+ var _a;
741
+ const catalogApi = useApi(catalogApiRef);
742
+ const identityApi = useApi(identityApiRef);
743
+ const [name, setName] = React.useState(void 0);
744
+ const [user, setUser] = React.useState(null);
745
+ const [initials, setInitials] = React.useState(null);
746
+ const [currentUser, setCurrentUser] = React.useState(null);
747
+ const anonymous = (_a = entity.anonymous) != null ? _a : false;
748
+ const { primaryTitle: userName } = useEntityPresentation(entity.author);
749
+ useEffect(() => {
750
+ if (!anonymous) {
751
+ catalogApi.getEntityByRef(entity.author).catch((_) => setUser(null)).then((data) => data ? setUser(data) : setUser(null));
752
+ }
753
+ }, [catalogApi, entity, anonymous]);
754
+ useEffect(() => {
755
+ identityApi.getBackstageIdentity().then((res) => {
756
+ var _a2;
757
+ setCurrentUser((_a2 = res.userEntityRef) != null ? _a2 : "user:default/guest");
758
+ });
759
+ }, [identityApi]);
760
+ useEffect(() => {
761
+ let displayName = userName;
762
+ if (entity.author === currentUser) {
763
+ displayName = "You";
764
+ if (anonymous) {
765
+ displayName += " (anonymous)";
766
+ }
767
+ }
768
+ setName(displayName);
769
+ }, [entity.author, anonymous, currentUser, userName]);
770
+ useEffect(() => {
771
+ const init = (name != null ? name : "").split(" ").map((p) => p[0]).join("").substring(0, 2).toUpperCase();
772
+ setInitials(init);
773
+ }, [name]);
774
+ return { name, initials, user };
775
+ };
716
776
 
717
777
  const formatEntityName = (username) => {
718
778
  if (!username) {
@@ -1013,12 +1073,44 @@ const RelativeTimeWithTooltip = (props) => {
1013
1073
  };
1014
1074
 
1015
1075
  const QuestionListItem = (props) => {
1076
+ var _a, _b;
1016
1077
  const { question, entity } = props;
1017
1078
  const questionRoute = useRouteRef(questionRouteRef);
1018
1079
  const userRoute = useRouteRef(userRouteRef);
1019
- const { primaryTitle: userName } = useEntityPresentation(question.author);
1020
1080
  const theme = useTheme();
1021
- return /* @__PURE__ */ React.createElement(Card, { className: "qetaQuestionListItem" }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5", component: "div" }, /* @__PURE__ */ React.createElement(
1081
+ const styles = useStyles$1();
1082
+ const { name, initials, user } = useEntityAuthor(question);
1083
+ return /* @__PURE__ */ React.createElement(Card, { className: "qetaQuestionListItem" }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Box, { className: styles.questionListItemStats }, /* @__PURE__ */ React.createElement(
1084
+ Typography,
1085
+ {
1086
+ display: "block",
1087
+ variant: "caption",
1088
+ className: "qetaQuestionListItemScore"
1089
+ },
1090
+ question.score,
1091
+ " score"
1092
+ ), /* @__PURE__ */ React.createElement(
1093
+ Typography,
1094
+ {
1095
+ variant: "caption",
1096
+ display: "block",
1097
+ className: `qetaQuestionListItemAnswers ${question.correctAnswer ? "qetaQuestionListItemCorrectAnswer" : "quetaQuestionListItemNoCorrectAnswer"}`,
1098
+ style: {
1099
+ color: question.correctAnswer ? theme.palette.success.main : void 0
1100
+ }
1101
+ },
1102
+ question.answersCount,
1103
+ " answers"
1104
+ ), /* @__PURE__ */ React.createElement(
1105
+ Typography,
1106
+ {
1107
+ display: "block",
1108
+ variant: "caption",
1109
+ className: "qetaQuestionListItemViews"
1110
+ },
1111
+ question.views,
1112
+ " views"
1113
+ )), /* @__PURE__ */ React.createElement(Box, { className: styles.questionListItemContent }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5", component: "div" }, /* @__PURE__ */ React.createElement(
1022
1114
  Link,
1023
1115
  {
1024
1116
  to: entity ? `${questionRoute({
@@ -1027,66 +1119,49 @@ const QuestionListItem = (props) => {
1027
1119
  className: "qetaQuestionListItemQuestionBtn"
1028
1120
  },
1029
1121
  question.title
1030
- ))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, style: { paddingTop: 0, paddingBottom: 0 } }, /* @__PURE__ */ React.createElement(
1122
+ )), /* @__PURE__ */ React.createElement(
1031
1123
  Typography,
1032
1124
  {
1033
1125
  variant: "caption",
1034
1126
  noWrap: true,
1035
1127
  component: "div",
1036
- className: "qetaQuestionListItemContent"
1128
+ className: "qetaQuestionListItemContent",
1129
+ style: { marginBottom: "5px" }
1037
1130
  },
1038
1131
  DOMPurify.sanitize(
1039
1132
  truncate(removeMarkdownFormatting(question.content), 150)
1040
1133
  )
1041
- )), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
1042
- Typography,
1043
- {
1044
- variant: "body2",
1045
- display: "block",
1046
- className: "qetaQuestionListItemAuthor"
1047
- },
1048
- "By",
1049
- " ",
1050
- question.author === "anonymous" ? "Anonymous" : /* @__PURE__ */ React.createElement(Link, { to: `${userRoute()}/${question.author}` }, userName),
1051
- " ",
1052
- /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: question.created })
1053
- ), /* @__PURE__ */ React.createElement(
1134
+ ), /* @__PURE__ */ React.createElement(TagsAndEntities, { question }), /* @__PURE__ */ React.createElement(
1054
1135
  Typography,
1055
1136
  {
1056
1137
  variant: "caption",
1057
1138
  display: "inline",
1058
- gutterBottom: true,
1059
- className: "qetaQuestionListItemScore"
1139
+ className: `${styles.questionListItemAuthor} qetaQuestionListItemAuthor`
1060
1140
  },
1061
- "Score: ",
1062
- question.score,
1141
+ /* @__PURE__ */ React.createElement(
1142
+ Avatar,
1143
+ {
1144
+ src: (_b = (_a = user == null ? void 0 : user.spec) == null ? void 0 : _a.profile) == null ? void 0 : _b.picture,
1145
+ className: styles.questionListItemAvatar,
1146
+ alt: name,
1147
+ variant: "rounded"
1148
+ },
1149
+ initials
1150
+ ),
1151
+ question.author === "anonymous" ? "Anonymous" : /* @__PURE__ */ React.createElement(Link, { to: `${userRoute()}/${question.author}` }, name),
1063
1152
  " ",
1064
- " | "
1065
- ), /* @__PURE__ */ React.createElement(
1066
- Typography,
1067
- {
1068
- variant: "caption",
1069
- className: `qetaQuestionListItemAnswers ${question.correctAnswer ? "qetaQuestionListItemCorrectAnswer" : "quetaQuestionListItemNoCorrectAnswer"}`,
1070
- style: {
1071
- color: question.correctAnswer ? theme.palette.success.main : void 0
1153
+ /* @__PURE__ */ React.createElement(
1154
+ Link,
1155
+ {
1156
+ to: entity ? `${questionRoute({
1157
+ id: question.id.toString(10)
1158
+ })}?entity=${entity}` : questionRoute({ id: question.id.toString(10) }),
1159
+ className: "qetaQuestionListItemQuestionBtn"
1072
1160
  },
1073
- display: "inline",
1074
- gutterBottom: true
1075
- },
1076
- "Answers: ",
1077
- question.answersCount
1078
- ), /* @__PURE__ */ React.createElement(
1079
- Typography,
1080
- {
1081
- variant: "caption",
1082
- display: "inline",
1083
- gutterBottom: true,
1084
- className: "qetaQuestionListItemViews"
1085
- },
1086
- " | ",
1087
- " Views: ",
1088
- question.views
1089
- )), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(TagsAndEntities, { question })))));
1161
+ "asked ",
1162
+ /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: question.created })
1163
+ )
1164
+ ))));
1090
1165
  };
1091
1166
 
1092
1167
  const NoQuestionsCard = (props) => {
@@ -1135,19 +1210,32 @@ const QuestionList = (props) => {
1135
1210
  entityPage
1136
1211
  } = props;
1137
1212
  const styles = useStyles$1();
1213
+ const listRef = useRef(null);
1214
+ const [initialLoad, setInitialLoad] = useState(true);
1215
+ useEffect(() => {
1216
+ if (!initialLoad) {
1217
+ setInitialLoad(false);
1218
+ }
1219
+ }, [initialLoad, loading]);
1138
1220
  const handlePageChange = (_event, value) => {
1221
+ if (listRef.current) {
1222
+ listRef.current.scrollIntoView();
1223
+ }
1139
1224
  onPageChange(value);
1140
1225
  };
1141
1226
  const handlePageSizeChange = (event) => {
1227
+ if (listRef.current) {
1228
+ listRef.current.scrollIntoView();
1229
+ }
1142
1230
  onPageSizeChange(Number.parseInt(event.target.value, 10));
1143
1231
  };
1144
- if (loading) {
1232
+ if (loading && initialLoad) {
1145
1233
  return /* @__PURE__ */ React.createElement(Progress, null);
1146
1234
  }
1147
1235
  if (error || response === void 0) {
1148
1236
  return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load questions." }, error == null ? void 0 : error.message);
1149
1237
  }
1150
- if (!response.questions || response.questions.length === 0) {
1238
+ if (initialLoad && (!response.questions || response.questions.length === 0)) {
1151
1239
  return /* @__PURE__ */ React.createElement(
1152
1240
  NoQuestionsCard,
1153
1241
  {
@@ -1158,7 +1246,7 @@ const QuestionList = (props) => {
1158
1246
  );
1159
1247
  }
1160
1248
  const pageCount = response.total < props.pageSize ? 1 : Math.ceil(response.total / props.pageSize);
1161
- return /* @__PURE__ */ React.createElement(Box, { sx: { mt: 2 }, className: "qetaQuestionList" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2, className: "qetaQuestionListGrid" }, response.questions.map((question) => {
1249
+ return /* @__PURE__ */ React.createElement("div", { ref: listRef }, /* @__PURE__ */ React.createElement(Box, { sx: { mt: 2 }, className: "qetaQuestionList" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2, className: "qetaQuestionListGrid" }, response.questions.map((question) => {
1162
1250
  return /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, key: question.id }, /* @__PURE__ */ React.createElement(QuestionListItem, { question, entity }), /* @__PURE__ */ React.createElement(Divider, null));
1163
1251
  })), /* @__PURE__ */ React.createElement(
1164
1252
  Grid,
@@ -1196,7 +1284,7 @@ const QuestionList = (props) => {
1196
1284
  showLastButton: true
1197
1285
  }
1198
1286
  )
1199
- ));
1287
+ )));
1200
1288
  };
1201
1289
 
1202
1290
  const AskQuestionButton = (props) => {
@@ -2266,5 +2354,5 @@ const StatisticsPage = () => {
2266
2354
  return /* @__PURE__ */ React.createElement(Content$1, { className: "qetaStatisticsPage" }, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Statistics" }, /* @__PURE__ */ React.createElement(BackToQuestionsButton, null), /* @__PURE__ */ React.createElement(AskQuestionButton, null)), /* @__PURE__ */ React.createElement(TopRankingUsers, { limit: 10 })));
2267
2355
  };
2268
2356
 
2269
- export { AskForm as A, BackToQuestionsButton as B, Content as C, MarkdownEditor as M, QuestionsContainer as Q, RelativeTimeWithTooltip as R, StatisticsPage as S, TagsAndEntities as T, UpdatedByLink as U, useStyles$1 as a, AuthorLink as b, AskAnonymouslyCheckbox as c, useQetaApi as d, AskQuestionButton as e, formatEntityName as f, useIdentityApi as g, TrophyIcon as h, QuestionsTable as i, qetaPlugin as j, QetaPage as k, QuestionTableCard as l, RankingRow as m, RankingCard as n, TopRankingUsers as o, QetaClient as p, qetaApiRef as q, useBasePath as u };
2270
- //# sourceMappingURL=index-ba15a52c.esm.js.map
2357
+ export { AskForm as A, BackToQuestionsButton as B, Content as C, MarkdownEditor as M, QuestionsContainer as Q, RelativeTimeWithTooltip as R, StatisticsPage as S, TagsAndEntities as T, UpdatedByLink as U, useStyles$1 as a, useEntityAuthor as b, AuthorLink as c, AskAnonymouslyCheckbox as d, useQetaApi as e, AskQuestionButton as f, useIdentityApi as g, TrophyIcon as h, QuestionsTable as i, qetaPlugin as j, QetaPage as k, QuestionTableCard as l, RankingRow as m, RankingCard as n, TopRankingUsers as o, QetaClient as p, qetaApiRef as q, useBasePath as u };
2358
+ //# sourceMappingURL=index-05d845fb.esm.js.map