Compare commits
5 commits
master
...
99-reports
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeb3d75f81 | ||
| 2029781d8c | |||
| 1c868d15c8 | |||
| 1cc1a92853 | |||
| 1e0b94d8ff |
7 changed files with 813 additions and 211 deletions
|
|
@ -238,3 +238,9 @@ access_keys:
|
||||||
label: Update
|
label: Update
|
||||||
- id: promo.delete
|
- id: promo.delete
|
||||||
label: Delete
|
label: Delete
|
||||||
|
|
||||||
|
- id: report
|
||||||
|
label: Reports Access
|
||||||
|
acls:
|
||||||
|
- id: report.view
|
||||||
|
label: View
|
||||||
|
|
|
||||||
81
config/routes/report.yaml
Normal file
81
config/routes/report.yaml
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
# reports
|
||||||
|
|
||||||
|
# dispatch
|
||||||
|
report_dispatch_time:
|
||||||
|
path: /reports/dispatch-time
|
||||||
|
controller: App\Controller\ReportController::dispatchTimeForm
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
report_dispatch_time_submit:
|
||||||
|
path: /reports/dispatch-time
|
||||||
|
controller: App\Controller\ReportController::dispatchTimeSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_dispatch_time_generate:
|
||||||
|
path: /reports/dispatch-time/generate/{start_date}/{end_date}
|
||||||
|
controller: App\Controller\ReportController::dispatchTimeGenerate
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
# out of stock
|
||||||
|
report_out_of_stock:
|
||||||
|
path: /reports/out-of-stock
|
||||||
|
controller: App\Controller\ReportController::outOfStockForm
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
report_out_of_stock_submit:
|
||||||
|
path: /reports/out-of-stock
|
||||||
|
controller: App\Controller\ReportController::outOfStockSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_out_of_stock_generate:
|
||||||
|
path: /reports/out-of-stock/generate/{start_date}/{end_date}
|
||||||
|
controller: App\Controller\ReportController::outOfStockGenerate
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
# rejection
|
||||||
|
report_rejection:
|
||||||
|
path: /reports/rejection
|
||||||
|
controller: App\Controller\ReportController::rejectionForm
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
report_rejection_submit:
|
||||||
|
path: /reports/rejection
|
||||||
|
controller: App\Controller\ReportController::rejectionSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_rejection_generate:
|
||||||
|
path: /reports/rejection/generate/{start_date}/{end_date}
|
||||||
|
controller: App\Controller\ReportController::rejectionGenerate
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
# sales
|
||||||
|
report_sales:
|
||||||
|
path: /reports/sales
|
||||||
|
controller: App\Controller\ReportController::salesForm
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
report_sales_submit:
|
||||||
|
path: /reports/sales
|
||||||
|
controller: App\Controller\ReportController::salesSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_sales_generate:
|
||||||
|
path: /reports/sales/generate/{start_date}/{end_date}
|
||||||
|
controller: App\Controller\ReportController::salesGenerate
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
# service
|
||||||
|
report_service:
|
||||||
|
path: /reports/service
|
||||||
|
controller: App\Controller\ReportController::serviceForm
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
report_service_submit:
|
||||||
|
path: /reports/service
|
||||||
|
controller: App\Controller\ReportController::serviceSubmit
|
||||||
|
methods: [POST]
|
||||||
|
|
||||||
|
report_service_generate:
|
||||||
|
path: /reports/service/generate/{start_date}/{end_date}
|
||||||
|
controller: App\Controller\ReportController::serviceGenerate
|
||||||
|
methods: [GET]
|
||||||
439
src/Controller/ReportController.php
Normal file
439
src/Controller/ReportController.php
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Ramcar\BaseController;
|
||||||
|
use App\Entity\Hub;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use DateInterval;
|
||||||
|
use DatePeriod;
|
||||||
|
|
||||||
|
class ReportController extends BaseController
|
||||||
|
{
|
||||||
|
public function dispatchTimeForm(Request $req)
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('report.view', null, 'No access.');
|
||||||
|
|
||||||
|
$params = $this->initParameters('report_dispatch_time');
|
||||||
|
|
||||||
|
return $this->render('report/dispatch-time.html.twig', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatchTimeSubmit(Request $req)
|
||||||
|
{
|
||||||
|
// initialize error list
|
||||||
|
$error_array = [];
|
||||||
|
|
||||||
|
$start_date = $req->request->get('start_date');
|
||||||
|
$end_date = $req->request->get('end_date');
|
||||||
|
|
||||||
|
// validate dates
|
||||||
|
$dates = $this->validateDateRange($start_date, $end_date, $error_array);
|
||||||
|
|
||||||
|
// check if any errors were found
|
||||||
|
if (!empty($error_array))
|
||||||
|
{
|
||||||
|
// return validation failure response
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $error_array
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return response
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'url' => $this->generateUrl('report_dispatch_time_generate', [
|
||||||
|
'start_date' => $dates['start'],
|
||||||
|
'end_date' => $dates['end']
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatchTimeGenerate(Request $req, $start_date, $end_date)
|
||||||
|
{
|
||||||
|
// get dates
|
||||||
|
$date_format = "Y-m-d";
|
||||||
|
$sd = DateTime::createFromFormat($date_format, $start_date);
|
||||||
|
$ed = DateTime::createFromFormat($date_format, $end_date);
|
||||||
|
|
||||||
|
// format filename
|
||||||
|
$filename = "consolidated-dispatch-time-" . $sd->format('Ymd') . '-' . $ed->format('Ymd') . ".csv";
|
||||||
|
|
||||||
|
// build response
|
||||||
|
$response = new StreamedResponse();
|
||||||
|
|
||||||
|
$response->setCallback(function() use ($sd, $ed) {
|
||||||
|
$handle = fopen('php://output', 'w+');
|
||||||
|
|
||||||
|
// make table headers
|
||||||
|
$headers = ['Agent'];
|
||||||
|
|
||||||
|
// add date interval headers
|
||||||
|
$period = $this->generateDateHeaders($sd, $ed, $headers);
|
||||||
|
|
||||||
|
$headers[] = 'Grand Total';
|
||||||
|
|
||||||
|
// set table headers
|
||||||
|
fputcsv($handle, $headers);
|
||||||
|
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
$conn = $em->getConnection();
|
||||||
|
|
||||||
|
// get all users
|
||||||
|
$sql = "SELECT * FROM user ORDER BY first_name ASC";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->execute();
|
||||||
|
$users = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// initiate
|
||||||
|
$tallies = [];
|
||||||
|
$jo_counts = [];
|
||||||
|
$averages = [];
|
||||||
|
$col_jo_counts = [];
|
||||||
|
$col_totals = [];
|
||||||
|
|
||||||
|
// get all job orders
|
||||||
|
$sql = "SELECT * FROM job_order WHERE date_schedule IS NOT NULL AND date_assign IS NOT NULL AND date_assign BETWEEN ? AND ?";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bindValue(1, $sd->format("Y-m-d"));
|
||||||
|
$stmt->bindValue(2, $ed->format("Y-m-d"));
|
||||||
|
$stmt->execute();
|
||||||
|
$job_orders = $stmt->fetchAll();
|
||||||
|
|
||||||
|
foreach ($job_orders as $job_order)
|
||||||
|
{
|
||||||
|
$date_schedule = DateTime::createFromFormat("Y-m-d H:i:s", $job_order['date_schedule']);
|
||||||
|
$date_assign = DateTime::createFromFormat("Y-m-d H:i:s", $job_order['date_assign']);
|
||||||
|
$date_assign_formatted = $date_assign->format("Y-m-d");
|
||||||
|
|
||||||
|
// get difference between times
|
||||||
|
$date_schedule_ts = $date_schedule->getTimestamp();
|
||||||
|
$date_assign_ts = $date_assign->getTimestamp();
|
||||||
|
$diff_mins = ($date_assign_ts - $date_schedule_ts) / 60;
|
||||||
|
|
||||||
|
// add to minutes tally
|
||||||
|
if (!isset($tallies[$job_order['assign_user_id']][$date_assign_formatted]))
|
||||||
|
$tallies[$job_order['assign_user_id']][$date_assign_formatted] = $diff_mins;
|
||||||
|
else
|
||||||
|
$tallies[$job_order['assign_user_id']][$date_assign_formatted] += $diff_mins;
|
||||||
|
|
||||||
|
// append to jo counter
|
||||||
|
if (!isset($jo_counts[$job_order['assign_user_id']][$date_assign_formatted]))
|
||||||
|
$jo_counts[$job_order['assign_user_id']][$date_assign_formatted] = 1;
|
||||||
|
else
|
||||||
|
$jo_counts[$job_order['assign_user_id']][$date_assign_formatted]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append to csv
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
// check if this user is an agent
|
||||||
|
$sql = "SELECT * FROM user_role WHERE user_id = ? AND (role_id = ? OR role_id = ?)";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bindValue(1, $user['id']);
|
||||||
|
$stmt->bindValue(2, "ROLE_AGENT_TIER_1");
|
||||||
|
$stmt->bindValue(3, "ROLE_AGENT_TIER_2");
|
||||||
|
$stmt->execute();
|
||||||
|
$roles = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// only include if user is agent
|
||||||
|
if (count($roles) > 0)
|
||||||
|
{
|
||||||
|
$total_mins = 0;
|
||||||
|
$total_jos = 0;
|
||||||
|
|
||||||
|
$full_name = implode(" ", [
|
||||||
|
$user['first_name'],
|
||||||
|
$user['last_name']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$rowdata = [$full_name];
|
||||||
|
|
||||||
|
foreach ($period as $date)
|
||||||
|
{
|
||||||
|
$date_formatted = $date->format("Y-m-d");
|
||||||
|
|
||||||
|
// get average per date for this user
|
||||||
|
if (isset($tallies[$user['id']][$date_formatted]) && isset($jo_counts[$user['id']][$date_formatted]))
|
||||||
|
{
|
||||||
|
$date_tally = $tallies[$user['id']][$date_formatted];
|
||||||
|
$jo_tally = $jo_counts[$user['id']][$date_formatted];
|
||||||
|
$rowdata[] = $date_tally / $jo_tally;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$date_tally = 0;
|
||||||
|
$jo_tally = 0;
|
||||||
|
$rowdata[] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// add to totals
|
||||||
|
$total_mins += $date_tally;
|
||||||
|
$total_jos += $jo_tally;
|
||||||
|
|
||||||
|
// add to column totals
|
||||||
|
if (!isset($col_totals[$date_formatted]))
|
||||||
|
$col_totals[$date_formatted] = $date_tally;
|
||||||
|
else
|
||||||
|
$col_totals[$date_formatted] += $date_tally;
|
||||||
|
|
||||||
|
// append to column jo counter
|
||||||
|
if (!isset($col_jo_counts[$date_formatted]))
|
||||||
|
$col_jo_counts[$date_formatted] = $jo_tally;
|
||||||
|
else
|
||||||
|
$col_jo_counts[$date_formatted] += $jo_tally;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add total to rowdata
|
||||||
|
$rowdata[] = $total_jos > 0 ? $total_mins / $total_jos : 0;
|
||||||
|
|
||||||
|
fputcsv($handle, $rowdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// include column totals row
|
||||||
|
$totals_row = ['GRAND TOTAL'];
|
||||||
|
|
||||||
|
foreach ($period as $date)
|
||||||
|
{
|
||||||
|
$date_formatted = $date->format("Y-m-d");
|
||||||
|
$totals_row[] = $col_jo_counts[$date_formatted] > 0 ? $col_totals[$date_formatted] / $col_jo_counts[$date_formatted] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add overall total
|
||||||
|
$overall_jo_total = count($job_orders);
|
||||||
|
$totals_row[] = $overall_jo_total > 0 ? array_sum($col_totals) / $overall_jo_total : 0;
|
||||||
|
|
||||||
|
fputcsv($handle, $totals_row);
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add response headers
|
||||||
|
$response->setStatusCode(200);
|
||||||
|
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename);
|
||||||
|
|
||||||
|
// send
|
||||||
|
$response->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serviceForm(Request $req)
|
||||||
|
{
|
||||||
|
$this->denyAccessUnlessGranted('report.view', null, 'No access.');
|
||||||
|
|
||||||
|
$params = $this->initParameters('report_service');
|
||||||
|
|
||||||
|
return $this->render('report/service.html.twig', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serviceSubmit(Request $req)
|
||||||
|
{
|
||||||
|
// initialize error list
|
||||||
|
$error_array = [];
|
||||||
|
|
||||||
|
$start_date = $req->request->get('start_date');
|
||||||
|
$end_date = $req->request->get('end_date');
|
||||||
|
|
||||||
|
// validate dates
|
||||||
|
$dates = $this->validateDateRange($start_date, $end_date, $error_array);
|
||||||
|
|
||||||
|
// check if any errors were found
|
||||||
|
if (!empty($error_array))
|
||||||
|
{
|
||||||
|
// return validation failure response
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'errors' => $error_array
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return response
|
||||||
|
return $this->json([
|
||||||
|
'success' => true,
|
||||||
|
'url' => $this->generateUrl('report_service_generate', [
|
||||||
|
'start_date' => $dates['start'],
|
||||||
|
'end_date' => $dates['end']
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serviceGenerate(Request $req, $start_date, $end_date)
|
||||||
|
{
|
||||||
|
// get dates
|
||||||
|
$date_format = "Y-m-d";
|
||||||
|
$sd = DateTime::createFromFormat($date_format, $start_date);
|
||||||
|
$ed = DateTime::createFromFormat($date_format, $end_date);
|
||||||
|
|
||||||
|
// format filename
|
||||||
|
$filename = "service-" . $sd->format('Ymd') . '-' . $ed->format('Ymd') . ".csv";
|
||||||
|
|
||||||
|
// build response
|
||||||
|
$response = new StreamedResponse();
|
||||||
|
|
||||||
|
$response->setCallback(function() use ($sd, $ed) {
|
||||||
|
$handle = fopen('php://output', 'w+');
|
||||||
|
|
||||||
|
// make table headers
|
||||||
|
$headers = [
|
||||||
|
'Hub',
|
||||||
|
'Branch'
|
||||||
|
];
|
||||||
|
|
||||||
|
// add date interval headers
|
||||||
|
$period = $this->generateDateHeaders($sd, $ed, $headers);
|
||||||
|
|
||||||
|
$headers[] = 'GRAND TOTAL';
|
||||||
|
|
||||||
|
// set table headers
|
||||||
|
fputcsv($handle, $headers);
|
||||||
|
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
$conn = $em->getConnection();
|
||||||
|
|
||||||
|
// get all hubs
|
||||||
|
$sql = "SELECT * FROM hub ORDER BY name ASC";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->execute();
|
||||||
|
$hubs = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$tallies = [];
|
||||||
|
$col_totals = [];
|
||||||
|
|
||||||
|
// get all job orders
|
||||||
|
$sql = "SELECT * FROM job_order WHERE date_schedule BETWEEN ? AND ?";
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
$stmt->bindValue(1, $sd->format("Y-m-d"));
|
||||||
|
$stmt->bindValue(2, $ed->format("Y-m-d"));
|
||||||
|
$stmt->execute();
|
||||||
|
$job_orders = $stmt->fetchAll();
|
||||||
|
|
||||||
|
foreach ($job_orders as $job_order)
|
||||||
|
{
|
||||||
|
$date_sched = DateTime::createFromFormat("Y-m-d H:i:s", $job_order['date_schedule']);
|
||||||
|
$date_sched_formatted = $date_sched->format("Y-m-d");
|
||||||
|
|
||||||
|
if (!isset($tallies[$job_order['hub_id']][$date_sched_formatted]))
|
||||||
|
$tallies[$job_order['hub_id']][$date_sched_formatted] = 1;
|
||||||
|
else
|
||||||
|
$tallies[$job_order['hub_id']][$date_sched_formatted]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append to csv
|
||||||
|
foreach ($hubs as $hub)
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
|
||||||
|
$rowdata = [
|
||||||
|
$hub['name'],
|
||||||
|
$hub['branch']
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($period as $date)
|
||||||
|
{
|
||||||
|
$date_formatted = $date->format("Y-m-d");
|
||||||
|
$date_tally = $tallies[$hub['id']][$date_formatted] ?? 0;
|
||||||
|
$rowdata[] = $date_tally;
|
||||||
|
$total += $date_tally;
|
||||||
|
|
||||||
|
// add to column totals
|
||||||
|
if (!isset($col_totals[$date_formatted]))
|
||||||
|
$col_totals[$date_formatted] = $date_tally;
|
||||||
|
else
|
||||||
|
$col_totals[$date_formatted] += $date_tally;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add total to rowdata
|
||||||
|
$rowdata[] = $total;
|
||||||
|
|
||||||
|
fputcsv($handle, $rowdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// include column totals row
|
||||||
|
$totals_row = ['GRAND TOTAL', ''];
|
||||||
|
|
||||||
|
foreach ($period as $date)
|
||||||
|
$totals_row[] = $col_totals[$date->format("Y-m-d")];
|
||||||
|
|
||||||
|
// add overall total
|
||||||
|
$totals_row[] = array_sum($col_totals);
|
||||||
|
|
||||||
|
fputcsv($handle, $totals_row);
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
});
|
||||||
|
|
||||||
|
// add response headers
|
||||||
|
$response->setStatusCode(200);
|
||||||
|
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename);
|
||||||
|
|
||||||
|
// send
|
||||||
|
$response->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateDateRange($start_date, $end_date, &$error_array)
|
||||||
|
{
|
||||||
|
$date_format = "d M Y";
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
if (empty($start_date))
|
||||||
|
{
|
||||||
|
$error = true;
|
||||||
|
$error_array['start_date'] = 'No start date specified.';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sd = DateTime::createFromFormat($date_format, $start_date);
|
||||||
|
|
||||||
|
if (!$sd || $sd->format($date_format) != $start_date)
|
||||||
|
{
|
||||||
|
$error = true;
|
||||||
|
$error_array['start_date'] = 'Invalid start date specified.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($end_date))
|
||||||
|
{
|
||||||
|
$error = true;
|
||||||
|
$error_array['end_date'] = 'No end date specified.';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$ed = DateTime::createFromFormat($date_format, $end_date);
|
||||||
|
|
||||||
|
if (!$ed || $ed->format($date_format) != $end_date)
|
||||||
|
{
|
||||||
|
$error = true;
|
||||||
|
$error_array['end_date'] = 'Invalid end date specified.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $error ? false : [
|
||||||
|
'start' => $sd->format("Y-m-d"),
|
||||||
|
'end' => $ed->format("Y-m-d")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateDateHeaders($start_date, $end_date, &$headers)
|
||||||
|
{
|
||||||
|
$interval = DateInterval::createFromDateString('1 day');
|
||||||
|
$period = new DatePeriod($start_date, $interval, $end_date);
|
||||||
|
|
||||||
|
foreach ($period as $date)
|
||||||
|
$headers[] = $date->format('M j, Y');
|
||||||
|
|
||||||
|
return $period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -96,247 +96,53 @@
|
||||||
<i class="m-menu__hor-arrow la la-angle-down"></i>
|
<i class="m-menu__hor-arrow la la-angle-down"></i>
|
||||||
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<div class="m-menu__submenu m-menu__submenu--fixed m-menu__submenu--left" style="width:1000px">
|
<div class="m-menu__submenu m-menu__submenu--fixed m-menu__submenu--left" style="width:600px">
|
||||||
<span class="m-menu__arrow m-menu__arrow--adjust"></span>
|
<span class="m-menu__arrow m-menu__arrow--adjust"></span>
|
||||||
<div class="m-menu__subnav">
|
<div class="m-menu__subnav">
|
||||||
<ul class="m-menu__content">
|
<ul class="m-menu__content">
|
||||||
<li class="m-menu__item">
|
<li class="m-menu__item">
|
||||||
<h3 class="m-menu__heading m-menu__toggle">
|
<ul class="m-menu__inner" style="padding-top:20px">
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Finance Reports
|
|
||||||
</span>
|
|
||||||
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
|
||||||
</h3>
|
|
||||||
<ul class="m-menu__inner">
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
<a href="{{ url('report_dispatch_time') }}" class="m-menu__link">
|
||||||
<i class="m-menu__link-icon flaticon-map"></i>
|
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
||||||
|
<span></span>
|
||||||
|
</i>
|
||||||
<span class="m-menu__link-text">
|
<span class="m-menu__link-text">
|
||||||
Annual Reports
|
Consolidated Dispatch Time
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
<a href="{{ url('report_rejection') }}" class="m-menu__link">
|
||||||
<i class="m-menu__link-icon flaticon-user"></i>
|
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
||||||
|
<span></span>
|
||||||
|
</i>
|
||||||
<span class="m-menu__link-text">
|
<span class="m-menu__link-text">
|
||||||
HR Reports
|
Consolidated Rejection
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-icon flaticon-clipboard"></i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
IPO Reports
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-icon flaticon-graphic-1"></i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Finance Margins
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-icon flaticon-graphic-2"></i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Revenue Reports
|
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="m-menu__item">
|
<li class="m-menu__item">
|
||||||
<h3 class="m-menu__heading m-menu__toggle">
|
<ul class="m-menu__inner" style="padding-top:20px">
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Project Reports
|
|
||||||
</span>
|
|
||||||
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
|
||||||
</h3>
|
|
||||||
<ul class="m-menu__inner">
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
<a href="{{ url('report_sales') }}" class="m-menu__link">
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Coca Cola CRM
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Delta Airlines Booking Site
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Malibu Accounting
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Vineseed Website Rewamp
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Zircon Mobile App
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--line">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Mercury CMS
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item">
|
|
||||||
<h3 class="m-menu__heading m-menu__toggle">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
HR Reports
|
|
||||||
</span>
|
|
||||||
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
|
||||||
</h3>
|
|
||||||
<ul class="m-menu__inner">
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
||||||
<span></span>
|
<span></span>
|
||||||
</i>
|
</i>
|
||||||
<span class="m-menu__link-text">
|
<span class="m-menu__link-text">
|
||||||
Staff Directory
|
Consolidated Sales
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
<a href="{{ url('report_service') }}" class="m-menu__link">
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
||||||
<span></span>
|
<span></span>
|
||||||
</i>
|
</i>
|
||||||
<span class="m-menu__link-text">
|
<span class="m-menu__link-text">
|
||||||
Client Directory
|
Service
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Salary Reports
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Staff Payslips
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Corporate Expenses
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<i class="m-menu__link-bullet m-menu__link-bullet--dot">
|
|
||||||
<span></span>
|
|
||||||
</i>
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Project Expenses
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item">
|
|
||||||
<h3 class="m-menu__heading m-menu__toggle">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Reporting Apps
|
|
||||||
</span>
|
|
||||||
<i class="m-menu__ver-arrow la la-angle-right"></i>
|
|
||||||
</h3>
|
|
||||||
<ul class="m-menu__inner">
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Report Adjusments
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Sources & Mediums
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Reporting Settings
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Conversions
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Report Flows
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="m-menu__item " data-redirect="true" aria-haspopup="true">
|
|
||||||
<a href="header/actions.html" class="m-menu__link">
|
|
||||||
<span class="m-menu__link-text">
|
|
||||||
Audit & Logs
|
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
125
templates/report/dispatch-time.html.twig
Normal file
125
templates/report/dispatch-time.html.twig
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<!-- BEGIN: Subheader -->
|
||||||
|
<div class="m-subheader">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="mr-auto">
|
||||||
|
<h3 class="m-subheader__title">Reports</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END: Subheader -->
|
||||||
|
<div class="m-content">
|
||||||
|
<!--Begin::Section-->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6">
|
||||||
|
<div class="m-portlet m-portlet--mobile">
|
||||||
|
<div class="m-portlet__head">
|
||||||
|
<div class="m-portlet__head-caption">
|
||||||
|
<div class="m-portlet__head-title">
|
||||||
|
<span class="m-portlet__head-icon">
|
||||||
|
<i class="la la-user"></i>
|
||||||
|
</span>
|
||||||
|
<h3 class="m-portlet__head-text">
|
||||||
|
Consolidated Dispatch Time
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form id="row-form" class="m-form m-form--fit m-form--label-align-right" method="post" action="{{ url('report_dispatch_time_submit') }}">
|
||||||
|
|
||||||
|
{% include 'report/range-picker.html.twig' %}
|
||||||
|
|
||||||
|
<div class="m-portlet__foot m-portlet__foot--fit">
|
||||||
|
<div class="m-form__actions m-form__actions--solid m-form__actions--right">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<button type="submit" class="btn btn-success">Download Report</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
// date range picker
|
||||||
|
$('#daterange_picker').datepicker({
|
||||||
|
todayHighlight: true,
|
||||||
|
autoclose: true,
|
||||||
|
bootcssVer: 3,
|
||||||
|
format: "dd M yyyy",
|
||||||
|
orientation: "bottom left",
|
||||||
|
templates: {
|
||||||
|
leftArrow: '<i class="la la-angle-left"></i>',
|
||||||
|
rightArrow: '<i class="la la-angle-right"></i>'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#row-form").submit(function(e) {
|
||||||
|
var form = $(this);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: "POST",
|
||||||
|
url: form.prop('action'),
|
||||||
|
data: form.serialize()
|
||||||
|
}).done(function(response) {
|
||||||
|
// remove all error classes
|
||||||
|
removeErrors();
|
||||||
|
window.location.href = response.url;
|
||||||
|
}).fail(function(response) {
|
||||||
|
if (response.status == 422) {
|
||||||
|
var errors = response.responseJSON.errors;
|
||||||
|
var firstfield = false;
|
||||||
|
|
||||||
|
// remove all error classes first
|
||||||
|
removeErrors();
|
||||||
|
|
||||||
|
// display errors contextually
|
||||||
|
$.each(errors, function(field, msg) {
|
||||||
|
var formfield = $("[name='" + field + "'], [data-name='" + field + "']");
|
||||||
|
var label = $("label[data-field='" + field + "']");
|
||||||
|
var msgbox = $(".form-control-feedback[data-field='" + field + "']");
|
||||||
|
|
||||||
|
// add error classes to bad fields
|
||||||
|
formfield.addClass('form-control-danger');
|
||||||
|
label.addClass('has-danger');
|
||||||
|
msgbox.html(msg).addClass('has-danger').removeClass('hide');
|
||||||
|
|
||||||
|
// check if this field comes first in DOM
|
||||||
|
var domfield = formfield.get(0);
|
||||||
|
|
||||||
|
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
|
||||||
|
firstfield = domfield;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// focus on first bad field
|
||||||
|
//firstfield.focus();
|
||||||
|
|
||||||
|
// scroll to above that field to make it visible
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $(firstfield).offset().top - 200
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove all error classes
|
||||||
|
function removeErrors() {
|
||||||
|
$(".form-control-danger").removeClass('form-control-danger');
|
||||||
|
$("[data-field]").removeClass('has-danger');
|
||||||
|
$(".form-control-feedback[data-field]").addClass('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
20
templates/report/range-picker.html.twig
Normal file
20
templates/report/range-picker.html.twig
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="m-portlet__body">
|
||||||
|
<div class="m-form__section m-form__section--first">
|
||||||
|
<div class="m-form__heading">
|
||||||
|
<h3 class="m-form__heading-title">
|
||||||
|
Date Range
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="form-group m-form__group">
|
||||||
|
<div class="input-daterange input-group" id="daterange_picker">
|
||||||
|
<input type="text" class="form-control m-input" name="start_date" placeholder="Start date">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
to
|
||||||
|
</span>
|
||||||
|
<input type="text" class="form-control" name="end_date" placeholder="End date">
|
||||||
|
</div>
|
||||||
|
<div class="form-control-feedback hide" data-field="start_date"></div>
|
||||||
|
<div class="form-control-feedback hide" data-field="end_date"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
125
templates/report/service.html.twig
Normal file
125
templates/report/service.html.twig
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<!-- BEGIN: Subheader -->
|
||||||
|
<div class="m-subheader">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="mr-auto">
|
||||||
|
<h3 class="m-subheader__title">Reports</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END: Subheader -->
|
||||||
|
<div class="m-content">
|
||||||
|
<!--Begin::Section-->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6">
|
||||||
|
<div class="m-portlet m-portlet--mobile">
|
||||||
|
<div class="m-portlet__head">
|
||||||
|
<div class="m-portlet__head-caption">
|
||||||
|
<div class="m-portlet__head-title">
|
||||||
|
<span class="m-portlet__head-icon">
|
||||||
|
<i class="la la-user"></i>
|
||||||
|
</span>
|
||||||
|
<h3 class="m-portlet__head-text">
|
||||||
|
Service
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form id="row-form" class="m-form m-form--fit m-form--label-align-right" method="post" action="{{ url('report_service_submit') }}">
|
||||||
|
|
||||||
|
{% include 'report/range-picker.html.twig' %}
|
||||||
|
|
||||||
|
<div class="m-portlet__foot m-portlet__foot--fit">
|
||||||
|
<div class="m-form__actions m-form__actions--solid m-form__actions--right">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<button type="submit" class="btn btn-success">Download Report</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
// date range picker
|
||||||
|
$('#daterange_picker').datepicker({
|
||||||
|
todayHighlight: true,
|
||||||
|
autoclose: true,
|
||||||
|
bootcssVer: 3,
|
||||||
|
format: "dd M yyyy",
|
||||||
|
orientation: "bottom left",
|
||||||
|
templates: {
|
||||||
|
leftArrow: '<i class="la la-angle-left"></i>',
|
||||||
|
rightArrow: '<i class="la la-angle-right"></i>'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#row-form").submit(function(e) {
|
||||||
|
var form = $(this);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: "POST",
|
||||||
|
url: form.prop('action'),
|
||||||
|
data: form.serialize()
|
||||||
|
}).done(function(response) {
|
||||||
|
// remove all error classes
|
||||||
|
removeErrors();
|
||||||
|
window.location.href = response.url;
|
||||||
|
}).fail(function(response) {
|
||||||
|
if (response.status == 422) {
|
||||||
|
var errors = response.responseJSON.errors;
|
||||||
|
var firstfield = false;
|
||||||
|
|
||||||
|
// remove all error classes first
|
||||||
|
removeErrors();
|
||||||
|
|
||||||
|
// display errors contextually
|
||||||
|
$.each(errors, function(field, msg) {
|
||||||
|
var formfield = $("[name='" + field + "'], [data-name='" + field + "']");
|
||||||
|
var label = $("label[data-field='" + field + "']");
|
||||||
|
var msgbox = $(".form-control-feedback[data-field='" + field + "']");
|
||||||
|
|
||||||
|
// add error classes to bad fields
|
||||||
|
formfield.addClass('form-control-danger');
|
||||||
|
label.addClass('has-danger');
|
||||||
|
msgbox.html(msg).addClass('has-danger').removeClass('hide');
|
||||||
|
|
||||||
|
// check if this field comes first in DOM
|
||||||
|
var domfield = formfield.get(0);
|
||||||
|
|
||||||
|
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
|
||||||
|
firstfield = domfield;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// focus on first bad field
|
||||||
|
//firstfield.focus();
|
||||||
|
|
||||||
|
// scroll to above that field to make it visible
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop: $(firstfield).offset().top - 200
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove all error classes
|
||||||
|
function removeErrors() {
|
||||||
|
$(".form-control-danger").removeClass('form-control-danger');
|
||||||
|
$("[data-field]").removeClass('has-danger');
|
||||||
|
$(".form-control-feedback[data-field]").addClass('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Reference in a new issue