@cohiva/support-widget 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(index_exports);
39
39
 
40
40
  // src/components/SupportWidget.tsx
41
41
  var import_react2 = require("react");
42
+ var import_lucide_react = require("lucide-react");
42
43
 
43
44
  // src/adapters/centralApi.ts
44
45
  function createCentralFeedbackClient(options) {
@@ -724,7 +725,7 @@ function buildFeedbackDescription(formData, diagnostics) {
724
725
  }
725
726
 
726
727
  // src/components/SupportWidget.tsx
727
- var import_widget = require("./widget-PDWZ5OMY.css");
728
+ var import_widget = require("./widget-E4MWTWY5.css");
728
729
 
729
730
  // src/utils/classNames.ts
730
731
  function classNames(...items) {
@@ -733,6 +734,12 @@ function classNames(...items) {
733
734
 
734
735
  // src/components/SupportWidget.tsx
735
736
  var import_jsx_runtime = require("react/jsx-runtime");
737
+ var typeIconMap = {
738
+ bug: { icon: import_lucide_react.Bug, color: "#ef4444" },
739
+ feature: { icon: import_lucide_react.Lightbulb, color: "#f59e0b" },
740
+ feedback: { icon: import_lucide_react.MessageCircle, color: "#3b82f6" },
741
+ improvement: { icon: import_lucide_react.Wrench, color: "#8b5cf6" }
742
+ };
736
743
  function labelForType(type) {
737
744
  return feedbackTypes.find((item) => item.value === type)?.label || type;
738
745
  }
@@ -892,7 +899,8 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
892
899
  children: [
893
900
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-label": "Close", className: "sw-close", onClick: handleClose, type: "button", children: "x" }),
894
901
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "sw-sr-only", id: "support-widget-description", children: "Support ticket dialog" }),
895
- isSubmitted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-success", "data-testid": "feedback-success-screen", children: [
902
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-modal-body", children: isSubmitted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-success", "data-testid": "feedback-success-screen", children: [
903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckCircle, { "aria-hidden": "true", className: "sw-success-icon", color: "#0f766e", size: 48 }),
896
904
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "Thank You!" }),
897
905
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Your feedback helps us improve the product." }),
898
906
  submitResult?.issue_number || submitResult?.issue_url ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { "data-testid": "feedback-success-issue-link", children: [
@@ -904,23 +912,31 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
904
912
  ] }) : null
905
913
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
906
914
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "sw-header", children: [
907
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: step === 1 ? "Send Feedback" : selectedTypeConfig?.label || "Support Ticket" }),
908
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: step === 1 ? "What kind of support ticket would you like to submit?" : selectedTypeConfig?.description })
915
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: step === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MessageSquarePlus, { "aria-hidden": "true", className: "sw-header-icon", size: 22 }),
917
+ " Send Feedback"
918
+ ] }) : selectedTypeConfig?.label || "Support Ticket" }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: step === 1 ? "Help us improve Cohiva by sharing your experience" : selectedTypeConfig?.description })
909
920
  ] }),
910
- step === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-type-grid", children: feedbackTypes.map((typeConfig) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
911
- "button",
912
- {
913
- className: "sw-type-card",
914
- "data-testid": `feedback-type-${typeConfig.value}`,
915
- onClick: () => handleTypeSelect(typeConfig.value),
916
- type: "button",
917
- children: [
918
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: typeConfig.label }),
919
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: typeConfig.description })
920
- ]
921
- },
922
- typeConfig.value
923
- )) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "sw-form", onSubmit: handleSubmit, children: [
921
+ step === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sw-type-grid", children: feedbackTypes.map((typeConfig) => {
922
+ const entry = typeIconMap[typeConfig.value];
923
+ const Icon = entry?.icon;
924
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
925
+ "button",
926
+ {
927
+ className: "sw-type-card",
928
+ "data-testid": `feedback-type-${typeConfig.value}`,
929
+ onClick: () => handleTypeSelect(typeConfig.value),
930
+ type: "button",
931
+ children: [
932
+ Icon ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", className: "sw-type-card-icon", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { color: entry.color, size: 36, strokeWidth: 1.5 }) }) : null,
933
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: typeConfig.label }),
934
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: typeConfig.description })
935
+ ]
936
+ },
937
+ typeConfig.value
938
+ );
939
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: "sw-form", onSubmit: handleSubmit, children: [
924
940
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "support-title", children: "Title *" }),
925
941
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
926
942
  "input",
@@ -1020,10 +1036,13 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
1020
1036
  !validation.valid ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "sw-error", children: validation.description }) : null,
1021
1037
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sw-actions", children: [
1022
1038
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setStep(1), type: "button", children: "Back" }),
1023
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: isSubmitting || !validation.valid, type: "submit", children: isSubmitting ? "Submitting..." : "Submit" })
1039
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: isSubmitting || !validation.valid, type: "submit", children: isSubmitting ? "Submitting..." : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Send, { "aria-hidden": "true", size: 14, style: { marginRight: "0.35rem", verticalAlign: "middle" } }),
1041
+ "Submit"
1042
+ ] }) })
1024
1043
  ] })
