8aab968e09
Build & Deploy / Build & Push Docker Image (push) Successful in 1m2s
Test / Type Check (all packages) (push) Failing after 28s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Storefront Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Deploy / Deploy to VPS (push) Successful in 3s
Comprehensive rename of all marketplace references to storefront: - API module: apps/api/src/modules/marketplace/ → storefront/ - Components: MarketplaceHeader → StorefrontHeader, MarketplaceShell → StorefrontShell, MarketplaceFooter → StorefrontFooter - Types: marketplace-homepage.ts → storefront-homepage.ts - Test files: employee-marketplace-* → employee-storefront-* - All source code identifiers, imports, route paths, and strings - Documentation (docs/), CI config (.gitlab-ci.yml), scripts - Dashboard, admin, storefront workspace references - Prisma field names preserved (isListedOnMarketplace, marketplaceRating, marketplaceFunnelEvent) as they map to database schema Validation: - API type-check: 0 errors - Storefront type-check: 0 errors - Dashboard type-check: 0 errors - Full monorepo type-check: only pre-existing admin TS18046
104 lines
4.1 KiB
TypeScript
104 lines
4.1 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useState } from 'react'
|
||
import Link from 'next/link'
|
||
import { useRouter } from 'next/navigation'
|
||
import { RenterAuthError, loadRenterProfile, type SavedCompany } from '@/lib/renter'
|
||
import { useStorefrontPreferences } from '@/components/StorefrontShell'
|
||
|
||
export default function RenterSavedCompaniesPage() {
|
||
const router = useRouter()
|
||
const { language } = useStorefrontPreferences()
|
||
const dict = {
|
||
en: {
|
||
load: 'Could not load saved companies.',
|
||
renter: 'Renter',
|
||
title: 'Saved companies',
|
||
back: 'Back to dashboard',
|
||
loading: 'Loading saved companies…',
|
||
empty: 'You have not saved any companies yet.',
|
||
browse: 'Browse the storefront',
|
||
rentalCompany: 'Rental company',
|
||
},
|
||
fr: {
|
||
load: 'Impossible de charger les entreprises sauvegardées.',
|
||
renter: 'Client',
|
||
title: 'Entreprises sauvegardées',
|
||
back: 'Retour au tableau de bord',
|
||
loading: 'Chargement des entreprises sauvegardées…',
|
||
empty: 'Vous n’avez encore enregistré aucune entreprise.',
|
||
browse: 'Parcourir la storefront',
|
||
rentalCompany: 'Société de location',
|
||
},
|
||
ar: {
|
||
load: 'تعذر تحميل الشركات المحفوظة.',
|
||
renter: 'المستأجر',
|
||
title: 'الشركات المحفوظة',
|
||
back: 'العودة إلى بوابة المستأجر',
|
||
loading: 'جارٍ تحميل الشركات المحفوظة…',
|
||
empty: 'لم تقم بحفظ أي شركة بعد.',
|
||
browse: 'تصفح السوق',
|
||
rentalCompany: 'شركة تأجير',
|
||
},
|
||
}[language]
|
||
const [companies, setCompanies] = useState<SavedCompany[]>([])
|
||
const [errorMsg, setErrorMsg] = useState('')
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
useEffect(() => {
|
||
loadRenterProfile()
|
||
.then((profile) => setCompanies(profile.savedCompanies ?? []))
|
||
.catch((err) => {
|
||
if (err instanceof RenterAuthError) {
|
||
router.replace('/')
|
||
return
|
||
}
|
||
setErrorMsg(err instanceof Error ? err.message : dict.load)
|
||
})
|
||
.finally(() => setLoading(false))
|
||
}, [router])
|
||
|
||
return (
|
||
<div className="shell space-y-6">
|
||
<h1 className="text-3xl font-black tracking-tight text-blue-900">{dict.title}</h1>
|
||
|
||
{loading ? <div className="card p-8 text-sm text-stone-500">{dict.loading}</div> : null}
|
||
{!loading && errorMsg ? <div className="card p-8 text-sm text-rose-600">{errorMsg}</div> : null}
|
||
|
||
{!loading && !errorMsg && companies.length === 0 ? (
|
||
<div className="card p-10 text-center">
|
||
<p className="text-sm text-stone-500">{dict.empty}</p>
|
||
<Link href="/explore" className="mt-5 inline-flex rounded-full bg-orange-600 px-6 py-2.5 text-sm font-semibold text-white">
|
||
{dict.browse}
|
||
</Link>
|
||
</div>
|
||
) : null}
|
||
|
||
{!loading && !errorMsg && companies.length > 0 ? (
|
||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||
{companies.map((company) => (
|
||
<Link
|
||
key={company.id}
|
||
href={`/explore/${company.brand?.subdomain ?? company.id}`}
|
||
className="card flex items-center gap-4 p-5 hover:border-orange-300 transition-colors"
|
||
>
|
||
{company.brand?.logoUrl ? (
|
||
// eslint-disable-next-line @next/next/no-img-element
|
||
<img src={company.brand.logoUrl} alt={company.brand.displayName} className="h-11 w-11 rounded-xl object-contain" />
|
||
) : (
|
||
<div className="flex h-11 w-11 items-center justify-center rounded-xl bg-orange-100 text-sm font-bold text-orange-700">
|
||
{company.brand?.displayName?.charAt(0) ?? '?'}
|
||
</div>
|
||
)}
|
||
<div className="min-w-0">
|
||
<p className="truncate text-sm font-semibold text-blue-900">{company.brand?.displayName ?? dict.rentalCompany}</p>
|
||
<p className="mt-1 truncate text-xs text-stone-400">{company.brand?.subdomain ?? company.id}</p>
|
||
</div>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
)
|
||
}
|