Interactive ROI Calculator Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Implement a dynamic ROI calculator on the homepage that users can interact with to see potential savings from automation, with all text and logic configuration managed via Obsidian.

Architecture: Use ObsidianSyncCommand to parse a new roi-calculator.md file and store the configuration (multipliers, labels, thresholds) in the PageMeta model. Create a reactive Vue component for the UI and integrate it into the homepage.

Tech Stack: Laravel, Vue 3, Tailwind CSS, Spatie Yaml Front Matter.


Task 1: Obsidian Source & Model Verification

Files:

  • Create: content/Home/roi-calculator.md

  • Verify: content/masri-programmer-app/app/Models/PageMeta.php

  • Step 1: Create the Obsidian source file

---
title: Calculate Your Automation ROI
description: See how much time and money your business could save by automating manual tasks.
efficiency_multiplier: 0.7
cta_threshold: 100000
labels:
  team_size: "Team Size"
  manual_hours: "Manual Hours / Week / Person"
  hourly_cost: "Average Cost / Hour"
  annual_savings: "Potential Annual Savings"
  weekly_savings: "Weekly Savings"
  cta_default: "Book Your Free Discovery Call"
  cta_high_value: "Apply for Private Automation Audit"
---
# Why Automate?
Beyond just saving money, automation reduces burnout and allows your best people to focus on what matters.
  • Step 2: Commit the Obsidian file
git add content/Home/roi-calculator.md
git commit -m "content: add ROI calculator source in Obsidian"

Task 2: Update ObsidianSyncCommand

Files:

  • Modify: content/masri-programmer-app/app/Console/Commands/ObsidianSyncCommand.php

  • Step 1: Update the sync command to handle ROI calculator data

// In ObsidianSyncCommand.php
 
// In handle()
$this->syncHomeSections($obsidianRoot . 'Home');
 
// Add new method
protected function syncHomeSections($path)
{
    if (!File::isDirectory($path)) return;
 
    $file = $path . '/roi-calculator.md';
    if (File::exists($file)) {
        $object = YamlFrontMatter::parse(File::get($file));
        \App\Models\PageMeta::updateOrCreate(
            ['page_key' => 'roi_calculator'],
            [
                'meta_title' => $object->matter('title'),
                'meta_description' => $object->matter('description'),
                'keywords' => json_encode([
                    'multiplier' => $object->matter('efficiency_multiplier'),
                    'threshold' => $object->matter('cta_threshold'),
                    'labels' => $object->matter('labels'),
                    'body' => $object->body(),
                ]),
            ]
        );
        $this->info('ROI Calculator synced.');
    }
}
  • Step 2: Run the sync command and verify database

Run: php artisan obsidian:sync Run: php artisan tinker --execute="echo \App\Models\PageMeta::where('page_key', 'roi_calculator')->first()->meta_title;" Expected: “Calculate Your Automation ROI”

  • Step 3: Commit changes
git add content/masri-programmer-app/app/Console/Commands/ObsidianSyncCommand.php
git commit -m "feat: sync ROI calculator config from Obsidian"

Task 3: Create ROICalculator Component

Files:

  • Create: content/masri-programmer-app/resources/js/pages/home/ROICalculator.vue

  • Step 1: Implement the Vue component with reactive logic

<script setup lang="ts">
import { ref, computed } from 'vue';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Slider } from '@/components/ui/slider';
import { Button } from '@/components/ui/button';
import { TrendingUp, Clock, Users, Euro } from 'lucide-vue-next';
 
const props = defineProps<{
    config: {
        title: string;
        description: string;
        multiplier: number;
        threshold: number;
        labels: Record<string, string>;
    }
}>();
 
const teamSize = ref([10]);
const manualHours = ref([5]);
const hourlyCost = ref([50]);
 
const annualSavings = computed(() => {
    return Math.round(teamSize.value[0] * manualHours.value[0] * hourlyCost.value[0] * props.config.multiplier * 52);
});
 
const isHighValue = computed(() => annualSavings.value >= props.config.threshold);
 
const formatCurrency = (val: number) => {
    return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(val);
};
</script>
 
