Files
community-rule/lib/create/communityChipIds.ts
T
adilalloandCursor bcbc6f30d9 Import the remade decks on stable ids and recommend one card per job, so a saved draft still loads after a chip is renamed.
The real-group profiles stay as a test, so a change that moves a pick fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-04 14:22:54 -07:00

129 lines
3.6 KiB
TypeScript

import type { CommunityStructureChipSnapshotRow } from "../../app/(app)/create/types";
import type { Stage } from "./deckTypes";
/**
* Stable chip ids for Create Community. Old drafts stored a 1-based position.
* Enterprise and Larger organization both load as Established.
*/
const SIZE_BY_POSITION = [
"oneMember",
"twoToFive",
"sixToTwelve",
"thirteenToOneHundred",
"oneHundredToOneHundredK",
] as const;
const ORG_BY_POSITION = [
"workersCoop",
"mutualAid",
"openSource",
"nonprofit",
"forProfit",
"dao",
] as const;
const SCALE_BY_POSITION = ["local", "regional", "national", "global"] as const;
const MATURITY_BY_POSITION = [
"earlyStage",
"growthStage",
"established",
"established",
] as const;
const MATURITY_LABEL_ALIASES: Record<string, Stage> = {
"Early stage": "earlyStage",
"Growth stage": "growthStage",
Established: "established",
Enterprise: "established",
"Larger organization": "established",
};
const ORG_LABEL_ALIASES: Record<string, string> = {
"Worker cooperative": "workersCoop",
"Worker co-op": "workersCoop",
"Mutual aid": "mutualAid",
"Open source project": "openSource",
Nonprofit: "nonprofit",
"For profit business": "forProfit",
DAO: "dao",
};
function fromPosition(
id: string,
positions: readonly string[],
): string | null {
if (!/^\d+$/.test(id)) return null;
const index = Number(id) - 1;
if (index < 0 || index >= positions.length) return null;
return positions[index] ?? null;
}
export function resolveSizeChipId(id: string): string {
return fromPosition(id, SIZE_BY_POSITION) ?? id;
}
export function resolveOrgChipId(id: string, label?: string): string {
if (label && ORG_LABEL_ALIASES[label]) return ORG_LABEL_ALIASES[label];
return fromPosition(id, ORG_BY_POSITION) ?? id;
}
export function resolveScaleChipId(id: string): string {
return fromPosition(id, SCALE_BY_POSITION) ?? id;
}
export function resolveMaturityChipId(id: string, label?: string): string {
if (label && MATURITY_LABEL_ALIASES[label]) return MATURITY_LABEL_ALIASES[label];
if (id === "enterprise" || id === "larger-organization") return "established";
return fromPosition(id, MATURITY_BY_POSITION) ?? id;
}
export function resolveChipIdList(
ids: readonly string[] | undefined,
resolve: (id: string) => string,
): string[] | undefined {
if (!ids) return undefined;
const out: string[] = [];
const seen = new Set<string>();
for (const id of ids) {
const next = resolve(id);
if (!next || seen.has(next)) continue;
seen.add(next);
out.push(next);
}
return out;
}
type PresetChip = { id: string; label: string };
/**
* Rewrite a saved chip snapshot onto the current preset ids and labels.
* Custom rows stay. Several maturity answers stay, as distinct ids.
*/
export function migrateChipSnapshot(
rows: CommunityStructureChipSnapshotRow[] | undefined,
presets: readonly PresetChip[],
resolve: (id: string, label?: string) => string,
): CommunityStructureChipSnapshotRow[] | undefined {
if (!rows || rows.length === 0) return rows;
const presetIds = new Set(presets.map((p) => p.id));
const selected = new Set<string>();
const customs: CommunityStructureChipSnapshotRow[] = [];
for (const row of rows) {
const nextId = resolve(row.id, row.label);
if (presetIds.has(nextId)) {
if (row.state === "selected") selected.add(nextId);
continue;
}
customs.push({ ...row, id: nextId });
}
return [
...presets.map((preset) => ({
id: preset.id,
label: preset.label,
state: selected.has(preset.id) ? "selected" : "unselected",
})),
...customs,
];
}