expand sub tasks

This commit is contained in:
chamiakJ
2025-07-03 01:31:05 +05:30
parent 3bef18901a
commit ecd4d29a38
435 changed files with 13150 additions and 11087 deletions

View File

@@ -35,15 +35,18 @@ const Navbar = () => {
const authService = useAuthService();
const [navRoutesList, setNavRoutesList] = useState<NavRoutesType[]>(navRoutes);
const [isOwnerOrAdmin, setIsOwnerOrAdmin] = useState<boolean>(authService.isOwnerOrAdmin());
const showUpgradeTypes = [ISUBSCRIPTION_TYPE.TRIAL]
const showUpgradeTypes = [ISUBSCRIPTION_TYPE.TRIAL];
useEffect(() => {
authApiService.verify().then(authorizeResponse => {
authApiService
.verify()
.then(authorizeResponse => {
if (authorizeResponse.authenticated) {
authService.setCurrentSession(authorizeResponse.user);
setIsOwnerOrAdmin(!!(authorizeResponse.user.is_admin || authorizeResponse.user.owner));
}
}).catch(error => {
})
.catch(error => {
logger.error('Error during authorization', error);
});
}, []);
@@ -67,9 +70,13 @@ const Navbar = () => {
() =>
navRoutesList
.filter(route => {
if (!route.freePlanFeature && currentSession?.subscription_type === ISUBSCRIPTION_TYPE.FREE) return false;
if (route.adminOnly && !isOwnerOrAdmin) return false;
if (
!route.freePlanFeature &&
currentSession?.subscription_type === ISUBSCRIPTION_TYPE.FREE
)
return false;
if (route.adminOnly && !isOwnerOrAdmin) return false;
return true;
})
.map((route, index) => ({
@@ -91,7 +98,6 @@ const Navbar = () => {
}, [location]);
return (
<Col
style={{
width: '100%',
@@ -139,9 +145,10 @@ const Navbar = () => {
<ConfigProvider wave={{ disabled: true }}>
{isDesktop && (
<Flex gap={20} align="center">
{isOwnerOrAdmin && showUpgradeTypes.includes(currentSession?.subscription_type as ISUBSCRIPTION_TYPE) && (
<UpgradePlanButton />
)}
{isOwnerOrAdmin &&
showUpgradeTypes.includes(
currentSession?.subscription_type as ISUBSCRIPTION_TYPE
) && <UpgradePlanButton />}
{isOwnerOrAdmin && <InviteButton />}
<Flex align="center">
<SwitchTeamButton />

View File

@@ -33,9 +33,9 @@ const TimerButton = () => {
try {
setLoading(true);
setError(null);
const response = await taskTimeLogsApiService.getRunningTimers();
if (response && response.done) {
const timers = Array.isArray(response.body) ? response.body : [];
setRunningTimers(timers);
@@ -54,24 +54,25 @@ const TimerButton = () => {
const updateCurrentTimes = useCallback(() => {
try {
if (!Array.isArray(runningTimers) || runningTimers.length === 0) return;
const newTimes: Record<string, string> = {};
runningTimers.forEach(timer => {
try {
if (!timer || !timer.task_id || !timer.start_time) return;
const startTime = moment(timer.start_time);
if (!startTime.isValid()) {
logError(`Invalid start time for timer ${timer.task_id}: ${timer.start_time}`);
return;
}
const now = moment();
const duration = moment.duration(now.diff(startTime));
const hours = Math.floor(duration.asHours());
const minutes = duration.minutes();
const seconds = duration.seconds();
newTimes[timer.task_id] = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
newTimes[timer.task_id] =
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
} catch (error) {
logError(`Error updating time for timer ${timer?.task_id}`, error);
}
@@ -84,12 +85,12 @@ const TimerButton = () => {
useEffect(() => {
fetchRunningTimers();
// Set up polling to refresh timers every 30 seconds
const pollInterval = setInterval(() => {
fetchRunningTimers();
}, 30000);
return () => clearInterval(pollInterval);
}, [fetchRunningTimers]);
@@ -175,12 +176,12 @@ const TimerButton = () => {
logError('Socket not available for stopping timer');
return;
}
if (!taskId) {
logError('Invalid task ID for stopping timer');
return;
}
try {
socket.emit(SocketEvents.TASK_TIMER_STOP.toString(), JSON.stringify({ task_id: taskId }));
dispatch(updateTaskTimeTracking({ taskId, timeTracking: null }));
@@ -200,15 +201,15 @@ const TimerButton = () => {
}
return (
<div
style={{
width: 350,
maxHeight: 400,
<div
style={{
width: 350,
maxHeight: 400,
overflow: 'auto',
backgroundColor: token.colorBgElevated,
borderRadius: token.borderRadius,
boxShadow: token.boxShadowSecondary,
border: `1px solid ${token.colorBorderSecondary}`
border: `1px solid ${token.colorBorderSecondary}`,
}}
>
{!Array.isArray(runningTimers) || runningTimers.length === 0 ? (
@@ -218,15 +219,15 @@ const TimerButton = () => {
) : (
<List
dataSource={runningTimers}
renderItem={(timer) => {
renderItem={timer => {
if (!timer || !timer.task_id) return null;
return (
<List.Item
style={{
<List.Item
style={{
padding: '12px 16px',
borderBottom: `1px solid ${token.colorBorderSecondary}`,
backgroundColor: 'transparent'
backgroundColor: 'transparent',
}}
>
<div style={{ width: '100%' }}>
@@ -234,16 +235,18 @@ const TimerButton = () => {
<Text strong style={{ fontSize: 14, color: token.colorText }}>
{timer.task_name || 'Unnamed Task'}
</Text>
<div style={{
display: 'inline-block',
backgroundColor: token.colorPrimaryBg,
color: token.colorPrimary,
padding: '2px 8px',
borderRadius: token.borderRadiusSM,
fontSize: 11,
fontWeight: 500,
marginTop: 2
}}>
<div
style={{
display: 'inline-block',
backgroundColor: token.colorPrimaryBg,
color: token.colorPrimary,
padding: '2px 8px',
borderRadius: token.borderRadiusSM,
fontSize: 11,
fontWeight: 500,
marginTop: 2,
}}
>
{timer.project_name || 'Unnamed Project'}
</div>
{timer.parent_task_name && (
@@ -251,18 +254,34 @@ const TimerButton = () => {
Parent: {timer.parent_task_name}
</Text>
)}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
marginBottom: 4,
}}
>
<Text type="secondary" style={{ fontSize: 11 }}>
Started: {timer.start_time ? moment(timer.start_time).format('HH:mm') : '--:--'}
Started:{' '}
{timer.start_time
? moment(timer.start_time).format('HH:mm')
: '--:--'}
</Text>
<Text
strong
style={{
fontSize: 14,
<Text
strong
style={{
fontSize: 14,
color: token.colorPrimary,
fontFamily: 'monospace'
fontFamily: 'monospace',
}}
>
{currentTimes[timer.task_id] || '00:00:00'}
@@ -272,7 +291,7 @@ const TimerButton = () => {
<Button
size="small"
icon={<StopOutlined />}
onClick={(e) => {
onClick={e => {
e.stopPropagation();
handleStopTimer(timer.task_id);
}}
@@ -280,7 +299,7 @@ const TimerButton = () => {
backgroundColor: token.colorErrorBg,
borderColor: token.colorError,
color: token.colorError,
fontWeight: 500
fontWeight: 500,
}}
>
Stop
@@ -296,13 +315,13 @@ const TimerButton = () => {
{hasRunningTimers() && (
<>
<Divider style={{ margin: 0, borderColor: token.colorBorderSecondary }} />
<div
style={{
padding: '8px 16px',
textAlign: 'center',
<div
style={{
padding: '8px 16px',
textAlign: 'center',
backgroundColor: token.colorFillQuaternary,
borderBottomLeftRadius: token.borderRadius,
borderBottomRightRadius: token.borderRadius
borderBottomRightRadius: token.borderRadius,
}}
>
<Text type="secondary" style={{ fontSize: 11 }}>
@@ -376,4 +395,4 @@ const TimerButton = () => {
}
};
export default TimerButton;
export default TimerButton;

View File

@@ -23,7 +23,7 @@ const ProfileButton = ({ isOwnerOrAdmin }: ProfileButtonProps) => {
const { t } = useTranslation('navbar');
const authService = useAuthService();
const currentSession = useAppSelector((state: RootState) => state.userReducer);
const role = getRole();
const themeMode = useAppSelector((state: RootState) => state.themeReducer.mode);
@@ -49,13 +49,24 @@ const ProfileButton = ({ isOwnerOrAdmin }: ProfileButtonProps) => {
<Flex vertical style={{ flex: 1, minWidth: 0 }}>
<Typography.Text
ellipsis={{ tooltip: currentSession?.name }} // Show tooltip on hover
style={{ width: '100%', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
style={{
width: '100%',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{currentSession?.name}
</Typography.Text>
<Typography.Text
ellipsis={{ tooltip: currentSession?.email }} // Show tooltip on hover
style={{ fontSize: 12, width: '100%', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
style={{
fontSize: 12,
width: '100%',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{currentSession?.email}
</Typography.Text>