correção da tela branca
This commit is contained in:
parent
b793f7dca4
commit
9ec05a0599
6 changed files with 2757 additions and 211 deletions
130
App.tsx
130
App.tsx
|
|
@ -1,98 +1,100 @@
|
|||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Navbar } from './components/Navbar';
|
||||
import { Home } from './pages/Home';
|
||||
import { Dashboard } from './pages/Dashboard';
|
||||
import { Login } from './pages/Login';
|
||||
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
||||
import { DataProvider } from './contexts/DataContext';
|
||||
import { Construction } from 'lucide-react'; // Placeholder icon
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Navbar } from "./components/Navbar";
|
||||
import { Home } from "./pages/Home";
|
||||
import { Dashboard } from "./pages/Dashboard";
|
||||
import { Login } from "./pages/Login";
|
||||
import { AuthProvider, useAuth } from "./contexts/AuthContext";
|
||||
import { DataProvider } from "./contexts/DataContext";
|
||||
import { Construction } from "lucide-react";
|
||||
|
||||
const AppContent: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const [currentPage, setCurrentPage] = useState('home');
|
||||
const [currentPage, setCurrentPage] = useState("home");
|
||||
|
||||
useEffect(() => {
|
||||
if (user && currentPage === 'login') {
|
||||
setCurrentPage('dashboard');
|
||||
if (user && currentPage === "login") {
|
||||
setCurrentPage("dashboard");
|
||||
}
|
||||
}, [user, currentPage]);
|
||||
|
||||
// Simple Router Logic
|
||||
const renderPage = () => {
|
||||
if (currentPage === 'home') return <Home onEnter={() => setCurrentPage(user ? 'dashboard' : 'login')} />;
|
||||
if (currentPage === 'login') return user ? <Dashboard /> : <Login />;
|
||||
|
||||
// Protected Routes Check
|
||||
if (currentPage === "home")
|
||||
return (
|
||||
<Home onEnter={() => setCurrentPage(user ? "dashboard" : "login")} />
|
||||
);
|
||||
if (currentPage === "login") return user ? <Dashboard /> : <Login />;
|
||||
|
||||
if (!user) return <Login />;
|
||||
|
||||
switch (currentPage) {
|
||||
case 'dashboard':
|
||||
case 'events':
|
||||
case "dashboard":
|
||||
case "events":
|
||||
return <Dashboard initialView="list" />;
|
||||
|
||||
case 'request-event':
|
||||
|
||||
case "request-event":
|
||||
return <Dashboard initialView="create" />;
|
||||
|
||||
case 'uploads':
|
||||
|
||||
case "uploads":
|
||||
return <Dashboard initialView="uploads" />;
|
||||
|
||||
// Placeholder routes for future implementation
|
||||
case 'team':
|
||||
case 'finance':
|
||||
case 'settings':
|
||||
case 'albums':
|
||||
case 'calendar':
|
||||
case "team":
|
||||
case "finance":
|
||||
case "settings":
|
||||
case "albums":
|
||||
case "calendar":
|
||||
return (
|
||||
<div className="min-h-screen bg-white pt-32 px-4 text-center fade-in">
|
||||
<div className="max-w-md mx-auto bg-gray-50 p-12 rounded-lg border border-gray-100 shadow-sm">
|
||||
<div className="mx-auto w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center mb-6 text-gray-400">
|
||||
<Construction size={32} />
|
||||
</div>
|
||||
<h2 className="text-2xl font-serif font-bold mb-3 text-brand-black capitalize">
|
||||
{currentPage === 'team' ? 'Equipe & Fotógrafos' :
|
||||
currentPage === 'finance' ? 'Financeiro' :
|
||||
currentPage === 'calendar' ? 'Agenda' :
|
||||
currentPage}
|
||||
</h2>
|
||||
<p className="text-gray-500 mb-8 leading-relaxed">
|
||||
Esta funcionalidade está em desenvolvimento e estará disponível em breve no seu painel.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setCurrentPage('dashboard')}
|
||||
className="px-6 py-2 bg-brand-black text-white rounded-sm hover:bg-gray-800 transition-colors font-medium text-sm"
|
||||
>
|
||||
Voltar ao Dashboard
|
||||
</button>
|
||||
<div className="min-h-screen bg-white pt-32 px-4 text-center fade-in">
|
||||
<div className="max-w-md mx-auto bg-gray-50 p-12 rounded-lg border border-gray-100 shadow-sm">
|
||||
<div className="mx-auto w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center mb-6 text-gray-400">
|
||||
<Construction size={32} />
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-2xl font-serif font-bold mb-3 text-brand-black capitalize">
|
||||
{currentPage === "team"
|
||||
? "Equipe & Fotógrafos"
|
||||
: currentPage === "finance"
|
||||
? "Financeiro"
|
||||
: currentPage === "calendar"
|
||||
? "Agenda"
|
||||
: currentPage}
|
||||
</h2>
|
||||
<p className="text-gray-500 mb-8 leading-relaxed">
|
||||
Esta funcionalidade está em desenvolvimento e estará disponível
|
||||
em breve no seu painel.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setCurrentPage("dashboard")}
|
||||
className="px-6 py-2 bg-brand-black text-white rounded-sm hover:bg-gray-800 transition-colors font-medium text-sm"
|
||||
>
|
||||
Voltar ao Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
// Fallback
|
||||
return <Dashboard initialView="list" />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<Navbar
|
||||
onNavigate={setCurrentPage}
|
||||
currentPage={currentPage}
|
||||
/>
|
||||
<main>
|
||||
{renderPage()}
|
||||
</main>
|
||||
|
||||
{/* Footer only on Home */}
|
||||
{currentPage === 'home' && (
|
||||
<Navbar onNavigate={setCurrentPage} currentPage={currentPage} />
|
||||
<main>{renderPage()}</main>
|
||||
|
||||
{currentPage === "home" && (
|
||||
<footer className="bg-white border-t border-gray-100 py-12">
|
||||
<div className="max-w-7xl mx-auto px-4 flex flex-col md:flex-row justify-between items-center text-sm text-gray-500">
|
||||
<p>© 2024 PhotumManager. Todos os direitos reservados.</p>
|
||||
<div className="flex space-x-6 mt-4 md:mt-0">
|
||||
<a href="#" className="hover:text-brand-black">Política de Privacidade</a>
|
||||
<a href="#" className="hover:text-brand-black">Termos de Uso</a>
|
||||
<a href="#" className="hover:text-brand-black">Instagram</a>
|
||||
<a href="#" className="hover:text-brand-black">
|
||||
Política de Privacidade
|
||||
</a>
|
||||
<a href="#" className="hover:text-brand-black">
|
||||
Termos de Uso
|
||||
</a>
|
||||
<a href="#" className="hover:text-brand-black">
|
||||
Instagram
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
|||
134
index.html
134
index.html
|
|
@ -1,59 +1,91 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PhotumManager - Gestão de Eventos</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
serif: ['Playfair Display', 'serif'],
|
||||
},
|
||||
colors: {
|
||||
brand: {
|
||||
black: '#1a1a1a',
|
||||
gold: '#B9CF33', // Updated from #c5a059 to #B9CF33
|
||||
gray: '#f4f4f4',
|
||||
darkgray: '#333333'
|
||||
}
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PhotumManager - Gestão de Eventos</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,600;1,400&display=swap"
|
||||
rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
serif: ['Playfair Display', 'serif'],
|
||||
},
|
||||
colors: {
|
||||
brand: {
|
||||
black: '#1a1a1a',
|
||||
gold: '#B9CF33',
|
||||
gray: '#f4f4f4',
|
||||
darkgray: '#333333'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* Smooth scrolling */
|
||||
html { scroll-behavior: smooth; }
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar { width: 8px; }
|
||||
::-webkit-scrollbar-track { background: #f1f1f1; }
|
||||
::-webkit-scrollbar-thumb { background: #B9CF33; border-radius: 4px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: #a5bd2e; }
|
||||
|
||||
.fade-in { animation: fadeIn 0.5s ease-out forwards; }
|
||||
.slide-up { animation: slideUp 0.6s ease-out forwards; }
|
||||
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
@keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
||||
</style>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"react/": "https://aistudiocdn.com/react@^19.2.0/",
|
||||
"react": "https://aistudiocdn.com/react@^19.2.0",
|
||||
"lucide-react": "https://aistudiocdn.com/lucide-react@^0.554.0",
|
||||
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
|
||||
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.30.0"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #B9CF33;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #a5bd2e;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
.slide-up {
|
||||
animation: slideUp 0.6s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-white text-brand-black antialiased selection:bg-brand-gold selection:text-white">
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
<body class="bg-white text-brand-black antialiased selection:bg-brand-gold selection:text-white">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
12
index.tsx
12
index.tsx
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
const rootElement = document.getElementById("root");
|
||||
if (!rootElement) {
|
||||
throw new Error("Could not find root element to mount to");
|
||||
throw new Error("Could not find root element");
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
|
|
@ -12,4 +12,4 @@ root.render(
|
|||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
);
|
||||
|
|
|
|||
2588
package-lock.json
generated
Normal file
2588
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -9,10 +9,10 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.0",
|
||||
"@google/genai": "^1.30.0",
|
||||
"lucide-react": "^0.554.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"@google/genai": "^1.30.0"
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { GoogleGenAI } from "@google/genai";
|
||||
|
||||
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
||||
// Gemini API service temporarily disabled - requires API key configuration
|
||||
// import { GoogleGenAI } from "@google/genai";
|
||||
// const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
||||
|
||||
export interface GeoResult {
|
||||
street: string;
|
||||
|
|
@ -12,88 +12,12 @@ export interface GeoResult {
|
|||
mapLink?: string;
|
||||
}
|
||||
|
||||
export const searchLocationWithGemini = async (query: string): Promise<GeoResult[]> => {
|
||||
if (!query || query.length < 3) return [];
|
||||
|
||||
try {
|
||||
// Attempt to get user location for better context
|
||||
let userLocation: { latitude: number; longitude: number } | undefined;
|
||||
try {
|
||||
const position = await new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 2000 });
|
||||
});
|
||||
userLocation = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude
|
||||
};
|
||||
} catch (e) {
|
||||
// Ignore geolocation errors, proceed without it
|
||||
}
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-2.5-flash',
|
||||
contents: `Find the specific address details for the location query: "${query}".
|
||||
|
||||
You MUST return the address components in the following format (one per line):
|
||||
Description: [A concise formatted address]
|
||||
Street: [Street Name]
|
||||
Number: [Street Number, if available]
|
||||
City: [City Name]
|
||||
State: [State Code, e.g., SP, RJ]
|
||||
Zip: [Postal Code]
|
||||
|
||||
If a specific component is not found, leave it empty after the colon.`,
|
||||
config: {
|
||||
tools: [{ googleMaps: {} }],
|
||||
toolConfig: userLocation ? {
|
||||
retrievalConfig: {
|
||||
latLng: {
|
||||
latitude: userLocation.latitude,
|
||||
longitude: userLocation.longitude
|
||||
}
|
||||
}
|
||||
} : undefined
|
||||
}
|
||||
});
|
||||
|
||||
const text = response.text || "";
|
||||
|
||||
// Helper parser
|
||||
const parseField = (key: string) => {
|
||||
const regex = new RegExp(`${key}:\\s*(.*)`, 'i');
|
||||
const match = text.match(regex);
|
||||
return match ? match[1].trim() : '';
|
||||
};
|
||||
|
||||
// Extract Google Maps URI from grounding metadata
|
||||
let mapUri = '';
|
||||
const chunks = response.candidates?.[0]?.groundingMetadata?.groundingChunks || [];
|
||||
|
||||
// Look for the Web URI in the chunks (Google Maps grounding often returns this)
|
||||
for (const chunk of chunks) {
|
||||
if (chunk.web?.uri && (chunk.web.uri.includes('google.com/maps') || chunk.web.uri.includes('maps.google.com'))) {
|
||||
mapUri = chunk.web.uri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const result: GeoResult = {
|
||||
street: parseField('Street'),
|
||||
number: parseField('Number'),
|
||||
city: parseField('City'),
|
||||
state: parseField('State'),
|
||||
zip: parseField('Zip'),
|
||||
description: parseField('Description') || text.split('\n')[0], // Fallback to first line
|
||||
mapLink: mapUri
|
||||
};
|
||||
|
||||
// Filter out bad results (empty city/state usually means it failed to identify a place)
|
||||
if (!result.city && !result.street) return [];
|
||||
|
||||
return [result];
|
||||
|
||||
} catch (error) {
|
||||
console.error("Gemini Search Error:", error);
|
||||
return [];
|
||||
}
|
||||
// Temporarily disabled - requires API key setup
|
||||
export const searchLocationWithGemini = async (
|
||||
query: string
|
||||
): Promise<GeoResult[]> => {
|
||||
console.warn(
|
||||
"Gemini location search is disabled. Please configure API key to enable."
|
||||
);
|
||||
return [];
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue