Restyle full application from Barclays to Oliver Agency brand
Replace entire Barclays colour palette (navy #1A2142, lime #C3FB5A, violet #7A0FF9) with Oliver brand tokens: black #1A1A1A, gold #FFCB05, orange #FF5C00, azure #0487B6, sky #5DF5EA, grey #EFEFEF, green #09821F. - Switch font from Inter/Barclays Effra to Arial (system font) - Add new Oliver logo asset (BAR-ModComms-logo-v4.png) - Sidebar: black background, new logo, azure active state - Hero: orange "Intelligent Review" text, hide AI-Powered tagline - Hide ChecksOverview on Home page per Oliver design - Toast notification: orange background with black text - All tables: sky headers, alternating white/grey rows - Campaign badges: gold "In Progress", green "Completed" - Analytics: grey KPI cards, sky accent on Key Insight, oliver trend colours - All buttons: azure fill, pill-shaped (rounded-full) - All tabs/toggles/dropdowns: azure accent colour - Update HTML title to "Mod Comms - Intelligent Review" - Default border radius set to 10px Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
44fa8ba527
commit
4302b9391a
29 changed files with 652 additions and 671 deletions
|
|
@ -49,9 +49,9 @@ const App: React.FC = () => {
|
|||
// Show loading spinner during MSAL authentication interactions
|
||||
if (inProgress !== InteractionStatus.None) {
|
||||
return (
|
||||
<div className="fixed inset-0 bg-primary-blue flex items-center justify-center">
|
||||
<div className="fixed inset-0 bg-oliver-black flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<svg className="animate-spin h-12 w-12 text-active-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<svg className="animate-spin h-12 w-12 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
|
|
@ -932,7 +932,8 @@ const AppContent: React.FC<{ msalInstance: any }> = ({ msalInstance }) => {
|
|||
return (
|
||||
<>
|
||||
<Hero onGetStarted={() => handleNavigate('Campaigns')} />
|
||||
<ChecksOverview />
|
||||
{/* Hidden per Oliver design — client may want it back */}
|
||||
{/* <ChecksOverview /> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -941,9 +942,9 @@ const AppContent: React.FC<{ msalInstance: any }> = ({ msalInstance }) => {
|
|||
// Show loading spinner while user profile is loading
|
||||
if (isUserLoading) {
|
||||
return (
|
||||
<div className="fixed inset-0 bg-primary-blue flex items-center justify-center">
|
||||
<div className="fixed inset-0 bg-oliver-black flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<svg className="animate-spin h-12 w-12 text-active-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<svg className="animate-spin h-12 w-12 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
|
|
@ -954,7 +955,7 @@ const AppContent: React.FC<{ msalInstance: any }> = ({ msalInstance }) => {
|
|||
}
|
||||
|
||||
// Determine background color based on view to avoid grey bar on Home view
|
||||
const mainBgColor = currentView === 'Home' ? 'bg-white' : 'bg-grey-100';
|
||||
const mainBgColor = currentView === 'Home' ? 'bg-white' : 'bg-oliver-grey';
|
||||
|
||||
// Get user info from MSAL for sidebar display
|
||||
const userInfo = getUserInfo(msalInstance);
|
||||
|
|
@ -982,15 +983,15 @@ const AppContent: React.FC<{ msalInstance: any }> = ({ msalInstance }) => {
|
|||
|
||||
{/* Model fallback notification toast */}
|
||||
{notification && (
|
||||
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 z-50 flex items-start gap-3 bg-gray-800 text-white text-sm px-5 py-3.5 rounded-xl shadow-2xl max-w-md w-full mx-4 animate-fade-in">
|
||||
<svg className="w-5 h-5 text-yellow-400 shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 z-50 flex items-start gap-3 bg-oliver-orange text-oliver-black text-sm px-5 py-3.5 rounded-xl shadow-2xl max-w-md w-full mx-4 animate-fade-in">
|
||||
<svg className="w-5 h-5 text-oliver-black shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||
</svg>
|
||||
<div className="flex-1">
|
||||
<p className="font-semibold text-yellow-300 mb-0.5">AI Model Notice</p>
|
||||
<p className="text-gray-200 leading-snug">{notification}</p>
|
||||
<p className="font-semibold text-oliver-black mb-0.5">AI Model Notice</p>
|
||||
<p className="text-oliver-black leading-snug">{notification}</p>
|
||||
</div>
|
||||
<button onClick={() => setNotification(null)} className="text-gray-400 hover:text-white shrink-0">
|
||||
<button onClick={() => setNotification(null)} className="text-oliver-black/60 hover:text-oliver-black shrink-0">
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -25,21 +25,21 @@ export const AgencyFilterBar: React.FC<AgencyFilterBarProps> = ({ selectedAgency
|
|||
|
||||
return (
|
||||
<div className="bg-white border-b border-grey-300 px-6 py-3 flex items-center gap-3 flex-shrink-0">
|
||||
<label className="text-sm font-medium text-primary-blue whitespace-nowrap">
|
||||
<label className="text-sm font-medium text-oliver-black whitespace-nowrap">
|
||||
Filter by Agency:
|
||||
</label>
|
||||
<div className="relative max-w-xs">
|
||||
<select
|
||||
value={selectedAgencyId || ''}
|
||||
onChange={(e) => onAgencyChange(e.target.value || null)}
|
||||
className="bg-white border-2 border-active-blue text-black-title py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none min-w-[200px]"
|
||||
className="bg-white border-2 border-oliver-azure text-oliver-black py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none min-w-[200px]"
|
||||
>
|
||||
<option value="">All Agencies</option>
|
||||
{agencies.map(a => (
|
||||
<option key={a.id} value={a.id}>{a.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-active-blue pointer-events-none" />
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-oliver-azure pointer-events-none" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ const TrendIndicator: React.FC<{ trend: 'up' | 'down' | 'stable' }> = ({ trend }
|
|||
return <div className="flex items-center gap-1.5 text-success"><UpArrow/> Improving</div>;
|
||||
}
|
||||
if (trend === 'down') {
|
||||
return <div className="flex items-center gap-1.5 text-error"><DownArrow/> Declining</div>;
|
||||
return <div className="flex items-center gap-1.5 text-oliver-orange"><DownArrow/> Declining</div>;
|
||||
}
|
||||
return <div className="flex items-center gap-1.5 text-grey-700"><StableLine/> Stable</div>;
|
||||
return <div className="flex items-center gap-1.5 text-oliver-black"><StableLine/> Stable</div>;
|
||||
};
|
||||
|
||||
export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({ agencyId, isAdmin }) => {
|
||||
|
|
@ -82,8 +82,8 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-white">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">Performance analytics</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Overall usage and performance statistics for the tool.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Performance analytics</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Overall usage and performance statistics for the tool.</p>
|
||||
</header>
|
||||
|
||||
{/* Stats Cards */}
|
||||
|
|
@ -92,13 +92,13 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
{stats.map((stat) => {
|
||||
const Icon = stat.icon;
|
||||
return (
|
||||
<div key={stat.name} className="bg-white rounded-[10px] p-6 flex items-start shadow-sm transition-all hover:shadow-lg">
|
||||
<div className="p-3 rounded-full bg-white text-black-title mr-4 flex-shrink-0">
|
||||
<div key={stat.name} className="bg-oliver-grey rounded-[10px] p-6 flex items-start border-2 border-grey-300 transition-all hover:shadow-lg">
|
||||
<div className="p-3 rounded-full bg-white text-oliver-black mr-4 flex-shrink-0">
|
||||
<Icon className="h-9 w-9" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-grey-900">{stat.name}</p>
|
||||
<p className="text-3xl font-bold text-black-title mt-1">{stat.value}</p>
|
||||
<p className="text-sm font-medium text-oliver-black">{stat.name}</p>
|
||||
<p className="text-3xl font-bold text-oliver-black mt-1">{stat.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -109,18 +109,18 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
{/* Per-Agency Breakdown (admin only) */}
|
||||
{isAdmin && agencyAnalytics.length > 0 && (
|
||||
<section className="mt-10">
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-4">Agency performance</h2>
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-4">Agency performance</h2>
|
||||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proofs Reviewed</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Pass Rate</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Failed</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Errors</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Legal Review</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proofs Reviewed</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Pass Rate</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Failed</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Errors</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Legal Review</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
|
|
@ -136,23 +136,23 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
? 'bg-blue-50'
|
||||
: index % 2 === 0
|
||||
? 'bg-white'
|
||||
: 'bg-grey-100'
|
||||
: 'bg-oliver-grey'
|
||||
}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">
|
||||
{agency.agency_name}
|
||||
{isSelected && <span className="ml-2 text-xs text-primary-blue font-normal">(selected)</span>}
|
||||
{isSelected && <span className="ml-2 text-xs text-oliver-black font-normal">(selected)</span>}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agency.total_reviews}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{agency.total_reviews}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-black">
|
||||
<div className="flex items-center">
|
||||
<span className={`h-2.5 w-2.5 rounded-full mr-3 ${rate >= 80 ? 'bg-success' : rate < 70 ? 'bg-error' : 'bg-warning'}`}></span>
|
||||
{rate}%
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agency.failed}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agency.errors}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agency.legal_review}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{agency.failed}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{agency.errors}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{agency.legal_review}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
@ -165,14 +165,14 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
|
||||
{/* AI Performance Summary */}
|
||||
<section className="mt-10">
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-4">AI performance summary</h2>
|
||||
<div className="bg-white border-2 border-electric-violet text-black-title p-6 rounded-[10px] shadow-md flex items-start gap-4">
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-4">AI performance summary</h2>
|
||||
<div className="bg-white border-2 border-oliver-sky border-l-4 text-oliver-black p-6 rounded-[10px] shadow-md flex items-start gap-4" style={{ borderLeftColor: '#5DF5EA' }}>
|
||||
<div className="flex-shrink-0">
|
||||
<LightbulbIcon className="h-7 w-7 text-electric-violet" />
|
||||
<LightbulbIcon className="h-7 w-7 text-oliver-orange" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-primary-blue">Key Insight (Last 7 Days):</p>
|
||||
<p className="mt-1 text-black-title">
|
||||
<p className="font-semibold text-oliver-black">Key Insight (Last 7 Days):</p>
|
||||
<p className="mt-1 text-oliver-black">
|
||||
A sharp decline in Best Practice adherence has been noted, primarily driven by proofs from the <strong>Barclays Q4 Social</strong> campaign. The Brand Guardian agent also shows a declining performance trend, suggesting a potential need for updated brand guideline training or proof review.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -181,29 +181,29 @@ export const Analytics: React.FC<{ agencyId?: string; isAdmin?: boolean }> = ({
|
|||
|
||||
{/* Agent Performance Table */}
|
||||
<section className="mt-10">
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-4">Agent performance (last 7 days)</h2>
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-4">Agent performance (last 7 days)</h2>
|
||||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Pass Rate</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Avg. Issues per Proof</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Performance Trend</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Pass Rate</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Avg. Issues per Proof</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Performance Trend</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{agentPerformance.map((agent, index) => (
|
||||
<tr key={agent.name} className={index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-black-title">{agent.name}</td>
|
||||
<td className={`px-6 py-4 whitespace-nowrap text-sm font-semibold text-black-title`}>
|
||||
<tr key={agent.name} className={index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{agent.name}</td>
|
||||
<td className={`px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-black`}>
|
||||
<div className="flex items-center">
|
||||
<span className={`h-2.5 w-2.5 rounded-full mr-3 ${agent.passRate >= 80 ? 'bg-success' : agent.passRate < 70 ? 'bg-error' : 'bg-warning'}`}></span>
|
||||
<span className={`h-2.5 w-2.5 rounded-full mr-3 ${agent.passRate >= 80 ? 'bg-oliver-green' : agent.passRate < 70 ? 'bg-oliver-gold' : 'bg-oliver-orange'}`}></span>
|
||||
{agent.passRate}%
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agent.avgIssues}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{agent.avgIssues}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<TrendIndicator trend={agent.trend as 'up' | 'down' | 'stable'} />
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export const AssetPreview: React.FC<AssetPreviewProps> = ({ file, previewUrl, fi
|
|||
style={{ minHeight: '300px', maxHeight: 'calc(100vh - 9rem)' }}
|
||||
>
|
||||
<DocumentIcon className="h-20 w-20 text-gray-400 mb-4" />
|
||||
<p className="text-lg font-semibold text-primary-blue break-all">{displayName}</p>
|
||||
<p className="text-lg font-semibold text-oliver-black break-all">{displayName}</p>
|
||||
<p className="text-sm text-gray-500">{fileType}</p>
|
||||
<p className="text-sm text-gray-500 mt-2">No preview available for this file type.</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,40 +23,40 @@ const formatDate = (isoString: string) => {
|
|||
const FlagsTable: React.FC<{ items: FlaggedItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent Flagged</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">User Comments</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent Flagged</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">User Comments</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{items.length > 0 ? items.map((item, index) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
|
||||
className={`hover:bg-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
||||
onClick={() => onNavigate(item)}
|
||||
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-active-blue">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.agentFlagged}</td>
|
||||
<td className="px-6 py-4 text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.agentFlagged}</td>
|
||||
<td className="px-6 py-4 text-sm text-oliver-black">
|
||||
<div className="max-w-xs break-words" title={item.comments}>
|
||||
{item.comments || <span className="italic text-grey-700">No comment</span>}
|
||||
{item.comments || <span className="italic text-oliver-black/60">No comment</span>}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{formatDate(item.timestamp)}</td>
|
||||
</tr>
|
||||
)) : (
|
||||
<tr>
|
||||
<td colSpan={7} className="text-center py-10 text-grey-700">
|
||||
<td colSpan={7} className="text-center py-10 text-oliver-black/60">
|
||||
There are currently no flagged items to audit.
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -69,42 +69,42 @@ const FlagsTable: React.FC<{ items: FlaggedItem[], onNavigate: AuditingProps['on
|
|||
const ResolutionsTable: React.FC<{ items: ResolvedItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent Issue</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">User Comments</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent Issue</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">User Comments</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{items.length > 0 ? items.map((item, index) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
|
||||
className={`hover:bg-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
||||
onClick={() => onNavigate(item)}
|
||||
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-active-blue">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.agent}</td>
|
||||
<td className="px-6 py-4 text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.agent}</td>
|
||||
<td className="px-6 py-4 text-sm text-oliver-black">
|
||||
<div className="max-w-xs break-words" title={item.issue}>{item.issue}</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-black-title">
|
||||
<td className="px-6 py-4 text-sm text-oliver-black">
|
||||
<div className="max-w-xs break-words" title={item.resolution}>{item.resolution}</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{formatDate(item.timestamp)}</td>
|
||||
</tr>
|
||||
)) : (
|
||||
<tr>
|
||||
<td colSpan={8} className="text-center py-10 text-grey-700">
|
||||
<td colSpan={8} className="text-center py-10 text-oliver-black/60">
|
||||
There are currently no resolved items to audit.
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -117,38 +117,38 @@ const ResolutionsTable: React.FC<{ items: ResolvedItem[], onNavigate: AuditingPr
|
|||
const ErrorsTable: React.FC<{ items: ErrorItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Error Summary</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Error Summary</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{items.length > 0 ? items.map((item, index) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
|
||||
className={`hover:bg-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
||||
onClick={() => onNavigate(item)}
|
||||
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-active-blue">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
||||
<td className="px-6 py-4 text-sm text-oliver-black">
|
||||
<div className="max-w-md break-words" title={item.errorSummary}>
|
||||
{item.errorSummary}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{formatDate(item.timestamp)}</td>
|
||||
</tr>
|
||||
)) : (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center py-10 text-grey-700">
|
||||
<td colSpan={6} className="text-center py-10 text-oliver-black/60">
|
||||
There are currently no analysis errors to audit.
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -165,8 +165,8 @@ export const Auditing: React.FC<AuditingProps> = ({ flaggedItems, resolvedItems,
|
|||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-white">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">Auditing</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Review and investigate all user-flagged feedback.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Auditing</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Review and investigate all user-flagged feedback.</p>
|
||||
</header>
|
||||
|
||||
<div className="mb-6 border-b border-grey-300">
|
||||
|
|
@ -175,8 +175,8 @@ export const Auditing: React.FC<AuditingProps> = ({ flaggedItems, resolvedItems,
|
|||
onClick={() => setActiveTab('Flags')}
|
||||
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
|
||||
activeTab === 'Flags'
|
||||
? 'border-active-blue text-active-blue'
|
||||
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
|
||||
? 'border-oliver-azure text-oliver-azure'
|
||||
: 'border-transparent text-oliver-black hover:text-oliver-azure hover:border-grey-300'
|
||||
}`}
|
||||
aria-current={activeTab === 'Flags' ? 'page' : undefined}
|
||||
>
|
||||
|
|
@ -186,8 +186,8 @@ export const Auditing: React.FC<AuditingProps> = ({ flaggedItems, resolvedItems,
|
|||
onClick={() => setActiveTab('Resolutions')}
|
||||
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
|
||||
activeTab === 'Resolutions'
|
||||
? 'border-active-blue text-active-blue'
|
||||
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
|
||||
? 'border-oliver-azure text-oliver-azure'
|
||||
: 'border-transparent text-oliver-black hover:text-oliver-azure hover:border-grey-300'
|
||||
}`}
|
||||
aria-current={activeTab === 'Resolutions' ? 'page' : undefined}
|
||||
>
|
||||
|
|
@ -197,8 +197,8 @@ export const Auditing: React.FC<AuditingProps> = ({ flaggedItems, resolvedItems,
|
|||
onClick={() => setActiveTab('Errors')}
|
||||
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
|
||||
activeTab === 'Errors'
|
||||
? 'border-active-blue text-active-blue'
|
||||
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
|
||||
? 'border-oliver-azure text-oliver-azure'
|
||||
: 'border-transparent text-oliver-black hover:text-oliver-azure hover:border-grey-300'
|
||||
}`}
|
||||
aria-current={activeTab === 'Errors' ? 'page' : undefined}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -232,16 +232,16 @@ const StatusBadge: React.FC<{ status: string }> = ({ status }) => {
|
|||
let colorClasses = '';
|
||||
switch (status) {
|
||||
case 'In Progress':
|
||||
colorClasses = 'bg-warning text-black-title';
|
||||
colorClasses = 'bg-oliver-gold text-oliver-black';
|
||||
break;
|
||||
case 'Completed':
|
||||
colorClasses = 'bg-success text-white';
|
||||
colorClasses = 'bg-oliver-green text-white';
|
||||
break;
|
||||
case 'Needs Review':
|
||||
colorClasses = 'bg-warning-light text-black-title';
|
||||
colorClasses = 'bg-warning-light text-oliver-black';
|
||||
break;
|
||||
default:
|
||||
colorClasses = 'bg-grey-100 text-black-title';
|
||||
colorClasses = 'bg-oliver-grey text-oliver-black';
|
||||
}
|
||||
return (
|
||||
<span className={`px-2.5 py-0.5 text-xs font-semibold rounded-full ${colorClasses}`}>
|
||||
|
|
@ -260,10 +260,10 @@ const OverallStatusBadge: React.FC<{ status: OverallStatus }> = ({ status }) =>
|
|||
colorClasses = 'bg-error text-white';
|
||||
break;
|
||||
case 'Requires Manual Legal Review':
|
||||
colorClasses = 'bg-warning text-black-title';
|
||||
colorClasses = 'bg-warning text-oliver-black';
|
||||
break;
|
||||
case 'Analysis Error':
|
||||
colorClasses = 'bg-grey-300 text-black-title';
|
||||
colorClasses = 'bg-grey-300 text-oliver-black';
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -379,17 +379,17 @@ const CampaignList: React.FC<{
|
|||
}, [filteredAndSortedCampaigns]);
|
||||
|
||||
const getStatusSelectClasses = (status: string) => {
|
||||
let baseClasses = 'w-full text-center px-2.5 py-0.5 text-xs font-semibold rounded-full border border-transparent focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none cursor-pointer';
|
||||
let baseClasses = 'w-full text-center px-2.5 py-0.5 text-xs font-semibold rounded-full border border-transparent focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none cursor-pointer';
|
||||
let colorClasses = '';
|
||||
switch (status) {
|
||||
case 'In Progress':
|
||||
colorClasses = 'bg-warning text-black-title';
|
||||
colorClasses = 'bg-oliver-gold text-oliver-black';
|
||||
break;
|
||||
case 'Completed':
|
||||
colorClasses = 'bg-success text-white';
|
||||
colorClasses = 'bg-oliver-green text-white';
|
||||
break;
|
||||
default:
|
||||
colorClasses = 'bg-grey-100 text-black-title';
|
||||
colorClasses = 'bg-oliver-grey text-oliver-black';
|
||||
}
|
||||
return `${baseClasses} ${colorClasses}`;
|
||||
};
|
||||
|
|
@ -468,22 +468,22 @@ const CampaignList: React.FC<{
|
|||
<header className="mb-8">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">Campaigns</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Manage your campaigns and proof collections.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Campaigns</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Manage your campaigns and proof collections.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="show-completed" className="text-sm font-medium text-black-title whitespace-nowrap">Show Completed</label>
|
||||
<label htmlFor="show-completed" className="text-sm font-medium text-oliver-black whitespace-nowrap">Show Completed</label>
|
||||
<ToggleSwitch enabled={showCompleted} onChange={setShowCompleted} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="my-campaigns" className="text-sm font-medium text-black-title whitespace-nowrap">My Campaigns Only</label>
|
||||
<label htmlFor="my-campaigns" className="text-sm font-medium text-oliver-black whitespace-nowrap">My Campaigns Only</label>
|
||||
<ToggleSwitch enabled={showMyCampaignsOnly} onChange={setShowMyCampaignsOnly} />
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<button
|
||||
onClick={onOpenModal}
|
||||
className="flex items-center gap-2 bg-active-blue text-white font-semibold py-2.5 px-6 rounded-full hover:bg-active-blue/90 transition-colors duration-300"
|
||||
className="flex items-center gap-2 bg-oliver-azure text-white font-semibold py-2.5 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors duration-300"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
Create New Campaign
|
||||
|
|
@ -496,14 +496,14 @@ const CampaignList: React.FC<{
|
|||
<section>
|
||||
{/* Bulk Actions Bar */}
|
||||
{!readOnly && selectedCampaigns.size > 0 && (
|
||||
<div className="mb-4 bg-info-light border border-active-blue rounded-[10px] p-3 flex items-center justify-between">
|
||||
<div className="mb-4 bg-oliver-grey border border-oliver-azure rounded-[10px] p-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm font-medium text-active-blue">
|
||||
<span className="text-sm font-medium text-oliver-azure">
|
||||
{selectedCampaigns.size} campaign{selectedCampaigns.size !== 1 ? 's' : ''} selected
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setSelectedCampaigns(new Set())}
|
||||
className="text-sm text-active-blue hover:text-primary-blue underline"
|
||||
className="text-sm text-oliver-azure hover:text-oliver-black underline"
|
||||
>
|
||||
Clear selection
|
||||
</button>
|
||||
|
|
@ -522,7 +522,7 @@ const CampaignList: React.FC<{
|
|||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
{!readOnly && (
|
||||
<th scope="col" className="px-4 py-3 text-left">
|
||||
|
|
@ -533,7 +533,7 @@ const CampaignList: React.FC<{
|
|||
if (el) el.indeterminate = isIndeterminate;
|
||||
}}
|
||||
onChange={handleSelectAll}
|
||||
className="h-4 w-4 text-active-blue border-grey-300 rounded focus:ring-active-blue cursor-pointer"
|
||||
className="h-4 w-4 text-oliver-azure border-grey-300 rounded focus:ring-oliver-azure cursor-pointer"
|
||||
aria-label="Select all campaigns"
|
||||
/>
|
||||
</th>
|
||||
|
|
@ -550,12 +550,12 @@ const CampaignList: React.FC<{
|
|||
<th
|
||||
key={key}
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider cursor-pointer select-none hover:text-active-blue"
|
||||
className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider cursor-pointer select-none hover:text-oliver-azure"
|
||||
onClick={() => handleSort(key)}
|
||||
>
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{label}
|
||||
<span className={`text-[10px] leading-none ${sortKey === key ? 'text-active-blue' : 'text-grey-700'}`}>
|
||||
<span className={`text-[10px] leading-none ${sortKey === key ? 'text-oliver-azure' : 'text-oliver-black/60'}`}>
|
||||
{sortKey === key ? (sortDirection === 'asc' ? '\u25B2' : '\u25BC') : '\u25B4\u25BE'}
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -563,7 +563,7 @@ const CampaignList: React.FC<{
|
|||
))}
|
||||
{!readOnly && <th scope="col" className="relative px-4 py-3"><span className="sr-only">Actions</span></th>}
|
||||
</tr>
|
||||
<tr className="bg-grey-100">
|
||||
<tr className="bg-oliver-grey">
|
||||
{!readOnly && <td className="px-4 py-1"></td>}
|
||||
{(['name', 'proofs', 'status', 'agencyLead', 'agency', 'brandGuidelines', 'lastModified'] as SortKey[]).map((key) => (
|
||||
<td key={key} className="px-6 py-1">
|
||||
|
|
@ -573,7 +573,7 @@ const CampaignList: React.FC<{
|
|||
onChange={(e) => handleColumnFilterChange(key, e.target.value)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
placeholder="Filter..."
|
||||
className="w-full text-xs py-1 px-2 border border-grey-300 rounded bg-white focus:outline-none focus:border-active-blue focus:ring-1 focus:ring-active-blue"
|
||||
className="w-full text-xs py-1 px-2 border border-grey-300 rounded bg-white focus:outline-none focus:border-oliver-azure focus:ring-1 focus:ring-oliver-azure"
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
|
|
@ -586,7 +586,7 @@ const CampaignList: React.FC<{
|
|||
return (
|
||||
<tr
|
||||
key={campaign.name}
|
||||
className={`hover:bg-info-light cursor-pointer ${isSelected ? 'bg-info-light' : index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
|
||||
className={`hover:bg-oliver-grey cursor-pointer ${isSelected ? 'bg-oliver-grey' : index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
||||
onClick={() => onSelectCampaign(campaign.name)}
|
||||
>
|
||||
{!readOnly && (
|
||||
|
|
@ -596,13 +596,13 @@ const CampaignList: React.FC<{
|
|||
checked={isSelected}
|
||||
onChange={() => {}} // Handled by onClick
|
||||
onClick={(e) => handleSelectCampaign(campaign.name, e)}
|
||||
className="h-4 w-4 text-active-blue border-grey-300 rounded focus:ring-active-blue cursor-pointer"
|
||||
className="h-4 w-4 text-oliver-azure border-grey-300 rounded focus:ring-oliver-azure cursor-pointer"
|
||||
aria-label={`Select ${campaign.name}`}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{campaign.name}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{campaign.proofs}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{campaign.name}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{campaign.proofs}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm w-40">
|
||||
<div className="relative">
|
||||
<select
|
||||
|
|
@ -616,19 +616,19 @@ const CampaignList: React.FC<{
|
|||
<option value="In Progress">In Progress</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
<ChevronDownIcon className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 h-full w-4 text-black-title" />
|
||||
<ChevronDownIcon className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 h-full w-4 text-oliver-black" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{campaign.agencyLead}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{campaign.agency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{campaign.brandGuidelines}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{formatDate(campaign.lastModified)}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{campaign.agencyLead}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{campaign.agency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{campaign.brandGuidelines}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{formatDate(campaign.lastModified)}</td>
|
||||
{!readOnly && (
|
||||
<td className="px-4 py-4 whitespace-nowrap text-sm text-right">
|
||||
<button
|
||||
onClick={(e) => handleSingleDelete(campaign, e)}
|
||||
disabled={isDeleting}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-error-light hover:text-error transition-colors disabled:opacity-50"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-error-light hover:text-error transition-colors disabled:opacity-50"
|
||||
title={`Delete ${campaign.name}`}
|
||||
aria-label={`Delete ${campaign.name}`}
|
||||
>
|
||||
|
|
@ -754,12 +754,12 @@ const UploadProofModal: React.FC<{
|
|||
{/* Header */}
|
||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 border-b border-grey-300 flex justify-between items-start">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold text-primary-blue" id="modal-title">Upload New Proof</h3>
|
||||
<p className="text-sm text-grey-900 mt-1">Drag and drop your proof below to start AI analysis.</p>
|
||||
<h3 className="text-2xl font-bold text-oliver-black" id="modal-title">Upload New Proof</h3>
|
||||
<p className="text-sm text-oliver-black mt-1">Drag and drop your proof below to start AI analysis.</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded-full p-2 text-grey-700 hover:bg-grey-100 hover:text-black-title transition-colors"
|
||||
className="rounded-full p-2 text-oliver-black/60 hover:bg-oliver-grey hover:text-oliver-black transition-colors"
|
||||
>
|
||||
<XIcon className="h-6 w-6" />
|
||||
</button>
|
||||
|
|
@ -771,10 +771,10 @@ const UploadProofModal: React.FC<{
|
|||
<div className="space-y-6">
|
||||
{/* Proof Name */}
|
||||
<div>
|
||||
<label htmlFor="proof-name" className="block text-sm font-bold text-primary-blue mb-2">Proof Name</label>
|
||||
<label htmlFor="proof-name" className="block text-sm font-bold text-oliver-black mb-2">Proof Name</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<DocumentIcon className="h-5 w-5 text-grey-700" />
|
||||
<DocumentIcon className="h-5 w-5 text-oliver-black/60" />
|
||||
</div>
|
||||
<input
|
||||
id="proof-name"
|
||||
|
|
@ -783,13 +783,13 @@ const UploadProofModal: React.FC<{
|
|||
onChange={(e) => setProofName(e.target.value)}
|
||||
placeholder="e.g., Q4 Hero Instagram Post"
|
||||
disabled={isLoading}
|
||||
className="w-full rounded-[10px] border-2 border-grey-700 shadow-sm focus:border-active-blue focus:ring-active-blue p-3 pl-10 bg-white transition-all text-black-title"
|
||||
className="w-full rounded-[10px] border-2 border-oliver-azure shadow-sm focus:border-oliver-azure focus:ring-oliver-azure p-3 pl-10 bg-white transition-all text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{isNewVersion && (
|
||||
<p className="text-xs text-active-blue mt-2 p-2 bg-info-light rounded-[10px] flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-active-blue"></span>
|
||||
<p className="text-xs text-oliver-azure mt-2 p-2 bg-oliver-grey rounded-[10px] flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-oliver-azure"></span>
|
||||
A proof with this name already exists. This will be uploaded as a new version.
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -798,60 +798,60 @@ const UploadProofModal: React.FC<{
|
|||
{/* Dropdowns Grid */}
|
||||
<div className="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor="proof-channel" className="block text-sm font-bold text-primary-blue mb-2">Channel</label>
|
||||
<label htmlFor="proof-channel" className="block text-sm font-bold text-oliver-black mb-2">Channel</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<ChannelIcon className="h-5 w-5 text-grey-700" />
|
||||
<ChannelIcon className="h-5 w-5 text-oliver-black/60" />
|
||||
</div>
|
||||
<select
|
||||
id="proof-channel"
|
||||
value={channel}
|
||||
onChange={(e) => setChannel(e.target.value)}
|
||||
disabled={isLoading}
|
||||
className="w-full rounded-[10px] border-2 border-grey-700 shadow-sm focus:border-active-blue focus:ring-active-blue p-3 pl-10 bg-white transition-all text-black-title appearance-none"
|
||||
className="w-full rounded-[10px] border-2 border-oliver-azure shadow-sm focus:border-oliver-azure focus:ring-oliver-azure p-3 pl-10 bg-white transition-all text-oliver-black appearance-none"
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select Channel</option>
|
||||
{availableChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-oliver-black/60">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="proof-sub-channel" className="block text-sm font-bold text-primary-blue mb-2">Sub-Channel</label>
|
||||
<label htmlFor="proof-sub-channel" className="block text-sm font-bold text-oliver-black mb-2">Sub-Channel</label>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<TagIcon className="h-5 w-5 text-grey-700" />
|
||||
<TagIcon className="h-5 w-5 text-oliver-black/60" />
|
||||
</div>
|
||||
<select
|
||||
id="proof-sub-channel"
|
||||
value={subChannel}
|
||||
onChange={(e) => setSubChannel(e.target.value)}
|
||||
disabled={isLoading || !channel}
|
||||
className="w-full rounded-[10px] border-2 border-grey-700 shadow-sm focus:border-active-blue focus:ring-active-blue p-3 pl-10 bg-white transition-all text-black-title appearance-none disabled:bg-grey-100 disabled:text-grey-700"
|
||||
className="w-full rounded-[10px] border-2 border-oliver-azure shadow-sm focus:border-oliver-azure focus:ring-oliver-azure p-3 pl-10 bg-white transition-all text-oliver-black appearance-none disabled:bg-oliver-grey disabled:text-oliver-black/60"
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select Sub-Channel</option>
|
||||
{availableSubChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-oliver-black/60">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-2">
|
||||
<label htmlFor="proof-type" className="block text-sm font-bold text-primary-blue mb-2">Proof Type</label>
|
||||
<label htmlFor="proof-type" className="block text-sm font-bold text-oliver-black mb-2">Proof Type</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
id="proof-type"
|
||||
value={proofType}
|
||||
onChange={(e) => setProofType(e.target.value)}
|
||||
disabled={isLoading || !subChannel || availableProofTypes.length === 0}
|
||||
className="w-full rounded-[10px] border-2 border-grey-700 shadow-sm focus:border-active-blue focus:ring-active-blue p-3 bg-white transition-all text-black-title appearance-none disabled:bg-grey-100 disabled:text-grey-700"
|
||||
className="w-full rounded-[10px] border-2 border-oliver-azure shadow-sm focus:border-oliver-azure focus:ring-oliver-azure p-3 bg-white transition-all text-oliver-black appearance-none disabled:bg-oliver-grey disabled:text-oliver-black/60"
|
||||
required={showProofType}
|
||||
>
|
||||
<option value="" disabled>
|
||||
|
|
@ -864,7 +864,7 @@ const UploadProofModal: React.FC<{
|
|||
</option>
|
||||
{availableProofTypes.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-oliver-black/60">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -873,7 +873,7 @@ const UploadProofModal: React.FC<{
|
|||
|
||||
{/* Drag & Drop Zone */}
|
||||
<div>
|
||||
<label className="block text-sm font-bold text-primary-blue mb-2">Proof File</label>
|
||||
<label className="block text-sm font-bold text-oliver-black mb-2">Proof File</label>
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
|
|
@ -895,23 +895,23 @@ const UploadProofModal: React.FC<{
|
|||
<div className="text-center space-y-2 pointer-events-none">
|
||||
{!file ? (
|
||||
<>
|
||||
<div className="mx-auto h-16 w-16 bg-grey-100 text-grey-700 rounded-full flex items-center justify-center group-hover:scale-110 group-hover:text-success group-hover:bg-success-light transition-all duration-300">
|
||||
<div className="mx-auto h-16 w-16 bg-oliver-grey text-oliver-black/60 rounded-full flex items-center justify-center group-hover:scale-110 group-hover:text-success group-hover:bg-success-light transition-all duration-300">
|
||||
<UploadIcon className="h-8 w-8" />
|
||||
</div>
|
||||
<div className="text-sm text-black-title">
|
||||
<div className="text-sm text-oliver-black">
|
||||
<span className="font-bold text-success">Click to upload</span> or drag and drop
|
||||
</div>
|
||||
<p className="text-xs text-grey-700">SVG, PNG, JPG or GIF (max. 800x400px)</p>
|
||||
<p className="text-xs text-oliver-black/60">SVG, PNG, JPG or GIF (max. 800x400px)</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mx-auto h-16 w-16 bg-success-light text-success rounded-full flex items-center justify-center scale-110 shadow-sm">
|
||||
<CheckCircleIcon className="h-8 w-8" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-black-title truncate max-w-xs mx-auto">
|
||||
<div className="text-sm font-bold text-oliver-black truncate max-w-xs mx-auto">
|
||||
{file.name}
|
||||
</div>
|
||||
<p className="text-xs text-grey-900">Ready for analysis</p>
|
||||
<p className="text-xs text-oliver-black">Ready for analysis</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -921,19 +921,19 @@ const UploadProofModal: React.FC<{
|
|||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="bg-grey-100 px-4 py-4 sm:px-6 border-t border-grey-300 flex flex-col-reverse sm:flex-row sm:justify-end gap-3">
|
||||
<div className="bg-oliver-grey px-4 py-4 sm:px-6 border-t border-grey-300 flex flex-col-reverse sm:flex-row sm:justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-auto border-2 border-active-blue text-active-blue font-semibold py-2.5 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors duration-300 disabled:opacity-50"
|
||||
className="w-full sm:w-auto border-2 border-oliver-azure text-oliver-azure font-semibold py-2.5 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors duration-300 disabled:opacity-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitDisabled}
|
||||
className="w-full sm:w-auto flex items-center justify-center bg-active-blue text-white font-bold py-2.5 px-6 rounded-full hover:bg-active-blue/90 shadow-lg shadow-active-blue/20 transition-all duration-300 disabled:bg-grey-300 disabled:text-grey-700 disabled:shadow-none disabled:cursor-not-allowed"
|
||||
className="w-full sm:w-auto flex items-center justify-center bg-oliver-azure text-white font-bold py-2.5 px-6 rounded-full hover:bg-oliver-azure/90 shadow-lg shadow-oliver-azure/20 transition-all duration-300 disabled:bg-gray-300 disabled:text-oliver-black/60 disabled:shadow-none disabled:cursor-not-allowed"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
|
|
@ -967,11 +967,11 @@ const LoadingCell: React.FC<{ progress: { completed: number; total: number } }>
|
|||
<div className="flex flex-col items-center justify-center h-full px-2 text-center w-full">
|
||||
<div className="w-full bg-grey-300 rounded-full h-1.5 mb-1.5">
|
||||
<div
|
||||
className="bg-active-blue h-1.5 rounded-full"
|
||||
className="bg-oliver-azure h-1.5 rounded-full"
|
||||
style={{ width: `${percent}%`, transition: 'width 0.3s ease-in-out' }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-xs text-black-title truncate w-full">{statusText} ({percent}%)</span>
|
||||
<span className="text-xs text-oliver-black truncate w-full">{statusText} ({percent}%)</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -995,15 +995,15 @@ const DeleteConfirmationModal: React.FC<{
|
|||
className="bg-white rounded-[10px] shadow-xl p-6 sm:p-8 w-full max-w-md transform transition-all"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-xl font-bold text-primary-blue">Confirm Deletion</h3>
|
||||
<p className="text-black-title my-4">
|
||||
<h3 className="text-xl font-bold text-oliver-black">Confirm Deletion</h3>
|
||||
<p className="text-oliver-black my-4">
|
||||
Are you sure you want to permanently delete the proof "{proofName}"? This action cannot be undone.
|
||||
</p>
|
||||
<div className="mt-6 flex justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -1053,8 +1053,8 @@ const AnalysisErrorModal: React.FC<{
|
|||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary-blue">Analysis Error</h3>
|
||||
<p className="text-sm text-grey-700 mt-0.5">{proofName}</p>
|
||||
<h3 className="text-xl font-bold text-oliver-black">Analysis Error</h3>
|
||||
<p className="text-sm text-oliver-black/60 mt-0.5">{proofName}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1067,11 +1067,11 @@ const AnalysisErrorModal: React.FC<{
|
|||
|
||||
{failedAgents.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold text-grey-700">Agent Details</h4>
|
||||
<h4 className="text-sm font-semibold text-oliver-black/60">Agent Details</h4>
|
||||
{failedAgents.map(agent => (
|
||||
<div key={agent.label} className="bg-grey-100 rounded-lg p-3">
|
||||
<p className="text-sm font-medium text-primary-blue">{agent.label}</p>
|
||||
<p className="text-sm text-grey-700 mt-1">{agent.review.feedback}</p>
|
||||
<div key={agent.label} className="bg-oliver-grey rounded-lg p-3">
|
||||
<p className="text-sm font-medium text-oliver-black">{agent.label}</p>
|
||||
<p className="text-sm text-oliver-black/60 mt-1">{agent.review.feedback}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -1081,7 +1081,7 @@ const AnalysisErrorModal: React.FC<{
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -1113,8 +1113,8 @@ const CampaignDeleteConfirmationModal: React.FC<{
|
|||
className="bg-white rounded-[10px] shadow-xl p-6 sm:p-8 w-full max-w-md transform transition-all"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-xl font-bold text-primary-blue">Confirm Deletion</h3>
|
||||
<p className="text-black-title my-4">
|
||||
<h3 className="text-xl font-bold text-oliver-black">Confirm Deletion</h3>
|
||||
<p className="text-oliver-black my-4">
|
||||
{isBulk ? (
|
||||
<>Are you sure you want to permanently delete <strong>{selectedCount} selected campaign{selectedCount !== 1 ? 's' : ''}</strong>? All associated proofs will also be deleted. This action cannot be undone.</>
|
||||
) : (
|
||||
|
|
@ -1125,7 +1125,7 @@ const CampaignDeleteConfirmationModal: React.FC<{
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -1282,15 +1282,15 @@ const CampaignDetail: React.FC<{
|
|||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-grey-300 hover:text-primary-blue transition-colors duration-200"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-grey-300 hover:text-oliver-black transition-colors duration-200"
|
||||
title="Back to campaigns"
|
||||
aria-label="Back to campaigns list"
|
||||
>
|
||||
<ArrowLeftIcon className="h-6 w-6" />
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">{campaignName}</h1>
|
||||
<p className="text-base lg:text-lg text-grey-900 mt-1">Proof overview and compliance status.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">{campaignName}</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Proof overview and compliance status.</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -1319,7 +1319,7 @@ const CampaignDetail: React.FC<{
|
|||
<button
|
||||
onClick={() => handleExportPDF(proofs.filter(p => p.status === 'completed'), `${campaignName} - Campaign Report`)}
|
||||
disabled={isExporting || proofs.filter(p => p.status === 'completed').length === 0}
|
||||
className="flex items-center gap-2 bg-white text-active-blue font-semibold py-2 px-4 rounded-full border-2 border-active-blue hover:bg-active-blue hover:text-white transition-colors duration-300 disabled:bg-grey-300 disabled:text-grey-700 disabled:border-grey-300 disabled:cursor-wait"
|
||||
className="flex items-center gap-2 bg-white text-oliver-azure font-semibold py-2 px-4 rounded-full border-2 border-oliver-azure hover:bg-oliver-azure hover:text-white transition-colors duration-300 disabled:bg-gray-300 disabled:text-oliver-black/60 disabled:border-grey-300 disabled:cursor-wait"
|
||||
>
|
||||
{isExporting ? <SpinnerIcon className="h-5 w-5 custom-spinner" /> : <ExportIcon className="h-5 w-5" />}
|
||||
{isExporting ? 'Exporting...' : 'Export Campaign Report'}
|
||||
|
|
@ -1327,7 +1327,7 @@ const CampaignDetail: React.FC<{
|
|||
{!readOnly && (
|
||||
<button
|
||||
onClick={() => setIsUploadFormVisible(true)}
|
||||
className="flex items-center gap-2 bg-active-blue text-white font-semibold py-2 px-4 rounded-full hover:bg-active-blue/90 transition-colors duration-300"
|
||||
className="flex items-center gap-2 bg-oliver-azure text-white font-semibold py-2 px-4 rounded-full hover:bg-oliver-azure/90 transition-colors duration-300"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
Upload New Proof
|
||||
|
|
@ -1338,14 +1338,14 @@ const CampaignDetail: React.FC<{
|
|||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-grey-300">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Workfront #</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Sub-Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Type</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Overall Status</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Workfront #</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Sub-Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Type</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Overall Status</th>
|
||||
<th scope="col" className="relative px-6 py-3"><span className="sr-only">Actions</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -1353,18 +1353,18 @@ const CampaignDetail: React.FC<{
|
|||
{proofs.map((proof, index) => {
|
||||
if (proof.status === 'analyzing') {
|
||||
return (
|
||||
<tr key={proof.tempId} className="bg-grey-100 opacity-80">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{proof.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700 italic">Pending</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.proofType || 'N/A'}</td>
|
||||
<tr key={proof.tempId} className="bg-oliver-grey opacity-80">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{proof.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black/60 italic">Pending</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.proofType || 'N/A'}</td>
|
||||
<td className="px-6 py-4" colSpan={2}>
|
||||
{proof.analysisProgress ?
|
||||
<LoadingCell progress={proof.analysisProgress} /> :
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<SpinnerIcon className="h-5 w-5 text-active-blue custom-spinner" />
|
||||
<span className="ml-2 text-sm text-grey-900">Preparing...</span>
|
||||
<SpinnerIcon className="h-5 w-5 text-oliver-azure custom-spinner" />
|
||||
<span className="ml-2 text-sm text-oliver-black">Preparing...</span>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
|
|
@ -1375,11 +1375,11 @@ const CampaignDetail: React.FC<{
|
|||
if (proof.status === 'error') {
|
||||
return (
|
||||
<tr key={proof.tempId} className="bg-error-light">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{proof.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700 italic">Failed</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.proofType || 'N/A'}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{proof.proofName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black/60 italic">Failed</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.proofType || 'N/A'}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-error font-semibold" colSpan={2}>
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
|
|
@ -1390,7 +1390,7 @@ const CampaignDetail: React.FC<{
|
|||
</button>
|
||||
<button
|
||||
onClick={() => onRetryAnalysis(campaignName, proof.tempId || proof._id)}
|
||||
className="flex items-center gap-1.5 text-xs font-semibold text-active-blue hover:text-primary-blue whitespace-nowrap px-3 py-1.5 rounded-full bg-active-blue/10 hover:bg-active-blue/20 transition-colors"
|
||||
className="flex items-center gap-1.5 text-xs font-semibold text-oliver-azure hover:text-oliver-black whitespace-nowrap px-3 py-1.5 rounded-full bg-oliver-azure/10 hover:bg-oliver-azure/20 transition-colors"
|
||||
>
|
||||
<ArrowPathIcon className="h-4 w-4" />
|
||||
Retry
|
||||
|
|
@ -1410,21 +1410,21 @@ const CampaignDetail: React.FC<{
|
|||
return (
|
||||
<tr
|
||||
key={latestVersion.workfrontId || index}
|
||||
className={`${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'} ${isClickable ? "hover:bg-info-light cursor-pointer" : ""}`}
|
||||
className={`${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'} ${isClickable ? "hover:bg-oliver-grey cursor-pointer" : ""}`}
|
||||
onClick={() => isClickable && onSelectProof(proof)}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">
|
||||
{proof.proofName}
|
||||
{isVersioned && (
|
||||
<span className="ml-2 bg-grey-300 text-black-title text-xs font-bold px-2 py-0.5 rounded-full">
|
||||
<span className="ml-2 bg-grey-300 text-oliver-black text-xs font-bold px-2 py-0.5 rounded-full">
|
||||
V{latestVersion.version}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{latestVersion.workfrontId}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-primary-blue">{proof.proofType || 'N/A'}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{latestVersion.workfrontId}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{proof.proofType || 'N/A'}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<OverallStatusBadge status={latestVersion.overallStatus} />
|
||||
</td>
|
||||
|
|
@ -1433,7 +1433,7 @@ const CampaignDetail: React.FC<{
|
|||
{!readOnly && (
|
||||
<button
|
||||
onClick={(e) => handleNewVersionClick(e, proof)}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-info-light hover:text-active-blue transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-oliver-grey hover:text-oliver-azure transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title={`Upload new version for ${proof.proofName}`}
|
||||
disabled={isUploading || isExporting}
|
||||
>
|
||||
|
|
@ -1446,7 +1446,7 @@ const CampaignDetail: React.FC<{
|
|||
handleExportPDF([proof], `${campaignName} - ${proof.proofName} Report`);
|
||||
}}
|
||||
disabled={isExporting}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-success-light hover:text-success transition-colors disabled:opacity-50 disabled:cursor-wait"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-success-light hover:text-success transition-colors disabled:opacity-50 disabled:cursor-wait"
|
||||
title={`Export PDF for ${proof.proofName}`}
|
||||
>
|
||||
<PDFIcon className="h-5 w-5" />
|
||||
|
|
@ -1458,7 +1458,7 @@ const CampaignDetail: React.FC<{
|
|||
setProofToDelete(proof);
|
||||
}}
|
||||
disabled={isExporting}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-error-light hover:text-error transition-colors disabled:opacity-50"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-error-light hover:text-error transition-colors disabled:opacity-50"
|
||||
title={`Delete ${proof.proofName}`}
|
||||
>
|
||||
<TrashIcon className="h-5 w-5" />
|
||||
|
|
@ -1676,15 +1676,15 @@ const ProofDetailView: React.FC<{
|
|||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-grey-300 hover:text-primary-blue transition-colors duration-200"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-grey-300 hover:text-oliver-black transition-colors duration-200"
|
||||
title="Back to campaign details"
|
||||
aria-label="Back to campaign details"
|
||||
>
|
||||
<ArrowLeftIcon className="h-6 w-6" />
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">{proof.proofName}</h1>
|
||||
<div className="flex items-center gap-2 mt-2 text-sm text-grey-700 font-medium">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">{proof.proofName}</h1>
|
||||
<div className="flex items-center gap-2 mt-2 text-sm text-oliver-black/60 font-medium">
|
||||
<span>{proof.channel}</span>
|
||||
<span className="text-grey-300">•</span>
|
||||
<span>{proof.subChannel}</span>
|
||||
|
|
@ -1703,7 +1703,7 @@ const ProofDetailView: React.FC<{
|
|||
<div className="lg:col-span-1">
|
||||
<div className="sticky top-8 flex flex-col gap-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-4">
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-4">
|
||||
Proof Preview
|
||||
</h2>
|
||||
<ProofPreview
|
||||
|
|
@ -1715,14 +1715,14 @@ const ProofDetailView: React.FC<{
|
|||
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3 gap-2">
|
||||
<h3 className="text-xl font-semibold text-primary-blue flex items-center gap-2">
|
||||
<HistoryIcon className="h-6 w-6 text-active-blue"/>
|
||||
<h3 className="text-xl font-semibold text-oliver-black flex items-center gap-2">
|
||||
<HistoryIcon className="h-6 w-6 text-oliver-azure"/>
|
||||
Version History
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
className="flex items-center gap-2 text-sm bg-active-blue text-white font-semibold py-1.5 px-3 rounded-full border border-transparent hover:bg-active-blue/90 transition-colors duration-200"
|
||||
className="flex items-center gap-2 text-sm bg-oliver-azure text-white font-semibold py-1.5 px-3 rounded-full border border-transparent hover:bg-oliver-azure/90 transition-colors duration-200"
|
||||
title={`Download Version ${selectedVersion.version}`}
|
||||
>
|
||||
<DownloadIcon className="h-4 w-4" />
|
||||
|
|
@ -1731,7 +1731,7 @@ const ProofDetailView: React.FC<{
|
|||
<button
|
||||
onClick={handleDownloadReport}
|
||||
disabled={isExporting}
|
||||
className="flex items-center gap-2 text-sm bg-white text-active-blue font-semibold py-1.5 px-3 rounded-full border-2 border-active-blue hover:bg-active-blue hover:text-white transition-colors duration-200 disabled:bg-grey-300 disabled:text-grey-700 disabled:border-grey-300 disabled:cursor-wait"
|
||||
className="flex items-center gap-2 text-sm bg-white text-oliver-azure font-semibold py-1.5 px-3 rounded-full border-2 border-oliver-azure hover:bg-oliver-azure hover:text-white transition-colors duration-200 disabled:bg-gray-300 disabled:text-oliver-black/60 disabled:border-grey-300 disabled:cursor-wait"
|
||||
title={`Download Report for Version ${selectedVersion.version}`}
|
||||
>
|
||||
{isExporting ? (
|
||||
|
|
@ -1750,7 +1750,7 @@ const ProofDetailView: React.FC<{
|
|||
<button
|
||||
onClick={handleUploadClick}
|
||||
disabled={isUploadingNewVersion}
|
||||
className="flex items-center gap-2 text-sm bg-white text-active-blue font-semibold py-1.5 px-3 rounded-full border-2 border-active-blue hover:bg-active-blue hover:text-white transition-colors duration-200 disabled:bg-grey-300 disabled:text-grey-700 disabled:border-grey-300 disabled:cursor-wait"
|
||||
className="flex items-center gap-2 text-sm bg-white text-oliver-azure font-semibold py-1.5 px-3 rounded-full border-2 border-oliver-azure hover:bg-oliver-azure hover:text-white transition-colors duration-200 disabled:bg-gray-300 disabled:text-oliver-black/60 disabled:border-grey-300 disabled:cursor-wait"
|
||||
title="Upload a new version of this proof"
|
||||
>
|
||||
{isUploadingNewVersion ? (
|
||||
|
|
@ -1785,15 +1785,15 @@ const ProofDetailView: React.FC<{
|
|||
onClick={() => setSelectedVersionIndex(index)}
|
||||
className={`w-full text-left p-3 rounded-[10px] border-2 transition-all ${
|
||||
isActive
|
||||
? 'bg-info-light border-active-blue shadow-sm'
|
||||
: 'bg-white border-grey-300 hover:border-active-blue/50 hover:bg-grey-100'
|
||||
? 'bg-oliver-grey border-oliver-azure shadow-sm'
|
||||
: 'bg-white border-grey-300 hover:border-oliver-azure/50 hover:bg-oliver-grey'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className={`font-bold ${isActive ? 'text-primary-blue' : 'text-black-title'}`}>Version {version.version}</p>
|
||||
<p className="text-xs text-grey-700">{version.timestamp}</p>
|
||||
<p className={`font-bold ${isActive ? 'text-oliver-black' : 'text-oliver-black'}`}>Version {version.version}</p>
|
||||
<p className="text-xs text-oliver-black/60">{version.timestamp}</p>
|
||||
</div>
|
||||
<p className="text-sm text-grey-900 mt-1">Workfront ID: {version.workfrontId}</p>
|
||||
<p className="text-sm text-oliver-black mt-1">Workfront ID: {version.workfrontId}</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
|
@ -1806,8 +1806,8 @@ const ProofDetailView: React.FC<{
|
|||
<div className="mb-6 bg-warning-light border border-warning rounded-[10px] p-4 flex items-start gap-3">
|
||||
<ExclamationTriangleIcon className="w-5 h-5 text-warning flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-semibold text-black-title">Identical File Detected</p>
|
||||
<p className="text-sm text-grey-900 mt-1">
|
||||
<p className="font-semibold text-oliver-black">Identical File Detected</p>
|
||||
<p className="text-sm text-oliver-black mt-1">
|
||||
This file is exactly the same as the previous version.
|
||||
The analysis results shown are from the new analysis,
|
||||
but no changes were made to the creative.
|
||||
|
|
|
|||
|
|
@ -46,14 +46,14 @@ export const ChecksOverview: React.FC = () => {
|
|||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
{/* Header */}
|
||||
<div className="max-w-3xl mx-auto text-center mb-12">
|
||||
<div className="inline-flex items-center px-4 py-1.5 rounded-full bg-white border border-lime text-primary-blue text-xs font-bold uppercase tracking-widest mb-4 shadow-sm">
|
||||
<div className="inline-flex items-center px-4 py-1.5 rounded-full bg-white border border-oliver-sky text-oliver-black text-xs font-bold uppercase tracking-widest mb-4 shadow-sm">
|
||||
<span className="w-2 h-2 rounded-full bg-success mr-2 animate-pulse"></span>
|
||||
Intelligent Workflow
|
||||
</div>
|
||||
<h2 className="text-3xl md:text-4xl font-semibold text-primary-blue tracking-tight mb-4">
|
||||
<h2 className="text-3xl md:text-4xl font-semibold text-oliver-black tracking-tight mb-4">
|
||||
Orchestrated by AI
|
||||
</h2>
|
||||
<p className="text-lg text-black-title leading-relaxed">
|
||||
<p className="text-lg text-oliver-black leading-relaxed">
|
||||
A central Lead Agent coordinates specialized experts to review every aspect of your content in parallel.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -62,17 +62,17 @@ export const ChecksOverview: React.FC = () => {
|
|||
|
||||
{/* 1. LEAD AGENT CARD (Left) */}
|
||||
<div className="w-full max-w-md lg:w-[380px] relative z-20 group/lead flex flex-col">
|
||||
<div className="flex-1 relative bg-primary-blue rounded-[20px] p-6 shadow-2xl shadow-primary-blue/20 border border-white/10 text-center transition-all duration-500 hover:-translate-y-2 hover:shadow-primary-blue/40 overflow-hidden flex flex-col items-center justify-center">
|
||||
<div className="flex-1 relative bg-oliver-black rounded-[20px] p-6 shadow-2xl shadow-oliver-black/20 border border-white/10 text-center transition-all duration-500 hover:-translate-y-2 hover:shadow-oliver-black/40 overflow-hidden flex flex-col items-center justify-center">
|
||||
{/* Animated Gradient bg */}
|
||||
<div className="absolute top-[-50%] left-[-50%] w-[200%] h-[200%] bg-gradient-to-b from-transparent via-white/5 to-transparent rotate-45 transition-transform duration-[3s] group-hover/lead:translate-y-10"></div>
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="h-20 w-20 mx-auto rounded-2xl bg-white/10 border border-white/20 backdrop-blur-md flex items-center justify-center mb-4 shadow-lg shadow-primary-blue/30 group-hover/lead:scale-110 transition-all duration-500">
|
||||
<div className="h-20 w-20 mx-auto rounded-2xl bg-white/10 border border-white/20 backdrop-blur-md flex items-center justify-center mb-4 shadow-lg shadow-oliver-black/30 group-hover/lead:scale-110 transition-all duration-500">
|
||||
<LeadAgentIcon className="h-10 w-10 text-white" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-white mb-2">Lead Agent</h3>
|
||||
<p className="text-cyan-brand font-bold text-xs uppercase tracking-widest mb-4">Orchestrator</p>
|
||||
<p className="text-oliver-azure font-bold text-xs uppercase tracking-widest mb-4">Orchestrator</p>
|
||||
<p className="text-slate-300 text-sm leading-relaxed">
|
||||
Synthesises feedback & coordinates analysis across all specialist agents.
|
||||
</p>
|
||||
|
|
@ -80,11 +80,11 @@ export const ChecksOverview: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Connection Node (Desktop) */}
|
||||
<div className="hidden lg:flex absolute top-1/2 -right-4 w-8 h-8 bg-primary-blue rounded-full border-4 border-lime/50 items-center justify-center z-30 -translate-y-1/2 shadow-sm translate-x-1/2">
|
||||
<div className="hidden lg:flex absolute top-1/2 -right-4 w-8 h-8 bg-oliver-black rounded-full border-4 border-oliver-sky/50 items-center justify-center z-30 -translate-y-1/2 shadow-sm translate-x-1/2">
|
||||
<div className="w-2.5 h-2.5 bg-success rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
{/* Connection Node (Mobile) */}
|
||||
<div className="lg:hidden absolute -bottom-4 left-1/2 w-8 h-8 bg-primary-blue rounded-full border-4 border-lime/50 items-center justify-center z-30 -translate-x-1/2 shadow-sm">
|
||||
<div className="lg:hidden absolute -bottom-4 left-1/2 w-8 h-8 bg-oliver-black rounded-full border-4 border-oliver-sky/50 items-center justify-center z-30 -translate-x-1/2 shadow-sm">
|
||||
<div className="w-2.5 h-2.5 bg-success rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -93,8 +93,8 @@ export const ChecksOverview: React.FC = () => {
|
|||
<div className="hidden lg:block w-24 relative z-10 self-center h-48">
|
||||
<svg className="absolute inset-0 w-full h-full overflow-visible" viewBox="0 0 100 200" preserveAspectRatio="none">
|
||||
{/* Connecting paths */}
|
||||
<path d="M0,100 C50,100 50,30 100,30" fill="none" stroke="#C3FB5A" strokeWidth="2" strokeDasharray="6 6" />
|
||||
<path d="M0,100 C50,100 50,170 100,170" fill="none" stroke="#C3FB5A" strokeWidth="2" strokeDasharray="6 6" />
|
||||
<path d="M0,100 C50,100 50,30 100,30" fill="none" stroke="#5DF5EA" strokeWidth="2" strokeDasharray="6 6" />
|
||||
<path d="M0,100 C50,100 50,170 100,170" fill="none" stroke="#5DF5EA" strokeWidth="2" strokeDasharray="6 6" />
|
||||
|
||||
{/* Animated pulses on paths */}
|
||||
<circle r="3" fill="#09821F">
|
||||
|
|
@ -107,7 +107,7 @@ export const ChecksOverview: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Mobile Connector Spacer */}
|
||||
<div className="lg:hidden h-8 w-0.5 bg-lime my-3"></div>
|
||||
<div className="lg:hidden h-8 w-0.5 bg-oliver-sky my-3"></div>
|
||||
|
||||
{/* 3. SPECIALIST AGENTS (Right) */}
|
||||
<div className="flex-1 grid grid-cols-1 md:grid-cols-2 gap-4 w-full max-w-4xl pl-0 lg:pl-4">
|
||||
|
|
@ -117,16 +117,16 @@ export const ChecksOverview: React.FC = () => {
|
|||
className="group relative rounded-[10px] p-5 border border-grey-300 shadow-sm transition-all duration-500 hover:shadow-xl hover:shadow-success/10 hover:-translate-y-2 hover:border-success overflow-hidden bg-white"
|
||||
>
|
||||
{/* Base Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white via-white to-lime/10 opacity-100 transition-opacity duration-500 group-hover:opacity-0"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white via-white to-oliver-sky/10 opacity-100 transition-opacity duration-500 group-hover:opacity-0"></div>
|
||||
|
||||
{/* Hover Gradient - Interactive Green */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white via-success-light to-lime/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white via-success-light to-oliver-sky/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
|
||||
|
||||
{/* Animated Shine/Sweep Effect */}
|
||||
<div className="absolute top-[-50%] left-[-50%] w-[200%] h-[200%] bg-gradient-to-b from-transparent via-success/5 to-transparent rotate-45 translate-y-12 transition-transform duration-1000 group-hover:translate-y-[-10%] pointer-events-none"></div>
|
||||
|
||||
{/* Glassy Highlight */}
|
||||
<div className="absolute -top-24 -right-24 w-48 h-48 bg-gradient-to-br from-lime/30 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none"></div>
|
||||
<div className="absolute -top-24 -right-24 w-48 h-48 bg-gradient-to-br from-oliver-sky/30 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none"></div>
|
||||
|
||||
<div className="relative z-10 flex items-start gap-4">
|
||||
<div className={`flex-shrink-0 w-12 h-12 rounded-[10px] flex items-center justify-center transition-all duration-300 group-hover:scale-110 group-hover:shadow-md bg-success-light text-success group-hover:bg-success group-hover:text-white`}>
|
||||
|
|
@ -134,13 +134,13 @@ export const ChecksOverview: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<h4 className="text-base font-bold text-black-title group-hover:text-primary-blue transition-colors duration-300 mb-0.5">
|
||||
<h4 className="text-base font-bold text-oliver-black group-hover:text-oliver-black transition-colors duration-300 mb-0.5">
|
||||
{agent.name}
|
||||
</h4>
|
||||
<p className="text-[10px] font-extrabold text-grey-700 uppercase tracking-wider mb-1 group-hover:text-success transition-colors duration-300">
|
||||
<p className="text-[10px] font-extrabold text-oliver-black/60 uppercase tracking-wider mb-1 group-hover:text-success transition-colors duration-300">
|
||||
{agent.role}
|
||||
</p>
|
||||
<p className="text-xs text-black-title leading-relaxed group-hover:text-black-title">
|
||||
<p className="text-xs text-oliver-black leading-relaxed group-hover:text-oliver-black">
|
||||
{agent.description}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ export const CopyGenAI: React.FC = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full bg-grey-100">
|
||||
<div className="flex h-full bg-oliver-grey">
|
||||
{/* Conversation History Sidebar */}
|
||||
<aside className="w-80 flex-shrink-0 bg-gray-100 border-r border-gray-200 flex flex-col">
|
||||
<div className="p-4 border-b border-gray-200">
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-active-blue text-white font-semibold py-2.5 px-4 rounded-lg hover:bg-primary-blue transition-colors duration-300">
|
||||
<button className="w-full flex items-center justify-center gap-2 bg-oliver-azure text-white font-semibold py-2.5 px-4 rounded-lg hover:bg-oliver-azure transition-colors duration-300">
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
Start new conversation
|
||||
</button>
|
||||
|
|
@ -41,7 +41,7 @@ export const CopyGenAI: React.FC = () => {
|
|||
onClick={() => setActiveConversationId(convo.id)}
|
||||
className={`w-full flex items-center gap-3 px-3 py-2.5 text-left text-sm font-medium rounded-md transition-colors duration-200 ${
|
||||
activeConversationId === convo.id
|
||||
? 'bg-active-blue/10 text-active-blue'
|
||||
? 'bg-oliver-azure/10 text-oliver-azure'
|
||||
: 'text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -71,7 +71,7 @@ export const CopyGenAI: React.FC = () => {
|
|||
{/* Message Display */}
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-primary-blue">CopyGenAI</h1>
|
||||
<h1 className="text-3xl font-bold text-oliver-black">CopyGenAI</h1>
|
||||
<p className="mt-2 text-gray-600">How can I help you today?</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -89,13 +89,13 @@ export const CopyGenAI: React.FC = () => {
|
|||
handleSendMessage(e);
|
||||
}
|
||||
}}
|
||||
className="w-full p-3 pr-14 border border-gray-300 rounded-lg resize-none focus:ring-2 focus:ring-active-blue focus:border-active-blue transition"
|
||||
className="w-full p-3 pr-14 border border-gray-300 rounded-lg resize-none focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition"
|
||||
placeholder="Type your message here..."
|
||||
rows={1}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-full text-white bg-active-blue hover:bg-primary-blue disabled:bg-gray-400 transition-colors"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-full text-white bg-oliver-azure hover:bg-oliver-azure disabled:bg-gray-400 transition-colors"
|
||||
disabled={!message.trim()}
|
||||
aria-label="Send message"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -93,31 +93,31 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-primary-blue">Create New Campaign</h2>
|
||||
<button onClick={onClose} className="p-1 rounded-full text-grey-700 hover:bg-grey-100 hover:text-black-title transition-colors">
|
||||
<h2 className="text-2xl font-bold text-oliver-black">Create New Campaign</h2>
|
||||
<button onClick={onClose} className="p-1 rounded-full text-oliver-black/60 hover:bg-oliver-grey hover:text-oliver-black transition-colors">
|
||||
<XIcon className="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} noValidate>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="campaign-name" className="block text-sm font-medium text-black-title">Campaign Name</label>
|
||||
<label htmlFor="campaign-name" className="block text-sm font-medium text-oliver-black">Campaign Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="campaign-name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="brand-guidelines" className="block text-sm font-medium text-black-title">Brand Guidelines</label>
|
||||
<label htmlFor="brand-guidelines" className="block text-sm font-medium text-oliver-black">Brand Guidelines</label>
|
||||
<select
|
||||
id="brand-guidelines"
|
||||
value={selectedBrandGuideline}
|
||||
onChange={(e) => setSelectedBrandGuideline(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select brand guidelines</option>
|
||||
|
|
@ -127,13 +127,13 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
|
|||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="workfront-id" className="block text-sm font-medium text-black-title">Workfront Campaign ID <span className="text-grey-700 font-normal">(Optional)</span></label>
|
||||
<label htmlFor="workfront-id" className="block text-sm font-medium text-oliver-black">Workfront Campaign ID <span className="text-oliver-black/60 font-normal">(Optional)</span></label>
|
||||
<input
|
||||
type="text"
|
||||
id="workfront-id"
|
||||
value={workfrontId}
|
||||
onChange={handleWorkfrontIdChange}
|
||||
className={`mt-1 block w-full p-2 border-2 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title placeholder:text-grey-700 ${error ? 'border-error focus:border-error focus:ring-error' : 'border-grey-700'}`}
|
||||
className={`mt-1 block w-full p-2 border-2 rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black placeholder:text-oliver-black/60 ${error ? 'border-error focus:border-error focus:ring-error' : 'border-oliver-azure'}`}
|
||||
placeholder="#WF_12345"
|
||||
aria-invalid={!!error}
|
||||
aria-describedby="workfront-id-error"
|
||||
|
|
@ -141,33 +141,33 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
|
|||
{error && <p id="workfront-id-error" className="mt-1 text-sm text-error">{error}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="client-lead" className="block text-sm font-medium text-black-title">Client Lead</label>
|
||||
<label htmlFor="client-lead" className="block text-sm font-medium text-oliver-black">Client Lead</label>
|
||||
<input
|
||||
type="text"
|
||||
id="client-lead"
|
||||
value={clientLead}
|
||||
onChange={(e) => setClientLead(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-black-title">Agency</label>
|
||||
<label className="block text-sm font-medium text-oliver-black">Agency</label>
|
||||
<input
|
||||
type="text"
|
||||
value={user?.agencyName ?? ''}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-grey-100 text-grey-900 cursor-not-allowed"
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-oliver-grey text-oliver-black cursor-not-allowed"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="agency-lead" className="block text-sm font-medium text-black-title">Agency Lead</label>
|
||||
<label htmlFor="agency-lead" className="block text-sm font-medium text-oliver-black">Agency Lead</label>
|
||||
<input
|
||||
type="text"
|
||||
id="agency-lead"
|
||||
value={agencyLead}
|
||||
onChange={(e) => setAgencyLead(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -176,13 +176,13 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors duration-300"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors duration-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors duration-300 disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
disabled={isFormInvalid || !!error}
|
||||
>
|
||||
Create Campaign
|
||||
|
|
|
|||
|
|
@ -79,32 +79,32 @@ export const CreateProjectModal: React.FC<CreateProjectModalProps> = ({ isOpen,
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-primary-blue">Create New Project</h2>
|
||||
<button onClick={onClose} className="p-1 rounded-full text-grey-700 hover:bg-grey-100 hover:text-black-title transition-colors">
|
||||
<h2 className="text-2xl font-bold text-oliver-black">Create New Project</h2>
|
||||
<button onClick={onClose} className="p-1 rounded-full text-oliver-black/60 hover:bg-oliver-grey hover:text-oliver-black transition-colors">
|
||||
<XIcon className="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} noValidate>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="project-name" className="block text-sm font-medium text-black-title">Project Name</label>
|
||||
<label htmlFor="project-name" className="block text-sm font-medium text-oliver-black">Project Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="project-name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="workfront-id" className="block text-sm font-medium text-black-title">Workfront Project ID</label>
|
||||
<label htmlFor="workfront-id" className="block text-sm font-medium text-oliver-black">Workfront Project ID</label>
|
||||
<input
|
||||
type="text"
|
||||
id="workfront-id"
|
||||
value={workfrontId}
|
||||
onChange={handleWorkfrontIdChange}
|
||||
className={`mt-1 block w-full p-2 border-2 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title placeholder:text-grey-700 ${error ? 'border-error focus:border-error focus:ring-error' : 'border-grey-700'}`}
|
||||
className={`mt-1 block w-full p-2 border-2 rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black placeholder:text-oliver-black/60 ${error ? 'border-error focus:border-error focus:ring-error' : 'border-oliver-azure'}`}
|
||||
placeholder="#WF_12345"
|
||||
required
|
||||
aria-invalid={!!error}
|
||||
|
|
@ -113,31 +113,31 @@ export const CreateProjectModal: React.FC<CreateProjectModalProps> = ({ isOpen,
|
|||
{error && <p id="workfront-id-error" className="mt-1 text-sm text-error">{error}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="client-lead" className="block text-sm font-medium text-black-title">Client Lead</label>
|
||||
<label htmlFor="client-lead" className="block text-sm font-medium text-oliver-black">Client Lead</label>
|
||||
<input
|
||||
type="text"
|
||||
id="client-lead"
|
||||
value={clientLead}
|
||||
onChange={(e) => setClientLead(e.target.value)}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-700 rounded-[10px] shadow-sm focus:ring-active-blue focus:border-active-blue transition bg-white text-black-title"
|
||||
className="mt-1 block w-full p-2 border-2 border-oliver-azure rounded-[10px] shadow-sm focus:ring-oliver-azure focus:border-oliver-azure transition bg-white text-oliver-black"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-black-title">Agency</label>
|
||||
<label className="block text-sm font-medium text-oliver-black">Agency</label>
|
||||
<input
|
||||
type="text"
|
||||
value={user?.agencyName ?? ''}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-grey-100 text-grey-900 cursor-not-allowed"
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-oliver-grey text-oliver-black cursor-not-allowed"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-black-title">Agency Lead</label>
|
||||
<label className="block text-sm font-medium text-oliver-black">Agency Lead</label>
|
||||
<input
|
||||
type="text"
|
||||
value={user?.name ?? ''}
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-grey-100 text-grey-900 cursor-not-allowed"
|
||||
className="mt-1 block w-full p-2 border-2 border-grey-300 rounded-[10px] shadow-sm bg-oliver-grey text-oliver-black cursor-not-allowed"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -146,13 +146,13 @@ export const CreateProjectModal: React.FC<CreateProjectModalProps> = ({ isOpen,
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors duration-300"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors duration-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors duration-300 disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
disabled={isFormInvalid || !!error}
|
||||
>
|
||||
Create Project
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const RagStatusBadge: React.FC<{ status: RagStatus; isLarge?: boolean }> = ({ st
|
|||
iconColor = 'text-error';
|
||||
break;
|
||||
case 'Amber':
|
||||
colorClasses = 'bg-warning-light border-warning text-black-title';
|
||||
colorClasses = 'bg-warning-light border-warning text-oliver-black';
|
||||
iconColor = 'text-warning';
|
||||
break;
|
||||
case 'Green':
|
||||
|
|
@ -121,8 +121,8 @@ const RagStatusBadge: React.FC<{ status: RagStatus; isLarge?: boolean }> = ({ st
|
|||
iconColor = 'text-success';
|
||||
break;
|
||||
case 'Error':
|
||||
colorClasses = 'bg-grey-100 border-grey-300 text-grey-900';
|
||||
iconColor = 'text-grey-700';
|
||||
colorClasses = 'bg-oliver-grey border-grey-300 text-oliver-black';
|
||||
iconColor = 'text-oliver-black/60';
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -165,20 +165,20 @@ const ResolveIssueModal: React.FC<{
|
|||
className="bg-white rounded-[10px] shadow-2xl p-8 w-full max-w-lg transform transition-all border border-grey-300"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-2xl font-bold text-primary-blue mb-2">Resolve Issue</h3>
|
||||
<p className="text-black-title mb-6">Please provide a reason for manually resolving this issue.</p>
|
||||
<h3 className="text-2xl font-bold text-oliver-black mb-2">Resolve Issue</h3>
|
||||
<p className="text-oliver-black mb-6">Please provide a reason for manually resolving this issue.</p>
|
||||
|
||||
<div className="my-6 p-4 bg-grey-100 border border-grey-300 rounded-[10px] text-black-title italic text-sm">
|
||||
<div className="my-6 p-4 bg-oliver-grey border border-grey-300 rounded-[10px] text-oliver-black italic text-sm">
|
||||
"{issueText}"
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<label htmlFor="resolution-reason" className="block text-sm font-bold text-black-title mb-2">Reason for resolution</label>
|
||||
<label htmlFor="resolution-reason" className="block text-sm font-bold text-oliver-black mb-2">Reason for resolution</label>
|
||||
<textarea
|
||||
id="resolution-reason"
|
||||
value={reason}
|
||||
onChange={(e) => setReason(e.target.value)}
|
||||
className="w-full p-4 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue/50 focus:border-active-blue transition-all bg-grey-100 focus:bg-white resize-none text-black-title"
|
||||
className="w-full p-4 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure/50 focus:border-oliver-azure transition-all bg-oliver-grey focus:bg-white resize-none text-oliver-black"
|
||||
rows={4}
|
||||
placeholder="e.g. 'Legal team has approved this exception via email...'"
|
||||
required
|
||||
|
|
@ -187,13 +187,13 @@ const ResolveIssueModal: React.FC<{
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-6 py-2.5 rounded-full border-2 border-active-blue text-active-blue font-semibold hover:bg-active-blue hover:text-white transition-colors"
|
||||
className="px-6 py-2.5 rounded-full border-2 border-oliver-azure text-oliver-azure font-semibold hover:bg-oliver-azure hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-6 py-2.5 rounded-full bg-active-blue text-white font-bold shadow-lg shadow-active-blue/30 hover:bg-active-blue/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-6 py-2.5 rounded-full bg-oliver-azure text-white font-bold shadow-lg shadow-oliver-azure/30 hover:bg-oliver-azure/90 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={!reason.trim()}
|
||||
>
|
||||
Submit Resolution
|
||||
|
|
@ -239,15 +239,15 @@ const FlagIssueModal: React.FC<{
|
|||
{showSuccess ? (
|
||||
<div className="text-center py-8">
|
||||
<CheckCircleIcon className="h-12 w-12 text-success mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold text-primary-blue mb-2">Flag Submitted</h3>
|
||||
<h3 className="text-xl font-bold text-oliver-black mb-2">Flag Submitted</h3>
|
||||
<p className="text-slate-500">Thank you for your feedback on the {agentName}'s review. This has been logged for auditing.</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold text-primary-blue">Flag Feedback</h3>
|
||||
<p className="text-slate-500 text-sm mt-1">Reporting incorrect feedback from <span className="font-semibold text-active-blue">{agentName}</span></p>
|
||||
<h3 className="text-2xl font-bold text-oliver-black">Flag Feedback</h3>
|
||||
<p className="text-slate-500 text-sm mt-1">Reporting incorrect feedback from <span className="font-semibold text-oliver-azure">{agentName}</span></p>
|
||||
</div>
|
||||
<button onClick={onClose} className="p-2 rounded-full hover:bg-slate-100 text-slate-400 hover:text-slate-600 transition-colors">
|
||||
<XIcon className="h-6 w-6" />
|
||||
|
|
@ -555,7 +555,7 @@ const SubReviewCard: React.FC<{
|
|||
{issue.status === 'actionable' ? (
|
||||
<button
|
||||
onClick={() => handleOpenModal(actualIndex)}
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-active-blue hover:text-primary-blue bg-cyan-brand/10 hover:bg-cyan-brand/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-oliver-azure hover:text-oliver-black bg-oliver-azure/10 hover:bg-oliver-azure/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Mark Resolved
|
||||
</button>
|
||||
|
|
@ -570,7 +570,7 @@ const SubReviewCard: React.FC<{
|
|||
</span>
|
||||
<button
|
||||
onClick={() => handleReopen(actualIndex)}
|
||||
className="text-xs font-semibold text-slate-400 hover:text-primary-blue bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="text-xs font-semibold text-slate-400 hover:text-oliver-black bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Re-open
|
||||
</button>
|
||||
|
|
@ -630,7 +630,7 @@ const SubReviewCard: React.FC<{
|
|||
{issue.status === 'actionable' ? (
|
||||
<button
|
||||
onClick={() => handleOpenModal(actualIndex)}
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-active-blue hover:text-primary-blue bg-cyan-brand/10 hover:bg-cyan-brand/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-oliver-azure hover:text-oliver-black bg-oliver-azure/10 hover:bg-oliver-azure/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Mark Resolved
|
||||
</button>
|
||||
|
|
@ -645,7 +645,7 @@ const SubReviewCard: React.FC<{
|
|||
</span>
|
||||
<button
|
||||
onClick={() => handleReopen(actualIndex)}
|
||||
className="text-xs font-semibold text-slate-400 hover:text-primary-blue bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="text-xs font-semibold text-slate-400 hover:text-oliver-black bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Re-open
|
||||
</button>
|
||||
|
|
@ -691,7 +691,7 @@ const SubReviewCard: React.FC<{
|
|||
{issue.status === 'actionable' ? (
|
||||
<button
|
||||
onClick={() => handleOpenModal(index)}
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-active-blue hover:text-primary-blue bg-cyan-brand/10 hover:bg-cyan-brand/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="flex-shrink-0 opacity-0 group-hover/issue:opacity-100 focus:opacity-100 text-xs font-semibold text-oliver-azure hover:text-oliver-black bg-oliver-azure/10 hover:bg-oliver-azure/20 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Mark Resolved
|
||||
</button>
|
||||
|
|
@ -706,7 +706,7 @@ const SubReviewCard: React.FC<{
|
|||
</span>
|
||||
<button
|
||||
onClick={() => handleReopen(index)}
|
||||
className="text-xs font-semibold text-slate-400 hover:text-primary-blue bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
className="text-xs font-semibold text-slate-400 hover:text-oliver-black bg-slate-100 hover:bg-slate-200 px-2.5 py-1 rounded-lg transition-all"
|
||||
>
|
||||
Re-open
|
||||
</button>
|
||||
|
|
@ -734,8 +734,8 @@ const SubReviewCard: React.FC<{
|
|||
const LeadAgentSummary: React.FC<{ status: OverallStatus, summary: string, onFlag: () => void; isFlagged?: boolean; }> = ({ status, summary, onFlag, isFlagged }) => {
|
||||
const isPassed = status === 'Passed';
|
||||
|
||||
let themeStyles = 'from-sky-50 to-white border-sky-100 text-primary-blue';
|
||||
let iconBg = 'bg-sky-100 text-active-blue';
|
||||
let themeStyles = 'from-sky-50 to-white border-sky-100 text-oliver-black';
|
||||
let iconBg = 'bg-sky-100 text-oliver-azure';
|
||||
let icon = <InformationCircleIcon className="h-8 w-8" />;
|
||||
let blobColor = 'bg-sky-400';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { EagleIcon } from './icons/EagleIcon';
|
|||
|
||||
export const Header: React.FC = () => {
|
||||
return (
|
||||
<header className="bg-primary-blue p-4 shadow-md">
|
||||
<header className="bg-oliver-black p-4 shadow-md">
|
||||
<div className="max-w-7xl mx-auto flex items-center">
|
||||
<EagleIcon className="h-10 w-10 text-cyan-brand" />
|
||||
<EagleIcon className="h-10 w-10 text-oliver-azure" />
|
||||
<h1 className="text-xl sm:text-2xl font-bold text-white ml-3">
|
||||
AI Proof Reviewer
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -17,25 +17,21 @@ export const Hero: React.FC<HeroProps> = ({ onGetStarted }) => {
|
|||
<div className="relative overflow-hidden bg-white min-h-[350px] sm:min-h-[400px] md:min-h-[500px] flex items-center">
|
||||
<div className="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16 md:py-24 relative z-10 w-full">
|
||||
<div className="max-w-3xl">
|
||||
<div className="text-teal-brand text-xs font-bold uppercase tracking-widest mb-6">
|
||||
AI-Powered Compliance
|
||||
</div>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-semibold text-teal-brand leading-tight mb-6">
|
||||
<h1 className="text-5xl md:text-7xl font-semibold text-oliver-black leading-tight mb-6">
|
||||
Mod Comms <br/>
|
||||
<span className="text-electric-violet">
|
||||
<span className="text-oliver-orange">
|
||||
Intelligent Review
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-lg md:text-xl text-teal-brand mb-10 leading-relaxed max-w-2xl">
|
||||
<p className="text-lg md:text-xl text-oliver-black mb-10 leading-relaxed max-w-2xl">
|
||||
Streamline your creative approval process. Mod Comms analyses your proofs against guidelines and best practice in seconds, not days.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<button
|
||||
onClick={onGetStarted}
|
||||
className="group inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white bg-active-blue rounded-full hover:bg-active-blue/90 transition-all duration-300"
|
||||
className="group inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white bg-oliver-azure rounded-full hover:bg-oliver-azure/90 transition-all duration-300"
|
||||
>
|
||||
Start Analysis
|
||||
<ArrowRightIcon className="ml-2 h-5 w-5 transform group-hover:translate-x-1 transition-transform" />
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const StatusBadge: React.FC<{ status: string }> = ({ status }) => {
|
|||
partial: 'partial parse',
|
||||
};
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${colors[status] || 'bg-grey-100 text-grey-700'}`}>
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${colors[status] || 'bg-oliver-grey text-oliver-black/60'}`}>
|
||||
{labels[status] || status}
|
||||
</span>
|
||||
);
|
||||
|
|
@ -275,13 +275,13 @@ export const KnowledgeBase: React.FC = () => {
|
|||
if (diffResult && selectedKb) {
|
||||
return (
|
||||
<div className="p-8 max-w-6xl mx-auto w-full">
|
||||
<button onClick={handleBackFromDiff} className="flex items-center gap-2 text-active-blue hover:underline mb-6 text-sm font-medium">
|
||||
<button onClick={handleBackFromDiff} className="flex items-center gap-2 text-oliver-azure hover:underline mb-6 text-sm font-medium">
|
||||
<ArrowLeftIcon className="h-4 w-4" /> Back to Version History
|
||||
</button>
|
||||
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-primary-blue">{selectedKb.display_name} - Diff</h1>
|
||||
<p className="text-grey-700 mt-1">
|
||||
<h1 className="text-2xl font-bold text-oliver-black">{selectedKb.display_name} - Diff</h1>
|
||||
<p className="text-oliver-black/60 mt-1">
|
||||
Version {diffResult.version_a} vs Version {diffResult.version_b}
|
||||
<span className="ml-4 text-green-600 font-medium">+{diffResult.additions}</span>
|
||||
<span className="ml-2 text-red-600 font-medium">-{diffResult.deletions}</span>
|
||||
|
|
@ -299,10 +299,10 @@ export const KnowledgeBase: React.FC = () => {
|
|||
''
|
||||
}`}
|
||||
>
|
||||
<span className="w-12 text-right pr-2 text-grey-700 select-none border-r border-grey-200 flex-shrink-0 py-0.5">
|
||||
<span className="w-12 text-right pr-2 text-oliver-black/60 select-none border-r border-grey-200 flex-shrink-0 py-0.5">
|
||||
{line.line_number_old || ''}
|
||||
</span>
|
||||
<span className="w-12 text-right pr-2 text-grey-700 select-none border-r border-grey-200 flex-shrink-0 py-0.5">
|
||||
<span className="w-12 text-right pr-2 text-oliver-black/60 select-none border-r border-grey-200 flex-shrink-0 py-0.5">
|
||||
{line.line_number_new || ''}
|
||||
</span>
|
||||
<span className={`w-6 text-center flex-shrink-0 py-0.5 ${
|
||||
|
|
@ -325,15 +325,15 @@ export const KnowledgeBase: React.FC = () => {
|
|||
if (viewingSpec) {
|
||||
return (
|
||||
<div className="p-8 max-w-6xl mx-auto w-full">
|
||||
<button onClick={() => setViewingSpec(null)} className="flex items-center gap-2 text-active-blue hover:underline mb-6 text-sm font-medium">
|
||||
<button onClick={() => setViewingSpec(null)} className="flex items-center gap-2 text-oliver-azure hover:underline mb-6 text-sm font-medium">
|
||||
<ArrowLeftIcon className="h-4 w-4" /> Back to Version History
|
||||
</button>
|
||||
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-primary-blue">
|
||||
<h1 className="text-2xl font-bold text-oliver-black">
|
||||
{selectedKb?.display_name} - Version {viewingSpec.version_number}
|
||||
</h1>
|
||||
<p className="text-grey-700 mt-1">
|
||||
<p className="text-oliver-black/60 mt-1">
|
||||
{formatDate(viewingSpec.created_at)} | {viewingSpec.char_count.toLocaleString()} chars
|
||||
{viewingSpec.generated_by_name && <span> | Generated by {viewingSpec.generated_by_name}</span>}
|
||||
{viewingSpec.is_active && <span className="ml-2 inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>}
|
||||
|
|
@ -341,7 +341,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-grey-300 p-6 overflow-x-auto max-h-[70vh] overflow-y-auto">
|
||||
<pre className="whitespace-pre-wrap text-sm text-black-title font-mono leading-relaxed">{viewingSpec.content}</pre>
|
||||
<pre className="whitespace-pre-wrap text-sm text-oliver-black font-mono leading-relaxed">{viewingSpec.content}</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -353,25 +353,25 @@ export const KnowledgeBase: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="p-8 max-w-6xl mx-auto w-full">
|
||||
<button onClick={handleBack} className="flex items-center gap-2 text-active-blue hover:underline mb-6 text-sm font-medium">
|
||||
<button onClick={handleBack} className="flex items-center gap-2 text-oliver-azure hover:underline mb-6 text-sm font-medium">
|
||||
<ArrowLeftIcon className="h-4 w-4" /> Back to Knowledge Bases
|
||||
</button>
|
||||
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-primary-blue">{selectedKb.display_name}</h1>
|
||||
{selectedKb.description && <p className="text-grey-700 mt-1">{selectedKb.description}</p>}
|
||||
<h1 className="text-2xl font-bold text-oliver-black">{selectedKb.display_name}</h1>
|
||||
{selectedKb.description && <p className="text-oliver-black/60 mt-1">{selectedKb.description}</p>}
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 mb-6 bg-grey-100 p-1 rounded-xl w-fit">
|
||||
<div className="flex gap-1 mb-6 bg-oliver-grey p-1 rounded-xl w-fit">
|
||||
{(['documents', 'versions'] as const).map(tab => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`px-5 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
|
||||
activeTab === tab
|
||||
? 'bg-white text-primary-blue shadow-sm'
|
||||
: 'text-grey-700 hover:text-primary-blue'
|
||||
? 'bg-white text-oliver-black shadow-sm'
|
||||
: 'text-oliver-black/60 hover:text-oliver-black'
|
||||
}`}
|
||||
>
|
||||
{tab === 'documents' ? 'Source Documents' : 'Version History'}
|
||||
|
|
@ -387,12 +387,12 @@ export const KnowledgeBase: React.FC = () => {
|
|||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={`border-2 border-dashed rounded-xl p-8 mb-6 text-center transition-colors ${
|
||||
isDragging ? 'border-active-blue bg-active-blue/5' : 'border-grey-300 hover:border-active-blue/50'
|
||||
isDragging ? 'border-oliver-azure bg-oliver-azure/5' : 'border-grey-300 hover:border-oliver-azure/50'
|
||||
}`}
|
||||
>
|
||||
<UploadIcon className="h-10 w-10 mx-auto text-grey-700 mb-3" />
|
||||
<p className="text-grey-700 mb-2">Drag & drop files here, or</p>
|
||||
<label className="inline-flex items-center gap-2 bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors cursor-pointer">
|
||||
<UploadIcon className="h-10 w-10 mx-auto text-oliver-black/60 mb-3" />
|
||||
<p className="text-oliver-black/60 mb-2">Drag & drop files here, or</p>
|
||||
<label className="inline-flex items-center gap-2 bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors cursor-pointer">
|
||||
{uploading ? <SpinnerIcon className="h-4 w-4 animate-spin" /> : null}
|
||||
{uploading ? 'Uploading...' : 'Browse Files'}
|
||||
<input
|
||||
|
|
@ -404,7 +404,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
disabled={uploading}
|
||||
/>
|
||||
</label>
|
||||
<p className="text-xs text-grey-700 mt-2">PDF, DOCX, PPTX, XLSX, HTML, TXT, MD, PNG, JPG, WebP</p>
|
||||
<p className="text-xs text-oliver-black/60 mt-2">PDF, DOCX, PPTX, XLSX, HTML, TXT, MD, PNG, JPG, WebP</p>
|
||||
</div>
|
||||
|
||||
{/* Process button + job status */}
|
||||
|
|
@ -412,12 +412,12 @@ export const KnowledgeBase: React.FC = () => {
|
|||
<button
|
||||
onClick={handleProcess}
|
||||
disabled={!!isJobRunning || selectedKb.source_documents.length === 0}
|
||||
className="bg-active-blue text-white font-semibold py-2.5 px-8 rounded-full hover:bg-active-blue/90 transition-colors disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
className="bg-oliver-azure text-white font-semibold py-2.5 px-8 rounded-full hover:bg-oliver-azure/90 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isJobRunning ? 'Processing...' : 'Process Documents'}
|
||||
</button>
|
||||
{selectedKb.active_spec_version && (
|
||||
<span className="text-sm text-grey-700">
|
||||
<span className="text-sm text-oliver-black/60">
|
||||
Active spec: v{selectedKb.active_spec_version} ({selectedKb.active_spec_char_count?.toLocaleString()} chars)
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -427,8 +427,8 @@ export const KnowledgeBase: React.FC = () => {
|
|||
{isJobRunning && activeJob && (
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-4 mb-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<SpinnerIcon className="h-5 w-5 text-active-blue animate-spin" />
|
||||
<span className="font-medium text-primary-blue">
|
||||
<SpinnerIcon className="h-5 w-5 text-oliver-azure animate-spin" />
|
||||
<span className="font-medium text-oliver-black">
|
||||
{activeJob.status === 'parsing_documents' ? 'Parsing documents...' :
|
||||
activeJob.status === 'distilling' ? 'Distilling spec with AI...' :
|
||||
'Starting...'}
|
||||
|
|
@ -437,12 +437,12 @@ export const KnowledgeBase: React.FC = () => {
|
|||
{activeJob.status === 'parsing_documents' && (
|
||||
<div className="w-full bg-blue-100 rounded-full h-2">
|
||||
<div
|
||||
className="bg-active-blue h-2 rounded-full transition-all duration-500"
|
||||
className="bg-oliver-azure h-2 rounded-full transition-all duration-500"
|
||||
style={{ width: `${activeJob.total_documents > 0 ? (activeJob.parsed_documents / activeJob.total_documents) * 100 : 0}%` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-sm text-grey-700 mt-1">
|
||||
<p className="text-sm text-oliver-black/60 mt-1">
|
||||
{activeJob.parsed_documents} / {activeJob.total_documents} documents parsed
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -455,7 +455,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
? 'bg-green-50 border-green-200 text-green-800'
|
||||
: selectedKb.latest_job.status === 'failed'
|
||||
? 'bg-red-50 border-red-200 text-red-800'
|
||||
: 'bg-grey-100 border-grey-300 text-grey-700'
|
||||
: 'bg-oliver-grey border-grey-300 text-oliver-black/60'
|
||||
}`}>
|
||||
Last processing: <StatusBadge status={selectedKb.latest_job.status} />
|
||||
{selectedKb.latest_job.completed_at && <span className="ml-2">{formatDate(selectedKb.latest_job.completed_at)}</span>}
|
||||
|
|
@ -468,7 +468,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
<div className="bg-white rounded-xl border border-grey-300 overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-grey-100 text-left text-grey-700">
|
||||
<tr className="bg-oliver-grey text-left text-oliver-black/60">
|
||||
<th className="px-4 py-3 font-medium">Filename</th>
|
||||
<th className="px-4 py-3 font-medium">Size</th>
|
||||
<th className="px-4 py-3 font-medium">Uploaded</th>
|
||||
|
|
@ -481,15 +481,15 @@ export const KnowledgeBase: React.FC = () => {
|
|||
{selectedKb.source_documents.map(doc => (
|
||||
<React.Fragment key={doc.id}>
|
||||
<tr className="hover:bg-grey-50 transition-colors">
|
||||
<td className="px-4 py-3 font-medium text-primary-blue">{doc.filename}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{formatBytes(doc.file_size_bytes)}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{formatDate(doc.created_at)}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{doc.uploaded_by_name || '-'}</td>
|
||||
<td className="px-4 py-3 font-medium text-oliver-black">{doc.filename}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{formatBytes(doc.file_size_bytes)}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{formatDate(doc.created_at)}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{doc.uploaded_by_name || '-'}</td>
|
||||
<td className="px-4 py-3"><StatusBadge status={doc.parse_status} /></td>
|
||||
<td className="px-4 py-3">
|
||||
<button
|
||||
onClick={() => handleRemoveDoc(doc.id)}
|
||||
className="text-grey-700 hover:text-error transition-colors"
|
||||
className="text-oliver-black/60 hover:text-error transition-colors"
|
||||
title="Remove document"
|
||||
>
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
|
|
@ -511,7 +511,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12 text-grey-700 bg-grey-100 rounded-xl">
|
||||
<div className="text-center py-12 text-oliver-black/60 bg-oliver-grey rounded-xl">
|
||||
No source documents uploaded yet.
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -524,7 +524,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
<div className="mb-4">
|
||||
<button
|
||||
onClick={handleCompare}
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors"
|
||||
>
|
||||
Compare Selected Versions
|
||||
</button>
|
||||
|
|
@ -535,7 +535,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
<div className="bg-white rounded-xl border border-grey-300 overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-grey-100 text-left text-grey-700">
|
||||
<tr className="bg-oliver-grey text-left text-oliver-black/60">
|
||||
<th className="px-4 py-3 font-medium w-10">
|
||||
<span className="sr-only">Compare</span>
|
||||
</th>
|
||||
|
|
@ -555,27 +555,27 @@ export const KnowledgeBase: React.FC = () => {
|
|||
type="checkbox"
|
||||
checked={selectedForDiff.includes(v.id)}
|
||||
onChange={() => handleDiffToggle(v.id)}
|
||||
className="h-4 w-4 rounded border-grey-300 text-active-blue focus:ring-active-blue"
|
||||
className="h-4 w-4 rounded border-grey-300 text-oliver-azure focus:ring-oliver-azure"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4 py-3 font-medium text-primary-blue">v{v.version_number}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{formatDate(v.created_at)}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{v.generated_by_name || '-'}</td>
|
||||
<td className="px-4 py-3 text-grey-700">{v.char_count.toLocaleString()}</td>
|
||||
<td className="px-4 py-3 font-medium text-oliver-black">v{v.version_number}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{formatDate(v.created_at)}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{v.generated_by_name || '-'}</td>
|
||||
<td className="px-4 py-3 text-oliver-black/60">{v.char_count.toLocaleString()}</td>
|
||||
<td className="px-4 py-3">
|
||||
{v.is_active ? (
|
||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||||
Active
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-grey-700 text-xs">Inactive</span>
|
||||
<span className="text-oliver-black/60 text-xs">Inactive</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => handleViewSpec(v.id)}
|
||||
className="text-active-blue hover:underline text-xs font-medium"
|
||||
className="text-oliver-azure hover:underline text-xs font-medium"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
|
|
@ -595,7 +595,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-12 text-grey-700 bg-grey-100 rounded-xl">
|
||||
<div className="text-center py-12 text-oliver-black/60 bg-oliver-grey rounded-xl">
|
||||
No spec versions generated yet.
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -609,7 +609,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
if (loading) {
|
||||
return (
|
||||
<div className="p-8 flex items-center justify-center min-h-[400px]">
|
||||
<SpinnerIcon className="h-8 w-8 text-active-blue animate-spin" />
|
||||
<SpinnerIcon className="h-8 w-8 text-oliver-azure animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -617,8 +617,8 @@ export const KnowledgeBase: React.FC = () => {
|
|||
return (
|
||||
<div className="p-8 max-w-6xl mx-auto w-full">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-primary-blue">Knowledge Base</h1>
|
||||
<p className="text-grey-700 mt-1">Manage the AI agent knowledge bases. Upload source documents, process them, and version the resulting specs.</p>
|
||||
<h1 className="text-2xl font-bold text-oliver-black">Knowledge Base</h1>
|
||||
<p className="text-oliver-black/60 mt-1">Manage the AI agent knowledge bases. Upload source documents, process them, and version the resulting specs.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
|
|
@ -632,22 +632,22 @@ export const KnowledgeBase: React.FC = () => {
|
|||
<button
|
||||
key={kb.id}
|
||||
onClick={() => handleSelectKb(kb)}
|
||||
className={`text-left p-6 rounded-xl border-2 ${statusColor} hover:border-active-blue hover:shadow-md transition-all duration-200 group`}
|
||||
className={`text-left p-6 rounded-xl border-2 ${statusColor} hover:border-oliver-azure hover:shadow-md transition-all duration-200 group`}
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-primary-blue group-hover:text-active-blue transition-colors">
|
||||
<h3 className="text-lg font-semibold text-oliver-black group-hover:text-oliver-azure transition-colors">
|
||||
{kb.display_name}
|
||||
</h3>
|
||||
{kb.description && (
|
||||
<p className="text-sm text-grey-700 mt-1 line-clamp-2">{kb.description}</p>
|
||||
<p className="text-sm text-oliver-black/60 mt-1 line-clamp-2">{kb.description}</p>
|
||||
)}
|
||||
<div className="mt-4 space-y-1 text-sm text-grey-700">
|
||||
<div className="mt-4 space-y-1 text-sm text-oliver-black/60">
|
||||
<div className="flex justify-between">
|
||||
<span>Source Documents</span>
|
||||
<span className="font-medium text-primary-blue">{kb.source_document_count}</span>
|
||||
<span className="font-medium text-oliver-black">{kb.source_document_count}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Active Spec</span>
|
||||
<span className="font-medium text-primary-blue">
|
||||
<span className="font-medium text-oliver-black">
|
||||
{kb.active_spec_version ? `v${kb.active_spec_version}` : 'None'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -658,7 +658,7 @@ export const KnowledgeBase: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
{kb.latest_job_completed_at && (
|
||||
<div className="text-xs text-grey-700 text-right">
|
||||
<div className="text-xs text-oliver-black/60 text-right">
|
||||
{formatDate(kb.latest_job_completed_at)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ export const LoadingVisual: React.FC = () => {
|
|||
<div className="flex flex-col items-center justify-center text-center py-20 px-6 min-h-[600px]">
|
||||
<div className="relative flex items-center justify-center w-48 h-48">
|
||||
{/* Pulsing circles */}
|
||||
<div className="absolute w-full h-full rounded-full bg-active-blue/20 animate-slow-ping"></div>
|
||||
<div className="absolute w-3/4 h-3/4 rounded-full bg-active-blue/30 animate-slow-ping [animation-delay:-1s]"></div>
|
||||
<div className="absolute w-full h-full rounded-full bg-oliver-azure/20 animate-slow-ping"></div>
|
||||
<div className="absolute w-3/4 h-3/4 rounded-full bg-oliver-azure/30 animate-slow-ping [animation-delay:-1s]"></div>
|
||||
|
||||
{/* Central Icon */}
|
||||
<div className="relative bg-white backdrop-blur-sm rounded-full p-6 border border-gray-200 shadow-lg flex items-center justify-center w-28 h-28">
|
||||
<BarclaysLogo className="h-16 w-16 text-active-blue" />
|
||||
<BarclaysLogo className="h-16 w-16 text-oliver-azure" />
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-primary-blue mt-12">
|
||||
<h2 className="text-2xl font-bold text-oliver-black mt-12">
|
||||
Finalizing your report...
|
||||
</h2>
|
||||
<p className="text-gray-500 mt-2 max-w-md">
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const SupportModal: React.FC<{
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<h3 className="text-xl font-bold text-primary-blue">Contact Support</h3>
|
||||
<h3 className="text-xl font-bold text-oliver-black">Contact Support</h3>
|
||||
<button onClick={onClose} className="-mt-2 -mr-2 p-2 rounded-full hover:bg-gray-200 transition-colors">
|
||||
<XIcon className="h-6 w-6 text-gray-600" />
|
||||
</button>
|
||||
|
|
@ -78,7 +78,7 @@ const SupportModal: React.FC<{
|
|||
<textarea
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-active-blue focus:border-active-blue transition"
|
||||
className="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition"
|
||||
rows={5}
|
||||
placeholder="Type your message here..."
|
||||
required
|
||||
|
|
@ -95,7 +95,7 @@ const SupportModal: React.FC<{
|
|||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-4 rounded-md hover:bg-primary-blue transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-4 rounded-md hover:bg-oliver-azure transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
disabled={!query.trim() || isSubmitting}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
|
|
@ -163,10 +163,10 @@ export const Login: React.FC = () => {
|
|||
{/* Modern Glassy Background */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
{/* Top Left Blob */}
|
||||
<div className="absolute -top-[20%] -left-[10%] w-[70%] h-[70%] rounded-full bg-gradient-to-br from-active-blue/30 to-purple-600/30 blur-[120px] animate-pulse" style={{ animationDuration: '8s' }}></div>
|
||||
<div className="absolute -top-[20%] -left-[10%] w-[70%] h-[70%] rounded-full bg-gradient-to-br from-oliver-azure/30 to-purple-600/30 blur-[120px] animate-pulse" style={{ animationDuration: '8s' }}></div>
|
||||
|
||||
{/* Bottom Right Blob */}
|
||||
<div className="absolute -bottom-[20%] -right-[10%] w-[70%] h-[70%] rounded-full bg-gradient-to-tl from-cyan-brand/30 to-emerald-500/30 blur-[120px] animate-pulse" style={{ animationDuration: '10s', animationDelay: '1s' }}></div>
|
||||
<div className="absolute -bottom-[20%] -right-[10%] w-[70%] h-[70%] rounded-full bg-gradient-to-tl from-oliver-azure/30 to-emerald-500/30 blur-[120px] animate-pulse" style={{ animationDuration: '10s', animationDelay: '1s' }}></div>
|
||||
|
||||
{/* Noise Texture */}
|
||||
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-20 mix-blend-soft-light"></div>
|
||||
|
|
@ -175,10 +175,10 @@ export const Login: React.FC = () => {
|
|||
{/* Login Card */}
|
||||
<div className="relative z-10 bg-white/90 backdrop-blur-xl p-8 sm:p-12 shadow-2xl rounded-3xl w-full max-w-md flex flex-col items-center text-center border border-white/50 ring-1 ring-white/50">
|
||||
<div className="mb-8 transform transition-transform hover:scale-105 duration-300">
|
||||
<BarclaysLogo className="h-12 w-auto text-primary-blue" />
|
||||
<BarclaysLogo className="h-12 w-auto text-oliver-black" />
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-extrabold text-primary-blue mb-2 tracking-tight">Mod Comms</h1>
|
||||
<h1 className="text-3xl font-extrabold text-oliver-black mb-2 tracking-tight">Mod Comms</h1>
|
||||
<p className="text-slate-500 mb-8 font-medium">Proof Review & Compliance Platform</p>
|
||||
|
||||
<div className="w-full space-y-6">
|
||||
|
|
@ -204,7 +204,7 @@ export const Login: React.FC = () => {
|
|||
className="w-full flex items-center justify-center gap-3 bg-white hover:bg-gray-50 text-slate-700 font-bold py-4 px-6 rounded-xl border border-gray-200 shadow-lg shadow-gray-200/50 hover:shadow-xl hover:border-gray-300 transition-all duration-300 group disabled:opacity-70 disabled:cursor-wait transform hover:-translate-y-0.5"
|
||||
>
|
||||
{isLoggingIn ? (
|
||||
<svg className="animate-spin h-5 w-5 text-active-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<svg className="animate-spin h-5 w-5 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
|
|
@ -221,7 +221,7 @@ export const Login: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsSupportModalOpen(true)}
|
||||
className="text-sm text-slate-500 hover:text-active-blue transition-colors font-medium"
|
||||
className="text-sm text-slate-500 hover:text-oliver-azure transition-colors font-medium"
|
||||
>
|
||||
Having trouble signing in? Contact Support
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -92,18 +92,18 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, msalInstance }) => {
|
|||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-white">
|
||||
<header className="mb-8">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">Your Profile</h1>
|
||||
<p className="text-base lg:text-lg text-grey-700 mt-1">View your account details and manage settings.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Your Profile</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black/60 mt-1">View your account details and manage settings.</p>
|
||||
</header>
|
||||
|
||||
<div className="max-w-3xl">
|
||||
<section className="p-6 sm:p-8">
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-6">Account Information</h2>
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-6">Account Information</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-5">
|
||||
{Object.entries(userDetails).map(([key, value]) => (
|
||||
<div key={key}>
|
||||
<p className="text-sm font-semibold text-grey-700 tracking-wide uppercase">{key}</p>
|
||||
<p className="text-lg text-black-title mt-1">{value}</p>
|
||||
<p className="text-sm font-semibold text-oliver-black/60 tracking-wide uppercase">{key}</p>
|
||||
<p className="text-lg text-oliver-black mt-1">{value}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -118,7 +118,7 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, msalInstance }) => {
|
|||
</button>
|
||||
<button
|
||||
onClick={handleToggleQuestionForm}
|
||||
className="flex items-center justify-center gap-2 text-active-blue hover:text-active-blue/80 bg-transparent font-semibold py-2 px-4 rounded-md transition-colors duration-300"
|
||||
className="flex items-center justify-center gap-2 text-oliver-azure hover:text-oliver-azure/80 bg-transparent font-semibold py-2 px-4 rounded-md transition-colors duration-300"
|
||||
>
|
||||
<QuestionMarkIcon className="h-5 w-5" />
|
||||
Got a question?
|
||||
|
|
@ -128,9 +128,9 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, msalInstance }) => {
|
|||
|
||||
{isQuestionFormVisible && (
|
||||
<section className="mt-8 p-6 sm:p-8">
|
||||
<h2 className="text-2xl font-semibold text-primary-blue mb-4">Ask a Question</h2>
|
||||
<h2 className="text-2xl font-semibold text-oliver-black mb-4">Ask a Question</h2>
|
||||
<form onSubmit={handleSubmitQuestion}>
|
||||
<p className="text-grey-700 mb-4">Your question will be sent to the OLIVER Agency support team.</p>
|
||||
<p className="text-oliver-black/60 mb-4">Your question will be sent to the OLIVER Agency support team.</p>
|
||||
{submitStatus && (
|
||||
<div className={`mb-4 p-3 rounded-md ${submitStatus.type === 'success' ? 'bg-green-50 text-green-700 border border-green-200' : 'bg-red-50 text-red-700 border border-red-200'}`}>
|
||||
{submitStatus.message}
|
||||
|
|
@ -139,7 +139,7 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, msalInstance }) => {
|
|||
<textarea
|
||||
value={question}
|
||||
onChange={(e) => setQuestion(e.target.value)}
|
||||
className="w-full p-3 border border-grey-300 rounded-md focus:ring-2 focus:ring-active-blue focus:border-active-blue transition text-black-title"
|
||||
className="w-full p-3 border border-grey-300 rounded-md focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition text-oliver-black"
|
||||
placeholder="Type your question here..."
|
||||
rows={5}
|
||||
required
|
||||
|
|
@ -148,7 +148,7 @@ export const Profile: React.FC<ProfileProps> = ({ onLogout, msalInstance }) => {
|
|||
<div className="mt-4 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-5 rounded-md hover:bg-primary-blue transition-colors duration-300 disabled:bg-grey-300 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-5 rounded-md hover:bg-oliver-azure transition-colors duration-300 disabled:bg-gray-300 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
disabled={!question.trim() || isSubmitting}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
|
|
|
|||
|
|
@ -157,16 +157,16 @@ const StatusBadge: React.FC<{ status: string }> = ({ status }) => {
|
|||
let colorClasses = '';
|
||||
switch (status) {
|
||||
case 'In Progress':
|
||||
colorClasses = 'bg-warning text-black-title';
|
||||
colorClasses = 'bg-warning text-oliver-black';
|
||||
break;
|
||||
case 'Completed':
|
||||
colorClasses = 'bg-success text-white';
|
||||
break;
|
||||
case 'Needs Review':
|
||||
colorClasses = 'bg-warning-light text-black-title';
|
||||
colorClasses = 'bg-warning-light text-oliver-black';
|
||||
break;
|
||||
default:
|
||||
colorClasses = 'bg-grey-100 text-black-title';
|
||||
colorClasses = 'bg-oliver-grey text-oliver-black';
|
||||
}
|
||||
return (
|
||||
<span className={`px-2.5 py-0.5 text-xs font-semibold rounded-full ${colorClasses}`}>
|
||||
|
|
@ -185,7 +185,7 @@ const OverallStatusBadge: React.FC<{ status: 'Passed' | 'Failed' | 'Analysis Err
|
|||
colorClasses = 'bg-error text-white';
|
||||
break;
|
||||
case 'Analysis Error':
|
||||
colorClasses = 'bg-grey-300 text-black-title';
|
||||
colorClasses = 'bg-grey-300 text-oliver-black';
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -209,37 +209,37 @@ const ProjectList: React.FC<{
|
|||
const filteredProjects = showCompleted ? projects : projects.filter(p => p.status !== 'Completed');
|
||||
|
||||
const getStatusSelectClasses = (status: string) => {
|
||||
let baseClasses = 'w-full text-center px-2.5 py-0.5 text-xs font-semibold rounded-full border border-transparent focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none cursor-pointer';
|
||||
let baseClasses = 'w-full text-center px-2.5 py-0.5 text-xs font-semibold rounded-full border border-transparent focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none cursor-pointer';
|
||||
let colorClasses = '';
|
||||
switch (status) {
|
||||
case 'In Progress':
|
||||
colorClasses = 'bg-warning text-black-title';
|
||||
colorClasses = 'bg-warning text-oliver-black';
|
||||
break;
|
||||
case 'Completed':
|
||||
colorClasses = 'bg-success text-white';
|
||||
break;
|
||||
default:
|
||||
colorClasses = 'bg-grey-100 text-black-title';
|
||||
colorClasses = 'bg-oliver-grey text-oliver-black';
|
||||
}
|
||||
return `${baseClasses} ${colorClasses}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-grey-100">
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-oliver-grey">
|
||||
<header className="mb-8">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-primary-blue">Projects</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Manage your campaigns and proof collections.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-oliver-black">Projects</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Manage your campaigns and proof collections.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label htmlFor="show-completed" className="text-sm font-medium text-black-title whitespace-nowrap">Show Completed</label>
|
||||
<label htmlFor="show-completed" className="text-sm font-medium text-oliver-black whitespace-nowrap">Show Completed</label>
|
||||
<ToggleSwitch enabled={showCompleted} onChange={setShowCompleted} />
|
||||
</div>
|
||||
<button
|
||||
onClick={onOpenModal}
|
||||
className="flex items-center gap-2 bg-active-blue text-white font-semibold py-2 px-4 rounded-full hover:bg-primary-blue transition-colors duration-300"
|
||||
className="flex items-center gap-2 bg-oliver-azure text-white font-semibold py-2 px-4 rounded-full hover:bg-oliver-azure transition-colors duration-300"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
Create New Project
|
||||
|
|
@ -252,22 +252,22 @@ const ProjectList: React.FC<{
|
|||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-grey-300">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Project Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proofs</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Status</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Created By</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Owning Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Last Modified</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Project Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proofs</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Status</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Created By</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Owning Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Last Modified</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-grey-300">
|
||||
{filteredProjects.map((project) => {
|
||||
return (
|
||||
<tr key={project.name} className="hover:bg-info-light cursor-pointer even:bg-grey-100" onClick={() => onSelectProject(project.name)}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{project.name}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{project.assets}</td>
|
||||
<tr key={project.name} className="hover:bg-oliver-grey cursor-pointer even:bg-oliver-grey" onClick={() => onSelectProject(project.name)}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{project.name}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{project.assets}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm w-40">
|
||||
<div className="relative">
|
||||
<select
|
||||
|
|
@ -280,12 +280,12 @@ const ProjectList: React.FC<{
|
|||
<option value="In Progress">In Progress</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
<ChevronDownIcon className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 h-full w-4 text-black-title" />
|
||||
<ChevronDownIcon className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 h-full w-4 text-oliver-black" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{project.agencyLead}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{project.agency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{project.lastModified}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{project.agencyLead}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{project.agency}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{project.lastModified}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
|
|
@ -345,11 +345,11 @@ const ProjectAssetUpload: React.FC<{
|
|||
|
||||
return (
|
||||
<section className="bg-white rounded-[10px] shadow-md p-6 border border-grey-300 mb-8">
|
||||
<h2 className="text-xl font-bold text-primary-blue mb-4">Upload New Proof</h2>
|
||||
<h2 className="text-xl font-bold text-oliver-black mb-4">Upload New Proof</h2>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="asset-name" className="flex items-center text-sm font-medium text-black-title mb-1">
|
||||
<DocumentIcon className="h-5 w-5 mr-2 text-active-blue shrink-0" />
|
||||
<label htmlFor="asset-name" className="flex items-center text-sm font-medium text-oliver-black mb-1">
|
||||
<DocumentIcon className="h-5 w-5 mr-2 text-oliver-azure shrink-0" />
|
||||
Proof Name
|
||||
</label>
|
||||
<input
|
||||
|
|
@ -359,11 +359,11 @@ const ProjectAssetUpload: React.FC<{
|
|||
onChange={(e) => setAssetName(e.target.value)}
|
||||
placeholder="e.g., Q4 Hero Instagram Post"
|
||||
disabled={isLoading}
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 px-3 text-black-title focus:outline-none focus:ring-2 focus:ring-active-blue disabled:bg-grey-100"
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 px-3 text-oliver-black focus:outline-none focus:ring-2 focus:ring-oliver-azure disabled:bg-oliver-grey"
|
||||
required
|
||||
/>
|
||||
{isNewVersion && (
|
||||
<p className="text-xs text-active-blue mt-2 p-2 bg-info-light rounded-[10px]">
|
||||
<p className="text-xs text-oliver-azure mt-2 p-2 bg-oliver-grey rounded-[10px]">
|
||||
A proof with this name already exists. This will be uploaded as a new version.
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -371,8 +371,8 @@ const ProjectAssetUpload: React.FC<{
|
|||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Channel Dropdown */}
|
||||
<div>
|
||||
<label htmlFor="asset-channel" className="flex items-center text-sm font-medium text-black-title mb-1">
|
||||
<ChannelIcon className="h-5 w-5 mr-2 text-active-blue shrink-0" />
|
||||
<label htmlFor="asset-channel" className="flex items-center text-sm font-medium text-oliver-black mb-1">
|
||||
<ChannelIcon className="h-5 w-5 mr-2 text-oliver-azure shrink-0" />
|
||||
Channel
|
||||
</label>
|
||||
<div className="relative">
|
||||
|
|
@ -381,21 +381,21 @@ const ProjectAssetUpload: React.FC<{
|
|||
value={channel}
|
||||
onChange={(e) => setChannel(e.target.value)}
|
||||
disabled={isLoading}
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 pl-3 pr-10 text-black-title focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none disabled:bg-grey-100"
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 pl-3 pr-10 text-oliver-black focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none disabled:bg-oliver-grey"
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select Channel</option>
|
||||
{availableChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-oliver-black/60">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Sub-Channel Dropdown */}
|
||||
<div>
|
||||
<label htmlFor="asset-sub-channel" className="flex items-center text-sm font-medium text-black-title mb-1">
|
||||
<TagIcon className="h-5 w-5 mr-2 text-active-blue shrink-0" />
|
||||
<label htmlFor="asset-sub-channel" className="flex items-center text-sm font-medium text-oliver-black mb-1">
|
||||
<TagIcon className="h-5 w-5 mr-2 text-oliver-azure shrink-0" />
|
||||
Sub-Channel
|
||||
</label>
|
||||
<div className="relative">
|
||||
|
|
@ -404,13 +404,13 @@ const ProjectAssetUpload: React.FC<{
|
|||
value={subChannel}
|
||||
onChange={(e) => setSubChannel(e.target.value)}
|
||||
disabled={isLoading || !channel}
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 pl-3 pr-10 text-black-title focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none disabled:bg-grey-100 disabled:text-grey-700"
|
||||
className="w-full bg-white border border-grey-300 rounded-[10px] py-2 pl-3 pr-10 text-oliver-black focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none disabled:bg-oliver-grey disabled:text-oliver-black/60"
|
||||
required
|
||||
>
|
||||
<option value="" disabled>Select Sub-Channel</option>
|
||||
{availableSubChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
</select>
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-oliver-black/60">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -418,27 +418,27 @@ const ProjectAssetUpload: React.FC<{
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-black-title mb-1">Proof File</label>
|
||||
<label className="block text-sm font-medium text-oliver-black mb-1">Proof File</label>
|
||||
<div className="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-grey-300 border-dashed rounded-[10px]">
|
||||
<div className="space-y-1 text-center">
|
||||
<UploadIcon className="mx-auto h-12 w-12 text-grey-700" />
|
||||
<div className="flex text-sm text-black-title">
|
||||
<label htmlFor="file-upload" className="relative cursor-pointer bg-white rounded-[10px] font-medium text-active-blue hover:text-primary-blue focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-active-blue">
|
||||
<UploadIcon className="mx-auto h-12 w-12 text-oliver-black/60" />
|
||||
<div className="flex text-sm text-oliver-black">
|
||||
<label htmlFor="file-upload" className="relative cursor-pointer bg-white rounded-[10px] font-medium text-oliver-azure hover:text-oliver-black focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-oliver-azure">
|
||||
<span>Upload a file</span>
|
||||
<input id="file-upload" name="file-upload" type="file" className="sr-only" ref={fileInputRef} onChange={handleFileChange} disabled={isLoading} accept="image/png, image/jpeg, image/webp, image/gif, video/mp4, application/pdf" />
|
||||
</label>
|
||||
<p className="pl-1">or drag and drop</p>
|
||||
</div>
|
||||
{file ? <p className="text-sm font-semibold text-primary-blue mt-2">{file.name}</p> : <p className="text-xs text-grey-900">PNG, JPG, GIF, WEBP, MP4, PDF</p>}
|
||||
{file ? <p className="text-sm font-semibold text-oliver-black mt-2">{file.name}</p> : <p className="text-xs text-oliver-black">PNG, JPG, GIF, WEBP, MP4, PDF</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end pt-2 gap-3">
|
||||
<button type="button" onClick={onCancel} disabled={isLoading} className="bg-grey-300 text-black-title font-semibold py-2.5 px-5 rounded-full hover:bg-grey-700 hover:text-white transition-colors duration-300 disabled:opacity-50">
|
||||
<button type="button" onClick={onCancel} disabled={isLoading} className="bg-grey-300 text-oliver-black font-semibold py-2.5 px-5 rounded-full hover:bg-gray-500 hover:text-white transition-colors duration-300 disabled:opacity-50">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" disabled={isSubmitDisabled} className="flex items-center justify-center bg-active-blue text-white font-bold py-2.5 px-5 rounded-full hover:bg-primary-blue transition-all duration-300 disabled:bg-grey-700 disabled:cursor-not-allowed">
|
||||
<button type="submit" disabled={isSubmitDisabled} className="flex items-center justify-center bg-oliver-azure text-white font-bold py-2.5 px-5 rounded-full hover:bg-oliver-azure transition-all duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed">
|
||||
{isLoading ? (
|
||||
<>
|
||||
<svg className="animate-spin -ml-1 mr-3 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
|
|
@ -472,11 +472,11 @@ const LoadingCell: React.FC<{ progress: { completed: number; total: number } }>
|
|||
<div className="flex flex-col items-center justify-center h-full px-2 text-center w-full">
|
||||
<div className="w-full bg-grey-300 rounded-full h-1.5 mb-1.5">
|
||||
<div
|
||||
className="bg-active-blue h-1.5 rounded-full"
|
||||
className="bg-oliver-azure h-1.5 rounded-full"
|
||||
style={{ width: `${percent}%`, transition: 'width 0.3s ease-in-out' }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-xs text-black-title truncate w-full">{statusText} ({percent}%)</span>
|
||||
<span className="text-xs text-oliver-black truncate w-full">{statusText} ({percent}%)</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -514,8 +514,8 @@ const AnalysisErrorModal: React.FC<{
|
|||
<ExclamationTriangleIcon className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary-blue">Analysis Error</h3>
|
||||
<p className="text-sm text-grey-700 mt-0.5">{assetName}</p>
|
||||
<h3 className="text-xl font-bold text-oliver-black">Analysis Error</h3>
|
||||
<p className="text-sm text-oliver-black/60 mt-0.5">{assetName}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -528,11 +528,11 @@ const AnalysisErrorModal: React.FC<{
|
|||
|
||||
{failedAgents.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold text-grey-700">Agent Details</h4>
|
||||
<h4 className="text-sm font-semibold text-oliver-black/60">Agent Details</h4>
|
||||
{failedAgents.map(agent => (
|
||||
<div key={agent.label} className="bg-grey-100 rounded-lg p-3">
|
||||
<p className="text-sm font-medium text-primary-blue">{agent.label}</p>
|
||||
<p className="text-sm text-grey-700 mt-1">{agent.review.feedback}</p>
|
||||
<div key={agent.label} className="bg-oliver-grey rounded-lg p-3">
|
||||
<p className="text-sm font-medium text-oliver-black">{agent.label}</p>
|
||||
<p className="text-sm text-oliver-black/60 mt-1">{agent.review.feedback}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -542,7 +542,7 @@ const AnalysisErrorModal: React.FC<{
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="border-2 border-active-blue text-active-blue font-semibold py-2 px-6 rounded-full hover:bg-active-blue hover:text-white transition-colors"
|
||||
className="border-2 border-oliver-azure text-oliver-azure font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure hover:text-white transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
|
@ -571,15 +571,15 @@ const DeleteConfirmationModal: React.FC<{
|
|||
className="bg-white rounded-[10px] shadow-xl p-6 sm:p-8 w-full max-w-md transform transition-all"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-xl font-bold text-primary-blue">Confirm Deletion</h3>
|
||||
<p className="text-black-title my-4">
|
||||
<h3 className="text-xl font-bold text-oliver-black">Confirm Deletion</h3>
|
||||
<p className="text-oliver-black my-4">
|
||||
Are you sure you want to permanently delete the proof "{assetName}"? This action cannot be undone.
|
||||
</p>
|
||||
<div className="mt-6 flex justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="bg-grey-300 text-black-title font-semibold py-2 px-4 rounded-full hover:bg-grey-700 hover:text-white transition-colors"
|
||||
className="bg-grey-300 text-oliver-black font-semibold py-2 px-4 rounded-full hover:bg-gray-500 hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -649,7 +649,7 @@ const ProjectDetail: React.FC<{
|
|||
|
||||
|
||||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-grey-100">
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-oliver-grey">
|
||||
<DeleteConfirmationModal
|
||||
isOpen={!!assetToDelete}
|
||||
onClose={() => setAssetToDelete(null)}
|
||||
|
|
@ -677,15 +677,15 @@ const ProjectDetail: React.FC<{
|
|||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-grey-300 hover:text-primary-blue transition-colors duration-200"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-grey-300 hover:text-oliver-black transition-colors duration-200"
|
||||
title="Back to projects"
|
||||
aria-label="Back to projects list"
|
||||
>
|
||||
<ArrowLeftIcon className="h-6 w-6" />
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-primary-blue">{projectName}</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Proof overview and compliance status.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-oliver-black">{projectName}</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Proof overview and compliance status.</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -708,14 +708,14 @@ const ProjectDetail: React.FC<{
|
|||
<div className="mb-6 flex justify-end gap-3">
|
||||
<button
|
||||
onClick={() => console.log('Download all proofs clicked')}
|
||||
className="flex items-center gap-2 bg-white text-active-blue font-semibold py-2 px-4 rounded-full border-2 border-active-blue hover:bg-active-blue/10 transition-colors duration-300"
|
||||
className="flex items-center gap-2 bg-white text-oliver-azure font-semibold py-2 px-4 rounded-full border-2 border-oliver-azure hover:bg-oliver-azure/10 transition-colors duration-300"
|
||||
>
|
||||
<DownloadIcon className="h-5 w-5" />
|
||||
Download All Proofs
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsUploadFormVisible(true)}
|
||||
className="flex items-center gap-2 bg-active-blue text-white font-semibold py-2 px-4 rounded-full hover:bg-primary-blue transition-colors duration-300"
|
||||
className="flex items-center gap-2 bg-oliver-azure text-white font-semibold py-2 px-4 rounded-full hover:bg-oliver-azure transition-colors duration-300"
|
||||
>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
Upload New Proof
|
||||
|
|
@ -725,13 +725,13 @@ const ProjectDetail: React.FC<{
|
|||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-grey-300">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Workfront #</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Sub-Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Overall Status</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Workfront #</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Sub-Channel</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Overall Status</th>
|
||||
<th scope="col" className="relative px-6 py-3"><span className="sr-only">Actions</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -739,17 +739,17 @@ const ProjectDetail: React.FC<{
|
|||
{assets.map((asset, index) => {
|
||||
if (asset.status === 'analyzing') {
|
||||
return (
|
||||
<tr key={asset.tempId} className="bg-grey-100 opacity-80">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{asset.assetName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700 italic">Pending</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.subChannel}</td>
|
||||
<tr key={asset.tempId} className="bg-oliver-grey opacity-80">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{asset.assetName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black/60 italic">Pending</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.subChannel}</td>
|
||||
<td className="px-6 py-4" colSpan={2}>
|
||||
{asset.analysisProgress ?
|
||||
<LoadingCell progress={asset.analysisProgress} /> :
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<SpinnerIcon className="h-5 w-5 text-active-blue custom-spinner" />
|
||||
<span className="ml-2 text-sm text-black-title">Preparing...</span>
|
||||
<SpinnerIcon className="h-5 w-5 text-oliver-azure custom-spinner" />
|
||||
<span className="ml-2 text-sm text-oliver-black">Preparing...</span>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
|
|
@ -760,10 +760,10 @@ const ProjectDetail: React.FC<{
|
|||
if (asset.status === 'error') {
|
||||
return (
|
||||
<tr key={asset.tempId} className="bg-error-light">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">{asset.assetName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700 italic">Failed</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">{asset.assetName}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black/60 italic">Failed</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-error font-semibold" colSpan={2}>
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
|
|
@ -774,7 +774,7 @@ const ProjectDetail: React.FC<{
|
|||
</button>
|
||||
<button
|
||||
onClick={() => onRetryAnalysis(projectName, asset.tempId || asset._id)}
|
||||
className="flex items-center gap-1.5 text-xs font-semibold text-active-blue hover:text-primary-blue whitespace-nowrap px-3 py-1.5 rounded-full bg-active-blue/10 hover:bg-active-blue/20 transition-colors"
|
||||
className="flex items-center gap-1.5 text-xs font-semibold text-oliver-azure hover:text-oliver-black whitespace-nowrap px-3 py-1.5 rounded-full bg-oliver-azure/10 hover:bg-oliver-azure/20 transition-colors"
|
||||
>
|
||||
<ArrowPathIcon className="h-4 w-4" />
|
||||
Retry
|
||||
|
|
@ -794,20 +794,20 @@ const ProjectDetail: React.FC<{
|
|||
return (
|
||||
<tr
|
||||
key={latestVersion.workfrontId || index}
|
||||
className={isClickable ? "hover:bg-info-light cursor-pointer even:bg-grey-100" : "even:bg-grey-100"}
|
||||
className={isClickable ? "hover:bg-oliver-grey cursor-pointer even:bg-oliver-grey" : "even:bg-oliver-grey"}
|
||||
onClick={() => isClickable && onSelectAsset(asset)}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-primary-blue">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-oliver-black">
|
||||
{asset.assetName}
|
||||
{isVersioned && (
|
||||
<span className="ml-2 bg-grey-300 text-black-title text-xs font-bold px-2 py-0.5 rounded-full">
|
||||
<span className="ml-2 bg-grey-300 text-oliver-black text-xs font-bold px-2 py-0.5 rounded-full">
|
||||
V{latestVersion.version}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{latestVersion.workfrontId}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{asset.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{latestVersion.workfrontId}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.channel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{asset.subChannel}</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<OverallStatusBadge status={latestVersion.overallStatus} />
|
||||
</td>
|
||||
|
|
@ -815,7 +815,7 @@ const ProjectDetail: React.FC<{
|
|||
<div className="flex items-center justify-end gap-1">
|
||||
<button
|
||||
onClick={(e) => handleNewVersionClick(e, asset)}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-info-light hover:text-active-blue transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-oliver-grey hover:text-oliver-azure transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title={`Upload new version of ${asset.assetName}`}
|
||||
disabled={isUploading}
|
||||
>
|
||||
|
|
@ -823,7 +823,7 @@ const ProjectDetail: React.FC<{
|
|||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); console.log('Download proof clicked'); }}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-success-light hover:text-success transition-colors"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-success-light hover:text-success transition-colors"
|
||||
title={`Download ${asset.assetName}`}
|
||||
>
|
||||
<DownloadIcon className="h-5 w-5" />
|
||||
|
|
@ -833,7 +833,7 @@ const ProjectDetail: React.FC<{
|
|||
e.stopPropagation();
|
||||
setAssetToDelete(asset);
|
||||
}}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-error-light hover:text-error transition-colors"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-error-light hover:text-error transition-colors"
|
||||
title={`Delete ${asset.assetName}`}
|
||||
>
|
||||
<TrashIcon className="h-5 w-5" />
|
||||
|
|
@ -923,20 +923,20 @@ const AssetDetailView: React.FC<{
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-grey-100">
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-oliver-grey">
|
||||
<header className="mb-8">
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-2 text-grey-700 rounded-full hover:bg-grey-300 hover:text-primary-blue transition-colors duration-200"
|
||||
className="p-2 text-oliver-black/60 rounded-full hover:bg-grey-300 hover:text-oliver-black transition-colors duration-200"
|
||||
title="Back to project details"
|
||||
aria-label="Back to project details"
|
||||
>
|
||||
<ArrowLeftIcon className="h-6 w-6" />
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-primary-blue">{asset.assetName}</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">Detailed AI feedback report.</p>
|
||||
<h1 className="text-3xl lg:text-4xl font-bold text-oliver-black">{asset.assetName}</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">Detailed AI feedback report.</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -945,7 +945,7 @@ const AssetDetailView: React.FC<{
|
|||
<div className="lg:col-span-1">
|
||||
<div className="sticky top-8 flex flex-col gap-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-primary-blue mb-4">
|
||||
<h2 className="text-2xl font-bold text-oliver-black mb-4">
|
||||
Proof Preview
|
||||
</h2>
|
||||
<AssetPreview
|
||||
|
|
@ -956,14 +956,14 @@ const AssetDetailView: React.FC<{
|
|||
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3 gap-2">
|
||||
<h3 className="text-xl font-bold text-primary-blue flex items-center gap-2">
|
||||
<HistoryIcon className="h-6 w-6 text-active-blue"/>
|
||||
<h3 className="text-xl font-bold text-oliver-black flex items-center gap-2">
|
||||
<HistoryIcon className="h-6 w-6 text-oliver-azure"/>
|
||||
Version History
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => console.log('Download version clicked')}
|
||||
className="flex items-center gap-2 text-sm bg-active-blue text-white font-semibold py-1.5 px-3 rounded-full border border-transparent hover:bg-primary-blue transition-colors duration-200"
|
||||
className="flex items-center gap-2 text-sm bg-oliver-azure text-white font-semibold py-1.5 px-3 rounded-full border border-transparent hover:bg-oliver-azure transition-colors duration-200"
|
||||
title={`Download Version ${selectedVersion.version}`}
|
||||
>
|
||||
<DownloadIcon className="h-4 w-4" />
|
||||
|
|
@ -972,7 +972,7 @@ const AssetDetailView: React.FC<{
|
|||
<button
|
||||
onClick={handleUploadClick}
|
||||
disabled={isUploadingNewVersion}
|
||||
className="flex items-center gap-2 text-sm bg-white text-active-blue font-semibold py-1.5 px-3 rounded-full border-2 border-active-blue hover:bg-active-blue/10 transition-colors duration-200 disabled:bg-grey-300 disabled:text-grey-700 disabled:cursor-wait"
|
||||
className="flex items-center gap-2 text-sm bg-white text-oliver-azure font-semibold py-1.5 px-3 rounded-full border-2 border-oliver-azure hover:bg-oliver-azure/10 transition-colors duration-200 disabled:bg-gray-300 disabled:text-oliver-black/60 disabled:cursor-wait"
|
||||
title="Upload a new version of this proof"
|
||||
>
|
||||
{isUploadingNewVersion ? (
|
||||
|
|
@ -1006,15 +1006,15 @@ const AssetDetailView: React.FC<{
|
|||
onClick={() => setSelectedVersionIndex(index)}
|
||||
className={`w-full text-left p-3 rounded-[10px] border-2 transition-all ${
|
||||
isActive
|
||||
? 'bg-info-light border-active-blue shadow-sm'
|
||||
: 'bg-white border-grey-300 hover:border-active-blue/50 hover:bg-grey-100'
|
||||
? 'bg-oliver-grey border-oliver-azure shadow-sm'
|
||||
: 'bg-white border-grey-300 hover:border-oliver-azure/50 hover:bg-oliver-grey'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className={`font-bold ${isActive ? 'text-primary-blue' : 'text-black-title'}`}>Version {version.version}</p>
|
||||
<p className="text-xs text-grey-700">{version.timestamp}</p>
|
||||
<p className={`font-bold ${isActive ? 'text-oliver-black' : 'text-oliver-black'}`}>Version {version.version}</p>
|
||||
<p className="text-xs text-oliver-black/60">{version.timestamp}</p>
|
||||
</div>
|
||||
<p className="text-sm text-black-title mt-1">Workfront ID: {version.workfrontId}</p>
|
||||
<p className="text-sm text-oliver-black mt-1">Workfront ID: {version.workfrontId}</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export const ProofPreview: React.FC<ProofPreviewProps> = ({ file, previewUrl, fi
|
|||
style={{ minHeight: '300px', maxHeight: 'calc(100vh - 9rem)' }}
|
||||
>
|
||||
<DocumentIcon className="h-20 w-20 text-gray-400 mb-4" />
|
||||
<p className="text-lg font-semibold text-primary-blue break-all">{displayName}</p>
|
||||
<p className="text-lg font-semibold text-oliver-black break-all">{displayName}</p>
|
||||
<p className="text-sm text-gray-500">{fileType}</p>
|
||||
<p className="text-sm text-gray-500 mt-2">No preview available for this file type.</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const ProofTypeManager: React.FC<ProofTypeManagerProps> = ({
|
|||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-md p-6 border border-gray-200 flex flex-col h-full">
|
||||
<h2 className="text-xl font-bold text-primary-blue mb-2">Proof Type Manager</h2>
|
||||
<h2 className="text-xl font-bold text-oliver-black mb-2">Proof Type Manager</h2>
|
||||
<p className="text-sm text-gray-500 mb-4">Assign and manage specific proof types for each sub-channel.</p>
|
||||
|
||||
<div className="mb-4">
|
||||
|
|
@ -51,7 +51,7 @@ export const ProofTypeManager: React.FC<ProofTypeManagerProps> = ({
|
|||
id="subchannel-select"
|
||||
value={selectedSubChannel}
|
||||
onChange={(e) => setSelectedSubChannel(e.target.value)}
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 pl-3 pr-10 text-gray-900 focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none disabled:bg-gray-100"
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 pl-3 pr-10 text-gray-900 focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none disabled:bg-gray-100"
|
||||
disabled={disabled}
|
||||
>
|
||||
{subChannels.map(sc => (
|
||||
|
|
@ -70,12 +70,12 @@ export const ProofTypeManager: React.FC<ProofTypeManagerProps> = ({
|
|||
value={newItem}
|
||||
onChange={(e) => setNewItem(e.target.value)}
|
||||
placeholder="New Proof Type..."
|
||||
className="flex-grow p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-active-blue focus:border-active-blue transition disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
className="flex-grow p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition disabled:bg-gray-100 disabled:cursor-not-allowed"
|
||||
disabled={disabled || !selectedSubChannel}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-4 rounded-md hover:bg-primary-blue transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-4 rounded-md hover:bg-oliver-azure transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
disabled={!newItem.trim() || disabled || !selectedSubChannel}
|
||||
>
|
||||
Add
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export const AssetUpload: React.FC<AssetUploadProps> = ({ onFileUpload, isLoadin
|
|||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
title={isUploadDisabled ? "Please complete all selections above" : "Upload a proof for review"}
|
||||
className="w-full bg-cyan-brand text-primary-blue font-bold py-3 px-6 rounded-full hover:bg-white transition-all duration-300 disabled:bg-gray-500 disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center"
|
||||
className="w-full bg-oliver-azure text-oliver-black font-bold py-3 px-6 rounded-full hover:bg-white transition-all duration-300 disabled:bg-gray-500 disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const ManagementCard: React.FC<ManagementCardProps> = ({ title, items, onAdd, on
|
|||
|
||||
return (
|
||||
<div className="p-6 flex flex-col h-full">
|
||||
<h2 className="text-xl font-semibold text-primary-blue mb-4">{title}</h2>
|
||||
<h2 className="text-xl font-semibold text-oliver-black mb-4">{title}</h2>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex gap-2 mb-4">
|
||||
<input
|
||||
|
|
@ -37,12 +37,12 @@ const ManagementCard: React.FC<ManagementCardProps> = ({ title, items, onAdd, on
|
|||
value={newItem}
|
||||
onChange={(e) => setNewItem(e.target.value)}
|
||||
placeholder={placeholder || `New ${title.slice(0, -1)}...`}
|
||||
className="flex-grow p-2 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue focus:border-active-blue transition disabled:bg-grey-100 disabled:cursor-not-allowed text-black-title"
|
||||
className="flex-grow p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition disabled:bg-oliver-grey disabled:cursor-not-allowed text-oliver-black"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors duration-300 disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors duration-300 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
disabled={!newItem.trim() || disabled}
|
||||
>
|
||||
Add
|
||||
|
|
@ -55,12 +55,12 @@ const ManagementCard: React.FC<ManagementCardProps> = ({ title, items, onAdd, on
|
|||
{items.map((item) => (
|
||||
<li
|
||||
key={item}
|
||||
className="flex items-center justify-between bg-grey-100 p-2.5 rounded-[10px] border border-grey-300 group hover:border-active-blue/30 transition-colors"
|
||||
className="flex items-center justify-between bg-oliver-grey p-2.5 rounded-[10px] border border-grey-300 group hover:border-oliver-azure/30 transition-colors"
|
||||
>
|
||||
<span className="text-black-title">{item}</span>
|
||||
<span className="text-oliver-black">{item}</span>
|
||||
<button
|
||||
onClick={() => onRemove(item)}
|
||||
className={`text-grey-700 hover:text-error transition-opacity ${disabled ? 'opacity-0 cursor-not-allowed' : 'opacity-0 group-hover:opacity-100'}`}
|
||||
className={`text-oliver-black/60 hover:text-error transition-opacity ${disabled ? 'opacity-0 cursor-not-allowed' : 'opacity-0 group-hover:opacity-100'}`}
|
||||
title={`Remove ${item}`}
|
||||
disabled={disabled}
|
||||
>
|
||||
|
|
@ -70,7 +70,7 @@ const ManagementCard: React.FC<ManagementCardProps> = ({ title, items, onAdd, on
|
|||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="text-center py-8 text-grey-700 bg-grey-100 rounded-[10px]">
|
||||
<div className="text-center py-8 text-oliver-black/60 bg-oliver-grey rounded-[10px]">
|
||||
No items found.
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -158,49 +158,49 @@ const UsersTab: React.FC = () => {
|
|||
<div className="space-y-8">
|
||||
{/* Add User Section */}
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg font-semibold text-primary-blue mb-4 flex items-center gap-2">
|
||||
<PlusIcon className="h-5 w-5 text-active-blue" />
|
||||
<h3 className="text-lg font-semibold text-oliver-black mb-4 flex items-center gap-2">
|
||||
<PlusIcon className="h-5 w-5 text-oliver-azure" />
|
||||
Add New User
|
||||
</h3>
|
||||
<form onSubmit={handleAddUser} className="grid grid-cols-1 md:grid-cols-4 gap-4 items-end">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-black-title mb-1">Name</label>
|
||||
<label className="block text-xs font-medium text-oliver-black mb-1">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={newName}
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
className="w-full p-2 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue focus:border-active-blue text-black-title"
|
||||
className="w-full p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure text-oliver-black"
|
||||
placeholder="e.g. John Smith"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-black-title mb-1">Email</label>
|
||||
<label className="block text-xs font-medium text-oliver-black mb-1">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
value={newEmail}
|
||||
onChange={(e) => setNewEmail(e.target.value)}
|
||||
className="w-full p-2 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue focus:border-active-blue text-black-title"
|
||||
className="w-full p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure text-oliver-black"
|
||||
placeholder="user@example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-black-title mb-1">Agency</label>
|
||||
<label className="block text-xs font-medium text-oliver-black mb-1">Agency</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={newAgency}
|
||||
onChange={(e) => setNewAgency(e.target.value)}
|
||||
className="w-full p-2 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue focus:border-active-blue appearance-none text-black-title"
|
||||
className="w-full p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure appearance-none text-oliver-black"
|
||||
>
|
||||
{agencies.map(a => <option key={a} value={a}>{a}</option>)}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-grey-700 pointer-events-none" />
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-oliver-black/60 pointer-events-none" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors"
|
||||
>
|
||||
Add User
|
||||
</button>
|
||||
|
|
@ -211,46 +211,46 @@ const UsersTab: React.FC = () => {
|
|||
<div className="overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Email</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Assigned Agency</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Name</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Email</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Assigned Agency</th>
|
||||
<th scope="col" className="relative px-6 py-3"><span className="sr-only">Actions</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{users.map((user, index) => (
|
||||
<tr key={user.id} className={index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}>
|
||||
<tr key={user.id} className={index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-info-light flex items-center justify-center text-active-blue">
|
||||
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-oliver-grey flex items-center justify-center text-oliver-azure">
|
||||
<UserIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<div className="text-sm font-medium text-black-title">{user.name}</div>
|
||||
<div className="text-sm font-medium text-oliver-black">{user.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">
|
||||
{user.email}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">
|
||||
<div className="relative max-w-xs">
|
||||
<select
|
||||
value={user.agency}
|
||||
onChange={(e) => handleAgencyChange(user.id, e.target.value)}
|
||||
className="w-full bg-white border-2 border-active-blue text-black-title py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none"
|
||||
className="w-full bg-white border-2 border-oliver-azure text-oliver-black py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none"
|
||||
>
|
||||
{agencies.map(a => <option key={a} value={a}>{a}</option>)}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-active-blue pointer-events-none" />
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-oliver-azure pointer-events-none" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
onClick={() => handleDeleteUser(user.id)}
|
||||
className="text-grey-700 hover:text-error transition-colors"
|
||||
className="text-oliver-black/60 hover:text-error transition-colors"
|
||||
title="Remove User"
|
||||
>
|
||||
<TrashIcon className="h-5 w-5" />
|
||||
|
|
@ -321,12 +321,12 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-white flex flex-col">
|
||||
<header className="mb-6 flex-shrink-0">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">Settings</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Settings</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">
|
||||
Configure application defaults and user access.
|
||||
</p>
|
||||
{readOnly && (
|
||||
<div className="mt-3 inline-flex items-center gap-2 bg-info-light border border-active-blue text-active-blue text-sm font-medium px-4 py-2 rounded-[10px]">
|
||||
<div className="mt-3 inline-flex items-center gap-2 bg-oliver-grey border border-oliver-azure text-oliver-azure text-sm font-medium px-4 py-2 rounded-[10px]">
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
|
||||
View-only mode
|
||||
</div>
|
||||
|
|
@ -350,8 +350,8 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
className={`
|
||||
whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm transition-colors
|
||||
${activeTab === tab.id
|
||||
? 'border-active-blue text-active-blue'
|
||||
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
|
||||
? 'border-oliver-azure text-oliver-azure'
|
||||
: 'border-transparent text-oliver-black hover:text-oliver-azure hover:border-grey-300'
|
||||
}
|
||||
`}
|
||||
aria-current={activeTab === tab.id ? 'page' : undefined}
|
||||
|
|
@ -393,17 +393,17 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
{activeTab === 'SubChannels' && (
|
||||
<div className="max-w-3xl space-y-4">
|
||||
<div className="bg-white p-4 rounded-[10px] border border-grey-300 shadow-sm">
|
||||
<label className="block text-sm font-medium text-black-title mb-2">Select Parent Channel</label>
|
||||
<label className="block text-sm font-medium text-oliver-black mb-2">Select Parent Channel</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={selectedChannel}
|
||||
onChange={(e) => setSelectedChannel(e.target.value)}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-active-blue appearance-none text-black-title ${selectedChannel ? 'bg-info-light border-active-blue' : 'border-active-blue'}`}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-oliver-azure appearance-none text-oliver-black ${selectedChannel ? 'bg-oliver-grey border-oliver-azure' : 'border-oliver-azure'}`}
|
||||
>
|
||||
<option value="" disabled>Select Channel</option>
|
||||
{Object.keys(options.channels).map(c => <option key={c} value={c}>{c}</option>)}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-active-blue pointer-events-none"/>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-oliver-azure pointer-events-none"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
<div className="max-w-3xl space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="bg-white p-4 rounded-[10px] border border-grey-300 shadow-sm">
|
||||
<label className="block text-sm font-medium text-black-title mb-2">Select Channel</label>
|
||||
<label className="block text-sm font-medium text-oliver-black mb-2">Select Channel</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={selectedChannel}
|
||||
|
|
@ -430,22 +430,22 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
setSelectedChannel(e.target.value);
|
||||
setSelectedSubChannel('');
|
||||
}}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-active-blue appearance-none text-black-title ${selectedChannel ? 'bg-info-light border-active-blue' : 'border-active-blue'}`}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-oliver-azure appearance-none text-oliver-black ${selectedChannel ? 'bg-oliver-grey border-oliver-azure' : 'border-oliver-azure'}`}
|
||||
>
|
||||
<option value="" disabled>Select Channel</option>
|
||||
{Object.keys(options.channels).map(c => <option key={c} value={c}>{c}</option>)}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-active-blue pointer-events-none"/>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-oliver-azure pointer-events-none"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-4 rounded-[10px] border border-grey-300 shadow-sm">
|
||||
<label className="block text-sm font-medium text-black-title mb-2">Select Sub-Channel</label>
|
||||
<label className="block text-sm font-medium text-oliver-black mb-2">Select Sub-Channel</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={selectedSubChannel}
|
||||
onChange={(e) => setSelectedSubChannel(e.target.value)}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-active-blue appearance-none text-black-title disabled:bg-grey-100 disabled:border-grey-300 ${selectedSubChannel ? 'bg-info-light border-active-blue' : 'border-active-blue'}`}
|
||||
className={`w-full p-2 border-2 rounded-[10px] focus:ring-2 focus:ring-oliver-azure appearance-none text-oliver-black disabled:bg-oliver-grey disabled:border-grey-300 ${selectedSubChannel ? 'bg-oliver-grey border-oliver-azure' : 'border-oliver-azure'}`}
|
||||
disabled={!selectedChannel}
|
||||
>
|
||||
<option value="" disabled>Select Sub-Channel</option>
|
||||
|
|
@ -453,7 +453,7 @@ export const Settings: React.FC<SettingsProps> = ({
|
|||
<option key={sc} value={sc}>{sc}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-active-blue pointer-events-none"/>
|
||||
<ChevronDownIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-oliver-azure pointer-events-none"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import React from 'react';
|
||||
import { BarclaysLogo } from './icons/BarclaysLogo';
|
||||
import { DashboardIcon } from './icons/DashboardIcon';
|
||||
import { AnalyticsIcon } from './icons/AnalyticsIcon';
|
||||
import { SettingsIcon } from './icons/SettingsIcon';
|
||||
|
|
@ -36,19 +35,10 @@ interface SidebarProps {
|
|||
|
||||
export const Sidebar: React.FC<SidebarProps> = ({ activeItem, onNavigate, userName, userEmail, userRole }) => {
|
||||
return (
|
||||
<aside className="w-72 flex-shrink-0 bg-primary-blue text-slate-200 flex flex-col border-r border-white/10 font-sans">
|
||||
<aside className="w-72 flex-shrink-0 bg-oliver-black text-slate-200 flex flex-col font-sans">
|
||||
{/* Brand Header */}
|
||||
<div className="py-6 px-8 border-b border-white/10 flex flex-col items-center text-center">
|
||||
<BarclaysLogo className="h-10 w-auto text-cyan-brand mb-2" />
|
||||
<span className="text-lg font-bold tracking-tight text-white leading-tight">
|
||||
Mod Comms
|
||||
</span>
|
||||
<span className="text-xs text-lime uppercase tracking-widest font-semibold">
|
||||
Compliance AI
|
||||
</span>
|
||||
<span className="text-xs text-grey-700 tracking-wider mt-0.5">
|
||||
powered by <span className="font-bold">OLIVER</span>
|
||||
</span>
|
||||
<img src="/BAR-ModComms-logo-v4.png" alt="Mod Comms AI" className="h-12 w-auto" />
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
|
|
@ -63,7 +53,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeItem, onNavigate, userNa
|
|||
onClick={() => onNavigate(item.name)}
|
||||
className={`group w-full flex items-center pl-8 pr-4 py-3.5 text-left text-sm font-medium transition-all duration-300 ease-in-out ${
|
||||
isActive
|
||||
? 'bg-white text-primary-blue shadow-lg rounded-r-[10px] rounded-l-none'
|
||||
? 'bg-oliver-azure text-white shadow-lg rounded-r-[10px] rounded-l-none'
|
||||
: 'text-slate-300 hover:bg-white/10 hover:text-white rounded-[10px]'
|
||||
}`}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
|
|
@ -76,7 +66,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeItem, onNavigate, userNa
|
|||
</nav>
|
||||
|
||||
{/* User Profile Snippet */}
|
||||
<div className="p-4 border-t border-white/10 bg-primary-blue/80">
|
||||
<div className="p-4 border-t border-white/10 bg-oliver-black/80">
|
||||
<button
|
||||
onClick={() => onNavigate('Profile')}
|
||||
className={`group w-full flex items-center gap-3 p-3 rounded-[10px] text-left transition-all duration-200 ${
|
||||
|
|
@ -85,7 +75,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeItem, onNavigate, userNa
|
|||
: 'hover:bg-white/10'
|
||||
}`}
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full bg-active-blue flex items-center justify-center ring-2 ring-white/20 group-hover:ring-active-blue/50 transition-all">
|
||||
<div className="w-10 h-10 rounded-full bg-oliver-azure flex items-center justify-center ring-2 ring-white/20 group-hover:ring-oliver-azure/50 transition-all">
|
||||
<UserIcon className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const StatusInfo: React.FC<StatusInfoProps> = ({ status, isHeroVariant = false }
|
|||
complete: 'text-success',
|
||||
issues: 'text-warning',
|
||||
error: 'text-error',
|
||||
pending: 'text-grey-700'
|
||||
pending: 'text-oliver-black/60'
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
|
|
@ -80,18 +80,18 @@ export const StatusDashboard: React.FC<StatusDashboardProps> = ({ status, varian
|
|||
case 'error':
|
||||
return `${defaultBase} bg-error-light border-error`;
|
||||
default: // 'pending' or 'in-progress'
|
||||
return `${defaultBase} bg-grey-100 border-grey-300`;
|
||||
return `${defaultBase} bg-oliver-grey border-grey-300`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={isHeroVariant ? '' : "mb-12"}>
|
||||
<h3 className={`text-2xl font-bold text-center mb-8 ${isHeroVariant ? 'text-white' : 'text-primary-blue'}`}>Review Progress</h3>
|
||||
<h3 className={`text-2xl font-bold text-center mb-8 ${isHeroVariant ? 'text-white' : 'text-oliver-black'}`}>Review Progress</h3>
|
||||
<div className={`grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 ${isHeroVariant ? 'gap-4' : 'gap-6'}`}>
|
||||
{AGENT_NAMES.map((agentName) => {
|
||||
const agentStatus = status[agentName] || 'pending';
|
||||
const iconColor = isHeroVariant ? 'text-cyan-brand' : 'text-active-blue';
|
||||
const iconColor = isHeroVariant ? 'text-oliver-azure' : 'text-oliver-azure';
|
||||
const Icon = agentIcons[agentName];
|
||||
|
||||
return (
|
||||
|
|
@ -100,11 +100,11 @@ export const StatusDashboard: React.FC<StatusDashboardProps> = ({ status, varian
|
|||
<div className="mb-3 h-8 w-8 flex items-center justify-center">
|
||||
<Icon className={`h-8 w-8 ${iconColor}`} />
|
||||
</div>
|
||||
<h4 className={`text-base font-bold ${isHeroVariant ? 'text-white' : 'text-primary-blue'}`}>{agentName}</h4>
|
||||
<h4 className={`text-base font-bold ${isHeroVariant ? 'text-white' : 'text-oliver-black'}`}>{agentName}</h4>
|
||||
</div>
|
||||
<div className="font-semibold text-xs mt-2 h-6 flex items-center justify-center">
|
||||
{agentStatus === 'in-progress' ? (
|
||||
<div className={`flex items-center ${isHeroVariant ? 'text-white/80' : 'text-grey-700'}`}>
|
||||
<div className={`flex items-center ${isHeroVariant ? 'text-white/80' : 'text-oliver-black/60'}`}>
|
||||
<SpinnerIcon className="h-4 w-4 mr-2 custom-spinner" />
|
||||
<span>Analysing...</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export const ToggleSwitch: React.FC<ToggleSwitchProps> = ({ enabled, onChange })
|
|||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-active-blue focus:ring-offset-2 ${
|
||||
enabled ? 'bg-active-blue' : 'bg-gray-300'
|
||||
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-oliver-azure focus:ring-offset-2 ${
|
||||
enabled ? 'bg-oliver-azure' : 'bg-gray-300'
|
||||
}`}
|
||||
role="switch"
|
||||
aria-checked={enabled}
|
||||
|
|
|
|||
|
|
@ -195,11 +195,11 @@ export const UserManagement: React.FC = () => {
|
|||
return (
|
||||
<div className="p-8 flex items-center justify-center h-full">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<svg className="animate-spin h-8 w-8 text-active-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<svg className="animate-spin h-8 w-8 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
<p className="text-grey-700 text-sm">Loading user management...</p>
|
||||
<p className="text-oliver-black/60 text-sm">Loading user management...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -208,8 +208,8 @@ export const UserManagement: React.FC = () => {
|
|||
return (
|
||||
<div className="p-4 sm:p-6 lg:p-8 h-full bg-white overflow-y-auto">
|
||||
<header className="mb-6">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-primary-blue">User Management</h1>
|
||||
<p className="text-base lg:text-lg text-primary-blue mt-1">
|
||||
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">User Management</h1>
|
||||
<p className="text-base lg:text-lg text-oliver-black mt-1">
|
||||
{isSuperAdmin
|
||||
? 'Manage user roles and agency assignments. Users are provisioned automatically via Azure AD.'
|
||||
: 'View user roles and agency assignments.'}
|
||||
|
|
@ -232,34 +232,34 @@ export const UserManagement: React.FC = () => {
|
|||
|
||||
{/* Users Table */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-xl font-semibold text-primary-blue mb-4">Users ({users.length})</h2>
|
||||
<h2 className="text-xl font-semibold text-oliver-black mb-4">Users ({users.length})</h2>
|
||||
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-lime">
|
||||
<thead className="bg-oliver-sky">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Name</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Email</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Role</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agency</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Created</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider w-20"></th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Name</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Email</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Role</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agency</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Created</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider w-20"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{users.map((user, index) => (
|
||||
<tr key={user.id} className={index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}>
|
||||
<tr key={user.id} className={index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-info-light flex items-center justify-center text-active-blue">
|
||||
<div className="flex-shrink-0 h-8 w-8 rounded-full bg-oliver-grey flex items-center justify-center text-oliver-azure">
|
||||
<UserIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<div className="text-sm font-medium text-black-title">{user.name}</div>
|
||||
<div className="text-sm font-medium text-oliver-black">{user.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">
|
||||
{user.email}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
|
|
@ -268,16 +268,16 @@ export const UserManagement: React.FC = () => {
|
|||
<select
|
||||
value={user.role}
|
||||
onChange={(e) => handleRoleChange(user.id, e.target.value)}
|
||||
className="w-full bg-white border-2 border-active-blue text-black-title py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none"
|
||||
className="w-full bg-white border-2 border-oliver-azure text-oliver-black py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none"
|
||||
>
|
||||
{ROLE_OPTIONS.map(r => (
|
||||
<option key={r.value} value={r.value}>{r.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-active-blue pointer-events-none" />
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-oliver-azure pointer-events-none" />
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-black-title">{formatRoleLabel(user.role)}</span>
|
||||
<span className="text-sm text-oliver-black">{formatRoleLabel(user.role)}</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
|
|
@ -286,22 +286,22 @@ export const UserManagement: React.FC = () => {
|
|||
<select
|
||||
value={user.agency_id || ''}
|
||||
onChange={(e) => handleAgencyChange(user.id, e.target.value || null)}
|
||||
className="w-full bg-white border-2 border-active-blue text-black-title py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none"
|
||||
className="w-full bg-white border-2 border-oliver-azure text-oliver-black py-1.5 pl-3 pr-8 rounded-[10px] text-sm focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none"
|
||||
>
|
||||
<option value="">None (Unassigned)</option>
|
||||
{agencies.map(a => (
|
||||
<option key={a.id} value={a.id}>{a.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-active-blue pointer-events-none" />
|
||||
<ChevronDownIcon className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-oliver-azure pointer-events-none" />
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-black-title">
|
||||
<span className="text-sm text-oliver-black">
|
||||
{user.agency || 'Unassigned'}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black/60">
|
||||
{formatDate(user.created_at)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
|
|
@ -309,7 +309,7 @@ export const UserManagement: React.FC = () => {
|
|||
<button
|
||||
onClick={() => handleOpenHistory(user)}
|
||||
title="View change history"
|
||||
className="p-1 text-grey-700 hover:text-active-blue transition-colors rounded-full hover:bg-info-light"
|
||||
className="p-1 text-oliver-black/60 hover:text-oliver-azure transition-colors rounded-full hover:bg-oliver-grey"
|
||||
>
|
||||
<ClockIcon className="h-4 w-4" />
|
||||
</button>
|
||||
|
|
@ -327,7 +327,7 @@ export const UserManagement: React.FC = () => {
|
|||
))}
|
||||
{users.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-6 py-8 text-center text-grey-700">
|
||||
<td colSpan={6} className="px-6 py-8 text-center text-oliver-black/60">
|
||||
No users found.
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -354,13 +354,13 @@ export const UserManagement: React.FC = () => {
|
|||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-black-title">Confirm Super Admin Assignment</h3>
|
||||
<h3 className="text-lg font-semibold text-oliver-black">Confirm Super Admin Assignment</h3>
|
||||
</div>
|
||||
<p className="text-sm text-grey-700 mb-4">
|
||||
You are about to grant Super Admin privileges to <strong className="text-black-title">{pendingSuperAdminUser.userName}</strong>. This gives full system access including user management. This action should only be performed intentionally.
|
||||
<p className="text-sm text-oliver-black/60 mb-4">
|
||||
You are about to grant Super Admin privileges to <strong className="text-oliver-black">{pendingSuperAdminUser.userName}</strong>. This gives full system access including user management. This action should only be performed intentionally.
|
||||
</p>
|
||||
<p className="text-sm text-grey-700 mb-2">
|
||||
To confirm, type <strong className="text-black-title">make this user super admin</strong> below:
|
||||
<p className="text-sm text-oliver-black/60 mb-2">
|
||||
To confirm, type <strong className="text-oliver-black">make this user super admin</strong> below:
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -373,10 +373,10 @@ export const UserManagement: React.FC = () => {
|
|||
onDrop={(e) => e.preventDefault()}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
placeholder="Type the phrase above..."
|
||||
className={`w-full p-2 border-2 rounded-[10px] text-sm text-black-title focus:outline-none focus:ring-2 transition ${
|
||||
className={`w-full p-2 border-2 rounded-[10px] text-sm text-oliver-black focus:outline-none focus:ring-2 transition ${
|
||||
confirmationError
|
||||
? 'border-error focus:ring-error'
|
||||
: 'border-grey-700 focus:ring-active-blue focus:border-active-blue'
|
||||
: 'border-oliver-azure focus:ring-oliver-azure focus:border-oliver-azure'
|
||||
}`}
|
||||
autoFocus
|
||||
/>
|
||||
|
|
@ -386,14 +386,14 @@ export const UserManagement: React.FC = () => {
|
|||
<div className="flex justify-end gap-3 mt-5">
|
||||
<button
|
||||
onClick={handleCancelSuperAdmin}
|
||||
className="px-4 py-2 text-sm font-medium text-grey-700 hover:text-black-title border border-grey-300 rounded-full hover:bg-grey-100 transition-colors"
|
||||
className="px-4 py-2 text-sm font-medium text-oliver-black/60 hover:text-oliver-black border border-grey-300 rounded-full hover:bg-oliver-grey transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirmSuperAdmin}
|
||||
disabled={confirmationText !== 'make this user super admin'}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-active-blue rounded-full hover:bg-active-blue/90 transition-colors disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-oliver-azure rounded-full hover:bg-oliver-azure/90 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
|
|
@ -413,12 +413,12 @@ export const UserManagement: React.FC = () => {
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold text-black-title">
|
||||
<h3 className="text-lg font-semibold text-oliver-black">
|
||||
Change History — {historyUser.name}
|
||||
</h3>
|
||||
<button
|
||||
onClick={handleCloseHistory}
|
||||
className="p-1 text-grey-700 hover:text-black-title transition-colors"
|
||||
className="p-1 text-oliver-black/60 hover:text-oliver-black transition-colors"
|
||||
>
|
||||
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
|
|
@ -428,32 +428,32 @@ export const UserManagement: React.FC = () => {
|
|||
<div className="overflow-y-auto flex-1">
|
||||
{historyLoading ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<svg className="animate-spin h-6 w-6 text-active-blue" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<svg className="animate-spin h-6 w-6 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
</div>
|
||||
) : historyEntries.length === 0 ? (
|
||||
<p className="text-center py-8 text-grey-700">No change history recorded.</p>
|
||||
<p className="text-center py-8 text-oliver-black/60">No change history recorded.</p>
|
||||
) : (
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-grey-100 sticky top-0">
|
||||
<thead className="bg-oliver-grey sticky top-0">
|
||||
<tr>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-black-title uppercase tracking-wider">Change</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-black-title uppercase tracking-wider">Changed by</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Date</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Change</th>
|
||||
<th className="px-4 py-2 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Changed by</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-grey-300">
|
||||
{historyEntries.map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-grey-700">
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-oliver-black/60">
|
||||
{formatHistoryDate(entry.created_at)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-black-title">
|
||||
<td className="px-4 py-3 text-sm text-oliver-black">
|
||||
{formatChangeDescription(entry)}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-grey-700">
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-oliver-black/60">
|
||||
{entry.changed_by_name || 'System'}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -468,7 +468,7 @@ export const UserManagement: React.FC = () => {
|
|||
|
||||
{/* Agency Management */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-primary-blue mb-4">Agencies ({agencies.length})</h2>
|
||||
<h2 className="text-xl font-semibold text-oliver-black mb-4">Agencies ({agencies.length})</h2>
|
||||
<div className="max-w-xl">
|
||||
{isSuperAdmin && (
|
||||
<form onSubmit={handleCreateAgency} className="flex gap-2 mb-4">
|
||||
|
|
@ -477,12 +477,12 @@ export const UserManagement: React.FC = () => {
|
|||
value={newAgencyName}
|
||||
onChange={(e) => setNewAgencyName(e.target.value)}
|
||||
placeholder="New agency name..."
|
||||
className="flex-grow p-2 border-2 border-grey-700 rounded-[10px] focus:ring-2 focus:ring-active-blue focus:border-active-blue transition text-black-title"
|
||||
className="flex-grow p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition text-oliver-black"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!newAgencyName.trim() || isCreatingAgency}
|
||||
className="bg-active-blue text-white font-semibold py-2 px-6 rounded-full hover:bg-active-blue/90 transition-colors disabled:bg-grey-700 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
className="bg-oliver-azure text-white font-semibold py-2 px-6 rounded-full hover:bg-oliver-azure/90 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add
|
||||
|
|
@ -494,16 +494,16 @@ export const UserManagement: React.FC = () => {
|
|||
{agencies.map(agency => (
|
||||
<div
|
||||
key={agency.id}
|
||||
className="flex items-center justify-between bg-grey-100 p-3 rounded-[10px] border border-grey-300"
|
||||
className="flex items-center justify-between bg-oliver-grey p-3 rounded-[10px] border border-grey-300"
|
||||
>
|
||||
<span className="text-black-title font-medium">{agency.name}</span>
|
||||
<span className="text-xs text-grey-700">
|
||||
<span className="text-oliver-black font-medium">{agency.name}</span>
|
||||
<span className="text-xs text-oliver-black/60">
|
||||
{users.filter(u => u.agency_id === agency.id).length} user(s)
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
{agencies.length === 0 && (
|
||||
<div className="text-center py-6 text-grey-700 bg-grey-100 rounded-[10px]">
|
||||
<div className="text-center py-6 text-oliver-black/60 bg-oliver-grey rounded-[10px]">
|
||||
No agencies created yet.
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ const ConfirmationComponent: React.FC<{
|
|||
<select
|
||||
value={channel}
|
||||
onChange={e => setChannel(e.target.value)}
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none"
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none"
|
||||
>
|
||||
<option value="" disabled>Select Channel</option>
|
||||
{availableChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
|
|
@ -91,7 +91,7 @@ const ConfirmationComponent: React.FC<{
|
|||
value={subChannel}
|
||||
onChange={e => setSubChannel(e.target.value)}
|
||||
disabled={!channel}
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none disabled:bg-gray-100"
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none disabled:bg-gray-100"
|
||||
>
|
||||
<option value="" disabled>Select Sub-Channel</option>
|
||||
{availableSubChannels.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
|
|
@ -104,7 +104,7 @@ const ConfirmationComponent: React.FC<{
|
|||
<select
|
||||
value={proofType}
|
||||
onChange={e => setProofType(e.target.value)}
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-active-blue appearance-none"
|
||||
className="w-full bg-white border border-gray-300 rounded-md py-2 px-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-oliver-azure appearance-none"
|
||||
>
|
||||
<option value="" disabled>Select Proof Type</option>
|
||||
{availableProofTypes.map(opt => <option key={opt} value={opt}>{opt}</option>)}
|
||||
|
|
@ -115,7 +115,7 @@ const ConfirmationComponent: React.FC<{
|
|||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitDisabled}
|
||||
className="w-full bg-active-blue text-white font-bold py-2 px-4 rounded-md hover:bg-primary-blue transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
className="w-full bg-oliver-azure text-white font-bold py-2 px-4 rounded-md hover:bg-oliver-azure transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||
>
|
||||
Confirm & Analyse
|
||||
</button>
|
||||
|
|
@ -127,12 +127,12 @@ const ConfirmationComponent: React.FC<{
|
|||
const MessageBubble: React.FC<{ sender: 'user' | 'agent'; children: React.ReactNode }> = ({ sender, children }) => {
|
||||
const isUser = sender === 'user';
|
||||
const bubbleClasses = isUser
|
||||
? 'bg-active-blue text-white'
|
||||
? 'bg-oliver-azure text-white'
|
||||
: 'bg-white text-gray-800 border border-gray-200';
|
||||
const alignmentClasses = isUser ? 'items-end' : 'items-start';
|
||||
const avatar = isUser
|
||||
? <div className="w-8 h-8 rounded-full bg-primary-blue flex items-center justify-center ring-2 ring-white"><UserIcon className="h-5 w-5 text-white"/></div>
|
||||
: <div className="w-8 h-8 rounded-full bg-cyan-brand flex items-center justify-center ring-2 ring-white"><LeadAgentIcon className="h-5 w-5 text-primary-blue"/></div>;
|
||||
? <div className="w-8 h-8 rounded-full bg-oliver-black flex items-center justify-center ring-2 ring-white"><UserIcon className="h-5 w-5 text-white"/></div>
|
||||
: <div className="w-8 h-8 rounded-full bg-oliver-azure flex items-center justify-center ring-2 ring-white"><LeadAgentIcon className="h-5 w-5 text-oliver-black"/></div>;
|
||||
|
||||
const isConfirmation = React.isValidElement(children) && children.type === ConfirmationComponent;
|
||||
|
||||
|
|
@ -247,9 +247,9 @@ export const WIPReviewer: React.FC<WIPReviewerProps> = ({ dropdownOptions, msalI
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-grey-100">
|
||||
<div className="flex flex-col h-full bg-oliver-grey">
|
||||
<header className="p-4 border-b border-gray-200 bg-white/80 backdrop-blur-sm flex-shrink-0">
|
||||
<h1 className="text-xl font-bold text-primary-blue text-center">WIP Reviewer</h1>
|
||||
<h1 className="text-xl font-bold text-oliver-black text-center">WIP Reviewer</h1>
|
||||
</header>
|
||||
|
||||
{/* Message Display Area */}
|
||||
|
|
@ -303,7 +303,7 @@ export const WIPReviewer: React.FC<WIPReviewerProps> = ({ dropdownOptions, msalI
|
|||
type="button"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isLoading}
|
||||
className="p-2 text-gray-500 hover:text-active-blue rounded-full transition-colors disabled:opacity-50"
|
||||
className="p-2 text-gray-500 hover:text-oliver-azure rounded-full transition-colors disabled:opacity-50"
|
||||
aria-label="Attach file"
|
||||
>
|
||||
<PaperClipIcon className="h-6 w-6"/>
|
||||
|
|
@ -317,14 +317,14 @@ export const WIPReviewer: React.FC<WIPReviewerProps> = ({ dropdownOptions, msalI
|
|||
handleSendMessage(e);
|
||||
}
|
||||
}}
|
||||
className="w-full p-2.5 mx-2 border border-gray-300 rounded-lg resize-none focus:ring-2 focus:ring-active-blue focus:border-active-blue transition"
|
||||
className="w-full p-2.5 mx-2 border border-gray-300 rounded-lg resize-none focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition"
|
||||
placeholder="Type your message..."
|
||||
rows={1}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="p-2.5 rounded-full text-white bg-active-blue hover:bg-primary-blue disabled:bg-gray-400 transition-colors"
|
||||
className="p-2.5 rounded-full text-white bg-oliver-azure hover:bg-oliver-azure disabled:bg-gray-400 transition-colors"
|
||||
disabled={!userInput.trim() || isLoading}
|
||||
aria-label="Send message"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,44 +4,38 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Barclays Mod Comms</title>
|
||||
<!-- Google Fonts - Inter as fallback for Barclays Effra (proprietary font not available as web font) -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<title>Mod Comms - Intelligent Review</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
// Barclays Effra is the brand font but is proprietary; Inter is the web fallback
|
||||
'sans': ['Barclays Effra', 'Inter', 'Arial', 'sans-serif'],
|
||||
'sans': ['Arial', 'sans-serif'],
|
||||
},
|
||||
borderRadius: {
|
||||
DEFAULT: '10px',
|
||||
},
|
||||
colors: {
|
||||
// Primary Brand Colors
|
||||
'primary-blue': '#1A2142',
|
||||
'active-blue': '#006DE3',
|
||||
'electric-violet': '#7A0FF9',
|
||||
'lime': '#C3FB5A',
|
||||
'cyan-brand': '#00AEEF',
|
||||
'teal-brand': '#01A1A2',
|
||||
// Oliver Brand Colors
|
||||
'oliver-black': '#1A1A1A',
|
||||
'oliver-gold': '#FFCB05',
|
||||
'oliver-orange': '#FF5C00',
|
||||
'oliver-azure': '#0487B6',
|
||||
'oliver-sky': '#5DF5EA',
|
||||
'oliver-grey': '#EFEFEF',
|
||||
'oliver-green': '#09821F',
|
||||
|
||||
// Functional Colors
|
||||
'grey-100': '#F6F6F6',
|
||||
'grey-300': '#E2E2E2',
|
||||
'grey-700': '#8E8E8E',
|
||||
'grey-900': '#515151',
|
||||
'black-title': '#272727',
|
||||
|
||||
// Status Colors
|
||||
'info-light': '#E7F0FB',
|
||||
// Keep functional status colours for RAG system
|
||||
'success': '#09821F',
|
||||
'success-light': '#E9F4EA',
|
||||
'warning': '#FFBA00',
|
||||
'warning': '#FFCB05',
|
||||
'warning-light': '#FFF8E7',
|
||||
'error': '#E3000F',
|
||||
'error-light': '#FDE7E9',
|
||||
|
||||
// Keep grey scale for borders
|
||||
'grey-300': '#E2E2E2',
|
||||
},
|
||||
animation: {
|
||||
'slow-ping': 'ping 2s cubic-bezier(0, 0, 0.2, 1) infinite',
|
||||
|
|
|
|||
BIN
frontend/public/BAR-ModComms-logo-v4.png
Normal file
BIN
frontend/public/BAR-ModComms-logo-v4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Loading…
Add table
Reference in a new issue