Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion app/admin/content/_components/form-elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,25 @@ export function TextInput({
placeholder,
disabled,
mono,
className,
autoFocus,
}: {
value: string;
onChange: (v: string) => void;
placeholder?: string;
disabled?: boolean;
mono?: boolean;
className?: string;
autoFocus?: boolean;
}) {
return (
<input
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
disabled={disabled}
className={`h-10 w-full rounded-lg border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 disabled:opacity-50 ${mono ? "font-mono" : ""}`}
autoFocus={autoFocus}
className={`h-10 w-full rounded-lg border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 disabled:opacity-50 ${mono ? "font-mono" : ""} ${className || ""}`}
/>
);
}
Expand Down
157 changes: 0 additions & 157 deletions app/admin/content/_components/forms/pricing-copy-form.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions app/admin/content/content-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { GenericForm } from "./_components/forms/generic-form";
import { InterviewEnForm } from "./_components/forms/interview-en-form";
import { ProblemForm } from "./_components/forms/problem-form";
import { TechnicalTestForm } from "./_components/forms/technical-test-form";
import { PricingCopyForm } from "./_components/forms/pricing-copy-form";
import {
ContentEditorProps,
DEFAULT_META,
Expand Down Expand Up @@ -107,8 +106,6 @@ export function ContentEditor({ mode, initialData }: ContentEditorProps) {
return <ConceptForm {...formProps} />;
case "technical-test":
return <TechnicalTestForm {...formProps} />;
case "pricing-copy":
return <PricingCopyForm {...formProps} />;
default:
return <GenericForm {...formProps} contentType={contentType} />;
}
Expand Down
2 changes: 1 addition & 1 deletion app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const ALL_NAV_ITEMS = [
adminOnly: true,
},
{
href: "/admin/content?access=pro",
href: "/admin/pricing",
label: "Planos & Preços",
icon: "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
adminOnly: true,
Expand Down
102 changes: 102 additions & 0 deletions app/admin/pricing/_components/feature-manager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use client";

import { useState } from "react";
import { Plus, Trash2, Edit2, Check } from "lucide-react";
import { Button } from "@/components/ui/button";
import { TextInput } from "../../content/_components/form-elements";

interface Feature {
id: string;
label: string;
planId: string;
}

interface FeatureManagerProps {
planId: string;
features: Feature[];
onAdd: (planId: string, label: string) => Promise<void>;
onRemove: (id: string, planId: string) => Promise<void>;
onUpdate: (id: string, label: string, planId: string) => Promise<void>;
}

export function FeatureManager({
planId,
features,
onAdd,
onRemove,
onUpdate
}: FeatureManagerProps) {
const [newLabel, setNewLabel] = useState("");
const [editingId, setEditingId] = useState<string | null>(null);

async function handleAdd() {
if (!newLabel.trim()) return;
await onAdd(planId, newLabel.trim());
setNewLabel("");
}

return (
<div className="space-y-3 mt-6 border-t border-border pt-6">
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-muted-foreground mb-4">Vantagens Manuais</h4>

<div className="space-y-2">
{features.map((f) => (
<div key={f.id} className="flex items-center justify-between gap-2 group min-h-[32px]">
<div className="flex-1">
{editingId === f.id ? (
<div className="flex items-center gap-2">
<TextInput
value={f.label}
onChange={(v) => onUpdate(f.id, v, planId)}
className="h-8 text-xs"
autoFocus
/>
<button
onClick={() => setEditingId(null)}
className="p-1.5 bg-primary text-primary-foreground"
>
<Check className="h-3 w-3" />
</button>
</div>
) : (
<div className="flex items-center gap-2 text-xs text-foreground/80">
<div className="h-1 w-1 bg-primary/40" />
{f.label}
</div>
)}
</div>

{!editingId && (
<div className="flex items-center opacity-0 group-hover:opacity-100 transition-all">
<button
onClick={() => setEditingId(f.id)}
className="p-1 text-primary hover:bg-primary/10"
>
<Edit2 className="h-3 w-3" />
</button>
<button
onClick={() => onRemove(f.id, planId)}
className="p-1 text-destructive hover:bg-destructive/10"
>
<Trash2 className="h-3 w-3" />
</button>
</div>
)}
</div>
))}
</div>

<div className="flex gap-2 mt-4">
<TextInput
placeholder="Nova vantagem manual..."
value={newLabel}
onChange={setNewLabel}
className="h-8 text-xs"
/>
<Button onClick={handleAdd} size="sm" className="h-8 px-3">
<Plus className="h-3 w-3" />
</Button>
</div>
</div>
);
}
61 changes: 61 additions & 0 deletions app/admin/pricing/_components/inventory-summary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client";

import { Package } from "lucide-react";

interface SummaryItem {
category?: string;
label?: string;
count: number;
}

interface InventorySummaryProps {
pro: SummaryItem[];
free: SummaryItem[];
}

export function InventorySummary({ pro, free }: InventorySummaryProps) {
if (pro.length === 0 && free.length === 0) return null;

return (
<div className="rounded-xl border border-primary/20 bg-primary/5 p-4 space-y-4">
<div className="flex items-center gap-2">
<Package className="h-4 w-4 text-primary" />
<h3 className="text-xs font-black uppercase tracking-widest text-primary">Resumo do Conteúdo (Referência)</h3>
</div>

<div className="grid gap-6 md:grid-cols-2">
{/* PRO */}
<div className="space-y-2">
<div className="text-[10px] font-bold text-primary uppercase">Plano Pro</div>
<div className="flex flex-wrap gap-2">
{pro.map((s) => (
<div key={s.category} className="flex items-center gap-2 bg-background/50 px-2 py-1 border border-primary/10">
<span className="text-xs font-bold text-foreground">{s.count}</span>
<span className="text-[10px] uppercase tracking-tight text-muted-foreground">{s.category}</span>
</div>
))}
{pro.length === 0 && <span className="text-[10px] italic text-muted-foreground">Nenhum conteúdo Pro</span>}
</div>
</div>

{/* FREE */}
<div className="space-y-2">
<div className="text-[10px] font-bold text-muted-foreground uppercase">Plano Free</div>
<div className="flex flex-wrap gap-2">
{free.map((s) => (
<div key={s.label} className="flex items-center gap-2 bg-background/50 px-2 py-1 border border-border">
<span className="text-xs font-bold text-foreground">{s.count}</span>
<span className="text-[10px] uppercase tracking-tight text-muted-foreground">{s.label}</span>
</div>
))}
{free.length === 0 && <span className="text-[10px] italic text-muted-foreground">Nenhum conteúdo Free</span>}
</div>
</div>
</div>

<p className="text-[9px] text-muted-foreground italic border-t border-primary/10 pt-2">
Use estes números como base para escrever as vantagens manuais abaixo.
</p>
</div>
);
}
Loading
Loading