feat: Implement AI tutor functionality, add branding fields, and improve API URL handling.

This commit is contained in:
2026-01-23 14:48:41 -03:00
parent 60e2af72f0
commit 470c7f0172
30 changed files with 1352 additions and 274 deletions
@@ -1,7 +1,7 @@
'use client';
import { useState, useEffect } from 'react';
import { cmsApi, Organization, getImageUrl } from '@/lib/api';
import { cmsApi, Organization, getImageUrl, API_BASE_URL } from '@/lib/api';
import { useAuth } from '@/context/AuthContext';
import Image from 'next/image';
import { Plus, Building2, Globe, Calendar, ExternalLink, ShieldCheck, Palette, Upload, Save, X, Fingerprint, Key, Settings2 } from 'lucide-react';
@@ -176,18 +176,18 @@ export default function OrganizationsPage() {
}
return (
<div className="space-y-8 animate-in fade-in duration-500">
<div className="flex justify-between items-center">
<div className="space-y-6 md:space-y-8 animate-in fade-in duration-500 p-4 md:p-0">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div>
<h1 className="text-3xl font-bold tracking-tight">Organizations</h1>
<p className="text-gray-400 mt-1">Manage tenants and isolated environments.</p>
<h1 className="text-2xl md:text-3xl font-bold tracking-tight">Organizations</h1>
<p className="text-gray-400 mt-1 text-sm">Manage tenants and isolated environments.</p>
</div>
<button
onClick={() => setIsModalOpen(true)}
className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-all shadow-lg shadow-blue-500/20 shadow-glow"
className="w-full sm:w-auto flex items-center justify-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-all shadow-lg shadow-blue-500/20 shadow-glow"
>
<Plus className="w-4 h-4" />
New Organization
<span className="inline">New Organization</span>
</button>
</div>
@@ -570,7 +570,7 @@ export default function OrganizationsPage() {
</div>
<p className="text-[10px] text-blue-300 leading-relaxed">
1. Register OpenCCB as an application in your Identity Provider (Okta, Google, Azure AD).<br />
2. Set the Redirect URI to: <span className="font-mono bg-blue-500/20 px-1">http://localhost:3001/auth/sso/callback</span><br />
2. Set the Redirect URI to: <span className="font-mono bg-blue-500/20 px-1">{API_BASE_URL}/auth/sso/callback</span><br />
3. Copy the Issuer URL, Client ID, and Client Secret here.
</p>
</div>
+1 -1
View File
@@ -237,7 +237,7 @@ export default function StudioLoginPage() {
<div className="mt-6 pt-6 border-t border-white/10 text-center">
<p className="text-sm text-gray-400">
Are you a student?{" "}
<a href="http://localhost:3003/auth/login" className="text-blue-400 hover:text-blue-300 font-bold">
<a href="http://192.168.0.254:3003/auth/login" className="text-blue-400 hover:text-blue-300 font-bold">
Go to Student Portal
</a>
</p>
+1 -1
View File
@@ -31,7 +31,7 @@ body {
}
.glass-card {
@apply glass rounded-2xl p-6;
@apply glass rounded-2xl p-4 md:p-6;
}
.btn-premium {
+1 -1
View File
@@ -29,7 +29,7 @@ export default function RootLayout({
<I18nProvider>
<AuthGuard>
<BrandingManager />
<header className="h-20 glass sticky top-0 z-50 px-8 flex items-center justify-between border-b border-white/5 backdrop-blur-xl bg-black/40">
<header className="h-16 md:h-20 glass sticky top-0 z-50 px-4 md:px-8 flex items-center justify-between border-b border-white/5 backdrop-blur-xl bg-black/40">
<Link href="/" className="flex items-center gap-3 group">
<div className="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center font-black text-white shadow-lg shadow-blue-500/20 group-hover:scale-105 transition-transform">
<BookOpen size={20} />
+8 -8
View File
@@ -10,17 +10,17 @@ export default function AuthHeader() {
<div className="flex items-center gap-4">
{user?.role === 'admin' && (
<>
<Link href="/admin/organizations" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2">
<Building2 size={16} /> Org
<Link href="/admin/organizations" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2" title="Organizations">
<Building2 size={16} /> <span className="hidden md:inline">Org</span>
</Link>
<Link href="/admin/audit" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2">
<ShieldAlert size={16} /> Audit
<Link href="/admin/audit" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2" title="Audit Logs">
<ShieldAlert size={16} /> <span className="hidden md:inline">Audit</span>
</Link>
<Link href="/admin/tasks" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2">
<Activity size={16} /> Tasks
<Link href="/admin/tasks" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2" title="Tasks">
<Activity size={16} /> <span className="hidden md:inline">Tasks</span>
</Link>
<Link href="/settings" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2">
<Settings size={16} /> Settings
<Link href="/settings" className="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-white transition-colors flex items-center gap-2" title="Settings">
<Settings size={16} /> <span className="hidden md:inline">Settings</span>
</Link>
</>
)}
+11 -1
View File
@@ -1,4 +1,14 @@
export const API_BASE_URL = process.env.NEXT_PUBLIC_CMS_API_URL || "http://localhost:3001";
const getApiBaseUrl = (defaultPort: string, envVar?: string) => {
if (typeof window !== 'undefined') {
const hostname = window.location.hostname;
// Detect if we are on a custom domain or IP
const protocol = window.location.protocol;
return `${protocol}//${hostname}:${defaultPort}`;
}
return envVar || `http://localhost:${defaultPort}`;
};
export const API_BASE_URL = getApiBaseUrl("3001", process.env.NEXT_PUBLIC_CMS_API_URL);
export const getImageUrl = (path?: string) => {
if (!path) return '';