- Add comprehensive Company Wiki feature with complete state management - CompanyWikiManager, empty states, invite modals - Implement new Chat system with enhanced layout and components - ChatLayout, ChatSidebar, MessageThread, FileUploadInput - Create modern Login and OTP verification flows - LoginNew page, OTPVerification component - Add new Employee Forms system with enhanced controller - Introduce Figma-based design components and multiple choice inputs - Add new font assets (NeueMontreal) and robot images for onboarding - Enhance existing components with improved styling and functionality - Update build configuration and dependencies - Remove deprecated ModernLogin component
244 lines
9.4 KiB
TypeScript
244 lines
9.4 KiB
TypeScript
import React, { useState, useRef, useCallback } from 'react';
|
|
|
|
interface FileUploadPreviewProps {
|
|
files: string[];
|
|
onRemoveFile: (index: number) => void;
|
|
}
|
|
|
|
const FileUploadPreview: React.FC<FileUploadPreviewProps> = ({ files, onRemoveFile }) => {
|
|
if (files.length === 0) return null;
|
|
|
|
const getFileIcon = (fileName: string) => {
|
|
const extension = fileName.split('.').pop()?.toLowerCase();
|
|
|
|
switch (extension) {
|
|
case 'pdf':
|
|
return (
|
|
<div className="w-6 h-6 bg-red-500 rounded flex items-center justify-center">
|
|
<span className="text-white text-xs font-bold">P</span>
|
|
</div>
|
|
);
|
|
case 'doc':
|
|
case 'docx':
|
|
return (
|
|
<div className="w-6 h-6 bg-blue-500 rounded flex items-center justify-center">
|
|
<span className="text-white text-xs font-bold">W</span>
|
|
</div>
|
|
);
|
|
case 'xls':
|
|
case 'xlsx':
|
|
return (
|
|
<div className="w-6 h-6 bg-green-500 rounded flex items-center justify-center">
|
|
<span className="text-white text-xs font-bold">E</span>
|
|
</div>
|
|
);
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
case 'png':
|
|
case 'gif':
|
|
return (
|
|
<div className="w-6 h-6 bg-purple-500 rounded flex items-center justify-center">
|
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M1 9L3.5 6.5L5 8L8.5 4.5L11 7M1 1H11V11H1V1Z" stroke="white" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</div>
|
|
);
|
|
default:
|
|
return (
|
|
<div className="w-6 h-6 bg-gray-500 rounded flex items-center justify-center">
|
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M7 1H2C1.44772 1 1 1.44772 1 2V10C1 10.5523 1.44772 11 2 11H10C10.5523 11 11 10.5523 11 10V5M7 1L11 5M7 1V5H11" stroke="white" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="mb-3 flex flex-wrap gap-2">
|
|
{files.map((file, index) => (
|
|
<div
|
|
key={index}
|
|
className="inline-flex items-center gap-2 px-3 py-2 bg-Neutrals-NeutralSlate100 rounded-lg hover:bg-Neutrals-NeutralSlate150 transition-colors group"
|
|
>
|
|
{getFileIcon(file)}
|
|
<span className="text-sm text-Neutrals-NeutralSlate700 max-w-[150px] truncate">{file}</span>
|
|
<button
|
|
onClick={() => onRemoveFile(index)}
|
|
className="w-5 h-5 text-Neutrals-NeutralSlate400 hover:text-red-500 hover:bg-red-50 rounded transition-colors flex items-center justify-center"
|
|
title="Remove file"
|
|
>
|
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M9 3L3 9M3 3L9 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
interface FileUploadDropzoneProps {
|
|
onFilesSelected: (files: File[]) => void;
|
|
children: React.ReactNode;
|
|
accept?: string;
|
|
multiple?: boolean;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
const FileUploadDropzone: React.FC<FileUploadDropzoneProps> = ({
|
|
onFilesSelected,
|
|
children,
|
|
accept = "*/*",
|
|
multiple = true,
|
|
disabled = false
|
|
}) => {
|
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
if (!disabled) {
|
|
setIsDragOver(true);
|
|
}
|
|
}, [disabled]);
|
|
|
|
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
setIsDragOver(false);
|
|
}, []);
|
|
|
|
const handleDrop = useCallback((e: React.DragEvent) => {
|
|
e.preventDefault();
|
|
setIsDragOver(false);
|
|
|
|
if (disabled) return;
|
|
|
|
const files = Array.from(e.dataTransfer.files);
|
|
if (files.length > 0) {
|
|
onFilesSelected(files);
|
|
}
|
|
}, [onFilesSelected, disabled]);
|
|
|
|
const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const files = Array.from(e.target.files || []);
|
|
if (files.length > 0) {
|
|
onFilesSelected(files);
|
|
}
|
|
// Reset input value to allow selecting the same file again
|
|
if (fileInputRef.current) {
|
|
fileInputRef.current.value = '';
|
|
}
|
|
}, [onFilesSelected]);
|
|
|
|
const handleClick = () => {
|
|
if (!disabled && fileInputRef.current) {
|
|
fileInputRef.current.click();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
onDragOver={handleDragOver}
|
|
onDragLeave={handleDragLeave}
|
|
onDrop={handleDrop}
|
|
onClick={handleClick}
|
|
className={`
|
|
relative cursor-pointer transition-all
|
|
${isDragOver ? 'opacity-80' : ''}
|
|
${disabled ? 'cursor-not-allowed opacity-50' : ''}
|
|
`}
|
|
>
|
|
<input
|
|
ref={fileInputRef}
|
|
type="file"
|
|
accept={accept}
|
|
multiple={multiple}
|
|
onChange={handleFileSelect}
|
|
className="hidden"
|
|
disabled={disabled}
|
|
/>
|
|
|
|
{children}
|
|
|
|
{/* Drag overlay */}
|
|
{isDragOver && (
|
|
<div className="absolute inset-0 bg-Brand-Orange/10 border-2 border-dashed border-Brand-Orange rounded-xl flex items-center justify-center">
|
|
<div className="text-Brand-Orange font-medium">Drop files here</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
interface FileUploadInputProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
placeholder?: string;
|
|
disabled?: boolean;
|
|
uploadedFiles: string[];
|
|
onRemoveFile: (index: number) => void;
|
|
onFilesSelected: (files: File[]) => void;
|
|
}
|
|
|
|
const FileUploadInput: React.FC<FileUploadInputProps> = ({
|
|
value,
|
|
onChange,
|
|
onKeyDown,
|
|
placeholder = "Ask about your team's performance, culture, or any insights...",
|
|
disabled = false,
|
|
uploadedFiles,
|
|
onRemoveFile,
|
|
onFilesSelected
|
|
}) => {
|
|
const handleFilesSelected = (files: File[]) => {
|
|
// For demo purposes, we'll just add the file names
|
|
// In a real implementation, you'd upload the files and get URLs back
|
|
const fileNames = files.map(file => file.name);
|
|
onFilesSelected(files);
|
|
};
|
|
|
|
return (
|
|
<div className="w-full">
|
|
{/* File Upload Preview */}
|
|
<FileUploadPreview files={uploadedFiles} onRemoveFile={onRemoveFile} />
|
|
|
|
{/* Input Field with File Upload */}
|
|
<FileUploadDropzone
|
|
onFilesSelected={handleFilesSelected}
|
|
disabled={disabled}
|
|
accept=".pdf,.doc,.docx,.xls,.xlsx,.txt,.jpg,.jpeg,.png,.gif"
|
|
>
|
|
<div className="relative flex items-end gap-3">
|
|
<div className="flex-1 relative">
|
|
<textarea
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
onKeyDown={onKeyDown}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
className="w-full min-h-[44px] max-h-32 px-4 py-3 pr-12 border border-Neutrals-NeutralSlate200 rounded-xl resize-none focus:outline-none focus:border-Brand-Orange focus:ring-1 focus:ring-Brand-Orange disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
rows={1}
|
|
/>
|
|
|
|
{/* File Upload Button */}
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
className="absolute right-3 top-3 w-6 h-6 text-Neutrals-NeutralSlate400 hover:text-Brand-Orange disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
title="Upload files"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M21 15V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V15M17 8L12 3M12 3L7 8M12 3V15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</FileUploadDropzone>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FileUploadInput;
|
|
export { FileUploadPreview, FileUploadDropzone }; |