From 895ab8038525fbbd1a70c4b871aea7ea48b5cb8f Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 2 Aug 2024 17:19:09 +0200 Subject: [PATCH 1/5] fixbug: duplicate action icons --- apps/web/lib/features/task/task-estimate.tsx | 51 +++++++++++-------- .../team/user-team-card/task-estimate.tsx | 7 ++- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/apps/web/lib/features/task/task-estimate.tsx b/apps/web/lib/features/task/task-estimate.tsx index 812f20f05..5a0c5b864 100644 --- a/apps/web/lib/features/task/task-estimate.tsx +++ b/apps/web/lib/features/task/task-estimate.tsx @@ -14,9 +14,18 @@ type Props = { loadingRef?: MutableRefObject; closeable_fc?: () => void; wrapperClassName?: string; + showEditAndSaveButton?: boolean; }; -export function TaskEstimate({ _task, onCloseEdition, className, loadingRef, closeable_fc, wrapperClassName }: Props) { +export function TaskEstimate({ + _task, + onCloseEdition, + className, + loadingRef, + closeable_fc, + wrapperClassName, + showEditAndSaveButton = true +}: Props) { const { targetEl, value, @@ -101,26 +110,28 @@ export function TaskEstimate({ _task, onCloseEdition, className, loadingRef, clo `${editableMode ? 'block' : parseInt(value['minutes']) > 0 ? 'block' : parseInt(value['hours']) > 0 ? 'hidden' : 'block'}` )} /> -
- {!updateLoading ? ( - editableMode ? ( - + {showEditAndSaveButton && ( +
+ {!updateLoading ? ( + editableMode ? ( + + ) : ( + + ) ) : ( - - ) - ) : ( - - )} -
+ + )} +
+ )} ); } diff --git a/apps/web/lib/features/team/user-team-card/task-estimate.tsx b/apps/web/lib/features/team/user-team-card/task-estimate.tsx index 3fee804ca..e33188e37 100644 --- a/apps/web/lib/features/team/user-team-card/task-estimate.tsx +++ b/apps/web/lib/features/team/user-team-card/task-estimate.tsx @@ -60,7 +60,12 @@ export function TaskEstimateInput({ memberInfo, edition }: Omit {task && ( <> - + - + From 56cafad6df1fbc7dfe6b0b29a08ee385d429d13c Mon Sep 17 00:00:00 2001 From: "Thierry CH." Date: Sun, 4 Aug 2024 22:14:30 +0200 Subject: [PATCH 3/5] feat: separate api call from react component (#2821) * feat: separate api call from react component * feat: add manual time * fix: add validation * feat: check if the endTime is after the startTime --- apps/web/app/hooks/features/useManualTime.ts | 38 ++++++++ apps/web/app/interfaces/ITimer.ts | 7 ++ apps/web/app/interfaces/timer/ITimerLogs.ts | 46 ++++++++++ .../services/client/api/timer/manual-time.ts | 13 +++ .../manual-time/add-manual-time-modal.tsx | 90 ++++++++++--------- 5 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 apps/web/app/hooks/features/useManualTime.ts create mode 100644 apps/web/app/services/client/api/timer/manual-time.ts diff --git a/apps/web/app/hooks/features/useManualTime.ts b/apps/web/app/hooks/features/useManualTime.ts new file mode 100644 index 000000000..ba5127675 --- /dev/null +++ b/apps/web/app/hooks/features/useManualTime.ts @@ -0,0 +1,38 @@ +import { useCallback, useState } from 'react'; +import { useQuery } from '../useQuery'; +import { useAuthenticateUser } from './useAuthenticateUser'; +import { addManualTimeRequestAPI } from '@app/services/client/api/timer/manual-time'; +import { IAddManualTimeRequest, ITimeLog } from '@app/interfaces/timer/ITimerLogs'; +import { TimeLogType, TimerSource } from '@app/interfaces'; + +export function useManualTime() { + const { user } = useAuthenticateUser(); + + const { loading: addManualTimeLoading, queryCall: queryAddManualTime } = useQuery(addManualTimeRequestAPI); + const [timeLog, setTimeLog] = useState(); + + const addManualTime = useCallback( + (data: Omit) => { + queryAddManualTime({ + tenantId: user?.tenantId ?? '', + employeeId: user?.employee.id ?? '', + logType: TimeLogType.MANUAL, + source: TimerSource.BROWSER, + ...data + }) + .then((response) => { + setTimeLog(response.data); + }) + .catch((error) => { + console.log(error); + }); + }, + [queryAddManualTime, user?.employee.id, user?.tenantId] + ); + + return { + addManualTimeLoading, + addManualTime, + timeLog + }; +} diff --git a/apps/web/app/interfaces/ITimer.ts b/apps/web/app/interfaces/ITimer.ts index 996cad947..dc68def2d 100644 --- a/apps/web/app/interfaces/ITimer.ts +++ b/apps/web/app/interfaces/ITimer.ts @@ -33,6 +33,13 @@ export enum TimerSource { 'TEAMS' = 'TEAMS' } +export enum TimeLogType { + TRACKED = 'TRACKED', + MANUAL = 'MANUAL', + IDLE = 'IDLE', + RESUMED = 'RESUMED' +} + export interface ITimerStatus { duration: number; lastLog?: ITimer; diff --git a/apps/web/app/interfaces/timer/ITimerLogs.ts b/apps/web/app/interfaces/timer/ITimerLogs.ts index 73e1f3a88..5437d922c 100644 --- a/apps/web/app/interfaces/timer/ITimerLogs.ts +++ b/apps/web/app/interfaces/timer/ITimerLogs.ts @@ -1,3 +1,9 @@ +import { IEmployee } from '../IEmployee'; +import { IOrganization } from '../IOrganization'; +import { ITeamTask } from '../ITask'; +import { TimeLogType, TimerSource } from '../ITimer'; +import { ITimerSlot } from './ITimerSlot'; + export interface ITimerLogsDailyReportRequest { tenantId: string; organizationId: string; @@ -11,3 +17,43 @@ export interface ITimerLogsDailyReport { date: string; // '2024-07-19' sum: number; // in seconds } + +export interface IAddManualTimeRequest { + employeeId: string; + projectId?: string; + taskId?: string; + organizationContactId?: string; + description?: string; + reason?: string; + startedAt: Date; + stoppedAt: Date; + editedAt?: Date; + tags?: string[]; + isBillable?: boolean; + organizationId?: string; + organization?: Pick; + tenantId?: string; + logType: TimeLogType; + source: TimerSource.BROWSER; +} + +export interface ITimeLog { + employee: IEmployee; + employeeId: string; + timesheetId?: string; + task?: ITeamTask; + taskId?: string; + timeSlots?: ITimerSlot[]; + projectId?: string; + startedAt?: Date; + stoppedAt?: Date; + /** Edited At* */ + editedAt?: Date; + description?: string; + reason?: string; + duration: number; + isBillable: boolean; + tags?: string[]; + isRunning?: boolean; + isEdited?: boolean; +} diff --git a/apps/web/app/services/client/api/timer/manual-time.ts b/apps/web/app/services/client/api/timer/manual-time.ts new file mode 100644 index 000000000..9843d61f4 --- /dev/null +++ b/apps/web/app/services/client/api/timer/manual-time.ts @@ -0,0 +1,13 @@ +import { post } from '@app/services/client/axios'; +import { IAddManualTimeRequest, ITimeLog } from '@app/interfaces/timer/ITimerLogs'; + +export async function addManualTimeRequestAPI(request: IAddManualTimeRequest) { + const { startedAt, stoppedAt, ...rest } = request; + const data = { + ...rest, + startedAt: startedAt.toISOString(), + stoppedAt: stoppedAt.toISOString() + }; + + return post(`/timesheet/time-log`, data); +} diff --git a/apps/web/lib/features/manual-time/add-manual-time-modal.tsx b/apps/web/lib/features/manual-time/add-manual-time-modal.tsx index 8137c7da1..cf8c1b4b9 100644 --- a/apps/web/lib/features/manual-time/add-manual-time-modal.tsx +++ b/apps/web/lib/features/manual-time/add-manual-time-modal.tsx @@ -1,6 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import '../../../styles/style.css'; import { useOrganizationTeams, useTeamTasks } from '@app/hooks'; -import api from '@app/services/client/axios'; import { clsxm } from '@app/utils'; import { DatePicker } from '@components/ui/DatePicker'; import { PencilSquareIcon } from '@heroicons/react/20/solid'; @@ -11,6 +11,9 @@ import { FaRegCalendarAlt } from 'react-icons/fa'; import { HiMiniClock } from 'react-icons/hi2'; import { manualTimeReasons } from '@app/constants'; import { useTranslations } from 'next-intl'; +import { IOrganizationTeamList } from '@app/interfaces'; +import { useManualTime } from '@app/hooks/features/useManualTime'; +import { IAddManualTimeRequest } from '@app/interfaces/timer/ITimerLogs'; interface IAddManualTimeModalProps { isOpen: boolean; @@ -23,17 +26,18 @@ export function AddManualTimeModal(props: IAddManualTimeModalProps) { const [isBillable, setIsBillable] = useState(false); const [description, setDescription] = useState(''); const [reason, setReason] = useState(''); - const [errorMsg, setError] = useState(''); - const [loading, setLoading] = useState(false); + const [errorMsg, setErrorMsg] = useState(''); const [endTime, setEndTime] = useState(''); const [date, setDate] = useState(new Date()); const [startTime, setStartTime] = useState(''); - const [teamId, setTeamId] = useState(''); + const [team, setTeam] = useState(); const [taskId, setTaskId] = useState(''); const [timeDifference, setTimeDifference] = useState(''); - const { activeTeamTask, tasks, activeTeamId, activeTeam } = useTeamTasks(); + const { activeTeamTask, tasks, activeTeam } = useTeamTasks(); const { teams } = useOrganizationTeams(); + const { addManualTime, addManualTimeLoading, timeLog } = useManualTime(); + useEffect(() => { const now = new Date(); const currentTime = now.toTimeString().slice(0, 5); @@ -47,40 +51,38 @@ export function AddManualTimeModal(props: IAddManualTimeModalProps) { (e: FormEvent) => { e.preventDefault(); - const timeObject = { - date, - isBillable, - startTime, - endTime, - teamId, + const startedAt = new Date(date); + const stoppedAt = new Date(date); + + // Set time for the started date + startedAt.setHours(parseInt(startTime.split(':')[0])); + startedAt.setMinutes(parseInt(startTime.split(':')[1])); + + // Set time for the stopped date + stoppedAt.setHours(parseInt(endTime.split(':')[0])); + stoppedAt.setMinutes(parseInt(endTime.split(':')[1])); + + const requestData: Omit = { + startedAt, + stoppedAt, taskId, description, reason, - timeDifference + isBillable, + organizationId: team?.organizationId }; - if (date && startTime && endTime && teamId && taskId) { - setLoading(true); - setError(''); - const postData = async () => { - try { - const response = await api.post('/add_time', timeObject); - if (response.data.message) { - setLoading(false); - closeModal(); - } - } catch (err) { - setError('Failed to post data'); - setLoading(false); - } - }; - - postData(); + if (date && startTime && endTime && team && taskId) { + if (endTime > startTime) { + addManualTime(requestData); // [TODO : api] Allow team member to add manual time as well + } else { + setErrorMsg('End time should be after than start time'); + } } else { - setError(`Please complete all required fields with a ${'*'}`); + setErrorMsg("Please complete all required fields with a '*'"); } }, - [closeModal, date, description, endTime, isBillable, reason, startTime, taskId, teamId, timeDifference] + [addManualTime, date, description, endTime, isBillable, reason, startTime, taskId, team] ); const calculateTimeDifference = useCallback(() => { @@ -108,10 +110,18 @@ export function AddManualTimeModal(props: IAddManualTimeModalProps) { if (activeTeamTask) { setTaskId(activeTeamTask.id); } - if (activeTeamId) { - setTeamId(activeTeamId); + if (activeTeam) { + setTeam(activeTeam); + } + }, [activeTeamTask, activeTeam]); + + useEffect(() => { + if (!addManualTimeLoading && timeLog) { + closeModal(); + setDescription(''); + setErrorMsg(''); } - }, [activeTeamTask, activeTeamId]); + }, [addManualTimeLoading, closeModal, timeLog]); return ( * setTeamId(value ? value.id : '')} + onValueChange={(team) => setTeam(team)} itemId={(team) => (team ? team.id : '')} itemToString={(team) => (team ? team.name : '')} triggerClassName="border-slate-100 dark:border-slate-600" @@ -238,10 +248,10 @@ export function AddManualTimeModal(props: IAddManualTimeModalProps) { Task* setTaskId(value ? value.id : '')} + onValueChange={(task) => setTaskId(task ? task.id : '')} itemId={(task) => (task ? task.id : '')} - defaultValue={activeTeamTask} itemToString={(task) => (task ? task.title : '')} triggerClassName="border-slate-100 dark:border-slate-600" /> @@ -278,8 +288,8 @@ export function AddManualTimeModal(props: IAddManualTimeModalProps) { View timesheet + ), + )} + +
+ {activeTab === 'media' && ( + <> + {settings.media && settings.media.camera && ( + <> +

Camera

+
+ Camera +
+ +
+
+ + )} + {settings.media && settings.media.microphone && ( + <> +

Microphone

+
+ Microphone +
+ +
+
+ + )} + {settings.media && settings.media.speaker && ( + <> +

Speaker & Headphones

+
+ Audio Output +
+ +
+
+ + )} + + )} + {activeTab === 'effects' && ( + <> +

Audio

+
+ + setIsNoiseFilterEnabled(ev.target.checked)} + checked={isNoiseFilterEnabled} + > +
+ + )} +
+
+
+ You can invite your colleagues to join the meeting by sharing this link. + +
+ + +
+ + ); +} diff --git a/apps/web/lib/utils.ts b/apps/web/lib/utils.ts index 694abe8b1..bd8399a5b 100644 --- a/apps/web/lib/utils.ts +++ b/apps/web/lib/utils.ts @@ -40,3 +40,12 @@ export function chunk(array: T[], size: number): T[][] { return result; } + +export const shortenLink = (value: any): string => { + if (typeof value !== 'string' || value.length <= 19) { + return value; + } + const start = value.substring(0, window.location.origin.length); + const end = value.substring(value.length - 10); + return `${start}...${end}`; +}; diff --git a/apps/web/package.json b/apps/web/package.json index e45bd7e8a..45189cfb7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -35,6 +35,7 @@ "@jitsu/jitsu-react": "^1.3.0", "@livekit/components-react": "^2.4.1", "@livekit/components-styles": "^1.0.12", + "@livekit/krisp-noise-filter": "^0.2.5", "@nivo/calendar": "^0.87.0", "@nivo/core": "^0.87.0", "@opentelemetry/api": "^1.7.0", diff --git a/apps/web/styles/settings.module.css b/apps/web/styles/settings.module.css new file mode 100644 index 000000000..d1cd763ea --- /dev/null +++ b/apps/web/styles/settings.module.css @@ -0,0 +1,17 @@ +.tabs { + position: relative; + display: flex; + align-content: space-between; +} + +.tabs>.tab { + padding: 0.5rem; + border-radius: 0; + padding-bottom: 0.5rem; + border-bottom: 3px solid; + border-color: var(--bg5); +} + +.tabs>.tab[aria-pressed='true'] { + border-color: #3826a6; +} diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index d7203d899..936605bb2 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -129,7 +129,8 @@ module.exports = { }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out' + 'accordion-up': 'accordion-up 0.2s ease-out', + "spine": "spin 10s linear infinite", }, typography: { DEFAULT: { diff --git a/yarn.lock b/yarn.lock index 50dfd001c..5c2486c87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3691,6 +3691,11 @@ resolved "https://registry.yarnpkg.com/@livekit/components-styles/-/components-styles-1.0.12.tgz#53b2e4f933b46f53c15b889a6ba7047cdab3a2a1" integrity sha512-Hsxkfq240w0tMPtkQTHQotpkYfIY4lhP2pzegvOIIV/nYxj8LeRYypUjxJpFw3s6jQcV/WQS7oCYmFQdy98Jtw== +"@livekit/krisp-noise-filter@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@livekit/krisp-noise-filter/-/krisp-noise-filter-0.2.5.tgz#889268d0a842cb22e75cfc838c42b40c2a6c226c" + integrity sha512-/lY0geizAGp34qbTqp6WUZsNuIM5c2B8UV0WePNUyCntmku9WbkGJiJSUeXnunpf0a7tVPDUK6QFCXBw7dub5w== + "@livekit/protocol@1.19.1", "@livekit/protocol@^1.19.0": version "1.19.1" resolved "https://registry.yarnpkg.com/@livekit/protocol/-/protocol-1.19.1.tgz#ee35dd2abb92a1232bb36edbbf230bd3c376363c" From 40bf40bb7eb2efc1dd5281eba1c0c6687b6a0456 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Mon, 5 Aug 2024 00:21:15 +0200 Subject: [PATCH 5/5] Fix: Linting issues in settings-livekit and Daily-plan-compare-estima (#2830) * fix: linting issues in settings-livekit and daily-plan-compare-estimate-modal * fix: cspell --- .cspell.json | 2 ++ .../daily-plan/daily-plan-compare-estimate-modal.tsx | 1 + .../features/integrations/livekit/settings-livekit.tsx | 8 +++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.cspell.json b/.cspell.json index 7041e1bf3..56963e340 100644 --- a/.cspell.json +++ b/.cspell.json @@ -187,6 +187,8 @@ "Kolkata", "Kosrae", "Koyeb", + "Krisp", + "krisp", "labore", "Lask", "lastest", diff --git a/apps/web/lib/features/daily-plan/daily-plan-compare-estimate-modal.tsx b/apps/web/lib/features/daily-plan/daily-plan-compare-estimate-modal.tsx index 967bd61f5..0d983212b 100644 --- a/apps/web/lib/features/daily-plan/daily-plan-compare-estimate-modal.tsx +++ b/apps/web/lib/features/daily-plan/daily-plan-compare-estimate-modal.tsx @@ -12,6 +12,7 @@ import { useDailyPlan, useTeamMemberCard, useTimer, useTMCardTaskEdit } from '@a import { dailyPlanCompareEstimated } from '@app/helpers/daily-plan-estimated'; import { secondsToTime } from '@app/helpers'; import { DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE } from '@app/constants'; +import { ScrollArea } from '@components/ui/scroll-bar'; export interface IDailyPlanCompareEstimated { difference?: boolean; diff --git a/apps/web/lib/features/integrations/livekit/settings-livekit.tsx b/apps/web/lib/features/integrations/livekit/settings-livekit.tsx index 5ad2adcc0..7ef3f09ef 100644 --- a/apps/web/lib/features/integrations/livekit/settings-livekit.tsx +++ b/apps/web/lib/features/integrations/livekit/settings-livekit.tsx @@ -15,10 +15,12 @@ import { shortenLink } from 'lib/utils'; import { BiLoaderCircle } from "react-icons/bi"; - +// eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SettingsMenuProps extends React.HTMLAttributes { } export function SettingsMenu(props: SettingsMenuProps) { + const TrackToggleComponent = TrackToggle as React.ElementType; + const layoutContext = useMaybeLayoutContext(); const [copied, setCopied] = React.useState(false); @@ -93,7 +95,7 @@ export function SettingsMenu(props: SettingsMenuProps) { <>

Camera

- Camera + Camera
@@ -104,7 +106,7 @@ export function SettingsMenu(props: SettingsMenuProps) { <>

Microphone

- Microphone + Microphone