app/pages/templates/membership-agreement.vue

1153 lines
42 KiB
Vue

<template>
<div>
<!-- Wizard Subnav -->
<WizardSubnav />
<!-- Export Options - Top -->
<ExportOptions
:export-data="exportData"
filename="membership-agreement"
title="Membership Agreement"
/>
<div
class="template-wrapper bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100"
>
<!-- Document Container -->
<div class="document-page">
<div class="template-content">
<!-- Document Title -->
<div class="text-center mb-8">
<h1
class="text-3xl md:text-5xl font-bold uppercase text-neutral-900 dark:text-white m-0 py-4 border-t-2 border-b-2 border-neutral-900 dark:border-neutral-100"
:data-coop-name="formData.cooperativeName || 'Worker Cooperative'"
>
MEMBERSHIP AGREEMENT
</h1>
</div>
<!-- Section 1: Who We Are -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
1. Who We Are
</h2>
<div class="space-y-8">
<UFormField label="Cooperative Name" class="form-group-large">
<UInput
v-model="formData.cooperativeName"
placeholder="Enter cooperative name"
size="xl"
class="w-full"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
<UFormField label="Date Established" class="form-group-large">
<UInput
v-model="formData.dateEstablished"
type="date"
size="xl"
class="large-field"
@change="autoSave"
/>
</UFormField>
<UFormField label="Our Purpose" class="form-group-large">
<UTextarea
v-model="formData.purpose"
:rows="3"
placeholder="Write 1-2 sentences about what your cooperative does and why it exists"
size="xl"
class="large-field"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
<UFormField label="Our Core Values" class="form-group-large">
<UTextarea
v-model="formData.coreValues"
:rows="4"
placeholder="List 3-5 values that guide everything you do. Examples: mutual care, creative sustainability, economic justice, collective liberation"
size="xl"
class="large-field"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
<div class="form-group-large">
<div class="flex items-center justify-between mb-4">
<label
class="text-lg font-semibold text-neutral-900 dark:text-neutral-100"
>Current Members</label
>
<UButton
@click="addMember"
size="sm"
color="primary"
variant="outline"
icon="i-heroicons-plus"
>
Add Member
</UButton>
</div>
<div class="space-y-4">
<div
v-for="(member, index) in formData.members"
:key="index"
class="border border-neutral-600 rounded-lg p-4"
>
<div class="flex items-start justify-between mb-3">
<h4 class="font-medium text-neutral-900 dark:text-neutral-100">
Member {{ index + 1 }}
</h4>
<UButton
v-if="formData.members.length > 1"
@click="removeMember(index)"
size="sm"
color="red"
variant="ghost"
icon="i-heroicons-trash"
>
</UButton>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<UFormField label="Full Name" class="form-group-large">
<UInput
v-model="member.name"
placeholder="Enter full name"
size="xl"
class="large-field"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
<UFormField label="Email" class="form-group-large">
<UInput
v-model="member.email"
type="email"
placeholder="Enter email address"
size="xl"
class="large-field"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
<UFormField label="Join Date" class="form-group-large">
<UInput
v-model="member.joinDate"
type="date"
size="xl"
class="large-field"
@change="autoSave"
/>
</UFormField>
<UFormField
label="Current Role (Optional)"
class="form-group-large"
>
<UInput
v-model="member.role"
placeholder="e.g., Coordinator, Developer, etc."
size="xl"
class="large-field"
@input="debouncedAutoSave"
@change="autoSave"
/>
</UFormField>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Section 2: Membership -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
2. Membership
</h2>
<div class="space-y-4">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Who Can Be a Member
</h3>
<p class="content-paragraph mb-3 leading-relaxed text-left">
Any person who:
</p>
<ul class="content-list my-2 pl-6 list-disc">
<li>Shares our values and purpose</li>
<li>
Contributes labour to the cooperative (by doing actual work, not just
investing money)
</li>
<li>Commits to collective decision-making</li>
<li>Participates in governance responsibilities</li>
</ul>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Becoming a Member
</h3>
<p class="content-paragraph">
New members join through a consent process, which means existing members
must agree that adding this person won't harm the cooperative.
</p>
<ol class="content-list numbered my-2 pl-6 list-decimal">
<li class="flex items-baseline gap-2 flex-wrap">
Trial period of
<UInput
v-model="formData.trialPeriodMonths"
type="number"
placeholder="3"
class="inline-field number-field"
@change="autoSave"
/>
months working together
</li>
<li>Values alignment conversation</li>
<li>Consent decision by current members</li>
<li class="flex items-baseline gap-2 flex-wrap">
Optional - Equal buy-in contribution of $<UInput
v-model="formData.buyInAmount"
type="number"
placeholder="1000"
class="inline-field number-field"
@change="autoSave"
/>
(can be paid over time or waived based on need)
</li>
</ol>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Leaving the Cooperative
</h3>
<p class="content-paragraph flex items-baseline gap-2 flex-wrap">
Members can leave anytime with
<UInput
v-model="formData.noticeDays"
type="number"
placeholder="30"
class="inline-field number-field"
@change="autoSave"
/>
days notice. The cooperative will:
</p>
<ul class="content-list my-2 pl-6 list-disc">
<li class="flex items-baseline gap-2 flex-wrap">
Pay out their share of any surplus within
<UInput
v-model="formData.surplusPayoutDays"
type="number"
placeholder="30"
class="inline-field number-field"
@change="autoSave"
/>
days
</li>
<li class="flex items-baseline gap-2 flex-wrap">
Return their buy-in contribution within
<UInput
v-model="formData.buyInReturnDays"
type="number"
placeholder="90"
class="inline-field number-field"
@change="autoSave"
/>
days
</li>
<li>Maintain respectful ongoing relationships when possible</li>
</ul>
</div>
</div>
</div>
<!-- Section 3: How We Make Decisions -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
3. How We Make Decisions
</h2>
<div class="space-y-4">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Consent-Based Decisions
</h3>
<p class="content-paragraph mb-3 leading-relaxed text-left">
We use consent, not consensus. This means we move forward when no one
has a principled objection that would harm the cooperative. An objection
must explain how the proposal would contradict our values or threaten
our sustainability.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Day-to-Day Decisions
</h3>
<p
class="content-paragraph mb-3 leading-relaxed text-left flex items-baseline gap-2 flex-wrap"
>
Decisions under $<UInput
v-model="formData.dayToDayLimit"
type="number"
placeholder="100"
class="inline-field number-field"
@change="autoSave"
/>
can be made by any member. Just tell others what you did at the next
meeting.
</p>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Regular Decisions
</h3>
<p
class="content-paragraph mb-3 leading-relaxed text-left flex items-baseline gap-2 flex-wrap"
>
Decisions between $<UInput
v-model="formData.regularDecisionMin"
type="number"
placeholder="100"
class="inline-field number-field"
@change="autoSave"
/>
and $<UInput
v-model="formData.regularDecisionMax"
type="number"
placeholder="1000"
class="inline-field number-field"
@change="autoSave"
/>
need consent from members present at a meeting (minimum 2 members).
</p>
</div>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Major Decisions
</h3>
<p class="content-paragraph mb-3 leading-relaxed text-left">
These require consent from all members:
</p>
<ul class="content-list my-2 pl-6 list-disc">
<li>Adding or removing members</li>
<li>Changing this agreement</li>
<li class="flex items-baseline gap-2 flex-wrap">
Taking on debt over $<UInput
v-model="formData.majorDebtThreshold"
type="number"
placeholder="5000"
class="inline-field number-field"
@change="autoSave"
/>
</li>
<li>Fundamental changes to our purpose or structure</li>
<li>Dissolution of the cooperative</li>
</ul>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Meeting Structure
</h3>
<ul class="content-list my-2 pl-6 list-disc">
<li class="flex items-baseline gap-2 flex-wrap">
Regular meetings happen
<UInput
v-model="formData.meetingFrequency"
placeholder="weekly"
class="inline-field"
@change="autoSave"
/>
</li>
<li class="flex items-baseline gap-2 flex-wrap">
Emergency meetings need
<UInput
v-model="formData.emergencyNoticeHours"
type="number"
placeholder="24"
class="inline-field number-field"
@change="autoSave"
/>
hours notice
</li>
<li>We rotate who facilitates meetings</li>
<li>Decisions and reasoning get documented in shared notes</li>
</ul>
</div>
</div>
</div>
<!-- Section 4: Money and Labour -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
4. Money and Labour
</h2>
<div class="space-y-4">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Equal Ownership
</h3>
<p class="content-paragraph mb-3 leading-relaxed text-left">
Each member owns an equal share of the cooperative, regardless of hours
worked or tenure.
</p>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Paying Ourselves
</h3>
<ul class="content-list my-2 pl-6 list-disc">
<li class="flex items-baseline gap-2 flex-wrap">
Base rate: $<UInput
v-model="formData.baseRate"
type="number"
placeholder="25"
class="inline-field number-field"
@change="autoSave"
/>/hour for all members
</li>
<li class="flex items-baseline gap-2 flex-wrap">
Or: Equal monthly draw of $<UInput
v-model="formData.monthlyDraw"
type="number"
placeholder="2000"
class="inline-field number-field"
@change="autoSave"
/>
per member
</li>
<li class="flex items-baseline gap-2 flex-wrap">
Paid on the
<USelect
v-model="formData.paymentDay"
:items="dayOptions"
placeholder="15"
class="inline-field"
@change="autoSave"
/>
of each month
</li>
<li class="flex items-baseline gap-2 flex-wrap">
Surplus (profit) distributed equally every
<UInput
v-model="formData.surplusFrequency"
placeholder="quarter"
class="inline-field"
@change="autoSave"
/>
</li>
</ul>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Work Expectations
</h3>
<ul class="content-list my-2 pl-6 list-disc">
<li class="flex items-baseline gap-2 flex-wrap">
Target hours per week:
<UInput
v-model="formData.targetHours"
type="number"
placeholder="40"
class="inline-field number-field"
@change="autoSave"
/>
(flexible based on capacity)
</li>
<li>We explicitly reject crunch culture</li>
<li>Members communicate their capacity openly</li>
<li>
We adjust workload collectively when someone needs reduced hours
</li>
</ul>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Financial Transparency
</h3>
<ul class="content-list my-2 pl-6 list-disc">
<li>All members can access all financial records anytime</li>
<li>Monthly financial check-ins at meetings</li>
<li>
Quarterly reviews of our runway (how many months we can operate)
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Section 5: Roles and Responsibilities -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
5. Roles and Responsibilities
</h2>
<div class="space-y-4">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Rotating Roles
</h3>
<p
class="content-paragraph mb-3 leading-relaxed text-left flex items-baseline gap-2 flex-wrap"
>
We rotate operational roles every
<UInput
v-model="formData.roleRotationMonths"
type="number"
placeholder="6"
class="inline-field number-field"
@change="autoSave"
/>
months. Current roles include:
</p>
<ul class="content-list">
<li>
Financial coordinator (handles bookkeeping, not financial decisions)
</li>
<li>Meeting facilitator</li>
<li>External communications</li>
<li>Others</li>
</ul>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Shared Responsibilities
</h3>
<p class="content-paragraph mb-3 leading-relaxed text-left">
All members participate in:
</p>
<ul class="content-list my-2 pl-6 list-disc">
<li>Governance and decision-making</li>
<li>Strategic planning</li>
<li>Mutual support and care</li>
</ul>
</div>
</div>
</div>
<!-- Section 6: Conflict and Care -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
6. Conflict and Care
</h2>
<div class="space-y-4">
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
When Conflict Happens
</h3>
<ol class="content-list numbered my-2 pl-6 list-decimal">
<li>Direct conversation between parties (if comfortable)</li>
<li>Mediation with another member</li>
<li>Full group discussion with structured process</li>
<li>External mediation if needed</li>
</ol>
</div>
<div>
<h3
class="subsection-title text-xl font-semibold text-neutral-700 dark:text-neutral-200 border-b border-neutral-200 dark:border-neutral-600 pb-1 mb-3"
>
Care Commitments
</h3>
<ul class="content-list my-2 pl-6 list-disc">
<li>We check in about capacity and wellbeing regularly</li>
<li>We honour diverse access needs</li>
<li>We maintain flexibility for life circumstances</li>
<li>We contribute to mutual aid when members face hardship</li>
</ul>
</div>
</div>
</div>
<!-- Section 7: Changing This Agreement -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
7. Changing This Agreement
</h2>
<p
class="content-paragraph mb-3 leading-relaxed text-left flex items-baseline gap-2 flex-wrap"
>
This is a living document. We review it every
<UInput
v-model="formData.reviewFrequency"
placeholder="year"
class="inline-field"
@change="autoSave"
/>
and update it through our consent process. Small clarifications can happen
anytime; structural changes need full member consent.
</p>
</div>
<!-- Section 8: If We Need to Close -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
8. If We Need to Close
</h2>
<div class="space-y-4">
<p class="content-paragraph mb-3 leading-relaxed text-left">
If the cooperative dissolves:
</p>
<ol class="content-list numbered my-2 pl-6 list-decimal">
<li>Pay all debts and obligations</li>
<li>Return member contributions</li>
<li>Distribute remaining assets equally among members</li>
<li class="flex items-baseline gap-2 flex-wrap">
Or donate remaining assets to
<UInput
v-model="formData.assetDonationTarget"
placeholder="Enter organization name"
class="inline-field wide-field"
/>
</li>
</ol>
</div>
</div>
<!-- Section 9: Legal Bits -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
9. Legal Bits
</h2>
<div class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<UFormField label="Legal Structure" class="form-group-block">
<UInput
v-model="formData.legalStructure"
size="xl"
class="w-full"
placeholder="Cooperative corporation, LLC, partnership, etc."
/>
</UFormField>
<UFormField label="Registered in" class="form-group-inline">
<UInput
v-model="formData.registeredLocation"
placeholder="State/Province"
size="xl"
class="inline-field w-full"
@change="autoSave"
/>
</UFormField>
<div class="fiscal-year-group">
<UFormField label="Fiscal Year-End" class="form-group-inline">
<div class="flex gap-2 items-center">
<USelect
v-model="formData.fiscalYearEndMonth"
:items="monthOptions"
placeholder="Month"
size="xl"
class="w-60"
@change="autoSave"
/>
<USelect
v-model="formData.fiscalYearEndDay"
:items="dayOptions"
placeholder="Day"
size="xl"
class="w-40"
@change="autoSave"
/>
</div>
</UFormField>
</div>
</div>
<p class="content-paragraph mb-3 leading-relaxed text-left">
This agreement works alongside but doesn't replace our legal incorporation
documents. Where they conflict, we follow the law but work to align our
legal structure with our values.
</p>
</div>
</div>
<!-- Section 10: Agreement Review -->
<div class="section-card">
<h2
class="section-title text-2xl font-extrabold text-neutral-900 dark:text-neutral-100 mb-4"
>
10. Agreement Review
</h2>
<div class="space-y-4">
<p class="content-paragraph mb-3 leading-relaxed text-left">
By using this agreement, we commit to these principles and to showing up
for each other.
</p>
<div
class="bg-neutral-50 dark:bg-neutral-900 p-4 rounded-md border-l-4 border-emerald-300"
>
<p
class="content-paragraph mb-3 leading-relaxed text-left flex items-baseline gap-2 flex-wrap"
>
This agreement was last updated on
<UInput
v-model="formData.lastUpdated"
type="date"
class="inline-field"
@change="autoSave"
/>. We commit to reviewing it on
<UInput
v-model="formData.nextReview"
type="date"
class="inline-field"
@change="autoSave"
/>
or sooner if circumstances require.
</p>
</div>
<div
class="signature-space mt-8 p-8 border border-dashed border-neutral-300 rounded-md bg-neutral-50 dark:bg-neutral-950"
>
<p
class="content-paragraph mb-3 leading-relaxed text-center text-neutral-600 italic"
>
[Space for member signatures when printed]
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Export Options - Bottom -->
<ExportOptions
:export-data="exportData"
filename="membership-agreement"
title="Membership Agreement"
/>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
definePageMeta({
layout: false,
});
// Import PDF export composable
const { exportToPDF } = usePdfExportBasic();
const monthOptions = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const dayOptions = Array.from({ length: 31 }, (_, i) => (i + 1).toString());
const formData = ref({
cooperativeName: "",
dateEstablished: "",
purpose: "",
coreValues: "",
members: [{ name: "", email: "", joinDate: "", role: "" }],
trialPeriodMonths: 3,
buyInAmount: "",
noticeDays: 30,
surplusPayoutDays: 30,
buyInReturnDays: 90,
dayToDayLimit: 100,
regularDecisionMin: 100,
regularDecisionMax: 1000,
majorDebtThreshold: 5000,
meetingFrequency: "weekly",
emergencyNoticeHours: 24,
baseRate: 25,
monthlyDraw: "",
paymentDay: 15,
surplusFrequency: "quarter",
targetHours: 40,
roleRotationMonths: 6,
reviewFrequency: "year",
assetDonationTarget: "",
legalStructure: "",
registeredLocation: "",
fiscalYearEndMonth: "December",
fiscalYearEndDay: 31,
lastUpdated: new Date().toISOString().split("T")[0],
nextReview: "",
});
// Load saved data immediately (before watchers)
const loadSavedData = () => {
const saved = localStorage.getItem("membership-agreement-data");
if (saved) {
try {
const parsedData = JSON.parse(saved);
formData.value = { ...formData.value, ...parsedData };
console.log("Loaded saved data:", parsedData);
} catch (error) {
console.error("Error loading saved data:", error);
}
}
};
// Load data immediately
loadSavedData();
// Auto-save to localStorage (removed immediate to prevent overwriting)
watch(
formData,
(newData) => {
localStorage.setItem("membership-agreement-data", JSON.stringify(newData));
console.log("Auto-saved data");
},
{ deep: true }
);
// Also ensure data is loaded on mount as backup
onMounted(() => {
loadSavedData();
});
// Auto-save individual field changes immediately
const autoSave = () => {
localStorage.setItem("membership-agreement-data", JSON.stringify(formData.value));
console.log("Manual auto-save triggered:", formData.value);
};
// Debounced auto-save for better performance
const debouncedAutoSave = debounce(autoSave, 300);
// Helper function for debouncing
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Member management functions
const addMember = () => {
formData.value.members.push({ name: "", email: "", joinDate: "", role: "" });
debouncedAutoSave();
};
const removeMember = (index) => {
if (formData.value.members.length > 1) {
formData.value.members.splice(index, 1);
debouncedAutoSave();
}
};
// Print function with proper form value handling
const handlePrint = () => {
// Create print-friendly version by replacing inputs with their values from formData
const inputs = document.querySelectorAll("input, textarea, select");
const printValues = [];
// Also create signature section replacement
const signatureSpace = document.querySelector(".signature-space");
let signatureReplacement = null;
if (signatureSpace) {
const signatureDiv = document.createElement("div");
signatureDiv.className = "print-signatures";
signatureDiv.innerHTML = `
<p style="font-weight: bold; margin-bottom: 12pt; color: #000;">Member Signatures</p>
<p style="margin-bottom: 16pt; color: #000;">By signing below, we agree to these terms and commit to working together as equals in this cooperative.</p>
`;
// Add signature lines for each member
const membersWithNames = formData.value.members?.filter((m) => m.name) || [];
const numSignatures = Math.max(2, Math.min(8, membersWithNames.length || 4));
for (let i = 0; i < numSignatures; i++) {
const memberName = membersWithNames[i]?.name || "";
const sigDiv = document.createElement("div");
sigDiv.style.marginBottom = "24pt";
sigDiv.innerHTML = `
<p style="margin-bottom: 8pt; font-weight: bold; color: #000;">${
memberName || `Member ${i + 1}:`
}</p>
<div style="border-bottom: 1pt solid #000; width: 300pt; height: 16pt; margin-bottom: 4pt;"></div>
<p style="font-size: 9pt; color: #666;">Signature &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date: ___________</p>
`;
signatureDiv.appendChild(sigDiv);
}
signatureReplacement = signatureDiv;
signatureSpace.style.display = "none";
signatureSpace.parentNode.insertBefore(signatureDiv, signatureSpace);
}
inputs.forEach((input, index) => {
// Get value from the actual formData reactive object instead of DOM
let value = "";
// Try to match the input to formData properties
if (input.closest('[label="Cooperative Name"]'))
value = formData.value.cooperativeName;
else if (input.closest('[label="Date Established"]'))
value = formData.value.dateEstablished;
else if (input.closest('[label="Our Purpose"]')) value = formData.value.purpose;
else if (input.closest('[label="Our Core Values"]'))
value = formData.value.coreValues;
else if (input.closest('[label="Legal Structure"]'))
value = formData.value.legalStructure;
else if (input.closest('[label="Registered in"]'))
value = formData.value.registeredLocation;
else if (input.closest('[label="Fiscal Year-End"]')) {
if (input.tagName === "SELECT" || input.type === "select-one") {
value = formData.value.fiscalYearEndMonth;
} else {
value = formData.value.fiscalYearEndDay;
}
}
// Handle member fields
else if (input.closest(".border-neutral-200")) {
const memberCard = input.closest(".border-neutral-200");
const memberIndex = Array.from(memberCard.parentNode.children).indexOf(memberCard);
const member = formData.value.members?.[memberIndex];
if (member) {
if (input.closest('[label="Full Name"]')) value = member.name;
else if (input.closest('[label="Email"]')) value = member.email;
else if (input.closest('[label="Join Date"]')) value = member.joinDate;
else if (input.closest('[label="Current Role (Optional)"]')) value = member.role;
}
}
// Fallback to input.value
else {
value = input.value?.trim() || "";
}
// Create a span element to replace the input for print
const printSpan = document.createElement("span");
printSpan.className = "print-value";
if (value) {
// Has a value - show it in bold
printSpan.textContent = value;
printSpan.style.fontWeight = "bold";
} else {
// Empty field - show as blank line
printSpan.innerHTML = "&nbsp;";
printSpan.setAttribute("data-empty", "true");
}
printSpan.setAttribute("data-original-index", index);
// Store original element info for restoration
printValues.push({
element: input,
parent: input.parentNode,
nextSibling: input.nextSibling,
replacement: printSpan,
});
// Replace input with span for print
input.style.display = "none";
input.parentNode.insertBefore(printSpan, input);
});
// Trigger print
setTimeout(() => {
window.print();
// Restore original inputs after print dialog
setTimeout(() => {
printValues.forEach((item) => {
item.element.style.display = "";
if (item.replacement.parentNode) {
item.replacement.parentNode.removeChild(item.replacement);
}
});
// Restore signature section
if (signatureReplacement && signatureSpace) {
signatureSpace.style.display = "";
if (signatureReplacement.parentNode) {
signatureReplacement.parentNode.removeChild(signatureReplacement);
}
}
}, 100);
}, 100);
};
// Export data for the ExportOptions component
const exportData = computed(() => ({
formData: formData.value,
cooperativeName: formData.value.cooperativeName || "Worker Cooperative",
dateEstablished: formData.value.dateEstablished,
purpose: formData.value.purpose,
coreValues: formData.value.coreValues,
members: formData.value.members,
trialPeriodMonths: formData.value.trialPeriodMonths,
policies: {
buyInAmount: formData.value.buyInAmount,
noticeDays: formData.value.noticeDays,
surplusPayoutDays: formData.value.surplusPayoutDays,
buyInReturnDays: formData.value.buyInReturnDays,
dayToDayLimit: formData.value.dayToDayLimit,
regularDecisionMin: formData.value.regularDecisionMin,
regularDecisionMax: formData.value.regularDecisionMax,
majorDebtThreshold: formData.value.majorDebtThreshold,
meetingFrequency: formData.value.meetingFrequency,
emergencyNoticeHours: formData.value.emergencyNoticeHours,
baseRate: formData.value.baseRate,
},
exportedAt: new Date().toISOString(),
section: "membership-agreement",
}));
</script>
<style scoped>
@reference "tailwindcss";
/* Template-specific styles not in main.css */
.template-content {
max-width: none;
line-height: 1.6;
color: #1f2937;
}
/* Fiscal year group styling */
.fiscal-year-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
.fiscal-year-group .flex {
align-items: center;
}
/* Signature space styling */
.signature-space {
margin-top: 2rem;
padding: 2rem 1rem;
border: 1px dashed #d1d5db;
border-radius: 0.375rem;
background: #f9fafb;
min-height: 4rem;
display: flex;
align-items: center;
justify-content: center;
}
/* Review dates styling */
.review-dates {
background: #f9fafb;
padding: 1rem;
border-radius: 0.375rem;
border-left: 3px solid #3b82f6;
}
/* Font families */
.template-content.font-source-serif,
.template-content.font-source-serif * {
font-family: "Source Serif 4", "Source Serif Pro", Georgia, serif !important;
}
.template-content.font-ubuntu,
.template-content.font-ubuntu * {
font-family: "Ubuntu", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
}
.template-content.font-inter,
.template-content.font-inter * {
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
}
</style>