Make Workfront Campaign ID optional and propagate to proofs

The Workfront Campaign ID field is now optional when creating campaigns.
When provided, it is used as the base for proof workfront IDs instead of
generating random ones.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael 2026-01-08 09:49:23 -06:00
parent abfcb6aae2
commit 8038e4014e
2 changed files with 24 additions and 12 deletions

View file

@ -5,7 +5,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.models.models import Proof, ProofVersion
from app.models.models import Campaign, Proof, ProofVersion
class ProofRepository:
@ -198,9 +198,17 @@ class ProofRepository:
latest_version = await self.get_latest_version_number(proof.id)
new_version_number = latest_version + 1
# Generate workfront ID
# Get campaign's workfront_id to use as base for proof workfront IDs
campaign_result = await self.session.execute(
select(Campaign).where(Campaign.id == campaign_id)
)
campaign = campaign_result.scalar_one_or_none()
campaign_workfront_id = campaign.workfront_id if campaign else None
# Use campaign's workfront_id as base, or proof's existing workfront_id,
# or generate random one as fallback
import random
base_id = proof.workfront_id or f"#WF_{random.randint(10000, 99999)}"
base_id = proof.workfront_id or campaign_workfront_id or f"#WF_{random.randint(10000, 99999)}"
version_workfront_id = f"{base_id.split('-V')[0]}-V{new_version_number}"
if not proof.workfront_id:

View file

@ -37,13 +37,18 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
}, [isOpen]);
const validateWorkfrontId = (id: string): boolean => {
// Empty is valid (field is optional)
if (id.length === 0) {
setError(null);
return true;
}
const isValid = /^#WF_\d+$/.test(id);
if (!isValid && id.length > 0) {
if (!isValid) {
setError("Workfront Campaign ID must be in the format '#WF_12345'");
} else {
setError(null);
}
return isValid || id.length === 0;
return isValid;
};
const handleWorkfrontIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -54,25 +59,25 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const isIdValidOnSubmit = /^#WF_\d+$/.test(workfrontId);
if (!name.trim() || !clientLead.trim() || !agencyLead.trim() || !workfrontId.trim() || !selectedBrandGuideline.trim()) {
if (!name.trim() || !clientLead.trim() || !agencyLead.trim() || !selectedBrandGuideline.trim()) {
return;
}
if (!isIdValidOnSubmit) {
// Validate workfrontId format only if provided
if (workfrontId.trim() && !/^#WF_\d+$/.test(workfrontId)) {
setError("Workfront Campaign ID must be in the format '#WF_12345'");
return;
}
setError(null);
onAddCampaign({ name, workfrontId, clientLead, agencyLead, brandGuidelines: selectedBrandGuideline });
onAddCampaign({ name, workfrontId: workfrontId.trim(), clientLead, agencyLead, brandGuidelines: selectedBrandGuideline });
onClose();
};
if (!isOpen) return null;
const isFormInvalid = !name.trim() || !workfrontId.trim() || !clientLead.trim() || !agencyLead.trim() || !selectedBrandGuideline.trim();
const isFormInvalid = !name.trim() || !clientLead.trim() || !agencyLead.trim() || !selectedBrandGuideline.trim();
return (
<div
@ -120,7 +125,7 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
</select>
</div>
<div>
<label htmlFor="workfront-id" className="block text-sm font-medium text-gray-700">Workfront Campaign ID</label>
<label htmlFor="workfront-id" className="block text-sm font-medium text-gray-700">Workfront Campaign ID <span className="text-gray-400 font-normal">(Optional)</span></label>
<input
type="text"
id="workfront-id"
@ -128,7 +133,6 @@ export const CreateCampaignModal: React.FC<CreateCampaignModalProps> = ({ isOpen
onChange={handleWorkfrontIdChange}
className={`mt-1 block w-full p-2 border rounded-md shadow-sm focus:ring-brand-accent focus:border-brand-accent transition bg-white text-gray-900 placeholder:text-gray-400 ${error ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : 'border-gray-300'}`}
placeholder="#WF_12345"
required
aria-invalid={!!error}
aria-describedby="workfront-id-error"
/>