diff --git a/.github/CONTRIBUTING.MD b/.github/CONTRIBUTING.MD new file mode 100644 index 000000000..d49cbe3a6 --- /dev/null +++ b/.github/CONTRIBUTING.MD @@ -0,0 +1,31 @@ +# Contributing to Ever Teams Platform + +We would love for you to contribute to Ever Teams Platform! + +## Submitting Pull Requests + +If you're changing the structure of the repository please create an issue first. + +By default, when you submitting contributions with Pull Requests, we require electronic submission of individual [Contributor Assignment Agreement (CAA)](https://gist.github.com/evereq/95f74ae09510766ffa9379006715ccfd). In some cases (when contributions are very small, at our discretion) instead of CAA we may accept submission of individual [Contributor License Agreement (CLA)](https://gist.github.com/evereq/53ddec283243481344fb61df1706ec40). + +If you submitting contribution on behalf of some legal entity, you need to submit Entity Contributor Assignment Agreement (CAA) or Entity Contributor License Agreement (CLA), which you can request by sending us an email to . + +We are using open-source [CLA assistant](https://github.com/cla-assistant/cla-assistant) project to collect your signatures on CAA. +The templates for our CAA/CLA documents generated by . + +## Submitting bug reports + +Make sure you are on latest changes. +If you can, please provide more information about your environment such as browser, operating system, node version, and yarn version. + +## Feature requests + +You are more than welcome to submit future requests here + +## Legal + +This is an open source project. +Contributions you make to this public Gauzy Platform repository are completely voluntary. +When you submit an issue, bug report, question, enhancement, pull request, etc., you are offering your contribution without expectation of payment, you expressly waive any future pay claims against the Ever Co. LTD related to your contribution, and you acknowledge that this does not create an obligation on the part of the Ever Co. LTD of any kind. Furthermore, your contributing to this project does not create an employer-employee relationship between the Ever Co. LTD and the contributor. + +See also "Submitting Pull Requests" section above for more information on CAA/CLA, required for any contributions. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..d1a534fd7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +## Description + +Please include a summary of the changes and the related issue. + +## Type of Change + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation update + +## Checklist + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings + +## Previous screenshots + +Please add here videos or images of previous status + +## Current screenshots + +Please add here videos or images of previous status diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index 598e61876..7ebb81c22 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -78,8 +78,7 @@ function AuthPasscode() { {form.authScreen.screen === 'workspace' && ( )} - - {/* Social logins */} + @@ -325,6 +324,8 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } [selectedWorkspace, selectedTeam, form] ); + const lastSelectedTeamFromAPI = form.getLastTeamIdWithRecentLogout(); + useEffect(() => { if (form.workspaces.length === 1) { setSelectedWorkspace(0); @@ -335,7 +336,11 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } if (form.workspaces.length === 1 && currentTeams?.length === 1) { setSelectedTeam(currentTeams[0].team_id); } else { - const lastSelectedTeam = window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || currentTeams[0]?.team_id; + const lastSelectedTeam = + window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || + lastSelectedTeamFromAPI || + form.defaultTeamId || + currentTeams[0]?.team_id; const lastSelectedWorkspace = form.workspaces.findIndex((workspace) => workspace.current_teams.find((team) => team.team_id === lastSelectedTeam) @@ -349,7 +354,7 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } document.getElementById('continue-to-workspace')?.click(); }, 100); } - }, [form.workspaces]); + }, [form.defaultTeamId, form.workspaces, lastSelectedTeamFromAPI]); useEffect(() => { if (form.authScreen.screen === 'workspace') { @@ -423,8 +428,9 @@ export function WorkSpaceComponent(props: IWorkSpace) { {props.workspaces?.map((worksace, index) => (
diff --git a/apps/web/app/[locale]/auth/password/component.tsx b/apps/web/app/[locale]/auth/password/component.tsx index 055a8dc0d..dec387ab3 100644 --- a/apps/web/app/[locale]/auth/password/component.tsx +++ b/apps/web/app/[locale]/auth/password/component.tsx @@ -116,6 +116,8 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } [selectedWorkspace, selectedTeam, form] ); + const lastSelectedTeamFromAPI = form.getLastTeamIdWithRecentLogout(); + const hasMultipleTeams = useMemo( () => form.workspaces.some((workspace) => workspace.current_teams.length > 1), [form.workspaces] @@ -129,7 +131,8 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } } const currentTeams = form.workspaces.find((el) => el.current_teams && el.current_teams.length)?.current_teams; - const lastSelectedTeamId = window.localStorage.getItem(LAST_WORSPACE_AND_TEAM); + const lastSelectedTeamId = + window.localStorage.getItem(LAST_WORSPACE_AND_TEAM) || lastSelectedTeamFromAPI || form.defaultTeamId; if (currentTeams) { setSelectedWorkspace( diff --git a/apps/web/app/[locale]/integration/github/component.tsx b/apps/web/app/[locale]/integration/github/component.tsx index 666fa38db..e8d9b8d0c 100644 --- a/apps/web/app/[locale]/integration/github/component.tsx +++ b/apps/web/app/[locale]/integration/github/component.tsx @@ -13,6 +13,7 @@ const GitHub = () => { const searchParams = useSearchParams(); const installation_id = searchParams?.get('installation_id'); const setup_action = searchParams?.get('setup_action'); + const initialLoad = useRef(false); const t = useTranslations(); @@ -49,7 +50,9 @@ const GitHub = () => { }, [integrationTenantLoading, integrationTenant, getRepositories]); useEffect(() => { - if (!loadingIntegrationTypes && integrationTypes.length === 0) { + if (!loadingIntegrationTypes && integrationTypes.length === 0 && !initialLoad.current) { + initialLoad.current = true; + getIntegrationTypes().then((types) => { const allIntegrations = types.find((item: any) => item.name === 'All Integrations'); if (allIntegrations && allIntegrations?.id) { @@ -57,7 +60,7 @@ const GitHub = () => { } }); } - }, [loadingIntegrationTypes, integrationTypes, getIntegrationTypes, getIntegrationTenant]); + }, [loadingIntegrationTypes, integrationTypes, getIntegrationTypes, getIntegrationTenant, initialLoad]); return (
diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx index 03bc54d81..2c6c76b80 100644 --- a/apps/web/app/[locale]/page-component.tsx +++ b/apps/web/app/[locale]/page-component.tsx @@ -55,7 +55,7 @@ function MainPage() { React.useEffect(() => { window && window?.localStorage.getItem('conf-fullWidth-mode'); setFullWidth(JSON.parse(window?.localStorage.getItem('conf-fullWidth-mode') || 'true')); - }, [fullWidth, setFullWidth]); + }, [setFullWidth]); if (!online) { return ; diff --git a/apps/web/app/api/auth/_signin-workspace/route.ts b/apps/web/app/api/auth/_signin-workspace/route.ts index f60af0416..81d33b022 100644 --- a/apps/web/app/api/auth/_signin-workspace/route.ts +++ b/apps/web/app/api/auth/_signin-workspace/route.ts @@ -7,7 +7,7 @@ import { verifyInviteCodeRequest } from '@app/services/server/requests'; import { generateToken, setAuthCookies, setNoTeamPopupShowCookie } from '@app/helpers'; -import { ILoginResponse } from '@app/interfaces'; +import { ILoginResponse, IOrganizationTeam } from '@app/interfaces'; import { NextResponse } from 'next/server'; export async function POST(req: Request) { @@ -19,6 +19,8 @@ export async function POST(req: Request) { token: string; teamId: string; code: string; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId?: IOrganizationTeam['id']; }; let loginResponse: ILoginResponse | null = null; @@ -134,8 +136,8 @@ export async function POST(req: Request) { return NextResponse.json({ team, loginResponse }); } // Accept Invite Flow End - - const { data } = await signInWorkspaceRequest(body.email, body.token); + const { email, token, defaultTeamId, lastTeamId } = body; + const { data } = await signInWorkspaceRequest({ email, token, defaultTeamId, lastTeamId }); /** * Get the first team from first organization diff --git a/apps/web/app/api/auth/signin-workspace/signin-workspace.ts b/apps/web/app/api/auth/signin-workspace/signin-workspace.ts index 2cf7a3691..2a77bcadb 100644 --- a/apps/web/app/api/auth/signin-workspace/signin-workspace.ts +++ b/apps/web/app/api/auth/signin-workspace/signin-workspace.ts @@ -8,7 +8,7 @@ import { verifyInviteCodeRequest } from '@app/services/server/requests'; import { generateToken, setAuthCookies, setNoTeamPopupShowCookie } from '@app/helpers'; -import { ILoginResponse } from '@app/interfaces'; +import { ILoginResponse, IOrganizationTeam } from '@app/interfaces'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { @@ -20,6 +20,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) token: string; teamId: string; code: string; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId?: IOrganizationTeam['id']; }; const { errors, valid: formValid } = authFormValidate(['email'], body as any); @@ -143,7 +145,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Accept Invite Flow End - const { data } = await signInWorkspaceRequest(body.email, body.token); + const { email, token, defaultTeamId, lastTeamId } = body; + const { data } = await signInWorkspaceRequest({ email, token, defaultTeamId, lastTeamId }); /** * Get the first team from first organization diff --git a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts index 901e8d771..724f309e1 100644 --- a/apps/web/app/hooks/auth/useAuthenticationPasscode.ts +++ b/apps/web/app/hooks/auth/useAuthenticationPasscode.ts @@ -44,6 +44,7 @@ export function useAuthenticationPasscode() { const inputCodeRef = useRef(null); const [screen, setScreen] = useState<'email' | 'passcode' | 'workspace'>('email'); const [workspaces, setWorkspaces] = useState([]); + const [defaultTeamId, setDefaultTeamId] = useState(undefined); const [authenticated, setAuthenticated] = useState(false); const [formValues, setFormValues] = useState({ @@ -74,7 +75,15 @@ export function useAuthenticationPasscode() { /** * Verify auth request */ - const verifySignInEmailConfirmRequest = async ({ email, code }: { email: string; code: string }) => { + const verifySignInEmailConfirmRequest = async ({ + email, + code, + lastTeamId + }: { + email: string; + code: string; + lastTeamId?: string; + }) => { signInEmailConfirmQueryCall(email, code) .then((res) => { if ('team' in res.data) { @@ -103,6 +112,7 @@ export function useAuthenticationPasscode() { if (data && Array.isArray(data.workspaces) && data.workspaces.length > 0) { setWorkspaces(data.workspaces); + setDefaultTeamId(data.defaultTeamId); setScreen('workspace'); } @@ -119,7 +129,8 @@ export function useAuthenticationPasscode() { email: email, code: code, token: currentWorkspace?.token as string, - selectedTeam: queryTeamId as string + selectedTeam: queryTeamId as string, + lastTeamId }); } } @@ -169,6 +180,8 @@ export function useAuthenticationPasscode() { token: string; selectedTeam: string; code?: string; + defaultTeamId?: string; + lastTeamId?: string; }) => { signInWorkspaceQueryCall(params) .then(() => { @@ -234,7 +247,9 @@ export function useAuthenticationPasscode() { email: formValues.email, code: formValues.code, token, - selectedTeam + selectedTeam, + defaultTeamId: selectedTeam, + lastTeamId: selectedTeam }); }; @@ -270,6 +285,20 @@ export function useAuthenticationPasscode() { return promise; }, [formValues, signInEmailQueryCall]); + const getLastTeamIdWithRecentLogout = useCallback(() => { + if (workspaces.length === 0) { + throw new Error('No workspaces found'); + } + + const mostRecentWorkspace = workspaces.reduce((prev, current) => { + const prevDate = new Date(prev.user.lastLoginAt ?? ''); + const currentDate = new Date(current.user.lastLoginAt ?? ''); + return currentDate > prevDate ? current : prev; + }); + + return mostRecentWorkspace.user.lastTeamId; + }, [workspaces]); + return { sendAuthCodeHandler, errors, @@ -290,9 +319,11 @@ export function useAuthenticationPasscode() { signInEmailConfirmQueryCall, signInEmailConfirmLoading, workspaces, + defaultTeamId, sendCodeQueryCall, signInWorkspaceLoading, - handleWorkspaceSubmit + handleWorkspaceSubmit, + getLastTeamIdWithRecentLogout }; } diff --git a/apps/web/app/hooks/auth/useAuthenticationPassword.ts b/apps/web/app/hooks/auth/useAuthenticationPassword.ts index 5858556cb..9cdc2f914 100644 --- a/apps/web/app/hooks/auth/useAuthenticationPassword.ts +++ b/apps/web/app/hooks/auth/useAuthenticationPassword.ts @@ -1,8 +1,8 @@ 'use client'; import { validateForm } from '@app/helpers'; -import { ISigninEmailConfirmWorkspaces } from '@app/interfaces'; -import { useRef, useState } from 'react'; +import { IOrganizationTeam, ISigninEmailConfirmWorkspaces } from '@app/interfaces'; +import { useCallback, useRef, useState } from 'react'; import { useQuery } from '../useQuery'; import { signInEmailPasswordAPI, signInWorkspaceAPI } from '@app/services/client/api'; import { AxiosError, isAxiosError } from 'axios'; @@ -22,6 +22,8 @@ export function useAuthenticationPassword() { const [workspaces, setWorkspaces] = useState([]); + const [defaultTeamId, setDefaultTeamId] = useState(undefined); + const [authenticated, setAuthenticated] = useState(false); const [formValues, setFormValues] = useState({ email: '', password: '' }); @@ -65,6 +67,7 @@ export function useAuthenticationPassword() { if (Array.isArray(data.workspaces) && data.workspaces.length > 0) { setScreen('workspace'); setWorkspaces(data.workspaces); + setDefaultTeamId(data.defaultTeamId); } }) .catch((err: AxiosError<{ errors: Record }, any> | { errors: Record }) => { @@ -78,16 +81,14 @@ export function useAuthenticationPassword() { }); }; - const signInToWorkspaceRequest = ({ - email, - token, - selectedTeam - }: { + const signInToWorkspaceRequest = (params: { email: string; token: string; selectedTeam: string; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId: string; }) => { - signInWorkspaceQueryCall({ email, token, selectedTeam }) + signInWorkspaceQueryCall(params) .then(() => { setAuthenticated(true); router.push('/'); @@ -116,10 +117,25 @@ export function useAuthenticationPassword() { signInToWorkspaceRequest({ email: formValues.email, token, - selectedTeam + selectedTeam, + lastTeamId: selectedTeam }); }; + const getLastTeamIdWithRecentLogout = useCallback(() => { + if (workspaces.length === 0) { + throw new Error('No workspaces found'); + } + + const mostRecentWorkspace = workspaces.reduce((prev, current) => { + const prevDate = new Date(prev.user.lastLoginAt ?? ''); + const currentDate = new Date(current.user.lastLoginAt ?? ''); + return currentDate > prevDate ? current : prev; + }); + + return mostRecentWorkspace.user.lastTeamId; + }, [workspaces]); + return { errors, setErrors, @@ -131,10 +147,12 @@ export function useAuthenticationPassword() { inputCodeRef, authScreen: { screen, setScreen }, workspaces, + defaultTeamId, signInQueryCall, signInLoading, signInWorkspaceLoading, - authenticated + authenticated, + getLastTeamIdWithRecentLogout }; } diff --git a/apps/web/app/hooks/auth/useAuthenticationSocialLogin.ts b/apps/web/app/hooks/auth/useAuthenticationSocialLogin.ts index 10bce10a6..62f24a7c1 100644 --- a/apps/web/app/hooks/auth/useAuthenticationSocialLogin.ts +++ b/apps/web/app/hooks/auth/useAuthenticationSocialLogin.ts @@ -4,7 +4,7 @@ import { setAuthCookies } from '@app/helpers'; import { useCallback, useState } from 'react'; import { useRouter } from 'next/navigation'; import { getUserOrganizationsRequest, signInWorkspaceAPI } from '@app/services/client/api/auth/invite-accept'; -import { ISigninEmailConfirmWorkspaces } from '@app/interfaces'; +import { IOrganizationTeam, ISigninEmailConfirmWorkspaces } from '@app/interfaces'; import { useSession } from 'next-auth/react'; type SigninResult = { access_token: string; @@ -29,10 +29,15 @@ export function useAuthenticationSocialLogin() { signinResult: SigninResult, workspaces: ISigninEmailConfirmWorkspaces[], selectedWorkspace: number, - selectedTeam: string + selectedTeam: string, + defaultTeamId?: IOrganizationTeam['id'] ) => { setSignInWorkspaceLoading(true); - signInWorkspaceAPI(signinResult.confirmed_mail, workspaces[selectedWorkspace].token) + signInWorkspaceAPI({ + email: signinResult.confirmed_mail, + token: workspaces[selectedWorkspace].token, + defaultTeamId + }) .then(async (result) => { const tenantId = result.user?.tenantId || ''; const access_token = result.token; diff --git a/apps/web/app/hooks/features/useAuthenticateUser.ts b/apps/web/app/hooks/features/useAuthenticateUser.ts index 9d6233dc6..286542cea 100644 --- a/apps/web/app/hooks/features/useAuthenticateUser.ts +++ b/apps/web/app/hooks/features/useAuthenticateUser.ts @@ -1,12 +1,12 @@ 'use client'; -import { DEFAULT_APP_PATH } from '@app/constants'; +import { DEFAULT_APP_PATH, LAST_WORSPACE_AND_TEAM } from '@app/constants'; import { removeAuthCookies } from '@app/helpers/cookies'; import { IUser } from '@app/interfaces/IUserData'; import { getAuthenticatedUserDataAPI, refreshTokenAPI } from '@app/services/client/api/auth'; -import { userState } from '@app/stores'; +import { activeTeamState, userState } from '@app/stores'; import { useCallback, useMemo, useRef } from 'react'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { useQuery } from '../useQuery'; import { useIsMemberManager } from './useTeamMember'; @@ -17,6 +17,7 @@ export const useAuthenticateUser = (defaultUser?: IUser) => { const [user, setUser] = useRecoilState(userState); const $user = useRef(defaultUser); const intervalRt = useRef(0); + const activeTeam = useRecoilValue(activeTeamState); const { isTeamManager } = useIsMemberManager(user); @@ -40,10 +41,11 @@ export const useAuthenticateUser = (defaultUser?: IUser) => { }, [user]); const logOut = useCallback(() => { + window && window?.localStorage.setItem(LAST_WORSPACE_AND_TEAM, activeTeam?.id ?? ''); removeAuthCookies(); window.clearInterval(intervalRt.current); window.location.replace(DEFAULT_APP_PATH); - }, []); + }, [activeTeam?.id]); const timeToTimeRefreshToken = useCallback((interval = 3000 * 60) => { window.clearInterval(intervalRt.current); diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts index 67f79d4f4..deb1bbcd7 100644 --- a/apps/web/app/hooks/features/useDailyPlan.ts +++ b/apps/web/app/hooks/features/useDailyPlan.ts @@ -303,8 +303,11 @@ export function useDailyPlan() { [...profileDailyPlans.items].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); useEffect(() => { - getMyDailyPlans(); - }, [getMyDailyPlans]); + if (firstLoad) { + getMyDailyPlans(); + getAllDayPlans(); + } + }, [getMyDailyPlans, getAllDayPlans, firstLoad]); return { dailyPlan, diff --git a/apps/web/app/hooks/features/useOrganizationTeams.ts b/apps/web/app/hooks/features/useOrganizationTeams.ts index dadabf6aa..1d6108b41 100644 --- a/apps/web/app/hooks/features/useOrganizationTeams.ts +++ b/apps/web/app/hooks/features/useOrganizationTeams.ts @@ -33,6 +33,8 @@ import { useFirstLoad } from '../useFirstLoad'; import { useQuery } from '../useQuery'; import { useSyncRef } from '../useSyncRef'; import { useAuthenticateUser } from './useAuthenticateUser'; +import { useSettings } from './useSettings'; +import { LAST_WORSPACE_AND_TEAM } from '@app/constants'; /** * It updates the `teams` state with the `members` status from the `team` status API @@ -181,6 +183,7 @@ export function useOrganizationTeams() { const { firstLoad, firstLoadData: firstLoadTeamsData } = useFirstLoad(); const [isTeamMember, setIsTeamMember] = useRecoilState(isTeamMemberState); const { updateUserFromAPI, refreshToken, user } = useAuthenticateUser(); + const { updateAvatar: updateUserLastTeam } = useSettings(); const timerStatus = useRecoilValue(timerStatusState); // const setMemberActiveTaskId = useSetRecoilState(memberActiveTaskIdState); @@ -228,8 +231,10 @@ export function useOrganizationTeams() { if (team && team?.projects && team.projects.length) { setActiveProjectIdCookie(team.projects[0].id); } + window && window?.localStorage.setItem(LAST_WORSPACE_AND_TEAM, team.id); + if (user) updateUserLastTeam({ id: user.id, lastTeamId: team.id }); }, - [setActiveTeamId] + [setActiveTeamId, updateUserLastTeam, user] ); const loadTeamsData = useCallback(() => { diff --git a/apps/web/app/hooks/features/useTimeLogs.ts b/apps/web/app/hooks/features/useTimeLogs.ts index eecfc6c64..fe3b31e2e 100644 --- a/apps/web/app/hooks/features/useTimeLogs.ts +++ b/apps/web/app/hooks/features/useTimeLogs.ts @@ -5,9 +5,13 @@ import { timerLogsDailyReportState } from '@app/stores/time-logs'; import { useQuery } from '../useQuery'; import { useCallback, useEffect } from 'react'; import moment from 'moment'; +import { useFirstLoad } from '../useFirstLoad'; export function useTimeLogs() { const { user } = useAuthenticateUser(); + + const { firstLoadData: firstLoadTimeLogs, firstLoad } = useFirstLoad(); + const [timerLogsDailyReport, setTimerLogsDailyReport] = useRecoilState(timerLogsDailyReportState); const { loading: timerLogsDailyReportLoading, queryCall: queryTimerLogsDailyReport } = useQuery( @@ -42,11 +46,14 @@ export function useTimeLogs() { ); useEffect(() => { - getTimerLogsDailyReport(); - }, [getTimerLogsDailyReport]); + if (firstLoad) { + getTimerLogsDailyReport(); + } + }, [getTimerLogsDailyReport, firstLoad]); return { timerLogsDailyReport, - timerLogsDailyReportLoading + timerLogsDailyReportLoading, + firstLoadTimeLogs }; } diff --git a/apps/web/app/hooks/features/useTimeSlot.ts b/apps/web/app/hooks/features/useTimeSlot.ts index d33770836..7cff0ddd6 100644 --- a/apps/web/app/hooks/features/useTimeSlot.ts +++ b/apps/web/app/hooks/features/useTimeSlot.ts @@ -46,11 +46,10 @@ export function useTimeSlots(hasFilter?: boolean) { organizationId: user?.employee.organizationId ?? '', ids: ids }).then(() => { - const updatedSlots = timeSlots.filter((el) => (!ids?.includes(el.id) ? el : null)); - setTimeSlots(updatedSlots); + setTimeSlots((timeSlots) => timeSlots.filter((el) => (!ids?.includes(el.id) ? el : null))); }); }, - [queryDeleteCall, setTimeSlots, timeSlots, user] + [queryDeleteCall, setTimeSlots, user] ); useEffect(() => { diff --git a/apps/web/app/hooks/features/useTimer.ts b/apps/web/app/hooks/features/useTimer.ts index 38b298b69..157af1a37 100644 --- a/apps/web/app/hooks/features/useTimer.ts +++ b/apps/web/app/hooks/features/useTimer.ts @@ -84,22 +84,22 @@ function useLocalTimeCounter(timerStatus: ITimerStatus | null, activeTeamTask: I // Update local time status (storage and store) only when global timerStatus changes useEffect(() => { - // if (firstLoad) { - const localStatus = getLocalCounterStatus(); - localStatus && setLocalTimerStatus(localStatus); - - const timerStatusDate = timerStatus?.lastLog?.createdAt - ? moment(timerStatus?.lastLog?.createdAt).unix() * 1000 - timerStatus?.lastLog?.duration - : 0; - - timerStatus && - updateLocalTimerStatus({ - runnedDateTime: - (timerStatus.running ? timerStatusDate || Date.now() : 0) || localStatus?.runnedDateTime || 0, - running: timerStatus.running, - lastTaskId: timerStatus.lastLog?.taskId || null - }); - // } + if (firstLoad) { + const localStatus = getLocalCounterStatus(); + localStatus && setLocalTimerStatus(localStatus); + + const timerStatusDate = timerStatus?.lastLog?.createdAt + ? moment(timerStatus?.lastLog?.createdAt).unix() * 1000 - timerStatus?.lastLog?.duration + : 0; + + timerStatus && + updateLocalTimerStatus({ + runnedDateTime: + (timerStatus.running ? timerStatusDate || Date.now() : 0) || localStatus?.runnedDateTime || 0, + running: timerStatus.running, + lastTaskId: timerStatus.lastLog?.taskId || null + }); + } }, [firstLoad, timerStatus, getLocalCounterStatus, setLocalTimerStatus, updateLocalTimerStatus]); // THis is form constant update of the progress line @@ -115,16 +115,16 @@ function useLocalTimeCounter(timerStatus: ITimerStatus | null, activeTeamTask: I }, [seconds, firstLoad, timerStatusRef]); useEffect(() => { - // if (firstLoad) { - timerSecondsRef.current = 0; - setTimerSeconds(0); - // } + if (firstLoad) { + timerSecondsRef.current = 0; + setTimerSeconds(0); + } }, [activeTeamTask?.id, setTimerSeconds, firstLoad, timerSecondsRef]); useEffect(() => { - // if (firstLoad) { - setTimerSeconds(timerSecondsRef.current); - // } + if (firstLoad) { + setTimerSeconds(timerSecondsRef.current); + } }, [setTimerSeconds, firstLoad]); // Time Counter @@ -276,9 +276,9 @@ export function useTimer() { // Loading states useEffect(() => { - // if (firstLoad) { - setTimerStatusFetching(loading); - // } + if (firstLoad) { + setTimerStatusFetching(loading); + } }, [loading, firstLoad, setTimerStatusFetching]); useEffect(() => { diff --git a/apps/web/app/hooks/features/useTimezoneSettings.ts b/apps/web/app/hooks/features/useTimezoneSettings.ts index 93841670d..77146274f 100644 --- a/apps/web/app/hooks/features/useTimezoneSettings.ts +++ b/apps/web/app/hooks/features/useTimezoneSettings.ts @@ -2,18 +2,18 @@ import { setActiveTimezoneCookie } from '@app/helpers'; import { activeTimezoneState, timezoneListState, activeTimezoneIdState, timezonesFetchingState } from '@app/stores'; -import { useCallback, useEffect } from 'react'; +import { useCallback } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; export function useTimezoneSettings() { - const [timezones, setTimezone] = useRecoilState(timezoneListState); + const [timezones] = useRecoilState(timezoneListState); const activeTimezone = useRecoilValue(activeTimezoneState); const [, setActiveTimezoneId] = useRecoilState(activeTimezoneIdState); - const [timezonesFetching, setTimezoneFetching] = useRecoilState(timezonesFetchingState); + const [timezonesFetching] = useRecoilState(timezonesFetchingState); - useEffect(() => { - setTimezone(timezones); - }, [setTimezoneFetching, activeTimezone, setTimezone, timezones]); + // useEffect(() => { + // setTimezone(timezones); + // }, [setTimezone, timezones]); const setActiveTimezone = useCallback( (timezoneId: (typeof timezones)[0]) => { diff --git a/apps/web/app/hooks/useCallbackRef.ts b/apps/web/app/hooks/useCallbackRef.ts index 12fa75f8f..3b76fcfe1 100644 --- a/apps/web/app/hooks/useCallbackRef.ts +++ b/apps/web/app/hooks/useCallbackRef.ts @@ -2,7 +2,7 @@ import { useRef } from 'react'; -export function useCallbackRef void>(func?: T) { +export function useCallbackRef void) | undefined>(func: T) { const ref = useRef(func); ref.current = func; diff --git a/apps/web/app/hooks/useLiveKit.ts b/apps/web/app/hooks/useLiveKit.ts index 0e313cb62..409c22dd3 100644 --- a/apps/web/app/hooks/useLiveKit.ts +++ b/apps/web/app/hooks/useLiveKit.ts @@ -1,33 +1,32 @@ -"use client"; -import { tokenLiveKitRoom } from "@app/services/server/livekitroom"; -import { useEffect, useState } from "react"; +'use client'; +import { tokenLiveKitRoom } from '@app/services/server/livekitroom'; +import { useEffect, useState } from 'react'; interface ITokenLiveKitProps { - roomName: string; - username: string; + roomName: string; + username: string; } export function useTokenLiveKit({ roomName, username }: ITokenLiveKitProps) { + const [token, setToken] = useState(() => { + if (typeof window !== 'undefined') { + return window.localStorage.getItem('token-live-kit'); + } + return null; + }); - const [token, setToken] = useState(() => { - if (typeof window !== 'undefined') { - return window.localStorage.getItem('token-live-kit'); - } - return null; - }); + useEffect(() => { + const fetchToken = async () => { + try { + const response = await tokenLiveKitRoom({ roomName, username }); + window.localStorage.setItem('token-live-kit', response.token); + setToken(response.token); + } catch (error) { + console.error('Failed to fetch token:', error); + } + }; + fetchToken(); + }, [roomName, username]); - useEffect(() => { - const fetchToken = async () => { - try { - const response = await tokenLiveKitRoom({ roomName, username }); - window.localStorage.setItem('token-live-kit', response.token); - setToken(response.token); - } catch (error) { - console.error('Failed to fetch token:', error); - } - }; - fetchToken(); - }, [roomName, username, token]); - - return { token }; + return { token }; } diff --git a/apps/web/app/interfaces/IAuthentication.ts b/apps/web/app/interfaces/IAuthentication.ts index 6f1e314da..96e47360d 100644 --- a/apps/web/app/interfaces/IAuthentication.ts +++ b/apps/web/app/interfaces/IAuthentication.ts @@ -1,3 +1,4 @@ +import { IOrganizationTeam } from './IOrganizationTeam'; import { ITeamProps, IUser } from './IUserData'; export interface ILoginResponse { @@ -6,6 +7,13 @@ export interface ILoginResponse { refresh_token: string; } +export interface ISigninWorkspaceInput { + email: string; + token: string; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId?: IOrganizationTeam['id']; +} + export interface IRegisterDataRequest { user: Required>; password: string; @@ -40,6 +48,8 @@ export interface ISigninEmailConfirmWorkspaces { user: { email: string; imageUrl: string; + lastTeamId?: string; + lastLoginAt?: string; name: string; tenant: { name: string; logo: string }; }; @@ -57,4 +67,5 @@ export interface ISigninEmailConfirmResponse { show_popup: boolean; workspaces: ISigninEmailConfirmWorkspaces[]; status?: number; + defaultTeamId?: string; } diff --git a/apps/web/app/interfaces/IUserData.ts b/apps/web/app/interfaces/IUserData.ts index 8455fa730..46cfb12fe 100644 --- a/apps/web/app/interfaces/IUserData.ts +++ b/apps/web/app/interfaces/IUserData.ts @@ -1,5 +1,7 @@ import { IEmployee } from './IEmployee'; import { IImageAssets } from './IImageAssets'; +import { IOrganization } from './IOrganization'; +import { IOrganizationTeam } from './IOrganizationTeam'; export interface ITeamProps { email: string; @@ -25,6 +27,15 @@ export interface IUser { employee: IEmployee; role: Role; tenant: Tenant; + defaultTeam?: IOrganizationTeam; + defaultTeamId?: IOrganizationTeam['id']; + lastTeam?: IOrganizationTeam; + lastTeamId?: IOrganizationTeam['id']; + defaultOrganization?: IOrganization; + defaultOrganizationId?: IOrganization['id']; + lastOrganization?: IOrganization; + lastOrganizationId?: IOrganization['id']; + lastLoginAt?: Date; createdAt: string; updatedAt: string; timeZone?: string; diff --git a/apps/web/app/services/client/api/auth.ts b/apps/web/app/services/client/api/auth.ts index fe53873f5..de12b4d18 100644 --- a/apps/web/app/services/client/api/auth.ts +++ b/apps/web/app/services/client/api/auth.ts @@ -1,5 +1,5 @@ import { getRefreshTokenCookie, getTenantIdCookie, setAccessTokenCookie } from '@app/helpers/cookies'; -import { ISuccessResponse, IUser } from '@app/interfaces'; +import { IOrganizationTeam, ISuccessResponse, IUser } from '@app/interfaces'; import { ILoginResponse, IRegisterDataAPI, ISigninEmailConfirmResponse } from '@app/interfaces/IAuthentication'; import api, { get, post } from '../axios'; import { @@ -134,13 +134,22 @@ 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; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId?: IOrganizationTeam['id']; +}) => { if (GAUZY_API_BASE_SERVER_URL.value) { return signInWorkspaceGauzy({ email: params.email, token: params.token, teamId: params.selectedTeam, - code: params.code + code: params.code, + defaultTeamId: params.defaultTeamId, + lastTeamId: params.lastTeamId }); } 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 75b3be2c7..490544cc5 100644 --- a/apps/web/app/services/client/api/auth/invite-accept.ts +++ b/apps/web/app/services/client/api/auth/invite-accept.ts @@ -7,7 +7,9 @@ import { ITeamRequestParams, TimerSource, IOrganizationTeamList, - ISigninEmailConfirmResponse + ISigninEmailConfirmResponse, + ISigninWorkspaceInput, + IOrganizationTeam } from '@app/interfaces'; import { AcceptInviteParams } from '@app/services/server/requests'; import { get, post } from '../../axios'; @@ -191,11 +193,8 @@ export async function signInEmailCodeConfirmGauzy(email: string, code: string) { return loginResponse; } -export function signInWorkspaceAPI(email: string, token: string) { - return post('/auth/signin.workspace', { - email, - token - }).then((res) => res.data); +export function signInWorkspaceAPI(input: ISigninWorkspaceInput) { + return post('/auth/signin.workspace', input).then((res) => res.data); } /** @@ -228,7 +227,14 @@ export async function signInEmailConfirmGauzy(email: string, code: string) { /** * @param params */ -export async function signInWorkspaceGauzy(params: { email: string; token: string; teamId: string; code?: string }) { +export async function signInWorkspaceGauzy(params: { + email: string; + token: string; + teamId: string; + code?: string; + defaultTeamId?: IOrganizationTeam['id']; + lastTeamId?: IOrganizationTeam['id']; +}) { if (params.code) { let loginResponse; try { @@ -242,7 +248,12 @@ export async function signInWorkspaceGauzy(params: { email: string; token: strin } } - const data = await signInWorkspaceAPI(params.email, params.token); + const data = await signInWorkspaceAPI({ + email: params.email, + token: params.token, + defaultTeamId: params.defaultTeamId, + lastTeamId: params.lastTeamId + }); /** * Get the first team from first organization diff --git a/apps/web/app/services/server/requests/OAuth.ts b/apps/web/app/services/server/requests/OAuth.ts index fb92a0e41..cb6416825 100644 --- a/apps/web/app/services/server/requests/OAuth.ts +++ b/apps/web/app/services/server/requests/OAuth.ts @@ -157,7 +157,10 @@ async function signIn(provider: ProviderEnum, access_token: string) { }); } - const data = await signInWorkspaceAPI(gauzyUser?.data.confirmed_email, gauzyUser?.data.workspaces[0].token); + const data = await signInWorkspaceAPI({ + email: gauzyUser?.data.confirmed_email, + token: gauzyUser?.data.workspaces[0].token + }); const tenantId = data.user?.tenantId || ''; const token = data.token; const userId = data.user?.id; diff --git a/apps/web/app/services/server/requests/auth.ts b/apps/web/app/services/server/requests/auth.ts index 3bad0d876..c4e002a11 100644 --- a/apps/web/app/services/server/requests/auth.ts +++ b/apps/web/app/services/server/requests/auth.ts @@ -1,6 +1,11 @@ import { VERIFY_EMAIL_CALLBACK_URL, APP_NAME, APP_SIGNATURE, APP_LOGO_URL } from '@app/constants'; import { ISuccessResponse } from '@app/interfaces'; -import { ILoginResponse, IRegisterDataRequest, ISigninEmailConfirmResponse } from '@app/interfaces/IAuthentication'; +import { + ILoginResponse, + IRegisterDataRequest, + ISigninEmailConfirmResponse, + ISigninWorkspaceInput +} from '@app/interfaces/IAuthentication'; import { IUser } from '@app/interfaces/IUserData'; import { serverFetch } from '../fetch'; import qs from 'qs'; @@ -69,12 +74,12 @@ export const signInEmailConfirmRequest = (data: { code: string; email: string }) }); }; -export function signInWorkspaceRequest(email: string, token: string) { +export function signInWorkspaceRequest(input: ISigninWorkspaceInput) { return serverFetch({ path: '/auth/signin.workspace', method: 'POST', - body: { email, token }, - bearer_token: token + body: input, + bearer_token: input.token }); } diff --git a/apps/web/components/layout/header/position-dropdown.tsx b/apps/web/components/layout/header/position-dropdown.tsx index b734f1380..54dc1f41e 100644 --- a/apps/web/components/layout/header/position-dropdown.tsx +++ b/apps/web/components/layout/header/position-dropdown.tsx @@ -34,15 +34,18 @@ export const PositionDropDown = ({ title: 'CTO' } ]); - const handleAddNew = (position: string) => { - setNewPosition(position); - setPositions([ - ...positions, - { - title: position - } - ]); - }; + const handleAddNew = useCallback( + (position: string) => { + setNewPosition(position); + setPositions([ + ...positions, + { + title: position + } + ]); + }, + [positions, setPositions, setNewPosition] + ); const items = useMemo(() => mapPositionItems(positions), [positions]); diff --git a/apps/web/components/pages/setting/interaction-observer.tsx b/apps/web/components/pages/setting/interaction-observer.tsx index 2ca5e50dc..b60657c97 100644 --- a/apps/web/components/pages/setting/interaction-observer.tsx +++ b/apps/web/components/pages/setting/interaction-observer.tsx @@ -1,4 +1,5 @@ 'use client'; +import { useCallbackRef } from '@app/hooks'; import { clsxm } from '@app/utils'; import { useIntersectionObserver } from '@uidotdev/usehooks'; import React, { useEffect } from 'react'; @@ -6,13 +7,15 @@ import React, { useEffect } from 'react'; export const InteractionObserverVisible = ({ id, setActiveSection, - children, + children }: { id: string; setActiveSection: (v: any) => void; children: React.ReactNode; className?: string; }) => { + const $setActiveSection = useCallbackRef(setActiveSection); + const [ref, entry] = useIntersectionObserver({ threshold: 0.9, root: null, @@ -20,9 +23,9 @@ export const InteractionObserverVisible = ({ }); useEffect(() => { if (entry?.isIntersecting) { - setActiveSection(id); + $setActiveSection.current?.(id); } - }, [entry, id, setActiveSection]); + }, [entry, id, $setActiveSection]); return (
diff --git a/apps/web/components/pages/task/description-block/editor-footer.tsx b/apps/web/components/pages/task/description-block/editor-footer.tsx index bd6451925..8fb3cd2de 100644 --- a/apps/web/components/pages/task/description-block/editor-footer.tsx +++ b/apps/web/components/pages/task/description-block/editor-footer.tsx @@ -1,4 +1,4 @@ -import { useTeamTasks } from '@app/hooks'; +import { useCallbackRef, useTeamTasks } from '@app/hooks'; import { detailedTaskState } from '@app/stores'; import { Button } from 'lib/components'; import Image from 'next/image'; @@ -17,6 +17,7 @@ interface IDFooterProps { } const EditorFooter = ({ isUpdated, setIsUpdated, editorValue, editorRef, clearUnsavedValues }: IDFooterProps) => { + const $setIsUpdated = useCallbackRef(setIsUpdated); const [task] = useRecoilState(detailedTaskState); const { updateDescription } = useTeamTasks(); const t = useTranslations(); @@ -37,7 +38,7 @@ const EditorFooter = ({ isUpdated, setIsUpdated, editorValue, editorRef, clearUn useEffect(() => { const handleClickOutsideEditor = (event: MouseEvent) => { if (editorRef.current && !editorRef.current.contains(event.target)) { - setIsUpdated(); + $setIsUpdated.current(); } }; // Add event listener when component mounts @@ -46,16 +47,14 @@ const EditorFooter = ({ isUpdated, setIsUpdated, editorValue, editorRef, clearUn // Clean up event listener when component unmounts document.removeEventListener('mousedown', handleClickOutsideEditor); }; - }, [editorRef, setIsUpdated]); + }, [editorRef, $setIsUpdated]); return (
- -
-
- - ) + return ( +
+
+ + + {moment(currentDate).format('MMM')} {currentDate.getFullYear()} + +
+
+ + +
+
+ ); } 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 7c221d4c9..65e8df85a 100644 --- a/apps/web/lib/features/task/task-all-status-type.tsx +++ b/apps/web/lib/features/task/task-all-status-type.tsx @@ -36,7 +36,7 @@ export function TaskAllStatusTypes({ const taskLabels = useTaskLabelsValue(); const taskStatus = useTaskStatusValue(); - const { dailyPlan, getAllDayPlans } = useDailyPlan(); + const { dailyPlan } = useDailyPlan(); const { viewportRef, nextBtnEnabled, scrollNext, prevBtnEnabled, scrollPrev, emblaApi } = useCustomEmblaCarousel( 0, @@ -52,9 +52,9 @@ export function TaskAllStatusTypes({ emblaApiRef.current?.reInit(); }, [task, emblaApiRef]); - useEffect(() => { - getAllDayPlans(); - }, [getAllDayPlans]); + // useEffect(() => { + // getAllDayPlans(); + // }, [getAllDayPlans]); const tags = useMemo(() => { return ( @@ -66,10 +66,7 @@ export function TaskAllStatusTypes({ ); }, [taskLabels, task?.tags]); - const taskId = planBadgeContPast( - dailyPlan.items, - task!.id - ) + const taskId = planBadgeContPast(dailyPlan.items, task!.id); return (
@@ -108,7 +105,11 @@ export function TaskAllStatusTypes({ {planBadgeContent(dailyPlan.items, task?.id ?? '', tab) && (
diff --git a/apps/web/lib/features/team-members-card-view.tsx b/apps/web/lib/features/team-members-card-view.tsx index f31cf7eb7..36b107214 100644 --- a/apps/web/lib/features/team-members-card-view.tsx +++ b/apps/web/lib/features/team-members-card-view.tsx @@ -11,7 +11,7 @@ import { InviteFormModal } from './team/invite/invite-form-modal'; import { InvitedCard, InviteUserTeamCard } from './team/invite/user-invite-card'; import { InviteUserTeamSkeleton, UserTeamCard, UserTeamCardSkeleton } from '.'; import { OT_Member } from '@app/interfaces'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { DailyPlanCompareEstimatedModal } from './daily-plan'; import { DAILY_PLAN_ESTIMATE_HOURS_MODAL_DATE } from '@app/constants'; @@ -58,7 +58,14 @@ const TeamMembersCardView: React.FC = ({ } }, [defaultOpenPopup, plan]); - function handleSort() { + const handleChangeOrder = useCallback( + (employee: OT_Member, order: number) => { + updateOrganizationTeamEmployeeOrderOnList(employee, order); + }, + [updateOrganizationTeamEmployeeOrderOnList] + ); + + const handleSort = useCallback(() => { const peopleClone = [...memberOrdereds]; const temp = peopleClone[dragTeamMember.current]; peopleClone[dragTeamMember.current] = peopleClone[draggedOverTeamMember.current]; @@ -67,11 +74,7 @@ const TeamMembersCardView: React.FC = ({ // TODO: update teamMembers index handleChangeOrder(peopleClone[dragTeamMember.current], draggedOverTeamMember.current); handleChangeOrder(peopleClone[draggedOverTeamMember.current], dragTeamMember.current); - } - - const handleChangeOrder = (employee: OT_Member, order: number) => { - updateOrganizationTeamEmployeeOrderOnList(employee, order); - }; + }, [memberOrdereds, dragTeamMember, draggedOverTeamMember]); return ( <> diff --git a/apps/web/lib/features/team/team-outstanding-notifications.tsx b/apps/web/lib/features/team/team-outstanding-notifications.tsx index 2017b53ea..fab2b7fa7 100644 --- a/apps/web/lib/features/team/team-outstanding-notifications.tsx +++ b/apps/web/lib/features/team/team-outstanding-notifications.tsx @@ -14,14 +14,14 @@ interface IEmployeeWithOutstanding { } export function TeamOutstandingNotifications() { - const { getAllDayPlans, dailyPlan, getEmployeeDayPlans, outstandingPlans } = useDailyPlan(); + const { dailyPlan, getEmployeeDayPlans, outstandingPlans } = useDailyPlan(); const { isTeamManager, user } = useAuthenticateUser(); useEffect(() => { - getAllDayPlans(); + // getAllDayPlans(); getEmployeeDayPlans(user?.employee.id || ''); - }, [getAllDayPlans, getEmployeeDayPlans, user?.employee.id]); + }, [getEmployeeDayPlans, user?.employee.id]); return (
diff --git a/apps/web/lib/settings/choose-dropdown.tsx b/apps/web/lib/settings/choose-dropdown.tsx index 57171d70e..0ff846239 100644 --- a/apps/web/lib/settings/choose-dropdown.tsx +++ b/apps/web/lib/settings/choose-dropdown.tsx @@ -40,7 +40,7 @@ export const ChooseDropdown = ({ ); useEffect(() => { - if (!chooseItem) { + if (!chooseItem && items.length > 0) { setChooseItem(items[0]); } }, [chooseItem, items]); @@ -49,7 +49,7 @@ export const ChooseDropdown = ({ if (active && chooseList.every((choose) => choose.title !== active.title)) { setChoose([...chooseList, active]); } - }, [chooseList, setChoose, setChooseItem, active]); + }, [chooseList, setChoose, active]); useEffect(() => { if (active) { diff --git a/apps/web/lib/settings/color-dropdown.tsx b/apps/web/lib/settings/color-dropdown.tsx index 711ff37ce..ad400a585 100644 --- a/apps/web/lib/settings/color-dropdown.tsx +++ b/apps/web/lib/settings/color-dropdown.tsx @@ -71,7 +71,7 @@ export const ColorDropdown = ({ ); useEffect(() => { - if (!colorItem) { + if (!colorItem && items.length > 0) { setColorItem(items[0]); } }, [colorItem, items]); diff --git a/apps/web/lib/settings/day-dropdown.tsx b/apps/web/lib/settings/day-dropdown.tsx index 901c49453..43dfd7754 100644 --- a/apps/web/lib/settings/day-dropdown.tsx +++ b/apps/web/lib/settings/day-dropdown.tsx @@ -26,7 +26,7 @@ export const DayDropdown = ({ setValue, active }: { setValue: UseFormSetValue { - if (!DayItem) { + if (!DayItem && items.length > 0) { setDayItem(items[0]); } }, [DayItem, items]); diff --git a/apps/web/lib/settings/filter-by-dropdown.tsx b/apps/web/lib/settings/filter-by-dropdown.tsx index 95339aaf6..2f9559d6c 100644 --- a/apps/web/lib/settings/filter-by-dropdown.tsx +++ b/apps/web/lib/settings/filter-by-dropdown.tsx @@ -46,7 +46,7 @@ export const FilterDropdown = ({ ); useEffect(() => { - if (!filterItem) { + if (!filterItem && items.length > 0) { setFilterItem(items[0]); } }, [filterItem, items]); diff --git a/apps/web/lib/settings/icon-dropdown.tsx b/apps/web/lib/settings/icon-dropdown.tsx index 54efcc0d2..8837b8eaa 100644 --- a/apps/web/lib/settings/icon-dropdown.tsx +++ b/apps/web/lib/settings/icon-dropdown.tsx @@ -33,7 +33,7 @@ export const IconDropdown = ({ ); useEffect(() => { - if (!iconItem) { + if (!iconItem && items.length > 0) { setIconItem(items[0]); } }, [iconItem, items]); diff --git a/apps/web/lib/settings/invitation-expire-dropdown.tsx b/apps/web/lib/settings/invitation-expire-dropdown.tsx index 30c996fe1..01dd4e2af 100644 --- a/apps/web/lib/settings/invitation-expire-dropdown.tsx +++ b/apps/web/lib/settings/invitation-expire-dropdown.tsx @@ -36,7 +36,7 @@ export const InvitationExpireDropdown = ({ ); useEffect(() => { - if (!expireItem) { + if (!expireItem && items.length > 0) { setExpireItem(items[0]); } }, [expireItem, items]); diff --git a/apps/web/lib/settings/notify-dropdown.tsx b/apps/web/lib/settings/notify-dropdown.tsx index 0bfbb5cfb..7909b7b86 100644 --- a/apps/web/lib/settings/notify-dropdown.tsx +++ b/apps/web/lib/settings/notify-dropdown.tsx @@ -34,7 +34,7 @@ export const NotifyDropdown = ({ ); useEffect(() => { - if (!NotifyItem) { + if (!NotifyItem && items.length > 0) { setNotifyItem(items[0]); } }, [NotifyItem, items]); diff --git a/apps/web/lib/settings/page-dropdown.tsx b/apps/web/lib/settings/page-dropdown.tsx index a859fbcc5..ae21585b4 100644 --- a/apps/web/lib/settings/page-dropdown.tsx +++ b/apps/web/lib/settings/page-dropdown.tsx @@ -47,7 +47,7 @@ export const PaginationDropdown = ({ ); useEffect(() => { - if (!paginationItem) { + if (!paginationItem && items.length > 0) { setPaginationItem(items[0]); } }, [paginationItem, items]); diff --git a/apps/web/lib/settings/period-dropdown.tsx b/apps/web/lib/settings/period-dropdown.tsx index 5ac0df206..d1822e75c 100644 --- a/apps/web/lib/settings/period-dropdown.tsx +++ b/apps/web/lib/settings/period-dropdown.tsx @@ -37,7 +37,7 @@ export const PeriodDropdown = ({ ); useEffect(() => { - if (!PeriodItem) { + if (!PeriodItem && items.length > 0) { setPeriodItem(items[0]); } }, [PeriodItem, items]); diff --git a/apps/web/lib/settings/proof-dropdown.tsx b/apps/web/lib/settings/proof-dropdown.tsx index 81b15232c..b4a7cc209 100644 --- a/apps/web/lib/settings/proof-dropdown.tsx +++ b/apps/web/lib/settings/proof-dropdown.tsx @@ -37,7 +37,7 @@ export const ProofDropdown = ({ ); useEffect(() => { - if (!ProofItem) { + if (!ProofItem && items.length > 0) { setProofItem(items[0]); } }, [ProofItem, items]); diff --git a/apps/web/lib/settings/sort-by-dropdown.tsx b/apps/web/lib/settings/sort-by-dropdown.tsx index 063832342..7617686ee 100644 --- a/apps/web/lib/settings/sort-by-dropdown.tsx +++ b/apps/web/lib/settings/sort-by-dropdown.tsx @@ -37,7 +37,7 @@ export const SortDropdown = ({ ); useEffect(() => { - if (!sortItem) { + if (!sortItem && items.length > 0) { setSortItem(items[0]); } }, [sortItem, items]);