Skip to content

Commit

Permalink
Merge pull request #2111 from ever-co/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
evereq authored Jan 23, 2024
2 parents 4bbce1b + 25bedac commit 5f7e676
Show file tree
Hide file tree
Showing 49 changed files with 674 additions and 45 deletions.
10 changes: 8 additions & 2 deletions apps/web/app/[locale]/error.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
'use client';

const Error = () => {
return <>Error Page...</>;
import ErrorPage from '@components/pages/error';

const Error = ({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) => {
return (
<>
<ErrorPage error={error} reset={reset} />
</>
);
};

export default Error;
13 changes: 11 additions & 2 deletions apps/web/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import '../../styles/globals.css';
import { ThemeProvider } from 'next-themes';
import { JitsuRoot } from 'lib/settings/JitsuRoot';
import { JitsuOptions } from '@jitsu/jitsu-react/dist/useJitsu';
import { useCheckAPI } from '@app/hooks/useCheckAPI';
import Maintenance from '@components/pages/maintenance';

const locales = ['en', 'de', 'ar', 'bg', 'zh', 'nl', 'de', 'he', 'it', 'pl', 'pt', 'ru', 'es', 'fr'];

Expand Down Expand Up @@ -43,6 +45,7 @@ interface Props {
const LocaleLayout = ({ children, params: { locale }, pageProps }: Props) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale as any)) notFound();
const { isApiWork } = useCheckAPI();
// Enable static rendering
// unstable_setRequestLocale(locale);

Expand Down Expand Up @@ -70,8 +73,14 @@ const LocaleLayout = ({ children, params: { locale }, pageProps }: Props) => {
<body className={clsx('flex h-full flex-col dark:bg-[#191A20]')}>
<RecoilRoot>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<AppState />
<JitsuRoot pageProps={pageProps}>{children}</JitsuRoot>
{isApiWork ? (
<>
<AppState />
<JitsuRoot pageProps={pageProps}>{children}</JitsuRoot>
</>
) : (
<Maintenance />
)}
</ThemeProvider>
</RecoilRoot>
</body>
Expand Down
6 changes: 6 additions & 0 deletions apps/web/app/api/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getDefaultRequest } from '@app/services/server/requests/default';
import { NextResponse } from 'next/server';

export async function GET() {
return NextResponse.json(await getDefaultRequest());
}
28 changes: 25 additions & 3 deletions apps/web/app/api/tasks/employee/[employeeId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
import { authenticatedGuard } from '@app/services/server/guards/authenticated-guard-app';
import { deleteEmployeeFromTasksRequest } from '@app/services/server/requests';
import { deleteEmployeeFromTasksRequest, getEmployeeTasksRequest } from '@app/services/server/requests';
import { NextResponse } from 'next/server';

export async function DELETE(req: Request, { params }: { params: { employeeId: string } }) {
export async function GET(req: Request, { params }: { params: { employeeId: string } }) {
const res = new NextResponse();
const { $res, user, tenantId, access_token: bearer_token } = await authenticatedGuard(req, res);
if (!user) return $res('Unauthorized');

const { searchParams } = new URL(req.url);

const { employeeId} = params
const { employeeId } = params;
const { organizationTeamId } = searchParams as unknown as {
organizationTeamId: string;
};

return $res(
await getEmployeeTasksRequest({
tenantId,
employeeId,
organizationTeamId,
bearer_token
})
);
}

export async function DELETE(req: Request, { params }: { params: { employeeId: string } }) {
const res = new NextResponse();
const { $res, user, tenantId, access_token: bearer_token } = await authenticatedGuard(req, res);
if (!user) return $res('Unauthorized');

const { searchParams } = new URL(req.url);

const { employeeId } = params;
const { organizationTeamId } = searchParams as unknown as {
organizationTeamId: string;
};
Expand Down
27 changes: 25 additions & 2 deletions apps/web/app/hooks/features/useTeamTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ import {
getTeamTasksAPI,
updateTaskAPI,
deleteEmployeeFromTasksAPI,
getTasksByIdAPI
getTasksByIdAPI,
getTasksByEmployeeIdAPI
} from '@app/services/client/api';
import { activeTeamState, detailedTaskState, memberActiveTaskIdState, userState } from '@app/stores';
import {
activeTeamState,
detailedTaskState,
// employeeTasksState,
memberActiveTaskIdState,
userState
} from '@app/stores';
import { activeTeamTaskState, tasksByTeamState, tasksFetchingState, teamTasksState } from '@app/stores';
import isEqual from 'lodash/isEqual';
import { useCallback, useEffect } from 'react';
Expand All @@ -40,6 +47,7 @@ export function useTeamTasks() {
const [tasksFetching, setTasksFetching] = useRecoilState(tasksFetchingState);
const authUser = useSyncRef(useRecoilValue(userState));
const memberActiveTaskId = useRecoilValue(memberActiveTaskIdState);
// const [employeeState, setEmployeeState] = useRecoilState(employeeTasksState);

const activeTeam = useRecoilValue(activeTeamState);
const activeTeamRef = useSyncRef(activeTeam);
Expand All @@ -51,6 +59,8 @@ export function useTeamTasks() {
// Queries hooks
const { queryCall, loading, loadingRef } = useQuery(getTeamTasksAPI);
const { queryCall: getTasksByIdQueryCall, loading: getTasksByIdLoading } = useQuery(getTasksByIdAPI);
const { queryCall: getTasksByEmployeeIdQueryCall, loading: getTasksByEmployeeIdLoading } =
useQuery(getTasksByEmployeeIdAPI);

const { queryCall: deleteQueryCall, loading: deleteLoading } = useQuery(deleteTaskAPI);

Expand All @@ -71,6 +81,16 @@ export function useTeamTasks() {
[getTasksByIdQueryCall, setDetailedTask]
);

const getTasksByEmployeeId = useCallback(
(employeeId: string, organizationTeamId: string) => {
return getTasksByEmployeeIdQueryCall(employeeId, organizationTeamId).then((res) => {
// setEmployeeState(res?.data || []);
return res.data;
});
},
[getTasksByEmployeeIdQueryCall]
);

const deepCheckAndUpdateTasks = useCallback(
(responseTasks: ITeamTask[], deepCheck?: boolean) => {
if (responseTasks && responseTasks.length) {
Expand Down Expand Up @@ -398,6 +418,9 @@ export function useTeamTasks() {
updateDescription,
updatePublicity,
handleStatusUpdate,
// employeeState,
getTasksByEmployeeId,
getTasksByEmployeeIdLoading,
activeTeam,
activeTeamId: activeTeam?.id,
setAllTasks,
Expand Down
5 changes: 1 addition & 4 deletions apps/web/app/hooks/features/useUserProfilePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,17 @@ import { useAuthTeamTasks } from './useAuthTeamTasks';
import { useOrganizationTeams } from './useOrganizationTeams';
import { useTaskStatistics } from './useTaskStatistics';
import { useTeamTasks } from './useTeamTasks';
import { useRecoilValue } from 'recoil';
import { activityTypeState } from '@app/stores/activity-type';

export function useUserProfilePage() {
const { activeTeam } = useOrganizationTeams();
const { activeTeamTask, updateTask } = useTeamTasks();
const activityFilter = useRecoilValue(activityTypeState);

const { user: auth } = useAuthenticateUser();
const { getTasksStatsData } = useTaskStatistics();

const params = useParams();
const memberId: string = useMemo(() => {
return (params?.memberId ?? activityFilter.member?.id ?? '') as string;
return (params?.memberId ?? '') as string;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [params]);

Expand Down
68 changes: 68 additions & 0 deletions apps/web/app/hooks/features/useUserSelectedPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { ITeamTask } from '@app/interfaces';
import { useCallback, useEffect } from 'react';
import { useAuthenticateUser } from './useAuthenticateUser';
import { useAuthTeamTasks } from './useAuthTeamTasks';
import { useOrganizationTeams } from './useOrganizationTeams';
import { useTaskStatistics } from './useTaskStatistics';
import { useTeamTasks } from './useTeamTasks';

export function useUserSelectedPage(id?: string) {
const { activeTeam } = useOrganizationTeams();
const { activeTeamTask, updateTask } = useTeamTasks();

const { user: auth } = useAuthenticateUser();
const { getTasksStatsData } = useTaskStatistics();

const memberId: string = id || '';

const members = activeTeam?.members || [];

const matchUser = members.find((m) => {
return m.employee.userId === memberId;
});

const isAuthUser = auth?.employee?.userId === memberId;

const activeUserTeamTask = isAuthUser ? activeTeamTask : matchUser?.lastWorkedTask;

const userProfile = isAuthUser ? auth : matchUser?.employee.user;

const employeeId = isAuthUser ? auth?.employee?.id : matchUser?.employeeId;

/* Filtering the tasks */
const tasksGrouped = useAuthTeamTasks(userProfile);

useEffect(() => {
if (employeeId) {
getTasksStatsData(employeeId);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [employeeId]);

const assignTask = useCallback(
(task: ITeamTask) => {
if (!matchUser?.employeeId) {
return Promise.resolve();
}

return updateTask({
...task,
members: [...task.members, (matchUser?.employeeId ? { id: matchUser?.employeeId } : {}) as any]
});
},
[updateTask, matchUser]
);

return {
isAuthUser,
activeUserTeamTask,
userProfile,
tasksGrouped,
member: matchUser,
assignTask
};
}

export type I_UserProfilePage = ReturnType<typeof useUserSelectedPage>;
28 changes: 28 additions & 0 deletions apps/web/app/hooks/useCheckAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import React, { useCallback, useEffect } from 'react';
import { useQuery } from './useQuery';
import { getDefaultAPI } from '@app/services/client/api';

export function useCheckAPI() {
const { queryCall } = useQuery(getDefaultAPI);
const [isApiWork, setIsApiWork] = React.useState(true);

const checkAPI = useCallback(() => {
queryCall()
.then(() => {
setIsApiWork(true);
})
.catch(() => {
setIsApiWork(false);
});
}, [queryCall]);

useEffect(() => {
checkAPI();
}, [checkAPI]);

return {
isApiWork
};
}
5 changes: 5 additions & 0 deletions apps/web/app/services/client/api/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import api from '../axios';

export function getDefaultAPI() {
return api.get(`/`);
}
1 change: 1 addition & 0 deletions apps/web/app/services/client/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ export * from './organization-projects';

export * from './activity/time-slots';
export * from './activity/activity';
export * from './default';
4 changes: 4 additions & 0 deletions apps/web/app/services/client/api/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,7 @@ export function allTaskTimesheetStatisticsAPI() {
export function deleteEmployeeFromTasksAPI(employeeId: string, organizationTeamId: string) {
return api.delete<DeleteResponse>(`/tasks/employee/${employeeId}?organizationTeamId=${organizationTeamId}`);
}

export function getTasksByEmployeeIdAPI(employeeId: string, organizationTeamId: string) {
return api.get<ITeamTask[]>(`/tasks/employee/${employeeId}?organizationTeamId=${organizationTeamId}`);
}
8 changes: 8 additions & 0 deletions apps/web/app/services/server/requests/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { serverFetch } from '../fetch';

export function getDefaultRequest() {
return serverFetch({
path: `/`,
method: 'GET'
});
}
1 change: 1 addition & 0 deletions apps/web/app/services/server/requests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ export * from './integrations/integration-tenant';
export * from './integrations/types';

export * from './organization-projects';
export * from './default';
19 changes: 19 additions & 0 deletions apps/web/app/services/server/requests/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,22 @@ export function deleteEmployeeFromTasksRequest({
tenantId
});
}

export function getEmployeeTasksRequest({
tenantId,
employeeId,
organizationTeamId,
bearer_token
}: {
tenantId: string;
employeeId: string;
organizationTeamId: string;
bearer_token: string;
}) {
return serverFetch<ITeamTask[]>({
path: `/tasks/employee/${employeeId}?organizationTeamId=${organizationTeamId}`,
method: 'GET',
bearer_token,
tenantId
});
}
7 changes: 6 additions & 1 deletion apps/web/app/stores/team-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@ export const detailedTaskState = atom<ITeamTask | null>({
default: null
});

// export const employeeTasksState = atom<ITeamTask[] | null>({
// key: 'employeeTasksState',
// default: null
// });

export const tasksByTeamState = selector<ITeamTask[]>({
key: 'tasksByTeamState',
get: ({ get }) => {
const tasks = get(teamTasksState);

return tasks
.filter(() => {
return true
return true;
})
.sort((a, b) => moment(b.createdAt).diff(a.createdAt));
}
Expand Down
41 changes: 41 additions & 0 deletions apps/web/components/pages/error/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import SadCry from '@components/ui/svgs/sad-cry';
import { Text } from 'lib/components';
import { useTranslations } from 'next-intl';
import React from 'react';

function ErrorPage({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
const t = useTranslations();

React.useEffect(() => {
console.error(error);
}, [error]);

return (
<div className="mt-28 flex flex-col gap-7 items-center">
<div className="m-auto relative flex justify-center items-center gap-4 text-center ">
<SadCry width={97} height={97} />
<Text className="text-[78px] font-semibold text-chetwodeBlue">{t('pages.error.TITLE')}</Text>
</div>

<Text className="text-[40px] font-bold text-center text-[#282048] dark:text-light--theme">
{t('pages.error.HEADING_TITLE')}
</Text>
<div className="flex flex-col gap-4">
<Text className="text-[20px] font-normal text-center text-gray-400">
{t('pages.error.HEADING_DESCRIPTION')}
</Text>
<button onClick={() => reset()}>Try again</button>
</div>

<div className="p-2 flex flex-col gap-y-3 container max-w-5xl border border-red-500 rounded mt-5">
<h4 className="text-2xl text-red-400 font-medium">{JSON.stringify(error.cause)}</h4>
<p className="text-lg text-red-400 font-semibold">{error.message}</p>
<p className="text-sm text-red-400">{error.stack}</p>
</div>
</div>
);
}

export default ErrorPage;
Loading

0 comments on commit 5f7e676

Please sign in to comment.