diff --git a/apps/web/app/api/auth/register/route.ts b/apps/web/app/api/auth/register/route.ts index 30aa95656..658fefb2b 100644 --- a/apps/web/app/api/auth/register/route.ts +++ b/apps/web/app/api/auth/register/route.ts @@ -62,6 +62,8 @@ export async function POST(req: Request) { const password = generateToken(8); const names = body.name.split(' '); + console.log('Random password: ', password); + // Register user const { data: user } = await registerUserRequest({ password: password, diff --git a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts index 91b76cd8b..ed9043051 100644 --- a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts +++ b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts @@ -156,7 +156,12 @@ export function useAuthenticationPasscode() { [queryCall] ); - const signInToWorkspaceRequest = (params: { email: string; token: string; selectedTeam: string; code: string }) => { + const signInToWorkspaceRequest = (params: { + email: string; + token: string; + selectedTeam: string; + code?: string; + }) => { signInWorkspaceQueryCall(params) .then(() => { setAuthenticated(true); diff --git a/apps/web/app/hooks/auth/useAuthenticationPassword.ts b/apps/web/app/hooks/auth/useAuthenticationPassword.ts index b553b24aa..5858556cb 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, code: '' }) + signInWorkspaceQueryCall({ email, token, selectedTeam }) .then(() => { setAuthenticated(true); router.push('/'); diff --git a/apps/web/app/hooks/features/useTaskEstimation.ts b/apps/web/app/hooks/features/useTaskEstimation.ts index b5ed39c7a..3e5c02724 100644 --- a/apps/web/app/hooks/features/useTaskEstimation.ts +++ b/apps/web/app/hooks/features/useTaskEstimation.ts @@ -124,6 +124,7 @@ export function useTaskEstimation(task?: Nullable) { if (updateLoading || !editableMode) return; handleSubmit(); }, [updateLoading, editableMode, handleSubmit]); + const { targetEl, ignoreElementRef } = useOutsideClick(handleOutsideClick); return { diff --git a/apps/web/app/services/client/api/auth.ts b/apps/web/app/services/client/api/auth.ts index 3758550c5..c47b5de84 100644 --- a/apps/web/app/services/client/api/auth.ts +++ b/apps/web/app/services/client/api/auth.ts @@ -130,7 +130,7 @@ export async function signInEmailConfirmAPI(email: string, code: string) { }); } -export const signInWorkspaceAPI = (params: { email: string; token: string; selectedTeam: string; code: string }) => { +export const signInWorkspaceAPI = (params: { email: string; token: string; selectedTeam: string; code?: string }) => { if (GAUZY_API_BASE_SERVER_URL.value) { return signInWorkspaceGauzy({ email: params.email, diff --git a/apps/web/app/services/client/api/auth/invite-accept.ts b/apps/web/app/services/client/api/auth/invite-accept.ts index f8d3a5894..c07e6a5ea 100644 --- a/apps/web/app/services/client/api/auth/invite-accept.ts +++ b/apps/web/app/services/client/api/auth/invite-accept.ts @@ -31,11 +31,7 @@ export function verifyInviteCodeAPI(params: IInviteVerifyCode) { * @param params - Parameters including tenantId, userId, and token for authentication. * @returns A promise that resolves to a pagination response of user organizations. */ -export function getUserOrganizationsRequest(params: { - tenantId: string; - userId: string; - token: string -}) { +export function getUserOrganizationsRequest(params: { tenantId: string; userId: string; token: string }) { // Create a new instance of URLSearchParams for query string construction const query = new URLSearchParams(); @@ -54,7 +50,7 @@ export function getUserOrganizationsRequest(params: { tenantId: params.tenantId, headers: { Authorization: `Bearer ${params.token}` - }, + } }); } @@ -221,11 +217,13 @@ export async function signInEmailConfirmGauzy(email: string, code: string) { /** * @param params */ -export async function signInWorkspaceGauzy(params: { email: string; token: string; teamId: string; code: string }) { - const loginResponse = await signInEmailCodeConfirmGauzy(params.email, params.code); +export async function signInWorkspaceGauzy(params: { email: string; token: string; teamId: string; code?: string }) { + if (params.code) { + const loginResponse = await signInEmailCodeConfirmGauzy(params.email, params.code); - if (loginResponse) { - return loginResponse; + if (loginResponse) { + return loginResponse; + } } const data = await signInWorkspaceAPI(params.email, params.token); diff --git a/apps/web/app/services/client/api/public-organization-team.ts b/apps/web/app/services/client/api/public-organization-team.ts index 5a7df3d2d..59026e546 100644 --- a/apps/web/app/services/client/api/public-organization-team.ts +++ b/apps/web/app/services/client/api/public-organization-team.ts @@ -11,7 +11,6 @@ export function getPublicOrganizationTeamsAPI(profile_link: string, team_id: str 'tasks.teams', 'tasks.tags', 'members', - // 'members.role', 'members.employee', 'members.employee.user' ]; diff --git a/apps/web/components/pages/task/title-block/task-title-block.tsx b/apps/web/components/pages/task/title-block/task-title-block.tsx index 6c2f9c2ec..3dd1a6958 100644 --- a/apps/web/components/pages/task/title-block/task-title-block.tsx +++ b/apps/web/components/pages/task/title-block/task-title-block.tsx @@ -3,7 +3,7 @@ import { ITeamTask } from '@app/interfaces'; import { detailedTaskState } from '@app/stores'; import { HoverCard, HoverCardContent, HoverCardTrigger } from '@components/ui/hover-card'; import { useToast } from '@components/ui/use-toast'; -import { Button, Tooltip } from 'lib/components'; +import { Button, CopyTooltip } from 'lib/components'; import { ActiveTaskIssuesDropdown } from 'lib/features'; import Image from 'next/image'; import { CheckSimpleIcon, CopyRoundIcon } from 'assets/svg'; @@ -30,7 +30,6 @@ const TaskTitleBlock = () => { //States const [edit, setEdit] = useState(false); - const [copied, setCopied] = useState(false); const [task] = useRecoilState(detailedTaskState); const [title, setTitle] = useState(''); @@ -97,16 +96,6 @@ const TaskTitleBlock = () => { titleDOM.current?.style.setProperty('height', titleDOM.current.scrollHeight + 'px'); }; - const copyTitle = () => { - navigator.clipboard.writeText(title); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - }; - - const copyTaskNumber = () => { - task && navigator.clipboard.writeText(task?.taskNumber); - }; - const handleTaskTitleChange = (event: ChangeEvent) => { setTitle(event.target.value); }; @@ -124,7 +113,7 @@ const TaskTitleBlock = () => { value={title} disabled={!edit} ref={titleDOM} - > + /> {edit ? (
@@ -144,7 +133,7 @@ const TaskTitleBlock = () => {
) : ( -
+
- + +
)}
@@ -223,13 +212,12 @@ const TaskTitleBlock = () => { - + + + ); diff --git a/apps/web/lib/components/copy-tooltip.tsx b/apps/web/lib/components/copy-tooltip.tsx new file mode 100644 index 000000000..dfbdb3b89 --- /dev/null +++ b/apps/web/lib/components/copy-tooltip.tsx @@ -0,0 +1,29 @@ +import { cn } from 'lib/utils'; +import { Tooltip } from './tooltip'; +import { useState, PropsWithChildren } from 'react'; + +type Props = { + text: string; + className?: string; + copiedTooltipText?: string; + defaultTooltipText?: string; +}; + +export function CopyTooltip(props: PropsWithChildren) { + const { copiedTooltipText = 'Copied', defaultTooltipText = 'Copy' } = props; + const [copied, setCopied] = useState(false); + + const copyTitle = () => { + navigator.clipboard.writeText(props.text); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + + return ( + +
+ {props.children} +
+
+ ); +} diff --git a/apps/web/lib/components/index.ts b/apps/web/lib/components/index.ts index 3493d483d..f5f1215e6 100644 --- a/apps/web/lib/components/index.ts +++ b/apps/web/lib/components/index.ts @@ -26,3 +26,4 @@ export * from './inputs/input'; export * from './inputs/auth-code-input'; export * from './services/recaptcha'; +export * from './copy-tooltip'; diff --git a/apps/web/lib/features/task/task-input.tsx b/apps/web/lib/features/task/task-input.tsx index 851b8857d..8fd027896 100644 --- a/apps/web/lib/features/task/task-input.tsx +++ b/apps/web/lib/features/task/task-input.tsx @@ -330,11 +330,9 @@ export function TaskInput(props: Props) { task={inputTask} forParentChildRelationship={true} taskStatusClassName={clsxm( - `${ - inputTask && inputTask.issueType === 'Bug' - ? '!px-[0.3312rem] py-[0.2875rem] rounded-sm' - : '!px-[0.375rem] py-[0.375rem] rounded-sm' - } `, + inputTask && inputTask.issueType === 'Bug' + ? '!px-[0.3312rem] py-[0.2875rem] rounded-sm' + : '!px-[0.375rem] py-[0.375rem] rounded-sm', 'border-none' )} /> @@ -425,6 +423,7 @@ function TaskCard({ forParentChildRelationship?: boolean; updatedTaskList?: ITeamTask[]; }) { + const [, setCount] = useState(0); const t = useTranslations(); const activeTaskEl = useRef(null); const { taskLabels: taskLabelsData } = useTaskLabels(); @@ -480,6 +479,7 @@ function TaskCard({ if (v && taskStatus) { taskStatus.current = v; } + setCount((c) => c + 1); }} defaultValue={taskStatus?.current as ITaskStatus} task={null} @@ -492,6 +492,7 @@ function TaskCard({ if (v && taskPriority) { taskPriority.current = v; } + setCount((c) => c + 1); }} defaultValue={taskPriority?.current as ITaskPriority} task={null} @@ -504,6 +505,7 @@ function TaskCard({ if (v && taskSize) { taskSize.current = v; } + setCount((c) => c + 1); }} defaultValue={taskSize?.current as ITaskSize} task={null} diff --git a/apps/web/lib/settings/team-setting-form.tsx b/apps/web/lib/settings/team-setting-form.tsx index 6a12e600e..8aa91f7eb 100644 --- a/apps/web/lib/settings/team-setting-form.tsx +++ b/apps/web/lib/settings/team-setting-form.tsx @@ -211,11 +211,11 @@ export const TeamSettingForm = () => { {/* Team Color */} -
+
{t('pages.settingsTeam.TEAM_COLOR')} -
+
{ diff --git a/apps/web/pages/api/auth/signin-workspace.ts b/apps/web/pages/api/auth/signin-workspace.ts index 4319ef6a4..2cf7a3691 100644 --- a/apps/web/pages/api/auth/signin-workspace.ts +++ b/apps/web/pages/api/auth/signin-workspace.ts @@ -22,8 +22,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) code: string; }; - let loginResponse: ILoginResponse | null = null; - const { errors, valid: formValid } = authFormValidate(['email'], body as any); if (!formValid) { @@ -31,100 +29,117 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Accept Invite Flow Start - /** - * Verify first if match with invite code - */ - const inviteReq = await verifyInviteCodeRequest({ - email: body.email, - code: body.code - }).catch(() => void 0); - - // General a random password with 8 chars - if (inviteReq && inviteReq.data.fullName) { - const password = generateToken(8); - - const names = inviteReq.data.fullName.split(' '); - const acceptInviteRes = await acceptInviteRequest({ - code: body.code, - email: body.email, - password: password, - user: { - firstName: names[0], - lastName: names[1] || '', - email: body.email - } - }).catch(() => void 0); - - if ( - !acceptInviteRes || - !acceptInviteRes.response.ok || - acceptInviteRes.response.status === 401 || - acceptInviteRes.response.status === 400 || - (acceptInviteRes?.data as any).response?.statusCode - ) { - return res.status(400).json({ - errors: { - email: 'Authentication code or email address invalid' - } - }); + const loginWithCode = async () => { + if (!body.code) { + return; } - loginResponse = acceptInviteRes.data; - if (!loginResponse) { - return res.status(400).json({ - errors: { - email: 'Authentication code or email address invalid' - } - }); - } - } - if (loginResponse) { - console.log('loginResponse>>>', loginResponse); + let loginResponse: ILoginResponse | null = null; /** - * Get the first team from first organization + * Verify first if match with invite code */ - const tenantId = loginResponse.user?.tenantId || ''; - const access_token = loginResponse.token; - const userId = loginResponse.user?.id; - - const { data: organizations } = await getUserOrganizationsRequest({ tenantId, userId }, access_token); - const organization = organizations?.items[0]; + const inviteReq = await verifyInviteCodeRequest({ + email: body.email, + code: body.code + }).catch(() => void 0); - if (!organization) { - return res.status(400).json({ - errors: { - email: 'Your account is not yet ready to be used on the Ever Teams Platform' + // General a random password with 8 chars + if (inviteReq && inviteReq.data.fullName) { + const password = generateToken(8); + + const names = inviteReq.data.fullName.split(' '); + const acceptInviteRes = await acceptInviteRequest({ + code: body.code, + email: body.email, + password: password, + user: { + firstName: names[0], + lastName: names[1] || '', + email: body.email } - }); - } - const { data: teams } = await getAllOrganizationTeamRequest( - { tenantId, organizationId: organization.organizationId }, - access_token - ); - - const team = teams.items[0]; - if (!team) { - setNoTeamPopupShowCookie(true); + }).catch(() => void 0); + + if ( + !acceptInviteRes || + !acceptInviteRes.response.ok || + acceptInviteRes.response.status === 401 || + acceptInviteRes.response.status === 400 || + (acceptInviteRes?.data as any).response?.statusCode + ) { + return res.status(400).json({ + errors: { + email: 'Authentication code or email address invalid' + } + }); + } + loginResponse = acceptInviteRes.data; + + if (!loginResponse) { + return res.status(400).json({ + errors: { + email: 'Authentication code or email address invalid' + } + }); + } } - setAuthCookies( - { - access_token: loginResponse.token, - refresh_token: { - token: loginResponse.refresh_token + if (loginResponse) { + console.log('loginResponse>>>', loginResponse); + + /** + * Get the first team from first organization + */ + const tenantId = loginResponse.user?.tenantId || ''; + const access_token = loginResponse.token; + const userId = loginResponse.user?.id; + + const { data: organizations } = await getUserOrganizationsRequest({ tenantId, userId }, access_token); + const organization = organizations?.items[0]; + + if (!organization) { + return res.status(400).json({ + errors: { + email: 'Your account is not yet ready to be used on the Ever Teams Platform' + } + }); + } + const { data: teams } = await getAllOrganizationTeamRequest( + { tenantId, organizationId: organization.organizationId }, + access_token + ); + + const team = teams.items[0]; + if (!team) { + setNoTeamPopupShowCookie(true); + } + + setAuthCookies( + { + access_token: loginResponse.token, + refresh_token: { + token: loginResponse.refresh_token + }, + teamId: team?.id, + tenantId, + organizationId: organization?.organizationId, + languageId: 'en', // TODO: not sure what should be here + noTeamPopup: true, + userId }, - teamId: team?.id, - tenantId, - organizationId: organization?.organizationId, - languageId: 'en', // TODO: not sure what should be here - noTeamPopup: true, - userId - }, - { req, res } - ); + { req, res } + ); + + return res.status(200).json({ team, loginResponse }); + } + + return null; + }; + + const loginCode = await loginWithCode(); - return res.status(200).json({ team, loginResponse }); + if (loginCode) { + return loginCode; } // Accept Invite Flow End