feat: move public portal to root and admin login to /login
This commit is contained in:
@@ -41,7 +41,7 @@ export default function NotFoundClient({ primaryColor, orgName }: NotFoundClient
|
|||||||
|
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard"
|
href="/"
|
||||||
className="flex items-center justify-center gap-2 group px-6 py-3 rounded-xl text-white font-semibold transition-all shadow-lg active:scale-95 hover:opacity-90"
|
className="flex items-center justify-center gap-2 group px-6 py-3 rounded-xl text-white font-semibold transition-all shadow-lg active:scale-95 hover:opacity-90"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: primaryColor,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export async function login(email: string, password: string) {
|
|||||||
// Logout
|
// Logout
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
(await cookies()).delete("auth-token");
|
(await cookies()).delete("auth-token");
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar sessão
|
// Verificar sessão
|
||||||
|
|||||||
@@ -75,7 +75,15 @@ export default function DashboardClient({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
target="_blank"
|
||||||
|
className="w-full sm:w-auto h-11 sm:h-10 px-4 sm:px-6 rounded-xl sm:rounded-lg font-bold text-xs bg-slate-100 hover:bg-slate-200 text-slate-600 transition-all flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<Eye size={16} />
|
||||||
|
Ver Portal Público
|
||||||
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/dashboard/documentos?upload=true"
|
href="/dashboard/documentos?upload=true"
|
||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default async function ConfiguracoesPage() {
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const organization = await getOrganization();
|
const organization = await getOrganization();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default async function DocumentDetailPage({
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default async function NewDocumentPage({
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = await searchParams;
|
const params = await searchParams;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default async function DocumentsPage({
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = await searchParams;
|
const params = await searchParams;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default async function DashboardPage() {
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats = await getDashboardStats();
|
const stats = await getDashboardStats();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default async function ProfilePage() {
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default async function UsuariosPage() {
|
|||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = await getUsers();
|
const users = await getUsers();
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ export default function DocumentViewClient({
|
|||||||
style={{ backgroundColor: primaryColor }}
|
style={{ backgroundColor: primaryColor }}
|
||||||
>
|
>
|
||||||
<div className="max-w-5xl mx-auto flex items-center justify-between">
|
<div className="max-w-5xl mx-auto flex items-center justify-between">
|
||||||
<a href="/portal" className="flex items-center gap-2 md:gap-3 hover:opacity-80 transition-opacity">
|
<a href="/" className="flex items-center gap-2 md:gap-3 hover:opacity-80 transition-opacity">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<img
|
<img
|
||||||
src={organization.logoUrl}
|
src={organization.logoUrl}
|
||||||
@@ -204,7 +204,7 @@ export default function DocumentViewClient({
|
|||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
<a
|
<a
|
||||||
href="/portal"
|
href="/"
|
||||||
className="text-white/80 hover:text-white flex items-center gap-1 md:gap-2 text-xs md:text-sm bg-white/10 hover:bg-white/20 px-2.5 py-1.5 rounded-lg transition-colors"
|
className="text-white/80 hover:text-white flex items-center gap-1 md:gap-2 text-xs md:text-sm bg-white/10 hover:bg-white/20 px-2.5 py-1.5 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
<Home size={14} />
|
<Home size={14} />
|
||||||
|
|||||||
26
src/app/login/page.tsx
Normal file
26
src/app/login/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { prisma } from "@/lib/db";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import LoginClient from "@/components/LoginClient";
|
||||||
|
import { getSession } from "@/app/actions/auth";
|
||||||
|
|
||||||
|
// Force dynamic rendering - this page needs database access
|
||||||
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
// Verifica se o usuário já está logado
|
||||||
|
const session = await getSession();
|
||||||
|
if (session) {
|
||||||
|
redirect("/dashboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se existe alguma organização cadastrada
|
||||||
|
const organization = await prisma.organization.findFirst();
|
||||||
|
|
||||||
|
// Se não houver organização, redireciona para o setup inicial (Instalação)
|
||||||
|
if (!organization) {
|
||||||
|
redirect("/setup");
|
||||||
|
}
|
||||||
|
|
||||||
|
return <LoginClient organization={organization} />;
|
||||||
|
}
|
||||||
@@ -1,28 +1,57 @@
|
|||||||
import React from "react";
|
|
||||||
import { prisma } from "@/lib/db";
|
import { prisma } from "@/lib/db";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import LoginClient from "@/components/LoginClient";
|
import PortalClient from "./portal/PortalClient";
|
||||||
import { getSession } from "@/app/actions/auth";
|
|
||||||
|
|
||||||
// Force dynamic rendering - this page needs database access
|
// Force dynamic rendering - this page needs database access
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
|
export default async function PortalPage() {
|
||||||
export default async function Page() {
|
|
||||||
// Verifica se o usuário já está logado
|
|
||||||
const session = await getSession();
|
|
||||||
if (session) {
|
|
||||||
redirect("/dashboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica se existe alguma organização cadastrada
|
|
||||||
const organization = await prisma.organization.findFirst();
|
const organization = await prisma.organization.findFirst();
|
||||||
|
|
||||||
// Se não houver organização, redireciona para o setup inicial (Instalação)
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
redirect("/setup");
|
redirect("/setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
return <LoginClient organization={organization} />;
|
// Get all public root folders (projects)
|
||||||
|
const publicFolders = await prisma.folder.findMany({
|
||||||
|
where: {
|
||||||
|
organizationId: organization.id,
|
||||||
|
parentId: null,
|
||||||
|
isPublished: true,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: {
|
||||||
|
documents: {
|
||||||
|
where: { isPublished: true }
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
where: { isPublished: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PortalClient
|
||||||
|
organization={{
|
||||||
|
name: organization.name,
|
||||||
|
logoUrl: organization.logoUrl,
|
||||||
|
primaryColor: organization.primaryColor,
|
||||||
|
}}
|
||||||
|
folders={publicFolders.map((f: any) => ({
|
||||||
|
id: f.id,
|
||||||
|
name: f.name,
|
||||||
|
description: f.description,
|
||||||
|
color: f.color,
|
||||||
|
imageUrl: f.imageUrl,
|
||||||
|
documentsCount: f._count.documents,
|
||||||
|
foldersCount: f._count.children,
|
||||||
|
createdAt: f.createdAt,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default function PortalClient({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-between">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-between">
|
||||||
<a href="/portal" className="flex items-center gap-3 sm:gap-4 min-w-0 hover:opacity-80 transition-opacity">
|
<a href="/" className="flex items-center gap-3 sm:gap-4 min-w-0 hover:opacity-80 transition-opacity">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<img src={organization.logoUrl} alt={organization.name} className="h-8 sm:h-10 object-contain shrink-0" />
|
<img src={organization.logoUrl} alt={organization.name} className="h-8 sm:h-10 object-contain shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,56 +1,5 @@
|
|||||||
import { prisma } from "@/lib/db";
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import PortalClient from "./PortalClient";
|
|
||||||
|
|
||||||
// Force dynamic rendering - this page needs database access
|
export default function PortalRedirect() {
|
||||||
export const dynamic = 'force-dynamic';
|
redirect("/");
|
||||||
|
|
||||||
export default async function PortalPage() {
|
|
||||||
const organization = await prisma.organization.findFirst();
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
redirect("/setup");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all public root folders (projects)
|
|
||||||
const publicFolders = await prisma.folder.findMany({
|
|
||||||
where: {
|
|
||||||
organizationId: organization.id,
|
|
||||||
parentId: null,
|
|
||||||
isPublished: true,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: "desc" },
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
documents: {
|
|
||||||
where: { isPublished: true }
|
|
||||||
},
|
|
||||||
children: {
|
|
||||||
where: { isPublished: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PortalClient
|
|
||||||
organization={{
|
|
||||||
name: organization.name,
|
|
||||||
logoUrl: organization.logoUrl,
|
|
||||||
primaryColor: organization.primaryColor,
|
|
||||||
}}
|
|
||||||
folders={publicFolders.map((f: any) => ({
|
|
||||||
id: f.id,
|
|
||||||
name: f.name,
|
|
||||||
description: f.description,
|
|
||||||
color: f.color,
|
|
||||||
imageUrl: f.imageUrl,
|
|
||||||
documentsCount: f._count.documents,
|
|
||||||
foldersCount: f._count.children,
|
|
||||||
createdAt: f.createdAt,
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default async function SetupPage() {
|
|||||||
|
|
||||||
// Se já existe, redireciona para o login (setup já foi feito)
|
// Se já existe, redireciona para o login (setup já foi feito)
|
||||||
if (existingOrg) {
|
if (existingOrg) {
|
||||||
redirect("/");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Se não existe, mostra o setup
|
// Se não existe, mostra o setup
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default function UnauthorizedClient({ primaryColor, orgName }: Unauthoriz
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/login"
|
||||||
className="px-6 py-3 rounded-xl font-semibold text-slate-500 hover:bg-slate-100 transition-all"
|
className="px-6 py-3 rounded-xl font-semibold text-slate-500 hover:bg-slate-100 transition-all"
|
||||||
>
|
>
|
||||||
Fazer Login com outra conta
|
Fazer Login com outra conta
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default function FolderViewClient({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
<header className="bg-white border-b border-slate-100 sticky top-0 z-10">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-between">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 sm:h-20 flex items-center justify-between">
|
||||||
<a href="/portal" className="flex items-center gap-3 sm:gap-4 min-w-0 hover:opacity-80 transition-opacity">
|
<a href="/" className="flex items-center gap-3 sm:gap-4 min-w-0 hover:opacity-80 transition-opacity">
|
||||||
{organization.logoUrl ? (
|
{organization.logoUrl ? (
|
||||||
<img src={organization.logoUrl} alt={organization.name} className="h-8 sm:h-10 object-contain shrink-0" />
|
<img src={organization.logoUrl} alt={organization.name} className="h-8 sm:h-10 object-contain shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
@@ -120,7 +120,7 @@ export default function FolderViewClient({
|
|||||||
</a>
|
</a>
|
||||||
<div className="flex items-center gap-2 sm:gap-3">
|
<div className="flex items-center gap-2 sm:gap-3">
|
||||||
<a
|
<a
|
||||||
href="/portal"
|
href="/"
|
||||||
className="flex items-center gap-1.5 px-2.5 sm:px-3 py-1.5 sm:py-2 rounded-lg bg-slate-100 hover:bg-slate-200 text-slate-600 hover:text-slate-900 transition-colors text-[10px] sm:text-xs font-bold uppercase tracking-wide"
|
className="flex items-center gap-1.5 px-2.5 sm:px-3 py-1.5 sm:py-2 rounded-lg bg-slate-100 hover:bg-slate-200 text-slate-600 hover:text-slate-900 transition-colors text-[10px] sm:text-xs font-bold uppercase tracking-wide"
|
||||||
>
|
>
|
||||||
<Home size={14} />
|
<Home size={14} />
|
||||||
|
|||||||
Reference in New Issue
Block a user