1025
1044
  ] })
1026
- ] })
1045
+ ] }) })
1027
1046
  ]
1028
1047
  }
1029
1048
  ) }) : null
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/components/SupportWidget.tsx
2
2
  import { useCallback, useEffect as useEffect2, useMemo, useState } from "react";
3
+ import { Bug, CheckCircle, Lightbulb, MessageCircle, MessageSquarePlus, Send, Wrench } from "lucide-react";
3
4
 
4
5
  // src/adapters/centralApi.ts
5
6
  function createCentralFeedbackClient(options) {
@@ -685,7 +686,7 @@ function buildFeedbackDescription(formData, diagnostics) {
685
686
  }
686
687
 
687
688
  // src/components/SupportWidget.tsx
688
- import "./widget-PDWZ5OMY.css";
689
+ import "./widget-E4MWTWY5.css";
689
690
 
690
691
  // src/utils/classNames.ts
691
692
  function classNames(...items) {
@@ -694,6 +695,12 @@ function classNames(...items) {
694
695
 
695
696
  // src/components/SupportWidget.tsx
696
697
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
698
+ var typeIconMap = {
699
+ bug: { icon: Bug, color: "#ef4444" },
700
+ feature: { icon: Lightbulb, color: "#f59e0b" },
701
+ feedback: { icon: MessageCircle, color: "#3b82f6" },
702
+ improvement: { icon: Wrench, color: "#8b5cf6" }
703
+ };
697
704
  function labelForType(type) {
698
705
  return feedbackTypes.find((item) => item.value === type)?.label || type;
699
706
  }
@@ -853,7 +860,8 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
853
860
  children: [
854
861
  /* @__PURE__ */ jsx("button", { "aria-label": "Close", className: "sw-close", onClick: handleClose, type: "button", children: "x" }),
855
862
  /* @__PURE__ */ jsx("p", { className: "sw-sr-only", id: "support-widget-description", children: "Support ticket dialog" }),
856
- isSubmitted ? /* @__PURE__ */ jsxs("div", { className: "sw-success", "data-testid": "feedback-success-screen", children: [
863
+ /* @__PURE__ */ jsx("div", { className: "sw-modal-body", children: isSubmitted ? /* @__PURE__ */ jsxs("div", { className: "sw-success", "data-testid": "feedback-success-screen", children: [
864
+ /* @__PURE__ */ jsx(CheckCircle, { "aria-hidden": "true", className: "sw-success-icon", color: "#0f766e", size: 48 }),
857
865
  /* @__PURE__ */ jsx("h2", { children: "Thank You!" }),
858
866
  /* @__PURE__ */ jsx("p", { children: "Your feedback helps us improve the product." }),
859
867
  submitResult?.issue_number || submitResult?.issue_url ? /* @__PURE__ */ jsxs("p", { "data-testid": "feedback-success-issue-link", children: [
@@ -865,23 +873,31 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
865
873
  ] }) : null
866
874
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
867
875
  /* @__PURE__ */ jsxs("header", { className: "sw-header", children: [
868
- /* @__PURE__ */ jsx("h2", { children: step === 1 ? "Send Feedback" : selectedTypeConfig?.label || "Support Ticket" }),
869
- /* @__PURE__ */ jsx("p", { children: step === 1 ? "What kind of support ticket would you like to submit?" : selectedTypeConfig?.description })
876
+ /* @__PURE__ */ jsx("h2", { children: step === 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
877
+ /* @__PURE__ */ jsx(MessageSquarePlus, { "aria-hidden": "true", className: "sw-header-icon", size: 22 }),
878
+ " Send Feedback"
879
+ ] }) : selectedTypeConfig?.label || "Support Ticket" }),
880
+ /* @__PURE__ */ jsx("p", { children: step === 1 ? "Help us improve Cohiva by sharing your experience" : selectedTypeConfig?.description })
870
881
  ] }),
871
- step === 1 ? /* @__PURE__ */ jsx("div", { className: "sw-type-grid", children: feedbackTypes.map((typeConfig) => /* @__PURE__ */ jsxs(
872
- "button",
873
- {
874
- className: "sw-type-card",
875
- "data-testid": `feedback-type-${typeConfig.value}`,
876
- onClick: () => handleTypeSelect(typeConfig.value),
877
- type: "button",
878
- children: [
879
- /* @__PURE__ */ jsx("strong", { children: typeConfig.label }),
880
- /* @__PURE__ */ jsx("span", { children: typeConfig.description })
881
- ]
882
- },
883
- typeConfig.value
884
- )) }) : /* @__PURE__ */ jsxs("form", { className: "sw-form", onSubmit: handleSubmit, children: [
882
+ step === 1 ? /* @__PURE__ */ jsx("div", { className: "sw-type-grid", children: feedbackTypes.map((typeConfig) => {
883
+ const entry = typeIconMap[typeConfig.value];
884
+ const Icon = entry?.icon;
885
+ return /* @__PURE__ */ jsxs(
886
+ "button",
887
+ {
888
+ className: "sw-type-card",
889
+ "data-testid": `feedback-type-${typeConfig.value}`,
890
+ onClick: () => handleTypeSelect(typeConfig.value),
891
+ type: "button",
892
+ children: [
893
+ Icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "sw-type-card-icon", children: /* @__PURE__ */ jsx(Icon, { color: entry.color, size: 36, strokeWidth: 1.5 }) }) : null,
894
+ /* @__PURE__ */ jsx("strong", { children: typeConfig.label }),
895
+ /* @__PURE__ */ jsx("span", { children: typeConfig.description })
896
+ ]
897
+ },
898
+ typeConfig.value
899
+ );
900
+ }) }) : /* @__PURE__ */ jsxs("form", { className: "sw-form", onSubmit: handleSubmit, children: [
885
901
  /* @__PURE__ */ jsx("label", { htmlFor: "support-title", children: "Title *" }),
886
902
  /* @__PURE__ */ jsx(
887
903
  "input",
@@ -981,10 +997,13 @@ function SupportWidget({ config, isVisible = true, onSuccess, onError, onSubmit
981
997
  !validation.valid ? /* @__PURE__ */ jsx("p", { className: "sw-error", children: validation.description }) : null,
982
998
  /* @__PURE__ */ jsxs("div", { className: "sw-actions", children: [
983
999
  /* @__PURE__ */ jsx("button", { onClick: () => setStep(1), type: "button", children: "Back" }),
984
- /* @__PURE__ */ jsx("button", { disabled: isSubmitting || !validation.valid, type: "submit", children: isSubmitting ? "Submitting..." : "Submit" })
1000
+ /* @__PURE__ */ jsx("button", { disabled: isSubmitting || !validation.valid, type: "submit", children: isSubmitting ? "Submitting..." : /* @__PURE__ */ jsxs(Fragment, { children: [
1001
+ /* @__PURE__ */ jsx(Send, { "aria-hidden": "true", size: 14, style: { marginRight: "0.35rem", verticalAlign: "middle" } }),
1002
+ "Submit"
1003
+ ] }) })
985
1004
  ] })
986
1005
  ] })
987
- ] })
1006
+ ] }) })
988
1007
  ]
989
1008
  }
990
1009
  ) }) : null
