correção da tela branca
This commit is contained in:
parent
b793f7dca4
commit
9ec05a0599
6 changed files with 2757 additions and 211 deletions
92
App.tsx
92
App.tsx
|
|
@ -1,48 +1,47 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
import React, { useState, useEffect } from 'react';
|
import { Navbar } from "./components/Navbar";
|
||||||
import { Navbar } from './components/Navbar';
|
import { Home } from "./pages/Home";
|
||||||
import { Home } from './pages/Home';
|
import { Dashboard } from "./pages/Dashboard";
|
||||||
import { Dashboard } from './pages/Dashboard';
|
import { Login } from "./pages/Login";
|
||||||
import { Login } from './pages/Login';
|
import { AuthProvider, useAuth } from "./contexts/AuthContext";
|
||||||
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
import { DataProvider } from "./contexts/DataContext";
|
||||||
import { DataProvider } from './contexts/DataContext';
|
import { Construction } from "lucide-react";
|
||||||
import { Construction } from 'lucide-react'; // Placeholder icon
|
|
||||||
|
|
||||||
const AppContent: React.FC = () => {
|
const AppContent: React.FC = () => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [currentPage, setCurrentPage] = useState('home');
|
const [currentPage, setCurrentPage] = useState("home");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && currentPage === 'login') {
|
if (user && currentPage === "login") {
|
||||||
setCurrentPage('dashboard');
|
setCurrentPage("dashboard");
|
||||||
}
|
}
|
||||||
}, [user, currentPage]);
|
}, [user, currentPage]);
|
||||||
|
|
||||||
// Simple Router Logic
|
|
||||||
const renderPage = () => {
|
const renderPage = () => {
|
||||||
if (currentPage === 'home') return <Home onEnter={() => setCurrentPage(user ? 'dashboard' : 'login')} />;
|
if (currentPage === "home")
|
||||||
if (currentPage === 'login') return user ? <Dashboard /> : <Login />;
|
return (
|
||||||
|
<Home onEnter={() => setCurrentPage(user ? "dashboard" : "login")} />
|
||||||
|
);
|
||||||
|
if (currentPage === "login") return user ? <Dashboard /> : <Login />;
|
||||||
|
|
||||||
// Protected Routes Check
|
|
||||||
if (!user) return <Login />;
|
if (!user) return <Login />;
|
||||||
|
|
||||||
switch (currentPage) {
|
switch (currentPage) {
|
||||||
case 'dashboard':
|
case "dashboard":
|
||||||
case 'events':
|
case "events":
|
||||||
return <Dashboard initialView="list" />;
|
return <Dashboard initialView="list" />;
|
||||||
|
|
||||||
case 'request-event':
|
case "request-event":
|
||||||
return <Dashboard initialView="create" />;
|
return <Dashboard initialView="create" />;
|
||||||
|
|
||||||
case 'uploads':
|
case "uploads":
|
||||||
return <Dashboard initialView="uploads" />;
|
return <Dashboard initialView="uploads" />;
|
||||||
|
|
||||||
// Placeholder routes for future implementation
|
case "team":
|
||||||
case 'team':
|
case "finance":
|
||||||
case 'finance':
|
case "settings":
|
||||||
case 'settings':
|
case "albums":
|
||||||
case 'albums':
|
case "calendar":
|
||||||
case 'calendar':
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white pt-32 px-4 text-center fade-in">
|
<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="max-w-md mx-auto bg-gray-50 p-12 rounded-lg border border-gray-100 shadow-sm">
|
||||||
|
|
@ -50,16 +49,20 @@ const AppContent: React.FC = () => {
|
||||||
<Construction size={32} />
|
<Construction size={32} />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl font-serif font-bold mb-3 text-brand-black capitalize">
|
<h2 className="text-2xl font-serif font-bold mb-3 text-brand-black capitalize">
|
||||||
{currentPage === 'team' ? 'Equipe & Fotógrafos' :
|
{currentPage === "team"
|
||||||
currentPage === 'finance' ? 'Financeiro' :
|
? "Equipe & Fotógrafos"
|
||||||
currentPage === 'calendar' ? 'Agenda' :
|
: currentPage === "finance"
|
||||||
currentPage}
|
? "Financeiro"
|
||||||
|
: currentPage === "calendar"
|
||||||
|
? "Agenda"
|
||||||
|
: currentPage}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-500 mb-8 leading-relaxed">
|
<p className="text-gray-500 mb-8 leading-relaxed">
|
||||||
Esta funcionalidade está em desenvolvimento e estará disponível em breve no seu painel.
|
Esta funcionalidade está em desenvolvimento e estará disponível
|
||||||
|
em breve no seu painel.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setCurrentPage('dashboard')}
|
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"
|
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
|
Voltar ao Dashboard
|
||||||
|
|
@ -69,30 +72,29 @@ const AppContent: React.FC = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Fallback
|
|
||||||
return <Dashboard initialView="list" />;
|
return <Dashboard initialView="list" />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<div className="min-h-screen bg-white">
|
||||||
<Navbar
|
<Navbar onNavigate={setCurrentPage} currentPage={currentPage} />
|
||||||
onNavigate={setCurrentPage}
|
<main>{renderPage()}</main>
|
||||||
currentPage={currentPage}
|
|
||||||
/>
|
|
||||||
<main>
|
|
||||||
{renderPage()}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* Footer only on Home */}
|
{currentPage === "home" && (
|
||||||
{currentPage === 'home' && (
|
|
||||||
<footer className="bg-white border-t border-gray-100 py-12">
|
<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">
|
<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>
|
<p>© 2024 PhotumManager. Todos os direitos reservados.</p>
|
||||||
<div className="flex space-x-6 mt-4 md:mt-0">
|
<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">
|
||||||
<a href="#" className="hover:text-brand-black">Termos de Uso</a>
|
Política de Privacidade
|
||||||
<a href="#" className="hover:text-brand-black">Instagram</a>
|
</a>
|
||||||
|
<a href="#" className="hover:text-brand-black">
|
||||||
|
Termos de Uso
|
||||||
|
</a>
|
||||||
|
<a href="#" className="hover:text-brand-black">
|
||||||
|
Instagram
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
90
index.html
90
index.html
|
|
@ -1,11 +1,14 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="pt-BR">
|
<html lang="pt-BR">
|
||||||
<head>
|
|
||||||
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>PhotumManager - Gestão de Eventos</title>
|
<title>PhotumManager - Gestão de Eventos</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<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">
|
<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>
|
<script>
|
||||||
tailwind.config = {
|
tailwind.config = {
|
||||||
theme: {
|
theme: {
|
||||||
|
|
@ -17,7 +20,7 @@
|
||||||
colors: {
|
colors: {
|
||||||
brand: {
|
brand: {
|
||||||
black: '#1a1a1a',
|
black: '#1a1a1a',
|
||||||
gold: '#B9CF33', // Updated from #c5a059 to #B9CF33
|
gold: '#B9CF33',
|
||||||
gray: '#f4f4f4',
|
gray: '#f4f4f4',
|
||||||
darkgray: '#333333'
|
darkgray: '#333333'
|
||||||
}
|
}
|
||||||
|
|
@ -27,33 +30,62 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
/* Smooth scrolling */
|
html {
|
||||||
html { scroll-behavior: smooth; }
|
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>
|
::-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>
|
</head>
|
||||||
<body class="bg-white text-brand-black antialiased selection:bg-brand-gold selection:text-white">
|
|
||||||
|
<body class="bg-white text-brand-black antialiased selection:bg-brand-gold selection:text-white">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
<script type="module" src="/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
10
index.tsx
10
index.tsx
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
|
|
||||||
const rootElement = document.getElementById('root');
|
const rootElement = document.getElementById("root");
|
||||||
if (!rootElement) {
|
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);
|
const root = ReactDOM.createRoot(rootElement);
|
||||||
|
|
|
||||||
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"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.2.0",
|
"@google/genai": "^1.30.0",
|
||||||
"lucide-react": "^0.554.0",
|
"lucide-react": "^0.554.0",
|
||||||
"react-dom": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"@google/genai": "^1.30.0"
|
"react-dom": "^19.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.14.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { GoogleGenAI } from "@google/genai";
|
// Gemini API service temporarily disabled - requires API key configuration
|
||||||
|
// import { GoogleGenAI } from "@google/genai";
|
||||||
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
// const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
||||||
|
|
||||||
export interface GeoResult {
|
export interface GeoResult {
|
||||||
street: string;
|
street: string;
|
||||||
|
|
@ -12,88 +12,12 @@ export interface GeoResult {
|
||||||
mapLink?: string;
|
mapLink?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const searchLocationWithGemini = async (query: string): Promise<GeoResult[]> => {
|
// Temporarily disabled - requires API key setup
|
||||||
if (!query || query.length < 3) return [];
|
export const searchLocationWithGemini = async (
|
||||||
|
query: string
|
||||||
try {
|
): Promise<GeoResult[]> => {
|
||||||
// Attempt to get user location for better context
|
console.warn(
|
||||||
let userLocation: { latitude: number; longitude: number } | undefined;
|
"Gemini location search is disabled. Please configure API key to enable."
|
||||||
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 [];
|
return [];
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue