Update modals and remaining UI components in Campaigns
- Update UploadProofModal with new design system colors - Update LoadingCell progress bar to active-blue - Update DeleteConfirmationModal with pill buttons and rounded corners - Update CampaignDeleteConfirmationModal styling - Apply consistent border radius (10px) to all modals - Update drag & drop zone colors to use success theme Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
532d7541d6
commit
75b2c6e699
1 changed files with 78 additions and 78 deletions
|
|
@ -616,38 +616,38 @@ const UploadProofModal: React.FC<{
|
|||
return (
|
||||
<div className="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-slate-900/75 backdrop-blur-sm transition-opacity"
|
||||
aria-hidden="true"
|
||||
<div
|
||||
className="fixed inset-0 bg-slate-900/75 backdrop-blur-sm transition-opacity"
|
||||
aria-hidden="true"
|
||||
onClick={onClose}
|
||||
></div>
|
||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle" aria-hidden="true">​</span>
|
||||
|
||||
<div className="relative inline-block transform overflow-hidden rounded-2xl bg-white text-left align-bottom shadow-2xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:align-middle border border-gray-100">
|
||||
|
||||
<div className="relative inline-block transform overflow-hidden rounded-[10px] bg-white text-left align-bottom shadow-2xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:align-middle border border-grey-300">
|
||||
{/* Header */}
|
||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 border-b border-gray-100 flex justify-between items-start">
|
||||
<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-brand-dark-blue" id="modal-title">Upload New Proof</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">Drag and drop your asset below to start AI analysis.</p>
|
||||
<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 asset below to start AI analysis.</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded-full p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-600 transition-colors"
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="rounded-full p-2 text-grey-700 hover:bg-grey-100 hover:text-black-title transition-colors"
|
||||
>
|
||||
<XIcon className="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Content */}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="px-4 pt-5 pb-4 sm:p-8">
|
||||
<div className="space-y-6">
|
||||
{/* Proof Name */}
|
||||
<div>
|
||||
<label htmlFor="proof-name" className="block text-sm font-bold text-brand-dark-blue mb-2">Proof Name</label>
|
||||
<label htmlFor="proof-name" className="block text-sm font-bold text-primary-blue 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-gray-400" />
|
||||
<DocumentIcon className="h-5 w-5 text-grey-700" />
|
||||
</div>
|
||||
<input
|
||||
id="proof-name"
|
||||
|
|
@ -656,13 +656,13 @@ const UploadProofModal: React.FC<{
|
|||
onChange={(e) => setProofName(e.target.value)}
|
||||
placeholder="e.g., Q4 Hero Instagram Post"
|
||||
disabled={isLoading}
|
||||
className="w-full rounded-xl border-gray-300 shadow-sm focus:border-brand-accent focus:ring-brand-accent p-3 pl-10 bg-white transition-all text-gray-900"
|
||||
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"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{isNewVersion && (
|
||||
<p className="text-xs text-blue-600 mt-2 p-2 bg-blue-50 rounded-lg flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-blue-600"></span>
|
||||
<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>
|
||||
A proof with this name already exists. This will be uploaded as a new version.
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -671,73 +671,73 @@ 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-brand-dark-blue mb-2">Channel</label>
|
||||
<label htmlFor="proof-channel" className="block text-sm font-bold text-primary-blue 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-gray-400" />
|
||||
<ChannelIcon className="h-5 w-5 text-grey-700" />
|
||||
</div>
|
||||
<select
|
||||
id="proof-channel"
|
||||
value={channel}
|
||||
onChange={(e) => setChannel(e.target.value)}
|
||||
disabled={isLoading}
|
||||
className="w-full rounded-xl border-gray-300 shadow-sm focus:border-brand-accent focus:ring-brand-accent p-3 pl-10 bg-white transition-all text-gray-900 appearance-none"
|
||||
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"
|
||||
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-gray-400">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label htmlFor="proof-sub-channel" className="block text-sm font-bold text-brand-dark-blue mb-2">Sub-Channel</label>
|
||||
<label htmlFor="proof-sub-channel" className="block text-sm font-bold text-primary-blue 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-gray-400" />
|
||||
<TagIcon className="h-5 w-5 text-grey-700" />
|
||||
</div>
|
||||
<select
|
||||
id="proof-sub-channel"
|
||||
value={subChannel}
|
||||
onChange={(e) => setSubChannel(e.target.value)}
|
||||
disabled={isLoading || !channel}
|
||||
className="w-full rounded-xl border-gray-300 shadow-sm focus:border-brand-accent focus:ring-brand-accent p-3 pl-10 bg-white transition-all text-gray-900 appearance-none disabled:bg-gray-100 disabled:text-gray-400"
|
||||
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"
|
||||
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-gray-400">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<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-brand-dark-blue mb-2">Proof Type</label>
|
||||
<label htmlFor="proof-type" className="block text-sm font-bold text-primary-blue 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-xl border-gray-300 shadow-sm focus:border-brand-accent focus:ring-brand-accent p-3 bg-white transition-all text-gray-900 appearance-none disabled:bg-gray-100 disabled:text-gray-400"
|
||||
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"
|
||||
required={showProofType}
|
||||
>
|
||||
<option value="" disabled>
|
||||
{!subChannel
|
||||
? "Select a Sub-Channel first"
|
||||
: availableProofTypes.length === 0
|
||||
? "No proof types available"
|
||||
{!subChannel
|
||||
? "Select a Sub-Channel first"
|
||||
: availableProofTypes.length === 0
|
||||
? "No proof types available"
|
||||
: "Select Proof Type"
|
||||
}
|
||||
</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-gray-400">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-grey-700">
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -746,45 +746,45 @@ const UploadProofModal: React.FC<{
|
|||
|
||||
{/* Drag & Drop Zone */}
|
||||
<div>
|
||||
<label className="block text-sm font-bold text-brand-dark-blue mb-2">Proof File</label>
|
||||
<div
|
||||
<label className="block text-sm font-bold text-primary-blue mb-2">Proof File</label>
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
className={`
|
||||
relative mt-1 flex justify-center px-6 pt-10 pb-10 border-2 border-dashed rounded-xl transition-all group cursor-pointer
|
||||
${file ? 'border-emerald-500 bg-emerald-50' : 'border-gray-300 hover:bg-emerald-50/30 hover:border-emerald-400/50'}
|
||||
relative mt-1 flex justify-center px-6 pt-10 pb-10 border-2 border-dashed rounded-[10px] transition-all group cursor-pointer
|
||||
${file ? 'border-success bg-success-light' : 'border-grey-300 hover:bg-success-light/30 hover:border-success/50'}
|
||||
`}
|
||||
>
|
||||
<input
|
||||
id="file-upload"
|
||||
name="file-upload"
|
||||
type="file"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileChange}
|
||||
disabled={isLoading}
|
||||
accept="image/png, image/jpeg, image/webp, image/gif, video/mp4, application/pdf"
|
||||
<input
|
||||
id="file-upload"
|
||||
name="file-upload"
|
||||
type="file"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileChange}
|
||||
disabled={isLoading}
|
||||
accept="image/png, image/jpeg, image/webp, image/gif, video/mp4, application/pdf"
|
||||
/>
|
||||
<div className="text-center space-y-2 pointer-events-none">
|
||||
{!file ? (
|
||||
<>
|
||||
<div className="mx-auto h-16 w-16 bg-gray-100 text-gray-400 rounded-full flex items-center justify-center group-hover:scale-110 group-hover:text-emerald-600 group-hover:bg-emerald-100 transition-all duration-300">
|
||||
<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">
|
||||
<UploadIcon className="h-8 w-8" />
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
<span className="font-bold text-emerald-600">Click to upload</span> or drag and drop
|
||||
<div className="text-sm text-black-title">
|
||||
<span className="font-bold text-success">Click to upload</span> or drag and drop
|
||||
</div>
|
||||
<p className="text-xs text-gray-400">SVG, PNG, JPG or GIF (max. 800x400px)</p>
|
||||
<p className="text-xs text-grey-700">SVG, PNG, JPG or GIF (max. 800x400px)</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mx-auto h-16 w-16 bg-emerald-100 text-emerald-600 rounded-full flex items-center justify-center scale-110 shadow-sm">
|
||||
<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-brand-dark-blue truncate max-w-xs mx-auto">
|
||||
<div className="text-sm font-bold text-black-title truncate max-w-xs mx-auto">
|
||||
{file.name}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">Ready for analysis</p>
|
||||
<p className="text-xs text-grey-900">Ready for analysis</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -794,19 +794,19 @@ const UploadProofModal: React.FC<{
|
|||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="bg-gray-50 px-4 py-4 sm:px-6 border-t border-gray-100 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 bg-white border border-gray-300 text-gray-700 font-semibold py-2.5 px-5 rounded-xl hover:bg-gray-50 hover:text-gray-900 transition-colors duration-300 disabled:opacity-50"
|
||||
<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">
|
||||
<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"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitDisabled}
|
||||
className="w-full sm:w-auto flex items-center justify-center bg-brand-accent text-white font-bold py-2.5 px-6 rounded-xl hover:bg-brand-dark-blue shadow-lg shadow-brand-accent/20 transition-all duration-300 disabled:bg-gray-300 disabled:text-gray-500 disabled:shadow-none disabled:cursor-not-allowed"
|
||||
<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"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
|
|
@ -825,7 +825,7 @@ const UploadProofModal: React.FC<{
|
|||
|
||||
const LoadingCell: React.FC<{ progress: { completed: number; total: number } }> = ({ progress }) => {
|
||||
const percent = progress.total > 0 ? Math.round((progress.completed / progress.total) * 100) : 0;
|
||||
|
||||
|
||||
let statusText = 'Analyzing...';
|
||||
if (progress.completed < AGENT_NAMES.length) {
|
||||
const agentName = AGENT_NAMES[progress.completed];
|
||||
|
|
@ -838,13 +838,13 @@ const LoadingCell: React.FC<{ progress: { completed: number; total: number } }>
|
|||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full px-2 text-center w-full">
|
||||
<div className="w-full bg-gray-200 rounded-full h-1.5 mb-1.5">
|
||||
<div
|
||||
className="bg-brand-accent h-1.5 rounded-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"
|
||||
style={{ width: `${percent}%`, transition: 'width 0.3s ease-in-out' }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-xs text-gray-600 truncate w-full">{statusText} ({percent}%)</span>
|
||||
<span className="text-xs text-black-title truncate w-full">{statusText} ({percent}%)</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -865,25 +865,25 @@ const DeleteConfirmationModal: React.FC<{
|
|||
role="dialog"
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-lg shadow-xl p-6 sm:p-8 w-full max-w-md transform transition-all"
|
||||
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-brand-dark-blue">Confirm Deletion</h3>
|
||||
<p className="text-gray-600 my-4">
|
||||
<h3 className="text-xl font-bold text-primary-blue">Confirm Deletion</h3>
|
||||
<p className="text-black-title 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="bg-gray-200 text-gray-800 font-semibold py-2 px-4 rounded-md hover:bg-gray-300 transition-colors"
|
||||
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"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onConfirm}
|
||||
className="bg-red-600 text-white font-semibold py-2 px-4 rounded-md hover:bg-red-700 transition-colors"
|
||||
className="bg-error text-white font-semibold py-2 px-6 rounded-full hover:bg-error/90 transition-colors"
|
||||
>
|
||||
Delete Proof
|
||||
</button>
|
||||
|
|
@ -912,11 +912,11 @@ const CampaignDeleteConfirmationModal: React.FC<{
|
|||
role="dialog"
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-lg shadow-xl p-6 sm:p-8 w-full max-w-md transform transition-all"
|
||||
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-brand-dark-blue">Confirm Deletion</h3>
|
||||
<p className="text-gray-600 my-4">
|
||||
<h3 className="text-xl font-bold text-primary-blue">Confirm Deletion</h3>
|
||||
<p className="text-black-title 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.</>
|
||||
) : (
|
||||
|
|
@ -927,14 +927,14 @@ const CampaignDeleteConfirmationModal: React.FC<{
|
|||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="bg-gray-200 text-gray-800 font-semibold py-2 px-4 rounded-md hover:bg-gray-300 transition-colors"
|
||||
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"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onConfirm}
|
||||
className="bg-red-600 text-white font-semibold py-2 px-4 rounded-md hover:bg-red-700 transition-colors"
|
||||
className="bg-error text-white font-semibold py-2 px-6 rounded-full hover:bg-error/90 transition-colors"
|
||||
>
|
||||
{isBulk ? `Delete ${selectedCount} Campaign${selectedCount !== 1 ? 's' : ''}` : 'Delete Campaign'}
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue