Skip to content

Commit 583e48b

Browse files
committed
feat: Add export csv ability to field falues tables.
1 parent d2412d9 commit 583e48b

File tree

4 files changed

+181
-19
lines changed

4 files changed

+181
-19
lines changed

resources/views/customfield/devices.blade.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,35 @@
397397
398398
$("#device_filter").submit(function(e) {
399399
e.preventDefault();
400-
grid.bootgrid("search", $("#hostname").val());
400+
grid.bootgrid("search", $("#global_search").val());
401401
});
402402
403+
$("div#manage-device-customfields").on('click', '#export-csv-btn', function() {
404+
let exportUrl = "{{ route('plugin.nmscustomfields.table.customfieldvalues.export') }}";
405+
let params = {
406+
custom_field_id: $("#custom_field_id").val(),
407+
device_id: $("#device_id").val(),
408+
search: $("#global_search").val(),
409+
_token: "{{ csrf_token() }}"
410+
};
411+
412+
let $form = $('<form>')
413+
.attr('method', 'POST')
414+
.attr('action', exportUrl);
415+
416+
$.each(params, function(key, value) {
417+
if (value) {
418+
$form.append($('<input>')
419+
.attr('type', 'hidden')
420+
.attr('name', key)
421+
.attr('value', value));
422+
}
423+
});
424+
425+
$('body').append($form);
426+
$form.submit();
427+
$form.remove();
428+
});
403429
404430
});
405431
</script>
Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,44 @@
1-
<div id="@{{ctx.id}}" class="@{{css.header}} row ">
2-
<div class="col-sm-6">
3-
<form method="post" role="form" id="device_filter" class="form-inline">
1+
<div id="@{{ctx.id}}" class="@{{css.header}} row">
2+
<div class="col-sm-12 col-md-8">
3+
<form method="post" role="form" id="device_filter" class="form">
44
{!! csrf_field() !!}
5-
<div class="row form-group">
6-
<span>@lang('Device')</span>
7-
<select class="form-control" id="device_id" name="device_id" data-placeholder="Select a Device"></select>
8-
<span>Selected Field</span>
9-
<select class="form-control" id="custom_field_id" name="custom_field_id" data-allow-clear="false">
10-
<option value="{{ $customfield->id }}">{{ $customfield->name }}</option>
11-
</select>
5+
6+
<div class="row mb-2">
7+
<div class="col-sm-5 form-group">
8+
<label for="device_id">@lang('Device')</label>
9+
<select class="form-control" id="device_id" name="device_id" data-placeholder="Select a Device"></select>
10+
</div>
11+
<div class="col-sm-5 form-group">
12+
<label for="custom_field_id">Selected Field</label>
13+
<select class="form-control" id="custom_field_id" name="custom_field_id" data-allow-clear="false">
14+
<option value="{{ $customfield->id }}">{{ $customfield->name }}</option>
15+
</select>
16+
</div>
1217
</div>
13-
<div class="row form-group tw-mt-5">
14-
<span>@lang('Device')</span>
15-
<input type="text" class="form-control" id="hostname" name="hostname" placeholder="Search device">
16-
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> @lang('Search')</button>
18+
19+
<div class="row">
20+
<div class="col-sm-9 form-group">
21+
<label for="global_search">Search</label>
22+
<div class="input-group">
23+
<input type="text" class="form-control" id="global_search" name="global_search" placeholder="Search hostname, sysName or field value">
24+
<span class="input-group-btn">
25+
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> @lang('Search')</button>
26+
</span>
27+
</div>
28+
</div>
1729
</div>
1830
</form>
1931
</div>
20-
<div class="col-sm-6">
21-
<div class="actionBar pull-right">
22-
<div class="btn-toolbar" role="toolbar">
32+
33+
<div class="col-sm-12 col-md-4">
34+
<div class="actionBar">
35+
<div style="margin-bottom: 10px;">
2336
<button id="device-add-btn" class="btn btn-primary"><i class="fa fa-plus"></i> Add device</button>
2437
<button id="bulk-edit-btn" class="btn btn-primary" disabled><i class="fa fa-pencil"></i> Bulk Edit</button>
2538
<button id="bulk-delete-btn" class="btn btn-danger" disabled><i class="fa fa-trash"></i> Bulk Delete</button>
26-
<div class="@{{css.actions}}"></div>
39+
<button id="export-csv-btn" class="btn btn-success"><i class="fa fa-download"></i> Export CSV</button>
2740
</div>
41+
<div class="@{{css.actions}}" style="clear: both;"></div>
2842
</div>
2943
</div>
3044
</div>

