2026-08-24 10:07:16 +00:00
|
|
|
|
import { useState } from 'react';
|
2026-09-02 06:41:12 +00:00
|
|
|
|
import styled, {css} from 'styled-components';
|
2026-08-24 10:07:16 +00:00
|
|
|
|
import { declension } from '../../utils/declension';
|
|
|
|
|
|
import type { User } from '../../types/user';
|
|
|
|
|
|
import { PersonModal } from './PersonModal';
|
|
|
|
|
|
|
|
|
|
|
|
interface FamilySectionProps {
|
|
|
|
|
|
users: User[];
|
|
|
|
|
|
availableTags: string[];
|
|
|
|
|
|
onChange: (users: User[]) => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function FamilySection({
|
|
|
|
|
|
users,
|
|
|
|
|
|
availableTags,
|
|
|
|
|
|
onChange,
|
|
|
|
|
|
}: FamilySectionProps) {
|
|
|
|
|
|
const [editingIndex, setEditingIndex] = useState<number | null>(null);
|
|
|
|
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
function handleDelete(index: number) {
|
|
|
|
|
|
onChange(users.filter((_, userIndex) => userIndex !== index));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSave(user: User) {
|
|
|
|
|
|
if (editingIndex === null) {
|
|
|
|
|
|
onChange([...users, user]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
onChange(users.map((item, index) => index === editingIndex ? user : item));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setIsModalOpen(false);
|
|
|
|
|
|
setEditingIndex(null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<FamilyList>
|
|
|
|
|
|
{users.map((user, index) => (
|
|
|
|
|
|
<FamilyMember
|
|
|
|
|
|
key={`${user.name}-${index}`}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setEditingIndex(index);
|
|
|
|
|
|
setIsModalOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Avatar>
|
|
|
|
|
|
<img src={getAvatarSrc(user)} alt="" />
|
|
|
|
|
|
</Avatar>
|
|
|
|
|
|
|
|
|
|
|
|
<Info>
|
|
|
|
|
|
<Name>{user.name}</Name>
|
2026-08-30 11:47:34 +00:00
|
|
|
|
<Age>
|
|
|
|
|
|
{user.age} {declension(user.age, ['год', 'года', 'лет'])}
|
|
|
|
|
|
<b> • </b>
|
|
|
|
|
|
<span>{user.gender === 'm' ? 'Мужской' : 'Женский'}</span>
|
|
|
|
|
|
</Age>
|
2026-08-24 10:07:16 +00:00
|
|
|
|
</Info>
|
|
|
|
|
|
|
|
|
|
|
|
<DeleteButton
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={event => {
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
|
handleDelete(index);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-09-02 06:41:12 +00:00
|
|
|
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2026-09-03 10:50:12 +00:00
|
|
|
|
<path d="M14.167 14.1641L5.83366 5.83073M14.167 5.83073L5.83366 14.1641" stroke="#CEDFD0" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
2026-09-02 06:41:12 +00:00
|
|
|
|
</svg>
|
|
|
|
|
|
|
2026-08-24 10:07:16 +00:00
|
|
|
|
</DeleteButton>
|
|
|
|
|
|
</FamilyMember>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</FamilyList>
|
|
|
|
|
|
|
|
|
|
|
|
<AddWrapper>
|
|
|
|
|
|
<AddButton
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setEditingIndex(null);
|
|
|
|
|
|
setIsModalOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
Добавить человека <span>+</span>
|
|
|
|
|
|
</AddButton>
|
|
|
|
|
|
</AddWrapper>
|
|
|
|
|
|
|
|
|
|
|
|
<PersonModal
|
|
|
|
|
|
isOpen={isModalOpen}
|
|
|
|
|
|
user={editingIndex === null ? null : users[editingIndex]}
|
|
|
|
|
|
availableTags={availableTags}
|
|
|
|
|
|
onClose={() => {
|
|
|
|
|
|
setIsModalOpen(false);
|
|
|
|
|
|
setEditingIndex(null);
|
|
|
|
|
|
}}
|
|
|
|
|
|
onSave={handleSave}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getAvatarSrc(user: User): string {
|
|
|
|
|
|
if (user.age < 16) {
|
|
|
|
|
|
return user.gender === 'f'
|
|
|
|
|
|
? '/images/broc-girl.png'
|
|
|
|
|
|
: '/images/broc-boy.png';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return user.gender === 'f'
|
|
|
|
|
|
? '/images/broc-woman.png'
|
|
|
|
|
|
: '/images/broc-man.png';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const FamilyList = styled.div`
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
2026-09-02 06:41:12 +00:00
|
|
|
|
const FamilyMember = styled.div`
|
2026-08-24 10:07:16 +00:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: flex;
|
2026-09-02 06:41:12 +00:00
|
|
|
|
align-items: stretch;
|
|
|
|
|
|
padding: 12px 20px;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
border: 1px solid ${({ theme }) => theme.colors.border};
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
transition: 0.2s;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
text-align: left;
|
2026-09-02 06:41:12 +00:00
|
|
|
|
height: 100%;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
border-color: #c0c0c0;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:nth-child(n+1) ${() => Avatar} {
|
|
|
|
|
|
background-color: #CEE0FF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-30 11:47:34 +00:00
|
|
|
|
&:nth-child(2n+2) ${() => Avatar} {
|
2026-08-24 10:07:16 +00:00
|
|
|
|
background-color: #E6F696;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-30 11:47:34 +00:00
|
|
|
|
&:nth-child(3n+3) ${() => Avatar} {
|
|
|
|
|
|
background-color: #fff488;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-30 11:47:34 +00:00
|
|
|
|
&:nth-child(4n+4) ${() => Avatar} {
|
|
|
|
|
|
background-color: #ffd623;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const Avatar = styled.div`
|
|
|
|
|
|
width: 46px;
|
|
|
|
|
|
height: 46px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-right: 14px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
|
|
img {
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const Info = styled.div`
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-09-02 06:41:12 +00:00
|
|
|
|
justify-content: space-between;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const Name = styled.div`
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: ${({ theme }) => theme.colors.darkGreen};
|
2026-09-02 06:41:12 +00:00
|
|
|
|
${props => props.theme.media.mobile(css`
|
|
|
|
|
|
padding-top: 1.5px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
`)}
|
2026-08-24 10:07:16 +00:00
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const Age = styled.div`
|
2026-08-31 08:55:33 +00:00
|
|
|
|
font-size: 16px;
|
2026-08-30 11:47:34 +00:00
|
|
|
|
font-weight: 350;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
color: ${({ theme }) => theme.colors.gray};
|
2026-09-02 06:41:12 +00:00
|
|
|
|
* {
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
${props => props.theme.media.mobile(css`
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
padding-bottom: 4.5px;
|
|
|
|
|
|
`)}
|
2026-08-24 10:07:16 +00:00
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const DeleteButton = styled.button`
|
|
|
|
|
|
color: #d1d5db;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
transition: 0.2s;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 0;
|
2026-09-02 06:41:12 +00:00
|
|
|
|
width: 20px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
align-self: center;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
color: #e53935;
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const AddWrapper = styled.div`
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const AddButton = styled.button`
|
|
|
|
|
|
padding: 18px 23px;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
height: 52px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-08-31 08:55:33 +00:00
|
|
|
|
margin-top: 14px;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
color: ${({ theme }) => theme.colors.green};
|
2026-09-02 11:17:13 +00:00
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
2026-08-24 10:07:16 +00:00
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: 0.2s;
|
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
|
2026-09-02 06:41:12 +00:00
|
|
|
|
span {
|
|
|
|
|
|
color:inherit;
|
2026-09-03 09:28:48 +00:00
|
|
|
|
font-weight: inherit;
|
2026-09-02 06:41:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-24 10:07:16 +00:00
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: #F7FBF6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:active,
|
|
|
|
|
|
&:focus {
|
|
|
|
|
|
background-color: #EEF8EB;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
|
font-weight: 300;
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|