Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import type { ShopifyConfig, WooCommerceConfig } from "$lib/types";
export type StoreWizardChannel = "shopify" | "woocommerce";
export type StoreWizardStepId = "connect" | "test" | "sync" | "done";
export const STORE_WIZARD_STEPS: readonly StoreWizardStepId[] = [
"connect",
"test",
"sync",
"done"
] as const;
export type StoreWizardEvidence = {
hasCredentials: boolean;
testOk: boolean;
hasSynced: boolean;
};
const CHANNEL_KEY = "descrybe.storeWizard.channel";
export function isStoreWizardChannel(value: string | null | undefined): value is StoreWizardChannel {
return value === "shopify" || value === "woocommerce";
}
export function readStoredWizardChannel(): StoreWizardChannel | null {
if (typeof localStorage === "undefined") return null;
try {
const raw = localStorage.getItem(CHANNEL_KEY);
return isStoreWizardChannel(raw) ? raw : null;
} catch {
return null;
}
}
export function writeStoredWizardChannel(channel: StoreWizardChannel | null): void {
if (typeof localStorage === "undefined") return;
try {
if (!channel) localStorage.removeItem(CHANNEL_KEY);
else localStorage.setItem(CHANNEL_KEY, channel);
} catch {
/* ignore quota / private mode */
}
}
export function connectorHref(
channel: StoreWizardChannel,
step: Exclude<StoreWizardStepId, "done">
): string {
const base = channel === "shopify" ? "/stores/shopify" : "/woocommerce";
const params = new URLSearchParams({ from: "wizard" });
if (step === "test") params.set("focus", "test");
if (step === "sync") params.set("focus", "sync");
return `${base}?${params.toString()}`;
}
export function evidenceFromConfig(
config: ShopifyConfig | WooCommerceConfig | null | undefined
): StoreWizardEvidence {
if (!config) {
return { hasCredentials: false, testOk: false, hasSynced: false };
}
const testStatus = String(config.last_test_status ?? "").toLowerCase();
const productMaps = Number(config.product_map_count ?? 0);
return {
hasCredentials: Boolean(config.has_credentials),
testOk: testStatus === "ok" || testStatus === "success",
hasSynced:
Boolean(config.last_synced_at) ||
Boolean(config.pending_sync) ||
productMaps > 0
};
}
/** Contiguous completed-step count (stop at first incomplete). Order matches STORE_WIZARD_STEPS. */
export function wizardCompletedCount(evidence: StoreWizardEvidence): number {
const flags = [evidence.hasCredentials, evidence.testOk, evidence.hasSynced, true];
let count = 0;
for (let i = 0; i < STORE_WIZARD_STEPS.length; i++) {
const step = STORE_WIZARD_STEPS[i];
if (step === "done") {
if (count >= 3) count = 4;
break;
}
if (!flags[i]) break;
count += 1;
}
return count;
}
export function wizardCurrentStep(evidence: StoreWizardEvidence): StoreWizardStepId {
const done = wizardCompletedCount(evidence);
if (done >= STORE_WIZARD_STEPS.length) return "done";
return STORE_WIZARD_STEPS[done] ?? "connect";
}
export function parseWizardFocus(raw: string | null | undefined): "test" | "sync" | null {
if (raw === "test" || raw === "sync") return raw;
return null;
}
export function isWizardReturn(searchParams: URLSearchParams): boolean {
return searchParams.get("from") === "wizard" || parseWizardFocus(searchParams.get("focus")) != null;
}