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 - /> ); @@ -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) => ( + + ))} +
+
+ ); +}; + +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