feat: update analytics

This commit is contained in:
ivannoskov
2026-02-03 18:55:09 +03:00
parent d87d6e2568
commit e27a171c63
8 changed files with 1529 additions and 461 deletions

View File

@@ -471,7 +471,7 @@ function CellRenderer({
<td key={column.id} className="py-2 px-3">
{placement.invite_link ? (
<code className="text-xs bg-muted/50 px-2 py-1 rounded truncate max-w-[200px]">
{placement.invite_link}
{placement.invite_link.slice(8, 20)}
</code>
) : (
<span className="text-muted-foreground"></span>
@@ -498,8 +498,16 @@ function CellRenderer({
);
case "cost":
const isFixedFormat = placement.details?.cost?.type === "fixed";
const canEditCost = canEdit && isFixedFormat;
const costType = placement.details?.cost?.type || "fixed";
const costValue = placement.details?.cost?.value || 0;
const viewsCount = placement.placement_post?.views_count || 0;
const subsCount = placement.placement_post?.subscriptions_count || 0;
// Если тип CPM - показываем рассчитанную стоимость = CPM * views / 1000
// Если тип Fixed - показываем фиксированную стоимость
const calculatedCost = costType === "cpm" ? (viewsCount > 0 ? (costValue * viewsCount) / 1000 : null) : costValue;
const canEditCost = canEdit && costType === "fixed";
return (
<td key={column.id} className="py-2 px-3">
{isEditing && editingCell?.field === "cost" ? (
@@ -509,7 +517,7 @@ function CellRenderer({
step="0.01"
autoFocus
className="h-7 min-w-[80px] bg-background px-2 text-xs"
defaultValue={effectiveCost}
defaultValue={calculatedCost || 0}
onChange={(e) => {
const raw = e.target.value;
if (!raw) {
@@ -533,8 +541,55 @@ function CellRenderer({
canEditCost && setEditingCell({ placementId: placement.id, field: "cost" })
}
>
<span>{effectiveCost ? formatCurrency(effectiveCost) : "—"}</span>
<Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
<span className="font-medium">{calculatedCost ? formatCurrency(calculatedCost) : "—"}</span>
{costType === "fixed" && <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />}
</button>
)}
</td>
);
case "cpm":
// Если тип CPM - показываем CPM значение
// Если тип Fixed - рассчитываем CPM = cost / views * 1000
const cpmType = placement.details?.cost?.type || "fixed";
const cpmValue = placement.details?.cost?.value || 0;
const cpmViewsCount = placement.placement_post?.views_count || 0;
const calculatedCpm = cpmType === "cpm" ? cpmValue : (cpmViewsCount > 0 ? (cpmValue / cpmViewsCount) * 1000 : null);
const canEditCpm = canEdit && cpmType === "cpm";
return (
<td key={column.id} className="py-2 px-3">
{isEditing && editingCell?.field === "cpm" ? (
<Input
type="number"
min="0"
step="0.01"
autoFocus
className="h-7 min-w-[80px] bg-background px-2 text-xs"
defaultValue={calculatedCpm || 0}
onChange={(e) => {
const raw = e.target.value;
if (!raw) return;
const value = Number(raw);
if (Number.isNaN(value)) return;
handleDraftChange(placement.id, {
cost: { type: "cpm", value },
});
}}
onBlur={() => setEditingCell(null)}
/>
) : (
<button
type="button"
disabled={!canEditCpm}
className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
onClick={() =>
canEditCpm && setEditingCell({ placementId: placement.id, field: "cpm" })
}
>
<span>{calculatedCpm ? formatCurrency(calculatedCpm) : "—"}</span>
{cpmType === "cpm" && <Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />}
</button>
)}
</td>
@@ -750,48 +805,6 @@ function CellRenderer({
</td>
);
case "cpm":
const isCpmFormat = placement.details?.cost?.type === "cpm";
const canEditCpm = canEdit && isCpmFormat;
return (
<td key={column.id} className="py-2 px-3">
{isEditing && editingCell?.field === "cpm" ? (
<Input
type="number"
min="0"
step="0.01"
autoFocus
className="h-7 min-w-[80px] bg-background px-2 text-xs"
defaultValue={cpm || 0}
onChange={(e) => {
const raw = e.target.value;
if (!raw) return;
const value = Number(raw);
if (Number.isNaN(value)) return;
const currentCost = placement.details?.cost;
handleDraftChange(placement.id, {
cost: { type: "cpm", value: (currentCost?.type === "cpm" ? currentCost.value : 0) },
cpm: value,
});
}}
onBlur={() => setEditingCell(null)}
/>
) : (
<button
type="button"
disabled={!canEditCpm}
className="group inline-flex min-h-[1.75rem] w-full items-center justify-between rounded px-2 text-xs text-left hover:bg-muted/50 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-default"
onClick={() =>
canEditCpm && setEditingCell({ placementId: placement.id, field: "cpm" })
}
>
<span>{cpm ? formatCurrency(cpm) : "—"}</span>
<Pencil className="h-3 w-3 opacity-0 group-hover:opacity-50 transition-opacity" />
</button>
)}
</td>
);
case "conversion_24h":
return (
<td key={column.id} className="py-2 px-3 text-muted-foreground text-xs">