From 73b7e171c9bfff3a1d176f2a6b7a509b093fc4e4 Mon Sep 17 00:00:00 2001 From: Sidney Date: Sun, 7 Jun 2026 20:27:28 -0300 Subject: [PATCH] feat: adicionar campo de senha ao criar novo tenant no Admin --- admin/package-lock.json | 17 +++++++++++++++++ admin/package.json | 2 ++ admin/src/app/api/tenants/route.ts | 25 +++++++++++++++++++++++-- admin/src/components/Tenants.tsx | 7 +++++-- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/admin/package-lock.json b/admin/package-lock.json index 4d68638..9fb1bc2 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -8,11 +8,13 @@ "name": "agendapro-admin", "version": "1.0.0", "dependencies": { + "@types/bcryptjs": "^2.4.6", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^22.15.21", "@types/pg": "^8.11.10", "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", + "bcryptjs": "^3.0.3", "jsonwebtoken": "^9.0.3", "lucide-react": "^0.511.0", "next": "^15.3.3", @@ -641,6 +643,12 @@ "tslib": "^2.8.0" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "license": "MIT" + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", @@ -695,6 +703,15 @@ "@types/react": "^19.2.0" } }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", diff --git a/admin/package.json b/admin/package.json index 94d9d1d..15c3abc 100644 --- a/admin/package.json +++ b/admin/package.json @@ -8,11 +8,13 @@ "start": "next start -p 3001" }, "dependencies": { + "@types/bcryptjs": "^2.4.6", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^22.15.21", "@types/pg": "^8.11.10", "@types/react": "^19.1.4", "@types/react-dom": "^19.1.5", + "bcryptjs": "^3.0.3", "jsonwebtoken": "^9.0.3", "lucide-react": "^0.511.0", "next": "^15.3.3", diff --git a/admin/src/app/api/tenants/route.ts b/admin/src/app/api/tenants/route.ts index 2712391..2a71df9 100644 --- a/admin/src/app/api/tenants/route.ts +++ b/admin/src/app/api/tenants/route.ts @@ -28,22 +28,43 @@ export async function GET(req: NextRequest) { } } +import bcrypt from 'bcryptjs'; + export async function POST(req: NextRequest) { try { - const { name, slug, email, phone, plan, status } = await req.json(); + const { name, slug, email, phone, plan, status, password } = await req.json(); if (!name || !slug) return NextResponse.json({ error: 'Missing fields' }, { status: 400 }); const planRes = await query('SELECT id FROM plans WHERE name = $1 LIMIT 1', [plan || 'Starter']); const planId = planRes.rows.length > 0 ? planRes.rows[0].id : null; + // Start transaction + await query('BEGIN'); + + // 1. Create Tenant const res = await query( `INSERT INTO tenants (name, slug, email, phone, active_plan_id, subscription_status, subscription_ends_at) VALUES ($1, $2, $3, $4, $5, $6, NOW() + INTERVAL '30 days') RETURNING *`, [name, slug, email, phone, planId, status || 'trial'] ); - return NextResponse.json(res.rows[0]); + const tenant = res.rows[0]; + + // 2. Create Owner User if password provided + if (email && password) { + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(password, salt); + await query( + `INSERT INTO users (tenant_id, name, email, password_hash, role, phone) + VALUES ($1, $2, $3, $4, 'owner', $5)`, + [tenant.id, name, email, hash, phone] + ); + } + + await query('COMMIT'); + return NextResponse.json(tenant); } catch (error: any) { + await query('ROLLBACK'); return NextResponse.json({ error: error.message }, { status: 500 }); } } diff --git a/admin/src/components/Tenants.tsx b/admin/src/components/Tenants.tsx index e3886d1..d55be80 100644 --- a/admin/src/components/Tenants.tsx +++ b/admin/src/components/Tenants.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import { Search, Plus, X, Edit, Trash2, AlertTriangle, Ban, CheckCircle } from 'lucide-react'; interface Tenant { - id: string; name: string; slug: string; type: string; plan: string; status: string; email: string; phone: string; mrr: number; createdAt: string; trialEnds: string; + id: string; name: string; slug: string; type: string; plan: string; status: string; email: string; phone: string; mrr: number; createdAt: string; trialEnds: string; password?: string; } const statusMap: Record = { @@ -168,9 +168,12 @@ export default function Tenants() {
-
setForm(p => ({ ...p, email: e.target.value }))} />
+
setForm(p => ({ ...p, email: e.target.value }))} />
setForm(p => ({ ...p, phone: e.target.value }))} />
+ {!editing && ( +
setForm(p => ({ ...p, password: e.target.value }))} placeholder="Mínimo 6 caracteres" />
+ )}