mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Point at the Google key only where its absence is the reason
This commit is contained in:
@@ -402,3 +402,41 @@ describe('PlaceDetailsColumn — hours and rating', () => {
|
||||
expect(screen.queryByText('places.details.nothing')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
/**
|
||||
* FE-PDC-027..029 — the nudge towards a Google key.
|
||||
*
|
||||
* Deliberately narrow: it appears only when the free sources came up empty AND
|
||||
* no key is configured. On an instance with a key there is nothing to suggest,
|
||||
* and on a place the free sources described it would read as an advert.
|
||||
*/
|
||||
describe('PlaceDetailsColumn — no Google key', () => {
|
||||
const empty = () => placeEnrichment.mockResolvedValue({ photos: [], facts: [], description: null })
|
||||
|
||||
it('FE-PDC-027: suggests a key when nothing was found and none is set', async () => {
|
||||
empty()
|
||||
renderColumn({ hasMapsKey: false })
|
||||
|
||||
expect(await screen.findByText('places.details.noKeyTitle')).toBeInTheDocument()
|
||||
expect(screen.getByText('places.details.noKeyHint')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-PDC-028: stays quiet when a key is already configured', async () => {
|
||||
empty()
|
||||
renderColumn({ hasMapsKey: true })
|
||||
|
||||
await screen.findByText('places.details.nothing')
|
||||
expect(screen.queryByText('places.details.noKeyTitle')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-PDC-029: stays quiet when the free sources did find something', async () => {
|
||||
placeEnrichment.mockResolvedValue({
|
||||
photos: [],
|
||||
facts: [],
|
||||
description: { text: 'Ein Museum.', source: 'wikipedia', sourceUrl: null, license: 'CC BY-SA 4.0' },
|
||||
})
|
||||
renderColumn({ hasMapsKey: false })
|
||||
|
||||
await screen.findByText('Ein Museum.')
|
||||
expect(screen.queryByText('places.details.noKeyTitle')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
Loader2,
|
||||
ScrollText,
|
||||
ShoppingBag,
|
||||
Sparkles,
|
||||
Sprout,
|
||||
Star,
|
||||
Sun,
|
||||
@@ -48,6 +49,8 @@ interface PlaceDetailsColumnProps {
|
||||
timeFormat?: string
|
||||
/** For grouping the rating count's digits. */
|
||||
locale?: string
|
||||
/** False on an instance with no Google key, which is most of them. */
|
||||
hasMapsKey?: boolean
|
||||
t: TranslationFn
|
||||
}
|
||||
|
||||
@@ -113,6 +116,7 @@ export default function PlaceDetailsColumn({
|
||||
language,
|
||||
timeFormat = '24h',
|
||||
locale = 'en-US',
|
||||
hasMapsKey = false,
|
||||
t,
|
||||
}: PlaceDetailsColumnProps): React.ReactElement {
|
||||
const [data, setData] = useState<MapsPlaceEnrichmentResult | null>(null)
|
||||
@@ -213,9 +217,15 @@ export default function PlaceDetailsColumn({
|
||||
)}
|
||||
|
||||
{isEmpty && (
|
||||
<div className="flex items-center gap-2 text-caption text-content-muted">
|
||||
<ImageOff className="w-4 h-4 shrink-0" />
|
||||
{t('places.details.nothing')}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-caption text-content-muted">
|
||||
<ImageOff className="w-4 h-4 shrink-0" />
|
||||
{t('places.details.nothing')}
|
||||
</div>
|
||||
{/* Only when both are true. With a key configured there is nothing
|
||||
to suggest, and on a place the free sources DID describe the
|
||||
suggestion would be an advert. */}
|
||||
{!hasMapsKey && <NoKeyHint t={t} />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -380,6 +390,30 @@ function PhotoCredit({ photo }: { photo: PlacePhotoCandidate }): React.ReactElem
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shown when the free sources found nothing and no Google key is configured.
|
||||
*
|
||||
* Both halves matter. With a key there is nothing to suggest, and on a place
|
||||
* the free sources did describe the same card would just be an advert — which
|
||||
* is why it lives inside the empty state rather than at the foot of the column.
|
||||
*
|
||||
* It also names who to ask: the key is an instance-wide setting, so on most
|
||||
* installs the person reading this cannot act on it themselves.
|
||||
*/
|
||||
function NoKeyHint({ t }: { t: TranslationFn }): React.ReactElement {
|
||||
return (
|
||||
<div className="rounded-xl border border-accent/25 bg-accent-subtle p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-accent text-accent-text">
|
||||
<Sparkles className="h-3 w-3" />
|
||||
</span>
|
||||
<p className="text-caption font-semibold text-content">{t('places.details.noKeyTitle')}</p>
|
||||
</div>
|
||||
<p className="mt-2 text-caption leading-relaxed text-content-secondary">{t('places.details.noKeyHint')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The star rating, as stars.
|
||||
*
|
||||
|
||||
@@ -638,6 +638,7 @@ export default function PlaceFormModal(props: PlaceFormModalProps) {
|
||||
language={language}
|
||||
timeFormat={S.timeFormat}
|
||||
locale={S.locale}
|
||||
hasMapsKey={S.hasMapsKey}
|
||||
t={t}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'أفرغ حقل الوصف أولًا',
|
||||
'places.details.facts': 'معلومات مفيدة',
|
||||
'places.details.nothing': 'لم يتم العثور على شيء لهذا المكان.',
|
||||
'places.details.noKeyTitle': 'تتوفر تفاصيل أكثر',
|
||||
'places.details.noKeyHint': 'بدون مفتاح Google API يستخدم هذا التثبيت المصادر الحرة فقط. اطلب من مسؤول TREK إضافة مفتاح للحصول على التقييمات وساعات العمل والصور.',
|
||||
'places.details.fact.rating': 'التقييم',
|
||||
'places.details.fact.cuisine': 'المطبخ',
|
||||
'places.details.fact.openingHours': 'ساعات العمل',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Esvazie primeiro o campo de descrição',
|
||||
'places.details.facts': 'Bom saber',
|
||||
'places.details.nothing': 'Nada encontrado para este lugar.',
|
||||
'places.details.noKeyTitle': 'Mais detalhes disponíveis',
|
||||
'places.details.noKeyHint': 'Sem uma chave de API do Google, esta instância usa apenas fontes livres. Peça ao administrador do TREK para adicionar uma para avaliações, horários e fotos.',
|
||||
'places.details.fact.rating': 'Avaliação',
|
||||
'places.details.fact.cuisine': 'Cozinha',
|
||||
'places.details.fact.openingHours': 'Horário',
|
||||
|
||||
@@ -95,6 +95,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Buida primer el camp de descripció',
|
||||
'places.details.facts': 'Bo saber',
|
||||
'places.details.nothing': "No s'ha trobat res per a aquest lloc.",
|
||||
'places.details.noKeyTitle': 'Hi ha més detalls disponibles',
|
||||
'places.details.noKeyHint': 'Sense una clau API de Google, aquesta instància només fa servir fonts lliures. Demana a l\'administració de TREK que n\'afegeixi una per a valoracions, horaris i fotos.',
|
||||
'places.details.fact.rating': 'Valoració',
|
||||
'places.details.fact.cuisine': 'Cuina',
|
||||
'places.details.fact.openingHours': 'Horari',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Nejdřív vyprázdni pole popisu',
|
||||
'places.details.facts': 'Dobré vědět',
|
||||
'places.details.nothing': 'Pro toto místo se nic nenašlo.',
|
||||
'places.details.noKeyTitle': 'K dispozici je více podrobností',
|
||||
'places.details.noKeyHint': 'Bez klíče Google API používá tato instance jen volné zdroje. Požádejte správce TREK o jeho přidání pro hodnocení, otevírací dobu a fotografie.',
|
||||
'places.details.fact.rating': 'Hodnocení',
|
||||
'places.details.fact.cuisine': 'Kuchyně',
|
||||
'places.details.fact.openingHours': 'Otevírací doba',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Erst das Beschreibungsfeld leeren',
|
||||
'places.details.facts': 'Gut zu wissen',
|
||||
'places.details.nothing': 'Zu diesem Ort wurde nichts gefunden.',
|
||||
'places.details.noKeyTitle': 'Mehr Details möglich',
|
||||
'places.details.noKeyHint': 'Ohne Google-API-Key nutzt diese Instanz nur freie Quellen. Wende dich an deine TREK-Administration, um Bewertungen, Öffnungszeiten und Fotos freizuschalten.',
|
||||
'places.details.fact.rating': 'Bewertung',
|
||||
'places.details.fact.cuisine': 'Küche',
|
||||
'places.details.fact.openingHours': 'Öffnungszeiten',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Clear the description field first',
|
||||
'places.details.facts': 'Good to know',
|
||||
'places.details.nothing': 'Nothing found for this place.',
|
||||
'places.details.noKeyTitle': 'More detail is available',
|
||||
'places.details.noKeyHint': 'Without a Google API key this instance can only use free sources. Ask your TREK administrator to add one for ratings, opening hours and photos.',
|
||||
'places.details.fact.rating': 'Rating',
|
||||
'places.details.fact.cuisine': 'Cuisine',
|
||||
'places.details.fact.openingHours': 'Opening hours',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Vacía primero el campo de descripción',
|
||||
'places.details.facts': 'Bueno saber',
|
||||
'places.details.nothing': 'No se encontró nada para este lugar.',
|
||||
'places.details.noKeyTitle': 'Hay más detalles disponibles',
|
||||
'places.details.noKeyHint': 'Sin una clave de API de Google, esta instancia solo usa fuentes libres. Pide a tu administrador de TREK que añada una para valoraciones, horarios y fotos.',
|
||||
'places.details.fact.rating': 'Valoración',
|
||||
'places.details.fact.cuisine': 'Cocina',
|
||||
'places.details.fact.openingHours': 'Horario',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': "Videz d'abord le champ description",
|
||||
'places.details.facts': 'Bon à savoir',
|
||||
'places.details.nothing': 'Rien trouvé pour ce lieu.',
|
||||
'places.details.noKeyTitle': 'Plus de détails disponibles',
|
||||
'places.details.noKeyHint': 'Sans clé API Google, cette instance n\'utilise que des sources libres. Demandez à votre administrateur TREK d\'en ajouter une pour les avis, les horaires et les photos.',
|
||||
'places.details.fact.rating': 'Note',
|
||||
'places.details.fact.cuisine': 'Cuisine',
|
||||
'places.details.fact.openingHours': 'Horaires',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Άδειασε πρώτα το πεδίο περιγραφής',
|
||||
'places.details.facts': 'Καλό να ξέρεις',
|
||||
'places.details.nothing': 'Δεν βρέθηκε τίποτα για αυτήν την τοποθεσία.',
|
||||
'places.details.noKeyTitle': 'Διατίθενται περισσότερες λεπτομέρειες',
|
||||
'places.details.noKeyHint': 'Χωρίς κλειδί Google API, αυτή η εγκατάσταση χρησιμοποιεί μόνο ελεύθερες πηγές. Ζητήστε από τον διαχειριστή TREK να προσθέσει ένα για βαθμολογίες, ώρες λειτουργίας και φωτογραφίες.',
|
||||
'places.details.fact.rating': 'Βαθμολογία',
|
||||
'places.details.fact.cuisine': 'Κουζίνα',
|
||||
'places.details.fact.openingHours': 'Ωράριο',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Előbb ürítsd ki a leírás mezőt',
|
||||
'places.details.facts': 'Jó tudni',
|
||||
'places.details.nothing': 'Ehhez a helyhez nem található semmi.',
|
||||
'places.details.noKeyTitle': 'További részletek érhetők el',
|
||||
'places.details.noKeyHint': 'Google API-kulcs nélkül ez a példány csak szabad forrásokat használ. Kérd a TREK rendszergazdáját, hogy adjon hozzá egyet az értékelésekhez, nyitvatartáshoz és fotókhoz.',
|
||||
'places.details.fact.rating': 'Értékelés',
|
||||
'places.details.fact.cuisine': 'Konyha',
|
||||
'places.details.fact.openingHours': 'Nyitvatartás',
|
||||
|
||||
@@ -96,6 +96,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Kosongkan dulu kolom deskripsi',
|
||||
'places.details.facts': 'Perlu diketahui',
|
||||
'places.details.nothing': 'Tidak ada yang ditemukan untuk tempat ini.',
|
||||
'places.details.noKeyTitle': 'Tersedia detail lebih lanjut',
|
||||
'places.details.noKeyHint': 'Tanpa kunci API Google, instans ini hanya memakai sumber bebas. Minta administrator TREK menambahkannya untuk rating, jam buka, dan foto.',
|
||||
'places.details.fact.rating': 'Peringkat',
|
||||
'places.details.fact.cuisine': 'Masakan',
|
||||
'places.details.fact.openingHours': 'Jam buka',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Svuota prima il campo descrizione',
|
||||
'places.details.facts': 'Da sapere',
|
||||
'places.details.nothing': 'Nessun risultato per questo luogo.',
|
||||
'places.details.noKeyTitle': 'Sono disponibili più dettagli',
|
||||
'places.details.noKeyHint': 'Senza una chiave API di Google questa istanza usa solo fonti libere. Chiedi all\'amministratore di TREK di aggiungerne una per valutazioni, orari e foto.',
|
||||
'places.details.fact.rating': 'Valutazione',
|
||||
'places.details.fact.cuisine': 'Cucina',
|
||||
'places.details.fact.openingHours': 'Orari',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': '先に説明欄を空にしてください',
|
||||
'places.details.facts': '知っておくと便利',
|
||||
'places.details.nothing': 'この場所の情報は見つかりませんでした。',
|
||||
'places.details.noKeyTitle': 'さらに詳しい情報を表示できます',
|
||||
'places.details.noKeyHint': 'Google API キーがない場合、このインスタンスは自由に使えるソースのみを利用します。評価・営業時間・写真のために、TREK の管理者にキーの追加を依頼してください。',
|
||||
'places.details.fact.rating': '評価',
|
||||
'places.details.fact.cuisine': '料理',
|
||||
'places.details.fact.openingHours': '営業時間',
|
||||
|
||||
@@ -96,6 +96,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': '설명란을 먼저 비워 주세요',
|
||||
'places.details.facts': '알아두면 좋아요',
|
||||
'places.details.nothing': '이 장소에 대한 정보를 찾지 못했습니다.',
|
||||
'places.details.noKeyTitle': '더 자세한 정보를 볼 수 있습니다',
|
||||
'places.details.noKeyHint': 'Google API 키가 없으면 이 인스턴스는 무료 소스만 사용합니다. 평점, 영업시간, 사진을 보려면 TREK 관리자에게 키 추가를 요청하세요.',
|
||||
'places.details.fact.rating': '평점',
|
||||
'places.details.fact.cuisine': '요리',
|
||||
'places.details.fact.openingHours': '영업시간',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Maak eerst het beschrijvingsveld leeg',
|
||||
'places.details.facts': 'Goed om te weten',
|
||||
'places.details.nothing': 'Niets gevonden voor deze plaats.',
|
||||
'places.details.noKeyTitle': 'Meer details beschikbaar',
|
||||
'places.details.noKeyHint': 'Zonder Google API-sleutel gebruikt deze installatie alleen vrije bronnen. Vraag je TREK-beheerder er een toe te voegen voor beoordelingen, openingstijden en foto\'s.',
|
||||
'places.details.fact.rating': 'Beoordeling',
|
||||
'places.details.fact.cuisine': 'Keuken',
|
||||
'places.details.fact.openingHours': 'Openingstijden',
|
||||
|
||||
@@ -88,6 +88,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Najpierw wyczyść pole opisu',
|
||||
'places.details.facts': 'Warto wiedzieć',
|
||||
'places.details.nothing': 'Nic nie znaleziono dla tego miejsca.',
|
||||
'places.details.noKeyTitle': 'Dostępnych jest więcej szczegółów',
|
||||
'places.details.noKeyHint': 'Bez klucza API Google ta instancja korzysta tylko ze źródeł otwartych. Poproś administratora TREK o dodanie klucza, aby uzyskać oceny, godziny otwarcia i zdjęcia.',
|
||||
'places.details.fact.rating': 'Ocena',
|
||||
'places.details.fact.cuisine': 'Kuchnia',
|
||||
'places.details.fact.openingHours': 'Godziny otwarcia',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Сначала очистите поле описания',
|
||||
'places.details.facts': 'Полезно знать',
|
||||
'places.details.nothing': 'Для этого места ничего не найдено.',
|
||||
'places.details.noKeyTitle': 'Доступно больше подробностей',
|
||||
'places.details.noKeyHint': 'Без ключа Google API этот экземпляр использует только свободные источники. Попросите администратора TREK добавить ключ для оценок, часов работы и фотографий.',
|
||||
'places.details.fact.rating': 'Рейтинг',
|
||||
'places.details.fact.cuisine': 'Кухня',
|
||||
'places.details.fact.openingHours': 'Часы работы',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Töm beskrivningsfältet först',
|
||||
'places.details.facts': 'Bra att veta',
|
||||
'places.details.nothing': 'Inget hittades för den här platsen.',
|
||||
'places.details.noKeyTitle': 'Fler detaljer finns tillgängliga',
|
||||
'places.details.noKeyHint': 'Utan en Google API-nyckel använder den här instansen bara fria källor. Be din TREK-administratör lägga till en för betyg, öppettider och foton.',
|
||||
'places.details.fact.rating': 'Betyg',
|
||||
'places.details.fact.cuisine': 'Kök',
|
||||
'places.details.fact.openingHours': 'Öppettider',
|
||||
|
||||
@@ -99,6 +99,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Önce açıklama alanını boşalt',
|
||||
'places.details.facts': 'Bilmekte fayda var',
|
||||
'places.details.nothing': 'Bu yer için bir şey bulunamadı.',
|
||||
'places.details.noKeyTitle': 'Daha fazla ayrıntı mevcut',
|
||||
'places.details.noKeyHint': 'Google API anahtarı olmadan bu örnek yalnızca özgür kaynakları kullanır. Puanlar, açılış saatleri ve fotoğraflar için TREK yöneticinizden bir anahtar eklemesini isteyin.',
|
||||
'places.details.fact.rating': 'Puan',
|
||||
'places.details.fact.cuisine': 'Mutfak',
|
||||
'places.details.fact.openingHours': 'Çalışma saatleri',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Спершу очистіть поле опису',
|
||||
'places.details.facts': 'Корисно знати',
|
||||
'places.details.nothing': 'Для цього місця нічого не знайдено.',
|
||||
'places.details.noKeyTitle': 'Доступно більше деталей',
|
||||
'places.details.noKeyHint': 'Без ключа Google API цей екземпляр використовує лише вільні джерела. Попросіть адміністратора TREK додати ключ, щоб отримати оцінки, години роботи та фото.',
|
||||
'places.details.fact.rating': 'Рейтинг',
|
||||
'places.details.fact.cuisine': 'Кухня',
|
||||
'places.details.fact.openingHours': 'Години роботи',
|
||||
|
||||
@@ -97,6 +97,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': 'Hãy xóa trống ô mô tả trước',
|
||||
'places.details.facts': 'Thông tin hữu ích',
|
||||
'places.details.nothing': 'Không tìm thấy gì cho địa điểm này.',
|
||||
'places.details.noKeyTitle': 'Có thêm thông tin chi tiết',
|
||||
'places.details.noKeyHint': 'Không có khóa API Google, phiên bản này chỉ dùng nguồn miễn phí. Hãy đề nghị quản trị viên TREK thêm khóa để có xếp hạng, giờ mở cửa và ảnh.',
|
||||
'places.details.fact.rating': 'Đánh giá',
|
||||
'places.details.fact.cuisine': 'Ẩm thực',
|
||||
'places.details.fact.openingHours': 'Giờ mở cửa',
|
||||
|
||||
@@ -95,6 +95,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': '請先清空描述欄位',
|
||||
'places.details.facts': '值得一看',
|
||||
'places.details.nothing': '找不到此地點的資訊。',
|
||||
'places.details.noKeyTitle': '可取得更多詳細資訊',
|
||||
'places.details.noKeyHint': '沒有 Google API 金鑰時,此執行個體僅使用免費來源。請聯絡 TREK 管理員新增金鑰,以取得評分、營業時間與照片。',
|
||||
'places.details.fact.rating': '評分',
|
||||
'places.details.fact.cuisine': '菜系',
|
||||
'places.details.fact.openingHours': '營業時間',
|
||||
|
||||
@@ -95,6 +95,8 @@ const places: TranslationStrings = {
|
||||
'places.details.adoptBlocked': '请先清空描述字段',
|
||||
'places.details.facts': '值得一看',
|
||||
'places.details.nothing': '未找到该地点的信息。',
|
||||
'places.details.noKeyTitle': '可获取更多详情',
|
||||
'places.details.noKeyHint': '没有 Google API 密钥时,此实例仅使用免费来源。请联系 TREK 管理员添加密钥,以获取评分、营业时间和照片。',
|
||||
'places.details.fact.rating': '评分',
|
||||
'places.details.fact.cuisine': '菜系',
|
||||
'places.details.fact.openingHours': '营业时间',
|
||||
|
||||
Reference in New Issue
Block a user