Expose AZ data under neutral /api/az and update frontend routes
This commit is contained in:
@@ -49,9 +49,9 @@ export default function App() {
|
||||
<Route path="/scraper-tools" element={<PrivateRoute><ScraperTools /></PrivateRoute>} />
|
||||
<Route path="/scraper-monitor" element={<PrivateRoute><ScraperMonitor /></PrivateRoute>} />
|
||||
<Route path="/scraper-schedule" element={<PrivateRoute><ScraperSchedule /></PrivateRoute>} />
|
||||
<Route path="/dutchie-az-schedule" element={<PrivateRoute><DutchieAZSchedule /></PrivateRoute>} />
|
||||
<Route path="/dutchie-az" element={<PrivateRoute><DutchieAZStores /></PrivateRoute>} />
|
||||
<Route path="/dutchie-az/stores/:id" element={<PrivateRoute><DutchieAZStoreDetail /></PrivateRoute>} />
|
||||
<Route path="/az-schedule" element={<PrivateRoute><DutchieAZSchedule /></PrivateRoute>} />
|
||||
<Route path="/az" element={<PrivateRoute><DutchieAZStores /></PrivateRoute>} />
|
||||
<Route path="/az/stores/:id" element={<PrivateRoute><DutchieAZStoreDetail /></PrivateRoute>} />
|
||||
<Route path="/api-permissions" element={<PrivateRoute><ApiPermissions /></PrivateRoute>} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
|
||||
@@ -163,18 +163,18 @@ export function Layout({ children }: LayoutProps) {
|
||||
/>
|
||||
</NavSection>
|
||||
|
||||
<NavSection title="Dutchie AZ">
|
||||
<NavSection title="AZ Data">
|
||||
<NavLink
|
||||
to="/dutchie-az"
|
||||
to="/az"
|
||||
icon={<Store className="w-4 h-4" />}
|
||||
label="AZ Stores"
|
||||
isActive={isActive('/dutchie-az', false)}
|
||||
isActive={isActive('/az', false)}
|
||||
/>
|
||||
<NavLink
|
||||
to="/dutchie-az-schedule"
|
||||
to="/az-schedule"
|
||||
icon={<Calendar className="w-4 h-4" />}
|
||||
label="AZ Schedule"
|
||||
isActive={isActive('/dutchie-az-schedule')}
|
||||
isActive={isActive('/az-schedule')}
|
||||
/>
|
||||
</NavSection>
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// DUTCHIE AZ API
|
||||
// AZ DATA API (formerly dutchie-az)
|
||||
// ============================================================
|
||||
|
||||
// Dutchie AZ Dashboard
|
||||
@@ -540,7 +540,7 @@ class ApiClient {
|
||||
failedJobCount: number;
|
||||
brandCount: number;
|
||||
categoryCount: number;
|
||||
}>('/api/dutchie-az/dashboard');
|
||||
}>('/api/az/dashboard');
|
||||
}
|
||||
|
||||
// Dutchie AZ Schedules (CRUD)
|
||||
@@ -562,7 +562,7 @@ class ApiClient {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>;
|
||||
}>('/api/dutchie-az/admin/schedules');
|
||||
}>('/api/az/admin/schedules');
|
||||
}
|
||||
|
||||
async getDutchieAZSchedule(id: number) {
|
||||
@@ -581,7 +581,7 @@ class ApiClient {
|
||||
jobConfig: Record<string, any> | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>(`/api/dutchie-az/admin/schedules/${id}`);
|
||||
}>(`/api/az/admin/schedules/${id}`);
|
||||
}
|
||||
|
||||
async createDutchieAZSchedule(data: {
|
||||
@@ -593,7 +593,7 @@ class ApiClient {
|
||||
jobConfig?: Record<string, any>;
|
||||
startImmediately?: boolean;
|
||||
}) {
|
||||
return this.request<any>('/api/dutchie-az/admin/schedules', {
|
||||
return this.request<any>('/api/az/admin/schedules', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
@@ -606,26 +606,26 @@ class ApiClient {
|
||||
jitterMinutes?: number;
|
||||
jobConfig?: Record<string, any>;
|
||||
}) {
|
||||
return this.request<any>(`/api/dutchie-az/admin/schedules/${id}`, {
|
||||
return this.request<any>(`/api/az/admin/schedules/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async deleteDutchieAZSchedule(id: number) {
|
||||
return this.request<{ success: boolean; message: string }>(`/api/dutchie-az/admin/schedules/${id}`, {
|
||||
return this.request<{ success: boolean; message: string }>(`/api/az/admin/schedules/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
async triggerDutchieAZSchedule(id: number) {
|
||||
return this.request<{ success: boolean; message: string }>(`/api/dutchie-az/admin/schedules/${id}/trigger`, {
|
||||
return this.request<{ success: boolean; message: string }>(`/api/az/admin/schedules/${id}/trigger`, {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
async initDutchieAZSchedules() {
|
||||
return this.request<{ success: boolean; schedules: any[] }>('/api/dutchie-az/admin/schedules/init', {
|
||||
return this.request<{ success: boolean; schedules: any[] }>('/api/az/admin/schedules/init', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
@@ -636,7 +636,7 @@ class ApiClient {
|
||||
if (limit) params.append('limit', limit.toString());
|
||||
if (offset) params.append('offset', offset.toString());
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return this.request<{ logs: any[]; total: number }>(`/api/dutchie-az/admin/schedules/${scheduleId}/logs${queryString}`);
|
||||
return this.request<{ logs: any[]; total: number }>(`/api/az/admin/schedules/${scheduleId}/logs${queryString}`);
|
||||
}
|
||||
|
||||
async getDutchieAZRunLogs(options?: { scheduleId?: number; jobName?: string; limit?: number; offset?: number }) {
|
||||
@@ -646,28 +646,28 @@ class ApiClient {
|
||||
if (options?.limit) params.append('limit', options.limit.toString());
|
||||
if (options?.offset) params.append('offset', options.offset.toString());
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return this.request<{ logs: any[]; total: number }>(`/api/dutchie-az/admin/run-logs${queryString}`);
|
||||
return this.request<{ logs: any[]; total: number }>(`/api/az/admin/run-logs${queryString}`);
|
||||
}
|
||||
|
||||
// Dutchie AZ Scheduler Control
|
||||
async getDutchieAZSchedulerStatus() {
|
||||
return this.request<{ running: boolean; pollIntervalMs: number }>('/api/dutchie-az/admin/scheduler/status');
|
||||
return this.request<{ running: boolean; pollIntervalMs: number }>('/api/az/admin/scheduler/status');
|
||||
}
|
||||
|
||||
async startDutchieAZScheduler() {
|
||||
return this.request<{ success: boolean; message: string }>('/api/dutchie-az/admin/scheduler/start', {
|
||||
return this.request<{ success: boolean; message: string }>('/api/az/admin/scheduler/start', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
async stopDutchieAZScheduler() {
|
||||
return this.request<{ success: boolean; message: string }>('/api/dutchie-az/admin/scheduler/stop', {
|
||||
return this.request<{ success: boolean; message: string }>('/api/az/admin/scheduler/stop', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
async triggerDutchieAZImmediateCrawl() {
|
||||
return this.request<{ success: boolean; message: string }>('/api/dutchie-az/admin/scheduler/trigger', {
|
||||
return this.request<{ success: boolean; message: string }>('/api/az/admin/scheduler/trigger', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
@@ -680,11 +680,11 @@ class ApiClient {
|
||||
if (params?.limit) searchParams.append('limit', params.limit.toString());
|
||||
if (params?.offset) searchParams.append('offset', params.offset.toString());
|
||||
const queryString = searchParams.toString() ? `?${searchParams.toString()}` : '';
|
||||
return this.request<{ stores: any[]; total: number }>(`/api/dutchie-az/stores${queryString}`);
|
||||
return this.request<{ stores: any[]; total: number }>(`/api/az/stores${queryString}`);
|
||||
}
|
||||
|
||||
async getDutchieAZStore(id: number) {
|
||||
return this.request<any>(`/api/dutchie-az/stores/${id}`);
|
||||
return this.request<any>(`/api/az/stores/${id}`);
|
||||
}
|
||||
|
||||
async getDutchieAZStoreSummary(id: number) {
|
||||
@@ -700,7 +700,7 @@ class ApiClient {
|
||||
brandCount: number;
|
||||
categoryCount: number;
|
||||
lastCrawl: any | null;
|
||||
}>(`/api/dutchie-az/stores/${id}/summary`);
|
||||
}>(`/api/az/stores/${id}/summary`);
|
||||
}
|
||||
|
||||
async getDutchieAZStoreProducts(id: number, params?: {
|
||||
@@ -752,19 +752,19 @@ class ApiClient {
|
||||
total: number;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}>(`/api/dutchie-az/stores/${id}/products${queryString}`);
|
||||
}>(`/api/az/stores/${id}/products${queryString}`);
|
||||
}
|
||||
|
||||
async getDutchieAZStoreBrands(id: number) {
|
||||
return this.request<{
|
||||
brands: Array<{ brand: string; product_count: number }>;
|
||||
}>(`/api/dutchie-az/stores/${id}/brands`);
|
||||
}>(`/api/az/stores/${id}/brands`);
|
||||
}
|
||||
|
||||
async getDutchieAZStoreCategories(id: number) {
|
||||
return this.request<{
|
||||
categories: Array<{ type: string; subcategory: string; product_count: number }>;
|
||||
}>(`/api/dutchie-az/stores/${id}/categories`);
|
||||
}>(`/api/az/stores/${id}/categories`);
|
||||
}
|
||||
|
||||
// Dutchie AZ Debug
|
||||
@@ -795,7 +795,7 @@ class ApiClient {
|
||||
dispensary_name: string;
|
||||
crawled_at: string;
|
||||
}>;
|
||||
}>('/api/dutchie-az/debug/summary');
|
||||
}>('/api/az/debug/summary');
|
||||
}
|
||||
|
||||
async getDutchieAZDebugStore(id: number) {
|
||||
@@ -823,11 +823,11 @@ class ApiClient {
|
||||
outOfStock: any[];
|
||||
};
|
||||
categories: Array<{ type: string; subcategory: string; count: string }>;
|
||||
}>(`/api/dutchie-az/debug/store/${id}`);
|
||||
}>(`/api/az/debug/store/${id}`);
|
||||
}
|
||||
|
||||
async triggerDutchieAZCrawl(id: number, options?: { pricingType?: string; useBothModes?: boolean }) {
|
||||
return this.request<any>(`/api/dutchie-az/admin/crawl/${id}`, {
|
||||
return this.request<any>(`/api/az/admin/crawl/${id}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(options || {}),
|
||||
});
|
||||
|
||||
@@ -114,7 +114,14 @@ export function DutchieAZSchedule() {
|
||||
|
||||
const handleUpdateSchedule = async (id: number, updates: Partial<JobSchedule>) => {
|
||||
try {
|
||||
await api.updateDutchieAZSchedule(id, updates);
|
||||
const payload = {
|
||||
description: updates.description ?? undefined,
|
||||
enabled: updates.enabled,
|
||||
baseIntervalMinutes: updates.baseIntervalMinutes,
|
||||
jitterMinutes: updates.jitterMinutes,
|
||||
jobConfig: updates.jobConfig ?? undefined,
|
||||
};
|
||||
await api.updateDutchieAZSchedule(id, payload);
|
||||
setEditingSchedule(null);
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
|
||||
@@ -140,11 +140,11 @@ export function DutchieAZStoreDetail() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<button
|
||||
onClick={() => navigate('/dutchie-az')}
|
||||
onClick={() => navigate('/az')}
|
||||
className="flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
Back to Dutchie AZ Stores
|
||||
Back to AZ Stores
|
||||
</button>
|
||||
|
||||
{/* Update Button */}
|
||||
|
||||
@@ -175,7 +175,7 @@ export function DutchieAZStores() {
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => navigate(`/dutchie-az/stores/${store.id}`)}
|
||||
onClick={() => navigate(`/az/stores/${store.id}`)}
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={!store.platform_dispensary_id}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user