Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 4 additions & 52 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import React, { useState, useMemo, useEffect } from 'react';
import { Search, Sparkles, MapPin, Sun, Moon, Globe, Heart, Database, PhoneForwarded, Key, ExternalLink, ArrowRight, User as UserIcon, Shield, Lock } from 'lucide-react';
import { Search, Sparkles, MapPin, Sun, Moon, Globe, Heart, Database, PhoneForwarded, User as UserIcon, Shield } from 'lucide-react';
import { MapView } from './components/MapView';
import { TableView } from './components/TableView';
import { GeminiChat } from './components/GeminiChat';
Expand Down Expand Up @@ -31,7 +31,6 @@ const App: React.FC = () => {
const [isPrivacyOpen, setIsPrivacyOpen] = useState(false);
const [isTermsOpen, setIsTermsOpen] = useState(false);
const [referralOrg, setReferralOrg] = useState<Organization | null>(null);
const [isKeySelected, setIsKeySelected] = useState<boolean | null>(null);
const [mapKey, setMapKey] = useState(0);

// RBAC State
Expand All @@ -42,29 +41,13 @@ const App: React.FC = () => {
});
const [isProfileOpen, setIsProfileOpen] = useState(false);

useEffect(() => {
const checkKey = async () => {
if (window.aistudio && typeof window.aistudio.hasSelectedApiKey === 'function') {
const selected = await window.aistudio.hasSelectedApiKey();
setIsKeySelected(selected);
} else {
setIsKeySelected(true);
}
};
checkKey();
}, []);

useEffect(() => {
document.documentElement.classList.toggle('dark', isDarkMode);
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');
}, [isDarkMode]);

const handleSelectKey = async () => {
if (window.aistudio && typeof window.aistudio.openSelectKey === 'function') {
await window.aistudio.openSelectKey();
setIsKeySelected(true);
setMapKey(prev => prev + 1);
}
const handleResetMap = () => {
setMapKey(prev => prev + 1);
};

const changeRole = (role: UserRole) => {
Expand All @@ -86,37 +69,6 @@ const App: React.FC = () => {
});
}, [organizations, activeRegion, searchTerm]);

if (isKeySelected === false) {
return (
<div className="h-screen w-screen flex items-center justify-center bg-slate-50 dark:bg-slate-950 p-6 font-sans">
<div className="max-w-md w-full bg-white dark:bg-slate-900 rounded-[2.5rem] shadow-2xl p-10 text-center border border-slate-100 dark:border-slate-800 animate-in-dialog">
<div className="w-20 h-20 bg-teal-100 dark:bg-teal-900/30 rounded-3xl flex items-center justify-center text-teal-600 dark:text-teal-400 mx-auto mb-8 shadow-inner">
<Key size={40} />
</div>
<h1 className="text-2xl font-black text-slate-800 dark:text-white mb-4 uppercase tracking-tight">Потрібна активація</h1>
<p className="text-slate-500 dark:text-slate-400 text-sm mb-8 leading-relaxed">
Для роботи мапи та інтелектуального пошуку необхідно підключити ваш Google Cloud Project з активованим Google Maps API та білінгом.
</p>
<div className="flex flex-col gap-3">
<button
onClick={handleSelectKey}
className="w-full py-4 bg-teal-600 text-white rounded-2xl font-black uppercase tracking-widest text-xs hover:bg-teal-700 transition-all flex items-center justify-center gap-3 shadow-lg shadow-teal-500/20 active:scale-95"
>
Підключити проект <ArrowRight size={16} />
</button>
<a
href="https://ai.google.dev/gemini-api/docs/billing"
target="_blank"
className="text-[10px] font-bold text-slate-400 uppercase tracking-widest hover:text-teal-600 transition flex items-center justify-center gap-2"
>
Документація по білінгу <ExternalLink size={12} />
</a>
</div>
</div>
</div>
);
}

