Compare commits
2 Commits
main
...
chore/mixp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b69e36d3 | ||
|
|
08ee87da17 |
@@ -23,12 +23,15 @@ import { fetchBoardTaskGroups } from '@/features/board/board-slice';
|
||||
import { setImportTaskTemplateDrawerOpen } from '@/features/project/project.slice';
|
||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||
import { fetchTaskGroups } from '@/features/tasks/tasks.slice';
|
||||
import { evt_project_import_tasks } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const ImportTaskTemplate = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation('project-view/import-task-templates');
|
||||
const { tab } = useTabSearchParam();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const { importTaskTemplateDrawerOpen, projectId } = useAppSelector(state => state.projectReducer);
|
||||
const [templates, setTemplates] = useState<ITaskTemplatesGetResponse[]>([]);
|
||||
@@ -86,6 +89,7 @@ const ImportTaskTemplate = () => {
|
||||
if (!projectId || tasks.length === 0) return;
|
||||
|
||||
try {
|
||||
trackMixpanelEvent(evt_project_import_tasks);
|
||||
setImporting(true);
|
||||
const res = await taskTemplatesApiService.importTemplate(projectId, tasks);
|
||||
if (res.done) {
|
||||
@@ -117,7 +121,12 @@ const ImportTaskTemplate = () => {
|
||||
footer={
|
||||
<Flex justify="end" gap={10}>
|
||||
<Button onClick={handleClose}>{t('cancel')}</Button>
|
||||
<Button type="primary" onClick={handleImport} loading={importing} disabled={tasks.length === 0}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleImport}
|
||||
loading={importing}
|
||||
disabled={tasks.length === 0}
|
||||
>
|
||||
{t('import')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
@@ -8,6 +8,8 @@ import logger from '@/utils/errorLogger';
|
||||
import { ITaskTemplateGetResponse } from '@/types/settings/task-templates.types';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { setSelectedTasks } from '@/features/project/project.slice';
|
||||
import { evt_project_task_create } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
interface TaskTemplateDrawerProps {
|
||||
showDrawer: boolean;
|
||||
@@ -21,6 +23,7 @@ const TaskTemplateDrawer = ({
|
||||
onClose,
|
||||
}: TaskTemplateDrawerProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {trackMixpanelEvent} = useMixpanelTracking();
|
||||
const { t } = useTranslation('task-template-drawer');
|
||||
const [form] = Form.useForm();
|
||||
const [templateData, setTemplateData] = useState<ITaskTemplateGetResponse>({});
|
||||
@@ -75,6 +78,8 @@ const TaskTemplateDrawer = ({
|
||||
const values = form.getFieldsValue();
|
||||
if (!values.name || !templateData.tasks) return;
|
||||
try {
|
||||
trackMixpanelEvent(evt_project_task_create);
|
||||
|
||||
setCreatingTemplate(true);
|
||||
const res = await taskTemplatesApiService.createTemplate({
|
||||
name: values.name || '',
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { NewTaskType, updateTaskDate } from '@features/roadmap/roadmap-slice';
|
||||
import { colors } from '@/styles/colors';
|
||||
import RoadmapTaskCell from './roadmap-task-cell';
|
||||
import { evt_roadmap_drag_change_date } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const RoadmapTable = () => {
|
||||
// Get task list and expanded tasks from roadmap slice
|
||||
@@ -15,11 +17,18 @@ const RoadmapTable = () => {
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
// function to handle date changes
|
||||
const handleDateChange = (taskId: string, dateType: 'start' | 'end', date: Dayjs) => {
|
||||
const updatedDate = date.toDate();
|
||||
|
||||
trackMixpanelEvent(evt_roadmap_drag_change_date, {
|
||||
task_id: taskId,
|
||||
date_type: dateType,
|
||||
new_date: updatedDate.toISOString(),
|
||||
});
|
||||
|
||||
dispatch(
|
||||
updateTaskDate({
|
||||
taskId,
|
||||
|
||||
@@ -37,7 +37,7 @@ import logger from '@/utils/errorLogger';
|
||||
// Components
|
||||
import EmptyListPlaceholder from '../../../../components/EmptyListPlaceholder';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { evt_project_members_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { evt_project_members_visit, evt_people_delete } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
interface PaginationType {
|
||||
@@ -60,7 +60,7 @@ const ProjectViewMembers = () => {
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const { refreshTimestamp } = useAppSelector(state => state.projectReducer);
|
||||
|
||||
|
||||
// State
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [members, setMembers] = useState<IProjectMembersViewModel>();
|
||||
@@ -104,6 +104,11 @@ const ProjectViewMembers = () => {
|
||||
try {
|
||||
const res = await projectMembersApiService.deleteProjectMember(memberId, projectId);
|
||||
if (res.done) {
|
||||
trackMixpanelEvent(evt_people_delete, {
|
||||
project_id: projectId,
|
||||
member_id: memberId,
|
||||
});
|
||||
|
||||
void getProjectMembers();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -138,7 +143,14 @@ const ProjectViewMembers = () => {
|
||||
// Effects
|
||||
useEffect(() => {
|
||||
void getProjectMembers();
|
||||
}, [refreshTimestamp, projectId, pagination.current, pagination.pageSize, pagination.field, pagination.order]);
|
||||
}, [
|
||||
refreshTimestamp,
|
||||
projectId,
|
||||
pagination.current,
|
||||
pagination.pageSize,
|
||||
pagination.field,
|
||||
pagination.order,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_project_members_visit);
|
||||
@@ -151,9 +163,13 @@ const ProjectViewMembers = () => {
|
||||
title: t('nameColumn'),
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
sortOrder: pagination.order === 'ascend' && pagination.field === 'name' ? 'ascend' :
|
||||
pagination.order === 'descend' && pagination.field === 'name' ? 'descend' : null,
|
||||
render: (_,record: IProjectMemberViewModel) => (
|
||||
sortOrder:
|
||||
pagination.order === 'ascend' && pagination.field === 'name'
|
||||
? 'ascend'
|
||||
: pagination.order === 'descend' && pagination.field === 'name'
|
||||
? 'descend'
|
||||
: null,
|
||||
render: (_, record: IProjectMemberViewModel) => (
|
||||
<Flex gap={8} align="center">
|
||||
<Avatar size={28} src={record.avatar_url}>
|
||||
{record.name?.charAt(0)}
|
||||
@@ -167,8 +183,12 @@ const ProjectViewMembers = () => {
|
||||
title: t('jobTitleColumn'),
|
||||
dataIndex: 'job_title',
|
||||
sorter: true,
|
||||
sortOrder: pagination.order === 'ascend' && pagination.field === 'job_title' ? 'ascend' :
|
||||
pagination.order === 'descend' && pagination.field === 'job_title' ? 'descend' : null,
|
||||
sortOrder:
|
||||
pagination.order === 'ascend' && pagination.field === 'job_title'
|
||||
? 'ascend'
|
||||
: pagination.order === 'descend' && pagination.field === 'job_title'
|
||||
? 'descend'
|
||||
: null,
|
||||
render: (_, record: IProjectMemberViewModel) => (
|
||||
<Typography.Text style={{ marginInlineStart: 12 }}>
|
||||
{record?.job_title || '-'}
|
||||
@@ -180,8 +200,12 @@ const ProjectViewMembers = () => {
|
||||
title: t('emailColumn'),
|
||||
dataIndex: 'email',
|
||||
sorter: true,
|
||||
sortOrder: pagination.order === 'ascend' && pagination.field === 'email' ? 'ascend' :
|
||||
pagination.order === 'descend' && pagination.field === 'email' ? 'descend' : null,
|
||||
sortOrder:
|
||||
pagination.order === 'ascend' && pagination.field === 'email'
|
||||
? 'ascend'
|
||||
: pagination.order === 'descend' && pagination.field === 'email'
|
||||
? 'descend'
|
||||
: null,
|
||||
render: (_, record: IProjectMemberViewModel) => (
|
||||
<Typography.Text>{record.email}</Typography.Text>
|
||||
),
|
||||
@@ -210,8 +234,12 @@ const ProjectViewMembers = () => {
|
||||
title: t('accessColumn'),
|
||||
dataIndex: 'access',
|
||||
sorter: true,
|
||||
sortOrder: pagination.order === 'ascend' && pagination.field === 'access' ? 'ascend' :
|
||||
pagination.order === 'descend' && pagination.field === 'access' ? 'descend' : null,
|
||||
sortOrder:
|
||||
pagination.order === 'ascend' && pagination.field === 'access'
|
||||
? 'ascend'
|
||||
: pagination.order === 'descend' && pagination.field === 'access'
|
||||
? 'descend'
|
||||
: null,
|
||||
render: (_, record: IProjectMemberViewModel) => (
|
||||
<Typography.Text style={{ textTransform: 'capitalize' }}>{record.access}</Typography.Text>
|
||||
),
|
||||
|
||||
@@ -22,8 +22,17 @@ import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { SocketEvents } from '@/shared/socket-events';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
import { useSocket } from '@/socket/socketContext';
|
||||
import { setProject, setImportTaskTemplateDrawerOpen, setRefreshTimestamp } from '@features/project/project.slice';
|
||||
import { addTask, fetchTaskGroups, fetchTaskListColumns, IGroupBy } from '@features/tasks/tasks.slice';
|
||||
import {
|
||||
setProject,
|
||||
setImportTaskTemplateDrawerOpen,
|
||||
setRefreshTimestamp,
|
||||
} from '@features/project/project.slice';
|
||||
import {
|
||||
addTask,
|
||||
fetchTaskGroups,
|
||||
fetchTaskListColumns,
|
||||
IGroupBy,
|
||||
} from '@features/tasks/tasks.slice';
|
||||
import ProjectStatusIcon from '@/components/common/project-status-icon/project-status-icon';
|
||||
import { formatDate } from '@/utils/timeUtils';
|
||||
import { toggleSaveAsTemplateDrawer } from '@/features/projects/projectsSlice';
|
||||
@@ -49,6 +58,14 @@ import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||
import { addTaskCardToTheTop, fetchBoardTaskGroups } from '@/features/board/board-slice';
|
||||
import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/phases.slice';
|
||||
|
||||
import {
|
||||
evt_project_task_create,
|
||||
evt_project_refresh_click,
|
||||
evt_project_settings_click,
|
||||
evt_project_import_tasks_click,
|
||||
} from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const ProjectViewHeader = () => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('project-view/project-view-header');
|
||||
@@ -56,24 +73,29 @@ const ProjectViewHeader = () => {
|
||||
const currentSession = useAuthService().getCurrentSession();
|
||||
const isOwnerOrAdmin = useAuthService().isOwnerOrAdmin();
|
||||
const isProjectManager = useIsProjectManager();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
const { tab } = useTabSearchParam();
|
||||
|
||||
const { socket } = useSocket();
|
||||
|
||||
const {
|
||||
project: selectedProject,
|
||||
projectId,
|
||||
} = useAppSelector(state => state.projectReducer);
|
||||
const { project: selectedProject, projectId } = useAppSelector(state => state.projectReducer);
|
||||
const { loadingGroups, groupBy } = useAppSelector(state => state.taskReducer);
|
||||
|
||||
const [creatingTask, setCreatingTask] = useState(false);
|
||||
|
||||
const handleRefresh = () => {
|
||||
if (!projectId) return;
|
||||
|
||||
trackMixpanelEvent(evt_project_refresh_click, {
|
||||
project_id: projectId,
|
||||
tab: tab,
|
||||
project_name: selectedProject?.name,
|
||||
});
|
||||
|
||||
switch (tab) {
|
||||
case 'tasks-list':
|
||||
dispatch(fetchTaskListColumns(projectId));
|
||||
dispatch(fetchPhasesByProjectId(projectId))
|
||||
dispatch(fetchPhasesByProjectId(projectId));
|
||||
dispatch(fetchTaskGroups(projectId));
|
||||
break;
|
||||
case 'board':
|
||||
@@ -113,6 +135,11 @@ const ProjectViewHeader = () => {
|
||||
|
||||
const handleSettingsClick = () => {
|
||||
if (selectedProject?.id) {
|
||||
trackMixpanelEvent(evt_project_settings_click, {
|
||||
project_id: selectedProject.id,
|
||||
project_name: selectedProject.name,
|
||||
});
|
||||
|
||||
dispatch(setProjectId(selectedProject.id));
|
||||
dispatch(fetchProjectData(selectedProject.id));
|
||||
dispatch(toggleProjectDrawer());
|
||||
@@ -123,6 +150,14 @@ const ProjectViewHeader = () => {
|
||||
try {
|
||||
setCreatingTask(true);
|
||||
|
||||
trackMixpanelEvent(evt_project_task_create, {
|
||||
project_id: selectedProject?.id,
|
||||
project_name: selectedProject?.name,
|
||||
reporter_id: currentSession?.id,
|
||||
team_id: currentSession?.team_id,
|
||||
creation_method: 'quick_create',
|
||||
});
|
||||
|
||||
const body: ITaskCreateRequest = {
|
||||
name: DEFAULT_TASK_NAME,
|
||||
project_id: selectedProject?.id,
|
||||
@@ -155,6 +190,11 @@ const ProjectViewHeader = () => {
|
||||
};
|
||||
|
||||
const handleImportTaskTemplate = () => {
|
||||
trackMixpanelEvent(evt_project_import_tasks_click, {
|
||||
project_id: selectedProject?.id,
|
||||
project_name: selectedProject?.name,
|
||||
});
|
||||
|
||||
dispatch(setImportTaskTemplateDrawerOpen(true));
|
||||
};
|
||||
|
||||
@@ -221,7 +261,7 @@ const ProjectViewHeader = () => {
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{(isOwnerOrAdmin) && (
|
||||
{isOwnerOrAdmin && (
|
||||
<Tooltip title="Save as template">
|
||||
<Button
|
||||
shape="circle"
|
||||
@@ -298,10 +338,9 @@ const ProjectViewHeader = () => {
|
||||
style={{ paddingInline: 0, marginBlockEnd: 12 }}
|
||||
extra={renderHeaderActions()}
|
||||
/>
|
||||
{createPortal(<ProjectDrawer onClose={() => { }} />, document.body, 'project-drawer')}
|
||||
{createPortal(<ProjectDrawer onClose={() => {}} />, document.body, 'project-drawer')}
|
||||
{createPortal(<ImportTaskTemplate />, document.body, 'import-task-template')}
|
||||
{createPortal(<SaveProjectAsTemplate />, document.body, 'save-project-as-template')}
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,11 +12,14 @@ import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSli
|
||||
import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/phases.slice';
|
||||
import { Empty } from 'antd';
|
||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||
import { evt_project_task_list_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const ProjectViewTaskList = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { projectView } = useTabSearchParam();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const { projectId } = useAppSelector(state => state.projectReducer);
|
||||
const { taskGroups, loadingGroups, groupBy, archived, fields, search } = useAppSelector(
|
||||
@@ -38,6 +41,8 @@ const ProjectViewTaskList = () => {
|
||||
}, [projectView, searchParams, setSearchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_project_task_list_visit);
|
||||
|
||||
if (projectId && groupBy) {
|
||||
if (!loadingColumns) dispatch(fetchTaskListColumns(projectId));
|
||||
if (!loadingPhases) dispatch(fetchPhasesByProjectId(projectId));
|
||||
@@ -54,10 +59,10 @@ const ProjectViewTaskList = () => {
|
||||
<Flex vertical gap={16} style={{ overflowX: 'hidden' }}>
|
||||
<TaskListFilters position="list" />
|
||||
|
||||
{(taskGroups.length === 0 && !loadingGroups) ? (
|
||||
{taskGroups.length === 0 && !loadingGroups ? (
|
||||
<Empty description="No tasks group found" />
|
||||
) : (
|
||||
<Skeleton active loading={loadingGroups} className='mt-4 p-4'>
|
||||
<Skeleton active loading={loadingGroups} className="mt-4 p-4">
|
||||
<TaskGroupWrapper taskGroups={taskGroups} groupBy={groupBy} />
|
||||
</Skeleton>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button, DatePicker, DatePickerProps, Flex, Select, Space } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setDate, setType, toggleSettingsDrawer } from '@/features/schedule/scheduleSlice';
|
||||
@@ -11,6 +11,8 @@ import ScheduleDrawer from '@/features/schedule/ScheduleDrawer';
|
||||
import GranttChart from '@/components/schedule/grant-chart/grantt-chart';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { PickerType } from '@/types/schedule/schedule-v2.types';
|
||||
import { evt_schedule_page_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@@ -31,6 +33,7 @@ const Schedule: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const granttChartRef = useRef<any>(null);
|
||||
const { date, type } = useAppSelector(state => state.scheduleReducer);
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
useDocumentTitle('Schedule');
|
||||
|
||||
@@ -53,6 +56,10 @@ const Schedule: React.FC = () => {
|
||||
console.log('Today:', today);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_schedule_page_visit);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ marginBlockStart: 65, minHeight: '90vh' }}>
|
||||
<Flex align="center" justify="space-between">
|
||||
|
||||
@@ -20,6 +20,11 @@ import { categoriesApiService } from '@/api/settings/categories/categories.api.s
|
||||
import { IProjectCategory, IProjectCategoryViewModel } from '@/types/project/projectCategory.types';
|
||||
import { useDocumentTitle } from '@/hooks/useDoumentTItle';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import {
|
||||
evt_settings_categories_visit,
|
||||
evt_settings_category_delete,
|
||||
} from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const CategoriesSettings = () => {
|
||||
// localization
|
||||
@@ -28,6 +33,7 @@ const CategoriesSettings = () => {
|
||||
useDocumentTitle('Manage Categories');
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
// get currently hover row
|
||||
const [hoverRow, setHoverRow] = useState<string | null>(null);
|
||||
const [categories, setCategories] = useState<IProjectCategoryViewModel[]>([]);
|
||||
@@ -56,6 +62,10 @@ const CategoriesSettings = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_categories_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
getCategories();
|
||||
}, [getCategories]);
|
||||
@@ -70,7 +80,9 @@ const CategoriesSettings = () => {
|
||||
{
|
||||
key: 'associatedTask',
|
||||
title: t('associatedTaskColumn'),
|
||||
render: (record: IProjectCategoryViewModel) => <Typography.Text>{record.usage}</Typography.Text>,
|
||||
render: (record: IProjectCategoryViewModel) => (
|
||||
<Typography.Text>{record.usage}</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'actionBtns',
|
||||
@@ -82,7 +94,10 @@ const CategoriesSettings = () => {
|
||||
icon={<ExclamationCircleFilled style={{ color: colors.vibrantOrange }} />}
|
||||
okText={t('deleteConfirmationOk')}
|
||||
cancelText={t('deleteConfirmationCancel')}
|
||||
onConfirm={() => record.id && dispatch(deleteCategory(record.id))}
|
||||
onConfirm={() => {
|
||||
trackMixpanelEvent(evt_settings_category_delete, { categoryId: record.id });
|
||||
record.id && dispatch(deleteCategory(record.id));
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Delete">
|
||||
<Button shape="default" icon={<DeleteOutlined />} size="small" />
|
||||
|
||||
@@ -31,11 +31,14 @@ import { DEFAULT_PAGE_SIZE } from '@/shared/constants';
|
||||
import ClientDrawer from './client-drawer';
|
||||
import { useDocumentTitle } from '@/hooks/useDoumentTItle';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { evt_settings_clients_visit, evt_settings_clients_create } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const ClientsSettings: React.FC = () => {
|
||||
const { t } = useTranslation('settings/clients');
|
||||
const { clients } = useAppSelector(state => state.clientReducer);
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
useDocumentTitle('Manage Clients');
|
||||
|
||||
@@ -62,6 +65,10 @@ const ClientsSettings: React.FC = () => {
|
||||
};
|
||||
}, [pagination, searchQuery, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_clients_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
getClients();
|
||||
}, [searchQuery]);
|
||||
|
||||
@@ -27,6 +27,11 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import JobTitleDrawer from './job-titles-drawer';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import {
|
||||
evt_settings_job_titles_visit,
|
||||
evt_settings_job_titles_create,
|
||||
} from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
interface PaginationType {
|
||||
current: number;
|
||||
@@ -42,6 +47,7 @@ const JobTitlesSettings = () => {
|
||||
const { t } = useTranslation('settings/job-titles');
|
||||
const dispatch = useAppDispatch();
|
||||
useDocumentTitle('Manage Job Titles');
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
|
||||
const [showDrawer, setShowDrawer] = useState(false);
|
||||
@@ -73,6 +79,10 @@ const JobTitlesSettings = () => {
|
||||
};
|
||||
}, [pagination.current, pagination.pageSize, pagination.field, pagination.order, searchQuery]);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_job_titles_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
getJobTitles();
|
||||
}, [getJobTitles]);
|
||||
@@ -83,6 +93,8 @@ const JobTitlesSettings = () => {
|
||||
};
|
||||
|
||||
const handleCreateClick = () => {
|
||||
trackMixpanelEvent(evt_settings_job_titles_create);
|
||||
|
||||
setSelectedJobId(null);
|
||||
setShowDrawer(true);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,11 @@ import { labelsApiService } from '@/api/taskAttributes/labels/labels.api.service
|
||||
import CustomColorLabel from '@components/task-list-common/labelsSelector/custom-color-label';
|
||||
import { useDocumentTitle } from '@/hooks/useDoumentTItle';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import {
|
||||
evt_settings_labels_visit,
|
||||
evt_settings_labels_delete,
|
||||
} from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const LabelsSettings = () => {
|
||||
const { t } = useTranslation('settings/labels');
|
||||
@@ -27,6 +32,7 @@ const LabelsSettings = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [labels, setLabels] = useState<ITaskLabel[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const filteredData = useMemo(
|
||||
() =>
|
||||
@@ -49,12 +55,18 @@ const LabelsSettings = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_labels_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
getLabels();
|
||||
}, [getLabels]);
|
||||
|
||||
const deleteLabel = async (id: string) => {
|
||||
try {
|
||||
trackMixpanelEvent(evt_settings_labels_delete, { labelId: id });
|
||||
|
||||
const response = await labelsApiService.deleteById(id);
|
||||
if (response.done) {
|
||||
getLabels();
|
||||
|
||||
@@ -6,17 +6,24 @@ import { useDocumentTitle } from '@/hooks/useDoumentTItle';
|
||||
import { INotificationSettings } from '@/types/settings/notifications.types';
|
||||
import { profileSettingsApiService } from '@/api/settings/profile/profile-settings.api.service';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { evt_settings_notifications_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const NotificationsSettings = () => {
|
||||
const { t } = useTranslation('settings/notifications');
|
||||
const [form] = Form.useForm();
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const [notificationsSettings, setNotificationsSettings] = useState<INotificationSettings>({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useDocumentTitle(t('title'));
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_notifications_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
const fetchNotificationsSettings = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -11,10 +11,16 @@ import { ITaskTemplatesGetResponse } from '@/types/settings/task-templates.types
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { taskTemplatesApiService } from '@/api/task-templates/task-templates.api.service';
|
||||
import { calculateTimeGap } from '@/utils/calculate-time-gap';
|
||||
import {
|
||||
evt_settings_task_templates_visit,
|
||||
evt_settings_task_templates_delete,
|
||||
} from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const TaskTemplatesSettings = () => {
|
||||
const { t } = useTranslation('settings/task-templates');
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
const [taskTemplates, setTaskTemplates] = useState<ITaskTemplatesGetResponse[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -34,12 +40,18 @@ const TaskTemplatesSettings = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_task_templates_visit);
|
||||
}, [trackMixpanelEvent]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTaskTemplates();
|
||||
}, []);
|
||||
|
||||
const handleDeleteTemplate = async (id: string) => {
|
||||
try {
|
||||
trackMixpanelEvent(evt_settings_task_templates_delete, { templateId: id });
|
||||
|
||||
setIsLoading(true);
|
||||
await taskTemplatesApiService.deleteTemplate(id);
|
||||
await fetchTaskTemplates();
|
||||
@@ -111,10 +123,10 @@ const TaskTemplatesSettings = () => {
|
||||
<Table
|
||||
loading={isLoading}
|
||||
size="small"
|
||||
pagination={{
|
||||
pagination={{
|
||||
size: 'small',
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => t('totalItems', { total })
|
||||
showTotal: total => t('totalItems', { total }),
|
||||
}}
|
||||
columns={columns}
|
||||
dataSource={taskTemplates}
|
||||
|
||||
@@ -37,11 +37,15 @@ import { DEFAULT_PAGE_SIZE, PAGE_SIZE_OPTIONS } from '@/shared/constants';
|
||||
import { teamMembersApiService } from '@/api/team-members/teamMembers.api.service';
|
||||
import { colors } from '@/styles/colors';
|
||||
|
||||
import { evt_people_refresh_click, evt_people_delete, evt_settings_teams_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const TeamMembersSettings = () => {
|
||||
const { t } = useTranslation('settings/team-members');
|
||||
const dispatch = useAppDispatch();
|
||||
const { socket } = useSocket();
|
||||
const refreshTeamMembers = useAppSelector(state => state.memberReducer.refreshTeamMembers); // Listen to refresh flag
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
const [model, setModel] = useState<ITeamMembersViewModel>({ total: 0, data: [] });
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
@@ -96,6 +100,11 @@ const TeamMembersSettings = () => {
|
||||
setIsLoading(true);
|
||||
const res = await teamMembersApiService.delete(record.id);
|
||||
if (res.done) {
|
||||
trackMixpanelEvent(evt_people_delete, {
|
||||
member_id: record.id,
|
||||
member_name: record.name,
|
||||
});
|
||||
|
||||
await getTeamMembers();
|
||||
}
|
||||
} finally {
|
||||
@@ -114,6 +123,9 @@ const TeamMembersSettings = () => {
|
||||
|
||||
const handleRefresh = useCallback(() => {
|
||||
setIsLoading(true);
|
||||
|
||||
trackMixpanelEvent(evt_people_refresh_click);
|
||||
|
||||
getTeamMembers().finally(() => setIsLoading(false));
|
||||
}, [getTeamMembers]);
|
||||
|
||||
@@ -152,6 +164,8 @@ const TeamMembersSettings = () => {
|
||||
}, [refreshTeamMembers, handleRefresh]);
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_teams_visit);
|
||||
|
||||
getTeamMembers();
|
||||
}, [getTeamMembers]);
|
||||
|
||||
@@ -340,14 +354,11 @@ const TeamMembersSettings = () => {
|
||||
/>
|
||||
</Card>
|
||||
{createPortal(
|
||||
<UpdateMemberDrawer
|
||||
selectedMemberId={selectedMemberId}
|
||||
onRoleUpdate={handleRoleUpdate}
|
||||
/>,
|
||||
<UpdateMemberDrawer selectedMemberId={selectedMemberId} onRoleUpdate={handleRoleUpdate} />,
|
||||
document.body
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamMembersSettings;
|
||||
export default TeamMembersSettings;
|
||||
|
||||
@@ -11,6 +11,9 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { useDocumentTitle } from '@/hooks/useDoumentTItle';
|
||||
import { ITeamGetResponse } from '@/types/teams/team.type';
|
||||
|
||||
import { evt_settings_teams_visit } from '@/shared/worklenz-analytics-events';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
|
||||
const TeamsSettings = () => {
|
||||
useDocumentTitle('Teams');
|
||||
|
||||
@@ -18,8 +21,11 @@ const TeamsSettings = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const { teamsList } = useAppSelector(state => state.teamReducer);
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
useEffect(() => {
|
||||
trackMixpanelEvent(evt_settings_teams_visit);
|
||||
|
||||
dispatch(fetchTeams());
|
||||
}, [dispatch]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user