@robotical/webapp-types 3.15.18 → 3.15.19

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.
@@ -1,5 +1,7 @@
1
+ import { FeedbackRating } from "../../../utils/feedback/submitFeedback";
1
2
  type Props = {
2
- otherInfoObject?: object;
3
+ otherInfoObject?: Record<string, unknown>;
4
+ rating?: FeedbackRating;
3
5
  };
4
- export default function DetailedFeedbackModal({ otherInfoObject }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export default function DetailedFeedbackModal({ otherInfoObject, rating, }: Props): import("react/jsx-runtime").JSX.Element;
5
7
  export {};
@@ -9,31 +9,156 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
12
48
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
- import { useState } from "react";
49
+ import { useRef, useState } from "react";
14
50
  import { useTranslation } from "react-i18next";
15
51
  import modalState from "../../../state-observables/modal/ModalState";
52
+ import { submitFeedback, } from "../../../utils/feedback/submitFeedback";
16
53
  import styles from "./styles.module.css";
17
54
  import SimpleButton from "../../disposables/buttons/SimpleButton";
18
55
  export default function DetailedFeedbackModal(_a) {
19
- var otherInfoObject = _a.otherInfoObject;
56
+ var _this = this;
57
+ var otherInfoObject = _a.otherInfoObject, rating = _a.rating;
20
58
  var t = useTranslation().t;
21
59
  var _b = useState(""), report = _b[0], setReport = _b[1];
22
60
  var _c = useState(""), email = _c[0], setEmail = _c[1];
23
61
  var _d = useState(false), reportSent = _d[0], setReportSent = _d[1];
24
- var onReport = function () {
25
- if (!report)
26
- return;
27
- if (!email) {
28
- var confirm_1 = window.confirm(t("detailed_feedback_modal.no_email_confirm") || "");
29
- if (!confirm_1)
30
- return;
31
- }
32
- var reportWithContact = { text: "".concat(email ? email : t('detailed_feedback_modal.anonymous', 'Anonymous'), ": ").concat(report) };
33
- // window.applicationManager.analyticsManager.logEvent("textfeedback", { ...reportWithContact, ...otherInfoObject });
34
- setReportSent(true);
35
- modalState.updateModalProps({ modalTitle: "🚀" });
36
- setTimeout(function () { return modalState.closeModal(); }, 1500);
37
- };
38
- return (_jsx("div", __assign({ className: styles.bugReportModalContainer }, { children: reportSent ? (t("detailed_feedback_modal.thank_you", "Thank you for your feedback!")) : (_jsxs(_Fragment, { children: [_jsxs("div", __assign({ className: styles.bugReportModalInfo }, { children: [t("detailed_feedback_modal.info", "Please help us improve! Please let us know if you spot any irregularities/bugs. "), _jsx("strong", __assign({ className: styles.bugReportModalInfoStrong }, { children: t("detailed_feedback_modal.info_strong", "You can optionally leave your contact details so we can reach out and help you") })), ". ", t("detailed_feedback_modal.thank_you", "Thank you!")] })), _jsxs("div", __assign({ className: styles.bugReportModalForm }, { children: [_jsx("textarea", { className: styles.bugReportModalTextInput, placeholder: t("detailed_feedback_modal.report_placeholder", "Your report...") || "Your report...", onChange: function (e) { return setReport(e.target.value); }, value: report }), _jsx("label", __assign({ className: styles.bugReportModalEmailLabel }, { children: t("detailed_feedback_modal.email_label", "Provide your email if you want us to get back to you with a solution!") })), _jsx("input", { type: "email", className: styles.bugReportModalEmailInput, placeholder: t("detailed_feedback_modal.email_placeholder", "Email (optional)") || "Email (optional)", onChange: function (e) { return setEmail(e.target.value); }, value: email })] })), _jsxs("div", __assign({ className: styles.bugReportModalButtons }, { children: [_jsx(SimpleButton, { onClick: function () { return modalState.closeModal(); }, title: t("detailed_feedback_modal.cancel", "Cancel") }), _jsx(SimpleButton, { onClick: onReport, title: t("detailed_feedback_modal.report", "Report") })] }))] })) })));
62
+ var _e = useState(false), isSubmitting = _e[0], setIsSubmitting = _e[1];
63
+ var hasSubmittedFeedback = useRef(false);
64
+ var submitRatingOnlyOnClose = function () { return __awaiter(_this, void 0, void 0, function () {
65
+ var error_1;
66
+ var _a, _b;
67
+ return __generator(this, function (_c) {
68
+ switch (_c.label) {
69
+ case 0:
70
+ if (!rating || hasSubmittedFeedback.current) {
71
+ return [2 /*return*/, true];
72
+ }
73
+ _c.label = 1;
74
+ case 1:
75
+ _c.trys.push([1, 3, , 4]);
76
+ return [4 /*yield*/, submitFeedback({
77
+ rating: rating,
78
+ context: otherInfoObject,
79
+ source: "emoji-modal",
80
+ })];
81
+ case 2:
82
+ _c.sent();
83
+ hasSubmittedFeedback.current = true;
84
+ return [2 /*return*/, true];
85
+ case 3:
86
+ error_1 = _c.sent();
87
+ (_b = (_a = window.applicationManager) === null || _a === void 0 ? void 0 : _a.toaster) === null || _b === void 0 ? void 0 : _b.error("There was an error sending your feedback. Please try again later.");
88
+ return [2 /*return*/, false];
89
+ case 4: return [2 /*return*/];
90
+ }
91
+ });
92
+ }); };
93
+ var onClose = function () { return __awaiter(_this, void 0, void 0, function () {
94
+ var wasSubmitted;
95
+ return __generator(this, function (_a) {
96
+ switch (_a.label) {
97
+ case 0:
98
+ if (isSubmitting) {
99
+ return [2 /*return*/];
100
+ }
101
+ if (!rating || hasSubmittedFeedback.current) {
102
+ modalState.closeModal();
103
+ return [2 /*return*/];
104
+ }
105
+ setIsSubmitting(true);
106
+ return [4 /*yield*/, submitRatingOnlyOnClose()];
107
+ case 1:
108
+ wasSubmitted = _a.sent();
109
+ setIsSubmitting(false);
110
+ if (wasSubmitted) {
111
+ modalState.closeModal();
112
+ }
113
+ return [2 /*return*/];
114
+ }
115
+ });
116
+ }); };
117
+ var onReport = function () { return __awaiter(_this, void 0, void 0, function () {
118
+ var confirm_1, error_2;
119
+ var _a, _b;
120
+ return __generator(this, function (_c) {
121
+ switch (_c.label) {
122
+ case 0:
123
+ if (!report || isSubmitting)
124
+ return [2 /*return*/];
125
+ if (!email) {
126
+ confirm_1 = window.confirm(t("detailed_feedback_modal.no_email_confirm") || "");
127
+ if (!confirm_1)
128
+ return [2 /*return*/];
129
+ }
130
+ setIsSubmitting(true);
131
+ _c.label = 1;
132
+ case 1:
133
+ _c.trys.push([1, 3, 4, 5]);
134
+ return [4 /*yield*/, submitFeedback({
135
+ rating: rating,
136
+ message: report,
137
+ email: email,
138
+ context: otherInfoObject,
139
+ source: "detailed-feedback",
140
+ })];
141
+ case 2:
142
+ _c.sent();
143
+ hasSubmittedFeedback.current = true;
144
+ setReportSent(true);
145
+ modalState.updateModalProps({ modalTitle: "🚀" });
146
+ setTimeout(function () { return modalState.closeModal(); }, 1500);
147
+ return [3 /*break*/, 5];
148
+ case 3:
149
+ error_2 = _c.sent();
150
+ (_b = (_a = window.applicationManager) === null || _a === void 0 ? void 0 : _a.toaster) === null || _b === void 0 ? void 0 : _b.error("There was an error sending your feedback. Please try again later.");
151
+ return [3 /*break*/, 5];
152
+ case 4:
153
+ setIsSubmitting(false);
154
+ return [7 /*endfinally*/];
155
+ case 5: return [2 /*return*/];
156
+ }
157
+ });
158
+ }); };
159
+ return (_jsx("div", __assign({ className: styles.bugReportModalContainer }, { children: reportSent ? (t("detailed_feedback_modal.thank_you", "Thank you for your feedback!")) : (_jsxs(_Fragment, { children: [_jsxs("div", __assign({ className: styles.bugReportModalInfo }, { children: [t("detailed_feedback_modal.info", "Please help us improve! Please let us know if you spot any irregularities/bugs. "), _jsx("strong", __assign({ className: styles.bugReportModalInfoStrong }, { children: t("detailed_feedback_modal.info_strong", "You can optionally leave your contact details so we can reach out and help you") })), ". ", t("detailed_feedback_modal.thank_you", "Thank you!")] })), _jsxs("div", __assign({ className: styles.bugReportModalForm }, { children: [_jsx("textarea", { className: styles.bugReportModalTextInput, placeholder: t("detailed_feedback_modal.report_placeholder", "Your report...") || "Your report...", onChange: function (e) { return setReport(e.target.value); }, value: report }), _jsx("label", __assign({ className: styles.bugReportModalEmailLabel }, { children: t("detailed_feedback_modal.email_label", "Provide your email if you want us to get back to you with a solution!") })), _jsx("input", { type: "email", className: styles.bugReportModalEmailInput, placeholder: t("detailed_feedback_modal.email_placeholder", "Email (optional)") || "Email (optional)", onChange: function (e) { return setEmail(e.target.value); }, value: email })] })), _jsxs("div", __assign({ className: styles.bugReportModalButtons }, { children: [_jsx(SimpleButton, { onClick: function () {
160
+ void onClose();
161
+ }, title: t("detailed_feedback_modal.cancel", "Cancel"), disabled: isSubmitting }), _jsx(SimpleButton, { onClick: function () {
162
+ void onReport();
163
+ }, title: t("detailed_feedback_modal.report", "Report"), disabled: isSubmitting })] }))] })) })));
39
164
  }
@@ -0,0 +1,25 @@
1
+ export type FeedbackRating = 1 | 2 | 3 | 4 | 5;
2
+ export type FeedbackSource = "emoji-modal" | "detailed-feedback" | "help-bug-report";
3
+ export type SubmitFeedbackInput = {
4
+ rating?: FeedbackRating;
5
+ message?: string;
6
+ email?: string;
7
+ context?: Record<string, unknown> | null;
8
+ source: FeedbackSource;
9
+ };
10
+ export type FeedbackRecord = {
11
+ rating: FeedbackRating | null;
12
+ message: string | null;
13
+ email: string | null;
14
+ source: FeedbackSource;
15
+ context: Record<string, unknown> | null;
16
+ timestamp: string;
17
+ text: string;
18
+ };
19
+ export type SubmitFeedbackResult = {
20
+ record: FeedbackRecord;
21
+ slackDelivered: boolean;
22
+ };
23
+ export declare function buildFeedbackText(record: FeedbackRecord): string;
24
+ export declare function createFeedbackRecord(input: SubmitFeedbackInput): FeedbackRecord;
25
+ export declare function submitFeedback(input: SubmitFeedbackInput): Promise<SubmitFeedbackResult>;
@@ -0,0 +1,145 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ import Logger from "../../services/logger/Logger";
38
+ import randomHashGenerator from "../helpers/randomHashGenerator";
39
+ var SHOW_LOGS = true;
40
+ var TAG = "submitFeedback";
41
+ var WEB_APP_FEEDBACK_DB_URL = "https://web-app-bug-reports-default-rtdb.europe-west1.firebasedatabase.app/";
42
+ var WEB_APP_FEEDBACK_SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/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";
43
+ function normalizeOptionalString(value) {
44
+ var trimmedValue = value === null || value === void 0 ? void 0 : value.trim();
45
+ return trimmedValue ? trimmedValue : null;
46
+ }
47
+ export function buildFeedbackText(record) {
48
+ var lines = ["Source: ".concat(record.source)];
49
+ var page = record.context && typeof record.context.page === "string"
50
+ ? record.context.page
51
+ : null;
52
+ if (page) {
53
+ lines.push("Page: ".concat(page));
54
+ }
55
+ if (record.context) {
56
+ var contextWithoutPage = Object.fromEntries(Object.entries(record.context).filter(function (_a) {
57
+ var key = _a[0];
58
+ return key !== "page";
59
+ }));
60
+ if (Object.keys(contextWithoutPage).length > 0) {
61
+ lines.push("Context: ".concat(JSON.stringify(contextWithoutPage)));
62
+ }
63
+ }
64
+ if (record.rating) {
65
+ lines.push("Rating: ".concat(record.rating, "/5"));
66
+ }
67
+ lines.push("Email: ".concat(record.email || "Anonymous"));
68
+ if (record.message) {
69
+ lines.push("Message: ".concat(record.message));
70
+ }
71
+ return lines.join("\n");
72
+ }
73
+ export function createFeedbackRecord(input) {
74
+ var _a;
75
+ var normalizedMessage = normalizeOptionalString(input.message);
76
+ var normalizedEmail = normalizeOptionalString(input.email);
77
+ var timestamp = new Date().toISOString();
78
+ var record = {
79
+ rating: (_a = input.rating) !== null && _a !== void 0 ? _a : null,
80
+ message: normalizedMessage,
81
+ email: normalizedEmail,
82
+ source: input.source,
83
+ context: input.context || null,
84
+ timestamp: timestamp,
85
+ text: "",
86
+ };
87
+ record.text = buildFeedbackText(record);
88
+ return record;
89
+ }
90
+ export function submitFeedback(input) {
91
+ return __awaiter(this, void 0, void 0, function () {
92
+ var record, firebaseResponse, errorText, slackDelivered, slackResponse, errorText, error_1;
93
+ return __generator(this, function (_a) {
94
+ switch (_a.label) {
95
+ case 0:
96
+ record = createFeedbackRecord(input);
97
+ return [4 /*yield*/, fetch("".concat(WEB_APP_FEEDBACK_DB_URL).concat(randomHashGenerator(), ".json"), {
98
+ method: "PATCH",
99
+ headers: {
100
+ Application: "application/json",
101
+ "Content-Type": "application/json",
102
+ },
103
+ body: JSON.stringify(record),
104
+ })];
105
+ case 1:
106
+ firebaseResponse = _a.sent();
107
+ if (!!firebaseResponse.ok) return [3 /*break*/, 3];
108
+ return [4 /*yield*/, firebaseResponse.text()];
109
+ case 2:
110
+ errorText = _a.sent();
111
+ Logger.error(SHOW_LOGS, TAG, "Failed to submit feedback to Firebase: ".concat(firebaseResponse.status, " ").concat(errorText));
112
+ throw new Error("Failed to submit feedback");
113
+ case 3:
114
+ slackDelivered = true;
115
+ _a.label = 4;
116
+ case 4:
117
+ _a.trys.push([4, 8, , 9]);
118
+ return [4 /*yield*/, fetch(WEB_APP_FEEDBACK_SLACK_WEBHOOK_URL, {
119
+ method: "POST",
120
+ headers: {
121
+ "Content-Type": "application/json",
122
+ },
123
+ body: JSON.stringify({ text: record.text }),
124
+ })];
125
+ case 5:
126
+ slackResponse = _a.sent();
127
+ if (!!slackResponse.ok) return [3 /*break*/, 7];
128
+ return [4 /*yield*/, slackResponse.text()];
129
+ case 6:
130
+ errorText = _a.sent();
131
+ throw new Error("Failed Slack response: ".concat(slackResponse.status, " ").concat(errorText));
132
+ case 7: return [3 /*break*/, 9];
133
+ case 8:
134
+ error_1 = _a.sent();
135
+ slackDelivered = false;
136
+ Logger.error(SHOW_LOGS, TAG, "Failed to submit feedback to Slack", error_1);
137
+ return [3 /*break*/, 9];
138
+ case 9: return [2 /*return*/, {
139
+ record: record,
140
+ slackDelivered: slackDelivered,
141
+ }];
142
+ }
143
+ });
144
+ });
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robotical/webapp-types",
3
- "version": "3.15.18",
3
+ "version": "3.15.19",
4
4
  "description": "Type definitions for the Application Manager",
5
5
  "main": "dist/application-manager.d.ts",
6
6
  "types": "dist/application-manager.d.ts",