diff --git a/frontend/app/initialize.tsx b/frontend/app/initialize.tsx
index f56dc05c0..e4c5fdbd4 100644
--- a/frontend/app/initialize.tsx
+++ b/frontend/app/initialize.tsx
@@ -1,5 +1,5 @@
import './styles/index.css';
-import './styles/global.css'
+import './styles/global.css';
import React from 'react';
import { createRoot } from 'react-dom/client';
import './init';
@@ -7,19 +7,19 @@ import Router from './Router';
import { StoreProvider, RootStore } from './mstore';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
-import { ConfigProvider, theme, ThemeConfig } from 'antd';
+import { ConfigProvider, App, theme, ThemeConfig } from 'antd';
import colors from 'App/theme/colors';
import { BrowserRouter } from 'react-router-dom';
import { Notification, MountPoint } from 'UI';
import {
QueryClient,
- QueryClientProvider,
-} from '@tanstack/react-query'
+ QueryClientProvider
+} from '@tanstack/react-query';
// @ts-ignore
window.getCommitHash = () => console.log(window.env.COMMIT_HASH);
-const queryClient = new QueryClient()
+const queryClient = new QueryClient();
const customTheme: ThemeConfig = {
// algorithm: theme.compactAlgorithm,
components: {
@@ -29,7 +29,7 @@ const customTheme: ThemeConfig = {
},
Segmented: {
itemSelectedBg: '#FFFFFF',
- itemSelectedColor: colors['main'],
+ itemSelectedColor: colors['main']
},
Menu: {
colorPrimary: colors.teal,
@@ -46,13 +46,13 @@ const customTheme: ThemeConfig = {
itemSelectedColor: colors['teal'],
itemMarginBlock: 0,
- itemPaddingInline: 50,
- iconMarginInlineEnd: 14,
- collapsedWidth: 180,
+ // itemPaddingInline: 50,
+ // iconMarginInlineEnd: 14,
+ collapsedWidth: 180
},
Button: {
colorPrimary: colors.teal
- }
+ },
},
token: {
colorPrimary: colors.teal,
@@ -64,7 +64,8 @@ const customTheme: ThemeConfig = {
borderRadius: 4,
fontSize: 14,
- fontFamily: '\'Roboto\', \'ArialMT\', \'Arial\''
+ fontFamily: '\'Roboto\', \'ArialMT\', \'Arial\'',
+ fontWeightStrong: 400,
}
};
@@ -73,21 +74,22 @@ document.addEventListener('DOMContentLoaded', () => {
// @ts-ignore
const root = createRoot(container);
-
// const theme = window.localStorage.getItem('theme');
root.render(
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
);
});
diff --git a/frontend/app/layout/SideMenu.tsx b/frontend/app/layout/SideMenu.tsx
index afe132432..901da31f1 100644
--- a/frontend/app/layout/SideMenu.tsx
+++ b/frontend/app/layout/SideMenu.tsx
@@ -9,11 +9,7 @@ import * as routes from 'App/routes';
import {
CLIENT_DEFAULT_TAB,
CLIENT_TABS,
- bookmarks,
client,
- fflags,
- notes,
- sessions,
withSiteId
} from 'App/routes';
import { MODULES } from 'Components/Client/Modules';
@@ -33,13 +29,6 @@ import { useStore } from 'App/mstore';
const { Text } = Typography;
-const TabToUrlMap = {
- all: sessions() as '/sessions',
- bookmark: bookmarks() as '/bookmarks',
- notes: notes() as '/notes',
- flags: fflags() as '/feature-flags'
-};
-
interface Props extends RouteComponentProps {
siteId?: string;
isCollapsed?: boolean;
@@ -135,16 +124,6 @@ function SideMenu(props: Props) {
});
}, [isAdmin, isEnterprise, isPreferencesActive, modules, spotOnly, siteId]);
- React.useEffect(() => {
- const currentLocation = location.pathname;
- // const tab = Object.keys(TabToUrlMap).find((tab: keyof typeof TabToUrlMap) =>
- // currentLocation.includes(TabToUrlMap[tab])
- // );
- // if (tab && tab !== searchStore.activeTab && siteId) {
- // searchStore.setActiveTab({ type: tab });
- // }
- }, [location.pathname]);
-
const menuRoutes: any = {
[MENU.EXIT]: () =>
props.history.push(withSiteId(routes.sessions(), siteId)),
@@ -164,7 +143,6 @@ function SideMenu(props: Props) {
[PREFERENCES_MENU.SESSION_LISTING]: () =>
client(CLIENT_TABS.SESSIONS_LISTING),
[PREFERENCES_MENU.INTEGRATIONS]: () => client(CLIENT_TABS.INTEGRATIONS),
- [PREFERENCES_MENU.METADATA]: () => client(CLIENT_TABS.CUSTOM_FIELDS),
[PREFERENCES_MENU.WEBHOOKS]: () => client(CLIENT_TABS.WEBHOOKS),
[PREFERENCES_MENU.PROJECTS]: () => client(CLIENT_TABS.SITES),
[PREFERENCES_MENU.ROLES_ACCESS]: () => client(CLIENT_TABS.MANAGE_ROLES),
diff --git a/frontend/app/layout/data.ts b/frontend/app/layout/data.ts
index c17c51bc9..7a1c92302 100644
--- a/frontend/app/layout/data.ts
+++ b/frontend/app/layout/data.ts
@@ -24,7 +24,6 @@ export const enum PREFERENCES_MENU {
ACCOUNT = 'account',
SESSION_LISTING = 'session-listing',
INTEGRATIONS = 'integrations',
- METADATA = 'metadata',
WEBHOOKS = 'webhooks',
MODULES = 'modules',
PROJECTS = 'projects',
@@ -131,7 +130,6 @@ export const preferences: Category[] = [
{ label: 'Account', key: PREFERENCES_MENU.ACCOUNT, icon: 'person' },
{ label: 'Sessions Listing', key: PREFERENCES_MENU.SESSION_LISTING, icon: 'card-list' },
{ label: 'Integrations', key: PREFERENCES_MENU.INTEGRATIONS, icon: 'plug' },
- { label: 'Metadata', key: PREFERENCES_MENU.METADATA, icon: 'tags' },
{ label: 'Webhooks', key: PREFERENCES_MENU.WEBHOOKS, icon: 'link-45deg' },
{ label: 'Modules', key: PREFERENCES_MENU.MODULES, icon: 'puzzle' },
{ label: 'Projects', key: PREFERENCES_MENU.PROJECTS, icon: 'folder2' },
@@ -159,4 +157,4 @@ export const spotOnlyCats = [
MENU.PREFERENCES,
MENU.SUPPORT,
MENU.SPOTS,
-]
\ No newline at end of file
+]
diff --git a/frontend/app/mstore/projectsStore.ts b/frontend/app/mstore/projectsStore.ts
index c30e1f862..272ccb1a3 100644
--- a/frontend/app/mstore/projectsStore.ts
+++ b/frontend/app/mstore/projectsStore.ts
@@ -2,7 +2,14 @@ import { makeAutoObservable, runInAction } from 'mobx';
import Project from './types/project';
import GDPR from './types/gdpr';
import { GLOBAL_HAS_NO_RECORDINGS, SITE_ID_STORAGE_KEY } from 'App/constants/storageKeys';
-import { projectsService } from "App/services";
+import { projectsService } from 'App/services';
+import { toast } from '.store/react-toastify-virtual-9dd0f3eae1/package';
+
+interface Config {
+ project: Project | null;
+ pid: number | undefined;
+ tab: string;
+}
export default class ProjectsStore {
list: Project[] = [];
@@ -11,6 +18,11 @@ export default class ProjectsStore {
active: Project | null = null;
sitesLoading = true;
loading = false;
+ config: Config = {
+ project: null,
+ pid: undefined,
+ tab: 'installation'
+ };
constructor() {
const storedSiteId = localStorage.getItem(SITE_ID_STORAGE_KEY);
@@ -22,45 +34,52 @@ export default class ProjectsStore {
return this.active ? ['ios', 'android'].includes(this.active.platform) : false;
}
+ syncProjectInList = (project: Partial
) => {
+ const index = this.list.findIndex(site => site.id === project.id);
+ if (index !== -1) {
+ this.list[index] = this.list[index].edit(project);
+ }
+ }
+
getSiteId = () => {
return {
siteId: this.siteId,
- active: this.active,
+ active: this.active
};
- }
+ };
initProject = (project: Partial) => {
this.instance = new Project(project);
- }
+ };
setSitesLoading = (loading: boolean) => {
this.sitesLoading = loading;
- }
+ };
setLoading = (loading: boolean) => {
this.loading = loading;
- }
+ };
setSiteId = (siteId: string) => {
localStorage.setItem(SITE_ID_STORAGE_KEY, siteId.toString());
this.siteId = siteId;
this.active = this.list.find((site) => site.id! === siteId) ?? null;
- }
+ };
editGDPR = (gdprData: Partial) => {
if (this.instance) {
this.instance.gdpr.edit(gdprData);
}
- }
+ };
editInstance = (instance: Partial) => {
if (!this.instance) return;
this.instance = this.instance.edit(instance);
- }
+ };
fetchGDPR = async (siteId: string) => {
try {
- const response = await projectsService.fetchGDPR(siteId)
+ const response = await projectsService.fetchGDPR(siteId);
runInAction(() => {
if (this.instance) {
Object.assign(this.instance.gdpr, response.data);
@@ -69,7 +88,7 @@ export default class ProjectsStore {
} catch (error) {
console.error('Failed to fetch GDPR:', error);
}
- }
+ };
saveGDPR = async (siteId: string) => {
if (!this.instance) return;
@@ -77,10 +96,19 @@ export default class ProjectsStore {
const gdprData = this.instance.gdpr.toData();
const response = await projectsService.saveGDPR(siteId, gdprData);
this.editGDPR(response.data);
+
+ try {
+ this.syncProjectInList({
+ id: siteId,
+ gdpr: response.data
+ })
+ } catch (error) {
+ console.error('Failed to sync project in list:', error);
+ }
} catch (error) {
console.error('Failed to save GDPR:', error);
}
- }
+ };
fetchList = async (siteIdFromPath?: string) => {
this.setSitesLoading(true);
@@ -115,7 +143,7 @@ export default class ProjectsStore {
} finally {
this.setSitesLoading(false);
}
- }
+ };
save = async (projectData: Partial) => {
this.setLoading(true);
@@ -132,12 +160,13 @@ export default class ProjectsStore {
this.setSiteId(newSite.id);
this.active = newSite;
});
- } catch (error) {
- console.error('Failed to save site:', error);
+ return response;
+ } catch (error: any) {
+ throw error || 'An error occurred while saving the project.';
} finally {
this.setLoading(false);
}
- }
+ };
updateProjectRecordingStatus = (siteId: string, status: boolean) => {
const site = this.list.find(site => site.id === siteId);
@@ -150,7 +179,7 @@ export default class ProjectsStore {
localStorage.removeItem(GLOBAL_HAS_NO_RECORDINGS);
}
}
- }
+ };
removeProject = async (projectId: string) => {
this.setLoading(true);
@@ -161,13 +190,13 @@ export default class ProjectsStore {
if (this.siteId === projectId) {
this.setSiteId(this.list[0].id!);
}
- })
+ });
} catch (e) {
console.error('Failed to remove project:', e);
} finally {
this.setLoading(false);
}
- }
+ };
updateProject = async (projectId: string, projectData: Partial) => {
this.setLoading(true);
@@ -183,7 +212,24 @@ export default class ProjectsStore {
} catch (error) {
console.error('Failed to update site:', error);
} finally {
- this.setLoading(false)
+ this.setLoading(false);
}
- }
+ };
+
+ setConfigProject = (pid?: number) => {
+ if (!pid) {
+ const firstProject = this.list[0];
+ this.config.pid = firstProject?.projectId ?? undefined;
+ this.config.project = firstProject ?? null;
+ return;
+ }
+
+ const project = this.list.find(site => site.projectId === pid);
+ this.config.pid = project?.projectId ?? undefined;
+ this.config.project = project ?? null;
+ };
+
+ setConfigTab = (tab: string | null) => {
+ this.config.tab = tab ?? 'installation';
+ };
}
diff --git a/frontend/app/mstore/settingsStore.ts b/frontend/app/mstore/settingsStore.ts
index fe51173fa..2c84ac202 100644
--- a/frontend/app/mstore/settingsStore.ts
+++ b/frontend/app/mstore/settingsStore.ts
@@ -6,6 +6,7 @@ import Webhook, { IWebhook } from 'Types/webhook';
import { webhookService } from 'App/services';
import { GettingStarted } from './types/gettingStarted';
import { MENU_COLLAPSED } from 'App/constants/storageKeys';
+import { projectStore } from '@/mstore/index';
interface CaptureConditions {
rate: number;
@@ -102,6 +103,15 @@ export default class SettingsStore {
conditionalCapture: data.conditionalCapture,
captureConditions: data.conditions,
});
+
+ try {
+ projectStore.syncProjectInList({
+ id: projectId + '',
+ sampleRate: data.rate,
+ })
+ } catch (e) {
+ console.error('Failed to update project in list:', e);
+ }
toast.success('Settings updated successfully');
})
.catch((err) => {
diff --git a/frontend/app/mstore/tagWatchStore.ts b/frontend/app/mstore/tagWatchStore.ts
index 433b27451..2cc39b81c 100644
--- a/frontend/app/mstore/tagWatchStore.ts
+++ b/frontend/app/mstore/tagWatchStore.ts
@@ -1,6 +1,7 @@
import { makeAutoObservable } from 'mobx';
import { tagWatchService } from 'App/services';
import { CreateTag, Tag } from 'App/services/TagWatchService';
+import { projectStore } from '@/mstore';
export default class TagWatchStore {
tags: Tag[] = [];
@@ -18,13 +19,14 @@ export default class TagWatchStore {
this.isLoading = loading;
};
- getTags = async () => {
+ getTags = async (projectId?: number) => {
if (this.isLoading) {
return;
}
this.setLoading(true);
try {
- const tags: Tag[] = await tagWatchService.getTags();
+ const pid = projectId || projectStore.active?.projectId;
+ const tags: Tag[] = await tagWatchService.getTags(pid!);
this.setTags(tags);
return tags;
} catch (e) {
@@ -34,28 +36,31 @@ export default class TagWatchStore {
}
};
- createTag = async (data: CreateTag) => {
+ createTag = async (data: CreateTag, projectId?: number) => {
try {
- const tagId: number = await tagWatchService.createTag(data);
+ const pid = projectId || projectStore.active?.projectId;
+ const tagId: number = await tagWatchService.createTag(pid!, data);
return tagId;
} catch (e) {
console.error(e);
}
};
- deleteTag = async (id: number) => {
+ deleteTag = async (id: number, projectId?: number) => {
try {
- await tagWatchService.deleteTag(id);
+ const pid = projectId || projectStore.active?.projectId;
+ await tagWatchService.deleteTag(pid!, id);
this.setTags(this.tags.filter((t) => t.tagId !== id));
} catch (e) {
console.error(e);
}
};
- updateTagName = async (id: number, name: string) => {
+ updateTagName = async (id: number, name: string, projectId?: number) => {
try {
- await tagWatchService.updateTagName(id, name);
- const updatedTag = this.tags.find((t) => t.tagId === id)
+ const pid = projectId || projectStore.active?.projectId;
+ await tagWatchService.updateTagName(pid!, id, name);
+ const updatedTag = this.tags.find((t) => t.tagId === id);
if (updatedTag) {
this.setTags(this.tags.map((t) => t.tagId === id ? { ...updatedTag, name } : t));
}
diff --git a/frontend/app/services/ProjectsService.ts b/frontend/app/services/ProjectsService.ts
index dec448903..393bcb835 100644
--- a/frontend/app/services/ProjectsService.ts
+++ b/frontend/app/services/ProjectsService.ts
@@ -1,39 +1,47 @@
-import BaseService from "./BaseService";
+import BaseService from './BaseService';
export default class ProjectsService extends BaseService {
fetchGDPR = async (siteId: string) => {
const r = await this.client.get(`/${siteId}/gdpr`);
return await r.json();
- }
+ };
saveGDPR = async (siteId: string, gdprData: any) => {
const r = await this.client.post(`/${siteId}/gdpr`, gdprData);
return await r.json();
- }
+ };
fetchList = async () => {
const r = await this.client.get('/projects');
return await r.json();
- }
+ };
- saveProject = async (projectData: any) => {
- const r = await this.client.post('/projects', projectData);
+ saveProject = async (projectData: any): Promise => {
+ try {
+ const response = await this.client.post('/projects', projectData);
+ return response.json();
+ } catch (error: any) {
+ if (error.response) {
+ const errorData = await error.response.json();
+ throw errorData.errors?.[0] || 'An error occurred while saving the project.';
+ }
- return await r.json();
- }
+ throw 'An unexpected error occurred.';
+ }
+ };
removeProject = async (projectId: string) => {
- const r = await this.client.delete(`/projects/${projectId}`)
+ const r = await this.client.delete(`/projects/${projectId}`);
return await r.json();
- }
+ };
updateProject = async (projectId: string, projectData: any) => {
const r = await this.client.put(`/projects/${projectId}`, projectData);
return await r.json();
- }
+ };
}
diff --git a/frontend/app/services/TagWatchService.ts b/frontend/app/services/TagWatchService.ts
index f8c9ff461..ab12d3e0c 100644
--- a/frontend/app/services/TagWatchService.ts
+++ b/frontend/app/services/TagWatchService.ts
@@ -12,27 +12,27 @@ export interface Tag extends CreateTag {
}
export default class TagWatchService extends BaseService {
- createTag(data: CreateTag) {
- return this.client.post('/tags', data)
- .then(r => r.json())
- .then((response: { data: any; }) => response.data || {})
+ async createTag(projectId: number, data: CreateTag) {
+ const r = await this.client.post(`/${projectId}/tags`, data);
+ const response = await r.json();
+ return response.data || {};
}
- getTags() {
- return this.client.get('/tags')
- .then(r => r.json())
- .then((response: { data: any; }) => response.data || {})
+ async getTags(projectId: number) {
+ const r = await this.client.get(`/${projectId}/tags`);
+ const response = await r.json();
+ return response.data || {};
}
- deleteTag(id: number) {
- return this.client.delete(`/tags/${id}`)
- .then(r => r.json())
- .then((response: { data: any; }) => response.data || {})
+ async deleteTag(projectId: number, id: number) {
+ const r = await this.client.delete(`/${projectId}/tags/${id}`);
+ const response = await r.json();
+ return response.data || {};
}
- updateTagName(id: number, name: string) {
- return this.client.put(`/tags/${id}`, { name })
- .then(r => r.json())
- .then((response: { data: any; }) => response.data || {})
+ async updateTagName(projectId: number, id: number, name: string) {
+ const r = await this.client.put(`/${projectId}/tags/${id}`, { name });
+ const response = await r.json();
+ return response.data || {};
}
-}
\ No newline at end of file
+}