rename: "Team" billing tier to "Family"
All literal "team" tier-value references renamed to "family" across API routes, admin UI, OpenAPI schemas, and lib/tiers.ts. The DB enum value itself is renamed in place via ALTER TYPE ... RENAME VALUE (migration 0044) rather than drizzle-kit's auto-generated drop-and-recreate-the-enum migration, which would have failed against any existing row still holding 'team' — RENAME VALUE preserves existing data with no cast/backfill needed. Also adds STRIPE_PLAN.md — a full Stripe billing integration plan (Checkout+Portal, tier→Price mapping, admin billing dashboard, and a multi-user Family-group design since Family is meant to cover several accounts under one subscription, not one payer). Planning only, no Stripe code yet. v0.47.0
This commit is contained in:
@@ -16,7 +16,7 @@ export function CreateUserDialog() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
const [role, setRole] = useState<"user" | "moderator" | "admin">("user");
|
||||
const [tier, setTier] = useState<"free" | "pro" | "team">("free");
|
||||
const [tier, setTier] = useState<"free" | "pro" | "family">("free");
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
async function handleCreate() {
|
||||
@@ -85,7 +85,7 @@ export function CreateUserDialog() {
|
||||
<SelectContent>
|
||||
<SelectItem value="free">Free</SelectItem>
|
||||
<SelectItem value="pro">Pro</SelectItem>
|
||||
<SelectItem value="team">Team</SelectItem>
|
||||
<SelectItem value="family">Family</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ type Invite = {
|
||||
token: string;
|
||||
email: string | null;
|
||||
role: "user" | "moderator" | "admin";
|
||||
tier: "free" | "pro" | "team";
|
||||
tier: "free" | "pro" | "family";
|
||||
createdAt: string;
|
||||
expiresAt: string | null;
|
||||
};
|
||||
@@ -33,7 +33,7 @@ export function InvitesManager({ invites, appUrl }: { invites: Invite[]; appUrl:
|
||||
const router = useRouter();
|
||||
const [email, setEmail] = useState("");
|
||||
const [role, setRole] = useState<"user" | "moderator" | "admin">("user");
|
||||
const [tier, setTier] = useState<"free" | "pro" | "team">("free");
|
||||
const [tier, setTier] = useState<"free" | "pro" | "family">("free");
|
||||
const [creating, setCreating] = useState(false);
|
||||
const [revokeId, setRevokeId] = useState<string | null>(null);
|
||||
|
||||
@@ -106,7 +106,7 @@ export function InvitesManager({ invites, appUrl }: { invites: Invite[]; appUrl:
|
||||
<SelectContent>
|
||||
<SelectItem value="free">Free</SelectItem>
|
||||
<SelectItem value="pro">Pro</SelectItem>
|
||||
<SelectItem value="team">Team</SelectItem>
|
||||
<SelectItem value="family">Family</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
@@ -15,12 +15,12 @@ import { Label } from "@/components/ui/label";
|
||||
interface UserEditorProps {
|
||||
userId: string;
|
||||
currentRole: "user" | "moderator" | "admin";
|
||||
currentTier: "free" | "pro" | "team";
|
||||
currentTier: "free" | "pro" | "family";
|
||||
}
|
||||
|
||||
export function UserEditor({ userId, currentRole, currentTier }: UserEditorProps) {
|
||||
const [role, setRole] = useState<"user" | "moderator" | "admin">(currentRole);
|
||||
const [tier, setTier] = useState<"free" | "pro" | "team">(currentTier);
|
||||
const [tier, setTier] = useState<"free" | "pro" | "family">(currentTier);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
async function handleSave() {
|
||||
@@ -68,7 +68,7 @@ export function UserEditor({ userId, currentRole, currentTier }: UserEditorProps
|
||||
<SelectContent>
|
||||
<SelectItem value="free">Free</SelectItem>
|
||||
<SelectItem value="pro">Pro</SelectItem>
|
||||
<SelectItem value="team">Team</SelectItem>
|
||||
<SelectItem value="family">Family</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user