@drodil/backstage-plugin-qeta 1.10.2 → 1.11.0

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.
@@ -1,4 +1,4 @@
1
- export { C as Content, d as QuestionsTable } from './index-ceb65cf4.esm.js';
1
+ export { C as Content, g as QuestionsTable } from './index-6bfa5b72.esm.js';
2
2
  import '@backstage/core-plugin-api';
3
3
  import '@backstage/plugin-home';
4
4
  import '@backstage/errors';
@@ -25,4 +25,7 @@ import 'react-mde/lib/styles/css/react-mde-editor.css';
25
25
  import 'react-mde/lib/styles/css/react-mde-toolbar.css';
26
26
  import 'file-type';
27
27
  import '@material-ui/icons/Refresh';
28
- //# sourceMappingURL=index-7d99dfe6.esm.js.map
28
+ import '@material-ui/icons/HomeOutlined';
29
+ import '@backstage/plugin-permission-react';
30
+ import '@drodil/backstage-plugin-qeta-common';
31
+ //# sourceMappingURL=index-4867abd8.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-4867abd8.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,15 +1,15 @@
1
- import { createRouteRef, createApiRef, createPlugin, createApiFactory, configApiRef, fetchApiRef, createRoutableExtension, useApi, identityApiRef, errorApiRef, useAnalytics } from '@backstage/core-plugin-api';
1
+ import { createRouteRef, createApiRef, createPlugin, createApiFactory, fetchApiRef, discoveryApiRef, createRoutableExtension, useApi, identityApiRef, configApiRef, errorApiRef, useAnalytics } from '@backstage/core-plugin-api';
2
2
  import { createCardExtension } from '@backstage/plugin-home';
3
3
  import { CustomErrorBase } from '@backstage/errors';
4
4
  import omitBy from 'lodash/omitBy';
5
5
  import isEmpty from 'lodash/isEmpty';
6
6
  import { useAsync } from 'react-use';
7
- import { makeStyles, Box, Grid, FormGroup, FormLabel, FormControlLabel, Checkbox, FormControl, RadioGroup, Radio, Chip, Tooltip, useTheme, Card, CardContent, Typography, Divider, Select, MenuItem, TextField, Button, Collapse, ButtonGroup, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, TablePagination } from '@material-ui/core';
7
+ import { makeStyles, Box, Grid, FormGroup, FormLabel, FormControlLabel, Checkbox, FormControl, RadioGroup, Radio, Chip, Tooltip, useTheme, Card, CardContent, Typography, Divider, Select, MenuItem, TextField, Button, Collapse, ButtonGroup, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, TablePagination, SvgIcon, ListItem, ListItemAvatar, Avatar, ListItemText, List, Container } from '@material-ui/core';
8
8
  import { catalogApiRef } from '@backstage/plugin-catalog-react';
9
9
  import { trimEnd, compact } from 'lodash';
10
10
  import React, { useEffect, useMemo } from 'react';
11
11
  import useDebounce from 'react-use/lib/useDebounce';
12
- import { Link, Progress, WarningPanel, LinkButton, MarkdownContent } from '@backstage/core-components';
12
+ import { Link, Progress, WarningPanel, LinkButton, MarkdownContent, CardTab, TabbedCard, Content as Content$1, ContentHeader } from '@backstage/core-components';
13
13
  import RelativeTime from 'react-relative-time';
14
14
  import DOMPurify from 'dompurify';
15
15
  import { stringifyEntityRef } from '@backstage/catalog-model';
@@ -24,6 +24,9 @@ import 'react-mde/lib/styles/css/react-mde-editor.css';
24
24
  import 'react-mde/lib/styles/css/react-mde-toolbar.css';
25
25
  import FileType from 'file-type';
26
26
  import RefreshIcon from '@material-ui/icons/Refresh';
27
+ import HomeOutlined from '@material-ui/icons/HomeOutlined';
28
+ import { RequirePermission } from '@backstage/plugin-permission-react';
29
+ import { qetaCreateQuestionPermission } from '@drodil/backstage-plugin-qeta-common';
27
30
 
