Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.238.025"
VERSION = "0.239.001"

SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')

Expand Down
119 changes: 114 additions & 5 deletions application/single_app/static/js/group/manage_group.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ $(document).ready(function () {
loadActivityTimeline(limit);
});

// Retention policy settings
$("#saveRetentionBtn").on("click", function () {
saveGroupRetentionSettings();
});
$('#settings-tab').on('shown.bs.tab', function () {
loadGroupRetentionSettings();
});

// Bulk Actions Events
$("#selectAllMembers").on("change", function () {
const isChecked = $(this).prop("checked");
Expand Down Expand Up @@ -321,14 +329,16 @@ function loadGroupInfo(doneCallback) {
$("#addMemberBtn").show();
$("#addBulkMemberBtn").show();
}

$("#pendingRequestsSection").show();
$("#activityTimelineSection").show();
$("#stats-tab-item").show();
$("#settings-tab-item").removeClass("d-none");

loadPendingRequests();
loadGroupStats();
loadActivityTimeline(50);
loadGroupRetentionSettings();
}

if (typeof doneCallback === "function") {
Expand Down Expand Up @@ -1422,29 +1432,29 @@ async function bulkAssignRole() {

async function bulkRemoveMembers() {
const selectedMembers = getSelectedMembers();

if (selectedMembers.length === 0) {
alert("No members selected");
return;
}

// Close modal
$("#bulkRemoveMembersModal").modal("hide");

let successCount = 0;
let failedCount = 0;
const failures = [];

for (let i = 0; i < selectedMembers.length; i++) {
const member = selectedMembers[i];

try {
const response = await fetch(`/api/groups/${groupId}/members/${member.userId}`, {
method: 'DELETE'
});

const data = await response.json();

if (response.ok && data.success) {
successCount++;
} else {
Expand All @@ -1470,3 +1480,102 @@ async function bulkRemoveMembers() {
// Reload members and clear selection
loadMembers();
}

/* ===================== GROUP RETENTION POLICY ===================== */

async function loadGroupRetentionSettings() {
const convSelect = document.getElementById('group-conversation-retention-days');
const docSelect = document.getElementById('group-document-retention-days');

if (!convSelect || !docSelect) return;

try {
const orgDefaultsResp = await fetch('/api/retention-policy/defaults/group');
const orgData = await orgDefaultsResp.json();

if (orgData.success) {
const convDefaultOption = convSelect.querySelector('option[value="default"]');
const docDefaultOption = docSelect.querySelector('option[value="default"]');

if (convDefaultOption) {
convDefaultOption.textContent = `Using organization default (${orgData.default_conversation_label})`;
}
if (docDefaultOption) {
docDefaultOption.textContent = `Using organization default (${orgData.default_document_label})`;
}
}
} catch (error) {
console.error('Error loading group retention defaults:', error);
}

try {
const groupResp = await fetch(`/api/groups/${groupId}`);

if (!groupResp.ok) {
throw new Error(`Failed to fetch group: ${groupResp.status}`);
}

const groupData = await groupResp.json();

if (groupData && groupData.retention_policy) {
const retentionPolicy = groupData.retention_policy;
let convRetention = retentionPolicy.conversation_retention_days;
let docRetention = retentionPolicy.document_retention_days;

if (convRetention === undefined || convRetention === null) convRetention = 'default';
if (docRetention === undefined || docRetention === null) docRetention = 'default';

convSelect.value = convRetention;
docSelect.value = docRetention;
} else {
convSelect.value = 'default';
docSelect.value = 'default';
}
} catch (error) {
console.error('Error loading group retention settings:', error);
convSelect.value = 'default';
docSelect.value = 'default';
}
}

async function saveGroupRetentionSettings() {
const convSelect = document.getElementById('group-conversation-retention-days');
const docSelect = document.getElementById('group-document-retention-days');
const statusSpan = document.getElementById('group-retention-save-status');

if (!convSelect || !docSelect) return;

const retentionData = {
conversation_retention_days: convSelect.value,
document_retention_days: docSelect.value
};

if (statusSpan) {
statusSpan.innerHTML = '<span class="text-info"><i class="bi bi-hourglass-split"></i> Saving...</span>';
}

try {
const response = await fetch(`/api/retention-policy/group/${groupId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(retentionData)
});

const data = await response.json();

if (response.ok && data.success) {
if (statusSpan) {
statusSpan.innerHTML = '<span class="text-success"><i class="bi bi-check-circle-fill"></i> Saved successfully!</span>';
setTimeout(() => { statusSpan.innerHTML = ''; }, 3000);
}
} else {
throw new Error(data.error || 'Failed to save retention settings');
}
} catch (error) {
console.error('Error saving group retention settings:', error);
if (statusSpan) {
statusSpan.innerHTML = `<span class="text-danger"><i class="bi bi-exclamation-circle-fill"></i> Error: ${error.message}</span>`;
}
showToast(`Error saving retention settings: ${error.message}`, 'danger');
}
}
116 changes: 112 additions & 4 deletions application/single_app/static/js/public/manage_public_workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ $(document).ready(function () {
loadWorkspaceStats();
});

// Retention policy settings
$("#savePublicRetentionBtn").on("click", function () {
savePublicRetentionSettings();
});
$('#settings-tab').on('shown.bs.tab', function () {
loadPublicRetentionSettings();
});

// Activity timeline pagination
$('input[name="activityLimit"]').on('change', function() {
const limit = parseInt($(this).val());
Expand Down Expand Up @@ -281,7 +289,9 @@ function loadWorkspaceInfo(callback) {
$("#addBulkMemberBtn").show();
$("#pendingRequestsSection").show();
$("#activityTimelineSection").show();
$("#settings-tab-item").removeClass("d-none");
loadPendingRequests();
loadPublicRetentionSettings();
}

if (callback) callback();
Expand Down Expand Up @@ -1244,29 +1254,29 @@ async function bulkAssignRole() {

async function bulkRemoveMembers() {
const selectedMembers = getSelectedMembers();

if (selectedMembers.length === 0) {
alert("No members selected");
return;
}

// Close modal
$("#bulkRemoveMembersModal").modal("hide");

let successCount = 0;
let failedCount = 0;
const failures = [];

for (let i = 0; i < selectedMembers.length; i++) {
const member = selectedMembers[i];

try {
const response = await fetch(`/api/public_workspaces/${workspaceId}/members/${member.userId}`, {
method: 'DELETE'
});

const data = await response.json();

if (response.ok && data.success) {
successCount++;
} else {
Expand All @@ -1293,3 +1303,101 @@ async function bulkRemoveMembers() {
loadMembers();
}

/* ===================== PUBLIC RETENTION POLICY ===================== */

async function loadPublicRetentionSettings() {
const convSelect = document.getElementById('public-conversation-retention-days');
const docSelect = document.getElementById('public-document-retention-days');

if (!convSelect || !docSelect) return;

try {
const orgDefaultsResp = await fetch('/api/retention-policy/defaults/public');
const orgData = await orgDefaultsResp.json();

if (orgData.success) {
const convDefaultOption = convSelect.querySelector('option[value="default"]');
const docDefaultOption = docSelect.querySelector('option[value="default"]');

if (convDefaultOption) {
convDefaultOption.textContent = `Using organization default (${orgData.default_conversation_label})`;
}
if (docDefaultOption) {
docDefaultOption.textContent = `Using organization default (${orgData.default_document_label})`;
}
}
} catch (error) {
console.error('Error loading public workspace retention defaults:', error);
}

try {
const workspaceResp = await fetch(`/api/public_workspaces/${workspaceId}`);

if (!workspaceResp.ok) {
throw new Error(`Failed to fetch workspace: ${workspaceResp.status}`);
}

const workspaceData = await workspaceResp.json();

if (workspaceData && workspaceData.retention_policy) {
const retentionPolicy = workspaceData.retention_policy;
let convRetention = retentionPolicy.conversation_retention_days;
let docRetention = retentionPolicy.document_retention_days;

if (convRetention === undefined || convRetention === null) convRetention = 'default';
if (docRetention === undefined || docRetention === null) docRetention = 'default';

convSelect.value = convRetention;
docSelect.value = docRetention;
} else {
convSelect.value = 'default';
docSelect.value = 'default';
}
} catch (error) {
console.error('Error loading public workspace retention settings:', error);
convSelect.value = 'default';
docSelect.value = 'default';
}
}

async function savePublicRetentionSettings() {
const convSelect = document.getElementById('public-conversation-retention-days');
const docSelect = document.getElementById('public-document-retention-days');
const statusSpan = document.getElementById('public-retention-save-status');

if (!convSelect || !docSelect) return;

const retentionData = {
conversation_retention_days: convSelect.value,
document_retention_days: docSelect.value
};

if (statusSpan) {
statusSpan.innerHTML = '<span class="text-info"><i class="bi bi-hourglass-split"></i> Saving...</span>';
}

try {
const response = await fetch(`/api/retention-policy/public/${workspaceId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(retentionData)
});

const data = await response.json();

if (response.ok && data.success) {
if (statusSpan) {
statusSpan.innerHTML = '<span class="text-success"><i class="bi bi-check-circle-fill"></i> Saved successfully!</span>';
setTimeout(() => { statusSpan.innerHTML = ''; }, 3000);
}
} else {
throw new Error(data.error || 'Failed to save retention settings');
}
} catch (error) {
console.error('Error saving public workspace retention settings:', error);
if (statusSpan) {
statusSpan.innerHTML = `<span class="text-danger"><i class="bi bi-exclamation-circle-fill"></i> Error: ${error.message}</span>`;
}
alert(`Error saving retention settings: ${error.message}`);
}
}
Loading