diff --git a/apps/mobile/yarn.lock b/apps/mobile/yarn.lock index 3963b6634..196734dc3 100644 --- a/apps/mobile/yarn.lock +++ b/apps/mobile/yarn.lock @@ -4014,11 +4014,11 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -7162,7 +7162,7 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.0.0, follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.4.1: +follow-redirects@^1.0.0, follow-redirects@^1.14.9, follow-redirects@^1.15.6, follow-redirects@^1.4.1: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index 4887069ae..cd861d5ac 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -13,6 +13,7 @@ import { useRouter } from 'next/navigation'; import { Dispatch, FormEvent, FormEventHandler, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'; import stc from 'string-to-color'; +import { ScrollArea, ScrollBar } from '@components/ui/scroll-bar'; function AuthPasscode() { const form = useAuthenticationPasscode(); @@ -173,8 +174,8 @@ function PasscodeScreen({ form, className }: { form: TAuthenticationPasscode } & form.errors['code'] || form.errors['email'] ? 'error' : form.authenticated - ? 'success' - : undefined + ? 'success' + : undefined } autoFocus={form.authScreen.screen === 'passcode'} /> @@ -317,83 +318,84 @@ export function WorkSpaceComponent(props: IWorkSpace) { {t('pages.auth.SELECT_WORKSPACE')} - -
- {props.workspaces?.map((worksace, index) => ( -
-
-
- {worksace.user.tenant.name} - { - props.setSelectedWorkspace(index); - if ( - props.selectedTeam && - !worksace.current_teams - ?.map((team) => team.team_id) - .includes(props.selectedTeam) - ) { - props.setSelectedTeam(worksace.current_teams[0].team_id); - } - }} - > - {props.selectedWorkspace === index ? ( - - ) : ( - - )} - -
- - {/*
*/} -
- {worksace.current_teams?.map((team) => ( -
+
+ {props.workspaces?.map((worksace, index) => ( +
+
+
+ {worksace.user.tenant.name} + { + props.setSelectedWorkspace(index); + if ( + props.selectedTeam && + !worksace.current_teams + ?.map((team) => team.team_id) + .includes(props.selectedTeam) + ) { + props.setSelectedTeam(worksace.current_teams[0].team_id); + } + }} > - - -
- - {team.team_name} - - ({team.team_member_count}) -
-
- { - props.setSelectedTeam(team.team_id); - if (props.selectedWorkspace !== index) { - props.setSelectedWorkspace(index); - } - }} + {props.selectedWorkspace === index ? ( + + ) : ( + + )} + +
+ + {/*
*/} +
+ {worksace.current_teams?.map((team) => ( +
- {props.selectedTeam === team.team_id ? ( - - ) : ( - - )} - -
- ))} + + +
+ + {team.team_name} + + ({team.team_member_count}) +
+
+ { + props.setSelectedTeam(team.team_id); + if (props.selectedWorkspace !== index) { + props.setSelectedWorkspace(index); + } + }} + > + {props.selectedTeam === team.team_id ? ( + + ) : ( + + )} + +
+ ))} +
-
- ))} -
- + ))} +
+ +
diff --git a/apps/web/app/[locale]/kanban/page.tsx b/apps/web/app/[locale]/kanban/page.tsx index 7aa669af1..bf5b3fea2 100644 --- a/apps/web/app/[locale]/kanban/page.tsx +++ b/apps/web/app/[locale]/kanban/page.tsx @@ -32,6 +32,7 @@ import { useRecoilValue } from 'recoil'; import { fullWidthState } from '@app/stores/fullWidth'; import { CircleIcon } from 'lucide-react'; import { XMarkIcon } from '@heroicons/react/20/solid'; +import Head from 'next/head'; const Kanban = () => { const { @@ -85,6 +86,11 @@ const Kanban = () => { }); return ( <> + + + {t('common.KANBAN')} {t('common.BOARD')} + + { if (!locales.includes(locale as any)) notFound(); const router = useRouter(); const pathname = usePathname(); + const searchParams = useSearchParams(); const { isApiWork, loading } = useCheckAPI(); // Enable static rendering // unstable_setRequestLocale(locale); + const formatTitle = (url: string) => { + // Separate the URL into pathname and query parts + const [pathname, queryString] = url.split('?'); + + // Ignore language codes or any initial two-letter or specific codes like 'ru', 'ur' + const segments = pathname + .split('/') + .filter((seg) => seg && seg.length > 2) + .map((seg) => { + // Replace dashes with spaces in the segment if it looks like a UUID or has digits (likely an ID) + if (seg.includes('-') || /\d/.test(seg)) { + return ''; // Exclude IDs from title + } + return seg.charAt(0).toUpperCase() + seg.slice(1).toLowerCase(); // Capitalize non-ID segments + }) + .filter((seg: string) => seg); // Remove empty strings resulting from ID exclusion + + // Process query parameters, specifically looking for 'name' + let namePart = ''; + if (queryString) { + const params = new URLSearchParams(queryString); + if (params?.get('name')) { + const name = params.get('name') ?? ''; + const nameValue = name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); + namePart = nameValue; + } + } + + // Combine the pathname segments with the name part, if present + const title = [...segments, namePart].filter((part) => part).join(' | '); + + return title; + }; + + const name = searchParams?.get('name'); // eslint-disable-next-line @typescript-eslint/no-var-requires const messages = require(`../../messages/${locale}.json`); @@ -69,6 +104,9 @@ const LocaleLayout = ({ children, params: { locale }, pageProps }: Props) => { }, [isApiWork, loading, router, pathname]); return ( + + {formatTitle(`${pathname}${name ? `?name=${name}` : ''}`) || 'Home'} + {/* diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx index ba39e11d6..5f8cd7d97 100644 --- a/apps/web/app/[locale]/profile/[memberId]/page.tsx +++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx @@ -32,7 +32,6 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId const fullWidth = useRecoilValue(fullWidthState); const [activityFilter, setActivityFilter] = useState('Tasks'); const setActivityTypeFilter = useSetRecoilState(activityTypeState); - const hook = useTaskFilter(profile); const isManagerConnectedUser = activeTeamManagers.findIndex((member) => member.employee?.user?.id == user?.id); @@ -69,6 +68,8 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId // eslint-disable-next-line react-hooks/exhaustive-deps }, [profile.member]); + // Example usage + return ( <> diff --git a/apps/web/app/hooks/features/useTaskInput.ts b/apps/web/app/hooks/features/useTaskInput.ts index 134168405..5bc027d80 100644 --- a/apps/web/app/hooks/features/useTaskInput.ts +++ b/apps/web/app/hooks/features/useTaskInput.ts @@ -1,6 +1,6 @@ 'use client'; -import { useAuthenticateUser, useModal, useSyncRef } from '@app/hooks'; +import { useAuthenticateUser, useModal, useSyncRef, useTaskStatus } from '@app/hooks'; import { useTeamTasks } from '@app/hooks/features/useTeamTasks'; import { ITaskLabelsItemList, Nullable } from '@app/interfaces'; import { ITaskStatus, ITeamTask } from '@app/interfaces/ITask'; @@ -36,7 +36,7 @@ export function useTaskInput({ } = {}) { const { isOpen: isModalOpen, openModal, closeModal } = useModal(); const [closeableTask, setCloseableTaskTask] = useState(null); - + const { taskStatus: taskStatusList } = useTaskStatus(); const { tasks: teamTasks, activeTeamTask, @@ -140,11 +140,13 @@ export function useTaskInput({ }[]; } = {}) => { if (query.trim().length < 2 || inputTask?.title === query.trim() || !userRef.current?.isEmailVerified) return; - + const openId = taskStatusList.find((item) => item.value === 'open')?.id; + const statusId = taskStatusList.find((item) => item.name === taskStatus.current)?.id; return createTask( { taskName: query.trim(), issueType: taskIssue.current || 'Bug', + taskStatusId: statusId || openId as string, status: taskStatus.current || undefined, priority: taskPriority.current || undefined, size: taskSize.current || undefined, diff --git a/apps/web/app/hooks/features/useTeamTasks.ts b/apps/web/app/hooks/features/useTeamTasks.ts index 8097779ab..e8dace649 100644 --- a/apps/web/app/hooks/features/useTeamTasks.ts +++ b/apps/web/app/hooks/features/useTeamTasks.ts @@ -215,6 +215,7 @@ export function useTeamTasks() { { taskName, issueType, + taskStatusId, status = taskStatus[0]?.name, priority, size, @@ -224,6 +225,7 @@ export function useTeamTasks() { taskName: string; issueType?: string; status?: string; + taskStatusId: string; priority?: string; size?: string; tags?: ITaskLabelsItemList[]; @@ -231,13 +233,11 @@ export function useTeamTasks() { }, members?: { id: string }[] ) => { - const activeStatus = taskStatus.find((ts) => ts.name == status); return createQueryCall( { title: taskName, issueType, status, - taskStatusId: activeStatus?.id, priority, size, tags, @@ -249,7 +249,8 @@ export function useTeamTasks() { } : {}), ...(description ? { description: `

${description}

` } : {}), - ...(members ? { members } : {}) + ...(members ? { members } : {}), + taskStatusId: taskStatusId, }, $user.current ).then((res) => { diff --git a/apps/web/components/shared/collaborate/index.tsx b/apps/web/components/shared/collaborate/index.tsx index 8c4e08ee7..e760935ba 100644 --- a/apps/web/components/shared/collaborate/index.tsx +++ b/apps/web/components/shared/collaborate/index.tsx @@ -21,6 +21,7 @@ import stc from 'string-to-color'; import { JitsuAnalytics } from '../../../lib/components/services/jitsu-analytics'; import { useTranslations } from 'next-intl'; import { BrushSquareIcon, PhoneUpArrowIcon, UserLinearIcon } from 'assets/svg'; +import { ScrollArea } from '@components/ui/scroll-bar'; const Collaborate = () => { const { onMeetClick, onBoardClick, collaborativeMembers, setCollaborativeMembers } = useCollaborative(); @@ -89,58 +90,64 @@ const Collaborate = () => { {t('common.USER_NOT_FOUND')} - - {members.map((member) => ( - { - handleMemberClick(member); - }} - > -
+ + {members.map((member) => ( + { + handleMemberClick(member); }} > - {(member?.image?.thumbUrl || member?.image?.fullUrl || member?.imageUrl) && - isValidUrl( - member?.image?.thumbUrl || member?.image?.fullUrl || member?.imageUrl - ) ? ( - + {(member?.image?.thumbUrl || + member?.image?.fullUrl || + member?.imageUrl) && + isValidUrl( + member?.image?.thumbUrl || member?.image?.fullUrl || member?.imageUrl - } - alt="Team Avatar" - imageTitle={member?.name || ''} - > - ) : member?.name ? ( - imgTitle(member?.name || ' ').charAt(0) - ) : ( - '' - )} -
+ ) ? ( + + ) : member?.name ? ( + imgTitle(member?.name || ' ').charAt(0) + ) : ( + '' + )} +
-
-

{member?.name}

-

{member?.email}

-
- {selectedMemberIds.includes(member?.id) ? ( - - ) : null} - - ))} - +
+

{member?.name}

+

{member?.email}

+
+ {selectedMemberIds.includes(member?.id) ? ( + + ) : null} + + ))} + + diff --git a/apps/web/components/ui/scroll-bar.tsx b/apps/web/components/ui/scroll-bar.tsx new file mode 100644 index 000000000..b94c3e717 --- /dev/null +++ b/apps/web/components/ui/scroll-bar.tsx @@ -0,0 +1,42 @@ +'use client'; + +import * as React from 'react'; +import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'; +import { clsxm } from '@app/utils'; + + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = 'vertical', ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export { ScrollArea, ScrollBar }; diff --git a/apps/web/lib/components/image-overlapper.tsx b/apps/web/lib/components/image-overlapper.tsx index 75e51c9d6..47b772154 100644 --- a/apps/web/lib/components/image-overlapper.tsx +++ b/apps/web/lib/components/image-overlapper.tsx @@ -3,6 +3,7 @@ import Image from 'next/image'; import Link from 'next/link'; import Skeleton from 'react-loading-skeleton'; import { Tooltip } from './tooltip'; +import { ScrollArea } from '@components/ui/scroll-bar'; export interface ImageOverlapperProps { id: string; url: string; @@ -37,7 +38,7 @@ export default function ImageOverlapper({ className="relative " > {firstArray.map((image, index) => ( - +
- -
- {secondArray.map((image: ImageOverlapperProps, index: number) => { - return ( - + +
+ {secondArray.map((image: ImageOverlapperProps, index: number) => { + return ( + @@ -92,9 +94,10 @@ export default function ImageOverlapper({

{image.alt}

- ); - })} -
+ ); + })} +
+ )} diff --git a/apps/web/lib/features/task/task-item.tsx b/apps/web/lib/features/task/task-item.tsx index a0f735665..0812df4ca 100644 --- a/apps/web/lib/features/task/task-item.tsx +++ b/apps/web/lib/features/task/task-item.tsx @@ -129,7 +129,7 @@ export function TaskAvatars({ task, limit = 2 }: { task: ITeamTask; limit?: numb const size = 30; return ( - +
{t('common.MY_TASKS')} diff --git a/apps/web/lib/settings/left-side-setting-menu.tsx b/apps/web/lib/settings/left-side-setting-menu.tsx index 55d9e164c..83944101e 100644 --- a/apps/web/lib/settings/left-side-setting-menu.tsx +++ b/apps/web/lib/settings/left-side-setting-menu.tsx @@ -11,6 +11,7 @@ import { useTranslations } from 'next-intl'; import { useRecoilState } from 'recoil'; import Link from 'next/link'; import { clsxm } from '@app/utils'; +import { ScrollArea, ScrollBar } from '@components/ui/scroll-bar'; export const LeftSideSettingMenu = ({ className }: { className?: string }) => { const t = useTranslations(); @@ -69,30 +70,31 @@ export const LeftSideSettingMenu = ({ className }: { className?: string }) => { {t('common.SETTINGS')} -
- - {activePage === '/settings/personal' ? ( - - ) : ( - - )} - {t('common.PERSONAL')} - - } - className="bg-transparent" - textClassName={` + +
+ + {activePage === '/settings/personal' ? ( + + ) : ( + + )} + {t('common.PERSONAL')} + + } + className="bg-transparent" + textClassName={` ${ activePage === '/settings/personal' ? `text-[#3826a6] font-semibold` : 'border-l-transparent font-normal dark:text-[#7E7991]' } `} - wrapperClassName={`w-full border-t-0 border-r-0 border-b-0 rounded-none + wrapperClassName={`w-full border-t-0 border-r-0 border-b-0 rounded-none font-normal text-[#7e7991] justify-start pt-[24px] pb-[24px] pl-[24px] border-l-[5px] ${ activePage === '/settings/personal' @@ -100,42 +102,46 @@ export const LeftSideSettingMenu = ({ className }: { className?: string }) => { : 'border-l-transparent' } `} - > -
- {PersonalAccordianData.map((ad, index) => { - return ( - - +
+ {PersonalAccordianData.map((ad, index) => { + return ( + - {ad.title} - - - ); - })} -
- + + {ad.title} + + + ); + })} +
+
- - {activePage === '/settings/team' ? ( - - ) : ( - - )} - {t('common.TEAM')} - - } - className="bg-[transparent]" - textClassName={`${ - activePage === '/settings/team' - ? ' text-[#3826a6] text-primary font-semibold' - : ' border-l-transparent font-normal dark:text-[#7E7991]' - }`} - wrapperClassName={`w-full border-t-0 border-r-0 border-b-0 rounded-none + + {activePage === '/settings/team' ? ( + + ) : ( + + )} + {t('common.TEAM')} + + } + className="bg-[transparent]" + textClassName={`${ + activePage === '/settings/team' + ? ' text-[#3826a6] text-primary font-semibold' + : ' border-l-transparent font-normal dark:text-[#7E7991]' + }`} + wrapperClassName={`w-full border-t-0 border-r-0 border-b-0 rounded-none font-normal text-[#7e7991] justify-start text-sm pt-[24px] pb-[24px] pl-[24px] border-l-[5px] ${ activePage === '/settings/team' @@ -143,26 +149,32 @@ export const LeftSideSettingMenu = ({ className }: { className?: string }) => { : ' border-l-transparent' } `} - > -
- {TeamAccordianData.filter((ad) => (!isTeamManager && !ad.managerOnly) || isTeamManager).map( - (ad, index) => { - return ( - - +
+ {TeamAccordianData.filter((ad) => (!isTeamManager && !ad.managerOnly) || isTeamManager).map( + (ad, index) => { + return ( + - {ad.title} - - - ); - } - )} -
- -
+ + {ad.title} + + + ); + } + )} +
+
+
+ +
); }; diff --git a/apps/web/package.json b/apps/web/package.json index 4908ee9bb..c079b71ef 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -35,6 +35,7 @@ "@radix-ui/react-hover-card": "^1.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.0.6", + "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.4", diff --git a/yarn.lock b/yarn.lock index bf7d3fd88..c4c5e04cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5424,6 +5424,22 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-scroll-area@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.5.tgz#01160c6893f24a2ddb5aa399ae5b3ba84ad4d3cc" + integrity sha512-b6PAgH4GQf9QEn8zbT2XUHpW5z8BzqEc7Kl11TwDrvuTrxlkcjTD5qa/bxgKr+nmuXKu4L/W5UZ4mlP/VG/5Gw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/number" "1.0.1" + "@radix-ui/primitive" "1.0.1" + "@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-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-select@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.0.0.tgz#a3511792a51a7018d6559357323a7f52e0e38887" @@ -7879,16 +7895,7 @@ axios@1.1.3: form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@^1.0.0, axios@^1.1.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" - integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -axios@^1.6.0: +axios@^1.0.0, axios@^1.1.2, axios@^1.6.0: version "1.6.8" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==