fix: issue with translate
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { motion } from "framer-motion";
|
||||
import PlusIcon from "./icons/PlusIcon";
|
||||
import { motion } from 'framer-motion';
|
||||
import PlusIcon from './icons/PlusIcon';
|
||||
|
||||
interface MoreProjectButtonProps {
|
||||
onClick?: () => void;
|
||||
@@ -10,12 +10,12 @@ function MoreProjectButton({ onClick }: MoreProjectButtonProps) {
|
||||
<motion.button
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
viewport={{ once: true, margin: '-100px' }}
|
||||
transition={{ duration: 1, ease: [0.58, 0.12, 0.27, 0.98], delay: 0.2 }}
|
||||
className="sm:aspect-[4/3] border border-[#3D425C] rounded-[48px] px-6 py-4 flex sm:flex-col sm:justify-center justify-between items-center gap-2"
|
||||
className='sm:aspect-[4/3] border border-[#3D425C] rounded-[48px] px-6 py-4 flex sm:flex-col sm:justify-center justify-between items-center gap-2'
|
||||
onClick={onClick}
|
||||
>
|
||||
<p className="font-gilroy font-medium leading-none">Показать еще</p>
|
||||
<p className='font-medium leading-none font-gilroy'>Show more</p>
|
||||
<PlusIcon />
|
||||
</motion.button>
|
||||
);
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { motion } from "framer-motion";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { motion } from 'framer-motion';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import ArrowLeftIcon from './icons/ArrowLeftIcon';
|
||||
import ArrowRightIcon from './icons/ArrowRightIcon';
|
||||
import { useSwipeable } from 'react-swipeable';
|
||||
|
||||
function VideoSliderMobile() {
|
||||
const [items] = useState<any[]>([
|
||||
{
|
||||
title: "Virtual tour of the residential complex",
|
||||
desc: "The client can personally assess the angle of sunlight, minor architectural forms, and landscape, moving through the complex with a tap.",
|
||||
video: "/videos/features/virtual_tour.mp4",
|
||||
title: 'Virtual tour of the residential complex',
|
||||
desc: 'The client can personally assess the angle of sunlight, minor architectural forms, and landscape, moving through the complex with a tap.',
|
||||
video: '/videos/features/virtual_tour.mp4',
|
||||
},
|
||||
{
|
||||
title: "All infrastructure on one screen",
|
||||
desc: "The ability to assess the infrastructure of the area will show the client important points of interest and the time it takes to reach them.",
|
||||
video: "/videos/features/nks_infra.mp4",
|
||||
title: 'All infrastructure on one screen',
|
||||
desc: 'The ability to assess the infrastructure of the area will show the client important points of interest and the time it takes to reach them.',
|
||||
video: '/videos/features/nks_infra.mp4',
|
||||
},
|
||||
{
|
||||
title: "Конфигуратор интерьера",
|
||||
desc: "The client can freely choose furniture and design using the interior configurator. It is possible to select the style for the entire apartment or change individual details.",
|
||||
video: "/videos/features/uralsky.mp4",
|
||||
title: 'Interior Configurator',
|
||||
desc: 'The client can freely choose furniture and design using the interior configurator. It is possible to select the style for the entire apartment or change individual details.',
|
||||
video: '/videos/features/uralsky.mp4',
|
||||
},
|
||||
{
|
||||
title: "Parametric apartment search",
|
||||
desc: "The filter will allow marking specific advantages, determine the number of rooms, desired floor, price, and receive a selection of suitable options.",
|
||||
video: "/videos/features/parametric.mp4",
|
||||
title: 'Parametric apartment search',
|
||||
desc: 'The filter will allow marking specific advantages, determine the number of rooms, desired floor, price, and receive a selection of suitable options.',
|
||||
video: '/videos/features/parametric.mp4',
|
||||
},
|
||||
{
|
||||
title: "Any render in a few seconds",
|
||||
desc: "When you need any object from any angle for advertising, just take a picture inside the presentation.",
|
||||
video: "/videos/features/render.mp4",
|
||||
title: 'Any render in a few seconds',
|
||||
desc: 'When you need any object from any angle for advertising, just take a picture inside the presentation.',
|
||||
video: '/videos/features/render.mp4',
|
||||
},
|
||||
{
|
||||
title: "Formation of a wishlist",
|
||||
desc: "The client can add apartment options to favorites, compare them with each other by the main parameters, and choose their future apartment.",
|
||||
video: "/videos/features/wish.mp4",
|
||||
title: 'Formation of a wishlist',
|
||||
desc: 'The client can add apartment options to favorites, compare them with each other by the main parameters, and choose their future apartment.',
|
||||
video: '/videos/features/wish.mp4',
|
||||
},
|
||||
{
|
||||
title: "Integration with CRM",
|
||||
title: 'Integration with CRM',
|
||||
desc: "The application transfers client information to the developer's CRM system and receives current information about prices and statuses of apartments.",
|
||||
video: "/videos/features/integra_crm.mp4",
|
||||
video: '/videos/features/integra_crm.mp4',
|
||||
},
|
||||
{
|
||||
title: "Sending a commercial offer",
|
||||
desc: "A commercial offer with selected apartments can be sent to the client by mail or printed and handed over personally.",
|
||||
video: "/videos/features/send.mp4",
|
||||
title: 'Sending a commercial offer',
|
||||
desc: 'A commercial offer with selected apartments can be sent to the client by mail or printed and handed over personally.',
|
||||
video: '/videos/features/send.mp4',
|
||||
},
|
||||
// {
|
||||
// title: "Интерактивная инсоляция",
|
||||
@@ -65,11 +65,11 @@ function VideoSliderMobile() {
|
||||
const videoRefs = items.map(() => useRef<HTMLVideoElement>(null));
|
||||
const handlers = useSwipeable({
|
||||
onSwiped: (e) => {
|
||||
if (e.dir === "Left") {
|
||||
if (e.dir === 'Left') {
|
||||
handleClickNext();
|
||||
}
|
||||
|
||||
if (e.dir === "Right") {
|
||||
if (e.dir === 'Right') {
|
||||
handleClickPrev();
|
||||
}
|
||||
},
|
||||
@@ -106,7 +106,7 @@ function VideoSliderMobile() {
|
||||
return (
|
||||
<div
|
||||
{...handlers}
|
||||
className="xl:hidden flex flex-col sm:gap-6 gap-4 border-b border-[#3D425C] pb-5"
|
||||
className='xl:hidden flex flex-col sm:gap-6 gap-4 border-b border-[#3D425C] pb-5'
|
||||
>
|
||||
<div
|
||||
// ref={videosContainerRef}
|
||||
@@ -121,39 +121,39 @@ function VideoSliderMobile() {
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
preload='metadata'
|
||||
className={`relative aspect-video transition-all duration-500 sm:w-[calc(100%-88px)] ${
|
||||
index !== activeIndex
|
||||
? "sm:scale-[70%] scale-[80%] sm:-translate-y-[15%] -translate-y-[10%] sm:-translate-x-[calc(15%+88px-12px)] -translate-x-[calc(10%-8px)]"
|
||||
: ""
|
||||
? 'sm:scale-[70%] scale-[80%] sm:-translate-y-[15%] -translate-y-[10%] sm:-translate-x-[calc(15%+88px-12px)] -translate-x-[calc(10%-8px)]'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between sm:gap-[30px] gap-3 sm:h-auto h-[168px]">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-xl font-gilroy font-medium">
|
||||
<div className='flex justify-between sm:gap-[30px] gap-3 sm:h-auto h-[168px]'>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<p className='text-xl font-medium font-gilroy'>
|
||||
{items[activeIndex].title}
|
||||
</p>
|
||||
<p className="text-sm">{items[activeIndex].desc}</p>
|
||||
<p className='text-sm'>{items[activeIndex].desc}</p>
|
||||
</div>
|
||||
<div className="relative flex flex-col items-center gap-4">
|
||||
<div className="sm:absolute -top-[64px]">
|
||||
<p className="sm:text-2xl text-xl font-gilroy font-medium">
|
||||
<div className='relative flex flex-col items-center gap-4'>
|
||||
<div className='sm:absolute -top-[64px]'>
|
||||
<p className='text-xl font-medium sm:text-2xl font-gilroy'>
|
||||
{activeIndex + 1}/{items.length}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 self-end">
|
||||
<div className='flex flex-col self-end gap-2'>
|
||||
<button
|
||||
onClick={handleClickNext}
|
||||
className="sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none"
|
||||
className='sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none'
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClickPrev}
|
||||
className="sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none"
|
||||
className='sm:p-4 p-2 border border-[#3D425C] rounded-full outline-none'
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</button>
|
||||
|
||||
@@ -1,292 +1,292 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import api from '../../utils/api';
|
||||
import Button from '../Button';
|
||||
import IProject from '../../types/IProject';
|
||||
import useModalStore from '../../stores/useModalStore';
|
||||
import { format } from 'date-fns';
|
||||
import Close2Icon from '../icons/Close2Icon';
|
||||
// /* eslint-disable react-hooks/exhaustive-deps */
|
||||
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// import { ChangeEvent, useEffect, useState } from 'react';
|
||||
// import api from '../../utils/api';
|
||||
// import Button from '../Button';
|
||||
// import IProject from '../../types/IProject';
|
||||
// import useModalStore from '../../stores/useModalStore';
|
||||
// import { format } from 'date-fns';
|
||||
// import Close2Icon from '../icons/Close2Icon';
|
||||
|
||||
function CreateProjectModal() {
|
||||
const [project, setProject] = useState<IProject>({
|
||||
title: '',
|
||||
company: '',
|
||||
city: '',
|
||||
image: '',
|
||||
releaseDate: format(new Date(), 'yyyy-MM-dd'),
|
||||
devices: [],
|
||||
});
|
||||
// function CreateProjectModal() {
|
||||
// const [project, setProject] = useState<IProject>({
|
||||
// title: '',
|
||||
// company: '',
|
||||
// city: '',
|
||||
// image: '',
|
||||
// releaseDate: format(new Date(), 'yyyy-MM-dd'),
|
||||
// devices: [],
|
||||
// });
|
||||
|
||||
const [file, setFile] = useState<File>();
|
||||
const [previewFile, setPreviewFile] = useState<string>();
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
// const [file, setFile] = useState<File>();
|
||||
// const [previewFile, setPreviewFile] = useState<string>();
|
||||
// const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
|
||||
function handleChangeFile(e: ChangeEvent<HTMLInputElement>) {
|
||||
if (!e.target.files) return;
|
||||
// function handleChangeFile(e: ChangeEvent<HTMLInputElement>) {
|
||||
// if (!e.target.files) return;
|
||||
|
||||
const targetFile = e.target.files[0];
|
||||
// const targetFile = e.target.files[0];
|
||||
|
||||
setFile(targetFile);
|
||||
setPreviewFile(URL.createObjectURL(targetFile));
|
||||
}
|
||||
// setFile(targetFile);
|
||||
// setPreviewFile(URL.createObjectURL(targetFile));
|
||||
// }
|
||||
|
||||
async function uploadFile() {
|
||||
if (!file) return;
|
||||
// async function uploadFile() {
|
||||
// if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
// const formData = new FormData();
|
||||
// formData.append('file', file);
|
||||
|
||||
try {
|
||||
const { file }: { file: string } = await api
|
||||
.post('upload', { body: formData })
|
||||
.json();
|
||||
// try {
|
||||
// const { file }: { file: string } = await api
|
||||
// .post('upload', { body: formData })
|
||||
// .json();
|
||||
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
image: file,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// image: file,
|
||||
// }));
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
async function createProject() {
|
||||
try {
|
||||
await api.post('projects', { json: { ...project } });
|
||||
// async function createProject() {
|
||||
// try {
|
||||
// await api.post('projects', { json: { ...project } });
|
||||
|
||||
setModal(null);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// setModal(null);
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
async function handleSubmit(e: ChangeEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
// async function handleSubmit(e: ChangeEvent<HTMLFormElement>) {
|
||||
// e.preventDefault();
|
||||
|
||||
await createProject();
|
||||
// await createProject();
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
// window.location.reload();
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
uploadFile();
|
||||
}, [file]);
|
||||
// useEffect(() => {
|
||||
// uploadFile();
|
||||
// }, [file]);
|
||||
|
||||
return (
|
||||
<div className='bg-white shadow-lg text-black p-8 rounded-xl flex flex-col gap-4'>
|
||||
<div className='flex justify-between items-center border-b border-[#ccc] pb-4 gap-4'>
|
||||
<p className='text-xl'>Создание проекта</p>
|
||||
<button
|
||||
onClick={() => setModal(null)}
|
||||
className='p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full'
|
||||
>
|
||||
<Close2Icon />
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className='grid grid-cols-2 gap-4 w-[512px]'
|
||||
>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Название</label>
|
||||
<input
|
||||
autoFocus
|
||||
required
|
||||
type='text'
|
||||
placeholder='Название'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.title}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, title: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// return (
|
||||
// <div className='flex flex-col gap-4 p-8 text-black bg-white shadow-lg rounded-xl'>
|
||||
// <div className='flex justify-between items-center border-b border-[#ccc] pb-4 gap-4'>
|
||||
// <p className='text-xl'>Создание проекта</p>
|
||||
// <button
|
||||
// onClick={() => setModal(null)}
|
||||
// className='p-2 transition-colors rounded-full hover:bg-white hover:bg-opacity-10'
|
||||
// >
|
||||
// <Close2Icon />
|
||||
// </button>
|
||||
// </div>
|
||||
// <form
|
||||
// onSubmit={handleSubmit}
|
||||
// className='grid grid-cols-2 gap-4 w-[512px]'
|
||||
// >
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Название</label>
|
||||
// <input
|
||||
// autoFocus
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Название'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.title}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, title: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Компания</label>
|
||||
<input
|
||||
required
|
||||
type='text'
|
||||
placeholder='Компания'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.company}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, company: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Компания</label>
|
||||
// <input
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Компания'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.company}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, company: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Город</label>
|
||||
<input
|
||||
required
|
||||
type='text'
|
||||
placeholder='Город'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.city}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, city: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Город</label>
|
||||
// <input
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Город'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.city}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, city: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<label className='relative border border-dashed border-neutral-500 px-3 py-2 hover:bg-opacity-10 hover:bg-black cursor-pointer rounded-lg flex flex-col gap-2'>
|
||||
<input
|
||||
required
|
||||
type='file'
|
||||
accept='image/*'
|
||||
className='absolute opacity-0'
|
||||
onChange={handleChangeFile}
|
||||
/>
|
||||
<p className='truncate'>
|
||||
{file ? file.name : 'Выберите изображение'}
|
||||
</p>
|
||||
// <label className='relative flex flex-col gap-2 px-3 py-2 border border-dashed rounded-lg cursor-pointer border-neutral-500 hover:bg-opacity-10 hover:bg-black'>
|
||||
// <input
|
||||
// required
|
||||
// type='file'
|
||||
// accept='image/*'
|
||||
// className='absolute opacity-0'
|
||||
// onChange={handleChangeFile}
|
||||
// />
|
||||
// <p className='truncate'>
|
||||
// {file ? file.name : 'Выберите изображение'}
|
||||
// </p>
|
||||
|
||||
{previewFile && <img src={previewFile} alt='' />}
|
||||
</label>
|
||||
// {previewFile && <img src={previewFile} alt='' />}
|
||||
// </label>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Стадия</label>
|
||||
<select
|
||||
required
|
||||
value={project.stage || ''}
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, stage: +e.target.value }))
|
||||
}
|
||||
>
|
||||
<option value='' disabled>
|
||||
Выберите стадию
|
||||
</option>
|
||||
<option value={1}>1</option>
|
||||
<option value={2}>2</option>
|
||||
<option value={3}>3</option>
|
||||
<option value={4}>4</option>
|
||||
<option value={5}>5</option>
|
||||
<option value={6}>6</option>
|
||||
</select>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Стадия</label>
|
||||
// <select
|
||||
// required
|
||||
// value={project.stage || ''}
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, stage: +e.target.value }))
|
||||
// }
|
||||
// >
|
||||
// <option value='' disabled>
|
||||
// Выберите стадию
|
||||
// </option>
|
||||
// <option value={1}>1</option>
|
||||
// <option value={2}>2</option>
|
||||
// <option value={3}>3</option>
|
||||
// <option value={4}>4</option>
|
||||
// <option value={5}>5</option>
|
||||
// <option value={6}>6</option>
|
||||
// </select>
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Дата релиза</label>
|
||||
<input
|
||||
type='date'
|
||||
required
|
||||
value={project.releaseDate}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
releaseDate: e.target.value,
|
||||
}))
|
||||
}
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Дата релиза</label>
|
||||
// <input
|
||||
// type='date'
|
||||
// required
|
||||
// value={project.releaseDate}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// releaseDate: e.target.value,
|
||||
// }))
|
||||
// }
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<p className='text-sm'>Девайсы</p>
|
||||
<div className=''>
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'stream'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'stream'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Stream</span>
|
||||
</label>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <p className='text-sm'>Девайсы</p>
|
||||
// <div className=''>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'stream'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'stream'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Stream</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'touch'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'touch'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Touch</span>
|
||||
</label>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'touch'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'touch'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Touch</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'mobile'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'mobile'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Mobile</span>
|
||||
</label>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'mobile'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'mobile'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Mobile</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'vr'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'vr'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>VR</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'vr'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'vr'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>VR</span>
|
||||
// </label>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
<div className='col-span-full flex justify-end'>
|
||||
<Button className='text-white outline-none'>Добавить проект</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// <div className='flex justify-end col-span-full'>
|
||||
// <Button className='text-white outline-none'>Добавить проект</Button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default CreateProjectModal;
|
||||
// export default CreateProjectModal;
|
||||
|
||||
@@ -1,325 +1,325 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import api from '../../utils/api';
|
||||
import Button from '../Button';
|
||||
import IProject from '../../types/IProject';
|
||||
import useModalStore from '../../stores/useModalStore';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
import Close2Icon from '../icons/Close2Icon';
|
||||
// /* eslint-disable react-hooks/exhaustive-deps */
|
||||
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// import { ChangeEvent, useEffect, useState } from 'react';
|
||||
// import api from '../../utils/api';
|
||||
// import Button from '../Button';
|
||||
// import IProject from '../../types/IProject';
|
||||
// import useModalStore from '../../stores/useModalStore';
|
||||
// import { format, parseISO } from 'date-fns';
|
||||
// import Close2Icon from '../icons/Close2Icon';
|
||||
|
||||
interface EditProjectModalProps {
|
||||
projectId: string;
|
||||
}
|
||||
// interface EditProjectModalProps {
|
||||
// projectId: string;
|
||||
// }
|
||||
|
||||
function EditProjectModal({ projectId }: EditProjectModalProps) {
|
||||
const [project, setProject] = useState<IProject>({
|
||||
title: '',
|
||||
company: '',
|
||||
city: '',
|
||||
image: '',
|
||||
releaseDate: '2023-01-01',
|
||||
devices: [],
|
||||
});
|
||||
// function EditProjectModal({ projectId }: EditProjectModalProps) {
|
||||
// const [project, setProject] = useState<IProject>({
|
||||
// title: '',
|
||||
// company: '',
|
||||
// city: '',
|
||||
// image: '',
|
||||
// releaseDate: '2023-01-01',
|
||||
// devices: [],
|
||||
// });
|
||||
|
||||
const [file, setFile] = useState<File>();
|
||||
const [previewFile, setPreviewFile] = useState<string>();
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
// const [file, setFile] = useState<File>();
|
||||
// const [previewFile, setPreviewFile] = useState<string>();
|
||||
// const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
|
||||
function handleChangeFile(e: ChangeEvent<HTMLInputElement>) {
|
||||
if (!e.target.files) return;
|
||||
// function handleChangeFile(e: ChangeEvent<HTMLInputElement>) {
|
||||
// if (!e.target.files) return;
|
||||
|
||||
const targetFile = e.target.files[0];
|
||||
// const targetFile = e.target.files[0];
|
||||
|
||||
setFile(targetFile);
|
||||
setPreviewFile(URL.createObjectURL(targetFile));
|
||||
}
|
||||
// setFile(targetFile);
|
||||
// setPreviewFile(URL.createObjectURL(targetFile));
|
||||
// }
|
||||
|
||||
async function uploadFile() {
|
||||
if (!file) return;
|
||||
// async function uploadFile() {
|
||||
// if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
// const formData = new FormData();
|
||||
// formData.append('file', file);
|
||||
|
||||
try {
|
||||
const { file }: { file: string } = await api
|
||||
.post('upload', { body: formData })
|
||||
.json();
|
||||
// try {
|
||||
// const { file }: { file: string } = await api
|
||||
// .post('upload', { body: formData })
|
||||
// .json();
|
||||
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
image: file,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// image: file,
|
||||
// }));
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
async function updateProject() {
|
||||
try {
|
||||
await api.put(`projects/${projectId}`, { json: { ...project } });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// async function updateProject() {
|
||||
// try {
|
||||
// await api.put(`projects/${projectId}`, { json: { ...project } });
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
async function handleSubmit(e: ChangeEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
// async function handleSubmit(e: ChangeEvent<HTMLFormElement>) {
|
||||
// e.preventDefault();
|
||||
|
||||
await updateProject();
|
||||
setModal(null);
|
||||
window.location.reload();
|
||||
}
|
||||
// await updateProject();
|
||||
// setModal(null);
|
||||
// window.location.reload();
|
||||
// }
|
||||
|
||||
async function getProject() {
|
||||
try {
|
||||
const project: IProject = await api.get(`projects/${projectId}`).json();
|
||||
project.releaseDate = format(parseISO(project.releaseDate), 'yyyy-MM-dd');
|
||||
// async function getProject() {
|
||||
// try {
|
||||
// const project: IProject = await api.get(`projects/${projectId}`).json();
|
||||
// project.releaseDate = format(parseISO(project.releaseDate), 'yyyy-MM-dd');
|
||||
|
||||
setProject(project);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// setProject(project);
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
uploadFile();
|
||||
}, [file]);
|
||||
// useEffect(() => {
|
||||
// uploadFile();
|
||||
// }, [file]);
|
||||
|
||||
useEffect(() => {
|
||||
getProject();
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// getProject();
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<div className='bg-white shadow-lg text-black p-8 rounded-xl flex flex-col gap-4'>
|
||||
<div className='flex justify-between items-center border-b border-[#ccc] pb-4 gap-4'>
|
||||
<p className='text-xl'>Редактирование проекта</p>
|
||||
<button
|
||||
onClick={() => setModal(null)}
|
||||
className='p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full'
|
||||
>
|
||||
<Close2Icon />
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className='grid grid-cols-2 gap-4 w-[512px]'
|
||||
>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Название</label>
|
||||
<input
|
||||
autoFocus
|
||||
required
|
||||
type='text'
|
||||
placeholder='Название'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.title}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, title: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// return (
|
||||
// <div className='flex flex-col gap-4 p-8 text-black bg-white shadow-lg rounded-xl'>
|
||||
// <div className='flex justify-between items-center border-b border-[#ccc] pb-4 gap-4'>
|
||||
// <p className='text-xl'>Редактирование проекта</p>
|
||||
// <button
|
||||
// onClick={() => setModal(null)}
|
||||
// className='p-2 transition-colors rounded-full hover:bg-white hover:bg-opacity-10'
|
||||
// >
|
||||
// <Close2Icon />
|
||||
// </button>
|
||||
// </div>
|
||||
// <form
|
||||
// onSubmit={handleSubmit}
|
||||
// className='grid grid-cols-2 gap-4 w-[512px]'
|
||||
// >
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Название</label>
|
||||
// <input
|
||||
// autoFocus
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Название'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.title}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, title: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Компания</label>
|
||||
<input
|
||||
required
|
||||
type='text'
|
||||
placeholder='Компания'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.company}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, company: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Компания</label>
|
||||
// <input
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Компания'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.company}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, company: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Город</label>
|
||||
<input
|
||||
required
|
||||
type='text'
|
||||
placeholder='Город'
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
value={project.city}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, city: e.target.value }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Город</label>
|
||||
// <input
|
||||
// required
|
||||
// type='text'
|
||||
// placeholder='Город'
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// value={project.city}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, city: e.target.value }))
|
||||
// }
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<label className='relative border border-dashed border-neutral-500 px-3 py-2 hover:bg-opacity-10 hover:bg-black cursor-pointer rounded-lg flex flex-col gap-2'>
|
||||
<input
|
||||
type='file'
|
||||
accept='image/*'
|
||||
className='absolute opacity-0'
|
||||
onChange={handleChangeFile}
|
||||
/>
|
||||
<p className='truncate'>
|
||||
{file ? file.name : 'Выберите изображение'}
|
||||
</p>
|
||||
// <label className='relative flex flex-col gap-2 px-3 py-2 border border-dashed rounded-lg cursor-pointer border-neutral-500 hover:bg-opacity-10 hover:bg-black'>
|
||||
// <input
|
||||
// type='file'
|
||||
// accept='image/*'
|
||||
// className='absolute opacity-0'
|
||||
// onChange={handleChangeFile}
|
||||
// />
|
||||
// <p className='truncate'>
|
||||
// {file ? file.name : 'Выберите изображение'}
|
||||
// </p>
|
||||
|
||||
{previewFile ? (
|
||||
<img src={previewFile} alt='' />
|
||||
) : (
|
||||
project.image && (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_API_URL}/upload/${project.image}`}
|
||||
alt=''
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</label>
|
||||
// {previewFile ? (
|
||||
// <img src={previewFile} alt='' />
|
||||
// ) : (
|
||||
// project.image && (
|
||||
// <img
|
||||
// src={`${import.meta.env.VITE_API_URL}/upload/${project.image}`}
|
||||
// alt=''
|
||||
// />
|
||||
// )
|
||||
// )}
|
||||
// </label>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Стадия</label>
|
||||
<select
|
||||
required
|
||||
value={project.stage || ''}
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({ ...prev, stage: +e.target.value }))
|
||||
}
|
||||
>
|
||||
<option value='' disabled>
|
||||
Выберите стадию
|
||||
</option>
|
||||
<option value={1}>1</option>
|
||||
<option value={2}>2</option>
|
||||
<option value={3}>3</option>
|
||||
<option value={4}>4</option>
|
||||
<option value={5}>5</option>
|
||||
<option value={6}>6</option>
|
||||
</select>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Стадия</label>
|
||||
// <select
|
||||
// required
|
||||
// value={project.stage || ''}
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({ ...prev, stage: +e.target.value }))
|
||||
// }
|
||||
// >
|
||||
// <option value='' disabled>
|
||||
// Выберите стадию
|
||||
// </option>
|
||||
// <option value={1}>1</option>
|
||||
// <option value={2}>2</option>
|
||||
// <option value={3}>3</option>
|
||||
// <option value={4}>4</option>
|
||||
// <option value={5}>5</option>
|
||||
// <option value={6}>6</option>
|
||||
// </select>
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='text-sm'>Дата релиза</label>
|
||||
<input
|
||||
type='date'
|
||||
required
|
||||
value={project.releaseDate}
|
||||
onChange={(e) =>
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
releaseDate: e.target.value,
|
||||
}))
|
||||
}
|
||||
className='border border-neutral-500 px-3 py-2 rounded-lg outline-none'
|
||||
/>
|
||||
</div>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <label className='text-sm'>Дата релиза</label>
|
||||
// <input
|
||||
// type='date'
|
||||
// required
|
||||
// value={project.releaseDate}
|
||||
// onChange={(e) =>
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// releaseDate: e.target.value,
|
||||
// }))
|
||||
// }
|
||||
// className='px-3 py-2 border rounded-lg outline-none border-neutral-500'
|
||||
// />
|
||||
// </div>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<p className='text-sm'>Девайсы</p>
|
||||
<div className=''>
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={project.devices!.includes('stream')}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'stream'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'stream'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Stream</span>
|
||||
</label>
|
||||
// <div className='flex flex-col gap-1'>
|
||||
// <p className='text-sm'>Девайсы</p>
|
||||
// <div className=''>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// checked={project.devices!.includes('stream')}
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'stream'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'stream'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Stream</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={project.devices!.includes('touch')}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'touch'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'touch'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Touch</span>
|
||||
</label>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// checked={project.devices!.includes('touch')}
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'touch'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'touch'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Touch</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={project.devices!.includes('mobile')}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'mobile'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'mobile'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>Mobile</span>
|
||||
</label>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// checked={project.devices!.includes('mobile')}
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'mobile'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'mobile'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>Mobile</span>
|
||||
// </label>
|
||||
|
||||
<label className='flex items-center gap-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={project.devices!.includes('vr')}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: [...prev.devices!, 'vr'],
|
||||
}));
|
||||
} else {
|
||||
setProject((prev) => ({
|
||||
...prev,
|
||||
devices: prev.devices!.filter(
|
||||
(device) => device !== 'vr'
|
||||
),
|
||||
}));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span>VR</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
// <label className='flex items-center gap-2'>
|
||||
// <input
|
||||
// type='checkbox'
|
||||
// checked={project.devices!.includes('vr')}
|
||||
// onChange={(e) => {
|
||||
// if (e.target.checked) {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: [...prev.devices!, 'vr'],
|
||||
// }));
|
||||
// } else {
|
||||
// setProject((prev) => ({
|
||||
// ...prev,
|
||||
// devices: prev.devices!.filter(
|
||||
// (device) => device !== 'vr'
|
||||
// ),
|
||||
// }));
|
||||
// }
|
||||
// }}
|
||||
// />
|
||||
// <span>VR</span>
|
||||
// </label>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
<div className='col-span-full flex justify-end'>
|
||||
<Button className='text-white outline-none'>
|
||||
Сохранить изменения
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// <div className='flex justify-end col-span-full'>
|
||||
// <Button className='text-white outline-none'>
|
||||
// Сохранить изменения
|
||||
// </Button>
|
||||
// </div>
|
||||
// </form>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default EditProjectModal;
|
||||
// export default EditProjectModal;
|
||||
|
||||
+100
-100
@@ -1,108 +1,108 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import api from "../utils/api";
|
||||
import IProject from "../types/IProject";
|
||||
import ProjectCard from "../components/ProjectCard";
|
||||
import Button from "../components/Button";
|
||||
import useModalStore from "../stores/useModalStore";
|
||||
import CreateProjectModal from "../components/modals/CreateProjectModal";
|
||||
import ModalContainer from "../components/ModalContainer";
|
||||
import EditProjectModal from "../components/modals/EditProjectModal";
|
||||
import DeleteProjectModal from "../components/modals/DeleteProjectModal";
|
||||
// import { useEffect, useState } from "react";
|
||||
// import api from "../utils/api";
|
||||
// import IProject from "../types/IProject";
|
||||
// import ProjectCard from "../components/ProjectCard";
|
||||
// import Button from "../components/Button";
|
||||
// import useModalStore from "../stores/useModalStore";
|
||||
// import CreateProjectModal from "../components/modals/CreateProjectModal";
|
||||
// import ModalContainer from "../components/ModalContainer";
|
||||
// import EditProjectModal from "../components/modals/EditProjectModal";
|
||||
// import DeleteProjectModal from "../components/modals/DeleteProjectModal";
|
||||
|
||||
function ProjectsPage() {
|
||||
const [projects, setProjects] = useState<IProject[]>([]);
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
// function ProjectsPage() {
|
||||
// const [projects, setProjects] = useState<IProject[]>([]);
|
||||
// const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
|
||||
async function getProjects() {
|
||||
try {
|
||||
const projects: IProject[] = await api.get("projects").json();
|
||||
// async function getProjects() {
|
||||
// try {
|
||||
// const projects: IProject[] = await api.get("projects").json();
|
||||
|
||||
setProjects(projects);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// setProjects(projects);
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// alert(`Error: ${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function handleClickCreateProject() {
|
||||
setModal(<CreateProjectModal />);
|
||||
}
|
||||
// function handleClickCreateProject() {
|
||||
// setModal(<CreateProjectModal />);
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
getProjects();
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// getProjects();
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="fixed top-0 left-0 z-10 p-4 bg-[#14161F] border-b border-white border-opacity-10 w-full shadow-2xl">
|
||||
<Button onClick={handleClickCreateProject}>Добавить проект</Button>
|
||||
</div>
|
||||
<div className="conatiner mx-auto 2xl:px-10 xl:px-8 sm:px-6 px-4 2xl:max-w-screen-2xl mt-20">
|
||||
<div className="relative py-8 flex flex-col gap-8">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{projects.map((project, index) => (
|
||||
<div key={index} className="relative">
|
||||
<ProjectCard {...project} />
|
||||
<div className="absolute top-0 right-0 p-4 flex gap-2">
|
||||
<button
|
||||
onClick={() =>
|
||||
setModal(<EditProjectModal projectId={project.id!} />)
|
||||
}
|
||||
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
Редактировать
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setModal(<DeleteProjectModal projectId={project.id!} />)
|
||||
}
|
||||
className="group relative p-2 bg-black bg-opacity-60 hover:bg-opacity-70 transition-opacity rounded-full"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
<span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
Удалить
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// return (
|
||||
// <div className="min-h-screen">
|
||||
// <div className="fixed top-0 left-0 z-10 p-4 bg-[#14161F] border-b border-white border-opacity-10 w-full shadow-2xl">
|
||||
// <Button onClick={handleClickCreateProject}>Добавить проект</Button>
|
||||
// </div>
|
||||
// <div className="px-4 mx-auto mt-20 conatiner 2xl:px-10 xl:px-8 sm:px-6 2xl:max-w-screen-2xl">
|
||||
// <div className="relative flex flex-col gap-8 py-8">
|
||||
// <div className="grid grid-cols-3 gap-4">
|
||||
// {projects.map((project, index) => (
|
||||
// <div key={index} className="relative">
|
||||
// <ProjectCard {...project} />
|
||||
// <div className="absolute top-0 right-0 flex gap-2 p-4">
|
||||
// <button
|
||||
// onClick={() =>
|
||||
// setModal(<EditProjectModal projectId={project.id!} />)
|
||||
// }
|
||||
// className="relative p-2 transition-opacity bg-black rounded-full group bg-opacity-60 hover:bg-opacity-70"
|
||||
// >
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// strokeWidth={1.5}
|
||||
// stroke="currentColor"
|
||||
// className="w-6 h-6"
|
||||
// >
|
||||
// <path
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
|
||||
// />
|
||||
// </svg>
|
||||
// <span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
// Редактировать
|
||||
// </span>
|
||||
// </button>
|
||||
// <button
|
||||
// onClick={() =>
|
||||
// setModal(<DeleteProjectModal projectId={project.id!} />)
|
||||
// }
|
||||
// className="relative p-2 transition-opacity bg-black rounded-full group bg-opacity-60 hover:bg-opacity-70"
|
||||
// >
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// strokeWidth={1.5}
|
||||
// stroke="currentColor"
|
||||
// className="w-6 h-6"
|
||||
// >
|
||||
// <path
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||
// />
|
||||
// </svg>
|
||||
// <span className="pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity absolute -bottom-[90%] left-[50%] -translate-x-[50%] bg-neutral-900 px-2 py-1 text-sm rounded-lg">
|
||||
// Удалить
|
||||
// </span>
|
||||
// </button>
|
||||
// </div>
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
<ModalContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// <ModalContainer />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default ProjectsPage;
|
||||
// export default ProjectsPage;
|
||||
|
||||
Reference in New Issue
Block a user