@@ -81,7 +95,6 @@ export function TaskAllStatusTypes({
titleClassName={'text-[0.625rem] font-[500]'}
/>
)}
-
{task?.priority && (
+ )}
{planBadgeContent(dailyPlan.items, task?.id ?? '', tab) && (
;
@@ -61,3 +64,58 @@ export function TaskNameInfoDisplay({
);
}
+
+const formatTime = (hours: number, minutes: number) => (
+
+ {String(hours).padStart(2, '0')}
+ :
+ {String(minutes).padStart(2, '0')}
+
+);
+
+export const DisplayTimeForTimesheet = ({ duration }: { duration: number }) => {
+ if (duration < 0) {
+ console.warn('Negative duration provided to DisplayTimeForTimesheet');
+ duration = 0;
+ }
+ const { h: hours, m: minute } = secondsToTime(duration || 0);
+ return (
+
+
+
+ {formatTime(hours, minute)}
+
+
+ )
+
+}
+
+export const TotalTimeDisplay = React.memo(({ timesheetLog }: { timesheetLog: TimesheetLog[] }) => {
+ const totalDuration = Array.isArray(timesheetLog)
+ ? timesheetLog.reduce((acc, curr) => acc + (curr.timesheet?.duration || 0), 0)
+ : 0;
+ const { h: hours, m: minute } = secondsToTime(totalDuration || 0);
+ return (
+
+ {formatTime(hours, minute)}
+
)
+});
+TotalTimeDisplay.displayName = 'TotalTimeDisplay';
+
+
+export const TotalDurationByDate = React.memo(
+ ({ timesheetLog, createdAt }: { timesheetLog: TimesheetLog[]; createdAt: Date | string }) => {
+ const targetDateISO = new Date(createdAt).toISOString();
+ const filteredLogs = timesheetLog.filter(
+ (item) => formatDate(item.timesheet.createdAt) === formatDate(targetDateISO));
+ const totalDurationInSeconds = filteredLogs.reduce(
+ (total, log) => total + (log.timesheet?.duration || 0), 0);
+ const { h: hours, m: minutes } = secondsToTime(totalDurationInSeconds);
+ return (
+
+ {formatTime(hours, minutes)}
+
+ );
+ }
+);
+TotalDurationByDate.displayName = 'TotalDurationByDate';
diff --git a/apps/web/lib/features/task/task-input-kanban.tsx b/apps/web/lib/features/task/task-input-kanban.tsx
index 59000339f..2430c2580 100644
--- a/apps/web/lib/features/task/task-input-kanban.tsx
+++ b/apps/web/lib/features/task/task-input-kanban.tsx
@@ -1,72 +1,55 @@
'use client';
import {
- HostKeys,
- RTuseTaskInput,
- useCallbackRef,
- useHotkeys,
- useOutsideClick,
- useTaskInput,
- useTaskLabels,
- useTaskStatus
+ HostKeys,
+ RTuseTaskInput,
+ useCallbackRef,
+ useHotkeys,
+ useOutsideClick,
+ useTaskInput,
+ useTaskLabels,
+ useTaskStatus
} from '@app/hooks';
import { ITaskPriority, ITaskSize, ITeamTask, Nullable } from '@app/interfaces';
import { timerStatusState } from '@app/stores';
import { clsxm } from '@app/utils';
import { PlusIcon } from '@heroicons/react/20/solid';
-import {
- Button,
- Card,
- InputField,
- SpinnerLoader,
- Tooltip
-} from 'lib/components';
-import {
- MutableRefObject,
- PropsWithChildren,
- useCallback,
- useEffect,
- useMemo,
- useRef,
- useState
-} from 'react';
+import { Button, Card, InputField, SpinnerLoader, Tooltip } from 'lib/components';
+import { MutableRefObject, PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useAtomValue } from 'jotai';
import { TaskIssuesDropdown } from './task-issue';
-import {
- ActiveTaskPropertiesDropdown,
- ActiveTaskSizesDropdown
-} from './task-status';
+import { ActiveTaskPropertiesDropdown, ActiveTaskSizesDropdown } from './task-status';
import { useTranslations } from 'next-intl';
import { TaskLabels } from './task-labels';
type Props = {
- task?: Nullable
;
- tasks?: ITeamTask[];
- kanbanTitle?: string;
- onTaskClick?: (task: ITeamTask) => void;
- initEditMode?: boolean;
- onCloseCombobox?: () => void;
- inputLoader?: boolean;
- onEnterKey?: (taskName: string, task: ITeamTask) => void;
- keepOpen?: boolean;
- loadingRef?: MutableRefObject;
- closeable_fc?: () => void;
- viewType?: 'input-trigger' | 'one-view';
- createOnEnterClick?: boolean;
- showTaskNumber?: boolean;
- showCombobox?: boolean;
- autoAssignTaskAuth?: boolean;
- fullWidthCombobox?: boolean;
- fullHeightCombobox?: boolean;
- placeholder?: string;
- autoFocus?: boolean;
- autoInputSelectText?: boolean;
- usersTaskCreatedAssignTo?: { id: string }[];
- onTaskCreated?: (task: ITeamTask | undefined) => void;
- cardWithoutShadow?: boolean;
- onClose: any;
-
- forParentChildRelationship?: boolean;
+ task?: Nullable;
+ tasks?: ITeamTask[];
+ kanbanTitle?: string;
+ onTaskClick?: (task: ITeamTask) => void;
+ initEditMode?: boolean;
+ onCloseCombobox?: () => void;
+ inputLoader?: boolean;
+ onEnterKey?: (taskName: string, task: ITeamTask) => void;
+ keepOpen?: boolean;
+ loadingRef?: MutableRefObject;
+ closeable_fc?: () => void;
+ viewType?: 'input-trigger' | 'one-view';
+ createOnEnterClick?: boolean;
+ showTaskNumber?: boolean;
+ showCombobox?: boolean;
+ autoAssignTaskAuth?: boolean;
+ fullWidthCombobox?: boolean;
+ fullHeightCombobox?: boolean;
+ placeholder?: string;
+ autoFocus?: boolean;
+ autoInputSelectText?: boolean;
+ usersTaskCreatedAssignTo?: { id: string }[];
+ onTaskCreated?: (task: ITeamTask | undefined) => void;
+ cardWithoutShadow?: boolean;
+ onClose: any;
+
+ forParentChildRelationship?: boolean;
} & PropsWithChildren;
/**
@@ -77,414 +60,387 @@ type Props = {
*/
export function TaskInputKanban(props: Props) {
- const t = useTranslations();
-
- const { viewType = 'input-trigger', showTaskNumber = false } = props;
-
- const datas = useTaskInput({
- task: props.task,
- initEditMode: props.initEditMode,
- tasks: props.tasks
- });
-
- const onCloseComboboxRef = useCallbackRef(props.onCloseCombobox);
- const closeable_fcRef = useCallbackRef(props.closeable_fc);
- const $onTaskCreated = useCallbackRef(props.onTaskCreated);
- const inputRef = useRef(null);
- const timerStatus = useAtomValue(timerStatusState);
- const timerRunningStatus = useMemo(() => {
- return Boolean(timerStatus?.running);
- }, [timerStatus]);
-
- const onTaskCreated = useCallback(
- (task: ITeamTask | undefined) =>
- $onTaskCreated.current && $onTaskCreated.current(task),
- [$onTaskCreated]
- );
-
- const {
- inputTask,
- setTaskIssue,
- editMode,
- setEditMode,
- setQuery,
- updateLoading,
- updateTaskTitleHandler,
- setFilter
- } = datas;
-
- const inputTaskTitle = useMemo(() => inputTask?.title || '', [
- inputTask?.title
- ]);
-
- const [taskName, setTaskName] = useState('');
-
- const { targetEl, ignoreElementRef } = useOutsideClick(
- () => !props.keepOpen && setEditMode(false)
- );
-
- useEffect(() => {
- setQuery(taskName === inputTask?.title ? '' : taskName);
- }, [taskName, inputTask, setQuery]);
-
- useEffect(() => {
- setTaskName(inputTaskTitle);
- }, [editMode, inputTaskTitle]);
-
- useEffect(() => {
- /**
- * Call onCloseCombobox only when the menu has been closed
- */
- !editMode && onCloseComboboxRef.current && onCloseComboboxRef.current();
- }, [editMode, onCloseComboboxRef]);
-
- /**
- * On update task name
- */
- const updateTaskNameHandler = useCallback(
- (task: ITeamTask, title: string) => {
- if (task.title !== title) {
- !updateLoading && updateTaskTitleHandler(task, title);
- }
- },
- [updateLoading, updateTaskTitleHandler]
- );
-
- /**
- * Signle parent about updating and close event (that can trigger close component e.g)
- */
- useEffect(() => {
- if (props.loadingRef?.current && !updateLoading) {
- closeable_fcRef.current && closeable_fcRef.current();
- }
-
- if (props.loadingRef) {
- props.loadingRef.current = updateLoading;
- }
- }, [updateLoading, props.loadingRef, closeable_fcRef]);
-
- /* Setting the filter to open when the edit mode is true. */
- useEffect(() => {
- editMode && setFilter('open');
- }, [editMode, setFilter]);
-
- /*
+ const t = useTranslations();
+
+ const { viewType = 'input-trigger', showTaskNumber = false } = props;
+
+ const datas = useTaskInput({
+ task: props.task,
+ initEditMode: props.initEditMode,
+ tasks: props.tasks
+ });
+
+ const onCloseComboboxRef = useCallbackRef(props.onCloseCombobox);
+ const closeable_fcRef = useCallbackRef(props.closeable_fc);
+ const $onTaskCreated = useCallbackRef(props.onTaskCreated);
+ const inputRef = useRef(null);
+ const timerStatus = useAtomValue(timerStatusState);
+ const timerRunningStatus = useMemo(() => {
+ return Boolean(timerStatus?.running);
+ }, [timerStatus]);
+
+ const onTaskCreated = useCallback(
+ (task: ITeamTask | undefined) => $onTaskCreated.current && $onTaskCreated.current(task),
+ [$onTaskCreated]
+ );
+
+ const {
+ inputTask,
+ setTaskIssue,
+ editMode,
+ setEditMode,
+ setQuery,
+ updateLoading,
+ updateTaskTitleHandler,
+ setFilter
+ } = datas;
+
+ const inputTaskTitle = useMemo(() => inputTask?.title || '', [inputTask?.title]);
+
+ const [taskName, setTaskName] = useState('');
+
+ const { targetEl, ignoreElementRef } = useOutsideClick(
+ () => !props.keepOpen && setEditMode(false)
+ );
+
+ useEffect(() => {
+ setQuery(taskName === inputTask?.title ? '' : taskName);
+ }, [taskName, inputTask, setQuery]);
+
+ useEffect(() => {
+ setTaskName(inputTaskTitle);
+ }, [editMode, inputTaskTitle]);
+
+ useEffect(() => {
+ /**
+ * Call onCloseCombobox only when the menu has been closed
+ */
+ !editMode && onCloseComboboxRef.current && onCloseComboboxRef.current();
+ }, [editMode, onCloseComboboxRef]);
+
+ /**
+ * On update task name
+ */
+ const updateTaskNameHandler = useCallback(
+ (task: ITeamTask, title: string) => {
+ if (task.title !== title) {
+ !updateLoading && updateTaskTitleHandler(task, title);
+ }
+ },
+ [updateLoading, updateTaskTitleHandler]
+ );
+
+ /**
+ * Signle parent about updating and close event (that can trigger close component e.g)
+ */
+ useEffect(() => {
+ if (props.loadingRef?.current && !updateLoading) {
+ closeable_fcRef.current && closeable_fcRef.current();
+ }
+
+ if (props.loadingRef) {
+ props.loadingRef.current = updateLoading;
+ }
+ }, [updateLoading, props.loadingRef, closeable_fcRef]);
+
+ /* Setting the filter to open when the edit mode is true. */
+ useEffect(() => {
+ editMode && setFilter('open');
+ }, [editMode, setFilter]);
+
+ /*
If task is passed then we don't want to set the active task for the authenticated user.
after task creation
*/
- const handleTaskCreation = useCallback(async () => {
- /* Checking if the `handleTaskCreation` is available and if the `hasCreateForm` is true. */
- datas &&
- datas.handleTaskCreation &&
- datas.hasCreateForm &&
- datas
- .handleTaskCreation({
- autoActiveTask: true,
- autoAssignTaskAuth: props.autoAssignTaskAuth,
- assignToUsers: props.usersTaskCreatedAssignTo || []
- })
- ?.then(onTaskCreated)
- .finally(async () => {
- setTaskName('');
-
- props.onClose && props.onClose();
- });
- }, [datas, props, onTaskCreated]);
-
- const updatedTaskList = useMemo(() => {
- let updatedTaskList: ITeamTask[] = [];
- if (props.forParentChildRelationship) {
- if (
- // Story can have ParentId set to Epic ID
- props.task?.issueType === 'Story'
- ) {
- updatedTaskList = datas.filteredTasks.filter(
- (item) => item.issueType === 'Epic'
- );
- } else if (
- // TASK|BUG can have ParentId to be set either to Story ID or Epic ID
- props.task?.issueType === 'Task' ||
- props.task?.issueType === 'Bug' ||
- !props.task?.issueType
- ) {
- updatedTaskList = datas.filteredTasks.filter(
- (item) => item.issueType === 'Epic' || item.issueType === 'Story'
- );
- } else {
- updatedTaskList = datas.filteredTasks;
- }
-
- if (props.task?.children && props.task?.children?.length) {
- const childrenTaskIds = props.task?.children?.map((item) => item.id);
- updatedTaskList = updatedTaskList.filter(
- (item) => !childrenTaskIds.includes(item.id)
- );
- }
- }
-
- return updatedTaskList;
- }, [props.task, datas.filteredTasks]);
-
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (
- inputRef.current &&
- !inputRef.current.contains(event.target as Node) &&
- editMode
- ) {
- inputTask && updateTaskNameHandler(inputTask, taskName);
- }
- };
-
- // Attach the event listener
- document.addEventListener('mousedown', handleClickOutside);
-
- // Clean up the event listener on component unmount
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [inputTask, taskName, updateTaskNameHandler, editMode]);
-
- // Handling Hotkeys
- const handleCommandKeySequence = useCallback(() => {
- if (!editMode) {
- setEditMode(true);
- if (targetEl.current) {
- targetEl.current.focus();
- }
- } else {
- setEditMode(false);
- }
- }, [setEditMode, editMode, targetEl]);
- useHotkeys(HostKeys.CREATE_TASK, handleCommandKeySequence);
-
- useEffect(() => {
- if (props.autoFocus && targetEl.current) {
- targetEl.current.focus();
- }
- }, [props.autoFocus, targetEl]);
-
- const inputField = (
- {
- setEditMode(true);
- props.autoInputSelectText && setTimeout(() => e?.target?.select(), 10);
- }}
- onChange={(event) => {
- setTaskName(event.target.value);
- }}
- onKeyUp={(e) => {
- if (e.key === 'Enter' && inputTask) {
- /* If createOnEnterClick is false then updateTaskNameHandler is called. */
- !props.createOnEnterClick &&
- updateTaskNameHandler(inputTask, taskName);
-
- props.onEnterKey && props.onEnterKey(taskName, inputTask);
- }
- /* Creating a new task when the enter key is pressed. */
- if (e.key === 'Enter') {
- props.createOnEnterClick && handleTaskCreation();
- }
- }}
- trailingNode={
- /* Showing the spinner when the task is being updated. */
-
- {props.task ? (
- (updateLoading || props.inputLoader) &&
- ) : (
- <>{updateLoading && }>
- )}
-
- }
- className={clsxm(
- showTaskNumber && inputTask && ['pl-2'],
- 'dark:bg-[#1B1D22]',
- props.initEditMode && 'h-10'
- )}
- /* Showing the task number. */
- leadingNode={
- // showTaskNumber &&
- // inputTask &&
-
- setTaskIssue(v)}
- />
-
- }
- />
- );
-
- const taskCard = (
-
- );
-
- return taskCard;
+ const handleTaskCreation = useCallback(async () => {
+ /* Checking if the `handleTaskCreation` is available and if the `hasCreateForm` is true. */
+ datas &&
+ datas.handleTaskCreation &&
+ datas.hasCreateForm &&
+ datas
+ .handleTaskCreation({
+ autoActiveTask: true,
+ autoAssignTaskAuth: props.autoAssignTaskAuth,
+ assignToUsers: props.usersTaskCreatedAssignTo || []
+ })
+ ?.then(onTaskCreated)
+ .finally(async () => {
+ setTaskName('');
+
+ props.onClose && props.onClose();
+ });
+ }, [datas, props, onTaskCreated]);
+
+ const updatedTaskList = useMemo(() => {
+ let updatedTaskList: ITeamTask[] = [];
+ if (props.forParentChildRelationship) {
+ if (
+ // Story can have ParentId set to Epic ID
+ props.task?.issueType === 'Story'
+ ) {
+ updatedTaskList = datas.filteredTasks.filter((item) => item.issueType === 'Epic');
+ } else if (
+ // TASK|BUG can have ParentId to be set either to Story ID or Epic ID
+ props.task?.issueType === 'Task' ||
+ props.task?.issueType === 'Bug' ||
+ !props.task?.issueType
+ ) {
+ updatedTaskList = datas.filteredTasks.filter(
+ (item) => item.issueType === 'Epic' || item.issueType === 'Story'
+ );
+ } else {
+ updatedTaskList = datas.filteredTasks;
+ }
+
+ if (props.task?.children && props.task?.children?.length) {
+ const childrenTaskIds = props.task?.children?.map((item) => item.id);
+ updatedTaskList = updatedTaskList.filter((item) => !childrenTaskIds.includes(item.id));
+ }
+ }
+
+ return updatedTaskList;
+ }, [props.task, datas.filteredTasks]);
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (inputRef.current && !inputRef.current.contains(event.target as Node) && editMode) {
+ inputTask && updateTaskNameHandler(inputTask, taskName);
+ }
+ };
+
+ // Attach the event listener
+ document.addEventListener('mousedown', handleClickOutside);
+
+ // Clean up the event listener on component unmount
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [inputTask, taskName, updateTaskNameHandler, editMode]);
+
+ // Handling Hotkeys
+ const handleCommandKeySequence = useCallback(() => {
+ if (!editMode) {
+ setEditMode(true);
+ if (targetEl.current) {
+ targetEl.current.focus();
+ }
+ } else {
+ setEditMode(false);
+ }
+ }, [setEditMode, editMode, targetEl]);
+ useHotkeys(HostKeys.CREATE_TASK, handleCommandKeySequence);
+
+ useEffect(() => {
+ if (props.autoFocus && targetEl.current) {
+ targetEl.current.focus();
+ }
+ }, [props.autoFocus, targetEl]);
+
+ const inputField = (
+ {
+ setEditMode(true);
+ props.autoInputSelectText && setTimeout(() => e?.target?.select(), 10);
+ }}
+ onChange={(event) => {
+ setTaskName(event.target.value);
+ }}
+ onKeyUp={(e) => {
+ if (e.key === 'Enter' && inputTask) {
+ /* If createOnEnterClick is false then updateTaskNameHandler is called. */
+ !props.createOnEnterClick && updateTaskNameHandler(inputTask, taskName);
+
+ props.onEnterKey && props.onEnterKey(taskName, inputTask);
+ }
+ /* Creating a new task when the enter key is pressed. */
+ if (e.key === 'Enter') {
+ props.createOnEnterClick && handleTaskCreation();
+ }
+ }}
+ trailingNode={
+ /* Showing the spinner when the task is being updated. */
+
+ {props.task ? (
+ (updateLoading || props.inputLoader) &&
+ ) : (
+ <>{updateLoading && }>
+ )}
+
+ }
+ className={clsxm(
+ showTaskNumber && inputTask && ['pl-2'],
+ 'dark:bg-[#1B1D22]',
+ props.initEditMode && 'h-10'
+ )}
+ /* Showing the task number. */
+ leadingNode={
+ // showTaskNumber &&
+ // inputTask &&
+
+ setTaskIssue(v)}
+ />
+
+ }
+ />
+ );
+
+ const taskCard = (
+
+ );
+
+ return taskCard;
}
/**
* A component that is used to render the task list.
*/
function TaskCard({
- datas,
- inputField,
- kanbanTitle,
- handleTaskCreation
+ datas,
+ inputField,
+ kanbanTitle,
+ handleTaskCreation
}: {
- datas: Partial;
- inputField?: JSX.Element;
- kanbanTitle: string;
- fullWidth?: boolean;
- fullHeight?: boolean;
- handleTaskCreation: () => void;
- cardWithoutShadow?: boolean;
- updatedTaskList?: ITeamTask[];
+ datas: Partial;
+ inputField?: JSX.Element;
+ kanbanTitle: string;
+ fullWidth?: boolean;
+ fullHeight?: boolean;
+ handleTaskCreation: () => void;
+ cardWithoutShadow?: boolean;
+ updatedTaskList?: ITeamTask[];
}) {
- const t = useTranslations();
- const activeTaskEl = useRef(null);
- const { taskLabels: taskLabelsData } = useTaskLabels();
-
- const {
- taskStatus,
- taskPriority,
- taskSize,
- taskDescription,
- taskLabels
- } = datas;
- useEffect(() => {
- if (taskStatus) {
- taskStatus.current = kanbanTitle ?? 'open';
- }
- }, [taskStatus, datas.hasCreateForm, kanbanTitle]);
- useEffect(() => {
- if (datas.editMode) {
- window.setTimeout(() => {
- activeTaskEl?.current?.scrollIntoView({
- block: 'nearest',
- inline: 'start'
- });
- }, 10);
- }
- }, [datas.editMode]);
- const taskStatusHook = useTaskStatus();
- return (
- <>
-
- {inputField}
-
- {/* Create team button */}
-
-
- {datas.hasCreateForm && (
-
-
{
- if (taskDescription) {
- taskDescription.current = e.target.value;
- }
- }}
- className={'dark:bg-[#1B1D22]'}
- />
-
-
-
{
- if (v && taskPriority) {
- taskPriority.current = v;
- }
- }}
- defaultValue={taskPriority?.current as ITaskPriority}
- task={null}
- />
-
- {
- if (v && taskSize) {
- taskSize.current = v;
- }
- }}
- defaultValue={taskSize?.current as ITaskSize}
- task={null}
- />
- {
- taskLabelsData.filter((tag) =>
- tag.name ? values?.includes(tag.name) : false
- );
-
- if (taskLabels && values?.length) {
- taskLabels.current = taskLabelsData.filter((tag) =>
- tag.name ? values?.includes(tag.name) : false
- );
- }
- }}
- task={datas.inputTask}
- />
-
-
- )}
-
-
-
-
-
-
-
-
- {/* Just some spaces at the end */}
- {'|'}
- >
- );
+ const t = useTranslations();
+ const activeTaskEl = useRef(null);
+ const { taskLabels: taskLabelsData } = useTaskLabels();
+
+ const { taskStatus, taskPriority, taskSize, taskDescription, taskLabels } = datas;
+ useEffect(() => {
+ if (taskStatus) {
+ taskStatus.current = kanbanTitle ?? 'open';
+ }
+ }, [taskStatus, datas.hasCreateForm, kanbanTitle]);
+ useEffect(() => {
+ if (datas.editMode) {
+ window.setTimeout(() => {
+ activeTaskEl?.current?.scrollIntoView({
+ block: 'nearest',
+ inline: 'start'
+ });
+ }, 10);
+ }
+ }, [datas.editMode]);
+ const taskStatusHook = useTaskStatus();
+ return (
+ <>
+
+ {inputField}
+
+ {/* Create team button */}
+
+
+ {datas.hasCreateForm && (
+
+
{
+ if (taskDescription) {
+ taskDescription.current = e.target.value;
+ }
+ }}
+ className={'dark:bg-[#1B1D22]'}
+ />
+
+
+
{
+ if (v && taskPriority) {
+ taskPriority.current = v;
+ }
+ }}
+ defaultValue={taskPriority?.current as ITaskPriority}
+ task={null}
+ />
+
+ {
+ if (v && taskSize) {
+ taskSize.current = v;
+ }
+ }}
+ defaultValue={taskSize?.current as ITaskSize}
+ task={null}
+ />
+ {
+ taskLabelsData.filter((tag) =>
+ tag.name ? values?.includes(tag.name) : false
+ );
+
+ if (taskLabels && values?.length) {
+ taskLabels.current = taskLabelsData.filter((tag) =>
+ tag.name ? values?.includes(tag.name) : false
+ );
+ }
+ }}
+ task={datas.inputTask}
+ />
+
+
+ )}
+
+
+
+
+
+
+
+
+ {/* Just some spaces at the end */}
+ {'|'}
+ >
+ );
}
diff --git a/apps/web/lib/features/task/task-input.tsx b/apps/web/lib/features/task/task-input.tsx
index f63ad9ed9..a05abd268 100644
--- a/apps/web/lib/features/task/task-input.tsx
+++ b/apps/web/lib/features/task/task-input.tsx
@@ -1,61 +1,49 @@
'use client';
import {
- HostKeys,
- RTuseTaskInput,
- useAuthenticateUser,
- useCallbackRef,
- useHotkeys,
- useIssueType,
- useOrganizationEmployeeTeams,
- useOrganizationTeams,
- useOutsideClick,
- useTaskInput,
- useTaskLabels
+ HostKeys,
+ RTuseTaskInput,
+ useAuthenticateUser,
+ useCallbackRef,
+ useHotkeys,
+ useIssueType,
+ useOrganizationEmployeeTeams,
+ useOrganizationTeams,
+ useOutsideClick,
+ useTaskInput,
+ useTaskLabels
} from '@app/hooks';
import {
- IIssueTypesItemList,
- ITaskIssue,
- ITaskPriority,
- ITaskSize,
- ITaskStatus,
- ITeamTask,
- Nullable,
- OT_Member
+ IIssueTypesItemList,
+ ITaskIssue,
+ ITaskPriority,
+ ITaskSize,
+ ITaskStatus,
+ ITeamTask,
+ Nullable,
+ OT_Member
} from '@app/interfaces';
import { activeTeamTaskId, timerStatusState } from '@app/stores';
import { clsxm } from '@app/utils';
import { Combobox, Popover, Transition } from '@headlessui/react';
import { CheckIcon, ChevronUpDownIcon, PlusIcon } from '@heroicons/react/20/solid';
-import {
- Button,
- Card,
- Divider,
- InputField,
- OutlineBadge,
- SpinnerLoader,
- Tooltip
-} from 'lib/components';
+import { Button, Card, Divider, InputField, OutlineBadge, SpinnerLoader, Tooltip } from 'lib/components';
import { CheckCircleTickIcon as TickCircleIcon } from 'assets/svg';
import {
- Fragment,
- MutableRefObject,
- PropsWithChildren,
- useCallback,
- useEffect,
- useMemo,
- useRef,
- useState
+ Fragment,
+ MutableRefObject,
+ PropsWithChildren,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState
} from 'react';
import { useAtomValue, useSetAtom } from 'jotai';
import { ActiveTaskIssuesDropdown, TaskIssuesDropdown } from './task-issue';
import { TaskItem } from './task-item';
import { TaskLabels } from './task-labels';
-import {
- ActiveTaskPropertiesDropdown,
- ActiveTaskSizesDropdown,
- ActiveTaskStatusDropdown
-} from './task-status';
+import { ActiveTaskPropertiesDropdown, ActiveTaskSizesDropdown, ActiveTaskStatusDropdown } from './task-status';
import { useTranslations } from 'next-intl';
import { useInfinityScrolling } from '@app/hooks/useInfinityFetch';
import { ObserverComponent } from '@components/shared/Observer';
@@ -63,32 +51,32 @@ import { LazyRender } from 'lib/components/lazy-render';
import { ProjectDropDown } from '@components/pages/task/details-section/blocks/task-secondary-info';
type Props = {
- task?: Nullable;
- tasks?: ITeamTask[];
- onTaskClick?: (task: ITeamTask) => void;
- initEditMode?: boolean;
- onCloseCombobox?: () => void;
- inputLoader?: boolean;
- onEnterKey?: (taskName: string, task: ITeamTask) => void;
- keepOpen?: boolean;
- loadingRef?: MutableRefObject;
- closeable_fc?: () => void;
- viewType?: 'input-trigger' | 'one-view';
- createOnEnterClick?: boolean;
- showTaskNumber?: boolean;
- showCombobox?: boolean;
- showEmoji?: boolean;
- autoAssignTaskAuth?: boolean;
- fullWidthCombobox?: boolean;
- fullHeightCombobox?: boolean;
- placeholder?: string;
- autoFocus?: boolean;
- autoInputSelectText?: boolean;
- usersTaskCreatedAssignTo?: { id: string }[];
- onTaskCreated?: (task: ITeamTask | undefined) => void;
- cardWithoutShadow?: boolean;
- assignTaskPopup?: boolean;
- forParentChildRelationship?: boolean;
+ task?: Nullable;
+ tasks?: ITeamTask[];
+ onTaskClick?: (task: ITeamTask) => void;
+ initEditMode?: boolean;
+ onCloseCombobox?: () => void;
+ inputLoader?: boolean;
+ onEnterKey?: (taskName: string, task: ITeamTask) => void;
+ keepOpen?: boolean;
+ loadingRef?: MutableRefObject;
+ closeable_fc?: () => void;
+ viewType?: 'input-trigger' | 'one-view';
+ createOnEnterClick?: boolean;
+ showTaskNumber?: boolean;
+ showCombobox?: boolean;
+ showEmoji?: boolean;
+ autoAssignTaskAuth?: boolean;
+ fullWidthCombobox?: boolean;
+ fullHeightCombobox?: boolean;
+ placeholder?: string;
+ autoFocus?: boolean;
+ autoInputSelectText?: boolean;
+ usersTaskCreatedAssignTo?: { id: string }[];
+ onTaskCreated?: (task: ITeamTask | undefined) => void;
+ cardWithoutShadow?: boolean;
+ assignTaskPopup?: boolean;
+ forParentChildRelationship?: boolean;
} & PropsWithChildren;
/**
@@ -99,357 +87,324 @@ type Props = {
*/
export function TaskInput(props: Props) {
- const t = useTranslations();
- const { issueTypes } = useIssueType();
- const defaultIssueType: IIssueTypesItemList | undefined = issueTypes.find(
- (issue) => issue.isDefault
- );
-
- const {
- viewType = 'input-trigger',
- showTaskNumber = false,
- showCombobox = true,
- } = props;
-
- const datas = useTaskInput({
- task: props.task,
- initEditMode: props.initEditMode,
- tasks: props.tasks
- });
-
- const { updateOrganizationTeamEmployee } = useOrganizationEmployeeTeams();
- const { activeTeam } = useOrganizationTeams();
- const { user } = useAuthenticateUser();
-
- const onCloseComboboxRef = useCallbackRef(props.onCloseCombobox);
- const closeable_fcRef = useCallbackRef(props.closeable_fc);
- const $onTaskClick = useCallbackRef(props.onTaskClick);
- const $onTaskCreated = useCallbackRef(props.onTaskCreated);
- const inputRef = useRef(null);
- const timerStatus = useAtomValue(timerStatusState);
- const timerRunningStatus = useMemo(() => {
- return Boolean(timerStatus?.running);
- }, [timerStatus]);
-
- const onTaskCreated = useCallback(
- (task: ITeamTask | undefined) =>
- $onTaskCreated.current && $onTaskCreated.current(task),
- [$onTaskCreated]
- );
-
- const onTaskClick = useCallback(
- (task: ITeamTask) => $onTaskClick.current && $onTaskClick.current(task),
- [$onTaskClick]
- );
-
- const {
- inputTask,
- setTaskIssue,
- editMode,
- setEditMode,
- setQuery,
- updateLoading,
- updateTaskTitleHandler,
- setFilter
- } = datas;
- const setActiveTask = useSetAtom(activeTeamTaskId);
-
- const inputTaskTitle = useMemo(() => inputTask?.title || '', [
- inputTask?.title
- ]);
-
- const [taskName, setTaskName] = useState('');
-
- const { targetEl, ignoreElementRef } = useOutsideClick(
- () => !props.keepOpen && setEditMode(false)
- );
-
- useEffect(() => {
- setQuery(taskName === inputTask?.title ? '' : taskName);
- }, [taskName, inputTask, setQuery]);
-
- useEffect(() => {
- setTaskName(inputTaskTitle);
- }, [editMode, inputTaskTitle]);
-
- useEffect(() => {
- /**
- * Call onCloseCombobox only when the menu has been closed
- */
- !editMode && onCloseComboboxRef.current && onCloseComboboxRef.current();
- }, [editMode, onCloseComboboxRef]);
-
- /**
- * set the active task for the authenticated user
- */
- const setAuthActiveTask = useCallback(
- (task: ITeamTask) => {
- if (datas.setActiveTask) {
- datas.setActiveTask(task);
-
- // Update Current user's active task to sync across multiple devices
- const currentEmployeeDetails = activeTeam?.members.find(
- (member) => member.employeeId === user?.employee?.id
- );
- if (currentEmployeeDetails && currentEmployeeDetails.id) {
- updateOrganizationTeamEmployee(currentEmployeeDetails.id, {
- organizationId: task.organizationId,
- activeTaskId: task.id,
- organizationTeamId: activeTeam?.id,
- tenantId: activeTeam?.tenantId
- });
- }
- }
- setEditMode(false);
- },
- [datas, setEditMode, activeTeam, user, updateOrganizationTeamEmployee]
- );
-
- /**
- * On update task name
- */
- const updateTaskNameHandler = useCallback(
- (task: ITeamTask, title: string) => {
- if (task.title !== title) {
- !updateLoading && updateTaskTitleHandler(task, title);
- }
- },
- [updateLoading, updateTaskTitleHandler]
- );
-
- /**
- * Signle parent about updating and close event (that can trigger close component e.g)
- */
- useEffect(() => {
- if (props.loadingRef?.current && !updateLoading) {
- closeable_fcRef.current && closeable_fcRef.current();
- }
-
- if (props.loadingRef) {
- props.loadingRef.current = updateLoading;
- }
- }, [updateLoading, props.loadingRef, closeable_fcRef]);
-
- /* Setting the filter to open when the edit mode is true. */
- useEffect(() => {
- editMode && setFilter('open');
- }, [editMode, setFilter]);
-
- /*
+ const t = useTranslations();
+ const { issueTypes } = useIssueType();
+ const defaultIssueType: IIssueTypesItemList | undefined = issueTypes.find((issue) => issue.isDefault);
+
+ const { viewType = 'input-trigger', showTaskNumber = false, showCombobox = true } = props;
+
+ const datas = useTaskInput({
+ task: props.task,
+ initEditMode: props.initEditMode,
+ tasks: props.tasks
+ });
+
+ const { updateOrganizationTeamEmployee } = useOrganizationEmployeeTeams();
+ const { activeTeam } = useOrganizationTeams();
+ const { user } = useAuthenticateUser();
+
+ const onCloseComboboxRef = useCallbackRef(props.onCloseCombobox);
+ const closeable_fcRef = useCallbackRef(props.closeable_fc);
+ const $onTaskClick = useCallbackRef(props.onTaskClick);
+ const $onTaskCreated = useCallbackRef(props.onTaskCreated);
+ const inputRef = useRef(null);
+ const timerStatus = useAtomValue(timerStatusState);
+ const timerRunningStatus = useMemo(() => {
+ return Boolean(timerStatus?.running);
+ }, [timerStatus]);
+
+ const onTaskCreated = useCallback(
+ (task: ITeamTask | undefined) => $onTaskCreated.current && $onTaskCreated.current(task),
+ [$onTaskCreated]
+ );
+
+ const onTaskClick = useCallback(
+ (task: ITeamTask) => $onTaskClick.current && $onTaskClick.current(task),
+ [$onTaskClick]
+ );
+
+ const {
+ inputTask,
+ setTaskIssue,
+ editMode,
+ setEditMode,
+ setQuery,
+ updateLoading,
+ updateTaskTitleHandler,
+ setFilter
+ } = datas;
+ const setActiveTask = useSetAtom(activeTeamTaskId);
+
+ const inputTaskTitle = useMemo(() => inputTask?.title || '', [inputTask?.title]);
+
+ const [taskName, setTaskName] = useState('');
+
+ const { targetEl, ignoreElementRef } = useOutsideClick(
+ () => !props.keepOpen && setEditMode(false)
+ );
+
+ useEffect(() => {
+ setQuery(taskName === inputTask?.title ? '' : taskName);
+ }, [taskName, inputTask, setQuery]);
+
+ useEffect(() => {
+ setTaskName(inputTaskTitle);
+ }, [editMode, inputTaskTitle]);
+
+ useEffect(() => {
+ /**
+ * Call onCloseCombobox only when the menu has been closed
+ */
+ !editMode && onCloseComboboxRef.current && onCloseComboboxRef.current();
+ }, [editMode, onCloseComboboxRef]);
+
+ /**
+ * set the active task for the authenticated user
+ */
+ const setAuthActiveTask = useCallback(
+ (task: ITeamTask) => {
+ if (datas.setActiveTask) {
+ datas.setActiveTask(task);
+
+ // Update Current user's active task to sync across multiple devices
+ const currentEmployeeDetails = activeTeam?.members.find(
+ (member) => member.employeeId === user?.employee?.id
+ );
+ if (currentEmployeeDetails && currentEmployeeDetails.id) {
+ updateOrganizationTeamEmployee(currentEmployeeDetails.id, {
+ organizationId: task.organizationId,
+ activeTaskId: task.id,
+ organizationTeamId: activeTeam?.id,
+ tenantId: activeTeam?.tenantId
+ });
+ }
+ }
+ setEditMode(false);
+ },
+ [datas, setEditMode, activeTeam, user, updateOrganizationTeamEmployee]
+ );
+
+ /**
+ * On update task name
+ */
+ const updateTaskNameHandler = useCallback(
+ (task: ITeamTask, title: string) => {
+ if (task.title !== title) {
+ !updateLoading && updateTaskTitleHandler(task, title);
+ }
+ },
+ [updateLoading, updateTaskTitleHandler]
+ );
+
+ /**
+ * Signle parent about updating and close event (that can trigger close component e.g)
+ */
+ useEffect(() => {
+ if (props.loadingRef?.current && !updateLoading) {
+ closeable_fcRef.current && closeable_fcRef.current();
+ }
+
+ if (props.loadingRef) {
+ props.loadingRef.current = updateLoading;
+ }
+ }, [updateLoading, props.loadingRef, closeable_fcRef]);
+
+ /* Setting the filter to open when the edit mode is true. */
+ useEffect(() => {
+ editMode && setFilter('open');
+ }, [editMode, setFilter]);
+
+ /*
If task is passed then we don't want to set the active task for the authenticated user.
after task creation
*/
- const autoActiveTask: boolean = props.task === undefined;
- const handleTaskCreation = useCallback(() => {
- /* Checking if the `handleTaskCreation` is available and if the `hasCreateForm` is true. */
- datas &&
- datas.handleTaskCreation &&
- datas.hasCreateForm &&
- datas
- .handleTaskCreation({
- autoActiveTask,
- autoAssignTaskAuth: props.autoAssignTaskAuth,
- assignToUsers: props.usersTaskCreatedAssignTo || []
- })
- ?.then(onTaskCreated)
- .finally(() => {
- viewType === 'one-view' && setTaskName('');
- });
- }, [datas, props, autoActiveTask, onTaskCreated, viewType]);
-
- const updatedTaskList = useMemo(() => {
- let updatedTaskList: ITeamTask[] = [];
- if (props.forParentChildRelationship) {
- if (
- // Story can have ParentId set to Epic ID
- props.task?.issueType === 'Story'
- ) {
- updatedTaskList = datas.filteredTasks.filter(
- (item) => item.issueType === 'Epic'
- );
- } else if (
- // TASK|BUG can have ParentId to be set either to Story ID or Epic ID
- props.task?.issueType === 'Task' ||
- props.task?.issueType === 'Bug' ||
- !props.task?.issueType
- ) {
- updatedTaskList = datas.filteredTasks.filter(
- (item) => item.issueType === 'Epic' || item.issueType === 'Story'
- );
- } else {
- updatedTaskList = datas.filteredTasks;
- }
-
- if (props.task?.children && props.task?.children?.length) {
- const childrenTaskIds = props.task?.children?.map((item) => item.id);
- updatedTaskList = updatedTaskList.filter(
- (item) => !childrenTaskIds.includes(item.id)
- );
- }
- }
-
- return updatedTaskList;
- }, [props.task, datas.filteredTasks]);
-
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (
- inputRef.current &&
- !inputRef.current.contains(event.target as Node) &&
- editMode
- ) {
- // inputTask && updateTaskNameHandler(inputTask, taskName);
- if (taskName == inputTaskTitle) {
- setEditMode(false);
- setActiveTask({
- id: ''
- });
- }
- }
- };
-
- // Attach the event listener
- document.addEventListener('mousedown', handleClickOutside);
-
- // Clean up the event listener on component unmount
- return () => {
- document.removeEventListener('mousedown', handleClickOutside);
- };
- }, [
- inputTask,
- taskName,
- setActiveTask,
- updateTaskNameHandler,
- editMode,
- inputTaskTitle,
- setEditMode
- ]);
- const [openPopoverId, setOpenPopoverId] = useState(null);
- const handlePopoverToggle = useCallback((popoverId: string) => {
- if (openPopoverId === popoverId) {
- setOpenPopoverId(null);
- } else {
- setOpenPopoverId(popoverId);
- }
- }, [openPopoverId]);
-
- // Handling Hotkeys
- const handleCommandKeySequence = useCallback(() => {
- if (!editMode) {
- setEditMode(true);
- if (targetEl.current) {
- targetEl.current.focus();
- }
- } else {
- setEditMode(false);
- }
- }, [setEditMode, editMode, targetEl]);
-
- useHotkeys(HostKeys.CREATE_TASK, handleCommandKeySequence);
-
- useEffect(() => {
- if (props.autoFocus && targetEl.current) {
- targetEl.current.focus();
- }
- }, [props.autoFocus, targetEl]);
-
- // const savedIssueType : string | null = localStorage.getItem('savedIssueType') as string && null;
-
- const inputField = (
-
- }
- autoFocus={props.autoFocus}
- wrapperClassName={`rounded-lg dark:bg-[#1B1D22]`}
- placeholder={props.placeholder || t('form.TASK_INPUT_PLACEHOLDER')}
- onFocus={(e) => {
- setEditMode(true);
- props.autoInputSelectText && setTimeout(() => e?.target?.select(), 10);
- }}
- onChange={(event) => {
- setTaskName(event.target.value);
- }}
- onKeyUp={(e) => {
- if (e.key === 'Enter' && inputTask) {
- /* If createOnEnterClick is false then updateTaskNameHandler is called. */
- !props.createOnEnterClick &&
- updateTaskNameHandler(inputTask, taskName);
-
- props.onEnterKey && props.onEnterKey(taskName, inputTask);
- }
- /* Creating a new task when the enter key is pressed. */
- if (e.key === 'Enter') {
- props.createOnEnterClick && handleTaskCreation();
- }
- }}
- trailingNode={
- /* Showing the spinner when the task is being updated. */
-
- {props.task ? (
- (updateLoading || props.inputLoader) &&
- ) : (
- <>{updateLoading && }>
- )}
-
- }
- className={clsxm(
- showTaskNumber && inputTask && ['pl-2'],
- 'dark:bg-[#1B1D22]',
- props.initEditMode && 'h-10'
- )}
- /* Showing the task number and issue type */
- leadingNode={
- // showTaskNumber &&
- // inputTask &&
-
- {!datas.hasCreateForm ? (
-
- ) : (
-
setTaskIssue(v)}
- defaultValue={
- defaultIssueType
- ? defaultIssueType.name
- : (localStorage.getItem('lastTaskIssue') as ITaskIssue) ||
- null
- }
- />
- )}
-
- {!datas.hasCreateForm && (
-
- #{(inputTask && inputTask.taskNumber) || ''}
-
- )}
-
- }
- />
- );
-
- const taskCard = (
+ const autoActiveTask: boolean = props.task === undefined;
+ const handleTaskCreation = useCallback(() => {
+ /* Checking if the `handleTaskCreation` is available and if the `hasCreateForm` is true. */
+ datas &&
+ datas.handleTaskCreation &&
+ datas.hasCreateForm &&
+ datas
+ .handleTaskCreation({
+ autoActiveTask,
+ autoAssignTaskAuth: props.autoAssignTaskAuth,
+ assignToUsers: props.usersTaskCreatedAssignTo || []
+ })
+ ?.then(onTaskCreated)
+ .finally(() => {
+ viewType === 'one-view' && setTaskName('');
+ });
+ }, [datas, props, autoActiveTask, onTaskCreated, viewType]);
+
+ const updatedTaskList = useMemo(() => {
+ let updatedTaskList: ITeamTask[] = [];
+ if (props.forParentChildRelationship) {
+ if (
+ // Story can have ParentId set to Epic ID
+ props.task?.issueType === 'Story'
+ ) {
+ updatedTaskList = datas.filteredTasks.filter((item) => item.issueType === 'Epic');
+ } else if (
+ // TASK|BUG can have ParentId to be set either to Story ID or Epic ID
+ props.task?.issueType === 'Task' ||
+ props.task?.issueType === 'Bug' ||
+ !props.task?.issueType
+ ) {
+ updatedTaskList = datas.filteredTasks.filter(
+ (item) => item.issueType === 'Epic' || item.issueType === 'Story'
+ );
+ } else {
+ updatedTaskList = datas.filteredTasks;
+ }
+
+ if (props.task?.children && props.task?.children?.length) {
+ const childrenTaskIds = props.task?.children?.map((item) => item.id);
+ updatedTaskList = updatedTaskList.filter((item) => !childrenTaskIds.includes(item.id));
+ }
+ }
+
+ return updatedTaskList;
+ }, [props.task, datas.filteredTasks]);
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (inputRef.current && !inputRef.current.contains(event.target as Node) && editMode) {
+ // inputTask && updateTaskNameHandler(inputTask, taskName);
+ if (taskName == inputTaskTitle) {
+ setEditMode(false);
+ setActiveTask({
+ id: ''
+ });
+ }
+ }
+ };
+
+ // Attach the event listener
+ document.addEventListener('mousedown', handleClickOutside);
+
+ // Clean up the event listener on component unmount
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [inputTask, taskName, setActiveTask, updateTaskNameHandler, editMode, inputTaskTitle, setEditMode]);
+ const [openPopoverId, setOpenPopoverId] = useState(null);
+ const handlePopoverToggle = useCallback(
+ (popoverId: string) => {
+ if (openPopoverId === popoverId) {
+ setOpenPopoverId(null);
+ } else {
+ setOpenPopoverId(popoverId);
+ }
+ },
+ [openPopoverId]
+ );
+
+ // Handling Hotkeys
+ const handleCommandKeySequence = useCallback(() => {
+ if (!editMode) {
+ setEditMode(true);
+ if (targetEl.current) {
+ targetEl.current.focus();
+ }
+ } else {
+ setEditMode(false);
+ }
+ }, [setEditMode, editMode, targetEl]);
+
+ useHotkeys(HostKeys.CREATE_TASK, handleCommandKeySequence);
+
+ useEffect(() => {
+ if (props.autoFocus && targetEl.current) {
+ targetEl.current.focus();
+ }
+ }, [props.autoFocus, targetEl]);
+
+ // const savedIssueType : string | null = localStorage.getItem('savedIssueType') as string && null;
+
+ const inputField = (
+ }
+ autoFocus={props.autoFocus}
+ wrapperClassName={`rounded-lg dark:bg-[#1B1D22]`}
+ placeholder={props.placeholder || t('form.TASK_INPUT_PLACEHOLDER')}
+ onFocus={(e) => {
+ setEditMode(true);
+ props.autoInputSelectText && setTimeout(() => e?.target?.select(), 10);
+ }}
+ onChange={(event) => {
+ setTaskName(event.target.value);
+ }}
+ onKeyUp={(e) => {
+ if (e.key === 'Enter' && inputTask) {
+ /* If createOnEnterClick is false then updateTaskNameHandler is called. */
+ !props.createOnEnterClick && updateTaskNameHandler(inputTask, taskName);
+
+ props.onEnterKey && props.onEnterKey(taskName, inputTask);
+ }
+ /* Creating a new task when the enter key is pressed. */
+ if (e.key === 'Enter') {
+ props.createOnEnterClick && handleTaskCreation();
+ }
+ }}
+ trailingNode={
+ /* Showing the spinner when the task is being updated. */
+
+ {props.task ? (
+ (updateLoading || props.inputLoader) &&
+ ) : (
+ <>{updateLoading && }>
+ )}
+
+ }
+ className={clsxm(
+ showTaskNumber && inputTask && ['pl-2'],
+ 'dark:bg-[#1B1D22]',
+ props.initEditMode && 'h-10'
+ )}
+ /* Showing the task number and issue type */
+ leadingNode={
+ // showTaskNumber &&
+ // inputTask &&
+
+ {!datas.hasCreateForm ? (
+
+ ) : (
+
setTaskIssue(v)}
+ defaultValue={
+ defaultIssueType
+ ? defaultIssueType.name
+ : (localStorage.getItem('lastTaskIssue') as ITaskIssue) || null
+ }
+ />
+ )}
+
+ {!datas.hasCreateForm && (
+ #{(inputTask && inputTask.taskNumber) || ''}
+ )}
+
+ }
+ />
+ );
+
+ const taskCard = (
- );
-
- return viewType === 'one-view' ? (
- taskCard
- ) : (
- handlePopoverToggle('popover1')}
- className="relative z-20 w-full" ref={inputRef}>
-
- {inputField}
-
- {props.children}
-
-
-
- {taskCard}
-
-
-
- );
+ );
+
+ return viewType === 'one-view' ? (
+ taskCard
+ ) : (
+ handlePopoverToggle('popover1')} className="relative z-20 w-full" ref={inputRef}>
+
+ {inputField}
+
+ {props.children}
+
+
+
+ {taskCard}
+
+
+
+ );
}
/**
* A component that is used to render the task list.
*/
function TaskCard({
- datas,
- onItemClick,
- inputField,
- fullWidth,
- fullHeight,
- handleTaskCreation,
- cardWithoutShadow,
- forParentChildRelationship,
- updatedTaskList,
- assignTaskPopup,
+ datas,
+ onItemClick,
+ inputField,
+ fullWidth,
+ fullHeight,
+ handleTaskCreation,
+ cardWithoutShadow,
+ forParentChildRelationship,
+ updatedTaskList,
+ assignTaskPopup
}: {
- datas: Partial;
- onItemClick?: (task: ITeamTask) => void;
- inputField?: JSX.Element;
- fullWidth?: boolean;
- fullHeight?: boolean;
- handleTaskCreation: () => void;
- cardWithoutShadow?: boolean;
- forParentChildRelationship?: boolean;
- updatedTaskList?: ITeamTask[];
- assignTaskPopup?: boolean;
+ datas: Partial;
+ onItemClick?: (task: ITeamTask) => void;
+ inputField?: JSX.Element;
+ fullWidth?: boolean;
+ fullHeight?: boolean;
+ handleTaskCreation: () => void;
+ cardWithoutShadow?: boolean;
+ forParentChildRelationship?: boolean;
+ updatedTaskList?: ITeamTask[];
+ assignTaskPopup?: boolean;
}) {
- const [, setCount] = useState(0);
- const t = useTranslations();
- const activeTaskEl = useRef(null);
- const { taskLabels: taskLabelsData } = useTaskLabels();
- const { activeTeam } = useOrganizationTeams();
-
- const {
- taskStatus,
- taskPriority,
- taskSize,
- taskLabels,
- taskDescription,
- taskProject,
- taskAssignees
- } = datas;
- const { nextOffset, data } = useInfinityScrolling(updatedTaskList ?? [], 5);
-
- useEffect(() => {
- if (datas.editMode) {
- window.setTimeout(() => {
- activeTaskEl?.current?.scrollIntoView({
- block: 'nearest',
- inline: 'start'
- });
- }, 10);
- }
- }, [datas.editMode]);
-
- return (
- <>
-
- {inputField}
-
- {/* Create team button */}
-
- {datas.hasCreateForm && (
-
-
{
- if (taskDescription) {
- taskDescription.current = e.target.value;
- }
- }}
- className={'dark:bg-[#1B1D22]'}
- />
-
-
-
{
- if (v && taskStatus) {
- taskStatus.current = v;
- }
- setCount((c) => c + 1);
- }}
- defaultValue={taskStatus?.current as ITaskStatus}
- task={null}
- />
-
- {
- if (v && taskPriority) {
- taskPriority.current = v;
- }
- setCount((c) => c + 1);
- }}
- defaultValue={taskPriority?.current as ITaskPriority}
- task={null}
- />
-
- {
- if (v && taskSize) {
- taskSize.current = v;
- }
- setCount((c) => c + 1);
- }}
- defaultValue={taskSize?.current as ITaskSize}
- task={null}
- />
-
- {
- taskLabelsData.filter((tag) =>
- tag.name ? values?.includes(tag.name) : false
- );
-
- if (taskLabels && values?.length) {
- taskLabels.current = taskLabelsData.filter((tag) =>
- tag.name ? values?.includes(tag.name) : false
- );
- }
- }}
- task={datas.inputTask}
- />
-
- {taskAssignees !== undefined && }
-
-
- {
- if (taskProject) {
- taskProject.current = project.id
- }
- }}
- />
-
-
-
- )}
-
-
-
-
-
-
- {/* Task filter buttons */}
-
-
datas.setFilter && datas.setFilter('open')}
- >
-
-
- {datas.openTaskCount || 0} {t('common.OPEN')}
-
-
-
-
datas.setFilter && datas.setFilter('closed')}
- >
-
-
- {datas.closedTaskCount || 0} {t('common.CLOSED')}
-
-
-
-
-
-
- {/* Task list */}
-
- {forParentChildRelationship && (
-
- {(task, i) => {
- const last = (datas.filteredTasks?.length || 0) - 1 === i;
- const active = datas.inputTask === task;
-
- return (
- -
-
-
- {!last && }
-
- );
- }}
-
- )}
-
- {!forParentChildRelationship && (
-
- {(task, i) => {
- const last = (datas.filteredTasks?.length || 0) - 1 === i;
- const active = datas.inputTask === task;
-
- return (
- -
-
-
- {!last && }
-
- );
- }}
-
- )}
-
- {(forParentChildRelationship &&
- updatedTaskList &&
- updatedTaskList.length === 0) ||
- (!forParentChildRelationship &&
- datas.filteredTasks &&
- datas.filteredTasks.length === 0 && (
- {t('common.NO_TASKS')}
- ))}
-
-
-
- {/* Just some spaces at the end */}
- {'|'}
- >
- );
-}
+ const [, setCount] = useState(0);
+ const t = useTranslations();
+ const activeTaskEl = useRef(null);
+ const { taskLabels: taskLabelsData } = useTaskLabels();
+ const { activeTeam } = useOrganizationTeams();
+
+ const { taskStatus, taskPriority, taskSize, taskLabels, taskDescription, taskProject, taskAssignees } = datas;
+ const { nextOffset, data } = useInfinityScrolling(updatedTaskList ?? [], 5);
+
+ useEffect(() => {
+ if (datas.editMode) {
+ window.setTimeout(() => {
+ activeTaskEl?.current?.scrollIntoView({
+ block: 'nearest',
+ inline: 'start'
+ });
+ }, 10);
+ }
+ }, [datas.editMode]);
+
+ return (
+ <>
+
+ {inputField}
+
+ {/* Create team button */}
+
+ {datas.hasCreateForm && (
+
+
{
+ if (taskDescription) {
+ taskDescription.current = e.target.value;
+ }
+ }}
+ className={'dark:bg-[#1B1D22]'}
+ />
+
+
+
{
+ if (v && taskStatus) {
+ taskStatus.current = v;
+ }
+ setCount((c) => c + 1);
+ }}
+ defaultValue={taskStatus?.current as ITaskStatus}
+ task={null}
+ />
+
+ {
+ if (v && taskPriority) {
+ taskPriority.current = v;
+ }
+ setCount((c) => c + 1);
+ }}
+ defaultValue={taskPriority?.current as ITaskPriority}
+ task={null}
+ />
+
+ {
+ if (v && taskSize) {
+ taskSize.current = v;
+ }
+ setCount((c) => c + 1);
+ }}
+ defaultValue={taskSize?.current as ITaskSize}
+ task={null}
+ />
+
+ {
+ taskLabelsData.filter((tag) =>
+ tag.name ? values?.includes(tag.name) : false
+ );
+
+ if (taskLabels && values?.length) {
+ taskLabels.current = taskLabelsData.filter((tag) =>
+ tag.name ? values?.includes(tag.name) : false
+ );
+ }
+ }}
+ task={datas.inputTask}
+ />
+
+ {taskAssignees !== undefined && (
+
+ )}
+
+ {
+ if (taskProject) {
+ taskProject.current = project.id;
+ }
+ }}
+ />
+
+
+ )}
+
+
+
+
+
+ {/* Task filter buttons */}
+
+
datas.setFilter && datas.setFilter('open')}
+ >
+
+
+ {datas.openTaskCount || 0} {t('common.OPEN')}
+
+
+
+
datas.setFilter && datas.setFilter('closed')}
+ >
+
+
+ {datas.closedTaskCount || 0} {t('common.CLOSED')}
+
+
+
+
+
+
+ {/* Task list */}
+
+ {forParentChildRelationship && (
+
+ {(task, i) => {
+ const last = (datas.filteredTasks?.length || 0) - 1 === i;
+ const active = datas.inputTask === task;
+
+ return (
+ -
+
+
+ {!last && }
+
+ );
+ }}
+
+ )}
+
+ {!forParentChildRelationship && (
+
+ {(task, i) => {
+ const last = (datas.filteredTasks?.length || 0) - 1 === i;
+ const active = datas.inputTask === task;
+
+ return (
+ -
+
+
+ {!last && }
+
+ );
+ }}
+
+ )}
+
+ {(forParentChildRelationship && updatedTaskList && updatedTaskList.length === 0) ||
+ (!forParentChildRelationship && datas.filteredTasks && datas.filteredTasks.length === 0 && (
+ {t('common.NO_TASKS')}
+ ))}
+
+
+
+ {/* Just some spaces at the end */}
+ {'|'}
+ >
+ );
+}
/**
* ----------------------------------------------
@@ -809,9 +726,11 @@ function TaskCard({
interface ITeamMemberSelectProps {
teamMembers: OT_Member[];
- assignees : MutableRefObject<{
- id: string;
- }[]>
+ assignees: MutableRefObject<
+ {
+ id: string;
+ }[]
+ >;
}
/**
* A multi select component for assignees
@@ -822,11 +741,14 @@ interface ITeamMemberSelectProps {
*
* @return {JSX.Element} The multi select component
*/
- function AssigneesSelect(props: ITeamMemberSelectProps): JSX.Element {
- const { teamMembers , assignees} = props;
+function AssigneesSelect(props: ITeamMemberSelectProps): JSX.Element {
+ const { teamMembers, assignees } = props;
const t = useTranslations();
- const {user} = useAuthenticateUser()
- const authMember = useMemo(() => teamMembers.find(member => member.employee.user?.id == user?.id), [teamMembers, user?.id])
+ const { user } = useAuthenticateUser();
+ const authMember = useMemo(
+ () => teamMembers.find((member) => member.employee.user?.id == user?.id),
+ [teamMembers, user?.id]
+ );
return (
@@ -873,18 +795,21 @@ interface ITeamMemberSelectProps {
}`
}
onClick={() => {
- const isAssigned = assignees.current.map(el => el.id).includes(member.employee.id);
-
+ const isAssigned = assignees.current
+ .map((el) => el.id)
+ .includes(member.employee.id);
if (isAssigned) {
- assignees.current = assignees.current.filter((el) => el.id != member.employee.id)
+ assignees.current = assignees.current.filter(
+ (el) => el.id != member.employee.id
+ );
} else {
- assignees.current = [...assignees.current, {id:member.employee.id}];
+ assignees.current = [...assignees.current, { id: member.employee.id }];
}
}}
value={member}
>
- {assignees.current.map(el => el.id).includes(member.employee.id) && (
+ {assignees.current.map((el) => el.id).includes(member.employee.id) && (
diff --git a/apps/web/lib/features/user-nav-menu.tsx b/apps/web/lib/features/user-nav-menu.tsx
index 7762a76ae..f2ce88a50 100644
--- a/apps/web/lib/features/user-nav-menu.tsx
+++ b/apps/web/lib/features/user-nav-menu.tsx
@@ -46,7 +46,7 @@ export function UserNavAvatar() {
}, [timerStatus, currentMember, publicTeam]);
return (
-
+
-
+
diff --git a/apps/web/lib/layout/main-layout.tsx b/apps/web/lib/layout/main-layout.tsx
index 84923e728..9a4a284b1 100644
--- a/apps/web/lib/layout/main-layout.tsx
+++ b/apps/web/lib/layout/main-layout.tsx
@@ -5,11 +5,11 @@ import { useAtomValue } from 'jotai';
import { fullWidthState } from '@app/stores/fullWidth';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@components/ui/resizable';
import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar';
-import { AppSidebar } from '@components/app-sidebar';
import MainSidebarTrigger from './MainSidebarTrigger';
import AppContainer from './AppContainer';
import GlobalHeader from './GlobalHeader';
import GlobalFooter from './GlobalFooter';
+import { AppSidebar } from '@components/app-sidebar';
/**
* Props interface for the MainLayout component
diff --git a/apps/web/next.config.js b/apps/web/next.config.js
index 8ea592699..74c755633 100644
--- a/apps/web/next.config.js
+++ b/apps/web/next.config.js
@@ -44,6 +44,7 @@ const eslintBuildConfig = process.env.NEXT_IGNORE_ESLINT_ERROR_ON_BUILD
const nextConfig = {
output: ['standalone', 'export'].includes(BUILD_OUTPUT_MODE) ? BUILD_OUTPUT_MODE : undefined,
reactStrictMode: false,
+ transpilePackages: ['geist'],
...eslintBuildConfig,
swcMinify: true,
webpack: (config, { isServer }) => {
diff --git a/apps/web/package.json b/apps/web/package.json
index 2f70e08b0..608b20c86 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -37,6 +37,7 @@
"@fullcalendar/timegrid": "^6.1.15",
"@headlessui/react": "^1.7.7",
"@heroicons/react": "^2.0.12",
+ "@hookform/resolvers": "^3.9.1",
"@jitsi/react-sdk": "^1.3.0",
"@jitsu/jitsu-react": "^1.3.0",
"@livekit/components-react": "^2.4.1",
@@ -60,6 +61,7 @@
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-hover-card": "^1.0.6",
"@radix-ui/react-icons": "^1.3.0",
+ "@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-scroll-area": "^1.2.1",
"@radix-ui/react-select": "^2.1.1",
@@ -86,6 +88,7 @@
"emoji-mart": "^5.5.2",
"eslint-config-next": "^14.0.4",
"firebase": "8.3.3",
+ "geist": "^1.3.1",
"hotkeys-js": "^3.12.0",
"i18next": "^23.6.0",
"jotai": "^2.9.3",
@@ -115,7 +118,7 @@
"react-day-picker": "^8.8.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^2.1.0",
- "react-hook-form": "^7.42.1",
+ "react-hook-form": "^7.53.2",
"react-icons": "^5.2.0",
"react-loading-skeleton": "^3.1.1",
"react-paginate": "^8.2.0",
@@ -130,7 +133,8 @@
"slate-serializers": "^0.4.0",
"sonner": "^1.5.0",
"string-to-color": "^2.2.2",
- "tailwind-merge": "^1.14.0"
+ "tailwind-merge": "^1.14.0",
+ "zod": "^3.23.8"
},
"devDependencies": {
"@svgr/webpack": "^8.1.0",
diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css
index 91ea4af66..2b15ec5a8 100644
--- a/apps/web/styles/globals.css
+++ b/apps/web/styles/globals.css
@@ -148,9 +148,8 @@ html.dark {
body {
font-style: normal;
font-weight: 300;
- font-size: 16px;
line-height: 160%;
- @apply text-default dark:text-white bg-[rgb(242,242,242)] dark:bg-background text-foreground;
+ @apply text-default dark:text-white bg-[rgb(242,242,242)] dark:bg-background text-foreground text-sm;
}
.x-container {
diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js
index cc39fabe1..b66c6486c 100644
--- a/apps/web/tailwind.config.js
+++ b/apps/web/tailwind.config.js
@@ -12,177 +12,177 @@ module.exports = {
...createGlobPatternsForDependencies(__dirname)
],
theme: {
- screens: {
- xs: '414px',
- sm: '576px',
- md: '768px',
- lg: '992px',
- xl: '1200px',
- '2xl': '1400px',
- '3xl': '1600px'
- },
- container: {
- center: 'true',
- padding: '2rem',
- screens: {
- '2xl': '1400px'
- }
- },
- extend: {
- colors: {
- transparent: 'transparent',
- current: 'currentColor',
- neutral: '#7E7991',
- chetwodeBlue: '#8C7AE4',
- indianRed: '#D95F5F',
- grey: '#868296',
- transparentWhite: 'rgba(255, 255, 255, 0.30)',
- default: {
- DEFAULT: '#282048'
- },
- 'light--theme': {
- light: '#fff',
- DEFAULT: '#f7f7f8',
- dark: '#E7E7EA'
- },
- 'dark--theme': {
- light: '#1E2025',
- DEFAULT: 'var(--tw-color-dark--theme)'
- },
- primary: {
- DEFAULT: '#3826A6',
- light: '#6755C9',
- xlight: '#8E76FA',
- mid: '#483A95',
- foreground: 'hsl(var(--primary-foreground))'
- },
- dark: {
- high: '#16171B',
- lighter: '#1E2430',
- DEFAULT: '#1A1C1E'
- },
- 'regal-blue': '#6A71E7',
- 'regal-rose': '#E93CB9',
- border: 'hsl(var(--border))',
- ring: 'hsl(var(--ring))',
- background: 'hsl(var(--background))',
- foreground: 'hsl(var(--foreground))',
- secondary: {
- DEFAULT: 'hsl(var(--secondary))',
- foreground: 'hsl(var(--secondary-foreground))'
- },
- destructive: {
- DEFAULT: 'hsl(var(--destructive))',
- foreground: 'hsl(var(--destructive-foreground))'
- },
- muted: {
- DEFAULT: 'hsl(var(--muted))',
- foreground: 'hsl(var(--muted-foreground))'
- },
- accent: {
- DEFAULT: 'hsl(var(--accent))',
- foreground: 'hsl(var(--accent-foreground))'
- },
- popover: {
- DEFAULT: 'hsl(var(--popover))',
- foreground: 'hsl(var(--popover-foreground))'
- },
- card: {
- DEFAULT: 'hsl(var(--card))',
- foreground: 'hsl(var(--card-foreground))'
- },
- sidebar: {
- DEFAULT: 'hsl(var(--sidebar-background))',
- foreground: 'hsl(var(--sidebar-foreground))',
- primary: 'hsl(var(--sidebar-primary))',
- 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
- accent: 'hsl(var(--sidebar-accent))',
- 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
- border: 'hsl(var(--sidebar-border))',
- ring: 'hsl(var(--sidebar-ring))'
- }
- },
- fontFamily: {
- PlusJakartaSans: ['Plus-Jakarta-Sans-VariableFont_wght'],
- PlusJakartaSansRegular: ['Plus-Jakarta-Sans-Regular'],
- PlusJakartaSansBold: ['Plus-Jakarta-Sans-Bold'],
- PlusJakartaSansLight: ['Plus-Jakarta-Sans-Light'],
- PlusJakartaSansMedium: ['Plus-Jakarta-Sans-Medium'],
- PlusJakartaSansSemiBold: ['Plus-Jakarta-Sans-SemiBold']
- },
- boxShadow: {
- lgcard: '0px 50px 200px rgba(0, 0, 0, 0.1)',
- xlcard: '0px 16px 79px rgba(0, 0, 0, 0.12)',
- 'lgcard-white': '0px 50px 200px rgba(255, 255, 255, 0.1)',
- 'xlcard-white': '0px 16px 79px rgba(255, 255, 255, 0.12)',
- darker: '-8px -9px 14px rgba(255, 255, 255, 0.05), 10px 14px 34px rgba(0, 0, 0, 0.6), 0px 4px 24px rgba(0, 0, 0, 0.25)'
- },
- borderRadius: {
- lg: 'var(--radius)',
- md: 'calc(var(--radius) - 2px)',
- sm: 'calc(var(--radius) - 4px)'
- },
- keyframes: {
- 'accordion-down': {
- from: {
- height: '0'
- },
- to: {
- height: 'var(--radix-accordion-content-height)'
- }
- },
- 'accordion-up': {
- from: {
- height: 'var(--radix-accordion-content-height)'
- },
- to: {
- height: '0'
- }
- }
- },
- animation: {
- 'accordion-down': 'accordion-down 0.2s ease-out',
- 'accordion-up': 'accordion-up 0.2s ease-out',
- spine: 'spin 10s linear infinite'
- },
- typography: {
- DEFAULT: {
- css: {
- maxWidth: '100ch',
- 'h3, h4, h5, h6, p, span, em, ul, ol, dl, blockquote, code, figure, pre': {
- marginTop: '0.5rem',
- marginBottom: '0.5rem',
- lineHeight: '1.25rem',
- fontSize: '14px',
- wordSpacing: '-1px',
- fontWeight: '400'
- },
- h1: {
- fontSize: '1.3rem',
- marginTop: '0.65rem',
- marginBottom: '0.65rem',
- lineHeight: '40px'
- },
- h2: {
- fontSize: '1.1rem',
- marginTop: '0.35rem',
- marginBottom: '0.35rem',
- lineHeight: '30px'
- },
- 'h1 span': {
- fontSize: '1.3rem'
- },
- 'h2 span': {
- fontSize: '1.1rem',
- fontWeight: 'bold'
- },
- 'strong span': {
- fontWeight: '600'
- }
- }
- }
- }
- }
- },
+ screens: {
+ xs: '414px',
+ sm: '576px',
+ md: '768px',
+ lg: '992px',
+ xl: '1200px',
+ '2xl': '1400px',
+ '3xl': '1600px'
+ },
+ container: {
+ center: 'true',
+ padding: '2rem',
+ screens: {
+ '2xl': '1400px'
+ }
+ },
+ extend: {
+ colors: {
+ transparent: 'transparent',
+ current: 'currentColor',
+ neutral: '#7E7991',
+ chetwodeBlue: '#8C7AE4',
+ indianRed: '#D95F5F',
+ grey: '#868296',
+ transparentWhite: 'rgba(255, 255, 255, 0.30)',
+ default: {
+ DEFAULT: '#282048'
+ },
+ 'light--theme': {
+ light: '#fff',
+ DEFAULT: '#f7f7f8',
+ dark: '#E7E7EA'
+ },
+ 'dark--theme': {
+ light: '#1E2025',
+ DEFAULT: 'var(--tw-color-dark--theme)'
+ },
+ primary: {
+ DEFAULT: '#3826A6',
+ light: '#6755C9',
+ xlight: '#8E76FA',
+ mid: '#483A95',
+ foreground: 'hsl(var(--primary-foreground))'
+ },
+ dark: {
+ high: '#16171B',
+ lighter: '#1E2430',
+ DEFAULT: '#1A1C1E'
+ },
+ 'regal-blue': '#6A71E7',
+ 'regal-rose': '#E93CB9',
+ border: 'hsl(var(--border))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ },
+ sidebar: {
+ DEFAULT: 'hsl(var(--sidebar-background))',
+ foreground: 'hsl(var(--sidebar-foreground))',
+ primary: 'hsl(var(--sidebar-primary))',
+ 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
+ accent: 'hsl(var(--sidebar-accent))',
+ 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
+ border: 'hsl(var(--sidebar-border))',
+ ring: 'hsl(var(--sidebar-ring))'
+ }
+ },
+ fontFamily: {
+ PlusJakartaSans: ['Plus-Jakarta-Sans-VariableFont_wght'],
+ PlusJakartaSansRegular: ['Plus-Jakarta-Sans-Regular'],
+ PlusJakartaSansBold: ['Plus-Jakarta-Sans-Bold'],
+ PlusJakartaSansLight: ['Plus-Jakarta-Sans-Light'],
+ PlusJakartaSansMedium: ['Plus-Jakarta-Sans-Medium'],
+ PlusJakartaSansSemiBold: ['Plus-Jakarta-Sans-SemiBold']
+ },
+ boxShadow: {
+ lgcard: '0px 50px 200px rgba(0, 0, 0, 0.1)',
+ xlcard: '0px 16px 79px rgba(0, 0, 0, 0.12)',
+ 'lgcard-white': '0px 50px 200px rgba(255, 255, 255, 0.1)',
+ 'xlcard-white': '0px 16px 79px rgba(255, 255, 255, 0.12)',
+ darker: '-8px -9px 14px rgba(255, 255, 255, 0.05), 10px 14px 34px rgba(0, 0, 0, 0.6), 0px 4px 24px rgba(0, 0, 0, 0.25)'
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)'
+ },
+ keyframes: {
+ 'accordion-down': {
+ from: {
+ height: '0'
+ },
+ to: {
+ height: 'var(--radix-accordion-content-height)'
+ }
+ },
+ 'accordion-up': {
+ from: {
+ height: 'var(--radix-accordion-content-height)'
+ },
+ to: {
+ height: '0'
+ }
+ }
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ spine: 'spin 10s linear infinite'
+ },
+ typography: {
+ DEFAULT: {
+ css: {
+ maxWidth: '100ch',
+ 'h3, h4, h5, h6, p, span, em, ul, ol, dl, blockquote, code, figure, pre': {
+ marginTop: '0.5rem',
+ marginBottom: '0.5rem',
+ lineHeight: '1.25rem',
+ fontSize: '14px',
+ wordSpacing: '-1px',
+ fontWeight: '400'
+ },
+ h1: {
+ fontSize: '1.3rem',
+ marginTop: '0.65rem',
+ marginBottom: '0.65rem',
+ lineHeight: '40px'
+ },
+ h2: {
+ fontSize: '1.1rem',
+ marginTop: '0.35rem',
+ marginBottom: '0.35rem',
+ lineHeight: '30px'
+ },
+ 'h1 span': {
+ fontSize: '1.3rem'
+ },
+ 'h2 span': {
+ fontSize: '1.1rem',
+ fontWeight: 'bold'
+ },
+ 'strong span': {
+ fontWeight: '600'
+ }
+ }
+ }
+ }
+ }
+ },
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')]
};
diff --git a/yarn.lock b/yarn.lock
index e64a251b5..5bbcaf487 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2772,6 +2772,11 @@
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.18.tgz#f80301907c243df03c7e9fd76c0286e95361f7c1"
integrity sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==
+"@hookform/resolvers@^3.9.1":
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.9.1.tgz#a23883c40bfd449cb6c6ab5a0fa0729184c950ff"
+ integrity sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==
+
"@humanwhocodes/config-array@^0.11.10":
version "0.11.11"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844"
@@ -6396,6 +6401,13 @@
dependencies:
"@radix-ui/react-use-layout-effect" "1.1.0"
+"@radix-ui/react-label@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.0.tgz#3aa2418d70bb242be37c51ff5e51a2adcbc372e3"
+ integrity sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==
+ dependencies:
+ "@radix-ui/react-primitive" "2.0.0"
+
"@radix-ui/react-menu@2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.2.tgz#91f6815845a4298dde775563ed2d80b7ad667899"
@@ -15367,6 +15379,11 @@ gcp-metadata@^6.0.0:
gaxios "^6.0.0"
json-bigint "^1.0.0"
+geist@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/geist/-/geist-1.3.1.tgz#bbd95db23b2a00baf6020e3b1b63a5752f4787d2"
+ integrity sha512-Q4gC1pBVPN+D579pBaz0TRRnGA4p9UK6elDY/xizXdFk/g4EKR5g0I+4p/Kj6gM0SajDBZ/0FvDV9ey9ud7BWw==
+
gensequence@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/gensequence/-/gensequence-6.0.0.tgz#ae46a0f89ebd7cc334e45cfb8f1c99a65248694e"
@@ -22770,10 +22787,10 @@ react-google-recaptcha@^2.1.0:
prop-types "^15.5.0"
react-async-script "^1.1.1"
-react-hook-form@^7.42.1:
- version "7.46.0"
- resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.46.0.tgz#dba3491e4bc0968346e5615d9d669af780b47cd4"
- integrity sha512-sc22pXwuKgbWBR5/EYWOVoFw4i/w893tDRUgQY2/Xb7wlpajJBrqAMFhb4z1CDhZ0TSFFfX62+iKx3gCXnCHHw==
+react-hook-form@^7.53.2:
+ version "7.53.2"
+ resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.2.tgz#6fa37ae27330af81089baadd7f322cc987b8e2ac"
+ integrity sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==
react-i18next@^14.1.0:
version "14.1.2"
@@ -27172,3 +27189,8 @@ yup@^0.32.11:
nanoclone "^0.2.1"
property-expr "^2.0.4"
toposort "^2.0.2"
+
+zod@^3.23.8:
+ version "3.23.8"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
+ integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==