242 lines
9.5 KiB
TypeScript
242 lines
9.5 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { Button } from "@/components/ui/button";
|
||
import { TextInput } from "@/components/ui/TextInput/TextInput";
|
||
import { AGE_GROUPS, GENERATION_GROUPS, parseAgeRange } from "@/lib/age-utils";
|
||
|
||
interface AgeSelectorProps {
|
||
selectedValues: string[];
|
||
onToggleValue: (value: string) => void;
|
||
onAddCustomValue: (value: string) => void;
|
||
}
|
||
|
||
export function AgeSelector({ selectedValues, onToggleValue, onAddCustomValue }: AgeSelectorProps) {
|
||
const [customValue, setCustomValue] = useState("");
|
||
|
||
const handleAddCustom = () => {
|
||
if (customValue.trim()) {
|
||
onAddCustomValue(customValue.trim());
|
||
setCustomValue("");
|
||
}
|
||
};
|
||
|
||
const isValueSelected = (value: string) => selectedValues.includes(value);
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
{/* 🎂 ВОЗРАСТНЫЕ ГРУППЫ */}
|
||
<div className="space-y-3">
|
||
<h4 className="text-sm font-medium text-foreground">🎂 Возрастные группы</h4>
|
||
<div className="grid grid-cols-2 gap-2">
|
||
{AGE_GROUPS.map((group) => {
|
||
const isSelected = isValueSelected(group.id);
|
||
return (
|
||
<button
|
||
key={group.id}
|
||
onClick={() => onToggleValue(group.id)}
|
||
className={`
|
||
relative group p-3 rounded-lg border-2 transition-all duration-200
|
||
hover:shadow-md text-left
|
||
${isSelected
|
||
? "border-primary bg-primary/10 shadow-md"
|
||
: "border-border hover:border-primary/50"
|
||
}
|
||
`}
|
||
title={group.description}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
{/* Возрастной диапазон */}
|
||
<span className="text-lg">🎂</span>
|
||
|
||
{/* Информация */}
|
||
<div className="flex-1 min-w-0">
|
||
<div className={`font-medium text-sm ${
|
||
isSelected ? "text-primary" : "text-foreground"
|
||
}`}>
|
||
{group.name}
|
||
</div>
|
||
<div className="text-xs text-muted-foreground truncate">
|
||
{group.description}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Индикатор выбранного */}
|
||
{isSelected && (
|
||
<div className="w-4 h-4 bg-primary rounded-full flex items-center justify-center flex-shrink-0">
|
||
<span className="text-[10px] text-white">✓</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 🚀 ПОКОЛЕНИЯ */}
|
||
<div className="space-y-3">
|
||
<h4 className="text-sm font-medium text-foreground">🚀 Поколения</h4>
|
||
<div className="grid grid-cols-1 gap-2">
|
||
{GENERATION_GROUPS.map((generation) => {
|
||
const isSelected = isValueSelected(generation.id);
|
||
return (
|
||
<button
|
||
key={generation.id}
|
||
onClick={() => onToggleValue(generation.id)}
|
||
className={`
|
||
relative group p-3 rounded-lg border-2 transition-all duration-200
|
||
hover:shadow-md text-left
|
||
${isSelected
|
||
? "border-primary bg-primary/10 shadow-md"
|
||
: "border-border hover:border-primary/50"
|
||
}
|
||
`}
|
||
title={`Родились ${generation.minYear}-${generation.maxYear}`}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
{/* Иконка поколения */}
|
||
<span className="text-lg">
|
||
{generation.id === 'gen-z' ? '📱' :
|
||
generation.id === 'millennials' ? '💻' :
|
||
generation.id === 'gen-x' ? '📺' :
|
||
generation.id === 'boomers' ? '📻' : '📰'}
|
||
</span>
|
||
|
||
{/* Информация */}
|
||
<div className="flex-1 min-w-0">
|
||
<div className={`font-medium text-sm ${
|
||
isSelected ? "text-primary" : "text-foreground"
|
||
}`}>
|
||
{generation.name}
|
||
</div>
|
||
<div className="text-xs text-muted-foreground">
|
||
{generation.minYear}-{generation.maxYear} • {generation.description}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Индикатор выбранного */}
|
||
{isSelected && (
|
||
<div className="w-4 h-4 bg-primary rounded-full flex items-center justify-center flex-shrink-0">
|
||
<span className="text-[10px] text-white">✓</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 🎯 ДОБАВЛЕНИЕ КАСТОМНЫХ ЗНАЧЕНИЙ */}
|
||
<div className="space-y-2">
|
||
<label className="block text-sm font-medium text-foreground">
|
||
Или добавить точный возраст/диапазон:
|
||
</label>
|
||
<div className="flex gap-2">
|
||
<TextInput
|
||
placeholder="25, 18-21, 30-35, 60+ и т.д."
|
||
value={customValue}
|
||
onChange={(e) => setCustomValue(e.target.value)}
|
||
onKeyDown={(e) => {
|
||
if (e.key === "Enter") {
|
||
e.preventDefault();
|
||
handleAddCustom();
|
||
}
|
||
}}
|
||
/>
|
||
<Button
|
||
onClick={handleAddCustom}
|
||
disabled={!customValue.trim()}
|
||
className="text-sm px-3 py-1"
|
||
>
|
||
Добавить
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Подсказки по форматам */}
|
||
<div className="text-xs text-muted-foreground">
|
||
<strong>Примеры:</strong> 25 (точный возраст), 18-21 (диапазон), 60+ (от 60 лет), age-25 (альтернативный формат)
|
||
</div>
|
||
</div>
|
||
|
||
{/* 📋 ВЫБРАННЫЕ ЗНАЧЕНИЯ */}
|
||
{selectedValues.length > 0 && (
|
||
<div className="space-y-2">
|
||
<label className="block text-sm font-medium text-foreground">
|
||
Выбранные возрастные условия:
|
||
</label>
|
||
<div className="flex flex-wrap gap-1">
|
||
{selectedValues.map((value) => {
|
||
// Ищем в возрастных группах
|
||
const ageGroup = AGE_GROUPS.find(group => group.id === value);
|
||
if (ageGroup) {
|
||
return (
|
||
<span
|
||
key={value}
|
||
className="inline-flex items-center gap-1 px-2 py-1 text-xs bg-primary/10 text-primary rounded-full"
|
||
>
|
||
🎂 {ageGroup.name}
|
||
<button
|
||
onClick={() => onToggleValue(value)}
|
||
className="ml-1 hover:text-red-500 transition-colors"
|
||
title="Удалить"
|
||
>
|
||
×
|
||
</button>
|
||
</span>
|
||
);
|
||
}
|
||
|
||
// Ищем в поколениях
|
||
const generation = GENERATION_GROUPS.find(gen => gen.id === value);
|
||
if (generation) {
|
||
return (
|
||
<span
|
||
key={value}
|
||
className="inline-flex items-center gap-1 px-2 py-1 text-xs bg-blue-100 text-blue-700 rounded-full"
|
||
>
|
||
🚀 {generation.name}
|
||
<button
|
||
onClick={() => onToggleValue(value)}
|
||
className="ml-1 hover:text-red-500 transition-colors"
|
||
title="Удалить"
|
||
>
|
||
×
|
||
</button>
|
||
</span>
|
||
);
|
||
}
|
||
|
||
// Кастомное значение
|
||
const range = parseAgeRange(value);
|
||
return (
|
||
<span
|
||
key={value}
|
||
className="inline-flex items-center gap-1 px-2 py-1 text-xs bg-green-100 text-green-700 rounded-full"
|
||
>
|
||
🎯 {range ? `${range.min}-${range.max === 120 ? '+' : range.max}` : value}
|
||
<button
|
||
onClick={() => onToggleValue(value)}
|
||
className="ml-1 hover:text-red-500 transition-colors"
|
||
title="Удалить"
|
||
>
|
||
×
|
||
</button>
|
||
</span>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 💡 ПОДСКАЗКА */}
|
||
<div className="text-xs text-muted-foreground bg-muted/30 rounded-lg p-3">
|
||
<strong>💡 Как это работает:</strong> Система автоматически рассчитывает возраст из
|
||
даты рождения пользователя. Выберите возрастные группы или поколения, при которых
|
||
должен показываться этот вариант экрана. Можно комбинировать разные условия.
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|