marketing/src/components/Family/FamilySection.tsx

238 lines
5.9 KiB
TypeScript
Raw Normal View History

2026-08-24 10:07:16 +00:00
import { useState } from 'react';
import styled from 'styled-components';
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}`}
type="button"
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);
}}
>
×
</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;
`;
const FamilyMember = styled.button`
width: 100%;
display: flex;
align-items: center;
padding: 12px 16px;
border: 1px solid ${({ theme }) => theme.colors.border};
border-radius: 12px;
background: #fff;
transition: 0.2s;
cursor: pointer;
text-align: left;
&: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;
`;
const Name = styled.div`
font-size: 16px;
font-weight: 600;
color: ${({ theme }) => theme.colors.darkGreen};
`;
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};
`;
const DeleteButton = styled.button`
color: #d1d5db;
cursor: pointer;
font-size: 18px;
padding: 0 8px;
transition: 0.2s;
background: transparent;
border: 0;
&: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};
font-size: 15px;
font-weight: 350;
cursor: pointer;
transition: 0.2s;
background-color: transparent;
border: 0;
&:hover {
background-color: #F7FBF6;
}
&:active,
&:focus {
background-color: #EEF8EB;
outline: none;
}
span {
font-size: 20px;
margin-left: 8px;
font-weight: 300;
}
`;