@stackoverflow/backstage-plugin-stack-overflow-teams 1.6.2 → 1.6.3
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/dist/api/StackOverflowAPI.esm.js +125 -0
- package/dist/api/StackOverflowAPI.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowMe.esm.js +125 -0
- package/dist/components/StackOverflow/StackOverflowMe.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js +803 -0
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js +444 -0
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js +175 -0
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowTags.esm.js +127 -0
- package/dist/components/StackOverflow/StackOverflowTags.esm.js.map +1 -0
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js +226 -0
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js.map +1 -0
- package/dist/components/StackOverflow/TiptapEditor.esm.js +312 -0
- package/dist/components/StackOverflow/TiptapEditor.esm.js.map +1 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js +128 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js.map +1 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js +53 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js.map +1 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js +39 -0
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js.map +1 -0
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js +48 -0
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js.map +1 -0
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js +38 -0
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js.map +1 -0
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js +22 -0
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js.map +1 -0
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js +238 -0
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js.map +1 -0
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js +40 -0
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js.map +1 -0
- package/dist/icons/LogoutIcon.esm.js +24 -0
- package/dist/icons/LogoutIcon.esm.js.map +1 -0
- package/dist/icons/StackOverflowIcon.esm.js +27 -0
- package/dist/icons/StackOverflowIcon.esm.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.esm.js +19 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/package.json.esm.js +6 -0
- package/dist/package.json.esm.js.map +1 -0
- package/dist/pages/StackOverflowHub.esm.js +138 -0
- package/dist/pages/StackOverflowHub.esm.js.map +1 -0
- package/dist/pages/StackOverflowTeamsPage.esm.js +43 -0
- package/dist/pages/StackOverflowTeamsPage.esm.js.map +1 -0
- package/dist/pages/index.esm.js +3 -0
- package/dist/pages/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +27 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/utils/decodeHtml.esm.js +8 -0
- package/dist/utils/decodeHtml.esm.js.map +1 -0
- package/dist/utils/getTimeAgo.esm.js +30 -0
- package/dist/utils/getTimeAgo.esm.js.map +1 -0
- package/package.json +12 -4
- package/src/index.ts +0 -5
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
3
|
+
import { stackoverflowteamsApiRef } from '../../../api/StackOverflowAPI.esm.js';
|
|
4
|
+
|
|
5
|
+
const useStackOverflowData = (type, options) => {
|
|
6
|
+
const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);
|
|
7
|
+
const [data, setData] = useState(null);
|
|
8
|
+
const [loading, setLoading] = useState(true);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const fetchData = useCallback(async () => {
|
|
11
|
+
setLoading(true);
|
|
12
|
+
setError(null);
|
|
13
|
+
try {
|
|
14
|
+
let response;
|
|
15
|
+
switch (type) {
|
|
16
|
+
case "questions":
|
|
17
|
+
response = await stackOverflowTeamsApi.getQuestions(options?.questionFilters);
|
|
18
|
+
setData({
|
|
19
|
+
questions: response.items,
|
|
20
|
+
totalPages: response.totalPages,
|
|
21
|
+
totalCount: response.totalCount,
|
|
22
|
+
currentPage: response.page
|
|
23
|
+
});
|
|
24
|
+
break;
|
|
25
|
+
case "tags":
|
|
26
|
+
response = await stackOverflowTeamsApi.getTags(options?.tagSearch);
|
|
27
|
+
setData({ tags: response.items });
|
|
28
|
+
break;
|
|
29
|
+
case "users":
|
|
30
|
+
response = await stackOverflowTeamsApi.getUsers();
|
|
31
|
+
setData({ users: response.items });
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(`Invalid data type: ${type}`);
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
38
|
+
} finally {
|
|
39
|
+
setLoading(false);
|
|
40
|
+
}
|
|
41
|
+
}, [type, stackOverflowTeamsApi, options?.questionFilters, options?.tagSearch]);
|
|
42
|
+
const fetchActiveQuestions = useCallback(async (page) => {
|
|
43
|
+
if (type !== "questions") return;
|
|
44
|
+
setLoading(true);
|
|
45
|
+
setError(null);
|
|
46
|
+
try {
|
|
47
|
+
const response = await stackOverflowTeamsApi.getActiveQuestions(page);
|
|
48
|
+
setData({
|
|
49
|
+
questions: response.items,
|
|
50
|
+
totalPages: response.totalPages,
|
|
51
|
+
totalCount: response.totalCount,
|
|
52
|
+
currentPage: response.page
|
|
53
|
+
});
|
|
54
|
+
} catch (e) {
|
|
55
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
56
|
+
} finally {
|
|
57
|
+
setLoading(false);
|
|
58
|
+
}
|
|
59
|
+
}, [type, stackOverflowTeamsApi]);
|
|
60
|
+
const fetchNewestQuestions = useCallback(async (page) => {
|
|
61
|
+
if (type !== "questions") return;
|
|
62
|
+
setLoading(true);
|
|
63
|
+
setError(null);
|
|
64
|
+
try {
|
|
65
|
+
const response = await stackOverflowTeamsApi.getNewestQuestions(page);
|
|
66
|
+
setData({
|
|
67
|
+
questions: response.items,
|
|
68
|
+
totalPages: response.totalPages,
|
|
69
|
+
totalCount: response.totalCount,
|
|
70
|
+
currentPage: response.page
|
|
71
|
+
});
|
|
72
|
+
} catch (e) {
|
|
73
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
74
|
+
} finally {
|
|
75
|
+
setLoading(false);
|
|
76
|
+
}
|
|
77
|
+
}, [type, stackOverflowTeamsApi]);
|
|
78
|
+
const fetchTopScoredQuestions = useCallback(async (page) => {
|
|
79
|
+
if (type !== "questions") return;
|
|
80
|
+
setLoading(true);
|
|
81
|
+
setError(null);
|
|
82
|
+
try {
|
|
83
|
+
const response = await stackOverflowTeamsApi.getTopScoredQuestions(page);
|
|
84
|
+
setData({
|
|
85
|
+
questions: response.items,
|
|
86
|
+
totalPages: response.totalPages,
|
|
87
|
+
totalCount: response.totalCount,
|
|
88
|
+
currentPage: response.page
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
92
|
+
} finally {
|
|
93
|
+
setLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}, [type, stackOverflowTeamsApi]);
|
|
96
|
+
const fetchUnansweredQuestions = useCallback(async (page) => {
|
|
97
|
+
if (type !== "questions") return;
|
|
98
|
+
setLoading(true);
|
|
99
|
+
setError(null);
|
|
100
|
+
try {
|
|
101
|
+
const response = await stackOverflowTeamsApi.getUnansweredQuestions(page);
|
|
102
|
+
setData({
|
|
103
|
+
questions: response.items,
|
|
104
|
+
totalPages: response.totalPages,
|
|
105
|
+
totalCount: response.totalCount,
|
|
106
|
+
currentPage: response.page
|
|
107
|
+
});
|
|
108
|
+
} catch (e) {
|
|
109
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
110
|
+
} finally {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
}
|
|
113
|
+
}, [type, stackOverflowTeamsApi]);
|
|
114
|
+
return {
|
|
115
|
+
data,
|
|
116
|
+
loading,
|
|
117
|
+
error,
|
|
118
|
+
fetchData,
|
|
119
|
+
// Convenience methods for questions
|
|
120
|
+
fetchActiveQuestions,
|
|
121
|
+
fetchNewestQuestions,
|
|
122
|
+
fetchTopScoredQuestions,
|
|
123
|
+
fetchUnansweredQuestions
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export { useStackOverflowData };
|
|
128
|
+
//# sourceMappingURL=useStackOverflowData.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStackOverflowData.esm.js","sources":["../../../../src/components/StackOverflow/hooks/useStackOverflowData.ts"],"sourcesContent":["import { useState, useCallback } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { Question, stackoverflowteamsApiRef, Tag, User } from '../../../api/';\n\ntype PostType = 'questions' | 'tags' | 'users' \n\ninterface QuestionFilters {\n sort?: 'activity' | 'creation' | 'score';\n order?: 'asc' | 'desc';\n isAnswered?: boolean;\n page?: number;\n pageSize?: number;\n}\n\ninterface StackOverflowData {\n questions?: Question[];\n tags?: Tag[];\n users?: User[];\n totalPages?: number;\n totalCount?: number;\n currentPage?: number;\n}\n\ninterface UseStackOverflowDataOptions {\n questionFilters?: QuestionFilters;\n tagSearch?: string;\n}\n\nexport const useStackOverflowData = (type: PostType, options?: UseStackOverflowDataOptions) => {\n const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);\n const [data, setData] = useState<StackOverflowData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n \n try {\n let response;\n switch (type) {\n case 'questions':\n response = await stackOverflowTeamsApi.getQuestions(options?.questionFilters);\n setData({ \n questions: response.items,\n totalPages: response.totalPages,\n totalCount: response.totalCount,\n currentPage: response.page\n });\n break;\n case 'tags':\n response = await stackOverflowTeamsApi.getTags(options?.tagSearch);\n setData({ tags: response.items });\n break;\n case 'users':\n response = await stackOverflowTeamsApi.getUsers();\n setData({ users: response.items });\n break;\n default:\n throw new Error(`Invalid data type: ${type}`);\n }\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [type, stackOverflowTeamsApi, options?.questionFilters, options?.tagSearch]);\n\n // Convenience methods for questions\n const fetchActiveQuestions = useCallback(async (page?: number) => {\n if (type !== 'questions') return;\n setLoading(true);\n setError(null);\n \n try {\n const response = await stackOverflowTeamsApi.getActiveQuestions(page);\n setData({ \n questions: response.items,\n totalPages: response.totalPages,\n totalCount: response.totalCount,\n currentPage: response.page\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [type, stackOverflowTeamsApi]);\n\n const fetchNewestQuestions = useCallback(async (page?: number) => {\n if (type !== 'questions') return;\n setLoading(true);\n setError(null);\n \n try {\n const response = await stackOverflowTeamsApi.getNewestQuestions(page);\n setData({ \n questions: response.items,\n totalPages: response.totalPages,\n totalCount: response.totalCount,\n currentPage: response.page\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [type, stackOverflowTeamsApi]);\n\n const fetchTopScoredQuestions = useCallback(async (page?: number) => {\n if (type !== 'questions') return;\n setLoading(true);\n setError(null);\n \n try {\n const response = await stackOverflowTeamsApi.getTopScoredQuestions(page);\n setData({ \n questions: response.items,\n totalPages: response.totalPages,\n totalCount: response.totalCount,\n currentPage: response.page\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [type, stackOverflowTeamsApi]);\n\n const fetchUnansweredQuestions = useCallback(async (page?: number) => {\n if (type !== 'questions') return;\n setLoading(true);\n setError(null);\n \n try {\n const response = await stackOverflowTeamsApi.getUnansweredQuestions(page);\n setData({ \n questions: response.items,\n totalPages: response.totalPages,\n totalCount: response.totalCount,\n currentPage: response.page\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [type, stackOverflowTeamsApi]);\n\n return { \n data, \n loading, \n error, \n fetchData,\n // Convenience methods for questions\n fetchActiveQuestions,\n fetchNewestQuestions, \n fetchTopScoredQuestions,\n fetchUnansweredQuestions\n };\n};"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
2
|
+
import { stackoverflowteamsApiRef } from '../../../api/StackOverflowAPI.esm.js';
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
const useStackOverflowSearch = () => {
|
|
6
|
+
const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);
|
|
7
|
+
const [searchData, setSearchData] = useState(null);
|
|
8
|
+
const [loading, setLoading] = useState(false);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
const search = useCallback(async (query, page) => {
|
|
11
|
+
if (!query.trim()) {
|
|
12
|
+
setSearchData(null);
|
|
13
|
+
setError(null);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
setLoading(true);
|
|
17
|
+
setError(null);
|
|
18
|
+
try {
|
|
19
|
+
const response = await stackOverflowTeamsApi.search(query, page);
|
|
20
|
+
setSearchData({
|
|
21
|
+
items: response.items,
|
|
22
|
+
totalCount: response.totalCount,
|
|
23
|
+
totalPages: response.totalPages,
|
|
24
|
+
page: response.page,
|
|
25
|
+
pageSize: response.pageSize
|
|
26
|
+
});
|
|
27
|
+
} catch (e) {
|
|
28
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
29
|
+
setSearchData(null);
|
|
30
|
+
} finally {
|
|
31
|
+
setLoading(false);
|
|
32
|
+
}
|
|
33
|
+
}, [stackOverflowTeamsApi]);
|
|
34
|
+
const clearSearch = useCallback(() => {
|
|
35
|
+
setSearchData(null);
|
|
36
|
+
setError(null);
|
|
37
|
+
}, []);
|
|
38
|
+
return {
|
|
39
|
+
searchData,
|
|
40
|
+
loading,
|
|
41
|
+
error,
|
|
42
|
+
search,
|
|
43
|
+
clearSearch,
|
|
44
|
+
// Convenience getters
|
|
45
|
+
hasResults: !!searchData?.items.length,
|
|
46
|
+
totalPages: searchData?.totalPages || 0,
|
|
47
|
+
currentPage: searchData?.page || 1,
|
|
48
|
+
totalCount: searchData?.totalCount
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { useStackOverflowSearch };
|
|
53
|
+
//# sourceMappingURL=useStackOverflowSearch.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStackOverflowSearch.esm.js","sources":["../../../../src/components/StackOverflow/hooks/useStackOverflowSearch.ts"],"sourcesContent":["import { useApi } from \"@backstage/core-plugin-api\";\nimport { stackoverflowteamsApiRef } from \"../../../api/StackOverflowAPI\";\nimport { useCallback, useState } from \"react\";\nimport { PaginatedResponse } from \"../../../types\";\n\ninterface SearchData {\n items: any[];\n totalCount: number;\n totalPages: number;\n page: number;\n pageSize: number;\n}\n\nexport const useStackOverflowSearch = () => {\n const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);\n const [searchData, setSearchData] = useState<SearchData | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const search = useCallback(async (query: string, page?: number) => {\n if (!query.trim()) {\n setSearchData(null);\n setError(null);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const response: PaginatedResponse<any> = await stackOverflowTeamsApi.search(query, page);\n setSearchData({\n items: response.items,\n totalCount: response.totalCount,\n totalPages: response.totalPages,\n page: response.page,\n pageSize: response.pageSize,\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setSearchData(null);\n } finally {\n setLoading(false);\n }\n }, [stackOverflowTeamsApi]);\n\n const clearSearch = useCallback(() => {\n setSearchData(null);\n setError(null);\n }, []);\n\n return { \n searchData, \n loading, \n error, \n search, \n clearSearch,\n // Convenience getters\n hasResults: !!searchData?.items.length,\n totalPages: searchData?.totalPages || 0,\n currentPage: searchData?.page || 1,\n totalCount: searchData?.totalCount,\n };\n};"],"names":[],"mappings":";;;;AAaO,MAAM,yBAAyB,MAAM;AAC1C,EAAA,MAAM,qBAAA,GAAwB,OAAO,wBAAwB,CAAA;AAC7D,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAA4B,IAAI,CAAA;AACpE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,OAAO,KAAA,EAAe,IAAA,KAAkB;AACjE,IAAA,IAAI,CAAC,KAAA,CAAM,IAAA,EAAK,EAAG;AACjB,MAAA,aAAA,CAAc,IAAI,CAAA;AAClB,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA;AAAA,IACF;AAEA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AAEb,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAmC,MAAM,qBAAA,CAAsB,MAAA,CAAO,OAAO,IAAI,CAAA;AACvF,MAAA,aAAA,CAAc;AAAA,QACZ,OAAO,QAAA,CAAS,KAAA;AAAA,QAChB,YAAY,QAAA,CAAS,UAAA;AAAA,QACrB,YAAY,QAAA,CAAS,UAAA;AAAA,QACrB,MAAM,QAAA,CAAS,IAAA;AAAA,QACf,UAAU,QAAA,CAAS;AAAA,OACpB,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,QAAA,CAAS,CAAA,YAAa,QAAQ,CAAA,GAAI,IAAI,MAAM,MAAA,CAAO,CAAC,CAAC,CAAC,CAAA;AACtD,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,qBAAqB,CAAC,CAAA;AAE1B,EAAA,MAAM,WAAA,GAAc,YAAY,MAAM;AACpC,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA;AAAA;AAAA,IAEA,UAAA,EAAY,CAAC,CAAC,UAAA,EAAY,KAAA,CAAM,MAAA;AAAA,IAChC,UAAA,EAAY,YAAY,UAAA,IAAc,CAAA;AAAA,IACtC,WAAA,EAAa,YAAY,IAAA,IAAQ,CAAA;AAAA,IACjC,YAAY,UAAA,EAAY;AAAA,GAC1B;AACF;;;;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
2
|
+
|
|
3
|
+
const useStackOverflowStyles = makeStyles((theme) => ({
|
|
4
|
+
title: {
|
|
5
|
+
color: theme.palette.type === "dark" ? "#4CA8FF" : "#0077CC",
|
|
6
|
+
// Brighter blue in dark mode
|
|
7
|
+
fontWeight: "bold"
|
|
8
|
+
},
|
|
9
|
+
unanswered: {
|
|
10
|
+
backgroundColor: theme.palette.type === "dark" ? "#3B2D1F" : "#FFF4E5",
|
|
11
|
+
// Darker orange for dark mode
|
|
12
|
+
borderRadius: "4px",
|
|
13
|
+
padding: "4px 8px"
|
|
14
|
+
},
|
|
15
|
+
answered: {
|
|
16
|
+
backgroundColor: theme.palette.type === "dark" ? "#1E4620" : "#E3FCEF",
|
|
17
|
+
// Darker green for dark mode
|
|
18
|
+
borderRadius: "4px",
|
|
19
|
+
padding: "4px 8px"
|
|
20
|
+
},
|
|
21
|
+
button: {
|
|
22
|
+
backgroundColor: "#F48024",
|
|
23
|
+
// Stack Overflow Internal orange
|
|
24
|
+
color: "#fff",
|
|
25
|
+
"&:hover": {
|
|
26
|
+
backgroundColor: theme.palette.type === "dark" ? "#C15C17" : "#D4691E"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
tableRow: {
|
|
30
|
+
backgroundColor: theme.palette.background.paper,
|
|
31
|
+
"&:hover": {
|
|
32
|
+
backgroundColor: theme.palette.type === "dark" ? theme.palette.action.hover : "#F5F5F5"
|
|
33
|
+
// Light gray hover effect in light mode
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
export { useStackOverflowStyles };
|
|
39
|
+
//# sourceMappingURL=useStackOverflowStyles.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStackOverflowStyles.esm.js","sources":["../../../../src/components/StackOverflow/hooks/useStackOverflowStyles.ts"],"sourcesContent":["import { makeStyles, Theme } from '@material-ui/core/styles';\n\n/**\n * Reusable styles for Stack Overflow Internal-themed components with dark mode support\n */\nexport const useStackOverflowStyles = makeStyles((theme: Theme) => ({\n title: {\n color: theme.palette.type === 'dark' ? '#4CA8FF' : '#0077CC', // Brighter blue in dark mode\n fontWeight: 'bold',\n },\n unanswered: {\n backgroundColor: theme.palette.type === 'dark' ? '#3B2D1F' : '#FFF4E5', // Darker orange for dark mode\n borderRadius: '4px',\n padding: '4px 8px',\n },\n answered: {\n backgroundColor: theme.palette.type === 'dark' ? '#1E4620' : '#E3FCEF', // Darker green for dark mode\n borderRadius: '4px',\n padding: '4px 8px',\n },\n button: {\n backgroundColor: '#F48024', // Stack Overflow Internal orange\n color: '#fff',\n '&:hover': {\n backgroundColor: theme.palette.type === 'dark' ? '#C15C17' : '#D4691E',\n },\n },\n tableRow: {\n backgroundColor: theme.palette.background.paper,\n '&:hover': {\n backgroundColor:\n theme.palette.type === 'dark'\n ? theme.palette.action.hover\n : '#F5F5F5', // Light gray hover effect in light mode\n },\n },\n}));\n"],"names":[],"mappings":";;AAKO,MAAM,sBAAA,GAAyB,UAAA,CAAW,CAAC,KAAA,MAAkB;AAAA,EAClE,KAAA,EAAO;AAAA,IACL,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY,SAAA;AAAA;AAAA,IACnD,UAAA,EAAY;AAAA,GACd;AAAA,EACA,UAAA,EAAY;AAAA,IACV,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY,SAAA;AAAA;AAAA,IAC7D,YAAA,EAAc,KAAA;AAAA,IACd,OAAA,EAAS;AAAA,GACX;AAAA,EACA,QAAA,EAAU;AAAA,IACR,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY,SAAA;AAAA;AAAA,IAC7D,YAAA,EAAc,KAAA;AAAA,IACd,OAAA,EAAS;AAAA,GACX;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,eAAA,EAAiB,SAAA;AAAA;AAAA,IACjB,KAAA,EAAO,MAAA;AAAA,IACP,SAAA,EAAW;AAAA,MACT,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,SAAS,SAAA,GAAY;AAAA;AAC/D,GACF;AAAA,EACA,QAAA,EAAU;AAAA,IACR,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,KAAA;AAAA,IAC1C,SAAA,EAAW;AAAA,MACT,eAAA,EACE,MAAM,OAAA,CAAQ,IAAA,KAAS,SACnB,KAAA,CAAM,OAAA,CAAQ,OAAO,KAAA,GACrB;AAAA;AAAA;AACR;AAEJ,CAAA,CAAE;;;;"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
3
|
+
import { stackoverflowteamsApiRef } from '../../api/StackOverflowAPI.esm.js';
|
|
4
|
+
import { useState, useEffect } from 'react';
|
|
5
|
+
import { useLocation } from 'react-router-dom';
|
|
6
|
+
import { StackAuthSuccess } from './StackAuthSuccess.esm.js';
|
|
7
|
+
import { StackAuthLoading } from './StackAuthLoading.esm.js';
|
|
8
|
+
import { StackAuthFailed } from './StackAuthFailed.esm.js';
|
|
9
|
+
|
|
10
|
+
const StackOverflowCallback = () => {
|
|
11
|
+
const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);
|
|
12
|
+
const location = useLocation();
|
|
13
|
+
const [status, setStatus] = useState("loading");
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const handleCallback = async () => {
|
|
16
|
+
const queryParams = new URLSearchParams(location.search);
|
|
17
|
+
const code = queryParams.get("code");
|
|
18
|
+
const state = queryParams.get("state");
|
|
19
|
+
const isLoggedIn = await stackOverflowTeamsApi.getAuthStatus();
|
|
20
|
+
if (isLoggedIn) {
|
|
21
|
+
setStatus("success");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!code || !state) {
|
|
25
|
+
setStatus("error");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
await stackOverflowTeamsApi.completeAuth(code, state);
|
|
30
|
+
setStatus("success");
|
|
31
|
+
window.history.replaceState({}, document.title, location.pathname);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
setStatus("error");
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
handleCallback();
|
|
37
|
+
}, [location.pathname, location.search, stackOverflowTeamsApi]);
|
|
38
|
+
if (status === "loading") {
|
|
39
|
+
return /* @__PURE__ */ jsx(StackAuthLoading, {});
|
|
40
|
+
}
|
|
41
|
+
if (status === "success") {
|
|
42
|
+
return /* @__PURE__ */ jsx(StackAuthSuccess, {});
|
|
43
|
+
}
|
|
44
|
+
return /* @__PURE__ */ jsx(StackAuthFailed, {});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { StackOverflowCallback };
|
|
48
|
+
//# sourceMappingURL=StackAuthCallback.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StackAuthCallback.esm.js","sources":["../../../src/components/StackOverflowAuth/StackAuthCallback.tsx"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { stackoverflowteamsApiRef } from '../../api';\nimport { useEffect, useState } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { StackAuthSuccess } from './StackAuthSuccess';\nimport { StackAuthLoading } from './StackAuthLoading';\nimport { StackAuthFailed } from './StackAuthFailed';\n\nexport const StackOverflowCallback = () => {\n const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);\n const location = useLocation();\n const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');\n\n useEffect(() => {\n const handleCallback = async () => {\n const queryParams = new URLSearchParams(location.search);\n const code = queryParams.get('code');\n const state = queryParams.get('state');\n const isLoggedIn = await stackOverflowTeamsApi.getAuthStatus();\n\n if (isLoggedIn) {\n setStatus('success')\n return;\n }\n\n if (!code || !state) {\n setStatus('error');\n return;\n }\n\n try {\n await stackOverflowTeamsApi.completeAuth(code, state);\n setStatus('success');\n window.history.replaceState({}, document.title, location.pathname);\n } catch (error) {\n setStatus('error');\n }\n };\n\n handleCallback();\n }, [location.pathname,location.search, stackOverflowTeamsApi]);\n\n if (status === 'loading') {\n return <StackAuthLoading />;\n }\n\n if (status === 'success') {\n return <StackAuthSuccess />;\n }\n\n return <StackAuthFailed />;\n};\n"],"names":[],"mappings":";;;;;;;;;AAQO,MAAM,wBAAwB,MAAM;AACzC,EAAA,MAAM,qBAAA,GAAwB,OAAO,wBAAwB,CAAA;AAC7D,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAA0C,SAAS,CAAA;AAE/E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,iBAAiB,YAAY;AACjC,MAAA,MAAM,WAAA,GAAc,IAAI,eAAA,CAAgB,QAAA,CAAS,MAAM,CAAA;AACvD,MAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,CAAI,MAAM,CAAA;AACnC,MAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AACrC,MAAA,MAAM,UAAA,GAAa,MAAM,qBAAA,CAAsB,aAAA,EAAc;AAE7D,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,SAAA,CAAU,SAAS,CAAA;AACnB,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,EAAO;AACnB,QAAA,SAAA,CAAU,OAAO,CAAA;AACjB,QAAA;AAAA,MACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,qBAAA,CAAsB,YAAA,CAAa,IAAA,EAAM,KAAK,CAAA;AACpD,QAAA,SAAA,CAAU,SAAS,CAAA;AACnB,QAAA,MAAA,CAAO,QAAQ,YAAA,CAAa,IAAI,QAAA,CAAS,KAAA,EAAO,SAAS,QAAQ,CAAA;AAAA,MACnE,SAAS,KAAA,EAAO;AACd,QAAA,SAAA,CAAU,OAAO,CAAA;AAAA,MACnB;AAAA,IACF,CAAA;AAEA,IAAA,cAAA,EAAe;AAAA,EACjB,GAAG,CAAC,QAAA,CAAS,UAAS,QAAA,CAAS,MAAA,EAAQ,qBAAqB,CAAC,CAAA;AAE7D,EAAA,IAAI,WAAW,SAAA,EAAW;AACxB,IAAA,2BAAQ,gBAAA,EAAA,EAAiB,CAAA;AAAA,EAC3B;AAEA,EAAA,IAAI,WAAW,SAAA,EAAW;AACxB,IAAA,2BAAQ,gBAAA,EAAA,EAAiB,CAAA;AAAA,EAC3B;AAEA,EAAA,2BAAQ,eAAA,EAAA,EAAgB,CAAA;AAC1B;;;;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, Typography, Button } from '@mui/material';
|
|
3
|
+
import 'react';
|
|
4
|
+
import '@backstage/core-plugin-api';
|
|
5
|
+
import '../../api/StackOverflowAPI.esm.js';
|
|
6
|
+
import { useStackOverflowStyles } from '../StackOverflow/hooks/useStackOverflowStyles.esm.js';
|
|
7
|
+
import { useNavigate } from 'react-router-dom';
|
|
8
|
+
|
|
9
|
+
const StackAuthFailed = () => {
|
|
10
|
+
const classes = useStackOverflowStyles();
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
13
|
+
Box,
|
|
14
|
+
{
|
|
15
|
+
display: "flex",
|
|
16
|
+
flexDirection: "column",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
justifyContent: "center",
|
|
19
|
+
height: "100vh",
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h5", sx: { color: "#FF6B6B" }, children: "Authentication Failed" }),
|
|
22
|
+
/* @__PURE__ */ jsx(Box, { mt: 1, children: /* @__PURE__ */ jsx(Typography, { variant: "body1", sx: { color: "textSecondary" }, children: "Something went wrong. Please try again." }) }),
|
|
23
|
+
/* @__PURE__ */ jsx(Box, { mt: 1, children: /* @__PURE__ */ jsx(
|
|
24
|
+
Button,
|
|
25
|
+
{
|
|
26
|
+
variant: "contained",
|
|
27
|
+
className: classes.button,
|
|
28
|
+
onClick: () => navigate("/stack-overflow-teams"),
|
|
29
|
+
children: "Return to Stack Internal"
|
|
30
|
+
}
|
|
31
|
+
) })
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { StackAuthFailed };
|
|
38
|
+
//# sourceMappingURL=StackAuthFailed.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StackAuthFailed.esm.js","sources":["../../../src/components/StackOverflowAuth/StackAuthFailed.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { Box, Typography, Button } from '@mui/material';\nimport { useStackOverflowStyles } from '../StackOverflow/hooks'; \nimport { useNavigate } from 'react-router-dom'; \n\nexport const StackAuthFailed = () => {\n const classes = useStackOverflowStyles(); \n const navigate = useNavigate(); \n return (\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n justifyContent=\"center\"\n height=\"100vh\"\n >\n <Typography variant=\"h5\" sx={{ color: '#FF6B6B' }}> \n Authentication Failed\n </Typography>\n <Box mt={1}>\n <Typography variant=\"body1\" sx={{ color: 'textSecondary' }}>\n Something went wrong. Please try again.\n </Typography>\n </Box>\n <Box mt={1}>\n <Button\n variant=\"contained\"\n className={classes.button} \n \n onClick={() => navigate('/stack-overflow-teams')} \n >\n Return to Stack Internal\n </Button>\n </Box>\n </Box>\n );\n};"],"names":[],"mappings":";;;;;;;;AAKO,MAAM,kBAAkB,MAAM;AACnC,EAAA,MAAM,UAAU,sBAAA,EAAuB;AACvC,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,MAAA;AAAA,MACR,aAAA,EAAc,QAAA;AAAA,MACd,UAAA,EAAW,QAAA;AAAA,MACX,cAAA,EAAe,QAAA;AAAA,MACf,MAAA,EAAO,OAAA;AAAA,MAEP,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,IAAA,EAAK,EAAA,EAAI,EAAE,KAAA,EAAO,SAAA,IAAa,QAAA,EAAA,uBAAA,EAEnD,CAAA;AAAA,wBACA,GAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EACT,8BAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAS,EAAA,EAAI,EAAE,KAAA,EAAO,eAAA,EAAgB,EAAG,qDAE7D,CAAA,EACA,CAAA;AAAA,wBACA,GAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EACT,QAAA,kBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,WAAA;AAAA,YACR,WAAW,OAAA,CAAQ,MAAA;AAAA,YAEnB,OAAA,EAAS,MAAM,QAAA,CAAS,uBAAuB,CAAA;AAAA,YAChD,QAAA,EAAA;AAAA;AAAA,SAED,EACA;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Box, CircularProgress, Typography } from '@mui/material';
|
|
3
|
+
|
|
4
|
+
const StackAuthLoading = () => {
|
|
5
|
+
return /* @__PURE__ */ jsxs(
|
|
6
|
+
Box,
|
|
7
|
+
{
|
|
8
|
+
display: "flex",
|
|
9
|
+
flexDirection: "column",
|
|
10
|
+
alignItems: "center",
|
|
11
|
+
justifyContent: "center",
|
|
12
|
+
height: "100vh",
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx(CircularProgress, { sx: { color: " #F48024" } }),
|
|
15
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", mt: 2, children: "Authenticating..." })
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { StackAuthLoading };
|
|
22
|
+
//# sourceMappingURL=StackAuthLoading.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StackAuthLoading.esm.js","sources":["../../../src/components/StackOverflowAuth/StackAuthLoading.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { Box, Typography, CircularProgress } from '@mui/material';\n\nexport const StackAuthLoading = () => {\n\n return (\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n justifyContent=\"center\"\n height=\"100vh\" // Center vertically and horizontally\n >\n <CircularProgress sx={{ color:' #F48024' }} />\n <Typography variant=\"h6\" mt={2} >\n Authenticating...\n </Typography>\n </Box>\n );\n};"],"names":[],"mappings":";;;AAGO,MAAM,mBAAmB,MAAM;AAEpC,EAAA,uBACE,IAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,MAAA;AAAA,MACR,aAAA,EAAc,QAAA;AAAA,MACd,UAAA,EAAW,QAAA;AAAA,MACX,cAAA,EAAe,QAAA;AAAA,MACf,MAAA,EAAO,OAAA;AAAA,MAEP,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,EAAA,EAAI,EAAE,KAAA,EAAM,YAAW,EAAG,CAAA;AAAA,4BAC3C,UAAA,EAAA,EAAW,OAAA,EAAQ,IAAA,EAAK,EAAA,EAAI,GAAI,QAAA,EAAA,mBAAA,EAEjC;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
4
|
+
import { stackoverflowteamsApiRef } from '../../api/StackOverflowAPI.esm.js';
|
|
5
|
+
import Button from '@mui/material/Button';
|
|
6
|
+
import Typography from '@mui/material/Typography';
|
|
7
|
+
import Box from '@mui/material/Box';
|
|
8
|
+
import TextField from '@mui/material/TextField';
|
|
9
|
+
import Paper from '@mui/material/Paper';
|
|
10
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
11
|
+
import IconButton from '@mui/material/IconButton';
|
|
12
|
+
import Snackbar from '@mui/material/Snackbar';
|
|
13
|
+
import Alert from '@mui/material/Alert';
|
|
14
|
+
import Link from '@mui/material/Link';
|
|
15
|
+
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
16
|
+
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
|
17
|
+
import { useStackOverflowStyles } from '../StackOverflow/hooks/useStackOverflowStyles.esm.js';
|
|
18
|
+
import { StackOverflowIcon } from '../../icons/StackOverflowIcon.esm.js';
|
|
19
|
+
|
|
20
|
+
const StackOverflowAuthStart = () => {
|
|
21
|
+
const stackOverflowTeamsApi = useApi(stackoverflowteamsApiRef);
|
|
22
|
+
const classes = useStackOverflowStyles();
|
|
23
|
+
const [authError, setAuthError] = useState(null);
|
|
24
|
+
const [accessToken, setAccessToken] = useState("");
|
|
25
|
+
const [showToken, setShowToken] = useState(false);
|
|
26
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27
|
+
const [tokenSuccess, setTokenSuccess] = useState(false);
|
|
28
|
+
const [teamName, setTeamName] = useState(null);
|
|
29
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const fetchTeamName = async () => {
|
|
32
|
+
try {
|
|
33
|
+
setIsLoading(true);
|
|
34
|
+
const teamNameValue = await stackOverflowTeamsApi.getTeamName();
|
|
35
|
+
setTeamName(teamNameValue);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
setAuthError("Failed to fetch Stack Overflow Internal instance information.");
|
|
38
|
+
} finally {
|
|
39
|
+
setIsLoading(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
fetchTeamName();
|
|
43
|
+
}, [stackOverflowTeamsApi]);
|
|
44
|
+
const isBasicOrBusinessPlan = Boolean(teamName);
|
|
45
|
+
const handleAuth = async () => {
|
|
46
|
+
try {
|
|
47
|
+
setAuthError(null);
|
|
48
|
+
const authUrl = await stackOverflowTeamsApi.startAuth();
|
|
49
|
+
window.location.href = authUrl;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
setAuthError(
|
|
52
|
+
"Something went wrong during authentication. Please try again."
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const handleTokenSubmit = async () => {
|
|
57
|
+
if (!accessToken.trim()) {
|
|
58
|
+
setAuthError("Access token cannot be empty");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setIsSubmitting(true);
|
|
62
|
+
setAuthError(null);
|
|
63
|
+
try {
|
|
64
|
+
const success = await stackOverflowTeamsApi.submitAccessToken(
|
|
65
|
+
accessToken
|
|
66
|
+
);
|
|
67
|
+
if (success) {
|
|
68
|
+
setTokenSuccess(true);
|
|
69
|
+
setAccessToken("");
|
|
70
|
+
window.location.reload();
|
|
71
|
+
} else {
|
|
72
|
+
setAuthError(
|
|
73
|
+
"Failed to validate token. Please check your token and try again."
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
setAuthError("Network error. Please try again.");
|
|
78
|
+
} finally {
|
|
79
|
+
setIsSubmitting(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
if (isLoading) {
|
|
83
|
+
return /* @__PURE__ */ jsx(
|
|
84
|
+
Box,
|
|
85
|
+
{
|
|
86
|
+
display: "flex",
|
|
87
|
+
flexDirection: "column",
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
justifyContent: "center",
|
|
90
|
+
height: "100vh",
|
|
91
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "body1", children: "Loading Stack Overflow Internal configuration..." })
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return /* @__PURE__ */ jsxs(
|
|
96
|
+
Box,
|
|
97
|
+
{
|
|
98
|
+
display: "flex",
|
|
99
|
+
flexDirection: "column",
|
|
100
|
+
alignItems: "center",
|
|
101
|
+
justifyContent: "center",
|
|
102
|
+
height: "100vh",
|
|
103
|
+
bgcolor: "background.default",
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsxs(
|
|
106
|
+
Paper,
|
|
107
|
+
{
|
|
108
|
+
elevation: 3,
|
|
109
|
+
sx: {
|
|
110
|
+
padding: 4,
|
|
111
|
+
width: "100%",
|
|
112
|
+
maxWidth: "500px",
|
|
113
|
+
display: "flex",
|
|
114
|
+
flexDirection: "column",
|
|
115
|
+
alignItems: "center"
|
|
116
|
+
},
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsx(StackOverflowIcon, {}),
|
|
119
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h5", gutterBottom: true, children: "Stack Internal" }),
|
|
120
|
+
!isBasicOrBusinessPlan && /* @__PURE__ */ jsxs(Box, { width: "100%", mb: 3, children: [
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
Typography,
|
|
123
|
+
{
|
|
124
|
+
variant: "body1",
|
|
125
|
+
color: "textSecondary",
|
|
126
|
+
align: "center",
|
|
127
|
+
gutterBottom: true,
|
|
128
|
+
children: "Connect with your Stack Internal Enterprise account"
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
/* @__PURE__ */ jsx(Box, { mt: 2, display: "flex", justifyContent: "center", children: /* @__PURE__ */ jsx(
|
|
132
|
+
Button,
|
|
133
|
+
{
|
|
134
|
+
variant: "contained",
|
|
135
|
+
className: classes.button,
|
|
136
|
+
onClick: handleAuth,
|
|
137
|
+
disabled: isSubmitting,
|
|
138
|
+
fullWidth: true,
|
|
139
|
+
children: "Login with Stack Overflow Internal"
|
|
140
|
+
}
|
|
141
|
+
) })
|
|
142
|
+
] }),
|
|
143
|
+
isBasicOrBusinessPlan && /* @__PURE__ */ jsxs(Box, { width: "100%", children: [
|
|
144
|
+
/* @__PURE__ */ jsx(
|
|
145
|
+
Typography,
|
|
146
|
+
{
|
|
147
|
+
variant: "body1",
|
|
148
|
+
color: "textSecondary",
|
|
149
|
+
align: "center",
|
|
150
|
+
gutterBottom: true,
|
|
151
|
+
children: "Enter your Personal Access Token (PAT)"
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsx(
|
|
155
|
+
Typography,
|
|
156
|
+
{
|
|
157
|
+
variant: "body2",
|
|
158
|
+
color: "textSecondary",
|
|
159
|
+
align: "center",
|
|
160
|
+
gutterBottom: true,
|
|
161
|
+
children: /* @__PURE__ */ jsx(
|
|
162
|
+
Link,
|
|
163
|
+
{
|
|
164
|
+
href: "https://stackoverflowteams.help/en/articles/10908790-personal-access-tokens-pats-for-api-authentication",
|
|
165
|
+
target: "_blank",
|
|
166
|
+
rel: "noopener noreferrer",
|
|
167
|
+
color: "primary",
|
|
168
|
+
children: "Learn how to generate a Personal Access Token"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
/* @__PURE__ */ jsx(
|
|
174
|
+
TextField,
|
|
175
|
+
{
|
|
176
|
+
fullWidth: true,
|
|
177
|
+
label: "Personal Access Token",
|
|
178
|
+
variant: "outlined",
|
|
179
|
+
margin: "normal",
|
|
180
|
+
value: accessToken,
|
|
181
|
+
onChange: (e) => setAccessToken(e.target.value),
|
|
182
|
+
type: showToken ? "text" : "password",
|
|
183
|
+
disabled: isSubmitting,
|
|
184
|
+
InputProps: {
|
|
185
|
+
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx(
|
|
186
|
+
IconButton,
|
|
187
|
+
{
|
|
188
|
+
"aria-label": "toggle token visibility",
|
|
189
|
+
onClick: () => setShowToken(!showToken),
|
|
190
|
+
edge: "end",
|
|
191
|
+
children: showToken ? /* @__PURE__ */ jsx(VisibilityOffIcon, {}) : /* @__PURE__ */ jsx(VisibilityIcon, {})
|
|
192
|
+
}
|
|
193
|
+
) })
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
/* @__PURE__ */ jsx(Box, { mt: 2, children: /* @__PURE__ */ jsx(
|
|
198
|
+
Button,
|
|
199
|
+
{
|
|
200
|
+
variant: "contained",
|
|
201
|
+
color: "primary",
|
|
202
|
+
fullWidth: true,
|
|
203
|
+
onClick: handleTokenSubmit,
|
|
204
|
+
disabled: isSubmitting || !accessToken.trim(),
|
|
205
|
+
className: classes.button,
|
|
206
|
+
children: isSubmitting ? "Validating..." : "Submit Token"
|
|
207
|
+
}
|
|
208
|
+
) })
|
|
209
|
+
] }),
|
|
210
|
+
authError && /* @__PURE__ */ jsx(
|
|
211
|
+
Typography,
|
|
212
|
+
{
|
|
213
|
+
variant: "body2",
|
|
214
|
+
color: "error",
|
|
215
|
+
sx: { maxWidth: "100%", textAlign: "center", mt: 2 },
|
|
216
|
+
children: authError
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
),
|
|
222
|
+
/* @__PURE__ */ jsx(
|
|
223
|
+
Snackbar,
|
|
224
|
+
{
|
|
225
|
+
open: tokenSuccess,
|
|
226
|
+
autoHideDuration: 6e3,
|
|
227
|
+
onClose: () => setTokenSuccess(false),
|
|
228
|
+
anchorOrigin: { vertical: "bottom", horizontal: "center" },
|
|
229
|
+
children: /* @__PURE__ */ jsx(Alert, { severity: "success", variant: "filled", children: "Stack Overflow Internal token accepted successfully!" })
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export { StackOverflowAuthStart };
|
|
238
|
+
//# sourceMappingURL=StackAuthStart.esm.js.map
|