Teren-app/REPORTS_FRONTEND_REWORK_PLAN.md
2026-01-02 12:32:20 +01:00

16 KiB

Reports Frontend Rework Plan

Overview

This plan outlines the modernization of Reports frontend pages (Index.vue and Show.vue) using shadcn-vue components and AppCard containers, following the same patterns established in the Settings pages rework.

Current State Analysis

Reports/Index.vue (30 lines)

Current Implementation:

  • Simple grid layout with native divs
  • Report cards: border rounded-lg p-4 bg-white shadow-sm hover:shadow-md
  • Grid: md:grid-cols-2 lg:grid-cols-3
  • Each card shows: name (h2), description (p), Link to report
  • No shadcn-vue components used

Identified Issues:

  • Native HTML/Tailwind instead of shadcn-vue Card
  • Inconsistent with Settings pages styling
  • No icons for visual interest
  • Basic hover effects only

Reports/Show.vue (314 lines)

Current Implementation:

  • Complex page with filters, export buttons, and data table
  • Header section: title, description, export buttons (lines 190-196)
    • Buttons: px-3 py-2 rounded bg-gray-200 hover:bg-gray-300
  • Filter section: grid layout md:grid-cols-4 (lines 218-270)
    • Native inputs: border rounded px-2 py-1
    • Native selects: border rounded px-2 py-1
    • DatePicker component (already working)
    • Filter buttons: Apply (bg-indigo-600) and Reset (bg-gray-100)
  • Data table: DataTableServer component (lines 285-300)
  • Formatting functions: formatNumberEU, formatDateEU, formatDateTimeEU, formatCell

Identified Issues:

  • No Card containers for sections
  • Native buttons instead of shadcn Button
  • Native input/select elements instead of shadcn Input/Select
  • No visual separation between sections
  • Filter section could be extracted to partial

Target Architecture

Pattern Reference from Settings Pages

Settings/Index.vue Pattern:

<Card class="hover:shadow-lg transition-shadow">
  <CardHeader>
    <div class="flex items-center gap-2">
      <component :is="icon" class="h-5 w-5 text-muted-foreground" />
      <CardTitle>Title</CardTitle>
    </div>
    <CardDescription>Description</CardDescription>
  </CardHeader>
  <CardContent>
    <Button variant="ghost">Action </Button>
  </CardContent>
</Card>

Settings/Archive/Index.vue Pattern:

  • Uses AppCard for main container
  • Extracted partials: ArchiveRuleCard, CreateRuleForm, EditRuleForm
  • Alert components for warnings
  • Badge components for status indicators

Implementation Plan

Phase 1: Reports/Index.vue Rework (Simple)

Goal: Replace native divs with shadcn-vue Card components, add icons

Changes:

  1. Import shadcn-vue components:

    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/Components/ui/card";
    import { Button } from "@/Components/ui/button";
    import { BarChart3, FileText, Activity, Users, TrendingUp, Calendar } from "lucide-vue-next";
    
  2. Add icon mapping for reports:

    const reportIcons = {
      'contracts': FileText,
      'field': TrendingUp,
      'activities': Activity,
      // fallback icon
      default: BarChart3,
    };
    
    function getReportIcon(category) {
      return reportIcons[category] || reportIcons.default;
    }
    
  3. Replace report card structure:

    • Remove native <div class="border rounded-lg p-4 bg-white shadow-sm hover:shadow-md">
    • Use <Card class="hover:shadow-lg transition-shadow cursor-pointer">
    • Structure:
      <Card>
        <CardHeader>
          <div class="flex items-center gap-2">
            <component :is="getReportIcon(report.category)" class="h-5 w-5 text-muted-foreground" />
            <CardTitle>{{ report.name }}</CardTitle>
          </div>
          <CardDescription>{{ report.description }}</CardDescription>
        </CardHeader>
        <CardContent>
          <Link :href="route('reports.show', report.slug)">
            <Button variant="ghost" size="sm" class="w-full justify-start">
              Odpri  
            </Button>
          </Link>
        </CardContent>
      </Card>
      
  4. Update page header:

    • Wrap in proper container with consistent spacing
    • Match Settings/Index.vue header style

