fix: State dropdown and locked platform in schedule modal

- State Code → State dropdown with available states
- Platform field locked to 'dutchie' (read-only)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-13 01:26:58 -07:00
parent e826a4dd3e
commit 94ebbb2497
3 changed files with 46 additions and 17 deletions

View File

@@ -308,6 +308,31 @@ router.get('/retry-stats', async (req, res) => {
} }
}); });
// Mark all proxies as active
router.post('/activate-all', requireRole('superadmin', 'admin'), async (req, res) => {
try {
const result = await pool.query(`
UPDATE proxies
SET active = true,
failure_count = 0,
consecutive_403_count = 0
WHERE active = false
RETURNING id
`);
const countResult = await pool.query(`SELECT COUNT(*) as total FROM proxies`);
res.json({
message: `Activated ${result.rowCount} proxies`,
activated: result.rowCount,
total: parseInt(countResult.rows[0].total)
});
} catch (error) {
console.error('Error activating proxies:', error);
res.status(500).json({ error: 'Failed to activate proxies' });
}
});
// Manually re-enable proxies that have passed their retry interval // Manually re-enable proxies that have passed their retry interval
router.post('/reenable-failed', requireRole('superadmin', 'admin'), async (req, res) => { router.post('/reenable-failed', requireRole('superadmin', 'admin'), async (req, res) => {
try { try {

View File

@@ -7,7 +7,7 @@
<title>CannaIQ - Cannabis Menu Intelligence Platform</title> <title>CannaIQ - Cannabis Menu Intelligence Platform</title>
<meta name="description" content="CannaIQ provides real-time cannabis dispensary menu data, product tracking, and analytics for dispensaries across Arizona." /> <meta name="description" content="CannaIQ provides real-time cannabis dispensary menu data, product tracking, and analytics for dispensaries across Arizona." />
<meta name="keywords" content="cannabis, dispensary, menu, products, analytics, Arizona" /> <meta name="keywords" content="cannabis, dispensary, menu, products, analytics, Arizona" />
<script type="module" crossorigin src="/assets/index-5TyJIiu6.js"></script> <script type="module" crossorigin src="/assets/index-onY4oipq.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B0KNyXCG.css"> <link rel="stylesheet" crossorigin href="/assets/index-B0KNyXCG.css">
</head> </head>
<body> <body>

View File

@@ -410,13 +410,21 @@ function ScheduleEditModal({ isOpen, schedule, onClose, onSave }: ScheduleEditMo
const [intervalHours, setIntervalHours] = useState(4); const [intervalHours, setIntervalHours] = useState(4);
const [priority, setPriority] = useState(0); const [priority, setPriority] = useState(0);
const [stateCode, setStateCode] = useState(''); const [stateCode, setStateCode] = useState('');
const [platform, setPlatform] = useState('dutchie'); const [platform] = useState('dutchie'); // Fixed to dutchie only
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [availableStates, setAvailableStates] = useState<string[]>([]);
const isNew = !schedule; const isNew = !schedule;
const isImmutable = schedule?.is_immutable ?? false; const isImmutable = schedule?.is_immutable ?? false;
// Fetch available states on mount
useEffect(() => {
api.getOrchestratorStates().then(data => {
setAvailableStates(data.states?.map((s: any) => s.state) || []);
}).catch(console.error);
}, []);
useEffect(() => { useEffect(() => {
if (schedule) { if (schedule) {
setName(schedule.name); setName(schedule.name);
@@ -426,7 +434,6 @@ function ScheduleEditModal({ isOpen, schedule, onClose, onSave }: ScheduleEditMo
setIntervalHours(schedule.interval_hours); setIntervalHours(schedule.interval_hours);
setPriority(schedule.priority); setPriority(schedule.priority);
setStateCode(schedule.state_code || ''); setStateCode(schedule.state_code || '');
setPlatform(schedule.platform || 'dutchie');
} else { } else {
// Reset for new schedule // Reset for new schedule
setName(''); setName('');
@@ -436,7 +443,6 @@ function ScheduleEditModal({ isOpen, schedule, onClose, onSave }: ScheduleEditMo
setIntervalHours(4); setIntervalHours(4);
setPriority(0); setPriority(0);
setStateCode(''); setStateCode('');
setPlatform('dutchie');
} }
setError(null); setError(null);
}, [schedule, isOpen]); }, [schedule, isOpen]);
@@ -587,30 +593,28 @@ function ScheduleEditModal({ isOpen, schedule, onClose, onSave }: ScheduleEditMo
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1">State Code</label> <label className="block text-sm font-medium text-gray-700 mb-1">State</label>
<input <select
type="text"
value={stateCode} value={stateCode}
onChange={(e) => setStateCode(e.target.value.toUpperCase())} onChange={(e) => setStateCode(e.target.value)}
placeholder="e.g., AZ"
maxLength={2}
disabled={isImmutable} disabled={isImmutable}
className={`w-full px-3 py-2 border border-gray-200 rounded-lg ${ className={`w-full px-3 py-2 border border-gray-200 rounded-lg ${
isImmutable ? 'bg-gray-100 text-gray-500 cursor-not-allowed' : '' isImmutable ? 'bg-gray-100 text-gray-500 cursor-not-allowed' : ''
}`} }`}
/> >
<option value="">All States</option>
{availableStates.map(state => (
<option key={state} value={state}>{state}</option>
))}
</select>
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1">Platform</label> <label className="block text-sm font-medium text-gray-700 mb-1">Platform</label>
<input <input
type="text" type="text"
value={platform} value={platform}
onChange={(e) => setPlatform(e.target.value)} disabled
placeholder="e.g., dutchie" className="w-full px-3 py-2 border border-gray-200 rounded-lg bg-gray-100 text-gray-500 cursor-not-allowed"
disabled={isImmutable}
className={`w-full px-3 py-2 border border-gray-200 rounded-lg ${
isImmutable ? 'bg-gray-100 text-gray-500 cursor-not-allowed' : ''
}`}
/> />
</div> </div>
</div> </div>