routes/web.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
Route::prefix('table')->namespace('Table')->group(function () {
2929
Route::post('customfields', 'CustomFieldController')->name('table.customfields');
3030
Route::post('customfieldvalues', 'CustomFieldValueController')->name('table.customfieldvalues');
31+
Route::post('customfieldvalues/export', 'CustomFieldValueController@export')->name('table.customfieldvalues.export');
3132
});
3233
});
3334
});

src/Http/Controllers/Table/CustomFieldValueController.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Http\Controllers\Table\TableController;
99
use Illuminate\Database\Eloquent\Builder;
1010
use Illuminate\Http\Request;
11+
use Illuminate\Support\Str;
1112

1213
class CustomFieldValueController extends TableController
1314
{
@@ -137,4 +138,124 @@ protected function sort($request, $query)
137138

138139
return $query;
139140
}
141+
142+
/**
143+
* Export data as CSV
144+
*
145+
* @param Request $request
146+
* @return \Symfony\Component\HttpFoundation\StreamedResponse
147+
*/
148+
public function export(Request $request)
149+
{
150+
$query = $this->prepareExportQuery($request);
151+
$data = $query->get();
152+
153+
$filenameParts = ['devicefields'];
154+
if ($request->has('custom_field_id') && !empty($request->get('custom_field_id'))) {
155+
$customField = CustomField::find($request->get('custom_field_id'));
156+
if ($customField) {
157+
$filenameParts[] = Str::slug($customField->name);
158+
}
159+
}
160+
161+
$filenameParts[] = date('Y-m-d-His');
162+
$filename = implode('-', $filenameParts) . '.csv';
163+
164+
$headers = $this->getExportHeaders();
165+
166+
return $this->generateCsvResponse($data, $headers, $filename);
167+
}
168+
169+
/**
170+
* Prepare the query for export with all filters applied
171+
*
172+
* @param Request $request
173+
* @return Builder
174+
*/
175+
protected function prepareExportQuery(Request $request)
176+
{
177+
$query = $this->baseQuery($request);
178+
179+
foreach ($this->filterFields($request) as $field) {
180+
if ($request->has($field) && $request->get($field) !== '') {
181+
$query->where($field, $request->get($field));
182+
}
183+
}
184+
185+
if ($request->has('search') && !empty($request->get('search'))) {
186+
$query = $this->search($request->get('search'), $query, $this->searchFields($request));
187+
}
188+
189+
if ($request->has('sort')) {
190+
$query = $this->sort($request, $query);
191+
}
192+
193+
return $query;
194+
}
195+
196+
/**
197+
* Get headers for CSV export
198+
*
199+
* @return array
200+
*/
201+
protected function getExportHeaders()
202+
{
203+
return [
204+
'Device ID',
205+
'Hostname',
206+
'System Name',
207+
'Custom Field ID',
208+
'Custom Field Value',
209+
];
210+
}
211+
212+
/**
213+
* Format a row for CSV export
214+
*
215+
* @param mixed $item
216+
* @return array
217+
*/
218+
protected function formatExportRow($item)
219+
{
220+
return [
221+
$item->device_id,
222+
$item->device->hostname,
223+
$item->device->sysName,
224+
$item->custom_field_id,
225+
$item->customFieldValue->value,
226+
];
227+
}
228+
229+
/**
230+
* Generate CSV response from data
231+
*
232+
* @param \Illuminate\Support\Collection $data
233+
* @param array $headers
234+
* @param string $filename
235+
* @return \Symfony\Component\HttpFoundation\StreamedResponse
236+
*/
237+
protected function generateCsvResponse($data, $headers, $filename)
238+
{
239+
return response()->stream(
240+
function () use ($data, $headers) {
241+
$output = fopen('php://output', 'w');
242+
243+
fputcsv($output, $headers);
244+
245+
foreach ($data as $item) {
246+
fputcsv($output, $this->formatExportRow($item));
247+
}
248+
249+
fclose($output);
250+
},
251+
200,
252+
[
253+
'Content-Type' => 'text/csv',
254+
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
255+
'Pragma' => 'no-cache',
256+
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
257+
'Expires' => '0',
258+
]
259+
);
260+
}
140261
}

0 commit comments

Comments
 (0)