feat: major UI overhaul with new components and enhanced UX

- 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
This commit is contained in:
Ra
2025-08-20 03:30:04 -07:00
parent 1a9e92d7bd
commit cf565df13e
47 changed files with 6654 additions and 2007 deletions

View File

@@ -0,0 +1,150 @@
import React from 'react';
interface QAItem {
question: string;
answer: string;
}
interface CompanyWikiCompletedStateProps {
qaItems?: QAItem[];
activeSection?: number;
onSectionClick?: (section: number) => void;
onInviteEmployees?: () => void;
onCopyLink?: () => void;
}
const defaultQAItems: QAItem[] = [
{
question: "What is the mission of your company?",
answer: "To empower small businesses with AI-driven automation tools that increase efficiency and reduce operational overhead."
},
{
question: "How has your mission evolved in the last 13 years?",
answer: "We shifted from general SaaS tools to vertical-specific solutions, with deeper integrations and onboarding support."
},
{
question: "What is your 5-year vision for the company?",
answer: "To become the leading AI operations platform for SMBs in North America, serving over 100,000 customers."
},
{
question: "What are your company's top 3 strategic advantages?",
answer: "Fast product iteration enabled by in-house AI capabilities\nDeep customer understanding from vertical specialization\nHigh customer retention due to integrated onboarding"
},
{
question: "What are your biggest vulnerabilities or threats?",
answer: "Dependence on a single marketing channel, weak middle management, and rising customer acquisition costs."
}
];
const sections = [
"Company Overview & Vision",
"Leadership & Organizational Structure",
"Operations & Execution",
"Culture & Team Health",
"Sales, Marketing & Growth",
"Financial Health & Metrics",
"Innovation & Product/Service Strategy",
"Personal Leadership & Risk"
];
export const CompanyWikiCompletedState: React.FC<CompanyWikiCompletedStateProps> = ({
qaItems = defaultQAItems,
activeSection = 1,
onSectionClick,
onInviteEmployees,
onCopyLink
}) => {
return (
<div className="flex-1 self-stretch inline-flex justify-start items-center">
{/* Table of Contents */}
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 dark:border-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start">
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
<div className="flex-1 justify-start text-Neutrals-NeutralSlate950 dark:text-Neutrals-NeutralSlate50 text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
</div>
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
{sections.map((section, index) => {
const sectionNumber = index + 1;
const isActive = sectionNumber === activeSection;
return (
<div
key={index}
onClick={() => onSectionClick?.(sectionNumber)}
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden cursor-pointer hover:bg-Main-BG-Gray-50 dark:hover:bg-Neutrals-NeutralSlate700 ${isActive ? 'bg-Main-BG-Gray-100 dark:bg-Neutrals-NeutralSlate800 shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : ''}`}
>
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-Brand-Orange' : 'bg-Text-Gray-100 dark:bg-Neutrals-NeutralSlate600'}`}>
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-Neutrals-NeutralSlate0' : 'text-Text-Dark-950 dark:text-Neutrals-NeutralSlate200'}`}>
{sectionNumber}
</div>
</div>
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100' : 'text-Text-Gray-500 dark:text-Neutrals-NeutralSlate400'}`}>
{section}
</div>
</div>
);
})}
</div>
</div>
{/* Main Content */}
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
<div className="flex-1 justify-start text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100 text-xl font-medium font-['Neue_Montreal'] leading-normal">
{sections[activeSection - 1]}
</div>
</div>
<div className="self-stretch px-5 flex flex-col justify-start items-start gap-4">
{qaItems.map((item, index) => (
<div key={index} className="self-stretch p-3 bg-Neutrals-NeutralSlate100 dark:bg-Neutrals-NeutralSlate800 rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
<div className="flex-1 flex justify-center items-center gap-3">
<div className="w-3 self-stretch justify-start text-Neutrals-NeutralSlate300 dark:text-Neutrals-NeutralSlate500 text-base font-semibold font-['Inter'] leading-normal">Q</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate600 dark:text-Neutrals-NeutralSlate300 text-sm font-medium font-['Inter'] leading-tight">
{item.question}
</div>
</div>
</div>
<div className="self-stretch px-3 py-2 bg-Neutrals-NeutralSlate0 dark:bg-Neutrals-NeutralSlate900 rounded-[10px] inline-flex justify-between items-center">
<div className="flex-1 flex justify-start items-start gap-3">
<div className="w-3.5 h-6 justify-center text-Neutrals-NeutralSlate300 dark:text-Neutrals-NeutralSlate500 text-base font-semibold font-['Inter'] leading-normal">A</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100 text-base font-normal font-['Inter'] leading-normal whitespace-pre-line">
{item.answer}
</div>
</div>
</div>
</div>
))}
{/* Additional Questions */}
<div className="self-stretch pl-3 pr-6 pt-3 pb-6 bg-Text-Gray-100 rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
<div className="self-stretch inline-flex justify-center items-center gap-3">
<div className="flex-1 justify-start text-Text-Gray-600 text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
</div>
<div className="self-stretch px-3 py-2 bg-Light-Grays-l-gray08 rounded-[10px] outline outline-1 outline-offset-[-1px] outline-Text-Gray-200 inline-flex justify-between items-center">
<div className="flex-1 flex justify-start items-start gap-3">
<div className="flex-1 justify-start text-Text-Gray-800 text-base font-normal font-['Inter'] leading-normal">
Our mission is to not only create value but also to foster a collaborative environment where innovation thrives. We aim to empower our team members to contribute their unique skills and perspectives, ensuring that every project we undertake is a reflection of our collective creativity and dedication. By prioritizing both individual growth and teamwork, we strive to build a company culture that values excellence and continuous improvement. Together, we can achieve remarkable results that benefit not just our organization, but also our clients and the community at...
</div>
</div>
</div>
</div>
<div className="self-stretch p-3 bg-Neutrals-NeutralSlate100 rounded-2xl shadow-[0px_1px_2px_0px_rgba(0,0,0,0.02)] flex flex-col justify-center items-start gap-2 overflow-hidden">
<div className="self-stretch px-3 py-px inline-flex justify-start items-center gap-2.5">
<div className="flex-1 flex justify-center items-center gap-3">
<div className="w-3 self-stretch justify-start text-Neutrals-NeutralSlate300 text-base font-semibold font-['Inter'] leading-normal">Q</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate600 text-sm font-medium font-['Inter'] leading-tight">What is the mission of your company?</div>
</div>
</div>
<div className="self-stretch px-3 py-2 bg-Light-Grays-l-gray08 rounded-[10px] inline-flex justify-between items-center">
<div className="flex-1 flex justify-start items-start gap-3">
<div className="w-3.5 h-6 justify-center text-Neutrals-NeutralSlate300 text-base font-semibold font-['Inter'] leading-normal">A</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate800 text-base font-normal font-['Inter'] leading-normal">The mission is to create value as well as working</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,119 @@
import React from 'react';
interface CompanyWikiEmptyStateProps {
progress?: number;
onCompleteOnboarding?: () => void;
onInviteEmployees?: () => void;
onCopyLink?: () => void;
}
export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
progress = 60,
onCompleteOnboarding,
onInviteEmployees,
onCopyLink
}) => {
return (
<div className="flex-1 self-stretch inline-flex justify-start items-center">
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 inline-flex flex-col justify-start items-start">
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
<div className="flex-1 justify-start text-Neutrals-NeutralSlate950 text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
</div>
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
<div className="self-stretch p-2 bg-Main-BG-Gray-100 rounded-full shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Brand-Orange rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Neutrals-NeutralSlate0 text-xs font-medium font-['Inter'] leading-none">1</div>
</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate800 text-xs font-medium font-['Inter'] leading-none">Company Overview & Vision</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">2</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Leadership & Organizational Structure</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">3</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Operations & Execution</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">4</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Culture & Team Health</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">5</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Sales, Marketing & Growth</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">6</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Financial Health & Metrics</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">7</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Innovation & Product/Service Strategy</div>
</div>
<div className="self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden">
<div className="h-5 p-0.5 bg-Text-Gray-100 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden">
<div className="w-4 text-center justify-start text-Text-Dark-950 text-xs font-normal font-['Inter'] leading-none">8</div>
</div>
<div className="flex-1 justify-start text-Text-Gray-500 text-xs font-medium font-['Inter'] leading-none">Personal Leadership & Risk</div>
</div>
</div>
</div>
<div className="flex-1 self-stretch inline-flex flex-col justify-center items-center p-8">
{/* Empty State Illustration */}
<div className="w-80 h-64 mb-8 relative">
{/* Placeholder for illustration - would contain the actual empty state SVG */}
<div className="w-full h-full bg-Neutrals-NeutralSlate100 rounded-2xl flex items-center justify-center">
<div className="text-center">
<div className="w-16 h-16 bg-Neutrals-NeutralSlate200 rounded-full mx-auto mb-4 flex items-center justify-center">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 30V18.6667C12 17.3867 12 16.7467 12.1453 16.248C12.2731 15.8071 12.5171 15.4109 12.848 15.1053C13.2133 14.7667 13.7066 14.6667 14.6933 14.6667H17.3067C18.2934 14.6667 18.7867 14.7667 19.152 15.1053C19.4829 15.4109 19.7269 15.8071 19.8547 16.248C20 16.7467 20 17.3867 20 18.6667V30M14.6903 3.68533L6.04715 11.5188C5.44269 12.0684 5.14047 12.3431 4.92271 12.6778C4.73015 12.9739 4.58613 13.3073 4.49871 13.6608C4.4 14.0575 4.4 14.4803 4.4 15.3261V23.7333C4.4 25.2267 4.4 25.9733 4.69065 26.544C4.94631 27.0458 5.35421 27.4537 5.85603 27.7093C6.42669 28 7.17323 28 8.66667 28H23.3333C24.8268 28 25.5733 28 26.144 27.7093C26.6458 27.4537 27.0537 27.0458 27.3093 26.544C27.6 25.9733 27.6 25.2267 27.6 23.7333V15.3261C27.6 14.4803 27.6 14.0575 27.5013 13.6608C27.4139 13.3073 27.2699 12.9739 27.0773 12.6778C26.8595 12.3431 26.5573 12.0684 25.9529 11.5188L17.3097 3.68533C16.8413 3.27241 16.6071 3.06595 16.3485 2.98821C16.1203 2.9184 15.8797 2.9184 15.6515 2.98821C15.3929 3.06595 15.1587 3.27241 14.6903 3.68533Z" stroke="var(--Neutrals-NeutralSlate400)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<div className="text-Neutrals-NeutralSlate600 text-sm">Company Wiki Empty State</div>
</div>
</div>
</div>
{/* Progress and Call to Action */}
<div className="text-center max-w-md">
<div className="mb-6">
<div className="text-Neutrals-NeutralSlate950 text-2xl font-medium font-['Neue_Montreal'] leading-normal mb-2">
You're {progress}% Done
</div>
<div className="text-Neutrals-NeutralSlate600 text-base leading-normal">
Complete your onboarding to unlock your Company Wiki
</div>
</div>
{/* Progress Bar */}
<div className="w-full bg-Neutrals-NeutralSlate200 rounded-full h-2 mb-8">
<div
className="bg-Brand-Orange h-2 rounded-full transition-all duration-300"
style={{ width: `${progress}%` }}
></div>
</div>
{/* Action Button */}
<button
onClick={onCompleteOnboarding}
className="px-8 py-3 bg-Brand-Orange text-white rounded-[999px] font-medium text-base hover:bg-Brand-Orange/90 transition-colors"
>
Complete Onboarding
</button>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,110 @@
import React from 'react';
interface CompanyWikiEmptyStateProps {
progress?: number;
onCompleteOnboarding?: () => void;
onInviteEmployees?: () => void;
onCopyLink?: () => void;
}
const sections = [
"Company Overview & Vision",
"Leadership & Organizational Structure",
"Operations & Execution",
"Culture & Team Health",
"Sales, Marketing & Growth",
"Financial Health & Metrics",
"Innovation & Product/Service Strategy",
"Personal Leadership & Risk"
];
export const CompanyWikiEmptyState: React.FC<CompanyWikiEmptyStateProps> = ({
progress = 60,
onCompleteOnboarding,
onInviteEmployees,
onCopyLink
}) => {
return (
<div className="flex-1 self-stretch inline-flex justify-start items-center">
{/* Table of Contents */}
<div className="flex-1 self-stretch max-w-64 min-w-64 border-r border-Outline-Outline-Gray-200 dark:border-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start">
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
<div className="flex-1 justify-start text-Neutrals-NeutralSlate950 dark:text-Neutrals-NeutralSlate50 text-base font-medium font-['Inter'] leading-normal">Table of contents</div>
</div>
<div className="self-stretch px-3 flex flex-col justify-start items-start gap-1.5">
{sections.map((section, index) => {
const sectionNumber = index + 1;
const isActive = sectionNumber === 1; // First section is always active in empty state
return (
<div
key={index}
className={`self-stretch p-2 rounded-[10px] inline-flex justify-start items-center gap-2 overflow-hidden ${isActive ? 'bg-Main-BG-Gray-100 dark:bg-Neutrals-NeutralSlate800 shadow-[0px_1px_2px_0px_rgba(10,13,20,0.03)]' : 'hover:bg-Main-BG-Gray-50 dark:hover:bg-Neutrals-NeutralSlate700'}`}
>
<div className={`h-5 p-0.5 rounded-[999px] inline-flex flex-col justify-center items-center gap-0.5 overflow-hidden ${isActive ? 'bg-Brand-Orange' : 'bg-Text-Gray-100 dark:bg-Neutrals-NeutralSlate600'}`}>
<div className={`w-4 text-center justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-Neutrals-NeutralSlate0' : 'text-Text-Dark-950 dark:text-Neutrals-NeutralSlate200'}`}>
{sectionNumber}
</div>
</div>
<div className={`flex-1 justify-start text-xs font-medium font-['Inter'] leading-none ${isActive ? 'text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100' : 'text-Text-Gray-500 dark:text-Neutrals-NeutralSlate400'}`}>
{section}
</div>
</div>
);
})}
</div>
</div>
{/* Main Content */}
<div className="flex-1 self-stretch inline-flex flex-col justify-start items-start">
<div className="self-stretch p-5 inline-flex justify-start items-center gap-2.5">
<div className="flex-1 justify-start text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100 text-xl font-medium font-['Neue_Montreal'] leading-normal">
Company Overview & Vision
</div>
</div>
<div className="self-stretch flex-1 p-5 flex justify-center items-center">
<div className="w-[440px] flex flex-col justify-center items-center gap-8">
{/* Progress Illustration Placeholder */}
<div className="w-[280px] h-[200px] bg-Text-Gray-100 dark:bg-Neutrals-NeutralSlate700 rounded-2xl flex items-center justify-center">
<div className="text-center">
<div className="w-16 h-16 mx-auto mb-4 bg-Brand-Orange rounded-full flex items-center justify-center">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" className="text-white">
<path d="M16 8v8l4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<circle cx="16" cy="16" r="12" stroke="currentColor" strokeWidth="2" />
</svg>
</div>
<div className="text-Text-Gray-600 dark:text-Neutrals-NeutralSlate300 text-sm">Progress Illustration</div>
</div>
</div>
{/* Progress Content */}
<div className="self-stretch flex flex-col justify-center items-center gap-4 text-center">
<div className="text-Neutrals-NeutralSlate800 dark:text-Neutrals-NeutralSlate100 text-2xl font-semibold font-['Neue_Montreal'] leading-8">
You're {progress}% Done
</div>
<div className="self-stretch text-Text-Gray-600 dark:text-Neutrals-NeutralSlate300 text-base font-normal font-['Inter'] leading-normal">
Complete your company onboarding to unlock your company wiki and comprehensive insights about your organization.
</div>
{/* Progress Bar */}
<div className="self-stretch h-2 bg-Text-Gray-100 dark:bg-Neutrals-NeutralSlate700 rounded-full overflow-hidden">
<div
className="h-full bg-Brand-Orange rounded-full transition-all duration-300"
style={{ width: `${progress}%` }}
/>
</div>
{/* Action Button */}
<button
onClick={onCompleteOnboarding}
className="w-full px-6 py-3 bg-Brand-Orange hover:bg-orange-600 text-Neutrals-NeutralSlate0 text-base font-medium font-['Inter'] leading-normal rounded-xl transition-colors"
>
Complete Onboarding
</button>
</div>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,116 @@
import React, { useState } from 'react';
import { CompanyWikiEmptyState } from './CompanyWikiEmptyState';
import { CompanyWikiCompletedState } from './CompanyWikiCompletedState';
import { InviteEmployeesModal } from './InviteEmployeesModal';
import { InviteMultipleEmployeesModal } from './InviteMultipleEmployeesModal';
export type WikiState = 'empty' | 'completed';
export type InviteModalState = 'none' | 'single' | 'multiple';
interface Employee {
id: string;
name: string;
email: string;
avatar?: string;
}
interface CompanyWikiManagerProps {
initialState?: WikiState;
onboardingProgress?: number;
onCompleteOnboarding?: () => void;
qaItems?: Array<{ question: string; answer: string }>;
suggestedEmployees?: Employee[];
}
export const CompanyWikiManager: React.FC<CompanyWikiManagerProps> = ({
initialState = 'empty',
onboardingProgress = 60,
onCompleteOnboarding,
qaItems,
suggestedEmployees
}) => {
const [wikiState, setWikiState] = useState<WikiState>(initialState);
const [inviteModalState, setInviteModalState] = useState<InviteModalState>('none');
const [activeSection, setActiveSection] = useState(1);
const handleCompleteOnboarding = () => {
onCompleteOnboarding?.();
setWikiState('completed');
};
const handleInviteEmployee = (email: string) => {
console.log('Inviting employee:', email);
// Here you would typically call an API to send the invitation
setInviteModalState('none');
// You could show a success toast here
};
const handleInviteMultipleEmployees = (employees: Employee[]) => {
console.log('Inviting multiple employees:', employees);
// Here you would typically call an API to send multiple invitations
setInviteModalState('none');
// You could show a success toast here
};
const handleSectionClick = (section: number) => {
setActiveSection(section);
};
const handleCopyLink = () => {
// Copy wiki link to clipboard
const wikiUrl = `${window.location.origin}/#/company-wiki`;
navigator.clipboard.writeText(wikiUrl).then(() => {
console.log('Wiki link copied to clipboard');
// You could show a success toast here
});
};
const renderWikiContent = () => {
switch (wikiState) {
case 'empty':
return (
<CompanyWikiEmptyState
progress={onboardingProgress}
onCompleteOnboarding={handleCompleteOnboarding}
onInviteEmployees={() => setInviteModalState('single')}
onCopyLink={handleCopyLink}
/>
);
case 'completed':
return (
<CompanyWikiCompletedState
qaItems={qaItems}
activeSection={activeSection}
onSectionClick={handleSectionClick}
onInviteEmployees={() => setInviteModalState('single')}
onCopyLink={handleCopyLink}
/>
);
default:
return null;
}
};
return (
<div className="w-full h-full flex flex-col">
{renderWikiContent()}
{/* Modals */}
<InviteEmployeesModal
isOpen={inviteModalState === 'single'}
onClose={() => setInviteModalState('none')}
onInvite={handleInviteEmployee}
onMultipleInvite={() => setInviteModalState('multiple')}
/>
<InviteMultipleEmployeesModal
isOpen={inviteModalState === 'multiple'}
onClose={() => setInviteModalState('none')}
onInviteSelected={handleInviteMultipleEmployees}
suggestedEmployees={suggestedEmployees}
/>
</div>
);
};

View File

@@ -0,0 +1,92 @@
import React, { useState } from 'react';
interface InviteEmployeesModalProps {
isOpen: boolean;
onClose: () => void;
onInvite: (email: string) => void;
onMultipleInvite?: () => void;
}
export const InviteEmployeesModal: React.FC<InviteEmployeesModalProps> = ({
isOpen,
onClose,
onInvite,
onMultipleInvite
}) => {
const [email, setEmail] = useState('');
if (!isOpen) return null;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (email.trim()) {
onInvite(email.trim());
setEmail('');
}
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="w-[420px] bg-Neutrals-NeutralSlate0 dark:bg-Neutrals-NeutralSlate900 rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
{/* Header */}
<div className="self-stretch p-6 inline-flex justify-between items-center">
<div className="flex justify-start items-center gap-2.5">
<div className="justify-start text-Text-Dark-950 dark:text-Neutrals-NeutralSlate50 text-lg font-semibold font-['Inter'] leading-7">Invite employees</div>
</div>
<button
onClick={onClose}
className="w-6 h-6 flex justify-center items-center hover:bg-Text-Gray-100 dark:hover:bg-Neutrals-NeutralSlate700 rounded"
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M13 1L1 13M1 1L13 13" stroke="#666D80" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="self-stretch px-6 flex flex-col justify-start items-start gap-4">
<div className="self-stretch flex flex-col justify-start items-start gap-1">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate950 dark:text-Neutrals-NeutralSlate50 text-sm font-medium font-['Inter'] leading-tight">
Email
</div>
<div className="self-stretch h-10 px-3 py-2 bg-Neutrals-NeutralSlate0 dark:bg-Neutrals-NeutralSlate800 rounded-lg border border-Outline-Outline-Gray-300 dark:border-Neutrals-NeutralSlate600 inline-flex justify-start items-center gap-2">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email address"
className="flex-1 text-Neutrals-NeutralSlate950 dark:text-Neutrals-NeutralSlate50 text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-Text-Gray-500 dark:placeholder:text-Neutrals-NeutralSlate400"
autoFocus
/>
</div>
</div>
</form>
{/* Footer */}
<div className="self-stretch p-6 inline-flex justify-between items-center">
<button
onClick={onMultipleInvite}
className="text-Brand-Orange text-sm font-medium font-['Inter'] leading-tight hover:underline"
>
Invite multiple employees
</button>
<div className="flex justify-start items-start gap-3">
<button
onClick={onClose}
className="px-4 py-2 bg-Neutrals-NeutralSlate0 dark:bg-Neutrals-NeutralSlate800 rounded-lg border border-Outline-Outline-Gray-300 dark:border-Neutrals-NeutralSlate600 text-Neutrals-NeutralSlate700 dark:text-Neutrals-NeutralSlate200 text-sm font-medium font-['Inter'] leading-tight hover:bg-Text-Gray-50 dark:hover:bg-Neutrals-NeutralSlate700"
>
Cancel
</button>
<button
onClick={handleSubmit}
disabled={!email.trim()}
className="px-4 py-2 bg-Brand-Orange rounded-lg text-Neutrals-NeutralSlate0 text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
Send Invite
</button>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,178 @@
import React, { useState } from 'react';
interface Employee {
id: string;
name: string;
email: string;
avatar?: string;
}
interface InviteMultipleEmployeesModalProps {
isOpen: boolean;
onClose: () => void;
onInviteSelected: (employees: Employee[]) => void;
suggestedEmployees?: Employee[];
}
const defaultSuggestedEmployees: Employee[] = [
{ id: '1', name: 'John Smith', email: 'john@company.com' },
{ id: '2', name: 'Sarah Johnson', email: 'sarah@company.com' },
{ id: '3', name: 'Mike Chen', email: 'mike@company.com' },
{ id: '4', name: 'Emily Davis', email: 'emily@company.com' },
{ id: '5', name: 'Alex Rodriguez', email: 'alex@company.com' },
];
export const InviteMultipleEmployeesModal: React.FC<InviteMultipleEmployeesModalProps> = ({
isOpen,
onClose,
onInviteSelected,
suggestedEmployees = defaultSuggestedEmployees
}) => {
const [searchTerm, setSearchTerm] = useState('');
const [selectedEmployees, setSelectedEmployees] = useState<Employee[]>([]);
const [showDropdown, setShowDropdown] = useState(false);
if (!isOpen) return null;
const filteredEmployees = suggestedEmployees.filter(emp =>
(emp.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
emp.email.toLowerCase().includes(searchTerm.toLowerCase())) &&
!selectedEmployees.find(selected => selected.id === emp.id)
);
const handleEmployeeSelect = (employee: Employee) => {
setSelectedEmployees(prev => [...prev, employee]);
setSearchTerm('');
setShowDropdown(false);
};
const handleEmployeeRemove = (employeeId: string) => {
setSelectedEmployees(prev => prev.filter(emp => emp.id !== employeeId));
};
const handleInvite = () => {
if (selectedEmployees.length > 0) {
onInviteSelected(selectedEmployees);
setSelectedEmployees([]);
setSearchTerm('');
}
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="w-[480px] bg-Neutrals-NeutralSlate0 dark:bg-Neutrals-NeutralSlate900 rounded-3xl shadow-[0px_25px_50px_-12px_rgba(0,0,0,0.25)] flex flex-col justify-start items-start overflow-hidden">
{/* Header */}
<div className="self-stretch p-6 inline-flex justify-between items-center">
<div className="flex justify-start items-center gap-2.5">
<div className="justify-start text-Text-Dark-950 dark:text-Neutrals-NeutralSlate50 text-lg font-semibold font-['Inter'] leading-7">
Invite multiple employees
</div>
</div>
<button
onClick={onClose}
className="w-6 h-6 flex justify-center items-center hover:bg-Text-Gray-100 dark:hover:bg-Neutrals-NeutralSlate700 rounded"
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M13 1L1 13M1 1L13 13" stroke="#666D80" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
{/* Search Input with Dropdown */}
<div className="self-stretch px-6 flex flex-col justify-start items-start gap-4">
<div className="self-stretch flex flex-col justify-start items-start gap-1 relative">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">
Search employees
</div>
<div className="self-stretch h-10 px-3 py-2 bg-Neutrals-NeutralSlate0 rounded-lg border border-Outline-Outline-Gray-300 inline-flex justify-start items-center gap-2">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" className="text-Text-Gray-400">
<path d="M15 15L11.15 11.15M12.6667 7.33333C12.6667 10.2789 10.2789 12.6667 7.33333 12.6667C4.38781 12.6667 2 10.2789 2 7.33333C2 4.38781 4.38781 2 7.33333 2C10.2789 2 12.6667 4.38781 12.6667 7.33333Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<input
type="text"
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
setShowDropdown(e.target.value.length > 0);
}}
onFocus={() => setShowDropdown(searchTerm.length > 0)}
placeholder="Type name or email to search..."
className="flex-1 text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight bg-transparent outline-none placeholder:text-Text-Gray-500"
/>
</div>
{/* Dropdown */}
{showDropdown && filteredEmployees.length > 0 && (
<div className="absolute top-full left-0 right-0 mt-1 bg-Neutrals-NeutralSlate0 border border-Outline-Outline-Gray-200 rounded-lg shadow-lg z-10 max-h-48 overflow-y-auto">
{filteredEmployees.map((employee) => (
<button
key={employee.id}
onClick={() => handleEmployeeSelect(employee)}
className="w-full px-3 py-2 text-left hover:bg-Text-Gray-50 flex items-center gap-3 border-b border-Text-Gray-100 last:border-b-0"
>
<div className="w-8 h-8 bg-Brand-Orange rounded-full flex items-center justify-center text-white text-sm font-medium">
{employee.name.charAt(0)}
</div>
<div className="flex-1">
<div className="text-sm font-medium text-Neutrals-NeutralSlate950">{employee.name}</div>
<div className="text-xs text-Text-Gray-500">{employee.email}</div>
</div>
</button>
))}
</div>
)}
</div>
{/* Selected Employees */}
{selectedEmployees.length > 0 && (
<div className="self-stretch flex flex-col justify-start items-start gap-2">
<div className="text-sm font-medium text-Neutrals-NeutralSlate950">
Selected ({selectedEmployees.length})
</div>
<div className="self-stretch flex flex-wrap gap-2">
{selectedEmployees.map((employee) => (
<div
key={employee.id}
className="px-3 py-1.5 bg-Brand-Orange bg-opacity-10 rounded-full flex items-center gap-2"
>
<div className="w-5 h-5 bg-Brand-Orange rounded-full flex items-center justify-center text-white text-xs font-medium">
{employee.name.charAt(0)}
</div>
<span className="text-sm text-Neutrals-NeutralSlate950">{employee.name}</span>
<button
onClick={() => handleEmployeeRemove(employee.id)}
className="w-4 h-4 flex items-center justify-center hover:bg-Brand-Orange hover:bg-opacity-20 rounded-full"
>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M9 3L3 9M3 3L9 9" stroke="#666D80" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
))}
</div>
</div>
)}
</div>
{/* Footer */}
<div className="self-stretch p-6 inline-flex justify-end items-center">
<div className="flex justify-start items-start gap-3">
<button
onClick={onClose}
className="px-4 py-2 bg-Neutrals-NeutralSlate0 rounded-lg border border-Outline-Outline-Gray-300 text-Neutrals-NeutralSlate700 text-sm font-medium font-['Inter'] leading-tight hover:bg-Text-Gray-50"
>
Cancel
</button>
<button
onClick={handleInvite}
disabled={selectedEmployees.length === 0}
className="px-4 py-2 bg-Brand-Orange rounded-lg text-Neutrals-NeutralSlate0 text-sm font-medium font-['Inter'] leading-tight hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
Send Invites ({selectedEmployees.length})
</button>
</div>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,6 @@
export { CompanyWikiEmptyState } from './CompanyWikiEmptyState';
export { CompanyWikiCompletedState } from './CompanyWikiCompletedState';
export { InviteEmployeesModal } from './InviteEmployeesModal';
export { InviteMultipleEmployeesModal } from './InviteMultipleEmployeesModal';
export { CompanyWikiManager } from './CompanyWikiManager';
export type { WikiState, InviteModalState } from './CompanyWikiManager';

View File

@@ -4,6 +4,7 @@ import { useTheme } from '../contexts/ThemeContext';
import { Theme, NavItem } from '../types';
import { useOrg } from '../contexts/OrgContext';
import { useAuth } from '../contexts/AuthContext';
import FigmaSidebar from './figma/Sidebar';
// ========== ICONS ==========
@@ -265,14 +266,18 @@ const Sidebar = () => {
);
};
export const Layout = () => (
<div className="flex h-screen bg-[--background-primary]">
<Sidebar />
<main className="flex-1 overflow-y-auto">
<Outlet />
</main>
</div>
);
export const Layout = () => {
const { org } = useOrg();
return (
<div className="flex h-screen bg-Neutrals-NeutralSlate0">
<FigmaSidebar companyName={org?.name || "Auditly"} />
<main className="flex-1 overflow-y-auto">
<Outlet />
</main>
</div>
);
};
// ========== UI PRIMITIVES ==========

View File

@@ -0,0 +1,180 @@
import React from 'react';
interface SuggestionCardProps {
category: string;
title: string;
description: string;
icon: React.ReactNode;
onClick?: () => void;
}
const SuggestionCard: React.FC<SuggestionCardProps> = ({ category, title, description, icon, onClick }) => (
<div
className="p-4 bg-Neutrals-NeutralSlate0 rounded-2xl border border-Neutrals-NeutralSlate200 hover:border-Brand-Orange hover:shadow-sm transition-all cursor-pointer group"
onClick={onClick}
>
<div className="flex items-start gap-3">
<div className="w-10 h-10 bg-Brand-Orange/10 rounded-xl flex items-center justify-center text-Brand-Orange group-hover:bg-Brand-Orange group-hover:text-white transition-colors">
{icon}
</div>
<div className="flex-1 min-w-0">
<div className="text-xs text-Brand-Orange font-medium mb-1">{category}</div>
<div className="text-sm font-medium text-Neutrals-NeutralSlate950 mb-1">{title}</div>
<div className="text-xs text-Neutrals-NeutralSlate500 leading-relaxed">{description}</div>
</div>
</div>
</div>
);
interface CategoryTabProps {
label: string;
isActive: boolean;
onClick: () => void;
}
const CategoryTab: React.FC<CategoryTabProps> = ({ label, isActive, onClick }) => (
<button
onClick={onClick}
className={`px-4 py-2 rounded-full text-sm font-medium transition-colors ${isActive
? 'bg-Brand-Orange text-white'
: 'bg-Neutrals-NeutralSlate100 text-Neutrals-NeutralSlate600 hover:bg-Neutrals-NeutralSlate200'
}`}
>
{label}
</button>
);
const ChatEmptyState: React.FC = () => {
const [activeCategory, setActiveCategory] = React.useState('All');
const categories = ['All', 'Performance', 'Culture', 'Reports', 'Analysis'];
const suggestions = [
{
category: 'Performance',
title: 'Analyze team performance trends',
description: 'Get insights on productivity patterns and improvement areas across your organization.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 17.5013V5.83464C2.5 5.36793 2.5 5.13458 2.59083 4.95631C2.67072 4.79951 2.79821 4.67202 2.95501 4.59213C3.13327 4.5013 3.36662 4.5013 3.83333 4.5013H5.16667C5.63338 4.5013 5.86673 4.5013 6.04499 4.59213C6.20179 4.67202 6.32928 4.79951 6.40917 4.95631C6.5 5.13458 6.5 5.36793 6.5 5.83464V17.5013M17.5 17.5013V9.16797C17.5 8.70126 17.5 8.46791 17.4092 8.28965C17.3293 8.13285 17.2018 8.00536 17.045 7.92547C16.8667 7.83464 16.6334 7.83464 16.1667 7.83464H14.8333C14.3666 7.83464 14.1333 7.83464 13.955 7.92547C13.7982 8.00536 13.6707 8.13285 13.5908 8.28965C13.5 8.46791 13.5 8.70126 13.5 9.16797V17.5013M12.5 17.5013V2.5013C12.5 2.03459 12.5 1.80124 12.4092 1.62298C12.3293 1.46618 12.2018 1.33869 12.045 1.2588C11.8667 1.16797 11.6334 1.16797 11.1667 1.16797H9.83333C9.36662 1.16797 9.13327 1.16797 8.95501 1.2588C8.79821 1.33869 8.67072 1.46618 8.59083 1.62298C8.5 1.80124 8.5 2.03459 8.5 2.5013V17.5013M18.3333 17.5013H1.66667" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
},
{
category: 'Culture',
title: 'Assess company culture health',
description: 'Review employee satisfaction, engagement levels, and cultural alignment metrics.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 5.83464C7.5 6.752 7.5 7.21068 7.70552 7.54611C7.88497 7.84313 8.15687 8.11503 8.45389 8.29448C8.78932 8.5 9.248 8.5 10.1654 8.5H11.5013C12.4187 8.5 12.8774 8.5 13.2128 8.29448C13.5098 8.11503 13.7817 7.84313 13.9612 7.54611C14.1667 7.21068 14.1667 6.752 14.1667 5.83464V4.16797C14.1667 3.25061 14.1667 2.79193 13.9612 2.4565C13.7817 2.15948 13.5098 1.88758 13.2128 1.70813C12.8774 1.5026 12.4187 1.5026 11.5013 1.5026H10.1654C9.248 1.5026 8.78932 1.5026 8.45389 1.70813C8.15687 1.88758 7.88497 2.15948 7.70552 2.4565C7.5 2.79193 7.5 3.25061 7.5 4.16797V5.83464Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M2.5 14.168C2.5 15.0854 2.5 15.544 2.70552 15.8795C2.88497 16.1765 3.15687 16.4484 3.45389 16.6278C3.78932 16.8333 4.248 16.8333 5.16536 16.8333H6.50131C7.41867 16.8333 7.87735 16.8333 8.21278 16.6278C8.5098 16.4484 8.7817 16.1765 8.96115 15.8795C9.16667 15.544 9.16667 15.0854 9.16667 14.168V12.5013C9.16667 11.5839 9.16667 11.1253 8.96115 10.7898C8.7817 10.4928 8.5098 10.2209 8.21278 10.0415C7.87735 9.83594 7.41867 9.83594 6.50131 9.83594H5.16536C4.248 9.83594 3.78932 9.83594 3.45389 10.0415C3.15687 10.2209 2.88497 10.4928 2.70552 10.7898C2.5 11.1253 2.5 11.5839 2.5 12.5013V14.168Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M10.8346 14.168C10.8346 15.0854 10.8346 15.544 11.0401 15.8795C11.2196 16.1765 11.4915 16.4484 11.7885 16.6278C12.1239 16.8333 12.5826 16.8333 13.5 16.8333H14.8359C15.7533 16.8333 16.212 16.8333 16.5474 16.6278C16.8444 16.4484 17.1163 16.1765 17.2958 15.8795C17.5013 15.544 17.5013 15.0854 17.5013 14.168V12.5013C17.5013 11.5839 17.5013 11.1253 17.2958 10.7898C17.1163 10.4928 16.8444 10.2209 16.5474 10.0415C16.212 9.83594 15.7533 9.83594 14.8359 9.83594H13.5C12.5826 9.83594 12.1239 9.83594 11.7885 10.0415C11.4915 10.2209 11.2196 10.4928 11.0401 10.7898C10.8346 11.1253 10.8346 11.5839 10.8346 12.5013V14.168Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
},
{
category: 'Reports',
title: 'Generate executive summary',
description: 'Create comprehensive reports on organizational strengths, risks, and recommendations.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6666 9.16797H6.66659M8.33325 12.5013H6.66659M13.3333 5.83464H6.66659M16.6666 5.66797V14.3346C16.6666 15.7348 16.6666 16.4348 16.3941 16.9696C16.1544 17.44 15.772 17.8225 15.3016 18.0622C14.7668 18.3346 14.0667 18.3346 12.6666 18.3346H7.33325C5.93312 18.3346 5.23306 18.3346 4.69828 18.0622C4.22787 17.8225 3.84542 17.44 3.60574 16.9696C3.33325 16.4348 3.33325 15.7348 3.33325 14.3346V5.66797C3.33325 4.26784 3.33325 3.56777 3.60574 3.03299C3.84542 2.56259 4.22787 2.18014 4.69828 1.94045C5.23306 1.66797 5.93312 1.66797 7.33325 1.66797H12.6666C14.0667 1.66797 14.7668 1.66797 15.3016 1.94045C15.772 2.18014 16.1544 2.56259 16.3941 3.03299C16.6666 3.56777 16.6666 4.26784 16.6666 5.66797Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
},
{
category: 'Analysis',
title: 'Compare department metrics',
description: 'Analyze cross-departmental performance and identify areas for improvement.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 1.66797C11.0944 1.66797 12.1781 1.88352 13.1891 2.30231C14.2002 2.7211 15.1188 3.33493 15.8926 4.10875C16.6665 4.88257 17.2803 5.80123 17.6991 6.81228C18.1179 7.82332 18.3334 8.90696 18.3334 10.0013M10.0001 1.66797V10.0013M10.0001 1.66797C5.39771 1.66797 1.66675 5.39893 1.66675 10.0013C1.66675 14.6037 5.39771 18.3346 10.0001 18.3346C14.6025 18.3346 18.3334 14.6037 18.3334 10.0013M10.0001 1.66797C14.6025 1.66797 18.3334 5.39893 18.3334 10.0013M18.3334 10.0013L10.0001 10.0013M18.3334 10.0013C18.3334 11.3164 18.0222 12.6128 17.4251 13.7846C16.8281 14.9563 15.9622 15.9701 14.8983 16.7431L10.0001 10.0013" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
},
{
category: 'Performance',
title: 'Review individual performance',
description: 'Deep dive into specific employee performance data and development opportunities.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0013 12.5C11.3821 12.5 12.5013 11.3807 12.5013 10C12.5013 8.61929 11.3821 7.5 10.0013 7.5C8.62061 7.5 7.50132 8.61929 7.50132 10C7.50132 11.3807 8.62061 12.5 10.0013 12.5Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M10.0013 1.66797C14.6037 1.66797 18.3346 5.39893 18.3346 10.0013C18.3346 14.6037 14.6037 18.3346 10.0013 18.3346C5.39893 18.3346 1.66797 14.6037 1.66797 10.0013C1.66797 5.39893 5.39893 1.66797 10.0013 1.66797Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
},
{
category: 'Culture',
title: 'Identify team dynamics',
description: 'Understand collaboration patterns, communication effectiveness, and team cohesion.',
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.168 5.83464C14.168 7.67561 12.675 9.16797 10.8346 9.16797C8.99367 9.16797 7.50131 7.67561 7.50131 5.83464C7.50131 3.99367 8.99367 2.5013 10.8346 2.5013C12.675 2.5013 14.168 3.99367 14.168 5.83464Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M10.8346 11.668C7.52292 11.668 4.83594 14.3549 4.83594 17.6666H16.8346C16.8346 14.3549 14.1477 11.668 10.8346 11.668Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M5.83464 9.16797C5.83464 10.5488 4.71536 11.668 3.33464 11.668C1.95393 11.668 0.834635 10.5488 0.834635 9.16797C0.834635 7.78725 1.95393 6.66797 3.33464 6.66797C4.71536 6.66797 5.83464 7.78725 5.83464 9.16797Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M3.33594 13.3346C1.49497 13.3346 0.00260794 14.827 0.00260794 16.668H6.66927C6.66927 15.7686 6.35594 14.9346 5.83594 14.2513" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
}
];
const filteredSuggestions = activeCategory === 'All'
? suggestions
: suggestions.filter(s => s.category === activeCategory);
const handleSuggestionClick = (suggestion: any) => {
// Handle suggestion click - could pass this up to parent component
console.log('Clicked suggestion:', suggestion.title);
};
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] px-4">
{/* Welcome Message */}
<div className="text-center mb-8 max-w-2xl">
<h2 className="text-2xl font-semibold text-Neutrals-NeutralSlate950 mb-3">
Welcome to Auditly Chat
</h2>
<p className="text-Neutrals-NeutralSlate600 text-lg leading-relaxed">
Ask me anything about your team's performance, company culture, or organizational insights.
I can analyze employee data, generate reports, and provide actionable recommendations.
</p>
</div>
{/* Category Tabs */}
<div className="flex flex-wrap gap-2 mb-6">
{categories.map((category) => (
<CategoryTab
key={category}
label={category}
isActive={activeCategory === category}
onClick={() => setActiveCategory(category)}
/>
))}
</div>
{/* Suggestion Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 max-w-6xl w-full">
{filteredSuggestions.map((suggestion, index) => (
<SuggestionCard
key={index}
category={suggestion.category}
title={suggestion.title}
description={suggestion.description}
icon={suggestion.icon}
onClick={() => handleSuggestionClick(suggestion)}
/>
))}
</div>
{/* Additional Help Text */}
<div className="mt-8 text-center text-sm text-Neutrals-NeutralSlate500 max-w-xl">
<p>
You can also upload files, mention specific employees using @, or ask custom questions about your organization.
I'll provide insights based on your team's data and industry best practices.
</p>
</div>
</div>
);
};
export default ChatEmptyState;

View File

@@ -0,0 +1,171 @@
import React, { useState, useEffect, useRef } from 'react';
import { useOrg } from '../../contexts/OrgContext';
import { Employee } from '../../types';
import ChatSidebar from './ChatSidebar';
import MessageThread from './MessageThread';
import FileUploadInput from './FileUploadInput';
interface Message {
id: string;
text: string;
isUser: boolean;
timestamp: number;
files?: string[];
}
interface ChatLayoutProps {
children?: React.ReactNode;
}
const ChatLayout: React.FC<ChatLayoutProps> = ({ children }) => {
const { employees } = useOrg();
const [selectedEmployees, setSelectedEmployees] = useState<Employee[]>([]);
const [messages, setMessages] = useState<Message[]>([]);
const [inputValue, setInputValue] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleNavigation = (page: string) => {
// Handle navigation to different pages
console.log('Navigate to:', page);
};
const handleSendMessage = async () => {
if (!inputValue.trim() && uploadedFiles.length === 0) return;
const userMessage: Message = {
id: Date.now().toString(),
text: inputValue,
isUser: true,
timestamp: Date.now(),
files: uploadedFiles.length > 0 ? [...uploadedFiles] : undefined
};
setMessages(prev => [...prev, userMessage]);
setInputValue('');
setUploadedFiles([]);
setIsLoading(true);
// Simulate AI response
setTimeout(() => {
const aiMessage: Message = {
id: (Date.now() + 1).toString(),
text: "I understand you're asking about the employee data. Based on the information provided, I can help analyze the performance metrics and provide insights.\n\nHere are some key findings from your team's data:\n\n• **Performance Trends**: Overall team productivity has increased by 15% this quarter\n• **Cultural Health**: Employee satisfaction scores are above industry average\n• **Areas for Growth**: Communication and cross-team collaboration could be improved\n\nWould you like me to dive deeper into any of these areas?",
isUser: false,
timestamp: Date.now()
};
setMessages(prev => [...prev, aiMessage]);
setIsLoading(false);
}, 2000);
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
const handleRemoveFile = (index: number) => {
setUploadedFiles(prev => prev.filter((_, i) => i !== index));
};
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);
setUploadedFiles(prev => [...prev, ...fileNames]);
};
const hasMessages = messages.length > 0;
return (
<div className="w-full h-screen bg-Neutrals-NeutralSlate0 inline-flex overflow-hidden">
{/* Sidebar */}
<ChatSidebar currentPage="chat" onNavigate={handleNavigation} />
{/* Main Content Area */}
<div className="flex-1 flex flex-col overflow-hidden">
{/* Header with Employee Selection */}
<div className="px-6 py-4 bg-Neutrals-NeutralSlate0 border-b border-Neutrals-NeutralSlate200 flex justify-between items-center">
<div className="flex items-center gap-3">
<h1 className="text-xl font-semibold text-Neutrals-NeutralSlate950">Chat</h1>
{selectedEmployees.length > 0 && (
<div className="flex items-center gap-2">
<span className="text-sm text-Neutrals-NeutralSlate500">Analyzing:</span>
<div className="flex items-center gap-1">
{selectedEmployees.slice(0, 3).map((emp, index) => (
<div key={emp.id} className="px-2 py-1 bg-Brand-Orange/10 rounded-full text-xs text-Brand-Orange">
{emp.name}
</div>
))}
{selectedEmployees.length > 3 && (
<div className="px-2 py-1 bg-Neutrals-NeutralSlate100 rounded-full text-xs text-Neutrals-NeutralSlate600">
+{selectedEmployees.length - 3} more
</div>
)}
</div>
</div>
)}
</div>
</div>
{/* Messages Area */}
<div className="flex-1 overflow-y-auto px-6 py-4">
{hasMessages ? (
<div className="max-w-4xl mx-auto">
<MessageThread
messages={messages}
isLoading={isLoading}
/>
<div ref={messagesEndRef} />
</div>
) : (
children
)}
</div>
{/* Input Area */}
<div className="px-6 py-4 bg-Neutrals-NeutralSlate0 border-t border-Neutrals-NeutralSlate200">
<div className="max-w-4xl mx-auto">
<div className="flex items-end gap-3">
<FileUploadInput
value={inputValue}
onChange={setInputValue}
onKeyDown={handleKeyPress}
placeholder="Ask about your team's performance, culture, or any insights..."
disabled={isLoading}
uploadedFiles={uploadedFiles}
onRemoveFile={handleRemoveFile}
onFilesSelected={handleFilesSelected}
/>
{/* Send Button */}
<button
onClick={handleSendMessage}
disabled={!inputValue.trim() && uploadedFiles.length === 0}
className="px-4 py-3 bg-Brand-Orange text-white rounded-xl hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex-shrink-0"
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.3346 1.66797L9.16797 10.8346M18.3346 1.66797L12.5013 18.3346L9.16797 10.8346M18.3346 1.66797L1.66797 7.5013L9.16797 10.8346" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default ChatLayout;

View File

@@ -0,0 +1,263 @@
import React from 'react';
import { useOrg } from '../../contexts/OrgContext';
interface NavItemProps {
icon: React.ReactNode;
label: string;
isActive?: boolean;
onClick?: () => void;
}
const NavItem: React.FC<NavItemProps> = ({ icon, label, isActive, onClick }) => (
<div
className={`w-60 px-4 py-2.5 ${isActive ? 'bg-Neutrals-NeutralSlate100' : ''} rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer hover:bg-Neutrals-NeutralSlate50 transition-colors`}
onClick={onClick}
>
{icon}
<div className={`justify-start text-sm font-medium font-['Inter'] leading-tight ${isActive ? 'text-Neutrals-NeutralSlate950' : 'text-Neutrals-NeutralSlate500'}`}>
{label}
</div>
</div>
);
interface ChatSidebarProps {
currentPage?: string;
onNavigate?: (page: string) => void;
}
const ChatSidebar: React.FC<ChatSidebarProps> = ({ currentPage = 'chat', onNavigate }) => {
const { org } = useOrg();
const handleNavigation = (page: string) => {
if (onNavigate) {
onNavigate(page);
}
};
return (
<div className="w-64 self-stretch max-w-64 min-w-64 px-3 pt-4 pb-3 bg-Neutrals-NeutralSlate0 border-r border-Neutrals-NeutralSlate200 inline-flex flex-col justify-between items-center overflow-hidden">
{/* Company Selection */}
<div className="self-stretch flex flex-col justify-start items-start gap-5">
<div className="w-60 pl-2 pr-4 py-2 bg-Neutrals-NeutralSlate0 rounded-3xl outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex justify-between items-center overflow-hidden">
<div className="flex-1 flex justify-start items-center gap-2">
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
<div className="w-8 h-8 relative bg-Brand-Orange rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
<div className="w-8 h-8 left-0 top-0 absolute bg-gradient-to-b from-white/0 to-white/10" />
<div className="left-[8.80px] top-[7.20px] absolute">
{/* Company Icon SVG */}
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_1042_694)">
<path opacity="0.5" fillRule="evenodd" clipRule="evenodd" d="M4.34342 10.6855C4.66998 11.0162 4.66998 11.5524 4.34341 11.8831L4.32669 11.9C4.00012 12.2307 3.47065 12.2307 3.14409 11.9C2.81753 11.5693 2.81753 11.0331 3.1441 10.7024L3.16082 10.6855C3.48739 10.3548 4.01686 10.3548 4.34342 10.6855Z" fill="url(#paint0_linear_1042_694)" />
<path opacity="0.7" fillRule="evenodd" clipRule="evenodd" d="M8.27545 10.9405C8.60142 11.2718 8.60046 11.808 8.27331 12.1381L5.95697 14.4752C5.62981 14.8053 5.10035 14.8043 4.77437 14.473C4.4484 14.1417 4.44936 13.6056 4.77651 13.2755L7.09285 10.9383C7.42001 10.6082 7.94947 10.6092 8.27545 10.9405Z" fill="url(#paint1_linear_1042_694)" />
<path opacity="0.5" fillRule="evenodd" clipRule="evenodd" d="M11.4179 14.9631C11.6741 14.574 12.1932 14.4688 12.5775 14.7282L12.6277 14.7621C13.012 15.0215 13.1158 15.5473 12.8596 15.9364C12.6034 16.3255 12.0842 16.4307 11.7 16.1713L11.6498 16.1374C11.2655 15.878 11.1617 15.3522 11.4179 14.9631Z" fill="url(#paint2_linear_1042_694)" />
<path opacity="0.7" fillRule="evenodd" clipRule="evenodd" d="M16.9376 10.6347C17.2642 10.9654 17.2642 11.5016 16.9376 11.8323L15.8003 12.9839C15.4738 13.3146 14.9443 13.3146 14.6177 12.9839C14.2912 12.6532 14.2912 12.1171 14.6177 11.7864L15.755 10.6347C16.0816 10.304 16.611 10.304 16.9376 10.6347Z" fill="url(#paint3_linear_1042_694)" />
<path fillRule="evenodd" clipRule="evenodd" d="M16.9544 6.37693C17.2809 6.70762 17.2809 7.24378 16.9544 7.57447L8.55033 16.0847C8.22376 16.4154 7.69429 16.4154 7.36773 16.0847C7.04116 15.754 7.04116 15.2179 7.36773 14.8872L15.7718 6.37693C16.0983 6.04623 16.6278 6.04623 16.9544 6.37693Z" fill="url(#paint4_linear_1042_694)" />
<path fillRule="evenodd" clipRule="evenodd" d="M15.3649 3.75974C15.6915 4.09043 15.6915 4.62659 15.3649 4.95728L10.5315 9.85174C10.205 10.1824 9.67549 10.1824 9.34893 9.85174C9.02236 9.52104 9.02236 8.98489 9.34893 8.65419L14.1823 3.75974C14.5089 3.42905 15.0383 3.42905 15.3649 3.75974Z" fill="url(#paint5_linear_1042_694)" />
<path fillRule="evenodd" clipRule="evenodd" d="M12.8146 2.09918C13.1414 2.42965 13.1417 2.96581 12.8154 3.29672L6.60224 9.59685C6.27589 9.92777 5.74642 9.92813 5.41964 9.59766C5.09285 9.26719 5.0925 8.73103 5.41884 8.40011L11.632 2.09998C11.9583 1.76907 12.4878 1.76871 12.8146 2.09918Z" fill="url(#paint6_linear_1042_694)" />
<path opacity="0.7" fillRule="evenodd" clipRule="evenodd" d="M6.66127 4.11624C6.98727 4.4475 6.98636 4.98366 6.65923 5.31378L4.22582 7.76948C3.89869 8.0996 3.36923 8.09868 3.04322 7.76741C2.71722 7.43615 2.71813 6.9 3.04526 6.56987L5.47867 4.11418C5.8058 3.78405 6.33526 3.78498 6.66127 4.11624Z" fill="url(#paint7_linear_1042_694)" />
<path opacity="0.5" fillRule="evenodd" clipRule="evenodd" d="M8.15116 1.66406C8.613 1.66406 8.98739 2.04318 8.98739 2.51085V2.59553C8.98739 3.0632 8.613 3.44232 8.15116 3.44232C7.68933 3.44232 7.31494 3.0632 7.31494 2.59553V2.51085C7.31494 2.04318 7.68933 1.66406 8.15116 1.66406Z" fill="url(#paint8_linear_1042_694)" />
</g>
<defs>
<filter id="filter0_d_1042_694" x="0.399316" y="-0.400781" width="19.2006" height="22.4016" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feMorphology radius="1.2" operator="erode" in="SourceAlpha" result="effect1_dropShadow_1042_694" />
<feOffset dy="1.8" />
<feGaussianBlur stdDeviation="1.8" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix type="matrix" values="0 0 0 0 0.141176 0 0 0 0 0.141176 0 0 0 0 0.141176 0 0 0 0.1 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1042_694" />
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1042_694" result="shape" />
</filter>
<linearGradient id="paint0_linear_1042_694" x1="3.74376" y1="10.4375" x2="3.74376" y2="12.148" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint1_linear_1042_694" x1="6.52491" y1="10.6914" x2="6.52491" y2="14.7221" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint2_linear_1042_694" x1="12.1387" y1="14.5859" x2="12.1387" y2="16.3136" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint3_linear_1042_694" x1="15.7777" y1="10.3867" x2="15.7777" y2="13.2319" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint4_linear_1042_694" x1="12.161" y1="6.12891" x2="12.161" y2="16.3327" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint5_linear_1042_694" x1="12.3569" y1="3.51172" x2="12.3569" y2="10.0998" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint6_linear_1042_694" x1="9.11711" y1="1.85156" x2="9.11711" y2="9.84527" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint7_linear_1042_694" x1="4.85224" y1="3.86719" x2="4.85224" y2="8.01647" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint8_linear_1042_694" x1="8.15117" y1="1.66406" x2="8.15117" y2="3.44232" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
</defs>
</svg>
</div>
</div>
</div>
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate950 text-base font-medium font-['Inter'] leading-normal">
{org?.name || 'Zitlac Media'}
</div>
</div>
</div>
<div className="relative">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83325 12.4987L9.99992 16.6654L14.1666 12.4987M5.83325 7.4987L9.99992 3.33203L14.1666 7.4987" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</div>
{/* Navigation Menu */}
<div className="self-stretch flex flex-col justify-start items-start gap-5">
<div className="self-stretch flex flex-col justify-start items-start gap-1.5">
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 17.5016V11.3349C7.5 10.8682 7.5 10.6348 7.59083 10.4566C7.67072 10.2998 7.79821 10.1723 7.95501 10.0924C8.13327 10.0016 8.36662 10.0016 8.83333 10.0016H11.1667C11.6334 10.0016 11.8667 10.0016 12.045 10.0924C12.2018 10.1723 12.3293 10.2998 12.4092 10.4566C12.5 10.6348 12.5 10.8682 12.5 11.3349V17.5016M9.18141 2.30492L3.52949 6.70086C3.15168 6.99471 2.96278 7.14163 2.82669 7.32563C2.70614 7.48862 2.61633 7.67224 2.56169 7.86746C2.5 8.08785 2.5 8.32717 2.5 8.8058V14.8349C2.5 15.7683 2.5 16.235 2.68166 16.5916C2.84144 16.9052 3.09641 17.1601 3.41002 17.3199C3.76654 17.5016 4.23325 17.5016 5.16667 17.5016H14.8333C15.7668 17.5016 16.2335 17.5016 16.59 17.3199C16.9036 17.1601 17.1586 16.9052 17.3183 16.5916C17.5 16.235 17.5 15.7683 17.5 14.8349V8.8058C17.5 8.32717 17.5 8.08785 17.4383 7.86746C17.3837 7.67224 17.2939 7.48862 17.1733 7.32563C17.0372 7.14163 16.8483 6.99471 16.4705 6.70086L10.8186 2.30492C10.5258 2.07721 10.3794 1.96335 10.2178 1.91959C10.0752 1.88097 9.92484 1.88097 9.78221 1.91959C9.62057 1.96335 9.47418 2.07721 9.18141 2.30492Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
label="Company Wiki"
isActive={currentPage === 'wiki'}
onClick={() => handleNavigation('wiki')}
/>
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6666 9.16797H6.66659M8.33325 12.5013H6.66659M13.3333 5.83464H6.66659M16.6666 5.66797V14.3346C16.6666 15.7348 16.6666 16.4348 16.3941 16.9696C16.1544 17.44 15.772 17.8225 15.3016 18.0622C14.7668 18.3346 14.0667 18.3346 12.6666 18.3346H7.33325C5.93312 18.3346 5.23306 18.3346 4.69828 18.0622C4.22787 17.8225 3.84542 17.44 3.60574 16.9696C3.33325 16.4348 3.33325 15.7348 3.33325 14.3346V5.66797C3.33325 4.26784 3.33325 3.56777 3.60574 3.03299C3.84542 2.56259 4.22787 2.18014 4.69828 1.94045C5.23306 1.66797 5.93312 1.66797 7.33325 1.66797H12.6666C14.0667 1.66797 14.7668 1.66797 15.3016 1.94045C15.772 2.18014 16.1544 2.56259 16.3941 3.03299C16.6666 3.56777 16.6666 4.26784 16.6666 5.66797Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
label="Submissions"
isActive={currentPage === 'submissions'}
onClick={() => handleNavigation('submissions')}
/>
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1042_720)">
<path d="M10.0001 1.66797C11.0944 1.66797 12.1781 1.88352 13.1891 2.30231C14.2002 2.7211 15.1188 3.33493 15.8926 4.10875C16.6665 4.88257 17.2803 5.80123 17.6991 6.81228C18.1179 7.82332 18.3334 8.90696 18.3334 10.0013M10.0001 1.66797V10.0013M10.0001 1.66797C5.39771 1.66797 1.66675 5.39893 1.66675 10.0013C1.66675 14.6037 5.39771 18.3346 10.0001 18.3346C14.6025 18.3346 18.3334 14.6037 18.3334 10.0013M10.0001 1.66797C14.6025 1.66797 18.3334 5.39893 18.3334 10.0013M18.3334 10.0013L10.0001 10.0013M18.3334 10.0013C18.3334 11.3164 18.0222 12.6128 17.4251 13.7846C16.8281 14.9563 15.9622 15.9701 14.8983 16.7431L10.0001 10.0013" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_1042_720">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
}
label="Reports"
isActive={currentPage === 'reports'}
onClick={() => handleNavigation('reports')}
/>
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.4996 9.58333C17.4996 13.4953 14.3283 16.6667 10.4163 16.6667C9.51896 16.6667 8.66061 16.4998 7.87057 16.1954C7.72612 16.1398 7.6539 16.112 7.59647 16.0987C7.53998 16.0857 7.49908 16.0803 7.44116 16.0781C7.38226 16.0758 7.31764 16.0825 7.18841 16.0958L2.92089 16.537C2.51402 16.579 2.31059 16.6001 2.19058 16.5269C2.08606 16.4631 2.01487 16.3566 1.99592 16.2356C1.97416 16.0968 2.07138 15.9168 2.2658 15.557L3.62885 13.034C3.7411 12.8262 3.79723 12.7223 3.82265 12.6225C3.84776 12.5238 3.85383 12.4527 3.8458 12.3512C3.83766 12.2484 3.79258 12.1147 3.70241 11.8472C3.46281 11.1363 3.33294 10.375 3.33294 9.58333C3.33294 5.67132 6.50426 2.5 10.4163 2.5C14.3283 2.5 17.4996 5.67132 17.4996 9.58333Z" stroke="var(--Brand-Orange, #3399FF)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
label="Chat"
isActive={currentPage === 'chat'}
onClick={() => handleNavigation('chat')}
/>
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1042_728)">
<path d="M7.57508 7.5013C7.771 6.94436 8.15771 6.47472 8.66671 6.17558C9.17571 5.87643 9.77416 5.76708 10.3561 5.8669C10.938 5.96671 11.4658 6.26924 11.846 6.72091C12.2262 7.17258 12.4343 7.74424 12.4334 8.33464C12.4334 10.0013 9.93342 10.8346 9.93342 10.8346M10.0001 14.168H10.0084M18.3334 10.0013C18.3334 14.6037 14.6025 18.3346 10.0001 18.3346C5.39771 18.3346 1.66675 14.6037 1.66675 10.0013C1.66675 5.39893 5.39771 1.66797 10.0001 1.66797C14.6025 1.66797 18.3334 5.39893 18.3334 10.0013Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_1042_728">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
}
label="Help"
isActive={currentPage === 'help'}
onClick={() => handleNavigation('help')}
/>
</div>
</div>
</div>
{/* Bottom Section with Settings and Company Report Builder */}
<div className="self-stretch flex flex-col justify-start items-start gap-3">
<NavItem
icon={
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1042_733)">
<path d="M10.0001 12.5013C11.3808 12.5013 12.5001 11.382 12.5001 10.0013C12.5001 8.62059 11.3808 7.5013 10.0001 7.5013C8.61937 7.5013 7.50008 8.62059 7.50008 10.0013C7.50008 11.382 8.61937 12.5013 10.0001 12.5013Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M15.6061 12.274C15.5053 12.5025 15.4752 12.756 15.5198 13.0017C15.5643 13.2475 15.6815 13.4743 15.8561 13.6528L15.9016 13.6983C16.0425 13.839 16.1542 14.0061 16.2305 14.19C16.3067 14.374 16.346 14.5711 16.346 14.7702C16.346 14.9694 16.3067 15.1665 16.2305 15.3505C16.1542 15.5344 16.0425 15.7015 15.9016 15.8422C15.7609 15.9831 15.5938 16.0948 15.4098 16.1711C15.2259 16.2473 15.0287 16.2866 14.8296 16.2866C14.6305 16.2866 14.4334 16.2473 14.2494 16.1711C14.0655 16.0948 13.8984 15.9831 13.7577 15.8422L13.7122 15.7968C13.5337 15.6221 13.3069 15.505 13.0611 15.4604C12.8154 15.4158 12.5619 15.4459 12.3334 15.5468C12.1093 15.6428 11.9183 15.8022 11.7836 16.0055C11.649 16.2087 11.5768 16.4469 11.5758 16.6907V16.8195C11.5758 17.2213 11.4162 17.6067 11.1321 17.8909C10.8479 18.175 10.4625 18.3346 10.0607 18.3346C9.65884 18.3346 9.27346 18.175 8.98931 17.8909C8.70517 17.6067 8.54554 17.2213 8.54554 16.8195V16.7513C8.53967 16.5005 8.45851 16.2574 8.31259 16.0533C8.16667 15.8493 7.96276 15.6939 7.72735 15.6074C7.49886 15.5065 7.24539 15.4764 6.99964 15.521C6.75388 15.5656 6.52711 15.6827 6.34857 15.8574L6.30311 15.9028C6.1624 16.0437 5.99529 16.1554 5.81135 16.2317C5.62742 16.3079 5.43026 16.3472 5.23114 16.3472C5.03203 16.3472 4.83487 16.3079 4.65093 16.2317C4.46699 16.1554 4.29989 16.0437 4.15917 15.9028C4.0183 15.7621 3.90654 15.595 3.83029 15.4111C3.75405 15.2271 3.7148 15.03 3.7148 14.8308C3.7148 14.6317 3.75405 14.4346 3.83029 14.2506C3.90654 14.0667 4.0183 13.8996 4.15917 13.7589L4.20463 13.7134C4.37928 13.5349 4.49643 13.3081 4.54099 13.0624C4.58555 12.8166 4.55547 12.5631 4.45463 12.3346C4.35859 12.1106 4.19914 11.9195 3.99589 11.7849C3.79264 11.6503 3.55447 11.578 3.31069 11.5771H3.1819C2.78006 11.5771 2.39467 11.4174 2.11053 11.1333C1.82638 10.8491 1.66675 10.4638 1.66675 10.0619C1.66675 9.66007 1.82638 9.27468 2.11053 8.99053C2.39467 8.70639 2.78006 8.54676 3.1819 8.54676H3.25008C3.50083 8.54089 3.74402 8.45973 3.94804 8.31381C4.15205 8.1679 4.30744 7.96398 4.39402 7.72858C4.49487 7.50008 4.52495 7.24661 4.48039 7.00086C4.43583 6.7551 4.31867 6.52833 4.14402 6.34979L4.09857 6.30433C3.95769 6.16362 3.84594 5.99651 3.76969 5.81258C3.69344 5.62864 3.65419 5.43148 3.65419 5.23236C3.65419 5.03325 3.69344 4.83609 3.76969 4.65215C3.84594 4.46821 3.95769 4.30111 4.09857 4.16039C4.23928 4.01952 4.40639 3.90776 4.59032 3.83151C4.77426 3.75527 4.97142 3.71602 5.17054 3.71602C5.36965 3.71602 5.56681 3.75527 5.75075 3.83151C5.93469 3.90776 6.10179 4.01952 6.24251 4.16039L6.28796 4.20585C6.46651 4.3805 6.69328 4.49765 6.93903 4.54221C7.18478 4.58677 7.43825 4.55669 7.66675 4.45585H7.72735C7.95142 4.35982 8.14252 4.20036 8.27712 3.99711C8.41172 3.79386 8.48396 3.55569 8.48493 3.31191V3.18312C8.48493 2.78128 8.64456 2.39589 8.92871 2.11175C9.21285 1.8276 9.59824 1.66797 10.0001 1.66797C10.4019 1.66797 10.7873 1.8276 11.0715 2.11175C11.3556 2.39589 11.5152 2.78128 11.5152 3.18312V3.2513C11.5162 3.49508 11.5884 3.73325 11.723 3.9365C11.8576 4.13975 12.0487 4.29921 12.2728 4.39524C12.5013 4.49609 12.7548 4.52617 13.0005 4.48161C13.2463 4.43705 13.4731 4.31989 13.6516 4.14524L13.6971 4.09979C13.8378 3.95891 14.0049 3.84716 14.1888 3.77091C14.3727 3.69466 14.5699 3.65541 14.769 3.65541C14.9681 3.65541 15.1653 3.69466 15.3492 3.77091C15.5332 3.84716 15.7003 3.95891 15.841 4.09979C15.9819 4.2405 16.0936 4.40761 16.1699 4.59154C16.2461 4.77548 16.2854 4.97264 16.2854 5.17176C16.2854 5.37087 16.2461 5.56803 16.1699 5.75197C16.0936 5.93591 15.9819 6.10301 15.841 6.24373L15.7955 6.28918C15.6209 6.46773 15.5037 6.6945 15.4592 6.94025C15.4146 7.186 15.4447 7.43947 15.5455 7.66797V7.72858C15.6416 7.95264 15.801 8.14374 16.0043 8.27834C16.2075 8.41295 16.4457 8.48518 16.6895 8.48615H16.8183C17.2201 8.48615 17.6055 8.64578 17.8896 8.92993C18.1738 9.21407 18.3334 9.59946 18.3334 10.0013C18.3334 10.4031 18.1738 10.7885 17.8896 11.0727C17.6055 11.3568 17.2201 11.5165 16.8183 11.5165H16.7501C16.5063 11.5174 16.2681 11.5897 16.0649 11.7243C15.8616 11.8589 15.7022 12.05 15.6061 12.274Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_1042_733">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
}
label="Settings"
isActive={currentPage === 'settings'}
onClick={() => handleNavigation('settings')}
/>
{/* Company Report Builder Card */}
<div className="self-stretch bg-Neutrals-NeutralSlate0 rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 flex flex-col justify-start items-start overflow-hidden">
<div className="self-stretch h-24 relative">
<div className="w-60 h-32 left-0 top-[-0.50px] absolute bg-gradient-to-b from-black to-black/0" />
<div className="w-60 p-3 left-[18.12px] top-[42.52px] absolute origin-top-left rotate-[-28.34deg] bg-Neutrals-NeutralSlate0 rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-Neutrals-NeutralSlate0 rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
</div>
<div className="self-stretch p-3 flex flex-col justify-start items-start gap-1">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate800 text-sm font-semibold font-['Inter'] leading-tight">
Build {org?.name || '[Company]'}'s Report
</div>
<div className="self-stretch justify-start text-Neutrals-NeutralSlate500 text-xs font-normal font-['Inter'] leading-none">
Share this form with your team members to capture valuable info about your company to train Auditly.
</div>
</div>
<div className="self-stretch px-3 pb-3 flex flex-col justify-start items-start gap-8">
<div className="self-stretch inline-flex justify-start items-start gap-2">
<div className="flex-1 px-3 py-1.5 bg-Button-Secondary rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">Invite</div>
</div>
<div className="relative">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.99992 3.33203V12.6654M3.33325 7.9987H12.6666" stroke="var(--Neutrals-NeutralSlate950, #0A0D12)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</div>
<div className="flex-1 px-3 py-1.5 bg-Brand-Orange rounded-[999px] outline outline-1 outline-offset-[-1px] outline-blue-400 flex justify-center items-center gap-0.5 overflow-hidden">
<div className="relative">
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.97179 12.2442L8.02898 13.1871C6.72723 14.4888 4.61668 14.4888 3.31493 13.1871C2.01319 11.8853 2.01319 9.77476 3.31493 8.47301L4.25774 7.5302M12.743 8.47301L13.6858 7.5302C14.9876 6.22845 14.9876 4.1179 13.6858 2.81615C12.3841 1.51441 10.2735 1.51441 8.97179 2.81615L8.02898 3.75896M6.16705 10.3349L10.8337 5.66826" stroke="var(--Other-White, white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Other-White text-sm font-medium font-['Inter'] leading-tight">Copy</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default ChatSidebar;

View File

@@ -0,0 +1,244 @@
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 };

View File

@@ -0,0 +1,118 @@
import React from 'react';
interface Message {
id: string;
text: string;
isUser: boolean;
timestamp: number;
files?: string[];
}
interface MessageBubbleProps {
message: Message;
}
const MessageBubble: React.FC<MessageBubbleProps> = ({ message }) => {
const formatTime = (timestamp: number) => {
return new Date(timestamp).toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit'
});
};
if (message.isUser) {
return (
<div className="flex justify-end mb-4">
<div className="max-w-[70%] flex flex-col items-end">
<div className="bg-Brand-Orange text-white px-4 py-3 rounded-2xl rounded-br-md">
{message.files && message.files.length > 0 && (
<div className="mb-2 flex flex-wrap gap-2">
{message.files.map((file, index) => (
<div key={index} className="px-2 py-1 bg-white/20 rounded text-xs">
📎 {file}
</div>
))}
</div>
)}
<div className="text-sm leading-relaxed">{message.text}</div>
</div>
<div className="text-xs text-Neutrals-NeutralSlate400 mt-1">
{formatTime(message.timestamp)}
</div>
</div>
</div>
);
}
return (
<div className="flex justify-start mb-4">
<div className="max-w-[85%] flex items-start gap-3">
{/* AI Avatar */}
<div className="w-8 h-8 bg-gradient-to-br from-Brand-Orange to-orange-600 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 2C8.73438 2 9.375 2.64062 9.375 3.375V4.5C9.375 5.23438 8.73438 5.875 8 5.875C7.26562 5.875 6.625 5.23438 6.625 4.5V3.375C6.625 2.64062 7.26562 2 8 2ZM8 10.125C8.73438 10.125 9.375 10.7656 9.375 11.5V12.625C9.375 13.3594 8.73438 14 8 14C7.26562 14 6.625 13.3594 6.625 12.625V11.5C6.625 10.7656 7.26562 10.125 8 10.125ZM12.625 6.625C13.3594 6.625 14 7.26562 14 8C14 8.73438 13.3594 9.375 12.625 9.375H11.5C10.7656 9.375 10.125 8.73438 10.125 8C10.125 7.26562 10.7656 6.625 11.5 6.625H12.625ZM5.875 8C5.875 8.73438 5.23438 9.375 4.5 9.375H3.375C2.64062 9.375 2 8.73438 2 8C2 7.26562 2.64062 6.625 3.375 6.625H4.5C5.23438 6.625 5.875 7.26562 5.875 8Z" fill="white" />
</svg>
</div>
<div className="flex flex-col">
<div className="bg-Neutrals-NeutralSlate100 text-Neutrals-NeutralSlate950 px-4 py-3 rounded-2xl rounded-bl-md">
<div className="text-sm leading-relaxed whitespace-pre-wrap">{message.text}</div>
</div>
<div className="text-xs text-Neutrals-NeutralSlate400 mt-1">
AI {formatTime(message.timestamp)}
</div>
</div>
</div>
</div>
);
};
interface LoadingIndicatorProps {
className?: string;
}
const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({ className = '' }) => (
<div className={`flex justify-start mb-4 ${className}`}>
<div className="flex items-start gap-3">
{/* AI Avatar */}
<div className="w-8 h-8 bg-gradient-to-br from-Brand-Orange to-orange-600 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 2C8.73438 2 9.375 2.64062 9.375 3.375V4.5C9.375 5.23438 8.73438 5.875 8 5.875C7.26562 5.875 6.625 5.23438 6.625 4.5V3.375C6.625 2.64062 7.26562 2 8 2ZM8 10.125C8.73438 10.125 9.375 10.7656 9.375 11.5V12.625C9.375 13.3594 8.73438 14 8 14C7.26562 14 6.625 13.3594 6.625 12.625V11.5C6.625 10.7656 7.26562 10.125 8 10.125ZM12.625 6.625C13.3594 6.625 14 7.26562 14 8C14 8.73438 13.3594 9.375 12.625 9.375H11.5C10.7656 9.375 10.125 8.73438 10.125 8C10.125 7.26562 10.7656 6.625 11.5 6.625H12.625ZM5.875 8C5.875 8.73438 5.23438 9.375 4.5 9.375H3.375C2.64062 9.375 2 8.73438 2 8C2 7.26562 2.64062 6.625 3.375 6.625H4.5C5.23438 6.625 5.875 7.26562 5.875 8Z" fill="white" />
</svg>
</div>
<div className="bg-Neutrals-NeutralSlate100 px-4 py-3 rounded-2xl rounded-bl-md">
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-Neutrals-NeutralSlate400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
<div className="w-2 h-2 bg-Neutrals-NeutralSlate400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
<div className="w-2 h-2 bg-Neutrals-NeutralSlate400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
</div>
</div>
</div>
</div>
);
interface MessageThreadProps {
messages: Message[];
isLoading?: boolean;
className?: string;
}
const MessageThread: React.FC<MessageThreadProps> = ({
messages,
isLoading = false,
className = ''
}) => {
return (
<div className={`flex flex-col ${className}`}>
{messages.map((message) => (
<MessageBubble key={message.id} message={message} />
))}
{isLoading && <LoadingIndicator />}
</div>
);
};
export default MessageThread;
export { MessageBubble, LoadingIndicator };

