Intranet LocationPicker düzeltmesi

This commit is contained in:
Sedat ÖZTÜRK 2026-05-08 17:17:11 +03:00
parent cbd96fd8f2
commit 0554717bc6
4 changed files with 114 additions and 24 deletions

View file

@ -12306,6 +12306,36 @@
"en": "Google Maps could not be loaded. Please check your internet connection.", "en": "Google Maps could not be loaded. Please check your internet connection.",
"tr": "Google Maps yüklenemedi. Lütfen internet bağlantınızı kontrol edin." "tr": "Google Maps yüklenemedi. Lütfen internet bağlantınızı kontrol edin."
}, },
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.NoResults",
"en": "No results found",
"tr": "Sonuç bulunamadı"
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.OverQueryLimit",
"en": "Google Places query limit exceeded. Please try again later.",
"tr": "Google Places sorgu limiti aşıldı. Lütfen daha sonra tekrar deneyiniz."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.RequestDenied",
"en": "Google Places request denied. Please check your API key, billing, or permissions settings.",
"tr": "Google Places isteği reddedildi. API key, billing veya yetki ayarlarını kontrol ediniz."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.InvalidRequest",
"en": "Invalid location search request. Please check your input and try again.",
"tr": "Geçersiz konum arama isteği."
},
{
"resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.UnknownError",
"en": "Google Places returned a temporary error. Please try again.",
"tr": "Google Places geçici bir hata döndürdü. Lütfen tekrar deneyiniz."
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Platform.Intranet.SocialWall.LocationPicker.SearchFailed", "key": "App.Platform.Intranet.SocialWall.LocationPicker.SearchFailed",

19
ui/.env
View file

@ -2,4 +2,21 @@ VITE_API_URL='https://localhost:44344'
VITE_CDN_URL='http://localhost:4005' VITE_CDN_URL='http://localhost:4005'
VITE_REACT_APP_VERSION=$npm_package_version VITE_REACT_APP_VERSION=$npm_package_version
VITE_AI_URL='https://ai.sozsoft.com/webhook/' VITE_AI_URL='https://ai.sozsoft.com/webhook/'
VITE_GOOGLE_MAPS_API_KEY='AIzaSyAefS2rvF-xwq7OHpZ27UYxXPbMo6OwACc' VITE_GOOGLE_MAPS_API_KEY='AIzaSyAefS2rvF-xwq7OHpZ27UYxXPbMo6OwACc'
# Google Cloud Consoleda:
# APIs & Services > Enabled APIs & services bölümüne gir.
# Şunların aktif olduğundan emin ol:
# Maps JavaScript API
# Places API
# Eğer ileride koordinat/adres çözümleme yapıyorsan ayrıca:
# Geocoding API
# APIs & Services > Credentials > API Key içine gir.
# API restrictions bölümünde:
# Eğer Restrict key seçiliyse, izinli API listesine şunları ekle:
# Maps JavaScript API
# Places API

View file

@ -1001,7 +1001,7 @@ const FileManager = () => {
size="sm" size="sm"
icon={<FaTh />} icon={<FaTh />}
className={classNames( className={classNames(
'rounded-r-none border-r px-2 sm:px-3', 'rounded-r-none border-r ',
viewMode === 'grid' && 'bg-blue-50 dark:bg-blue-900/20 text-blue-600', viewMode === 'grid' && 'bg-blue-50 dark:bg-blue-900/20 text-blue-600',
)} )}
onClick={() => setViewMode('grid')} onClick={() => setViewMode('grid')}
@ -1012,7 +1012,7 @@ const FileManager = () => {
size="sm" size="sm"
icon={<FaList />} icon={<FaList />}
className={classNames( className={classNames(
'rounded-l-none px-2 sm:px-3', 'rounded-l-none ',
viewMode === 'list' && 'bg-blue-50 dark:bg-blue-900/20 text-blue-600', viewMode === 'list' && 'bg-blue-50 dark:bg-blue-900/20 text-blue-600',
)} )}
onClick={() => setViewMode('list')} onClick={() => setViewMode('list')}

View file

@ -29,7 +29,7 @@ declare global {
} }
const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) => { const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) => {
const { translate } = useLocalization(); const { translate } = useLocalization()
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [locations, setLocations] = useState<LocationData[]>([]) const [locations, setLocations] = useState<LocationData[]>([])
const [selectedLocation, setSelectedLocation] = useState<LocationData | null>(null) const [selectedLocation, setSelectedLocation] = useState<LocationData | null>(null)
@ -82,7 +82,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
script.src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&libraries=places&language=tr` script.src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&libraries=places&language=tr`
script.async = true script.async = true
script.defer = true script.defer = true
script.onload = () => { script.onload = () => {
if (window.google && window.google.maps && window.google.maps.places) { if (window.google && window.google.maps && window.google.maps.places) {
setIsGoogleLoaded(true) setIsGoogleLoaded(true)
@ -108,6 +108,28 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
searchInputRef.current?.focus() searchInputRef.current?.focus()
}, []) }, [])
const getGooglePlacesErrorMessage = (status: any) => {
switch (status) {
case window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.NoResults')
case window.google.maps.places.PlacesServiceStatus.OVER_QUERY_LIMIT:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.OverQueryLimit')
case window.google.maps.places.PlacesServiceStatus.REQUEST_DENIED:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.RequestDenied')
case window.google.maps.places.PlacesServiceStatus.INVALID_REQUEST:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.InvalidRequest')
case window.google.maps.places.PlacesServiceStatus.UNKNOWN_ERROR:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.UnknownError')
default:
return translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchFailed')
}
}
// Google Places Autocomplete ile konum arama // Google Places Autocomplete ile konum arama
useEffect(() => { useEffect(() => {
if (debounceTimerRef.current) { if (debounceTimerRef.current) {
@ -134,28 +156,32 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
{ {
input: searchQuery, input: searchQuery,
componentRestrictions: { country: 'tr' }, componentRestrictions: { country: 'tr' },
language: 'tr' language: 'tr',
}, },
async (predictions: any, status: any) => { async (predictions: any, status: any) => {
if (status === window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS) { if (status === window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
setLocations([]) setLocations([])
setError(getGooglePlacesErrorMessage(status))
setIsLoading(false) setIsLoading(false)
return return
} }
if (status !== window.google.maps.places.PlacesServiceStatus.OK) { if (status !== window.google.maps.places.PlacesServiceStatus.OK) {
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchFailed')) setLocations([])
setError(getGooglePlacesErrorMessage(status))
setIsLoading(false) setIsLoading(false)
return return
} }
if (!predictions || predictions.length === 0) { if (!predictions || predictions.length === 0) {
setLocations([]) setLocations([])
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.NoResults'))
setIsLoading(false) setIsLoading(false)
return return
} }
// Her bir prediction için detaylı bilgi al setError(null)
const detailedLocations: LocationData[] = [] const detailedLocations: LocationData[] = []
let completed = 0 let completed = 0
@ -163,7 +189,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
placesServiceRef.current.getDetails( placesServiceRef.current.getDetails(
{ {
placeId: prediction.place_id, placeId: prediction.place_id,
fields: ['name', 'formatted_address', 'geometry', 'place_id'] fields: ['name', 'formatted_address', 'geometry', 'place_id'],
}, },
(place: any, placeStatus: any) => { (place: any, placeStatus: any) => {
completed++ completed++
@ -175,19 +201,24 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
address: place.formatted_address, address: place.formatted_address,
lat: place.geometry.location.lat(), lat: place.geometry.location.lat(),
lng: place.geometry.location.lng(), lng: place.geometry.location.lng(),
placeId: place.place_id placeId: place.place_id,
}) })
} }
// Tüm istekler tamamlandıysa state'i güncelle
if (completed === predictions.length) { if (completed === predictions.length) {
if (detailedLocations.length === 0) {
setError('Konum detayları alınamadı. API yetkilerini kontrol ediniz.')
} else {
setError(null)
}
setLocations(detailedLocations) setLocations(detailedLocations)
setIsLoading(false) setIsLoading(false)
} }
} },
) )
}) })
} },
) )
} catch (err) { } catch (err) {
console.error('Location search error:', err) console.error('Location search error:', err)
@ -224,7 +255,9 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
> >
{/* Header */} {/* Header */}
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700"> <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.AddLocation')}</h2> <h2 className="text-xl font-bold text-gray-900 dark:text-white">
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.AddLocation')}
</h2>
<button <button
onClick={onClose} onClick={onClose}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors" className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
@ -242,7 +275,9 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
type="text" type="text"
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
placeholder={translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchPlaceholder')} placeholder={translate(
'::App.Platform.Intranet.SocialWall.LocationPicker.SearchPlaceholder',
)}
disabled={!isGoogleLoaded} disabled={!isGoogleLoaded}
className="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-600 disabled:cursor-not-allowed" className="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-600 disabled:cursor-not-allowed"
/> />
@ -259,12 +294,16 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
{!isGoogleLoaded ? ( {!isGoogleLoaded ? (
<div className="text-center py-12"> <div className="text-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.LoadingGoogleMaps')}</p> <p className="text-gray-500 dark:text-gray-400">
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.LoadingGoogleMaps')}
</p>
</div> </div>
) : isLoading ? ( ) : isLoading ? (
<div className="text-center py-12"> <div className="text-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchingLocations')}</p> <p className="text-gray-500 dark:text-gray-400">
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchingLocations')}
</p>
</div> </div>
) : error ? ( ) : error ? (
<div className="text-center py-12"> <div className="text-center py-12">
@ -298,7 +337,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
'w-full text-left p-3 rounded-lg transition-all hover:bg-gray-50 dark:hover:bg-gray-700', 'w-full text-left p-3 rounded-lg transition-all hover:bg-gray-50 dark:hover:bg-gray-700',
selectedLocation?.id === location.id selectedLocation?.id === location.id
? 'bg-blue-50 dark:bg-blue-900/30 border-2 border-blue-500' ? 'bg-blue-50 dark:bg-blue-900/30 border-2 border-blue-500'
: 'border-2 border-transparent' : 'border-2 border-transparent',
)} )}
> >
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
@ -306,9 +345,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
<FaMapMarkerAlt <FaMapMarkerAlt
className={classNames( className={classNames(
'w-5 h-5', 'w-5 h-5',
selectedLocation?.id === location.id selectedLocation?.id === location.id ? 'text-blue-600' : 'text-gray-400',
? 'text-blue-600'
: 'text-gray-400'
)} )}
/> />
</div> </div>
@ -318,7 +355,7 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
'font-semibold mb-1', 'font-semibold mb-1',
selectedLocation?.id === location.id selectedLocation?.id === location.id
? 'text-blue-600 dark:text-blue-400' ? 'text-blue-600 dark:text-blue-400'
: 'text-gray-900 dark:text-gray-100' : 'text-gray-900 dark:text-gray-100',
)} )}
> >
{location.name} {location.name}
@ -333,7 +370,11 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
{selectedLocation?.id === location.id && ( {selectedLocation?.id === location.id && (
<div className="mt-1"> <div className="mt-1">
<div className="w-5 h-5 bg-blue-600 rounded-full flex items-center justify-center"> <div className="w-5 h-5 bg-blue-600 rounded-full flex items-center justify-center">
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20"> <svg
className="w-3 h-3 text-white"
fill="currentColor"
viewBox="0 0 20 20"
>
<path <path
fillRule="evenodd" fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
@ -361,7 +402,9 @@ const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) =>
</span> </span>
</span> </span>
) : ( ) : (
<span>{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SelectLocation')}</span> <span>
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SelectLocation')}
</span>
)} )}
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">