28
31
  const rootRouteRef = createRouteRef({
29
32
  id: "qeta"
@@ -41,11 +44,14 @@ class QetaError extends CustomErrorBase {
41
44
  class QetaClient {
42
45
  constructor(options) {
43
46
  this.fetchApi = options.fetchApi;
44
- this.baseUrl = options.configApi.getString("backend.baseUrl");
47
+ this.discoveryApi = options.discoveryApi;
48
+ }
49
+ async getBaseUrl() {
50
+ return this.discoveryApi.getBaseUrl("qeta");
45
51
  }
46
52
  async getQuestions(options) {
47
53
  const query = this.getQueryParameters(options).toString();
48
- let url = `${this.baseUrl}/api/qeta/questions`;
54
+ let url = `${await this.getBaseUrl()}/questions`;
49
55
  if (query) {
50
56
  url += `?${query}`;
51
57
  }
@@ -61,7 +67,7 @@ class QetaClient {
61
67
  }
62
68
  async getQuestionsList(type) {
63
69
  const query = new URLSearchParams({ limit: "7" }).toString();
64
- let url = `${this.baseUrl}/api/qeta/questions/list/${type}`;
70
+ let url = `${await this.getBaseUrl()}/questions/list/${type}`;
65
71
  if (query) {
66
72
  url += `?${query}`;
67
73
  }
@@ -77,7 +83,7 @@ class QetaClient {
77
83
  }
78
84
  async postQuestion(question) {
79
85
  const response = await this.fetchApi.fetch(
80
- `${this.baseUrl}/api/qeta/questions`,
86
+ `${await this.getBaseUrl()}/questions`,
81
87
  {
82
88
  method: "POST",
83
89
  body: JSON.stringify(question),
@@ -92,7 +98,7 @@ class QetaClient {
92
98
  }
93
99
  async commentQuestion(id, content) {
94
100
  const response = await this.fetchApi.fetch(
95
- `${this.baseUrl}/api/qeta/questions/${id}/comments`,
101
+ `${await this.getBaseUrl()}/questions/${id}/comments`,
96
102
  {
97
103
  method: "POST",
98
104
  body: JSON.stringify({ content }),
@@ -107,7 +113,7 @@ class QetaClient {
107
113
  }
108
114
  async deleteQuestionComment(questionId, id) {
109
115
  const response = await this.fetchApi.fetch(
110
- `${this.baseUrl}/api/qeta/questions/${questionId}/comments/${id}`,
116
+ `${await this.getBaseUrl()}/questions/${questionId}/comments/${id}`,
111
117
  {
112
118
  method: "DELETE"
113
119
  }
@@ -123,7 +129,7 @@ class QetaClient {
123
129
  throw new QetaError("Invalid id provided", void 0);
124
130
  }
125
131
  const response = await this.fetchApi.fetch(
126
- `${this.baseUrl}/api/qeta/questions/${id}`
132
+ `${await this.getBaseUrl()}/questions/${id}`
127
133
  );
128
134
  const data = await response.json();
129
135
  if ("errors" in data) {
@@ -132,7 +138,9 @@ class QetaClient {
132
138
  return data;
133
139
  }
134
140
  async getTags() {
135
- const response = await this.fetchApi.fetch(`${this.baseUrl}/api/qeta/tags`);
141
+ const response = await this.fetchApi.fetch(
142
+ `${await this.getBaseUrl()}/tags`
143
+ );
136
144
  return await response.json();
137
145
  }
138
146
  async voteQuestionUp(id) {
@@ -140,7 +148,7 @@ class QetaClient {
140
148
  throw new QetaError("Invalid id provided", void 0);
141
149
  }
142
150
  const response = await this.fetchApi.fetch(
143
- `${this.baseUrl}/api/qeta/questions/${id}/upvote`
151
+ `${await this.getBaseUrl()}/questions/${id}/upvote`
144
152
  );
145
153
  const data = await response.json();
146
154
  if ("errors" in data) {
@@ -153,7 +161,7 @@ class QetaClient {
153
161
  throw new QetaError("Invalid id provided", void 0);
154
162
  }
155
163
  const response = await this.fetchApi.fetch(
156
- `${this.baseUrl}/api/qeta/questions/${id}/downvote`
164
+ `${await this.getBaseUrl()}/questions/${id}/downvote`
157
165
  );
158
166
  const data = await response.json();
159
167
  if ("errors" in data) {
@@ -166,7 +174,7 @@ class QetaClient {
166
174
  throw new QetaError("Invalid id provided", void 0);
167
175
  }
168
176
  const response = await this.fetchApi.fetch(
169
- `${this.baseUrl}/api/qeta/questions/${id}/favorite`
177
+ `${await this.getBaseUrl()}/questions/${id}/favorite`
170
178
  );
171
179
  const data = await response.json();
172
180
  if ("errors" in data) {
@@ -179,7 +187,7 @@ class QetaClient {
179
187
  throw new QetaError("Invalid id provided", void 0);
180
188
  }
181
189
  const response = await this.fetchApi.fetch(
182
- `${this.baseUrl}/api/qeta/questions/${id}/unfavorite`
190
+ `${await this.getBaseUrl()}/questions/${id}/unfavorite`
183
191
  );
184
192
  const data = await response.json();
185
193
  if ("errors" in data) {
@@ -189,7 +197,7 @@ class QetaClient {
189
197
  }
190
198
  async postAnswer(answer) {
191
199
  const response = await this.fetchApi.fetch(
192
- `${this.baseUrl}/api/qeta/questions/${answer.questionId}/answers`,
200
+ `${await this.getBaseUrl()}/questions/${answer.questionId}/answers`,
193
201
  {
194
202
  method: "POST",
195
203
  body: JSON.stringify({ answer: answer.answer, images: answer.images }),
@@ -204,7 +212,7 @@ class QetaClient {
204
212
  }
205
213
  async commentAnswer(questionId, id, content) {
206
214
  const response = await this.fetchApi.fetch(
207
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/comments`,
215
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}/comments`,
208
216
  {
209
217
  method: "POST",
210
218
  body: JSON.stringify({ content }),
@@ -219,7 +227,7 @@ class QetaClient {
219
227
  }
220
228
  async deleteAnswerComment(questionId, answerId, id) {
221
229
  const response = await this.fetchApi.fetch(
222
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${answerId}/comments/${id}`,
230
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${answerId}/comments/${id}`,
223
231
  {
224
232
  method: "DELETE"
225
233
  }
@@ -235,7 +243,7 @@ class QetaClient {
235
243
  throw new QetaError("Invalid id provided", void 0);
236
244
  }
237
245
  const response = await this.fetchApi.fetch(
238
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/upvote`
246
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}/upvote`
239
247
  );
240
248
  const data = await response.json();
241
249
  if ("errors" in data) {
@@ -248,7 +256,7 @@ class QetaClient {
248
256
  throw new QetaError("Invalid id provided", void 0);
249
257
  }
250
258
  const response = await this.fetchApi.fetch(
251
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/downvote`
259
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}/downvote`
252
260
  );
253
261
  const data = await response.json();
254
262
  if ("errors" in data) {
@@ -261,7 +269,7 @@ class QetaClient {
261
269
  throw new QetaError("Invalid id provided", void 0);
262
270
  }
263
271
  const response = await this.fetchApi.fetch(
264
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/correct`
272
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}/correct`
265
273
  );
266
274
  const data = await response;
267
275
  return data.ok;
@@ -271,7 +279,7 @@ class QetaClient {
271
279
  throw new QetaError("Invalid id provided", void 0);
272
280
  }
273
281
  const response = await this.fetchApi.fetch(
274
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/incorrect`
282
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}/incorrect`
275
283
  );
276
284
  const data = await response;
277
285
  return data.ok;
@@ -281,7 +289,7 @@ class QetaClient {
281
289
  throw new QetaError("Invalid id provided", void 0);
282
290
  }
283
291
  const response = await this.fetchApi.fetch(
284
- `${this.baseUrl}/api/qeta/questions/${questionId}`,
292
+ `${await this.getBaseUrl()}/questions/${questionId}`,
285
293
  {
286
294
  method: "DELETE"
287
295
  }
@@ -294,7 +302,7 @@ class QetaClient {
294
302
  throw new QetaError("Invalid id provided", void 0);
295
303
  }
296
304
  const response = await this.fetchApi.fetch(
297
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}`,
305
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}`,
298
306
  {
299
307
  method: "DELETE"
300
308
  }
@@ -304,7 +312,7 @@ class QetaClient {
304
312
  }
305
313
  async updateQuestion(id, question) {
306
314
  const response = await this.fetchApi.fetch(
307
- `${this.baseUrl}/api/qeta/questions/${id}`,
315
+ `${await this.getBaseUrl()}/questions/${id}`,
308
316
  {
309
317
  method: "POST",
310
318
  body: JSON.stringify(question),
@@ -319,7 +327,7 @@ class QetaClient {
319
327
  }
320
328
  async updateAnswer(id, answer) {
321
329
  const response = await this.fetchApi.fetch(
322
- `${this.baseUrl}/api/qeta/questions/${answer.questionId}/answers/${id}`,
330
+ `${await this.getBaseUrl()}/questions/${answer.questionId}/answers/${id}`,
323
331
  {
324
332
  method: "POST",
325
333
  body: JSON.stringify({ answer: answer.answer, images: answer.images }),
@@ -337,7 +345,7 @@ class QetaClient {
337
345
  throw new QetaError("Invalid id provided", void 0);
338
346
  }
339
347
  const response = await this.fetchApi.fetch(
340
- `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}`
348
+ `${await this.getBaseUrl()}/questions/${questionId}/answers/${id}`
341
349
  );
342
350
  const data = await response.json();
343
351
  if ("errors" in data) {
@@ -346,7 +354,7 @@ class QetaClient {
346
354
  return data;
347
355
  }
348
356
  async postAttachment(file) {
349
- const qetaUrl = `${this.baseUrl}/api/qeta/attachments`;
357
+ const qetaUrl = `${await this.getBaseUrl()}/attachments`;
350
358
  const formData = new FormData();
351
359
  formData.append("image", file);
352
360
  const requestOptions = {
@@ -356,6 +364,44 @@ class QetaClient {
356
364
  const response = await fetch(qetaUrl, requestOptions);
357
365
  return await response.json();
358
366
  }
367
+ async getMostUpvotedAnswers(options) {
368
+ const query = this.getQueryParameters(options.options).toString();
369
+ let url = `${await this.getBaseUrl()}/statistics/answers/top-upvoted-users`;
370
+ if (query) {
371
+ url += `?${query}`;
372
+ }
373
+ const response = await this.fetchApi.fetch(url);
374
+ const data = await response.json();
375
+ return data;
376
+ }
377
+ async getMostUpvotedCorrectAnswers(options) {
378
+ const query = this.getQueryParameters(options.options).toString();
379
+ let url = `${await this.getBaseUrl()}/statistics/answers/top-correct-upvoted-users`;
380
+ if (query) {
381
+ url += `?${query}`;
382
+ }
383
+ const response = await this.fetchApi.fetch(url);
384
+ const data = await response.json();
385
+ return data;
386
+ }
387
+ async getMostUpvotedQuestions(options) {
388
+ const query = this.getQueryParameters(options.options).toString();
389
+ let url = `${await this.getBaseUrl()}/statistics/answers/top-correct-upvoted-users`;
390
+ if (query) {
391
+ url += `?${query}`;
392
+ }
393
+ const response = await this.fetchApi.fetch(url);
394
+ const data = await response.json();
395
+ return data;
396
+ }
397
+ async getTopStatisticsHomepage(options) {
398
+ const response = await Promise.all([
399
+ this.getMostUpvotedQuestions(options),
400
+ this.getMostUpvotedAnswers(options),
401
+ this.getMostUpvotedCorrectAnswers(options)
402
+ ]);
403
+ return response;
404
+ }
359
405
  getQueryParameters(params) {
360
406
  const asStrings = Object.fromEntries(
361
407
  Object.entries(params).map(([k, v]) => {
@@ -377,15 +423,15 @@ const qetaPlugin = createPlugin({
377
423
  apis: [
378
424
  createApiFactory({
379
425
  api: qetaApiRef,
380
- deps: { configApi: configApiRef, fetchApi: fetchApiRef },
381
- factory: ({ configApi, fetchApi }) => new QetaClient({ configApi, fetchApi })
426
+ deps: { fetchApi: fetchApiRef, discoveryApi: discoveryApiRef },
427
+ factory: ({ fetchApi, discoveryApi }) => new QetaClient({ fetchApi, discoveryApi })
382
428
  })
383
429
  ]
384
430
  });
385
431
  const QetaPage = qetaPlugin.provide(
386
432
  createRoutableExtension({
387
433
  name: "QetaPage",
388
- component: () => import('./index-6535e416.esm.js').then((m) => m.HomePage),
434
+ component: () => import('./index-8cc27392.esm.js').then((m) => m.HomePage),
389
435
  mountPoint: rootRouteRef
390
436
  })
391
437
  );
@@ -394,7 +440,7 @@ const QuestionTableCard = qetaPlugin.provide(
394
440
  name: "QuestionsTableCard",
395
441
  title: "Q&A",
396
442
  description: "Shows Q&A questions",
397
- components: () => import('./index-7d99dfe6.esm.js'),
443
+ components: () => import('./index-4867abd8.esm.js'),
398
444
  layout: {
399
445
  height: { minRows: 6 },
400
446
  width: { minColumns: 6 }
@@ -434,7 +480,7 @@ function useIdentityApi(f, deps = []) {
434
480
  return await f(identityApi);
435
481
  }, deps);
436
482
  }
437
- const useStyles = makeStyles((theme) => {
483
+ const useStyles$1 = makeStyles((theme) => {
438
484
  return {
439
485
  markdownEditor: {
440
486
  backgroundColor: "initial",
@@ -575,6 +621,11 @@ const useStyles = makeStyles((theme) => {
575
621
  marginTop: theme.spacing(2),
576
622
  float: "right"
577
623
  }
624
+ },
625
+ authorLink: {
626
+ textOverflow: "ellipsis",
627
+ overflow: "hidden",
628
+ whiteSpace: "nowrap"
578
629
  }
579
630
  };
580
631
  });
@@ -613,7 +664,7 @@ const filterKeys = [
613
664
  ];
614
665
  const FilterPanel = (props) => {
615
666
  const { onChange, filters } = props;
616
- const styles = useStyles();
667
+ const styles = useStyles$1();
617
668
  const handleChange = (event) => {
618
669
  let value = event.target.value;
619
670
  if (event.target.type === "checkbox") {
@@ -805,7 +856,7 @@ const QuestionList = (props) => {
805
856
  page,
806
857
  onPageSizeChange
807
858
  } = props;
808
- const styles = useStyles();
859
+ const styles = useStyles$1();
809
860
  const handlePageChange = (_event, value) => {
810
861
  onPageChange(value);
811
862
  };
@@ -1034,7 +1085,7 @@ const MarkdownEditor = (props) => {
1034
1085
  const [selectedTab, setSelectedTab] = React.useState(
1035
1086
  "write"
1036
1087
  );
1037
- const styles = useStyles();
1088
+ const styles = useStyles$1();
1038
1089
  const errorApi = useApi(errorApiRef);
1039
1090
  const qetaApi = useApi(qetaApiRef);
1040
1091
  const imageUpload = () => {
@@ -1147,7 +1198,7 @@ const AskForm = (props) => {
1147
1198
  const qetaApi = useApi(qetaApiRef);
1148
1199
  const catalogApi = useApi(catalogApiRef);
1149
1200
  const configApi = useApi(configApiRef);
1150
- const styles = useStyles();
1201
+ const styles = useStyles$1();
1151
1202
  const {
1152
1203
  register,
1153
1204
  handleSubmit,
@@ -1496,5 +1547,215 @@ const Content = (props) => {
1496
1547
  return /* @__PURE__ */ React.createElement(QuestionsTable, { hideTitle: true, ...props });
1497
1548
  };
1498
1549
 
1499
- export { AskForm as A, Content as C, MarkdownEditor as M, QuestionsContainer as Q, TagsAndEntities as T, useStyles as a, useQetaApi as b, useIdentityApi as c, QuestionsTable as d, qetaPlugin as e, formatEntityName as f, QetaPage as g, QuestionTableCard as h, QetaClient as i, qetaApiRef as q, useBasePath as u };
1500
- //# sourceMappingURL=index-ceb65cf4.esm.js.map
1550
+ const TrophyIcon = (props) => /* @__PURE__ */ React.createElement(SvgIcon, { ...props, viewBox: "0 0 24 24" }, /* @__PURE__ */ React.createElement(
1551
+ "path",
1552
+ {
1553
+ id: "secondary",
1554
+ d: "M17,12a1,1,0,0,1,0-2,3,3,0,0,0,3-3V6H17.17a1,1,0,0,1,0-2H20a2,2,0,0,1,2,2V7A5,5,0,0,1,17,12ZM8,11a1,1,0,0,0-1-1A3,3,0,0,1,4,7V6H6.74a1,1,0,0,0,0-2H4A2,2,0,0,0,2,6V7a5,5,0,0,0,5,5A1,1,0,0,0,8,11Zm5,10V16.18a1,1,0,0,0-2,0V21a1,1,0,0,0,2,0Z"
1555
+ }
1556
+ ), /* @__PURE__ */ React.createElement(
1557
+ "path",
1558
+ {
1559
+ id: "primary",
1560
+ d: "M16,22H8a1,1,0,0,1,0-2h8a1,1,0,0,1,0,2ZM17,2H7A1,1,0,0,0,6,3V9.57a7.75,7.75,0,0,0,4.89,7.22A3,3,0,0,0,12,17a3.13,3.13,0,0,0,1.12-.21A7.76,7.76,0,0,0,18,9.57V3A1,1,0,0,0,17,2Z"
1561
+ }
1562
+ ));
1563
+
1564
+ const useStyles = makeStyles((theme) => {
1565
+ return {
1566
+ trophyIcon: {
1567
+ backgroundColor: "initial",
1568
+ color: theme.palette.text.primary,
1569
+ borderRadius: "50%",
1570
+ boxSizing: "border-box",
1571
+ padding: "1rem",
1572
+ height: 100,
1573
+ width: 100
1574
+ },
1575
+ votesText: {
1576
+ display: "grid",
1577
+ placeItems: "center",
1578
+ marginLeft: "16px"
1579
+ }
1580
+ };
1581
+ });
1582
+
1583
+ const DefaultRankingIcons = /* @__PURE__ */ new Map([
1584
+ [
1585
+ 1,
1586
+ /* @__PURE__ */ React.createElement(
1587
+ TrophyIcon,
1588
+ {
1589
+ style: { color: "#DAA520", height: "2.2rem", width: "2.2rem" }
1590
+ }
1591
+ )
1592
+ ],
1593
+ [
1594
+ 2,
1595
+ /* @__PURE__ */ React.createElement(
1596
+ TrophyIcon,
1597
+ {
1598
+ style: { color: "#C0C0C0", height: "2.1rem", width: "2.1rem" }
1599
+ }
1600
+ )
1601
+ ],
1602
+ [
1603
+ 3,
1604
+ /* @__PURE__ */ React.createElement(TrophyIcon, { style: { color: "#B87333", height: "2rem", width: "2rem" } })
1605
+ ]
1606
+ ]);
1607
+ const DefaultUserIcon = /* @__PURE__ */ React.createElement(TrophyIcon, { style: { height: "2rem", width: "2rem" } });
1608
+ const getOrdinal = (n) => {
1609
+ if (n % 10 === 1 && n % 100 !== 11) {
1610
+ return `${n}st`;
1611
+ } else if (n % 10 === 2 && n % 100 !== 12) {
1612
+ return `${n}nd`;
1613
+ } else if (n % 10 === 3 && n % 100 !== 13) {
1614
+ return `${n}rd`;
1615
+ }
1616
+ return `${n}th`;
1617
+ };
1618
+ const RankingRow = (props) => {
1619
+ var _a, _b, _c;
1620
+ const classes = useStyles();
1621
+ const ordinalPosition = (props == null ? void 0 : props.position) ? getOrdinal(props == null ? void 0 : props.position) : "";
1622
+ const name = (_a = props == null ? void 0 : props.userRef) == null ? void 0 : _a.split("/")[1];
1623
+ const userIcon = ((_b = props.rankingIcon) == null ? void 0 : _b.userRankingIcon) ? (_c = props.rankingIcon) == null ? void 0 : _c.userRankingIcon : DefaultUserIcon;
1624
+ const topRankingIcon = props.rankingIcon ? props.rankingIcon.iconsByRanking.get(Number(props == null ? void 0 : props.position)) : DefaultRankingIcons.get(Number(props == null ? void 0 : props.position)) || DefaultUserIcon;
1625
+ const rankingIcon = (props == null ? void 0 : props.position) > 3 ? userIcon : topRankingIcon;
1626
+ return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemAvatar, null, /* @__PURE__ */ React.createElement(Avatar, { className: classes.trophyIcon }, rankingIcon)), /* @__PURE__ */ React.createElement(
1627
+ ListItemText,
1628
+ {
1629
+ disableTypography: true,
1630
+ style: {
1631
+ display: "flex",
1632
+ justifyContent: "center"
1633
+ },
1634
+ primary: /* @__PURE__ */ React.createElement("div", { style: { display: "flex" } }, /* @__PURE__ */ React.createElement(
1635
+ Typography,
1636
+ {
1637
+ style: { marginRight: "10px", fontWeight: 400 },
1638
+ variant: "subtitle1"
1639
+ },
1640
+ `${ordinalPosition}`
1641
+ ), /* @__PURE__ */ React.createElement(
1642
+ Link,
1643
+ {
1644
+ to: `/qeta/users/${name}`,
1645
+ variant: "subtitle1"
1646
+ },
1647
+ `${name}`
1648
+ ))
1649
+ }
1650
+ ), /* @__PURE__ */ React.createElement("div", { className: classes.votesText }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, props == null ? void 0 : props.votes, " votes")));
1651
+ };
1652
+ const RankingCard = (props) => {
1653
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1654
+ const rankingStats = props.limit ? (_a = props.statistic) == null ? void 0 : _a.ranking.slice(0, props.limit) : (_b = props.statistic) == null ? void 0 : _b.ranking;
1655
+ return /* @__PURE__ */ React.createElement("div", { style: { display: "block" } }, /* @__PURE__ */ React.createElement("span", null, props.description), /* @__PURE__ */ React.createElement(List, null, rankingStats == null ? void 0 : rankingStats.map((authorStats) => {
1656
+ return /* @__PURE__ */ React.createElement(
1657
+ RankingRow,
1658
+ {
1659
+ votes: authorStats.total || 0,
1660
+ position: authorStats.position || 0,
1661
+ userRef: authorStats.author
1662
+ }
1663
+ );
1664
+ }), !(rankingStats == null ? void 0 : rankingStats.some(
1665
+ (authorStats) => {
1666
+ var _a2, _b2;
1667
+ return authorStats.author === ((_b2 = (_a2 = props.statistic) == null ? void 0 : _a2.loggedUser) == null ? void 0 : _b2.author);
1668
+ }
1669
+ )) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("hr", null), /* @__PURE__ */ React.createElement(
1670
+ RankingRow,
1671
+ {
1672
+ votes: ((_d = (_c = props.statistic) == null ? void 0 : _c.loggedUser) == null ? void 0 : _d.total) || 0,
1673
+ position: ((_f = (_e = props.statistic) == null ? void 0 : _e.loggedUser) == null ? void 0 : _f.position) || 0,
1674
+ userRef: (_h = (_g = props.statistic) == null ? void 0 : _g.loggedUser) == null ? void 0 : _h.author
1675
+ }
1676
+ ))));
1677
+ };
1678
+ const TopRankingUsers = (props) => {
1679
+ const {
1680
+ value: topStatistics,
1681
+ loading,
1682
+ error
1683
+ } = useQetaApi(
1684
+ (api) => api.getTopStatisticsHomepage({
1685
+ options: { limit: 50 }
1686
+ })
1687
+ );
1688
+ const tabData = [
1689
+ {
1690
+ title: "Top Upvoted Questions",
1691
+ description: "People who have the highest rated questions"
1692
+ },
1693
+ {
1694
+ title: "Top Upvoted Answers",
1695
+ description: "People who have the highest rated answers"
1696
+ },
1697
+ {
1698
+ title: "Top Upvoted Correct Answers",
1699
+ description: "People who have the highest rated correct answers"
1700
+ }
1701
+ ];
1702
+ if ((error || topStatistics === void 0) && !loading) {
1703
+ return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load statistics." }, error == null ? void 0 : error.message);
1704
+ }
1705
+ let content;
1706
+ if (loading) {
1707
+ content = [
1708
+ /* @__PURE__ */ React.createElement(CardTab, null, /* @__PURE__ */ React.createElement(Progress, null))
1709
+ ];
1710
+ } else if (topStatistics && topStatistics.length > 0) {
1711
+ content = topStatistics == null ? void 0 : topStatistics.map((stats, index) => {
1712
+ return /* @__PURE__ */ React.createElement(CardTab, { label: tabData[index].title }, /* @__PURE__ */ React.createElement(
1713
+ RankingCard,
1714
+ {
1715
+ description: tabData[index].description,
1716
+ limit: 3,
1717
+ statistic: stats
1718
+ }
1719
+ ));
1720
+ });
1721
+ } else {
1722
+ content = [/* @__PURE__ */ React.createElement(CardTab, null, "No statistics available")];
1723
+ }
1724
+ return /* @__PURE__ */ React.createElement(TabbedCard, { title: props.title || "Ranking Q&A \u{1F3C6}" }, content);
1725
+ };
1726
+
1727
+ const AskQuestionButton = () => {
1728
+ return /* @__PURE__ */ React.createElement(
1729
+ RequirePermission,
1730
+ {
1731
+ permission: qetaCreateQuestionPermission,
1732
+ errorPage: /* @__PURE__ */ React.createElement(React.Fragment, null)
1733
+ },
1734
+ /* @__PURE__ */ React.createElement(
1735
+ LinkButton,
1736
+ {
1737
+ variant: "contained",
1738
+ to: "/qeta/ask",
1739
+ color: "primary",
1740
+ startIcon: /* @__PURE__ */ React.createElement(HelpOutline, null)
1741
+ },
1742
+ "Ask question"
1743
+ )
1744
+ );
1745
+ };
1746
+
1747
+ const StatisticsPage = () => {
1748
+ const styles = useStyles$1();
1749
+ return /* @__PURE__ */ React.createElement(Content$1, null, /* @__PURE__ */ React.createElement(Container, { maxWidth: "lg" }, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Statistics" }, /* @__PURE__ */ React.createElement(
1750
+ LinkButton,
1751
+ {
1752
+ to: "/qeta",
1753
+ className: styles.marginRight,
1754
+ startIcon: /* @__PURE__ */ React.createElement(HomeOutlined, null)
1755
+ },
1756
+ "Back to questions"
1757
+ ), /* @__PURE__ */ React.createElement(AskQuestionButton, null)), /* @__PURE__ */ React.createElement(TopRankingUsers, null)));
1758
+ };
1759
+
1760
+ export { AskForm as A, Content as C, MarkdownEditor as M, QuestionsContainer as Q, RankingRow as R, StatisticsPage as S, TagsAndEntities as T, useStyles$1 as a, useQetaApi as b, AskQuestionButton as c, useIdentityApi as d, TrophyIcon as e, formatEntityName as f, QuestionsTable as g, qetaPlugin as h, QetaPage as i, QuestionTableCard as j, RankingCard as k, TopRankingUsers as l, QetaClient as m, qetaApiRef as q, useBasePath as u };
1761
+ //# sourceMappingURL=index-6bfa5b72.esm.js.map