diff --git a/.cspell.json b/.cspell.json index 4aa1361ce..a6d0fcfd1 100644 --- a/.cspell.json +++ b/.cspell.json @@ -334,7 +334,11 @@ "Northflank", "prebuild", "dataToDisplay", - "GlobalSkeleton" + "GlobalSkeleton", + "dailyplan", + "tomorow", + "comparization", + "plan" ], "useGitignore": true, "ignorePaths": [ diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index 7861cd28c..f01490ef5 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -129,6 +129,12 @@ function PasscodeScreen({ form, className }: { form: TAuthenticationPasscode } & const t = useTranslations(); const inputsRef = useRef>([]); + const formatTime = (seconds: number) => { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')}`; + }; + const [timer, setTimer] = useState(60); const [disabled, setDisabled] = useState(true); @@ -229,14 +235,12 @@ function PasscodeScreen({ form, className }: { form: TAuthenticationPasscode } & > {!disabled ? ( - {'Re'} - {t('pages.auth.SEND_CODE')} + {t('pages.auth.RESEND_CODE')} ) : ( - {'Re'} - {t('pages.auth.SEND_CODE')} {' in 00:'} - {timer} + {t('pages.auth.RESEND_CODE_IN')} {' '} + {formatTime(timer)} )} diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx index d5c3762c6..4826c1c86 100644 --- a/apps/web/app/[locale]/profile/[memberId]/page.tsx +++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx @@ -2,7 +2,7 @@ /* eslint-disable no-mixed-spaces-and-tabs */ import { imgTitle } from '@app/helpers'; -import { useAuthenticateUser, useOrganizationTeams, useTimer, useUserProfilePage } from '@app/hooks'; +import { useAuthenticateUser, useDailyPlan, useOrganizationTeams, useTimer, useUserProfilePage } from '@app/hooks'; import { ITimerStatusEnum, OT_Member } from '@app/interfaces'; import { clsxm, isValidUrl } from '@app/utils'; import clsx from 'clsx'; @@ -29,6 +29,7 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId const profile = useUserProfilePage(); const { user } = useAuthenticateUser(); const { isTrackingEnabled, activeTeam, activeTeamManagers } = useOrganizationTeams(); + const { getEmployeeDayPlans } = useDailyPlan(); const fullWidth = useRecoilValue(fullWidthState); const [activityFilter, setActivityFilter] = useState('Tasks'); const setActivityTypeFilter = useSetRecoilState(activityTypeState); @@ -68,6 +69,10 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId // eslint-disable-next-line react-hooks/exhaustive-deps }, [profile.member]); + React.useEffect(() => { + getEmployeeDayPlans(params.memberId); + }, [getEmployeeDayPlans, params.memberId]); + // Example usage return ( diff --git a/apps/web/app/api/daily-plan/[id]/route.ts b/apps/web/app/api/daily-plan/[id]/route.ts new file mode 100644 index 000000000..0ae421835 --- /dev/null +++ b/apps/web/app/api/daily-plan/[id]/route.ts @@ -0,0 +1,24 @@ +import { NextResponse } from 'next/server'; +import { authenticatedGuard } from '@app/services/server/guards/authenticated-guard-app'; +import { getDayPlansByEmployee } from '@app/services/server/requests'; +import { INextParams } from '@app/interfaces'; + +export async function GET(req: Request, { params }: INextParams) { + const res = new NextResponse(); + const { id } = params; + if (!id) { + return; + } + + const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + if (!user) return $res('Unauthorized'); + + const response = await getDayPlansByEmployee({ + bearer_token: access_token, + employeeId: id, + organizationId, + tenantId + }); + + return $res(response.data); +} diff --git a/apps/web/app/api/daily-plan/route.ts b/apps/web/app/api/daily-plan/route.ts new file mode 100644 index 000000000..e186506c8 --- /dev/null +++ b/apps/web/app/api/daily-plan/route.ts @@ -0,0 +1,32 @@ +import { ICreateDailyPlan } from '@app/interfaces'; +import { authenticatedGuard } from '@app/services/server/guards/authenticated-guard-app'; +import { createPlanRequest, getAllDayPlans } from '@app/services/server/requests'; +import { NextResponse } from 'next/server'; + +export async function POST(req: Request) { + const res = new NextResponse(); + const { $res, user, access_token: bearer_token, tenantId } = await authenticatedGuard(req, res); + + if (!user) return $res('Unauthorized'); + + const body = (await req.json()) as unknown as ICreateDailyPlan; + + const response = await createPlanRequest({ data: body, bearer_token, tenantId }); + + return $res(response.data); +} + +export async function GET(req: Request) { + const res = new NextResponse(); + + const { $res, user, tenantId, organizationId, access_token } = await authenticatedGuard(req, res); + if (!user) return $res('Unauthorized'); + + const response = await getAllDayPlans({ + bearer_token: access_token, + organizationId, + tenantId + }); + + return $res(response.data); +} diff --git a/apps/web/app/helpers/date.ts b/apps/web/app/helpers/date.ts index 06e44c3a8..8f859639f 100644 --- a/apps/web/app/helpers/date.ts +++ b/apps/web/app/helpers/date.ts @@ -115,3 +115,18 @@ export const calculateRemainingDays = (startDate: string, endDate: string): numb return moment(endDate).diff(startDate, 'days'); }; + +export const tomorrowDate = moment().add(1, 'days').toDate(); + +export const formatDayPlanDate = (dateString: string | Date, format?: string) => { + if (dateString.toString().length > 10) { + dateString = dateString.toString().split('T')[0]; + } + const date = moment(dateString, 'YYYY-MM-DD'); + + if (date.isSame(moment(), 'day')) return 'Today'; + if (date.isSame(moment().add(1, 'day'), 'day')) return 'Tomorrow'; + if (date.isSame(moment().subtract(1, 'day'), 'day')) return 'Yesterday'; + if (format === 'DD MMM YYYY') return formatDateString(dateString.toString()); + return date.format('dddd, MMMM DD, YYYY'); +}; diff --git a/apps/web/app/helpers/index.ts b/apps/web/app/helpers/index.ts index 681b8e9f1..5ec7a1bb8 100644 --- a/apps/web/app/helpers/index.ts +++ b/apps/web/app/helpers/index.ts @@ -8,3 +8,4 @@ export * from './regex'; export * from './validations'; export * from './colors'; export * from './strings'; +export * from './plan-day-badge'; diff --git a/apps/web/app/helpers/plan-day-badge.ts b/apps/web/app/helpers/plan-day-badge.ts new file mode 100644 index 000000000..b40497c5d --- /dev/null +++ b/apps/web/app/helpers/plan-day-badge.ts @@ -0,0 +1,25 @@ +import { IDailyPlan, ITeamTask } from '@app/interfaces'; +import { formatDayPlanDate } from './date'; + +export const planBadgeContent = (plans: IDailyPlan[], taskId: ITeamTask['id']): string | null => { + // Search a plan that contains a given task + const plan = plans.find((plan) => plan.tasks?.some((task) => task.id === taskId)); + + // If at least one plan have this task + if (plan) { + // Check if the task appears in other plans + const otherPlansWithTask = plans.filter( + (pl) => pl.id !== plan.id && pl.tasks?.some((tsk) => tsk.id === taskId) + ); + + // If the task exists in other plans, the its planned many days + if (otherPlansWithTask.length > 0) { + return 'Planned'; + } else { + return `Planned ${formatDayPlanDate(plan.date, 'DD MMM YYYY')}`; + } + // The task does not exist in any plan + } else { + return null; + } +}; diff --git a/apps/web/app/helpers/validations.ts b/apps/web/app/helpers/validations.ts index d0db0fa89..f5a30caea 100644 --- a/apps/web/app/helpers/validations.ts +++ b/apps/web/app/helpers/validations.ts @@ -24,7 +24,7 @@ export const authFormValidate = (keys: (keyof IRegisterDataAPI)[], values: IRegi } break; case 'recaptcha': - if (RECAPTCHA_SITE_KEY) { + if (RECAPTCHA_SITE_KEY.value) { if (!values['recaptcha'] || values['recaptcha'].trim().length < 2) { err['recaptcha'] = 'Please check the ReCaptcha checkbox before continue'; } diff --git a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts index 986a9c46c..91b76cd8b 100644 --- a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts +++ b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts @@ -23,9 +23,8 @@ type AuthCodeRef = { export function useAuthenticationPasscode() { const pathname = usePathname(); const query = useSearchParams(); - const queryTeamId = useMemo(() => { - return query?.get('teamId'); - }, [query]); + + const queryTeamId = query?.get('teamId'); const queryEmail = useMemo(() => { const emailQuery = query?.get('email') || ''; @@ -110,6 +109,7 @@ export function useAuthenticationPasscode() { signInToWorkspaceRequest({ email: email, + code: code, token: currentWorkspace?.token as string, selectedTeam: queryTeamId as string }); @@ -156,16 +156,8 @@ export function useAuthenticationPasscode() { [queryCall] ); - const signInToWorkspaceRequest = ({ - email, - token, - selectedTeam - }: { - email: string; - token: string; - selectedTeam: string; - }) => { - signInWorkspaceQueryCall(email, token, selectedTeam) + const signInToWorkspaceRequest = (params: { email: string; token: string; selectedTeam: string; code: string }) => { + signInWorkspaceQueryCall(params) .then(() => { setAuthenticated(true); router.push('/'); @@ -227,6 +219,7 @@ export function useAuthenticationPasscode() { signInToWorkspaceRequest({ email: formValues.email, + code: formValues.code, token, selectedTeam }); diff --git a/apps/web/app/hooks/auth/useAuthenticationPassword.ts b/apps/web/app/hooks/auth/useAuthenticationPassword.ts index 4c8fe64d2..b553b24aa 100644 --- a/apps/web/app/hooks/auth/useAuthenticationPassword.ts +++ b/apps/web/app/hooks/auth/useAuthenticationPassword.ts @@ -87,7 +87,7 @@ export function useAuthenticationPassword() { token: string; selectedTeam: string; }) => { - signInWorkspaceQueryCall(email, token, selectedTeam) + signInWorkspaceQueryCall({ email, token, selectedTeam, code: '' }) .then(() => { setAuthenticated(true); router.push('/'); diff --git a/apps/web/app/hooks/auth/useAuthenticationTeam.ts b/apps/web/app/hooks/auth/useAuthenticationTeam.ts index d43ace7dc..72d017aa5 100644 --- a/apps/web/app/hooks/auth/useAuthenticationTeam.ts +++ b/apps/web/app/hooks/auth/useAuthenticationTeam.ts @@ -72,6 +72,7 @@ export function useAuthenticationTeam() { const { errors, valid } = authFormValidate(validationFields, formValues); if (!valid) { + console.log({ errors }); setErrors(errors as any); return; } diff --git a/apps/web/app/hooks/features/useAuthTeamTasks.ts b/apps/web/app/hooks/features/useAuthTeamTasks.ts index c6853703a..c202aa3b7 100644 --- a/apps/web/app/hooks/features/useAuthTeamTasks.ts +++ b/apps/web/app/hooks/features/useAuthTeamTasks.ts @@ -1,11 +1,12 @@ import { IUser } from '@app/interfaces'; -import { tasksByTeamState } from '@app/stores'; +import { dailyPlanListState, tasksByTeamState } from '@app/stores'; import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { useOrganizationTeams } from './useOrganizationTeams'; export function useAuthTeamTasks(user: IUser | undefined) { const tasks = useRecoilValue(tasksByTeamState); + const plans = useRecoilValue(dailyPlanListState); const { activeTeam } = useOrganizationTeams(); const currentMember = activeTeam?.members?.find((member) => member.employee?.userId === user?.id); @@ -24,6 +25,11 @@ export function useAuthTeamTasks(user: IUser | undefined) { }); }, [tasks, user]); + const dailyplan = useMemo(() => { + if (!user) return []; + return plans.items; + }, [plans, user]); + const totalTodayTasks = useMemo( () => currentMember?.totalTodayTasks && currentMember?.totalTodayTasks.length @@ -41,6 +47,7 @@ export function useAuthTeamTasks(user: IUser | undefined) { return { assignedTasks, unassignedTasks, - workedTasks + workedTasks, + dailyplan }; } diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts new file mode 100644 index 000000000..0f7dbd8f6 --- /dev/null +++ b/apps/web/app/hooks/features/useDailyPlan.ts @@ -0,0 +1,74 @@ +'use client'; + +import { useRecoilState } from 'recoil'; +import { useCallback, useEffect } from 'react'; +import { useQuery } from '../useQuery'; +import { dailyPlanFetchingState, dailyPlanListState, userState } from '@app/stores'; +import { createDailyPlanAPI, getAllDayPlansAPI, getDayPlansByEmployeeAPI } from '@app/services/client/api'; +import { ICreateDailyPlan } from '@app/interfaces'; +import { useFirstLoad } from '../useFirstLoad'; + +export function useDailyPlan() { + const [user] = useRecoilState(userState); + + const { loading, queryCall } = useQuery(getDayPlansByEmployeeAPI); + const { loading: getAllDayPlansLoading, queryCall: getAllQueryCall } = useQuery(getAllDayPlansAPI); + const { loading: createDailyPlanLoading, queryCall: createQueryCall } = useQuery(createDailyPlanAPI); + + const [dailyPlan, setDailyPlan] = useRecoilState(dailyPlanListState); + const [dailyPlanFetching, setDailyPlanFetching] = useRecoilState(dailyPlanFetchingState); + const { firstLoadData: firstLoadDailyPlanData, firstLoad } = useFirstLoad(); + + useEffect(() => { + if (firstLoad) { + setDailyPlanFetching(loading); + } + }, [loading, firstLoad, setDailyPlanFetching]); + + const getAllDayPlans = useCallback(() => { + getAllQueryCall().then((response) => { + if (response.data.items.length) { + const { items, total } = response.data; + setDailyPlan({ items, total }); + } + }); + }, [getAllQueryCall, setDailyPlan]); + + const getEmployeeDayPlans = useCallback( + (employeeId: string) => { + queryCall(employeeId).then((response) => { + if (response.data.items.length) { + const { items, total } = response.data; + setDailyPlan({ items, total }); + } + }); + }, + [queryCall, setDailyPlan] + ); + + const createDailyPlan = useCallback( + async (data: ICreateDailyPlan) => { + if (user?.tenantId) { + const res = await createQueryCall(data, user?.tenantId || ''); + return res; + } + }, + [createQueryCall, user] + ); + + return { + dailyPlan, + setDailyPlan, + dailyPlanFetching, + firstLoadDailyPlanData, + + getAllDayPlans, + getAllDayPlansLoading, + + getEmployeeDayPlans, + loading, + + createDailyPlan, + createDailyPlanLoading + }; +} diff --git a/apps/web/app/hooks/features/useTaskVersion.ts b/apps/web/app/hooks/features/useTaskVersion.ts index f96044907..f4e8694ca 100644 --- a/apps/web/app/hooks/features/useTaskVersion.ts +++ b/apps/web/app/hooks/features/useTaskVersion.ts @@ -14,10 +14,9 @@ import { useRecoilState, useRecoilValue } from 'recoil'; import { useFirstLoad } from '../useFirstLoad'; import { useQuery } from '../useQuery'; import isEqual from 'lodash/isEqual'; -import { useCallbackRef } from '../useCallbackRef'; import { getActiveTeamIdCookie } from '@app/helpers'; -export function useTaskVersion(onVersionCreated?: (version: ITaskVersionCreate) => void) { +export function useTaskVersion() { const [user] = useRecoilState(userState); const activeTeamId = useRecoilValue(activeTeamIdState); @@ -27,7 +26,6 @@ export function useTaskVersion(onVersionCreated?: (version: ITaskVersionCreate) const { loading: editTaskVersionLoading, queryCall: editQueryCall } = useQuery(editTaskVersionAPI); const [taskVersion, setTaskVersion] = useRecoilState(taskVersionListState); - const $onVersionCreated = useCallbackRef(onVersionCreated); const [taskVersionFetching, setTaskVersionFetching] = useRecoilState(taskVersionFetchingState); const { firstLoadData: firstLoadTaskVersionData, firstLoad } = useFirstLoad(); @@ -68,7 +66,7 @@ export function useTaskVersion(onVersionCreated?: (version: ITaskVersionCreate) } }, - [$onVersionCreated, createQueryCall, createTaskVersionLoading, deleteTaskVersionLoading, activeTeamId] + [createQueryCall, createTaskVersionLoading, deleteTaskVersionLoading, activeTeamId] ); const deleteTaskVersion = useCallback( diff --git a/apps/web/app/hooks/features/useTeamMemberCard.ts b/apps/web/app/hooks/features/useTeamMemberCard.ts index 77510197b..3d2b2612c 100644 --- a/apps/web/app/hooks/features/useTeamMemberCard.ts +++ b/apps/web/app/hooks/features/useTeamMemberCard.ts @@ -22,7 +22,8 @@ import cloneDeep from 'lodash/cloneDeep'; */ export function useTeamMemberCard(member: IOrganizationTeamList['members'][number] | undefined) { const { updateTask, tasks, setActiveTask, deleteEmployeeFromTasks } = useTeamTasks(); - + const [assignTaskLoading, setAssignTaskLoading] = useState(false); + const [unAssignTaskLoading, setUnAssignTaskLoading] = useState(false); const publicTeam = useRecoilValue(getPublicState); const allTaskStatistics = useRecoilValue(allTaskStatisticsState); @@ -167,7 +168,7 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe if (!member?.employeeId) { return Promise.resolve(); } - + setAssignTaskLoading(true); return updateTask({ ...task, members: [...task.members, (member?.employeeId ? { id: member?.employeeId } : {}) as any] @@ -175,6 +176,7 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe if (isAuthUser && !activeTeamTask) { setActiveTask(task); } + setAssignTaskLoading(false); }); }, [updateTask, member, isAuthUser, setActiveTask, activeTeamTask] @@ -185,12 +187,14 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe if (!member?.employeeId) { return Promise.resolve(); } + setUnAssignTaskLoading(true); return updateTask({ ...task, members: task.members.filter((m) => m.id !== member.employeeId) }).finally(() => { isAuthUser && setActiveTask(null); + setUnAssignTaskLoading(false); }); }, [updateTask, member, isAuthUser, setActiveTask] @@ -201,6 +205,8 @@ export function useTeamMemberCard(member: IOrganizationTeamList['members'][numbe memberUnassignTasks, isTeamManager, memberUser, + assignTaskLoading, + unAssignTaskLoading, member, memberTask: memberTaskRef.current, isAuthUser, diff --git a/apps/web/app/hooks/features/useTeamTasks.ts b/apps/web/app/hooks/features/useTeamTasks.ts index e8dace649..d0735f62c 100644 --- a/apps/web/app/hooks/features/useTeamTasks.ts +++ b/apps/web/app/hooks/features/useTeamTasks.ts @@ -74,6 +74,12 @@ export function useTeamTasks() { const getTaskById = useCallback( (taskId: string) => { + tasksRef.current.forEach((task) => { + if (task.id === taskId) { + setDetailedTask(task); + } + }); + return getTasksByIdQueryCall(taskId).then((res) => { setDetailedTask(res?.data || null); return res; @@ -246,11 +252,11 @@ export function useTeamTasks() { ...(activeTeam?.projects && activeTeam?.projects.length > 0 ? { projectId: activeTeam.projects[0].id - } + } : {}), ...(description ? { description: `

${description}

` } : {}), ...(members ? { members } : {}), - taskStatusId: taskStatusId, + taskStatusId: taskStatusId }, $user.current ).then((res) => { diff --git a/apps/web/app/hooks/index.ts b/apps/web/app/hooks/index.ts index 1ca8c9f67..8e1f27f7b 100644 --- a/apps/web/app/hooks/index.ts +++ b/apps/web/app/hooks/index.ts @@ -59,6 +59,9 @@ export * from './features/useTaskSizes'; export * from './features/useTaskStatus'; export * from './features/useTaskVersion'; +// Daily plan +export * from './features/useDailyPlan'; + export * from './features/useRefetchData'; export * from './features/useRolePermissions'; diff --git a/apps/web/app/interfaces/IDailyPlan.ts b/apps/web/app/interfaces/IDailyPlan.ts new file mode 100644 index 000000000..4e882b084 --- /dev/null +++ b/apps/web/app/interfaces/IDailyPlan.ts @@ -0,0 +1,35 @@ +import { IEmployee } from './IEmployee'; +import { IOrganization } from './IOrganization'; +import { ITeamTask } from './ITask'; + +export type IDailyPlan = { + id: string; + createdAt: string; + updatedAt: string; + date: Date; + workTimePlanned: number; + status: DailyPlanStatusEnum; + employee?: IEmployee; + employeeId?: IEmployee['id']; + organizationId?: IOrganization['id']; + organization?: IOrganization; + tasks?: ITeamTask[]; +}; + +export interface ICreateDailyPlan { + date: Date; + workTimePlanned: number; + status: DailyPlanStatusEnum; + employeeId?: IEmployee['id']; + taskId?: ITeamTask['id']; + organizationId?: string; + tenantId?: string | null; +} + +export enum DailyPlanStatusEnum { + OPEN = 'open', + IN_PROGRESS = 'in-progress', + COMPLETED = 'completed' +} + +export type IDailyPlanMode = 'today' | 'tomorow' | 'custom'; diff --git a/apps/web/app/interfaces/index.ts b/apps/web/app/interfaces/index.ts index f6ba725be..187fbd13a 100644 --- a/apps/web/app/interfaces/index.ts +++ b/apps/web/app/interfaces/index.ts @@ -16,6 +16,7 @@ export * from './ITaskSizes'; export * from './ITaskTimesheet'; export * from './ITaskLabels'; export * from './ITaskRelatedIssueType'; +export * from './IDailyPlan'; export * from './IColor'; export * from './hooks'; export * from './IIcon'; diff --git a/apps/web/app/services/client/api/auth.ts b/apps/web/app/services/client/api/auth.ts index d6cec2346..3758550c5 100644 --- a/apps/web/app/services/client/api/auth.ts +++ b/apps/web/app/services/client/api/auth.ts @@ -26,7 +26,7 @@ export const getAuthenticatedUserDataAPI = () => { // Construct the query string with 'qs', including the includeEmployee parameter const query = qs.stringify({ relations: relations, - includeEmployee: true // Append includeEmployee parameter set to true + includeEmployee: true // Append includeEmployee parameter set to true }); // Execute the GET request to fetch the user data @@ -130,15 +130,20 @@ export async function signInEmailConfirmAPI(email: string, code: string) { }); } -export const signInWorkspaceAPI = (email: string, token: string, selectedTeam: string) => { +export const signInWorkspaceAPI = (params: { email: string; token: string; selectedTeam: string; code: string }) => { if (GAUZY_API_BASE_SERVER_URL.value) { - return signInWorkspaceGauzy({ email, token, teamId: selectedTeam, code: 'sign-in-workspace' }); + return signInWorkspaceGauzy({ + email: params.email, + token: params.token, + teamId: params.selectedTeam, + code: params.code + }); } return api.post(`/auth/signin-workspace`, { - email, - token, - teamId: selectedTeam + email: params.email, + token: params.token, + teamId: params.selectedTeam }); }; diff --git a/apps/web/app/services/client/api/daily-plan.ts b/apps/web/app/services/client/api/daily-plan.ts new file mode 100644 index 000000000..9ff2aa955 --- /dev/null +++ b/apps/web/app/services/client/api/daily-plan.ts @@ -0,0 +1,48 @@ +import qs from 'qs'; +import { get, post } from '../axios'; +import { ICreateDailyPlan, IDailyPlan, PaginationResponse } from '@app/interfaces'; +import { getOrganizationIdCookie, getTenantIdCookie } from '@app/helpers'; + +export function getAllDayPlansAPI() { + const organizationId = getOrganizationIdCookie(); + const tenantId = getTenantIdCookie(); + + const relations = ['employee', 'tasks']; + + const obj = { + 'where[organizationId]': organizationId, + 'where[tenantId]': tenantId + } as Record; + + relations.forEach((relation, i) => { + obj[`relations[${i}]`] = relation; + }); + + const query = qs.stringify(obj); + return get>(`/daily-plan?${query}`, { tenantId }); +} + +export function getDayPlansByEmployeeAPI(employeeId?: string) { + const organizationId = getOrganizationIdCookie(); + const tenantId = getTenantIdCookie(); + + const relations = ['employee', 'tasks']; + + const obj = { + 'where[organizationId]': organizationId, + 'where[tenantId]': tenantId + } as Record; + + relations.forEach((relation, i) => { + obj[`relations[${i}]`] = relation; + }); + + const query = qs.stringify(obj); + return get>(`/daily-plan/employee/${employeeId}?${query}`, { tenantId }); +} + +export function createDailyPlanAPI(data: ICreateDailyPlan, tenantId?: string) { + return post('/daily-plan', data, { + tenantId + }); +} diff --git a/apps/web/app/services/client/api/index.ts b/apps/web/app/services/client/api/index.ts index 7b8f36e10..a3fc188f9 100644 --- a/apps/web/app/services/client/api/index.ts +++ b/apps/web/app/services/client/api/index.ts @@ -14,6 +14,7 @@ export * from './task-sizes'; export * from './task-labels'; export * from './issue-type'; export * from './task-related-issue-type'; +export * from './daily-plan'; export * from './user'; export * from './request-to-join-team'; diff --git a/apps/web/app/services/server/requests/daily-plan.ts b/apps/web/app/services/server/requests/daily-plan.ts new file mode 100644 index 000000000..756ec1fed --- /dev/null +++ b/apps/web/app/services/server/requests/daily-plan.ts @@ -0,0 +1,81 @@ +import qs from 'qs'; +import { ICreateDailyPlan, IDailyPlan } from '@app/interfaces/IDailyPlan'; +import { serverFetch } from '../fetch'; + +export function getAllDayPlans({ + organizationId, + tenantId, + bearer_token, + relations = ['employee', 'tasks'] +}: { + organizationId: string; + tenantId: string; + bearer_token: string; + relations?: string[]; +}) { + const obj = { + 'where[organizationId]': organizationId, + 'where[tenantId]': tenantId + } as Record; + + relations.forEach((relation, i) => { + obj[`relations[${i}]`] = relation; + }); + + const query = qs.stringify(obj); + + return serverFetch({ + path: `/daily-plan?${query}`, + method: 'GET', + bearer_token + }); +} + +export function getDayPlansByEmployee({ + employeeId, + organizationId, + tenantId, + bearer_token, + relations = ['employee', 'tasks'] +}: { + employeeId: string; + organizationId: string; + tenantId: string; + bearer_token: string; + relations?: string[]; +}) { + const obj = { + 'where[organizationId]': organizationId, + 'where[tenantId]': tenantId + } as Record; + + relations.forEach((relation, i) => { + obj[`relations[${i}]`] = relation; + }); + + const query = qs.stringify(obj); + + return serverFetch({ + path: `/daily-plan/employee/${employeeId}?${query}`, + method: 'GET', + bearer_token + }); +} + +export function createPlanRequest({ + data, + bearer_token, + tenantId +}: { + data: ICreateDailyPlan; + bearer_token: string; + tenantId?: any; +}) { + return serverFetch({ + method: 'POST', + path: '/daily-plan', + body: data, + bearer_token, + tenantId + }); +} diff --git a/apps/web/app/services/server/requests/index.ts b/apps/web/app/services/server/requests/index.ts index 3226b849d..8915f0553 100644 --- a/apps/web/app/services/server/requests/index.ts +++ b/apps/web/app/services/server/requests/index.ts @@ -5,6 +5,7 @@ export * from './organization-team-employee'; export * from './tenant'; export * from './timer'; export * from './tasks'; +export * from './daily-plan'; export * from './employee'; export * from './invite'; export * from './timesheet'; diff --git a/apps/web/app/stores/daily-plan.ts b/apps/web/app/stores/daily-plan.ts new file mode 100644 index 000000000..115eca379 --- /dev/null +++ b/apps/web/app/stores/daily-plan.ts @@ -0,0 +1,26 @@ +import { atom, selector } from 'recoil'; +import { IDailyPlan, PaginationResponse } from '@app/interfaces'; + +export const dailyPlanListState = atom>({ + key: 'dailyPlanListState', + default: { items: [], total: 0 } +}); + +export const activeDailyPlanIdState = atom({ + key: 'activeDailyPlanIdService', + default: null +}); + +export const dailyPlanFetchingState = atom({ + key: 'dailyPlanFetchingState', + default: false +}); + +export const activeDailyPlanState = selector({ + key: 'activeDailyPlanState', + get: ({ get }) => { + const dailyPlans = get(dailyPlanListState); + const activeId = get(activeDailyPlanIdState); + return dailyPlans.items.find((plan) => plan.id === activeId) || dailyPlans.items[0] || null; + } +}); diff --git a/apps/web/app/stores/index.ts b/apps/web/app/stores/index.ts index 02fab4487..35f126481 100644 --- a/apps/web/app/stores/index.ts +++ b/apps/web/app/stores/index.ts @@ -16,6 +16,7 @@ export * from './task-sizes'; export * from './task-labels'; export * from './issue-type'; export * from './task-related-issue-type'; +export * from './daily-plan'; export * from './roles'; export * from './role-permissions'; diff --git a/apps/web/app/stores/team-tasks.ts b/apps/web/app/stores/team-tasks.ts index 27161efe4..78783383e 100644 --- a/apps/web/app/stores/team-tasks.ts +++ b/apps/web/app/stores/team-tasks.ts @@ -12,7 +12,12 @@ export const activeTeamTaskState = atom({ key: 'activeTeamTaskState', default: null }); - +export const activeTeamTaskId = atom<{ id: string }>({ + key: 'activeTeamTaskId', + default: { + id: '' + } +}); export const tasksFetchingState = atom({ key: 'tasksFetchingState', default: false diff --git a/apps/web/components/pages/404/index.tsx b/apps/web/components/pages/404/index.tsx index a26d8643a..6ed5f7686 100644 --- a/apps/web/components/pages/404/index.tsx +++ b/apps/web/components/pages/404/index.tsx @@ -2,8 +2,10 @@ import SadCry from '@components/ui/svgs/sad-cry'; import { Button, Text } from 'lib/components'; import Link from 'next/link'; +import { useTranslations } from 'next-intl'; function NotFound() { + const t = useTranslations(); return (
@@ -12,7 +14,7 @@ function NotFound() {
- Page not found ! + {t('common.PAGE_NOT_FOUND')}
diff --git a/apps/web/components/pages/kanban/menu-kanban-card.tsx b/apps/web/components/pages/kanban/menu-kanban-card.tsx new file mode 100644 index 000000000..c91fecaf4 --- /dev/null +++ b/apps/web/components/pages/kanban/menu-kanban-card.tsx @@ -0,0 +1,86 @@ +import { useTeamMemberCard } from '@app/hooks'; +import { activeTeamTaskId } from '@app/stores'; +import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover'; +import { ThreeCircleOutlineVerticalIcon } from 'assets/svg'; +import { SpinnerLoader } from 'lib/components'; +import { useTranslations } from 'next-intl'; +import { useState } from 'react'; +import { useSetRecoilState } from 'recoil'; + +export default function MenuKanbanCard({ member, item }: { item: any; member: any }) { + const t = useTranslations(); + const { assignTask, unassignTask, assignTaskLoading, unAssignTaskLoading } = useTeamMemberCard(member); + const setActiveTask = useSetRecoilState(activeTeamTaskId); + const [load, setLoad] = useState<'' | 'assign' | 'unassign'>(''); + const menu = [ + { + name: t('common.EDIT_TASK'), + closable: true, + action: 'edit', + active: true, + onClick: () => { + setActiveTask({ + id: item.id + }); + } + }, + { + name: t('common.ESTIMATE'), + closable: true, + action: 'estimate', + onClick: () => { + // TODO: Implement estimate task after fixing the time estimate issue + }, + active: true + }, + { + name: t('common.ASSIGN_TASK'), + action: 'assign', + active: true, + onClick: () => { + setLoad('assign'); + assignTask(item); + } + }, + { + name: t('common.UNASSIGN_TASK'), + action: 'unassign', + closable: true, + active: true, + onClick: () => { + setLoad('unassign'); + unassignTask(item); + } + } + ].filter((item) => item.active || item.active === undefined); + + return ( + + + + + +
    + {menu.map((item) => { + return ( +
  • item?.onClick()}> + +
  • + ); + })} +
+
+
+ ); +} diff --git a/apps/web/components/pages/task/details-section/blocks/task-main-info.tsx b/apps/web/components/pages/task/details-section/blocks/task-main-info.tsx index 84de7e04c..23b320fc5 100644 --- a/apps/web/components/pages/task/details-section/blocks/task-main-info.tsx +++ b/apps/web/components/pages/task/details-section/blocks/task-main-info.tsx @@ -15,6 +15,7 @@ import TaskRow from '../components/task-row'; import { DatePicker } from 'components/ui/DatePicker'; import Link from 'next/link'; import { useTranslations } from 'next-intl'; +import { PencilSquareIcon } from '@heroicons/react/20/solid'; const TaskMainInfo = () => { const [task] = useRecoilState(detailedTaskState); @@ -115,11 +116,13 @@ function DueDates() { 'leading-[140%] tracking-[-0.02em] text-[#282048] dark:text-white' )} > - {startDate - ? formatDateString(startDate.toISOString()) - : task?.startDate - ? formatDateString(task?.startDate) - : 'Set Start Date'} + {startDate ? ( + formatDateString(startDate.toISOString()) + ) : task?.startDate ? ( + formatDateString(task?.startDate) + ) : ( + + )}
} selected={$startDate.current ? (new Date($startDate.current) as Date) : undefined} @@ -161,11 +164,13 @@ function DueDates() { 'leading-[140%] tracking-[-0.02em] text-[#282048] dark:text-white' )} > - {dueDate - ? formatDateString(dueDate.toISOString()) - : task?.dueDate - ? formatDateString(task?.dueDate) - : 'Set Due Date'} + {dueDate ? ( + formatDateString(dueDate.toISOString()) + ) : task?.dueDate ? ( + formatDateString(task?.dueDate) + ) : ( + + )}
} selected={$dueDate.current ? (new Date($dueDate.current) as Date) : undefined} @@ -220,7 +225,7 @@ const ManageMembersPopover = (memberList: OT_Member[], task: ITeamTask | null) = memberList.filter((member) => member.employee ? !task?.members.map((item) => item.userId).includes(member.employee.userId) && - member.employee?.isActive + member.employee?.isActive : false ), [memberList, task?.members] @@ -231,7 +236,7 @@ const ManageMembersPopover = (memberList: OT_Member[], task: ITeamTask | null) = memberList.filter((member) => member.employee ? task?.members.map((item) => item.userId).includes(member.employee?.userId) && - member.employee?.isActive + member.employee?.isActive : false ), [memberList, task?.members] diff --git a/apps/web/components/pages/task/details-section/blocks/task-progress.tsx b/apps/web/components/pages/task/details-section/blocks/task-progress.tsx index 3281e335e..ff739b44b 100644 --- a/apps/web/components/pages/task/details-section/blocks/task-progress.tsx +++ b/apps/web/components/pages/task/details-section/blocks/task-progress.tsx @@ -70,8 +70,8 @@ const TaskProgress = () => { }, [userTotalTimeOnTaskToday]); useEffect(() => { - const matchingMembers: OT_Member[] | undefined = activeTeam?.members.filter( - (member) => task?.members.some((taskMember) => taskMember.id === member.employeeId) + const matchingMembers: OT_Member[] | undefined = activeTeam?.members.filter((member) => + task?.members.some((taskMember) => taskMember.id === member.employeeId) ); const usersTaskArray: ITasksTimesheet[] | undefined = matchingMembers @@ -181,12 +181,12 @@ const IndividualMembersTotalTime = ({ numMembersToShow }: { numMembersToShow: nu const [task] = useRecoilState(detailedTaskState); const { activeTeam } = useOrganizationTeams(); - const matchingMembers: OT_Member[] | undefined = activeTeam?.members.filter( - (member) => task?.members.some((taskMember) => taskMember.id === member.employeeId) + const matchingMembers: OT_Member[] | undefined = activeTeam?.members.filter((member) => + task?.members.some((taskMember) => taskMember.id === member.employeeId) ); const findUserTotalWorked = (user: OT_Member, id: string | undefined) => { - return user?.totalWorkedTasks.find((task: any) => task?.id === id)?.duration || 0; + return user?.totalWorkedTasks?.find((task: any) => task?.id === id)?.duration || 0; }; return ( diff --git a/apps/web/components/pages/task/details-section/blocks/task-publicity.tsx b/apps/web/components/pages/task/details-section/blocks/task-publicity.tsx index 6911e6316..2945f3a0a 100644 --- a/apps/web/components/pages/task/details-section/blocks/task-publicity.tsx +++ b/apps/web/components/pages/task/details-section/blocks/task-publicity.tsx @@ -5,7 +5,7 @@ import { debounce } from 'lodash'; import { useCallback, useEffect, useState } from 'react'; import { useRecoilState } from 'recoil'; import { useTranslations } from 'next-intl'; -import { GlobeIcon,LockIcon } from 'assets/svg'; +import { GlobeIcon, LockIcon } from 'assets/svg'; const TaskPublicity = () => { const [task] = useRecoilState(detailedTaskState); @@ -36,33 +36,27 @@ const TaskPublicity = () => { 'details-label px-4 flex justify-between' )} > - {isTaskPublic ? ( - <> -
+
+ {!isTaskPublic && ( + <> + +

{t('common.PRIVATE_TASK')}

+ + )} + + {isTaskPublic && ( + <>

{t('common.PUBLIC_TASK')}

-
-
handlePublicity(false)} - className="flex items-center cursor-pointer text-[0.625rem] 3xl:text-[0.700rem] text-[#A5A2B2]" - > - {t('common.PRIVATE_TASK_LABEL')} -
- - ) : ( - <> -
- -

{t('common.PRIVATE_TASK')}

-
-
handlePublicity(true)} - className="flex items-center cursor-pointer text-[0.625rem] text-[#A5A2B2]" - > - {t('common.PUBLIC_TASK_LABEL')} -
- - )} + + )} +
+
handlePublicity(!isTaskPublic)} + className="flex items-center cursor-pointer text-[0.625rem] text-[#A5A2B2]" + > + {!isTaskPublic ? t('common.PUBLIC_TASK_LABEL') : t('common.PRIVATE_TASK_LABEL')} +
); }; diff --git a/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx b/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx index d2500a643..260d9f22e 100644 --- a/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx +++ b/apps/web/components/pages/task/details-section/blocks/task-secondary-info.tsx @@ -50,9 +50,7 @@ const TaskSecondaryInfo = () => { const onVersionCreated = useCallback( (version: ITaskVersionCreate) => { - if ($taskVersion.current.length === 0) { - handleStatusUpdate(version.value || version.name, 'version', task); - } + handleStatusUpdate(version.value || version.name, 'version', task); }, [$taskVersion, task, handleStatusUpdate] ); @@ -72,6 +70,7 @@ const TaskSecondaryInfo = () => { ); const taskLabels = useTaskLabelsValue(); + const tags = useMemo(() => { return ( task?.tags @@ -120,6 +119,7 @@ const TaskSecondaryInfo = () => { /> )} + {task && } {/* Task Status */} @@ -235,7 +235,7 @@ const EpicParent = ({ task }: { task: ITeamTask }) => {
- , +
{`#${task?.rootEpic?.number} ${task?.rootEpic?.title}`}
diff --git a/apps/web/components/pages/task/details-section/components/profile-info-with-time.tsx b/apps/web/components/pages/task/details-section/components/profile-info-with-time.tsx index fb2cbd0cf..44137a048 100644 --- a/apps/web/components/pages/task/details-section/components/profile-info-with-time.tsx +++ b/apps/web/components/pages/task/details-section/components/profile-info-with-time.tsx @@ -15,6 +15,7 @@ const ProfileInfoWithTime = ({ profilePicSrc, names, profileInfoWrapperClassName diff --git a/apps/web/components/pages/task/details-section/components/profile-info.tsx b/apps/web/components/pages/task/details-section/components/profile-info.tsx index 3a8aba4be..e6fc77302 100644 --- a/apps/web/components/pages/task/details-section/components/profile-info.tsx +++ b/apps/web/components/pages/task/details-section/components/profile-info.tsx @@ -8,15 +8,16 @@ import stc from 'string-to-color'; type Props = { profilePicSrc?: string; names?: string; + fullName?: string; wrapperClassName?: string; profilePicSize?: number; }; -const ProfileInfo = ({ profilePicSrc, names, wrapperClassName, profilePicSize }: Props) => { +const ProfileInfo = ({ profilePicSrc, fullName, names, wrapperClassName, profilePicSize }: Props) => { const size = profilePicSize || 20; return ( -
+
{ onClick={() => saveTitle(title)} className="border-2 dark:border-[#464242] rounded-md" > - +
) : ( diff --git a/apps/web/components/ui/accordion.tsx b/apps/web/components/ui/accordion.tsx new file mode 100644 index 000000000..c60b2759b --- /dev/null +++ b/apps/web/components/ui/accordion.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import * as AccordionPrimitive from '@radix-ui/react-accordion'; +import { ChevronDown } from 'lucide-react'; + +import { cn } from 'lib/utils'; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = 'AccordionItem'; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180', + className + )} + {...props} + > + <> + {children} + + + + +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); + +AccordionContent.displayName = AccordionPrimitive.Content.displayName; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/apps/web/lib/components/Kanban.tsx b/apps/web/lib/components/Kanban.tsx index 5f3600047..70aac0ad4 100644 --- a/apps/web/lib/components/Kanban.tsx +++ b/apps/web/lib/components/Kanban.tsx @@ -98,7 +98,7 @@ function InnerItemList({ items, title }: { title: string; items: ITeamTask[]; dr {Array.isArray(items) && items?.length == 0 && (
- not found! + {t('common.NOT_FOUND')}!
( ({ className, type = 'text', label, dash = '__', wrapperClassName, value, loading, ...res }, ref) => { return ( -
+
( /> {dash}
- {!loading ? label : } + + {loading && } + {!loading && {label} }
); } diff --git a/apps/web/lib/components/kanban-card.tsx b/apps/web/lib/components/kanban-card.tsx index cfab76262..19a71f50e 100644 --- a/apps/web/lib/components/kanban-card.tsx +++ b/apps/web/lib/components/kanban-card.tsx @@ -1,22 +1,17 @@ import { DraggableProvided } from 'react-beautiful-dnd'; import PriorityIcon from '@components/ui/svgs/priority-icon'; import { ITaskPriority, ITeamTask, Tag } from '@app/interfaces'; -import { - useAuthenticateUser, - useCollaborative, - useOrganizationTeams, - useTMCardTaskEdit, - useTaskStatistics, - useTeamMemberCard -} from '@app/hooks'; +import { useAuthenticateUser, useOrganizationTeams, useTaskStatistics, useTeamMemberCard } from '@app/hooks'; import ImageComponent, { ImageOverlapperProps } from './image-overlapper'; import { TaskAllStatusTypes, TaskInput, TaskIssueStatus } from 'lib/features'; import Link from 'next/link'; import CircularProgress from '@components/ui/svgs/circular-progress'; import { HorizontalSeparator } from './separator'; import { secondsToTime } from '@app/helpers'; -import { UserTeamCardMenu } from 'lib/features/team/user-team-card/user-team-card-menu'; import { TaskStatus } from '@app/constants'; +import MenuKanbanCard from '@components/pages/kanban/menu-kanban-card'; +import { activeTeamTaskId } from '@app/stores'; +import { useRecoilState } from 'recoil'; function getStyle(provided: DraggableProvided, style: any) { if (!style) { @@ -133,6 +128,7 @@ export default function Item(props: ItemProps) { const { activeTeam } = useOrganizationTeams(); const { user } = useAuthenticateUser(); const { getEstimation } = useTaskStatistics(0); + const [activeTask, setActiveTask] = useRecoilState(activeTeamTaskId); const members = activeTeam?.members || []; const currentUser = members.find((m) => m.employee.userId === user?.id); @@ -145,7 +141,6 @@ export default function Item(props: ItemProps) { }); const memberInfo = useTeamMemberCard(currentUser); - const taskEdition = useTMCardTaskEdit(memberInfo.memberTask); const taskAssignee: ImageOverlapperProps[] = item.members.map((member: any) => { return { @@ -154,9 +149,7 @@ export default function Item(props: ItemProps) { alt: member.user.firstName }; }); - const { collaborativeSelect } = useCollaborative(memberInfo.memberUser); - const menu = <>{!collaborativeSelect && }; const progress = getEstimation(null, item, totalWorkedTasksTimer || 1, item.estimate || 0); const currentMember = activeTeam?.members.find((member) => member.id === memberInfo.member?.id || item?.id); @@ -183,11 +176,13 @@ export default function Item(props: ItemProps) { - {menu} + + +
- {!taskEdition.editMode ? ( + {activeTask?.id !== item.id ? ( <>
@@ -214,7 +209,7 @@ export default function Item(props: ItemProps) { ) : (
{ - taskEdition.setEditMode(false); + setActiveTask({ id: '' }); }} />
diff --git a/apps/web/lib/features/activity/screenshoots.tsx b/apps/web/lib/features/activity/screenshoots.tsx index 6a1984030..6cae32e83 100644 --- a/apps/web/lib/features/activity/screenshoots.tsx +++ b/apps/web/lib/features/activity/screenshoots.tsx @@ -21,7 +21,9 @@ export function ScreenshootTab() { {/* Stats cards */}
{t('timer.TIME_ACTIVITY')} -

{activityPercent.toFixed(2)} %

+

+ {isNaN(parseFloat(activityPercent.toFixed(2))) ? '00' : activityPercent.toFixed(2)} % +

diff --git a/apps/web/lib/features/daily-plan/create-daily-plan-form-modal.tsx b/apps/web/lib/features/daily-plan/create-daily-plan-form-modal.tsx new file mode 100644 index 000000000..bd9cee9b0 --- /dev/null +++ b/apps/web/lib/features/daily-plan/create-daily-plan-form-modal.tsx @@ -0,0 +1,128 @@ +import { useCallback, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { useRecoilState } from 'recoil'; +import { DailyPlanStatusEnum, IDailyPlanMode } from '@app/interfaces'; +import { useDailyPlan } from '@app/hooks'; +import { userState } from '@app/stores'; +import { Card, InputField, Modal, Text } from 'lib/components'; +import { tomorrowDate } from '@app/helpers'; +import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover'; +import { cn } from 'lib/utils'; +import { CalendarIcon, ReloadIcon } from '@radix-ui/react-icons'; +import moment from 'moment'; +import { Calendar } from '@components/ui/calendar'; +import { Button } from '@components/ui/button'; + +export function CreateDailyPlanFormModal({ + open, + closeModal, + taskId, + planMode +}: { + open: boolean; + closeModal: () => void; + taskId: string; + planMode: IDailyPlanMode; +}) { + const [user] = useRecoilState(userState); + const { handleSubmit, reset, register } = useForm(); + const { createDailyPlan, createDailyPlanLoading } = useDailyPlan(); + + const [date, setDate] = useState(new Date(tomorrowDate)); + + const onSubmit = useCallback( + async (values: any) => { + const toDay = new Date(); + createDailyPlan({ + workTimePlanned: parseInt(values.workTimePlanned), + taskId, + date: planMode == 'today' ? toDay : planMode == 'tomorow' ? tomorrowDate : date, + status: DailyPlanStatusEnum.OPEN, + tenantId: user?.tenantId, + employeeId: user?.employee.id, + organizationId: user?.employee.organizationId + }).then(() => { + reset(); + closeModal(); + }); + }, + [ + createDailyPlan, + taskId, + planMode, + date, + user?.tenantId, + user?.employee.id, + user?.employee.organizationId, + reset, + closeModal + ] + ); + return ( + +
+ +
+ {/* Form header */} +
+ + CREATE A DAY PLAN + + + You are creating a new plan +
+ + {/* Form Fields */} +
+ + + {planMode === 'custom' && ( + + + + + + setDate(day ?? new Date(tomorrowDate))} + initialFocus + disabled={{ from: new Date(1970, 1, 1), to: new Date() }} + // de + /> + + + )} + + +
+
+
+
+
+ ); +} diff --git a/apps/web/lib/features/index.ts b/apps/web/lib/features/index.ts index 5fc67fb4d..688b0f985 100644 --- a/apps/web/lib/features/index.ts +++ b/apps/web/lib/features/index.ts @@ -29,6 +29,7 @@ export * from './team/user-team-card/task-skeleton'; export * from './auth-user-task-input'; export * from './user-nav-menu'; +export * from './user-profile-plans'; export * from './user-profile-tasks'; // custom exports diff --git a/apps/web/lib/features/task/task-all-status-type.tsx b/apps/web/lib/features/task/task-all-status-type.tsx index 26c67f1a3..ce783a704 100644 --- a/apps/web/lib/features/task/task-all-status-type.tsx +++ b/apps/web/lib/features/task/task-all-status-type.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCustomEmblaCarousel, useSyncRef } from '@app/hooks'; +import { useCustomEmblaCarousel, useDailyPlan, useSyncRef } from '@app/hooks'; import { ITeamTask, Nullable } from '@app/interfaces'; import { RoundedButton } from 'lib/components'; import { useEffect, useMemo } from 'react'; @@ -12,6 +12,8 @@ import { useTaskStatusValue } from './task-status'; import { clsxm } from '@app/utils'; +import { planBadgeContent } from '@app/helpers'; +import { CalendarIcon } from '@radix-ui/react-icons'; export function TaskAllStatusTypes({ task, @@ -29,6 +31,8 @@ export function TaskAllStatusTypes({ const taskLabels = useTaskLabelsValue(); const taskStatus = useTaskStatusValue(); + const { dailyPlan, getAllDayPlans } = useDailyPlan(); + const { viewportRef, nextBtnEnabled, scrollNext, prevBtnEnabled, scrollPrev, emblaApi } = useCustomEmblaCarousel( 0, { @@ -43,6 +47,10 @@ export function TaskAllStatusTypes({ emblaApiRef.current?.reInit(); }, [task, emblaApiRef]); + useEffect(() => { + getAllDayPlans(); + }, [getAllDayPlans]); + const tags = useMemo(() => { return ( task?.tags @@ -86,7 +94,12 @@ export function TaskAllStatusTypes({ titleClassName={'text-[0.625rem] font-[500]'} /> )} - + {planBadgeContent(dailyPlan.items, task?.id ?? '') && ( +
+ + {planBadgeContent(dailyPlan.items, task?.id ?? '')} +
+ )} {tags.map((tag, i) => { return ( ; isAuthUser: boolean; activeAuthTask: boolean; - viewType?: 'default' | 'unassign'; + viewType?: 'default' | 'unassign' | 'dailyplan'; profile?: I_UserProfilePage; editTaskId?: string | null; setEditTaskId?: SetterOrUpdater; @@ -421,7 +424,7 @@ function TaskCardMenu({ task: ITeamTask; loading?: boolean; memberInfo?: I_TeamMemberCardHook; - viewType: 'default' | 'unassign'; + viewType: 'default' | 'unassign' | 'dailyplan'; }) { const t = useTranslations(); const handleAssignment = useCallback(() => { @@ -451,7 +454,7 @@ function TaskCardMenu({ {() => { return ( - +
  • -
  • +
  • + {viewType == 'default' && ( + <> + +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    + + )} + {/*
  • ); } + +function PlanTask({ planMode, taskId }: { taskId: string; planMode: IDailyPlanMode }) { + const { closeModal, isOpen, openModal } = useModal(); + + return ( + <> + + + {planMode === 'today' && 'Plan for today'} + {planMode === 'tomorow' && 'Plan for tomorow'} + {planMode === 'custom' && 'Plan for some day'} + + + ); +} diff --git a/apps/web/lib/features/task/task-filters.tsx b/apps/web/lib/features/task/task-filters.tsx index a94d77ebd..5cc31a119 100644 --- a/apps/web/lib/features/task/task-filters.tsx +++ b/apps/web/lib/features/task/task-filters.tsx @@ -14,7 +14,7 @@ import { TaskLabelsDropdown, TaskPropertiesDropdown, TaskSizesDropdown, TaskStat import { useTranslations } from 'next-intl'; import { SettingFilterIcon } from 'assets/svg'; -type ITab = 'worked' | 'assigned' | 'unassigned'; +type ITab = 'worked' | 'assigned' | 'unassigned' | 'dailyplan'; type ITabs = { tab: ITab; name: string; @@ -49,7 +49,8 @@ export function useTaskFilter(profile: I_UserProfilePage) { const tasksFiltered: { [x in ITab]: ITeamTask[] } = { unassigned: profile.tasksGrouped.unassignedTasks, assigned: profile.tasksGrouped.assignedTasks, - worked: profile.tasksGrouped.workedTasks + worked: profile.tasksGrouped.workedTasks, + dailyplan: [] // Change this soon }; const tasks = tasksFiltered[tab]; @@ -83,6 +84,12 @@ export function useTaskFilter(profile: I_UserProfilePage) { name: t('common.UNASSIGNED'), description: t('task.tabFilter.UNASSIGNED_DESCRIPTION'), count: profile.tasksGrouped.unassignedTasks.length + }, + { + tab: 'dailyplan', + name: 'Daily Plan', + description: 'This tab shows all yours tasks planned', + count: profile.tasksGrouped.dailyplan.length } ]; diff --git a/apps/web/lib/features/task/task-input-kanban.tsx b/apps/web/lib/features/task/task-input-kanban.tsx index 9d3a46d44..f87d3fd1f 100644 --- a/apps/web/lib/features/task/task-input-kanban.tsx +++ b/apps/web/lib/features/task/task-input-kanban.tsx @@ -92,8 +92,7 @@ export function TaskInputKanban(props: Props) { setQuery, updateLoading, updateTaskTitleHandler, - setFilter, - taskIssue + setFilter } = datas; const inputTaskTitle = useMemo(() => inputTask?.title || '', [inputTask?.title]); @@ -153,7 +152,6 @@ export function TaskInputKanban(props: Props) { If task is passed then we don't want to set the active task for the authenticated user. after task creation */ - const autoActiveTask = props.task !== undefined ? false : true; const handleTaskCreation = useCallback(async () => { /* Checking if the `handleTaskCreation` is available and if the `hasCreateForm` is true. */ datas && @@ -171,7 +169,7 @@ export function TaskInputKanban(props: Props) { props.onClose && props.onClose(); }); - }, [datas, taskIssue, autoActiveTask, props.autoAssignTaskAuth, props.usersTaskCreatedAssignTo, onTaskCreated]); + }, [datas, props, onTaskCreated]); let updatedTaskList: ITeamTask[] = []; if (props.forParentChildRelationship) { diff --git a/apps/web/lib/features/task/task-input.tsx b/apps/web/lib/features/task/task-input.tsx index 77d95bc96..6cffe7fe4 100644 --- a/apps/web/lib/features/task/task-input.tsx +++ b/apps/web/lib/features/task/task-input.tsx @@ -107,7 +107,7 @@ export function TaskInput(props: Props) { setQuery, updateLoading, updateTaskTitleHandler, - setFilter, + setFilter } = datas; const inputTaskTitle = useMemo(() => inputTask?.title || '', [inputTask?.title]); @@ -265,6 +265,7 @@ export function TaskInput(props: Props) { setEditMode(false); } }, [setEditMode, editMode, targetEl]); + useHotkeys(HostKeys.CREATE_TASK, handleCommandKeySequence); useEffect(() => { @@ -367,7 +368,7 @@ export function TaskInput(props: Props) { return viewType === 'one-view' ? ( taskCard ) : ( - + {inputField} -
    +
    {/* Create team button */}
    {datas.hasCreateForm && ( diff --git a/apps/web/lib/features/task/task-issue.tsx b/apps/web/lib/features/task/task-issue.tsx index 2925df597..c68719934 100644 --- a/apps/web/lib/features/task/task-issue.tsx +++ b/apps/web/lib/features/task/task-issue.tsx @@ -19,22 +19,26 @@ export const taskIssues: TStatus = { Bug: { icon: , name: 'Bug', - bgColor: '#923535' + bgColor: '#923535', + className: 'min-w-[5rem]' }, Task: { icon: , name: 'Task', - bgColor: '#5483BA' + bgColor: '#5483BA', + className: 'min-w-[5rem]' }, Story: { icon: , name: 'Story', - bgColor: '#66BB97' + bgColor: '#66BB97', + className: 'min-w-[5rem]' }, Epic: { icon: , name: 'Custom', - bgColor: '#8154BA' + bgColor: '#8154BA', + className: 'min-w-[5rem]' } }; @@ -103,6 +107,7 @@ export function ActiveTaskIssuesDropdown({ ...props }: IActiveTaskStatuses<'issu [IssueType.BUG]: items.filter((it) => [IssueType.STORY, IssueType.TASK].includes(it.value as IssueType)) }; + let updatedItemsBasedOnTaskIssueType: TStatusItem[] = []; if (props.task && props.task?.issueType && props.task.parent) { diff --git a/apps/web/lib/features/task/task-status.tsx b/apps/web/lib/features/task/task-status.tsx index 58b0eae4e..7e4617b03 100644 --- a/apps/web/lib/features/task/task-status.tsx +++ b/apps/web/lib/features/task/task-status.tsx @@ -42,6 +42,7 @@ export type TStatusItem = { value?: string; bordered?: boolean; showIcon?: boolean; + className?: string; }; export type TStatus = { @@ -824,11 +825,12 @@ export function TaskStatus({ {name && (issueType !== 'issue' || showIssueLabels) && (
    @@ -985,8 +987,7 @@ export function StatusDropdown({ 'text-dark dark:text-white bg-[#F2F2F2] dark:bg-dark--theme-light', forDetails && 'bg-transparent border dark:border-[#FFFFFF33] dark:bg-[#1B1D22]', - taskStatusClassName, - 'max-w-10' + taskStatusClassName )} name={ values.length > 0 @@ -1023,6 +1024,7 @@ export function StatusDropdown({ > {items.map((item, i) => { const item_value = item.value || item.name; + return ( ({ issueType === 'issue' && [ 'rounded-md px-2 text-white' ], - `${sidebarUI ? 'rounded-[4px]' : ''}`, - `${bordered ? 'input-border' : ''}`, - (isVersion || isEpic) && 'dark:text-white' + sidebarUI && 'rounded-[4px]', + bordered && 'input-border', + (isVersion || isEpic) && 'dark:text-white', + item?.className )} /> diff --git a/apps/web/lib/features/team-members-block-view.tsx b/apps/web/lib/features/team-members-block-view.tsx index bfe771765..0643621eb 100644 --- a/apps/web/lib/features/team-members-block-view.tsx +++ b/apps/web/lib/features/team-members-block-view.tsx @@ -30,13 +30,13 @@ const TeamMembersBlockView: React.FC = ({ emptyMessage = t('common.NO_USERS_ONLINE'); break; case 'running': - emptyMessage = 'No users are currently working.'; + emptyMessage = t('common.NO_USERS_WORKING'); break; case 'pause': - emptyMessage = 'No users are paused work at the moment.'; + emptyMessage = t('common.NO_USERS_PAUSED_WORK'); break; case 'idle': - emptyMessage = 'No users are idle right now.'; + emptyMessage = t('common.NO_USERS_IDLE'); break; } diff --git a/apps/web/lib/features/team/user-team-block/user-team-card-menu.tsx b/apps/web/lib/features/team/user-team-block/user-team-card-menu.tsx index 98564efe1..fc7e9cb62 100644 --- a/apps/web/lib/features/team/user-team-block/user-team-card-menu.tsx +++ b/apps/web/lib/features/team/user-team-block/user-team-card-menu.tsx @@ -103,7 +103,7 @@ function DropdownMenu({ edition, memberInfo }: Props) { leave="transition duration-75 ease-out" leaveFrom="transform scale-100 opacity-100" leaveTo="transform scale-95 opacity-0" - className="absolute z-10 -right-5 min-w-[13.125rem]" + className="absolute z-30 -right-5 min-w-[13.125rem]" > {({ close }) => { diff --git a/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx b/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx index 358c4ac20..a6dc4c52d 100644 --- a/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx +++ b/apps/web/lib/features/team/user-team-card/user-team-card-menu.tsx @@ -103,7 +103,7 @@ function DropdownMenu({ edition, memberInfo }: Props) { leave="transition duration-75 ease-out" leaveFrom="transform scale-100 opacity-100" leaveTo="transform scale-95 opacity-0" - className="absolute z-10 -right-5 min-w-[13.125rem]" + className="absolute z-30 -right-5 min-w-[13.125rem]" > {({ close }) => { diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx new file mode 100644 index 000000000..78bc2190c --- /dev/null +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -0,0 +1,131 @@ +'use client'; + +import { useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { useDailyPlan, useUserProfilePage } from '@app/hooks'; +import { TaskCard } from './task/task-card'; +import { IDailyPlan } from '@app/interfaces'; +import { Container, VerticalSeparator } from 'lib/components'; +import { clsxm } from '@app/utils'; +import { fullWidthState } from '@app/stores/fullWidth'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; +import { formatDayPlanDate } from '@app/helpers'; + +type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; + +export function UserProfilePlans() { + const profile = useUserProfilePage(); + const { dailyPlan } = useDailyPlan(); + const fullWidth = useRecoilValue(fullWidthState); + + const [currentTab, setCurrentTab] = useState('Today Tasks'); + + const tabsScreens = { + 'Today Tasks': , + 'Future Tasks': , + 'Past Tasks': , + 'All Tasks': , + Outstanding: <> + }; + + return ( +
    + +
    + {Object.keys(tabsScreens).map((filter, i) => ( +
    + {i !== 0 && } +
    setCurrentTab(filter as FilterTabs)} + > + {filter} +
    +
    + ))} +
    + {tabsScreens[currentTab]} +
    +
    + ); +} + +function AllPlans({ + plans, + profile, + currentTab = 'All Tasks' +}: { + plans: IDailyPlan[]; + profile: any; + currentTab?: FilterTabs; +}) { + // Sort plans + const ascSortedPlans = [...plans].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + const descSortedPlans = [...plans].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + // Filter plans + let filteredPlans: IDailyPlan[] = []; + + filteredPlans = [...plans].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); + + if (currentTab === 'Future Tasks') + filteredPlans = ascSortedPlans.filter((plan) => { + const planDate = new Date(plan.date); + const today = new Date(); + today.setHours(23, 59, 59, 0); // Set today time to exclude timestamps in comparization + return planDate.getTime() >= today.getTime(); + }); + + if (currentTab === 'Past Tasks') + filteredPlans = descSortedPlans.filter((plan) => { + const planDate = new Date(plan.date); + const today = new Date(); + today.setHours(0, 0, 0, 0); // Set today time to exclude timestamps in comparization + return planDate.getTime() < today.getTime(); + }); + + if (currentTab === 'Today Tasks') + filteredPlans = [...plans].filter((plan) => + plan.date.toString().startsWith(new Date().toISOString().split('T')[0]) + ); + + return ( +
      + + {filteredPlans.map((plan) => ( + + +
      + {formatDayPlanDate(plan.date.toString())} ({plan.tasks?.length}) +
      +
      + +
        + {plan.tasks?.map((task) => ( + + ))} +
      +
      +
      + ))} +
      +
    + ); +} diff --git a/apps/web/lib/features/user-profile-tasks.tsx b/apps/web/lib/features/user-profile-tasks.tsx index 623afdc3e..027b545fa 100644 --- a/apps/web/lib/features/user-profile-tasks.tsx +++ b/apps/web/lib/features/user-profile-tasks.tsx @@ -1,5 +1,6 @@ import { I_UserProfilePage, useLiveTimerStatus } from '@app/hooks'; import { Divider, Text } from 'lib/components'; +import { UserProfilePlans } from 'lib/features'; import { TaskCard } from './task/task-card'; import { I_TaskFilter } from './task/task-filters'; import { useTranslations } from 'next-intl'; @@ -71,6 +72,8 @@ export function UserProfileTask({ profile, tabFiltered }: Props) { /> )} + {tabFiltered.tab === 'dailyplan' && } + {tabFiltered.tab === 'worked' && otherTasks.length > 0 && (
    @@ -80,27 +83,29 @@ export function UserProfileTask({ profile, tabFiltered }: Props) {
    )} -
      - {otherTasks.map((task) => { - return ( -
    • - -
    • - ); - })} -
    + {tabFiltered.tab !== 'dailyplan' && ( +
      + {otherTasks.map((task) => { + return ( +
    • + +
    • + ); + })} +
    + )}
    ); } diff --git a/apps/web/lib/i18n/en.ts b/apps/web/lib/i18n/en.ts index 72764e8da..dc0c10db7 100644 --- a/apps/web/lib/i18n/en.ts +++ b/apps/web/lib/i18n/en.ts @@ -163,6 +163,8 @@ export const en = { auth: { SEND_CODE: 'send code', + RESEND_CODE: 'Resend Code', + RESEND_CODE_IN: 'Resend Code in', JOIN: 'Join', UNRECEIVED_CODE: "Didn't receive code ?", JOIN_TEAM: 'Join Team', diff --git a/apps/web/lib/settings/version-form.tsx b/apps/web/lib/settings/version-form.tsx index f97e82671..feaf8b2f0 100644 --- a/apps/web/lib/settings/version-form.tsx +++ b/apps/web/lib/settings/version-form.tsx @@ -1,7 +1,7 @@ import { Button, InputField, Text } from 'lib/components'; import { StatusesListCard } from './list-card'; -import { useTaskVersion } from '@app/hooks'; +import { useCallbackRef, useTaskVersion } from '@app/hooks'; import { ITaskVersionCreate, ITaskVersionItemList } from '@app/interfaces'; import { userState } from '@app/stores'; import { Spinner } from '@components/ui/loaders/spinner'; @@ -27,6 +27,7 @@ export const VersionForm = ({ formOnly = false, onCreated, onVersionCreated }: S const { register, setValue, handleSubmit, reset, getValues } = useForm(); const [createNew, setCreateNew] = useState(formOnly); const [edit, setEdit] = useState(null); + const $onVersionCreated = useCallbackRef(onVersionCreated); const { loading, @@ -36,7 +37,7 @@ export const VersionForm = ({ formOnly = false, onCreated, onVersionCreated }: S editTaskVersion, createTaskVersionLoading, editTaskVersionLoading - } = useTaskVersion(onVersionCreated); + } = useTaskVersion(); const { refetch } = useRefetchData(); useEffect(() => { @@ -64,10 +65,11 @@ export const VersionForm = ({ formOnly = false, onCreated, onVersionCreated }: S tenantId: user?.tenantId // icon: values.icon, // projectId: '', - })?.then(() => { + })?.then(({ data }) => { !formOnly && setCreateNew(false); onCreated && onCreated(); + $onVersionCreated.current && $onVersionCreated.current(data); refetch(); reset(); }); diff --git a/apps/web/messages/ar.json b/apps/web/messages/ar.json index 59d7c47e6..7cd0af5c4 100644 --- a/apps/web/messages/ar.json +++ b/apps/web/messages/ar.json @@ -183,7 +183,12 @@ "GITHUB_AUTO_SYNC_LABEL": "اختر تسمية التزامن التلقائي", "GITHUB_INTEGRATION_SUBTITLE_TEXT": "قم بتفعيل تكامل GitHub لمزامنة المشروع والمستودع", "THERE_IS_NO_TASK_ASSIGNED": "لا توجد مهام معينة", - "NO_USERS_ONLINE": "لا يوجد مستخدمين على الانترنت" + "NO_USERS_ONLINE": "لا يوجد مستخدمين على الانترنت", + "NOT_FOUND": "الصفحة غير موجودة", + "PAGE_NOT_FOUND": "غير معثور عليه", + "NO_USERS_WORKING": "لا يوجد مستخدمين يعملون حاليا", + "NO_USERS_PAUSED_WORK": "لم يتم إيقاف أي مستخدم مؤقتًا عن العمل في الوقت الحالي", + "NO_USERS_IDLE": "لا يوجد مستخدمين خاملين في الوقت الحالي" }, "alerts": { "REAL_TIME_ON_WORKING": "نحن نعمل على المزامنة في الوقت الحقيقي في الوقت الحالي، يرجى التحقق من هذه الميزة لاحقًا", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "إرسال الرمز", + "RESEND_CODE": "إعادة إرسال الرمز", + "RESEND_CODE_IN": "إعادة إرسال الرمز في", "JOIN": "انضمام", "UNRECEIVED_CODE": "لم تتلقى الرمز؟", "JOIN_TEAM": "انضمام للفريق", diff --git a/apps/web/messages/bg.json b/apps/web/messages/bg.json index f32f6b5cd..a11adf2e9 100644 --- a/apps/web/messages/bg.json +++ b/apps/web/messages/bg.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Синхронизирайте задачите избирателно, като ги свържете с конкретен етикет.", "GITHUB_AUTO_SYNC_LABEL": "Изберете етикет за автоматична синхронизация", "THERE_IS_NO_TASK_ASSIGNED": "Няма възложени задачи", - "NO_USERS_ONLINE": "Няма потребители онлайн" + "NO_USERS_ONLINE": "Няма потребители онлайн", + "NOT_FOUND": "не е намерено", + "PAGE_NOT_FOUND": "Страницата не е намерена", + "NO_USERS_WORKING":"В момента няма активни потребители", + "NO_USERS_PAUSED_WORK":"В момента няма потребители, които да са спрели работата си", + "NO_USERS_IDLE":"В момента няма неактивни потребители" }, "alerts": { "REAL_TIME_ON_WORKING": "В момента работим върху синхронизирането в реално време, моля, проверете тази функция по-късно.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "изпрати код", + "RESEND_CODE": "Изпрати код отново", + "RESEND_CODE_IN": "Изпрати код отново след", "JOIN": "Присъединяване", "UNRECEIVED_CODE": "Не получихте код?", "JOIN_TEAM": "Присъединяване към отбор", diff --git a/apps/web/messages/de.json b/apps/web/messages/de.json index b3eebf7d4..c564b18ed 100644 --- a/apps/web/messages/de.json +++ b/apps/web/messages/de.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronisieren Sie Aufgaben selektiv, indem Sie sie mit einem bestimmten Label verknüpfen.", "GITHUB_AUTO_SYNC_LABEL": "Auto-Sync-Label auswählen", "THERE_IS_NO_TASK_ASSIGNED": "Es ist keine Aufgabe zugewiesen", - "NO_USERS_ONLINE": "Es sind keine Benutzer online" + "NO_USERS_ONLINE": "Es sind keine Benutzer online", + "NOT_FOUND": "nicht gefunden", + "PAGE_NOT_FOUND": "Seite nicht gefunden", + "NO_USERS_WORKING": "Derzeit gibt es keine aktiven Benutzer", + "NO_USERS_PAUSED_WORK": "Derzeit gibt es keine Benutzer, die ihre Arbeit eingestellt haben", + "NO_USERS_IDLE": "Derzeit gibt es keine inaktiven Benutzer." }, "alerts": { @@ -244,6 +249,8 @@ }, "auth": { "SEND_CODE": "Code senden", + "RESEND_CODE": "Code erneut senden", + "RESEND_CODE_IN": "Code in", "JOIN": "Beitreten", "UNRECEIVED_CODE": "Keinen Code erhalten?", "JOIN_TEAM": "Team beitreten", diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 4b0fa979e..02dde9bba 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronize tasks selectively by associating them with specific label.", "GITHUB_AUTO_SYNC_LABEL": "Select Auto-Sync Label", "THERE_IS_NO_TASK_ASSIGNED": "There is no task assigned", - "NO_USERS_ONLINE": "There are no users online" + "NO_USERS_ONLINE": "There are no users online", + "NOT_FOUND": "Not Found", + "PAGE_NOT_FOUND": "Page not found", + "NO_USERS_WORKING": "There are currently no active users", + "NO_USERS_PAUSED_WORK": "There are currently no users who have paused their work", + "NO_USERS_IDLE": "There are currently no inactive users" }, "alerts": { "REAL_TIME_ON_WORKING": "We are working on Real-Time Sync at the moment, please check on this feature later.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Resend Code", + "RESEND_CODE_IN": "Resend Code in", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/messages/es.json b/apps/web/messages/es.json index 22d97aca7..638e33ce1 100644 --- a/apps/web/messages/es.json +++ b/apps/web/messages/es.json @@ -183,7 +183,12 @@ "GITHUB_AUTO_SYNC_LABEL": "Seleccionar etiqueta de sincronización automática", "GITHUB_INTEGRATION_SUBTITLE_TEXT": "Activa la integración de GitHub para la sincronización de proyectos y repositorios", "THERE_IS_NO_TASK_ASSIGNED": "No hay tareas asignadas", - "NO_USERS_ONLINE": "No hay usuarios en línea" + "NO_USERS_ONLINE": "No hay usuarios en línea", + "NOT_FOUND": "No encontrado", + "PAGE_NOT_FOUND": "Página no encontrada", + "NO_USERS_WORKING": "Actualmente no hay usuarios activos", + "NO_USERS_PAUSED_WORK": "Actualmente no hay usuarios que hayan dejado de trabajar", + "NO_USERS_IDLE": "Actualmente no hay usuarios inactivos" }, "alerts": { "REAL_TIME_ON_WORKING": "Estamos trabajando en la sincronización en tiempo real en este momento; verifique esta función más adelante.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "enviar código", + "RESEND_CODE": "Reenviar código", + "RESEND_CODE_IN": "Reenviar código en", "JOIN": "Unirse", "UNRECEIVED_CODE": "¿No recibiste el código?", "JOIN_TEAM": "Unirse al equipo", diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json index 2c81353e3..bb6e51b32 100644 --- a/apps/web/messages/fr.json +++ b/apps/web/messages/fr.json @@ -183,7 +183,12 @@ "GITHUB_AUTO_SYNC_LABEL": "Sélectionner l'étiquette de synchronisation automatique", "GITHUB_INTEGRATION_SUBTITLE_TEXT": "Activez l'intégration GitHub pour la synchronisation des projets et des dépôts", "THERE_IS_NO_TASK_ASSIGNED": "Aucune tâche n'est assignée", - "NO_USERS_ONLINE": "Aucun utilisateur en ligne" + "NO_USERS_ONLINE": "Aucun utilisateur en ligne", + "NOT_FOUND": "Non trouvé", + "PAGE_NOT_FOUND": "Page non trouvée", + "NO_USERS_WORKING": "Il n'y a actuellement aucun utilisateur actif", + "NO_USERS_PAUSED_WORK": "Il n'y a actuellement aucun utilisateur qui a mis en pause son travail", + "NO_USERS_IDLE": "Il n'y a actuellement aucun utilisateur inactif" }, "alerts": { "REAL_TIME_ON_WORKING": "Nous travaillons actuellement sur la synchronisation en temps réel, veuillez vérifier cette fonctionnalité plus tard.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "envoyer le code", + "RESEND_CODE": "Renvoyer le code", + "RESEND_CODE_IN": "Renvoyer le code dans", "JOIN": "Joindre", "UNRECEIVED_CODE": "Vous n'avez pas reçu de code ?", "JOIN_TEAM": "Rejoindre l'équipe", diff --git a/apps/web/messages/he.json b/apps/web/messages/he.json index 3a68279a5..f5d7aadd0 100644 --- a/apps/web/messages/he.json +++ b/apps/web/messages/he.json @@ -183,7 +183,12 @@ "GITHUB_AUTO_SYNC_LABEL": "בחר תגית אוטומטית לסנכרון", "GITHUB_INTEGRATION_SUBTITLE_TEXT": "הפעל אינטגרציה של GitHub עבור סנכרון פרויקטים ומאגרים", "THERE_IS_NO_TASK_ASSIGNED": "אין משימות מוקצות", - "NO_USERS_ONLINE": "אין משתמשים מחוברים" + "NO_USERS_ONLINE": "אין משתמשים מחוברים", + "NOT_FOUND": "לא נמצא", + "PAGE_NOT_FOUND": "הדף לא נמצא", + "NO_USERS_WORKING": "כרגע אין משתמשים פעילים", + "NO_USERS_PAUSED_WORK": "כרגע אין משתמשים שהפסיקו את עבודתם", + "NO_USERS_IDLE": "כרגע אין משתמשים לא פעילים" }, "alerts": { "REAL_TIME_ON_WORKING": "אנחנו עובדים על סנכרון בזמן אמת כרגע, אנא בדוק את התכונה הזו מאוחר יותר.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "שלח קוד", + "RESEND_CODE": "שלח קוד מחדש", + "RESEND_CODE_IN": "שלח קוד מחדש ב-", "JOIN": "הצטרף", "UNRECEIVED_CODE": "לא קיבלת קוד?", "JOIN_TEAM": "הצטרף לצוות", diff --git a/apps/web/messages/it.json b/apps/web/messages/it.json index 516d97e27..52e27d63a 100644 --- a/apps/web/messages/it.json +++ b/apps/web/messages/it.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Sincronizza le attività in modo selettivo associandole a un'etichetta specifica.", "GITHUB_AUTO_SYNC_LABEL": "Seleziona etichetta di sincronizzazione automatica", "THERE_IS_NO_TASK_ASSIGNED": "Non è stato assegnato alcun compito", - "NO_USERS_ONLINE": "Nessun utente online" + "NO_USERS_ONLINE": "Nessun utente online", + "NOT_FOUND": "Non Trovato", + "PAGE_NOT_FOUND": "Pagina non trovata", + "NO_USERS_WORKING": "Al momento non ci sono utenti attivi", + "NO_USERS_PAUSED_WORK": "Al momento non ci sono utenti che hanno interrotto il proprio lavoro", + "NO_USERS_IDLE": "Al momento non ci sono utenti inattivi" }, "alerts": { "REAL_TIME_ON_WORKING": "Stiamo lavorando alla sincronizzazione in tempo reale al momento, controlla questa funzionalità più tardi.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "Invia Codice", + "RESEND_CODE": "Reinvia Codice", + "RESEND_CODE_IN": "Invia nuovamente il codice", "JOIN": "Unisciti", "UNRECEIVED_CODE": "Non hai ricevuto il codice?", "JOIN_TEAM": "Unisciti al Team", diff --git a/apps/web/messages/nl.json b/apps/web/messages/nl.json index 46d4af706..382d25677 100644 --- a/apps/web/messages/nl.json +++ b/apps/web/messages/nl.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchroniseer taken selectief door ze te koppelen aan een specifiek label.", "GITHUB_AUTO_SYNC_LABEL": "Selecteer Auto-Sync-label", "THERE_IS_NO_TASK_ASSIGNED": "Er is geen taak toegewezen", - "NO_USERS_ONLINE": "Geen gebruiker online" + "NO_USERS_ONLINE": "Geen gebruiker online", + "NOT_FOUND": "Niet gevonden", + "PAGE_NOT_FOUND": "Pagina niet gevonden", + "NO_USERS_WORKING": "Er zijn momenteel geen actieve gebruikers", + "NO_USERS_PAUSED_WORK": "Er zijn momenteel geen gebruikers die hun werk hebben stopgezet", + "NO_USERS_IDLE": "Er zijn momenteel geen inactieve gebruikers" }, "alerts": { "REAL_TIME_ON_WORKING": "We werken momenteel aan Real-Time Sync. Bekijk deze functie later opnieuw.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "code verzenden", + "RESEND_CODE": "Code opnieuw verzenden", + "RESEND_CODE_IN": "Code opnieuw verzenden in", "JOIN": "Word lid", "UNRECEIVED_CODE": "Geen code ontvangen?", "JOIN_TEAM": "Word lid van team", diff --git a/apps/web/messages/pl.json b/apps/web/messages/pl.json index 42ed36052..e98839184 100644 --- a/apps/web/messages/pl.json +++ b/apps/web/messages/pl.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronizuj zadania selektywnie, łącząc je z konkretną etykietą.", "GITHUB_AUTO_SYNC_LABEL": "Wybierz etykietę automatycznej synchronizacji", "THERE_IS_NO_TASK_ASSIGNED": "Nie ma przypisanego zadania", - "NO_USERS_ONLINE": "Brak użytkowników online" + "NO_USERS_ONLINE": "Brak użytkowników online", + "NOT_FOUND": "Nie znaleziono", + "PAGE_NOT_FOUND": "Strona nie znaleziona", + "NO_USERS_WORKING": "Obecnie nie ma aktywnych użytkowników", + "NO_USERS_PAUSED_WORK": "Obecnie nie ma użytkowników, którzy przerwali pracę", + "NO_USERS_IDLE": "Obecnie nie ma nieaktywnych użytkowników" }, "alerts": { "REAL_TIME_ON_WORKING": "W tej chwili pracujemy nad synchronizacją w czasie rzeczywistym. Sprawdź tę funkcję później.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "wyślij kod", + "RESEND_CODE": "Wyślij ponownie kod", + "RESEND_CODE_IN": "Wyślij ponownie kod w", "JOIN": "Dołącz", "UNRECEIVED_CODE": "Nie otrzymałeś kodu?", "JOIN_TEAM": "Dołącz do Zespołu", diff --git a/apps/web/messages/pt.json b/apps/web/messages/pt.json index 8830aa48e..6b15fcc4d 100644 --- a/apps/web/messages/pt.json +++ b/apps/web/messages/pt.json @@ -183,8 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Sincronize tarefas seletivamente associando-as a uma etiqueta específica.", "GITHUB_AUTO_SYNC_LABEL": "Selecionar rótulo de sincronização automática", "THERE_IS_NO_TASK_ASSIGNED": "Não há tarefas atribuídas", - "NO_USERS_ONLINE": "Nenhum usuário online" - + "NO_USERS_ONLINE": "Nenhum usuário online", + "NO_USERS_WORKING": "Atualmente não há usuários ativos", + "NO_USERS_PAUSED_WORK": "Atualmente não há usuários que interromperam seu trabalho", + "NO_USERS_IDLE": "Atualmente não há usuários inativos", + "NOT_FOUND": "Não encontrado", + "PAGE_NOT_FOUND": "Página não encontrada" }, "alerts": { "REAL_TIME_ON_WORKING": "Estamos trabalhando na sincronização em tempo real no momento. Verifique esse recurso mais tarde.", @@ -244,6 +248,8 @@ }, "auth": { "SEND_CODE": "enviar código", + "RESEND_CODE": "Reenviar Código", + "RESEND_CODE_IN": "Reenviar Código em", "JOIN": "Participar", "UNRECEIVED_CODE": "Não recebeu o código?", "JOIN_TEAM": "Participar da Equipe", diff --git a/apps/web/messages/ru.json b/apps/web/messages/ru.json index 5e2591345..265dfa01c 100644 --- a/apps/web/messages/ru.json +++ b/apps/web/messages/ru.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Синхронизация задач выборочно путем ассоциирования их с конкретной меткой.", "GITHUB_AUTO_SYNC_LABEL": "Выберите метку автосинхронизации", "THERE_IS_NO_TASK_ASSIGNED": "НЕТ ЗАДАЧИ НАЗНАЧЕНО", - "NO_USERS_ONLINE": "Нет пользователей онлайн" + "NO_USERS_ONLINE": "Нет пользователей онлайн", + "NOT_FOUND": "Не найдено", + "PAGE_NOT_FOUND": "Страница не найдена", + "NO_USERS_WORKING": "На данный момент нет активных пользователей", + "NO_USERS_PAUSED_WORK": "На данный момент нет пользователей, прекративших свою работу", + "NO_USERS_IDLE": "На данный момент неактивных пользователей нет" }, "alerts": { "REAL_TIME_ON_WORKING": "В настоящее время мы работаем над синхронизацией в реальном времени, пожалуйста, проверьте эту функцию позже.", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "отправить код", + "RESEND_CODE": "Отправить код повторно", + "RESEND_CODE_IN": "Отправить код повторно через", "JOIN": "Присоединиться", "UNRECEIVED_CODE": "Не получили код?", "JOIN_TEAM": "Присоединиться к команде", diff --git a/apps/web/messages/zh.json b/apps/web/messages/zh.json index 57d90158f..7ec09d4c8 100644 --- a/apps/web/messages/zh.json +++ b/apps/web/messages/zh.json @@ -183,7 +183,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "通过将任务与特定标签关联,有选择地进行同步。", "GITHUB_AUTO_SYNC_LABEL": "选择自动同步标签", "THERE_IS_NO_TASK_ASSIGNED": "没有分配任务", - "NO_USERS_ONLINE": "没有在线用户" + "NO_USERS_ONLINE": "没有在线用户", + "NOT_FOUND": "未找到", + "PAGE_NOT_FOUND": "页面未找到", + "NO_USERS_WORKING": "目前没有活跃用户", + "NO_USERS_PAUSED_WORK": "目前还没有用户停止工作", + "NO_USERS_IDLE": "目前没有不活跃用户" }, "alerts": { "REAL_TIME_ON_WORKING": "我們目前正在開發即時同步功能,請稍後查看此功能。", @@ -243,6 +248,8 @@ }, "auth": { "SEND_CODE": "发送验证码", + "RESEND_CODE": "重新发送验证码", + "RESEND_CODE_IN": "重新发送验证码", "JOIN": "加入", "UNRECEIVED_CODE": "未收到验证码?", "JOIN_TEAM": "加入团队", diff --git a/apps/web/package.json b/apps/web/package.json index c3544b9ec..173c115ad 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,7 @@ "@opentelemetry/sdk-node": "^0.45.1", "@opentelemetry/semantic-conventions": "^1.18.1", "@popperjs/core": "^2.11.6", + "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-hover-card": "^1.0.6", diff --git a/apps/web/public/locales/ar/common.json b/apps/web/public/locales/ar/common.json index 3070f1388..e5e7b0885 100644 --- a/apps/web/public/locales/ar/common.json +++ b/apps/web/public/locales/ar/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "تزامن المهام بشكل انتقائي عن طريق ربطها بتصنيف معين.", "GITHUB_AUTO_SYNC_LABEL": "اختر تسمية التزامن التلقائي", "THERE_IS_NO_TASK_ASSIGNED": "لا توجد مهام معينة", - "NO_USERS_ONLINE": "لا يوجد مستخدمين متصلين" + "NO_USERS_ONLINE": "لا يوجد مستخدمين متصلين", + "NOT_FOUND": "لم يتم العثور", + "PAGE_NOT_FOUND": "الصفحة غير موجودة", + "NO_USERS_WORKING": "لا يوجد مستخدمين يعملون حاليا", + "NO_USERS_PAUSED_WORK": "لم يتم إيقاف أي مستخدم مؤقتًا عن العمل في الوقت الحالي", + "NO_USERS_IDLE": "لا يوجد مستخدمين خاملين في الوقت الحالي" }, "alerts": { "REAL_TIME_ON_WORKING": "نحن نعمل على المزامنة في الوقت الحقيقي في الوقت الحالي، يرجى التحقق من هذه الميزة لاحقًا.", @@ -238,6 +243,8 @@ "auth": { "SEND_CODE": "إرسال الرمز", + "RESEND_CODE": "إعادة إرسال الرمز", + "RESEND_CODE_IN": "إعادة إرسال الرمز في", "JOIN": "انضمام", "UNRECEIVED_CODE": "لم تتلقى الرمز؟", "JOIN_TEAM": "انضمام للفريق", diff --git a/apps/web/public/locales/bg/common.json b/apps/web/public/locales/bg/common.json index 9469988b6..94a697b57 100644 --- a/apps/web/public/locales/bg/common.json +++ b/apps/web/public/locales/bg/common.json @@ -174,7 +174,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Синхронизирайте задачите избирателно, като ги свържете с конкретен етикет.", "GITHUB_AUTO_SYNC_LABEL": "Изберете етикет за автоматична синхронизация", "THERE_IS_NO_TASK_ASSIGNED": "Няма възложени задачи", - "NO_USERS_ONLINE": "Няма потребители онлайн" + "NO_USERS_ONLINE": "Няма потребители онлайн", + "NOT_FOUND": "Не е намерено", + "PAGE_NOT_FOUND": "Страницата не е намерена", + "NO_USERS_WORKING":"В момента няма активни потребители", + "NO_USERS_PAUSED_WORK":"В момента няма потребители, които да са спрели работата си", + "NO_USERS_IDLE":"В момента няма неактивни потребители" }, "alerts": { "REAL_TIME_ON_WORKING": "В момента работим върху синхронизирането в реално време, моля, проверете тази функция по-късно.", @@ -239,6 +244,8 @@ "auth": { "SEND_CODE": "изпрати код", + "RESEND_CODE": "Изпрати код отново", + "RESEND_CODE_IN": "Изпрати код отново след", "JOIN": "Присъединяване", "UNRECEIVED_CODE": "Не получихте код?", "JOIN_TEAM": "Присъединяване към отбор", diff --git a/apps/web/public/locales/de/common.json b/apps/web/public/locales/de/common.json index 255187e43..405ce4612 100644 --- a/apps/web/public/locales/de/common.json +++ b/apps/web/public/locales/de/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronisieren Sie Aufgaben selektiv, indem Sie sie mit einem bestimmten Label verknüpfen.", "GITHUB_AUTO_SYNC_LABEL": "Auto-Sync-Label auswählen", "THERE_IS_NO_TASK_ASSIGNED": "Es ist keine Aufgabe zugewiesen", - "NO_USERS_ONLINE": "Es sind keine Benutzer online" + "NO_USERS_ONLINE": "Es sind keine Benutzer online", + "NOT_FOUND": "Nicht gefunden", + "PAGE_NOT_FOUND": "Seite nicht gefunden", + "NO_USERS_WORKING": "Derzeit gibt es keine aktiven Benutzer", + "NO_USERS_PAUSED_WORK": "Derzeit gibt es keine Benutzer, die ihre Arbeit eingestellt haben", + "NO_USERS_IDLE": "Derzeit gibt es keine inaktiven Benutzer" }, "alerts": { "REAL_TIME_ON_WORKING": "Wir arbeiten derzeit an der Echtzeitsynchronisierung. Bitte überprüfen Sie diese Funktion später.", @@ -238,6 +243,8 @@ "auth": { "SEND_CODE": "Code senden", + "RESEND_CODE": "Code erneut senden", + "RESEND_CODE_IN": "Code erneut einsenden", "JOIN": "Beitreten", "UNRECEIVED_CODE": "Keinen Code erhalten?", "JOIN_TEAM": "Team beitreten", diff --git a/apps/web/public/locales/en/common.json b/apps/web/public/locales/en/common.json index 26a28674a..aa16a41a2 100644 --- a/apps/web/public/locales/en/common.json +++ b/apps/web/public/locales/en/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronize tasks selectively by associating them with specific label.", "GITHUB_AUTO_SYNC_LABEL": "Select Auto-Sync Label", "THERE_IS_NO_TASK_ASSIGNED": "There is no task assigned", - "NO_USERS_ONLINE": "There are no users online" + "NO_USERS_ONLINE": "There are no users online", + "NOT_FOUND": "Not Found", + "PAGE_NOT_FOUND": "Page Not Found", + "NO_USERS_WORKING": "There are currently no active users", + "NO_USERS_PAUSED_WORK": "There are currently no users who have paused their work", + "NO_USERS_IDLE": "There are currently no inactive users" }, "alerts": { "REAL_TIME_ON_WORKING": "We are working on Real-Time Sync at the moment, please check on this feature later.", @@ -244,6 +249,8 @@ }, "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Resend Code", + "RESEND_CODE_IN": "Resend Code in", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/public/locales/es/common.json b/apps/web/public/locales/es/common.json index 5cf539159..6aca2a308 100644 --- a/apps/web/public/locales/es/common.json +++ b/apps/web/public/locales/es/common.json @@ -173,7 +173,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Sincronice tareas de manera selectiva asociándolas con una etiqueta específica.", "GITHUB_AUTO_SYNC_LABEL": "Seleccionar etiqueta de sincronización automática", "THERE_IS_NO_TASK_ASSIGNED": "No hay tareas asignadas", - "NO_USERS_ONLINE": "No hay usuarios en línea" + "NO_USERS_ONLINE": "No hay usuarios en línea", + "NOT_FOUND": "No encontrado", + "PAGE_NOT_FOUND": "Página no encontrada", + "NO_USERS_WORKING": "Actualmente no hay usuarios activos", + "NO_USERS_PAUSED_WORK": "Actualmente no hay usuarios que hayan dejado de trabajar", + "NO_USERS_IDLE": "Actualmente no hay usuarios inactivos" }, "alerts": { "REAL_TIME_ON_WORKING": "Estamos trabajando en la sincronización en tiempo real en este momento; verifique esta función más adelante.", @@ -230,6 +235,8 @@ }, "auth": { "SEND_CODE": "enviar código", + "RESEND_CODE": "Reenviar código", + "RESEND_CODE_IN": "Reenviar código en", "JOIN": "Unirse", "UNRECEIVED_CODE": "¿No recibiste el código?", "JOIN_TEAM": "Unirse al equipo", diff --git a/apps/web/public/locales/fr/common.json b/apps/web/public/locales/fr/common.json index 9ee1aeec2..88b4c7235 100644 --- a/apps/web/public/locales/fr/common.json +++ b/apps/web/public/locales/fr/common.json @@ -174,7 +174,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronisez sélectivement les tâches en les associant à une étiquette spécifique.", "GITHUB_AUTO_SYNC_LABEL": "Sélectionner l'étiquette de synchronisation automatique", "THERE_IS_NO_TASK_ASSIGNED": "Il n'y a pas de tâche assignée", - "NO_USERS_ONLINE": "Aucun utilisateur en ligne" + "NO_USERS_ONLINE": "Aucun utilisateur en ligne", + "NOT_FOUND": "Non trouvé", + "PAGE_NOT_FOUND": "Page non trouvée", + "NO_USERS_WORKING": "Il n'y a actuellement aucun utilisateur actif", + "NO_USERS_PAUSED_WORK": "Il n'y a actuellement aucun utilisateur qui a mis en pause son travail", + "NO_USERS_IDLE": "Il n'y a actuellement aucun utilisateur inactif" }, "alerts": { "REAL_TIME_ON_WORKING": "Nous travaillons actuellement sur la synchronisation en temps réel, veuillez vérifier cette fonctionnalité plus tard.", @@ -233,6 +238,8 @@ }, "auth": { "SEND_CODE": "envoyer le code", + "RESEND_CODE": "Renvoyer le code", + "RESEND_CODE_IN": "Renvoyer le code dans", "JOIN": "Joindre", "UNRECEIVED_CODE": "Vous n'avez pas reçu de code ?", "JOIN_TEAM": "Rejoindre l'équipe", diff --git a/apps/web/public/locales/he/common.json b/apps/web/public/locales/he/common.json index 638924063..726c08449 100644 --- a/apps/web/public/locales/he/common.json +++ b/apps/web/public/locales/he/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "סנכרן משימות באופן בררני על ידי הקשרתן לתג מסוים.", "GITHUB_AUTO_SYNC_LABEL": "בחר תגית אוטומטית לסנכרון", "THERE_IS_NO_TASK_ASSIGNED": "אין משימות מוקצות", - "NO_USERS_ONLINE": "אין משתמשים מחוברים" + "NO_USERS_ONLINE": "אין משתמשים מחוברים", + "NOT_FOUND": "לא נמצא", + "PAGE_NOT_FOUND": "הדף לא נמצא", + "NO_USERS_WORKING": "כרגע אין משתמשים פעילים", + "NO_USERS_PAUSED_WORK": "כרגע אין משתמשים שהפסיקו את עבודתם", + "NO_USERS_IDLE": "כרגע אין משתמשים לא פעילים" }, "alerts": { "REAL_TIME_ON_WORKING": "אנחנו עובדים על סנכרון בזמן אמת כרגע, אנא בדוק את התכונה הזו מאוחר יותר.", @@ -237,6 +242,8 @@ "auth": { "SEND_CODE": "שלח קוד", + "RESEND_CODE": "שלח קוד מחדש", + "RESEND_CODE_IN": "שלח קוד מחדש בעוד", "JOIN": "הצטרף", "UNRECEIVED_CODE": "לא קיבלת קוד?", "JOIN_TEAM": "הצטרף לצוות", diff --git a/apps/web/public/locales/it/common.json b/apps/web/public/locales/it/common.json index eda01700a..19301011c 100644 --- a/apps/web/public/locales/it/common.json +++ b/apps/web/public/locales/it/common.json @@ -178,7 +178,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Sincronizza le attività in modo selettivo associandole a un'etichetta specifica.", "GITHUB_AUTO_SYNC_LABEL": "Seleziona etichetta di sincronizzazione automatica", "THERE_IS_NO_TASK_ASSIGNED": "Non è stato assegnato alcun compito", - "NO_USERS_ONLINE": "Nessun utente online" + "NO_USERS_ONLINE": "Nessun utente online", + "NOT_FOUND": "Non trovato", + "PAGE_NOT_FOUND": "Pagina non trovata", + "NO_USERS_WORKING": "Al momento non ci sono utenti attivi", + "NO_USERS_PAUSED_WORK": "Al momento non ci sono utenti che hanno interrotto il proprio lavoro", + "NO_USERS_IDLE": "Al momento non ci sono utenti inattivi" }, "alerts": { "REAL_TIME_ON_WORKING": "Stiamo lavorando alla sincronizzazione in tempo reale al momento, controlla questa funzionalità più tardi.", @@ -240,6 +245,8 @@ "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Codice di rispedizione", + "RESEND_CODE_IN": "Invia nuovamente il codice", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/public/locales/nl/common.json b/apps/web/public/locales/nl/common.json index 95f13db7f..9c9af2d1f 100644 --- a/apps/web/public/locales/nl/common.json +++ b/apps/web/public/locales/nl/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchroniseer taken selectief door ze te koppelen aan een specifiek label.", "GITHUB_AUTO_SYNC_LABEL": "Selecteer Auto-Sync-label", "THERE_IS_NO_TASK_ASSIGNED": "Er is geen taak toegewezen", - "NO_USERS_ONLINE": "Er zijn geen gebruikers online" + "NO_USERS_ONLINE": "Er zijn geen gebruikers online", + "NOT_FOUND": "Niet gevonden", + "PAGE_NOT_FOUND": "Pagina niet gevonden", + "NO_USERS_WORKING": "Er zijn momenteel geen actieve gebruikers", + "NO_USERS_PAUSED_WORK": "Er zijn momenteel geen gebruikers die hun werk hebben stopgezet", + "NO_USERS_IDLE": "Er zijn momenteel geen inactieve gebruikers" }, "alerts": { "REAL_TIME_ON_WORKING": "We werken momenteel aan Real-Time Sync. Bekijk deze functie later opnieuw.", @@ -237,6 +242,8 @@ "auth": { "SEND_CODE": "code verzenden", + "RESEND_CODE": "Code opnieuw verzenden", + "RESEND_CODE_IN": "Code opnieuw verzenden in", "JOIN": "Word lid", "UNRECEIVED_CODE": "Geen code ontvangen?", "JOIN_TEAM": "Word lid van team", diff --git a/apps/web/public/locales/pl/common.json b/apps/web/public/locales/pl/common.json index 5c828065f..50ebe9ed2 100644 --- a/apps/web/public/locales/pl/common.json +++ b/apps/web/public/locales/pl/common.json @@ -178,7 +178,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Synchronizuj zadania selektywnie, łącząc je z konkretną etykietą.", "GITHUB_AUTO_SYNC_LABEL": "Wybierz etykietę automatycznej synchronizacji", "THERE_IS_NO_TASK_ASSIGNED": "Nie ma przypisanego zadania", - "NO_USERS_ONLINE": "Brak użytkowników online" + "NO_USERS_ONLINE": "Brak użytkowników online", + "NOT_FOUND": "Nie znaleziono", + "PAGE_NOT_FOUND": "Strona nie znaleziona", + "NO_USERS_WORKING": "Obecnie nie ma aktywnych użytkowników", + "NO_USERS_PAUSED_WORK": "Obecnie nie ma użytkowników, którzy przerwali pracę", + "NO_USERS_IDLE": "Obecnie nie ma nieaktywnych użytkowników" }, "alerts": { "REAL_TIME_ON_WORKING": "W tej chwili pracujemy nad synchronizacją w czasie rzeczywistym. Sprawdź tę funkcję później.", @@ -240,6 +245,8 @@ "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Wyślij kod ponownie", + "RESEND_CODE_IN": "Wyślij kod ponownie w", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/public/locales/pt/common.json b/apps/web/public/locales/pt/common.json index a2e2d144c..31f4280a8 100644 --- a/apps/web/public/locales/pt/common.json +++ b/apps/web/public/locales/pt/common.json @@ -178,7 +178,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Sincronize tarefas seletivamente associando-as a uma etiqueta específica.", "GITHUB_AUTO_SYNC_LABEL": "Selecionar rótulo de sincronização automática", "THERE_IS_NO_TASK_ASSIGNED": "Não há tarefa atribuída", - "NO_USERS_ONLINE": "Não há usuários online" + "NO_USERS_ONLINE": "Não há usuários online", + "NOT_FOUND": "Não encontrado", + "PAGE_NOT_FOUND": "Página não encontrada", + "NO_USERS_WORKING": "Atualmente não há usuários ativos", + "NO_USERS_PAUSED_WORK": "Atualmente não há usuários que interromperam seu trabalho", + "NO_USERS_IDLE": "Atualmente não há usuários inativos" }, "alerts": { "REAL_TIME_ON_WORKING": "Estamos trabalhando na sincronização em tempo real no momento. Verifique esse recurso mais tarde.", @@ -240,6 +245,8 @@ "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Reenviar código", + "RESEND_CODE_IN": "Reenviar código em", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/public/locales/ru/common.json b/apps/web/public/locales/ru/common.json index d4b0949ee..81ddeb1ff 100644 --- a/apps/web/public/locales/ru/common.json +++ b/apps/web/public/locales/ru/common.json @@ -177,7 +177,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "Синхронизация задач выборочно путем ассоциирования их с конкретной меткой.", "GITHUB_AUTO_SYNC_LABEL": "Выберите метку автосинхронизации", "THERE_IS_NO_TASK_ASSIGNED": "НЕТ ЗАДАЧИ НАЗНАЧЕНО", - "NO_USERS_ONLINE": "Пользователей онлайн нет" + "NO_USERS_ONLINE": "Пользователей онлайн нет", + "NOT_FOUND": "Не найдено", + "PAGE_NOT_FOUND": "Страница не найдена", + "NO_USERS_WORKING": "На данный момент нет активных пользователей", + "NO_USERS_PAUSED_WORK": "На данный момент нет пользователей, прекративших свою работу", + "NO_USERS_IDLE": "На данный момент неактивных пользователей нет" }, "alerts": { "REAL_TIME_ON_WORKING": "В настоящее время мы работаем над синхронизацией в реальном времени, пожалуйста, проверьте эту функцию позже.", @@ -239,6 +244,8 @@ "auth": { "SEND_CODE": "send code", + "RESEND_CODE": "Отправить код еще раз", + "RESEND_CODE_IN": "Повторно отправить код в", "JOIN": "Join", "UNRECEIVED_CODE": "Didn't receive code ?", "JOIN_TEAM": "Join Team", diff --git a/apps/web/public/locales/zh/common.json b/apps/web/public/locales/zh/common.json index 19ad537bc..946806801 100644 --- a/apps/web/public/locales/zh/common.json +++ b/apps/web/public/locales/zh/common.json @@ -176,7 +176,12 @@ "GITHUB_INTEGRATION_LABEL_SYNC_TASK_TEXT": "通过将任务与特定标签关联,有选择地进行同步。", "GITHUB_AUTO_SYNC_LABEL": "选择自动同步标签", "THERE_IS_NO_TASK_ASSIGNED": "没有分配任务", - "NO_USERS_ONLINE": "没有在线用户" + "NO_USERS_ONLINE": "没有在线用户", + "NOT_FOUND": "未找到", + "PAGE_NOT_FOUND": "页面未找到", + "NO_USERS_WORKING": "目前没有活跃用户", + "NO_USERS_PAUSED_WORK": "目前还没有用户停止工作", + "NO_USERS_IDLE": "目前没有不活跃用户" }, "alerts": { "REAL_TIME_ON_WORKING": "我們目前正在開發即時同步功能,請稍後查看此功能。", @@ -238,6 +243,8 @@ "auth": { "SEND_CODE": "发送验证码", + "RESEND_CODE": "重新发送验证码", + "ENTER_CODE": "输入验证码", "JOIN": "加入", "UNRECEIVED_CODE": "未收到验证码?", "JOIN_TEAM": "加入团队", diff --git a/yarn.lock b/yarn.lock index c4c5e04cc..d83c08d66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5086,6 +5086,22 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-accordion@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz#738441f7343e5142273cdef94d12054c3287966f" + integrity sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-collapsible" "1.0.3" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-arrow@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d" @@ -5105,6 +5121,21 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-collapsible@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz#df0e22e7a025439f13f62d4e4a9e92c4a0df5b81" + integrity sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-collection@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159" @@ -6312,16 +6343,16 @@ resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" integrity sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg== -"@types/cookie@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" - integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== - "@types/cookie@^0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.1.tgz#b29aa1f91a59f35e29ff8f7cb24faf1a3a750554" integrity sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g== +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/cookies@*": version "0.7.10" resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.10.tgz#c4881dca4dd913420c488508d192496c46eb4fd0" @@ -9712,19 +9743,19 @@ cookie@0.5.0, cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== -cookies-next@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cookies-next/-/cookies-next-4.1.0.tgz#1ecf2e4a65abe2ad3ffb3f4ad3d303ae008303c3" - integrity sha512-BREVc4TJT4NwXfyKjdjnYFXM6iRns+MYpCd34ClXuYqeisXnkPkbq7Ok9xaqi9mHmV6H2rwPE+p3EpMz4pF/kQ== +cookies-next@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/cookies-next/-/cookies-next-4.1.1.tgz#54498efe867bb5c1a47b5a99a7ea8563601c2413" + integrity sha512-20QaN0iQSz87Os0BhNg9M71eM++gylT3N5szTlhq2rK6QvXn1FYGPB4eAgU4qFTunbQKhD35zfQ95ZWgzUy3Cg== dependencies: - "@types/cookie" "^0.4.1" + "@types/cookie" "^0.6.0" "@types/node" "^16.10.2" - cookie "^0.4.0" + cookie "^0.6.0" copy-anything@^2.0.1: version "2.0.6" @@ -10911,9 +10942,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== ejs@^3.1.7: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" @@ -20336,7 +20367,16 @@ string-to-color@^2.2.2: lodash.words "^4.2.0" rgb-hex "^3.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -20418,7 +20458,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -22030,7 +22077,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -22048,6 +22095,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"