<template>
    <div class="relative overflow-hidden rounded-3xl border bg-background/50 p-8 backdrop-blur-xl md:p-12 shadow-2xl">
        <div class="grid gap-12 lg:grid-cols-2">
            <div class="space-y-8">
                <div>
                    <h3 class="text-3xl font-bold tracking-tight text-foreground">{{ config.title }}</h3>
                    <p class="mt-4 text-muted-foreground">{{ config.description }}</p>
                </div>
 
                <div class="space-y-6">
                    <div class="space-y-4">
                        <div class="flex justify-between text-sm font-medium">
                            <span class="flex items-center gap-2"><Users class="h-4 w-4" /> {{ config.labels.team_size }}</span>
                            <span class="text-primary font-bold">{{ teamSize[0] }}</span>
                        </div>
                        <Slider v-model="teamSize" :min="1" :max="200" :step="1" />
                    </div>
 
                    <div class="space-y-4">
                        <div class="flex justify-between text-sm font-medium">
                            <span class="flex items-center gap-2"><Clock class="h-4 w-4" /> {{ config.labels.manual_hours }}</span>
                            <span class="text-primary font-bold">{{ manualHours[0] }}h</span>
                        </div>
                        <Slider v-model="manualHours" :min="1" :max="40" :step="1" />
                    </div>
 
                    <div class="space-y-4">
                        <div class="flex justify-between text-sm font-medium">
                            <span class="flex items-center gap-2"><Euro class="h-4 w-4" /> {{ config.labels.hourly_cost }}</span>
                            <span class="text-primary font-bold">€{{ hourlyCost[0] }}</span>
                        </div>
                        <Slider v-model="hourlyCost" :min="20" :max="150" :step="5" />
                    </div>
                </div>
            </div>
 
            <div class="flex flex-col items-center justify-center rounded-2xl bg-primary/5 p-8 text-center border border-primary/10">
                <TrendingUp class="h-12 w-12 text-primary mb-4" />
                <h4 class="text-sm font-semibold uppercase tracking-wider text-muted-foreground">{{ config.labels.annual_savings }}</h4>
                <div class="mt-2 text-5xl font-black text-foreground md:text-6xl tabular-nums">
                    {{ formatCurrency(annualSavings) }}
                </div>
                <p class="mt-4 text-muted-foreground">{{ config.labels.weekly_savings }}: {{ formatCurrency(annualSavings / 52) }}</p>
                
                <Button size="lg" class="mt-8 w-full py-6 text-lg font-bold shadow-lg shadow-primary/20 transition-all hover:scale-105" :variant="isHighValue ? 'default' : 'secondary'" @click="saveAndBook">
                    {{ isHighValue ? config.labels.cta_high_value : config.labels.cta_default }}
                </Button>
            </div>
        </div>
    </div>
</template>
 
<script setup lang="ts">
// ... (rest of imports)
import { router } from '@inertiajs/vue3';
 
const saveAndBook = () => {
    localStorage.setItem('masri_roi_context', JSON.stringify({
        teamSize: teamSize.value[0],
        manualHours: manualHours.value[0],
        hourlyCost: hourlyCost.value[0],
        savings: annualSavings.value
    }));
    router.visit(route('contact'));
};
</script>
  • Step 2: Commit component
git add content/masri-programmer-app/resources/js/pages/home/ROICalculator.vue
git commit -m "feat: create ROICalculator component"

Task 4: Integrate into Homepage

Files:

  • Modify: content/masri-programmer-app/resources/js/pages/home/Homepage.vue

  • Modify: content/masri-programmer-app/app/Http/Controllers/ContentController.php (optional if home already has data)

  • Step 1: Update Homepage.vue to accept and render ROI config

// In Homepage.vue
 
import ROICalculator from './ROICalculator.vue';
 
// Define props if not already
const props = defineProps<{
    roiConfig?: any;
    // ...
}>();
 
// In template, insert where appropriate
<swr id="roi-calculator" class="py-24">
    <ROICalculator v-if="roiConfig" :config="roiConfig" />
</swr>
  • Step 2: Update the Home Route/Controller to pass the config
// If the route is a closure in web.php
Route::get('/', function () {
    $roi = \App\Models\PageMeta::where('page_key', 'roi_calculator')->first();
    $roiConfig = null;
    if ($roi) {
        $meta = json_decode($roi->keywords, true);
        $roiConfig = [
            'title' => $roi->meta_title,
            'description' => $roi->meta_description,
            'multiplier' => $meta['multiplier'] ?? 0.7,
            'threshold' => $meta['threshold'] ?? 100000,
            'labels' => $meta['labels'] ?? [],
        ];
    }
 
    return Inertia::render('home/Homepage', [
        'roiConfig' => $roiConfig
    ]);
})->name('home');
  • Step 3: Commit integration
git add content/masri-programmer-app/resources/js/pages/home/Homepage.vue content/masri-programmer-app/routes/web.php
git commit -m "feat: integrate ROI calculator into homepage"

Task 5: Final Verification

  • Step 1: Check the site

Verify the calculator appears on the homepage and math works. Change a label in Obsidian, sync, and verify it updates on the site.

  • Step 2: Cleanup

Remove any temporary tinker commands or logs.