Estimated Changes:

  • Lines: 30 → ~65 lines (with imports and icon logic)
  • Files modified: 1 (Index.vue)
  • Files created: 0

Risk Level: Low (simple page, straightforward replacement)


Phase 2: Reports/Show.vue Rework - Structure (Medium)

Goal: Add Card containers for sections, replace native buttons

Changes:

  1. Import shadcn-vue components:

    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/Components/ui/card";
    import { Button } from "@/Components/ui/button";
    import { Input } from "@/Components/ui/input";
    import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/Components/ui/select";
    import { Label } from "@/Components/ui/label";
    import { Badge } from "@/Components/ui/badge";
    import { Separator } from "@/Components/ui/separator";
    import { Download, Filter, RotateCcw } from "lucide-vue-next";
    
  2. Wrap header + export buttons in Card:

    <Card class="mb-6">
      <CardHeader>
        <div class="flex items-start justify-between">
          <div>
            <CardTitle>{{ name }}</CardTitle>
            <CardDescription v-if="description">{{ description }}</CardDescription>
          </div>
          <div class="flex gap-2">
            <Button variant="outline" size="sm" @click="exportFile('csv')">
              <Download class="mr-2 h-4 w-4" />
              CSV
            </Button>
            <Button variant="outline" size="sm" @click="exportFile('pdf')">
              <Download class="mr-2 h-4 w-4" />
              PDF
            </Button>
            <Button variant="outline" size="sm" @click="exportFile('xlsx')">
              <Download class="mr-2 h-4 w-4" />
              Excel
            </Button>
          </div>
        </div>
      </CardHeader>
    </Card>
    
  3. Wrap filters in Card:

    <Card class="mb-6">
      <CardHeader>
        <div class="flex items-center gap-2">
          <Filter class="h-5 w-5 text-muted-foreground" />
          <CardTitle>Filtri</CardTitle>
        </div>
      </CardHeader>
      <CardContent>
        <!-- Filter grid here -->
        <div class="grid gap-4 md:grid-cols-4">
          <!-- Filter inputs -->
        </div>
        <Separator class="my-4" />
        <div class="flex gap-2">
          <Button @click="applyFilters">
            <Filter class="mr-2 h-4 w-4" />
            Prikaži
          </Button>
          <Button variant="outline" @click="resetFilters">
            <RotateCcw class="mr-2 h-4 w-4" />
            Ponastavi
          </Button>
        </div>
      </CardContent>
    </Card>
    
  4. Wrap DataTableServer in Card:

    <Card>
      <CardHeader>
        <CardTitle>Rezultati</CardTitle>
        <CardDescription>
          Skupaj {{ meta?.total || 0 }} {{ meta?.total === 1 ? 'rezultat' : 'rezultatov' }}
        </CardDescription>
      </CardHeader>
      <CardContent>
        <DataTableServer
          <!-- props -->
        />
      </CardContent>
    </Card>
    
  5. Replace all native buttons with shadcn Button:

    • Export buttons: variant="outline" size="sm"
    • Apply filter button: default variant
    • Reset button: variant="outline"

Estimated Changes:

  • Lines: 314 → ~350 lines (with imports and Card wrappers)
  • Files modified: 1 (Show.vue)
  • Files created: 0
  • Keep formatting functions unchanged (working correctly)

Risk Level: Low-Medium (more complex but no logic changes)


Phase 3: Reports/Show.vue - Replace Native Inputs (Medium)

Goal: Replace native input/select elements with shadcn-vue components

