/** * Form definition for the Org Engagement Survey 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 `.` 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 — Survey (always visible) const stage0: StageSectionConfig = { rank: 0, id: "stage_0", label: "Check-in (organizing)", // intro: // "Core survey 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, }, // Contact identity, sourced from the cid in /api/data. Display-only — // never written back to Civi. These confirm to the form-filler which // contact record their submission will be attributed to. { name: "contact_first_name", label: "First name", type: "readonly", }, { name: "contact_last_name", label: "Last name", type: "readonly", }, { name: "contact_email", label: "Email", type: "readonly", }, // { // 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-Owner 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: "Member-Owners (current)", type: "number", civiField: `${G0}.Members__current_`, help: "How many paid member-owners does the co-op currently have? This can include partially paid, e.g. on installment plans.", }, { name: "Total_members_at_opening", label: "Total member-owners at opening", type: "number", civiField: `${G0}.Total_members_at_opening`, visibleWhen: visibleAtOrAfter(S.Stabilize), help: "How many member-owners will be required at opening for this store?", }, // { // 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), help: "Size, in sq ft, of the total storefront planned, including retail and non-retail areas", }, { 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), help: "Size, in sq ft, of the retail areas of the store being planned", }, // { // 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: "Actual_FTE", label: "Actual FTE", type: "number", civiField: `${G0}.Actual_FTE`, step: 0.1, visibleWhen: visibleAtOrAfter(S.Stabilize), help: "Actual number of employees (or full-time equivalents) working in the store after open. i.e. if you have 10 employees working full time and 20 employees working half time, that would be 20 full-time equivalents.", }, { name: "Total_cost_of_project", label: "Total cost of project", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Total_cost_of_project`, help: "Current estimate of the total cost of the project, up to store opening", }, // { // name: "Member_equity_total", // label: "Member equity total needed", // type: "currency", // civiField: `${G0}.Member_equity_total` // }, { name: "Member_equity_raised", label: "Member-Owner equity raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Member_equity_raised`, help: "Member-owner equity raised, as of the time of survey", }, // { // name: "Member_loans_total", // label: "Member loans total needed", // type: "currency", // civiField: `${G0}.Member_loans_total` // }, { name: "Member_loans_raised", label: "Member-Owner loans raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Member_loans_raised`, help: "Total amount (USD) raised from member-owner loans, as of the time of survey", }, // { // 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-Owner preferred shares raised", type: "currency", visibleWhen: visibleAtOrAfter(S.Stabilize), civiField: `${G0}.Member_preferred_shares_raised`, help: "Total amount (USD) raised from preferred shares sold to member-owners in the latest Sources and Uses doc", }, // { 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`, help: "Total amount (USD) raised from bank debt (or other first position lender) in the latest Sources and Uses doc" }, // { 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`, help: "Total amount (USD) raised from Grants and Donations in the latest Sources and Uses doc" }, // { 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`, help: "Total amount (USD) successfully raised from other sources in the latest Sources and Uses doc (e.g. community loans or grants, free fill, vendor credit, landlord contribution, etc). Does not include member-owner contributions (loans or equity) or bank debt." }, // { name: "Date_Closed_Folded", label: "Date Closed / Folded", type: "date", civiField: `${G0}.Date_Closed_Folded` }, { 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", }, ] 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`, help: "Please upload your Preliminary Market Assessment here.", }, { 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`, help: "Please upload your Preliminary Sources & Uses.", }, { 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`, help: "Please upload your vision document." }, { 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`, help: "Please upload your Business Concept", }, ] as FieldConfig[], // Visual clustering — each pair (date + upload) reads as one item. fieldGroups: [ { id: "prelim_market_assessment", fields: ["Preliminary_Market_Assessment", "Preliminary_Market_Assessment_Upload"], }, { id: "prelim_sources_uses", fields: ["Preliminary_Sources_Uses", "Preliminary_Sources_Uses_Upload"], }, { id: "vision", fields: ["Vision", "Vision_Upload"], }, { id: "business_concept", fields: ["Business_Concept", "Business_Concept_Upload"], }, ], }; // 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`, help: "What date was your market study completed?" }, { name: "Market_Study_Upload", label: "Market Study — Upload", type: "file", civiField: `${G2}.Market_Study_Upload`, help: "Please upload your most recent Market Study" }, { name: "Pro_Forma_date_completed", label: "Pro Forma — date completed", type: "date", civiField: `${G2}.Pro_Forma_date_completed`, help: "What date was your most recent Pro Forma completed?" }, { name: "Pro_Forma_Upload", label: "Pro Forma — Upload", type: "file", civiField: `${G2}.Pro_Forma_Upload`, help: "Please upload your most recent Pro Forma." }, // { // 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`, help: "Please upload the most recent copy of your business plan." }, { 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`, help: "Please upload the results of your most recent Board Self Assessment.", }, { 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[], // Visual clustering — each pair (date + upload) reads as one item. fieldGroups: [ { id: "market_study", fields: ["Market_Study_Date", "Market_Study_Upload"], }, { id: "pro_forma", fields: ["Pro_Forma_date_completed", "Pro_Forma_Upload"], }, { id: "business_plan", fields: ["Business_Plan", "Business_Plan_Upload"], }, { id: "board_self_assessment", fields: ["Board_Self_Assessment", "Board_Self_Assessment_Upload"], }, ], }; // 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`, help: "What date was your Letter of Intent for your site signed?", }, { name: "Site_Letter_of_Intent_Upload", label: "Site: Letter of Intent — Upload", type: "file", civiField: `${G3}.Site_Letter_of_Intent_Upload`, help: "Please upload the Letter of Intent for your Site.", }, // { // 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, if so select YES. Select NO if the co-op will be leasing or have some other type of arrangement", // }, { name: "Capital_Stack", label: "Capital Stack", type: "multiselect", civiField: `${G3}.Capital_Stack`, optionGroupId: 134, help: "Please 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`, help: "What date did your hire a project manager for co-op development? (This is NOT the construction-specific Project Manager.)", }, { name: "Store_Design_Plan_Completion", label: "Store Design — Plan Completion", type: "date", civiField: `${G3}.Store_Design_Plan_Completion`, help: "What date was the design for your co-op completed?", }, { name: "Store_Designer", label: "Store Designer", type: "text", civiField: `${G3}.Store_Designer`, help: "Which professional or team created your store design?" }, // { name: "NCG_Member", label: "NCG Member", type: "select", civiField: `${G3}.NCG_Member`, optionGroupId: 136, help: "Are you an NCG member, or considering NCG Membership?" }, // { name: "NCG_Corridor", label: "NCG Corridor", type: "select", civiField: `${G3}.NCG_Corridor`, optionGroupId: 143, help: "Which NCG corridor is this co-op in?" }, // { name: "INFRA_Member", label: "INFRA Member", type: "select", civiField: `${G3}.INFRA_Member`, optionGroupId: 137, help: "Are you an INFRA member, or considering INFRA membership?" }, // { // 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 distributor?", // }, { name: "Capital_Campaign_Owner_Participation_", label: "Capital Campaign: Owner Participation %", type: "percent", civiField: `${G3}.Capital_Campaign_Owner_Participation_`, help: "What percentage of your owners have contributed to your capital campaign?", }, { name: "Capital_Campaign_Average_Owner_Investment", label: "Capital Campaign: Average Owner Investment", type: "currency", civiField: `${G3}.Capital_Campaign_Average_Owner_Investment`, help: "What is the average investment size from your owners? (i.e. total raised divided by number of owners participating. If your co-op raised $100,000 from 100 owners, the average owner investment would be $1,000. $100,000 / 100 = $1,000.)", }, ] as FieldConfig[], fieldGroups: [ { id: "site", fields: ["Site_Letter_of_Intent_Date", "Site_Letter_of_Intent_Upload"] }, { id: "store_design", fields: ["Store_Designer", "Store_Design_Plan_Completion"] }, // { id: "cap_camp", fields: ["Capital_Campaign_Owner_Participation_", "Capital_Campaign_Average_Owner_Investment"]}, ], }; // 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: "General_Manager_Date_of_Hire", label: "General Manager — Date of Hire", type: "date", civiField: `${G4}.General_Manager_Date_of_Hire`, help: "What date was your GM hired?", }, { 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: "General_Manager_Name", label: "General Manager — Name", type: "text", civiField: `${G4}.General_Manager_Name`, help: "Please enter the name of your current General Manager." }, { name: "General_Manager_Phone", label: "General Manager Phone", type: "phone", civiField: `${G4}.General_Manager_Phone`, help: "What is your GM's phone number?" }, { name: "GM_Email", label: "General Manager Email", type: "email", civiField: `${G4}.GM_Email`, help: "What is the best email for your GM?" }, { 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?", }, { name: "Projected_Opening_Date", label: "Projected Opening Date", type: "date", civiField: `${G4}.Projected_Opening_Date`, help: "Date the co-op is projected to open." }, { name: "Construction_Completion_Date", label: "Construction Completion Date", type: "date", civiField: `${G4}.Construction_Completion_Date`, help: "What date was construction completed on the co-op?", }, { name: "Missions_Transition_Plan_Date", label: "Mission Transition Plan — Date", type: "date", civiField: `${G4}.Missions_Transition_Plan_Date`, help: "What date was your Mission Transition Plan adopted?", }, { name: "Mission_Transition_Plan_Upload", label: "Mission Transition Plan — Upload", type: "file", civiField: `${G4}.Mission_Transition_Plan_Upload`, help: "Please upload your Mission Transition Plan.", }, ] as FieldConfig[], fieldGroups: [ { id: "gm", label: "General Manager", fields: ["General_Manager_Date_of_Hire", "General_Manager_Background", "General_Manager_Name", "General_Manager_Phone", "GM_Email", "GM_Support_Training", "GM_Support_Team"] }, { id: "mission_transition", fields: ["Missions_Transition_Plan_Date", "Mission_Transition_Plan_Upload"] } ], }; // 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`, help: "What day did the co-op officially open for business?" }, { name: "Y1_Actual_Sales", label: "Y1 Actual Sales", type: "currency", civiField: `${G5}.Y1_Actual_Sales`, help: "Total Sales from Year 1" } ] 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 Survey", 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"), ), );