diff --git a/app/components/place-details.gjs b/app/components/place-details.gjs
index ef6e3da..3d46e0f 100644
--- a/app/components/place-details.gjs
+++ b/app/components/place-details.gjs
@@ -316,6 +316,70 @@ export default class PlaceDetails extends Component {
return this.formatMultiLine(val);
}
+ get operatorNames() {
+ const raw = this.tags.operator;
+ if (!raw) return null;
+ const values = raw
+ .split(';')
+ .map((value) => value.trim())
+ .filter(
+ (value) =>
+ value && value.toLowerCase() !== 'yes' && value.toLowerCase() !== 'no'
+ );
+ if (values.length === 0) return null;
+ return this.formatMultiLine(values.join(';'));
+ }
+
+ get operatorWebsite() {
+ const val = this.tags['operator:website'];
+ return val ? val.trim() : null;
+ }
+
+ get operatorWebsiteLabel() {
+ return this.operatorWebsite ? this.getDomain(this.operatorWebsite) : null;
+ }
+
+ get operatorWikidata() {
+ const val = this.tags['operator:wikidata'];
+ if (!val) return null;
+ return (
+ val
+ .split(';')
+ .map((v) => v.trim())
+ .filter(Boolean)[0] || null
+ );
+ }
+
+ get operatorWikipedia() {
+ const val = this.tags['operator:wikipedia'];
+ if (!val) return null;
+ return (
+ val
+ .split(';')
+ .map((v) => v.trim())
+ .filter(Boolean)[0] || null
+ );
+ }
+
+ get operatorWikipediaUrl() {
+ const val = this.operatorWikipedia;
+ if (!val) return null;
+ const match = val.match(/^([a-z-]+):(.+)$/i);
+ if (match) {
+ return `https://${match[1]}.wikipedia.org/wiki/${match[2]}`;
+ }
+ return `https://wikipedia.org/wiki/${val}`;
+ }
+
+ get showOperator() {
+ return !!(
+ this.operatorNames ||
+ this.operatorWebsite ||
+ this.operatorWikidata ||
+ this.operatorWikipedia
+ );
+ }
+
get cuisine() {
if (!this.tags.cuisine) return null;
return this.tags.cuisine
@@ -490,6 +554,45 @@ export default class PlaceDetails extends Component {
{{/if}}
+ {{#if this.showOperator}}
+
+
+
+ {{this.operatorNames}}
+ {{#if this.operatorWebsite}}
+
+
+ {{this.operatorWebsiteLabel}}
+
+ {{/if}}
+ {{#if this.operatorWikidata}}
+
+
+ Wikidata
+
+ {{/if}}
+ {{#if this.operatorWikipedia}}
+
+
+ Wikipedia
+
+ {{/if}}
+
+
+ {{/if}}
+
{{#if this.phone}}
diff --git a/app/utils/icons.js b/app/utils/icons.js
index 7bd5898..85f7d1d 100644
--- a/app/utils/icons.js
+++ b/app/utils/icons.js
@@ -5,6 +5,7 @@
import activity from 'feather-icons/dist/icons/activity.svg?raw';
import arrowLeft from 'feather-icons/dist/icons/arrow-left.svg?raw';
import bookmark from 'feather-icons/dist/icons/bookmark.svg?raw';
+import briefcase from 'feather-icons/dist/icons/briefcase.svg?raw';
import featherCamera from 'feather-icons/dist/icons/camera.svg?raw';
import checkSquare from 'feather-icons/dist/icons/check-square.svg?raw';
import chevronLeft from 'feather-icons/dist/icons/chevron-left.svg?raw';
@@ -182,6 +183,7 @@ const ICONS = {
'boxing-glove-up': boxingGloveUp,
'burger-and-drink-cup-with-straw': burgerAndDrinkCupWithStraw,
bridge,
+ briefcase,
bus,
camera,
'feather-camera': featherCamera,
diff --git a/tests/integration/components/place-details-test.gjs b/tests/integration/components/place-details-test.gjs
index 5b28284..fdd0024 100644
--- a/tests/integration/components/place-details-test.gjs
+++ b/tests/integration/components/place-details-test.gjs
@@ -457,4 +457,112 @@ module('Integration | Component | place-details', function (hooks) {
assert.dom('.place-payment-methods').doesNotExist();
});
+
+ test('it renders the operator name with a briefcase icon', async function (assert) {
+ const place = {
+ title: 'Operated Cafe',
+ osmTags: {
+ operator: 'Le Méridien',
+ },
+ };
+
+ await render();
+
+ const operatorBlock = this.element.querySelector(
+ '.meta-info .content-with-icon span.icon[title="Operator"]'
+ );
+ assert.ok(operatorBlock, 'Operator block is rendered');
+
+ const row = operatorBlock.closest('.content-with-icon');
+ assert.dom(row).includesText('Le Méridien');
+ });
+
+ test('it splits semicolon-separated operators onto separate lines', async function (assert) {
+ const place = {
+ title: 'Multi Operator Stop',
+ osmTags: {
+ operator: 'De Lijn;TEC',
+ },
+ };
+
+ await render();
+
+ const row = this.element
+ .querySelector('span.icon[title="Operator"]')
+ .closest('.content-with-icon');
+
+ assert.dom(row).includesText('De Lijn');
+ assert.dom(row).includesText('TEC');
+ assert.true(
+ row.querySelector('span:not(.icon)').innerHTML.includes('
'),
+ 'Operators are separated by a line break'
+ );
+ });
+
+ test('it suppresses invalid operator values of yes and no', async function (assert) {
+ const place = {
+ title: 'Invalid Operator',
+ osmTags: {
+ operator: 'yes',
+ },
+ };
+
+ await render();
+
+ assert
+ .dom('span.icon[title="Operator"]')
+ .doesNotExist('Operator block is hidden for operator=yes');
+ });
+
+ test('it renders operator wikidata and website links', async function (assert) {
+ const place = {
+ title: 'Linked Operator',
+ osmTags: {
+ operator: 'Flixbus',
+ 'operator:wikidata': 'Q15712278',
+ 'operator:website': 'https://www.flixbus.com/',
+ 'operator:wikipedia': 'en:FlixBus',
+ },
+ };
+
+ await render();
+
+ const row = this.element
+ .querySelector('span.icon[title="Operator"]')
+ .closest('.content-with-icon');
+
+ const wikidataLink = row.querySelector(
+ 'a[href="https://www.wikidata.org/wiki/Q15712278"]'
+ );
+ assert.ok(wikidataLink, 'Wikidata link is rendered');
+ assert.dom(wikidataLink).hasText('Wikidata');
+
+ const websiteLink = row.querySelector('a[href="https://www.flixbus.com/"]');
+ assert.ok(websiteLink, 'Operator website link is rendered');
+ assert.dom(websiteLink).hasText('www.flixbus.com');
+
+ const wikipediaLink = row.querySelector(
+ 'a[href="https://en.wikipedia.org/wiki/FlixBus"]'
+ );
+ assert.ok(wikipediaLink, 'Operator Wikipedia link is rendered');
+ assert.dom(wikipediaLink).hasText('Wikipedia');
+ });
+
+ test('it renders the operator block when only operator subkeys are present', async function (assert) {
+ const place = {
+ title: 'Subkey Only',
+ osmTags: {
+ 'operator:wikidata': 'Q42',
+ },
+ };
+
+ await render();
+
+ assert
+ .dom('span.icon[title="Operator"]')
+ .exists('Operator block is rendered from subkeys alone');
+ assert
+ .dom('a[href="https://www.wikidata.org/wiki/Q42"]')
+ .exists('Wikidata link is rendered');
+ });
});