New report system and views
This commit is contained in:
@@ -0,0 +1,528 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user