Intranet LocationPicker düzeltmesi
This commit is contained in:
parent
cbd96fd8f2
commit
0554717bc6
4 changed files with 114 additions and 24 deletions
|
|
@ -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
19
ui/.env
|
|
@ -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 Console’da:
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
@ -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')}
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue