@stackoverflow/backstage-plugin-stack-overflow-teams 1.6.1 → 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 (56) 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 +13 -7
@@ -0,0 +1,803 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { useState, useCallback, useMemo, useEffect } from 'react';
3
+ import { useApi } from '@backstage/core-plugin-api';
4
+ import Chip from '@mui/material/Chip';
5
+ import { stackoverflowteamsApiRef } from '../../api/StackOverflowAPI.esm.js';
6
+ import Modal from '@mui/material/Modal';
7
+ import Box from '@mui/material/Box';
8
+ import TextField from '@mui/material/TextField';
9
+ import Button from '@mui/material/Button';
10
+ import Typography from '@mui/material/Typography';
11
+ import Link from '@mui/material/Link';
12
+ import Paper from '@mui/material/Paper';
13
+ import Alert from '@mui/material/Alert';
14
+ import AlertTitle from '@mui/material/AlertTitle';
15
+ import List from '@mui/material/List';
16
+ import ListItem from '@mui/material/ListItem';
17
+ import ListItemIcon from '@mui/material/ListItemIcon';
18
+ import ListItemText from '@mui/material/ListItemText';
19
+ import Grid from '@mui/material/Grid';
20
+ import Card from '@mui/material/Card';
21
+ import CardContent from '@mui/material/CardContent';
22
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
23
+ import InfoIcon from '@mui/icons-material/Info';
24
+ import LightbulbIcon from '@mui/icons-material/Lightbulb';
25
+ import CodeIcon from '@mui/icons-material/Code';
26
+ import TitleIcon from '@mui/icons-material/Title';
27
+ import DescriptionIcon from '@mui/icons-material/Description';
28
+ import LocalOfferIcon from '@mui/icons-material/LocalOffer';
29
+ import GroupIcon from '@mui/icons-material/Group';
30
+ import PersonIcon from '@mui/icons-material/Person';
31
+ import CloseIcon from '@mui/icons-material/Close';
32
+ import IconButton from '@mui/material/IconButton';
33
+ import { useStackOverflowStyles } from './hooks/useStackOverflowStyles.esm.js';
34
+ import { TiptapEditor } from './TiptapEditor.esm.js';
35
+ import CircularProgress from '@mui/material/CircularProgress';
36
+ import { debounce } from '@material-ui/core';
37
+ import Divider from '@mui/material/Divider';
38
+
39
+ const isMac = () => {
40
+ return typeof navigator !== "undefined" && navigator.platform.toUpperCase().indexOf("MAC") >= 0;
41
+ };
42
+ const getModifierKey = () => {
43
+ const isApple = isMac();
44
+ return {
45
+ symbol: isApple ? "\u2318" : "Ctrl",
46
+ text: isApple ? "Cmd" : "Ctrl"
47
+ };
48
+ };
49
+ const StackOverflowPostQuestionModal = () => {
50
+ const stackOverflowApi = useApi(stackoverflowteamsApiRef);
51
+ const [title, setTitle] = useState("");
52
+ const [body, setBody] = useState("");
53
+ const [tags, setTags] = useState([]);
54
+ const [tagInput, setTagInput] = useState("");
55
+ const [focusedField, setFocusedField] = useState(null);
56
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
57
+ const [loading, setLoading] = useState(false);
58
+ const [error, setError] = useState(null);
59
+ const [success, setSuccess] = useState(false);
60
+ const [open, setOpen] = useState(false);
61
+ const [titleValidation, setTitleValidation] = useState("");
62
+ const [bodyValidation, setBodyValidation] = useState("");
63
+ const [tagsValidation, setTagsValidation] = useState("");
64
+ const classes = useStackOverflowStyles();
65
+ const [titleStarted, setTitleStarted] = useState(false);
66
+ const [bodyStarted, setBodyStarted] = useState(false);
67
+ const [tagsStarted, setTagsStarted] = useState(false);
68
+ const [popularTags, setPopularTags] = useState([]);
69
+ const [loadingTags, setLoadingTags] = useState(false);
70
+ const [tagError, setTagError] = useState(null);
71
+ const [tagSearchResults, setTagSearchResults] = useState([]);
72
+ const [searchingTags, setSearchingTags] = useState(false);
73
+ const [showCreateTagOption, setShowCreateTagOption] = useState(false);
74
+ const fetchPopularTags = useCallback(async function fetchPopularTags2() {
75
+ if (!isAuthenticated) return;
76
+ setLoadingTags(true);
77
+ setTagError(null);
78
+ try {
79
+ const response = await stackOverflowApi.getTags();
80
+ const topTags = response.items?.slice(0, 10) || [];
81
+ setPopularTags(topTags);
82
+ } catch (err) {
83
+ setTagError("Failed to load tags.");
84
+ setPopularTags([]);
85
+ } finally {
86
+ setLoadingTags(false);
87
+ }
88
+ }, [stackOverflowApi, isAuthenticated]);
89
+ const searchTags = useMemo(
90
+ () => debounce(async (searchTerm) => {
91
+ if (!searchTerm.trim() || !isAuthenticated) {
92
+ setTagSearchResults([]);
93
+ setShowCreateTagOption(false);
94
+ return;
95
+ }
96
+ setSearchingTags(true);
97
+ try {
98
+ const response = await stackOverflowApi.getTags(searchTerm.trim());
99
+ const results = response.items || [];
100
+ setTagSearchResults(results);
101
+ setShowCreateTagOption(results.length === 0);
102
+ } catch (err) {
103
+ setTagSearchResults([]);
104
+ setShowCreateTagOption(true);
105
+ } finally {
106
+ setSearchingTags(false);
107
+ }
108
+ }, 500),
109
+ [stackOverflowApi, isAuthenticated]
110
+ );
111
+ const modifierKey = getModifierKey();
112
+ function validateTitle(value) {
113
+ if (titleStarted && value.trim().length < 15) {
114
+ setTitleValidation("Title should be at least 15 characters for clarity.");
115
+ } else {
116
+ setTitleValidation("");
117
+ }
118
+ }
119
+ const validateBody = useCallback(function validateBody2(value) {
120
+ const textContent = value.replace(/<[^>]*>/g, "");
121
+ if (bodyStarted && textContent.trim().length < 30) {
122
+ setBodyValidation("Please provide more detail (minimum 30 characters).");
123
+ } else {
124
+ setBodyValidation("");
125
+ }
126
+ }, [bodyStarted]);
127
+ const validateTags = useCallback(function validateTags2() {
128
+ if (tagsStarted && tags.length === 0) {
129
+ setTagsValidation("At least one tag is required.");
130
+ } else {
131
+ setTagsValidation("");
132
+ }
133
+ }, [tagsStarted, tags]);
134
+ useEffect(() => {
135
+ const openModal = async () => {
136
+ const authStatus = await stackOverflowApi.getAuthStatus();
137
+ setIsAuthenticated(authStatus);
138
+ setSuccess(false);
139
+ setOpen(true);
140
+ };
141
+ window.addEventListener("openAskQuestionModal", openModal);
142
+ return () => {
143
+ window.removeEventListener("openAskQuestionModal", openModal);
144
+ };
145
+ }, [stackOverflowApi]);
146
+ useEffect(() => {
147
+ if (open && isAuthenticated && popularTags.length === 0) {
148
+ fetchPopularTags();
149
+ }
150
+ }, [open, isAuthenticated, fetchPopularTags, popularTags.length]);
151
+ useEffect(() => {
152
+ validateTags();
153
+ }, [tags, tagsStarted, validateTags]);
154
+ useEffect(() => {
155
+ validateBody(body);
156
+ }, [body, bodyStarted, validateBody]);
157
+ const handleSubmit = async () => {
158
+ validateTitle(title);
159
+ validateBody(body);
160
+ validateTags();
161
+ if (titleValidation || bodyValidation || tagsValidation) {
162
+ return;
163
+ }
164
+ const textContent = body.replace(/<[^>]*>/g, "").trim();
165
+ if (!title || !textContent || tags.length === 0) {
166
+ setError("Title, body, and at least one tag are required.");
167
+ return;
168
+ }
169
+ setLoading(true);
170
+ setError(null);
171
+ try {
172
+ const response = await stackOverflowApi.postQuestion(title, body, tags);
173
+ setSuccess(true);
174
+ setTitle("");
175
+ setBody("");
176
+ setTags([]);
177
+ setTagInput("");
178
+ if (response.webUrl) {
179
+ window.open(`${response.webUrl}?r=Backstage_Plugin`, "_blank");
180
+ }
181
+ } catch (err) {
182
+ if (err instanceof Error) {
183
+ setError(err.message);
184
+ } else {
185
+ setError("An unexpected error occurred.");
186
+ }
187
+ } finally {
188
+ setLoading(false);
189
+ }
190
+ };
191
+ const handleTagAdd = () => {
192
+ const newTags = tagInput.split(/[\s,]+/).map((tag) => tag.trim()).filter((tag) => tag.length > 0 && !tags.includes(tag));
193
+ if (newTags.length > 0 && tags.length + newTags.length <= 5) {
194
+ setTags([...tags, ...newTags]);
195
+ if (!tagsStarted) setTagsStarted(true);
196
+ }
197
+ setTagInput("");
198
+ setTagSearchResults([]);
199
+ };
200
+ const handleLoginRedirect = () => {
201
+ setOpen(false);
202
+ window.location.href = "/stack-overflow-teams";
203
+ };
204
+ const handleBodyChange = (value) => {
205
+ if (!bodyStarted) setBodyStarted(true);
206
+ setBody(value);
207
+ };
208
+ const handleBodyFocus = () => {
209
+ setFocusedField("body");
210
+ };
211
+ const renderTitleTips = () => /* @__PURE__ */ jsxs(Box, { children: [
212
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 2 }, children: [
213
+ /* @__PURE__ */ jsx(TitleIcon, { color: "primary" }),
214
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Writing a Good Title" })
215
+ ] }),
216
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { mb: 2, border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
217
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1.5, color: "success.main" }, children: "\u2713 Good Examples" }),
218
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 1 }, children: [
219
+ /* @__PURE__ */ jsx(Paper, { elevation: 0, sx: { p: 1.5, bgcolor: "success.50", border: "1px solid", borderColor: "success.200" }, children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: '"How to handle async errors in React useEffect hook?"' }) }),
220
+ /* @__PURE__ */ jsx(Paper, { elevation: 0, sx: { p: 1.5, bgcolor: "success.50", border: "1px solid", borderColor: "success.200" }, children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: '"Why does my Docker container fail to connect to PostgreSQL?"' }) })
221
+ ] }),
222
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mt: 2, mb: 1.5, color: "error.main" }, children: "\u2717 Avoid" }),
223
+ /* @__PURE__ */ jsx(Paper, { elevation: 0, sx: { p: 1.5, bgcolor: "error.50", border: "1px solid", borderColor: "error.200" }, children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: `"Help! My code doesn't work!"` }) })
224
+ ] }) }),
225
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
226
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1 }, children: "Tips for Success" }),
227
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
228
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
229
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
230
+ /* @__PURE__ */ jsx(
231
+ ListItemText,
232
+ {
233
+ primary: "Be specific about your problem",
234
+ primaryTypographyProps: { variant: "body2" }
235
+ }
236
+ )
237
+ ] }),
238
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
239
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
240
+ /* @__PURE__ */ jsx(
241
+ ListItemText,
242
+ {
243
+ primary: "Include relevant technologies",
244
+ primaryTypographyProps: { variant: "body2" }
245
+ }
246
+ )
247
+ ] }),
248
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
249
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
250
+ /* @__PURE__ */ jsx(
251
+ ListItemText,
252
+ {
253
+ primary: "Avoid vague terms like 'doesn't work'",
254
+ primaryTypographyProps: { variant: "body2" }
255
+ }
256
+ )
257
+ ] })
258
+ ] })
259
+ ] }) })
260
+ ] });
261
+ const renderBodyTips = () => /* @__PURE__ */ jsxs(Box, { children: [
262
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 2 }, children: [
263
+ /* @__PURE__ */ jsx(DescriptionIcon, { color: "primary" }),
264
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Detailed Explanation" })
265
+ ] }),
266
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { mb: 2, border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
267
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
268
+ /* @__PURE__ */ jsx(CodeIcon, { color: "info", fontSize: "small" }),
269
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, children: "Rich Text Formatting" })
270
+ ] }),
271
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
272
+ "Use the toolbar for bold, italic, code blocks, and lists. Shortcuts: ",
273
+ modifierKey.text,
274
+ "+B (bold), ",
275
+ modifierKey.text,
276
+ "+I (italic), ",
277
+ modifierKey.text,
278
+ "+U (underline), ",
279
+ modifierKey.text,
280
+ "+E (code)."
281
+ ] })
282
+ ] }) }),
283
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
284
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1 }, children: "Structure Your Question" }),
285
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
286
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
287
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
288
+ /* @__PURE__ */ jsx(
289
+ ListItemText,
290
+ {
291
+ primary: "What you're trying to achieve",
292
+ primaryTypographyProps: { variant: "body2" }
293
+ }
294
+ )
295
+ ] }),
296
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
297
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
298
+ /* @__PURE__ */ jsx(
299
+ ListItemText,
300
+ {
301
+ primary: "What you've tried so far",
302
+ primaryTypographyProps: { variant: "body2" }
303
+ }
304
+ )
305
+ ] }),
306
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
307
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
308
+ /* @__PURE__ */ jsx(
309
+ ListItemText,
310
+ {
311
+ primary: "Expected vs actual results",
312
+ primaryTypographyProps: { variant: "body2" }
313
+ }
314
+ )
315
+ ] }),
316
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
317
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
318
+ /* @__PURE__ */ jsx(
319
+ ListItemText,
320
+ {
321
+ primary: "Error messages (if any)",
322
+ primaryTypographyProps: { variant: "body2" }
323
+ }
324
+ )
325
+ ] })
326
+ ] })
327
+ ] }) })
328
+ ] });
329
+ const renderTagsTips = () => /* @__PURE__ */ jsxs(Box, { children: [
330
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 2 }, children: [
331
+ /* @__PURE__ */ jsx(LocalOfferIcon, { color: "primary" }),
332
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Choosing Tags" })
333
+ ] }),
334
+ (loadingTags || popularTags.length > 0 || tagError) && /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { mb: 2, border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
335
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1.5 }, children: "Popular Tags" }),
336
+ loadingTags && /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "center", alignItems: "center", py: 2 }, children: [
337
+ /* @__PURE__ */ jsx(CircularProgress, { size: 20 }),
338
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { ml: 1 }, color: "text.secondary", children: "Loading..." })
339
+ ] }),
340
+ !loadingTags && tagError && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "error", children: tagError }),
341
+ !loadingTags && !tagError && popularTags.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
342
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexWrap: "wrap", gap: 0.75 }, children: popularTags.map((tag) => /* @__PURE__ */ jsx(
343
+ Chip,
344
+ {
345
+ label: tag.name,
346
+ size: "small",
347
+ variant: "outlined",
348
+ onClick: () => !tags.includes(tag.name) && tags.length < 5 && setTags([...tags, tag.name]),
349
+ disabled: tags.includes(tag.name) || tags.length >= 5,
350
+ sx: {
351
+ cursor: tags.includes(tag.name) || tags.length >= 5 ? "default" : "pointer",
352
+ "&:hover": {
353
+ bgcolor: tags.includes(tag.name) || tags.length >= 5 ? "transparent" : "action.hover"
354
+ }
355
+ }
356
+ },
357
+ tag.name
358
+ )) }),
359
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { mt: 1.5, display: "block" }, children: "Click to add popular tags quickly" })
360
+ ] })
361
+ ] }) }),
362
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
363
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1 }, children: "Tag Guidelines" }),
364
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
365
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
366
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
367
+ /* @__PURE__ */ jsx(
368
+ ListItemText,
369
+ {
370
+ primary: "Use 1-5 tags that describe your question",
371
+ primaryTypographyProps: { variant: "body2" }
372
+ }
373
+ )
374
+ ] }),
375
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
376
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
377
+ /* @__PURE__ */ jsx(
378
+ ListItemText,
379
+ {
380
+ primary: "Try to use existing tags",
381
+ primaryTypographyProps: { variant: "body2" }
382
+ }
383
+ )
384
+ ] }),
385
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
386
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
387
+ /* @__PURE__ */ jsx(
388
+ ListItemText,
389
+ {
390
+ primary: "Add relevant tools and platforms",
391
+ primaryTypographyProps: { variant: "body2" }
392
+ }
393
+ )
394
+ ] })
395
+ ] })
396
+ ] }) })
397
+ ] });
398
+ const renderMentionTips = () => /* @__PURE__ */ jsxs(Box, { children: [
399
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 2 }, children: [
400
+ /* @__PURE__ */ jsx(GroupIcon, { color: "primary" }),
401
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Asking Team Members" })
402
+ ] }),
403
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { mb: 2, border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
404
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 1 }, children: [
405
+ /* @__PURE__ */ jsx(PersonIcon, { color: "info", fontSize: "small" }),
406
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, children: "When to Mention Someone" })
407
+ ] }),
408
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
409
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
410
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
411
+ /* @__PURE__ */ jsx(
412
+ ListItemText,
413
+ {
414
+ primary: "They're an expert in the relevant area",
415
+ primaryTypographyProps: { variant: "body2" }
416
+ }
417
+ )
418
+ ] }),
419
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
420
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
421
+ /* @__PURE__ */ jsx(
422
+ ListItemText,
423
+ {
424
+ primary: "They've worked on similar problems",
425
+ primaryTypographyProps: { variant: "body2" }
426
+ }
427
+ )
428
+ ] }),
429
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
430
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
431
+ /* @__PURE__ */ jsx(
432
+ ListItemText,
433
+ {
434
+ primary: "They're the owner of the code in question",
435
+ primaryTypographyProps: { variant: "body2" }
436
+ }
437
+ )
438
+ ] })
439
+ ] })
440
+ ] }) }),
441
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
442
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1 }, children: "Mention Guidelines" }),
443
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Type usernames or group names. You can mention:" }),
444
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
445
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
446
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
447
+ /* @__PURE__ */ jsx(
448
+ ListItemText,
449
+ {
450
+ primary: "Individual team members (@john.doe)",
451
+ primaryTypographyProps: { variant: "body2" }
452
+ }
453
+ )
454
+ ] }),
455
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
456
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(InfoIcon, { color: "info", fontSize: "small" }) }),
457
+ /* @__PURE__ */ jsx(
458
+ ListItemText,
459
+ {
460
+ primary: "Team groups (@frontend-team)",
461
+ primaryTypographyProps: { variant: "body2" }
462
+ }
463
+ )
464
+ ] })
465
+ ] })
466
+ ] }) })
467
+ ] });
468
+ const renderDefaultTips = () => /* @__PURE__ */ jsxs(Box, { children: [
469
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1, mb: 2 }, children: [
470
+ /* @__PURE__ */ jsx(LightbulbIcon, { color: "primary" }),
471
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Writing a Good Question" })
472
+ ] }),
473
+ /* @__PURE__ */ jsx(Card, { elevation: 0, sx: { border: "1px solid", borderColor: "divider" }, children: /* @__PURE__ */ jsxs(CardContent, { sx: { "&:last-child": { pb: 2 } }, children: [
474
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", fontWeight: 600, sx: { mb: 1.5 }, children: "Quick Tips" }),
475
+ /* @__PURE__ */ jsxs(List, { dense: true, disablePadding: true, children: [
476
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
477
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
478
+ /* @__PURE__ */ jsx(
479
+ ListItemText,
480
+ {
481
+ primary: "Be specific and clear in your title",
482
+ primaryTypographyProps: { variant: "body2" }
483
+ }
484
+ )
485
+ ] }),
486
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
487
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
488
+ /* @__PURE__ */ jsx(
489
+ ListItemText,
490
+ {
491
+ primary: "Include relevant code and error messages",
492
+ primaryTypographyProps: { variant: "body2" }
493
+ }
494
+ )
495
+ ] }),
496
+ /* @__PURE__ */ jsxs(ListItem, { sx: { py: 0.5, px: 0 }, children: [
497
+ /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(CheckCircleIcon, { color: "success", fontSize: "small" }) }),
498
+ /* @__PURE__ */ jsx(
499
+ ListItemText,
500
+ {
501
+ primary: "Tag your question appropriately",
502
+ primaryTypographyProps: { variant: "body2" }
503
+ }
504
+ )
505
+ ] })
506
+ ] })
507
+ ] }) })
508
+ ] });
509
+ const renderRightPanel = () => {
510
+ switch (focusedField) {
511
+ case "title":
512
+ return renderTitleTips();
513
+ case "body":
514
+ return renderBodyTips();
515
+ case "tags":
516
+ return renderTagsTips();
517
+ case "mentions":
518
+ return renderMentionTips();
519
+ default:
520
+ return renderDefaultTips();
521
+ }
522
+ };
523
+ const renderQuestionForm = () => /* @__PURE__ */ jsxs(Box, { sx: { m: 4 }, children: [
524
+ /* @__PURE__ */ jsxs(Box, { sx: { mb: 3 }, children: [
525
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", fontWeight: 600, sx: { mb: 0.5 }, children: "Title" }),
526
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1.5 }, children: "Be specific and imagine you're asking a question to another person." }),
527
+ /* @__PURE__ */ jsx(
528
+ TextField,
529
+ {
530
+ fullWidth: true,
531
+ variant: "outlined",
532
+ value: title,
533
+ onChange: (e) => {
534
+ if (!titleStarted) setTitleStarted(true);
535
+ setTitle(e.target.value);
536
+ validateTitle(e.target.value);
537
+ },
538
+ onFocus: () => setFocusedField("title"),
539
+ error: titleStarted && !!titleValidation,
540
+ helperText: titleStarted ? titleValidation : "",
541
+ placeholder: "e.g., How to handle authentication in React components?",
542
+ size: "small"
543
+ }
544
+ )
545
+ ] }),
546
+ /* @__PURE__ */ jsxs(Box, { sx: { mb: 3 }, children: [
547
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", fontWeight: 600, sx: { mb: 0.5 }, children: "What are the details of your problem?" }),
548
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1.5 }, children: "Introduce the problem and expand on what you put in the title." }),
549
+ /* @__PURE__ */ jsx(
550
+ TiptapEditor,
551
+ {
552
+ content: body,
553
+ onUpdate: handleBodyChange,
554
+ onFocus: handleBodyFocus,
555
+ placeholder: "Describe your problem in detail. Include any error messages, code snippets, and what you've tried so far...",
556
+ error: bodyStarted && !!bodyValidation,
557
+ modifierKey
558
+ }
559
+ ),
560
+ bodyStarted && bodyValidation && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", sx: { mt: 1, display: "block" }, children: bodyValidation })
561
+ ] }),
562
+ /* @__PURE__ */ jsxs(Box, { sx: { mb: 3, position: "relative" }, children: [
563
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle1", fontWeight: 600, sx: { mb: 0.5 }, children: "Tags" }),
564
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 1.5 }, children: "At least one tag is required." }),
565
+ /* @__PURE__ */ jsx(
566
+ TextField,
567
+ {
568
+ fullWidth: true,
569
+ variant: "outlined",
570
+ value: tagInput,
571
+ onChange: (e) => {
572
+ const value = e.target.value;
573
+ setTagInput(value);
574
+ setShowCreateTagOption(false);
575
+ const lastTag = value.split(/[\s,]/).pop()?.trim() || "";
576
+ if (lastTag.length >= 2) {
577
+ searchTags(lastTag);
578
+ } else {
579
+ setTagSearchResults([]);
580
+ }
581
+ if (value.includes(",") || value.includes(" ")) {
582
+ handleTagAdd();
583
+ }
584
+ },
585
+ onFocus: () => setFocusedField("tags"),
586
+ onKeyDown: (e) => e.key === "Enter" && handleTagAdd(),
587
+ placeholder: "e.g., react, javascript, authentication",
588
+ error: !!tagsValidation,
589
+ size: "small"
590
+ }
591
+ ),
592
+ (tagSearchResults.length > 0 || searchingTags || tagInput.trim() && tagSearchResults.length === 0 && !searchingTags) && /* @__PURE__ */ jsxs(
593
+ Paper,
594
+ {
595
+ elevation: 3,
596
+ sx: {
597
+ position: "absolute",
598
+ zIndex: 1e3,
599
+ width: "100%",
600
+ maxHeight: 200,
601
+ overflow: "auto",
602
+ mt: 0.5
603
+ },
604
+ children: [
605
+ searchingTags && /* @__PURE__ */ jsxs(Box, { sx: { p: 2, display: "flex", alignItems: "center", gap: 1 }, children: [
606
+ /* @__PURE__ */ jsx(CircularProgress, { size: 16 }),
607
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: "Searching tags..." })
608
+ ] }),
609
+ tagSearchResults.map((tag) => /* @__PURE__ */ jsx(
610
+ ListItem,
611
+ {
612
+ onClick: () => {
613
+ if (!tags.includes(tag.name) && tags.length < 5) {
614
+ setTags([...tags, tag.name]);
615
+ if (!tagsStarted) setTagsStarted(true);
616
+ }
617
+ setTagInput("");
618
+ setTagSearchResults([]);
619
+ },
620
+ sx: {
621
+ cursor: "pointer",
622
+ py: 1,
623
+ "&:hover": { backgroundColor: "action.hover" }
624
+ },
625
+ children: /* @__PURE__ */ jsx(
626
+ ListItemText,
627
+ {
628
+ primary: tag.name,
629
+ secondary: `${tag.postCount} posts`,
630
+ primaryTypographyProps: { variant: "body2" },
631
+ secondaryTypographyProps: { variant: "caption" }
632
+ }
633
+ )
634
+ },
635
+ tag.name
636
+ )),
637
+ tagInput.trim() && showCreateTagOption && /* @__PURE__ */ jsx(
638
+ ListItem,
639
+ {
640
+ onClick: () => {
641
+ const trimmedTag = tagInput.trim();
642
+ if (trimmedTag && !tags.includes(trimmedTag) && tags.length < 5) {
643
+ setTags([...tags, trimmedTag]);
644
+ if (!tagsStarted) setTagsStarted(true);
645
+ setShowCreateTagOption(false);
646
+ }
647
+ setTagInput("");
648
+ setTagSearchResults([]);
649
+ },
650
+ sx: {
651
+ cursor: "pointer",
652
+ py: 1,
653
+ "&:hover": { backgroundColor: "action.hover" },
654
+ borderTop: tagSearchResults.length > 0 ? "1px solid" : "none",
655
+ borderColor: "divider"
656
+ },
657
+ children: /* @__PURE__ */ jsx(
658
+ ListItemText,
659
+ {
660
+ primary: /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
661
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
662
+ 'Create "',
663
+ tagInput.trim(),
664
+ '"'
665
+ ] }),
666
+ /* @__PURE__ */ jsx(Chip, { size: "small", label: "New", color: "primary", variant: "outlined" })
667
+ ] }),
668
+ secondary: "This will create a new tag",
669
+ secondaryTypographyProps: { variant: "caption" }
670
+ }
671
+ )
672
+ }
673
+ )
674
+ ]
675
+ }
676
+ ),
677
+ tags.length > 0 && /* @__PURE__ */ jsx(Box, { sx: { display: "flex", flexWrap: "wrap", gap: 0.75, mt: 1.5 }, children: tags.map((tag, index) => /* @__PURE__ */ jsx(
678
+ Chip,
679
+ {
680
+ label: tag,
681
+ onDelete: () => setTags(tags.filter((t) => t !== tag)),
682
+ size: "small",
683
+ variant: "outlined",
684
+ color: "primary"
685
+ },
686
+ index
687
+ )) }),
688
+ tagsValidation && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", sx: { mt: 0.5, display: "block" }, children: tagsValidation })
689
+ ] }),
690
+ error && /* @__PURE__ */ jsx(Alert, { severity: "error", sx: { mb: 2 }, children: error }),
691
+ /* @__PURE__ */ jsx(Divider, { sx: { my: 3 } }),
692
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", gap: 2 }, children: [
693
+ /* @__PURE__ */ jsx(
694
+ Button,
695
+ {
696
+ variant: "contained",
697
+ className: classes.button,
698
+ size: "medium",
699
+ onClick: handleSubmit,
700
+ disabled: loading || !isAuthenticated,
701
+ sx: { minWidth: 140 },
702
+ children: loading ? "Posting..." : "Post Question"
703
+ }
704
+ ),
705
+ /* @__PURE__ */ jsx(
706
+ Button,
707
+ {
708
+ onClick: () => setOpen(false),
709
+ variant: "outlined",
710
+ size: "medium",
711
+ children: "Cancel"
712
+ }
713
+ )
714
+ ] })
715
+ ] });
716
+ const renderContent = () => {
717
+ if (!isAuthenticated) {
718
+ return /* @__PURE__ */ jsxs(Box, { sx: { textAlign: "center", py: 6 }, children: [
719
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", color: "text.primary", sx: { mb: 1 }, children: "Authentication Required" }),
720
+ /* @__PURE__ */ jsxs(Typography, { color: "text.secondary", sx: { mb: 3 }, children: [
721
+ "Please",
722
+ " ",
723
+ /* @__PURE__ */ jsx(Link, { component: "button", onClick: handleLoginRedirect, sx: { cursor: "pointer" }, children: "log in" }),
724
+ " ",
725
+ "to post questions."
726
+ ] })
727
+ ] });
728
+ }
729
+ if (success) {
730
+ return /* @__PURE__ */ jsx(Box, { sx: { py: 4 }, children: /* @__PURE__ */ jsxs(Alert, { severity: "success", children: [
731
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Question Posted Successfully!" }),
732
+ "Your question has been posted and will open in a new tab."
733
+ ] }) });
734
+ }
735
+ return /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
736
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 8, children: /* @__PURE__ */ jsx(Box, { sx: { pr: { md: 2 } }, children: renderQuestionForm() }) }),
737
+ /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 4, children: /* @__PURE__ */ jsx(
738
+ Box,
739
+ {
740
+ sx: {
741
+ position: { md: "sticky" },
742
+ top: { md: 24 },
743
+ pl: { md: 2 },
744
+ borderLeft: { md: "1px solid" },
745
+ borderColor: { md: "divider" }
746
+ },
747
+ children: renderRightPanel()
748
+ }
749
+ ) })
750
+ ] });
751
+ };
752
+ return /* @__PURE__ */ jsx(Modal, { open, onClose: () => setOpen(false), children: /* @__PURE__ */ jsxs(
753
+ Box,
754
+ {
755
+ sx: {
756
+ position: "absolute",
757
+ top: "50%",
758
+ left: "50%",
759
+ transform: "translate(-50%, -50%)",
760
+ width: success ? { xs: "90vw", sm: "500px" } : { xs: "95vw", sm: "90vw", md: "85vw", lg: "80vw", xl: "75vw" },
761
+ maxWidth: success ? "500px" : "1400px",
762
+ maxHeight: "90vh",
763
+ bgcolor: "background.paper",
764
+ boxShadow: 24,
765
+ borderRadius: 2,
766
+ overflow: "hidden",
767
+ display: "flex",
768
+ flexDirection: "column"
769
+ },
770
+ children: [
771
+ /* @__PURE__ */ jsxs(
772
+ Box,
773
+ {
774
+ sx: {
775
+ p: 2.5,
776
+ borderBottom: "1px solid",
777
+ borderColor: "divider",
778
+ display: "flex",
779
+ justifyContent: "space-between",
780
+ alignItems: "center"
781
+ },
782
+ children: [
783
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", fontWeight: 600, children: "Ask a Question" }),
784
+ /* @__PURE__ */ jsx(
785
+ IconButton,
786
+ {
787
+ onClick: () => setOpen(false),
788
+ size: "small",
789
+ sx: { ml: 2 },
790
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
791
+ }
792
+ )
793
+ ]
794
+ }
795
+ ),
796
+ /* @__PURE__ */ jsx(Box, { sx: { p: 3, overflow: "auto", flexGrow: 1 }, children: renderContent() })
797
+ ]
798
+ }
799
+ ) });
800
+ };
801
+
802
+ export { StackOverflowPostQuestionModal };
803
+ //# sourceMappingURL=StackOverflowPostQuestionModal.esm.js.map