Files
WebForm-mw/config/form.ts
Joel Brock 04e69ca04c Header: show Civi stage label prominently; restore current_stage readonly in Stage 0
- New STAGE_OPTION_GROUP_ID constant (=75) added to config and consumed
  by both the in-card readonly and the header. Because the field carries
  optionGroupId, /api/data auto-includes the Stage option group in its
  parallel fetch (via the existing optionGroupIds derivation).
- SubmissionContextHeader: dropped the small inline 'Stage Organizing'
  line. Replaced with an uppercase 'Current stage' eyebrow next to the
  progress dots, followed by a display-font 20/24px leaf-toned label
  underneath. Resolves the stored option value ('Organizing') to its
  Civi option label ('Stage 1 — Convene & Prepare') via the new
  resolveStageLabel helper; falls back to the raw value if the option
  group hasn't loaded.
- Stage 0 'Check-in (organizing)' section: re-added a current_stage
  readonly field at the top. With optionGroupId set, FieldRenderer's
  readonly branch resolves the value to the Civi label for display.
2026-05-13 12:09:24 -07:00

669 lines
25 KiB
TypeScript

/**
* Form definition for the Org Engagement Check-in form.
*
* Field references and types are sourced from the live CiviCRM DEV
* (`client.crm.fci.coop`) inventory pulled 2026-05-09 via APIv4
* `CustomField.get` filtered to the six custom groups bound to the
* `Check-in (organizing)` activity type:
* - Check_in_data__organizing_ (Stage 0)
* - Stage_1, Stage_2, Stage_3, Stage_4, Stage_5
*
* `civiField` uses APIv4's `<group_name>.<field_name>` syntax.
*
* Fields with `optionGroupId` set have their `options` populated at runtime
* via `/api/data` — the server fetches OptionValue rows for those option
* groups and embeds them in the FormDataPayload. The hardcoded `options`
* arrays here are placeholder fallbacks if the live fetch fails.
*/
import type { FieldConfig, FormConfig, StageSectionConfig, VisibilityRule } from "@/types/form";
// Active stage values (stored values in CiviCRM's `Stage` option group).
const S = {
Inquiry: "Inquiry",
Organizing: "Organizing",
Feasibility: "Feasibility",
BusinessFeasibility: "Business feasibility",
StoreImplementation: "Store Implementation",
Stabilize: "Stabilize newly opened co-op",
} as const;
const visibleAtOrAfter = (...stages: string[]): VisibilityRule => ({
field: "current_stage",
op: "in",
values: stages,
});
/**
* Option group ID for the `Stage` option group on `client.crm.fci.coop`.
* Used to resolve the current stage value (e.g. "Organizing") to its Civi
* label (e.g. "Stage 1 — Convene & Prepare") for display.
*/
export const STAGE_OPTION_GROUP_ID = 75;
// CiviCRM custom group machine names per the APIv4 inventory.
const G0 = "Check_in_data__organizing_";
const G1 = "Stage_1";
const G2 = "Stage_2";
const G3 = "Stage_3";
const G4 = "Stage_4";
const G5 = "Stage_5";
// Stage 0 — Check-in (always visible)
const stage0: StageSectionConfig = {
rank: 0,
id: "stage_0",
label: "Check-in (organizing)",
intro:
"Core check-in fields. These are visible at every stage and capture the data we follow over time across the lifecycle of the co-op.",
fields: [
// current_stage is a UI-only readonly field. Its value is the Civi
// option *value* (e.g. "Organizing") sourced by /api/data from the
// most recent stage-bearing Check-in (organizing) activity. Setting
// `optionGroupId` makes the readonly renderer resolve the value to
// its Civi option *label* (e.g. "Stage 1 — Convene & Prepare") for
// display. The form never writes Stage back to the activity.
{
name: "current_stage",
label: "Current stage",
type: "readonly",
optionGroupId: STAGE_OPTION_GROUP_ID,
},
{
name: "Peer_Group_Participation",
label: "Peer Group Participation",
type: "select",
civiField: `${G0}.Peer_Group_Participation`,
optionGroupId: 140,
help: "Is this co-op currently participating in Peer Learning Groups?",
visibleWhen: []
},
{
name: "Internal_Startup_Assessment",
label: "Internal Startup Assessment",
type: "select",
civiField: `${G0}.Internal_Startup_Assessment`,
optionGroupId: 132,
visibleWhen: []
},
{
name: "Internal_Startup_Assessment_Date",
label: "Internal Startup Assessment Date",
type: "date",
civiField: `${G0}.Internal_Startup_Assessment_Date`,
help: "Date of the latest Internal Startup Assessment rating.",
visibleWhen: []
},
{
name: "Member_Goal_for_current_Stage",
label: "Member Goal for current Stage",
type: "number",
civiField: `${G0}.Member_Goal_for_current_Stage`,
help: "What is your member / owner goal for your current co-op development Stage?",
},
{
name: "Members__current_",
label: "Members (current)",
type: "number",
civiField: `${G0}.Members__current_`,
help: "How many paid members does the co-op currently have? Include partially paid (e.g. installment plans).",
},
{
name: "Total_members_at_opening",
label: "Total members at opening",
type: "number",
civiField: `${G0}.Total_members_at_opening`,
visibleWhen: visibleAtOrAfter(S.Stabilize),
},
// {
// name: "Volunteers_Helping_In_Store",
// label: "Volunteers Helping In Store",
// type: "select",
// civiField: `${G0}.Volunteers_Helping_In_Store`,
// optionGroupId: 141,
// help: "Does this co-op use volunteers for day-to-day operations? (Not occasional events.)",
// },
// {
// name: "Work_from_Members",
// label: "Work from Members",
// type: "select",
// civiField: `${G0}.Work_from_Members`,
// optionGroupId: 142,
// help: "Does this co-op require work from members (e.g. annual hours)?",
// },
{
name: "Total_square_ft",
label: "Total square ft",
type: "number",
civiField: `${G0}.Total_square_ft`,
step: 1,
visibleWhen: visibleAtOrAfter(S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
},
{
name: "Retail_sq_ft",
label: "Retail sq ft",
type: "number",
civiField: `${G0}.Retail_sq_ft`,
step: 1,
visibleWhen: visibleAtOrAfter(S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
},
// {
// name: "Latest_Sources_and_Uses_doc",
// label: "Latest Sources and Uses doc",
// type: "file",
// civiField: `${G0}.Latest_Sources_and_Uses_doc`,
// },
// {
// name: "Latest_Pro_Forma_doc",
// label: "Latest Pro Forma doc",
// type: "file",
// civiField: `${G0}.Latest_Pro_Forma_doc`,
// },
// { name: "Projected_Year_1_Sales", label: "Projected Year 1 Sales", type: "currency", civiField: `${G0}.Projected_Year_1_Sales` },
// { name: "Projected_Year_2_Sales", label: "Projected Year 2 Sales", type: "currency", civiField: `${G0}.Projected_Year_2_Sales` },
// { name: "Projected_Year_3_Sales", label: "Projected Year 3 Sales", type: "currency", civiField: `${G0}.Projected_Year_3_Sales` },
// {
// name: "Projected_sales_at_maturity",
// label: "Projected sales once established",
// type: "currency",
// civiField: `${G0}.Projected_sales_at_maturity`,
// },
{
name: "FTEs",
label: "Projected FTEs",
type: "number",
visibleWhen: visibleAtOrAfter(S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
civiField: `${G0}.FTEs`,
step: 0.1,
help: "Number of full-time equivalent (FTE) employees planned to work at the store.",
},
{
name: "Actual_FTE",
label: "Actual FTE",
type: "number",
civiField: `${G0}.Actual_FTE`,
step: 0.1,
visibleWhen: visibleAtOrAfter(S.Stabilize),
},
{
name: "Total_cost_of_project",
label: "Total cost of project",
type: "currency",
visibleWhen: visibleAtOrAfter(S.Stabilize),
civiField: `${G0}.Total_cost_of_project`,
},
{
name: "Member_equity_total",
label: "Member equity total needed",
type: "currency",
civiField: `${G0}.Member_equity_total`
},
{
name: "Member_equity_raised",
label: "Member equity raised",
type: "currency",
visibleWhen: visibleAtOrAfter(S.Stabilize),
civiField: `${G0}.Member_equity_raised`
},
{
name: "Member_loans_total",
label: "Member loans total needed",
type: "currency",
civiField: `${G0}.Member_loans_total`
},
{
name: "Member_loans_raised",
label: "Member loans raised",
type: "currency",
visibleWhen: visibleAtOrAfter(S.Stabilize),
civiField: `${G0}.Member_loans_raised`
},
// {
// name: "Member_preferred_shares_total",
// label: "Member preferred shares total needed",
// type: "currency",
// civiField: `${G0}.Member_preferred_shares_total`,
// },
{
name: "Member_preferred_shares_raised",
label: "Member preferred shares raised",
type: "currency",
visibleWhen: visibleAtOrAfter(S.Stabilize),
civiField: `${G0}.Member_preferred_shares_raised`,
},
{ name: "Bank_debt_total", label: "Bank debt total needed", type: "currency", civiField: `${G0}.Bank_debt_total` },
{ name: "Bank_debt_raised", label: "Bank debt raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Bank_debt_raised` },
{ name: "Grant_Donations_Needed", label: "Grants / Donations Needed", type: "currency", civiField: `${G0}.Grant_Donations_Needed` },
{ name: "Grants_Donations_Raised", label: "Grants / Donations Raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Grants_Donations_Raised` },
{ name: "Other_sources_total", label: "Other sources total needed", type: "currency", civiField: `${G0}.Other_sources_total` },
{ name: "Other_sources_raised", label: "Other sources raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Other_sources_raised` },
{ name: "Date_Closed_Folded", label: "Date Closed / Folded", type: "date", civiField: `${G0}.Date_Closed_Folded` },
] as FieldConfig[],
};
// Stage 1 — Convene & Prepare (visible from Organizing onward)
const stage1: StageSectionConfig = {
rank: 1,
id: "stage_1",
label: "Stage 1 — Convene & Prepare",
visibleWhen: visibleAtOrAfter(S.Organizing, S.Feasibility, S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
fields: [
{
name: "Preliminary_Market_Assessment",
label: "Preliminary Market Assessment",
type: "date",
required: true,
civiField: `${G1}.Preliminary_Market_Assessment`,
help: "What date was your Preliminary Market Assessment completed?",
},
{
name: "Preliminary_Market_Assessment_Upload",
label: "Preliminary Market Assessment — Upload",
type: "file",
civiField: `${G1}.Preliminary_Market_Assessment_Upload`,
},
{
name: "Preliminary_Sources_Uses",
label: "Preliminary Sources And Uses",
type: "date",
civiField: `${G1}.Preliminary_Sources_Uses`,
help: "When was your Preliminary Sources & Uses completed?",
},
{
name: "Preliminary_Sources_Uses_Upload",
label: "Preliminary Sources and Uses — Upload",
type: "file",
civiField: `${G1}.Preliminary_Sources_Uses_Upload`,
},
{
name: "Vision",
label: "Vision",
type: "date",
civiField: `${G1}.Vision`,
help: "What date was your vision document completed?",
},
{ name: "Vision_Upload", label: "Vision — Upload", type: "file", civiField: `${G1}.Vision_Upload` },
{
name: "Business_Concept",
label: "Business Concept",
type: "date",
civiField: `${G1}.Business_Concept`,
help: "When was your business concept finalized?",
},
{
name: "Business_Concept_Upload",
label: "Business Concept — Upload",
type: "file",
civiField: `${G1}.Business_Concept_Upload`,
},
] as FieldConfig[],
};
// Stage 2 — Feasibility
const stage2: StageSectionConfig = {
rank: 2,
id: "stage_2",
label: "Stage 2 — Grow & Plan",
visibleWhen: visibleAtOrAfter(S.Feasibility, S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
fields: [
{ name: "Market_Study_Date", label: "Market Study Date", type: "date", civiField: `${G2}.Market_Study_Date` },
{ name: "Market_Study_Upload", label: "Market Study — Upload", type: "file", civiField: `${G2}.Market_Study_Upload` },
{ name: "Pro_Forma_date_completed", label: "Pro Forma — date completed", type: "date", civiField: `${G2}.Pro_Forma_date_completed` },
{ name: "Pro_Forma_Upload", label: "Pro Forma — Upload", type: "file", civiField: `${G2}.Pro_Forma_Upload` },
{
name: "Pro_Forma_Viability",
label: "Pro Forma Viability",
type: "select",
civiField: `${G2}.Pro_Forma_Viability`,
optionGroupId: 133,
help: "Rate the viability of the project.",
},
{
name: "Business_Plan",
label: "Business Plan",
type: "date",
civiField: `${G2}.Business_Plan`,
help: "What date was your most recent business plan completed?",
},
{ name: "Business_Plan_Upload", label: "Business Plan — Upload", type: "file", civiField: `${G2}.Business_Plan_Upload` },
{
name: "Board_Self_Assessment",
label: "Board Self Assessment",
type: "date",
civiField: `${G2}.Board_Self_Assessment`,
help: "When was your last Board Self Assessment completed?",
},
{
name: "Board_Self_Assessment_Upload",
label: "Board Self Assessment — Upload",
type: "file",
civiField: `${G2}.Board_Self_Assessment_Upload`,
},
{
name: "Governance_System_Used",
label: "Governance System Used",
type: "text",
civiField: `${G2}.Governance_System_Used`,
help: "What governance system does your board use, or how do you make decisions?",
},
] as FieldConfig[],
};
// Stage 3 — Connect & Gather
const stage3: StageSectionConfig = {
rank: 3,
id: "stage_3",
label: "Stage 3 — Connect & Gather",
visibleWhen: visibleAtOrAfter(S.BusinessFeasibility, S.StoreImplementation, S.Stabilize),
fields: [
{
name: "Site_Letter_of_Intent_Date",
label: "Site: Letter of Intent Date",
type: "date",
civiField: `${G3}.Site_Letter_of_Intent_Date`,
},
{
name: "Site_Letter_of_Intent_Upload",
label: "Site: Letter of Intent — Upload",
type: "file",
civiField: `${G3}.Site_Letter_of_Intent_Upload`,
},
{
name: "Own_the_building_property",
label: "Own the building / property",
type: "select",
civiField: `${G3}.Own_the_building_property`,
optionGroupId: 139,
help: "Will the co-op own the building or property?",
},
{
name: "Capital_Campaign_Owner_Participation_",
label: "Capital Campaign: Owner Participation %",
type: "percent",
civiField: `${G3}.Capital_Campaign_Owner_Participation_`,
},
{
name: "Capital_Campaign_Average_Owner_Investment",
label: "Capital Campaign: Average Owner Investment",
type: "currency",
civiField: `${G3}.Capital_Campaign_Average_Owner_Investment`,
},
{
name: "Capital_Stack",
label: "Capital Stack",
type: "multiselect",
civiField: `${G3}.Capital_Stack`,
optionGroupId: 134,
help: "Select all the types of funding that are part of your Sources.",
},
{
name: "Project_Manager_Date_of_Hire",
label: "Project Manager — Date of Hire",
type: "date",
civiField: `${G3}.Project_Manager_Date_of_Hire`,
},
{
name: "Store_Design_Plan_Completion",
label: "Store Design — Plan Completion",
type: "date",
civiField: `${G3}.Store_Design_Plan_Completion`,
},
{ name: "Store_Designer", label: "Store Designer", type: "text", civiField: `${G3}.Store_Designer` },
{ name: "NCG_Member", label: "NCG Member", type: "select", civiField: `${G3}.NCG_Member`, optionGroupId: 136 },
{ name: "NCG_Corridor", label: "NCG Corridor", type: "select", civiField: `${G3}.NCG_Corridor`, optionGroupId: 143 },
{ name: "INFRA_Member", label: "INFRA Member", type: "select", civiField: `${G3}.INFRA_Member`, optionGroupId: 137 },
{
name: "Other_Distributors",
label: "Other Distributors",
type: "select",
civiField: `${G3}.Other_Distributors`,
optionGroupId: 138,
help: "Are you using or considering one of these other distributors as your primary?",
},
] as FieldConfig[],
};
// Stage 4 — Excite & Build
const stage4: StageSectionConfig = {
rank: 4,
id: "stage_4",
label: "Stage 4 — Excite & Build",
visibleWhen: visibleAtOrAfter(S.StoreImplementation, S.Stabilize),
fields: [
{ name: "Projected_Opening_Date", label: "Projected Opening Date", type: "date", civiField: `${G4}.Projected_Opening_Date` },
{
name: "Construction_Completion_Date",
label: "Construction Completion Date",
type: "date",
civiField: `${G4}.Construction_Completion_Date`,
},
{
name: "Missions_Transition_Plan_Date",
label: "Mission Transition Plan — Date",
type: "date",
civiField: `${G4}.Missions_Transition_Plan_Date`,
},
{
name: "Mission_Transition_Plan_Upload",
label: "Mission Transition Plan — Upload",
type: "file",
civiField: `${G4}.Mission_Transition_Plan_Upload`,
},
{ name: "General_Manager_Name", label: "General Manager — Name", type: "text", civiField: `${G4}.General_Manager_Name` },
{ name: "General_Manager_Phone", label: "General Manager Phone", type: "phone", civiField: `${G4}.General_Manager_Phone` },
{ name: "GM_Email", label: "General Manager Email", type: "email", civiField: `${G4}.GM_Email` },
{
name: "General_Manager_Date_of_Hire",
label: "General Manager — Date of Hire",
type: "date",
civiField: `${G4}.General_Manager_Date_of_Hire`,
},
{
name: "General_Manager_Background",
label: "General Manager Background",
type: "select",
civiField: `${G4}.General_Manager_Background`,
optionGroupId: 135,
help: "What professional background does your General Manager have?",
},
{
name: "GM_Support_Training",
label: "GM Support and Training",
type: "text",
civiField: `${G4}.GM_Support_Training`,
help: "What training and/or onboarding support is being provided to your GM?",
},
{
name: "GM_Support_Team",
label: "GM Support Team",
type: "text",
civiField: `${G4}.GM_Support_Team`,
help: "Who is providing support and training to your GM?",
},
] as FieldConfig[],
};
// Stage 5 — Fulfill & Stabilize. Field machine names have historical
// inconsistencies; we list each one explicitly rather than building
// programmatically to keep the mapping auditable.
/*
const STAGE_5_FIELDS: FieldConfig[] = [
// Y1 Monthly Sales Targets — note the irregular machine names!
{ name: "Y1_Monthly_Sales_Targets", label: "Y1 Monthly Sales Target: M1", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Targets` },
{ name: "Y1_Monthly_Sales_Targets_M2", label: "Y1 Monthly Sales Target: M2", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Targets_M2` },
{ name: "Y1_Monthly_Sales_Target_M2", label: "Y1 Monthly Sales Target: M3", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M2`, help: "(Civi field name says M2 but represents M3.)" },
{ name: "Y1_Monthly_Sales_Target_M4", label: "Y1 Monthly Sales Target: M4", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M4` },
{ name: "Y1_Monthly_Sales_Target_M5", label: "Y1 Monthly Sales Target: M5", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M5` },
{ name: "Y1_Monthly_Sales_Target_M6", label: "Y1 Monthly Sales Target: M6", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M6` },
{ name: "Y1_Monthly_Sales_Target_M7", label: "Y1 Monthly Sales Target: M7", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M7` },
{ name: "Y1_Monthly_Sales_Target_M8", label: "Y1 Monthly Sales Target: M8", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M8` },
{ name: "Y1_Monthly_Sales_Target_M9", label: "Y1 Monthly Sales Target: M9", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M9` },
{ name: "Y1_Monthly_Sales_Target_M10", label: "Y1 Monthly Sales Target: M10", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M10` },
{ name: "Y1_Monthly_Sales_Target_M11", label: "Y1 Monthly Sales Target: M11", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M11` },
{ name: "Y1_Monthly_Sales_Target_M12", label: "Y1 Monthly Sales Target: M12", type: "currency", civiField: `${G5}.Y1_Monthly_Sales_Target_M12` },
// Y1 Actual Sales (M1..M12)
...[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((m) => ({
name: `Y1_M${m}_Actual_Sales`,
label: `Y1 M${m} Actual Sales`,
type: "currency" as const,
civiField: `${G5}.Y1_M${m}_Actual_Sales`,
})),
// Y1 Transactions (M1..M12)
...[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((m) => ({
name: `Y1_M${m}_Transactions`,
label: `Y1 M${m} Transactions`,
type: "number" as const,
civiField: `${G5}.Y1_M${m}_Transactions`,
})),
// Y1 Quarterly Labor (Q1..Q4) — stored as % of sales
...[1, 2, 3, 4].map((q) => ({
name: `Y1_Q${q}_Labor`,
label: `Y1 Q${q} Labor`,
type: "percent" as const,
civiField: `${G5}.Y1_Q${q}_Labor`,
help: `Labor, as a percent of sales for Y1 Q${q}.`,
})),
// Y1 Quarterly Margin (Q1..Q4)
...[1, 2, 3, 4].map((q) => ({
name: `Y1_Q${q}_Margin`,
label: `Y1 Q${q} Margin`,
type: "percent" as const,
civiField: `${G5}.Y1_Q${q}_Margin`,
help: `Margin, as a percent of sales, for Y1 Q${q}.`,
})),
// Y1 Quarterly Member Sales % (Q1..Q4) — note the trailing underscore in Civi machine names!
...[1, 2, 3, 4].map((q) => ({
name: `Y1_Q${q}_Member_Sales_`,
label: `Y1 Q${q} Member Sales %`,
type: "percent" as const,
civiField: `${G5}.Y1_Q${q}_Member_Sales_`,
help: `Percentage of sales to member-owners in Y1 Q${q}.`,
})),
];
// Helpers for building the field-name lists in the matrices below.
const months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const monthCols = months.map((m) => ({ key: `M${m}`, label: `M${m}` }));
const quarters = [1, 2, 3, 4];
const quarterCols = quarters.map((q) => ({ key: `Q${q}`, label: `Q${q}` }));
// Y1 Monthly Sales Target field names (irregular — built explicitly).
const Y1_TARGET_FIELDS = [
"Y1_Monthly_Sales_Targets", // M1
"Y1_Monthly_Sales_Targets_M2", // M2
"Y1_Monthly_Sales_Target_M2", // M3 (Civi field name says M2; label says M3)
"Y1_Monthly_Sales_Target_M4",
"Y1_Monthly_Sales_Target_M5",
"Y1_Monthly_Sales_Target_M6",
"Y1_Monthly_Sales_Target_M7",
"Y1_Monthly_Sales_Target_M8",
"Y1_Monthly_Sales_Target_M9",
"Y1_Monthly_Sales_Target_M10",
"Y1_Monthly_Sales_Target_M11",
"Y1_Monthly_Sales_Target_M12",
];
*/
const stage5: StageSectionConfig = {
rank: 5,
id: "stage_5",
label: "Stage 5 — Fulfill & Stabilize",
visibleWhen: visibleAtOrAfter(S.Stabilize),
fields: [
{ name: "Date_Opened", label: "Date Opened", type: "date", civiField: `${G5}.Date_Opened` },
{ name: "Y1_Actual_Sales", label: "Y1 Actual Sales", type: "currency", civiField: `${G5}.Y1_Actual_Sales` }
] as FieldConfig[],
// STAGE_5_FIELDS,
// matrixGroups: [
// {
// id: "y1_monthly_metrics",
// label: "Year 1 — Monthly Tracking",
// intro: "Targets and actuals by month. Leave blank for months you don't yet have data for.",
// cols: monthCols,
// rows: [
// { label: "Sales Target", type: "currency", fields: Y1_TARGET_FIELDS },
// {
// label: "Actual Sales",
// type: "currency",
// fields: months.map((m) => `Y1_M${m}_Actual_Sales`),
// },
// {
// label: "Transactions",
// type: "number",
// fields: months.map((m) => `Y1_M${m}_Transactions`),
// },
// ],
// },
// {
// id: "y1_quarterly_metrics",
// label: "Year 1 — Quarterly Tracking",
// intro: "Quarterly operating ratios.",
// cols: quarterCols,
// rows: [
// {
// label: "Labor",
// type: "percent",
// fields: quarters.map((q) => `Y1_Q${q}_Labor`),
// },
// {
// label: "Margin",
// type: "percent",
// fields: quarters.map((q) => `Y1_Q${q}_Margin`),
// },
// {
// label: "Member Sales",
// type: "percent",
// fields: quarters.map((q) => `Y1_Q${q}_Member_Sales_`),
// },
// ],
// },
// ],
};
export const formConfig: FormConfig = {
id: "org_engagement_check_in",
title: "Co-op Check-in",
subtitle:
"Update tracking data for your co-op as you progress through the organizing stages. The questions you'll see depend on where you are in the framework.",
stageField: "current_stage",
sections: [stage0, stage1, stage2, stage3, stage4, stage5],
};
/** Convenience: flatten all field configs across all sections for lookup by name. */
export const allFields = formConfig.sections.flatMap((s) => s.fields);
/** Activity type the form writes its submissions as. */
export const ACTIVITY_TYPE_NAME = "Check-in (organizing)";
/**
* Stage-snapshot field on the activity. The form does NOT write this field;
* it's set manually by staff on dedicated stage-change check-in activities.
* /api/data uses this field to derive the org's current stage by finding
* the most recent activity where it is non-empty.
*/
export const ACTIVITY_STAGE_FIELD = `${G0}.Stage`;
/**
* RelationshipType.name_a_b for the relationship that links the form-filler
* (Individual side) to the organization the form is about (Organization side).
*
* On `client.crm.fci.coop` we reuse the existing "Primary Contact"
* relationship type (id 24) rather than introducing a dedicated
* "Primary Form Contact" type. If you change this, also update the docs in
* the parent repo's spec.
*/
export const FORM_CONTACT_RELATIONSHIP = "Primary Contact";
/** All distinct option_group_ids referenced by fields in this form. */
export const optionGroupIds = Array.from(
new Set(
allFields
.map((f) => (f as FieldConfig).optionGroupId)
.filter((id): id is number => typeof id === "number"),
),
);