return (
<div className="h-screen flex flex-col bg-slate-50 dark:bg-slate-950 overflow-hidden font-sans transition-colors duration-300">
{showIntro && (
Expand Down Expand Up @@ -242,7 +194,7 @@ const App: React.FC = () => {
center={REGION_CONFIG[activeRegion].center as [number, number]}
zoom={REGION_CONFIG[activeRegion].zoom}
isDarkMode={isDarkMode}
onResetKey={handleSelectKey}
onResetKey={handleResetMap}
/>
</div>

Expand Down
6 changes: 3 additions & 3 deletions components/geminiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const analyzeData = async (query: string, organizations: Organization[],

const isMapQuery = lowerQuery.includes('де') || lowerQuery.includes('поруч') || lowerQuery.includes('маршрут') || lowerQuery.includes('як дістатися') || lowerQuery.includes('адреса') || lowerQuery.includes('карта');

const modelName = isMapQuery ? 'gemini-2.5-flash' : (useThinking ? 'gemini-3-pro-preview' : 'gemini-3-flash-preview');
const modelName = isMapQuery ? 'gemini-2.5-flash' : (useThinking ? 'gemini-2.5-pro' : 'gemini-2.5-flash');

const config: any = {
temperature: 0.7,
Expand All @@ -70,7 +70,7 @@ export const analyzeData = async (query: string, organizations: Organization[],
}
} else {
config.tools = [{ googleSearch: {} }];
if (useThinking && modelName === 'gemini-3-pro-preview') {
if (useThinking && modelName === 'gemini-2.5-pro') {
config.thinkingConfig = { thinkingBudget: 32768 };
}
}
Expand Down Expand Up @@ -101,7 +101,7 @@ export const analyzeData = async (query: string, organizations: Organization[],
export const getIntelligentSummary = async (organizations: Organization[]): Promise<string> => {
const ai = new GoogleGenAI({ apiKey: import.meta.env.VITE_GEMINI_API_KEY });
const response = await ai.models.generateContent({
model: 'gemini-3-flash-preview',
model: 'gemini-2.5-flash',
contents: `Надай короткий огляд (до 3 речень) стану допомоги в Україні на основі цих ${organizations.length} організацій. Пиши як пані Думка.`,
config: { systemInstruction: PANI_DUMKA_PROMPT }
});
Expand Down
3 changes: 3 additions & 0 deletions index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
21 changes: 0 additions & 21 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<title>Інклюзивна мапа соціальних послуг України | ШІ Пані Думка</title>
<meta name="description" content="Знайдіть допомогу поруч: 5000+ перевірених організацій та інтелектуальний пошук." />

<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
Expand Down Expand Up @@ -47,26 +46,6 @@
.gm-style-iw-d { overflow: hidden !important; }
.gm-ui-hover-svc { display: none !important; }
</style>

<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@19.0.0",
"react-dom": "https://esm.sh/react-dom@19.0.0",
"react-dom/client": "https://esm.sh/react-dom@19.0.0/client",
"lucide-react": "https://esm.sh/lucide-react@0.460.0?external=react",
"@google/genai": "https://esm.sh/@google/genai@1.35.0",
"@react-google-maps/api": "https://esm.sh/@react-google-maps/api@2.20.8?external=react,react-dom",
"firebase/app": "https://esm.sh/firebase@11.0.1/app",
"firebase/app-check": "https://esm.sh/firebase@11.0.1/app-check",
"firebase/": "https://esm.sh/firebase@^12.8.0/",
"@vitejs/plugin-react": "https://esm.sh/@vitejs/plugin-react@^5.1.2",
"react-dom/": "https://esm.sh/react-dom@^19.2.3/",
"vite": "https://esm.sh/vite@^7.3.1",
"react/": "https://esm.sh/react@^19.2.3/"
}
}
</script>
</head>

<body class="bg-slate-50 text-slate-900 antialiased overflow-hidden">
Expand Down
1 change: 1 addition & 0 deletions index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const rootElement = document.getElementById('root');
Expand Down
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
13 changes: 13 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./index.html',
'./*.tsx',
'./components/**/*.tsx',
],
darkMode: 'class',
theme: {
extend: {},
},
plugins: [],
};
Loading