diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index e403b63..6787a40 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -8,8 +8,10 @@
"name": "frontend",
"version": "0.0.0",
"dependencies": {
- "react": "^19.1.1",
- "react-dom": "^19.1.1"
+ "js-cookie": "^3.0.5",
+ "react": "^19.2.0",
+ "react-cookie": "^8.0.1",
+ "react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
@@ -1413,6 +1415,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz",
+ "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
+ "license": "MIT",
+ "dependencies": {
+ "hoist-non-react-statics": "^3.3.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1424,7 +1438,6 @@
"version": "19.1.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
"integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
@@ -1941,6 +1954,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1960,7 +1982,6 @@
"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/debug": {
@@ -2431,6 +2452,15 @@
"node": ">=8"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2508,6 +2538,15 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2883,26 +2922,46 @@
"license": "MIT"
},
"node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "node_modules/react-cookie": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-8.0.1.tgz",
+ "integrity": "sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA==",
"license": "MIT",
"dependencies": {
- "scheduler": "^0.26.0"
+ "@types/hoist-non-react-statics": "^3.3.6",
+ "hoist-non-react-statics": "^3.3.2",
+ "universal-cookie": "^8.0.0"
},
"peerDependencies": {
- "react": "^19.1.1"
+ "react": ">= 16.3.0"
}
},
+ "node_modules/react-dom": {
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/react-refresh": {
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@@ -3000,9 +3059,9 @@
}
},
"node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/semver": {
@@ -3199,6 +3258,15 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
+ "node_modules/universal-cookie": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.0.1.tgz",
+ "integrity": "sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.2"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index b92f39c..d9c6477 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,8 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
- "react": "^19.1.1",
- "react-dom": "^19.1.1"
+ "js-cookie": "^3.0.5",
+ "react": "^19.2.0",
+ "react-cookie": "^8.0.1",
+ "react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
diff --git a/frontend/src/App.css b/frontend/src/App.css
index 8c6c340..cd6817a 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -9,3 +9,55 @@
.mainText {
color: #5f5e5e;
}
+
+/* General table styles */
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 20px 0;
+ font-size: 1em;
+ font-family: 'Arial', sans-serif;
+}
+
+/* Table header styles */
+th {
+ background-color: #4CAF50; /* Green */
+ color: white;
+ padding: 10px;
+ text-align: left;
+}
+
+/* Table cell styles */
+td {
+ padding: 10px;
+ border: 1px solid #ddd;
+}
+
+/* Zebra striping for table rows */
+tr:nth-child(even) {
+ background-color: #f2f2f2;
+}
+
+/* Responsive table styles */
+@media (max-width: 600px) {
+ table {
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ }
+
+ th, td {
+ padding: 8px;
+ font-size: 0.9em;
+ }
+
+ th {
+ font-weight: bold;
+ background-color: #3C8A3C;
+ }
+}
+
+/* Hover effect for rows */
+tr:hover {
+ background-color: #f1f1f1;
+}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 1f309a6..57c78b3 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,13 +1,17 @@
import './App.css'
import Greeting from './components/Greeting'
import Hosting from './components/Hosting'
+import NameSelector from './components/NameSelector'
+import Program from './components/Program'
function App() {
return (
<>
+
+
>
)
}
diff --git a/frontend/src/components/Greeting.tsx b/frontend/src/components/Greeting.tsx
index 1bfe1a2..14a3c54 100644
--- a/frontend/src/components/Greeting.tsx
+++ b/frontend/src/components/Greeting.tsx
@@ -1,13 +1,20 @@
+import { useCookies } from "react-cookie";
+
function Greeting() {
+ const [cookie] = useCookies(['userName'])
+ const userName = cookie.userName;
+
return (
<>
Приглашение на Новый год 2025-2026 🎄
- Дорогие, Пятки! 🦶
+
+ {userName ? <>{userName}> : <>Дорогая пятка!>}
+ ! 🦶
+
+ Приглашаем тебя отпраздновать предстоящий Новый Год 2025-2026 с нами в сосновой избе, в которой, ко всему прочему, будет праздноваться годовщина нашей жизни в ней!
- Приглашаем вас отпраздновать предстоящий Новый Год 2025-2026 с нами в сосновой избе, в которой, ко всему прочему, будет праздноваться годовщина нашей жизни в ней!
-
- Мы ожидаем вас с 30.12.2025 . Праздник обычно длится до 01.01.2025 , но если вам будет безумно плохо, то иожно остаться и до второго числа.
+ Наши двери открыты с 30.12.2025 . Праздник обычно длится до 01.01.2025 , но если тебе или твоим спутникам будет безумно плохо, то можно остаться и до второго числа.
>
)
diff --git a/frontend/src/components/Hosting.tsx b/frontend/src/components/Hosting.tsx
index 410cf28..a8c8b7c 100644
--- a/frontend/src/components/Hosting.tsx
+++ b/frontend/src/components/Hosting.tsx
@@ -1,29 +1,25 @@
-import { useState } from "react";
import useFetchHosting from "../utils/fetchHosting";
import { useNotification } from "../NotificationContext";
+import { useCookies } from "react-cookie";
interface ReserveButtonProps {
- update: (name: string, id: number) => void,
- reservedBy: string,
- id: number,
+ update: (name: string, id: number) => void,
+ reservedBy: string,
+ id: number,
}
const ReserveButton: React.FC = (props) => {
const { reservedBy, update, id } = props;
- const [name, setName] = useState(reservedBy || ''); // Default to empty if not reserved
+ const [cookie] = useCookies(['userName'])
+ const userName = cookie.userName;
const isReserved = reservedBy !== '';
const notify = useNotification();
const handleReserve = async () => {
- if (!name.trim()) {
- notify('Поле имени не может быть пустым', 'error');
- return;
- }
-
try {
- await update(name, id); // Await the update call
- notify(`Успешно забронировано для ${name}`, 'success'); // Move success notification here
+ await update(userName, id);
+ notify(`Успешно забронировано для ${userName}`, 'success');
} catch (error) {
notify(`Не удалось забронировать: ${error instanceof Error ? error.message : 'Unknown error'}`, 'error');
}
@@ -31,15 +27,8 @@ const ReserveButton: React.FC = (props) => {
return (
<>
- setName(e.target.value)}
- placeholder="Введите ваше имя"
- disabled={isReserved} // Disable input if already reserved
- />
- {isReserved ? 'Занято' : 'Занять'}
+ {isReserved ? `Занято гостем ${reservedBy}` : 'Занять'}
>
);
@@ -52,16 +41,12 @@ function Hosting() {
<>
Поселение
- Мы готовы вас приютить в наших 150 квадратах. Хоть дом и кажется большим,
- но больше 5 гостей уместить будет сложно (но возможно!). При этом, если
+ Мы готовы приютить в наших 150 квадратах всех. У нас есть 6 спальных мест. При этом, если
вы не хотите тесниться, то рядом с нами есть
- отель ,
- а так же
+ отель , а так же
кэмпинг-виллы
(Лучше бронировать заранее если есть надобность. Оба в 1-1,5км от нашего дома).
-
- На данный момент мы можем вместить 5 гостей без лишних усилий.
- Это подразумевает:
+ Спальные места:
{loading && Loading...
}
{error && Error
}
@@ -71,7 +56,7 @@ function Hosting() {
Размещение
- Вместительность
+ Спальных мест
Бронирование
@@ -80,13 +65,14 @@ function Hosting() {
{item.name}
{item.capacity}
- {}
+ { }
))}
)}
+
>
);
}
diff --git a/frontend/src/components/NameSelector.tsx b/frontend/src/components/NameSelector.tsx
new file mode 100644
index 0000000..cc16807
--- /dev/null
+++ b/frontend/src/components/NameSelector.tsx
@@ -0,0 +1,64 @@
+import { useState } from 'react';
+import { useCookies } from 'react-cookie';
+import { GUESTS } from '../constants/constants';
+
+const NameSelector = () => {
+ const [cookie, setCookie] = useCookies(['userName']);
+ const [selectedName, setSelectedName] = useState(cookie.userName);
+
+ const handleSelect = (name: string) => {
+ if (name)
+ setSelectedName(name);
+ setCookie('userName', name, { path: '/' });
+ };
+
+ if (selectedName !== undefined) {
+ console.log('Selected', selectedName)
+ return null;
+ }
+
+ return (
+
+
Выбери себя
+
+ {GUESTS.map((name) => (
+ handleSelect(name)}>
+ {name}
+
+ ))}
+
+
+ );
+};
+
+const styles = {
+ container: {
+ position: 'fixed' as 'fixed',
+ top: 0,
+ left: 0,
+ width: '100vw',
+ height: '100vh',
+ backgroundColor: 'rgba(0, 0, 0, 1)',
+ display: 'flex',
+ flexDirection: 'column' as 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ color: '#fff',
+ zIndex: 1000,
+ },
+ title: {
+ marginBottom: '20px',
+ },
+ namesContainer: {
+ display: 'flex',
+ flexDirection: 'column' as 'column',
+ },
+ button: {
+ margin: '10px',
+ padding: '10px 20px',
+ fontSize: '16px',
+ cursor: 'pointer',
+ },
+};
+
+export default NameSelector;
diff --git a/frontend/src/components/Program.tsx b/frontend/src/components/Program.tsx
new file mode 100644
index 0000000..c17656a
--- /dev/null
+++ b/frontend/src/components/Program.tsx
@@ -0,0 +1,89 @@
+const Program = () => {
+ return (
+
+
Программа
+
+
30 декабря
+
+
+
+ Время
+ Действие
+
+
+
+
+ 15-18
+ Гости приезжают и селятся
+
+
+ 18-19
+ Ужин
+
+
+ 19-N/A
+ Отдых и заготовки к кануну Нового Года
+
+
+
+
+
31 декабря
+
+
+
+ Время
+ Действие
+
+
+
+
+ 07-10
+ Утро, завтрак
+
+
+ 11-14
+ Сюсьма, прогулки, дополнительные закупки, подготовка к вечеру, обед
+
+
+ 14-19
+ Готовим ужин, чиллим
+
+
+ 19-23:59
+ Ужин, отдых, разговоры, игры
+
+
+
+
+
1 января
+
+
+
+ Время
+ Действие
+
+
+
+
+ 00-N/A
+ 🎄 ☃️ 🍾 🥂 🎇 🎆
+
+
+ 07-12
+ Утро, завтрак
+
+
+ 12-15
+ Сауна/Отдых
+
+
+ 15-N/A
+ Кто-то остается, кто-то собирается домой
+
+
+
+
+ );
+};
+
+export default Program;
diff --git a/frontend/src/constants/constants.ts b/frontend/src/constants/constants.ts
new file mode 100644
index 0000000..1bd68ed
--- /dev/null
+++ b/frontend/src/constants/constants.ts
@@ -0,0 +1,13 @@
+
+export const API_URL = 'https://example.backend.com/hosting';
+export const GUESTS = [
+ "Медведь",
+ "Ксения",
+ "Дед",
+ "Фил",
+ "Классик",
+ "Янка",
+ "Швед",
+ "Тюлень",
+ "Тюлениха",
+]
\ No newline at end of file
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index d78cf4d..4e2b36d 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -3,9 +3,14 @@ import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { NotificationProvider } from './NotificationContext.tsx'
+import { CookiesProvider } from 'react-cookie'
createRoot(document.getElementById('root')!).render(
-
+
+
+
+
+
,
)
diff --git a/frontend/src/utils/fetchHosting.tsx b/frontend/src/utils/fetchHosting.tsx
index e75b3e9..1e4026b 100644
--- a/frontend/src/utils/fetchHosting.tsx
+++ b/frontend/src/utils/fetchHosting.tsx
@@ -13,6 +13,11 @@ const mockData: Hosting = {
capacity: 2
},
3: {
+ reservedBy: "",
+ name: "Матрац 90см",
+ capacity: 1
+ },
+ 4: {
reservedBy: "",
name: "Диван",
capacity: 1