@sailfish-ai/recorder 1.10.2 → 1.10.4
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/chunks/fiberHook-CEzmPkx_.js +125 -0
- package/dist/chunks/fiberHook-CEzmPkx_.js.br +0 -0
- package/dist/chunks/fiberHook-CEzmPkx_.js.gz +0 -0
- package/dist/chunks/fiberHook-DGANQ2ma.js +130 -0
- package/dist/chunks/fiberHook-DGANQ2ma.js.br +0 -0
- package/dist/chunks/fiberHook-DGANQ2ma.js.gz +0 -0
- package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js +181 -0
- package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js.br +0 -0
- package/dist/chunks/rrweb-plugin-console-record-BmAm-Ih_.js.gz +0 -0
- package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js +180 -0
- package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js.br +0 -0
- package/dist/chunks/rrweb-plugin-console-record-Cr-osXuj.js.gz +0 -0
- package/dist/chunks/rrweb-record-only-Ba4xyfd6.js +5253 -0
- package/dist/chunks/rrweb-record-only-Ba4xyfd6.js.br +0 -0
- package/dist/chunks/rrweb-record-only-Ba4xyfd6.js.gz +0 -0
- package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js +5253 -0
- package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js.br +0 -0
- package/dist/chunks/rrweb-record-only-C5Qb-uaQ.js.gz +0 -0
- package/dist/inAppReportIssueModal/index.js +171 -129
- package/dist/inAppReportIssueModal/integrations.js +84 -19
- package/dist/inAppReportIssueModal/state.js +1 -0
- package/dist/inAppReportIssueModal/types.js +1 -0
- package/dist/inAppReportIssueModal/ui.js +9 -0
- package/dist/index.js +259 -60
- package/dist/recorder.cjs +1954 -7344
- package/dist/recorder.js +1953 -7344
- package/dist/recorder.js.br +0 -0
- package/dist/recorder.js.gz +0 -0
- package/dist/recording.js +41 -32
- package/dist/session.js +12 -6
- package/dist/types/inAppReportIssueModal/integrations.d.ts +8 -0
- package/dist/types/inAppReportIssueModal/types.d.ts +3 -4
- package/dist/types/index.d.ts +11 -3
- package/dist/types/recording.d.ts +2 -2
- package/dist/types/session.d.ts +1 -0
- package/dist/types/websocket.d.ts +1 -0
- package/dist/websocket.js +11 -10
- package/package.json +1 -1
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createTriageAndIssueFromRecorder, createTriageFromRecorder, } from "../graphql";
|
|
2
|
-
import { getFieldsForProject, getIntegrationData, getProjectsForTeam, getUsers, hasValidIntegration, updateFormWithIntegrationData, updateIssueTypeOptions, } from "./integrations";
|
|
2
|
+
import { getFieldsForProject, getIntegrationData, getProjectsForTeam, getSprintFieldId, getUsers, hasValidIntegration, refreshIntegrationData, updateFormWithIntegrationData, updateIssueTypeOptions, } from "./integrations";
|
|
3
3
|
import { currentState, isRecording, recordingEndTime, recordingStartTime, resetState, setIsRecording, setRecordingEndTime, setRecordingStartTime, setTimerInterval, timerInterval, } from "./state";
|
|
4
4
|
import { STORAGE_KEYS } from "./types";
|
|
5
5
|
import { getChevronSVG, renderCustomMultiSelect, renderDynamicField, } from "./ui";
|
|
@@ -174,6 +174,23 @@ function generateEngTicketFieldsHTML() {
|
|
|
174
174
|
integrationData.labels.length > 0) {
|
|
175
175
|
fieldsHTML += renderCustomMultiSelect("sf-eng-ticket-labels", "Labels", integrationData.labels, currentState.engTicketLabels, false);
|
|
176
176
|
}
|
|
177
|
+
// Sprint field (only for Jira when sprints are available)
|
|
178
|
+
if (isJira &&
|
|
179
|
+
integrationData.sprints &&
|
|
180
|
+
Array.isArray(integrationData.sprints) &&
|
|
181
|
+
integrationData.sprints.length > 0) {
|
|
182
|
+
fieldsHTML += `
|
|
183
|
+
<div>
|
|
184
|
+
<label for="sf-eng-ticket-sprint" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
|
|
185
|
+
Sprint
|
|
186
|
+
</label>
|
|
187
|
+
<select id="sf-eng-ticket-sprint"
|
|
188
|
+
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white; color: #9ca3af;">
|
|
189
|
+
<option value="" disabled selected style="color: #9ca3af;">Select sprint...</option>
|
|
190
|
+
</select>
|
|
191
|
+
</div>
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
177
194
|
// Dynamic Fields Container
|
|
178
195
|
fieldsHTML += `
|
|
179
196
|
<div id="sf-dynamic-fields-container" style="display: flex; flex-direction: column; gap: 12px;"></div>
|
|
@@ -733,6 +750,32 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
733
750
|
// No integration, disable eng ticket
|
|
734
751
|
currentState.createEngTicket = false;
|
|
735
752
|
}
|
|
753
|
+
// Background refresh — silently re-fetch integration data so cloud
|
|
754
|
+
// changes are picked up without a full page reload.
|
|
755
|
+
if (ReportIssueContext.apiKey && ReportIssueContext.backendApi) {
|
|
756
|
+
refreshIntegrationData(ReportIssueContext.apiKey, ReportIssueContext.backendApi).then((fresh) => {
|
|
757
|
+
if (!fresh || !document.getElementById("sf-report-issue-modal"))
|
|
758
|
+
return;
|
|
759
|
+
ReportIssueContext.integrationData = fresh;
|
|
760
|
+
// Re-generate eng ticket fields HTML now that integration data is available
|
|
761
|
+
const container = document.getElementById("sf-eng-ticket-fields-container");
|
|
762
|
+
if (container) {
|
|
763
|
+
const newFieldsHTML = generateEngTicketFieldsHTML();
|
|
764
|
+
if (newFieldsHTML) {
|
|
765
|
+
container.innerHTML = newFieldsHTML;
|
|
766
|
+
initializeEngTicketForm();
|
|
767
|
+
bindEngTicketListeners();
|
|
768
|
+
updateFormWithIntegrationData(currentState);
|
|
769
|
+
renderDynamicFields(currentState.engTicketProject, currentState.engTicketIssueType);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// If integration just became available, show the checkbox
|
|
773
|
+
const label = document.getElementById("sf-create-eng-ticket-label");
|
|
774
|
+
if (label) {
|
|
775
|
+
label.style.display = "flex";
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
}
|
|
736
779
|
}
|
|
737
780
|
function initializeEngTicketForm() {
|
|
738
781
|
const integrationData = ReportIssueContext.integrationData;
|
|
@@ -834,6 +877,125 @@ function updateModeSpecificUI(mode) {
|
|
|
834
877
|
submitBtn.style.cursor = "pointer";
|
|
835
878
|
}
|
|
836
879
|
}
|
|
880
|
+
function bindEngTicketListeners() {
|
|
881
|
+
const engTeamSelect = document.getElementById("sf-eng-ticket-team");
|
|
882
|
+
const engProjectSelect = document.getElementById("sf-eng-ticket-project");
|
|
883
|
+
const engPrioritySelect = document.getElementById("sf-eng-ticket-priority");
|
|
884
|
+
const engLabelsContainer = document.getElementById("sf-eng-ticket-labels-container");
|
|
885
|
+
const engTypeSelect = document.getElementById("sf-eng-ticket-type");
|
|
886
|
+
const engSprintSelect = document.getElementById("sf-eng-ticket-sprint");
|
|
887
|
+
if (engTeamSelect) {
|
|
888
|
+
engTeamSelect.addEventListener("change", () => {
|
|
889
|
+
currentState.engTicketTeam = engTeamSelect.value;
|
|
890
|
+
engTeamSelect.style.color = engTeamSelect.value ? "" : "#9ca3af";
|
|
891
|
+
// Update projects dropdown when team changes (for Linear)
|
|
892
|
+
const projectSelect = document.getElementById("sf-eng-ticket-project");
|
|
893
|
+
if (projectSelect) {
|
|
894
|
+
currentState.engTicketProject = "";
|
|
895
|
+
currentState.engTicketCustomFields = {};
|
|
896
|
+
const projects = getProjectsForTeam(engTeamSelect.value);
|
|
897
|
+
projectSelect.innerHTML =
|
|
898
|
+
'<option value="">Select project...</option>';
|
|
899
|
+
projects.forEach((project) => {
|
|
900
|
+
const optionElement = document.createElement("option");
|
|
901
|
+
optionElement.value = project.id || project.value || project;
|
|
902
|
+
optionElement.textContent =
|
|
903
|
+
project.name || project.label || project;
|
|
904
|
+
projectSelect.appendChild(optionElement);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
if (engProjectSelect) {
|
|
910
|
+
engProjectSelect.addEventListener("change", () => {
|
|
911
|
+
currentState.engTicketProject = engProjectSelect.value;
|
|
912
|
+
engProjectSelect.style.color = engProjectSelect.value ? "" : "#9ca3af";
|
|
913
|
+
currentState.engTicketCustomFields = {};
|
|
914
|
+
const integrationData = getIntegrationData();
|
|
915
|
+
if (integrationData && engTypeSelect) {
|
|
916
|
+
updateIssueTypeOptions(engTypeSelect, engProjectSelect.value);
|
|
917
|
+
currentState.engTicketIssueType = engTypeSelect.value;
|
|
918
|
+
}
|
|
919
|
+
if (integrationData &&
|
|
920
|
+
integrationData.provider?.toLowerCase() === "jira" &&
|
|
921
|
+
integrationData.jiraReporterAccountId &&
|
|
922
|
+
engProjectSelect.value) {
|
|
923
|
+
const fields = getFieldsForProject(engProjectSelect.value, currentState.engTicketIssueType);
|
|
924
|
+
const reporterField = fields.find((f) => f.fieldId === "reporter");
|
|
925
|
+
if (reporterField) {
|
|
926
|
+
currentState.engTicketCustomFields["reporter"] =
|
|
927
|
+
integrationData.jiraReporterAccountId;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
renderDynamicFields(engProjectSelect.value, currentState.engTicketIssueType);
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
if (engSprintSelect) {
|
|
934
|
+
engSprintSelect.addEventListener("change", () => {
|
|
935
|
+
currentState.engTicketSprint = engSprintSelect.value;
|
|
936
|
+
engSprintSelect.style.color = engSprintSelect.value ? "" : "#9ca3af";
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
if (engPrioritySelect) {
|
|
940
|
+
engPrioritySelect.addEventListener("change", () => {
|
|
941
|
+
currentState.engTicketPriority = Number(engPrioritySelect.value);
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
if (engLabelsContainer) {
|
|
945
|
+
setupCustomMultiSelectListeners("sf-eng-ticket-labels", (selectedValues) => {
|
|
946
|
+
currentState.engTicketLabels = selectedValues;
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
if (engTypeSelect) {
|
|
950
|
+
engTypeSelect.addEventListener("change", () => {
|
|
951
|
+
currentState.engTicketIssueType = engTypeSelect.value;
|
|
952
|
+
engTypeSelect.style.color = engTypeSelect.value ? "" : "#9ca3af";
|
|
953
|
+
const projectSelect = document.getElementById("sf-eng-ticket-project");
|
|
954
|
+
if (projectSelect && projectSelect.value) {
|
|
955
|
+
const reporterValue = currentState.engTicketCustomFields["reporter"];
|
|
956
|
+
currentState.engTicketCustomFields = {};
|
|
957
|
+
if (reporterValue) {
|
|
958
|
+
currentState.engTicketCustomFields["reporter"] = reporterValue;
|
|
959
|
+
}
|
|
960
|
+
renderDynamicFields(projectSelect.value, engTypeSelect.value);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
const dynamicFieldsContainer = document.getElementById("sf-dynamic-fields-container");
|
|
965
|
+
if (dynamicFieldsContainer) {
|
|
966
|
+
dynamicFieldsContainer.addEventListener("input", (e) => {
|
|
967
|
+
const target = e.target;
|
|
968
|
+
if (target.classList.contains("sf-dynamic-field")) {
|
|
969
|
+
const fieldId = target.dataset.fieldId;
|
|
970
|
+
if (fieldId) {
|
|
971
|
+
if (target.type === "checkbox") {
|
|
972
|
+
currentState.engTicketCustomFields[fieldId] = target.checked;
|
|
973
|
+
}
|
|
974
|
+
else if (target.type === "number") {
|
|
975
|
+
currentState.engTicketCustomFields[fieldId] =
|
|
976
|
+
parseFloat(target.value) || null;
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
currentState.engTicketCustomFields[fieldId] = target.value;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
dynamicFieldsContainer.addEventListener("change", (e) => {
|
|
985
|
+
const target = e.target;
|
|
986
|
+
if (target.classList.contains("sf-dynamic-field")) {
|
|
987
|
+
const fieldId = target.dataset.fieldId;
|
|
988
|
+
if (fieldId) {
|
|
989
|
+
currentState.engTicketCustomFields[fieldId] = target.value;
|
|
990
|
+
}
|
|
991
|
+
if (target.tagName === "SELECT") {
|
|
992
|
+
const selectElement = target;
|
|
993
|
+
selectElement.style.color = selectElement.value ? "" : "#9ca3af";
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
837
999
|
function bindListeners() {
|
|
838
1000
|
const tabButtons = modalEl?.querySelectorAll(".sf-issue-tab");
|
|
839
1001
|
const recordBtn = document.getElementById("sf-start-recording-btn");
|
|
@@ -985,134 +1147,7 @@ function bindListeners() {
|
|
|
985
1147
|
});
|
|
986
1148
|
}
|
|
987
1149
|
// Engineering ticket form fields
|
|
988
|
-
|
|
989
|
-
const engProjectSelect = document.getElementById("sf-eng-ticket-project");
|
|
990
|
-
const engPrioritySelect = document.getElementById("sf-eng-ticket-priority");
|
|
991
|
-
const engLabelsContainer = document.getElementById("sf-eng-ticket-labels-container");
|
|
992
|
-
const engTypeSelect = document.getElementById("sf-eng-ticket-type");
|
|
993
|
-
if (engTeamSelect) {
|
|
994
|
-
engTeamSelect.addEventListener("change", () => {
|
|
995
|
-
currentState.engTicketTeam = engTeamSelect.value;
|
|
996
|
-
// Update text color based on selection
|
|
997
|
-
engTeamSelect.style.color = engTeamSelect.value ? "" : "#9ca3af";
|
|
998
|
-
// Update projects dropdown when team changes (for Linear)
|
|
999
|
-
if (engProjectSelect) {
|
|
1000
|
-
const projects = getProjectsForTeam(engTeamSelect.value);
|
|
1001
|
-
const projectSelect = document.getElementById("sf-eng-ticket-project");
|
|
1002
|
-
if (projectSelect) {
|
|
1003
|
-
// Clear current selection
|
|
1004
|
-
currentState.engTicketProject = "";
|
|
1005
|
-
currentState.engTicketCustomFields = {};
|
|
1006
|
-
// Populate new projects
|
|
1007
|
-
projectSelect.innerHTML =
|
|
1008
|
-
'<option value="">Select project...</option>';
|
|
1009
|
-
projects.forEach((project) => {
|
|
1010
|
-
const optionElement = document.createElement("option");
|
|
1011
|
-
optionElement.value = project.id || project.value || project;
|
|
1012
|
-
optionElement.textContent =
|
|
1013
|
-
project.name || project.label || project;
|
|
1014
|
-
projectSelect.appendChild(optionElement);
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
if (engProjectSelect) {
|
|
1021
|
-
engProjectSelect.addEventListener("change", () => {
|
|
1022
|
-
currentState.engTicketProject = engProjectSelect.value;
|
|
1023
|
-
// Update text color based on selection
|
|
1024
|
-
engProjectSelect.style.color = engProjectSelect.value ? "" : "#9ca3af";
|
|
1025
|
-
// Clear custom fields when project changes
|
|
1026
|
-
currentState.engTicketCustomFields = {};
|
|
1027
|
-
// Update issue types when project changes
|
|
1028
|
-
const integrationData = getIntegrationData();
|
|
1029
|
-
if (integrationData && engTypeSelect) {
|
|
1030
|
-
updateIssueTypeOptions(engTypeSelect, engProjectSelect.value);
|
|
1031
|
-
// Update state with the selected/default issue type
|
|
1032
|
-
currentState.engTicketIssueType = engTypeSelect.value;
|
|
1033
|
-
}
|
|
1034
|
-
// Handle reporter field for Jira
|
|
1035
|
-
if (integrationData &&
|
|
1036
|
-
integrationData.provider?.toLowerCase() === "jira" &&
|
|
1037
|
-
integrationData.jiraReporterAccountId &&
|
|
1038
|
-
engProjectSelect.value) {
|
|
1039
|
-
const fields = getFieldsForProject(engProjectSelect.value, currentState.engTicketIssueType);
|
|
1040
|
-
const reporterField = fields.find((f) => f.fieldId === "reporter");
|
|
1041
|
-
if (reporterField) {
|
|
1042
|
-
currentState.engTicketCustomFields["reporter"] =
|
|
1043
|
-
integrationData.jiraReporterAccountId;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
// Render dynamic fields for the selected project and issue type
|
|
1047
|
-
renderDynamicFields(engProjectSelect.value, currentState.engTicketIssueType);
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
if (engPrioritySelect) {
|
|
1051
|
-
engPrioritySelect.addEventListener("change", () => {
|
|
1052
|
-
currentState.engTicketPriority = Number(engPrioritySelect.value);
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
if (engLabelsContainer) {
|
|
1056
|
-
// Set up event listeners for custom multiselect
|
|
1057
|
-
setupCustomMultiSelectListeners("sf-eng-ticket-labels", (selectedValues) => {
|
|
1058
|
-
currentState.engTicketLabels = selectedValues;
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
if (engTypeSelect) {
|
|
1062
|
-
engTypeSelect.addEventListener("change", () => {
|
|
1063
|
-
currentState.engTicketIssueType = engTypeSelect.value;
|
|
1064
|
-
// Update text color based on selection
|
|
1065
|
-
engTypeSelect.style.color = engTypeSelect.value ? "" : "#9ca3af";
|
|
1066
|
-
// Re-render dynamic fields when issue type changes
|
|
1067
|
-
const engProjectSelect = document.getElementById("sf-eng-ticket-project");
|
|
1068
|
-
if (engProjectSelect && engProjectSelect.value) {
|
|
1069
|
-
// Clear custom fields except reporter
|
|
1070
|
-
const reporterValue = currentState.engTicketCustomFields["reporter"];
|
|
1071
|
-
currentState.engTicketCustomFields = {};
|
|
1072
|
-
if (reporterValue) {
|
|
1073
|
-
currentState.engTicketCustomFields["reporter"] = reporterValue;
|
|
1074
|
-
}
|
|
1075
|
-
renderDynamicFields(engProjectSelect.value, engTypeSelect.value);
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
// Dynamic fields event delegation
|
|
1080
|
-
const dynamicFieldsContainer = document.getElementById("sf-dynamic-fields-container");
|
|
1081
|
-
if (dynamicFieldsContainer) {
|
|
1082
|
-
dynamicFieldsContainer.addEventListener("input", (e) => {
|
|
1083
|
-
const target = e.target;
|
|
1084
|
-
if (target.classList.contains("sf-dynamic-field")) {
|
|
1085
|
-
const fieldId = target.dataset.fieldId;
|
|
1086
|
-
if (fieldId) {
|
|
1087
|
-
// Handle different input types
|
|
1088
|
-
if (target.type === "checkbox") {
|
|
1089
|
-
currentState.engTicketCustomFields[fieldId] = target.checked;
|
|
1090
|
-
}
|
|
1091
|
-
else if (target.type === "number") {
|
|
1092
|
-
currentState.engTicketCustomFields[fieldId] =
|
|
1093
|
-
parseFloat(target.value) || null;
|
|
1094
|
-
}
|
|
1095
|
-
else {
|
|
1096
|
-
currentState.engTicketCustomFields[fieldId] = target.value;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
});
|
|
1101
|
-
dynamicFieldsContainer.addEventListener("change", (e) => {
|
|
1102
|
-
const target = e.target;
|
|
1103
|
-
if (target.classList.contains("sf-dynamic-field")) {
|
|
1104
|
-
const fieldId = target.dataset.fieldId;
|
|
1105
|
-
if (fieldId) {
|
|
1106
|
-
currentState.engTicketCustomFields[fieldId] = target.value;
|
|
1107
|
-
}
|
|
1108
|
-
// Update text color for select elements (to remove placeholder gray)
|
|
1109
|
-
if (target.tagName === "SELECT") {
|
|
1110
|
-
const selectElement = target;
|
|
1111
|
-
selectElement.style.color = selectElement.value ? "" : "#9ca3af";
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1150
|
+
bindEngTicketListeners();
|
|
1116
1151
|
// Start recording
|
|
1117
1152
|
if (recordBtn) {
|
|
1118
1153
|
recordBtn.onclick = () => {
|
|
@@ -1188,6 +1223,13 @@ function bindListeners() {
|
|
|
1188
1223
|
}
|
|
1189
1224
|
}
|
|
1190
1225
|
});
|
|
1226
|
+
// Add sprint to custom fields if selected
|
|
1227
|
+
const engSprintSelect = document.getElementById("sf-eng-ticket-sprint");
|
|
1228
|
+
const sprintValue = engSprintSelect?.value || currentState.engTicketSprint;
|
|
1229
|
+
if (sprintValue) {
|
|
1230
|
+
const sprintFieldId = getSprintFieldId();
|
|
1231
|
+
engTicketCustomFields[sprintFieldId] = parseInt(sprintValue, 10);
|
|
1232
|
+
}
|
|
1191
1233
|
closeModal();
|
|
1192
1234
|
// Create triage + issue (with optional engineering ticket)
|
|
1193
1235
|
createTriageAndIssue(`${startTimestamp}`, `${endTimestamp}`, desc, issueName, issueDescription, currentState.createEngTicket, engTicketTeam, engTicketProject, engTicketPriority, engTicketLabels, engTicketIssueType, engTicketCustomFields);
|
|
@@ -1,42 +1,63 @@
|
|
|
1
1
|
import { fetchEngineeringTicketPlatformIntegrations } from "../graphql";
|
|
2
2
|
const SUPPORT_TICKET_INTEGRATIONS = ["jira", "linear", "zendesk"];
|
|
3
3
|
let integrationData = null;
|
|
4
|
+
// Cache per cloud so switching between clouds is instant
|
|
5
|
+
const cloudCache = new Map();
|
|
4
6
|
export function getIntegrationData() {
|
|
5
7
|
return integrationData;
|
|
6
8
|
}
|
|
7
9
|
export function hasValidIntegration() {
|
|
8
10
|
return integrationData !== null && integrationData.installed === true;
|
|
9
11
|
}
|
|
12
|
+
function resolveIntegration(response) {
|
|
13
|
+
if (response?.errors && response.errors.length > 0) {
|
|
14
|
+
console.error("GraphQL errors fetching integrations:", response.errors);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const integrations = response?.data?.getEngineeringTicketPlatformIntegrationsFromApiKey;
|
|
18
|
+
const validIntegrations = (integrations || []).filter((item) => SUPPORT_TICKET_INTEGRATIONS.includes(item.provider?.toLowerCase() || "") && item.installed === true);
|
|
19
|
+
if (validIntegrations.length === 0) {
|
|
20
|
+
console.warn("No valid installed integrations found");
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// Prefer Jira integration, fallback to first available
|
|
24
|
+
const chosen = validIntegrations.find((i) => i.provider?.toLowerCase() === "jira") || validIntegrations[0];
|
|
25
|
+
// Cache by cloud ID for instant switching
|
|
26
|
+
if (chosen?.primaryCloudId) {
|
|
27
|
+
cloudCache.set(chosen.primaryCloudId, chosen);
|
|
28
|
+
}
|
|
29
|
+
return chosen;
|
|
30
|
+
}
|
|
10
31
|
export async function fetchIntegrationData(apiKey, backendApi) {
|
|
11
32
|
if (integrationData) {
|
|
12
33
|
return;
|
|
13
34
|
}
|
|
14
35
|
try {
|
|
15
36
|
const response = await fetchEngineeringTicketPlatformIntegrations(apiKey, backendApi);
|
|
16
|
-
|
|
17
|
-
if (response?.errors && response.errors.length > 0) {
|
|
18
|
-
console.error("GraphQL errors fetching integrations:", response.errors);
|
|
19
|
-
integrationData = null;
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const integrations = response?.data?.getEngineeringTicketPlatformIntegrationsFromApiKey;
|
|
23
|
-
// Filter for installed support ticket integrations only
|
|
24
|
-
const validIntegrations = (integrations || []).filter((item) => SUPPORT_TICKET_INTEGRATIONS.includes(item.provider?.toLowerCase() || "") && item.installed === true);
|
|
25
|
-
if (validIntegrations.length === 0) {
|
|
26
|
-
console.warn("No valid installed integrations found");
|
|
27
|
-
integrationData = null;
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
// Prefer Jira integration, fallback to first available
|
|
31
|
-
integrationData =
|
|
32
|
-
validIntegrations.find((i) => i.provider?.toLowerCase() === "jira") ||
|
|
33
|
-
validIntegrations[0];
|
|
37
|
+
integrationData = resolveIntegration(response);
|
|
34
38
|
}
|
|
35
39
|
catch (error) {
|
|
36
40
|
console.error("Error fetching integration data:", error);
|
|
37
41
|
integrationData = null;
|
|
38
42
|
}
|
|
39
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Re-fetch integration data in the background. Returns the fresh data
|
|
46
|
+
* and updates the module-level cache. Callers can compare with previous
|
|
47
|
+
* data to decide whether to refresh the UI.
|
|
48
|
+
*/
|
|
49
|
+
export async function refreshIntegrationData(apiKey, backendApi) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetchEngineeringTicketPlatformIntegrations(apiKey, backendApi);
|
|
52
|
+
const fresh = resolveIntegration(response);
|
|
53
|
+
integrationData = fresh;
|
|
54
|
+
return fresh;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error("Error refreshing integration data:", error);
|
|
58
|
+
return integrationData; // keep existing on error
|
|
59
|
+
}
|
|
60
|
+
}
|
|
40
61
|
export function populateSelectOptions(selectElement, options, defaultValue) {
|
|
41
62
|
const placeholderOption = document.createElement("option");
|
|
42
63
|
placeholderOption.value = "";
|
|
@@ -103,6 +124,45 @@ export function populatePriorityOptions(selectElement, provider, defaultPriority
|
|
|
103
124
|
selectElement.value = "0";
|
|
104
125
|
}
|
|
105
126
|
}
|
|
127
|
+
export function populateSprintOptions(selectElement, sprints, currentValue) {
|
|
128
|
+
selectElement.innerHTML = "";
|
|
129
|
+
// Placeholder option
|
|
130
|
+
const placeholderOption = document.createElement("option");
|
|
131
|
+
placeholderOption.value = "";
|
|
132
|
+
placeholderOption.disabled = true;
|
|
133
|
+
placeholderOption.selected = !currentValue;
|
|
134
|
+
placeholderOption.textContent = "Select sprint...";
|
|
135
|
+
placeholderOption.style.color = "#9ca3af";
|
|
136
|
+
selectElement.appendChild(placeholderOption);
|
|
137
|
+
// Only show active or future sprints
|
|
138
|
+
const activeSprints = (sprints || []).filter((s) => s.state === "active" || s.state === "future");
|
|
139
|
+
activeSprints.forEach((sprint) => {
|
|
140
|
+
const optionElement = document.createElement("option");
|
|
141
|
+
optionElement.value = String(sprint.id);
|
|
142
|
+
optionElement.textContent = sprint.name || sprint.id;
|
|
143
|
+
selectElement.appendChild(optionElement);
|
|
144
|
+
});
|
|
145
|
+
if (currentValue) {
|
|
146
|
+
selectElement.value = currentValue;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function getSprintFieldId() {
|
|
150
|
+
if (!integrationData?.fieldConfigurations) {
|
|
151
|
+
return "customfield_10020";
|
|
152
|
+
}
|
|
153
|
+
// Search field configurations for the sprint field
|
|
154
|
+
const configs = Array.isArray(integrationData.fieldConfigurations)
|
|
155
|
+
? integrationData.fieldConfigurations
|
|
156
|
+
: [];
|
|
157
|
+
for (const config of configs) {
|
|
158
|
+
const fields = config.fields || [];
|
|
159
|
+
const sprintField = fields.find((f) => f.schema?.custom === "com.pyxis.greenhopper.jira:gh-sprint");
|
|
160
|
+
if (sprintField?.fieldId) {
|
|
161
|
+
return sprintField.fieldId;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return "customfield_10020";
|
|
165
|
+
}
|
|
106
166
|
export function updateIssueTypeOptions(selectElement, projectId) {
|
|
107
167
|
if (!integrationData?.projects || !projectId) {
|
|
108
168
|
selectElement.innerHTML = "";
|
|
@@ -244,9 +304,14 @@ export function updateFormWithIntegrationData(currentState) {
|
|
|
244
304
|
currentState.engTicketPriority = Number(prioritySelect.value);
|
|
245
305
|
}
|
|
246
306
|
}
|
|
307
|
+
// Update sprint dropdown (for Jira)
|
|
308
|
+
const sprintSelect = document.getElementById("sf-eng-ticket-sprint");
|
|
309
|
+
const isJira = integrationData.provider?.toLowerCase() === "jira";
|
|
310
|
+
if (sprintSelect && isJira && integrationData.sprints) {
|
|
311
|
+
populateSprintOptions(sprintSelect, integrationData.sprints, currentState.engTicketSprint || undefined);
|
|
312
|
+
}
|
|
247
313
|
// Update issue type dropdown based on selected project (for Jira)
|
|
248
314
|
const issueTypeSelect = document.getElementById("sf-eng-ticket-type");
|
|
249
|
-
const isJira = integrationData.provider?.toLowerCase() === "jira";
|
|
250
315
|
if (issueTypeSelect && isJira && currentState.engTicketProject) {
|
|
251
316
|
updateIssueTypeOptions(issueTypeSelect, currentState.engTicketProject);
|
|
252
317
|
// Preserve existing value if available, otherwise use DOM value
|
|
@@ -412,6 +412,15 @@ export function generateModalHTML(currentState, initialMode = "lookback") {
|
|
|
412
412
|
value="${currentState.engTicketLabels.join(", ")}"
|
|
413
413
|
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none;">
|
|
414
414
|
</div>
|
|
415
|
+
<div>
|
|
416
|
+
<label for="sf-eng-ticket-sprint" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
|
|
417
|
+
Sprint
|
|
418
|
+
</label>
|
|
419
|
+
<select id="sf-eng-ticket-sprint"
|
|
420
|
+
style="width:100%; padding:8px 12px; font-size:14px; border:1px solid #cbd5e1; border-radius:6px; outline:none; appearance:none; cursor:pointer; background-color: white;">
|
|
421
|
+
<option value="">None</option>
|
|
422
|
+
</select>
|
|
423
|
+
</div>
|
|
415
424
|
<div>
|
|
416
425
|
<label for="sf-eng-ticket-type" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
|
|
417
426
|
Issue Type
|