@stackoverflow/backstage-plugin-stack-overflow-teams 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/config.d.ts +7 -7
- package/package.json +4 -12
- package/dist/api/StackOverflowAPI.esm.js +0 -125
- package/dist/api/StackOverflowAPI.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowMe.esm.js +0 -110
- package/dist/components/StackOverflow/StackOverflowMe.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js +0 -406
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js +0 -402
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js +0 -147
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowTags.esm.js +0 -117
- package/dist/components/StackOverflow/StackOverflowTags.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js +0 -193
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js.map +0 -1
- package/dist/components/StackOverflow/TiptapEditor.esm.js +0 -308
- package/dist/components/StackOverflow/TiptapEditor.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js +0 -128
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js +0 -53
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js +0 -39
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js +0 -47
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js +0 -35
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js +0 -20
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js +0 -225
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js +0 -37
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js.map +0 -1
- package/dist/icons/LogoutIcon.esm.js +0 -24
- package/dist/icons/LogoutIcon.esm.js.map +0 -1
- package/dist/icons/StackOverflowIcon.esm.js +0 -25
- package/dist/icons/StackOverflowIcon.esm.js.map +0 -1
- package/dist/index.d.ts +0 -48
- package/dist/index.esm.js +0 -18
- package/dist/index.esm.js.map +0 -1
- package/dist/package.json.esm.js +0 -98
- package/dist/package.json.esm.js.map +0 -1
- package/dist/pages/StackOverflowHub.esm.js +0 -82
- package/dist/pages/StackOverflowHub.esm.js.map +0 -1
- package/dist/pages/StackOverflowTeamsPage.esm.js +0 -42
- package/dist/pages/StackOverflowTeamsPage.esm.js.map +0 -1
- package/dist/pages/index.esm.js +0 -3
- package/dist/pages/index.esm.js.map +0 -1
- package/dist/plugin.esm.js +0 -27
- package/dist/plugin.esm.js.map +0 -1
- package/dist/routes.esm.js +0 -8
- package/dist/routes.esm.js.map +0 -1
- package/dist/utils/decodeHtml.esm.js +0 -8
- package/dist/utils/decodeHtml.esm.js.map +0 -1
- package/dist/utils/getTimeAgo.esm.js +0 -30
- package/dist/utils/getTimeAgo.esm.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Stack
|
|
1
|
+
# Stack Internal Frontend Plugin
|
|
2
2
|
|
|
3
3
|
This package is the frontend counterpart of the `stack-overflow-teams` plugin for Backstage.
|
|
4
4
|
|
|
5
5
|
## Areas of Responsibility
|
|
6
6
|
|
|
7
|
-
It provides the UI and interacts with the [backend service](https://github.com/EstoesMoises/backstage-stackoverflow/tree/main/plugins/stack-overflow-teams-backend) to fetch data from your Stack
|
|
7
|
+
It provides the UI and interacts with the [backend service](https://github.com/EstoesMoises/backstage-stackoverflow/tree/main/plugins/stack-overflow-teams-backend) to fetch data from your Stack Internal Enterprise instance.
|
|
8
8
|
|
|
9
9
|
### Backend Dependency
|
|
10
10
|
|
|
11
|
-
To fully utilize this plugin, you must also install and configure the corresponding **backend package** (
|
|
11
|
+
To fully utilize this plugin, you must also install and configure the corresponding **backend package** (`@stackoverflow/backstage-plugin-stack-overflow-teams-backend`) in your Backstage backend. The frontend plugin relies on the backend for API communication and authentication handling.
|
|
12
12
|
|
|
13
13
|
## More details
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@ To fully utilize this plugin, you must also install and configure the correspond
|
|
|
16
16
|
|
|
17
17
|
This component is a modified version of the [community plugin.](https://github.com/backstage/community-plugins/tree/main/workspaces/stack-overflow/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem)
|
|
18
18
|
|
|
19
|
-
It adds a more Stack Overflow-like interface, including additional information such as the questions' score, user role, user reputation, and timestamp.
|
|
19
|
+
It adds a more Stack Overflow Internal-like interface, including additional information such as the questions' score, user role, user reputation, and timestamp.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -28,7 +28,7 @@ It adds a more Stack Overflow-like interface, including additional information s
|
|
|
28
28
|
|
|
29
29
|
- **`<StackOverflowPostQuestionModal />`**
|
|
30
30
|
|
|
31
|
-
Provides a form for users to create a new Stack Overflow question. Once submitted, an API request is executed to create the question.
|
|
31
|
+
Provides a form for users to create a new Stack Overflow Internal question. Once submitted, an API request is executed to create the question.
|
|
32
32
|
|
|
33
33
|
This form listens to the `'openAskQuestionModal'` event. You can utilize this anywhere in your Backstage UI. To invoke the form, add the component to your UI along with a button that dispatches the event. Example:
|
|
34
34
|
|
|
@@ -44,7 +44,7 @@ It adds a more Stack Overflow-like interface, including additional information s
|
|
|
44
44
|
|
|
45
45
|
- **`<StackOverflowQuestion />`**
|
|
46
46
|
|
|
47
|
-
Retrieves questions from the API. Uses
|
|
47
|
+
Retrieves questions from the API. Uses API pagination to navigate all pages of questions available to the instance.
|
|
48
48
|
|
|
49
49
|
- **`<StackOverflowTags />`**
|
|
50
50
|
|
|
@@ -70,7 +70,7 @@ It adds a more Stack Overflow-like interface, including additional information s
|
|
|
70
70
|
|
|
71
71
|
- **`<StackAuthCallback />`**
|
|
72
72
|
|
|
73
|
-
Receives the code and state from your Stack
|
|
73
|
+
Receives the code and state from your Stack Internal Enterprise instance as part of the OAuth process and initiates **`/callback`** in the backend.
|
|
74
74
|
|
|
75
75
|
- **`<StackAuthSuccess />`**
|
|
76
76
|
|
|
@@ -88,4 +88,4 @@ It adds a more Stack Overflow-like interface, including additional information s
|
|
|
88
88
|
|
|
89
89
|
### API Requests
|
|
90
90
|
|
|
91
|
-
The frontend plugin creates an API Ref for Stack
|
|
91
|
+
The frontend plugin creates an API Ref for Stack Internal, which can be found under the `/api` folder. **All API requests from the frontend are directed to Backstage's backend**.
|
package/config.d.ts
CHANGED
|
@@ -16,34 +16,34 @@
|
|
|
16
16
|
|
|
17
17
|
export interface Config {
|
|
18
18
|
/**
|
|
19
|
-
* Configuration options for the
|
|
19
|
+
* Configuration options for the Stack Internal frontend plugin.
|
|
20
20
|
*
|
|
21
|
-
* This configuration is shared with the backstage-plugin-stack-overflow-teams-backend and backstage-stack-overflow-teams-collator
|
|
21
|
+
* This configuration is shared with the @stackoverflow/backstage-plugin-stack-overflow-teams-backend and @stackoverflow/backstage-stack-overflow-teams-collator
|
|
22
22
|
*/
|
|
23
23
|
stackoverflow?: {
|
|
24
24
|
/**
|
|
25
|
-
* The base url of the Stack Overflow API used for the plugin
|
|
25
|
+
* The base url of the Stack Overflow Internal API used for the plugin
|
|
26
26
|
*/
|
|
27
27
|
baseUrl: string;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* The API Access Token to authenticate to Stack Overflow API Version 3
|
|
30
|
+
* The API Access Token to authenticate to Stack Overflow Internal API Version 3
|
|
31
31
|
* @visibility secret
|
|
32
32
|
*/
|
|
33
33
|
apiAccessToken: string;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The name of the team for a Stack
|
|
36
|
+
* The name of the team for a Stack Internal account
|
|
37
37
|
*/
|
|
38
38
|
teamName?: string;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Client Id for the OAuth Application, required to use the Stack
|
|
41
|
+
* Client Id for the OAuth Application, required to use the Stack Internal Hub and write actions.
|
|
42
42
|
*/
|
|
43
43
|
clientId: number;
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* RedirectUri for the OAuth Application, required to use the Stack
|
|
46
|
+
* RedirectUri for the OAuth Application, required to use the Stack Internal Hub and write actions.
|
|
47
47
|
*
|
|
48
48
|
* This should be your Backstage application domain ending in the plugin's <StackOverflowTeamsPage /> route
|
|
49
49
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackoverflow/backstage-plugin-stack-overflow-teams",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"main": "dist/index.esm.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"role": "frontend-plugin",
|
|
14
14
|
"pluginId": "stack-overflow-teams",
|
|
15
15
|
"pluginPackages": [
|
|
16
|
-
"backstage-plugin-stack-overflow-teams",
|
|
17
|
-
"backstage-plugin-stack-overflow-teams-backend"
|
|
16
|
+
"@stackoverflow/backstage-plugin-stack-overflow-teams",
|
|
17
|
+
"@stackoverflow/backstage-plugin-stack-overflow-teams-backend"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
@@ -76,13 +76,5 @@
|
|
|
76
76
|
"files": [
|
|
77
77
|
"dist",
|
|
78
78
|
"config.d.ts"
|
|
79
|
-
]
|
|
80
|
-
"typesVersions": {
|
|
81
|
-
"*": {
|
|
82
|
-
"index": [
|
|
83
|
-
"dist/index.d.ts"
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"module": "./dist/index.esm.js"
|
|
79
|
+
]
|
|
88
80
|
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { createApiRef } from '@backstage/core-plugin-api';
|
|
2
|
-
|
|
3
|
-
const stackoverflowteamsApiRef = createApiRef({
|
|
4
|
-
id: "plugin.stackoverflowteams.api"
|
|
5
|
-
});
|
|
6
|
-
const createStackOverflowApi = (discoveryApi, fetchApi) => {
|
|
7
|
-
const getBaseUrl = async () => discoveryApi.getBaseUrl("stack-overflow-teams");
|
|
8
|
-
const requestAPI = async (endpoint, method = "GET", body, params) => {
|
|
9
|
-
const baseUrl = await getBaseUrl();
|
|
10
|
-
const queryString = params ? `?${params.join("&")}` : "";
|
|
11
|
-
const url = `${baseUrl}/${endpoint}${queryString}`;
|
|
12
|
-
const response = await fetchApi.fetch(url, {
|
|
13
|
-
method,
|
|
14
|
-
credentials: "include",
|
|
15
|
-
headers: { "Content-Type": "application/json" },
|
|
16
|
-
body: body ? JSON.stringify(body) : void 0
|
|
17
|
-
});
|
|
18
|
-
if (!response.ok) {
|
|
19
|
-
const errorResponse = await response.json();
|
|
20
|
-
throw new Error(errorResponse.error || `Request failed: ${response.statusText}`);
|
|
21
|
-
}
|
|
22
|
-
return response.json();
|
|
23
|
-
};
|
|
24
|
-
const buildQuestionParams = (filter) => {
|
|
25
|
-
const params = [];
|
|
26
|
-
if (filter?.sort) {
|
|
27
|
-
params.push(`sort=${filter.sort}`);
|
|
28
|
-
}
|
|
29
|
-
if (filter?.order) {
|
|
30
|
-
params.push(`order=${filter.order}`);
|
|
31
|
-
}
|
|
32
|
-
if (filter?.isAnswered !== void 0) {
|
|
33
|
-
params.push(`isAnswered=${filter.isAnswered}`);
|
|
34
|
-
}
|
|
35
|
-
if (filter?.page !== void 0) {
|
|
36
|
-
params.push(`page=${filter.page}`);
|
|
37
|
-
}
|
|
38
|
-
if (filter?.pageSize !== void 0) {
|
|
39
|
-
params.push(`pageSize=${filter.pageSize}`);
|
|
40
|
-
}
|
|
41
|
-
return params;
|
|
42
|
-
};
|
|
43
|
-
return {
|
|
44
|
-
search: (query, page) => {
|
|
45
|
-
const body = { query };
|
|
46
|
-
if (page !== void 0) {
|
|
47
|
-
body.page = page;
|
|
48
|
-
}
|
|
49
|
-
return requestAPI("search", "POST", body);
|
|
50
|
-
},
|
|
51
|
-
getQuestions: (filter) => {
|
|
52
|
-
const params = buildQuestionParams(filter);
|
|
53
|
-
return requestAPI("questions", "GET", void 0, params.length > 0 ? params : void 0);
|
|
54
|
-
},
|
|
55
|
-
getTags: (search) => {
|
|
56
|
-
const params = search ? [`search=${encodeURIComponent(search)}`] : void 0;
|
|
57
|
-
return requestAPI("tags", "GET", void 0, params);
|
|
58
|
-
},
|
|
59
|
-
getUsers: () => requestAPI("users"),
|
|
60
|
-
getMe: () => requestAPI("me"),
|
|
61
|
-
getBaseUrl: async () => {
|
|
62
|
-
const response = await requestAPI("baseurl");
|
|
63
|
-
return response.SOInstance;
|
|
64
|
-
},
|
|
65
|
-
getTeamName: async () => {
|
|
66
|
-
const response = await requestAPI("baseurl");
|
|
67
|
-
return response.teamName;
|
|
68
|
-
},
|
|
69
|
-
postQuestion: (title, body, tags) => requestAPI("questions", "POST", { title, body, tags }),
|
|
70
|
-
startAuth: async () => {
|
|
71
|
-
const data = await requestAPI("auth/start");
|
|
72
|
-
return data.authUrl;
|
|
73
|
-
},
|
|
74
|
-
completeAuth: async (code, state) => {
|
|
75
|
-
await requestAPI("callback", "GET", void 0, [
|
|
76
|
-
`code=${encodeURIComponent(code)}`,
|
|
77
|
-
`state=${encodeURIComponent(state)}`
|
|
78
|
-
]);
|
|
79
|
-
},
|
|
80
|
-
getAuthStatus: async () => {
|
|
81
|
-
try {
|
|
82
|
-
await requestAPI("authStatus");
|
|
83
|
-
return true;
|
|
84
|
-
} catch {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
logout: async () => {
|
|
89
|
-
try {
|
|
90
|
-
await requestAPI("logout", "POST");
|
|
91
|
-
return true;
|
|
92
|
-
} catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
submitAccessToken: async (token) => {
|
|
97
|
-
try {
|
|
98
|
-
await requestAPI("auth/token", "POST", { accessToken: token });
|
|
99
|
-
return true;
|
|
100
|
-
} catch {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
// Convenience methods for common filtering scenarios
|
|
105
|
-
getActiveQuestions: (page) => {
|
|
106
|
-
const params = buildQuestionParams({ sort: "activity", order: "desc", page });
|
|
107
|
-
return requestAPI("questions", "GET", void 0, params);
|
|
108
|
-
},
|
|
109
|
-
getNewestQuestions: (page) => {
|
|
110
|
-
const params = buildQuestionParams({ sort: "creation", order: "desc", page });
|
|
111
|
-
return requestAPI("questions", "GET", void 0, params);
|
|
112
|
-
},
|
|
113
|
-
getTopScoredQuestions: (page) => {
|
|
114
|
-
const params = buildQuestionParams({ sort: "score", order: "desc", page });
|
|
115
|
-
return requestAPI("questions", "GET", void 0, params);
|
|
116
|
-
},
|
|
117
|
-
getUnansweredQuestions: (page) => {
|
|
118
|
-
const params = buildQuestionParams({ isAnswered: false, sort: "creation", order: "desc", page });
|
|
119
|
-
return requestAPI("questions", "GET", void 0, params);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export { createStackOverflowApi, stackoverflowteamsApiRef };
|
|
125
|
-
//# sourceMappingURL=StackOverflowAPI.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StackOverflowAPI.esm.js","sources":["../../src/api/StackOverflowAPI.ts"],"sourcesContent":["import {\n createApiRef,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { Question, Tag, User, PaginatedResponse } from '../api';\n\nexport const stackoverflowteamsApiRef = createApiRef<StackOverflowAPI>({\n id: 'plugin.stackoverflowteams.api',\n});\n\ntype ApiResponse<T> = PaginatedResponse<T>;\n\ninterface BaseUrlResponse {\n SOInstance: string;\n teamName: string\n}\n\n// Enhanced interface for questions filter options\ninterface QuestionsFilter {\n sort?: 'activity' | 'creation' | 'score';\n order?: 'asc' | 'desc';\n isAnswered?: boolean;\n page?: number;\n pageSize?: number;\n}\n\nexport interface StackOverflowAPI {\n search(query: string, page?: number): Promise<any>;\n getQuestions(filter?: QuestionsFilter): Promise<ApiResponse<Question>>;\n getTags(search?: string): Promise<ApiResponse<Tag>>;\n getUsers(): Promise<ApiResponse<User>>;\n getMe(): Promise<User>;\n getBaseUrl(): Promise<string>;\n getTeamName(): Promise<string>;\n postQuestion(title: string, body: string, tags: string[]): Promise<Question>;\n startAuth(): Promise<string>;\n completeAuth(code: string, state: string): Promise<void>;\n getAuthStatus: () => Promise<boolean>;\n logout: () => Promise<boolean>;\n submitAccessToken: (token: string) => Promise<boolean>;\n \n // Convenience methods for common filtering scenarios\n getActiveQuestions(page?: number): Promise<ApiResponse<Question>>;\n getNewestQuestions(page?: number): Promise<ApiResponse<Question>>;\n getTopScoredQuestions(page?: number): Promise<ApiResponse<Question>>;\n getUnansweredQuestions(page?: number): Promise<ApiResponse<Question>>;\n}\n\nexport const createStackOverflowApi = (\n discoveryApi: DiscoveryApi,\n fetchApi: FetchApi,\n): StackOverflowAPI => {\n const getBaseUrl = async () => discoveryApi.getBaseUrl('stack-overflow-teams');\n\n const requestAPI = async <T>(\n endpoint: string,\n method: 'GET' | 'POST' = 'GET',\n body?: Record<string, unknown>,\n params?: string[],\n ): Promise<T> => {\n const baseUrl = await getBaseUrl();\n const queryString = params ? `?${params.join('&')}` : '';\n const url = `${baseUrl}/${endpoint}${queryString}`;\n \n const response = await fetchApi.fetch(url, {\n method,\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorResponse = await response.json();\n throw new Error(errorResponse.error || `Request failed: ${response.statusText}`);\n }\n\n return response.json();\n };\n\n const buildQuestionParams = (filter?: QuestionsFilter): string[] => {\n const params: string[] = [];\n \n if (filter?.sort) {\n params.push(`sort=${filter.sort}`);\n }\n \n if (filter?.order) {\n params.push(`order=${filter.order}`);\n }\n \n if (filter?.isAnswered !== undefined) {\n params.push(`isAnswered=${filter.isAnswered}`);\n }\n \n if (filter?.page !== undefined) {\n params.push(`page=${filter.page}`);\n }\n \n if (filter?.pageSize !== undefined) {\n params.push(`pageSize=${filter.pageSize}`);\n }\n \n return params;\n };\n\n return {\n search: (query: string, page?: number) => {\n const body: { query: string; page?: number } = { query };\n if (page !== undefined) {\n body.page = page;\n }\n return requestAPI<any>('search', 'POST', body);\n },\n getQuestions: (filter?: QuestionsFilter) => {\n const params = buildQuestionParams(filter);\n return requestAPI<ApiResponse<Question>>('questions', 'GET', undefined, params.length > 0 ? params : undefined);\n },\n getTags: (search?: string) => {\n const params = search ? [`search=${encodeURIComponent(search)}`] : undefined;\n return requestAPI<ApiResponse<Tag>>('tags', 'GET', undefined, params);\n },\n getUsers: () => requestAPI<ApiResponse<User>>('users'),\n getMe: () => requestAPI<User>('me'),\n getBaseUrl: async () => {\n const response = await requestAPI<BaseUrlResponse>('baseurl');\n return response.SOInstance;\n },\n getTeamName: async () => {\n const response = await requestAPI<BaseUrlResponse>('baseurl')\n return response.teamName\n },\n postQuestion: (title: string, body: string, tags: string[]) =>\n requestAPI<Question>('questions', 'POST', { title, body, tags }),\n startAuth: async () => {\n const data = await requestAPI<{ authUrl: string }>('auth/start');\n return data.authUrl;\n },\n completeAuth: async (code: string, state: string) => {\n await requestAPI('callback', 'GET', undefined, [\n `code=${encodeURIComponent(code)}`,\n `state=${encodeURIComponent(state)}`,\n ]);\n },\n getAuthStatus: async (): Promise<boolean> => {\n try {\n await requestAPI('authStatus');\n return true;\n } catch {\n return false;\n }\n },\n logout: async (): Promise<boolean> => {\n try {\n await requestAPI('logout', 'POST');\n return true;\n } catch {\n return false;\n }\n },\n submitAccessToken: async (token: string): Promise<boolean> => {\n try {\n await requestAPI('auth/token', 'POST', { accessToken: token });\n return true;\n } catch {\n return false;\n }\n },\n\n // Convenience methods for common filtering scenarios\n getActiveQuestions: (page?: number) => {\n const params = buildQuestionParams({ sort: 'activity', order: 'desc', page });\n return requestAPI<ApiResponse<Question>>('questions', 'GET', undefined, params);\n },\n\n getNewestQuestions: (page?: number) => {\n const params = buildQuestionParams({ sort: 'creation', order: 'desc', page });\n return requestAPI<ApiResponse<Question>>('questions', 'GET', undefined, params);\n },\n\n getTopScoredQuestions: (page?: number) => {\n const params = buildQuestionParams({ sort: 'score', order: 'desc', page });\n return requestAPI<ApiResponse<Question>>('questions', 'GET', undefined, params);\n },\n\n getUnansweredQuestions: (page?: number) => {\n const params = buildQuestionParams({ isAnswered: false, sort: 'creation', order: 'desc', page });\n return requestAPI<ApiResponse<Question>>('questions', 'GET', undefined, params);\n },\n };\n};"],"names":[],"mappings":"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"}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { useApi } from '@backstage/core-plugin-api';
|
|
2
|
-
import { stackoverflowteamsApiRef } from '../../api/StackOverflowAPI.esm.js';
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
4
|
-
import { Box, CircularProgress, Typography, Card, IconButton, CardContent, Avatar, Chip, Link } from '@mui/material';
|
|
5
|
-
import { LogoutIcon } from '../../icons/LogoutIcon.esm.js';
|
|
6
|
-
|
|
7
|
-
const StackOverflowMe = () => {
|
|
8
|
-
const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);
|
|
9
|
-
const logout = async () => {
|
|
10
|
-
try {
|
|
11
|
-
const success = await stackOverflowTeamsApi.logout();
|
|
12
|
-
if (success) {
|
|
13
|
-
window.location.reload();
|
|
14
|
-
} else {
|
|
15
|
-
throw new Error("Logout failed.");
|
|
16
|
-
}
|
|
17
|
-
} catch (error2) {
|
|
18
|
-
throw new Error("Error during logout:", error2);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
const [userData, setUserData] = useState(null);
|
|
22
|
-
const [loading, setLoading] = useState(true);
|
|
23
|
-
const [error, setError] = useState(null);
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
const fetchMe = async () => {
|
|
26
|
-
try {
|
|
27
|
-
const data = await stackOverflowTeamsApi.getMe();
|
|
28
|
-
setUserData(data);
|
|
29
|
-
} catch (err) {
|
|
30
|
-
setError("Failed to load user data");
|
|
31
|
-
} finally {
|
|
32
|
-
setLoading(false);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
fetchMe();
|
|
36
|
-
}, [stackOverflowTeamsApi]);
|
|
37
|
-
if (loading) {
|
|
38
|
-
return /* @__PURE__ */ React.createElement(
|
|
39
|
-
Box,
|
|
40
|
-
{
|
|
41
|
-
display: "flex",
|
|
42
|
-
justifyContent: "center",
|
|
43
|
-
alignItems: "center",
|
|
44
|
-
width: "100%"
|
|
45
|
-
},
|
|
46
|
-
/* @__PURE__ */ React.createElement(CircularProgress, { color: "primary" })
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
if (error) {
|
|
50
|
-
return /* @__PURE__ */ React.createElement(
|
|
51
|
-
Box,
|
|
52
|
-
{
|
|
53
|
-
display: "flex",
|
|
54
|
-
justifyContent: "center",
|
|
55
|
-
alignItems: "center",
|
|
56
|
-
width: "100%"
|
|
57
|
-
},
|
|
58
|
-
/* @__PURE__ */ React.createElement(Typography, { color: "error" }, error)
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
if (!userData) return null;
|
|
62
|
-
return /* @__PURE__ */ React.createElement(
|
|
63
|
-
Box,
|
|
64
|
-
{
|
|
65
|
-
display: "flex",
|
|
66
|
-
justifyContent: "center",
|
|
67
|
-
alignItems: "center",
|
|
68
|
-
width: "100%"
|
|
69
|
-
},
|
|
70
|
-
/* @__PURE__ */ React.createElement(
|
|
71
|
-
Card,
|
|
72
|
-
{
|
|
73
|
-
variant: "outlined",
|
|
74
|
-
sx: {
|
|
75
|
-
p: 3,
|
|
76
|
-
position: "relative",
|
|
77
|
-
boxShadow: 3
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
/* @__PURE__ */ React.createElement(Box, { sx: { position: "absolute", top: 8, right: 8 } }, /* @__PURE__ */ React.createElement(
|
|
81
|
-
IconButton,
|
|
82
|
-
{
|
|
83
|
-
onClick: logout
|
|
84
|
-
},
|
|
85
|
-
/* @__PURE__ */ React.createElement(LogoutIcon, null)
|
|
86
|
-
)),
|
|
87
|
-
/* @__PURE__ */ React.createElement(CardContent, { sx: { textAlign: "center" } }, /* @__PURE__ */ React.createElement(Avatar, { src: userData.avatarUrl, sx: { mx: "auto", mb: 2 } }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", fontWeight: "bold", gutterBottom: true }, userData.name), /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React.createElement(
|
|
88
|
-
Chip,
|
|
89
|
-
{
|
|
90
|
-
label: `Reputation: ${userData.reputation}`,
|
|
91
|
-
size: "small",
|
|
92
|
-
color: "primary"
|
|
93
|
-
}
|
|
94
|
-
), /* @__PURE__ */ React.createElement(Chip, { label: userData.role, size: "small", variant: "outlined" })), userData.jobTitle && /* @__PURE__ */ React.createElement(Typography, { variant: "body2", fontWeight: "500", mt: 1 }, "Position: ", userData.jobTitle), /* @__PURE__ */ React.createElement(
|
|
95
|
-
Link,
|
|
96
|
-
{
|
|
97
|
-
href: userData.webUrl,
|
|
98
|
-
target: "_blank",
|
|
99
|
-
rel: "noopener",
|
|
100
|
-
color: "primary",
|
|
101
|
-
sx: { display: "block", mt: 2, fontWeight: "bold" }
|
|
102
|
-
},
|
|
103
|
-
"View Profile"
|
|
104
|
-
))
|
|
105
|
-
)
|
|
106
|
-
);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export { StackOverflowMe };
|
|
110
|
-
//# sourceMappingURL=StackOverflowMe.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StackOverflowMe.esm.js","sources":["../../../src/components/StackOverflow/StackOverflowMe.tsx"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { stackoverflowteamsApiRef, User } from '../../api';\nimport { useEffect, useState } from 'react';\nimport React from 'react';\n// eslint-disable-next-line no-restricted-imports\nimport {\n Box,\n Avatar,\n Typography,\n Card,\n CardContent,\n Link,\n CircularProgress,\n Chip,\n IconButton,\n} from '@mui/material';\nimport { LogoutIcon } from '../../icons';\n\nexport const StackOverflowMe = () => {\n const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);\n const logout = async () => {\n try {\n const success = await stackOverflowTeamsApi.logout();\n if (success) {\n window.location.reload();\n } else {\n throw new Error('Logout failed.');\n }\n } catch (error:any) {\n throw new Error('Error during logout:', error);\n }\n };\n\n const [userData, setUserData] = useState<User | null>(null);\n const [loading, setLoading] = useState<Boolean>(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const fetchMe = async () => {\n try {\n const data = await stackOverflowTeamsApi.getMe();\n setUserData(data);\n } catch (err) {\n setError('Failed to load user data');\n } finally {\n setLoading(false);\n }\n };\n\n fetchMe();\n }, [stackOverflowTeamsApi]);\n\n if (loading) {\n return (\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n width=\"100%\"\n >\n <CircularProgress color=\"primary\" />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n width=\"100%\"\n >\n <Typography color=\"error\">{error}</Typography>\n </Box>\n );\n }\n\n if (!userData) return null;\n\n return (\n <Box\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n width=\"100%\"\n >\n <Card\n variant=\"outlined\"\n sx={{\n p: 3,\n position: 'relative',\n boxShadow: 3,\n }}\n >\n {/* Logout Button */}\n <Box sx={{ position: 'absolute', top: 8, right: 8 }}>\n <IconButton\n \n onClick={logout}\n >\n <LogoutIcon />\n </IconButton>\n </Box>\n <CardContent sx={{ textAlign: 'center' }}>\n {/* Avatar */}\n <Avatar src={userData.avatarUrl} sx={{ mx: 'auto', mb: 2 }} />\n\n {/* User Details */}\n <Typography variant=\"h6\" fontWeight=\"bold\" gutterBottom>\n {userData.name}\n </Typography>\n\n <Box display=\"flex\" justifyContent=\"center\" gap={1} mb={1}>\n <Chip\n label={`Reputation: ${userData.reputation}`}\n size=\"small\"\n color=\"primary\"\n />\n <Chip label={userData.role} size=\"small\" variant=\"outlined\" />\n </Box>\n\n {userData.jobTitle && (\n <Typography variant=\"body2\" fontWeight=\"500\" mt={1}>\n Position: {userData.jobTitle}\n </Typography>\n )}\n\n {/* Profile Link */}\n <Link\n href={userData.webUrl}\n target=\"_blank\"\n rel=\"noopener\"\n color=\"primary\"\n sx={{ display: 'block', mt: 2, fontWeight: 'bold' }}\n >\n View Profile\n </Link>\n </CardContent>\n </Card>\n </Box>\n );\n};\n"],"names":["error"],"mappings":"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"}
|