turash/bugulma/frontend/components/organization/OrganizationHeader.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

182 lines
7.0 KiB
TypeScript

import React, { useCallback } 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);