fix notification and add billing page and contract
This commit is contained in:
@@ -1,14 +1,5 @@
|
||||
import { headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { resolveServerAppUrl } from '@/lib/appUrls'
|
||||
|
||||
export default function CompanyWorkspacePage() {
|
||||
const requestHeaders = headers()
|
||||
const dashboardUrl = resolveServerAppUrl(
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3001',
|
||||
requestHeaders.get('host'),
|
||||
requestHeaders.get('x-forwarded-proto'),
|
||||
)
|
||||
|
||||
redirect(`${dashboardUrl}/dashboard`)
|
||||
redirect('/sign-in')
|
||||
}
|
||||
|
||||
@@ -13,3 +13,123 @@ body {
|
||||
.card {
|
||||
@apply rounded-2xl border border-stone-200 bg-white shadow-sm transition-colors dark:border-stone-800 dark:bg-stone-900;
|
||||
}
|
||||
|
||||
body[data-theme='medium'] {
|
||||
background-color: rgb(41 37 36);
|
||||
color: rgb(231 229 228);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950 {
|
||||
background-color: rgb(41 37 36);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950\/80 {
|
||||
background-color: rgb(41 37 36 / 0.8);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950\/95 {
|
||||
background-color: rgb(41 37 36 / 0.95);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950\/50 {
|
||||
background-color: rgb(68 64 60 / 0.5);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950\/40 {
|
||||
background-color: rgb(68 64 60 / 0.4);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-950\/30 {
|
||||
background-color: rgb(68 64 60 / 0.3);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-900,
|
||||
body[data-theme='medium'] .dark\:bg-stone-900\/80,
|
||||
body[data-theme='medium'] .dark\:bg-stone-900\/70,
|
||||
body[data-theme='medium'] .dark\:bg-stone-900\/60 {
|
||||
background-color: rgb(68 64 60 / 0.85);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-stone-800 {
|
||||
background-color: rgb(87 83 78);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-amber-400 {
|
||||
background-color: rgb(251 191 36);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:border-stone-800 {
|
||||
border-color: rgb(120 113 108);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:border-stone-700,
|
||||
body[data-theme='medium'] .dark\:border-white\/5 {
|
||||
border-color: rgb(87 83 78);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:border-amber-300 {
|
||||
border-color: rgb(252 211 77);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-50,
|
||||
body[data-theme='medium'] .dark\:text-stone-100,
|
||||
body[data-theme='medium'] .dark\:text-white {
|
||||
color: rgb(250 250 249);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-200 {
|
||||
color: rgb(231 229 228);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-300,
|
||||
body[data-theme='medium'] .dark\:text-stone-400 {
|
||||
color: rgb(214 211 209);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-500,
|
||||
body[data-theme='medium'] .dark\:text-stone-700,
|
||||
body[data-theme='medium'] .dark\:text-stone-800 {
|
||||
color: rgb(168 162 158);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-950 {
|
||||
color: rgb(28 25 23);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-slate-100 {
|
||||
color: rgb(250 250 249);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-stone-600 {
|
||||
color: rgb(168 162 158);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-amber-300 {
|
||||
color: rgb(252 211 77);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:text-sky-400 {
|
||||
color: rgb(125 211 252);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:hover\:bg-stone-800:hover,
|
||||
body[data-theme='medium'] .dark\:hover\:bg-stone-900:hover {
|
||||
background-color: rgb(28 25 23);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:hover\:bg-amber-300:hover {
|
||||
background-color: rgb(252 211 77);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:hover\:text-stone-100:hover,
|
||||
body[data-theme='medium'] .dark\:hover\:text-white:hover {
|
||||
color: rgb(250 250 249);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:hover\:border-stone-200:hover {
|
||||
border-color: rgb(214 211 209);
|
||||
}
|
||||
|
||||
body[data-theme='medium'] .dark\:bg-\[\#111111\] {
|
||||
background-color: rgb(28 25 23);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import { headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { resolveServerAppUrl } from '@/lib/appUrls'
|
||||
|
||||
export default function PlatformOperationsPage() {
|
||||
const requestHeaders = headers()
|
||||
const adminUrl = resolveServerAppUrl(
|
||||
process.env.NEXT_PUBLIC_ADMIN_URL ?? 'http://localhost:3002',
|
||||
requestHeaders.get('host'),
|
||||
requestHeaders.get('x-forwarded-proto'),
|
||||
)
|
||||
|
||||
redirect(`${adminUrl}/dashboard`)
|
||||
redirect('/sign-in')
|
||||
}
|
||||
|
||||
@@ -143,74 +143,54 @@ export default function RenterDashboardPage() {
|
||||
})
|
||||
}, [router])
|
||||
|
||||
function handleLogout() {
|
||||
localStorage.removeItem('renter_token')
|
||||
router.push('/explore')
|
||||
}
|
||||
|
||||
// Loading skeleton
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell">
|
||||
<div className="animate-pulse space-y-6">
|
||||
<div className="h-8 w-48 rounded-xl bg-stone-200" />
|
||||
<div className="h-40 rounded-2xl bg-stone-200" />
|
||||
<div className="h-40 rounded-2xl bg-stone-200" />
|
||||
</div>
|
||||
<div className="shell">
|
||||
<div className="animate-pulse space-y-6">
|
||||
<div className="h-8 w-48 rounded-xl bg-stone-200" />
|
||||
<div className="h-40 rounded-2xl bg-stone-200" />
|
||||
<div className="h-40 rounded-2xl bg-stone-200" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell">
|
||||
<div className="card p-8 text-center">
|
||||
<p className="text-sm text-rose-600">{errorMsg}</p>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="mt-4 rounded-full bg-stone-900 px-6 py-2.5 text-sm font-semibold text-white"
|
||||
>
|
||||
{dict.backToExplore}
|
||||
</button>
|
||||
</div>
|
||||
<div className="shell">
|
||||
<div className="card p-8 text-center">
|
||||
<p className="text-sm text-rose-600">{errorMsg}</p>
|
||||
<Link
|
||||
href="/explore"
|
||||
className="mt-4 inline-flex rounded-full bg-stone-900 px-6 py-2.5 text-sm font-semibold text-white"
|
||||
>
|
||||
{dict.backToExplore}
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const savedCompanies = profile?.savedCompanies ?? []
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell space-y-8">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">
|
||||
{dict.renterDashboard}
|
||||
</p>
|
||||
<h1 className="mt-1 text-3xl font-black tracking-tight text-stone-900">
|
||||
{dict.welcome} {profile?.firstName}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
href="/explore"
|
||||
className="rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700 shadow-sm hover:border-stone-300"
|
||||
>
|
||||
{dict.exploreVehicles}
|
||||
</Link>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="rounded-full bg-stone-900 px-5 py-2.5 text-sm font-semibold text-white hover:bg-stone-700"
|
||||
>
|
||||
{dict.logout}
|
||||
</button>
|
||||
</div>
|
||||
<div className="shell space-y-8">
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">
|
||||
{dict.renterDashboard}
|
||||
</p>
|
||||
<h1 className="mt-1 text-3xl font-black tracking-tight text-stone-900">
|
||||
{dict.welcome} {profile?.firstName}
|
||||
</h1>
|
||||
</div>
|
||||
<Link
|
||||
href="/explore"
|
||||
className="self-start rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700 shadow-sm hover:border-stone-300"
|
||||
>
|
||||
{dict.exploreVehicles}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<section className="grid gap-4 sm:grid-cols-3">
|
||||
{[
|
||||
@@ -325,7 +305,6 @@ export default function RenterDashboardPage() {
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import RenterShell from '@/components/RenterShell'
|
||||
|
||||
export default function RenterLayout({ children }: { children: React.ReactNode }) {
|
||||
return <RenterShell>{children}</RenterShell>
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
RenterAuthError,
|
||||
@@ -101,64 +100,54 @@ export default function RenterNotificationsPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell space-y-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">{dict.renter}</p>
|
||||
<h1 className="mt-1 text-3xl font-black tracking-tight text-stone-900">{dict.title}</h1>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={markAllRead}
|
||||
disabled={loading || updating}
|
||||
className="rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700"
|
||||
>
|
||||
{dict.markAll}
|
||||
</button>
|
||||
<Link href="/renter/dashboard" className="rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700">
|
||||
{dict.back}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{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 && notifications.length === 0 ? (
|
||||
<div className="card p-10 text-center text-sm text-stone-500">{dict.empty}</div>
|
||||
) : null}
|
||||
|
||||
{!loading && !errorMsg && notifications.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{notifications.map((notification) => (
|
||||
<article key={notification.id} className="card p-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-700">{notification.type.replaceAll('_', ' ')}</p>
|
||||
<h2 className="mt-2 text-lg font-bold text-stone-900">{notification.title}</h2>
|
||||
</div>
|
||||
{notification.readAt ? (
|
||||
<span className="rounded-full bg-stone-100 px-3 py-1 text-xs font-semibold text-stone-600">{dict.read}</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => markRead(notification.id)}
|
||||
disabled={updating}
|
||||
className="rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-800"
|
||||
>
|
||||
{dict.markRead}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-3 text-sm leading-7 text-stone-600">{notification.body}</p>
|
||||
<p className="mt-4 text-xs text-stone-400">{new Date(notification.createdAt).toLocaleString()}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="shell space-y-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<h1 className="text-3xl font-black tracking-tight text-stone-900">{dict.title}</h1>
|
||||
<button
|
||||
type="button"
|
||||
onClick={markAllRead}
|
||||
disabled={loading || updating}
|
||||
className="rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700"
|
||||
>
|
||||
{dict.markAll}
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{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 && notifications.length === 0 ? (
|
||||
<div className="card p-10 text-center text-sm text-stone-500">{dict.empty}</div>
|
||||
) : null}
|
||||
|
||||
{!loading && !errorMsg && notifications.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{notifications.map((notification) => (
|
||||
<article key={notification.id} className="card p-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-700">{notification.type.replaceAll('_', ' ')}</p>
|
||||
<h2 className="mt-2 text-lg font-bold text-stone-900">{notification.title}</h2>
|
||||
</div>
|
||||
{notification.readAt ? (
|
||||
<span className="rounded-full bg-stone-100 px-3 py-1 text-xs font-semibold text-stone-600">{dict.read}</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => markRead(notification.id)}
|
||||
disabled={updating}
|
||||
className="rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-800"
|
||||
>
|
||||
{dict.markRead}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-3 text-sm leading-7 text-stone-600">{notification.body}</p>
|
||||
<p className="mt-4 text-xs text-stone-400">{new Date(notification.createdAt).toLocaleString()}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, type ReactNode } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
RenterAuthError,
|
||||
@@ -136,15 +135,15 @@ export default function RenterProfilePage() {
|
||||
}, [router])
|
||||
|
||||
if (status === 'loading') {
|
||||
return <RenterShell title={dict.profile} dict={dict}><div className="card p-8 text-sm text-stone-500">{dict.loading}</div></RenterShell>
|
||||
return <div className="shell"><div className="card p-8 text-sm text-stone-500">{dict.loading}</div></div>
|
||||
}
|
||||
|
||||
if (status === 'error') {
|
||||
return <RenterShell title={dict.profile} dict={dict}><ErrorCard message={errorMsg} /></RenterShell>
|
||||
return <div className="shell"><ErrorCard message={errorMsg} /></div>
|
||||
}
|
||||
|
||||
return (
|
||||
<RenterShell title={dict.profile} dict={dict}>
|
||||
<div className="shell space-y-6">
|
||||
<div className="card p-8">
|
||||
<h1 className="text-3xl font-black tracking-tight text-stone-900">{dict.yourProfile}</h1>
|
||||
<div className="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
@@ -265,39 +264,7 @@ export default function RenterProfilePage() {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</RenterShell>
|
||||
)
|
||||
}
|
||||
|
||||
function RenterShell({ title, children, dict }: { title: string; children: ReactNode; dict: Record<string, string> }) {
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell space-y-6">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Link href="/renter/dashboard" className="rounded-full border border-stone-200 bg-white px-4 py-2 text-sm font-semibold text-stone-700">
|
||||
{dict.back}
|
||||
</Link>
|
||||
<Link href="/renter/saved-companies" className="rounded-full border border-stone-200 bg-white px-4 py-2 text-sm font-semibold text-stone-700">
|
||||
{dict.savedCompanies}
|
||||
</Link>
|
||||
<Link href="/renter/notifications" className="rounded-full border border-stone-200 bg-white px-4 py-2 text-sm font-semibold text-stone-700">
|
||||
{dict.notifications}
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
clearRenterSession()
|
||||
window.location.href = '/renter/sign-in'
|
||||
}}
|
||||
className="rounded-full bg-stone-900 px-5 py-2 text-sm font-semibold text-white"
|
||||
>
|
||||
{dict.logout}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">{dict.renter} {title}</p>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -59,55 +59,45 @@ export default function RenterSavedCompaniesPage() {
|
||||
}, [router])
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-stone-50 py-10">
|
||||
<div className="shell space-y-6">
|
||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-amber-700">{dict.renter}</p>
|
||||
<h1 className="mt-1 text-3xl font-black tracking-tight text-stone-900">{dict.title}</h1>
|
||||
</div>
|
||||
<Link href="/renter/dashboard" className="rounded-full border border-stone-200 bg-white px-5 py-2.5 text-sm font-semibold text-stone-700">
|
||||
{dict.back}
|
||||
<div className="shell space-y-6">
|
||||
<h1 className="text-3xl font-black tracking-tight text-stone-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-stone-900 px-6 py-2.5 text-sm font-semibold text-white">
|
||||
{dict.browse}
|
||||
</Link>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{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-stone-900 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-amber-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-amber-100 text-sm font-bold text-amber-700">
|
||||
{company.brand?.displayName?.charAt(0) ?? '?'}
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm font-semibold text-stone-900">{company.brand?.displayName ?? dict.rentalCompany}</p>
|
||||
<p className="mt-1 truncate text-xs text-stone-400">{company.brand?.subdomain ?? company.id}</p>
|
||||
{!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-amber-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-amber-100 text-sm font-bold text-amber-700">
|
||||
{company.brand?.displayName?.charAt(0) ?? '?'}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm font-semibold text-stone-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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import WorkspaceFrame from '@/components/WorkspaceFrame'
|
||||
|
||||
export default function SignInPage() {
|
||||
return <WorkspaceFrame target="sign-in" />
|
||||
}
|
||||
Reference in New Issue
Block a user