Files
dyolink/frontend/src/components/ui/organizations/OrganizationSelectorContent.tsx

187 lines
6.4 KiB
TypeScript

'use client';
import { useMemo, useState } from 'react';
import { useAuth } from '@/lib/hooks/useAuth';
import { canCreateOrganizationFromCurrentOrg } from '@/components/shared/permissions';
import { Building2, Mail } from 'lucide-react';
import type { Organization } from '@/types/organization';
import { organizationTypeIcon } from '@/components/shared/organizationTypeIcon';
import { Input } from '@/components/ui/shared/Input';
import { Button } from '@/components/ui/shared/Button';
export function OrganizationSelectorContent() {
const {
organizations,
currentOrganization,
selectOrganization,
createOrganization,
isLoading,
error,
clearError,
} = useAuth();
const canCreateOrganization = useMemo(
() => canCreateOrganizationFromCurrentOrg(currentOrganization),
[currentOrganization],
);
const [isCreateOpen, setIsCreateOpen] = useState(false);
const [organizationName, setOrganizationName] = useState('');
const [organizationEmail, setOrganizationEmail] = useState('');
const [organizationType, setOrganizationType] = useState<'CLINIC' | 'LAB'>('CLINIC');
const renderOrgTypeIcon = (type: Organization['type']) => {
const Icon = organizationTypeIcon(type);
return <Icon className="h-8 w-8 icon-flat" />;
};
const handleCreateOrganization = async () => {
try {
clearError();
const createdId = await createOrganization(
organizationName.trim(),
organizationEmail.trim(),
organizationType,
);
setOrganizationName('');
setOrganizationEmail('');
setOrganizationType('CLINIC');
setIsCreateOpen(false);
await selectOrganization(createdId);
} catch {
// handled by auth context
}
};
if (isLoading) {
return <p className="text-text-secondary">Loading...</p>;
}
return (
<div className="space-y-6">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-3xl font-semibold text-text-primary">Organizations</h1>
<p className="text-text-secondary mt-2">
{canCreateOrganization
? 'Select an organization to continue, or create a new one.'
: 'Select an organization to continue.'}
</p>
</div>
{canCreateOrganization && (
<Button
type="button"
variant={isCreateOpen ? 'outline' : 'primary'}
onClick={() => {
clearError();
setIsCreateOpen((prev) => !prev);
}}
>
{isCreateOpen ? 'Cancel' : 'Create Organization'}
</Button>
)}
</div>
{canCreateOrganization && isCreateOpen && (
<div className="surface-card p-6 space-y-4">
<Input
label="Organization name"
value={organizationName}
onChange={(event) => setOrganizationName(event.target.value)}
placeholder="Sunshine Dental Clinic"
icon={<Building2 className="h-5 w-5 icon-flat" />}
/>
<Input
label="Organization email"
value={organizationEmail}
onChange={(event) => setOrganizationEmail(event.target.value)}
placeholder="contact@sunshineclinic.com"
type="email"
icon={<Mail className="h-5 w-5 icon-flat" />}
/>
<div>
<label className="block text-sm font-medium text-text-secondary mb-2">
Organization type
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setOrganizationType('CLINIC')}
className={`p-3 border rounded-[var(--radius-md)] text-sm ${
organizationType === 'CLINIC'
? 'border-primary/60 bg-primary-soft text-text-primary'
: 'border-border text-text-secondary hover:border-border-strong'
}`}
>
Dental Clinic
</button>
<button
type="button"
onClick={() => setOrganizationType('LAB')}
className={`p-3 border rounded-[var(--radius-md)] text-sm ${
organizationType === 'LAB'
? 'border-primary/60 bg-primary-soft text-text-primary'
: 'border-border text-text-secondary hover:border-border-strong'
}`}
>
Dental Lab
</button>
</div>
</div>
{error && (
<div className="p-3 bg-red-950/30 border border-red-600/40 rounded-[var(--radius-md)]">
<p className="text-sm text-red-600">{error}</p>
</div>
)}
<div className="flex justify-end">
<Button
type="button"
variant="primary"
onClick={handleCreateOrganization}
isLoading={isLoading}
disabled={!organizationName.trim() || !organizationEmail.trim()}
>
Create and Continue
</Button>
</div>
</div>
)}
{!organizations.length ? (
<div className="surface-card p-8 text-center">
<p className="text-text-secondary">
{canCreateOrganization
? 'No organizations found. Create your first one to continue.'
: 'No organizations found. Ask an organization owner to invite you.'}
</p>
</div>
) : (
<div className="grid gap-4">
{organizations.map((org) => (
<button
key={org.id}
onClick={() => selectOrganization(org.id)}
className="surface-card p-6 transition-all text-left flex items-center gap-4 hover:border-primary/60"
>
<div className="p-3 bg-primary-soft rounded-[var(--radius-sm)] text-primary">
{renderOrgTypeIcon(org.type)}
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-text-primary">
{org.name}
</h3>
<p className="text-sm text-text-secondary">
{org.type === 'CLINIC' ? 'Dental Clinic' : 'Dental Lab'}
</p>
</div>
<div className="text-primary text-sm">
Continue
</div>
</button>
))}
</div>
)}
</div>
);
}