Changes:

  1. Replace date inputs:

    <!-- Keep DatePicker as-is (already working) -->
    <div class="space-y-2">
      <Label>{{ inp.label || inp.key }}</Label>
      <DatePicker
        v-model="filters[inp.key]"
        format="dd.MM.yyyy"
        placeholder="Izberi datum"
      />
    </div>
    
  2. Replace text/number inputs:

    <div class="space-y-2">
      <Label>{{ inp.label || inp.key }}</Label>
      <Input
        v-model="filters[inp.key]"
        :type="inp.type === 'integer' ? 'number' : 'text'"
        placeholder="Vnesi vrednost"
      />
    </div>
    
  3. Replace select inputs (user/client):

    <div class="space-y-2">
      <Label>{{ inp.label || inp.key }}</Label>
      <Select v-model="filters[inp.key]">
        <SelectTrigger>
          <SelectValue placeholder="— brez —" />
        </SelectTrigger>
        <SelectContent>
          <SelectItem :value="null"> brez </SelectItem>
          <SelectItem v-for="u in userOptions" :key="u.id" :value="u.id">
            {{ u.name }}
          </SelectItem>
        </SelectContent>
      </Select>
      <div v-if="userLoading" class="text-xs text-muted-foreground">Nalagam</div>
    </div>
    
  4. Update filter grid layout:

    • Change from md:grid-cols-4 to md:grid-cols-2 lg:grid-cols-4
    • Use space-y-2 for label/input spacing
    • Consistent gap: gap-4

Estimated Changes:

  • Lines: ~350 → ~380 lines (shadcn Input/Select have more markup)
  • Files modified: 1 (Show.vue)
  • Files created: 0

Risk Level: Medium (v-model binding changes, test thoroughly)


Phase 4: Optional - Extract Filter Section Partial (Optional)

Goal: Reduce Show.vue complexity by extracting filter logic

Decision Criteria:

  • If filter section exceeds ~80 lines → extract to partial
  • If multiple filter types need separate handling → extract

Potential Partial Structure:

resources/js/Pages/Reports/Partials/
  FilterSection.vue

FilterSection.vue:

  • Props: inputs, filters (reactive object), userOptions, clientOptions, loading states
  • Emits: @apply, @reset
  • Contains: entire filter grid + buttons

Benefits:

  • Show.vue reduced from ~380 lines to ~300 lines
  • Filter logic isolated and reusable
  • Easier to maintain filter types

Risks:

  • Adds complexity with props/emits
  • Might not be worth it if filter logic is simple

Recommendation: Evaluate after Phase 3 completion. If filter section is clean and under 80 lines, skip this phase.


Component Inventory

shadcn-vue Components Needed

Already Installed (verify):

  • Card, CardHeader, CardTitle, CardDescription, CardContent
  • Button
  • Input
  • Select, SelectTrigger, SelectValue, SelectContent, SelectItem
  • Label
  • Badge
  • Separator

Need to Check:

  • lucide-vue-next icons (Download, Filter, RotateCcw, BarChart3, FileText, Activity, TrendingUp, Calendar)

Custom Components

  • AppCard (if needed for consistency)
  • DatePicker (already working, keep as-is)
  • DataTableServer (keep as-is)

Testing Checklist

Reports/Index.vue Testing:

  • Cards display with correct icons
  • Card hover effects work
  • Links navigate to correct report
  • Grid layout responsive (2 cols MD, 3 cols LG)
  • Icons match report categories

Reports/Show.vue Testing:

  • Header Card displays title, description, export buttons
  • Export buttons work (CSV, PDF, Excel)
  • Filter Card displays all filter inputs correctly
  • Date filters use DatePicker component
  • User/Client selects load options async
  • Apply filters button triggers report refresh
  • Reset button clears all filters
  • DataTableServer Card displays results
  • Formatting functions work (dates, numbers, currencies)
  • Pagination works
  • All 6 reports render correctly:
    • active-contracts
    • field-jobs-completed
    • decisions-counts
    • segment-activity-counts
    • actions-decisions-counts
    • activities-per-period

Implementation Order

Step 1: Reports/Index.vue (30 min)

  1. Import shadcn-vue components + icons
  2. Add icon mapping function
  3. Replace native divs with Card structure
  4. Test navigation and layout
  5. Verify responsive grid

