Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- 新增 saas-industry.ts mixin: listIndustries/getIndustryFullConfig/getMyIndustries - 新增 saas-types 行业类型: IndustryInfo/IndustryFullConfig/AccountIndustryItem - 新增 industryStore.ts: Zustand store + localStorage persist + Rust 注入 - 新增 viking_load_industry_keywords Tauri 命令: 接收 JSON configs → 全局存储 - 前端 bootstrap 后自动拉取行业配置并推送到 ButlerRouter
126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
/**
|
|
* Industry Store — Zustand store for industry configuration
|
|
*
|
|
* Manages industry configs fetched from SaaS, cached locally for
|
|
* offline fallback. Injected into ButlerRouter for dynamic keyword routing.
|
|
*/
|
|
|
|
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
import type { IndustryFullConfig, AccountIndustryItem } from '../lib/saas-client';
|
|
import { saasClient } from '../lib/saas-client';
|
|
|
|
// ============ Types ============
|
|
|
|
interface IndustryState {
|
|
/** User's authorized industries */
|
|
accountIndustries: AccountIndustryItem[];
|
|
/** Full configs for authorized industries (keyed by industry id) */
|
|
configs: Record<string, IndustryFullConfig>;
|
|
/** Last sync timestamp */
|
|
lastSynced: string | null;
|
|
/** Loading state */
|
|
isLoading: boolean;
|
|
/** Error message */
|
|
error: string | null;
|
|
}
|
|
|
|
interface IndustryActions {
|
|
/** Fetch user industries + full configs from SaaS */
|
|
fetchIndustries: () => Promise<void>;
|
|
/** Get all loaded industry keywords (for trigger system) */
|
|
getAllKeywords: () => string[];
|
|
/** Clear all data */
|
|
clear: () => void;
|
|
}
|
|
|
|
type IndustryStore = IndustryState & IndustryActions;
|
|
|
|
// ============ Store ============
|
|
|
|
export const useIndustryStore = create<IndustryStore>()(
|
|
persist(
|
|
(set, get) => ({
|
|
accountIndustries: [],
|
|
configs: {},
|
|
lastSynced: null,
|
|
isLoading: false,
|
|
error: null,
|
|
|
|
fetchIndustries: async () => {
|
|
set({ isLoading: true, error: null });
|
|
try {
|
|
// Step 1: Get user's authorized industries
|
|
const accountIndustries = await saasClient.getMyIndustries();
|
|
|
|
// Step 2: Fetch full config for each authorized industry
|
|
const configs: Record<string, IndustryFullConfig> = {};
|
|
for (const item of accountIndustries) {
|
|
try {
|
|
const fullConfig = await saasClient.getIndustryFullConfig(item.industry_id);
|
|
configs[item.industry_id] = fullConfig;
|
|
} catch (err) {
|
|
// Non-fatal: one industry failing shouldn't block others
|
|
console.warn(`[industryStore] Failed to fetch config for ${item.industry_id}:`, err);
|
|
}
|
|
}
|
|
|
|
set({
|
|
accountIndustries,
|
|
configs,
|
|
lastSynced: new Date().toISOString(),
|
|
isLoading: false,
|
|
});
|
|
|
|
// Step 3: Push to Rust ButlerRouter via Tauri invoke
|
|
try {
|
|
const { invoke } = await import('@tauri-apps/api/core');
|
|
const industryConfigs = Object.values(configs).map((c) => ({
|
|
id: c.id,
|
|
name: c.name,
|
|
keywords: c.keywords,
|
|
system_prompt: c.system_prompt,
|
|
}));
|
|
await invoke('viking_load_industry_keywords', { configs: JSON.stringify(industryConfigs) });
|
|
} catch (err) {
|
|
// Tauri not available (browser mode) — ignore
|
|
}
|
|
} catch (err) {
|
|
set({
|
|
error: err instanceof Error ? err.message : String(err),
|
|
isLoading: false,
|
|
});
|
|
}
|
|
},
|
|
|
|
getAllKeywords: () => {
|
|
const { configs } = get();
|
|
const allKeywords: string[] = [];
|
|
for (const config of Object.values(configs)) {
|
|
allKeywords.push(...config.keywords);
|
|
}
|
|
return allKeywords;
|
|
},
|
|
|
|
clear: () => {
|
|
set({
|
|
accountIndustries: [],
|
|
configs: {},
|
|
lastSynced: null,
|
|
isLoading: false,
|
|
error: null,
|
|
});
|
|
},
|
|
}),
|
|
{
|
|
name: 'zclaw-industry-store',
|
|
// Only persist configs and industries (not loading/error state)
|
|
partialize: (state) => ({
|
|
accountIndustries: state.accountIndustries,
|
|
configs: state.configs,
|
|
lastSynced: state.lastSynced,
|
|
}),
|
|
},
|
|
),
|
|
);
|