Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.
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
12 changes: 11 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
"javascript.format.enable": false,
"typescript.format.enable": false,

"editor.formatOnSave": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},

"search.exclude": {
".git": true,
".eslintcache": true,
Expand All @@ -24,5 +32,7 @@
"test/**/__snapshots__": true,
"yarn.lock": true,
"*.{css,sass,scss}.d.ts": true
}
},

"cSpell.words": ["Popconfirm"]
}
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"start": "node -r @babel/register ./.erb/scripts/CheckPortInUse.js && cross-env yarn start:renderer",
"start:main": "cross-env NODE_ENV=development electron -r ./.erb/scripts/BabelRegister ./src/main.dev.ts",
"start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.babel.js",
"test": "jest"
"test": "jest",
"tsc": "tsc"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
Expand Down Expand Up @@ -166,6 +167,7 @@
"@types/react": "^16.9.44",
"@types/react-dom": "^16.9.9",
"@types/react-router-dom": "^5.1.6",
"@types/uuid": "^8.3.3",
"@types/webpack-env": "^1.15.2",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
Expand Down Expand Up @@ -229,7 +231,7 @@
"@ant-design/colors": "6.0.0",
"@ant-design/icons": "4.6.2",
"@sentry/electron": "2.5.0",
"antd": "4.16.7",
"antd": "4.17.2",
"caniuse-lite": "1.0.30001214",
"clsx": "^1.1.1",
"date-fns": "2.20.1",
Expand All @@ -246,11 +248,13 @@
"moment": "2.29.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-hook-media-query": "^1.0.5",
"react-jss": "^10.6.0",
"react-router-dom": "^5.2.0",
"regenerator-runtime": "^0.13.5",
"source-map-support": "^0.5.19",
"universal-analytics": "^0.4.23"
"universal-analytics": "^0.4.23",
"uuid": "^8.3.2"
},
"devEngines": {
"node": ">=14.x",
Expand Down
12 changes: 3 additions & 9 deletions src/App.global.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@font-face {
font-family: "Material Icons";
src: url("~material-icons/iconfont/material-icons.woff2") format("woff2"),
url("~material-icons/iconfont/material-icons.woff2") format("woff");
font-family: 'Material Icons';
src: url('~material-icons/iconfont/material-icons.woff2') format('woff2'),
url('~material-icons/iconfont/material-icons.woff2') format('woff');
}

@import '~material-icons/css/material-icons.min.css';
Expand All @@ -16,9 +16,3 @@
display: flex;
align-items: center;
}

.flex-1 {
flex: 1
}

@purple: #713A91;
44 changes: 44 additions & 0 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Layout } from 'antd';
import { observer } from 'mobx-react';
import useMediaQuery from 'react-hook-media-query';
import { createUseStyles } from 'react-jss';

import HeaderLink from './HeaderLink';
import Profile from './Profile';
import TaskControl from './TaskControl';
import ProgressBar from './ProgressBar';

const { Header: HeaderBase } = Layout;

const query = '(min-width: 950px)';
function Header() {
const style = useStyle();
const isBigScreen = useMediaQuery(query);

return (
<HeaderBase>
<HeaderLink>
<Link to="/hours">Hours</Link>
</HeaderLink>
<HeaderLink>
<Link to="/projects">Projects</Link>
</HeaderLink>
<HeaderLink>
<Link to="/dashboard">Dashboard</Link>
</HeaderLink>
<span className={style.flex1}>{isBigScreen && <ProgressBar />}</span>
<TaskControl />
<Profile />
</HeaderBase>
);
}

export default observer(Header);

