Duffer Derek

Current Path : /var/www/uibuilder.cmshelp.dk/httpdocs/src/app/_components/core/Modal/
Upload File :
Current File : /var/www/uibuilder.cmshelp.dk/httpdocs/src/app/_components/core/Modal/GenericForm.tsx

'use client';
import React, { useState, ChangeEvent, FormEvent, useEffect } from 'react';
import mediaService from '../../services/mediaService';
import { X, ChevronRight, Folder } from 'lucide-react';

interface GenericFormProps {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  initialProperties: { [key: string]: any };
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  onSave: (properties: { [key: string]: any }) => void;
}

const GenericForm: React.FC<GenericFormProps> = ({ initialProperties, onSave }) => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const [properties, setProperties] = useState<{ [key: string]: any }>(initialProperties);
  const [isMediaModalOpen, setIsMediaModalOpen] = useState(false);
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const [mediaItems, setMediaItems] = useState<any[]>([]);
  const [selectedImageKey, setSelectedImageKey] = useState<string | null>(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [isFolderModalOpen, setIsFolderModalOpen] = useState(false);
  const [breadcrumb, setBreadcrumb] = useState<{ id: string | null, name: string }[]>([{ id: null, name: 'Root' }]);
  const fileInputRef = React.useRef<HTMLInputElement>(null);
  const [folderName, setFolderName] = useState('');
  const [fileName, setFileName] = useState('');
  const [currentFolder, setCurrentFolder] = useState<string | null>(null);

  useEffect(() => {
    if (isMediaModalOpen) {
      fetchMedia();
    }
  }, [isMediaModalOpen]);

  const fetchMedia = async (folderId: string | null = null) => {
    try {
      const media = await mediaService.fetchMedia(folderId);
      setMediaItems(media);
    } catch (error) {
      console.error('Failed to fetch media', error);
    }
  };

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const handleChange = (key: string, value: any) => {
    setProperties({ ...properties, [key]: value });
  };

  const handleNestedChange = (key: string, index: number, nestedKey: string, value: string) => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const updatedArray = properties[key].map((item: any, i: number) =>
      i === index ? { ...item, [nestedKey]: value } : item
    );
    setProperties({ ...properties, [key]: updatedArray });
  };

  const handleAddItem = (key: string) => {
    const newItem = properties[key].length > 0 ? { ...properties[key][0] } : { text: "", href: "" };
    setProperties({ ...properties, [key]: [...(properties[key] || []), newItem] });
  };

  const handleDeleteItem = (key: string, index: number) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const updatedArray = properties[key].filter((_: any, i: number) => i !== index);
    setProperties({ ...properties, [key]: updatedArray });
  };

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    onSave(properties);
  };

  const handleImageClick = (key: string) => {
    setSelectedImageKey(key);
    setIsMediaModalOpen(true);
  };

  const handleImageSelect = (url: string, id: string) => {
    if (selectedImageKey) {
      handleChange(selectedImageKey, { url, id });
    }
    setIsMediaModalOpen(false);
  };

  const handleAddMediaClick = () => {
    setIsModalOpen(true);
  };

  const handleCreateFolderClick = () => {
    setIsFolderModalOpen(true);
  };

  const handleModalClose = () => {
    setIsModalOpen(false);
  };

  const handleFolderModalClose = () => {
    setIsFolderModalOpen(false);
  };

  const handleDrop = (event: React.DragEvent) => {
    event.preventDefault();
    const files = event.dataTransfer.files;
    if (fileInputRef.current) {
      fileInputRef.current.files = files;
    }
  };

  const handleSave = async () => {
    if (fileInputRef.current && fileInputRef.current.files && fileInputRef.current.files.length > 0) {
      try {
        await mediaService.uploadMedia(currentFolder, fileInputRef.current.files[0], fileName);
        fetchMedia(currentFolder);
      } catch (error) {
        console.error('Failed to upload media', error);
      }
    }
    setIsModalOpen(false);
  };

  const handleCreateFolder = async () => {
    try {
      await mediaService.createFolder(folderName, currentFolder);
      fetchMedia(currentFolder);
    } catch (error) {
      console.error('Failed to create folder', error);
    }
    setFolderName('');
    setIsFolderModalOpen(false);
  };

  const handleFolderClick = async (folderId: string | null, folderName: string) => {
    setCurrentFolder(folderId);
    await fetchMedia(folderId);
    setBreadcrumb(prev => {
      const existingIndex = prev.findIndex(crumb => crumb.id === folderId);
      if (existingIndex !== -1) {
        return prev.slice(0, existingIndex + 1);
      }
      return [...prev, { id: folderId, name: folderName }];
    });
  };

  const handleBreadcrumbClick = async (index: number, event: React.MouseEvent) => {
    event.preventDefault();
    const newBreadcrumb = breadcrumb.slice(0, index + 1);
    const folderId = newBreadcrumb[newBreadcrumb.length - 1].id;
    setBreadcrumb(newBreadcrumb);
    setCurrentFolder(folderId);
    await fetchMedia(folderId);
  };

  const handleRemoveImage = (key: string) => {
    handleChange(key, { url: null, id: null });
  };
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const renderMediaItems = (items: any[], size: 'small' | 'large' = 'large') => {
    const folders = items.filter(media => media.type === 'Folder');
    const files = items.filter(media => media.type !== 'Folder');

    return (
      <>
        {folders.map(media => (
          <div key={media.id} className="mb-4">
            <div 
              onClick={() => handleFolderClick(media.id, media.name)}
              className="p-2 border border-gray-300 rounded mt-2 cursor-pointer flex items-center"
            >
              <Folder size={20} className="mr-2" />
              {media.name}
            </div>
          </div>
        ))}
        <div className="flex flex-wrap">
          {files.map(media => (
            <div key={media.id} className="mb-4 p-2 cursor-pointer" style={{ width: size === 'small' ? '10%' : '100%' }} onClick={() => handleImageSelect(media.urlInfos[0]?.url, media.id)}>
              <div className="border border-gray-300 rounded mt-2 cursor-pointer">
                {  // eslint-disable-next-line @typescript-eslint/no-explicit-any
                media.urlInfos.map((urlInfo: any, index: number) => (
                  <img 
                    key={index} 
                    src={urlInfo.url} 
                    alt="Media" 
                    className={`w-${size === 'small' ? '48' : 'full'} h-${size === 'small' ? '48' : 'auto'}`} 
                  />
                ))}
                <div className="text-center">{media.name}</div>
              </div>
            </div>
          ))}
        </div>
      </>
    );
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      {Object.keys(properties).map((key) => (
        <div key={key} className="mb-4">
          <label className="block text-sm font-medium text-gray-700">{key}</label>
          {Array.isArray(properties[key]) ? (
            <>
              {  // eslint-disable-next-line @typescript-eslint/no-explicit-any
              properties[key].map((item: any, index: number) => (
                <div key={index} className="mb-2">
                  {Object.keys(item).map((nestedKey) => (
                    <div key={nestedKey} className="mb-2">
                      <label className="block text-xs font-medium text-gray-500">{nestedKey}</label>
                      <input
                        type="text"
                        value={item[nestedKey] || ""}
                        onChange={(e: ChangeEvent<HTMLInputElement>) => handleNestedChange(key, index, nestedKey, e.target.value)}
                        className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
                      />
                    </div>
                  ))}
                  <button type="button" onClick={() => handleDeleteItem(key, index)} className="mt-2 text-red-500">- Remove Item</button>
                </div>
              ))}
              <button type="button" onClick={() => handleAddItem(key)} className="mt-2 text-blue-500">+ Add Item</button>
            </>
          ) : (
            <>
              {key === 'image' ? (
                <>
                  <button type="button" onClick={() => handleImageClick(key)} className="mt-2 text-blue-500">Select Image</button>
                  {properties[key]?.url && (
                    <>
                      <img src={properties[key].url} alt="Selected" className="mt-2 w-full h-[100] w-[100]" />
                      <button type="button" onClick={() => handleRemoveImage(key)} className="mt-2 text-red-500">Remove Image</button>
                    </>
                  )}
                </>
              ) : (
                <input
                  type="text"
                  value={properties[key] || ""}
                  onChange={(e: ChangeEvent<HTMLInputElement>) => handleChange(key, e.target.value)}
                  className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
                />
              )}
            </>
          )}
        </div>
      ))}
      <div className="flex justify-end">
        <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">Save</button>
      </div>

      {isMediaModalOpen && (
        <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]">
          <div className="bg-white p-4 rounded w-[90%] h-[90%] overflow-y-auto mt-[55px]">
            <div className="flex items-center justify-between mb-4">
              <h2 className="font-medium">Media</h2>
              {breadcrumb.length > 0 && (
                <div className="mb-4 flex items-center">
                  {breadcrumb.map((crumb, index) => (
                    <React.Fragment key={crumb.id}>
                      <button 
                        onClick={(event) => handleBreadcrumbClick(index, event)}
                        className="text-blue-500 hover:underline"
                      >
                        {crumb.name}
                      </button>
                      {index < breadcrumb.length - 1 && (
                        <ChevronRight size={16} className="mx-2 text-gray-500" />
                      )}
                    </React.Fragment>
                  ))}
                </div>
              )}
              <button 
                onClick={() => setIsMediaModalOpen(false)}
                className="p-1 hover:bg-gray-200 rounded-full transition-colors"
              >
                <X size={20} className="text-gray-500" />
              </button>
            </div>
            <button 
                onClick={handleAddMediaClick}
                className="mb-4 p-2 bg-blue-500 text-white rounded mr-1"
              >
                Add Media
              </button>
              <button 
                onClick={handleCreateFolderClick}
                className="mb-4 p-2 bg-green-500 text-white rounded"
              >
                Create Folder
              </button>
            {renderMediaItems(mediaItems, 'small')}
          </div>
        </div>
      )}

      {/* Modal for Adding Media */}
      {isModalOpen && (
        <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]">
          <div className="bg-white p-4 rounded">
            <h2 className="font-medium mb-4">Add Media</h2>
            <div 
              onDrop={handleDrop}
              onDragOver={(event) => event.preventDefault()}
              className="border-dashed border-2 border-gray-300 p-4 mb-4"
            >
              Drag and drop files here
            </div>
            <input 
              type="file"
              ref={fileInputRef}
              className="mb-4"
            />
            <input 
              type="text"
              value={fileName}
              onChange={(e) => setFileName(e.target.value)}
              placeholder="File Name"
              className="mb-4 p-2 border border-gray-300 rounded w-full"
            />
            <div className="flex justify-end space-x-2">
              <button 
                onClick={handleModalClose}
                className="p-2 bg-gray-500 text-white rounded"
              >
                Close
              </button>
              <button 
                onClick={handleSave}
                className="p-2 bg-blue-500 text-white rounded"
              >
                Save
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Modal for Creating Folder */}
      {isFolderModalOpen && (
        <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]">
          <div className="bg-white p-4 rounded">
            <h2 className="font-medium mb-4">Create Folder</h2>
            <div className="mb-4">
              <input 
                type="text"
                value={folderName}
                onChange={(e) => setFolderName(e.target.value)}
                placeholder="Folder Name"
                className="p-2 border border-gray-300 rounded w-full"
              />
              <button 
                onClick={handleCreateFolder}
                className="mt-2 p-2 bg-green-500 text-white rounded w-full"
              >
                Create Folder
              </button>
            </div>
            <div className="flex justify-end space-x-2">
              <button 
                onClick={handleFolderModalClose}
                className="p-2 bg-gray-500 text-white rounded"
              >
                Close
              </button>
            </div>
          </div>
        </div>
      )}
    </form>
  );
};

export default GenericForm;

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists