@drodil/backstage-plugin-qeta 2.3.0 → 2.3.2

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.
Files changed (103) hide show
  1. package/dist/api/QetaClient.esm.js +434 -0
  2. package/dist/api/QetaClient.esm.js.map +1 -0
  3. package/dist/components/AskAnonymouslyCheckbox/AskAnonymouslyCheckbox.esm.js +32 -0
  4. package/dist/components/AskAnonymouslyCheckbox/AskAnonymouslyCheckbox.esm.js.map +1 -0
  5. package/dist/components/AskForm/AskForm.esm.js +218 -0
  6. package/dist/components/AskForm/AskForm.esm.js.map +1 -0
  7. package/dist/components/AskForm/EntitiesInput.esm.js +98 -0
  8. package/dist/components/AskForm/EntitiesInput.esm.js.map +1 -0
  9. package/dist/components/AskForm/TagInput.esm.js +86 -0
  10. package/dist/components/AskForm/TagInput.esm.js.map +1 -0
  11. package/dist/components/AskPage/AskPage.esm.js +38 -0
  12. package/dist/components/AskPage/AskPage.esm.js.map +1 -0
  13. package/dist/components/Buttons/AskQuestionButton.esm.js +43 -0
  14. package/dist/components/Buttons/AskQuestionButton.esm.js.map +1 -0
  15. package/dist/components/Buttons/BackToQuestionsButton.esm.js +38 -0
  16. package/dist/components/Buttons/BackToQuestionsButton.esm.js.map +1 -0
  17. package/dist/components/CommentSection/CommentList.esm.js +47 -0
  18. package/dist/components/CommentSection/CommentList.esm.js.map +1 -0
  19. package/dist/components/CommentSection/CommentSection.esm.js +97 -0
  20. package/dist/components/CommentSection/CommentSection.esm.js.map +1 -0
  21. package/dist/components/DeleteModal/DeleteModal.esm.js +77 -0
  22. package/dist/components/DeleteModal/DeleteModal.esm.js.map +1 -0
  23. package/dist/components/FavoritePage/FavoritePage.esm.js +13 -0
  24. package/dist/components/FavoritePage/FavoritePage.esm.js.map +1 -0
  25. package/dist/components/HomePage/HomePage.esm.js +144 -0
  26. package/dist/components/HomePage/HomePage.esm.js.map +1 -0
  27. package/dist/components/HomePage/index.esm.js +2 -0
  28. package/dist/components/HomePage/index.esm.js.map +1 -0
  29. package/dist/components/Links/Links.esm.js +31 -0
  30. package/dist/components/Links/Links.esm.js.map +1 -0
  31. package/dist/components/MarkdownEditor/MarkdownEditor.esm.js +80 -0
  32. package/dist/components/MarkdownEditor/MarkdownEditor.esm.js.map +1 -0
  33. package/dist/components/QuestionHighlightList/QuestionHighlightList.esm.js +61 -0
  34. package/dist/components/QuestionHighlightList/QuestionHighlightList.esm.js.map +1 -0
  35. package/dist/components/QuestionPage/AnswerCard.esm.js +101 -0
  36. package/dist/components/QuestionPage/AnswerCard.esm.js.map +1 -0
  37. package/dist/components/QuestionPage/AnswerForm.esm.js +130 -0
  38. package/dist/components/QuestionPage/AnswerForm.esm.js.map +1 -0
  39. package/dist/components/QuestionPage/AuthorBox.esm.js +25 -0
  40. package/dist/components/QuestionPage/AuthorBox.esm.js.map +1 -0
  41. package/dist/components/QuestionPage/EntityChip.esm.js +26 -0
  42. package/dist/components/QuestionPage/EntityChip.esm.js.map +1 -0
  43. package/dist/components/QuestionPage/FavoriteButton.esm.js +43 -0
  44. package/dist/components/QuestionPage/FavoriteButton.esm.js.map +1 -0
  45. package/dist/components/QuestionPage/LinkButton.esm.js +32 -0
  46. package/dist/components/QuestionPage/LinkButton.esm.js.map +1 -0
  47. package/dist/components/QuestionPage/QuestionCard.esm.js +103 -0
  48. package/dist/components/QuestionPage/QuestionCard.esm.js.map +1 -0
  49. package/dist/components/QuestionPage/QuestionPage.esm.js +131 -0
  50. package/dist/components/QuestionPage/QuestionPage.esm.js.map +1 -0
  51. package/dist/components/QuestionPage/TagsAndEntities.esm.js +44 -0
  52. package/dist/components/QuestionPage/TagsAndEntities.esm.js.map +1 -0
  53. package/dist/components/QuestionPage/VoteButtons.esm.js +146 -0
  54. package/dist/components/QuestionPage/VoteButtons.esm.js.map +1 -0
  55. package/dist/components/QuestionTableCard/Content.esm.js +9 -0
  56. package/dist/components/QuestionTableCard/Content.esm.js.map +1 -0
  57. package/dist/components/QuestionTableCard/QuestionTableRow.esm.js +21 -0
  58. package/dist/components/QuestionTableCard/QuestionTableRow.esm.js.map +1 -0
  59. package/dist/components/QuestionTableCard/QuestionsTable.esm.js +130 -0
  60. package/dist/components/QuestionTableCard/QuestionsTable.esm.js.map +1 -0
  61. package/dist/components/QuestionTableCard/index.esm.js +3 -0
  62. package/dist/components/QuestionTableCard/index.esm.js.map +1 -0
  63. package/dist/components/QuestionsContainer/FilterPanel.esm.js +230 -0
  64. package/dist/components/QuestionsContainer/FilterPanel.esm.js.map +1 -0
  65. package/dist/components/QuestionsContainer/NoQuestionsCard.esm.js +46 -0
  66. package/dist/components/QuestionsContainer/NoQuestionsCard.esm.js.map +1 -0
  67. package/dist/components/QuestionsContainer/QuestionList.esm.js +102 -0
  68. package/dist/components/QuestionsContainer/QuestionList.esm.js.map +1 -0
  69. package/dist/components/QuestionsContainer/QuestionListItem.esm.js +119 -0
  70. package/dist/components/QuestionsContainer/QuestionListItem.esm.js.map +1 -0
  71. package/dist/components/QuestionsContainer/QuestionsContainer.esm.js +231 -0
  72. package/dist/components/QuestionsContainer/QuestionsContainer.esm.js.map +1 -0
  73. package/dist/components/RelativeTimeWithTooltip/RelativeTimeWithTooltip.esm.js +22 -0
  74. package/dist/components/RelativeTimeWithTooltip/RelativeTimeWithTooltip.esm.js.map +1 -0
  75. package/dist/components/Statistics/StatisticsPage.esm.js +13 -0
  76. package/dist/components/Statistics/StatisticsPage.esm.js.map +1 -0
  77. package/dist/components/Statistics/TopRankingUsersCard.esm.js +163 -0
  78. package/dist/components/Statistics/TopRankingUsersCard.esm.js.map +1 -0
  79. package/dist/components/Statistics/TrophyIcon.esm.js +19 -0
  80. package/dist/components/Statistics/TrophyIcon.esm.js.map +1 -0
  81. package/dist/components/Statistics/styles.esm.js +23 -0
  82. package/dist/components/Statistics/styles.esm.js.map +1 -0
  83. package/dist/components/TagPage/TagPage.esm.js +16 -0
  84. package/dist/components/TagPage/TagPage.esm.js.map +1 -0
  85. package/dist/components/TagPage/TagsContainer.esm.js +63 -0
  86. package/dist/components/TagPage/TagsContainer.esm.js.map +1 -0
  87. package/dist/components/UserPage/UserPage.esm.js +24 -0
  88. package/dist/components/UserPage/UserPage.esm.js.map +1 -0
  89. package/dist/index.esm.js +9 -32
  90. package/dist/index.esm.js.map +1 -1
  91. package/dist/plugin.esm.js +60 -0
  92. package/dist/plugin.esm.js.map +1 -0
  93. package/dist/utils/hooks.esm.js +283 -0
  94. package/dist/utils/hooks.esm.js.map +1 -0
  95. package/dist/utils/utils.esm.js +31 -0
  96. package/dist/utils/utils.esm.js.map +1 -0
  97. package/package.json +15 -15
  98. package/dist/esm/index-C7A2tOkT.esm.js +0 -33
  99. package/dist/esm/index-C7A2tOkT.esm.js.map +0 -1
  100. package/dist/esm/index-CFNWBJNR.esm.js +0 -2412
  101. package/dist/esm/index-CFNWBJNR.esm.js.map +0 -1
  102. package/dist/esm/index-rSPV8dxK.esm.js +0 -1120
  103. package/dist/esm/index-rSPV8dxK.esm.js.map +0 -1
