From db52a83f983c5a1b39913287e9d543853a5bd68e Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Thu, 23 Jul 2026 12:23:02 +0330 Subject: [PATCH] Improve portfolio title images, branding, and super-admin domains UI. Add aspect-ratio crop presets for portfolio title images, fixed card image placeholders with contained centering, Meshkee favicon/logo fallbacks, login branding updates, category FAB alignment, and super-admin website/domain SSL tooling. Co-authored-by: Cursor --- apps/business/index.html | 1 + apps/business/public/favicon.png | Bin 0 -> 4136 bytes apps/business/public/favicon.svg | 1 - apps/business/public/meshkee-logo.png | Bin 3070 -> 11859 bytes apps/business/src/assets/meshkee-logo.png | Bin 3070 -> 11859 bytes .../src/components/ImageCropper.module.css | 17 ++ apps/business/src/components/ImageCropper.tsx | 45 ++++- .../src/components/PortfolioCard.module.css | 7 +- .../src/context/TenantBrandingContext.tsx | 10 +- .../src/pages/AddNewPortfolioPage.tsx | 139 ++++++++++----- .../src/pages/AddNewProductPage.module.css | 46 ++++- .../business/src/pages/BlogCategoriesPage.tsx | 19 +- .../src/pages/CategoriesPage.module.css | 42 +++++ apps/business/src/pages/CategoriesPage.tsx | 5 +- apps/business/src/pages/LoginPage.module.css | 9 +- apps/business/src/pages/LoginPage.tsx | 10 +- .../src/pages/PortfolioCategoriesPage.tsx | 19 +- .../src/pages/PortfolioDetailsPage.module.css | 9 +- .../src/pages/PortfolioDetailsPage.tsx | 6 +- apps/customer/index.html | 1 + apps/customer/public/favicon.png | Bin 0 -> 4136 bytes apps/customer/public/favicon.svg | 1 - apps/customer/src/assets/meshkee-logo.png | Bin 3070 -> 11859 bytes apps/customer/src/components/Sidebar.tsx | 20 +-- .../components/checkout/CheckoutLayout.tsx | 20 +-- .../src/context/TenantBrandingContext.tsx | 13 +- apps/customer/src/pages/LoginPage.tsx | 10 +- apps/super-admin/index.html | 2 +- apps/super-admin/public/favicon.png | Bin 0 -> 4136 bytes apps/super-admin/public/favicon.svg | 1 - apps/super-admin/src/assets/meshkee-logo.png | Bin 3070 -> 11859 bytes .../src/pages/BusinessesPage.module.css | 18 +- apps/super-admin/src/pages/BusinessesPage.tsx | 22 ++- .../src/pages/WebsitesPage.module.css | 38 ++++ apps/super-admin/src/pages/WebsitesPage.tsx | 164 +++++++++++++++++- .../super-admin/src/services/domainService.ts | 9 +- apps/super-admin/src/types/domain.ts | 15 ++ assets/MeshkeeLogo.png | Bin 0 -> 10541 bytes assets/favicon.png | Bin 0 -> 2023 bytes packages/dashboard-core/src/utils/favicon.ts | 38 ++-- 40 files changed, 595 insertions(+), 162 deletions(-) create mode 100755 apps/business/public/favicon.png delete mode 100644 apps/business/public/favicon.svg mode change 100644 => 100755 apps/business/public/meshkee-logo.png mode change 100644 => 100755 apps/business/src/assets/meshkee-logo.png create mode 100755 apps/customer/public/favicon.png delete mode 100644 apps/customer/public/favicon.svg mode change 100644 => 100755 apps/customer/src/assets/meshkee-logo.png create mode 100755 apps/super-admin/public/favicon.png delete mode 100644 apps/super-admin/public/favicon.svg mode change 100644 => 100755 apps/super-admin/src/assets/meshkee-logo.png create mode 100755 assets/MeshkeeLogo.png create mode 100755 assets/favicon.png diff --git a/apps/business/index.html b/apps/business/index.html index b71258d..715411a 100644 --- a/apps/business/index.html +++ b/apps/business/index.html @@ -2,6 +2,7 @@ + diff --git a/apps/business/public/favicon.png b/apps/business/public/favicon.png new file mode 100755 index 0000000000000000000000000000000000000000..f80989a0c09740c506bdcff8f04e8c66109fdc4d GIT binary patch literal 4136 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 \ No newline at end of file diff --git a/apps/business/public/meshkee-logo.png b/apps/business/public/meshkee-logo.png old mode 100644 new mode 100755 index 9d12a5b27f3d8bf34d79fbd8cbb3317ea56c2620..9b7eaf115811a50ff1525215989153bf3046853c GIT binary patch literal 11859 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 J`K5th{ws4?{O|w( literal 3070 zcmeHJX;jl!7JorxQIX9ShNUoY6jQb$VqjQ8HL^`vj0zM1qtgz1!T=Hw5^_e72n7a> zT4YmW0F@;yVI31zBPyvOEE3j02qKWM$FRXj)0=T?b%qr#tG)GYS0pk>)4g`sueUjs+wY zu^nS2!qKk7UV7wenVv5W}-T(j0|ad1O#C*TKWvJfxY}z z4TjOTnO$#HpqBsOX%Q@oHD7KgA9dLFzMY75$riOfn|`_Z)|(Mu&@NB9#H*N0d(lnT z|E``Dw2mcK3k=kAt}!f%ue1u!g-yR350yTIT0_6i?nMbg(WVu3B)YsfOI((_{VtK7e)pVh4=cgIyvl+_mv)~I9kzp z%dLr90TGj?^7I=#RxzcHFA$yP;y2g@+l=BcUfrGjV}!}YLBUYUC*Y+4(9KgN#!H3g zc6KBU?j@rrfcd(&l<~@V;$h8&;EnCot)WW7{jxy|=jKAzTYKxxF_sBEUOi}W3TAt! z=EC=P*SM4zU3ED_&PeKwuFpaK*vJ)zgK}-X3Yd=eg~04%14s~t-!vN%;H37cP@MYQDLO&vgV`n7FItL~qv#&-CEOs@{N=--s)V%pEn&i_;mxqxi zfMor5%|jlD=3oz`CNRAxDRHf#;tM_P>eV|f04U+oD1VBNo^@rIU53$hB*|pHB{sQX z9E_harb)1=5de(qF+F_1`&0-WKf0v$WT_%);!OQDO4tD{ZMk9g={I^gX@AZI4R@re zB8r0M6UFG1z8+Uf_0E_3zMGHSOKQlFUacdr5}xFZY=@ao`IZTI_kr()wGv9b0<}A$ z8?)q}=@ecCHwEH}1t=4JGfBAQD66p)>&Ln(KwQ!}*OuXF-=qPT?6VA958_-&qxN4k zaRwa)Lu=kqGaL9~MH~TdHI&4=0nA!+2RFy#5y*xDwAYnIPWSUg`dMW>Z1@!Xfy)m6 zVv`h~0i(xR&RwVUbR1VAHP~zAQBI;(T@R5CCTk@>8bJn84gz57x6JB@+(9BM7+-ZC zgMk)1bkpDvqvL*F4V0B>aeQEu9tp# z0t_cI&d(J;Z;y{#OoP|=t73lV^%|YU6`2qV(4V=Ys*L2uv5~tX2Z&WjZJd*h_W8{R zyJ7z6a&C5vY+I=okWPf^7P}eG0=j&7X29(IR9j}hM!q{v8Hgvt=iYKlG_jfC z`J%vZkX9wN8!+2Ye9rXVZ5Y|wX|+7VCRrHKd<}Os674mV_>$2m(tP$Nb zbFpqGuQg#dH(;H|+Ix>8MceZWC9F$PP|-X%W=J}VB^`>2bX?&tv~gnDgvYonhZ&Kg zWn9fuRw8>b{W;Y8;DTf_Ol_k110gxC?KPZ7QJAc-UvKA%Gfkj=N;nI--EJ{;O!Zh@ zksCa!gYD;>>9z$Gs+ci-ePz-oN3@X(&qcH!HmNH$?PQ1Rnt{l_q3r|B_NO36p31hE zh)tM3GGYTiAFw_`8Nv?uk6H0)3C`-XrF#G&bpuULRB2c{PiEWmYN0t diff --git a/apps/business/src/assets/meshkee-logo.png b/apps/business/src/assets/meshkee-logo.png old mode 100644 new mode 100755 index 9d12a5b27f3d8bf34d79fbd8cbb3317ea56c2620..9b7eaf115811a50ff1525215989153bf3046853c GIT binary patch literal 11859 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 J`K5th{ws4?{O|w( literal 3070 zcmeHJX;jl!7JorxQIX9ShNUoY6jQb$VqjQ8HL^`vj0zM1qtgz1!T=Hw5^_e72n7a> zT4YmW0F@;yVI31zBPyvOEE3j02qKWM$FRXj)0=T?b%qr#tG)GYS0pk>)4g`sueUjs+wY zu^nS2!qKk7UV7wenVv5W}-T(j0|ad1O#C*TKWvJfxY}z z4TjOTnO$#HpqBsOX%Q@oHD7KgA9dLFzMY75$riOfn|`_Z)|(Mu&@NB9#H*N0d(lnT z|E``Dw2mcK3k=kAt}!f%ue1u!g-yR350yTIT0_6i?nMbg(WVu3B)YsfOI((_{VtK7e)pVh4=cgIyvl+_mv)~I9kzp z%dLr90TGj?^7I=#RxzcHFA$yP;y2g@+l=BcUfrGjV}!}YLBUYUC*Y+4(9KgN#!H3g zc6KBU?j@rrfcd(&l<~@V;$h8&;EnCot)WW7{jxy|=jKAzTYKxxF_sBEUOi}W3TAt! z=EC=P*SM4zU3ED_&PeKwuFpaK*vJ)zgK}-X3Yd=eg~04%14s~t-!vN%;H37cP@MYQDLO&vgV`n7FItL~qv#&-CEOs@{N=--s)V%pEn&i_;mxqxi zfMor5%|jlD=3oz`CNRAxDRHf#;tM_P>eV|f04U+oD1VBNo^@rIU53$hB*|pHB{sQX z9E_harb)1=5de(qF+F_1`&0-WKf0v$WT_%);!OQDO4tD{ZMk9g={I^gX@AZI4R@re zB8r0M6UFG1z8+Uf_0E_3zMGHSOKQlFUacdr5}xFZY=@ao`IZTI_kr()wGv9b0<}A$ z8?)q}=@ecCHwEH}1t=4JGfBAQD66p)>&Ln(KwQ!}*OuXF-=qPT?6VA958_-&qxN4k zaRwa)Lu=kqGaL9~MH~TdHI&4=0nA!+2RFy#5y*xDwAYnIPWSUg`dMW>Z1@!Xfy)m6 zVv`h~0i(xR&RwVUbR1VAHP~zAQBI;(T@R5CCTk@>8bJn84gz57x6JB@+(9BM7+-ZC zgMk)1bkpDvqvL*F4V0B>aeQEu9tp# z0t_cI&d(J;Z;y{#OoP|=t73lV^%|YU6`2qV(4V=Ys*L2uv5~tX2Z&WjZJd*h_W8{R zyJ7z6a&C5vY+I=okWPf^7P}eG0=j&7X29(IR9j}hM!q{v8Hgvt=iYKlG_jfC z`J%vZkX9wN8!+2Ye9rXVZ5Y|wX|+7VCRrHKd<}Os674mV_>$2m(tP$Nb zbFpqGuQg#dH(;H|+Ix>8MceZWC9F$PP|-X%W=J}VB^`>2bX?&tv~gnDgvYonhZ&Kg zWn9fuRw8>b{W;Y8;DTf_Ol_k110gxC?KPZ7QJAc-UvKA%Gfkj=N;nI--EJ{;O!Zh@ zksCa!gYD;>>9z$Gs+ci-ePz-oN3@X(&qcH!HmNH$?PQ1Rnt{l_q3r|B_NO36p31hE zh)tM3GGYTiAFw_`8Nv?uk6H0)3C`-XrF#G&bpuULRB2c{PiEWmYN0t diff --git a/apps/business/src/components/ImageCropper.module.css b/apps/business/src/components/ImageCropper.module.css index 9bdb967..acbda89 100644 --- a/apps/business/src/components/ImageCropper.module.css +++ b/apps/business/src/components/ImageCropper.module.css @@ -55,6 +55,12 @@ object-fit: cover; } +.previewImgNatural { + width: 100%; + height: auto; + display: block; +} + .removeBtn { position: absolute; top: 8px; @@ -85,9 +91,15 @@ position: relative; width: 100%; min-height: 160px; + max-height: 420px; background: #1e293b; } +.cropAreaPortrait { + max-height: none; + min-height: 0; +} + .cropControls { padding: 14px 16px; display: flex; @@ -141,6 +153,11 @@ background: var(--primary-dark); } +.applyBtn:disabled { + opacity: 0.55; + cursor: not-allowed; +} + .changeBtn { display: inline-flex; align-self: flex-start; diff --git a/apps/business/src/components/ImageCropper.tsx b/apps/business/src/components/ImageCropper.tsx index 940b999..1a6dbb2 100644 --- a/apps/business/src/components/ImageCropper.tsx +++ b/apps/business/src/components/ImageCropper.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import Cropper, { type Area } from 'react-easy-crop' import { ImagePlus, X } from 'lucide-react' import { getCroppedImage } from '../utils/cropImage' @@ -7,6 +7,7 @@ import styles from './ImageCropper.module.css' interface ImageCropperProps { value: string | null onChange: (value: string | null) => void + /** Crop aspect ratio. Defaults to square (1). */ aspect?: number outputFormat?: 'jpeg' | 'png' accept?: string @@ -30,6 +31,12 @@ export function ImageCropper({ const [zoom, setZoom] = useState(1) const [croppedArea, setCroppedArea] = useState(null) + useEffect(() => { + setCrop({ x: 0, y: 0 }) + setZoom(1) + setCroppedArea(null) + }, [aspect]) + const onCropComplete = useCallback((_: Area, pixels: Area) => { setCroppedArea(pixels) }, []) @@ -38,7 +45,12 @@ export function ImageCropper({ const file = e.target.files?.[0] if (!file) return const reader = new FileReader() - reader.onload = () => setImageSrc(reader.result as string) + reader.onload = () => { + setImageSrc(reader.result as string) + setCrop({ x: 0, y: 0 }) + setZoom(1) + setCroppedArea(null) + } reader.readAsDataURL(file) e.target.value = '' } @@ -62,13 +74,26 @@ export function ImageCropper({ onChange(null) } - const frameStyle = { aspectRatio: `${aspect}` as const } + const isPortrait = aspect < 1 + const frameStyle: React.CSSProperties = isPortrait + ? { + aspectRatio: `${aspect}`, + height: 'min(480px, 65vh)', + width: 'auto', + maxWidth: '100%', + marginInline: 'auto', + } + : { + aspectRatio: `${aspect}`, + width: '100%', + maxHeight: 'min(420px, 55vh)', + } return (
{value && !imageSrc && ( -
- Thumbnail preview +
+ Thumbnail preview @@ -86,8 +111,9 @@ export function ImageCropper({ {imageSrc && (
-
+
Cancel -
diff --git a/apps/business/src/components/PortfolioCard.module.css b/apps/business/src/components/PortfolioCard.module.css index 5d2fda5..9a44ea6 100644 --- a/apps/business/src/components/PortfolioCard.module.css +++ b/apps/business/src/components/PortfolioCard.module.css @@ -34,14 +34,17 @@ width: 100%; aspect-ratio: 3 / 2; overflow: hidden; - background: rgba(148, 163, 184, 0.08); + background: transparent; border-bottom: 1px solid rgba(148, 163, 184, 0.12); } .image { + position: relative; + z-index: 1; width: 100%; height: 100%; - object-fit: cover; + object-fit: contain; + display: block; } .imagePlaceholder { diff --git a/apps/business/src/context/TenantBrandingContext.tsx b/apps/business/src/context/TenantBrandingContext.tsx index 922b900..63e8d0e 100644 --- a/apps/business/src/context/TenantBrandingContext.tsx +++ b/apps/business/src/context/TenantBrandingContext.tsx @@ -16,6 +16,7 @@ import { resolveTenantByDomain } from '../services/tenantService' interface TenantBrandingContextValue { businessName: string + logoUrl: string | null faviconUrl: string | null refreshBranding: () => void } @@ -34,6 +35,7 @@ function pickBusinessName( export function TenantBrandingProvider({ children }: { children: ReactNode }) { const [businessName, setBusinessName] = useState('') + const [logoUrl, setLogoUrl] = useState(null) const [faviconUrl, setFaviconUrl] = useState(null) const [refreshToken, setRefreshToken] = useState(0) @@ -51,6 +53,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) { if (controller.signal.aborted) return let name = pickBusinessName(tenant.name, tenant.nameFa, domain) + let nextLogo = tenant.logoUrl?.trim() || null let nextFavicon = tenant.faviconUrl?.trim() || tenant.logoUrl?.trim() || null @@ -63,6 +66,7 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) { name, domain, ) + nextLogo = profile.profile.logoUrl?.trim() || nextLogo nextFavicon = profile.profile.faviconUrl?.trim() || profile.profile.logoUrl?.trim() || @@ -76,12 +80,14 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) { if (!controller.signal.aborted) { setBusinessName(name || domain) + setLogoUrl(nextLogo) setFaviconUrl(nextFavicon) applyDocumentFavicon(nextFavicon) } } catch (err) { if (isAbortError(err) || controller.signal.aborted) return setBusinessName(domain) + setLogoUrl(null) setFaviconUrl(null) applyDocumentFavicon(null) } @@ -101,8 +107,8 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) { }, [refreshToken, refreshBranding]) const value = useMemo( - () => ({ businessName, faviconUrl, refreshBranding }), - [businessName, faviconUrl, refreshBranding], + () => ({ businessName, logoUrl, faviconUrl, refreshBranding }), + [businessName, logoUrl, faviconUrl, refreshBranding], ) return ( diff --git a/apps/business/src/pages/AddNewPortfolioPage.tsx b/apps/business/src/pages/AddNewPortfolioPage.tsx index 2373a26..a598692 100644 --- a/apps/business/src/pages/AddNewPortfolioPage.tsx +++ b/apps/business/src/pages/AddNewPortfolioPage.tsx @@ -28,6 +28,19 @@ import { flattenCategories } from '../utils/categories' import pageStyles from '../components/PageContent.module.css' import styles from './AddNewProductPage.module.css' +type TitleImageAspectId = 'square' | '3:2' | '16:9' | '9:16' + +const TITLE_IMAGE_ASPECTS: { + id: TitleImageAspectId + label: string + aspect: number +}[] = [ + { id: 'square', label: 'Square', aspect: 1 }, + { id: '3:2', label: '3:2', aspect: 3 / 2 }, + { id: '16:9', label: '16:9', aspect: 16 / 9 }, + { id: '9:16', label: '9:16 (Reel)', aspect: 9 / 16 }, +] + export function AddNewPortfolioPage() { const { id } = useParams() const navigate = useNavigate() @@ -40,6 +53,7 @@ export function AddNewPortfolioPage() { const [abstract, setAbstract] = useState('') const [mainTextHtml, setMainTextHtml] = useState('') const [titleImage, setTitleImage] = useState(null) + const [titleImageAspect, setTitleImageAspect] = useState('3:2') const [images, setImages] = useState([]) const [tags, setTags] = useState([]) const [featuredMediaId, setFeaturedMediaId] = useState(null) @@ -49,6 +63,8 @@ export function AddNewPortfolioPage() { const [error, setError] = useState('') const categoryOptions = flattenCategories(categories) + const selectedAspect = + TITLE_IMAGE_ASPECTS.find((option) => option.id === titleImageAspect)?.aspect ?? 3 / 2 useEffect(() => { const controller = new AbortController() @@ -199,65 +215,96 @@ export function AddNewPortfolioPage() {
-
+
+
+ {TITLE_IMAGE_ASPECTS.map((option) => ( + + ))} +
-
- - -
+
+
+ + +
-
- - setTitle(e.target.value)} - required - disabled={isSubmitting} - /> -
+
+ + setTitle(e.target.value)} + required + disabled={isSubmitting} + /> +
-
- -