const useStyle = createUseStyles({
flex1: {
flex: 1,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface HeaderMenuProps {
children: React.ReactNode;
}

export default observer(function HeaderMenu({ children }: HeaderMenuProps) {
export default observer(function HeaderLink({ children }: HeaderMenuProps) {
const classes = useStyles();

return <span className={classes.root}>{children}</span>;
Expand Down
51 changes: 51 additions & 0 deletions src/components/PlayStopButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { SyntheticEvent, useCallback } from 'react';
import { CaretRightFilled, PauseOutlined } from '@ant-design/icons';
import { observer } from 'mobx-react';
import { createUseStyles } from 'react-jss';

import CircleButton from './CircleButton';
import rootStore from '../modules/RootStore';
import TaskModel from '../modules/tasks/models/TaskModel';

const { tasksStore } = rootStore;

interface PlayStopButtonProps {
task: TaskModel | undefined;
className?: string;
}

function PlayStopButton({ task, className }: PlayStopButtonProps) {
const classes = useStyles();

const toggleTask = useCallback(
(e: SyntheticEvent) => {
e.stopPropagation();
if (task) {
if (!task?.active) {
tasksStore.startTimer(task);
} else {
tasksStore.stopTimer();
}
}
},
[task]
);

return (
<CircleButton onClick={toggleTask} className={className}>
{!task?.active ? (
<CaretRightFilled className={classes.icon} />
) : (
<PauseOutlined className={classes.icon} />
)}
</CircleButton>
);
}

export default observer(PlayStopButton);

const useStyles = createUseStyles({
icon: {
color: 'white',
},
});
53 changes: 0 additions & 53 deletions src/components/PlayStopButton/PlayStopButton.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Space } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
import { createUseStyles } from 'react-jss';

import rootStore from '../../modules/RootStore';
import useModal from '../../hooks/ModalHook';
import SettingsModal from '../SettingsModal/SettingsModal';
import rootStore from '../modules/RootStore';
import useModal from '../hooks/ModalHook';
import SettingsModal from './SettingsModal/SettingsModal';

const { settingsStore } = rootStore;

Expand Down
98 changes: 98 additions & 0 deletions src/components/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useCallback, useMemo, useState } from 'react';
import { observer } from 'mobx-react';
import { Slider } from 'antd';
import { isSameDay } from 'date-fns';
import { createUseStyles } from 'react-jss';

import rootStore from '../modules/RootStore';
import { useInterval } from '../hooks/UseInterval';
import { getStartWorkingTime, getTimeItems } from '../helpers/TaskHelper';
import TaskTimeService from '../services/TaskTimeService';
import { toTimeFormat } from '../helpers/DateTime';

const { tasksStore, settingsStore } = rootStore;

function ProgressBar() {
const { settings } = settingsStore;
const workingHoursMs = settings.numberOfWorkingHours;

const style = useStyle();

const [dayUpdateEveryDay, setDayUpdateEveryDay] = useState(new Date());
const [_tick, setTick] = useState(new Date());

const shouldDayUpdate = useCallback(() => {
const now = new Date();
setTick(now);
if (!isSameDay(dayUpdateEveryDay, now)) {
setDayUpdateEveryDay(now);
}
}, [dayUpdateEveryDay]);

useInterval(shouldDayUpdate);

const tasksByProject = useMemo(() => Object.values(tasksStore.tasks), [
tasksStore.tasks,
tasksStore.versionHash,
]);

const tasks = useMemo(() => tasksStore.getTasksByDate(dayUpdateEveryDay), [
tasksByProject,
dayUpdateEveryDay,
]);

const timeItems = useMemo(() => getTimeItems(tasks, dayUpdateEveryDay), [
tasks,
]);

const workingTimeStart = useMemo(() => getStartWorkingTime(timeItems), [
timeItems,
]);

const timeRangeItems = useMemo(() => timeItems.map((t) => t.time), [
timeItems,
]);

const { estimatedWorkingTimeEnd, progress } = TaskTimeService.getDayProgress(
timeRangeItems,
workingTimeStart,
workingHoursMs
);

const progressRound = Math.round(progress);
const marks: Record<number, string> = {
0: toTimeFormat(workingTimeStart),
100: toTimeFormat(estimatedWorkingTimeEnd),
};
if (progressRound > 10 && progressRound < 90) {
marks[progressRound] = `${progressRound}%`;
}

const tipFormatter = useMemo(() => {
if (progressRound <= 10 || progressRound >= 90) {
return (value?: number) => `${value}%`;
}
return null;
}, [progressRound]);

return (
<Slider
marks={marks}
value={progress}
className={style.slider}
tipFormatter={tipFormatter}
/>
);
}

const useStyle = createUseStyles({
slider: {
'& .ant-slider-mark': {
'& .ant-slider-mark-text': {
color: 'white',
},
},
},
});

export default observer(ProgressBar);
Loading