All files / components/common AddProviderModal.tsx

67.74% Statements 21/31
47.82% Branches 11/23
33.33% Functions 3/9
67.74% Lines 21/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164                        3x                     3x               118x 118x 118x 118x   118x 108x 1x 1x 1x 1x   1x                   1x                 1x     1x 1x       118x           118x                                 118x       118x                                                                                                                                    
import { useState, useEffect } from 'react';
import { adminService, type Configuration, type AvailableProvider } from '@/services/admin.service';
import { AppDialog } from '@/components/common/AppDialog';
import { Button } from '@/components/ui/button';
import { ConfigurationForm } from './ConfigurationForm';
import { Loader2, ArrowLeft, BadgeCheck } from 'lucide-react';
import { ScrollArea } from '@/components/ui/scroll-area';
import { ProviderLogo } from '@/components/common/ProviderLogo';
import { useToast } from '@/hooks/use-toast';
 
 
// List of providers that have been manually tested and verified
const VERIFIED_PROVIDERS = ['google'];
 
interface AddProviderModalProps {
    open: boolean;
    onOpenChange: (open: boolean) => void;
    onConfigCreated: () => void;
    category?: string;
    accountId?: string;
    existingConfigs?: Configuration[];
}
 
export const AddProviderModal = ({
    open,
    onOpenChange,
    onConfigCreated,
    category,
    accountId,
    existingConfigs = [],
}: AddProviderModalProps) => {
    const [selectedProvider, setSelectedProvider] = useState<AvailableProvider | null>(null);
    const [availableProviders, setAvailableProviders] = useState<AvailableProvider[]>([]);
    const [isLoading, setIsLoading] = useState(false);
    const { toast } = useToast();
 
    useEffect(() => {
        if (open) {
            const loadProviders = async () => {
                setIsLoading(true);
                try {
                    const data = await adminService.getAvailableProviders(category);
                    // Filter out already configured providers and explicitly exclude email_password
                    const filtered = data.filter(
                        (p: AvailableProvider) =>
                            p.provider_name !== "email_password" &&
                            !existingConfigs.some((ec) => ec.provider_name === p.provider_name) &&
                            // System settings are only applicable to system level (no accountId)
                            !(p.category === 'system_settings' && accountId) &&
                            // Storage providers are system-level only
                            !(p.category === 'storage_providers' && accountId)
                    );
 
                    setAvailableProviders(filtered);
                } catch (error) {
                    console.error("Failed to load providers", error);
                    toast({
                        title: "Error",
                        description: "Failed to load available providers.",
                        variant: "destructive",
                    });
                } finally {
                    setIsLoading(false);
                }
            };
            loadProviders();
            setSelectedProvider(null);
        }
    }, [open, category, accountId, existingConfigs, toast]);
 
    const handleSuccess = () => {
        onConfigCreated();
        onOpenChange(false);
    };
 
    const title = (
        <div className="flex items-center gap-2">
            {selectedProvider && (
                <Button
                    variant="ghost"
                    size="icon"
                    className="h-8 w-8"
                    onClick={() => setSelectedProvider(null)}
                >
                    <ArrowLeft className="h-4 w-4" />
                </Button>
            )}
            {selectedProvider
                ? `Configure ${selectedProvider.display_name}`
                : 'Add Provider'}
        </div>
    );
 
    const description = selectedProvider
        ? `Fill in the details to set up ${selectedProvider.display_name}.`
        : 'Select a provider to add to your configuration.';
 
    return (
        <AppDialog
            open={open}
            onOpenChange={onOpenChange}
            title={title}
            description={description}
            className="sm:max-w-xl"
            bodyClassName="p-5 overflow-hidden flex flex-col"
        >
            <div className="flex-1 overflow-hidden h-full flex flex-col">
                {isLoading ? (
                    <div className="flex items-center justify-center p-8">
                        <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
                    </div>
                ) : selectedProvider ? (
                    <ConfigurationForm
                        category={selectedProvider.category}
                        providerName={selectedProvider.provider_name}
                        displayName={selectedProvider.display_name}
                        logoUrl={selectedProvider.logo_url}
                        accountId={accountId}
                        onSuccess={handleSuccess}
                        onCancel={() => setSelectedProvider(null)}
                    />
                ) : (
                    <ScrollArea className="flex-1">
                        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 py-2">
                            {availableProviders.map((p) => {
                                const isVerified = VERIFIED_PROVIDERS.includes(p.provider_name);
                                return (
                                    <Button
                                        key={`${p.category}:${p.provider_name}`}
                                        variant="outline"
                                        className="h-24 flex flex-col items-center justify-center gap-2 hover:border-primary hover:bg-primary/5 transition-all text-center p-4 relative"
                                        onClick={() => setSelectedProvider(p)}
                                    >
                                        {isVerified && (
                                            <div className="absolute top-2 right-2">
                                                <BadgeCheck className="h-4 w-4 text-green-600 dark:text-green-500" />
                                            </div>
                                        )}
                                        <ProviderLogo
                                            logoUrl={p.logo_url}
                                            providerName={p.provider_name}
                                            className="h-8 w-8"
                                            size={32}
                                        />
                                        <div className="flex flex-col">
                                            <span className="font-medium text-sm line-clamp-1">{p.display_name}</span>
                                            <span className="text-[10px] text-muted-foreground uppercase">{p.category.replace('_', ' ')}</span>
                                        </div>
                                    </Button>
                                );
                            })}
                            {availableProviders.length === 0 && (
                                <div className="col-span-full py-12 text-center text-muted-foreground">
                                    No providers available for this category.
                                </div>
                            )}
                        </div>
                    </ScrollArea>
                )}
            </div>
        </AppDialog>
    );
};