larxius commited on
Commit
3bc85d1
·
verified ·
1 Parent(s): 904c9be

Update frontend/src/pages/Settings.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/pages/Settings.jsx +9 -199
frontend/src/pages/Settings.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react';
2
  import { useAuth } from '../components/AuthContext';
3
  import toast from 'react-hot-toast';
4
  import Profile from './Profile';
@@ -219,205 +219,15 @@ export const AlertSettingsPage = () => {
219
  if (activeTab === 'billing' && reloadUser) {
220
  intervalId = setInterval(() => {
221
  reloadUser();
222
- }, 3000); // Poll every 3 seconds
 
 
223
  }
224
  return () => {
225
  if (intervalId) clearInterval(intervalId);
226
  };
227
  }, [activeTab, reloadUser]);
228
 
229
-
230
- const fetchTeamUsers = async () => {
231
- setLoadingTeam(true);
232
- try {
233
- const endpoint = user?.org_id ? `/api/auth/organizations/${user.org_id}/users` : '/api/auth/users';
234
- const res = await fetch(endpoint, {
235
- headers: { 'Authorization': `Bearer ${token}` }
236
- });
237
- if (res.ok) {
238
- const data = await res.json();
239
- const filteredUsers = (data.users || []).filter(u => u.role !== 'super_admin');
240
- setTeamUsers(filteredUsers);
241
- }
242
- } catch (err) {
243
- console.error('Failed to fetch org users', err);
244
- } finally {
245
- setLoadingTeam(false);
246
- }
247
- };
248
-
249
- useEffect(() => {
250
- if (activeTab === 'team' && (user?.role === 'org_admin' || user?.role === 'super_admin')) {
251
- fetchTeamUsers();
252
- }
253
- }, [activeTab, user]);
254
-
255
- const handleInviteUser = async (e) => {
256
- e.preventDefault();
257
- if (!newUserEmail) return;
258
- setInvitingUser(true);
259
- try {
260
- const res = await fetch('/api/auth/users/invite', {
261
- method: 'POST',
262
- headers: {
263
- 'Authorization': `Bearer ${token}`,
264
- 'Content-Type': 'application/json'
265
- },
266
- body: JSON.stringify({
267
- email: newUserEmail,
268
- role: newUserRole,
269
- first_name: newUserFirstName,
270
- last_name: newUserLastName,
271
- password: newUserPassword
272
- })
273
- });
274
- const data = await res.json();
275
- if (res.ok) {
276
- toast.success(`User added successfully!`);
277
- setNewUserFirstName('');
278
- setNewUserLastName('');
279
- setNewUserEmail('');
280
- setNewUserPassword('');
281
- setShowAddMember(false);
282
- setShowNewUserPassword(false);
283
- fetchTeamUsers();
284
- } else {
285
- toast.error(data.message || 'Failed to invite user');
286
- }
287
- } catch (err) {
288
- toast.error("Error inviting user");
289
- } finally {
290
- setInvitingUser(false);
291
- }
292
- };
293
-
294
- const handleUpdateUser = async (e) => {
295
- e.preventDefault();
296
- if (!editingUser) return;
297
- setUpdatingUser(true);
298
- try {
299
- const res = await fetch(`/api/auth/users/${editingUser.id}`, {
300
- method: 'PUT',
301
- headers: {
302
- 'Authorization': `Bearer ${token}`,
303
- 'Content-Type': 'application/json'
304
- },
305
- body: JSON.stringify({
306
- first_name: editingUser.first_name,
307
- last_name: editingUser.last_name,
308
- email: editingUser.email,
309
- password: editingUser.new_password,
310
- role: editingUser.role
311
- })
312
- });
313
- if (res.ok) {
314
- toast.success(`User updated successfully!`);
315
- setEditingUser(null);
316
- fetchTeamUsers();
317
- } else {
318
- const data = await res.json();
319
- toast.error(data.message || 'Failed to update user');
320
- }
321
- } catch (err) {
322
- toast.error("Error updating user");
323
- } finally {
324
- setUpdatingUser(false);
325
- }
326
- };
327
-
328
- const executeDeleteUser = async () => {
329
- if (!userToDelete) return;
330
- setDeletingUser(true);
331
- try {
332
- const res = await fetch(`/api/auth/users/${userToDelete.id}`, {
333
- method: 'DELETE',
334
- headers: { 'Authorization': `Bearer ${token}` }
335
- });
336
- if (res.ok) {
337
- toast.success("User removed successfully!");
338
- setUserToDelete(null);
339
- fetchTeamUsers();
340
- } else {
341
- const data = await res.json();
342
- toast.error(data.message || "Failed to remove user");
343
- }
344
- } catch (err) {
345
- toast.error("Error removing user");
346
- } finally {
347
- setDeletingUser(false);
348
- }
349
- };
350
-
351
- const handlePasswordChange = async (e) => {
352
- e.preventDefault();
353
- setPasswordStatus({ loading: true, error: null, success: false });
354
- if (passwordData.newPassword !== passwordData.confirmPassword) {
355
- setPasswordStatus({ loading: false, error: "New passwords do not match", success: false });
356
- return;
357
- }
358
- if (passwordData.newPassword.length < 6) {
359
- setPasswordStatus({ loading: false, error: "New password must be at least 6 characters", success: false });
360
- return;
361
- }
362
- try {
363
- const res = await fetch('/api/auth/password', {
364
- method: 'PUT',
365
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
366
- body: JSON.stringify({ currentPassword: passwordData.currentPassword, newPassword: passwordData.newPassword })
367
- });
368
- let data = {};
369
- try { data = await res.json(); } catch (e) { }
370
- if (res.ok) {
371
- setPasswordStatus({ loading: false, error: null, success: true });
372
- setPasswordData({ currentPassword: '', newPassword: '', confirmPassword: '' });
373
- toast.success("Password updated successfully!");
374
- setTimeout(() => setPasswordStatus(prev => ({ ...prev, success: false })), 3000);
375
- } else {
376
- const errorMsg = data.message || "Failed to update password";
377
- setPasswordStatus({ loading: false, error: errorMsg, success: false });
378
- toast.error(errorMsg);
379
- }
380
- } catch (err) {
381
- setPasswordStatus({ loading: false, error: "Network error occurred", success: false });
382
- toast.error("Network error occurred");
383
- }
384
- };
385
-
386
- const fetchSettings = async () => {
387
- try {
388
- const res = await fetch('/api/vulnerabilities/settings', {
389
- headers: { 'Authorization': `Bearer ${token}` }
390
- });
391
- if (res.ok) {
392
- const data = await res.json();
393
- setEmailNotifications(data.settings.email_notifications);
394
- setWebhookUrl(data.settings.webhook_url || '');
395
- setSeverityThreshold(data.settings.severity_threshold);
396
- }
397
- } catch (err) {
398
- console.error("Error loading alert settings", err);
399
- } finally {
400
- setLoading(false);
401
- }
402
- };
403
-
404
- const fetchNotificationHistory = async () => {
405
- setLoadingHistory(true);
406
- try {
407
- const res = await fetch('/api/auth/notifications', {
408
- headers: { 'Authorization': `Bearer ${token}` }
409
- });
410
- if (res.ok) {
411
- const data = await res.json();
412
- setNotificationHistory(data.notifications || []);
413
- }
414
- } catch (err) {
415
- console.error("Error loading notification history", err);
416
- } finally {
417
- setLoadingHistory(false);
418
- }
419
- };
420
-
421
  useEffect(() => {
422
  if (activeTab === 'billing') {
423
  fetchBillingHistory();
@@ -426,10 +236,10 @@ export const AlertSettingsPage = () => {
426
  if (activeTab === 'notifications') {
427
  fetchNotificationHistory();
428
  }
429
- }, [activeTab, user]);
430
 
431
- const fetchQuotas = async () => {
432
- setLoadingQuotas(true);
433
  try {
434
  // If user has organization_id, fetch from it. Otherwise we might fetch from a general endpoint if it existed, or we just try to fetch the first organization.
435
  // Usually users belong to one organization. Let's try to get their organization ID first, or fetch from /api/auth/organizations
@@ -460,8 +270,8 @@ export const AlertSettingsPage = () => {
460
  }
461
  };
462
 
463
- const fetchBillingHistory = async () => {
464
- setLoadingBilling(true);
465
  try {
466
  const res = await fetch('/api/billing/history', {
467
  headers: { 'Authorization': `Bearer ${token}` }
 
1
+ import { useState, useEffect } from 'react';
2
  import { useAuth } from '../components/AuthContext';
3
  import toast from 'react-hot-toast';
4
  import Profile from './Profile';
 
219
  if (activeTab === 'billing' && reloadUser) {
220
  intervalId = setInterval(() => {
221
  reloadUser();
222
+ fetchQuotas(true);
223
+ fetchBillingHistory(true);
224
+ }, 15000); // Poll every 15 seconds silently
225
  }
226
  return () => {
227
  if (intervalId) clearInterval(intervalId);
228
  };
229
  }, [activeTab, reloadUser]);
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  useEffect(() => {
232
  if (activeTab === 'billing') {
233
  fetchBillingHistory();
 
236
  if (activeTab === 'notifications') {
237
  fetchNotificationHistory();
238
  }
239
+ }, [activeTab, user?.org_id, user?.id]);
240
 
241
+ const fetchQuotas = async (isSilent = false) => {
242
+ if (!isSilent && scanQuotas.length === 0) setLoadingQuotas(true);
243
  try {
244
  // If user has organization_id, fetch from it. Otherwise we might fetch from a general endpoint if it existed, or we just try to fetch the first organization.
245
  // Usually users belong to one organization. Let's try to get their organization ID first, or fetch from /api/auth/organizations
 
270
  }
271
  };
272
 
273
+ const fetchBillingHistory = async (isSilent = false) => {
274
+ if (!isSilent && billingHistory.length === 0) setLoadingBilling(true);
275
  try {
276
  const res = await fetch('/api/billing/history', {
277
  headers: { 'Authorization': `Bearer ${token}` }