@@ -1,1120 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Grid, makeStyles, createStyles, Tooltip, IconButton, Typography, Box, Modal, Backdrop, Button, Avatar, Divider, TextField, Card, CardContent, Container, FormControl, Select, Chip, List, ListSubheader, ListItem, ListItemText, Menu, MenuItem, ListItemIcon } from '@material-ui/core';
3
- import { Content, ContentHeader, InfoCard, MarkdownContent, Link, WarningPanel, Progress, Page, Header } from '@backstage/core-components';
4
- import { useParams, useSearchParams, useNavigate, Routes, Route } from 'react-router-dom';
5
- import { B as BackToQuestionsButton, A as AskForm, q as qetaApiRef, u as useBasePath, a as useStyles$1, b as useEntityAuthor, R as RelativeTimeWithTooltip, U as UpdatedByLink, c as AuthorLink, T as TagsAndEntities, M as MarkdownEditor, d as AskAnonymouslyCheckbox, e as useQetaApi, f as AskQuestionButton, g as QuestionsContainer, S as StatisticsPage, h as useIdentityApi, i as TrophyIcon } from './index-CFNWBJNR.esm.js';
6
- import { useEntityPresentation } from '@backstage/plugin-catalog-react';
7
- import { filterTags, qetaCreateAnswerPermission } from '@drodil/backstage-plugin-qeta-common';
8
- import ArrowDownward from '@material-ui/icons/ArrowDownward';
9
- import ArrowUpward from '@material-ui/icons/ArrowUpward';
10
- import Check from '@material-ui/icons/Check';
11
- import { useAnalytics, useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api';
12
- import { useSignal } from '@backstage/plugin-signals-react';
13
- import { Alert, Skeleton } from '@material-ui/lab';
14
- import DeleteIcon from '@material-ui/icons/Delete';
15
- import EditIcon from '@material-ui/icons/Edit';
16
- import StarIcon from '@material-ui/icons/Star';
17
- import StarOutlineIcon from '@material-ui/icons/StarOutline';
18
- import { useForm, Controller } from 'react-hook-form';
19
- import { editQuestionRouteRef, tagRouteRef, questionRouteRef, askRouteRef, favoriteQuestionsRouteRef, tagsRouteRef, userRouteRef, statisticsRouteRef } from '@drodil/backstage-plugin-qeta-react';
20
- import Link$1 from '@material-ui/icons/Link';
21
- import { RequirePermission } from '@backstage/plugin-permission-react';
22
- import LoyaltyOutlined from '@material-ui/icons/LoyaltyOutlined';
23
- import Whatshot from '@material-ui/icons/Whatshot';
24
- import MoreVertIcon from '@material-ui/icons/MoreVert';
25
- import AccountBox from '@material-ui/icons/AccountBox';
26
- import '@backstage/plugin-home-react';
27
- import '@backstage/errors';
28
- import 'lodash/omitBy';
29
- import 'lodash/isEmpty';
30
- import 'react-use/lib/useAsync';
31
- import 'lodash';
32
- import 'react-use/lib/useDebounce';
33
- import '@backstage/catalog-model';
34
- import 'dompurify';
35
- import 'react-relative-time';
36
- import '@material-ui/icons/HelpOutline';
37
- import '@material-ui/icons/FilterList';
38
- import 'react-mde';
39
- import 'react-mde/lib/styles/css/react-mde.css';
40
- import 'react-mde/lib/styles/css/react-mde-editor.css';
41
- import 'react-mde/lib/styles/css/react-mde-toolbar.css';
42
- import 'file-type';
43
- import '@material-ui/icons/Refresh';
44
- import '@material-ui/icons/HomeOutlined';
45
-
46
- const AskPage = () => {
47
- var _a;
48
- const { id } = useParams();
49
- const [searchParams] = useSearchParams();
50
- const entity = (_a = searchParams.get("entity")) != null ? _a : void 0;
51
- const entityPage = searchParams.get("entityPage") === "true";
52
- const tags = filterTags(searchParams.get("tags"));
53
- let title;
54
- if (id) {
55
- title = "Edit question";
56
- } else if (entity) {
57
- const representation = useEntityPresentation(entity);
58
- title = `Ask a question about ${representation.primaryTitle}`;
59
- } else {
60
- title = "Ask question";
61
- }
62
- return /* @__PURE__ */ React.createElement(Content, { className: "qetaAskPage" }, /* @__PURE__ */ React.createElement(ContentHeader, { title }, /* @__PURE__ */ React.createElement(BackToQuestionsButton, { entityPage })), /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, null, /* @__PURE__ */ React.createElement(
63
- AskForm,
64
- {
65
- id,
66
- entity,
67
- entityPage,
68
- tags
69
- }
70
- )))));
71
- };
72
-
73
- const useStyles = makeStyles(
74
- (theme) => createStyles({
75
- qetaCorrectAnswerSelected: {
76
- color: theme.palette.success.main
77
- },
78
- qetaCorrectAnswer: {
79
- color: theme.palette.grey[500]
80
- }
81
- })
82
- );
83
- const VoteButtons = (props) => {
84
- var _a, _b, _c, _d, _e, _f, _g;
85
- const [entity, setEntity] = React.useState(
86
- props.entity
87
- );
88
- const [ownVote, setOwnVote] = React.useState((_a = props.entity.ownVote) != null ? _a : 0);
89
- const [correctAnswer, setCorrectAnswer] = useState(
90
- "questionId" in props.entity ? props.entity.correct : false
91
- );
92
- const [score, setScore] = useState(entity.score);
93
- const analytics = useAnalytics();
94
- const qetaApi = useApi(qetaApiRef);
95
- const isQuestion = "title" in entity;
96
- const own = (_b = props.entity.own) != null ? _b : false;
97
- const classes = useStyles();
98
- const { lastSignal } = useSignal(
99
- isQuestion ? `qeta:question_${entity.id}` : `qeta:answer_${entity.id}`
100
- );
101
- useEffect(() => {
102
- if (entity) {
103
- setScore(entity.score);
104
- }
105
- }, [entity]);
106
- useEffect(() => {
107
- if ((lastSignal == null ? void 0 : lastSignal.type) === "question_stats" || (lastSignal == null ? void 0 : lastSignal.type) === "answer_stats") {
108
- setCorrectAnswer(lastSignal.correctAnswer);
109
- setScore(lastSignal.score);
110
- }
111
- }, [lastSignal]);
112
- const voteUp = () => {
113
- if (isQuestion) {
114
- qetaApi.voteQuestionUp(entity.id).then((response) => {
115
- setOwnVote(1);
116
- analytics.captureEvent("vote", "question", { value: 1 });
117
- setEntity(response);
118
- });
119
- } else if ("questionId" in entity) {
120
- qetaApi.voteAnswerUp(entity.questionId, entity.id).then((response) => {
121
- setOwnVote(1);
122
- analytics.captureEvent("vote", "answer", { value: 1 });
123
- setEntity(response);
124
- });
125
- }
126
- };
127
- const voteDown = () => {
128
- if (isQuestion) {
129
- qetaApi.voteQuestionDown(entity.id).then((response) => {
130
- setOwnVote(-1);
131
- analytics.captureEvent("vote", "question", { value: -1 });
132
- setEntity(response);
133
- });
134
- } else if ("questionId" in entity) {
135
- qetaApi.voteAnswerDown(entity.questionId, entity.id).then((response) => {
136
- setOwnVote(-1);
137
- analytics.captureEvent("vote", "answer", { value: -1 });
138
- setEntity(response);
139
- });
140
- }
141
- };
142
- let correctTooltip = correctAnswer ? "Mark answer as incorrect" : "Mark answer as correct";
143
- if (!((_c = props.question) == null ? void 0 : _c.own)) {
144
- correctTooltip = correctAnswer ? "This answer has been marked as correct" : "";
145
- }
146
- let voteUpTooltip = isQuestion ? "This question is good" : "This answer is good";
147
- if (own) {
148
- voteUpTooltip = isQuestion ? "You cannot vote your own question" : "You cannot vote your own answer";
149
- }
150
- let voteDownTooltip = isQuestion ? "This question is not good" : "This answer is not good";
151
- if (own) {
152
- voteDownTooltip = voteUpTooltip;
153
- }
154
- const toggleCorrectAnswer = () => {
155
- if (!("questionId" in entity)) {
156
- return;
157
- }
158
- if (correctAnswer) {
159
- qetaApi.markAnswerIncorrect(entity.questionId, entity.id).then((response) => {
160
- if (response) {
161
- setCorrectAnswer(false);
162
- }
163
- });
164
- } else {
165
- qetaApi.markAnswerCorrect(entity.questionId, entity.id).then((response) => {
166
- setCorrectAnswer(response);
167
- });
168
- }
169
- };
170
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip, { title: voteUpTooltip }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
171
- IconButton,
172
- {
173
- "aria-label": "vote up",
174
- color: ownVote > 0 ? "primary" : "default",
175
- className: ownVote > 0 ? "qetaVoteUpSelected" : "qetaVoteUp",
176
- disabled: own,
177
- size: "small",
178
- onClick: voteUp
179
- },
180
- /* @__PURE__ */ React.createElement(ArrowUpward, null)
181
- ))), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, score), /* @__PURE__ */ React.createElement(Tooltip, { title: voteDownTooltip }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
182
- IconButton,
183
- {
184
- "aria-label": "vote down",
185
- color: ownVote < 0 ? "primary" : "default",
186
- className: ownVote < 0 ? "qetaVoteDownSelected" : "qetaVoteDown",
187
- disabled: own,
188
- size: "small",
189
- onClick: voteDown
190
- },
191
- /* @__PURE__ */ React.createElement(ArrowDownward, null)
192
- ))), "correct" in props.entity && (((_d = props.question) == null ? void 0 : _d.own) || ((_e = props.question) == null ? void 0 : _e.canEdit) || correctAnswer) && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Tooltip, { title: correctTooltip }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
193
- IconButton,
194
- {
195
- "aria-label": "mark correct",
196
- size: "small",
197
- onClick: ((_f = props.question) == null ? void 0 : _f.own) || ((_g = props.question) == null ? void 0 : _g.canEdit) ? toggleCorrectAnswer : void 0
198
- },
199
- /* @__PURE__ */ React.createElement(
200
- Check,
201
- {
202
- className: correctAnswer ? classes.qetaCorrectAnswerSelected : classes.qetaCorrectAnswer
203
- }
204
- )
205
- )))));
206
- };
207
-
208
- const DeleteModal = (props) => {
209
- const qetaApi = useApi(qetaApiRef);
210
- const base_path = useBasePath();
211
- const navigate = useNavigate();
212
- const { entity, open, question, onClose } = props;
213
- const styles = useStyles$1();
214
- const [error, setError] = React.useState(false);
215
- const isQuestion = "title" in entity;
216
- const title = isQuestion ? "Are you sure you want to delete this question?" : "Are you sure you want to delete this answer?";
217
- const handleDelete = () => {
218
- if (isQuestion) {
219
- qetaApi.deleteQuestion(entity.id).catch((_) => setError(true)).then((ret) => {
220
- if (ret) {
221
- onClose();
222
- navigate(`${base_path}/qeta`);
223
- } else {
224
- setError(true);
225
- }
226
- });
227
- } else if (question) {
228
- qetaApi.deleteAnswer(question.id, entity.id).catch((_) => setError(true)).then((ret) => {
229
- if (ret) {
230
- onClose();
231
- window.location.reload();
232
- } else {
233
- setError(true);
234
- }
235
- });
236
- }
237
- };
238
- return /* @__PURE__ */ React.createElement(
239
- Modal,
240
- {
241
- open,
242
- onClose,
243
- className: "qetaDeleteModal",
244
- "aria-labelledby": "modal-modal-title",
245
- "aria-describedby": "modal-modal-description",
246
- closeAfterTransition: true,
247
- BackdropComponent: Backdrop,
248
- BackdropProps: {
249
- timeout: 500
250
- }
251
- },
252
- /* @__PURE__ */ React.createElement(Box, { className: `qetaDeleteModalContent ${styles.deleteModal}` }, error && /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, "Failed to delete"), /* @__PURE__ */ React.createElement(
253
- Typography,
254
- {
255
- id: "modal-modal-title",
256
- className: "qetaDeleteModalTitle",
257
- variant: "h6",
258
- component: "h2"
259
- },
260
- title
261
- ), /* @__PURE__ */ React.createElement(
262
- Button,
263
- {
264
- onClick: handleDelete,
265
- className: "qetaDeleteModalDeleteBtn",
266
- startIcon: /* @__PURE__ */ React.createElement(DeleteIcon, null),
267
- color: "secondary"
268
- },
269
- "Delete"
270
- ), /* @__PURE__ */ React.createElement(Button, { onClick: onClose, className: "qetaDeleteModalCancelBtn" }, "Cancel"))
271
- );
272
- };
273
-
274
- const FavoriteButton = (props) => {
275
- const [entity, setEntity] = React.useState(props.entity);
276
- const qetaApi = useApi(qetaApiRef);
277
- const favoriteQuestion = () => {
278
- qetaApi.favoriteQuestion(entity.id).then((response) => {
279
- setEntity(response);
280
- });
281
- };
282
- const unfavoriteQuestion = () => {
283
- qetaApi.unfavoriteQuestion(entity.id).then((response) => {
284
- setEntity(response);
285
- });
286
- };
287
- return /* @__PURE__ */ React.createElement(React.Fragment, null, entity.favorite ? /* @__PURE__ */ React.createElement(Tooltip, { title: "Remove this question from favorites" }, /* @__PURE__ */ React.createElement(
288
- IconButton,
289
- {
290
- "aria-label": "unfavorite",
291
- size: "small",
292
- onClick: unfavoriteQuestion,
293
- className: "qetaUnfavoriteBtn"
294
- },
295
- /* @__PURE__ */ React.createElement(StarIcon, null)
296
- )) : /* @__PURE__ */ React.createElement(Tooltip, { title: "Mark this question as favorite" }, /* @__PURE__ */ React.createElement(
297
- IconButton,
298
- {
299
- "aria-label": "favorite",
300
- size: "small",
301
- onClick: favoriteQuestion,
302
- className: "qetaFavoriteBtn"
303
- },
304
- /* @__PURE__ */ React.createElement(StarOutlineIcon, null)
305
- )));
306
- };
307
-
308
- const AuthorBox = (props) => {
309
- var _a, _b;
310
- const { entity } = props;
311
- const styles = useStyles$1();
312
- const { name, initials, user } = useEntityAuthor(entity);
313
- return /* @__PURE__ */ React.createElement(Box, { className: `qetaAuthorBox ${styles.questionCardAuthor}` }, /* @__PURE__ */ React.createElement(Grid, { container: true, alignItems: "center" }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, style: { paddingBottom: 0 } }, /* @__PURE__ */ React.createElement(Typography, { className: "qetaAuthorBoxCreated", variant: "caption" }, "Posted ", /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: entity.created }))), entity.updated && entity.updatedBy && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, style: { paddingBottom: 0, paddingTop: 0 } }, /* @__PURE__ */ React.createElement(Typography, { className: "qetaAuthorBoxUpdated", variant: "caption" }, "Updated ", /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: entity.updated }), " by", " ", /* @__PURE__ */ React.createElement(UpdatedByLink, { entity }))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 2 }, /* @__PURE__ */ React.createElement(
314
- Avatar,
315
- {
316
- src: (_b = (_a = user == null ? void 0 : user.spec) == null ? void 0 : _a.profile) == null ? void 0 : _b.picture,
317
- className: "qetaAuthorBoxAvatar avatar",
318
- alt: name,
319
- variant: "rounded"
320
- },
321
- initials
322
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 10, className: styles.authorLink }, /* @__PURE__ */ React.createElement(Box, { style: { paddingLeft: "5px" } }, /* @__PURE__ */ React.createElement(AuthorLink, { entity })))));
323
- };
324
-
325
- const CommentList = (props) => {
326
- var _a;
327
- const { question, answer, onCommentDelete } = props;
328
- const entity = answer != null ? answer : question;
329
- const styles = useStyles$1();
330
- const qetaApi = useApi(qetaApiRef);
331
- const deleteComment = (id) => {
332
- if (answer) {
333
- qetaApi.deleteAnswerComment(question.id, answer.id, id).then((a) => {
334
- onCommentDelete(question, a);
335
- return;
336
- });
337
- }
338
- qetaApi.deleteQuestionComment(question.id, id).then((q) => onCommentDelete(q));
339
- };
340
- return /* @__PURE__ */ React.createElement(React.Fragment, null, (_a = entity.comments) == null ? void 0 : _a.map((c) => {
341
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { key: c.id, className: "qetaCommentBox" }, /* @__PURE__ */ React.createElement(
342
- MarkdownContent,
343
- {
344
- dialect: "gfm",
345
- content: c.content,
346
- className: `${styles.markdownContent} inline`
347
- }
348
- ), " \u2013 ", /* @__PURE__ */ React.createElement(AuthorLink, { entity: c }), " ", /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: "qetaCommentTime" }, /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: c.created })), (c.own || c.canDelete) && /* @__PURE__ */ React.createElement(React.Fragment, null, " / ", /* @__PURE__ */ React.createElement(
349
- Link,
350
- {
351
- underline: "none",
352
- to: "#",
353
- className: "qetaCommentDeleteBtn",
354
- onClick: () => deleteComment(c.id)
355
- },
356
- "delete"
357
- ))), /* @__PURE__ */ React.createElement(Divider, null));
358
- }));
359
- };
360
-
361
- const CommentSection = (props) => {
362
- const { answer, question, onCommentPost, onCommentDelete } = props;
363
- const analytics = useAnalytics();
364
- const qetaApi = useApi(qetaApiRef);
365
- const [posting, setPosting] = React.useState(false);
366
- const [formVisible, setFormVisible] = useState(false);
367
- const {
368
- handleSubmit,
369
- control,
370
- formState: { errors },
371
- reset
372
- } = useForm({});
373
- const postComment = (data) => {
374
- setPosting(true);
375
- if (answer) {
376
- qetaApi.commentAnswer(question.id, answer.id, data.content).then((a) => {
377
- setFormVisible(false);
378
- analytics.captureEvent("comment", "answer");
379
- reset();
380
- setPosting(false);
381
- onCommentPost(question, a);
382
- });
383
- return;
384
- }
385
- qetaApi.commentQuestion(question.id, data.content).then((q) => {
386
- setFormVisible(false);
387
- analytics.captureEvent("comment", "question");
388
- reset();
389
- setPosting(false);
390
- onCommentPost(q);
391
- });
392
- };
393
- return /* @__PURE__ */ React.createElement(Box, { marginLeft: 9, className: "qetaCommentSection" }, /* @__PURE__ */ React.createElement(
394
- CommentList,
395
- {
396
- question,
397
- answer,
398
- onCommentDelete
399
- }
400
- ), !formVisible && /* @__PURE__ */ React.createElement(
401
- Link,
402
- {
403
- underline: "none",
404
- to: "#",
405
- className: "qetaAddCommentBtn",
406
- onClick: () => setFormVisible(true)
407
- },
408
- "Add comment"
409
- ), formVisible && /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit(postComment), className: "qetaCommentForm" }, /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 11 }, /* @__PURE__ */ React.createElement(
410
- Controller,
411
- {
412
- control,
413
- defaultValue: "",
414
- rules: {
415
- required: true
416
- },
417
- render: ({ field: { onChange, value } }) => /* @__PURE__ */ React.createElement(
418
- TextField,
419
- {
420
- id: "comment",
421
- multiline: true,
422
- minRows: 2,
423
- fullWidth: true,
424
- className: "qetaCommentInput",
425
- value,
426
- placeholder: "Your commment",
427
- onChange,
428
- variant: "outlined",
429
- error: "content" in errors
430
- }
431
- ),
432
- name: "content"
433
- }
434
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 1 }, /* @__PURE__ */ React.createElement(
435
- Button,
436
- {
437
- variant: "contained",
438
- size: "small",
439
- className: "qetaCommentBtn",
440
- type: "submit",
441
- color: "primary",
442
- disabled: posting
443
- },
444
- "Post"
445
- )))));
446
- };
447
-
448
- const LinkButton = (props) => {
449
- const isQuestion = "title" in props.entity;
450
- const copyToClipboard = () => {
451
- const url = new URL(window.location.href);
452
- if (!isQuestion) {
453
- url.hash = `#answer_${props.entity.id}`;
454
- }
455
- window.navigator.clipboard.writeText(url.toString());
456
- };
457
- return /* @__PURE__ */ React.createElement(
458
- Tooltip,
459
- {
460
- title: `Copy link to this ${isQuestion ? "question" : "answer"} to clipboard`
461
- },
462
- /* @__PURE__ */ React.createElement(
463
- IconButton,
464
- {
465
- "aria-label": "copy link to clipboard",
466
- size: "small",
467
- onClick: copyToClipboard
468
- },
469
- /* @__PURE__ */ React.createElement(Link$1, null)
470
- )
471
- );
472
- };
473
-
474
- const QuestionCard = (props) => {
475
- var _a;
476
- const { question } = props;
477
- const styles = useStyles$1();
478
- const editQuestionRoute = useRouteRef(editQuestionRouteRef);
479
- const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
480
- const [questionEntity, setQuestionEntity] = React.useState(question);
481
- const handleDeleteModalOpen = () => setDeleteModalOpen(true);
482
- const handleDeleteModalClose = () => setDeleteModalOpen(false);
483
- const onCommentAction = (q, _) => {
484
- setQuestionEntity(q);
485
- };
486
- const highlightedAnswer = (_a = window.location.hash.slice(1)) != null ? _a : void 0;
487
- useEffect(() => {
488
- if (highlightedAnswer) {
489
- const element = document.querySelector(`#${highlightedAnswer}`);
490
- if (element) {
491
- element.scrollIntoView();
492
- }
493
- }
494
- }, [highlightedAnswer]);
495
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
496
- Card,
497
- {
498
- variant: "outlined",
499
- className: `qetaQuestionCard ${styles.questionCard}`
500
- },
501
- /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement("div", { className: styles.questionCardVote }, /* @__PURE__ */ React.createElement(VoteButtons, { entity: questionEntity }), /* @__PURE__ */ React.createElement(FavoriteButton, { entity: questionEntity }), /* @__PURE__ */ React.createElement(LinkButton, { entity: questionEntity })), /* @__PURE__ */ React.createElement("div", { className: styles.questionCardContent }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, /* @__PURE__ */ React.createElement(
502
- MarkdownContent,
503
- {
504
- content: questionEntity.content,
505
- dialect: "gfm",
506
- className: styles.markdownContent
507
- }
508
- )), /* @__PURE__ */ React.createElement(
509
- Grid,
510
- {
511
- container: true,
512
- item: true,
513
- justifyContent: "space-around",
514
- className: styles.questionCardMetadata
515
- },
516
- /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 9, style: { alignSelf: "flex-end" } }, /* @__PURE__ */ React.createElement(TagsAndEntities, { question: questionEntity }), (question.own || question.canEdit || question.canDelete) && /* @__PURE__ */ React.createElement(Box, { className: styles.questionCardActions }, (question.own || question.canDelete) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
517
- Button,
518
- {
519
- variant: "outlined",
520
- size: "small",
521
- color: "secondary",
522
- onClick: handleDeleteModalOpen,
523
- className: `${styles.marginRight} qetaQuestionCardDeleteBtn`,
524
- startIcon: /* @__PURE__ */ React.createElement(DeleteIcon, null)
525
- },
526
- "Delete"
527
- ), /* @__PURE__ */ React.createElement(
528
- DeleteModal,
529
- {
530
- open: deleteModalOpen,
531
- onClose: handleDeleteModalClose,
532
- entity: questionEntity
533
- }
534
- )), (question.own || question.canEdit) && /* @__PURE__ */ React.createElement(
535
- Button,
536
- {
537
- variant: "outlined",
538
- size: "small",
539
- startIcon: /* @__PURE__ */ React.createElement(EditIcon, null),
540
- href: editQuestionRoute({
541
- id: question.id.toString(10)
542
- }),
543
- className: "qetaQuestionCardEditBtn"
544
- },
545
- "Edit"
546
- ))),
547
- /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 3, className: styles.noPadding }, /* @__PURE__ */ React.createElement(AuthorBox, { entity: questionEntity }))
548
- )))
549
- ), /* @__PURE__ */ React.createElement(
550
- CommentSection,
551
- {
552
- question: questionEntity,
553
- onCommentDelete: onCommentAction,
554
- onCommentPost: onCommentAction
555
- }
556
- ));
557
- };
558
-
559
- const getDefaultValues = (questionId) => {
560
- return { questionId, answer: "" };
561
- };
562
- const AnswerForm = (props) => {
563
- const { question, onPost, id } = props;
564
- const [values, setValues] = React.useState(getDefaultValues(question.id));
565
- const analytics = useAnalytics();
566
- const [error, setError] = React.useState(false);
567
- const [images, setImages] = React.useState([]);
568
- const qetaApi = useApi(qetaApiRef);
569
- const styles = useStyles$1();
570
- const configApi = useApi(configApiRef);
571
- const allowAnonymouns = configApi.getOptionalBoolean("qeta.allowAnonymous");
572
- const {
573
- handleSubmit,
574
- control,
575
- formState: { errors },
576
- reset
577
- } = useForm({
578
- values,
579
- defaultValues: getDefaultValues(question.id)
580
- });
581
- const postAnswer = (data) => {
582
- if (id) {
583
- qetaApi.updateAnswer(id, {
584
- questionId: question.id,
585
- answer: data.answer,
586
- images
587
- }).then((a) => {
588
- if (!a || !("id" in a)) {
589
- setError(true);
590
- return;
591
- }
592
- analytics.captureEvent("edit", "answer");
593
- reset();
594
- onPost(a);
595
- }).catch((_e) => setError(true));
596
- return;
597
- }
598
- qetaApi.postAnswer({
599
- questionId: question.id,
600
- answer: data.answer,
601
- images,
602
- anonymous: data.anonymous
603
- }).then((a) => {
604
- if (!a || !("id" in a)) {
605
- setError(true);
606
- return;
607
- }
608
- analytics.captureEvent("post", "answer");
609
- reset();
610
- onPost(a);
611
- }).catch((_e) => setError(true));
612
- };
613
- useEffect(() => {
614
- if (id) {
615
- qetaApi.getAnswer(question.id, id).then((a) => {
616
- if ("content" in a) {
617
- setValues({ questionId: question.id, answer: a.content });
618
- } else {
619
- setError(true);
620
- }
621
- });
622
- }
623
- }, [id, question, qetaApi]);
624
- useEffect(() => {
625
- reset(values);
626
- }, [values, reset]);
627
- return /* @__PURE__ */ React.createElement(
628
- RequirePermission,
629
- {
630
- permission: qetaCreateAnswerPermission,
631
- errorPage: /* @__PURE__ */ React.createElement(React.Fragment, null)
632
- },
633
- /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit(postAnswer), className: "qetaAnswerForm" }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "Your answer"), error && /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not post answer" }), /* @__PURE__ */ React.createElement(
634
- Controller,
635
- {
636
- control,
637
- defaultValue: "",
638
- rules: {
639
- required: true
640
- },
641
- render: ({ field: { onChange, value } }) => /* @__PURE__ */ React.createElement(
642
- MarkdownEditor,
643
- {
644
- value,
645
- onChange,
646
- height: 200,
647
- error: "answer" in errors,
648
- config: configApi,
649
- onImageUpload: (imageId) => {
650
- setImages((prevImages) => [...prevImages, imageId]);
651
- }
652
- }
653
- ),
654
- name: "answer"
655
- }
656
- ), allowAnonymouns && !id && /* @__PURE__ */ React.createElement(
657
- AskAnonymouslyCheckbox,
658
- {
659
- control,
660
- label: "Answer anonymously"
661
- }
662
- ), /* @__PURE__ */ React.createElement(
663
- Button,
664
- {
665
- variant: "outlined",
666
- type: "submit",
667
- color: "primary",
668
- className: `qetaAnswerFormPostBtn ${styles.postButton}`
669
- },
670
- id ? "Save" : "Post"
671
- ))
672
- );
673
- };
674
-
675
- const AnswerCard = (props) => {
676
- const { answer, question } = props;
677
- const styles = useStyles$1();
678
- const [editMode, setEditMode] = React.useState(false);
679
- const [answerEntity, setAnswerEntity] = React.useState(answer);
680
- const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
681
- const handleDeleteModalOpen = () => setDeleteModalOpen(true);
682
- const handleDeleteModalClose = () => setDeleteModalOpen(false);
683
- const highlightedAnswer = window.location.hash.slice(1) === `answer_${answer.id}`;
684
- const onAnswerEdit = (a) => {
685
- setEditMode(false);
686
- setAnswerEntity(a);
687
- };
688
- const onCommentAction = (_, a) => {
689
- if (a) {
690
- setAnswerEntity(a);
691
- }
692
- };
693
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
694
- Card,
695
- {
696
- id: `answer_${answer.id}`,
697
- className: `qetaAnswerCard ${styles.questionCard} ${highlightedAnswer ? styles.highlight : ""}`
698
- },
699
- /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement("div", { className: styles.questionCardVote }, /* @__PURE__ */ React.createElement(VoteButtons, { entity: answerEntity, question }), /* @__PURE__ */ React.createElement(LinkButton, { entity: answerEntity })), /* @__PURE__ */ React.createElement("div", { className: styles.answerCardContent }, editMode ? /* @__PURE__ */ React.createElement(
700
- AnswerForm,
701
- {
702
- question,
703
- onPost: onAnswerEdit,
704
- id: answerEntity.id
705
- }
706
- ) : /* @__PURE__ */ React.createElement(Grid, { container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, /* @__PURE__ */ React.createElement(
707
- MarkdownContent,
708
- {
709
- className: `qetaAndwerCardAnswerContent ${styles.markdownContent}`,
710
- content: answerEntity.content,
711
- dialect: "gfm"
712
- }
713
- ))), /* @__PURE__ */ React.createElement(Grid, { item: true, container: true, justifyContent: "space-around" }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 9, style: { alignSelf: "flex-end" } }, (answerEntity.own || answerEntity.canDelete || answerEntity.canEdit) && /* @__PURE__ */ React.createElement(
714
- Box,
715
- {
716
- className: `qetaAnswerCardActions ${styles.questionCardActions}`
717
- },
718
- !answerEntity.correct && (answerEntity.own || answerEntity.canDelete) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
719
- Button,
720
- {
721
- variant: "outlined",
722
- size: "small",
723
- color: "secondary",
724
- onClick: handleDeleteModalOpen,
725
- className: `${styles.marginRight} qetaAnswerCardDeleteBtn`,
726
- startIcon: /* @__PURE__ */ React.createElement(DeleteIcon, null)
727
- },
728
- "Delete"
729
- ), /* @__PURE__ */ React.createElement(
730
- DeleteModal,
731
- {
732
- open: deleteModalOpen,
733
- onClose: handleDeleteModalClose,
734
- entity: answerEntity,
735
- question
736
- }
737
- )),
738
- (answerEntity.own || answerEntity.canEdit) && /* @__PURE__ */ React.createElement(
739
- Button,
740
- {
741
- variant: "outlined",
742
- size: "small",
743
- startIcon: /* @__PURE__ */ React.createElement(EditIcon, null),
744
- onClick: () => setEditMode(true),
745
- className: "qetaAnswerCardEditBtn"
746
- },
747
- "Edit"
748
- )
749
- )), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 3, className: styles.noPadding }, /* @__PURE__ */ React.createElement(AuthorBox, { entity: answerEntity }))))))
750
- ), /* @__PURE__ */ React.createElement(
751
- CommentSection,
752
- {
753
- question,
754
- answer: answerEntity,
755
- onCommentPost: onCommentAction,
756
- onCommentDelete: onCommentAction
757
- }
758
- ));
759
- };
760
-
761
- const QuestionPage = () => {
762
- var _a;
763
- const { id } = useParams();
764
- const styles = useStyles$1();
765
- const [newAnswers, setNewAnswers] = React.useState([]);
766
- const [answerSort, setAnswerSort] = React.useState("default");
767
- const [searchParams] = useSearchParams();
768
- const [answersCount, setAnswersCount] = useState(0);
769
- const [views, setViews] = useState(0);
770
- const { lastSignal } = useSignal(`qeta:question_${id}`);
771
- const {
772
- value: question,
773
- loading,
774
- error
775
- } = useQetaApi((api) => api.getQuestion(id), [id]);
776
- useEffect(() => {
777
- if (question) {
778
- setAnswersCount(question.answersCount);
779
- setViews(question.views);
780
- }
781
- }, [question]);
782
- useEffect(() => {
783
- if ((lastSignal == null ? void 0 : lastSignal.type) === "question_stats") {
784
- setAnswersCount(lastSignal.answersCount);
785
- setViews(lastSignal.views);
786
- }
787
- }, [lastSignal]);
788
- const onAnswerPost = (answer) => {
789
- setNewAnswers(newAnswers.concat([answer]));
790
- };
791
- const getDescription = (q) => {
792
- return /* @__PURE__ */ React.createElement("span", null, "Asked", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline", sx: { mr: 2 } }, /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: q.created })), q.updated && /* @__PURE__ */ React.createElement(React.Fragment, null, "Updated", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline", sx: { mr: 2 } }, /* @__PURE__ */ React.createElement(RelativeTimeWithTooltip, { value: q.updated }), " by", " ", /* @__PURE__ */ React.createElement(UpdatedByLink, { entity: q }))), "Viewed", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline" }, views, " times"));
793
- };
794
- if (loading) {
795
- return /* @__PURE__ */ React.createElement(Skeleton, { variant: "rect", height: 200 });
796
- }
797
- if (error || question === void 0) {
798
- return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load question." }, error == null ? void 0 : error.message);
799
- }
800
- const sortAnswers = (a, b) => {
801
- var _a2, _b, _c, _d, _e, _f;
802
- if (answerSort === "default") {
803
- return 1;
804
- }
805
- const parts = answerSort.split("_");
806
- const field = parts[0];
807
- const order = parts[1];
808
- let ret = -1;
809
- switch (field) {
810
- case "created":
811
- ret = a.created > b.created ? -1 : 1;
812
- break;
813
- case "score":
814
- ret = a.score > b.score ? -1 : 1;
815
- break;
816
- case "author":
817
- ret = a.author > b.author ? -1 : 1;
818
- break;
819
- case "comments":
820
- ret = ((_b = (_a2 = a.comments) == null ? void 0 : _a2.length) != null ? _b : 0) > ((_d = (_c = b.comments) == null ? void 0 : _c.length) != null ? _d : 0) ? -1 : 1;
821
- break;
822
- case "updated":
823
- ret = ((_e = a.updated) != null ? _e : a.created) > ((_f = b.updated) != null ? _f : b.created) ? -1 : 1;
824
- break;
825
- default:
826
- return 1;
827
- }
828
- if (order === "desc") {
829
- ret *= -1;
830
- }
831
- return ret;
832
- };
833
- const allAnswers = ((_a = question.answers) != null ? _a : []).concat(newAnswers);
834
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(
835
- ContentHeader,
836
- {
837
- title: question.title,
838
- description: getDescription(question)
839
- },
840
- /* @__PURE__ */ React.createElement(
841
- BackToQuestionsButton,
842
- {
843
- entityPage: searchParams.get("entityPage") === "true"
844
- }
845
- ),
846
- /* @__PURE__ */ React.createElement(AskQuestionButton, null)
847
- ), /* @__PURE__ */ React.createElement(QuestionCard, { question }), /* @__PURE__ */ React.createElement(Box, { sx: { mt: 3, mb: 2 } }, /* @__PURE__ */ React.createElement(Grid, { container: true, justifyContent: "space-between", alignItems: "center" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, answersCount + newAnswers.length, " answers")), allAnswers.length > 1 && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(FormControl, null, /* @__PURE__ */ React.createElement(
848
- Select,
849
- {
850
- native: true,
851
- label: "Sort answers",
852
- value: answerSort,
853
- onChange: (val) => setAnswerSort(val.target.value),
854
- inputProps: {
855
- name: "sortAnswers",
856
- id: "sort-answers"
857
- }
858
- },
859
- /* @__PURE__ */ React.createElement("option", { value: "default" }, "Default"),
860
- /* @__PURE__ */ React.createElement("option", { value: "created_desc" }, "Created (desc)"),
861
- /* @__PURE__ */ React.createElement("option", { value: "created_asc" }, "Created (asc)"),
862
- /* @__PURE__ */ React.createElement("option", { value: "score_desc" }, "Score (desc)"),
863
- /* @__PURE__ */ React.createElement("option", { value: "score_asc" }, "Score (asc)"),
864
- /* @__PURE__ */ React.createElement("option", { value: "comments_desc" }, "Comments (desc)"),
865
- /* @__PURE__ */ React.createElement("option", { value: "comments_asc" }, "Comments (asc)"),
866
- /* @__PURE__ */ React.createElement("option", { value: "author_desc" }, "Author (desc)"),
867
- /* @__PURE__ */ React.createElement("option", { value: "author_asc" }, "Author (asc)"),
868
- /* @__PURE__ */ React.createElement("option", { value: "updated_desc" }, "Updated (desc)"),
869
- /* @__PURE__ */ React.createElement("option", { value: "updated_asc" }, "Updated (asc)")
870
- ))))), allAnswers.sort(sortAnswers).map((a) => {
871
- return /* @__PURE__ */ React.createElement(React.Fragment, { key: a.id }, /* @__PURE__ */ React.createElement(Divider, { className: styles.questionDivider }), /* @__PURE__ */ React.createElement(Box, { key: a.id, sx: { mb: 1 } }, /* @__PURE__ */ React.createElement(AnswerCard, { answer: a, question })));
872
- }), /* @__PURE__ */ React.createElement(Divider, { className: styles.questionDivider }), /* @__PURE__ */ React.createElement(AnswerForm, { question, onPost: onAnswerPost })));
873
- };
874
-
875
- const TagsContainer = () => {
876
- const [searchQuery, setSearchQuery] = React.useState("");
877
- const tagRoute = useRouteRef(tagRouteRef);
878
- const {
879
- value: response,
880
- loading,
881
- error
882
- } = useQetaApi((api) => api.getTags(), []);
883
- if (loading) {
884
- return /* @__PURE__ */ React.createElement(Progress, null);
885
- }
886
- if (error || response === void 0) {
887
- return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load tags." }, error == null ? void 0 : error.message);
888
- }
889
- const filterData = (query, data) => {
890
- if (!query) {
891
- return data;
892
- }
893
- return data.filter((t) => t.tag.toLowerCase().includes(query));
894
- };
895
- const tags = filterData(searchQuery, response);
896
- return /* @__PURE__ */ React.createElement(Grid, { container: true, className: "qetaTagsContainer" }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
897
- TextField,
898
- {
899
- id: "search-bar",
900
- className: "text qetaTagsContainerSearchInput",
901
- onChange: (event) => setSearchQuery(event.target.value),
902
- label: "Search tag",
903
- variant: "outlined",
904
- placeholder: "Search...",
905
- size: "small"
906
- }
907
- ), /* @__PURE__ */ React.createElement(IconButton, { type: "submit", "aria-label": "search" })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(
908
- Typography,
909
- {
910
- variant: "h6",
911
- className: "qetaTagsContainerTitle"
912
- },
913
- `Showing ${tags.length} tags`
914
- )), /* @__PURE__ */ React.createElement(Grid, { item: true }, tags.map((tag) => /* @__PURE__ */ React.createElement(
915
- Chip,
916
- {
917
- key: tag.tag,
918
- variant: "outlined",
919
- avatar: /* @__PURE__ */ React.createElement(Avatar, null, tag.questionsCount),
920
- label: tag.tag,
921
- className: "qetaTagsContainerChip",
922
- component: "a",
923
- clickable: true,
924
- href: tagRoute({ tag: tag.tag })
925
- }
926
- ))));
927
- };
928
-
929
- const TagPage = () => {
930
- const { tag } = useParams();
931
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(ContentHeader, { title: tag ? `Questions tagged [${tag}]` : "Tags" }, /* @__PURE__ */ React.createElement(BackToQuestionsButton, null), /* @__PURE__ */ React.createElement(AskQuestionButton, { tags: tag ? [tag] : void 0 })), tag ? /* @__PURE__ */ React.createElement(QuestionsContainer, { tags: [tag != null ? tag : ""] }) : /* @__PURE__ */ React.createElement(TagsContainer, null)));
932
- };
933
-
934
- const UserPage = () => {
935
- var _a;
936
- const identity = (_a = useParams()["*"]) != null ? _a : "unknown";
937
- const presentation = useEntityPresentation(identity);
938
- return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(ContentHeader, { title: `Questions by ${presentation.primaryTitle}` }, /* @__PURE__ */ React.createElement(BackToQuestionsButton, null), /* @__PURE__ */ React.createElement(AskQuestionButton, null)), /* @__PURE__ */ React.createElement(
939
- QuestionsContainer,
940
- {
941
- author: identity != null ? identity : "",
942
- showNoQuestionsBtn: false
943
- }
944
- )));
945
- };
946
-
947
- const QuestionHighlightList = (props) => {
948
- var _a;
949
- const {
950
- value: response,
951
- loading,
952
- error
953
- } = useQetaApi((api) => api.getQuestionsList(props.type), []);
954
- const classes = useStyles$1();
955
- const questionRoute = useRouteRef(questionRouteRef);
956
- const questions = (_a = response == null ? void 0 : response.questions) != null ? _a : [];
957
- return /* @__PURE__ */ React.createElement(
958
- Box,
959
- {
960
- className: `qetaQuestionHighlightList ${classes.questionHighlightList}`,
961
- display: { md: "none", lg: "block" }
962
- },
963
- /* @__PURE__ */ React.createElement(
964
- List,
965
- {
966
- component: "nav",
967
- "aria-labelledby": "nested-list-subheader",
968
- className: "qetaQuestionHighlightListList",
969
- subheader: /* @__PURE__ */ React.createElement(
970
- ListSubheader,
971
- {
972
- disableSticky: true,
973
- component: "div",
974
- id: "nested-list-subheader",
975
- color: "primary"
976
- },
977
- props.title,
978
- props.icon
979
- )
980
- },
981
- loading && /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(Skeleton, { variant: "rect" })),
982
- error && /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemText, null, "Failed to load questions")),
983
- !error && questions.length === 0 && /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemText, null, props.noQuestionsLabel)),
984
- !error && questions.map((q) => /* @__PURE__ */ React.createElement(React.Fragment, { key: q.id }, /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(
985
- ListItem,
986
- {
987
- className: "qetaQuestionHighlightListListItem",
988
- button: true,
989
- dense: true,
990
- component: "a",
991
- href: questionRoute({ id: q.id.toString(10) })
992
- },
993
- /* @__PURE__ */ React.createElement(ListItemText, null, q.title)
994
- )))
995
- )
996
- );
997
- };
998
-
999
- const FavoritePage = () => {
1000
- return /* @__PURE__ */ React.createElement(Content, { className: "qetaFavoritePage" }, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Your favorite questions" }, /* @__PURE__ */ React.createElement(BackToQuestionsButton, null), /* @__PURE__ */ React.createElement(AskQuestionButton, null)), /* @__PURE__ */ React.createElement(QuestionsContainer, { favorite: true })));
1001
- };
1002
-
1003
- const MoreMenu = () => {
1004
- const [anchorEl, setAnchorEl] = React.useState(null);
1005
- const tagsRoute = useRouteRef(tagsRouteRef);
1006
- const favoritesRoute = useRouteRef(favoriteQuestionsRouteRef);
1007
- const statisticsRoute = useRouteRef(statisticsRouteRef);
1008
- const userRoute = useRouteRef(userRouteRef);
1009
- const open = Boolean(anchorEl);
1010
- const styles = useStyles$1();
1011
- const {
1012
- value: user,
1013
- loading: loadingUser,
1014
- error: userError
1015
- } = useIdentityApi((api) => api.getBackstageIdentity(), []);
1016
- const handleMenuOpen = (event) => {
1017
- setAnchorEl(event.currentTarget);
1018
- };
1019
- const handleMenuClose = () => {
1020
- setAnchorEl(null);
1021
- };
1022
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip, { arrow: true, title: "More" }, /* @__PURE__ */ React.createElement(
1023
- IconButton,
1024
- {
1025
- "aria-label": "more",
1026
- "aria-controls": "long-menu",
1027
- "aria-haspopup": "true",
1028
- onClick: handleMenuOpen
1029
- },
1030
- /* @__PURE__ */ React.createElement(MoreVertIcon, null)
1031
- )), /* @__PURE__ */ React.createElement(
1032
- Menu,
1033
- {
1034
- id: "long-menu",
1035
- anchorEl,
1036
- keepMounted: true,
1037
- open,
1038
- getContentAnchorEl: null,
1039
- anchorOrigin: {
1040
- vertical: "bottom",
1041
- horizontal: "center"
1042
- },
1043
- transformOrigin: {
1044
- vertical: "top",
1045
- horizontal: "center"
1046
- },
1047
- onClose: handleMenuClose
1048
- },
1049
- /* @__PURE__ */ React.createElement(MenuItem, { component: "a", href: tagsRoute() }, /* @__PURE__ */ React.createElement(ListItemIcon, { className: styles.menuIcon }, /* @__PURE__ */ React.createElement(LoyaltyOutlined, { fontSize: "small" })), "Tags"),
1050
- user && !loadingUser && !userError && /* @__PURE__ */ React.createElement(MenuItem, { component: "a", href: `${userRoute()}/${user.userEntityRef}` }, /* @__PURE__ */ React.createElement(ListItemIcon, { className: styles.menuIcon }, /* @__PURE__ */ React.createElement(AccountBox, { fontSize: "small" })), "My questions"),
1051
- /* @__PURE__ */ React.createElement(MenuItem, { component: "a", href: favoritesRoute() }, /* @__PURE__ */ React.createElement(ListItemIcon, { className: styles.menuIcon }, /* @__PURE__ */ React.createElement(StarIcon, { fontSize: "small" })), "Favorite questions"),
1052
- /* @__PURE__ */ React.createElement(MenuItem, { component: "a", href: statisticsRoute() }, /* @__PURE__ */ React.createElement(ListItemIcon, { className: styles.menuIcon }, /* @__PURE__ */ React.createElement(TrophyIcon, null)), "Statistics")
1053
- ));
1054
- };
1055
- const HomePageContent = () => {
1056
- const [searchParams] = useSearchParams();
1057
- const [entityRef, setEntityRef] = React.useState(
1058
- void 0
1059
- );
1060
- const [tags, setTags] = React.useState(void 0);
1061
- useEffect(() => {
1062
- var _a;
1063
- setEntityRef((_a = searchParams.get("entity")) != null ? _a : void 0);
1064
- setTags(filterTags(searchParams.get("tags")));
1065
- }, [searchParams, setEntityRef]);
1066
- return /* @__PURE__ */ React.createElement(Content, { className: "qetaHomePage" }, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, lg: 9, xl: 10 }, /* @__PURE__ */ React.createElement(ContentHeader, { title: "All questions" }, /* @__PURE__ */ React.createElement(MoreMenu, null), /* @__PURE__ */ React.createElement(AskQuestionButton, { entity: entityRef, tags })), /* @__PURE__ */ React.createElement(QuestionsContainer, { entity: entityRef, tags })), /* @__PURE__ */ React.createElement(Grid, { item: true, lg: 3, xl: 2 }, /* @__PURE__ */ React.createElement(
1067
- QuestionHighlightList,
1068
- {
1069
- type: "hot",
1070
- title: "Hot questions",
1071
- noQuestionsLabel: "No questions",
1072
- icon: /* @__PURE__ */ React.createElement(Whatshot, { fontSize: "small" })
1073
- }
1074
- ), /* @__PURE__ */ React.createElement(
1075
- QuestionHighlightList,
1076
- {
1077
- type: "unanswered",
1078
- title: "Unanswered questions",
1079
- noQuestionsLabel: "No unanswered questions"
1080
- }
1081
- ), /* @__PURE__ */ React.createElement(
1082
- QuestionHighlightList,
1083
- {
1084
- type: "incorrect",
1085
- title: "Questions without correct answer",
1086
- noQuestionsLabel: "No questions without correct answers"
1087
- }
1088
- )))));
1089
- };
1090
- const HomePage = (props) => {
1091
- const {
1092
- title = "Q&A",
1093
- subtitle,
1094
- headerElements,
1095
- themeId = "tool",
1096
- headerTooltip,
1097
- headerType,
1098
- headerTypeLink
1099
- } = props != null ? props : {};
1100
- return /* @__PURE__ */ React.createElement(Page, { themeId }, /* @__PURE__ */ React.createElement(
1101
- Header,
1102
- {
1103
- title,
1104
- subtitle,
1105
- type: headerType,
1106
- typeLink: headerTypeLink,
1107
- tooltip: headerTooltip
1108
- },
1109
- headerElements
1110
- ), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: "/", element: /* @__PURE__ */ React.createElement(HomePageContent, null) }), /* @__PURE__ */ React.createElement(Route, { path: askRouteRef.path, element: /* @__PURE__ */ React.createElement(AskPage, null) }), /* @__PURE__ */ React.createElement(
1111
- Route,
1112
- {
1113
- path: favoriteQuestionsRouteRef.path,
1114
- element: /* @__PURE__ */ React.createElement(FavoritePage, null)
1115
- }
1116
- ), /* @__PURE__ */ React.createElement(Route, { path: editQuestionRouteRef.path, element: /* @__PURE__ */ React.createElement(AskPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: questionRouteRef.path, element: /* @__PURE__ */ React.createElement(QuestionPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: tagsRouteRef.path, element: /* @__PURE__ */ React.createElement(TagPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: tagRouteRef.path, element: /* @__PURE__ */ React.createElement(TagPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: userRouteRef.path, element: /* @__PURE__ */ React.createElement(UserPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: statisticsRouteRef.path, element: /* @__PURE__ */ React.createElement(StatisticsPage, null) })));
1117
- };
1118
-
1119
- export { HomePage };
1120
- //# sourceMappingURL=index-rSPV8dxK.esm.js.map