package/dist/styles.css CHANGED
@@ -61,24 +61,33 @@
61
61
  inset: 0;
62
62
  z-index: 1100;
63
63
  background: rgba(2, 6, 23, 0.7);
64
- display: grid;
65
- place-items: center;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
66
67
  padding: 1rem;
67
68
  }
68
69
 
69
70
  .sw-modal {
70
71
  width: min(100%, 56rem);
71
- max-height: 92vh;
72
- overflow: auto;
72
+ max-height: calc(100dvh - 2rem);
73
+ display: flex;
74
+ flex-direction: column;
75
+ overflow: hidden;
73
76
  position: relative;
74
77
  border-radius: 1rem;
75
78
  border: 1px solid var(--sw-color-border);
76
79
  background: var(--sw-color-surface);
77
80
  color: var(--sw-color-text);
78
- padding: 1.25rem;
79
81
  box-shadow: var(--sw-shadow-elevated);
80
82
  }
81
83
 
84
+ .sw-modal-body {
85
+ flex: 1;
86
+ overflow-y: auto;
87
+ overscroll-behavior: contain;
88
+ padding: 1.25rem;
89
+ }
90
+
82
91
  .sw-close {
83
92
  position: absolute;
84
93
  right: 0.75rem;
@@ -93,6 +102,13 @@
93
102
  .sw-header h2 {
94
103
  margin: 0;
95
104
  font-size: 1.25rem;
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 0.4rem;
108
+ }
109
+
110
+ .sw-header-icon {
111
+ flex-shrink: 0;
96
112
  }
97
113
 
98
114
  .sw-header p {
@@ -111,12 +127,22 @@
111
127
  border: 2px solid var(--sw-color-border);
112
128
  border-radius: 0.75rem;
113
129
  background: #ffffff;
114
- padding: 0.85rem;
115
- display: grid;
116
- gap: 0.4rem;
130
+ padding: 1.25rem 1rem;
131
+ display: flex;
132
+ flex-direction: column;
133
+ align-items: center;
134
+ text-align: center;
135
+ gap: 0.5rem;
117
136
  transition: border-color 150ms ease, background 150ms ease;
118
137
  }
119
138
 
139
+ .sw-type-card-icon {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ margin-bottom: 0.25rem;
144
+ }
145
+
120
146
  .sw-type-card:hover {
121
147
  border-color: var(--sw-color-primary);
122
148
  background: var(--sw-color-surface-muted);
@@ -228,6 +254,14 @@
228
254
  .sw-success {
229
255
  text-align: center;
230
256
  padding: 3rem 1rem;
257
+ display: flex;
258
+ flex-direction: column;
259
+ align-items: center;
260
+ gap: 0.5rem;
261
+ }
262
+
263
+ .sw-success-icon {
264
+ margin-bottom: 0.5rem;
231
265
  }
232
266
 
233
267
  @media (max-width: 640px) {
@@ -0,0 +1,277 @@
1
+ :root {
2
+ --sw-color-launcher-bg: #6d28d9;
3
+ --sw-color-launcher-bg-hover: #5b21b6;
4
+ --sw-color-launcher-border: #8b5cf6;
5
+ --sw-color-surface: #ffffff;
6
+ --sw-color-surface-muted: #f8fafc;
7
+ --sw-color-text: #0f172a;
8
+ --sw-color-text-muted: #475569;
9
+ --sw-color-border: #cbd5e1;
10
+ --sw-color-primary: #0f766e;
11
+ --sw-color-primary-hover: #115e59;
12
+ --sw-color-danger: #b91c1c;
13
+ --sw-shadow-elevated: 0 20px 35px rgba(15, 23, 42, 0.25);
14
+ }
15
+
16
+ .sw-sr-only {
17
+ border: 0;
18
+ clip: rect(0, 0, 0, 0);
19
+ height: 1px;
20
+ margin: -1px;
21
+ overflow: hidden;
22
+ padding: 0;
23
+ position: absolute;
24
+ width: 1px;
25
+ }
26
+
27
+ .sw-launcher {
28
+ position: fixed;
29
+ right: 1.5rem;
30
+ bottom: 1.5rem;
31
+ z-index: 1000;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: 0.5rem;
35
+ border-radius: 999px;
36
+ border: 2px solid var(--sw-color-launcher-border);
37
+ background: var(--sw-color-launcher-bg);
38
+ color: #ffffff;
39
+ font-size: 0.875rem;
40
+ font-weight: 700;
41
+ padding: 0.625rem 1rem;
42
+ box-shadow: 0 10px 24px rgba(76, 29, 149, 0.38);
43
+ transition: transform 150ms ease, background 150ms ease;
44
+ }
45
+
46
+ .sw-launcher:hover {
47
+ transform: scale(1.05);
48
+ background: var(--sw-color-launcher-bg-hover);
49
+ }
50
+
51
+ .sw-launcher-icon {
52
+ width: 1.25rem;
53
+ height: 1.25rem;
54
+ display: inline-flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ }
58
+
59
+ .sw-overlay {
60
+ position: fixed;
61
+ inset: 0;
62
+ z-index: 1100;
63
+ background: rgba(2, 6, 23, 0.7);
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ padding: 1rem;
68
+ }
69
+
70
+ .sw-modal {
71
+ width: min(100%, 56rem);
72
+ max-height: calc(100dvh - 2rem);
73
+ display: flex;
74
+ flex-direction: column;
75
+ overflow: hidden;
76
+ position: relative;
77
+ border-radius: 1rem;
78
+ border: 1px solid var(--sw-color-border);
79
+ background: var(--sw-color-surface);
80
+ color: var(--sw-color-text);
81
+ box-shadow: var(--sw-shadow-elevated);
82
+ }
83
+
84
+ .sw-modal-body {
85
+ flex: 1;
86
+ overflow-y: auto;
87
+ overscroll-behavior: contain;
88
+ padding: 1.25rem;
89
+ }
90
+
91
+ .sw-close {
92
+ position: absolute;
93
+ right: 0.75rem;
94
+ top: 0.75rem;
95
+ width: 2rem;
96
+ height: 2rem;
97
+ border-radius: 0.5rem;
98
+ border: 1px solid var(--sw-color-border);
99
+ background: transparent;
100
+ }
101
+
102
+ .sw-header h2 {
103
+ margin: 0;
104
+ font-size: 1.25rem;
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 0.4rem;
108
+ }
109
+
110
+ .sw-header-icon {
111
+ flex-shrink: 0;
112
+ }
113
+
114
+ .sw-header p {
115
+ margin: 0.3rem 0 1rem;
116
+ color: var(--sw-color-text-muted);
117
+ }
118
+
119
+ .sw-type-grid {
120
+ display: grid;
121
+ grid-template-columns: repeat(2, minmax(0, 1fr));
122
+ gap: 0.75rem;
123
+ }
124
+
125
+ .sw-type-card {
126
+ text-align: left;
127
+ border: 2px solid var(--sw-color-border);
128
+ border-radius: 0.75rem;
129
+ background: #ffffff;
130
+ padding: 1.25rem 1rem;
131
+ display: flex;
132
+ flex-direction: column;
133
+ align-items: center;
134
+ text-align: center;
135
+ gap: 0.5rem;
136
+ transition: border-color 150ms ease, background 150ms ease;
137
+ }
138
+
139
+ .sw-type-card-icon {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ margin-bottom: 0.25rem;
144
+ }
145
+
146
+ .sw-type-card:hover {
147
+ border-color: var(--sw-color-primary);
148
+ background: var(--sw-color-surface-muted);
149
+ }
150
+
151
+ .sw-type-card span {
152
+ color: var(--sw-color-text-muted);
153
+ font-size: 0.85rem;
154
+ }
155
+
156
+ .sw-form {
157
+ display: grid;
158
+ gap: 0.8rem;
159
+ }
160
+
161
+ .sw-field {
162
+ display: grid;
163
+ gap: 0.35rem;
164
+ }
165
+
166
+ .sw-field textarea,
167
+ .sw-form input,
168
+ .sw-form select {
169
+ border: 1px solid var(--sw-color-border);
170
+ border-radius: 0.5rem;
171
+ padding: 0.6rem;
172
+ font: inherit;
173
+ }
174
+
175
+ .sw-row {
176
+ display: grid;
177
+ grid-template-columns: repeat(2, minmax(0, 1fr));
178
+ gap: 0.75rem;
179
+ }
180
+
181
+ .sw-section {
182
+ border: 1px solid var(--sw-color-border);
183
+ border-radius: 0.75rem;
184
+ background: var(--sw-color-surface-muted);
185
+ padding: 0.8rem;
186
+ }
187
+
188
+ .sw-section h3 {
189
+ margin-top: 0;
190
+ margin-bottom: 0.6rem;
191
+ font-size: 1rem;
192
+ }
193
+
194
+ .sw-rating {
195
+ display: grid;
196
+ gap: 0.45rem;
197
+ }
198
+
199
+ .sw-rating-button {
200
+ border: 1px solid var(--sw-color-border);
201
+ border-radius: 0.3rem;
202
+ background: transparent;
203
+ color: #94a3b8;
204
+ margin-right: 0.35rem;
205
+ }
206
+
207
+ .sw-rating-button.is-active {
208
+ color: #f59e0b;
209
+ border-color: #f59e0b;
210
+ }
211
+
212
+ .sw-diagnostics-note {
213
+ border: 1px dashed var(--sw-color-border);
214
+ border-radius: 0.55rem;
215
+ font-size: 0.8rem;
216
+ color: var(--sw-color-text-muted);
217
+ padding: 0.65rem;
218
+ }
219
+
220
+ .sw-error {
221
+ color: var(--sw-color-danger);
222
+ margin: 0;
223
+ }
224
+
225
+ .sw-actions {
226
+ display: flex;
227
+ gap: 0.75rem;
228
+ }
229
+
230
+ .sw-actions button {
231
+ flex: 1;
232
+ border: 1px solid var(--sw-color-border);
233
+ border-radius: 0.5rem;
234
+ padding: 0.6rem;
235
+ font: inherit;
236
+ background: #ffffff;
237
+ }
238
+
239
+ .sw-actions button[type='submit'] {
240
+ color: #ffffff;
241
+ background: var(--sw-color-primary);
242
+ border-color: var(--sw-color-primary);
243
+ }
244
+
245
+ .sw-actions button[type='submit']:hover {
246
+ background: var(--sw-color-primary-hover);
247
+ }
248
+
249
+ .sw-actions button:disabled {
250
+ opacity: 0.5;
251
+ cursor: not-allowed;
252
+ }
253
+
254
+ .sw-success {
255
+ text-align: center;
256
+ padding: 3rem 1rem;
257
+ display: flex;
258
+ flex-direction: column;
259
+ align-items: center;
260
+ gap: 0.5rem;
261
+ }
262
+
263
+ .sw-success-icon {
264
+ margin-bottom: 0.5rem;
265
+ }
266
+
267
+ @media (max-width: 640px) {
268
+ .sw-type-grid,
269
+ .sw-row {
270
+ grid-template-columns: 1fr;
271
+ }
272
+
273
+ .sw-launcher {
274
+ right: 1rem;
275
+ bottom: 1rem;
276
+ }
277
+ }
@@ -0,0 +1,252 @@
1
+ :root {
2
+ --sw-color-launcher-bg: #6d28d9;
3
+ --sw-color-launcher-bg-hover: #5b21b6;
4
+ --sw-color-launcher-border: #8b5cf6;
5
+ --sw-color-surface: #ffffff;
6
+ --sw-color-surface-muted: #f8fafc;
7
+ --sw-color-text: #0f172a;
8
+ --sw-color-text-muted: #475569;
9
+ --sw-color-border: #cbd5e1;
10
+ --sw-color-primary: #0f766e;
11
+ --sw-color-primary-hover: #115e59;
12
+ --sw-color-danger: #b91c1c;
13
+ --sw-shadow-elevated: 0 20px 35px rgba(15, 23, 42, 0.25);
14
+ }
15
+
16
+ .sw-sr-only {
17
+ border: 0;
18
+ clip: rect(0, 0, 0, 0);
19
+ height: 1px;
20
+ margin: -1px;
21
+ overflow: hidden;
22
+ padding: 0;
23
+ position: absolute;
24
+ width: 1px;
25
+ }
26
+
27
+ .sw-launcher {
28
+ position: fixed;
29
+ right: 1.5rem;
30
+ bottom: 1.5rem;
31
+ z-index: 1000;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: 0.5rem;
35
+ border-radius: 999px;
36
+ border: 2px solid var(--sw-color-launcher-border);
37
+ background: var(--sw-color-launcher-bg);
38
+ color: #ffffff;
39
+ font-size: 0.875rem;
40
+ font-weight: 700;
41
+ padding: 0.625rem 1rem;
42
+ box-shadow: 0 10px 24px rgba(76, 29, 149, 0.38);
43
+ transition: transform 150ms ease, background 150ms ease;
44
+ }
45
+
46
+ .sw-launcher:hover {
47
+ transform: scale(1.05);
48
+ background: var(--sw-color-launcher-bg-hover);
49
+ }
50
+
51
+ .sw-launcher-icon {
52
+ width: 1.25rem;
53
+ height: 1.25rem;
54
+ display: inline-flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ }
58
+
59
+ .sw-overlay {
60
+ position: fixed;
61
+ inset: 0;
62
+ z-index: 1100;
63
+ background: rgba(2, 6, 23, 0.7);
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ padding: 1rem;
68
+ }
69
+
70
+ .sw-modal {
71
+ width: min(100%, 56rem);
72
+ max-height: calc(100dvh - 2rem);
73
+ display: flex;
74
+ flex-direction: column;
75
+ overflow: hidden;
76
+ position: relative;
77
+ border-radius: 1rem;
78
+ border: 1px solid var(--sw-color-border);
79
+ background: var(--sw-color-surface);
80
+ color: var(--sw-color-text);
81
+ box-shadow: var(--sw-shadow-elevated);
82
+ }
83
+
84
+ .sw-modal-body {
85
+ flex: 1;
86
+ overflow-y: auto;
87
+ overscroll-behavior: contain;
88
+ padding: 1.25rem;
89
+ }
90
+
91
+ .sw-close {
92
+ position: absolute;
93
+ right: 0.75rem;
94
+ top: 0.75rem;
95
+ width: 2rem;
96
+ height: 2rem;
97
+ border-radius: 0.5rem;
98
+ border: 1px solid var(--sw-color-border);
99
+ background: transparent;
100
+ }
101
+
102
+ .sw-header h2 {
103
+ margin: 0;
104
+ font-size: 1.25rem;
105
+ }
106
+
107
+ .sw-header p {
108
+ margin: 0.3rem 0 1rem;
109
+ color: var(--sw-color-text-muted);
110
+ }
111
+
112
+ .sw-type-grid {
113
+ display: grid;
114
+ grid-template-columns: repeat(2, minmax(0, 1fr));
115
+ gap: 0.75rem;
116
+ }
117
+
118
+ .sw-type-card {
119
+ text-align: left;
120
+ border: 2px solid var(--sw-color-border);
121
+ border-radius: 0.75rem;
122
+ background: #ffffff;
123
+ padding: 0.85rem;
124
+ display: grid;
125
+ gap: 0.4rem;
126
+ transition: border-color 150ms ease, background 150ms ease;
127
+ }
128
+
129
+ .sw-type-card:hover {
130
+ border-color: var(--sw-color-primary);
131
+ background: var(--sw-color-surface-muted);
132
+ }
133
+
134
+ .sw-type-card span {
135
+ color: var(--sw-color-text-muted);
136
+ font-size: 0.85rem;
137
+ }
138
+
139
+ .sw-form {
140
+ display: grid;
141
+ gap: 0.8rem;
142
+ }
143
+
144
+ .sw-field {
145
+ display: grid;
146
+ gap: 0.35rem;
147
+ }
148
+
149
+ .sw-field textarea,
150
+ .sw-form input,
151
+ .sw-form select {
152
+ border: 1px solid var(--sw-color-border);
153
+ border-radius: 0.5rem;
154
+ padding: 0.6rem;
155
+ font: inherit;
156
+ }
157
+
158
+ .sw-row {
159
+ display: grid;
160
+ grid-template-columns: repeat(2, minmax(0, 1fr));
161
+ gap: 0.75rem;
162
+ }
163
+
164
+ .sw-section {
165
+ border: 1px solid var(--sw-color-border);
166
+ border-radius: 0.75rem;
167
+ background: var(--sw-color-surface-muted);
168
+ padding: 0.8rem;
169
+ }
170
+
171
+ .sw-section h3 {
172
+ margin-top: 0;
173
+ margin-bottom: 0.6rem;
174
+ font-size: 1rem;
175
+ }
176
+
177
+ .sw-rating {
178
+ display: grid;
179
+ gap: 0.45rem;
180
+ }
181
+
182
+ .sw-rating-button {
183
+ border: 1px solid var(--sw-color-border);
184
+ border-radius: 0.3rem;
185
+ background: transparent;
186
+ color: #94a3b8;
187
+ margin-right: 0.35rem;
188
+ }
189
+
190
+ .sw-rating-button.is-active {
191
+ color: #f59e0b;
192
+ border-color: #f59e0b;
193
+ }
194
+
195
+ .sw-diagnostics-note {
196
+ border: 1px dashed var(--sw-color-border);
197
+ border-radius: 0.55rem;
198
+ font-size: 0.8rem;
199
+ color: var(--sw-color-text-muted);
200
+ padding: 0.65rem;
201
+ }
202
+
203
+ .sw-error {
204
+ color: var(--sw-color-danger);
205
+ margin: 0;
206
+ }
207
+
208
+ .sw-actions {
209
+ display: flex;
210
+ gap: 0.75rem;
211
+ }
212
+
213
+ .sw-actions button {
214
+ flex: 1;
215
+ border: 1px solid var(--sw-color-border);
216
+ border-radius: 0.5rem;
217
+ padding: 0.6rem;
218
+ font: inherit;
219
+ background: #ffffff;
220
+ }
221
+
222
+ .sw-actions button[type='submit'] {
223
+ color: #ffffff;
224
+ background: var(--sw-color-primary);
225
+ border-color: var(--sw-color-primary);
226
+ }
227
+
228
+ .sw-actions button[type='submit']:hover {
229
+ background: var(--sw-color-primary-hover);
230
+ }
231
+
232
+ .sw-actions button:disabled {
233
+ opacity: 0.5;
234
+ cursor: not-allowed;
235
+ }
236
+
237
+ .sw-success {
238
+ text-align: center;
239
+ padding: 3rem 1rem;
240
+ }
241
+
242
+ @media (max-width: 640px) {
243
+ .sw-type-grid,
244
+ .sw-row {
245
+ grid-template-columns: 1fr;
246
+ }
247
+
248
+ .sw-launcher {
249
+ right: 1rem;
250
+ bottom: 1rem;
251
+ }
252
+ }
@@ -0,0 +1,247 @@
1
+ :root {
2
+ --sw-color-launcher-bg: #6d28d9;
3
+ --sw-color-launcher-bg-hover: #5b21b6;
4
+ --sw-color-launcher-border: #8b5cf6;
5
+ --sw-color-surface: #ffffff;
6
+ --sw-color-surface-muted: #f8fafc;
7
+ --sw-color-text: #0f172a;
8
+ --sw-color-text-muted: #475569;
9
+ --sw-color-border: #cbd5e1;
10
+ --sw-color-primary: #0f766e;
11
+ --sw-color-primary-hover: #115e59;
12
+ --sw-color-danger: #b91c1c;
13
+ --sw-shadow-elevated: 0 20px 35px rgba(15, 23, 42, 0.25);
14
+ }
15
+
16
+ .sw-sr-only {
17
+ border: 0;
18
+ clip: rect(0, 0, 0, 0);
19
+ height: 1px;
20
+ margin: -1px;
21
+ overflow: hidden;
22
+ padding: 0;
23
+ position: absolute;
24
+ width: 1px;
25
+ }
26
+
27
+ .sw-launcher {
28
+ position: fixed;
29
+ right: 1.5rem;
30
+ bottom: 1.5rem;
31
+ z-index: 1000;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: 0.5rem;
35
+ border-radius: 999px;
36
+ border: 2px solid var(--sw-color-launcher-border);
37
+ background: var(--sw-color-launcher-bg);
38
+ color: #ffffff;
39
+ font-size: 0.875rem;
40
+ font-weight: 700;
41
+ padding: 0.625rem 1rem;
42
+ box-shadow: 0 10px 24px rgba(76, 29, 149, 0.38);
43
+ transition: transform 150ms ease, background 150ms ease;
44
+ }
45
+
46
+ .sw-launcher:hover {
47
+ transform: scale(1.05);
48
+ background: var(--sw-color-launcher-bg-hover);
49
+ }
50
+
51
+ .sw-launcher-icon {
52
+ width: 1.25rem;
53
+ height: 1.25rem;
54
+ display: inline-flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ }
58
+
59
+ .sw-overlay {
60
+ position: fixed;
61
+ inset: 0;
62
+ z-index: 1100;
63
+ background: rgba(2, 6, 23, 0.7);
64
+ overflow-y: auto;
65
+ }
66
+
67
+ .sw-modal-wrapper {
68
+ display: flex;
69
+ min-height: 100%;
70
+ align-items: center;
71
+ justify-content: center;
72
+ padding: 1rem;
73
+ }
74
+
75
+ .sw-modal {
76
+ width: min(100%, 56rem);
77
+ position: relative;
78
+ border-radius: 1rem;
79
+ border: 1px solid var(--sw-color-border);
80
+ background: var(--sw-color-surface);
81
+ color: var(--sw-color-text);
82
+ padding: 1.25rem;
83
+ box-shadow: var(--sw-shadow-elevated);
84
+ }
85
+
86
+ .sw-close {
87
+ position: absolute;
88
+ right: 0.75rem;
89
+ top: 0.75rem;
90
+ width: 2rem;
91
+ height: 2rem;
92
+ border-radius: 0.5rem;
93
+ border: 1px solid var(--sw-color-border);
94
+ background: transparent;
95
+ }
96
+
97
+ .sw-header h2 {
98
+ margin: 0;
99
+ font-size: 1.25rem;
100
+ }
101
+
102
+ .sw-header p {
103
+ margin: 0.3rem 0 1rem;
104
+ color: var(--sw-color-text-muted);
105
+ }
106
+
107
+ .sw-type-grid {
108
+ display: grid;
109
+ grid-template-columns: repeat(2, minmax(0, 1fr));
110
+ gap: 0.75rem;
111
+ }
112
+
113
+ .sw-type-card {
114
+ text-align: left;
115
+ border: 2px solid var(--sw-color-border);
116
+ border-radius: 0.75rem;
117
+ background: #ffffff;
118
+ padding: 0.85rem;
119
+ display: grid;
120
+ gap: 0.4rem;
121
+ transition: border-color 150ms ease, background 150ms ease;
122
+ }
123
+
124
+ .sw-type-card:hover {
125
+ border-color: var(--sw-color-primary);
126
+ background: var(--sw-color-surface-muted);
127
+ }
128
+
129
+ .sw-type-card span {
130
+ color: var(--sw-color-text-muted);
131
+ font-size: 0.85rem;
132
+ }
133
+
134
+ .sw-form {
135
+ display: grid;
136
+ gap: 0.8rem;
137
+ }
138
+
139
+ .sw-field {
140
+ display: grid;
141
+ gap: 0.35rem;
142
+ }
143
+
144
+ .sw-field textarea,
145
+ .sw-form input,
146
+ .sw-form select {
147
+ border: 1px solid var(--sw-color-border);
148
+ border-radius: 0.5rem;
149
+ padding: 0.6rem;
150
+ font: inherit;
151
+ }
152
+
153
+ .sw-row {
154
+ display: grid;
155
+ grid-template-columns: repeat(2, minmax(0, 1fr));
156
+ gap: 0.75rem;
157
+ }
158
+
159
+ .sw-section {
160
+ border: 1px solid var(--sw-color-border);
161
+ border-radius: 0.75rem;
162
+ background: var(--sw-color-surface-muted);
163
+ padding: 0.8rem;
164
+ }
165
+
166
+ .sw-section h3 {
167
+ margin-top: 0;
168
+ margin-bottom: 0.6rem;
169
+ font-size: 1rem;
170
+ }
171
+
172
+ .sw-rating {
173
+ display: grid;
174
+ gap: 0.45rem;
175
+ }
176
+
177
+ .sw-rating-button {
178
+ border: 1px solid var(--sw-color-border);
179
+ border-radius: 0.3rem;
180
+ background: transparent;
181
+ color: #94a3b8;
182
+ margin-right: 0.35rem;
183
+ }
184
+
185
+ .sw-rating-button.is-active {
186
+ color: #f59e0b;
187
+ border-color: #f59e0b;
188
+ }
189
+
190
+ .sw-diagnostics-note {
191
+ border: 1px dashed var(--sw-color-border);
192
+ border-radius: 0.55rem;
193
+ font-size: 0.8rem;
194
+ color: var(--sw-color-text-muted);
195
+ padding: 0.65rem;
196
+ }
197
+
198
+ .sw-error {
199
+ color: var(--sw-color-danger);
200
+ margin: 0;
201
+ }
202
+
203
+ .sw-actions {
204
+ display: flex;
205
+ gap: 0.75rem;
206
+ }
207
+
208
+ .sw-actions button {
209
+ flex: 1;
210
+ border: 1px solid var(--sw-color-border);
211
+ border-radius: 0.5rem;
212
+ padding: 0.6rem;
213
+ font: inherit;
214
+ background: #ffffff;
215
+ }
216
+
217
+ .sw-actions button[type='submit'] {
218
+ color: #ffffff;
219
+ background: var(--sw-color-primary);
220
+ border-color: var(--sw-color-primary);
221
+ }
222
+
223
+ .sw-actions button[type='submit']:hover {
224
+ background: var(--sw-color-primary-hover);
225
+ }
226
+
227
+ .sw-actions button:disabled {
228
+ opacity: 0.5;
229
+ cursor: not-allowed;
230
+ }
231
+
232
+ .sw-success {
233
+ text-align: center;
234
+ padding: 3rem 1rem;
235
+ }
236
+
237
+ @media (max-width: 640px) {
238
+ .sw-type-grid,
239
+ .sw-row {
240
+ grid-template-columns: 1fr;
241
+ }
242
+
243
+ .sw-launcher {
244
+ right: 1rem;
245
+ bottom: 1rem;
246
+ }
247
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cohiva/support-widget",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Reusable Support Ticket widget with diagnostics capture and typed payload generation",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -63,6 +63,7 @@
63
63
  "react-dom": ">=18"
64
64
  },
65
65
  "dependencies": {
66
+ "lucide-react": "^1.7.0",
66
67
  "zod": "^3.24.1"
67
68
  },
68
69
  "devDependencies": {