-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Saved Custom Report Templates #15714
Merged
Merged
Changes from 174 commits
Commits
Show all changes
178 commits
Select commit
Hold shift + click to select a range
f9fc2a4
alphatyping for layout
akemidx c9fcc90
create saved reports migration
akemidx 78d589f
beginning of migrations
akemidx 734af87
template structuring
akemidx f1cc2c8
submitting form after capturing/formatting fixes
akemidx 45dbc02
save commit
akemidx e5792fd
api/savereports controller
akemidx 7a5faa9
Adjust margin on custom report page
marcusmoore 06186c9
WIP: Simply post the form to a different controller
marcusmoore b7011d8
WIP: add methods to restore settings from saved report
marcusmoore 4f03114
Scaffold a couple test cases
marcusmoore bca7f20
Implement restoring select values
marcusmoore 505d601
Implement restoring date ranges
marcusmoore 5041c07
WIP: implement restoring checkbox inputs
marcusmoore 327d275
Merge branch 'saved-custom-reports' into saving_custom_report_template
marcusmoore b7f6c7d
html work, code comments for tomorrow's tasks
akemidx 4a0bb31
Scaffold two more test cases
marcusmoore bd86c54
Add a few typehints
marcusmoore e636d7b
multiple saved reports, beginning of dynamic dropdown listing/queried…
akemidx c3b53b2
Allow saving custom reports
marcusmoore c9157dc
Update docblock
marcusmoore c3845f4
Add tests around loading saved reports
marcusmoore 52028dd
Add authorization to saving saved reports route
marcusmoore b9cda88
Alphabetize saved reports list
marcusmoore 89c47c1
Add validation for saving reports
marcusmoore c68a2a3
Add test case for saving custom reports
marcusmoore d92893b
Remove old javascript
marcusmoore e791ebb
Display report name in input
marcusmoore e9e6817
frontend stuff
akemidx ca35b66
frontys
akemidx ad202be
update method (WIP ver)
akemidx 75bd056
Display saved template name in header
marcusmoore 27bb938
WIP: add dedicated edit report page
marcusmoore 9fcb1a2
Rename SavedReport to ReportTemplate
marcusmoore ebf760a
translations, UI fixes
akemidx b2d0cbb
Display validation error for report name
marcusmoore c35179b
Use existing class in place of inline styling
marcusmoore 26cc449
Use dedicated show route for report templates
marcusmoore cf5c780
Only show template name input on default screen
marcusmoore 0527201
Allow templates to be updated
marcusmoore 9d062f9
Make control statements more explicit
marcusmoore 0504c09
Implement ability to delete templates
marcusmoore 9a5c8c4
Formatting and clean ups
marcusmoore 578495b
Update tests
marcusmoore fda7717
Simplify url
marcusmoore 495d74f
Formatting
marcusmoore 71761a4
A few more small clean ups
marcusmoore 6fcbb10
Implement test for filtering out invalid models from selectValues
marcusmoore 5c0c60a
Formatting
marcusmoore 2eeaef0
Improve readability
marcusmoore 1dd9273
Improve readability
marcusmoore 62f8353
Implement model filtering for selectValue method
marcusmoore 3e5f804
Display "Save" on the update template page button
marcusmoore 8a496cc
Formatting
marcusmoore 8785392
Formatting
marcusmoore 4c62e8a
Add guard against attempting to access property on unsaved template
marcusmoore dc27e67
Remove edit and delete buttons from edit template view
marcusmoore 7f0e3e2
Scaffold additional test
marcusmoore 92e3a1e
Update variable names
marcusmoore 48e5ee2
Add filtering to remaining selects
marcusmoore 4fc8e8d
Add todo
marcusmoore 7f153b3
Always return an array from selectValues method
marcusmoore 8c434c7
Implement and scaffold tests
marcusmoore 618bbc4
Mark test as incomplete
marcusmoore 1ac0be5
Remove test case
marcusmoore a23a3b9
Partially implement test
marcusmoore 9e0897b
Remove old comments
marcusmoore fcef604
Implement radioValue properly
marcusmoore 137193a
Remove duplicate test
marcusmoore d8d92a6
Scaffold test case
marcusmoore 740d46a
Repopulate report after validation error
marcusmoore a756d2b
Implement test
marcusmoore 25cba65
Remove marker test case
marcusmoore f2d34b2
Remove inline javascript
marcusmoore 2710312
messages/translations
akemidx a5099b5
translations/messages on report template controller
akemidx 5f8e914
clarifying name box
akemidx 6f6341b
about saved reports box
akemidx b34886e
Move box header into box
marcusmoore 0202a97
Add missing tag
marcusmoore 82df7a6
Add form label and remove info box from show and edit pages
marcusmoore e5fb888
Implement test
marcusmoore 8d8bf73
Scaffold additional tests
marcusmoore 9c1bea0
Add failing test
marcusmoore d72970b
Add global scope to limit template to current user
marcusmoore 20bd832
standardizing naming to use Template
akemidx 2768f19
code cleanup
akemidx 0883321
Only limit template creator scope when authenticated
marcusmoore 5a396cc
Add assertion
marcusmoore 4d8d069
Update placeholder
marcusmoore 82f4cc7
Improve variable name
marcusmoore 0ac1dd3
Organize tests
marcusmoore 691e81d
Implement some tests
marcusmoore bbfee27
Implement test
marcusmoore 1a1f417
Change variable name to keep foreach scoped
marcusmoore b24d806
Add clarifying comments
marcusmoore 786c41a
Add some type hints
marcusmoore f64aa4d
Improve variable name
marcusmoore 861ef63
Use is_iterable for checks instead of is_array
marcusmoore 1630392
Uncheck a couple boxes by default to match existing behavior
marcusmoore b74115b
Add docblocks
marcusmoore 2195402
Add trailing commas
marcusmoore 530ea47
Merge branch 'develop' into saving_custom_report_template
marcusmoore 0881301
Fix language strings
marcusmoore 3952fc1
Re-render order number properly
marcusmoore 2d56312
Indent
marcusmoore d65c0c8
Remove comma
marcusmoore 8bba11e
Merge branch 'develop' into saving_custom_report_template
marcusmoore b51c505
Fix department, manufacturer, and category multi-selects
marcusmoore 8e0b718
Scope Select All checkbox to fields on left side of page
marcusmoore 89e2d03
Fix the deleted assets radio
marcusmoore 6b70443
Formatting
marcusmoore 6e16f58
Remove reference to old trait
marcusmoore 5306e1c
Merge branch 'develop' into saving_custom_report_template
marcusmoore a20e03f
Merge branch 'develop' into saving_custom_report_template
marcusmoore dd97e4e
Update permission tests
marcusmoore d953519
Merge branch 'develop' into saving_custom_report_template
marcusmoore 02c22c9
Add test case
marcusmoore 5ebf013
Add comment
marcusmoore 37d65da
Add todo
marcusmoore c313a78
Remove outdated @since annotations
marcusmoore b6aae1f
Group routes
marcusmoore 271de1e
Improve tests
marcusmoore 3d28a90
Improve test
marcusmoore 4217d74
Improve tests
marcusmoore 94e168f
Fix label
marcusmoore 30dc5fa
Allow retrieving eol start and end dates
marcusmoore c1aa338
Add error message for eol start and end fields
marcusmoore 853e14f
Fix width
marcusmoore 8a06f4a
Improve label
marcusmoore 2fcc7e1
Make template name required
marcusmoore 3616c92
Reflash template name
marcusmoore e390a95
Improve back button behavior
marcusmoore 84f6638
Add authorization check
marcusmoore 7238238
Use created_by instead of user_id
marcusmoore 54dec8d
Add translations
marcusmoore b45749a
Update translation strings
marcusmoore ee00699
Remove unused strings
marcusmoore c881727
Add redirect for missing template
marcusmoore d4cf392
Organize tests
marcusmoore 2042733
Switch to array syntax
marcusmoore 7777147
Add custom-reporting group
marcusmoore 7c08fbe
Update test name
marcusmoore 0d58ac6
Make options required in model
marcusmoore b97c54a
Improve handling of old data for text inputs
marcusmoore 45e98e0
Merge branch 'develop' into saving_custom_report_template
marcusmoore 0eadab4
Add soft deletes
marcusmoore 54b4db8
Revert "Add soft deletes"
marcusmoore 702edf7
Remove x from select dropdown
marcusmoore 3c75fc2
Move test
marcusmoore 86762c5
Remove marker test case
marcusmoore 35f8a71
Test organization
marcusmoore 930ef3f
Work on tests for activity logging
marcusmoore 59e6874
Scaffold test cases
marcusmoore 0cc3031
Implement deleted log
marcusmoore d727b03
Implement activity log test
marcusmoore e871481
Add group
marcusmoore 20dab4d
Simplify tests
marcusmoore 4aedbb5
Implement activity logging transformer tests
marcusmoore ae24b73
Fix action_type from "created" to" create"
marcusmoore 5f83cb6
Use route model binding
marcusmoore 41f2534
Populate select with selected template
marcusmoore 4daa8e7
Indenting
marcusmoore 37d7923
Update page title dynamically
marcusmoore 8873137
Scaffold updating template name
marcusmoore c5710b8
Add test validation test for update method and remove name uniqueness…
marcusmoore 7862b74
Inline fields when updating
marcusmoore 4aa5961
Update page titles
marcusmoore f8d0ddb
Improve template name input
marcusmoore 363ec84
Re-introduce soft deletes
marcusmoore 0e3efdf
Add string to name validation
marcusmoore b8265d5
Improve comment
marcusmoore dc0b8c7
Inline route helpers in tests
marcusmoore 4bb1915
Move test
marcusmoore 7373e20
Improve comment
marcusmoore 5574c5f
Improve comments
marcusmoore 53de97d
Merge branch 'develop' into saving_custom_report_template
marcusmoore 92f33c0
Merge branch 'develop' into saving_custom_report_template
marcusmoore File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
namespace App\Http\Controllers; | ||
|
||
use App\Models\CustomField; | ||
use App\Models\ReportTemplate; | ||
use Illuminate\Http\RedirectResponse; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Arr; | ||
|
||
class ReportTemplatesController extends Controller | ||
{ | ||
public function store(Request $request): RedirectResponse | ||
{ | ||
$this->authorize('reports.view'); | ||
|
||
// Ignore "options" rules since data does not come in under that key... | ||
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options')); | ||
|
||
$report = $request->user()->reportTemplates()->create([ | ||
'name' => $validated['name'], | ||
'options' => $request->except(['_token', 'name']), | ||
]); | ||
|
||
session()->flash('success', trans('admin/reports/message.create.success')); | ||
|
||
return redirect()->route('report-templates.show', $report->id); | ||
} | ||
|
||
public function show(ReportTemplate $reportTemplate) | ||
{ | ||
$this->authorize('reports.view'); | ||
|
||
$customfields = CustomField::get(); | ||
$report_templates = ReportTemplate::orderBy('name')->get(); | ||
|
||
return view('reports/custom', [ | ||
'customfields' => $customfields, | ||
'report_templates' => $report_templates, | ||
'template' => $reportTemplate, | ||
]); | ||
} | ||
|
||
public function edit(ReportTemplate $reportTemplate) | ||
{ | ||
$this->authorize('reports.view'); | ||
|
||
return view('reports/custom', [ | ||
'customfields' => CustomField::get(), | ||
'template' => $reportTemplate, | ||
]); | ||
} | ||
|
||
public function update(Request $request, ReportTemplate $reportTemplate): RedirectResponse | ||
{ | ||
$this->authorize('reports.view'); | ||
|
||
// Ignore "options" rules since data does not come in under that key... | ||
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options')); | ||
|
||
$reportTemplate->update([ | ||
'name' => $validated['name'], | ||
'options' => $request->except(['_token', 'name']), | ||
]); | ||
|
||
session()->flash('success', trans('admin/reports/message.update.success')); | ||
|
||
return redirect()->route('report-templates.show', $reportTemplate->id); | ||
} | ||
|
||
public function destroy(ReportTemplate $reportTemplate): RedirectResponse | ||
{ | ||
$this->authorize('reports.view'); | ||
|
||
$reportTemplate->delete(); | ||
|
||
return redirect()->route('reports/custom') | ||
->with('success', trans('admin/reports/message.delete.success')); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
<?php | ||
|
||
namespace App\Models; | ||
|
||
use Illuminate\Database\Eloquent\Builder; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
use Watson\Validating\ValidatingTrait; | ||
|
||
class ReportTemplate extends Model | ||
{ | ||
use HasFactory; | ||
use SoftDeletes; | ||
use ValidatingTrait; | ||
|
||
protected $casts = [ | ||
'options' => 'array', | ||
]; | ||
|
||
protected $fillable = [ | ||
'created_by', | ||
'name', | ||
'options', | ||
]; | ||
|
||
protected $rules = [ | ||
'name' => [ | ||
'required', | ||
'string', | ||
], | ||
'options' => [ | ||
'required', | ||
'array', | ||
], | ||
]; | ||
|
||
protected static function booted() | ||
{ | ||
// Scope to current user | ||
static::addGlobalScope('current_user', function (Builder $builder) { | ||
if (auth()->check()) { | ||
$builder->where('created_by', auth()->id()); | ||
} | ||
}); | ||
|
||
static::created(function (ReportTemplate $reportTemplate) { | ||
$logAction = new Actionlog([ | ||
'item_type' => ReportTemplate::class, | ||
'item_id' => $reportTemplate->id, | ||
'created_by' => auth()->id(), | ||
]); | ||
|
||
$logAction->logaction('create'); | ||
}); | ||
|
||
static::updated(function (ReportTemplate $reportTemplate) { | ||
$changed = []; | ||
|
||
foreach ($reportTemplate->getDirty() as $key => $value) { | ||
$changed[$key] = [ | ||
'old' => $reportTemplate->getOriginal($key), | ||
'new' => $reportTemplate->getAttribute($key), | ||
]; | ||
} | ||
|
||
$logAction = new Actionlog(); | ||
$logAction->item_type = ReportTemplate::class; | ||
$logAction->item_id = $reportTemplate->id; | ||
$logAction->created_by = auth()->id(); | ||
$logAction->log_meta = json_encode($changed); | ||
$logAction->logaction('update'); | ||
}); | ||
|
||
static::deleted(function (ReportTemplate $reportTemplate) { | ||
$logAction = new Actionlog([ | ||
'item_type' => ReportTemplate::class, | ||
'item_id' => $reportTemplate->id, | ||
'created_by' => auth()->id(), | ||
]); | ||
|
||
$logAction->logaction('delete'); | ||
}); | ||
} | ||
|
||
/** | ||
* Establishes the report template -> creator relationship. | ||
* | ||
*/ | ||
public function creator(): BelongsTo | ||
{ | ||
return $this->belongsTo(User::class, 'created_by'); | ||
} | ||
|
||
/** | ||
* Get the value of a checkbox field for the given field name. | ||
* | ||
* @param string $fieldName | ||
* @param string $fallbackValue The value to return if the report template is not saved yet. | ||
* | ||
*/ | ||
public function checkmarkValue(string $fieldName, string $fallbackValue = '1'): string | ||
{ | ||
// Assuming we're using the null object pattern, and an empty model | ||
// was passed to the view when showing the default report page, | ||
// return the fallback value so that checkboxes are checked by default. | ||
if (is_null($this->id)) { | ||
return $fallbackValue; | ||
} | ||
|
||
// Return the field's value if it exists and return 0 | ||
// if not so that checkboxes are unchecked by default. | ||
return $this->options[$fieldName] ?? '0'; | ||
} | ||
|
||
/** | ||
* Get the value of a radio field for the given field name. | ||
* | ||
* @param string $fieldName | ||
* @param string $value The value to check against. | ||
* @param bool $isDefault Whether the radio input being checked is the default. | ||
* | ||
*/ | ||
public function radioValue(string $fieldName, string $value, bool $isDefault = false): bool | ||
{ | ||
$fieldExists = array_has($this->options, $fieldName); | ||
|
||
// If the field doesn't exist but the radio input | ||
// being checked is the default then return true. | ||
if (!$fieldExists && $isDefault) { | ||
return true; | ||
} | ||
|
||
// If the field exists and matches what we're checking then return true. | ||
if ($fieldExists && $this->options[$fieldName] === $value) { | ||
return true; | ||
} | ||
|
||
// Otherwise return false. | ||
return false; | ||
} | ||
|
||
/** | ||
* Get the value of a select field for the given field name. | ||
* | ||
* @param string $fieldName | ||
* @param string|null $model The Eloquent model to check against. | ||
* | ||
* @return mixed|null | ||
* | ||
*/ | ||
public function selectValue(string $fieldName, string $model = null) | ||
{ | ||
// If the field does not exist then return null. | ||
if (!isset($this->options[$fieldName])) { | ||
return null; | ||
} | ||
|
||
$value = $this->options[$fieldName]; | ||
|
||
// If the value was stored as an array, most likely | ||
// due to a previously being a multi-select, | ||
// then return the first value. | ||
if (is_array($value)) { | ||
$value = $value[0]; | ||
} | ||
|
||
// If a model is provided then we should ensure we only return | ||
// the value if the model still exists. | ||
// Note: It is possible $value is an id that no longer exists and this will return null. | ||
if ($model) { | ||
$foundModel = $model::find($value); | ||
|
||
return $foundModel ? $foundModel->id : null; | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
/** | ||
* Get the values of a multi-select field for the given field name. | ||
* | ||
* @param string $fieldName | ||
* @param string|null $model The Eloquent model to check against. | ||
* | ||
* @return iterable | ||
* | ||
*/ | ||
public function selectValues(string $fieldName, string $model = null): iterable | ||
{ | ||
// If the field does not exist then return an empty array. | ||
if (!isset($this->options[$fieldName])) { | ||
return []; | ||
} | ||
|
||
// If a model is provided then we should ensure we only return | ||
// the ids of models that exist and are not deleted. | ||
if ($model) { | ||
return $model::findMany($this->options[$fieldName])->pluck('id'); | ||
} | ||
|
||
// Wrap the value in an array if needed. This is to ensure | ||
// values previously stored as a single value, | ||
// most likely from a single select, are returned as an array. | ||
if (!is_array($this->options[$fieldName])) { | ||
return [$this->options[$fieldName]]; | ||
} | ||
|
||
return $this->options[$fieldName]; | ||
} | ||
|
||
/** | ||
* Get the value of a text field for the given field name. | ||
* | ||
* @param string $fieldName | ||
* @param string|null $fallbackValue | ||
* | ||
* @return string | ||
*/ | ||
public function textValue(string $fieldName, string|null $fallbackValue = ''): string | ||
{ | ||
// Assuming we're using the null object pattern, | ||
// return the default value if the object is not saved yet. | ||
if (is_null($this->id)) { | ||
return (string) $fallbackValue; | ||
} | ||
|
||
// Return the field's value if it exists | ||
// and return the default value if not. | ||
return $this->options[$fieldName] ?? ''; | ||
} | ||
|
||
public function getDisplayNameAttribute() | ||
{ | ||
return $this->name; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understandable if we want to move these to an observer.