feat: platform update
This commit is contained in:
@@ -45,7 +45,22 @@ import {
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import type { Channel } from "@/lib/types/api";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useWorkspace } from "@/components/providers/workspace-provider";
|
||||
import type { Channel, Project } from "@/lib/types/api";
|
||||
|
||||
// ============================================================================
|
||||
// Helpers
|
||||
@@ -70,6 +85,7 @@ type SortOrder = "asc" | "desc" | null;
|
||||
export default function ChannelsCatalogPage() {
|
||||
const params = useParams();
|
||||
const workspaceId = params?.workspaceId as string;
|
||||
const { currentProject, projects } = useWorkspace();
|
||||
|
||||
const [channels, setChannels] = useState<Channel[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -78,6 +94,11 @@ export default function ChannelsCatalogPage() {
|
||||
const [sortField, setSortField] = useState<SortField | null>(null);
|
||||
const [sortOrder, setSortOrder] = useState<SortOrder>(null);
|
||||
|
||||
// Placement dialog
|
||||
const [selectedChannel, setSelectedChannel] = useState<Channel | null>(null);
|
||||
const [showPlacementDialog, setShowPlacementDialog] = useState(false);
|
||||
const [placementProjectId, setPlacementProjectId] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const loadChannels = async () => {
|
||||
try {
|
||||
@@ -126,6 +147,23 @@ export default function ChannelsCatalogPage() {
|
||||
return <ArrowUpDown className="h-4 w-4 ml-1" />;
|
||||
};
|
||||
|
||||
const handleOpenPlacement = (channel: Channel) => {
|
||||
setSelectedChannel(channel);
|
||||
if (currentProject) {
|
||||
setPlacementProjectId(currentProject.id);
|
||||
}
|
||||
setShowPlacementDialog(true);
|
||||
};
|
||||
|
||||
const handleConfirmPlacement = () => {
|
||||
if (!placementProjectId || !selectedChannel) return;
|
||||
window.location.href = `/dashboard/${workspaceId}/purchase-plans/${placementProjectId}?openPlacement=true&channel_id=${selectedChannel.id}`;
|
||||
};
|
||||
|
||||
const activeProjects = useMemo(() => {
|
||||
return projects.filter(p => p.status === "active");
|
||||
}, [projects]);
|
||||
|
||||
// Filter and sort channels
|
||||
const filteredChannels = useMemo(() => {
|
||||
let result = [...channels];
|
||||
@@ -317,10 +355,8 @@ export default function ChannelsCatalogPage() {
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={`/dashboard/${workspaceId}/placements/new?channel_id=${channel.id}`}>
|
||||
Разместить
|
||||
</a>
|
||||
<Button variant="outline" size="sm" onClick={() => handleOpenPlacement(channel)}>
|
||||
Разместить
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -361,23 +397,65 @@ export default function ChannelsCatalogPage() {
|
||||
{channel.description}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||
<Users className="h-4 w-4" />
|
||||
{formatNumber(channel.subscribers_count)}
|
||||
</div>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={`/dashboard/${workspaceId}/placements/new?channel_id=${channel.id}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||
<Users className="h-4 w-4" />
|
||||
{formatNumber(channel.subscribers_count)}
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onClick={() => handleOpenPlacement(channel)}>
|
||||
Разместить
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Dialog open={showPlacementDialog} onOpenChange={setShowPlacementDialog}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Создание размещения</DialogTitle>
|
||||
<DialogDescription>
|
||||
Выберите проект для размещения на канале {selectedChannel?.title}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Проект</label>
|
||||
<Select value={placementProjectId} onValueChange={setPlacementProjectId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Выберите проект" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{activeProjects.map((project) => (
|
||||
<SelectItem key={project.id} value={project.id}>
|
||||
{project.title}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{activeProjects.length === 0 && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Нет активных проектов. Создайте проект в настройках.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-4">
|
||||
<Button variant="outline" onClick={() => setShowPlacementDialog(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleConfirmPlacement}
|
||||
disabled={!placementProjectId || activeProjects.length === 0}
|
||||
>
|
||||
Продолжить
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user