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:
@@ -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 {
|
||||||
|
|||||||
2
cannaiq/dist/index.html
vendored
2
cannaiq/dist/index.html
vendored
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user