@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.
Files changed (57) hide show
  1. package/dist/api/StackOverflowAPI.esm.js +125 -0
  2. package/dist/api/StackOverflowAPI.esm.js.map +1 -0
  3. package/dist/components/StackOverflow/StackOverflowMe.esm.js +125 -0
  4. package/dist/components/StackOverflow/StackOverflowMe.esm.js.map +1 -0
  5. package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js +803 -0
  6. package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js.map +1 -0
  7. package/dist/components/StackOverflow/StackOverflowPosts.esm.js +444 -0
  8. package/dist/components/StackOverflow/StackOverflowPosts.esm.js.map +1 -0
  9. package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js +175 -0
  10. package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js.map +1 -0
  11. package/dist/components/StackOverflow/StackOverflowTags.esm.js +127 -0
  12. package/dist/components/StackOverflow/StackOverflowTags.esm.js.map +1 -0
  13. package/dist/components/StackOverflow/StackOverflowUsers.esm.js +226 -0
  14. package/dist/components/StackOverflow/StackOverflowUsers.esm.js.map +1 -0
  15. package/dist/components/StackOverflow/TiptapEditor.esm.js +312 -0
  16. package/dist/components/StackOverflow/TiptapEditor.esm.js.map +1 -0
  17. package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js +128 -0
  18. package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js.map +1 -0
  19. package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js +53 -0
  20. package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js.map +1 -0
  21. package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js +39 -0
  22. package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js.map +1 -0
  23. package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js +48 -0
  24. package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js.map +1 -0
  25. package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js +38 -0
  26. package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js.map +1 -0
  27. package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js +22 -0
  28. package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js.map +1 -0
  29. package/dist/components/StackOverflowAuth/StackAuthStart.esm.js +238 -0
  30. package/dist/components/StackOverflowAuth/StackAuthStart.esm.js.map +1 -0
  31. package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js +40 -0
  32. package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js.map +1 -0
  33. package/dist/icons/LogoutIcon.esm.js +24 -0
  34. package/dist/icons/LogoutIcon.esm.js.map +1 -0
  35. package/dist/icons/StackOverflowIcon.esm.js +27 -0
  36. package/dist/icons/StackOverflowIcon.esm.js.map +1 -0
  37. package/dist/index.d.ts +47 -0
  38. package/dist/index.esm.js +19 -0
  39. package/dist/index.esm.js.map +1 -0
  40. package/dist/package.json.esm.js +6 -0
  41. package/dist/package.json.esm.js.map +1 -0
  42. package/dist/pages/StackOverflowHub.esm.js +138 -0
  43. package/dist/pages/StackOverflowHub.esm.js.map +1 -0
  44. package/dist/pages/StackOverflowTeamsPage.esm.js +43 -0
  45. package/dist/pages/StackOverflowTeamsPage.esm.js.map +1 -0
  46. package/dist/pages/index.esm.js +3 -0
  47. package/dist/pages/index.esm.js.map +1 -0
  48. package/dist/plugin.esm.js +27 -0
  49. package/dist/plugin.esm.js.map +1 -0
  50. package/dist/routes.esm.js +8 -0
  51. package/dist/routes.esm.js.map +1 -0
  52. package/dist/utils/decodeHtml.esm.js +8 -0
  53. package/dist/utils/decodeHtml.esm.js.map +1 -0
  54. package/dist/utils/getTimeAgo.esm.js +30 -0
  55. package/dist/utils/getTimeAgo.esm.js.map +1 -0
  56. package/package.json +12 -4
  57. 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