529 lines
16 KiB
Markdown
529 lines
16 KiB
Markdown
# 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:**
|
|
```vue
|
|
<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:**
|
|
```js
|
|
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:**
|
|
```js
|
|
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:
|
|
```vue
|
|
<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:**
|
|
```js
|
|
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:**
|
|
```vue
|
|
<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:**
|
|
```vue
|
|
<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:**
|
|
```vue
|
|
<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:**
|
|
```vue
|
|
<!-- 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:**
|
|
```vue
|
|
<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):**
|
|
```vue
|
|
<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.
|