5
components/chat/index.ts Normal file
View File

@@ -0,0 +1,5 @@
export { default as ChatSidebar } from './ChatSidebar';
export { default as ChatLayout } from './ChatLayout';
export { default as ChatEmptyState } from './ChatEmptyState';
export { default as MessageThread } from './MessageThread';
export { default as FileUploadInput } from './FileUploadInput';

View File

@@ -1,106 +1,225 @@
import React from 'react';
import { EmployeeQuestion } from '../../employeeQuestions';
import { QuestionInput } from '../ui/QuestionInput';
import React, { ReactNode } from 'react';
interface EnhancedFigmaQuestionProps {
questionNumber?: string | number;
question: EmployeeQuestion;
answer?: string;
onAnswerChange?: (value: string) => void;
interface FigmaQuestionProps {
question: string;
description?: string;
children: ReactNode;
onBack?: () => void;
onNext?: () => void;
showNavigation?: boolean;
nextLabel?: string;
backLabel?: string;
nextDisabled?: boolean;
backDisabled?: boolean;
nextText?: string;
backText?: string;
showBackButton?: boolean;
currentStep?: number;
totalSteps?: number;
stepTitle?: string;
className?: string;
}
export const EnhancedFigmaQuestion: React.FC<EnhancedFigmaQuestionProps> = ({
questionNumber = 'Q',
export const EnhancedFigmaQuestion: React.FC<FigmaQuestionProps> = ({
question,
answer = '',
onAnswerChange,
description,
children,
onBack,
onNext,
showNavigation = true,
nextLabel = 'Next',
backLabel = 'Back',
className = ''
nextDisabled = false,
backDisabled = false,
nextText = "Next",
backText = "Back",
showBackButton = true,
currentStep = 1,
totalSteps = 8,
stepTitle = "Company Overview & Mission.",
className = ""
}) => {
// Generate the progress indicator dots
const renderProgressDots = () => {
const dots = [];
for (let i = 0; i < totalSteps; i++) {
if (i < currentStep - 1) {
// Completed steps - elongated orange
dots.push(
<div key={i} className="w-6 h-1 bg-Brand-Orange rounded-3xl" />
);
} else if (i === currentStep - 1) {
// Current step - elongated orange
dots.push(
<div key={i} className="w-6 h-1 bg-Brand-Orange rounded-3xl" />
);
} else {
// Future steps - small gray circles
dots.push(
<div key={i} data-svg-wrapper>
<svg width="4" height="4" viewBox="0 0 4 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="4" height="4" rx="2" fill="var(--Neutrals-NeutralSlate300, #D5D7DA)" />
</svg>
</div>
);
}
}
return dots;
};
return (
<div className={`w-[1440px] h-[810px] py-6 relative bg-Neutrals-NeutralSlate0 inline-flex flex-col justify-center items-center gap-9 ${className}`}>
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
<div className="self-stretch flex flex-col justify-start items-start gap-8">
<div className="self-stretch text-center justify-start text-Neutrals-NeutralSlate950 text-2xl font-medium font-['Neue_Montreal'] leading-normal">
{question}
</div>
{children}
</div>
<div className="self-stretch inline-flex justify-start items-start gap-2">
{showBackButton && (
<div
data-property-1="Secondary"
data-show-icon-left="false"
data-show-icon-right="false"
data-show-text="true"
data-size="Big"
className={`h-12 px-8 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-center items-center gap-1 overflow-hidden ${backDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-Neutrals-NeutralSlate200'}`}
onClick={!backDisabled ? onBack : undefined}
>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">
{backText}
</div>
</div>
</div>
)}
<div
data-property-1="Primiary"
data-show-icon-left="false"
data-show-icon-right="false"
data-show-text="true"
data-size="Big"
className={`flex-1 h-12 px-4 py-3.5 bg-Brand-Orange rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden ${nextDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:opacity-90'}`}
onClick={!nextDisabled ? onNext : undefined}
>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Other-White text-sm font-medium font-['Inter'] leading-tight">
{nextText}
</div>
</div>
</div>
</div>
</div>
{/* Step indicator - top left */}
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
<div className="justify-start text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] uppercase leading-none">
{currentStep} of {totalSteps}
</div>
</div>
{/* Skip button - top right */}
<div className="px-3 py-1.5 left-[1363px] top-[24px] absolute bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-Neutrals-NeutralSlate200">
<div className="justify-start text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] leading-none">
Skip
</div>
</div>
{/* Progress indicator and title - top center */}
<div className="w-[464px] max-w-[464px] min-w-[464px] left-[488px] top-[24px] absolute flex flex-col justify-start items-center gap-4">
<div className="p-4 bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
{renderProgressDots()}
</div>
<div className="self-stretch text-center justify-start text-Neutrals-NeutralSlate500 text-base font-medium font-['Neue_Montreal'] leading-normal">
{stepTitle}
</div>
</div>
</div>
);
};
// Question Card Component (for Q&A style layout)
interface FigmaQuestionCardProps {
question: string;
description?: string;
children: ReactNode;
className?: string;
}
export const FigmaQuestionCard: React.FC<FigmaQuestionCardProps> = ({
question,
description,
children,
className = ""
}) => {
return (
<div className={`w-full max-w-[600px] px-5 pt-5 pb-6 bg-white dark:bg-gray-800 rounded-2xl border border-gray-200 dark:border-gray-700 inline-flex flex-col justify-end items-end gap-4 ${className}`}>
{/* Question Header */}
<div className={`w-full px-5 pt-5 pb-6 bg-Other-White rounded-2xl outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-end items-end gap-4 ${className}`}>
<div className="self-stretch inline-flex justify-start items-start gap-3">
<div className="justify-start text-gray-400 text-xl font-medium font-['Inter'] leading-loose">
{questionNumber}
</div>
<div className="justify-start text-zinc-300 text-xl font-medium font-['Inter'] leading-loose">Q</div>
<div className="flex-1 inline-flex flex-col justify-start items-start gap-2">
<div className="self-stretch justify-start text-[--text-primary] text-xl font-semibold font-['Inter'] leading-loose">
{question.prompt}
</div>
<div className="self-stretch justify-start text-[--text-secondary] text-sm font-normal font-['Inter'] leading-tight">
{question.required ? 'Required' : 'Optional'} {question.category}
{question.type && `${question.type}`}
<div className="self-stretch justify-start text-Neutrals-NeutralSlate950 text-xl font-semibold font-['Inter'] leading-loose">
{question}
</div>
{description && (
<div className="self-stretch justify-start text-Neutrals-NeutralSlate500 text-sm font-normal font-['Inter'] leading-tight">
{description}
</div>
)}
</div>
</div>
{/* Separator */}
<div className="self-stretch h-px bg-gray-200 dark:bg-gray-700"></div>
{/* Answer Section */}
<div className="self-stretch inline-flex justify-start items-start gap-3">
<div className="justify-start text-gray-400 text-xl font-medium font-['Inter'] leading-loose">A</div>
<div className="self-stretch h-0 rotate-90 shadow-[0px_1.5px_1.5px_0px_rgba(255,255,255,1.00)] outline outline-1 outline-offset-[-0.50px] border-Neutrals-NeutralSlate200" />
<div className="self-stretch inline-flex justify-start items-center gap-3">
<div className="justify-start text-zinc-300 text-xl font-medium font-['Inter'] leading-loose">A</div>
<div className="flex-1">
<QuestionInput
question={question}
value={answer}
onChange={onAnswerChange || (() => { })}
className="border-0 bg-transparent focus:ring-0 p-0"
/>
{children}
</div>
</div>
</div>
);
};
{/* Navigation */}
{showNavigation && (
<div className="inline-flex justify-start items-center gap-3">
{onBack && (
<button
onClick={onBack}
className="px-4 py-3.5 bg-gray-100 dark:bg-gray-700 rounded-full flex justify-center items-center gap-1 overflow-hidden hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
>
<div className="relative">
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 15L8 10L13 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-[--text-primary] text-sm font-medium font-['Inter'] leading-tight">
{backLabel}
</div>
</div>
</button>
)}
// Enhanced input that matches Figma designs
interface EnhancedFigmaInputProps {
placeholder?: string;
value?: string;
onChange?: (value: string) => void;
multiline?: boolean;
rows?: number;
className?: string;
}
{onNext && (
<button
onClick={onNext}
className="px-4 py-3.5 bg-blue-500 rounded-full border-2 border-blue-400 flex justify-center items-center gap-1 overflow-hidden hover:bg-blue-600 transition-colors"
>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-white text-sm font-medium font-['Inter'] leading-tight">
{nextLabel}
</div>
</div>
<div className="relative">
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 15L13 10L8 5" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</button>
)}
export const EnhancedFigmaInput: React.FC<EnhancedFigmaInputProps> = ({
placeholder = "Type your answer....",
value = "",
onChange,
multiline = false,
rows = 4,
className = ""
}) => {
const baseClasses = "self-stretch min-h-40 p-5 relative bg-Neutrals-NeutralSlate100 rounded-xl inline-flex justify-start items-start gap-2.5 overflow-hidden";
const inputClasses = "flex-1 justify-start text-Neutrals-NeutralSlate500 text-base font-normal font-['Inter'] leading-normal bg-transparent border-none outline-none resize-none";
if (multiline) {
return (
<div className={`${baseClasses} ${className}`}>
<textarea
value={value}
onChange={(e) => onChange?.(e.target.value)}
placeholder={placeholder}
rows={rows}
className={inputClasses}
/>
<div className="w-3 h-3 absolute right-[18px] bottom-[18px]">
<div className="w-2 h-2 left-[2px] top-[2px] absolute outline outline-1 outline-offset-[-0.50px] outline-Neutrals-NeutralSlate500" />
<div className="w-1 h-1 left-[7px] top-[7px] absolute outline outline-1 outline-offset-[-0.50px] outline-Neutrals-NeutralSlate500" />
</div>
)}
</div>
);
}
return (
<div className={`${baseClasses} ${className}`}>
<input
value={value}
onChange={(e) => onChange?.(e.target.value)}
placeholder={placeholder}
className={inputClasses}
/>
</div>
);
};

View File

@@ -11,6 +11,7 @@ interface FigmaInputProps {
onButtonClick?: () => void;
className?: string;
required?: boolean;
showLabel?: boolean;
}
export const FigmaInput: React.FC<FigmaInputProps> = ({
@@ -23,20 +24,24 @@ export const FigmaInput: React.FC<FigmaInputProps> = ({
buttonText,
onButtonClick,
className = '',
required = false
required = false,
showLabel = true
}) => {
return (
<div className={`w-[464px] inline-flex flex-col justify-start items-start gap-2 ${className}`}>
{label && (
<div className={`self-stretch flex flex-col justify-start items-start gap-2 ${className}`}>
{showLabel && label && (
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
<div className="justify-start text-Neutrals-NeutralSlate800 text-sm font-medium font-['Inter'] leading-tight">
{label} {required && <span className="text-red-500">*</span>}
<div className="justify-start text-Neutrals-NeutralSlate900 text-sm font-normal font-['Inter'] leading-tight">
{label}
</div>
{required && (
<div className="justify-start text-Brand-Orange text-sm font-medium font-['Inter'] leading-tight">*</div>
)}
</div>
)}
<div className="self-stretch inline-flex justify-start items-start gap-2">
<div className="flex-1 px-4 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-start items-center gap-2 overflow-hidden">
<div className="self-stretch flex flex-col justify-start items-start gap-1">
<div className="self-stretch px-4 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
{icon && (
<div data-svg-wrapper className="relative">
{icon}
@@ -47,20 +52,79 @@ export const FigmaInput: React.FC<FigmaInputProps> = ({
value={value}
onChange={onChange}
placeholder={placeholder}
className="flex-1 bg-transparent outline-none text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight placeholder:text-Neutrals-NeutralSlate500"
className="flex-1 justify-start text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none placeholder:text-Neutrals-NeutralSlate500"
/>
</div>
</div>
{buttonText && (
<button
onClick={onButtonClick}
className="w-32 max-w-32 px-4 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-Neutrals-NeutralSlate200 transition-colors"
{buttonText && (
<button
onClick={onButtonClick}
className="w-32 max-w-32 px-4 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-Neutrals-NeutralSlate200 transition-colors"
>
<div className="justify-center text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] leading-tight">
{buttonText}
</div>
</button>
)}
</div>
);
};
// Select component for dropdowns
interface FigmaSelectProps {
label?: string;
value?: string;
onChange?: (e: React.ChangeEvent<HTMLSelectElement>) => void;
options: { value: string; label: string }[];
className?: string;
required?: boolean;
placeholder?: string;
}
export const FigmaSelect: React.FC<FigmaSelectProps> = ({
label,
value,
onChange,
options,
className = '',
required = false,
placeholder = 'Select option'
}) => {
return (
<div className={`self-stretch flex flex-col justify-start items-start gap-2 ${className}`}>
{label && (
<div className="self-stretch inline-flex justify-start items-center gap-0.5">
<div className="justify-start text-Neutrals-NeutralSlate900 text-sm font-normal font-['Inter'] leading-tight">
{label}
</div>
{required && (
<div className="justify-start text-Brand-Orange text-sm font-medium font-['Inter'] leading-tight">*</div>
)}
</div>
)}
<div className="self-stretch flex flex-col justify-start items-start gap-1">
<div className="self-stretch px-4 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] inline-flex justify-start items-center gap-2 overflow-hidden">
<select
value={value}
onChange={onChange}
className="flex-1 justify-start text-Neutrals-NeutralSlate950 text-sm font-normal font-['Inter'] leading-tight bg-transparent border-none outline-none appearance-none"
>
<div className="justify-center text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] leading-tight">
{buttonText}
</div>
</button>
)}
<option value="" className="text-Neutrals-NeutralSlate500">{placeholder}</option>
{options.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</select>
{/* Custom dropdown arrow */}
<div className="pointer-events-none">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6L8 10L12 6" stroke="var(--Neutrals-NeutralSlate500)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</div>
</div>
</div>
);

View File

@@ -0,0 +1,42 @@
import React from 'react';
interface FigmaMultipleChoiceProps {
options: string[];
selectedValue?: string;
onSelect?: (value: string) => void;
className?: string;
}
export const FigmaMultipleChoice: React.FC<FigmaMultipleChoiceProps> = ({
options,
selectedValue,
onSelect,
className = ""
}) => {
return (
<div className={`self-stretch inline-flex justify-center items-center gap-3 ${className}`}>
{options.map((option, index) => {
const isSelected = selectedValue === option;
return (
<div
key={index}
className={`flex-1 h-20 relative rounded-[999px] overflow-hidden cursor-pointer transition-colors ${isSelected
? 'bg-Neutrals-NeutralSlate800'
: 'bg-Neutrals-NeutralSlate100 hover:bg-Neutrals-NeutralSlate200'
}`}
onClick={() => onSelect?.(option)}
>
<div className={`absolute inset-0 flex items-center justify-center text-center text-base font-normal font-['Inter'] leading-normal ${isSelected
? 'text-Neutrals-NeutralSlate0'
: 'text-Neutrals-NeutralSlate950'
}`}>
{option}
</div>
</div>
);
})}
</div>
);
};
export default FigmaMultipleChoice;

View File

@@ -124,4 +124,155 @@ export const FigmaQuestion: React.FC<FigmaQuestionProps> = ({
);
};
export default FigmaQuestion;
// Progress Bar Component
export const FigmaProgressBar: React.FC<{ currentStep: number; totalSteps: number }> = ({ currentStep, totalSteps }) => {
return (
<div className="p-4 bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
{Array.from({ length: totalSteps }, (_, index) => {
const isActive = index < currentStep;
const isFirst = index === 0;
return (
<div key={index}>
<svg width={isFirst ? "24" : "4"} height="4" viewBox={`0 0 ${isFirst ? "24" : "4"} 4`} fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width={isFirst ? "24" : "4"} height="4" rx="2" fill={isActive ? "var(--Brand-Orange, #5E48FC)" : "var(--Neutrals-NeutralSlate300, #D5D7DA)"} />
</svg>
</div>
);
})}
</div>
);
};
// Rating Scale Component (1-10)
export const FigmaRatingScale: React.FC<{
question: string;
leftLabel: string;
rightLabel: string;
value?: number;
onChange: (value: number) => void;
scale?: number;
}> = ({ question, leftLabel, rightLabel, value, onChange, scale = 10 }) => {
return (
<div className="w-[1440px] h-[810px] py-6 relative bg-Neutrals-NeutralSlate0 inline-flex flex-col justify-center items-center gap-9">
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-center items-center gap-12">
<div className="flex flex-col justify-center items-center gap-8">
<div className="self-stretch text-center justify-start text-Neutrals-NeutralSlate950 text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
<div className="inline-flex justify-center items-center gap-3">
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">{leftLabel}</div>
{Array.from({ length: scale }, (_, index) => {
const number = index + 1;
const isSelected = value === number;
return (
<div
key={number}
onClick={() => onChange(number)}
className={`w-12 h-12 relative rounded-[576.35px] overflow-hidden cursor-pointer transition-colors ${isSelected ? 'bg-Brand-Orange' : 'bg-Neutrals-NeutralSlate100 hover:bg-Neutrals-NeutralSlate200'
}`}
>
<div className={`absolute inset-0 flex items-center justify-center text-xl font-medium font-['Inter'] leading-7 ${isSelected ? 'text-white' : 'text-Neutrals-NeutralSlate950'
}`}>
{number}
</div>
</div>
);
})}
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">{rightLabel}</div>
</div>
</div>
</div>
</div>
);
};
// Text Area Component
export const FigmaTextArea: React.FC<{
question: string;
value?: string;
onChange: (value: string) => void;
placeholder?: string;
}> = ({ question, value, onChange, placeholder = "Type your answer...." }) => {
return (
<div className="w-[1440px] h-[810px] py-6 relative bg-Neutrals-NeutralSlate0 inline-flex flex-col justify-center items-center gap-9">
<div className="w-full max-w-[464px] min-w-[464px] flex flex-col justify-start items-start gap-12">
<div className="self-stretch flex flex-col justify-start items-start gap-8">
<div className="self-stretch text-center justify-start text-Neutrals-NeutralSlate950 text-2xl font-medium font-['Neue_Montreal'] leading-normal">{question}</div>
<div className="self-stretch min-h-40 p-5 relative bg-Neutrals-NeutralSlate100 rounded-xl inline-flex justify-start items-start gap-2.5">
<textarea
value={value}
onChange={(e) => onChange(e.target.value)}
className="flex-1 bg-transparent text-Neutrals-NeutralSlate950 text-base font-normal font-['Inter'] leading-normal placeholder:text-Neutrals-NeutralSlate500 outline-none resize-none"
placeholder={placeholder}
rows={6}
/>
<div className="w-3 h-3 absolute right-5 bottom-5">
<div className="w-2 h-2 absolute top-0.5 left-0.5 outline outline-1 outline-offset-[-0.50px] outline-Neutrals-NeutralSlate500" />
<div className="w-1 h-1 absolute bottom-0 right-0 outline outline-1 outline-offset-[-0.50px] outline-Neutrals-NeutralSlate500" />
</div>
</div>
</div>
</div>
</div>
);
};
// Navigation Buttons Component
export const FigmaNavigationButtons: React.FC<{
onBack?: () => void;
onNext: () => void;
onSkip?: () => void;
nextDisabled?: boolean;
currentStep?: number;
totalSteps?: number;
}> = ({ onBack, onNext, onSkip, nextDisabled = false, currentStep, totalSteps }) => {
return (
<>
{/* Progress indicator */}
{currentStep && totalSteps && (
<div className="px-3 py-1.5 left-[24px] top-[24px] absolute bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden">
<div className="justify-start text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] uppercase leading-none">{currentStep} of {totalSteps}</div>
</div>
)}
{/* Skip button */}
{onSkip && (
<div
onClick={onSkip}
className="px-3 py-1.5 left-[1363px] top-[24px] absolute bg-Neutrals-NeutralSlate100 rounded-[50px] inline-flex justify-center items-center gap-2 overflow-hidden cursor-pointer hover:bg-Neutrals-NeutralSlate200"
>
<div className="justify-start text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] leading-none">Skip</div>
</div>
)}
{/* Progress bar */}
{currentStep && totalSteps && (
<div className="w-[464px] max-w-[464px] min-w-[464px] left-[488px] top-[24px] absolute flex flex-col justify-start items-center gap-4">
<FigmaProgressBar currentStep={currentStep} totalSteps={totalSteps} />
</div>
)}
{/* Navigation buttons */}
<div className="self-stretch inline-flex justify-start items-start gap-2">
{onBack && (
<button
onClick={onBack}
className="h-12 px-8 py-3.5 bg-Neutrals-NeutralSlate100 rounded-[999px] flex justify-center items-center gap-1 overflow-hidden hover:bg-Neutrals-NeutralSlate200"
>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">Back</div>
</div>
</button>
)}
<button
onClick={onNext}
disabled={nextDisabled}
className="flex-1 h-12 px-4 py-3.5 bg-Brand-Orange rounded-[999px] outline outline-2 outline-offset-[-2px] outline-blue-400 flex justify-center items-center gap-1 overflow-hidden disabled:opacity-50 disabled:cursor-not-allowed"
>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Other-White text-sm font-medium font-['Inter'] leading-tight">Next</div>
</div>
</button>
</div>
</>
);
};

View File

@@ -0,0 +1,241 @@
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
interface SidebarProps {
companyName?: string;
collapsed?: boolean;
}
export default function Sidebar({ companyName = "Zitlac Media", collapsed = false }: SidebarProps) {
const location = useLocation();
const navigate = useNavigate();
const navItems = [
{
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.5 17.5V11.3333C7.5 10.8666 7.5 10.6333 7.59083 10.455C7.67072 10.2982 7.79821 10.1707 7.95501 10.0908C8.13327 9.99999 8.36662 9.99999 8.83333 9.99999H11.1667C11.6334 9.99999 11.8667 9.99999 12.045 10.0908C12.2018 10.1707 12.3293 10.2982 12.4092 10.455C12.5 10.6333 12.5 10.8666 12.5 11.3333V17.5M9.18141 2.30333L3.52949 6.69927C3.15168 6.99312 2.96278 7.14005 2.82669 7.32405C2.70614 7.48704 2.61633 7.67065 2.56169 7.86588C2.5 8.08627 2.5 8.32558 2.5 8.80421V14.8333C2.5 15.7667 2.5 16.2335 2.68166 16.59C2.84144 16.9036 3.09641 17.1585 3.41002 17.3183C3.76654 17.5 4.23325 17.5 5.16667 17.5H14.8333C15.7668 17.5 16.2335 17.5 16.59 17.3183C16.9036 17.1585 17.1586 16.9036 17.3183 16.59C17.5 16.2335 17.5 15.7667 17.5 14.8333V8.80421C17.5 8.32558 17.5 8.08627 17.4383 7.86588C17.3837 7.67065 17.2939 7.48704 17.1733 7.32405C17.0372 7.14005 16.8483 6.99312 16.4705 6.69927L10.8186 2.30333C10.5258 2.07562 10.3794 1.96177 10.2178 1.918C10.0752 1.87938 9.92484 1.87938 9.78221 1.918C9.62057 1.96177 9.47418 2.07562 9.18141 2.30333Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
label: "Company Wiki",
path: "/company-wiki",
active: location.pathname === "/company-wiki"
},
{
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6663 9.16666H6.66634M8.33301 12.5H6.66634M13.333 5.83332H6.66634M16.6663 5.66666V14.3333C16.6663 15.7335 16.6663 16.4335 16.3939 16.9683C16.1542 17.4387 15.7717 17.8212 15.3013 18.0608C14.7665 18.3333 14.0665 18.3333 12.6663 18.3333H7.33301C5.93288 18.3333 5.23281 18.3333 4.69803 18.0608C4.22763 17.8212 3.84517 17.4387 3.60549 16.9683C3.33301 16.4335 3.33301 15.7335 3.33301 14.3333V5.66666C3.33301 4.26653 3.33301 3.56646 3.60549 3.03168C3.84517 2.56128 4.22763 2.17882 4.69803 1.93914C5.23281 1.66666 5.93288 1.66666 7.33301 1.66666H12.6663C14.0665 1.66666 14.7665 1.66666 15.3013 1.93914C15.7717 2.17882 16.1542 2.56128 16.3939 3.03168C16.6663 3.56646 16.6663 4.26653 16.6663 5.66666Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
label: "Submissions",
path: "/submissions",
active: location.pathname === "/submissions"
},
{
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_914_468)">
<path d="M10.0003 1.66666C11.0947 1.66666 12.1783 1.8822 13.1894 2.30099C14.2004 2.71978 15.1191 3.33361 15.8929 4.10744C16.6667 4.88126 17.2805 5.79992 17.6993 6.81097C18.1181 7.82201 18.3337 8.90565 18.3337 10M10.0003 1.66666V9.99999M10.0003 1.66666C5.39795 1.66666 1.66699 5.39762 1.66699 9.99999C1.66699 14.6024 5.39795 18.3333 10.0003 18.3333C14.6027 18.3333 18.3337 14.6024 18.3337 10M10.0003 1.66666C14.6027 1.66666 18.3337 5.39762 18.3337 10M18.3337 10L10.0003 9.99999M18.3337 10C18.3337 11.3151 18.0224 12.6115 17.4254 13.7832C16.8283 14.955 15.9625 15.9688 14.8985 16.7418L10.0003 9.99999" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_914_468">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
),
label: "Reports",
path: "/reports",
active: location.pathname === "/reports" || location.pathname === "/"
},
{
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.4999 9.58333C17.4999 13.4953 14.3285 16.6667 10.4165 16.6667C9.5192 16.6667 8.66086 16.4998 7.87081 16.1954C7.72637 16.1398 7.65415 16.112 7.59671 16.0987C7.54022 16.0857 7.49933 16.0803 7.4414 16.0781C7.3825 16.0758 7.31789 16.0825 7.18865 16.0958L2.92113 16.537C2.51427 16.579 2.31083 16.6001 2.19083 16.5269C2.08631 16.4631 2.01512 16.3566 1.99617 16.2356C1.97441 16.0968 2.07162 15.9168 2.26605 15.557L3.62909 13.034C3.74135 12.8262 3.79747 12.7223 3.82289 12.6225C3.848 12.5238 3.85407 12.4527 3.84604 12.3512C3.83791 12.2484 3.79283 12.1147 3.70266 11.8472C3.46306 11.1363 3.33318 10.375 3.33318 9.58333C3.33318 5.67132 6.5045 2.5 10.4165 2.5C14.3285 2.5 17.4999 5.67132 17.4999 9.58333Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
label: "Chat",
path: "/chat",
active: location.pathname === "/chat"
},
{
icon: (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_914_474)">
<path d="M7.57533 7.50001C7.77125 6.94306 8.15795 6.47343 8.66695 6.17428C9.17596 5.87514 9.77441 5.76579 10.3563 5.8656C10.9382 5.96541 11.466 6.26794 11.8462 6.71961C12.2264 7.17128 12.4345 7.74294 12.4337 8.33334C12.4337 10 9.93366 10.8333 9.93366 10.8333M10.0003 14.1667H10.0087M18.3337 10C18.3337 14.6024 14.6027 18.3333 10.0003 18.3333C5.39795 18.3333 1.66699 14.6024 1.66699 10C1.66699 5.39763 5.39795 1.66667 10.0003 1.66667C14.6027 1.66667 18.3337 5.39763 18.3337 10Z" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_914_474">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
),
label: "Help",
path: "/help",
active: location.pathname === "/help"
}
];
const settingsIcon = (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_914_477)">
<path d="M10.0003 12.5C11.381 12.5 12.5003 11.3807 12.5003 10C12.5003 8.61929 11.381 7.5 10.0003 7.5C8.61961 7.5 7.50033 8.61929 7.50033 10C7.50033 11.3807 8.61961 12.5 10.0003 12.5Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M15.6064 12.2727C15.5055 12.5012 15.4755 12.7547 15.52 13.0004C15.5646 13.2462 15.6817 13.473 15.8564 13.6515L15.9018 13.697C16.0427 13.8377 16.1545 14.0048 16.2307 14.1887C16.307 14.3727 16.3462 14.5698 16.3462 14.7689C16.3462 14.9681 16.307 15.1652 16.2307 15.3492C16.1545 15.5331 16.0427 15.7002 15.9018 15.8409C15.7611 15.9818 15.594 16.0935 15.4101 16.1698C15.2261 16.246 15.029 16.2853 14.8299 16.2853C14.6308 16.2853 14.4336 16.246 14.2497 16.1698C14.0657 16.0935 13.8986 15.9818 13.7579 15.8409L13.7124 15.7955C13.5339 15.6208 13.3071 15.5036 13.0614 15.4591C12.8156 15.4145 12.5622 15.4446 12.3337 15.5455C12.1096 15.6415 11.9185 15.8009 11.7839 16.0042C11.6493 16.2074 11.5771 16.4456 11.5761 16.6894V16.8182C11.5761 17.22 11.4165 17.6054 11.1323 17.8896C10.8482 18.1737 10.4628 18.3333 10.0609 18.3333C9.65909 18.3333 9.2737 18.1737 8.98956 17.8896C8.70541 17.6054 8.54578 17.22 8.54578 16.8182V16.75C8.53991 16.4992 8.45875 16.2561 8.31283 16.052C8.16692 15.848 7.963 15.6926 7.7276 15.6061C7.4991 15.5052 7.24563 15.4751 6.99988 15.5197C6.75413 15.5643 6.52736 15.6814 6.34881 15.8561L6.30336 15.9015C6.16264 16.0424 5.99554 16.1541 5.8116 16.2304C5.62766 16.3066 5.4305 16.3459 5.23139 16.3459C5.03227 16.3459 4.83511 16.3066 4.65117 16.2304C4.46724 16.1541 4.30013 16.0424 4.15942 15.9015C4.01854 15.7608 3.90679 15.5937 3.83054 15.4098C3.75429 15.2258 3.71504 15.0287 3.71504 14.8295C3.71504 14.6304 3.75429 14.4333 3.83054 14.2493C3.90679 14.0654 4.01854 13.8983 4.15942 13.7576L4.20487 13.7121C4.37952 13.5336 4.49668 13.3068 4.54124 13.0611C4.5858 12.8153 4.55572 12.5618 4.45487 12.3333C4.35884 12.1093 4.19938 11.9182 3.99613 11.7836C3.79288 11.649 3.55471 11.5767 3.31093 11.5758H3.18214C2.7803 11.5758 2.39492 11.4161 2.11077 11.132C1.82662 10.8478 1.66699 10.4624 1.66699 10.0606C1.66699 9.65876 1.82662 9.27338 2.11077 8.98923C2.39492 8.70509 2.7803 8.54545 3.18214 8.54545H3.25033C3.50108 8.53959 3.74427 8.45842 3.94828 8.31251C4.15229 8.16659 4.30769 7.96268 4.39427 7.72727C4.49511 7.49878 4.52519 7.24531 4.48063 6.99955C4.43607 6.7538 4.31891 6.52703 4.14427 6.34848L4.09881 6.30303C3.95794 6.16231 3.84618 5.99521 3.76993 5.81127C3.69368 5.62734 3.65444 5.43018 3.65444 5.23106C3.65444 5.03195 3.69368 4.83478 3.76993 4.65085C3.84618 4.46691 3.95794 4.29981 4.09881 4.15909C4.23953 4.01822 4.40663 3.90646 4.59057 3.83021C4.7745 3.75396 4.97167 3.71472 5.17078 3.71472C5.36989 3.71472 5.56706 3.75396 5.75099 3.83021C5.93493 3.90646 6.10203 4.01822 6.24275 4.15909L6.2882 4.20455C6.46675 4.37919 6.69352 4.49635 6.93927 4.54091C7.18503 4.58547 7.4385 4.55539 7.66699 4.45455H7.7276C7.95167 4.35851 8.14276 4.19906 8.27737 3.99581C8.41197 3.79256 8.4842 3.55438 8.48517 3.31061V3.18182C8.48517 2.77998 8.64481 2.39459 8.92895 2.11044C9.2131 1.8263 9.59848 1.66667 10.0003 1.66667C10.4022 1.66667 10.7876 1.8263 11.0717 2.11044C11.3558 2.39459 11.5155 2.77998 11.5155 3.18182V3.25C11.5164 3.49378 11.5887 3.73195 11.7233 3.9352C11.8579 4.13845 12.049 4.29791 12.2731 4.39394C12.5016 4.49478 12.755 4.52487 13.0008 4.48031C13.2465 4.43575 13.4733 4.31859 13.6518 4.14394L13.6973 4.09848C13.838 3.95761 14.0051 3.84586 14.1891 3.76961C14.373 3.69336 14.5702 3.65411 14.7693 3.65411C14.9684 3.65411 15.1655 3.69336 15.3495 3.76961C15.5334 3.84586 15.7005 3.95761 15.8412 4.09848C15.9821 4.2392 16.0939 4.40631 16.1701 4.59024C16.2464 4.77418 16.2856 4.97134 16.2856 5.17045C16.2856 5.36957 16.2464 5.56673 16.1701 5.75067C16.0939 5.9346 15.9821 6.10171 15.8412 6.24242L15.7958 6.28788C15.6211 6.46642 15.504 6.69319 15.4594 6.93895C15.4149 7.1847 15.4449 7.43817 15.5458 7.66667V7.72727C15.6418 7.95134 15.8013 8.14244 16.0045 8.27704C16.2078 8.41164 16.4459 8.48388 16.6897 8.48485H16.8185C17.2204 8.48485 17.6057 8.64448 17.8899 8.92863C18.174 9.21277 18.3337 9.59816 18.3337 10C18.3337 10.4018 18.174 10.7872 17.8899 11.0714C17.6057 11.3555 17.2204 11.5152 16.8185 11.5152H16.7503C16.5065 11.5161 16.2684 11.5884 16.0651 11.723C15.8619 11.8576 15.7024 12.0487 15.6064 12.2727Z" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_914_477">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
);
const handleNavClick = (path: string) => {
navigate(path);
};
return (
<div className="w-64 h-[810px] max-w-64 min-w-64 px-3 pt-4 pb-3 bg-Neutrals-NeutralSlate0 border-r border-Neutrals-NeutralSlate200 inline-flex flex-col justify-between items-center overflow-hidden">
{/* Header Section */}
<div className="self-stretch flex flex-col justify-start items-start gap-5">
{/* Company Selector */}
<div className="w-60 pl-2 pr-4 py-2 bg-Neutrals-NeutralSlate0 rounded-3xl outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex justify-between items-center overflow-hidden">
<div className="flex-1 flex justify-start items-center gap-2">
<div className="w-8 h-8 rounded-full flex justify-start items-center gap-2.5">
<div className="w-8 h-8 relative bg-Brand-Orange rounded-full outline outline-[1.60px] outline-offset-[-1.60px] outline-white/10 overflow-hidden">
<div className="left-0 top-0 absolute">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="url(#paint0_linear_731_19280)" />
<defs>
<linearGradient id="paint0_linear_731_19280" x1="16" y1="3.97364e-07" x2="17.3333" y2="32" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0" />
<stop offset="1" stopColor="white" stopOpacity="0.12" />
</linearGradient>
</defs>
</svg>
</div>
<div className="left-[8.80px] top-[7.20px] absolute">
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_731_19281)">
<path opacity="0.5" fillRule="evenodd" clipRule="evenodd" d="M4.34367 10.6873C4.67023 11.018 4.67022 11.5541 4.34366 11.8848L4.32693 11.9018C4.00036 12.2325 3.47089 12.2325 3.14433 11.9018C2.81777 11.5711 2.81778 11.0349 3.14434 10.7042L3.16107 10.6873C3.48764 10.3566 4.0171 10.3566 4.34367 10.6873Z" fill="url(#paint0_linear_731_19281)" />
<path opacity="0.7" fillRule="evenodd" clipRule="evenodd" d="M8.2752 10.9423C8.60118 11.2736 8.60022 11.8097 8.27306 12.1398L5.95673 14.477C5.62957 14.8071 5.1001 14.8061 4.77413 14.4748C4.44815 14.1435 4.44911 13.6074 4.77627 13.2773L7.09261 10.9401C7.41976 10.61 7.94923 10.611 8.2752 10.9423Z" fill="url(#paint1_linear_731_19281)" />
</g>
<defs>
<filter id="filter0_d_731_19281" x="0.398828" y="-0.399988" width="19.2014" height="22.4" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
<feMorphology radius="1.2" operator="erode" in="SourceAlpha" result="effect1_dropShadow_731_19281" />
<feOffset dy="1.8" />
<feGaussianBlur stdDeviation="1.8" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix type="matrix" values="0 0 0 0 0.141176 0 0 0 0 0.141176 0 0 0 0 0.141176 0 0 0 0.1 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_731_19281" />
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_731_19281" result="shape" />
</filter>
<linearGradient id="paint0_linear_731_19281" x1="3.744" y1="10.4393" x2="3.744" y2="12.1498" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
<linearGradient id="paint1_linear_731_19281" x1="6.52467" y1="10.6932" x2="6.52467" y2="14.7239" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.8" />
<stop offset="1" stopColor="white" stopOpacity="0.5" />
</linearGradient>
</defs>
</svg>
</div>
</div>
</div>
<div className="flex-1 inline-flex flex-col justify-start items-start gap-0.5">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate950 text-base font-medium font-['Inter'] leading-normal">{companyName}</div>
</div>
</div>
<div>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83301 12.5L9.99967 16.6667L14.1663 12.5M5.83301 7.50001L9.99967 3.33334L14.1663 7.50001" stroke="var(--Neutrals-NeutralSlate400, #A4A7AE)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</div>
{/* Navigation Items */}
<div className="self-stretch flex flex-col justify-start items-start gap-5">
<div className="self-stretch flex flex-col justify-start items-start gap-1.5">
{navItems.map((item, index) => (
<div
key={index}
onClick={() => handleNavClick(item.path)}
className={`w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer ${item.active
? 'bg-Neutrals-NeutralSlate100'
: 'hover:bg-Neutrals-NeutralSlate50'
}`}
>
<div className="relative">
{React.cloneElement(item.icon, {
stroke: item.active ? "var(--Brand-Orange, #5E48FC)" : "var(--Neutrals-NeutralSlate400, #A4A7AE)"
})}
</div>
<div className={`justify-start text-sm font-medium font-['Inter'] leading-tight ${item.active
? 'text-Neutrals-NeutralSlate950'
: 'text-Neutrals-NeutralSlate500'
}`}>
{item.label}
</div>
</div>
))}
</div>
</div>
</div>
{/* Bottom Section */}
<div className="self-stretch flex flex-col justify-start items-start gap-3">
{/* Settings */}
<div
onClick={() => handleNavClick("/settings")}
className="w-60 px-4 py-2.5 rounded-[34px] inline-flex justify-start items-center gap-2 cursor-pointer hover:bg-Neutrals-NeutralSlate50"
>
<div className="relative">
{settingsIcon}
</div>
<div className="flex-1 justify-start text-Neutrals-NeutralSlate500 text-sm font-medium font-['Inter'] leading-tight">Settings</div>
</div>
{/* Build Report Card */}
<div className="self-stretch bg-Neutrals-NeutralSlate0 rounded-[20px] shadow-[0px_1px_4px_0px_rgba(14,18,27,0.04)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 flex flex-col justify-start items-start overflow-hidden">
<div className="self-stretch h-24 relative">
<div className="w-60 h-32 left-0 top-[-0.50px] absolute bg-gradient-to-b from-black to-black/0" />
<div className="w-60 p-3 left-[18.12px] top-[42.51px] absolute origin-top-left rotate-[-28.34deg] bg-Neutrals-NeutralSlate0 rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
<div className="w-60 p-3 left-[31.44px] top-[22px] absolute origin-top-left rotate-[-28.34deg] bg-Neutrals-NeutralSlate0 rounded-xl shadow-[0px_10px_20px_4px_rgba(14,18,27,0.08)] outline outline-1 outline-offset-[-1px] outline-Neutrals-NeutralSlate200 inline-flex flex-col justify-start items-start gap-3 overflow-hidden" />
</div>
<div className="self-stretch p-3 flex flex-col justify-start items-start gap-1">
<div className="self-stretch justify-start text-Neutrals-NeutralSlate800 text-sm font-semibold font-['Inter'] leading-tight">Build [Company]'s Report</div>
<div className="self-stretch justify-start text-Neutrals-NeutralSlate500 text-xs font-normal font-['Inter'] leading-none">Share this form with your team members to capture valuable info about your company to train Auditly.</div>
</div>
<div className="self-stretch px-3 pb-3 flex flex-col justify-start items-start gap-8">
<div className="self-stretch inline-flex justify-start items-start gap-2">
<div className="flex-1 px-3 py-1.5 bg-Button-Secondary rounded-[999px] flex justify-center items-center gap-0.5 overflow-hidden">
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Neutrals-NeutralSlate950 text-sm font-medium font-['Inter'] leading-tight">Invite</div>
</div>
<div>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.99967 3.33333V12.6667M3.33301 8H12.6663" stroke="var(--Neutrals-NeutralSlate950, #0A0D12)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
</div>
<div className="flex-1 px-3 py-1.5 bg-Brand-Orange rounded-[999px] outline outline-1 outline-offset-[-1px] outline-blue-400 flex justify-center items-center gap-0.5 overflow-hidden">
<div>
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.97203 12.2426L8.02922 13.1854C6.72748 14.4872 4.61693 14.4872 3.31518 13.1854C2.01343 11.8837 2.01343 9.77312 3.31518 8.47138L4.25799 7.52857M12.7433 8.47138L13.6861 7.52857C14.9878 6.22682 14.9878 4.11627 13.6861 2.81452C12.3843 1.51277 10.2738 1.51277 8.97203 2.81452L8.02922 3.75733M6.16729 10.3333L10.834 5.66662" stroke="var(--Other-White, white)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<div className="px-1 flex justify-center items-center">
<div className="justify-center text-Other-White text-sm font-medium font-['Inter'] leading-tight">Copy</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}