UserManagement now calls refresh() on the global UserContext when the current user's agency or role is changed, so downstream consumers (e.g. CreateCampaignModal) immediately reflect the update. CreateProjectModal now reads the Agency and Agency Lead fields from the current user's profile instead of hardcoding "OLIVER Agency" and "Steve O'Donoghue". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
165 lines
7.6 KiB
TypeScript
Executable file
165 lines
7.6 KiB
TypeScript
Executable file
import React, { useState, useEffect } from 'react';
|
|
import { XIcon } from './icons/XIcon';
|
|
import { useUser } from '../contexts/UserContext';
|
|
|
|
interface CreateProjectModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onAddProject: (projectData: {
|
|
name: string;
|
|
workfrontId: string;
|
|
clientLead: string;
|
|
}) => void;
|
|
}
|
|
|
|
export const CreateProjectModal: React.FC<CreateProjectModalProps> = ({ isOpen, onClose, onAddProject }) => {
|
|
const { user } = useUser();
|
|
const [name, setName] = useState('');
|
|
const [workfrontId, setWorkfrontId] = useState('');
|
|
const [clientLead, setClientLead] = useState('');
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Reset form when modal opens
|
|
if (isOpen) {
|
|
setName('');
|
|
setWorkfrontId('');
|
|
setClientLead('');
|
|
setError(null);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const validateWorkfrontId = (id: string): boolean => {
|
|
const isValid = /^#WF_\d+$/.test(id);
|
|
if (!isValid && id.length > 0) {
|
|
setError("Workfront Project ID must be in the format '#WF_12345'");
|
|
} else {
|
|
setError(null);
|
|
}
|
|
return isValid || id.length === 0;
|
|
};
|
|
|
|
const handleWorkfrontIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const newId = e.target.value;
|
|
setWorkfrontId(newId);
|
|
validateWorkfrontId(newId);
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
const isIdValidOnSubmit = /^#WF_\d+$/.test(workfrontId);
|
|
|
|
if (!name.trim() || !clientLead.trim() || !workfrontId.trim()) {
|
|
return;
|
|
}
|
|
|
|
if (!isIdValidOnSubmit) {
|
|
setError("Workfront Project ID must be in the format '#WF_12345'");
|
|
return;
|
|
}
|
|
|
|
setError(null);
|
|
onAddProject({ name, workfrontId, clientLead });
|
|
onClose();
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
const isFormInvalid = !name.trim() || !workfrontId.trim() || !clientLead.trim();
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center z-50 transition-opacity duration-300"
|
|
onClick={onClose}
|
|
aria-modal="true"
|
|
role="dialog"
|
|
>
|
|
<div
|
|
className="bg-white rounded-[10px] shadow-xl p-6 sm:p-8 w-full max-w-lg transform transition-all"
|
|
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">
|
|
<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>
|
|
<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"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="workfront-id" className="block text-sm font-medium text-black-title">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'}`}
|
|
placeholder="#WF_12345"
|
|
required
|
|
aria-invalid={!!error}
|
|
aria-describedby="workfront-id-error"
|
|
/>
|
|
{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>
|
|
<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"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-black-title">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"
|
|
disabled
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-black-title">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"
|
|
disabled
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="mt-8 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 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"
|
|
disabled={isFormInvalid || !!error}
|
|
>
|
|
Create Project
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|