feat: complete platform management features and enhance SEO

- Add comprehensive company management interface with editing, suspension
- Implement user invitation system within companies
- Add Add Company modal with form validation
- Create platform auth configuration in separate lib file
- Add comprehensive SEO metadata with OpenGraph and structured data
- Fix auth imports and route exports for Next.js 15 compatibility
- Add toast notifications with RadixUI components
- Update TODO status to reflect 100% completion of platform features
This commit is contained in:
2025-06-28 13:20:48 +02:00
parent fdb1a9c2b1
commit 1972c5e9f7
26 changed files with 1627 additions and 251 deletions

View File

@ -1,16 +1,26 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { platformAuthOptions } from "../../auth/[...nextauth]/route";
import { platformAuthOptions } from "../../../../../lib/platform-auth";
import { prisma } from "../../../../../lib/prisma";
import { CompanyStatus } from "@prisma/client";
interface PlatformSession {
user: {
id?: string;
name?: string;
email?: string;
isPlatformUser?: boolean;
platformRole?: string;
};
}
// GET /api/platform/companies/[id] - Get company details
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(platformAuthOptions);
const session = await getServerSession(platformAuthOptions) as PlatformSession | null;
if (!session?.user?.isPlatformUser) {
return NextResponse.json({ error: "Platform access required" }, { status: 401 });
@ -24,28 +34,19 @@ export async function GET(
users: {
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
updatedAt: true,
invitedBy: true,
invitedAt: true,
},
},
sessions: {
select: {
id: true,
startTime: true,
endTime: true,
sentiment: true,
category: true,
},
take: 10,
orderBy: { createdAt: "desc" },
},
_count: {
select: {
sessions: true,
imports: true,
users: true,
},
},
},
@ -55,7 +56,7 @@ export async function GET(
return NextResponse.json({ error: "Company not found" }, { status: 404 });
}
return NextResponse.json({ company });
return NextResponse.json(company);
} catch (error) {
console.error("Platform company details error:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
@ -76,10 +77,12 @@ export async function PATCH(
const { id } = await params;
const body = await request.json();
const { name, csvUrl, csvUsername, csvPassword, status } = body;
const { name, email, maxUsers, csvUrl, csvUsername, csvPassword, status } = body;
const updateData: any = {};
if (name !== undefined) updateData.name = name;
if (email !== undefined) updateData.email = email;
if (maxUsers !== undefined) updateData.maxUsers = maxUsers;
if (csvUrl !== undefined) updateData.csvUrl = csvUrl;
if (csvUsername !== undefined) updateData.csvUsername = csvUsername;
if (csvPassword !== undefined) updateData.csvPassword = csvPassword;

View File

@ -0,0 +1,132 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { platformAuthOptions } from "../../../../../../lib/platform-auth";
import { prisma } from "../../../../../../lib/prisma";
import { hash } from "bcryptjs";
// POST /api/platform/companies/[id]/users - Invite user to company
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(platformAuthOptions);
if (!session?.user?.isPlatformUser || session.user.platformRole === "SUPPORT") {
return NextResponse.json({ error: "Admin access required" }, { status: 403 });
}
const { id: companyId } = await params;
const body = await request.json();
const { name, email, role = "USER" } = body;
if (!name || !email) {
return NextResponse.json({ error: "Name and email are required" }, { status: 400 });
}
// Check if company exists
const company = await prisma.company.findUnique({
where: { id: companyId },
include: { _count: { select: { users: true } } },
});
if (!company) {
return NextResponse.json({ error: "Company not found" }, { status: 404 });
}
// Check if user limit would be exceeded
if (company._count.users >= company.maxUsers) {
return NextResponse.json(
{ error: "Company has reached maximum user limit" },
{ status: 400 }
);
}
// Check if user already exists in this company
const existingUser = await prisma.user.findFirst({
where: {
email,
companyId,
},
});
if (existingUser) {
return NextResponse.json(
{ error: "User already exists in this company" },
{ status: 400 }
);
}
// Generate a temporary password (in a real app, you'd send an invitation email)
const tempPassword = `temp${Math.random().toString(36).slice(-8)}`;
const hashedPassword = await hash(tempPassword, 10);
// Create the user
const user = await prisma.user.create({
data: {
name,
email,
hashedPassword,
role,
companyId,
invitedBy: session.user.email,
invitedAt: new Date(),
},
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
invitedBy: true,
invitedAt: true,
},
});
// In a real application, you would send an email with login credentials
// For now, we'll return the temporary password
return NextResponse.json({
user,
tempPassword, // Remove this in production and send via email
message: "User invited successfully. In production, credentials would be sent via email.",
});
} catch (error) {
console.error("Platform user invitation error:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}
// GET /api/platform/companies/[id]/users - Get company users
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await getServerSession(platformAuthOptions);
if (!session?.user?.isPlatformUser) {
return NextResponse.json({ error: "Platform access required" }, { status: 401 });
}
const { id: companyId } = await params;
const users = await prisma.user.findMany({
where: { companyId },
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
invitedBy: true,
invitedAt: true,
},
orderBy: { createdAt: "desc" },
});
return NextResponse.json({ users });
} catch (error) {
console.error("Platform users list error:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}