diff --git a/bun.lock b/bun.lock index 9dc826f..f8b40bd 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@react-router/dev": "^7.3.0", "@tailwindcss/vite": "^4.0.13", "@tanstack/react-query": "^5.67.3", + "@uidotdev/usehooks": "^2.4.1", "clsx": "^2.1.1", "ky": "^1.7.5", "motion": "^12.5.0", @@ -324,6 +325,8 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.26.1", "", { "dependencies": { "@typescript-eslint/types": "8.26.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg=="], + "@uidotdev/usehooks": ["@uidotdev/usehooks@2.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="], + "@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@3.8.0", "", { "dependencies": { "@swc/core": "^1.10.15" }, "peerDependencies": { "vite": "^4 || ^5 || ^6" } }, "sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw=="], "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], diff --git a/package.json b/package.json index 73a1a45..2eb888c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@react-router/dev": "^7.3.0", "@tailwindcss/vite": "^4.0.13", "@tanstack/react-query": "^5.67.3", + "@uidotdev/usehooks": "^2.4.1", "clsx": "^2.1.1", "ky": "^1.7.5", "motion": "^12.5.0", diff --git a/src/components/Button.tsx b/src/components/Button.tsx index cc5198c..0b40cce 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,36 +1,40 @@ -import React from "react"; -import { clsx as cn } from "clsx"; +import React from 'react'; +import { clsx as cn } from 'clsx'; interface ButtonProps extends React.ButtonHTMLAttributes { children: React.ReactNode; - variant?: "link" | "primary" | "secondary" | "tertiary"; + variant?: 'link' | 'primary' | 'secondary' | 'tertiary'; className?: string; - size?: "small" | "medium" | "large"; + size?: 'small' | 'medium' | 'large'; onlyIcon?: boolean; + ref?: React.RefObject; } function Button({ children, - variant = "primary", - size = "medium", + variant = 'primary', + size = 'medium', onlyIcon, className, + ref, ...props }: ButtonProps) { return ( + {isOpen && ( +
+ {servers.map((server) => ( + + ))} +
+ )} ); } diff --git a/src/components/Select.tsx b/src/components/Select.tsx index ed6015b..0b0a0a0 100644 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -1,28 +1,59 @@ -interface IOption { - id: string; +import { useState } from 'react'; +import Button from './Button'; +import { useClickAway } from '@uidotdev/usehooks'; +import ArrowDownIcon from './icons/ArrowDownIcon'; + +interface Props { + options: string[]; value: string; + onChange: (option: string) => void; } -interface ISelectProps - extends React.SelectHTMLAttributes { - multiple?: boolean; - options: T[]; - selected?: T; - // onClose: () => void; -} +export default function Select({ options, value, onChange }: Props) { + const [isOpen, setIsOpen] = useState(false); + + const ref = useClickAway(() => setIsOpen(false)); -export default function Select({ - multiple, - options, - selected, - onClose, - ...props -}: ISelectProps) { return ( - +
+ + {isOpen && ( +
+ {options.map((option) => ( + + ))} +
+ )} +
); } diff --git a/src/components/icons/ArrowDownIcon.tsx b/src/components/icons/ArrowDownIcon.tsx new file mode 100644 index 0000000..7d2c24f --- /dev/null +++ b/src/components/icons/ArrowDownIcon.tsx @@ -0,0 +1,13 @@ +export default function ArrowDownIcon() { + return ( + + + + ); +} diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index e8e2329..5bcb784 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -1,13 +1,41 @@ -import { IServer } from "../../types/IServer.ts"; -import Button from "../Button.tsx"; -import Input from "../Input.tsx"; -import DisplayIcon from "../icons/DisplayIcon.tsx"; +import { useEffect } from 'react'; +import { useState } from 'react'; +import { IServer } from '../../types/IServer.ts'; +import Button from '../Button.tsx'; +import DesktopSelect from '../DesktopSelect.tsx'; +import Input from '../Input.tsx'; +import DisplayIcon from '../icons/DisplayIcon.tsx'; +import Select from '../Select.tsx'; +import { useQueryClient } from '@tanstack/react-query'; +import { IUser } from '../../types/IUser.ts'; +import { IApp } from '../../types/IApp.ts'; interface Props { servers: IServer[]; } export default function CreateSessionModal({ servers }: Props) { + const queryClient = useQueryClient(); + + const user = queryClient.getQueryData(['me']); + + const [selectedServer, setSelectedServer] = useState( + servers.find( + ({ sessions }) => + !sessions || !sessions.length || sessions[0].status === 'ended' + ) + ); + + const [selectedApp, setSelectedApp] = useState( + user?.company.apps.filter((app) => app.serverId === selectedServer?.id)[0] + ); + + useEffect(() => { + setSelectedApp( + user?.company.apps.filter((app) => app.serverId === selectedServer?.id)[0] + ); + }, [selectedServer, user?.company.apps]); + return (
@@ -45,13 +73,48 @@ export default function CreateSessionModal({ servers }: Props) {
+
+
+

Стол

+ {selectedServer && ( + + )} +
+

+ При запуске нового сеанса текущий будет завершен принудительно. +

+
+ {user && selectedServer && ( +
+

Проекты

+
+ {selectedApp && ( +