mirror of
https://gh.wpcy.net/https://github.com/verygoodplugins/freescout-github.git
synced 2026-05-25 16:23:57 +08:00
1398 lines
No EOL
53 KiB
JavaScript
1398 lines
No EOL
53 KiB
JavaScript
/**
|
|
* GitHub Module JavaScript
|
|
* Following FreeScout Jira module pattern
|
|
*/
|
|
|
|
var GitHub = {
|
|
config: {
|
|
debounceDelay: 300,
|
|
searchMinLength: 2,
|
|
maxSearchResults: 10
|
|
},
|
|
cache: {
|
|
repositories: null
|
|
},
|
|
warningsShown: [] // Track warnings to prevent duplicates
|
|
};
|
|
|
|
function githubInitSettings() {
|
|
$(document).ready(function() {
|
|
// Show/hide OpenAI model dropdown based on AI service selection
|
|
function toggleOpenAIModel() {
|
|
var selectedService = $('#github_ai_service').val();
|
|
console.log('AI Service selected:', selectedService); // Debug log
|
|
|
|
if (selectedService === 'openai') {
|
|
$('#openai_model_group').show();
|
|
console.log('Showing OpenAI model group'); // Debug log
|
|
} else {
|
|
$('#openai_model_group').hide();
|
|
console.log('Hiding OpenAI model group'); // Debug log
|
|
}
|
|
}
|
|
|
|
// Bind the change event for AI service dropdown
|
|
$('#github_ai_service').on('change', toggleOpenAIModel);
|
|
|
|
// Set initial state with a small delay to ensure DOM is fully loaded
|
|
setTimeout(function() {
|
|
toggleOpenAIModel();
|
|
}, 100);
|
|
|
|
// Also trigger on page load in case the value is pre-selected
|
|
$(window).on('load', function() {
|
|
toggleOpenAIModel();
|
|
});
|
|
|
|
// Test connection button
|
|
$("#test-connection").click(function(e) {
|
|
e.preventDefault();
|
|
var button = $(this);
|
|
var token = $('#github_token').val();
|
|
|
|
if (!token) {
|
|
showFloatingAlert('error', 'Please enter a GitHub token first');
|
|
return;
|
|
}
|
|
|
|
button.button('loading');
|
|
|
|
fsAjax({
|
|
token: token
|
|
},
|
|
laroute.route('github.test_connection'),
|
|
function(response) {
|
|
button.button('reset');
|
|
if (isAjaxSuccess(response)) {
|
|
githubShowConnectionResult(response);
|
|
if (response.repositories) {
|
|
githubPopulateRepositories(response.repositories);
|
|
}
|
|
} else {
|
|
githubShowConnectionResult(response);
|
|
}
|
|
}, true, function(response) {
|
|
button.button('reset');
|
|
showFloatingAlert('error', Lang.get("messages.ajax_error"));
|
|
});
|
|
});
|
|
|
|
// Refresh repositories button
|
|
$("#refresh-repositories").click(function(e) {
|
|
e.preventDefault();
|
|
githubLoadRepositories();
|
|
});
|
|
|
|
// Refresh allowed labels button
|
|
$("#refresh-allowed-labels").click(function(e) {
|
|
e.preventDefault();
|
|
githubLoadAllowedLabels();
|
|
});
|
|
|
|
// Repository change handler
|
|
$("#github_default_repository").change(function() {
|
|
var repository = $(this).val();
|
|
if (repository) {
|
|
githubLoadLabelMappings(repository);
|
|
$('#label-mapping-section').show();
|
|
// Also load allowed labels when repository changes
|
|
githubLoadAllowedLabels();
|
|
} else {
|
|
$('#label-mapping-section').hide();
|
|
}
|
|
});
|
|
|
|
// Initialize Select2 on allowed labels field
|
|
var $allowedLabelsSelect = $('#github_allowed_labels');
|
|
if ($allowedLabelsSelect.length > 0) {
|
|
// Initialize with basic Select2 first
|
|
$allowedLabelsSelect.select2({
|
|
placeholder: $allowedLabelsSelect.attr('data-placeholder') || 'Select allowed labels...',
|
|
allowClear: false,
|
|
width: '100%'
|
|
});
|
|
}
|
|
|
|
// Load allowed labels on page load if repository is already selected
|
|
var defaultRepo = $("#github_default_repository").val();
|
|
if (defaultRepo) {
|
|
githubLoadAllowedLabels();
|
|
}
|
|
|
|
// Add label mapping
|
|
$("#add-label-mapping").click(function(e) {
|
|
e.preventDefault();
|
|
githubAddLabelMappingRow();
|
|
});
|
|
|
|
// Remove label mapping
|
|
$(document).on('click', '.remove-mapping', function(e) {
|
|
e.preventDefault();
|
|
$(this).closest('.label-mapping-row').remove();
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubInitConversation() {
|
|
$(document).ready(function() {
|
|
// Issue actions
|
|
$(document).on('click', '.github-issue-action', function(e) {
|
|
e.preventDefault();
|
|
var link = $(this);
|
|
var action = link.data('action');
|
|
var issueId = link.data('issue-id');
|
|
|
|
if (action === 'unlink') {
|
|
if (confirm('Are you sure you want to unlink this issue?')) {
|
|
githubUnlinkIssue(issueId);
|
|
}
|
|
} else if (action === 'refresh') {
|
|
githubRefreshIssue(issueId);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubInitModals() {
|
|
$(document).ready(function() {
|
|
// Create issue modal
|
|
$('#github-create-issue-modal').on('show.bs.modal', function() {
|
|
// Move modal to body to avoid z-index stacking context issues
|
|
if ($(this).parent().get(0) !== document.body) {
|
|
$(this).detach().appendTo('body');
|
|
}
|
|
|
|
// Only populate repositories if we don't have a default repository
|
|
// Most users will use the default repository, so avoid unnecessary API calls
|
|
var defaultRepo = GitHub.defaultRepository;
|
|
if (defaultRepo) {
|
|
// Just populate with the default repository to avoid API call
|
|
githubPopulateRepositories([{full_name: defaultRepo, name: defaultRepo.split('/')[1], has_issues: true}]);
|
|
} else {
|
|
// Only load all repositories if no default is set
|
|
if (GitHub.cache.repositories && GitHub.cache.repositories.length > 0) {
|
|
githubPopulateRepositories(GitHub.cache.repositories);
|
|
} else {
|
|
var cachedRepos = githubGetCachedRepositories();
|
|
if (cachedRepos) {
|
|
githubPopulateRepositories(cachedRepos);
|
|
} else {
|
|
githubLoadRepositories();
|
|
}
|
|
}
|
|
}
|
|
$('#github-create-issue-form')[0].reset();
|
|
|
|
// Initialize labels multiselect with Select2
|
|
var labelsSelect = $('#github-issue-labels');
|
|
if (!labelsSelect.hasClass('select2-hidden-accessible')) {
|
|
labelsSelect.select2({
|
|
placeholder: 'Select labels...',
|
|
allowClear: true,
|
|
closeOnSelect: false,
|
|
width: '100%',
|
|
dropdownParent: $('#github-create-issue-modal'),
|
|
dropdownCssClass: 'github-select2-dropdown' // Custom class for z-index fix
|
|
});
|
|
} else {
|
|
// Clear selection after form reset
|
|
labelsSelect.val(null).trigger('change');
|
|
}
|
|
|
|
// Restore default repository after form reset
|
|
setTimeout(function() {
|
|
githubSetDefaultRepository('#github-repository');
|
|
|
|
// Auto-generate content if fields are empty
|
|
if (!$('#github-issue-title').val() && !$('#github-issue-body').val()) {
|
|
githubGenerateIssueContent();
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
// Link issue modal
|
|
$('#github-link-issue-modal').on('show.bs.modal', function() {
|
|
// Move modal to body to avoid z-index stacking context issues
|
|
if ($(this).parent().get(0) !== document.body) {
|
|
$(this).detach().appendTo('body');
|
|
}
|
|
|
|
// Only populate repositories if we don't have a default repository
|
|
// Most users will use the default repository, so avoid unnecessary API calls
|
|
var defaultRepo = GitHub.defaultRepository;
|
|
if (defaultRepo) {
|
|
// Just populate with the default repository to avoid API call
|
|
githubPopulateRepositories([{full_name: defaultRepo, name: defaultRepo.split('/')[1], has_issues: true}]);
|
|
} else {
|
|
// Only load all repositories if no default is set
|
|
if (GitHub.cache.repositories && GitHub.cache.repositories.length > 0) {
|
|
githubPopulateRepositories(GitHub.cache.repositories);
|
|
} else {
|
|
var cachedRepos = githubGetCachedRepositories();
|
|
if (cachedRepos) {
|
|
githubPopulateRepositories(cachedRepos);
|
|
} else {
|
|
githubLoadRepositories();
|
|
}
|
|
}
|
|
}
|
|
$('#github-link-issue-form')[0].reset();
|
|
$('#github-search-results').hide();
|
|
// Restore default repository after form reset
|
|
setTimeout(function() {
|
|
githubSetDefaultRepository('#github-link-repository');
|
|
}, 10);
|
|
});
|
|
|
|
// Repository change in create modal
|
|
$(document).on('change', '#github-repository', function() {
|
|
var repository = $(this).val();
|
|
if (repository) {
|
|
githubLoadRepositoryLabels(repository);
|
|
}
|
|
});
|
|
|
|
|
|
// Manual generate content button
|
|
$(document).on('click', '#github-generate-content-btn', function(e) {
|
|
e.preventDefault();
|
|
githubGenerateIssueContent();
|
|
});
|
|
|
|
// Issue search
|
|
var searchTimeout;
|
|
$(document).on('input', '#github-issue-search', function() {
|
|
clearTimeout(searchTimeout);
|
|
var query = $(this).val();
|
|
var repository = $('#github-link-repository').val();
|
|
|
|
searchTimeout = setTimeout(function() {
|
|
if (repository && query.length >= GitHub.config.searchMinLength) {
|
|
githubSearchIssues(repository, query);
|
|
} else {
|
|
$('#github-search-results').hide();
|
|
}
|
|
}, GitHub.config.debounceDelay);
|
|
});
|
|
|
|
// Select search result
|
|
$(document).on('click', '.github-search-result-item', function() {
|
|
var issueNumber = $(this).data('issue-number');
|
|
$('#github-issue-number').val(issueNumber);
|
|
$('#github-search-results').hide();
|
|
});
|
|
|
|
// Create issue button
|
|
$(document).on('click', '#github-create-issue-btn', function(e) {
|
|
e.preventDefault();
|
|
var button = $(this);
|
|
button.button('loading');
|
|
|
|
var data = new FormData();
|
|
var form = $('#github-create-issue-form').serializeArray();
|
|
for (var field in form) {
|
|
data.append(form[field].name, form[field].value);
|
|
}
|
|
data.append('conversation_id', getGlobalAttr('conversation_id'));
|
|
|
|
fsAjax(
|
|
data,
|
|
laroute.route('github.create_issue'),
|
|
function(response) {
|
|
button.button('reset');
|
|
if (isAjaxSuccess(response)) {
|
|
$('#github-create-issue-modal').modal('hide');
|
|
window.location.href = '';
|
|
} else {
|
|
githubShowAjaxError(response);
|
|
}
|
|
}, true,
|
|
function(xhr) {
|
|
button.button('reset');
|
|
githubShowAjaxError(xhr.responseJSON || {message: Lang.get("messages.ajax_error")});
|
|
}, {
|
|
cache: false,
|
|
contentType: false,
|
|
processData: false
|
|
}
|
|
);
|
|
});
|
|
|
|
// Link issue button
|
|
$(document).on('click', '#github-link-issue-btn', function(e) {
|
|
e.preventDefault();
|
|
var button = $(this);
|
|
button.button('loading');
|
|
|
|
var data = new FormData();
|
|
var form = $('#github-link-issue-form').serializeArray();
|
|
for (var field in form) {
|
|
data.append(form[field].name, form[field].value);
|
|
}
|
|
data.append('conversation_id', getGlobalAttr('conversation_id'));
|
|
|
|
fsAjax(
|
|
data,
|
|
laroute.route('github.link_issue'),
|
|
function(response) {
|
|
button.button('reset');
|
|
if (isAjaxSuccess(response)) {
|
|
$('#github-link-issue-modal').modal('hide');
|
|
window.location.href = '';
|
|
} else {
|
|
githubShowAjaxError(response);
|
|
}
|
|
}, true,
|
|
function(xhr) {
|
|
button.button('reset');
|
|
githubShowAjaxError(xhr.responseJSON || {message: Lang.get("messages.ajax_error")});
|
|
}, {
|
|
cache: false,
|
|
contentType: false,
|
|
processData: false
|
|
}
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubLoadRepositories() {
|
|
var $loadingDiv = $('#github-repositories-loading');
|
|
var $refreshBtn = $('#refresh-repositories');
|
|
|
|
// Show loading indicator
|
|
$loadingDiv.show();
|
|
$refreshBtn.find('.glyphicon').addClass('glyphicon-spin');
|
|
|
|
fsAjax({},
|
|
laroute.route('github.repositories'),
|
|
function(response) {
|
|
if (isAjaxSuccess(response)) {
|
|
githubPopulateRepositories(response.repositories);
|
|
|
|
// Cache repositories in localStorage with timestamp
|
|
var cacheData = {
|
|
repositories: response.repositories,
|
|
timestamp: Date.now(),
|
|
token_hash: $('#github_token').val() ? btoa($('#github_token').val()).slice(-8) : null // Last 8 chars of token for validation
|
|
};
|
|
localStorage.setItem('github_repositories_cache', JSON.stringify(cacheData));
|
|
} else {
|
|
showFloatingAlert('error', 'Failed to load repositories: ' + (response.message || 'Unknown error'));
|
|
}
|
|
$loadingDiv.hide();
|
|
$refreshBtn.find('.glyphicon').removeClass('glyphicon-spin');
|
|
}, true, function() {
|
|
// Error callback
|
|
$loadingDiv.hide();
|
|
$refreshBtn.find('.glyphicon').removeClass('glyphicon-spin');
|
|
showFloatingAlert('error', 'Failed to load repositories');
|
|
});
|
|
}
|
|
|
|
// Check if we have cached repositories
|
|
function githubGetCachedRepositories() {
|
|
try {
|
|
var cached = localStorage.getItem('github_repositories_cache');
|
|
if (!cached) return null;
|
|
|
|
var cacheData = JSON.parse(cached);
|
|
var currentTokenHash = $('#github_token').val() ? btoa($('#github_token').val()).slice(-8) : null;
|
|
|
|
// Check if cache is less than 1 hour old and token matches
|
|
var maxAge = 60 * 60 * 1000; // 1 hour
|
|
var isValid = (Date.now() - cacheData.timestamp) < maxAge &&
|
|
cacheData.token_hash === currentTokenHash &&
|
|
cacheData.repositories && cacheData.repositories.length > 0;
|
|
|
|
if (isValid) {
|
|
return cacheData.repositories;
|
|
} else {
|
|
localStorage.removeItem('github_repositories_cache');
|
|
return null;
|
|
}
|
|
} catch (e) {
|
|
localStorage.removeItem('github_repositories_cache');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Helper function to set default repository from DOM data
|
|
function githubSetDefaultRepository(selectId) {
|
|
var select = $(selectId);
|
|
if (select.length === 0) return;
|
|
|
|
// Check for backend default first
|
|
if (GitHub.defaultRepository && selectId !== '#github_default_repository') {
|
|
select.val(GitHub.defaultRepository).trigger('change');
|
|
return;
|
|
}
|
|
|
|
// Check if there's already a selected option in the HTML (from Blade template)
|
|
var defaultOption = select.find('option[selected]').first();
|
|
if (defaultOption.length > 0) {
|
|
select.val(defaultOption.val()).trigger('change');
|
|
}
|
|
}
|
|
|
|
function githubPopulateRepositories(repositories) {
|
|
// Cache repositories for reuse
|
|
GitHub.cache.repositories = repositories;
|
|
|
|
var selects = ['#github_default_repository', '#github-repository', '#github-link-repository'];
|
|
|
|
$.each(selects, function(i, selectId) {
|
|
var select = $(selectId);
|
|
if (select.length === 0) return;
|
|
|
|
var currentValue = select.val();
|
|
var defaultOption = select.find('option[selected]').first();
|
|
var defaultValue = defaultOption.length > 0 ? defaultOption.val() : '';
|
|
|
|
// Use GitHub.defaultRepository if available and we're not in settings
|
|
var backendDefault = (selectId !== '#github_default_repository' && GitHub.defaultRepository) ? GitHub.defaultRepository : '';
|
|
|
|
// For settings page, preserve any manually entered value
|
|
if (selectId === '#github_default_repository' && currentValue) {
|
|
// Remove all options except the placeholder and current value
|
|
select.find('option').each(function() {
|
|
if ($(this).val() !== '' && $(this).val() !== currentValue) {
|
|
$(this).remove();
|
|
}
|
|
});
|
|
} else {
|
|
select.empty().append('<option value="">' + Lang.get("messages.select_repository") + '</option>');
|
|
}
|
|
|
|
// Determine which value should be selected (priority: current -> backend default -> template default)
|
|
var valueToSelect = currentValue || backendDefault || defaultValue;
|
|
|
|
// Add repositories that have issues enabled
|
|
var foundRepository = false;
|
|
$.each(repositories, function(i, repo) {
|
|
if (repo.has_issues) {
|
|
// Check if option already exists
|
|
if (select.find('option[value="' + repo.full_name + '"]').length === 0) {
|
|
var selected = repo.full_name === valueToSelect ? 'selected' : '';
|
|
if (repo.full_name === valueToSelect) {
|
|
foundRepository = true;
|
|
}
|
|
select.append('<option value="' + repo.full_name + '" ' + selected + '>' + repo.full_name + '</option>');
|
|
}
|
|
}
|
|
});
|
|
|
|
// Set the value if we have a value to select
|
|
if (valueToSelect) {
|
|
select.val(valueToSelect);
|
|
}
|
|
|
|
// Show warning if repository not found
|
|
if (valueToSelect && repositories.length > 0 && !foundRepository) {
|
|
// Only show warning once per repository
|
|
var warningKey = 'repo_not_found_' + valueToSelect;
|
|
if (GitHub.warningsShown.indexOf(warningKey) === -1) {
|
|
GitHub.warningsShown.push(warningKey);
|
|
showFloatingAlert('warning', Lang.get("messages.current_repository_not_found") + ': ' + valueToSelect);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubLoadAllowedLabels() {
|
|
var repository = $("#github_default_repository").val();
|
|
if (!repository) {
|
|
console.log('No repository selected for loading allowed labels');
|
|
return;
|
|
}
|
|
|
|
var $loadingDiv = $('#github-labels-loading');
|
|
var $refreshBtn = $('#refresh-allowed-labels');
|
|
var $select = $('#github_allowed_labels');
|
|
|
|
// Show loading indicator
|
|
$loadingDiv.show();
|
|
$refreshBtn.find('.glyphicon').addClass('glyphicon-spin');
|
|
|
|
// Get current allowed labels setting
|
|
var currentAllowedLabels = [];
|
|
try {
|
|
var allowedLabelsJson = $('input[name="current_allowed_labels"]').val();
|
|
if (allowedLabelsJson) {
|
|
var parsed = JSON.parse(allowedLabelsJson);
|
|
// Handle case where the stored value might be a JSON string or already an array
|
|
if (Array.isArray(parsed)) {
|
|
currentAllowedLabels = parsed;
|
|
} else if (typeof parsed === 'string') {
|
|
currentAllowedLabels = JSON.parse(parsed);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log('No current allowed labels found, will select all by default');
|
|
}
|
|
|
|
// Use laroute to generate URL with encoded parameter
|
|
var url = laroute.route('github.labels', { repository: repository });
|
|
|
|
$.ajax({
|
|
url: url,
|
|
type: 'GET',
|
|
success: function(response) {
|
|
if (response.status === 'success' && response.data) {
|
|
githubPopulateAllowedLabels(response.data, currentAllowedLabels);
|
|
} else {
|
|
console.error('Failed to load labels:', response.message);
|
|
showFloatingAlert('error', 'Failed to load labels: ' + (response.message || 'Unknown error'));
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
console.error('Failed to load labels:', xhr);
|
|
showFloatingAlert('error', 'Failed to load labels');
|
|
},
|
|
complete: function() {
|
|
$loadingDiv.hide();
|
|
$refreshBtn.find('.glyphicon').removeClass('glyphicon-spin');
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubPopulateAllowedLabels(labels, currentAllowedLabels) {
|
|
var $select = $('#github_allowed_labels');
|
|
|
|
// Destroy existing Select2 if it exists
|
|
if ($select.hasClass('select2-hidden-accessible')) {
|
|
$select.select2('destroy');
|
|
}
|
|
|
|
$select.empty();
|
|
|
|
// If no current allowed labels are set, select all by default
|
|
var selectAll = currentAllowedLabels.length === 0;
|
|
|
|
$.each(labels, function(i, label) {
|
|
var isSelected = selectAll || currentAllowedLabels.indexOf(label.name) !== -1;
|
|
var option = $('<option></option>')
|
|
.attr('value', label.name)
|
|
.text(label.name)
|
|
.prop('selected', isSelected);
|
|
|
|
// Add color styling if available
|
|
if (label.color) {
|
|
option.attr('data-color', '#' + label.color);
|
|
}
|
|
|
|
$select.append(option);
|
|
});
|
|
|
|
// Initialize Select2 with custom styling for labels
|
|
$select.select2({
|
|
placeholder: $select.attr('data-placeholder') || 'Select allowed labels...',
|
|
allowClear: false,
|
|
width: '100%',
|
|
templateResult: function(label) {
|
|
if (!label.id) return label.text;
|
|
|
|
// Find the option element to get the color
|
|
var $option = $select.find('option[value="' + label.id + '"]');
|
|
var color = $option.attr('data-color');
|
|
|
|
if (color) {
|
|
var $result = $(
|
|
'<span style="display: flex; align-items: center;">' +
|
|
'<span style="display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: 8px; background-color: ' + color + ';"></span>' +
|
|
'<span>' + label.text + '</span>' +
|
|
'</span>'
|
|
);
|
|
return $result;
|
|
}
|
|
|
|
return label.text;
|
|
},
|
|
templateSelection: function(label) {
|
|
if (!label.id) return label.text;
|
|
|
|
// Find the option element to get the color
|
|
var $option = $select.find('option[value="' + label.id + '"]');
|
|
var color = $option.attr('data-color');
|
|
|
|
if (color) {
|
|
var $selection = $(
|
|
'<span style="display: flex; align-items: center;">' +
|
|
'<span style="display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; background-color: ' + color + ';"></span>' +
|
|
'<span>' + label.text + '</span>' +
|
|
'</span>'
|
|
);
|
|
return $selection;
|
|
}
|
|
|
|
return label.text;
|
|
}
|
|
});
|
|
|
|
console.log('Populated allowed labels with Select2:', labels.length, 'labels, selected:', $select.val());
|
|
}
|
|
|
|
function githubLoadRepositoryLabels(repository) {
|
|
// Use laroute to generate URL with encoded parameter
|
|
var url = laroute.route('github.labels', { repository: repository });
|
|
|
|
$.ajax({
|
|
url: url,
|
|
type: 'GET',
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
githubPopulateLabels(response.data);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
console.error('Failed to load labels:', xhr);
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubPopulateLabels(labels) {
|
|
var select = $('#github-issue-labels');
|
|
|
|
console.log('Populating labels:', labels); // Debug log
|
|
|
|
// Destroy existing Select2 if it exists
|
|
if (select.hasClass('select2-hidden-accessible')) {
|
|
select.select2('destroy');
|
|
}
|
|
|
|
select.empty();
|
|
|
|
$.each(labels, function(i, label) {
|
|
var option = $('<option></option>')
|
|
.attr('value', label.name)
|
|
.text(label.name);
|
|
|
|
// Add color styling if available
|
|
if (label.color) {
|
|
option.attr('data-color', '#' + label.color);
|
|
}
|
|
|
|
select.append(option);
|
|
});
|
|
|
|
console.log('Options added, initializing Select2'); // Debug log
|
|
|
|
// Initialize Select2 for multiselect with custom styling for labels
|
|
select.select2({
|
|
placeholder: 'Select labels...',
|
|
allowClear: true,
|
|
closeOnSelect: false,
|
|
width: '100%',
|
|
dropdownParent: $('#github-create-issue-modal'), // Ensure dropdown renders in modal
|
|
dropdownCssClass: 'github-select2-dropdown', // Custom class for z-index fix
|
|
templateResult: function(label) {
|
|
if (!label.id) return label.text;
|
|
|
|
// Find the option element to get the color
|
|
var $option = select.find('option[value="' + label.id + '"]');
|
|
var color = $option.attr('data-color');
|
|
|
|
if (color) {
|
|
var $result = $(
|
|
'<span style="display: flex; align-items: center;">' +
|
|
'<span style="display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: 8px; background-color: ' + color + ';"></span>' +
|
|
'<span>' + label.text + '</span>' +
|
|
'</span>'
|
|
);
|
|
return $result;
|
|
}
|
|
|
|
return label.text;
|
|
},
|
|
templateSelection: function(label) {
|
|
if (!label.id) return label.text;
|
|
|
|
// Find the option element to get the color
|
|
var $option = select.find('option[value="' + label.id + '"]');
|
|
var color = $option.attr('data-color');
|
|
|
|
if (color) {
|
|
var $selection = $(
|
|
'<span style="display: flex; align-items: center;">' +
|
|
'<span style="display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; background-color: ' + color + ';"></span>' +
|
|
'<span>' + label.text + '</span>' +
|
|
'</span>'
|
|
);
|
|
return $selection;
|
|
}
|
|
|
|
return label.text;
|
|
}
|
|
});
|
|
|
|
console.log('Select2 initialized, options count:', select.find('option').length); // Debug log
|
|
}
|
|
|
|
function githubLoadLabelMappings(repository) {
|
|
$.ajax({
|
|
url: laroute.route('github.label_mappings'),
|
|
type: 'GET',
|
|
data: { repository: repository },
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
githubRenderLabelMappings(response.data);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
console.error('Failed to load label mappings:', xhr);
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubRenderLabelMappings(mappings) {
|
|
var container = $('#label-mappings-container');
|
|
container.empty();
|
|
|
|
if (mappings.length === 0) {
|
|
container.html('<p class="text-muted">No label mappings configured</p>');
|
|
return;
|
|
}
|
|
|
|
$.each(mappings, function(i, mapping) {
|
|
githubAddLabelMappingRow(mapping);
|
|
});
|
|
}
|
|
|
|
function githubAddLabelMappingRow(mapping) {
|
|
mapping = mapping || {};
|
|
|
|
var html = '<div class="label-mapping-row">' +
|
|
'<input type="text" class="form-control" name="freescout_tag" placeholder="FreeScout Tag" value="' + (mapping.freescout_tag || '') + '">' +
|
|
'<span>→</span>' +
|
|
'<input type="text" class="form-control" name="github_label" placeholder="GitHub Label" value="' + (mapping.github_label || '') + '">' +
|
|
'<input type="number" class="form-control" name="confidence_threshold" placeholder="0.80" value="' + (mapping.confidence_threshold || 0.80) + '" min="0" max="1" step="0.01">' +
|
|
'<button type="button" class="btn btn-danger btn-sm remove-mapping">' +
|
|
'<i class="glyphicon glyphicon-trash"></i>' +
|
|
'</button>' +
|
|
'</div>';
|
|
|
|
$('#label-mappings-container').append(html);
|
|
}
|
|
|
|
function githubSearchIssues(repository, query) {
|
|
fsAjax({
|
|
repository: repository,
|
|
query: query,
|
|
per_page: GitHub.config.maxSearchResults
|
|
},
|
|
laroute.route('github.search_issues'),
|
|
function(response) {
|
|
if (isAjaxSuccess(response)) {
|
|
githubDisplaySearchResults(response.issues);
|
|
}
|
|
}, true);
|
|
}
|
|
|
|
function githubDisplaySearchResults(issues) {
|
|
var container = $('#github-search-results-list');
|
|
container.empty();
|
|
|
|
if (issues.length === 0) {
|
|
container.html('<p class="text-muted">No issues found</p>');
|
|
} else {
|
|
$.each(issues, function(i, issue) {
|
|
var badgeClass = issue.state === 'open' ? 'success' : 'secondary';
|
|
var html = '<div class="github-search-result-item" data-issue-number="' + issue.number + '">' +
|
|
'<div class="github-search-result-number">#' + issue.number + '</div>' +
|
|
'<div class="github-search-result-title">' + issue.title + '</div>' +
|
|
'<div class="github-search-result-meta">' +
|
|
'<span class="badge badge-' + badgeClass + '">' + issue.state + '</span>' +
|
|
' • Updated ' + githubFormatDate(issue.updated_at) +
|
|
'</div>' +
|
|
'</div>';
|
|
container.append(html);
|
|
});
|
|
}
|
|
|
|
$('#github-search-results').show();
|
|
}
|
|
|
|
function githubGenerateIssueContent() {
|
|
var conversationId = $('#github-create-issue-form input[name="conversation_id"]').val();
|
|
|
|
// Fallback to global conversation ID if not found in form
|
|
if (!conversationId) {
|
|
conversationId = getGlobalAttr('conversation_id');
|
|
}
|
|
|
|
if (!conversationId) {
|
|
showFloatingAlert('error', 'No conversation ID found');
|
|
console.error('GitHub: Could not find conversation ID in form or global attributes');
|
|
return;
|
|
}
|
|
|
|
|
|
// Show loading state
|
|
var $titleField = $('#github-issue-title');
|
|
var $bodyField = $('#github-issue-body');
|
|
var $generateBtn = $('#github-generate-content-btn');
|
|
|
|
$generateBtn.prop('disabled', true).find('i').removeClass('glyphicon-flash').addClass('glyphicon-refresh glyphicon-spin');
|
|
|
|
// Show progress message for user feedback
|
|
showFloatingAlert('info', 'Generating AI content...');
|
|
|
|
$.ajax({
|
|
timeout: 65000, // 65 second timeout to match backend
|
|
url: laroute.route('github.generate_content'),
|
|
type: 'POST',
|
|
data: {
|
|
conversation_id: conversationId,
|
|
_token: $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
console.log('GitHub: Generate content success response:', response);
|
|
|
|
if (response.status === 'success') {
|
|
if (response.data.title && !$titleField.val()) {
|
|
$titleField.val(response.data.title);
|
|
}
|
|
if (response.data.body && !$bodyField.val()) {
|
|
$bodyField.val(response.data.body);
|
|
}
|
|
|
|
// Auto-select suggested labels if available
|
|
if (response.data.suggested_labels && response.data.suggested_labels.length > 0) {
|
|
var labelsSelect = $('#github-issue-labels');
|
|
if (labelsSelect.hasClass('select2-hidden-accessible')) {
|
|
labelsSelect.val(response.data.suggested_labels).trigger('change');
|
|
console.log('Auto-selected labels:', response.data.suggested_labels);
|
|
}
|
|
}
|
|
|
|
showFloatingAlert('success', 'Content generated successfully');
|
|
} else {
|
|
// Handle error responses that come back as 200 but with error status
|
|
var errorMessage = response.message || 'Failed to generate content';
|
|
console.error('GitHub: Server returned error status:', response);
|
|
showFloatingAlert('error', errorMessage);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
console.error('GitHub: Generate content error:', xhr);
|
|
var response = xhr.responseJSON || {};
|
|
var errorMessage = 'Failed to generate content';
|
|
|
|
// Handle cases where the response might be a JSON string instead of object
|
|
if (typeof response === 'string') {
|
|
try {
|
|
response = JSON.parse(response);
|
|
} catch (e) {
|
|
console.error('GitHub: Failed to parse error response as JSON:', response);
|
|
response = {};
|
|
}
|
|
}
|
|
|
|
// Extract the actual error message
|
|
if (response.message) {
|
|
errorMessage = response.message;
|
|
} else if (xhr.responseText) {
|
|
// Try to extract error from response text
|
|
try {
|
|
var textResponse = JSON.parse(xhr.responseText);
|
|
if (textResponse.message) {
|
|
errorMessage = textResponse.message;
|
|
}
|
|
} catch (e) {
|
|
// If it's not JSON, use the raw text (truncated)
|
|
errorMessage = xhr.responseText.length > 200 ?
|
|
xhr.responseText.substring(0, 200) + '...' :
|
|
xhr.responseText;
|
|
}
|
|
}
|
|
|
|
// Add more detailed error info for debugging
|
|
if (xhr.status === 400 && response.message) {
|
|
// API errors (like OpenAI parameter issues)
|
|
errorMessage = response.message;
|
|
} else if (xhr.status === 500 && !response.message) {
|
|
errorMessage = 'Server error occurred. Check server logs for details.';
|
|
} else if (xhr.status === 422 && response.errors) {
|
|
var errors = [];
|
|
for (var field in response.errors) {
|
|
errors = errors.concat(response.errors[field]);
|
|
}
|
|
errorMessage = errors.join(', ');
|
|
}
|
|
|
|
// Log detailed error for debugging
|
|
console.error('GitHub: Detailed error info:', {
|
|
status: xhr.status,
|
|
statusText: xhr.statusText,
|
|
response: response,
|
|
responseText: xhr.responseText,
|
|
finalErrorMessage: errorMessage
|
|
});
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
},
|
|
complete: function() {
|
|
$generateBtn.prop('disabled', false).find('i').removeClass('glyphicon-refresh glyphicon-spin').addClass('glyphicon-flash');
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubFormatDate(dateString) {
|
|
return new Date(dateString).toLocaleDateString();
|
|
}
|
|
|
|
function githubShowAjaxError(response) {
|
|
var errorMessage = 'An error occurred';
|
|
|
|
if (response.message) {
|
|
errorMessage = response.message;
|
|
|
|
// If there are validation errors, append them
|
|
if (response.errors) {
|
|
var errorDetails = [];
|
|
for (var field in response.errors) {
|
|
if (response.errors.hasOwnProperty(field)) {
|
|
var fieldErrors = response.errors[field];
|
|
if (Array.isArray(fieldErrors)) {
|
|
errorDetails = errorDetails.concat(fieldErrors);
|
|
}
|
|
}
|
|
}
|
|
if (errorDetails.length > 0) {
|
|
errorMessage += ':\n• ' + errorDetails.join('\n• ');
|
|
}
|
|
}
|
|
} else if (response.errors) {
|
|
// Handle case where there's no main message but there are errors
|
|
var errors = [];
|
|
for (var field in response.errors) {
|
|
if (response.errors.hasOwnProperty(field)) {
|
|
errors = errors.concat(response.errors[field]);
|
|
}
|
|
}
|
|
errorMessage = errors.length > 0 ? errors.join('\n') : 'Validation failed';
|
|
}
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
}
|
|
|
|
function githubShowConnectionResult(response) {
|
|
var $resultDiv = $('#github-connection-result');
|
|
var $alert = $resultDiv.find('.alert');
|
|
var $message = $resultDiv.find('.github-connection-message');
|
|
|
|
// Remove existing classes
|
|
$alert.removeClass('alert-success alert-danger alert-warning');
|
|
|
|
if (response.status === 'success') {
|
|
$alert.addClass('alert-success');
|
|
var message = '<strong>' + Lang.get("messages.successful") + '</strong><br>';
|
|
|
|
if (response.user) {
|
|
message += Lang.get("messages.connected_as") + ': ' + response.user + '<br>';
|
|
}
|
|
if (response.permissions) {
|
|
message += Lang.get("messages.permissions") + ': ' + response.permissions.join(', ') + '<br>';
|
|
}
|
|
if (response.rate_limit) {
|
|
message += Lang.get("messages.api_calls_remaining") + ': ' + response.rate_limit.remaining + '/' + response.rate_limit.limit;
|
|
}
|
|
|
|
$message.html(message);
|
|
} else {
|
|
$alert.addClass('alert-danger');
|
|
var errorMessage = '<strong>' + Lang.get("messages.error") + ':</strong> ' + (response.message || 'Unknown error');
|
|
|
|
// Add troubleshooting hints based on error type
|
|
if (response.message && response.message.includes('401')) {
|
|
errorMessage += '<br><small>' + Lang.get("messages.check_token_valid") + '</small>';
|
|
} else if (response.message && response.message.includes('404')) {
|
|
errorMessage += '<br><small>' + Lang.get("messages.check_token_permissions") + '</small>';
|
|
}
|
|
|
|
$message.html(errorMessage);
|
|
}
|
|
|
|
$resultDiv.fadeIn();
|
|
|
|
// Also show floating alert for quick feedback
|
|
var alertType = response.status === 'success' ? 'success' : 'error';
|
|
showFloatingAlert(alertType, response.message || 'Unknown response');
|
|
}
|
|
|
|
// Auto-initialize when DOM is ready
|
|
$(document).ready(function() {
|
|
// Check if we're on a page with GitHub sidebar
|
|
var $githubSidebar = $('.github-sidebar-block');
|
|
if ($githubSidebar.length > 0) {
|
|
// Get default repository from data attribute
|
|
var defaultRepo = $githubSidebar.data('default-repository');
|
|
if (defaultRepo) {
|
|
GitHub.defaultRepository = defaultRepo;
|
|
}
|
|
|
|
// Initialize the GitHub modals functionality
|
|
githubInitModals();
|
|
|
|
// Initialize sidebar action handlers
|
|
githubInitSidebarActions();
|
|
|
|
// Only load repositories if we don't have cached ones and we're actually going to use them
|
|
// Skip auto-loading on conversation pages since we already have default repository
|
|
if (!GitHub.cache.repositories) {
|
|
// Try localStorage cache first
|
|
var cachedRepos = githubGetCachedRepositories();
|
|
if (cachedRepos) {
|
|
GitHub.cache.repositories = cachedRepos;
|
|
}
|
|
// Don't auto-load repositories on conversation pages - only load when modals are opened
|
|
}
|
|
|
|
// Auto-refresh issues when conversation is opened (with intelligent caching)
|
|
githubAutoRefreshOnLoad();
|
|
}
|
|
|
|
// Check if we're on the settings page
|
|
if ($('#github_default_repository').length > 0) {
|
|
githubInitSettings();
|
|
|
|
// Auto-load repositories if token exists
|
|
if ($('#github_token').val()) {
|
|
// Try to load from cache first
|
|
var cachedRepos = githubGetCachedRepositories();
|
|
if (cachedRepos) {
|
|
githubPopulateRepositories(cachedRepos);
|
|
} else {
|
|
githubLoadRepositories();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Missing functions that were in the sidebar template
|
|
function githubCreateIssue() {
|
|
$('#github-create-issue-btn').click(function() {
|
|
var formData = $('#github-create-issue-form').serialize();
|
|
var $btn = $(this);
|
|
|
|
$btn.prop('disabled', true);
|
|
$btn.find('.glyphicon').removeClass('glyphicon-plus').addClass('glyphicon-refresh glyphicon-spin');
|
|
|
|
$.ajax({
|
|
url: laroute.route('github.create_issue'),
|
|
type: 'POST',
|
|
data: formData + '&_token=' + $('meta[name="csrf-token"]').attr('content'),
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
$('#github-create-issue-modal').modal('hide');
|
|
showFloatingAlert('success', response.message);
|
|
window.location.reload(); // Refresh to show new issue
|
|
} else {
|
|
showFloatingAlert('error', response.message);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {};
|
|
var errorMessage = 'An error occurred';
|
|
|
|
if (response.message) {
|
|
errorMessage = response.message;
|
|
} else if (response.errors) {
|
|
// Handle validation errors
|
|
var errors = [];
|
|
for (var field in response.errors) {
|
|
if (response.errors.hasOwnProperty(field)) {
|
|
errors = errors.concat(response.errors[field]);
|
|
}
|
|
}
|
|
errorMessage = errors.length > 0 ? errors.join(', ') : 'Validation failed';
|
|
} else if (xhr.status === 422) {
|
|
errorMessage = 'The given data was invalid. Please check your input and try again.';
|
|
} else if (xhr.status === 403) {
|
|
errorMessage = 'You do not have permission to perform this action.';
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'The requested resource was not found.';
|
|
} else if (xhr.status >= 500) {
|
|
errorMessage = 'Server error occurred. Please try again later.';
|
|
}
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
},
|
|
complete: function() {
|
|
$btn.prop('disabled', false);
|
|
$btn.find('.glyphicon').removeClass('glyphicon-refresh glyphicon-spin').addClass('glyphicon-plus');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubLinkIssue() {
|
|
$('#github-link-issue-btn').click(function() {
|
|
var formData = $('#github-link-issue-form').serialize();
|
|
var $btn = $(this);
|
|
|
|
$btn.prop('disabled', true);
|
|
$btn.find('.glyphicon').removeClass('glyphicon-link').addClass('glyphicon-refresh glyphicon-spin');
|
|
|
|
$.ajax({
|
|
url: laroute.route('github.link_issue'),
|
|
type: 'POST',
|
|
data: formData + '&_token=' + $('meta[name="csrf-token"]').attr('content'),
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
$('#github-link-issue-modal').modal('hide');
|
|
showFloatingAlert('success', response.message);
|
|
window.location.reload(); // Refresh to show linked issue
|
|
} else {
|
|
showFloatingAlert('error', response.message);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {};
|
|
var errorMessage = 'An error occurred';
|
|
|
|
if (response.message) {
|
|
errorMessage = response.message;
|
|
} else if (response.errors) {
|
|
// Handle validation errors
|
|
var errors = [];
|
|
for (var field in response.errors) {
|
|
if (response.errors.hasOwnProperty(field)) {
|
|
errors = errors.concat(response.errors[field]);
|
|
}
|
|
}
|
|
errorMessage = errors.length > 0 ? errors.join(', ') : 'Validation failed';
|
|
} else if (xhr.status === 422) {
|
|
errorMessage = 'The given data was invalid. Please check your input and try again.';
|
|
} else if (xhr.status === 403) {
|
|
errorMessage = 'You do not have permission to perform this action.';
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'The requested resource was not found.';
|
|
} else if (xhr.status >= 500) {
|
|
errorMessage = 'Server error occurred. Please try again later.';
|
|
}
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
},
|
|
complete: function() {
|
|
$btn.prop('disabled', false);
|
|
$btn.find('.glyphicon').removeClass('glyphicon-refresh glyphicon-spin').addClass('glyphicon-link');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubInitSidebarActions() {
|
|
$(document).ready(function() {
|
|
// Initialize create and link issue handlers
|
|
githubCreateIssue();
|
|
githubLinkIssue();
|
|
|
|
// Issue actions
|
|
$(document).on('click', '.github-issue-action', function(e) {
|
|
e.preventDefault();
|
|
var action = $(this).data('action');
|
|
var issueId = $(this).data('issue-id');
|
|
|
|
if (action === 'unlink') {
|
|
if (confirm('Are you sure you want to unlink this issue?')) {
|
|
githubUnlinkIssue(issueId);
|
|
}
|
|
} else if (action === 'refresh') {
|
|
githubRefreshIssue(issueId);
|
|
}
|
|
});
|
|
|
|
// Search result selection
|
|
$(document).on('click', '.github-search-result-item', function() {
|
|
var issueNumber = $(this).data('issue-number');
|
|
$('#github-issue-number').val(issueNumber);
|
|
$('#github-search-results').hide();
|
|
});
|
|
});
|
|
}
|
|
|
|
function githubUnlinkIssue(issueId) {
|
|
$.ajax({
|
|
url: laroute.route('github.unlink_issue'),
|
|
type: 'POST',
|
|
data: {
|
|
_token: $('meta[name="csrf-token"]').attr('content'),
|
|
conversation_id: $('#github-create-issue-form input[name="conversation_id"]').val(),
|
|
issue_id: issueId
|
|
},
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
showFloatingAlert('success', response.message);
|
|
window.location.reload();
|
|
} else {
|
|
showFloatingAlert('error', response.message);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function githubRefreshIssue(issueId) {
|
|
var $refreshButton = $('[data-issue-id="' + issueId + '"]').find('.glyphicon-refresh');
|
|
$refreshButton.addClass('glyphicon-spin');
|
|
|
|
var url = laroute.route('github.refresh_issue', {id: issueId});
|
|
|
|
$.ajax({
|
|
url: url,
|
|
type: 'POST',
|
|
data: {
|
|
_token: $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
showFloatingAlert('success', 'Issue refreshed successfully');
|
|
// Reload the page to show updated issue data
|
|
window.location.reload();
|
|
} else {
|
|
showFloatingAlert('error', response.message || 'Failed to refresh issue');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {};
|
|
var errorMessage = response.message || 'Failed to refresh issue';
|
|
|
|
if (xhr.status === 403) {
|
|
errorMessage = 'You do not have permission to refresh this issue';
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'Issue not found';
|
|
}
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
},
|
|
complete: function() {
|
|
$refreshButton.removeClass('glyphicon-spin');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Refresh all issues for the current conversation with intelligent caching
|
|
*/
|
|
function githubRefreshConversationIssues() {
|
|
var conversationId = getGlobalAttr('conversation_id');
|
|
if (!conversationId) {
|
|
console.warn('GitHub: No conversation ID found for auto-refresh');
|
|
return;
|
|
}
|
|
|
|
var $refreshButtons = $('.github-issue-action[data-action="refresh"] .glyphicon-refresh');
|
|
$refreshButtons.addClass('glyphicon-spin');
|
|
|
|
$.ajax({
|
|
url: laroute.route('github.refresh_conversation_issues'),
|
|
type: 'POST',
|
|
data: {
|
|
conversation_id: conversationId,
|
|
_token: $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
if (response.status === 'success') {
|
|
// Only show success message if manually triggered
|
|
var isManualRefresh = arguments.callee.caller && arguments.callee.caller.name === 'githubManualRefreshConversation';
|
|
if (isManualRefresh) {
|
|
showFloatingAlert('success', 'All issues refreshed successfully');
|
|
}
|
|
|
|
// Check if any issues were actually updated
|
|
var needsReload = false;
|
|
if (response.data && response.data.length > 0) {
|
|
// Simple check: if we have issues and they might have been updated
|
|
needsReload = true;
|
|
}
|
|
|
|
if (needsReload) {
|
|
// Reload to show updated data
|
|
window.location.reload();
|
|
}
|
|
} else {
|
|
showFloatingAlert('error', response.message || 'Failed to refresh issues');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var response = xhr.responseJSON || {};
|
|
var errorMessage = response.message || 'Failed to refresh issues';
|
|
|
|
if (xhr.status === 403) {
|
|
errorMessage = 'You do not have permission to refresh issues';
|
|
}
|
|
|
|
showFloatingAlert('error', errorMessage);
|
|
},
|
|
complete: function() {
|
|
$refreshButtons.removeClass('glyphicon-spin');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Manual refresh function for explicit user action
|
|
*/
|
|
function githubManualRefreshConversation() {
|
|
githubRefreshConversationIssues();
|
|
}
|
|
|
|
/**
|
|
* Auto-refresh issues when conversation is loaded
|
|
* Uses intelligent caching to prevent excessive API calls
|
|
*/
|
|
function githubAutoRefreshOnLoad() {
|
|
var conversationId = getGlobalAttr('conversation_id');
|
|
if (!conversationId) {
|
|
return;
|
|
}
|
|
|
|
// Check if there are any GitHub issues in the sidebar
|
|
var $githubIssues = $('.github-issue-item');
|
|
if ($githubIssues.length === 0) {
|
|
return; // No issues to refresh
|
|
}
|
|
|
|
// Check local storage to see when we last auto-refreshed this conversation
|
|
var lastAutoRefreshKey = 'github_auto_refresh_conv_' + conversationId;
|
|
var lastAutoRefresh = localStorage.getItem(lastAutoRefreshKey);
|
|
var now = Date.now();
|
|
var fiveMinutes = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
|
|
if (lastAutoRefresh && (now - parseInt(lastAutoRefresh)) < fiveMinutes) {
|
|
// Skip auto-refresh if we've done it recently
|
|
console.log('GitHub: Skipping auto-refresh, done recently');
|
|
return;
|
|
}
|
|
|
|
// Perform silent refresh (no success message)
|
|
console.log('GitHub: Auto-refreshing issues for conversation');
|
|
githubRefreshConversationIssues();
|
|
|
|
// Update the last auto-refresh timestamp
|
|
localStorage.setItem(lastAutoRefreshKey, now.toString());
|
|
|
|
// Clean up old timestamps (keep only last 50 conversations)
|
|
try {
|
|
var keysToClean = [];
|
|
for (var key in localStorage) {
|
|
if (key.startsWith('github_auto_refresh_conv_')) {
|
|
keysToClean.push({
|
|
key: key,
|
|
timestamp: parseInt(localStorage.getItem(key) || '0')
|
|
});
|
|
}
|
|
}
|
|
|
|
// Sort by timestamp and keep only the most recent 50
|
|
keysToClean.sort(function(a, b) { return b.timestamp - a.timestamp; });
|
|
if (keysToClean.length > 50) {
|
|
for (var i = 50; i < keysToClean.length; i++) {
|
|
localStorage.removeItem(keysToClean[i].key);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Ignore localStorage errors
|
|
console.warn('GitHub: Failed to clean up localStorage:', e);
|
|
}
|
|
} |