diff --git a/skypro-kanban/data.js b/skypro-kanban/data.js
new file mode 100644
index 0000000..5abf4b3
--- /dev/null
+++ b/skypro-kanban/data.js
@@ -0,0 +1,79 @@
+export const cardList = [
+ {
+ id: 1,
+ topic: "Web Design",
+ title: "Название задачи",
+ date: "30.10.23",
+ status: "БЕЗ СТАТУСА"
+ },
+ {
+ id: 2,
+ topic: "Research",
+ title: "Название задачи",
+ date: "31.10.23",
+ status: "БЕЗ СТАТУСА"
+ },
+ {
+ id: 3,
+ topic: "Web Design",
+ title: "Название задачи",
+ date: "30.10.23",
+ status: "БЕЗ СТАТУСА"
+ },
+ {
+ id: 4,
+ topic: "Copywriting",
+ title: "Название задачи",
+ date: "31.10.23",
+ status: "БЕЗ СТАТУСА"
+ },
+ {
+ id: 5,
+ topic: "Web Design",
+ title: "Название задачи",
+ date: "31.10.23",
+ status: "БЕЗ СТАТУСА"
+ },
+ {
+ id: 6,
+ topic: "Research",
+ title: "Название задачи",
+ date: "30.10.23",
+ status: "НУЖНО СДЕЛАТЬ"
+ },
+ {
+ id: 7,
+ topic: "Research",
+ title: "Название задачи",
+ date: "30.10.23",
+ status: "В РАБОТЕ"
+ },
+ {
+ id: 8,
+ topic: "Copywriting",
+ title: "Название задачи",
+ date: "30.10.23", status: "В РАБОТЕ"
+ },
+ {
+ id: 9,
+ topic: "Web Design",
+ title: "Название задачи",
+ date: "30.10.23",
+ status: "В РАБОТЕ"
+ },
+ {
+ id: 10,
+ topic: "Research",
+ title: "Название задачи",
+ date: "01.11.23",
+ status: "ТЕСТИРОВАНИЕ"
+ },
+ {
+ id: 11,
+ topic: "Research",
+ title: "Название задачи",
+ date: "01.11.23",
+ status: "ГОТОВО"
+ },
+];
+
diff --git a/skypro-kanban/package-lock.json b/skypro-kanban/package-lock.json
index eb01371..2b59ca3 100644
--- a/skypro-kanban/package-lock.json
+++ b/skypro-kanban/package-lock.json
@@ -10,7 +10,8 @@
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-router-dom": "^6.25.1"
+ "react-router-dom": "^6.25.1",
+ "styled-components": "^6.1.12"
},
"devDependencies": {
"@types/react": "^18.3.3",
@@ -393,6 +394,27 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
+ "license": "MIT"
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -1302,6 +1324,12 @@
"@types/react": "*"
}
},
+ "node_modules/@types/stylis": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
+ "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==",
+ "license": "MIT"
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -1646,6 +1674,15 @@
"node": ">=6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001642",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
@@ -1728,11 +1765,30 @@
"node": ">= 8"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/css-to-react-native": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
+ "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/data-view-buffer": {
@@ -3431,7 +3487,6 @@
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -3678,7 +3733,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
- "dev": true,
"license": "ISC"
},
"node_modules/possible-typed-array-names": {
@@ -3720,6 +3774,12 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "license": "MIT"
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -4094,6 +4154,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4140,7 +4206,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -4262,6 +4327,68 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/styled-components": {
+ "version": "6.1.12",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz",
+ "integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/is-prop-valid": "1.2.2",
+ "@emotion/unitless": "0.8.1",
+ "@types/stylis": "4.2.5",
+ "css-to-react-native": "3.2.0",
+ "csstype": "3.1.3",
+ "postcss": "8.4.38",
+ "shallowequal": "1.1.0",
+ "stylis": "4.3.2",
+ "tslib": "2.6.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0"
+ }
+ },
+ "node_modules/styled-components/node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
+ "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==",
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -4305,6 +4432,12 @@
"node": ">=4"
}
},
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/skypro-kanban/package.json b/skypro-kanban/package.json
index ae7570e..3da99a7 100644
--- a/skypro-kanban/package.json
+++ b/skypro-kanban/package.json
@@ -12,7 +12,8 @@
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-router-dom": "^6.25.1"
+ "react-router-dom": "^6.25.1",
+ "styled-components": "^6.1.12"
},
"devDependencies": {
"@types/react": "^18.3.3",
diff --git a/skypro-kanban/src/App.css b/skypro-kanban/src/App.css
index c094f14..353706c 100644
--- a/skypro-kanban/src/App.css
+++ b/skypro-kanban/src/App.css
@@ -1125,4 +1125,13 @@ body {
.pop-exit__form-group {
display: block;
}
+}
+
+.loading {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-size: 24px;
+ color: #333;
}
\ No newline at end of file
diff --git a/skypro-kanban/src/App.jsx b/skypro-kanban/src/App.jsx
index d985f0f..6c32f36 100644
--- a/skypro-kanban/src/App.jsx
+++ b/skypro-kanban/src/App.jsx
@@ -1,20 +1,58 @@
-import React from 'react';
+
+import React, { useState, useEffect } from 'react';
+import { ThemeProvider } from 'styled-components';
import Header from './components/Header/Header';
import Main from './components/Main/Main';
import PopBrowse from './components/Popups/PopBrowse/PopBrowse';
import PopNewCard from './components/Popups/PopNewCard/PopNewCard';
import PopUser from './components/Popups/PopUser/PopUser';
-import './App.css';
+import { cardList } from '../data';
+import GlobalStyle from './styles/GlobalStyles';
+import { themeColors } from './styles/Themes';
+import { LoadingWrapper, wrapperStyles } from "./styles/CommonStyles";
+import styled from 'styled-components';
+
+const AppWrapper = styled.div`
+ ${wrapperStyles}
+`;
function App() {
+ const [cards, setCards] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isUserPopupOpen, setIsUserPopupOpen] = useState(false);
+
+ useEffect(() => {
+ setTimeout(() => {
+ setCards(cardList);
+ setIsLoading(false);
+ }, 2000);
+ }, []);
+
+ function onCardAdd() {
+ const newCard = {
+ id: cards.length + 1,
+ topic: "Research",
+ title: "Название задачи",
+ date: new Date().toLocaleDateString('ru-RU'),
+ status: "Без статуса"
+ };
+ setCards([...cards, newCard]);
+ }
return (
-
+
+
+
+
+
+ setIsUserPopupOpen(!isUserPopupOpen)} />
+
+ {isLoading ? (
+ Данные загружаются...
+ ) : (
+
+ )}
+
+
);
}
diff --git a/skypro-kanban/src/components/Calendar/Calendar.jsx b/skypro-kanban/src/components/Calendar/Calendar.jsx
index 6e82b3f..0b9e8f5 100644
--- a/skypro-kanban/src/components/Calendar/Calendar.jsx
+++ b/skypro-kanban/src/components/Calendar/Calendar.jsx
@@ -1,80 +1,66 @@
import React from 'react';
-import '../../App.css';
+import {
+ CalendarWrapper,
+ CalendarTitle,
+ CalendarContent,
+ CalendarMonth,
+ CalendarDaysNames,
+ CalendarDayName,
+ CalendarCells,
+ CalendarCell,
+ CalendarNav,
+ NavAction,
+ CalendarPeriod,
+ CalendarP,
+ CalendarBlock,
+ NavActions
+} from './CalendarStyles';
function Calendar() {
return (
-
-
Даты
-
-
-
Сентябрь 2023
-
-
+
+ Даты
+
+
+ Сентябрь 2023
+
+
-
-
-
-
-
-
-
пн
-
вт
-
ср
-
чт
-
пт
-
сб
-
вс
-
-
-
28
-
29
-
30
-
31
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
1
-
-
+
+
+
+
+
+ пн
+ вт
+ ср
+ чт
+ пт
+ сб
+ вс
+
+
+ 28
+ 1
+ 8
+ {/* ... остальные ячейки */}
+
+
-
-
Выберите срок исполнения .
-
-
-
+
+
+ Выберите срок исполнения .
+
+
+
+
);
}
diff --git a/skypro-kanban/src/components/Calendar/CalendarStyles.js b/skypro-kanban/src/components/Calendar/CalendarStyles.js
new file mode 100644
index 0000000..fc74f1d
--- /dev/null
+++ b/skypro-kanban/src/components/Calendar/CalendarStyles.js
@@ -0,0 +1,139 @@
+import styled, { css } from 'styled-components';
+import { themeColors } from '../../styles/Themes';
+
+export const NavActions = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+`;
+
+export const CalendarWrapper = styled.div`
+ width: 182px;
+ margin-bottom: 20px;
+`;
+
+export const CalendarTitle = styled.p`
+ margin-bottom: 14px;
+ padding: 0 7px;
+ color: ${props => props.theme.textPrimary};
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1;
+`;
+
+export const CalendarContent = styled.div`
+ margin-bottom: 12px;
+`;
+
+export const CalendarMonth = styled.div`
+ color: ${themeColors.textSecondary};
+ font-size: 14px;
+ line-height: 25px;
+ font-weight: 600;
+`;
+
+export const CalendarDaysNames = styled.div`
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: center;
+ justify-content: space-between;
+ margin: 7px 0;
+ padding: 0 7px;
+`;
+
+export const CalendarDayName = styled.div`
+ color: ${themeColors.textSecondary};
+ font-size: 10px;
+ font-weight: 500;
+ line-height: normal;
+ letter-spacing: -0.2px;
+`;
+
+export const CalendarCells = styled.div`
+ width: 182px;
+ height: 126px;
+ display: flex;
+ flex-wrap: wrap;
+`;
+
+export const CalendarCell = styled.div`
+ width: 22px;
+ height: 22px;
+ margin: 2px;
+ border-radius: 50%;
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: center;
+ justify-content: center;
+ color: ${themeColors.textSecondary};
+ font-size: 10px;
+ line-height: 1;
+ letter-spacing: -0.2px;
+ cursor: pointer;
+
+ ${props => props.$isOtherMonth && `
+ opacity: 0;
+ `}
+
+ ${props => props.$isCurrent && `
+ font-weight: 700;
+ `}
+
+ &._other-month {
+ opacity: 0;
+ }
+
+ &._cell-day:hover {
+ color: ${themeColors.textSecondary};
+ background-color: ${themeColors.background};
+ }
+
+ &._active-day {
+ background-color: ${themeColors.secondary};
+ color: ${themeColors.cardBackground};
+ }
+
+ &._current {
+ font-weight: 700;
+ }
+`;
+
+export const CalendarNav = styled.div`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 14px;
+ padding: 0 7px;
+`;
+
+export const NavAction = styled.div`
+ width: 18px;
+ height: 25px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ svg {
+ fill: ${themeColors.textSecondary};
+ }
+`;
+
+export const CalendarPeriod = styled.div`
+ padding: 0 7px;
+`;
+
+export const CalendarP = styled.p`
+ color: ${themeColors.textSecondary};
+ font-size: 10px;
+ line-height: 1;
+
+ span {
+ color: ${themeColors.textPrimary};
+ }
+`;
+
+export const CalendarBlock = styled.div`
+ display: block;
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Card/Card.jsx b/skypro-kanban/src/components/Card/Card.jsx
index 27d3e09..cfaaecc 100644
--- a/skypro-kanban/src/components/Card/Card.jsx
+++ b/skypro-kanban/src/components/Card/Card.jsx
@@ -1,37 +1,44 @@
import React from 'react';
-import '../../App.css';
+import {
+ CardWrapper,
+ CardGroup,
+ CardTheme,
+ ThemeText,
+ CardButton,
+ ButtonDot,
+ CardContent,
+ CardTitle,
+ CardDate,
+ DateText
+} from './CardStyles';
-function Card({ title, category, date }) {
- const getThemeClass = (category) => {
- switch (category.toLowerCase()) {
- case 'web design': return '_orange';
- case 'research': return '_green';
- case 'copywriting': return '_purple';
- default: return '';
+function Card({ title, topic, date }) {
+ const getTopicClassName = (topic) => {
+ switch (topic.toLowerCase()) {
+ case 'web design': return 'web-design';
+ case 'research': return 'research';
+ case 'copywriting': return 'copywriting';
+ default: return 'default';
}
};
- const themeClass = getThemeClass(category);
+ const topicClassName = getTopicClassName(topic);
return (
-
-
-
-
- {title}
-
-
+
+
+
+ {topic}
+
+
+
+
+
+
+
+
+ {title}
+
- {date}
-
-
-
+ {date}
+
+
+
);
}
diff --git a/skypro-kanban/src/components/Card/CardStyles.js b/skypro-kanban/src/components/Card/CardStyles.js
new file mode 100644
index 0000000..70f76c2
--- /dev/null
+++ b/skypro-kanban/src/components/Card/CardStyles.js
@@ -0,0 +1,84 @@
+import styled from 'styled-components';
+import { cardContainerStyles } from '../../styles/CommonStyles';
+
+export const CardWrapper = styled.div`
+ ${cardContainerStyles}
+ width: 220px;
+ height: 130px;
+ display: flex;
+ flex-direction: column;
+ padding: 15px 13px 19px;
+ margin-bottom: 15px; // Добавляем отступ снизу
+`;
+
+export const CardGroup = styled.div`
+ width: 100%;
+ height: 20px;
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+`;
+
+export const CardTheme = styled.div`
+ width: auto;
+ height: 20px;
+ padding: 5px 14px;
+ border-radius: 18px;
+ background-color: ${({ $topic, theme }) =>
+ theme.topicStyles[$topic]?.backgroundColor || theme.topicStyles.default.backgroundColor};
+`;
+
+export const ThemeText = styled.p`
+ font-size: 10px;
+ font-weight: 600;
+ line-height: 10px;
+ color: ${({ $topic, theme }) =>
+ theme.topicStyles[$topic]?.color || theme.topicStyles.default.color};
+`;
+
+export const CardButton = styled.div`
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ padding: 2px;
+`;
+
+export const ButtonDot = styled.div`
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background-color: ${props => props.theme.textSecondary};
+`;
+
+export const CardContent = styled.div`
+ height: 64px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: space-between;
+`;
+
+export const CardTitle = styled.h3`
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 18px;
+ color: ${props => props.theme.textPrimary};
+ margin-bottom: 10px;
+`;
+
+export const CardDate = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+`;
+
+export const DateText = styled.p`
+ margin-left: 6px;
+ font-size: 10px;
+ line-height: 13px;
+ color: ${props => props.theme.textSecondary};
+ letter-spacing: 0.2px;
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Column/Column.jsx b/skypro-kanban/src/components/Column/Column.jsx
index 8bc4205..bb4bdd8 100644
--- a/skypro-kanban/src/components/Column/Column.jsx
+++ b/skypro-kanban/src/components/Column/Column.jsx
@@ -1,53 +1,25 @@
-// import React from 'react';
-// import Card from '../Card/Card';
-// import '../../App.css';
-
-// function Column({ title, tasks }) {
-// return (
-//
-//
-//
-// {tasks.map(task => (
-//
-//
-//
-// ))}
-//
-//
-// );
-// }
-
-// export default Column;
-
import React from 'react';
import Card from '../Card/Card';
-import '../../App.css';
+import { ColumnWrapper, ColumnTitle, CardsContainer } from './ColumnStyles';
function Column({ title, tasks }) {
return (
-
-
-
- {tasks.map((task, index) => (
-
-
-
+
+
+ {tasks.map((task) => (
+
))}
-
-
+
+
);
}
-export default Column;
+export default Column;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Column/ColumnStyles.js b/skypro-kanban/src/components/Column/ColumnStyles.js
new file mode 100644
index 0000000..a150a60
--- /dev/null
+++ b/skypro-kanban/src/components/Column/ColumnStyles.js
@@ -0,0 +1,32 @@
+import styled from 'styled-components';
+
+export const ColumnWrapper = styled.div`
+ width: 20%;
+ margin: 0 auto;
+ display: block;
+`;
+
+export const ColumnTitle = styled.div`
+ padding: 0 10px;
+ margin: 15px 0;
+
+ p {
+ color: ${props => props.theme.textSecondary};
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1;
+ text-transform: uppercase;
+ }
+`;
+
+export const CardsContainer = styled.div`
+ width: 100%;
+ display: block;
+ position: relative;
+`;
+export const CardsItem = styled.div`
+ padding: 5px;
+ animation-name: card-animation;
+ animation-duration: 500ms;
+ animation-timing-function: linear;
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Header/Header.jsx b/skypro-kanban/src/components/Header/Header.jsx
index 3741610..e940164 100644
--- a/skypro-kanban/src/components/Header/Header.jsx
+++ b/skypro-kanban/src/components/Header/Header.jsx
@@ -1,45 +1,72 @@
-import React from 'react';
-import '../../App.css';
+import React, { useState } from 'react';
+import {
+ HeaderWrapper,
+ HeaderBlock,
+ HeaderLogo,
+ HeaderNav,
+ HeaderBtnMainNew,
+ HeaderUser,
+ UserMenu,
+ UserName,
+ UserEmail,
+ ThemeToggleWrapper,
+ ThemeToggle,
+ ToggleInput,
+ ToggleSlider,
+ ExitButton,
+ LogoLink,
+ LogoImage
+} from './HeaderStyles';
+import styled from 'styled-components';
+import { containerStyles } from "../../styles/CommonStyles";
+
+const Container = styled.div`
+ ${containerStyles}
+`;
+
+function Header({ onCardAdd, onThemeToggle }) {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
-function Header() {
const handleUserClick = (e) => {
- e.preventDefault(); // Предотвращаем действие по умолчанию
- const targetElement = document.querySelector(e.currentTarget.getAttribute('href'));
- if (targetElement) {
- targetElement.style.display = targetElement.style.display === 'block' ? 'none' : 'block';
- }
+ e.preventDefault();
+ setIsMenuOpen(!isMenuOpen);
};
return (
-
+
+
+
+
+
+
+
+
+
+
+
+ Создать новую задачу
+
+
+ Ivan Ivanov
+
+ {isMenuOpen && (
+
+ Ivan Ivanov
+ ivan.ivanov@gmail.com
+
+ Темная тема
+
+
+
+
+
+ Выйти
+
+ )}
+
+
+
+
);
}
diff --git a/skypro-kanban/src/components/Header/HeaderStyles.js b/skypro-kanban/src/components/Header/HeaderStyles.js
new file mode 100644
index 0000000..fd19ea2
--- /dev/null
+++ b/skypro-kanban/src/components/Header/HeaderStyles.js
@@ -0,0 +1,195 @@
+import styled, { css } from 'styled-components';
+import { buttonStyles } from '../../styles/CommonStyles';
+
+export const HeaderWrapper = styled.header`
+ width: 100%;
+ margin: 0 auto;
+ background-color: ${props => props.theme.cardBackground};
+`;
+
+export const HeaderBlock = styled.div`
+ height: 70px;
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: center;
+ justify-content: space-between;
+ position: relative;
+ top: 0;
+ left: 0;
+ padding: 0 10px;
+`;
+
+export const HeaderLogo = styled.div`
+ img {
+ width: 85px;
+ }
+`;
+
+export const HeaderNav = styled.nav`
+ max-width: 290px;
+ padding: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+export const HeaderBtnMainNew = styled.button`
+ ${buttonStyles}
+ width: auto;
+ min-width: 178px;
+ height: 30px;
+ padding: 0 20px;
+ margin-right: 20px;
+ font-size: 14px;
+ white-space: nowrap;
+
+ @media (max-width: 1200px) {
+ font-size: 12px;
+ padding: 0 15px;
+ }
+
+ @media (max-width: 992px) {
+ min-width: 150px;
+ margin-right: 10px;
+ }
+`;
+
+export const HeaderUser = styled.a`
+ height: 20px;
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: center;
+ justify-content: center;
+ font-size: 14px;
+ line-height: 20px;
+ color: ${props => props.theme.primary};
+
+ &::after {
+ content: "";
+ display: block;
+ width: 6px;
+ height: 6px;
+ border-radius: 1px;
+ border-left: 1.9px solid ${props => props.theme.primary};
+ border-bottom: 1.9px solid ${props => props.theme.primary};
+ transform: rotate(-45deg);
+ margin: -6px 0 0 5px;
+ padding: 0;
+ }
+`;
+
+export const UserMenu = styled.div`
+ position: absolute;
+ top: 61px;
+ right: 0;
+ width: 213px;
+ background-color: ${props => props.theme.cardBackground};
+ border-radius: 10px;
+ box-shadow: 0px 10px 39px rgba(26, 56, 101, 0.21);
+ padding: 34px;
+ z-index: 10;
+ text-align: center;
+`;
+
+export const UserName = styled.p`
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 21px;
+ color: ${props => props.theme.textPrimary};
+ margin-bottom: 4px;
+`;
+
+export const UserEmail = styled.p`
+ font-size: 14px;
+ line-height: 21px;
+ color: ${props => props.theme.textSecondary};
+ margin-bottom: 10px;
+`;
+
+export const ThemeToggleWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 15px;
+`;
+
+export const ThemeToggle = styled.label`
+ position: relative;
+ display: inline-block;
+ width: 32px;
+ height: 20px;
+`;
+
+export const ToggleInput = styled.input`
+ opacity: 0;
+ width: 0;
+ height: 0;
+
+ &:checked + span {
+ background-color: #565EEF;
+ }
+
+ &:checked + span:before {
+ transform: translateX(12px);
+ }
+`;
+
+export const ToggleSlider = styled.span`
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #C1C1C1;
+ transition: .4s;
+ border-radius: 34px;
+
+ &:before {
+ position: absolute;
+ content: "";
+ height: 16px;
+ width: 16px;
+ left: 2px;
+ bottom: 2px;
+ background-color: white;
+ transition: .4s;
+ border-radius: 50%;
+ }
+`;
+
+export const ExitButton = styled.button`
+ width: 72px;
+ height: 30px;
+ background-color: transparent;
+ border: 1px solid ${props => props.theme.primary || '#565EEF'};
+ border-radius: 4px;
+ color: ${props => props.theme.primary || '#565EEF'};
+ font-size: 14px;
+ line-height: 21px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background-color 0.3s ease, color 0.3s ease;
+
+ &:hover {
+ background-color: ${props => props.theme.primary || '#565EEF'};
+ color: ${props => props.theme.cardBackground || '#FFFFFF'};
+ }
+`;
+
+export const LogoLink = styled.a`
+ display: block;
+`;
+
+export const LogoImage = styled.img`
+ width: 85px;
+ display: ${props => props.$isLight ? 'block' : 'none'};
+
+ ${props => props.$isDark && `
+ display: none;
+ `}
+
+ @media (prefers-color-scheme: dark) {
+ display: ${props => props.$isDark ? 'block' : 'none'};
+ }
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Main/Main.jsx b/skypro-kanban/src/components/Main/Main.jsx
index 05ffb89..5114679 100644
--- a/skypro-kanban/src/components/Main/Main.jsx
+++ b/skypro-kanban/src/components/Main/Main.jsx
@@ -1,44 +1,38 @@
import React from 'react';
import Column from '../Column/Column';
-import '../../App.css';
-
-const statusList = ["БЕЗ СТАТУСА", "НУЖНО СДЕЛАТЬ", "В РАБОТЕ", "ТЕСТИРОВАНИЕ", "ГОТОВО"];
-
-const tasks = [
- { id: 1, title: "Название задачи", category: "Web Design", status: "БЕЗ СТАТУСА", date: "30.10.23" },
- { id: 2, title: "Название задачи", category: "Research", status: "БЕЗ СТАТУСА", date: "31.10.23" },
- { id: 3, title: "Название задачи", category: "Web Design", status: "БЕЗ СТАТУСА", date: "30.10.23" },
- { id: 4, title: "Название задачи", category: "Copywriting", status: "БЕЗ СТАТУСА", date: "31.10.23" },
- { id: 5, title: "Название задачи", category: "Web Design", status: "БЕЗ СТАТУСА", date: "31.10.23" },
-
- { id: 6, title: "Название задачи", category: "Research", status: "НУЖНО СДЕЛАТЬ", date: "30.10.23" },
-
- { id: 7, title: "Название задачи", category: "Research", status: "В РАБОТЕ", date: "30.10.23" },
- { id: 8, title: "Название задачи", category: "Copywriting", status: "В РАБОТЕ", date: "30.10.23" },
- { id: 9, title: "Название задачи", category: "Web Design", status: "В РАБОТЕ", date: "30.10.23" },
-
- { id: 10, title: "Название задачи", category: "Research", status: "ТЕСТИРОВАНИЕ", date: "01.11.23" },
-
- { id: 11, title: "Название задачи", category: "Research", status: "ГОТОВО", date: "01.11.23" },
+import { MainWrapper, MainBlock, MainContent } from './MainStyles';
+import styled from 'styled-components';
+import { containerStyles } from "../../styles/CommonStyles";
+
+const Container = styled.div`
+ ${containerStyles}
+`;
+
+const statusList = [
+ "Без статуса",
+ "Нужно сделать",
+ "В работе",
+ "Тестирование",
+ "Готово",
];
-function Main() {
+function Main({ cards }) {
return (
-
-
-
-
- {statusList.map(status => (
+
+
+
+
+ {statusList.map((status) => (
task.status === status)}
+ tasks={cards.filter((card) => card.status.toLowerCase() === status.toLowerCase())}
/>
))}
-
-
-
-
+
+
+
+
);
}
diff --git a/skypro-kanban/src/components/Main/MainStyles.js b/skypro-kanban/src/components/Main/MainStyles.js
new file mode 100644
index 0000000..7a3d8a6
--- /dev/null
+++ b/skypro-kanban/src/components/Main/MainStyles.js
@@ -0,0 +1,17 @@
+import styled from 'styled-components';
+
+export const MainWrapper = styled.main`
+ width: 100%;
+ background-color: ${props => props.theme.background};
+`;
+
+export const MainBlock = styled.div`
+ width: 100%;
+ margin: 0 auto;
+ padding: 25px 0 49px;
+`;
+
+export const MainContent = styled.div`
+ width: 100%;
+ display: flex;
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/components/Popups/PopNewCard/PopNewCard.jsx b/skypro-kanban/src/components/Popups/PopNewCard/PopNewCard.jsx
index bb3fa3f..c11a503 100644
--- a/skypro-kanban/src/components/Popups/PopNewCard/PopNewCard.jsx
+++ b/skypro-kanban/src/components/Popups/PopNewCard/PopNewCard.jsx
@@ -1,8 +1,26 @@
-import React from 'react';
+import React, { useState } from 'react';
import Calendar from '../../Calendar/Calendar';
import '../../../App.css';
-function PopNewCard() {
+function PopNewCard({ onCardAdd }) {
+ const [title, setTitle] = useState('');
+ const [description, setDescription] = useState('');
+ const [category, setCategory] = useState('Web Design');
+
+ const handleCreateNewTask = (e) => {
+ e.preventDefault();
+ onCardAdd({
+ id: Date.now(),
+ title: title,
+ topic: topic,
+ date: new Date().toLocaleDateString('ru-RU'),
+ status: "Без статуса"
+ });
+ setTitle('');
+ setDescription('');
+ setCategory('Web Design');
+ };
+
return (
@@ -14,11 +32,27 @@ function PopNewCard() {
@@ -26,18 +60,33 @@ function PopNewCard() {
Категория
-
+
setCategory('Web Design')}
+ >
Web Design
-
+
setCategory('Research')}
+ >
Research
-
+
setCategory('Copywriting')}
+ >
Copywriting
-
+
@@ -45,4 +94,4 @@ function PopNewCard() {
);
}
-export default PopNewCard;
\ No newline at end of file
+export default PopNewCard;
diff --git a/skypro-kanban/src/components/Popups/PopUser/PopUser.jsx b/skypro-kanban/src/components/Popups/PopUser/PopUser.jsx
index a5b3b1a..981ad94 100644
--- a/skypro-kanban/src/components/Popups/PopUser/PopUser.jsx
+++ b/skypro-kanban/src/components/Popups/PopUser/PopUser.jsx
@@ -1,21 +1,26 @@
import React from 'react';
-import '../../../App.css';
+import {
+ PopUserWrapper,
+ UserName,
+ UserEmail,
+ ThemeToggle,
+ ExitButton
+} from './PopUserStyles';
function PopUser({ isOpen }) {
if (!isOpen) return null;
-
return (
-
-
Ivan Ivanov
-
ivan.ivanov@gmail.com
-
-
-
+
+
);
}
diff --git a/skypro-kanban/src/components/Popups/PopUser/PopUserStyles.js b/skypro-kanban/src/components/Popups/PopUser/PopUserStyles.js
new file mode 100644
index 0000000..ce4f6f1
--- /dev/null
+++ b/skypro-kanban/src/components/Popups/PopUser/PopUserStyles.js
@@ -0,0 +1,45 @@
+import styled from 'styled-components';
+import { buttonStyles, cardContainerStyles } from '../../../styles/CommonStyles';
+
+export const PopUserWrapper = styled.div`
+ ${cardContainerStyles}
+ display: ${props => props.isOpen ? 'block' : 'none'};
+ position: absolute;
+ top: 61px;
+ right: 0;
+ width: 213px;
+ padding: 34px;
+ text-align: center;
+ z-index: 2;
+`;
+
+export const UserName = styled.p`
+ color: ${props => props.theme.textPrimary};
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 21px;
+ margin-bottom: 4px;
+`;
+
+export const UserEmail = styled.p`
+ color: ${props => props.theme.textSecondary};
+ font-size: 14px;
+ line-height: 21px;
+ margin-bottom: 10px;
+`;
+
+export const ThemeToggle = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 30px;
+`;
+
+export const ExitButton = styled.button`
+ ${buttonStyles}
+ width: 72px;
+ height: 30px;
+ background: transparent;
+ color: ${props => props.theme.primary};
+ border: 1px solid ${props => props.theme.primary};
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/main.jsx b/skypro-kanban/src/main.jsx
index fd70880..5f30834 100644
--- a/skypro-kanban/src/main.jsx
+++ b/skypro-kanban/src/main.jsx
@@ -2,6 +2,7 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './App.css';
+import '../data.js';
ReactDOM.createRoot(document.getElementById('root')).render(
diff --git a/skypro-kanban/src/styles/CommonStyles.js b/skypro-kanban/src/styles/CommonStyles.js
new file mode 100644
index 0000000..3ca7feb
--- /dev/null
+++ b/skypro-kanban/src/styles/CommonStyles.js
@@ -0,0 +1,50 @@
+import { themeColors } from './Themes';
+import styled, { css } from 'styled-components';
+
+export const buttonStyles = css`
+ cursor: pointer;
+ border: none;
+ outline: none;
+ border-radius: 4px;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1;
+ color: ${themeColors.cardBackground};
+ background-color: ${themeColors.primary};
+ padding: 8px 20px;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background-color: ${themeColors.secondary};
+ }
+`;
+
+export const cardContainerStyles = css`
+ background-color: ${props => props.theme.cardBackground};
+ border-radius: 10px;
+ box-shadow: 0px 10px 39px 0px rgba(26, 56, 101, 0.21);
+`;
+
+export const LoadingWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-size: 24px;
+ color: #333;
+`;
+
+export const wrapperStyles = css`
+ max-width: 100%;
+ width: 100vw;
+ min-height: 100vh;
+ overflow: hidden;
+ background-color: #F1F1F1;
+`;
+
+export const containerStyles = css`
+ max-width: 1260px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0 30px;
+`;
\ No newline at end of file
diff --git a/skypro-kanban/src/styles/GlobalStyles.js b/skypro-kanban/src/styles/GlobalStyles.js
new file mode 100644
index 0000000..a94b087
--- /dev/null
+++ b/skypro-kanban/src/styles/GlobalStyles.js
@@ -0,0 +1,69 @@
+import { createGlobalStyle } from 'styled-components';
+
+const GlobalStyle = createGlobalStyle`
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ *:before,
+ *:after {
+ box-sizing: border-box;
+ }
+
+ body {
+ font-family: "Roboto", Arial, Helvetica, sans-serif;
+ color: ${props => props.theme.textPrimary};
+ background-color: ${props => props.theme.background};
+
+ *:focus {
+ outline: none;
+ }
+ }
+
+ input, textarea, button {
+ font-family: inherit;
+ }
+
+ a,
+ a:visited {
+ text-decoration: none;
+ cursor: pointer;
+ }
+
+ button {
+ cursor: pointer;
+ outline: none;
+ }
+
+ ul li {
+ list-style: none;
+ }
+
+ .wrapper {
+ max-width: 100%;
+ width: 100vw;
+ min-height: 100vh;
+ overflow: hidden;
+ background-color: ${props => props.theme.background};
+ }
+
+ .container {
+ max-width: 1260px;
+ width: 100%;
+ margin: 0 auto;
+ padding: 0 30px;
+ }
+
+ .loading {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ font-size: 24px;
+ color: #333;
+ }
+`;
+
+export default GlobalStyle;
\ No newline at end of file
diff --git a/skypro-kanban/src/styles/Themes.js b/skypro-kanban/src/styles/Themes.js
new file mode 100644
index 0000000..2cece32
--- /dev/null
+++ b/skypro-kanban/src/styles/Themes.js
@@ -0,0 +1,26 @@
+export const themeColors = {
+ primary: "#565EEF",
+ secondary: "#94A6BE",
+ background: "#EAEEF6",
+ cardBackground: "#FFFFFF",
+ textPrimary: "#000000",
+ textSecondary: "#94A6BE",
+ topicStyles: {
+ 'web-design': {
+ backgroundColor: "#FFE4C2",
+ color: "#FF6D00"
+ },
+ 'research': {
+ backgroundColor: "#B4FDD1",
+ color: "#06B16E"
+ },
+ 'copywriting': {
+ backgroundColor: "#E9D4FF",
+ color: "#9A48F1"
+ },
+ 'default': {
+ backgroundColor: "#94A6BE",
+ color: "#FFFFFF"
+ }
+ }
+};
\ No newline at end of file