@avament-pub/support-navbar-create 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
- # Temporary Holding Version
1
+ # `@avament-pub/support-navbar-create`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Lightweight support ticket create dialog for module navbars (`onSupportClick` on `@avament-pub/navbar-ui`).
4
+
5
+ The Support app should use its full `CreateTicketDialog` instead. Other modules pass `getAccessToken` and `apiBaseUrl` (same auth as the host app).
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+
3
+ type SupportNavbarCreateDialogLabels = {
4
+ title: string;
5
+ shortLabel: string;
6
+ detailsLabel: string;
7
+ submit: string;
8
+ cancel: string;
9
+ shortRequired: string;
10
+ submitError: string;
11
+ };
12
+ type SupportNavbarCreateDialogProps = {
13
+ open: boolean;
14
+ onOpenChange: (open: boolean) => void;
15
+ apiBaseUrl: string;
16
+ getAccessToken: () => string | null;
17
+ labels: SupportNavbarCreateDialogLabels;
18
+ };
19
+ declare function SupportNavbarCreateDialog({ open, onOpenChange, apiBaseUrl, getAccessToken, labels, }: SupportNavbarCreateDialogProps): React.JSX.Element | null;
20
+
21
+ type UseSupportNavbarCreateTicketOptions = {
22
+ apiBaseUrl: string;
23
+ getAccessToken: () => string | null;
24
+ labels: SupportNavbarCreateDialogLabels;
25
+ };
26
+ declare function useSupportNavbarCreateTicket({ apiBaseUrl, getAccessToken, labels, }: UseSupportNavbarCreateTicketOptions): {
27
+ onSupportClick: () => void;
28
+ dialog: React.JSX.Element;
29
+ open: boolean;
30
+ setOpen: React.Dispatch<React.SetStateAction<boolean>>;
31
+ };
32
+
33
+ export { SupportNavbarCreateDialog, type SupportNavbarCreateDialogLabels, type SupportNavbarCreateDialogProps, useSupportNavbarCreateTicket };
package/dist/index.js ADDED
@@ -0,0 +1,181 @@
1
+ "use client";
2
+
3
+ // SupportNavbarCreateDialog.tsx
4
+ import * as React from "react";
5
+
6
+ // createSupportTicket.ts
7
+ async function createSupportTicket(apiBaseUrl, accessToken, input) {
8
+ const base = apiBaseUrl.replace(/\/$/, "");
9
+ const response = await fetch(`${base}/api/support/tickets`, {
10
+ method: "POST",
11
+ credentials: "include",
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
15
+ },
16
+ body: JSON.stringify({
17
+ shortDescription: input.shortDescription.trim(),
18
+ longDescriptionHtml: input.longDescriptionHtml?.trim() || null,
19
+ typeKey: "task",
20
+ priority: "medium",
21
+ statusKey: "open"
22
+ })
23
+ });
24
+ if (!response.ok) {
25
+ const text = await response.text().catch(() => "");
26
+ throw new Error(text || `Support ticket create failed (${response.status})`);
27
+ }
28
+ }
29
+
30
+ // SupportNavbarCreateDialog.tsx
31
+ import { jsx, jsxs } from "react/jsx-runtime";
32
+ function SupportNavbarCreateDialog({
33
+ open,
34
+ onOpenChange,
35
+ apiBaseUrl,
36
+ getAccessToken,
37
+ labels
38
+ }) {
39
+ const [shortText, setShortText] = React.useState("");
40
+ const [details, setDetails] = React.useState("");
41
+ const [error, setError] = React.useState(null);
42
+ const [submitting, setSubmitting] = React.useState(false);
43
+ React.useEffect(() => {
44
+ if (!open) {
45
+ setShortText("");
46
+ setDetails("");
47
+ setError(null);
48
+ setSubmitting(false);
49
+ }
50
+ }, [open]);
51
+ if (!open) return null;
52
+ const close = () => onOpenChange(false);
53
+ const handleSubmit = async (event) => {
54
+ event.preventDefault();
55
+ const trimmed = shortText.trim();
56
+ if (!trimmed) {
57
+ setError(labels.shortRequired);
58
+ return;
59
+ }
60
+ setError(null);
61
+ setSubmitting(true);
62
+ try {
63
+ await createSupportTicket(apiBaseUrl, getAccessToken(), {
64
+ shortDescription: trimmed,
65
+ longDescriptionHtml: details.trim() || void 0
66
+ });
67
+ close();
68
+ } catch {
69
+ setError(labels.submitError);
70
+ } finally {
71
+ setSubmitting(false);
72
+ }
73
+ };
74
+ return /* @__PURE__ */ jsx(
75
+ "div",
76
+ {
77
+ className: "fixed inset-0 z-[200] flex items-center justify-center bg-black/50 p-4",
78
+ role: "presentation",
79
+ onMouseDown: (event) => {
80
+ if (event.target === event.currentTarget) close();
81
+ },
82
+ children: /* @__PURE__ */ jsxs(
83
+ "div",
84
+ {
85
+ role: "dialog",
86
+ "aria-modal": "true",
87
+ "aria-labelledby": "support-navbar-create-title",
88
+ className: "w-full max-w-lg rounded-lg border border-theme bg-theme p-4 shadow-lg",
89
+ onMouseDown: (event) => event.stopPropagation(),
90
+ children: [
91
+ /* @__PURE__ */ jsx(
92
+ "h2",
93
+ {
94
+ id: "support-navbar-create-title",
95
+ className: "mb-3 text-lg font-semibold text-theme-primary",
96
+ children: labels.title
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsxs("form", { className: "space-y-3", onSubmit: (event) => void handleSubmit(event), children: [
100
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1", children: [
101
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-theme-primary", children: labels.shortLabel }),
102
+ /* @__PURE__ */ jsx(
103
+ "input",
104
+ {
105
+ type: "text",
106
+ value: shortText,
107
+ onChange: (event) => setShortText(event.target.value),
108
+ className: "w-full rounded-md border border-theme bg-input-theme px-3 py-2 text-sm text-theme-primary",
109
+ autoFocus: true
110
+ }
111
+ )
112
+ ] }),
113
+ /* @__PURE__ */ jsxs("label", { className: "block space-y-1", children: [
114
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-theme-primary", children: labels.detailsLabel }),
115
+ /* @__PURE__ */ jsx(
116
+ "textarea",
117
+ {
118
+ value: details,
119
+ onChange: (event) => setDetails(event.target.value),
120
+ rows: 4,
121
+ className: "w-full rounded-md border border-theme bg-input-theme px-3 py-2 text-sm text-theme-primary"
122
+ }
123
+ )
124
+ ] }),
125
+ error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600", role: "alert", children: error }) : null,
126
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [
127
+ /* @__PURE__ */ jsx(
128
+ "button",
129
+ {
130
+ type: "button",
131
+ className: "rounded-md border border-theme px-3 py-1.5 text-sm text-theme-primary",
132
+ onClick: close,
133
+ disabled: submitting,
134
+ children: labels.cancel
135
+ }
136
+ ),
137
+ /* @__PURE__ */ jsx(
138
+ "button",
139
+ {
140
+ type: "submit",
141
+ className: "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white disabled:opacity-50",
142
+ disabled: submitting,
143
+ children: labels.submit
144
+ }
145
+ )
146
+ ] })
147
+ ] })
148
+ ]
149
+ }
150
+ )
151
+ }
152
+ );
153
+ }
154
+
155
+ // useSupportNavbarCreateTicket.tsx
156
+ import * as React2 from "react";
157
+ import { jsx as jsx2 } from "react/jsx-runtime";
158
+ function useSupportNavbarCreateTicket({
159
+ apiBaseUrl,
160
+ getAccessToken,
161
+ labels
162
+ }) {
163
+ const [open, setOpen] = React2.useState(false);
164
+ const onSupportClick = React2.useCallback(() => setOpen(true), []);
165
+ const dialog = /* @__PURE__ */ jsx2(
166
+ SupportNavbarCreateDialog,
167
+ {
168
+ open,
169
+ onOpenChange: setOpen,
170
+ apiBaseUrl,
171
+ getAccessToken,
172
+ labels
173
+ }
174
+ );
175
+ return { onSupportClick, dialog, open, setOpen };
176
+ }
177
+ export {
178
+ SupportNavbarCreateDialog,
179
+ useSupportNavbarCreateTicket
180
+ };
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../SupportNavbarCreateDialog.tsx","../createSupportTicket.ts","../useSupportNavbarCreateTicket.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createSupportTicket } from \"./createSupportTicket\";\n\nexport type SupportNavbarCreateDialogLabels = {\n title: string;\n shortLabel: string;\n detailsLabel: string;\n submit: string;\n cancel: string;\n shortRequired: string;\n submitError: string;\n};\n\nexport type SupportNavbarCreateDialogProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n apiBaseUrl: string;\n getAccessToken: () => string | null;\n labels: SupportNavbarCreateDialogLabels;\n};\n\nexport function SupportNavbarCreateDialog({\n open,\n onOpenChange,\n apiBaseUrl,\n getAccessToken,\n labels,\n}: SupportNavbarCreateDialogProps) {\n const [shortText, setShortText] = React.useState(\"\");\n const [details, setDetails] = React.useState(\"\");\n const [error, setError] = React.useState<string | null>(null);\n const [submitting, setSubmitting] = React.useState(false);\n\n React.useEffect(() => {\n if (!open) {\n setShortText(\"\");\n setDetails(\"\");\n setError(null);\n setSubmitting(false);\n }\n }, [open]);\n\n if (!open) return null;\n\n const close = () => onOpenChange(false);\n\n const handleSubmit = async (event: React.FormEvent) => {\n event.preventDefault();\n const trimmed = shortText.trim();\n if (!trimmed) {\n setError(labels.shortRequired);\n return;\n }\n setError(null);\n setSubmitting(true);\n try {\n await createSupportTicket(apiBaseUrl, getAccessToken(), {\n shortDescription: trimmed,\n longDescriptionHtml: details.trim() || undefined,\n });\n close();\n } catch {\n setError(labels.submitError);\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[200] flex items-center justify-center bg-black/50 p-4\"\n role=\"presentation\"\n onMouseDown={(event) => {\n if (event.target === event.currentTarget) close();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"support-navbar-create-title\"\n className=\"w-full max-w-lg rounded-lg border border-theme bg-theme p-4 shadow-lg\"\n onMouseDown={(event) => event.stopPropagation()}\n >\n <h2\n id=\"support-navbar-create-title\"\n className=\"mb-3 text-lg font-semibold text-theme-primary\"\n >\n {labels.title}\n </h2>\n <form className=\"space-y-3\" onSubmit={(event) => void handleSubmit(event)}>\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-theme-primary\">\n {labels.shortLabel}\n </span>\n <input\n type=\"text\"\n value={shortText}\n onChange={(event) => setShortText(event.target.value)}\n className=\"w-full rounded-md border border-theme bg-input-theme px-3 py-2 text-sm text-theme-primary\"\n autoFocus\n />\n </label>\n <label className=\"block space-y-1\">\n <span className=\"text-sm font-medium text-theme-primary\">\n {labels.detailsLabel}\n </span>\n <textarea\n value={details}\n onChange={(event) => setDetails(event.target.value)}\n rows={4}\n className=\"w-full rounded-md border border-theme bg-input-theme px-3 py-2 text-sm text-theme-primary\"\n />\n </label>\n {error ? (\n <p className=\"text-sm text-red-600\" role=\"alert\">{error}</p>\n ) : null}\n <div className=\"flex justify-end gap-2 pt-1\">\n <button\n type=\"button\"\n className=\"rounded-md border border-theme px-3 py-1.5 text-sm text-theme-primary\"\n onClick={close}\n disabled={submitting}\n >\n {labels.cancel}\n </button>\n <button\n type=\"submit\"\n className=\"rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white disabled:opacity-50\"\n disabled={submitting}\n >\n {labels.submit}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n","export type CreateSupportTicketInput = {\n shortDescription: string;\n longDescriptionHtml?: string;\n};\n\nexport async function createSupportTicket(\n apiBaseUrl: string,\n accessToken: string | null,\n input: CreateSupportTicketInput,\n): Promise<void> {\n const base = apiBaseUrl.replace(/\\/$/, \"\");\n const response = await fetch(`${base}/api/support/tickets`, {\n method: \"POST\",\n credentials: \"include\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),\n },\n body: JSON.stringify({\n shortDescription: input.shortDescription.trim(),\n longDescriptionHtml: input.longDescriptionHtml?.trim() || null,\n typeKey: \"task\",\n priority: \"medium\",\n statusKey: \"open\",\n }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new Error(text || `Support ticket create failed (${response.status})`);\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n SupportNavbarCreateDialog,\n type SupportNavbarCreateDialogLabels,\n} from \"./SupportNavbarCreateDialog\";\n\nexport type UseSupportNavbarCreateTicketOptions = {\n apiBaseUrl: string;\n getAccessToken: () => string | null;\n labels: SupportNavbarCreateDialogLabels;\n};\n\nexport function useSupportNavbarCreateTicket({\n apiBaseUrl,\n getAccessToken,\n labels,\n}: UseSupportNavbarCreateTicketOptions) {\n const [open, setOpen] = React.useState(false);\n const onSupportClick = React.useCallback(() => setOpen(true), []);\n\n const dialog = (\n <SupportNavbarCreateDialog\n open={open}\n onOpenChange={setOpen}\n apiBaseUrl={apiBaseUrl}\n getAccessToken={getAccessToken}\n labels={labels}\n />\n );\n\n return { onSupportClick, dialog, open, setOpen };\n}\n"],"mappings":"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","names":["React","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,32 @@
1
1
  {
2
2
  "name": "@avament-pub/support-navbar-create",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Support ticket create dialog for module navbars (headset → new report)",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": ["dist", "README.md"],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "peerDependencies": {
28
+ "react": ">=18",
29
+ "react-dom": ">=18"
30
+ },
31
+ "license": "MIT"
32
+ }