mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
179 lines
7.0 KiB
TypeScript
179 lines
7.0 KiB
TypeScript
import React, { useCallback, useState } from 'react';
|
|
import { getSectorDisplay } from '@/constants.tsx';
|
|
import { useTranslation } from '@/hooks/useI18n.tsx';
|
|
import { useToggle } from '@/hooks/useToggle';
|
|
import { getTranslatedSectorName, mapBackendSectorToTranslationKey } from '@/lib/sector-mapper.ts';
|
|
import { getOrganizationSubtypeLabel } from '@/schemas/organizationSubtype.ts';
|
|
import { Organization } from '@/types.ts';
|
|
import { Pencil } from 'lucide-react';
|
|
import Badge from '@/components/ui/Badge.tsx';
|
|
import Button from '@/components/ui/Button.tsx';
|
|
import { Card } from '@/components/ui/Card.tsx';
|
|
import ImageGallery from '@/components/ui/ImageGallery.tsx';
|
|
import ImageUpload from '@/components/ui/ImageUpload.tsx';
|
|
import VerifiedBadge from '@/components/ui/VerifiedBadge.tsx';
|
|
|
|
interface OrganizationHeaderProps {
|
|
organization: Organization;
|
|
onUpdateOrganization?: (org: Organization) => void;
|
|
}
|
|
|
|
const OrganizationHeader = ({ organization, onUpdateOrganization }: OrganizationHeaderProps) => {
|
|
const { t } = useTranslation();
|
|
const isEditingLogo = useToggle(false);
|
|
const isEditingGallery = useToggle(false);
|
|
const [logo, setLogo] = useState(organization.LogoURL);
|
|
const [galleryImages, setGalleryImages] = useState<string[]>(organization.GalleryImages || []);
|
|
const sectorDisplay = getSectorDisplay(organization.Sector);
|
|
|
|
const handleSaveLogo = useCallback(() => {
|
|
if (onUpdateOrganization) {
|
|
onUpdateOrganization({ ...organization, LogoURL: logo });
|
|
}
|
|
isEditingLogo.setFalse();
|
|
}, [onUpdateOrganization, organization, logo, isEditingLogo]);
|
|
|
|
const handleCancelLogoEdit = useCallback(() => {
|
|
setLogo(organization.LogoURL);
|
|
isEditingLogo.setFalse();
|
|
}, [organization.LogoURL, isEditingLogo]);
|
|
|
|
const handleSaveGallery = useCallback(() => {
|
|
if (onUpdateOrganization) {
|
|
onUpdateOrganization({ ...organization, GalleryImages: galleryImages });
|
|
}
|
|
isEditingGallery.setFalse();
|
|
}, [onUpdateOrganization, organization, galleryImages, isEditingGallery]);
|
|
|
|
const handleCancelGalleryEdit = useCallback(() => {
|
|
setGalleryImages(organization.GalleryImages || []);
|
|
isEditingGallery.setFalse();
|
|
}, [organization.GalleryImages, isEditingGallery]);
|
|
|
|
return (
|
|
<Card className="overflow-hidden">
|
|
<div className="flex flex-col md:flex-row items-center gap-6 p-6">
|
|
<div className="relative group shrink-0">
|
|
<div className="w-40 h-40 rounded-lg bg-muted flex items-center justify-center mx-auto overflow-hidden border">
|
|
{organization.LogoURL ? (
|
|
<img
|
|
src={organization.LogoURL}
|
|
alt={t('imageUpload.logoAlt', { name: organization.Name })}
|
|
loading="lazy"
|
|
decoding="async"
|
|
onError={(e) => {
|
|
(e.target as HTMLImageElement).style.display = 'none';
|
|
}}
|
|
className="max-w-full max-h-full object-contain"
|
|
/>
|
|
) : (
|
|
React.cloneElement(sectorDisplay.icon, {
|
|
className: 'w-16 h-16 text-muted-foreground',
|
|
})
|
|
)}
|
|
</div>
|
|
{onUpdateOrganization && (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity p-2 h-8 w-8 bg-background/50"
|
|
onClick={isEditingLogo.setTrue}
|
|
aria-label={t('organizationPage.logo.editLabel')}
|
|
>
|
|
<Pencil className="h-4 text-current w-4" />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex-1 text-center md:text-left">
|
|
<h1 className="font-serif text-3xl md:text-4xl font-bold">{organization.Name}</h1>
|
|
<div className="flex items-center justify-center md:justify-start flex-wrap gap-x-2 gap-y-1 mt-2">
|
|
<p className="text-muted-foreground text-lg">
|
|
{getTranslatedSectorName(organization.Sector, t)}
|
|
</p>
|
|
{organization.Subtype && (
|
|
<>
|
|
<span className="text-muted-foreground hidden sm:inline">·</span>
|
|
<Badge variant="secondary" className="text-xs">
|
|
{getOrganizationSubtypeLabel(organization.Subtype)}
|
|
</Badge>
|
|
</>
|
|
)}
|
|
{organization.Verified && (
|
|
<>
|
|
<span className="text-muted-foreground hidden sm:inline">·</span>
|
|
<VerifiedBadge />
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{isEditingLogo.value && onUpdateOrganization && (
|
|
<div className="bg-muted/50 p-4 border-t">
|
|
<div className="max-w-xs mx-auto space-y-2">
|
|
<ImageUpload value={logo} onChange={setLogo} />
|
|
<div className="flex gap-2">
|
|
<Button size="sm" onClick={handleSaveLogo} className="w-full">
|
|
{t('organizationPage.logo.save')}
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={handleCancelLogoEdit} className="w-full">
|
|
{t('organizationPage.logo.cancel')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Gallery Images Section */}
|
|
{(organization.GalleryImages?.length > 0 ||
|
|
(onUpdateOrganization && isEditingGallery.value)) && (
|
|
<Card className="mt-4">
|
|
<div className="p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-lg font-semibold">{t('organizationPage.gallery.title')}</h3>
|
|
{onUpdateOrganization && (
|
|
<Button variant="outline" size="sm" onClick={isEditingGallery.toggle}>
|
|
<Pencil className="h-4 mr-2 text-current w-4" />
|
|
{isEditingGallery.value
|
|
? t('organizationPage.gallery.cancel')
|
|
: t('organizationPage.gallery.edit')}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{isEditingGallery.value && onUpdateOrganization ? (
|
|
<div className="space-y-4">
|
|
<ImageGallery
|
|
images={galleryImages}
|
|
onChange={setGalleryImages}
|
|
maxImages={10}
|
|
editable={true}
|
|
title=""
|
|
/>
|
|
<div className="flex gap-2">
|
|
<Button size="sm" onClick={handleSaveGallery} className="w-full">
|
|
{t('organizationPage.gallery.save')}
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={handleCancelGalleryEdit}
|
|
className="w-full"
|
|
>
|
|
{t('organizationPage.gallery.cancel')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<ImageGallery images={organization.GalleryImages || []} editable={false} title="" />
|
|
)}
|
|
</div>
|
|
</Card>
|
|
)}
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
export default React.memo(OrganizationHeader);
|