@drodil/backstage-plugin-qeta 0.1.8
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.
- package/README.md +30 -0
- package/dist/esm/index-700345fb.esm.js +182 -0
- package/dist/esm/index-700345fb.esm.js.map +1 -0
- package/dist/esm/index-feb594e4.esm.js +395 -0
- package/dist/esm/index-feb594e4.esm.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +6 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +75 -0
- package/src/index.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# qeta
|
|
2
|
+
|
|
3
|
+
Welcome to the qeta plugin!
|
|
4
|
+
|
|
5
|
+
## Adding to your application
|
|
6
|
+
|
|
7
|
+
Add the plugin to your frontend app:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd packages/app && yarn add @drodil/backstage-plugin-qeta
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Expose the questions page:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// packages/app/src/App.tsx
|
|
17
|
+
import { QetaPage } from '@drodil/backstage-plugin-qeta';
|
|
18
|
+
|
|
19
|
+
// ...
|
|
20
|
+
|
|
21
|
+
const AppRoutes = () => (
|
|
22
|
+
<FlatRoutes>
|
|
23
|
+
// ...
|
|
24
|
+
<Route path="/qeta" element={<QetaPage />} />
|
|
25
|
+
// ...
|
|
26
|
+
</FlatRoutes>
|
|
27
|
+
);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
An interface for Q&A is now available at `/qeta`.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createRouteRef, createApiRef, createPlugin, createApiFactory, configApiRef, fetchApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
|
|
2
|
+
import { CustomErrorBase } from '@backstage/errors';
|
|
3
|
+
import omitBy from 'lodash/omitBy';
|
|
4
|
+
import isEmpty from 'lodash/isEmpty';
|
|
5
|
+
|
|
6
|
+
const rootRouteRef = createRouteRef({
|
|
7
|
+
id: "qeta"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const qetaApiRef = createApiRef({
|
|
11
|
+
id: "plugin.qeta.service"
|
|
12
|
+
});
|
|
13
|
+
class QetaError extends CustomErrorBase {
|
|
14
|
+
constructor(message, errors) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.errors = errors;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class QetaClient {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.fetchApi = options.fetchApi;
|
|
22
|
+
this.baseUrl = options.configApi.getString("backend.baseUrl");
|
|
23
|
+
}
|
|
24
|
+
async getQuestions(options) {
|
|
25
|
+
const query = new URLSearchParams(
|
|
26
|
+
omitBy(options, isEmpty)
|
|
27
|
+
).toString();
|
|
28
|
+
let url = `${this.baseUrl}/api/qeta/questions`;
|
|
29
|
+
if (query) {
|
|
30
|
+
url += `?${query}`;
|
|
31
|
+
}
|
|
32
|
+
const response = await this.fetchApi.fetch(url);
|
|
33
|
+
const data = await response.json();
|
|
34
|
+
if ("errors" in data) {
|
|
35
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
36
|
+
}
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
async postQuestion(question) {
|
|
40
|
+
const response = await this.fetchApi.fetch(
|
|
41
|
+
`${this.baseUrl}/api/qeta/questions`,
|
|
42
|
+
{
|
|
43
|
+
method: "POST",
|
|
44
|
+
body: JSON.stringify(question),
|
|
45
|
+
headers: { "Content-Type": "application/json" }
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
if ("errors" in data) {
|
|
50
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
51
|
+
}
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
async getQuestion(id) {
|
|
55
|
+
if (!id) {
|
|
56
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
57
|
+
}
|
|
58
|
+
const response = await this.fetchApi.fetch(
|
|
59
|
+
`${this.baseUrl}/api/qeta/questions/${id}`
|
|
60
|
+
);
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
if ("errors" in data) {
|
|
63
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
64
|
+
}
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
67
|
+
async getTags() {
|
|
68
|
+
const response = await this.fetchApi.fetch(`${this.baseUrl}/api/qeta/tags`);
|
|
69
|
+
return await response.json();
|
|
70
|
+
}
|
|
71
|
+
async voteQuestionUp(id) {
|
|
72
|
+
if (!id) {
|
|
73
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
74
|
+
}
|
|
75
|
+
const response = await this.fetchApi.fetch(
|
|
76
|
+
`${this.baseUrl}/api/qeta/questions/${id}/upvote`
|
|
77
|
+
);
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
if ("errors" in data) {
|
|
80
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
81
|
+
}
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
async voteQuestionDown(id) {
|
|
85
|
+
if (!id) {
|
|
86
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
87
|
+
}
|
|
88
|
+
const response = await this.fetchApi.fetch(
|
|
89
|
+
`${this.baseUrl}/api/qeta/questions/${id}/downvote`
|
|
90
|
+
);
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
if ("errors" in data) {
|
|
93
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
94
|
+
}
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
async postAnswer(answer) {
|
|
98
|
+
const response = await this.fetchApi.fetch(
|
|
99
|
+
`${this.baseUrl}/api/qeta/questions/${answer.questionId}/answers`,
|
|
100
|
+
{
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: JSON.stringify({ answer: answer.answer }),
|
|
103
|
+
headers: { "Content-Type": "application/json" }
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
const data = await response.json();
|
|
107
|
+
if ("errors" in data) {
|
|
108
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
109
|
+
}
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
async voteAnswerUp(questionId, id) {
|
|
113
|
+
if (!id || !questionId) {
|
|
114
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
115
|
+
}
|
|
116
|
+
const response = await this.fetchApi.fetch(
|
|
117
|
+
`${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/upvote`
|
|
118
|
+
);
|
|
119
|
+
const data = await response.json();
|
|
120
|
+
if ("errors" in data) {
|
|
121
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
122
|
+
}
|
|
123
|
+
return data;
|
|
124
|
+
}
|
|
125
|
+
async voteAnswerDown(questionId, id) {
|
|
126
|
+
if (!id || !questionId) {
|
|
127
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
128
|
+
}
|
|
129
|
+
const response = await this.fetchApi.fetch(
|
|
130
|
+
`${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/downvote`
|
|
131
|
+
);
|
|
132
|
+
const data = await response.json();
|
|
133
|
+
if ("errors" in data) {
|
|
134
|
+
throw new QetaError("Failed to fetch", data.errors);
|
|
135
|
+
}
|
|
136
|
+
return data;
|
|
137
|
+
}
|
|
138
|
+
async markAnswerCorrect(questionId, id) {
|
|
139
|
+
if (!id || !questionId) {
|
|
140
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
141
|
+
}
|
|
142
|
+
const response = await this.fetchApi.fetch(
|
|
143
|
+
`${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/correct`
|
|
144
|
+
);
|
|
145
|
+
const data = await response;
|
|
146
|
+
return data.ok;
|
|
147
|
+
}
|
|
148
|
+
async markAnswerIncorrect(questionId, id) {
|
|
149
|
+
if (!id || !questionId) {
|
|
150
|
+
throw new QetaError("Invalid id provided", void 0);
|
|
151
|
+
}
|
|
152
|
+
const response = await this.fetchApi.fetch(
|
|
153
|
+
`${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/incorrect`
|
|
154
|
+
);
|
|
155
|
+
const data = await response;
|
|
156
|
+
return data.ok;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const qetaPlugin = createPlugin({
|
|
161
|
+
id: "qeta",
|
|
162
|
+
routes: {
|
|
163
|
+
root: rootRouteRef
|
|
164
|
+
},
|
|
165
|
+
apis: [
|
|
166
|
+
createApiFactory({
|
|
167
|
+
api: qetaApiRef,
|
|
168
|
+
deps: { configApi: configApiRef, fetchApi: fetchApiRef },
|
|
169
|
+
factory: ({ configApi, fetchApi }) => new QetaClient({ configApi, fetchApi })
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
const QetaPage = qetaPlugin.provide(
|
|
174
|
+
createRoutableExtension({
|
|
175
|
+
name: "QetaPage",
|
|
176
|
+
component: () => import('./index-feb594e4.esm.js').then((m) => m.HomePage),
|
|
177
|
+
mountPoint: rootRouteRef
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
export { QetaPage as Q, qetaPlugin as a, qetaApiRef as q };
|
|
182
|
+
//# sourceMappingURL=index-700345fb.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-700345fb.esm.js","sources":["../../src/routes.ts","../../src/api/QetaClient.ts","../../src/plugin.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'qeta',\n});\n","import { GetQuestionsOptions, QetaApi } from './QetaApi';\nimport { ConfigApi, createApiRef, FetchApi } from '@backstage/core-plugin-api';\nimport { CustomErrorBase } from '@backstage/errors';\nimport {\n AnswerRequest,\n AnswerResponse,\n AnswerResponseBody,\n QuestionRequest,\n QuestionResponse,\n QuestionResponseBody,\n QuestionsResponse,\n QuestionsResponseBody,\n} from './types';\nimport omitBy from 'lodash/omitBy';\nimport isEmpty from 'lodash/isEmpty';\n\nexport const qetaApiRef = createApiRef<QetaApi>({\n id: 'plugin.qeta.service',\n});\n\nexport class QetaError extends CustomErrorBase {\n public errors: null | undefined | any[];\n\n constructor(message: string, errors: null | undefined | any[]) {\n super(message);\n\n this.errors = errors;\n }\n}\n\nexport class QetaClient implements QetaApi {\n private readonly baseUrl: string;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { configApi: ConfigApi; fetchApi: FetchApi }) {\n this.fetchApi = options.fetchApi;\n this.baseUrl = options.configApi.getString('backend.baseUrl');\n }\n\n async getQuestions(options: GetQuestionsOptions): Promise<QuestionsResponse> {\n const query = new URLSearchParams(\n omitBy(options as any, isEmpty),\n ).toString();\n\n let url = `${this.baseUrl}/api/qeta/questions`;\n if (query) {\n url += `?${query}`;\n }\n\n const response = await this.fetchApi.fetch(url);\n\n const data = (await response.json()) as QuestionsResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async postQuestion(question: QuestionRequest): Promise<QuestionResponse> {\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions`,\n {\n method: 'POST',\n body: JSON.stringify(question),\n headers: { 'Content-Type': 'application/json' },\n },\n );\n const data = (await response.json()) as QuestionResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async getQuestion(id?: string): Promise<QuestionResponse> {\n if (!id) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${id}`,\n );\n const data = (await response.json()) as QuestionResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async getTags(): Promise<string[]> {\n const response = await this.fetchApi.fetch(`${this.baseUrl}/api/qeta/tags`);\n return (await response.json()) as string[];\n }\n\n async voteQuestionUp(id: number): Promise<QuestionResponse> {\n if (!id) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${id}/upvote`,\n );\n const data = (await response.json()) as QuestionResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async voteQuestionDown(id: number): Promise<QuestionResponse> {\n if (!id) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${id}/downvote`,\n );\n const data = (await response.json()) as QuestionResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async postAnswer(answer: AnswerRequest): Promise<AnswerResponseBody> {\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${answer.questionId}/answers`,\n {\n method: 'POST',\n body: JSON.stringify({ answer: answer.answer }),\n headers: { 'Content-Type': 'application/json' },\n },\n );\n const data = (await response.json()) as AnswerResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async voteAnswerUp(questionId: number, id: number): Promise<AnswerResponse> {\n if (!id || !questionId) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/upvote`,\n );\n const data = (await response.json()) as AnswerResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async voteAnswerDown(\n questionId: number,\n id: number,\n ): Promise<AnswerResponse> {\n if (!id || !questionId) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/downvote`,\n );\n const data = (await response.json()) as AnswerResponseBody;\n\n if ('errors' in data) {\n throw new QetaError('Failed to fetch', data.errors);\n }\n\n return data;\n }\n\n async markAnswerCorrect(questionId: number, id: number): Promise<boolean> {\n if (!id || !questionId) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/correct`,\n );\n const data = await response;\n return data.ok;\n }\n\n async markAnswerIncorrect(questionId: number, id: number): Promise<boolean> {\n if (!id || !questionId) {\n throw new QetaError('Invalid id provided', undefined);\n }\n const response = await this.fetchApi.fetch(\n `${this.baseUrl}/api/qeta/questions/${questionId}/answers/${id}/incorrect`,\n );\n const data = await response;\n return data.ok;\n }\n}\n","import {\n configApiRef,\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { qetaApiRef, QetaClient } from './api';\n\nexport const qetaPlugin = createPlugin({\n id: 'qeta',\n routes: {\n root: rootRouteRef,\n },\n apis: [\n createApiFactory({\n api: qetaApiRef,\n deps: { configApi: configApiRef, fetchApi: fetchApiRef },\n factory: ({ configApi, fetchApi }) =>\n new QetaClient({ configApi, fetchApi }),\n }),\n ],\n});\n\nexport const QetaPage = qetaPlugin.provide(\n createRoutableExtension({\n name: 'QetaPage',\n component: () => import('./components/HomePage').then(m => m.HomePage),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;;;AAEO,MAAM,eAAe,cAAe,CAAA;AAAA,EACzC,EAAI,EAAA,MAAA;AACN,CAAC,CAAA;;ACYM,MAAM,aAAa,YAAsB,CAAA;AAAA,EAC9C,EAAI,EAAA,qBAAA;AACN,CAAC,EAAA;AAEM,MAAM,kBAAkB,eAAgB,CAAA;AAAA,EAG7C,WAAA,CAAY,SAAiB,MAAkC,EAAA;AAC7D,IAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAEb,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AAAA,GAChB;AACF,CAAA;AAEO,MAAM,UAA8B,CAAA;AAAA,EAIzC,YAAY,OAAuD,EAAA;AACjE,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA,CAAA;AACxB,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA,CAAQ,SAAU,CAAA,SAAA,CAAU,iBAAiB,CAAA,CAAA;AAAA,GAC9D;AAAA,EAEA,MAAM,aAAa,OAA0D,EAAA;AAC3E,IAAA,MAAM,QAAQ,IAAI,eAAA;AAAA,MAChB,MAAA,CAAO,SAAgB,OAAO,CAAA;AAAA,MAC9B,QAAS,EAAA,CAAA;AAEX,IAAI,IAAA,GAAA,GAAM,GAAG,IAAK,CAAA,OAAA,CAAA,mBAAA,CAAA,CAAA;AAClB,IAAA,IAAI,KAAO,EAAA;AACT,MAAA,GAAA,IAAO,CAAI,CAAA,EAAA,KAAA,CAAA,CAAA,CAAA;AAAA,KACb;AAEA,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,QAAA,CAAS,MAAM,GAAG,CAAA,CAAA;AAE9C,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,aAAa,QAAsD,EAAA;AACvE,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,GAAG,IAAK,CAAA,OAAA,CAAA,mBAAA,CAAA;AAAA,MACR;AAAA,QACE,MAAQ,EAAA,MAAA;AAAA,QACR,IAAA,EAAM,IAAK,CAAA,SAAA,CAAU,QAAQ,CAAA;AAAA,QAC7B,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAmB,EAAA;AAAA,OAChD;AAAA,KACF,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,YAAY,EAAwC,EAAA;AACxD,IAAA,IAAI,CAAC,EAAI,EAAA;AACP,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,KAAK,OAA8B,CAAA,oBAAA,EAAA,EAAA,CAAA,CAAA;AAAA,KACxC,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,OAA6B,GAAA;AACjC,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,SAAS,KAAM,CAAA,CAAA,EAAG,KAAK,OAAuB,CAAA,cAAA,CAAA,CAAA,CAAA;AAC1E,IAAQ,OAAA,MAAM,SAAS,IAAK,EAAA,CAAA;AAAA,GAC9B;AAAA,EAEA,MAAM,eAAe,EAAuC,EAAA;AAC1D,IAAA,IAAI,CAAC,EAAI,EAAA;AACP,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,KAAK,OAA8B,CAAA,oBAAA,EAAA,EAAA,CAAA,OAAA,CAAA;AAAA,KACxC,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,iBAAiB,EAAuC,EAAA;AAC5D,IAAA,IAAI,CAAC,EAAI,EAAA;AACP,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,KAAK,OAA8B,CAAA,oBAAA,EAAA,EAAA,CAAA,SAAA,CAAA;AAAA,KACxC,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,WAAW,MAAoD,EAAA;AACnE,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,IAAK,CAAA,OAAA,CAAA,oBAAA,EAA8B,MAAO,CAAA,UAAA,CAAA,QAAA,CAAA;AAAA,MAC7C;AAAA,QACE,MAAQ,EAAA,MAAA;AAAA,QACR,MAAM,IAAK,CAAA,SAAA,CAAU,EAAE,MAAQ,EAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,QAC9C,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAmB,EAAA;AAAA,OAChD;AAAA,KACF,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,YAAa,CAAA,UAAA,EAAoB,EAAqC,EAAA;AAC1E,IAAI,IAAA,CAAC,EAAM,IAAA,CAAC,UAAY,EAAA;AACtB,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,IAAK,CAAA,OAAA,CAAA,oBAAA,EAA8B,UAAsB,CAAA,SAAA,EAAA,EAAA,CAAA,OAAA,CAAA;AAAA,KAC9D,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,cACJ,CAAA,UAAA,EACA,EACyB,EAAA;AACzB,IAAI,IAAA,CAAC,EAAM,IAAA,CAAC,UAAY,EAAA;AACtB,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,IAAK,CAAA,OAAA,CAAA,oBAAA,EAA8B,UAAsB,CAAA,SAAA,EAAA,EAAA,CAAA,SAAA,CAAA;AAAA,KAC9D,CAAA;AACA,IAAM,MAAA,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AAElC,IAAA,IAAI,YAAY,IAAM,EAAA;AACpB,MAAA,MAAM,IAAI,SAAA,CAAU,iBAAmB,EAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,KACpD;AAEA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAM,iBAAkB,CAAA,UAAA,EAAoB,EAA8B,EAAA;AACxE,IAAI,IAAA,CAAC,EAAM,IAAA,CAAC,UAAY,EAAA;AACtB,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,IAAK,CAAA,OAAA,CAAA,oBAAA,EAA8B,UAAsB,CAAA,SAAA,EAAA,EAAA,CAAA,QAAA,CAAA;AAAA,KAC9D,CAAA;AACA,IAAA,MAAM,OAAO,MAAM,QAAA,CAAA;AACnB,IAAA,OAAO,IAAK,CAAA,EAAA,CAAA;AAAA,GACd;AAAA,EAEA,MAAM,mBAAoB,CAAA,UAAA,EAAoB,EAA8B,EAAA;AAC1E,IAAI,IAAA,CAAC,EAAM,IAAA,CAAC,UAAY,EAAA;AACtB,MAAM,MAAA,IAAI,SAAU,CAAA,qBAAA,EAAuB,KAAS,CAAA,CAAA,CAAA;AAAA,KACtD;AACA,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MACnC,CAAA,EAAG,IAAK,CAAA,OAAA,CAAA,oBAAA,EAA8B,UAAsB,CAAA,SAAA,EAAA,EAAA,CAAA,UAAA,CAAA;AAAA,KAC9D,CAAA;AACA,IAAA,MAAM,OAAO,MAAM,QAAA,CAAA;AACnB,IAAA,OAAO,IAAK,CAAA,EAAA,CAAA;AAAA,GACd;AACF;;AClMO,MAAM,aAAa,YAAa,CAAA;AAAA,EACrC,EAAI,EAAA,MAAA;AAAA,EACJ,MAAQ,EAAA;AAAA,IACN,IAAM,EAAA,YAAA;AAAA,GACR;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,UAAA;AAAA,MACL,IAAM,EAAA,EAAE,SAAW,EAAA,YAAA,EAAc,UAAU,WAAY,EAAA;AAAA,MACvD,OAAA,EAAS,CAAC,EAAE,SAAW,EAAA,QAAA,EACrB,KAAA,IAAI,UAAW,CAAA,EAAE,SAAW,EAAA,QAAA,EAAU,CAAA;AAAA,KACzC,CAAA;AAAA,GACH;AACF,CAAC,EAAA;AAEM,MAAM,WAAW,UAAW,CAAA,OAAA;AAAA,EACjC,uBAAwB,CAAA;AAAA,IACtB,IAAM,EAAA,UAAA;AAAA,IACN,WAAW,MAAM,OAAO,2BAAyB,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,QAAQ,CAAA;AAAA,IACrE,UAAY,EAAA,YAAA;AAAA,GACb,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { makeStyles, Grid, TextField, Button, Tooltip, IconButton, Typography, Box, Card, CardContent, Chip, Link, Divider } from '@material-ui/core';
|
|
3
|
+
import { Content, ContentHeader, WarningPanel, InfoCard, MarkdownContent, Page, Header, HeaderLabel } from '@backstage/core-components';
|
|
4
|
+
import { useNavigate, useParams, Route } from 'react-router-dom';
|
|
5
|
+
import { Routes } from 'react-router';
|
|
6
|
+
import MDEditor from '@uiw/react-md-editor';
|
|
7
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
8
|
+
import { q as qetaApiRef } from './index-700345fb.esm.js';
|
|
9
|
+
import { Autocomplete, Skeleton, Pagination } from '@material-ui/lab';
|
|
10
|
+
import { useAsync } from 'react-use';
|
|
11
|
+
import ArrowDownward from '@material-ui/icons/ArrowDownward';
|
|
12
|
+
import ArrowUpward from '@material-ui/icons/ArrowUpward';
|
|
13
|
+
import Check from '@material-ui/icons/Check';
|
|
14
|
+
import RelativeTime from 'react-relative-time';
|
|
15
|
+
import '@backstage/errors';
|
|
16
|
+
import 'lodash/omitBy';
|
|
17
|
+
import 'lodash/isEmpty';
|
|
18
|
+
|
|
19
|
+
function useQetaApi(f, deps = []) {
|
|
20
|
+
const qetaApi = useApi(qetaApiRef);
|
|
21
|
+
return useAsync(async () => {
|
|
22
|
+
return await f(qetaApi);
|
|
23
|
+
}, deps);
|
|
24
|
+
}
|
|
25
|
+
const useStyles = makeStyles((theme) => {
|
|
26
|
+
return {
|
|
27
|
+
markdownEditor: {
|
|
28
|
+
backgroundColor: "initial",
|
|
29
|
+
boxShadow: "none",
|
|
30
|
+
color: theme.palette.text.primary,
|
|
31
|
+
border: `1px solid ${theme.palette.grey[600]}`,
|
|
32
|
+
"& .w-md-editor-toolbar": {
|
|
33
|
+
backgroundColor: "initial"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
questionCardVote: {
|
|
37
|
+
textAlign: "center",
|
|
38
|
+
marginRight: "20px"
|
|
39
|
+
},
|
|
40
|
+
questionListPagination: {
|
|
41
|
+
marginTop: theme.spacing(2)
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const AskPage = () => {
|
|
47
|
+
const navigate = useNavigate();
|
|
48
|
+
const [question, setQuestion] = React.useState("");
|
|
49
|
+
const [title, setTitle] = React.useState("");
|
|
50
|
+
const [error, setError] = React.useState(false);
|
|
51
|
+
const [tags, setTags] = React.useState([]);
|
|
52
|
+
const [availableTags, setAvailableTags] = React.useState([]);
|
|
53
|
+
const qetaApi = useApi(qetaApiRef);
|
|
54
|
+
const styles = useStyles();
|
|
55
|
+
const postQuestion = () => {
|
|
56
|
+
qetaApi.postQuestion({ title, content: question, tags }).then((q) => {
|
|
57
|
+
if (!q || !q.id) {
|
|
58
|
+
setError(true);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
navigate(`/qeta/questions/${q.id}`);
|
|
62
|
+
}).catch((_e) => setError(true));
|
|
63
|
+
};
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
qetaApi.getTags().then((data) => setAvailableTags(data));
|
|
66
|
+
}, [qetaApi]);
|
|
67
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Ask question" }), /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3, direction: "column" }, error && /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not post question" }), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(InfoCard, null, /* @__PURE__ */ React.createElement("form", null, /* @__PURE__ */ React.createElement(
|
|
68
|
+
TextField,
|
|
69
|
+
{
|
|
70
|
+
label: "Title",
|
|
71
|
+
required: true,
|
|
72
|
+
fullWidth: true,
|
|
73
|
+
margin: "normal",
|
|
74
|
+
title,
|
|
75
|
+
onChange: (e) => setTitle(e.target.value),
|
|
76
|
+
variant: "outlined",
|
|
77
|
+
helperText: "Write good title for your question that people can understand"
|
|
78
|
+
}
|
|
79
|
+
), /* @__PURE__ */ React.createElement(
|
|
80
|
+
MDEditor,
|
|
81
|
+
{
|
|
82
|
+
value: question,
|
|
83
|
+
className: styles.markdownEditor,
|
|
84
|
+
onChange: (v) => setQuestion(v),
|
|
85
|
+
preview: "edit",
|
|
86
|
+
height: 400
|
|
87
|
+
}
|
|
88
|
+
), /* @__PURE__ */ React.createElement(
|
|
89
|
+
Autocomplete,
|
|
90
|
+
{
|
|
91
|
+
multiple: true,
|
|
92
|
+
id: "tags-standard",
|
|
93
|
+
options: availableTags,
|
|
94
|
+
defaultValue: tags,
|
|
95
|
+
onChange: (_e, inputTags) => {
|
|
96
|
+
if (inputTags.length < 5) {
|
|
97
|
+
setTags(inputTags);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
freeSolo: true,
|
|
101
|
+
renderInput: (params) => /* @__PURE__ */ React.createElement(
|
|
102
|
+
TextField,
|
|
103
|
+
{
|
|
104
|
+
...params,
|
|
105
|
+
variant: "outlined",
|
|
106
|
+
margin: "normal",
|
|
107
|
+
label: "Tags",
|
|
108
|
+
placeholder: "Type or select tags",
|
|
109
|
+
helperText: "Add up to 5 tags to categorize your question"
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
), /* @__PURE__ */ React.createElement(Button, { variant: "contained", onClick: postQuestion }, "Post"))))));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const VoteButtons = (props) => {
|
|
117
|
+
var _a, _b, _c, _d;
|
|
118
|
+
const [ownVote, setOwnVote] = React.useState((_a = props.entity.ownVote) != null ? _a : 0);
|
|
119
|
+
const isCorrectAnswer = "questionId" in props.entity ? props.entity.correct : false;
|
|
120
|
+
const [correct, setCorrect] = React.useState(isCorrectAnswer);
|
|
121
|
+
const [entity, setEntity] = React.useState(
|
|
122
|
+
props.entity
|
|
123
|
+
);
|
|
124
|
+
const qetaApi = useApi(qetaApiRef);
|
|
125
|
+
const voteUp = () => {
|
|
126
|
+
if ("title" in entity) {
|
|
127
|
+
qetaApi.voteQuestionUp(entity.id).then((response) => {
|
|
128
|
+
setOwnVote(1);
|
|
129
|
+
setEntity(response);
|
|
130
|
+
});
|
|
131
|
+
} else if ("questionId" in entity) {
|
|
132
|
+
qetaApi.voteAnswerUp(entity.questionId, entity.id).then((response) => {
|
|
133
|
+
setOwnVote(1);
|
|
134
|
+
setEntity(response);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const voteDown = () => {
|
|
139
|
+
if ("title" in entity) {
|
|
140
|
+
qetaApi.voteQuestionDown(entity.id).then((response) => {
|
|
141
|
+
setOwnVote(-1);
|
|
142
|
+
setEntity(response);
|
|
143
|
+
});
|
|
144
|
+
} else if ("questionId" in entity) {
|
|
145
|
+
qetaApi.voteAnswerDown(entity.questionId, entity.id).then((response) => {
|
|
146
|
+
setOwnVote(-1);
|
|
147
|
+
setEntity(response);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const toggleCorrectAnswer = () => {
|
|
152
|
+
if (!("questionId" in entity)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (correct) {
|
|
156
|
+
qetaApi.markAnswerIncorrect(entity.questionId, entity.id).then((response) => {
|
|
157
|
+
if (response) {
|
|
158
|
+
setCorrect(false);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
qetaApi.markAnswerCorrect(entity.questionId, entity.id).then((response) => {
|
|
163
|
+
setCorrect(response);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
168
|
+
Tooltip,
|
|
169
|
+
{
|
|
170
|
+
title: props.entity.own ? "You cannot vote your own post" : "This answer is good"
|
|
171
|
+
},
|
|
172
|
+
/* @__PURE__ */ React.createElement(
|
|
173
|
+
IconButton,
|
|
174
|
+
{
|
|
175
|
+
"aria-label": "vote up",
|
|
176
|
+
color: ownVote > 0 ? "primary" : "default",
|
|
177
|
+
disabled: (_b = props.entity.own) != null ? _b : false,
|
|
178
|
+
size: "small",
|
|
179
|
+
onClick: voteUp
|
|
180
|
+
},
|
|
181
|
+
/* @__PURE__ */ React.createElement(ArrowUpward, null)
|
|
182
|
+
)
|
|
183
|
+
), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, entity.score), /* @__PURE__ */ React.createElement(
|
|
184
|
+
Tooltip,
|
|
185
|
+
{
|
|
186
|
+
title: props.entity.own ? "You cannot vote your own post" : "This answer is not good"
|
|
187
|
+
},
|
|
188
|
+
/* @__PURE__ */ React.createElement(
|
|
189
|
+
IconButton,
|
|
190
|
+
{
|
|
191
|
+
"aria-label": "vote down",
|
|
192
|
+
color: ownVote < 0 ? "primary" : "default",
|
|
193
|
+
disabled: (_c = props.entity.own) != null ? _c : false,
|
|
194
|
+
size: "small",
|
|
195
|
+
onClick: voteDown
|
|
196
|
+
},
|
|
197
|
+
/* @__PURE__ */ React.createElement(ArrowDownward, null)
|
|
198
|
+
)
|
|
199
|
+
), "correct" in props.entity && ((_d = props.question) == null ? void 0 : _d.own) && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Tooltip, { title: "Mark answer as correct" }, /* @__PURE__ */ React.createElement(
|
|
200
|
+
IconButton,
|
|
201
|
+
{
|
|
202
|
+
"aria-label": "mark correct",
|
|
203
|
+
color: correct ? "primary" : "default",
|
|
204
|
+
size: "small",
|
|
205
|
+
onClick: toggleCorrectAnswer
|
|
206
|
+
},
|
|
207
|
+
/* @__PURE__ */ React.createElement(Check, null)
|
|
208
|
+
))));
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const QuestionCard = (props) => {
|
|
212
|
+
const { question } = props;
|
|
213
|
+
const styles = useStyles();
|
|
214
|
+
return /* @__PURE__ */ React.createElement(Card, { variant: "outlined" }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, /* @__PURE__ */ React.createElement(Grid, { item: true, className: styles.questionCardVote }, /* @__PURE__ */ React.createElement(VoteButtons, { entity: question })), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, /* @__PURE__ */ React.createElement(MarkdownContent, { content: question.content, dialect: "gfm" })), question.tags && question.tags.map((tag) => /* @__PURE__ */ React.createElement(
|
|
215
|
+
Chip,
|
|
216
|
+
{
|
|
217
|
+
label: tag,
|
|
218
|
+
size: "small",
|
|
219
|
+
component: "a",
|
|
220
|
+
href: `/qeta/tags/${tag}`,
|
|
221
|
+
clickable: true
|
|
222
|
+
}
|
|
223
|
+
)), /* @__PURE__ */ React.createElement(Box, null, "By", " ", /* @__PURE__ */ React.createElement(Link, { href: `/qeta/user/${question.author}` }, question.author))))));
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const AnswerForm = (props) => {
|
|
227
|
+
const { question, onPost } = props;
|
|
228
|
+
const [answer, setAnswer] = React.useState("");
|
|
229
|
+
const [error, setError] = React.useState(false);
|
|
230
|
+
const qetaApi = useApi(qetaApiRef);
|
|
231
|
+
const styles = useStyles();
|
|
232
|
+
const postAnswer = () => {
|
|
233
|
+
qetaApi.postAnswer({ questionId: question.id, answer }).then((a) => {
|
|
234
|
+
if (!a || !("id" in a)) {
|
|
235
|
+
setError(true);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
onPost(a);
|
|
239
|
+
setAnswer("");
|
|
240
|
+
}).catch((_e) => setError(true));
|
|
241
|
+
};
|
|
242
|
+
return /* @__PURE__ */ React.createElement("form", null, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "Your answer"), error && /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not post answer" }), /* @__PURE__ */ React.createElement(
|
|
243
|
+
MDEditor,
|
|
244
|
+
{
|
|
245
|
+
value: answer,
|
|
246
|
+
className: styles.markdownEditor,
|
|
247
|
+
onChange: (v) => setAnswer(v),
|
|
248
|
+
preview: "edit",
|
|
249
|
+
height: 200
|
|
250
|
+
}
|
|
251
|
+
), /* @__PURE__ */ React.createElement(Button, { variant: "contained", onClick: postAnswer }, "Post"));
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const AnswerCard = (props) => {
|
|
255
|
+
const { answer, question } = props;
|
|
256
|
+
const styles = useStyles();
|
|
257
|
+
return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 0 }, /* @__PURE__ */ React.createElement(Grid, { item: true, className: styles.questionCardVote }, /* @__PURE__ */ React.createElement(VoteButtons, { entity: answer, question })), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, /* @__PURE__ */ React.createElement(MarkdownContent, { content: answer.content, dialect: "gfm" })), /* @__PURE__ */ React.createElement(Box, null, "By", " ", /* @__PURE__ */ React.createElement(Link, { href: `/qeta/user/${answer.author}` }, answer.author))))));
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const QuestionPage = () => {
|
|
261
|
+
var _a;
|
|
262
|
+
const { id } = useParams();
|
|
263
|
+
const [newAnswers, setNewAnswers] = React.useState([]);
|
|
264
|
+
const {
|
|
265
|
+
value: question,
|
|
266
|
+
loading,
|
|
267
|
+
error
|
|
268
|
+
} = useQetaApi((api) => api.getQuestion(id), [id]);
|
|
269
|
+
const onAnswerPost = (answer) => {
|
|
270
|
+
setNewAnswers(newAnswers.concat([answer]));
|
|
271
|
+
};
|
|
272
|
+
const getDescription = (q) => {
|
|
273
|
+
return /* @__PURE__ */ React.createElement("span", null, "Asked", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline", sx: { mr: 2 } }, /* @__PURE__ */ React.createElement(RelativeTime, { value: q.created })), q.updated && /* @__PURE__ */ React.createElement(React.Fragment, null, "Updated", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline" }, /* @__PURE__ */ React.createElement(RelativeTime, { value: q.created }))), "Viewed", " ", /* @__PURE__ */ React.createElement(Box, { fontWeight: "fontWeightMedium", display: "inline" }, q.views, " times"));
|
|
274
|
+
};
|
|
275
|
+
if (loading) {
|
|
276
|
+
return /* @__PURE__ */ React.createElement(Skeleton, { variant: "rect", height: 200 });
|
|
277
|
+
}
|
|
278
|
+
if (error || question === void 0) {
|
|
279
|
+
return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load question." }, error == null ? void 0 : error.message);
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(
|
|
282
|
+
ContentHeader,
|
|
283
|
+
{
|
|
284
|
+
title: question.title,
|
|
285
|
+
description: getDescription(question)
|
|
286
|
+
},
|
|
287
|
+
/* @__PURE__ */ React.createElement(Button, { href: "/qeta" }, "Back to questions"),
|
|
288
|
+
/* @__PURE__ */ React.createElement(Button, { variant: "contained", href: "/qeta/ask" }, "Ask question")
|
|
289
|
+
), /* @__PURE__ */ React.createElement(QuestionCard, { question }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, question.answersCount + newAnswers.length, " answers"), ((_a = question.answers) != null ? _a : []).concat(newAnswers).map((a) => {
|
|
290
|
+
return /* @__PURE__ */ React.createElement(Box, { key: a.id, sx: { mb: 1 } }, /* @__PURE__ */ React.createElement(AnswerCard, { answer: a, question }), /* @__PURE__ */ React.createElement(Divider, null));
|
|
291
|
+
}), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(AnswerForm, { question, onPost: onAnswerPost }));
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const QuestionListItem = (props) => {
|
|
295
|
+
const { question } = props;
|
|
296
|
+
return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { gutterBottom: true, variant: "h5", component: "div" }, /* @__PURE__ */ React.createElement(Link, { href: `/qeta/questions/${question.id}` }, question.title)), question.tags && question.tags.map((tag) => /* @__PURE__ */ React.createElement(
|
|
297
|
+
Chip,
|
|
298
|
+
{
|
|
299
|
+
label: tag,
|
|
300
|
+
size: "small",
|
|
301
|
+
component: "a",
|
|
302
|
+
href: `/qeta/tags/${tag}`,
|
|
303
|
+
clickable: true
|
|
304
|
+
}
|
|
305
|
+
)), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", display: "block" }, "By", " ", /* @__PURE__ */ React.createElement(Link, { href: `/qeta/user/${question.author}` }, question.author), " ", /* @__PURE__ */ React.createElement(
|
|
306
|
+
RelativeTime,
|
|
307
|
+
{
|
|
308
|
+
value: question.created,
|
|
309
|
+
titleFormat: "YYYY/MM/DD HH:mm"
|
|
310
|
+
}
|
|
311
|
+
)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", display: "inline", gutterBottom: true }, "Score: ", question.score, " ", " | "), /* @__PURE__ */ React.createElement(
|
|
312
|
+
Typography,
|
|
313
|
+
{
|
|
314
|
+
variant: "caption",
|
|
315
|
+
color: question.correctAnswer ? "secondary" : "initial",
|
|
316
|
+
display: "inline",
|
|
317
|
+
gutterBottom: true
|
|
318
|
+
},
|
|
319
|
+
"Answers: ",
|
|
320
|
+
question.answersCount
|
|
321
|
+
), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", display: "inline", gutterBottom: true }, " | ", " Views: ", question.views)));
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const QuestionList = (props) => {
|
|
325
|
+
const { tags, author } = props;
|
|
326
|
+
const [page, setPage] = React.useState(1);
|
|
327
|
+
const pageSize = 10;
|
|
328
|
+
const offset = (page - 1) * pageSize;
|
|
329
|
+
const styles = useStyles();
|
|
330
|
+
const handleChange = (_event, value) => {
|
|
331
|
+
setPage(value);
|
|
332
|
+
};
|
|
333
|
+
const {
|
|
334
|
+
value: response,
|
|
335
|
+
loading,
|
|
336
|
+
error
|
|
337
|
+
} = useQetaApi(
|
|
338
|
+
(api) => api.getQuestions({ limit: pageSize, offset, tags, author }),
|
|
339
|
+
[page, offset]
|
|
340
|
+
);
|
|
341
|
+
if (loading) {
|
|
342
|
+
return /* @__PURE__ */ React.createElement(Skeleton, { variant: "rect", height: 200 });
|
|
343
|
+
}
|
|
344
|
+
if (error || response === void 0) {
|
|
345
|
+
return /* @__PURE__ */ React.createElement(WarningPanel, { severity: "error", title: "Could not load questions." }, error == null ? void 0 : error.message);
|
|
346
|
+
}
|
|
347
|
+
if (response.questions.length === 0) {
|
|
348
|
+
return /* @__PURE__ */ React.createElement(Box, null, "No questions found");
|
|
349
|
+
}
|
|
350
|
+
const pageCount = response.total < pageSize ? 1 : Math.ceil(response.total / pageSize);
|
|
351
|
+
return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, response.questions.map((question) => {
|
|
352
|
+
return /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, key: question.id }, /* @__PURE__ */ React.createElement(QuestionListItem, { question }), /* @__PURE__ */ React.createElement(Divider, null));
|
|
353
|
+
})), /* @__PURE__ */ React.createElement(
|
|
354
|
+
Grid,
|
|
355
|
+
{
|
|
356
|
+
container: true,
|
|
357
|
+
spacing: 0,
|
|
358
|
+
className: styles.questionListPagination,
|
|
359
|
+
direction: "column",
|
|
360
|
+
alignItems: "center",
|
|
361
|
+
justifyContent: "center"
|
|
362
|
+
},
|
|
363
|
+
/* @__PURE__ */ React.createElement(
|
|
364
|
+
Pagination,
|
|
365
|
+
{
|
|
366
|
+
page,
|
|
367
|
+
onChange: handleChange,
|
|
368
|
+
count: pageCount,
|
|
369
|
+
size: "large",
|
|
370
|
+
variant: "outlined",
|
|
371
|
+
showFirstButton: true,
|
|
372
|
+
showLastButton: true
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
));
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const TagPage = () => {
|
|
379
|
+
const { tag } = useParams();
|
|
380
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: `Questions tagged [${tag}]` }, /* @__PURE__ */ React.createElement(Button, { href: "/qeta" }, "Back to questions"), /* @__PURE__ */ React.createElement(Button, { variant: "contained", href: "/qeta/ask" }, "Ask question")), /* @__PURE__ */ React.createElement(QuestionList, { tags: [tag != null ? tag : ""] }));
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const UserPage = () => {
|
|
384
|
+
var _a;
|
|
385
|
+
const identity = (_a = useParams()["*"]) != null ? _a : "unknown";
|
|
386
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: `Questions by [${identity}]` }, /* @__PURE__ */ React.createElement(Button, { href: "/qeta" }, "Back to questions"), /* @__PURE__ */ React.createElement(Button, { variant: "contained", href: "/qeta/ask" }, "Ask question")), /* @__PURE__ */ React.createElement(QuestionList, { author: identity != null ? identity : "" }));
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const HomePageContent = () => {
|
|
390
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "All questions" }, /* @__PURE__ */ React.createElement(Button, { variant: "contained", href: "/qeta/ask" }, "Ask question")), /* @__PURE__ */ React.createElement(QuestionList, null));
|
|
391
|
+
};
|
|
392
|
+
const HomePage = () => /* @__PURE__ */ React.createElement(Page, { themeId: "tool" }, /* @__PURE__ */ React.createElement(Header, { title: "Q&A" }, /* @__PURE__ */ React.createElement(HeaderLabel, { label: "Lifecycle", value: "Alpha" })), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: "/", element: /* @__PURE__ */ React.createElement(HomePageContent, null) }), /* @__PURE__ */ React.createElement(Route, { path: "/ask", element: /* @__PURE__ */ React.createElement(AskPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: "/questions/:id", element: /* @__PURE__ */ React.createElement(QuestionPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: "/tags/:tag", element: /* @__PURE__ */ React.createElement(TagPage, null) }), /* @__PURE__ */ React.createElement(Route, { path: "/user/*", element: /* @__PURE__ */ React.createElement(UserPage, null) })));
|
|
393
|
+
|
|
394
|
+
export { HomePage };
|
|
395
|
+
//# sourceMappingURL=index-feb594e4.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-feb594e4.esm.js","sources":["../../src/utils/hooks.ts","../../src/components/AskPage/AskPage.tsx","../../src/components/QuestionPage/VoteButtons.tsx","../../src/components/QuestionPage/QuestionCard.tsx","../../src/components/QuestionPage/AnswerForm.tsx","../../src/components/QuestionPage/AnswerCard.tsx","../../src/components/QuestionPage/QuestionPage.tsx","../../src/components/HomePage/QuestionList/QuestionListItem.tsx","../../src/components/HomePage/QuestionList/QuestionList.tsx","../../src/components/TagPage/TagPage.tsx","../../src/components/UserPage/UserPage.tsx","../../src/components/HomePage/HomePage.tsx"],"sourcesContent":["import { QetaApi, qetaApiRef } from '../api';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { makeStyles } from '@material-ui/core';\n\nexport function useQetaApi<T>(\n f: (api: QetaApi) => Promise<T>,\n deps: any[] = [],\n) {\n const qetaApi = useApi(qetaApiRef);\n\n return useAsync(async () => {\n return await f(qetaApi);\n }, deps);\n}\n\nexport const useStyles = makeStyles(theme => {\n return {\n markdownEditor: {\n backgroundColor: 'initial',\n boxShadow: 'none',\n color: theme.palette.text.primary,\n border: `1px solid ${theme.palette.grey[600]}`,\n '& .w-md-editor-toolbar': {\n backgroundColor: 'initial',\n },\n },\n questionCardVote: {\n textAlign: 'center',\n marginRight: '20px',\n },\n questionListPagination: {\n marginTop: theme.spacing(2),\n },\n };\n});\n","import {\n Content,\n ContentHeader,\n InfoCard,\n WarningPanel,\n} from '@backstage/core-components';\nimport { Button, Grid, TextField } from '@material-ui/core';\nimport MDEditor from '@uiw/react-md-editor';\nimport React, { useEffect } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../../api';\nimport { useNavigate } from 'react-router-dom';\nimport { Autocomplete } from '@material-ui/lab';\nimport { useStyles } from '../../utils/hooks';\n\nexport const AskPage = () => {\n const navigate = useNavigate();\n const [question, setQuestion] = React.useState('');\n const [title, setTitle] = React.useState('');\n const [error, setError] = React.useState(false);\n const [tags, setTags] = React.useState<string[]>([]);\n const [availableTags, setAvailableTags] = React.useState<string[]>([]);\n const qetaApi = useApi(qetaApiRef);\n const styles = useStyles();\n\n const postQuestion = () => {\n qetaApi\n .postQuestion({ title, content: question, tags })\n .then(q => {\n if (!q || !q.id) {\n setError(true);\n return;\n }\n navigate(`/qeta/questions/${q.id}`);\n })\n .catch(_e => setError(true));\n };\n\n useEffect(() => {\n qetaApi.getTags().then(data => setAvailableTags(data));\n }, [qetaApi]);\n\n return (\n <Content>\n <ContentHeader title=\"Ask question\" />\n <Grid container spacing={3} direction=\"column\">\n {error && (\n <WarningPanel severity=\"error\" title=\"Could not post question\" />\n )}\n <Grid item>\n <InfoCard>\n <form>\n <TextField\n label=\"Title\"\n required\n fullWidth\n margin=\"normal\"\n title={title}\n onChange={e => setTitle(e.target.value)}\n variant=\"outlined\"\n helperText=\"Write good title for your question that people can understand\"\n />\n <MDEditor\n value={question}\n className={styles.markdownEditor}\n onChange={v => setQuestion(v as string)}\n preview=\"edit\"\n height={400}\n />\n <Autocomplete\n multiple\n id=\"tags-standard\"\n options={availableTags}\n defaultValue={tags}\n onChange={(_e, inputTags) => {\n if (inputTags.length < 5) {\n setTags(inputTags);\n }\n }}\n freeSolo\n renderInput={params => (\n <TextField\n {...params}\n variant=\"outlined\"\n margin=\"normal\"\n label=\"Tags\"\n placeholder=\"Type or select tags\"\n helperText=\"Add up to 5 tags to categorize your question\"\n />\n )}\n />\n <Button variant=\"contained\" onClick={postQuestion}>\n Post\n </Button>\n </form>\n </InfoCard>\n </Grid>\n </Grid>\n </Content>\n );\n};\n","import { AnswerResponse, qetaApiRef, QuestionResponse } from '../../api';\nimport { Box, IconButton, Tooltip, Typography } from '@material-ui/core';\nimport ArrowDownward from '@material-ui/icons/ArrowDownward';\nimport ArrowUpward from '@material-ui/icons/ArrowUpward';\nimport Check from '@material-ui/icons/Check';\nimport React from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport const VoteButtons = (props: {\n entity: QuestionResponse | AnswerResponse;\n question?: QuestionResponse;\n}) => {\n const [ownVote, setOwnVote] = React.useState(props.entity.ownVote ?? 0);\n const isCorrectAnswer =\n 'questionId' in props.entity ? props.entity.correct : false;\n const [correct, setCorrect] = React.useState(isCorrectAnswer);\n const [entity, setEntity] = React.useState<QuestionResponse | AnswerResponse>(\n props.entity,\n );\n const qetaApi = useApi(qetaApiRef);\n\n const voteUp = () => {\n if ('title' in entity) {\n qetaApi.voteQuestionUp(entity.id).then(response => {\n setOwnVote(1);\n setEntity(response);\n });\n } else if ('questionId' in entity) {\n qetaApi.voteAnswerUp(entity.questionId, entity.id).then(response => {\n setOwnVote(1);\n setEntity(response);\n });\n }\n };\n\n const voteDown = () => {\n if ('title' in entity) {\n qetaApi.voteQuestionDown(entity.id).then(response => {\n setOwnVote(-1);\n setEntity(response);\n });\n } else if ('questionId' in entity) {\n qetaApi.voteAnswerDown(entity.questionId, entity.id).then(response => {\n setOwnVote(-1);\n setEntity(response);\n });\n }\n };\n\n const toggleCorrectAnswer = () => {\n if (!('questionId' in entity)) {\n return;\n }\n if (correct) {\n qetaApi\n .markAnswerIncorrect(entity.questionId, entity.id)\n .then(response => {\n if (response) {\n setCorrect(false);\n }\n });\n } else {\n qetaApi.markAnswerCorrect(entity.questionId, entity.id).then(response => {\n setCorrect(response);\n });\n }\n };\n\n return (\n <React.Fragment>\n <Tooltip\n title={\n props.entity.own\n ? 'You cannot vote your own post'\n : 'This answer is good'\n }\n >\n <IconButton\n aria-label=\"vote up\"\n color={ownVote > 0 ? 'primary' : 'default'}\n disabled={props.entity.own ?? false}\n size=\"small\"\n onClick={voteUp}\n >\n <ArrowUpward />\n </IconButton>\n </Tooltip>\n <Typography variant=\"h6\">{entity.score}</Typography>\n <Tooltip\n title={\n props.entity.own\n ? 'You cannot vote your own post'\n : 'This answer is not good'\n }\n >\n <IconButton\n aria-label=\"vote down\"\n color={ownVote < 0 ? 'primary' : 'default'}\n disabled={props.entity.own ?? false}\n size=\"small\"\n onClick={voteDown}\n >\n <ArrowDownward />\n </IconButton>\n </Tooltip>\n {'correct' in props.entity && props.question?.own && (\n <Box>\n <Tooltip title=\"Mark answer as correct\">\n <IconButton\n aria-label=\"mark correct\"\n color={correct ? 'primary' : 'default'}\n size=\"small\"\n onClick={toggleCorrectAnswer}\n >\n <Check />\n </IconButton>\n </Tooltip>\n </Box>\n )}\n </React.Fragment>\n );\n};\n","import { QuestionResponse } from '../../api';\nimport {\n Box,\n Card,\n CardContent,\n Chip,\n Grid,\n Link,\n Typography,\n} from '@material-ui/core';\nimport React from 'react';\nimport { MarkdownContent } from '@backstage/core-components';\nimport { VoteButtons } from './VoteButtons';\nimport { useStyles } from '../../utils/hooks';\n\nexport const QuestionCard = (props: { question: QuestionResponse }) => {\n const { question } = props;\n const styles = useStyles();\n\n return (\n <Card variant=\"outlined\">\n <CardContent>\n <Grid container spacing={0}>\n <Grid item className={styles.questionCardVote}>\n <VoteButtons entity={question} />\n </Grid>\n <Grid item>\n <Typography variant=\"body1\" gutterBottom>\n <MarkdownContent content={question.content} dialect=\"gfm\" />\n </Typography>\n {question.tags &&\n question.tags.map(tag => (\n <Chip\n label={tag}\n size=\"small\"\n component=\"a\"\n href={`/qeta/tags/${tag}`}\n clickable\n />\n ))}\n <Box>\n By{' '}\n <Link href={`/qeta/user/${question.author}`}>\n {question.author}\n </Link>\n </Box>\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n );\n};\n","import { WarningPanel } from '@backstage/core-components';\nimport { Button, Typography } from '@material-ui/core';\nimport MDEditor from '@uiw/react-md-editor';\nimport React from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { AnswerResponse, qetaApiRef, QuestionResponse } from '../../api';\nimport { useStyles } from '../../utils/hooks';\n\nexport const AnswerForm = (props: {\n question: QuestionResponse;\n onPost: (answer: AnswerResponse) => void;\n}) => {\n const { question, onPost } = props;\n const [answer, setAnswer] = React.useState('');\n const [error, setError] = React.useState(false);\n const qetaApi = useApi(qetaApiRef);\n const styles = useStyles();\n\n const postAnswer = () => {\n qetaApi\n .postAnswer({ questionId: question.id, answer })\n .then(a => {\n if (!a || !('id' in a)) {\n setError(true);\n return;\n }\n onPost(a);\n setAnswer('');\n })\n .catch(_e => setError(true));\n };\n\n return (\n <form>\n <Typography variant=\"h6\">Your answer</Typography>\n {error && <WarningPanel severity=\"error\" title=\"Could not post answer\" />}\n <MDEditor\n value={answer}\n className={styles.markdownEditor}\n onChange={v => setAnswer(v as string)}\n preview=\"edit\"\n height={200}\n />\n <Button variant=\"contained\" onClick={postAnswer}>\n Post\n </Button>\n </form>\n );\n};\n","import { AnswerResponse, QuestionResponse } from '../../api';\nimport {\n Box,\n Card,\n CardContent,\n Grid,\n Link,\n Typography,\n} from '@material-ui/core';\nimport React from 'react';\nimport { MarkdownContent } from '@backstage/core-components';\nimport { VoteButtons } from './VoteButtons';\nimport { useStyles } from '../../utils/hooks';\n\nexport const AnswerCard = (props: {\n answer: AnswerResponse;\n question: QuestionResponse;\n}) => {\n const { answer, question } = props;\n const styles = useStyles();\n\n return (\n <Card>\n <CardContent>\n <Grid container spacing={0}>\n <Grid item className={styles.questionCardVote}>\n <VoteButtons entity={answer} question={question} />\n </Grid>\n <Grid item>\n <Typography variant=\"body1\" gutterBottom>\n <MarkdownContent content={answer.content} dialect=\"gfm\" />\n </Typography>\n <Box>\n By{' '}\n <Link href={`/qeta/user/${answer.author}`}>{answer.author}</Link>\n </Box>\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n );\n};\n","import React from 'react';\nimport { Box, Button, Divider, Typography } from '@material-ui/core';\nimport { useParams } from 'react-router-dom';\nimport {\n Content,\n ContentHeader,\n WarningPanel,\n} from '@backstage/core-components';\nimport { useQetaApi } from '../../utils/hooks';\nimport { QuestionCard } from './QuestionCard';\nimport { AnswerResponse, QuestionResponse } from '../../api';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { AnswerForm } from './AnswerForm';\nimport { AnswerCard } from './AnswerCard';\nimport { Skeleton } from '@material-ui/lab';\n\nexport const QuestionPage = () => {\n const { id } = useParams();\n const [newAnswers, setNewAnswers] = React.useState<AnswerResponse[]>([]);\n\n const {\n value: question,\n loading,\n error,\n } = useQetaApi(api => api.getQuestion(id), [id]);\n\n const onAnswerPost = (answer: AnswerResponse) => {\n setNewAnswers(newAnswers.concat([answer]));\n };\n\n const getDescription = (q: QuestionResponse) => {\n return (\n <span>\n Asked{' '}\n <Box fontWeight=\"fontWeightMedium\" display=\"inline\" sx={{ mr: 2 }}>\n <RelativeTime value={q.created} />\n </Box>\n {q.updated && (\n <React.Fragment>\n Updated{' '}\n <Box fontWeight=\"fontWeightMedium\" display=\"inline\">\n <RelativeTime value={q.created} />\n </Box>\n </React.Fragment>\n )}\n Viewed{' '}\n <Box fontWeight=\"fontWeightMedium\" display=\"inline\">\n {q.views} times\n </Box>\n </span>\n );\n };\n\n if (loading) {\n return <Skeleton variant=\"rect\" height={200} />;\n }\n\n if (error || question === undefined) {\n return (\n <WarningPanel severity=\"error\" title=\"Could not load question.\">\n {error?.message}\n </WarningPanel>\n );\n }\n\n return (\n <Content>\n <ContentHeader\n title={question.title}\n // @ts-ignore\n description={getDescription(question)}\n >\n <Button href=\"/qeta\">Back to questions</Button>\n <Button variant=\"contained\" href=\"/qeta/ask\">\n Ask question\n </Button>\n </ContentHeader>\n <QuestionCard question={question} />\n <Typography variant=\"h6\">\n {question.answersCount + newAnswers.length} answers\n </Typography>\n {(question.answers ?? []).concat(newAnswers).map(a => {\n return (\n <Box key={a.id} sx={{ mb: 1 }}>\n <AnswerCard answer={a} question={question} />\n <Divider />\n </Box>\n );\n })}\n <Divider />\n <AnswerForm question={question} onPost={onAnswerPost} />\n </Content>\n );\n};\n","import { QuestionResponse } from '../../../api';\nimport { Card, CardContent, Chip, Link, Typography } from '@material-ui/core';\nimport React from 'react';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\n\nexport const QuestionListItem = (props: { question: QuestionResponse }) => {\n const { question } = props;\n return (\n <Card>\n <CardContent>\n <Typography gutterBottom variant=\"h5\" component=\"div\">\n <Link href={`/qeta/questions/${question.id}`}>{question.title}</Link>\n </Typography>\n {question.tags &&\n question.tags.map(tag => (\n <Chip\n label={tag}\n size=\"small\"\n component=\"a\"\n href={`/qeta/tags/${tag}`}\n clickable\n />\n ))}\n <Typography variant=\"body2\" display=\"block\">\n By{' '}\n <Link href={`/qeta/user/${question.author}`}>{question.author}</Link>{' '}\n <RelativeTime\n value={question.created}\n titleFormat=\"YYYY/MM/DD HH:mm\"\n />\n </Typography>\n <Typography variant=\"caption\" display=\"inline\" gutterBottom>\n Score: {question.score} {' | '}\n </Typography>\n <Typography\n variant=\"caption\"\n color={question.correctAnswer ? 'secondary' : 'initial'}\n display=\"inline\"\n gutterBottom\n >\n Answers: {question.answersCount}\n </Typography>\n <Typography variant=\"caption\" display=\"inline\" gutterBottom>\n {' | '} Views: {question.views}\n </Typography>\n </CardContent>\n </Card>\n );\n};\n","import { useQetaApi, useStyles } from '../../../utils/hooks';\nimport { WarningPanel } from '@backstage/core-components';\nimport { Box, Divider, Grid } from '@material-ui/core';\nimport React from 'react';\nimport { QuestionListItem } from './QuestionListItem';\nimport { Pagination, Skeleton } from '@material-ui/lab';\n\nexport const QuestionList = (props: { tags?: string[]; author?: string }) => {\n const { tags, author } = props;\n const [page, setPage] = React.useState(1);\n const pageSize = 10;\n const offset = (page - 1) * pageSize;\n const styles = useStyles();\n\n const handleChange = (_event: React.ChangeEvent<unknown>, value: number) => {\n setPage(value);\n };\n\n const {\n value: response,\n loading,\n error,\n } = useQetaApi(\n api => api.getQuestions({ limit: pageSize, offset, tags, author }),\n [page, offset],\n );\n\n if (loading) {\n return <Skeleton variant=\"rect\" height={200} />;\n }\n\n if (error || response === undefined) {\n return (\n <WarningPanel severity=\"error\" title=\"Could not load questions.\">\n {error?.message}\n </WarningPanel>\n );\n }\n\n if (response.questions.length === 0) {\n return <Box>No questions found</Box>;\n }\n\n const pageCount =\n response.total < pageSize ? 1 : Math.ceil(response.total / pageSize);\n\n return (\n <Box>\n <Grid container spacing={2}>\n {response.questions.map(question => {\n return (\n <Grid item xs={12} key={question.id}>\n <QuestionListItem question={question} />\n <Divider />\n </Grid>\n );\n })}\n </Grid>\n <Grid\n container\n spacing={0}\n className={styles.questionListPagination}\n direction=\"column\"\n alignItems=\"center\"\n justifyContent=\"center\"\n >\n <Pagination\n page={page}\n onChange={handleChange}\n count={pageCount}\n size=\"large\"\n variant=\"outlined\"\n showFirstButton\n showLastButton\n />\n </Grid>\n </Box>\n );\n};\n","import React from 'react';\nimport { Button } from '@material-ui/core';\nimport { useParams } from 'react-router-dom';\nimport { Content, ContentHeader } from '@backstage/core-components';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { QuestionList } from '../HomePage/QuestionList';\n\nexport const TagPage = () => {\n const { tag } = useParams();\n return (\n <Content>\n <ContentHeader title={`Questions tagged [${tag}]`}>\n <Button href=\"/qeta\">Back to questions</Button>\n <Button variant=\"contained\" href=\"/qeta/ask\">\n Ask question\n </Button>\n </ContentHeader>\n <QuestionList tags={[tag ?? '']} />\n </Content>\n );\n};\n","import React from 'react';\nimport { Button } from '@material-ui/core';\nimport { useParams } from 'react-router-dom';\nimport { Content, ContentHeader } from '@backstage/core-components';\n// @ts-ignore\nimport RelativeTime from 'react-relative-time';\nimport { QuestionList } from '../HomePage/QuestionList';\n\nexport const UserPage = () => {\n const identity = useParams()['*'] ?? 'unknown';\n return (\n <Content>\n <ContentHeader title={`Questions by [${identity}]`}>\n <Button href=\"/qeta\">Back to questions</Button>\n <Button variant=\"contained\" href=\"/qeta/ask\">\n Ask question\n </Button>\n </ContentHeader>\n <QuestionList author={identity ?? ''} />\n </Content>\n );\n};\n","import React from 'react';\nimport { Button } from '@material-ui/core';\nimport {\n Content,\n ContentHeader,\n Header,\n HeaderLabel,\n Page,\n} from '@backstage/core-components';\nimport { Route } from 'react-router-dom';\nimport { Routes } from 'react-router';\nimport { AskPage } from '../AskPage';\nimport { QuestionPage } from '../QuestionPage/QuestionPage';\nimport { QuestionList } from './QuestionList/QuestionList';\nimport { TagPage } from '../TagPage/TagPage';\nimport { UserPage } from '../UserPage/UserPage';\n\nexport const HomePageContent = () => {\n return (\n <Content>\n <ContentHeader title=\"All questions\">\n <Button variant=\"contained\" href=\"/qeta/ask\">\n Ask question\n </Button>\n </ContentHeader>\n <QuestionList />\n </Content>\n );\n};\n\nexport const HomePage = () => (\n <Page themeId=\"tool\">\n <Header title=\"Q&A\">\n <HeaderLabel label=\"Lifecycle\" value=\"Alpha\" />\n </Header>\n <Routes>\n <Route path=\"/\" element={<HomePageContent />} />\n <Route path=\"/ask\" element={<AskPage />} />\n <Route path=\"/questions/:id\" element={<QuestionPage />} />\n <Route path=\"/tags/:tag\" element={<TagPage />} />\n <Route path=\"/user/*\" element={<UserPage />} />\n </Routes>\n </Page>\n);\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
3
|
+
|
|
4
|
+
declare const qetaPlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
5
|
+
root: _backstage_core_plugin_api.RouteRef<undefined>;
|
|
6
|
+
}, {}, {}>;
|
|
7
|
+
declare const QetaPage: () => JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { QetaPage, qetaPlugin };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drodil/backstage-plugin-qeta",
|
|
3
|
+
"description": "Backstage.io Q&A plugin frontend",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"backstage",
|
|
6
|
+
"plugin",
|
|
7
|
+
"frontend",
|
|
8
|
+
"backstage.io"
|
|
9
|
+
],
|
|
10
|
+
"version": "0.1.8",
|
|
11
|
+
"main": "dist/index.esm.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"prepublishOnly": "yarn tsc && yarn build",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/drodil/backstage-plugin-qeta",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/drodil/backstage-plugin-qeta/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/drodil/backstage-plugin-qeta.git"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"main": "dist/index.esm.js",
|
|
26
|
+
"types": "dist/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"backstage": {
|
|
29
|
+
"role": "frontend-plugin"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"start": "backstage-cli package start",
|
|
33
|
+
"build": "backstage-cli package build",
|
|
34
|
+
"lint": "backstage-cli package lint",
|
|
35
|
+
"test": "backstage-cli package test",
|
|
36
|
+
"clean": "backstage-cli package clean",
|
|
37
|
+
"prepack": "backstage-cli package prepack",
|
|
38
|
+
"postpack": "backstage-cli package postpack"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@backstage/core-components": "^0.12.0",
|
|
42
|
+
"@backstage/core-plugin-api": "^1.1.0",
|
|
43
|
+
"@backstage/errors": "^1.1.3",
|
|
44
|
+
"@backstage/theme": "^0.2.16",
|
|
45
|
+
"@material-ui/core": "^4.9.13",
|
|
46
|
+
"@material-ui/icons": "^4.11.3",
|
|
47
|
+
"@material-ui/lab": "4.0.0-alpha.57",
|
|
48
|
+
"@uiw/react-md-editor": "^3.20.0",
|
|
49
|
+
"ajv": "^8.11.0",
|
|
50
|
+
"ajv-formats": "^2.1.1",
|
|
51
|
+
"lodash": "^4.17.21",
|
|
52
|
+
"react-relative-time": "^0.0.7",
|
|
53
|
+
"react-router": "^6.5.0",
|
|
54
|
+
"react-router-dom": "^6.5.0",
|
|
55
|
+
"react-use": "^17.2.4"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": "^16.13.1 || ^17.0.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@backstage/cli": "^0.21.0",
|
|
62
|
+
"@backstage/core-app-api": "^1.2.0",
|
|
63
|
+
"@backstage/dev-utils": "^1.0.8",
|
|
64
|
+
"@backstage/test-utils": "^1.2.2",
|
|
65
|
+
"@testing-library/jest-dom": "^5.10.1",
|
|
66
|
+
"@testing-library/react": "^12.1.3",
|
|
67
|
+
"@testing-library/user-event": "^14.0.0",
|
|
68
|
+
"@types/node": "*",
|
|
69
|
+
"cross-fetch": "^3.1.5",
|
|
70
|
+
"msw": "^0.47.0"
|
|
71
|
+
},
|
|
72
|
+
"files": [
|
|
73
|
+
"dist"
|
|
74
|
+
]
|
|
75
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { qetaPlugin, QetaPage } from './plugin';
|