diff --git a/src/App.jsx b/src/App.jsx
index e7e5503..b3fdf51 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,12 +1,12 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { useState, useMemo } from "react";
import { ApolloProvider } from '@apollo/client/react';
-
import NotifierContext from "./context/NotifierContext";
import client from './lib/apolloClient';
import HomePage from "./pages/HomePage";
import InfoPage from "./pages/InfoPage";
import ProjectsPage from "./pages/ProjectsPage";
+import ProjectPage from "./pages/ProjectPage";
const App = () => {
const [message, setMessage] = useState("");
@@ -31,6 +31,7 @@ const App = () => {
} />
} />
} />
+ } />
} />
diff --git a/src/components/atoms/ErrorDisplay/ErrorDisplay.jsx b/src/components/atoms/ErrorDisplay/ErrorDisplay.jsx
new file mode 100644
index 0000000..c72080e
--- /dev/null
+++ b/src/components/atoms/ErrorDisplay/ErrorDisplay.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { Alert } from "react-bootstrap";
+
+const ErrorDisplay = ({ message }) => {
+ return (
+
+ Error
+ {message}
+
+ );
+};
+
+export default ErrorDisplay;
diff --git a/src/components/atoms/ErrorDisplay/index.js b/src/components/atoms/ErrorDisplay/index.js
new file mode 100644
index 0000000..7e63fb4
--- /dev/null
+++ b/src/components/atoms/ErrorDisplay/index.js
@@ -0,0 +1 @@
+export { default } from "./ErrorDisplay";
diff --git a/src/components/atoms/SkeletonLoading/ProjectSkeleton/ProjectSkeleton.jsx b/src/components/atoms/SkeletonLoading/ProjectSkeleton/ProjectSkeleton.jsx
new file mode 100644
index 0000000..16d2a02
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectSkeleton/ProjectSkeleton.jsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { SkeletonWrapper } from "./styled";
+
+const ProjectsSkeleton = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default ProjectsSkeleton;
diff --git a/src/components/atoms/SkeletonLoading/ProjectSkeleton/index.js b/src/components/atoms/SkeletonLoading/ProjectSkeleton/index.js
new file mode 100644
index 0000000..166e818
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectSkeleton/index.js
@@ -0,0 +1 @@
+export { default } from "./ProjectSkeleton";
diff --git a/src/components/atoms/SkeletonLoading/ProjectSkeleton/styled.js b/src/components/atoms/SkeletonLoading/ProjectSkeleton/styled.js
new file mode 100644
index 0000000..a0772fd
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectSkeleton/styled.js
@@ -0,0 +1,40 @@
+import styled, { keyframes } from "styled-components";
+
+const wave = keyframes`
+ 0% {
+ transform: translateX(-100%);
+ }
+ 50% {
+ transform: translateX(100%);
+ }
+ 100% {
+ transform: translateX(-100%);
+ }
+`;
+
+export const SkeletonWrapper = styled.div`
+ .skeleton-line {
+ height: 20px;
+ background-color: #f0f0f0;
+ border-radius: 4px;
+ margin-bottom: 10px;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .skeleton-line:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ to right,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.7) 50%,
+ rgba(255, 255, 255, 0) 100%
+ );
+ animation: ${wave} 3.5s infinite;
+ }
+`;
diff --git a/src/components/atoms/SkeletonLoading/ProjectsSkeleton/ProjectsSkeleton.jsx b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/ProjectsSkeleton.jsx
new file mode 100644
index 0000000..6493ce3
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/ProjectsSkeleton.jsx
@@ -0,0 +1,43 @@
+import React from "react";
+import { SkeletonWrapper, TableCol, TableColActions, TableHead, Table } from "./styled";
+
+const ProjectSkeleton = () => {
+ return (
+
+
+
+
+
+ id
+ Name
+ Description
+ Actions
+
+
+
+ {[1, 2, 3, 4, 5, 6, 7].map((item) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default ProjectSkeleton;
diff --git a/src/components/atoms/SkeletonLoading/ProjectsSkeleton/index.js b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/index.js
new file mode 100644
index 0000000..bd72b77
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/index.js
@@ -0,0 +1 @@
+export { default } from "./ProjectsSkeleton";
diff --git a/src/components/atoms/SkeletonLoading/ProjectsSkeleton/styled.js b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/styled.js
new file mode 100644
index 0000000..8a09ece
--- /dev/null
+++ b/src/components/atoms/SkeletonLoading/ProjectsSkeleton/styled.js
@@ -0,0 +1,92 @@
+import styled, { keyframes } from "styled-components";
+
+/* Wave Animation for Skeletons(from Yt Tutorial mixed with original table) */
+const wave = keyframes`
+ 0% {
+ transform: translateX(-100%);
+ }
+ 50% {
+ transform: translateX(100%);
+ }
+ 100% {
+ transform: translateX(-100%);
+ }
+`;
+
+export const SkeletonWrapper = styled.div`
+
+ .skeleton-line {
+ height: 20px;
+ background-color: #f0f0f0;
+ border-radius: 4px;
+ margin-bottom: 10px;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .skeleton-line:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ to right,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.7) 50%,
+ rgba(255, 255, 255, 0) 100%
+ );
+ animation: ${wave} 3.5s infinite;
+ }
+
+ .skeleton-button {
+ width: 100px;
+ height: 35px;
+ background-color: #f0f0f0;
+ border-radius: 4px;
+ margin-right: 10px;
+ display: inline-block;
+ position: relative;
+ overflow: hidden;
+ }
+
+ .skeleton-button:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ to right,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.7) 50%,
+ rgba(255, 255, 255, 0) 100%
+ );
+ animation: ${wave} 3.5s infinite;
+ }
+`;
+
+export const Table = styled.table`
+ margin: 1rem 0;
+ text-align: left;
+ width: 100%;
+ border-collapse: collapse;
+`;
+
+export const TableHead = styled.th`
+ padding 1rem;
+ border-bottom: 1px solid grey;
+`;
+
+export const TableCol = styled.td`
+ padding: 1rem;
+ border-bottom: 1px solid #dee2e6;
+`;
+
+export const TableColActions = styled(TableCol)`
+ width: 20rem;
+`;
+
+export default SkeletonWrapper;
diff --git a/src/components/organisms/ProjectsTable/ProjectsTable.jsx b/src/components/organisms/ProjectsTable/ProjectsTable.jsx
index 098c308..0be62cd 100644
--- a/src/components/organisms/ProjectsTable/ProjectsTable.jsx
+++ b/src/components/organisms/ProjectsTable/ProjectsTable.jsx
@@ -1,9 +1,7 @@
import Button from 'react-bootstrap/Button';
-
import { useState, useContext } from 'react';
-
+import { Link } from "react-router-dom";
import DeleteModal from '../../molecules/DeleteModal';
-
import { Table, TableHead, TableCol, TableColActions } from './styled';
import NotifierContext from "../../../context/NotifierContext";
@@ -46,14 +44,19 @@ const ProjectsTable = ({ projects }) => {
{id}
{name}
{description}
-
-
-
-
+
+
+
+
+
- )
+ );
})}
diff --git a/src/lib/apolloClient.js b/src/lib/apolloClient.js
index b363fdb..73a299a 100644
--- a/src/lib/apolloClient.js
+++ b/src/lib/apolloClient.js
@@ -1,8 +1,9 @@
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
- uri: process.env.REACT_APP_API_URL,
- cache: new InMemoryCache()
+ uri: "https://tasktracker-itis-2024-269a840ae88e.herokuapp.com/graphql",
+ cache: new InMemoryCache(),
+ connectToDevTools: true,
});
export default client;
diff --git a/src/lib/hooks/project.js b/src/lib/hooks/project.js
index 7ad2362..4086b8f 100644
--- a/src/lib/hooks/project.js
+++ b/src/lib/hooks/project.js
@@ -1,29 +1,12 @@
import { useQuery } from "@apollo/client";
-import Projects from "src/graphql/queries/projects";
-import Project from "src/graphql/queries/project";
-
-export const useProjects = () => {
- const { data, loading, error } = useQuery(Projects, {
- fetchPolicy: "cache-and-network",
- });
-
- return {
- loading,
- error,
- projects: data?.projects || [],
- }
-};
-
-
-
-
+import ProjectQuery from "src/graphql/queries/project";
export const useProject = ({ projectId }) => {
- const { data, loading, error } = useQuery(Project, {
+ const { data, loading, error } = useQuery(ProjectQuery, {
fetchPolicy: "cache-and-network",
- variables: { projectId }
+ variables: { projectId },
});
return {
@@ -31,4 +14,4 @@ export const useProject = ({ projectId }) => {
loading,
error,
};
-}
+};
diff --git a/src/lib/hooks/projects.js b/src/lib/hooks/projects.js
new file mode 100644
index 0000000..061efb5
--- /dev/null
+++ b/src/lib/hooks/projects.js
@@ -0,0 +1,16 @@
+import { useQuery } from "@apollo/client";
+
+import Projects from "src/graphql/queries/projects";
+
+export const useProjects = () => {
+ const { data, loading, error } = useQuery(Projects, {
+ fetchPolicy: "cache-and-network",
+ });
+
+ return {
+ loading,
+ error,
+ projects: data?.projects || [],
+ };
+};
+
diff --git a/src/pages/ProjectPage.jsx b/src/pages/ProjectPage.jsx
new file mode 100644
index 0000000..0f92d1c
--- /dev/null
+++ b/src/pages/ProjectPage.jsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { useParams } from "react-router-dom";
+import DefaultTemplate from "../components/templates/DefaultTemplate";
+import { useProject } from "../lib/hooks/project";
+import ErrorDisplay from "../components/atoms/ErrorDisplay";
+import ProjectSkeleton from "../components/atoms/SkeletonLoading/ProjectSkeleton";
+
+const ProjectPage = () => {
+ const { projectId } = useParams();
+ const { loading, error, project } = useProject({ projectId });
+
+ return (
+
+ {error && }
+ {loading && }
+
+
{project.name}
+
{project.description}
+
+
+ );
+};
+
+export default ProjectPage;
diff --git a/src/pages/ProjectsPage.jsx b/src/pages/ProjectsPage.jsx
index dd75acd..e829299 100644
--- a/src/pages/ProjectsPage.jsx
+++ b/src/pages/ProjectsPage.jsx
@@ -1,7 +1,10 @@
+import React from "react";
import DefaultTemplate from "../components/templates/DefaultTemplate";
-import ProjectsTable from '../components/organisms/ProjectsTable';
+import ProjectsTable from "../components/organisms/ProjectsTable";
+import ErrorDisplay from "../components/atoms/ErrorDisplay";
+import ProjectSkeleton from "../components/atoms/SkeletonLoading/ProjectsSkeleton";
-import { useProjects } from "../lib/hooks/project";
+import { useProjects } from "../lib/hooks/projects";
const ProjectsPage = () => {
const { projects, loading, error } = useProjects();
@@ -9,11 +12,8 @@ const ProjectsPage = () => {
return (
Projects List
-
- {error && !loading && Ошибка
}
-
- {loading && Загрузка...
}
-
+ {error && }
+ {loading && }
{projects && !loading && }
);