fix(apps): rename 'Websites' tab to 'Services' (#51)

Headless containers (databases, APIs, backends without a UI) belong in a
tab labelled 'Services', not 'Websites'. The categorisation logic already
routes UI-less packages there (built under #45); this finishes the rename
of the user-facing label across Apps, Marketplace, Discover and the mobile
nav, and makes 'services' the canonical tab state/query param. Old
?tab=websites bookmarks still resolve (back-compat acceptor kept).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-06-17 16:56:36 -04:00
parent 3ca1fadfea
commit c10f2ac22e
5 changed files with 14 additions and 14 deletions

View File

@ -8,7 +8,7 @@
<div class="mode-switcher hidden md:inline-flex"> <div class="mode-switcher hidden md:inline-flex">
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'apps' }" @click="activeTab = 'apps'; router.replace({ query: {} })">My Apps</button> <button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'apps' }" @click="activeTab = 'apps'; router.replace({ query: {} })">My Apps</button>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn">App Store</RouterLink>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'websites' }" @click="activeTab = 'websites'; router.replace({ query: { tab: 'websites' } })">Websites</button> <button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'services' }" @click="activeTab = 'services'; router.replace({ query: { tab: 'services' } })">Services</button>
</div> </div>
</div> </div>
<div v-show="activeTab === 'apps' && categoriesWithApps.length > 1 && !collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex"> <div v-show="activeTab === 'apps' && categoriesWithApps.length > 1 && !collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex">
@ -73,7 +73,7 @@
<div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3"> <div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3">
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'apps' }" @click="activeTab = 'apps'; router.replace({ query: {} })">My Apps</button> <button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'apps' }" @click="activeTab = 'apps'; router.replace({ query: {} })">My Apps</button>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn">App Store</RouterLink>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'websites' }" @click="activeTab = 'websites'; router.replace({ query: { tab: 'websites' } })">Websites</button> <button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'services' }" @click="activeTab = 'services'; router.replace({ query: { tab: 'services' } })">Services</button>
</div> </div>
<div v-if="activeTab === 'apps' && categoriesWithApps.length > 1" class="mobile-category-strip mb-3" aria-label="My Apps categories"> <div v-if="activeTab === 'apps' && categoriesWithApps.length > 1" class="mobile-category-strip mb-3" aria-label="My Apps categories">
<button <button
@ -401,11 +401,11 @@ const showStagger = !appsAnimationDone
// Tabs // Tabs
const activeTab = ref<AppsTab>( const activeTab = ref<AppsTab>(
route.query.tab === 'websites' || route.query.tab === 'services' ? 'websites' : 'apps' route.query.tab === 'websites' || route.query.tab === 'services' ? 'services' : 'apps'
) )
watch(() => route.query.tab, (tab) => { watch(() => route.query.tab, (tab) => {
activeTab.value = tab === 'websites' || tab === 'services' ? 'websites' : 'apps' activeTab.value = tab === 'websites' || tab === 'services' ? 'services' : 'apps'
}) })
// Search (debounced) // Search (debounced)

View File

@ -8,7 +8,7 @@
<div class="mode-switcher hidden md:inline-flex"> <div class="mode-switcher hidden md:inline-flex">
<RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink> <RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink>
<RouterLink to="/dashboard/apps?tab=websites" class="mode-switcher-btn">Websites</RouterLink> <RouterLink to="/dashboard/apps?tab=services" class="mode-switcher-btn">Services</RouterLink>
</div> </div>
</div> </div>
<div v-show="!collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex"> <div v-show="!collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex">
@ -63,7 +63,7 @@
<div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3"> <div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3">
<RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink> <RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink>
<RouterLink to="/dashboard/apps?tab=websites" class="mode-switcher-btn">Websites</RouterLink> <RouterLink to="/dashboard/apps?tab=services" class="mode-switcher-btn">Services</RouterLink>
</div> </div>
<div class="flex items-center gap-2 mb-3"> <div class="flex items-center gap-2 mb-3">
<span class="discover-terminal-tag">discover</span> <span class="discover-terminal-tag">discover</span>

View File

@ -8,7 +8,7 @@
<div class="mode-switcher hidden md:inline-flex"> <div class="mode-switcher hidden md:inline-flex">
<RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink> <RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink>
<RouterLink to="/dashboard/apps?tab=websites" class="mode-switcher-btn">Websites</RouterLink> <RouterLink to="/dashboard/apps?tab=services" class="mode-switcher-btn">Services</RouterLink>
</div> </div>
</div> </div>
<div v-show="!collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex"> <div v-show="!collapseCategories" class="mode-switcher category-tabs-wide hidden md:inline-flex">
@ -64,7 +64,7 @@
<div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3"> <div class="app-header-inline-tabs mode-switcher mode-switcher-full mb-3">
<RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink> <RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink> <RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink>
<RouterLink to="/dashboard/apps?tab=websites" class="mode-switcher-btn">Websites</RouterLink> <RouterLink to="/dashboard/apps?tab=services" class="mode-switcher-btn">Services</RouterLink>
</div> </div>
<div class="flex items-center gap-2 mb-3"> <div class="flex items-center gap-2 mb-3">
<span class="discover-terminal-tag">discover</span> <span class="discover-terminal-tag">discover</span>

View File

@ -79,9 +79,9 @@ export function isKnownApp(id: string, pkg?: PackageDataEntry): boolean {
} }
// True when the package's manifest declares a front-end UI interface. This is // True when the package's manifest declares a front-end UI interface. This is
// the authoritative "is this a user-facing app?" signal (#45): apps with a UI // the authoritative "is this a user-facing app?" signal (#45/#51): apps with a
// belong in "My Apps", while headless services (databases, backends, workers) // UI belong in "My Apps", while headless services (databases, APIs, backends,
// declare no UI and belong in "Websites"/"Services". // workers) declare no UI and belong in the "Services" tab.
export function hasFrontendUi(pkg?: PackageDataEntry): boolean { export function hasFrontendUi(pkg?: PackageDataEntry): boolean {
return !!pkg?.manifest?.interfaces?.main?.ui return !!pkg?.manifest?.interfaces?.main?.ui
} }

View File

@ -19,11 +19,11 @@
:class="{ 'mode-switcher-btn-active': route.path === '/dashboard/marketplace' || route.path.startsWith('/dashboard/marketplace/') || route.path === '/dashboard/discover' }" :class="{ 'mode-switcher-btn-active': route.path === '/dashboard/marketplace' || route.path.startsWith('/dashboard/marketplace/') || route.path === '/dashboard/discover' }"
>App Store</RouterLink> >App Store</RouterLink>
<RouterLink <RouterLink
to="/dashboard/apps?tab=websites" to="/dashboard/apps?tab=services"
class="mode-switcher-btn" class="mode-switcher-btn"
:class="{ 'mode-switcher-btn-active': route.query.tab === 'services' || route.query.tab === 'websites' }" :class="{ 'mode-switcher-btn-active': route.query.tab === 'services' || route.query.tab === 'websites' }"
@click.prevent="router.push({ path: '/dashboard/apps', query: { tab: 'websites' } })" @click.prevent="router.push({ path: '/dashboard/apps', query: { tab: 'services' } })"
>Websites</RouterLink> >Services</RouterLink>
</div> </div>
</div> </div>