mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Apply tenant theme on business login and polish login branding.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
56f6b23074
commit
a48a24e6f2
@@ -1,32 +1,24 @@
|
|||||||
import { useEffect, type ReactNode } from 'react'
|
import { useEffect, type ReactNode } from 'react'
|
||||||
import { useAuth } from './AuthContext'
|
|
||||||
import { getActiveBusinessId } from '../lib/businessContext'
|
|
||||||
import { isAbortError } from '../lib/api'
|
import { isAbortError } from '../lib/api'
|
||||||
import { getSettings } from '../services/settingsService'
|
import { getBusinessDomain } from '../lib/config'
|
||||||
|
import { resolveTenantByDomain } from '../services/tenantService'
|
||||||
import { applyBusinessPrimaryColor, resetBusinessPrimaryColor } from '../utils/applyBusinessTheme'
|
import { applyBusinessPrimaryColor, resetBusinessPrimaryColor } from '../utils/applyBusinessTheme'
|
||||||
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
import { normalizeBusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||||
|
|
||||||
export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
||||||
const { user, isLoading } = useAuth()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoading) return
|
|
||||||
|
|
||||||
if (!user || !getActiveBusinessId()) {
|
|
||||||
resetBusinessPrimaryColor()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
|
const domain = getBusinessDomain()
|
||||||
|
|
||||||
async function loadTheme() {
|
async function loadTheme() {
|
||||||
try {
|
try {
|
||||||
const data = await getSettings(controller.signal)
|
const tenant = await resolveTenantByDomain(domain, controller.signal)
|
||||||
|
if (controller.signal.aborted) return
|
||||||
applyBusinessPrimaryColor(
|
applyBusinessPrimaryColor(
|
||||||
normalizeBusinessPrimaryColorId(data.settings.branding?.primaryColor),
|
normalizeBusinessPrimaryColorId(tenant.primaryColor),
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isAbortError(err)) return
|
if (isAbortError(err) || controller.signal.aborted) return
|
||||||
applyBusinessPrimaryColor(undefined)
|
applyBusinessPrimaryColor(undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,8 +27,9 @@ export function BusinessThemeProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
controller.abort()
|
controller.abort()
|
||||||
|
resetBusinessPrimaryColor()
|
||||||
}
|
}
|
||||||
}, [user, isLoading])
|
}, [])
|
||||||
|
|
||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { resolveTenantByDomain } from '../services/tenantService'
|
|||||||
|
|
||||||
interface TenantBrandingContextValue {
|
interface TenantBrandingContextValue {
|
||||||
businessName: string
|
businessName: string
|
||||||
|
businessNameEn: string
|
||||||
logoUrl: string | null
|
logoUrl: string | null
|
||||||
faviconUrl: string | null
|
faviconUrl: string | null
|
||||||
refreshBranding: () => void
|
refreshBranding: () => void
|
||||||
@@ -135,9 +136,14 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
return nameEn.trim() || nameFa.trim() || getBusinessDomain()
|
return nameEn.trim() || nameFa.trim() || getBusinessDomain()
|
||||||
}, [locale, nameEn, nameFa])
|
}, [locale, nameEn, nameFa])
|
||||||
|
|
||||||
|
const businessNameEn = useMemo(
|
||||||
|
() => nameEn.trim() || nameFa.trim() || getBusinessDomain(),
|
||||||
|
[nameEn, nameFa],
|
||||||
|
)
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({ businessName, logoUrl, faviconUrl, refreshBranding }),
|
() => ({ businessName, businessNameEn, logoUrl, faviconUrl, refreshBranding }),
|
||||||
[businessName, logoUrl, faviconUrl, refreshBranding],
|
[businessName, businessNameEn, logoUrl, faviconUrl, refreshBranding],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
.page {
|
.page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@@ -60,6 +62,18 @@
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poweredBy {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-align: center;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poweredBy:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type SmsStep = 'phone' | 'code'
|
|||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { login, loginWithOtp } = useAuth()
|
const { login, loginWithOtp } = useAuth()
|
||||||
const { businessName, logoUrl } = useTenantBranding()
|
const { businessName, businessNameEn, logoUrl } = useTenantBranding()
|
||||||
const businessDomain = getBusinessDomain()
|
const businessDomain = getBusinessDomain()
|
||||||
const t = useT()
|
const t = useT()
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ export function LoginPage() {
|
|||||||
{logoUrl ? <img src={logoUrl} alt="" className={styles.logo} /> : null}
|
{logoUrl ? <img src={logoUrl} alt="" className={styles.logo} /> : null}
|
||||||
<div className={styles.brandText}>
|
<div className={styles.brandText}>
|
||||||
<span className={styles.businessName}>{businessName || businessDomain}</span>
|
<span className={styles.businessName}>{businessName || businessDomain}</span>
|
||||||
<span className={styles.appName}>{t('app.poweredBy')}</span>
|
<span className={styles.appName}>{businessNameEn || businessDomain}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.langSelect}>
|
<div className={styles.langSelect}>
|
||||||
<LanguageSelect />
|
<LanguageSelect />
|
||||||
@@ -673,6 +673,14 @@ export function LoginPage() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
className={styles.poweredBy}
|
||||||
|
href="https://meshkee.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Powered by Meshkee.app
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { apiRequest } from '../lib/api'
|
import { apiRequest } from '../lib/api'
|
||||||
import type { DashboardLocale } from '@meshkee/dashboard-core'
|
import type { DashboardLocale } from '@meshkee/dashboard-core'
|
||||||
|
import type { BusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||||
|
|
||||||
export interface ResolvedTenant {
|
export interface ResolvedTenant {
|
||||||
id: string
|
id: string
|
||||||
@@ -7,12 +8,13 @@ export interface ResolvedTenant {
|
|||||||
nameFa: string | null
|
nameFa: string | null
|
||||||
slug: string
|
slug: string
|
||||||
domain: string
|
domain: string
|
||||||
|
primaryColor: BusinessPrimaryColorId
|
||||||
defaultLocale?: DashboardLocale
|
defaultLocale?: DashboardLocale
|
||||||
logoUrl?: string | null
|
logoUrl?: string | null
|
||||||
faviconUrl?: string | null
|
faviconUrl?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveTenantByDomain(host: string) {
|
export async function resolveTenantByDomain(host: string, signal?: AbortSignal) {
|
||||||
const encodedHost = encodeURIComponent(host)
|
const encodedHost = encodeURIComponent(host)
|
||||||
return apiRequest<ResolvedTenant>(`/tenants/${encodedHost}`)
|
return apiRequest<ResolvedTenant>(`/tenants/${encodedHost}`, { signal })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { resolveTenantByDomain } from '../services/tenantService'
|
|||||||
|
|
||||||
interface TenantBrandingContextValue {
|
interface TenantBrandingContextValue {
|
||||||
businessName: string
|
businessName: string
|
||||||
|
businessNameEn: string
|
||||||
logoUrl: string | null
|
logoUrl: string | null
|
||||||
faviconUrl: string | null
|
faviconUrl: string | null
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,7 @@ function pickBusinessName(
|
|||||||
export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
||||||
const { setLocale } = useLocale()
|
const { setLocale } = useLocale()
|
||||||
const [businessName, setBusinessName] = useState('')
|
const [businessName, setBusinessName] = useState('')
|
||||||
|
const [businessNameEn, setBusinessNameEn] = useState('')
|
||||||
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
const [logoUrl, setLogoUrl] = useState<string | null>(null)
|
||||||
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
|
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
|
||||||
const defaultLocaleAppliedRef = useRef(false)
|
const defaultLocaleAppliedRef = useRef(false)
|
||||||
@@ -59,6 +61,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
tenant.name,
|
tenant.name,
|
||||||
domain,
|
domain,
|
||||||
)
|
)
|
||||||
|
const nameEn = pickBusinessName(info?.name, tenant.name, domain)
|
||||||
|
|
||||||
const nextLogo =
|
const nextLogo =
|
||||||
info?.logoUrl?.trim() || tenant.logoUrl?.trim() || null
|
info?.logoUrl?.trim() || tenant.logoUrl?.trim() || null
|
||||||
@@ -69,6 +72,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
null
|
null
|
||||||
|
|
||||||
setBusinessName(name || domain)
|
setBusinessName(name || domain)
|
||||||
|
setBusinessNameEn(nameEn || domain)
|
||||||
setLogoUrl(nextLogo)
|
setLogoUrl(nextLogo)
|
||||||
setFaviconUrl(nextFavicon)
|
setFaviconUrl(nextFavicon)
|
||||||
applyDocumentFavicon(nextFavicon)
|
applyDocumentFavicon(nextFavicon)
|
||||||
@@ -84,6 +88,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isAbortError(err) || controller.signal.aborted) return
|
if (isAbortError(err) || controller.signal.aborted) return
|
||||||
setBusinessName(domain)
|
setBusinessName(domain)
|
||||||
|
setBusinessNameEn(domain)
|
||||||
setLogoUrl(null)
|
setLogoUrl(null)
|
||||||
setFaviconUrl(null)
|
setFaviconUrl(null)
|
||||||
applyDocumentFavicon(null)
|
applyDocumentFavicon(null)
|
||||||
@@ -98,8 +103,8 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
|
|||||||
}, [domain, setLocale])
|
}, [domain, setLocale])
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({ businessName, logoUrl, faviconUrl }),
|
() => ({ businessName, businessNameEn, logoUrl, faviconUrl }),
|
||||||
[businessName, logoUrl, faviconUrl],
|
[businessName, businessNameEn, logoUrl, faviconUrl],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
.page {
|
.page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@@ -60,6 +62,18 @@
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.poweredBy {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-align: center;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poweredBy:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function LoginPage() {
|
|||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
const redirectTo = safeRedirectPath(searchParams.get('redirect'))
|
const redirectTo = safeRedirectPath(searchParams.get('redirect'))
|
||||||
const { login, loginWithOtp } = useAuth()
|
const { login, loginWithOtp } = useAuth()
|
||||||
const { businessName, logoUrl } = useTenantBranding()
|
const { businessName, businessNameEn, logoUrl } = useTenantBranding()
|
||||||
const tenantDomain = getTenantDomain()
|
const tenantDomain = getTenantDomain()
|
||||||
const t = useT()
|
const t = useT()
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ export function LoginPage() {
|
|||||||
<img src={logoUrl || meshkeeLogo} alt="" className={styles.logo} />
|
<img src={logoUrl || meshkeeLogo} alt="" className={styles.logo} />
|
||||||
<div className={styles.brandText}>
|
<div className={styles.brandText}>
|
||||||
<span className={styles.domain}>{businessName || tenantDomain}</span>
|
<span className={styles.domain}>{businessName || tenantDomain}</span>
|
||||||
<span className={styles.appName}>{t('app.poweredBy')}</span>
|
<span className={styles.appName}>{businessNameEn || tenantDomain}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.langSelect}>
|
<div className={styles.langSelect}>
|
||||||
<LanguageSelect />
|
<LanguageSelect />
|
||||||
@@ -704,6 +704,14 @@ export function LoginPage() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
className={styles.poweredBy}
|
||||||
|
href="https://meshkee.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Powered by Meshkee.app
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,11 @@
|
|||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.55);
|
border: 1px solid rgba(255, 255, 255, 0.85);
|
||||||
background: rgba(255, 255, 255, 0.28);
|
background: rgba(255, 255, 255, 0.78);
|
||||||
backdrop-filter: blur(48px) saturate(1.2);
|
backdrop-filter: blur(72px) saturate(1.35);
|
||||||
-webkit-backdrop-filter: blur(48px) saturate(1.2);
|
-webkit-backdrop-filter: blur(72px) saturate(1.35);
|
||||||
box-shadow: var(--glass-shadow);
|
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.16);
|
||||||
animation: menuIn 0.14s ease;
|
animation: menuIn 0.14s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +86,12 @@
|
|||||||
color: #ef4444;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menuDivider {
|
||||||
|
height: 1px;
|
||||||
|
margin: 4px 6px;
|
||||||
|
background: rgba(148, 163, 184, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes menuIn {
|
@keyframes menuIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { Building2, ExternalLink, Globe, MoreHorizontal, UserRound } from 'lucide-react'
|
import {
|
||||||
|
Building2,
|
||||||
|
ExternalLink,
|
||||||
|
Globe,
|
||||||
|
MoreHorizontal,
|
||||||
|
Trash2,
|
||||||
|
UserRound,
|
||||||
|
} from 'lucide-react'
|
||||||
import styles from './BusinessActionsOverflowMenu.module.css'
|
import styles from './BusinessActionsOverflowMenu.module.css'
|
||||||
|
|
||||||
type PopoverPosition = {
|
type PopoverPosition = {
|
||||||
@@ -10,6 +17,7 @@ type PopoverPosition = {
|
|||||||
|
|
||||||
interface DomainLinksOverflowMenuProps {
|
interface DomainLinksOverflowMenuProps {
|
||||||
host: string
|
host: string
|
||||||
|
onRemove: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function apexHost(host: string) {
|
function apexHost(host: string) {
|
||||||
@@ -28,7 +36,7 @@ function openHost(host: string) {
|
|||||||
window.open(siteUrl(host), '_blank', 'noopener,noreferrer')
|
window.open(siteUrl(host), '_blank', 'noopener,noreferrer')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DomainLinksOverflowMenu({ host }: DomainLinksOverflowMenuProps) {
|
export function DomainLinksOverflowMenu({ host, onRemove }: DomainLinksOverflowMenuProps) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [popoverPos, setPopoverPos] = useState<PopoverPosition | null>(null)
|
const [popoverPos, setPopoverPos] = useState<PopoverPosition | null>(null)
|
||||||
const wrapRef = useRef<HTMLDivElement>(null)
|
const wrapRef = useRef<HTMLDivElement>(null)
|
||||||
@@ -126,8 +134,8 @@ export function DomainLinksOverflowMenu({ host }: DomainLinksOverflowMenuProps)
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
toggleOpen()
|
toggleOpen()
|
||||||
}}
|
}}
|
||||||
title="Open sites"
|
title="More actions"
|
||||||
aria-label={`Open sites for ${apex}`}
|
aria-label={`More actions for ${apex}`}
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
>
|
>
|
||||||
@@ -141,7 +149,7 @@ export function DomainLinksOverflowMenu({ host }: DomainLinksOverflowMenuProps)
|
|||||||
ref={popoverRef}
|
ref={popoverRef}
|
||||||
className={styles.menu}
|
className={styles.menu}
|
||||||
role="menu"
|
role="menu"
|
||||||
aria-label={`Open sites for ${apex}`}
|
aria-label={`More actions for ${apex}`}
|
||||||
style={{
|
style={{
|
||||||
top: popoverPos.top,
|
top: popoverPos.top,
|
||||||
left: popoverPos.left,
|
left: popoverPos.left,
|
||||||
@@ -168,6 +176,19 @@ export function DomainLinksOverflowMenu({ host }: DomainLinksOverflowMenuProps)
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
<div className={styles.menuDivider} role="separator" />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`${styles.item} ${styles.itemDanger}`}
|
||||||
|
role="menuitem"
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false)
|
||||||
|
onRemove()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 size={16} />
|
||||||
|
<span>Remove</span>
|
||||||
|
</button>
|
||||||
</div>,
|
</div>,
|
||||||
document.body,
|
document.body,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -50,11 +50,11 @@
|
|||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.55);
|
border: 1px solid rgba(255, 255, 255, 0.85);
|
||||||
background: rgba(255, 255, 255, 0.28);
|
background: rgba(255, 255, 255, 0.78);
|
||||||
backdrop-filter: blur(48px) saturate(1.2);
|
backdrop-filter: blur(72px) saturate(1.35);
|
||||||
-webkit-backdrop-filter: blur(48px) saturate(1.2);
|
-webkit-backdrop-filter: blur(72px) saturate(1.35);
|
||||||
box-shadow: var(--glass-shadow);
|
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.option {
|
.option {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
Rocket,
|
Rocket,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Search,
|
Search,
|
||||||
Trash2,
|
|
||||||
Unlock,
|
Unlock,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
|
import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal'
|
||||||
@@ -590,16 +589,10 @@ export function WebsitesPage() {
|
|||||||
>
|
>
|
||||||
<Pencil size={16} />
|
<Pencil size={16} />
|
||||||
</button>
|
</button>
|
||||||
<DomainLinksOverflowMenu host={domain.host} />
|
<DomainLinksOverflowMenu
|
||||||
<button
|
host={domain.host}
|
||||||
type="button"
|
onRemove={() => setRemoveTarget(domain)}
|
||||||
className={`${tableStyles.controlBtn} ${tableStyles.controlBtnDanger}`}
|
/>
|
||||||
onClick={() => setRemoveTarget(domain)}
|
|
||||||
title="Remove"
|
|
||||||
aria-label="Remove"
|
|
||||||
>
|
|
||||||
<Trash2 size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user