Step 2: Reports/Show.vue - Structure (45 min)

  1. Import shadcn-vue components + icons
  2. Wrap header + exports in Card
  3. Wrap filters in Card
  4. Wrap DataTableServer in Card
  5. Replace all native buttons
  6. Test all 6 reports

Step 3: Reports/Show.vue - Inputs (60 min)

  1. Replace text/number inputs with shadcn Input
  2. Replace select inputs with shadcn Select
  3. Add Label components
  4. Test v-model bindings
  5. Test async user/client loading
  6. Test filter apply/reset
  7. Verify all filter types work

Step 4: Optional Partial Extraction (30 min, if needed)

  1. Create FilterSection.vue partial
  2. Move filter logic to partial
  3. Set up props/emits
  4. Test with all reports

Step 5: Final Testing (30 min)

  1. Test complete workflow (Index → Show → Filters → Export)
  2. Verify all 6 reports
  3. Test responsive layouts (mobile, tablet, desktop)
  4. Check formatting consistency
  5. Verify no regressions

Total Estimated Time: 2.5 - 3.5 hours


Risk Assessment

Low Risk:

  • Index.vue rework (simple structure, straightforward replacement)
  • Adding Card containers to Show.vue
  • Replacing native buttons with shadcn Button

Medium Risk:

  • Replacing native inputs with shadcn Input/Select
    • v-model bindings might need adjustments
    • Async select loading needs testing
    • Number input behavior might differ

Mitigation Strategies:

  1. Test each phase incrementally
  2. Keep formatting functions unchanged (already working)
  3. Test v-model bindings immediately after input replacement
  4. Verify async loading with console logs
  5. Test all 6 reports after each phase
  6. Keep git commits small and atomic

Success Criteria

Functional Requirements:

All reports navigate from Index page
All filters work correctly (date, text, number, user select, client select)
Apply filters refreshes report data
Reset filters clears all inputs
Export buttons generate CSV/PDF/Excel files
DataTableServer displays results correctly
Pagination works
Formatting functions work (dates, numbers)

Visual Requirements:

Consistent Card-based layout
shadcn-vue components throughout
Icons for visual interest
Hover effects on cards
Proper spacing and alignment
Responsive layout (mobile, tablet, desktop)
Matches Settings pages style

Code Quality:

No code duplication
Clean component imports
Consistent naming conventions
Proper TypeScript/Vue 3 patterns
Formatting functions unchanged
No regressions in functionality


Notes

  • DatePicker component: Already working, imported correctly, no changes needed
  • Formatting functions: Keep unchanged (formatNumberEU, formatDateEU, formatDateTimeEU, formatCell)
  • DataTableServer: Keep as-is, already working well
  • Async loading: User/client select loading works, just needs shadcn Select wrapper
  • Pattern consistency: Follow Settings/Index.vue and Settings/Archive/Index.vue patterns
  • Icon usage: Add icons to Index.vue for visual interest, use lucide-vue-next
  • Button variants: Use variant="outline" for secondary actions, default for primary

Post-Implementation

After completing all phases:

  1. Documentation:

    • Update this document with actual implementation notes
    • Document any deviations from plan
    • Note any unexpected issues
  2. Code Review:

    • Check for consistent component usage
    • Verify no native HTML/CSS buttons/inputs remain
    • Ensure proper import structure
  3. User Feedback:

    • Test with actual users
    • Gather feedback on UI improvements
    • Note any requested adjustments
  4. Performance:

    • Verify no performance regressions
    • Check bundle size impact
    • Monitor async loading times

Conclusion

This plan provides a structured approach to modernizing the Reports frontend pages using shadcn-vue components. The phased approach allows for incremental testing and reduces risk. The estimated total time is 2.5-3.5 hours, with low to medium risk level.

Recommendation: Start with Phase 1 (Index.vue) as a proof of concept, then proceed to Phase 2 and 3 for Show.vue. Evaluate Phase 4 (partial extraction) after Phase 3 completion based on actual complexity.