marketing/src/components/Family/FamilySection.tsx

260 lines
6.6 KiB
TypeScript
Raw Normal View History

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;
}
`;